From bc775d4fd63eff45c0e2ec1f17847d93b1edaf4e Mon Sep 17 00:00:00 2001 From: Draculabo Date: Thu, 25 Dec 2025 00:36:21 +0800 Subject: [PATCH] feat: add boss key --- src/ipc/router.ts | 2 + src/ipc/system/handler.ts | 20 +++++++ src/localization/i18n.ts | 6 ++ src/main.ts | 5 +- src/routes/proxy.tsx | 59 +++++++++++++++++++- src/server/main.ts | 8 ++- src/server/modules/proxy/proxy.controller.ts | 4 +- src/server/modules/proxy/proxy.guard.ts | 57 +++++++++++++++++++ src/server/server-config.ts | 11 ++++ src/types/config.ts | 2 +- 10 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 src/ipc/system/handler.ts create mode 100644 src/server/modules/proxy/proxy.guard.ts create mode 100644 src/server/server-config.ts diff --git a/src/ipc/router.ts b/src/ipc/router.ts index f765285..5f7607e 100644 --- a/src/ipc/router.ts +++ b/src/ipc/router.ts @@ -10,6 +10,7 @@ import { gatewayRouter } from './gateway/router'; import { os } from '@orpc/server'; import { z } from 'zod'; import { isProcessRunning, closeAntigravity, startAntigravity } from './process/handler'; +import { systemHandler } from './system/handler'; // Log middleware setup const logMiddleware = os.middleware(async (opts: any) => { @@ -51,4 +52,5 @@ export const router = os.use(logMiddleware).router({ cloud: cloudRouter, config: configRouter, gateway: gatewayRouter, + system: systemHandler, }); diff --git a/src/ipc/system/handler.ts b/src/ipc/system/handler.ts new file mode 100644 index 0000000..86daedc --- /dev/null +++ b/src/ipc/system/handler.ts @@ -0,0 +1,20 @@ +import { os } from '@orpc/server'; +import { z } from 'zod'; +import { networkInterfaces } from 'os'; + +export const systemHandler = os.route({ + get_local_ip: os.handler(async () => { + const nets = networkInterfaces(); + const results: string[] = []; + + for (const name of Object.keys(nets)) { + for (const net of nets[name] || []) { + // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses + if (net.family === 'IPv4' && !net.internal) { + results.push(net.address); + } + } + } + return results.length > 0 ? results[0] : '127.0.0.1'; + }), +}); diff --git a/src/localization/i18n.ts b/src/localization/i18n.ts index 354457a..e3d4f29 100644 --- a/src/localization/i18n.ts +++ b/src/localization/i18n.ts @@ -105,6 +105,7 @@ i18n default: 'Default', restoreDefaults: 'Restore Defaults', }, + examples: { title: 'Usage Examples', description: 'Example commands to call the local API proxy.', @@ -378,6 +379,7 @@ i18n default: '默认', restoreDefaults: '恢复默认配置', }, + examples: { title: '使用示例', description: '调用本地 API 代理的示例命令。', @@ -544,6 +546,10 @@ i18n api_key: 'API 密钥', auto_start: '随应用启动', auto_start_desc: '应用启动时自动启动代理服务', + local_access: '局域网访问:', + no_token_warning: '⚠️ 未设置 API 密钥。服务对公网开放!', + show_key: '显示密钥', + hide_key: '隐藏密钥', }, mapping: { title: '模型映射', diff --git a/src/main.ts b/src/main.ts index dfb2f2e..34f9432 100644 --- a/src/main.ts +++ b/src/main.ts @@ -222,7 +222,10 @@ app const config = ConfigManager.loadConfig(); if (config.proxy?.auto_start) { const port = config.proxy?.port || 8045; - await bootstrapNestServer(port); + // Default to a valid ProxyConfig object if null, although loadConfig ensures defaults + if (config.proxy) { + await bootstrapNestServer(config.proxy); + } logger.info(`NestJS Proxy: Auto-started on port ${port}`); } diff --git a/src/routes/proxy.tsx b/src/routes/proxy.tsx index a50746f..edaa6c7 100644 --- a/src/routes/proxy.tsx +++ b/src/routes/proxy.tsx @@ -4,6 +4,8 @@ */ import { createFileRoute } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; +import { useQuery } from '@tanstack/react-query'; +import { ipc } from '@/ipc/manager'; import { useState, useEffect } from 'react'; import { useAppConfig } from '@/hooks/useAppConfig'; import { ProxyConfig } from '@/types/config'; @@ -29,6 +31,8 @@ import { BrainCircuit, Code, Terminal, + Eye, + EyeOff, } from 'lucide-react'; import { Dialog, @@ -43,9 +47,19 @@ function ProxyPage() { const { t } = useTranslation(); const { config, isLoading, saveConfig } = useAppConfig(); + const { data: localIp } = useQuery({ + queryKey: ['system', 'localIp'], + queryFn: async () => { + // IPC might not be ready on first render, handled by query retry or lazy loading implication + // But ipc is imported statically now. + return (await ipc.client.system.get_local_ip()) as string; + }, + }); + // Local state for proxyConfig editing const [proxyConfig, setProxyConfig] = useState(undefined); const [isRegenerateDialogOpen, setIsRegenerateDialogOpen] = useState(false); + const [showKey, setShowKey] = useState(false); // Sync config.proxy to local state when loaded useEffect(() => { @@ -153,6 +167,27 @@ print(response.choices[0].message.content)`;

{t('proxy.description', 'Manage the local API proxy service.')}

+ + {/* Local Access Info Banner */} + {/* Local Access Info Banner */} + {proxyConfig?.enabled && ( +
+
+
{t('proxy.config.local_access', 'Local Access:')}
+ + http://{localIp || 'localhost'}:{proxyConfig.port}/v1 + +
+ {!proxyConfig.api_key && ( +
+ {t( + 'proxy.config.no_token_warning', + '⚠️ No API Key set. Service is open to the public network!', + )} +
+ )} +
+ )} {/* Service Control Card */} @@ -235,10 +270,30 @@ print(response.choices[0].message.content)`;
- +
+ + +