From 2ad5883aef89e4072f0ba2185efedb480e0ab303 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Tue, 1 Jul 2025 15:43:00 +0800 Subject: [PATCH] feat: dynamic URL (#3444) --- apps/maxkb/conf.py | 6 ++- apps/maxkb/settings/base.py | 6 +-- apps/maxkb/urls.py | 55 ++++++++++++++-------- ui/{entry/system/index.html => admin.html} | 5 ++ ui/{entry/chat/index.html => chat.html} | 5 ++ ui/env.d.ts | 6 +++ ui/env/.env | 6 +-- ui/env/.env.chat | 2 +- ui/src/api/application/application.ts | 3 +- ui/src/api/chat/chat.ts | 3 +- ui/src/request/chat/index.ts | 3 +- ui/src/request/index.ts | 2 +- ui/src/router/chat/index.ts | 2 +- ui/src/router/index.ts | 3 +- ui/vite.config.ts | 44 +++++++++++++---- 15 files changed, 106 insertions(+), 45 deletions(-) rename ui/{entry/system/index.html => admin.html} (83%) rename ui/{entry/chat/index.html => chat.html} (83%) diff --git a/apps/maxkb/conf.py b/apps/maxkb/conf.py index 89a17ac01..98212c0f4 100644 --- a/apps/maxkb/conf.py +++ b/apps/maxkb/conf.py @@ -102,11 +102,15 @@ class Config(dict): return self.get('LOG_LEVEL', 'DEBUG') def get_sandbox_python_package_paths(self): - return self.get('SANDBOX_PYTHON_PACKAGE_PATHS', '/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages') + return self.get('SANDBOX_PYTHON_PACKAGE_PATHS', + '/opt/py3/lib/python3.11/site-packages,/opt/maxkb-app/sandbox/python-packages,/opt/maxkb/python-packages') def get_admin_path(self): return self.get('ADMIN_PATH', 'admin') + def get_chat_path(self): + return self.get('CHAT_PATH', '/chat') + def get_session_timeout(self): return int(self.get('SESSION_TIMEOUT', 28800)) diff --git a/apps/maxkb/settings/base.py b/apps/maxkb/settings/base.py index 0092d5100..1b550e00c 100644 --- a/apps/maxkb/settings/base.py +++ b/apps/maxkb/settings/base.py @@ -55,7 +55,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', + ] REST_FRAMEWORK = { @@ -63,7 +63,7 @@ REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 'DEFAULT_AUTHENTICATION_CLASSES': ['common.auth.authenticate.AnonymousAuthentication'] } -STATICFILES_DIRS = [(os.path.join(PROJECT_DIR, 'ui', 'dist')), (os.path.join(PROJECT_DIR, 'chat', 'dist'))] +STATICFILES_DIRS = [(os.path.join(PROJECT_DIR, 'ui', 'dist'))] STATIC_ROOT = os.path.join(BASE_DIR.parent, 'static') ROOT_URLCONF = 'maxkb.urls' APPS_DIR = os.path.join(PROJECT_DIR, 'apps') @@ -71,7 +71,7 @@ APPS_DIR = os.path.join(PROJECT_DIR, 'apps') TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': ["apps/static/ui"], + 'DIRS': ["apps/static/admin"], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ diff --git a/apps/maxkb/urls.py b/apps/maxkb/urls.py index 690fdb836..af4150ce0 100644 --- a/apps/maxkb/urls.py +++ b/apps/maxkb/urls.py @@ -16,7 +16,7 @@ Including another URLconf """ import os -from django.http import HttpResponse +from django.http import HttpResponse,HttpResponseRedirect from django.urls import path, re_path, include from django.views import static from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView @@ -25,16 +25,21 @@ from rest_framework import status from common.result import Result from maxkb import settings from maxkb.conf import PROJECT_DIR +from maxkb.const import CONFIG +admin_api_prefix = CONFIG.get_admin_path()[1:] + '/api/' +admin_ui_prefix = CONFIG.get_admin_path() +chat_api_prefix = CONFIG.get_chat_path()[1:] + '/api/' +chat_ui_prefix = CONFIG.get_chat_path() urlpatterns = [ - path("api/", include("users.urls")), - path("api/", include("tools.urls")), - path("api/", include("models_provider.urls")), - path("api/", include("folders.urls")), - path("api/", include("knowledge.urls")), - path("api/", include("system_manage.urls")), - path("api/", include("application.urls")), - path("chat/api/", include("chat.urls")), + path(admin_api_prefix, include("users.urls")), + path(admin_api_prefix, include("tools.urls")), + path(admin_api_prefix, include("models_provider.urls")), + path(admin_api_prefix, include("folders.urls")), + path(admin_api_prefix, include("knowledge.urls")), + path(admin_api_prefix, include("system_manage.urls")), + path(admin_api_prefix, include("application.urls")), + path(chat_api_prefix, include("chat.urls")), path('oss/', include('oss.urls')), ] urlpatterns += [ @@ -54,12 +59,14 @@ def pro(): ) # 暴露ui静态资源 urlpatterns.append( - re_path(r'^ui/(?P.*)$', static.serve, {'document_root': os.path.join(settings.STATIC_ROOT, "ui")}, - name='ui'), + re_path(rf"^{CONFIG.get_admin_path()[1:]}/(?P.*)$", static.serve, + {'document_root': os.path.join(settings.STATIC_ROOT, "admin")}, + name='admin'), ) # 暴露ui静态资源 urlpatterns.append( - re_path(r'^chat/(?P.*)$', static.serve, {'document_root': os.path.join(settings.STATIC_ROOT, "chat")}, + re_path(rf'^{CONFIG.get_chat_path()[1:]}/(?P.*)$', static.serve, + {'document_root': os.path.join(settings.STATIC_ROOT, "chat")}, name='chat'), ) @@ -79,18 +86,26 @@ def page_not_found(request, exception): """ 页面不存在处理 """ - if request.path.startswith("/api/"): + if request.path.startswith(admin_ui_prefix + '/api/'): return Result(response_status=status.HTTP_404_NOT_FOUND, code=404, message="HTTP_404_NOT_FOUND") - if request.path.startswith("/chat/api/"): + if request.path.startswith(chat_ui_prefix + '/api/'): return Result(response_status=status.HTTP_404_NOT_FOUND, code=404, message="HTTP_404_NOT_FOUND") - if request.path.startswith('/chat'): + if request.path.startswith(chat_ui_prefix): index_path = os.path.join(PROJECT_DIR, 'apps', "static", 'chat', 'index.html') + content = get_index_html(index_path) + content.replace("prefix: '/chat'", f"prefix: {CONFIG.get_chat_path()}") + if not os.path.exists(index_path): + return HttpResponse("页面不存在", status=404) + return HttpResponse(content, status=200) + elif request.path.startswith(admin_ui_prefix): + index_path = os.path.join(PROJECT_DIR, 'apps', "static", 'admin', 'index.html') + if not os.path.exists(index_path): + return HttpResponse("页面不存在", status=404) + content = get_index_html(index_path) + content = content.replace("prefix: '/admin'", f"prefix: '{CONFIG.get_admin_path()}'") + return HttpResponse(content, status=200) else: - index_path = os.path.join(PROJECT_DIR, 'apps', "static", 'ui', 'index.html') - if not os.path.exists(index_path): - return HttpResponse("页面不存在", status=404) - content = get_index_html(index_path) - return HttpResponse(content, status=200) + return HttpResponseRedirect(admin_ui_prefix+'/') handler404 = page_not_found diff --git a/ui/entry/system/index.html b/ui/admin.html similarity index 83% rename from ui/entry/system/index.html rename to ui/admin.html index 86940d230..50f2a786e 100644 --- a/ui/entry/system/index.html +++ b/ui/admin.html @@ -7,6 +7,11 @@ %VITE_APP_TITLE% +
diff --git a/ui/entry/chat/index.html b/ui/chat.html similarity index 83% rename from ui/entry/chat/index.html rename to ui/chat.html index 5ab118316..ee16b632d 100644 --- a/ui/entry/chat/index.html +++ b/ui/chat.html @@ -7,6 +7,11 @@ %VITE_APP_TITLE% +
diff --git a/ui/env.d.ts b/ui/env.d.ts index 11f02fe2a..de80aae90 100644 --- a/ui/env.d.ts +++ b/ui/env.d.ts @@ -1 +1,7 @@ /// +interface Window { + sendMessage: ?((message: string, other_params_data: any) => void) + MaxKB: { + prefix: string + } +} diff --git a/ui/env/.env b/ui/env/.env index 815ded3ec..9a28770c0 100644 --- a/ui/env/.env +++ b/ui/env/.env @@ -1,5 +1,5 @@ -VITE_APP_NAME=ui -VITE_BASE_PATH=/ui/ +VITE_APP_NAME=admin +VITE_BASE_PATH=/admin/ VITE_APP_PORT=3000 VITE_APP_TITLE = 'MaxKB' -VITE_ENTRY="entry/system/index.html" \ No newline at end of file +VITE_ENTRY="admin.html" \ No newline at end of file diff --git a/ui/env/.env.chat b/ui/env/.env.chat index 6d60a20bc..bb6e752d8 100644 --- a/ui/env/.env.chat +++ b/ui/env/.env.chat @@ -2,4 +2,4 @@ VITE_APP_NAME=chat VITE_BASE_PATH=/chat/ VITE_APP_PORT=3001 VITE_APP_TITLE = 'MaxKB' -VITE_ENTRY="entry/chat/index.html" \ No newline at end of file +VITE_ENTRY="chat.html" \ No newline at end of file diff --git a/ui/src/api/application/application.ts b/ui/src/api/application/application.ts index 4cfc2c3dd..e85f38cfa 100644 --- a/ui/src/api/application/application.ts +++ b/ui/src/api/application/application.ts @@ -177,7 +177,8 @@ const open: (application_id: string, loading?: Ref) => Promise Promise = (chat_id, data) => { - return postStream(`/api/chat_message/${chat_id}`, data) + const prefix = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api' + return postStream(`${prefix}/chat_message/${chat_id}`, data) } /** * 获取对话用户认证类型 diff --git a/ui/src/api/chat/chat.ts b/ui/src/api/chat/chat.ts index 03a77affc..977669450 100644 --- a/ui/src/api/chat/chat.ts +++ b/ui/src/api/chat/chat.ts @@ -40,7 +40,8 @@ const open: (loading?: Ref) => Promise> = (loading) => { * data */ const chat: (chat_id: string, data: any) => Promise = (chat_id, data) => { - return postStream(`/chat/api/chat_message/${chat_id}`, data) + const prefix = (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/chat') + '/api' + return postStream(`${prefix}/chat_message/${chat_id}`, data) } /** diff --git a/ui/src/request/chat/index.ts b/ui/src/request/chat/index.ts index 954b76fba..9315e2d5a 100644 --- a/ui/src/request/chat/index.ts +++ b/ui/src/request/chat/index.ts @@ -4,12 +4,11 @@ import type { NProgress } from 'nprogress' import type { Ref } from 'vue' import type { Result } from '@/request/Result' import useStore from '@/stores' -import router from '@/router' import { ref, type WritableComputedRef } from 'vue' const axiosConfig = { - baseURL: '/chat/api', + baseURL: (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/chat') + '/api', withCredentials: false, timeout: 600000, headers: {}, diff --git a/ui/src/request/index.ts b/ui/src/request/index.ts index 892f5f066..3ae9a9b4a 100644 --- a/ui/src/request/index.ts +++ b/ui/src/request/index.ts @@ -9,7 +9,7 @@ import router from '@/router' import { ref, type WritableComputedRef } from 'vue' const axiosConfig = { - baseURL: '/api', + baseURL: (window.MaxKB?.prefix ? window.MaxKB?.prefix : '/admin') + '/api', withCredentials: false, timeout: 600000, headers: {}, diff --git a/ui/src/router/chat/index.ts b/ui/src/router/chat/index.ts index 52757f40e..1387e3e0a 100644 --- a/ui/src/router/chat/index.ts +++ b/ui/src/router/chat/index.ts @@ -12,7 +12,7 @@ import useStore from '@/stores' import { routes } from '@/router/chat/routes' NProgress.configure({ showSpinner: false, speed: 500, minimum: 0.3 }) const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), + history: createWebHistory(window.MaxKB?.prefix ? window.MaxKB?.prefix : import.meta.env.BASE_URL), routes: routes, }) diff --git a/ui/src/router/index.ts b/ui/src/router/index.ts index 3b86f2bd5..b713a8997 100644 --- a/ui/src/router/index.ts +++ b/ui/src/router/index.ts @@ -7,14 +7,13 @@ import { createWebHistory, type NavigationGuardNext, type RouteLocationNormalized, - type RouteRecordRaw, type RouteRecordName, } from 'vue-router' import useStore from '@/stores' import { routes } from '@/router/routes' NProgress.configure({ showSpinner: false, speed: 500, minimum: 0.3 }) const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), + history: createWebHistory(window.MaxKB?.prefix ? window.MaxKB?.prefix : import.meta.env.BASE_URL), routes: routes, }) diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 0fc525ca3..5e8e515d9 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -1,3 +1,4 @@ +import { model } from '@/permission/model' import { fileURLToPath, URL } from 'node:url' import type { ProxyOptions } from 'vite' import { defineConfig, loadEnv } from 'vite' @@ -6,21 +7,39 @@ import vueJsx from '@vitejs/plugin-vue-jsx' import DefineOptions from 'unplugin-vue-define-options/vite' import path from 'path' import { createHtmlPlugin } from 'vite-plugin-html' - +import fs from 'fs' // import vueDevTools from 'vite-plugin-vue-devtools' const envDir = './env' +// 自定义插件:重命名入口文件 +const renameHtmlPlugin = (outDir: string, entry: string) => { + return { + name: 'rename-html', + closeBundle: () => { + const buildDir = path.resolve(__dirname, outDir) + const oldFile = path.join(buildDir, entry) + const newFile = path.join(buildDir, 'index.html') + // 检查文件是否存在 + if (fs.existsSync(oldFile)) { + // 删除已存在的 index.html + if (fs.existsSync(newFile)) { + fs.unlinkSync(newFile) + } + // 重命名文件 + fs.renameSync(oldFile, newFile) + } + }, + } +} // https://vite.dev/config/ -export default defineConfig(({ mode }) => { +export default defineConfig((conf: any) => { + const mode = conf.mode const ENV = loadEnv(mode, envDir) - console.log(ENV) - const prefix = process.env.VITE_DYNAMIC_PREFIX || ENV.VITE_BASE_PATH const proxyConf: Record = {} - proxyConf['/api'] = { + proxyConf['/admin/api'] = { // target: 'http://47.92.195.88:8080', target: 'http://127.0.0.1:8080', changeOrigin: true, - rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'), } proxyConf['/oss'] = { target: 'http://127.0.0.1:8080', @@ -46,12 +65,19 @@ export default defineConfig(({ mode }) => { changeOrigin: true, rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'), } + return { preflight: false, lintOnSave: false, - base: prefix, + base: './', envDir: envDir, - plugins: [vue(), vueJsx(), DefineOptions(), createHtmlPlugin({ template: ENV.VITE_ENTRY })], + plugins: [ + vue(), + vueJsx(), + DefineOptions(), + createHtmlPlugin({ template: ENV.VITE_ENTRY }), + renameHtmlPlugin(`dist${ENV.VITE_BASE_PATH}`, ENV.VITE_ENTRY), + ], server: { cors: true, host: '0.0.0.0', @@ -62,7 +88,7 @@ export default defineConfig(({ mode }) => { build: { outDir: `dist${ENV.VITE_BASE_PATH}`, rollupOptions: { - input: path.resolve(__dirname, ENV.VITE_ENTRY), + input: ENV.VITE_ENTRY, }, }, resolve: {