Stclair 2 недель назад
Родитель
Сommit
28a2c030ae
6 измененных файлов с 308 добавлено и 30 удалено
  1. 93 0
      Dockerfile.backend
  2. 23 0
      Dockerfile.frontend
  3. 116 0
      docker-compose.yml
  4. 47 0
      ui/nginx.conf
  5. 2 2
      ui/package.json
  6. 27 28
      ui/vite.config.ts

+ 93 - 0
Dockerfile.backend

@@ -0,0 +1,93 @@
+FROM python:3.11-slim
+
+WORKDIR /opt/maxkb-app
+
+ENV DEBIAN_FRONTEND=noninteractive \
+    PYTHONDONTWRITEBYTECODE=1 \
+    PYTHONUNBUFFERED=1 \
+    HF_HOME=/opt/maxkb-app/model/base \
+    TMPDIR=/opt/maxkb-app/tmp \
+    PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \
+    PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    build-essential \
+    gcc \
+    python3-dev \
+    libpq-dev \
+    curl \
+    git \
+    wget \
+    && rm -rf /var/lib/apt/lists/*
+
+RUN pip install --no-cache-dir --upgrade pip setuptools wheel
+
+COPY pyproject.toml ./
+
+RUN pip install --no-cache-dir --break-system-packages \
+    django==5.2.13 \
+    drf-spectacular==0.28.0 \
+    django-redis==6.0.0 \
+    django-db-connection-pool==1.2.6 \
+    django-mptt==0.17.0 \
+    djangorestframework==3.17.1 \
+    psycopg==3.2.9 \
+    python-dotenv==1.1.1 \
+    uuid-utils==0.14.0 \
+    captcha==0.7.1 \
+    pytz==2025.2 \
+    psutil==7.0.0 \
+    cffi==1.17.1 \
+    beautifulsoup4==4.13.4 \
+    jieba==0.42.1 \
+    langchain==1.2.15 \
+    langchain-core==1.2.31 \
+    langchain-openai==1.1.14 \
+    langchain-anthropic==1.4.0 \
+    langchain-community==0.4.1 \
+    langchain-deepseek==1.0.1 \
+    langchain-google-genai==4.2.2 \
+    langchain-mcp-adapters==0.2.2 \
+    langchain-huggingface==1.2.1 \
+    langchain-ollama==1.1.0 \
+    langchain-aws==1.4.4 \
+    langgraph==1.1.6 \
+    deepagents==0.5.3 \
+    numpy==1.26.4 \
+    qianfan==0.4.12.3 \
+    zhipuai==2.1.5.20250708 \
+    volcengine-python-sdk==4.0.5 \
+    boto3==1.42.46 \
+    tencentcloud-sdk-python==3.0.1420 \
+    xinference-client==1.7.1.post1 \
+    anthropic==0.89.0 \
+    dashscope==1.25.16 \
+    celery==5.5.3 \
+    django-celery-beat==2.8.1 \
+    celery-once==3.0.1 \
+    django-apscheduler==0.7.0 \
+    html2text==2025.4.15 \
+    openpyxl==3.1.5 \
+    python-docx==1.2.0 \
+    xlrd==2.0.2 \
+    xlwt==1.3.0 \
+    pymupdf==1.26.3 \
+    pypdf==6.10.2 \
+    pydub==0.25.1 \
+    gunicorn==23.0.0 \
+    python-daemon==3.1.2 \
+    websockets==15.0.1 \
+    cohere==5.17.0 \
+    jsonpath-ng==1.8.0 \
+    sentence-transformers==5.0.0
+
+RUN pip install --no-cache-dir --break-system-packages torch==2.8.0 --index-url https://pypi.tuna.tsinghua.edu.cn/simple
+
+COPY apps/ ./apps/
+COPY main.py ./
+
+RUN mkdir -p /opt/maxkb-app/model/base /opt/maxkb-app/tmp /opt/maxkb-app/logs
+
+EXPOSE 8080
+
+CMD ["python", "main.py", "start", "all"]

+ 23 - 0
Dockerfile.frontend

@@ -0,0 +1,23 @@
+FROM node:22-alpine
+
+WORKDIR /opt/maxkb-app/ui
+
+COPY ui/package.json ./
+
+RUN npm config set registry https://registry.npmmirror.com && \
+    npm install
+
+COPY ui/ ./
+
+ENV NODE_OPTIONS="--max-old-space-size=4096"
+
+RUN npm run build-only
+
+FROM nginx:alpine
+
+COPY --from=0 /opt/maxkb-app/ui/dist /usr/share/nginx/html
+COPY ui/nginx.conf /etc/nginx/conf.d/default.conf
+
+EXPOSE 80
+
+CMD ["nginx", "-g", "daemon off;"]

+ 116 - 0
docker-compose.yml

@@ -0,0 +1,116 @@
+services:
+  postgres:
+    image: pgvector/pgvector:pg16
+    container_name: maxkb-postgres
+    environment:
+      POSTGRES_DB: maxkb
+      POSTGRES_USER: postgres
+      POSTGRES_PASSWORD: postgres
+    volumes:
+      - postgres_data:/var/lib/postgresql/data
+    ports:
+      - "5432:5432"
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U postgres"]
+      interval: 5s
+      timeout: 5s
+      retries: 5
+    networks:
+      - maxkb-network
+
+  redis:
+    image: redis:latest
+    container_name: maxkb-redis
+    volumes:
+      - redis_data:/data
+    ports:
+      - "6379:6379"
+    healthcheck:
+      test: ["CMD", "redis-cli", "ping"]
+      interval: 5s
+      timeout: 5s
+      retries: 5
+    networks:
+      - maxkb-network
+
+  maxkb-frontend:
+    image: zhagent-front:latest
+    container_name: maxkb-frontend
+    ports:
+      - "80:80"
+    networks:
+      - maxkb-network
+    restart: unless-stopped
+    depends_on:
+      - maxkb-web
+
+  maxkb-web:
+    image: zhagent-back:latest
+    container_name: maxkb-web
+    environment:
+      MAXKB_CONFIG_TYPE: ENV
+      MAXKB_DB_NAME: maxkb
+      MAXKB_DB_HOST: postgres
+      MAXKB_DB_PORT: 5432
+      MAXKB_DB_USER: postgres
+      MAXKB_DB_PASSWORD: postgres
+      MAXKB_REDIS_HOST: redis
+      MAXKB_REDIS_PORT: 6379
+      MAXKB_REDIS_DB: 0
+      MAXKB_CORE_WORKER: 4
+      MAXKB_STATIC_PATH: /opt/maxkb-app/static
+    ports:
+      - "8080:8080"
+    depends_on:
+      postgres:
+        condition: service_healthy
+      redis:
+        condition: service_healthy
+    volumes:
+      - model_data:/opt/maxkb-app/model/base
+      - tmp_data:/opt/maxkb-app/tmp
+      - log_data:/opt/maxkb-app/logs
+      - static_data:/opt/maxkb-app/static
+    networks:
+      - maxkb-network
+    restart: unless-stopped
+
+  maxkb-celery:
+    image: zhagent-back:latest
+    container_name: maxkb-celery
+    environment:
+      MAXKB_CONFIG_TYPE: ENV
+      MAXKB_DB_NAME: maxkb
+      MAXKB_DB_HOST: postgres
+      MAXKB_DB_PORT: 5432
+      MAXKB_DB_USER: postgres
+      MAXKB_DB_PASSWORD: postgres
+      MAXKB_REDIS_HOST: redis
+      MAXKB_REDIS_PORT: 6379
+      MAXKB_REDIS_DB: 0
+    depends_on:
+      postgres:
+        condition: service_healthy
+      redis:
+        condition: service_healthy
+      maxkb-web:
+        condition: service_started
+    volumes:
+      - model_data:/opt/maxkb-app/model/base
+      - tmp_data:/opt/maxkb-app/tmp
+      - log_data:/opt/maxkb-app/logs
+    networks:
+      - maxkb-network
+    restart: unless-stopped
+
+volumes:
+  postgres_data:
+  redis_data:
+  model_data:
+  tmp_data:
+  log_data:
+  static_data:
+
+networks:
+  maxkb-network:
+    driver: bridge

+ 47 - 0
ui/nginx.conf

@@ -0,0 +1,47 @@
+server {
+    listen 80;
+    server_name localhost;
+    root /usr/share/nginx/html;
+    index index.html;
+
+    location / {
+        try_files $uri $uri/ /index.html;
+    }
+
+    location /api {
+        proxy_pass http://maxkb-web:8080;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+
+    location /admin {
+        proxy_pass http://maxkb-web:8080;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+
+    location /chat {
+        proxy_pass http://maxkb-web:8080;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+
+    location /ws {
+        proxy_pass http://maxkb-web:8080;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+
+    gzip on;
+    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
+}

+ 2 - 2
ui/package.json

@@ -6,8 +6,8 @@
   "scripts": {
     "dev": "vite",
     "chat": "vite --mode chat",
-    "build": "run-p type-check \"build-only {@}\" --",
-    "build-chat": "run-p type-check \"build-only-chat {@}\" --",
+    "build": "run-p type-check build-only",
+    "build-chat": "run-p type-check build-only-chat",
     "preview": "vite preview",
     "build-only": "vite build",
     "build-only-chat": "vite build --mode chat",

+ 27 - 28
ui/vite.config.ts

@@ -7,9 +7,12 @@ 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 __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
+
 const envDir = './env'
-// 自定义插件:重命名入口文件
+
 const renameHtmlPlugin = (outDir: string, entry: string) => {
   return {
     name: 'rename-html',
@@ -18,22 +21,23 @@ const renameHtmlPlugin = (outDir: string, entry: string) => {
       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((conf: any) => {
   const mode = conf.mode
   const ENV = loadEnv(mode, envDir)
+
+  const basePath = ENV.VITE_BASE_PATH || '/'
+  const entryFile = ENV.VITE_ENTRY || 'index.html'
+
   const proxyConf: Record<string, string | ProxyOptions> = {}
   proxyConf['/admin/api'] = {
     target: 'http://127.0.0.1:8080',
@@ -46,46 +50,41 @@ export default defineConfig((conf: any) => {
   proxyConf['/doc'] = {
     target: 'http://127.0.0.1:8080',
     changeOrigin: true,
-    rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),
+    rewrite: (path: string) => path.replace(basePath, '/'),
   }
   proxyConf['/schema'] = {
     target: 'http://127.0.0.1:8080',
     changeOrigin: true,
-    rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),
+    rewrite: (path: string) => path.replace(basePath, '/'),
   }
   proxyConf['/static'] = {
     target: 'http://127.0.0.1:8080',
     changeOrigin: true,
-    rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),
+    rewrite: (path: string) => path.replace(basePath, '/'),
   }
 
-  // 前端静态资源转发到本身
-  proxyConf[`^${ENV.VITE_BASE_PATH}.+\/oss\/file\/.*$`] = {
-    target: `http://127.0.0.1:8080`,
+  proxyConf[`^${basePath}.+\/oss\/file\/.*$`] = {
+    target: 'http://127.0.0.1:8080',
     changeOrigin: true,
   }
-  // 前端静态资源转发到本身
-  proxyConf[`^${ENV.VITE_BASE_PATH}oss\/file\/.*$`] = {
-    target: `http://127.0.0.1:8080`,
+  proxyConf[`^${basePath}oss\/file\/.*$`] = {
+    target: 'http://127.0.0.1:8080',
     changeOrigin: true,
   }
-  proxyConf[`^${ENV.VITE_BASE_PATH}oss\/get_url\/.*$`] = {
-    target: `http://127.0.0.1:8080`,
+  proxyConf[`^${basePath}oss\/get_url\/.*$`] = {
+    target: 'http://127.0.0.1:8080',
     changeOrigin: true,
   }
-  // 前端静态资源转发到本身
-  proxyConf[ENV.VITE_BASE_PATH] = {
-    target: `http://127.0.0.1:${ENV.VITE_APP_PORT}`,
+  proxyConf[basePath] = {
+    target: `http://127.0.0.1:${ENV.VITE_APP_PORT || 8080}`,
     changeOrigin: true,
-    rewrite: (path: string) => path.replace(ENV.VITE_BASE_PATH, '/'),
+    rewrite: (path: string) => path.replace(basePath, '/'),
   }
 
-  // 代理 /chat 到 3001 端口
   if (mode !== 'chat') {
     proxyConf['/chat'] = {
       target: 'http://127.0.0.1:3001',
       changeOrigin: true,
-      // 避免代理后端的 API
       bypass: (req: any) => {
         if (req.url && req.url.startsWith('/chat/api')) {
           return req.url
@@ -103,21 +102,21 @@ export default defineConfig((conf: any) => {
       vue(),
       vueJsx(),
       DefineOptions(),
-      createHtmlPlugin({template: ENV.VITE_ENTRY}),
-      renameHtmlPlugin(`dist${ENV.VITE_BASE_PATH}`, ENV.VITE_ENTRY),
+      createHtmlPlugin({template: entryFile}),
+      renameHtmlPlugin(`dist${basePath}`, entryFile),
     ],
     server: {
       cors: true,
       host: '0.0.0.0',
-      port: Number(ENV.VITE_APP_PORT),
+      port: Number(ENV.VITE_APP_PORT) || 8080,
       strictPort: true,
       proxy: proxyConf,
     },
     build: {
-      outDir: `dist${ENV.VITE_BASE_PATH}`,
+      outDir: `dist${basePath}`,
       target: 'es2022',
       rollupOptions: {
-        input: ENV.VITE_ENTRY,
+        input: entryFile,
       },
     },
     resolve: {