From 7c22fd9f8622770ee67b404976e0de71e6d42271 Mon Sep 17 00:00:00 2001 From: Cheng Jinbao Date: Wed, 12 Aug 2026 13:09:46 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(api):=20=E5=AE=9E=E7=8E=B0=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E9=9D=A2/=E6=95=B0=E6=8D=AE=E9=9D=A2=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E7=9A=84=E5=8F=8C=E5=B1=82API=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入open/admin双surface认证体系,open surface支持open scope API Key和匿名访问 - 新增OpenDataResource和OpenFunctionResource提供面向终端用户的开放API端点 - 修改Nginx配置实现子域名路由到页面和开放API,主域名路由到管理API - 更新API Key生成逻辑,区分admin/open作用域并调整前缀格式 - 重构认证过滤器实现基于路径的API surface隔离和权限校验 - 调整边缘函数调用路径从/functions/{projectId}/{name}改为/open/{projectId}/functions/{name} - 更新相关测试用例和依赖配置以适配新的API架构设计 --- src/locales/en.json | 3 + src/locales/zh.json | 3 + src/pages/ApiKeys/index.tsx | 51 ++++++++------ .../components/FunctionInvokePanel.tsx | 6 +- .../Overview/components/ProjectInfoPanel.tsx | 2 +- src/services/function.ts | 68 +------------------ src/types/api-key.d.ts | 4 +- vite.config.ts | 4 +- 8 files changed, 46 insertions(+), 95 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 05c2547..09bb1c7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -104,6 +104,9 @@ "project_ids": "Accessible Projects", "project_ids_placeholder": "Leave empty for all projects", "all_projects": "All Projects", + "scope": "Scope", + "scope_open": "Open", + "scope_admin": "Admin", "read_only": "Read Only", "regenerate": "Regenerate", "yes": "Yes", diff --git a/src/locales/zh.json b/src/locales/zh.json index 81cd102..51452b5 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -104,6 +104,9 @@ "project_ids": "可访问项目", "project_ids_placeholder": "留空表示可访问所有项目", "all_projects": "所有项目", + "scope": "范围", + "scope_open": "开放", + "scope_admin": "管理员", "read_only": "只读", "regenerate": "重新生成", "yes": "是", diff --git a/src/pages/ApiKeys/index.tsx b/src/pages/ApiKeys/index.tsx index d4aa60e..ebfef81 100644 --- a/src/pages/ApiKeys/index.tsx +++ b/src/pages/ApiKeys/index.tsx @@ -65,7 +65,7 @@ const ApiKeys: React.FC = () => { const values = await form.validateFields(); const req: CreateApiKeyRequest = { name: values.name, - keyType: "custom", + scope: values.scope || "open", projectIds: (values.projectIds as string[] | undefined)?.join(",") || "", readOnly: values.readOnly ?? false, }; @@ -107,13 +107,16 @@ const ApiKeys: React.FC = () => { }); }; - const getKeyTypeTag = (keyType: string) => { + const getScopeTag = (scope: string) => { const colorMap: Record = { - anon: "blue", - service: "red", - custom: "default", + admin: "red", + open: "blue", }; - return {t(`key_type_${keyType}`)}; + const labelMap: Record = { + admin: t("scope_admin"), + open: t("scope_open"), + }; + return {labelMap[scope] || scope}; }; const columns = [ @@ -123,10 +126,10 @@ const ApiKeys: React.FC = () => { key: "name", }, { - title: t("key_type"), - dataIndex: "keyType", - key: "keyType", - render: (keyType: string) => getKeyTypeTag(keyType), + title: t("scope"), + dataIndex: "scope", + key: "scope", + render: (scope: string) => getScopeTag(scope), }, { title: t("key_prefix"), @@ -169,16 +172,14 @@ const ApiKeys: React.FC = () => { {t("regenerate")} - {record.keyType === "custom" && ( - handleDelete(record.id)} - > - - - )} + handleDelete(record.id)} + > + + ), }, @@ -216,10 +217,18 @@ const ApiKeys: React.FC = () => { message={t("api_key_frontend_warning")} style={{marginBottom: 16}} /> -
+ + + = ({ try { // 请求体直接作为函数的 Request body(不再嵌套在 input 字段中) - const res = await invokeFunctionViaServer(projectId, functionName, data); + const res = await invokeFunction(projectId, functionName, data); setResponse(res); } catch (err: any) { setError(err?.message || String(err)); diff --git a/src/pages/Overview/components/ProjectInfoPanel.tsx b/src/pages/Overview/components/ProjectInfoPanel.tsx index b3b86ca..1df5d2c 100644 --- a/src/pages/Overview/components/ProjectInfoPanel.tsx +++ b/src/pages/Overview/components/ProjectInfoPanel.tsx @@ -29,7 +29,7 @@ const ProjectInfoPanel: React.FC = () => { projectId, ); const functionsUrl = resolveUrl( - config.edgeUrlTemplate || '/functions/{{projectId}}/{{name}}', + config.edgeUrlTemplate || '/open/{{projectId}}/functions/{{name}}', projectId, ); diff --git a/src/services/function.ts b/src/services/function.ts index 35537a6..0143216 100644 --- a/src/services/function.ts +++ b/src/services/function.ts @@ -42,11 +42,6 @@ export interface FunctionInvokeResult { meta?: FunctionInvokeMeta; } -export interface InvokeTokenResponse { - invokeToken: string; - runtimeUrl: string; -} - export interface PageDTO { list: T[]; total: number; @@ -83,74 +78,15 @@ export const deleteFunction = ( return api.delete(`/projects/${projectId}/functions/${encodeURIComponent(name)}`); }; -// ---- Invoke Token (Edge Function) ---- - -/** - * Get an invoke-token for edge function direct invocation. - * Returns invokeToken + runtimeUrl for direct Deno Runtime call. - */ -export const getInvokeToken = ( - projectId: string, - name: string, -): Promise => { - return api.post(`/projects/${projectId}/functions/${encodeURIComponent(name)}/invoke-token`); -}; - -// ---- Invoke (Edge Function — direct Deno call) ---- +// ---- Invoke (via Java server proxy → Deno Runtime) ---- /** - * Invoke a function via edge route (direct Deno Runtime call). - * 1. Get invoke-token from Java server - * 2. Call Deno Runtime directly with the token + * Invoke a function via the Java server, which proxies to the Deno Runtime. */ export const invokeFunction = async ( projectId: string, name: string, data: any, -): Promise => { - // 1. Get invoke-token from Java server - const {invokeToken, runtimeUrl} = await getInvokeToken(projectId, name); - - // 2. Call Deno Runtime directly - const response = await fetch(runtimeUrl, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${invokeToken}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }); - - // 3. Parse response - let meta: FunctionInvokeMeta | undefined; - const metaStr = response.headers.get('x-function-meta'); - if (metaStr) { - try { - meta = JSON.parse(metaStr); - } catch { /* ignore */ - } - } - - let responseData: any; - try { - responseData = await response.json(); - } catch { - responseData = await response.text(); - } - - return {status: response.status, data: responseData, meta}; -}; - -// ---- Invoke (Legacy — via Java server, kept as fallback) ---- - -/** - * Invoke a function via Java server (legacy path). - * Use invokeFunction() for edge function direct invocation instead. - */ -export const invokeFunctionViaServer = async ( - projectId: string, - name: string, - data: any, ): Promise => { const response = await api.rawPost( `/projects/${projectId}/functions/${encodeURIComponent(name)}/invoke`, diff --git a/src/types/api-key.d.ts b/src/types/api-key.d.ts index b2f94f9..cafb52e 100644 --- a/src/types/api-key.d.ts +++ b/src/types/api-key.d.ts @@ -5,7 +5,7 @@ export interface ApiKey { id: string; name: string; keyPrefix: string; - keyType: 'anon' | 'service' | 'custom'; + scope: 'admin' | 'open'; projectIds?: string; readOnly: boolean; expiresAt: string | null; @@ -20,7 +20,7 @@ export interface ApiKey { */ export interface CreateApiKeyRequest { name: string; - keyType?: string; + scope?: string; projectIds?: string; readOnly?: boolean; } diff --git a/vite.config.ts b/vite.config.ts index 43dba93..5ddb14a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -180,7 +180,7 @@ export default defineConfig({ server: { host: "0.0.0.0", proxy: { - "/api": { + "/api/": { target: "http://localhost:8080", ws: true, changeOrigin: true, @@ -191,7 +191,7 @@ export default defineConfig({ changeOrigin: true, }, // Pages fallback → Java server (when local files not found) - "/pages": { + "/pages/": { target: "http://localhost:8080", changeOrigin: true, }, From 44f6e9f6446470b416ad2108d7af95cb1ae7a72b Mon Sep 17 00:00:00 2001 From: Cheng Jinbao Date: Wed, 12 Aug 2026 16:07:58 +0800 Subject: [PATCH 2/4] =?UTF-8?q?chore(deps):=20=E6=9B=B4=E6=96=B0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E5=8C=85=E7=89=88=E6=9C=AC=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?UI=E7=BB=84=E4=BB=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新 deno.lock 中 @hono/hono 版本从 4.12.24 到 4.12.27 - 移除 deno.lock 中 jsonwebtoken 和 openai 相关依赖 - 在数据建模页面添加刷新功能按钮 - 调整函数页面中搜索框和按钮的位置布局 - 移除项目概览面板中的函数入口URL显示 - 为文件浏览器组件添加上传文件列表状态管理 - 实现上传模态框关闭时清空文件列表功能 --- src/pages/DataModeling/index.tsx | 10 ++++++++- src/pages/Functions/index.tsx | 22 +++++++++---------- .../Overview/components/ProjectInfoPanel.tsx | 12 ---------- src/pages/Storage/components/FileBrowser.tsx | 11 ++++++++-- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/pages/DataModeling/index.tsx b/src/pages/DataModeling/index.tsx index f694c5f..6356a6f 100644 --- a/src/pages/DataModeling/index.tsx +++ b/src/pages/DataModeling/index.tsx @@ -10,7 +10,7 @@ import EnumForm from "@/pages/DataModeling/components/EnumForm"; import type {Enum} from "@/types/data-modeling.d.ts"; import ERDiagram from "@/pages/DataModeling/components/ERDiagramView"; import {useProject} from "@/store/appStore"; -import {AppstoreOutlined, UnorderedListOutlined} from "@ant-design/icons"; +import {AppstoreOutlined, ReloadOutlined, UnorderedListOutlined} from "@ant-design/icons"; import ERView from "@/pages/DataView/components/ERView.tsx"; import {spacing} from "@/theme/designTokens"; @@ -51,6 +51,10 @@ const ModelingPage: React.FC = () => { setNativeQueryIsEditing(prev => !prev); }, []); + const handleRefresh = useCallback(() => { + setSelectModelVersion(v => v + 1); + }, []); + const handleCancelNativeQueryEdit = useCallback(() => { setNativeQueryIsEditing(false); }, []); @@ -118,6 +122,10 @@ const ModelingPage: React.FC = () => { title={t('data_modeling')} extra={[ + @@ -182,17 +193,6 @@ const FunctionsPage: React.FC = () => { } loading={loading} > - - } - value={searchName} - onChange={(e) => { setSearchName(e.target.value); setPage(1); }} - style={{width: 220}} - allowClear - /> - - { config.pagesUrlTemplate || '/pages/{{projectId}}', projectId, ); - const functionsUrl = resolveUrl( - config.edgeUrlTemplate || '/open/{{projectId}}/functions/{{name}}', - projectId, - ); return ( { 请先选择项目 )} -
- 函数入口 - {projectId ? ( - {functionsUrl} - ) : ( - 请先选择项目 - )} -
); diff --git a/src/pages/Storage/components/FileBrowser.tsx b/src/pages/Storage/components/FileBrowser.tsx index 9d10092..23aa0e5 100644 --- a/src/pages/Storage/components/FileBrowser.tsx +++ b/src/pages/Storage/components/FileBrowser.tsx @@ -36,6 +36,7 @@ const FileBrowser: React.FC = ({bucketName, projectId, visibil const [createFolderVisible, setCreateFolderVisible] = useState(false); const [folderName, setFolderName] = useState(''); const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [uploadFileList, setUploadFileList] = useState([]); const loadFiles = useCallback(async () => { setLoading(true); @@ -67,6 +68,10 @@ const FileBrowser: React.FC = ({bucketName, projectId, visibil const handleUpload = () => { setUploadVisible(true); }; + const handleUploadClose = () => { + setUploadVisible(false); + setUploadFileList([]); + }; const handleCreateFolder = () => { setCreateFolderVisible(true); @@ -285,9 +290,9 @@ const FileBrowser: React.FC = ({bucketName, projectId, visibil setUploadVisible(false)} + onCancel={handleUploadClose} footer={[ - , ]} @@ -295,6 +300,8 @@ const FileBrowser: React.FC = ({bucketName, projectId, visibil setUploadFileList(fileList)} customRequest={async ({file, onSuccess, onError}) => { try { const fileObj = file as File; From a9836ea93babc63038ce72d4ad23a954e0350150 Mon Sep 17 00:00:00 2001 From: Cheng Jinbao Date: Thu, 13 Aug 2026 15:44:36 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor(auth):=20=E7=A7=BB=E9=99=A4API?= =?UTF-8?q?=E5=AF=86=E9=92=A5=E7=9A=84=E4=BD=9C=E7=94=A8=E5=9F=9F=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除了ApiKeyResponse、CreateApiKeyRequest中的scope字段 - 移除了ApiKeyGenerator中与作用域相关的生成逻辑 - 更新了ApiKeyService中创建和重新生成密钥的方法实现 - 移除了AuthFilter中对管理接口和开放接口的作用域验证 - 删除了数据库模型中auth_api_key表的scope字段定义 - 修改了UI界面中的表单和表格列配置,移除作用域选择器 - 更新了端到端测试中请求数据的作用域字段 - 调整了项目基础域名配置为Optional类型并更新相关引用 --- src/pages/ApiKeys/index.tsx | 29 +---------------------------- src/types/api-key.d.ts | 2 -- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/src/pages/ApiKeys/index.tsx b/src/pages/ApiKeys/index.tsx index ebfef81..a2bd544 100644 --- a/src/pages/ApiKeys/index.tsx +++ b/src/pages/ApiKeys/index.tsx @@ -65,7 +65,6 @@ const ApiKeys: React.FC = () => { const values = await form.validateFields(); const req: CreateApiKeyRequest = { name: values.name, - scope: values.scope || "open", projectIds: (values.projectIds as string[] | undefined)?.join(",") || "", readOnly: values.readOnly ?? false, }; @@ -107,30 +106,12 @@ const ApiKeys: React.FC = () => { }); }; - const getScopeTag = (scope: string) => { - const colorMap: Record = { - admin: "red", - open: "blue", - }; - const labelMap: Record = { - admin: t("scope_admin"), - open: t("scope_open"), - }; - return {labelMap[scope] || scope}; - }; - const columns = [ { title: t("name"), dataIndex: "name", key: "name", }, - { - title: t("scope"), - dataIndex: "scope", - key: "scope", - render: (scope: string) => getScopeTag(scope), - }, { title: t("key_prefix"), dataIndex: "keyPrefix", @@ -217,18 +198,10 @@ const ApiKeys: React.FC = () => { message={t("api_key_frontend_warning")} style={{marginBottom: 16}} /> - + - - Date: Thu, 13 Aug 2026 15:45:51 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor(i18n):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=9C=AA=E4=BD=BF=E7=94=A8=E7=9A=84=E6=9D=83=E9=99=90=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E6=9C=AC=E5=9C=B0=E5=8C=96=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除了英文配置中的 scope、scope_open 和 scope_admin 字段 - 删除了中文配置中的 scope、scope_open 和 scope_admin 字段 - 清理了不再需要的国际化翻译资源 - 减少了语言包文件大小和内存占用 --- src/locales/en.json | 3 --- src/locales/zh.json | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 09bb1c7..05c2547 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -104,9 +104,6 @@ "project_ids": "Accessible Projects", "project_ids_placeholder": "Leave empty for all projects", "all_projects": "All Projects", - "scope": "Scope", - "scope_open": "Open", - "scope_admin": "Admin", "read_only": "Read Only", "regenerate": "Regenerate", "yes": "Yes", diff --git a/src/locales/zh.json b/src/locales/zh.json index 51452b5..81cd102 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -104,9 +104,6 @@ "project_ids": "可访问项目", "project_ids_placeholder": "留空表示可访问所有项目", "all_projects": "所有项目", - "scope": "范围", - "scope_open": "开放", - "scope_admin": "管理员", "read_only": "只读", "regenerate": "重新生成", "yes": "是",