// app/api/postgrest-client.ts import { apiRequest, type QueryParams } from './client'; import { handleApiError } from './error-handler'; /** * PostgresREST 特定的查询参数接口 */ export interface PostgrestParams { select?: string; order?: string; limit?: number; offset?: number; filter?: Record; schema?: string; // 指定 PostgreSQL schema or?: Array> | string; // 支持OR条件查询 [key: string]: unknown; // 允许添加其他参数 headers?: Record; } /** * 打印 PostgREST 查询日志 * @param endpoint 端点 * @param params 参数 */ function logPostgrestQuery(endpoint: string, params?: QueryParams): void { if (process.env.NODE_ENV !== 'production') { // const baseUrl = 'http://172.16.0.119:9000/admin'; const baseUrl = 'http://172.18.0.100:3000'; console.log('\n📦 PostgREST 查询日志 ========================'); console.log(`📦 API 端点: ${baseUrl}/${endpoint}`); if (params && Object.keys(params).length > 0) { console.log('📦 查询参数:'); // 以可读格式单独打印每个参数 Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { if (key === 'select' && typeof value === 'string') { // 美化 select 参数,使其看起来像 SQL 查询 console.log(` - ${key}:`); const fields = value.replace(/\s+/g, ' ').trim().split(','); fields.forEach(field => { console.log(` ${field.trim()}`); }); } else if (key === 'order' && typeof value === 'string') { // 格式化排序参数 console.log(` - ${key}: ${value.replace(/\./g, ' ')}`); // 例如:created_at desc } else if (key === 'or' && typeof value === 'string') { // 格式化OR条件 console.log(` - ${key}: ${value.replace(/\./g, ' -> ').replace(/,/g, ' 或 ')}`); } else { console.log(` - ${key}: ${JSON.stringify(value)}`); } } }); // 构建人类可读的简化URL const readableQueryString = Object.entries(params) .filter(([, value]) => value !== undefined) .map(([key, value]) => { if (key === 'select' && typeof value === 'string') { // 简化select查询 return `${key}=...`; } return `${key}=${value}`; }) .join('&'); console.log(`\n📦 可读URL: ${baseUrl}/${endpoint}${readableQueryString ? '?' + readableQueryString : ''}`); // 格式化查询为 PostgreSQL 风格的查询 let postgrestQuery = `SELECT `; if (params.select && typeof params.select === 'string') { postgrestQuery += params.select.replace(/\s+/g, ' ').trim(); } else { postgrestQuery += '*'; } postgrestQuery += ` FROM ${endpoint}`; const conditions: string[] = []; // 添加过滤条件 if (params.filter) { Object.entries(params.filter).forEach(([key, value]) => { if (value !== undefined && typeof value === 'string') { // 解析 eq.X, neq.X, gt.X 等 const parts = value.toString().split('.'); if (parts.length >= 2) { const operator = parts[0]; const operatorMap: Record = { 'eq': '=', 'neq': '!=', 'gt': '>', 'gte': '>=', 'lt': '<', 'lte': '<=', 'like': 'LIKE', 'ilike': 'ILIKE' }; const sqlOperator = operatorMap[operator] || operator; const value = parts.slice(1).join('.'); conditions.push(`${key} ${sqlOperator} '${value}'`); } } }); } // 添加 OR 条件 if (params.or) { if (typeof params.or === 'string') { if (params.or.startsWith('(') && params.or.endsWith(')')) { // 处理 or=(cond1,cond2) 格式 const orConditions = params.or.slice(1, -1).split(',').map(cond => { const [field, operator] = cond.split('.'); return `${field} ${operator.replace(/ilike/i, 'ILIKE')}`; }); if (orConditions.length > 0) { conditions.push(`(${orConditions.join(' OR ')})`); } } else { // 简单 or 条件 conditions.push(params.or); } } } // 添加WHERE子句 if (conditions.length > 0) { postgrestQuery += ` WHERE ${conditions.join(' AND ')}`; } // 添加ORDER BY if (params.order && typeof params.order === 'string') { const [field, direction] = params.order.split('.'); postgrestQuery += ` ORDER BY ${field} ${direction.toUpperCase()}`; } // 添加LIMIT和OFFSET if (params.limit !== undefined) { postgrestQuery += ` LIMIT ${params.limit}`; } if (params.offset !== undefined) { postgrestQuery += ` OFFSET ${params.offset}`; } console.log('\n📦 等效SQL查询:'); console.log(postgrestQuery); console.log('=========================================\n'); } } } /** * 将通用查询参数转换为 PostgresREST 支持的格式 * @param params 查询参数 * @returns 转换后的 PostgresREST 参数 */ export function transformParams(params: PostgrestParams): QueryParams { const result: QueryParams = {}; // 处理 select 参数 if (params.select) { result.select = params.select; } // 处理 order 参数 if (params.order) { result.order = params.order; } // 处理 limit 和 offset 参数 if (params.limit !== undefined) { result.limit = params.limit; } if (params.offset !== undefined) { result.offset = params.offset; } // 处理 schema 参数 if (params.schema) { result.schema = params.schema; } // 处理或条件 (OR) - 两种格式支持 if (params.or) { // 如果是字符串格式 (例如 "name.ilike.*keyword*,code.ilike.*keyword*") if (typeof params.or === 'string') { result.or = params.or; } // 如果是数组格式, 转换为 PostgREST 的 or=(condition1,condition2) 格式 else if (Array.isArray(params.or)) { const orConditions: string[] = []; params.or.forEach(condition => { const entries = Object.entries(condition); if (entries.length === 1) { const [field, operator] = entries[0]; orConditions.push(`${field}.${operator}`); } }); if (orConditions.length > 0) { result.or = `(${orConditions.join(',')})`; } } } // 处理过滤条件 - PostgresREST 格式 if (params.filter) { Object.entries(params.filter).forEach(([key, value]) => { // 如果值不为 undefined,则添加到查询参数中 if (value !== undefined) { // 支持 PostgreSQL 的比较操作符 (eq, gt, lt, gte, lte, like, ilike 等) result[key] = value as string | number | boolean; } }); } // 处理其他额外参数 Object.entries(params).forEach(([key, value]) => { // 跳过已处理的特殊参数 if (!['select', 'order', 'limit', 'offset', 'filter', 'schema', 'or'].includes(key) && value !== undefined) { result[key] = value as string | number | boolean; } }); return result; } /** * 发送 GET 请求到 PostgresREST 接口 * @param endpoint 端点 * @param params 查询参数 * @returns 响应数据 */ export async function postgrestGet(endpoint: string, params?: PostgrestParams): Promise<{data: T; headers?: Record; error?: never} | {data?: never; error: string; status?: number}> { try { const queryParams = params ? transformParams(params) : {}; // 确保端点没有前导斜杠,因为API_BASE_URL已经包含了路径前缀 const apiEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; // 打印查询信息 logPostgrestQuery(apiEndpoint, queryParams); // 提取并移除自定义头部参数 const headers: Record = params?.headers || {}; // 清除查询参数中的headers属性,避免将其作为URL参数 if (queryParams.headers) { delete queryParams.headers; } const response = await apiRequest( apiEndpoint, { method: 'GET', headers: headers }, queryParams ); if (response.error) { throw new Error(response.error); } // 返回数据和响应头 return { data: response.data as T, headers: response.headers // 假设apiRequest函数已返回响应头 }; } catch (error) { const apiError = handleApiError(error); return { error: apiError.message, status: apiError.status }; } } /** * 发送 POST 请求到 PostgresREST 接口 * @param endpoint 端点 * @param data 请求体数据 * @returns 响应数据 */ export async function postgrestPost>(endpoint: string, data: D): Promise<{data: T; error?: never} | {data?: never; error: string; status?: number}> { try { // 确保端点没有前导斜杠 const apiEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; // 打印查询信息(POST请求只打印端点) logPostgrestQuery(apiEndpoint); const response = await apiRequest( apiEndpoint, { method: 'POST', body: JSON.stringify(data), headers: { 'Prefer': 'return=representation' } } ); if (response.error) { throw new Error(response.error); } return { data: response.data as T }; } catch (error) { const apiError = handleApiError(error); return { error: apiError.message, status: apiError.status }; } } /** * 发送 PUT 请求到 PostgresREST 接口 * @param endpoint 端点 * @param data 请求体数据 * @returns 响应数据 */ export async function postgrestPut>(endpoint: string, data: D): Promise<{data: T; error?: never} | {data?: never; error: string; status?: number}> { try { // 确保端点没有前导斜杠 const apiEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; // 打印查询信息(PUT请求只打印端点) logPostgrestQuery(apiEndpoint); const response = await apiRequest( apiEndpoint, { method: 'PATCH', body: JSON.stringify(data), headers: { 'Prefer': 'return=representation' } } ); if (response.error) { throw new Error(response.error); } return { data: response.data as T }; } catch (error) { const apiError = handleApiError(error); return { error: apiError.message, status: apiError.status }; } } /** * 发送 DELETE 请求到 PostgresREST 接口 * @param endpoint 端点 * @param params 查询参数,用于指定要删除的记录 * @returns 响应数据 */ export async function postgrestDelete(endpoint: string, params?: PostgrestParams): Promise<{data: T; error?: never} | {data?: never; error: string; status?: number}> { try { // 确保端点没有前导斜杠 const apiEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; // 转换查询参数 const queryParams = params ? transformParams(params) : {}; // 提取并移除自定义头部参数 const headers: Record = { 'Prefer': 'return=representation', // 默认请求返回被删除的记录 ...(params?.headers || {}) }; // 清除查询参数中的headers属性,避免将其作为URL参数 if (queryParams.headers) { delete queryParams.headers; } // 打印查询信息 logPostgrestQuery(apiEndpoint, queryParams); const response = await apiRequest( apiEndpoint, { method: 'DELETE', headers }, queryParams ); if (response.error) { throw new Error(response.error); } return { data: response.data as T }; } catch (error) { const apiError = handleApiError(error); return { error: apiError.message, status: apiError.status }; } }