链接上数据库

This commit is contained in:
2026-02-27 12:02:15 +08:00
parent d3872250dd
commit ab0a5c53f7
5 changed files with 577 additions and 144 deletions

View File

@@ -1,6 +1,8 @@
// /components/supadb/aksupa.uts
import { AkReqResponse, AkReqUploadOptions, AkReq } from '@/uni_modules/ak-req/index.uts'
import type { AkReqOptions } from '@/uni_modules/ak-req/index.uts'
import { toUniError } from '@/utils/utils.uts'
import { IS_TEST_MODE } from '@/ak/config.uts'
export type AkSupaSignInResult = {
access_token : string;
@@ -805,14 +807,22 @@ export class AkSupa {
* { usr_id: { lt: 800 }, name: { ilike: '%foo%' }, status: 'active', age: { gte: 18, lte: 30 } }
* 操作符支持 eq, neq, lt, lte, gt, gte, like, ilike, in, is, not, contains, containedBy, range, fts, plfts, phfts, wfts
*/
async select(table : string, filter ?: string | null, options ?: AkSupaSelectOptions) : Promise<AkReqResponse<any>> {
let url = this.baseUrl + '/rest/v1/' + table;
let headers = {
apikey: this.apikey,
'Content-Type': 'application/json',
Authorization: `Bearer ${AkReq.getToken() ?? ''}`
} as UTSJSONObject;
let params : string[] = [];
async select(table : string, filter ?: string | null, options ?: AkSupaSelectOptions) : Promise<AkReqResponse<any>> {
let url = this.baseUrl + '/rest/v1/' + table;
const token = AkReq.getToken()
let headers = {
apikey: this.apikey,
'Content-Type': 'application/json'
} as UTSJSONObject;
// 只有在明确有用户 token 的情况下才发送 Authorization
// 否则只带 apikey这样 Kong 会自动映射到 anon 角色,避免 JWT 校验失败
if (token != null && token != '') {
headers['Authorization'] = `Bearer ${token}`;
}
let params : string[] = [];
if (options != null) {
if (options.columns != null && !(options.columns == "")) params.push('select=' + encodeURIComponent(options.columns ?? ""));
if (options.limit != null) {
@@ -897,10 +907,17 @@ async select_uts(table : string, filter ?: UTSJSONObject | null, options ?: AkSu
*/
async insert(table : string, row : UTSJSONObject | Array<UTSJSONObject>) : Promise<AkReqResponse<any>> {
const url = this.baseUrl + '/rest/v1/' + table;
const headers = {
const token = AkReq.getToken()
let authHeader = `Bearer ${this.apikey}`;
if (token != null && token != '') {
authHeader = `Bearer ${token}`;
}
let headers = {
apikey: this.apikey,
'Content-Type': 'application/json',
Authorization: `Bearer ${AkReq.getToken() ?? ''}`,
Authorization: authHeader,
Prefer: 'return=representation'
} as UTSJSONObject;
@@ -910,7 +927,7 @@ async select_uts(table : string, filter ?: UTSJSONObject | null, options ?: AkSu
url,
method: 'POST',
headers,
data: row, // 可以是单个对象或数组
data: row,
contentType: 'application/json'
};
return await this.requestWithAutoRefresh(reqOptions);
@@ -928,12 +945,18 @@ async update(table : string, filter : string | null, values : UTSJSONObject) : P
if (filter!=null && filter !== "") {
url += '?' + filter;
}
const headers = {
const token = AkReq.getToken()
let headers = {
apikey: this.apikey,
'Content-Type': 'application/json',
Authorization: `Bearer ${AkReq.getToken() ?? ''}`,
Prefer: 'return=representation'
} as UTSJSONObject;
if (token != null && token != '') {
headers['Authorization'] = `Bearer ${token}`;
}
let reqOptions : AkReqOptions = {
url,
method: 'PATCH',
@@ -955,12 +978,18 @@ async delete(table : string, filter : string | null) : Promise<AkReqResponse<any
if (filter!=null && filter !== "") {
url += '?' + filter;
}
const headers = {
const token = AkReq.getToken()
let headers = {
apikey: this.apikey,
'Content-Type': 'application/json',
Authorization: `Bearer ${AkReq.getToken() ?? ''}`,
Prefer: 'return=representation'
} as UTSJSONObject;
if (token != null && token != '') {
headers['Authorization'] = `Bearer ${token}`;
}
let reqOptions : AkReqOptions = {
url,
method: 'DELETE',
@@ -978,11 +1007,17 @@ async delete(table : string, filter : string | null) : Promise<AkReqResponse<any
*/
async rpc(functionName : string, params ?: UTSJSONObject) : Promise<AkReqResponse<any>> {
const url = `${this.baseUrl}/rest/v1/rpc/${functionName}`;
const headers = {
const token = AkReq.getToken()
let headers = {
apikey: this.apikey,
'Content-Type': 'application/json',
Authorization: `Bearer ${AkReq.getToken() ?? ''}`
'Content-Type': 'application/json'
} as UTSJSONObject;
if (token != null && token != '') {
headers['Authorization'] = `Bearer ${token}`;
}
let reqOptions : AkReqOptions = {
url,
method: 'POST',
@@ -1042,24 +1077,34 @@ async delete(table : string, filter : string | null) : Promise<AkReqResponse<any
// AkSupa类内新增自动刷新封装
async requestWithAutoRefresh(reqOptions : AkReqOptions, isRetry = false) : Promise<AkReqResponse<any>> {
let res = await AkReq.request(reqOptions, false);
// JWT过期Supabase风格
const isJwtExpired = (res.status == 401); //res != null && res.data != null && typeof res.data == 'object' && (res.data as UTSJSONObject)?.getString('code') == 'PGRST301';
// 401未授权
const isUnauthorized = (res.status == 401);
if ((isJwtExpired || isUnauthorized) && !isRetry) {
// JWT过期/401未授权
const needsHandle = (res.status == 401);
if (needsHandle && !isRetry) {
const ok = await this.refreshSession();
if (ok) {
const newToken = AkReq.getToken() ?? ''
let headers = reqOptions.headers
if (headers == null) {
headers = new UTSJSONObject()
}
if (typeof headers.set == 'function') {
headers.set('Authorization', `Bearer ${AkReq.getToken() ?? ''}`)
headers.set('Authorization', `Bearer ${newToken}`)
reqOptions.headers = headers
}
res = await AkReq.request(reqOptions, false);
} else {
// 如果是测试模式且失败401且无法刷新不再抛出异常阻止执行但确保 res.error 有值
if (IS_TEST_MODE === true) {
console.warn('[TestMode] Token expired or not found, but continuing anyway. Status:', res.status)
console.log('[TestMode] Response body:', JSON.stringify(res.data))
if (res.error == null) {
res.error = toUniError('认证失败 (401)', 'UNAUTHORIZED');
}
return res;
}
uni.removeStorageSync('user_id');
uni.removeStorageSync('token');
@@ -1067,6 +1112,12 @@ async delete(table : string, filter : string | null) : Promise<AkReqResponse<any
throw toUniError('登录已过期,请重新登录', '用户认证失败');
}
}
// 额外检查:如果 status >= 400 且 res.error 为空,注入一个 error
if (res.status >= 400 && res.error == null) {
res.error = toUniError(`请求失败: ${res.status}`, 'HTTP_ERROR');
}
return res;
}
}