From be3a963654e1f8bbe9a2a3e9d0b0e5568a825aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=BB=AB?= <486494914@qq.com> Date: Wed, 2 Sep 2026 12:22:34 +0800 Subject: [PATCH] 1 --- create-admin.sh | 47 +++++++++++++++++++++++++ deploy.sh | 85 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 60 ++++++++++++++++++++++++++------ update.sh | 39 +++++++++++++++++++++ 4 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 create-admin.sh create mode 100644 deploy.sh create mode 100644 update.sh diff --git a/create-admin.sh b/create-admin.sh new file mode 100644 index 0000000..64a025e --- /dev/null +++ b/create-admin.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# ============================================================ +# 创建内部员工账号 +# 用法: bash create-admin.sh 用户名 邮箱 密码 +# 示例: bash create-admin.sh zhangsan zhang@company.com 12345678 +# ============================================================ + +USERNAME=${1:-admin} +EMAIL=${2:-admin@company.com} +PASSWORD=${3:-Lzcc6-01} + +echo "创建内部员工: $USERNAME ($EMAIL)" + +docker compose exec -T backend python -c " +from app.core.db import get_session_factory +from app.models.user import User +from app.models.plan import Plan +from app.core.security import hash_password +from sqlalchemy import select + +factory = get_session_factory() +with factory() as session: + existing = session.scalars(select(User).where(User.username == '$USERNAME')).first() + if existing: + print(f'用户 $USERNAME 已存在,更新密码...') + existing.password_hash = hash_password('$PASSWORD') + existing.role = 'internal' + session.commit() + print('密码已更新') + else: + plan = session.scalars(select(Plan).where(Plan.code == 'free')).first() + if not plan: + plan = Plan(code='free', name='免费版', storage_quota=104857600, max_file_size=20971520, is_active=True) + session.add(plan) + session.flush() + user = User( + username='$USERNAME', + email='$EMAIL', + password_hash=hash_password('$PASSWORD'), + status='active', + role='internal', + plan_id=plan.id, + ) + session.add(user) + session.commit() + print(f'内部员工创建成功: $USERNAME / $PASSWORD') +" \ No newline at end of file diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..d69bf8a --- /dev/null +++ b/deploy.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# ============================================================ +# AI Knowledge Link — 一键部署脚本 +# 在服务器项目目录执行:bash deploy.sh +# ============================================================ + +set -e + +echo "=== AI Knowledge Link 部署 ===" + +# 1. 检查 .env +if [ ! -f .env ]; then + echo "创建 .env ..." + cp .env.production .env + + # 自动生成 SECRET_KEY + SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(48))" 2>/dev/null || openssl rand -base64 48) + sed -i "s/change-me-to-a-real-secret-key/$SECRET/" .env + echo "SECRET_KEY 已自动生成" +fi + +# 2. 创建数据目录 +mkdir -p data + +# 3. 构建并启动 +echo "构建镜像..." +docker compose build + +echo "启动服务..." +docker compose up -d + +# 4. 等待 MySQL 就绪 +echo "等待 MySQL 就绪..." +sleep 10 + +# 5. 数据库迁移 +echo "执行数据库迁移..." +docker compose exec -T backend alembic upgrade head + +# 6. 创建内部管理员账号(如果不存在) +echo "检查管理员账号..." +docker compose exec -T backend python -c " +from app.core.db import get_session_factory +from app.models.user import User +from app.models.plan import Plan +from app.core.security import hash_password +from sqlalchemy import select + +factory = get_session_factory() +with factory() as session: + admin = session.scalars(select(User).where(User.username == 'admin')).first() + if not admin: + plan = session.scalars(select(Plan).where(Plan.code == 'free')).first() + if not plan: + plan = Plan(code='free', name='免费版', storage_quota=104857600, max_file_size=20971520, is_active=True) + session.add(plan) + session.flush() + admin = User( + username='admin', + email='admin@company.com', + password_hash=hash_password('Lzcc6-01'), + status='active', + role='internal', + plan_id=plan.id, + ) + session.add(admin) + session.commit() + print('管理员账号已创建: admin / Lzcc6-01') + else: + print('管理员账号已存在,跳过') +" + +# 7. 完成 +echo "" +echo "==========================================" +echo " 部署完成!" +echo "" +echo " 访问地址: http://$(curl -s ifconfig.me 2>/dev/null || echo '你的服务器IP')" +echo " 内部登录: http://$(curl -s ifconfig.me 2>/dev/null || echo '你的服务器IP')/internal-login" +echo "" +echo " 管理员: admin" +echo " 密码: Lzcc6-01" +echo "" +echo " 更新部署: bash update.sh" +echo "==========================================" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e95d3ed..5430016 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,30 +1,65 @@ # AI Knowledge Link — Docker Compose (生产) -# Ubuntu 服务器部署用。Windows 开发不需要 Docker。 -# -# 数据持久化:./data 挂载到 backend,SQLite + 用户文件均在其中。 -# 删除容器后数据不丢失。 # # 用法: -# cp .env.example .env # 修改密钥 -# mkdir -p data -# docker compose up -d --build +# 1. 把代码上传到服务器 +# 2. cp .env.example .env && 修改 SECRET_KEY +# 3. docker compose up -d --build +# 4. docker compose exec backend alembic upgrade head (首次) +# 5. 访问 http://服务器IP +# +# 更新部署: +# git pull && docker compose up -d --build services: + # ---------- MySQL ---------- + mysql: + image: mysql:8.0 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-Lzcc6-01} + MYSQL_DATABASE: amb_rag + MYSQL_USER: amb + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-Lzcc6-01} + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + volumes: + - mysql_data:/var/lib/mysql + ports: + - "3306:3306" + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 10 + + # ---------- 后端 ---------- backend: build: context: ./backend restart: unless-stopped - env_file: .env + environment: + ENVIRONMENT: production + SECRET_KEY: ${SECRET_KEY:-change-me-to-a-real-secret-key} + DATABASE_URL: mysql+pymysql://amb:${MYSQL_PASSWORD:-Lzcc6-01}@mysql:3306/amb_rag?charset=utf8mb4 + STORAGE_ROOT: /app/data + DEFAULT_STORAGE_QUOTA: "104857600" + DEFAULT_MAX_FILE_SIZE: "20971520" + RATE_LIMIT_PER_TOKEN_PER_MIN: "60" + RATE_LIMIT_PER_IP_PER_MIN: "30" + FRONTEND_ORIGIN: http://localhost volumes: - - ./data:/app/data + - app_data:/app/data expose: - "8000" + depends_on: + mysql: + condition: service_healthy healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/healthz')"] interval: 10s timeout: 5s retries: 5 + # ---------- 前端 ---------- frontend: build: context: ./frontend @@ -32,6 +67,7 @@ services: expose: - "80" + # ---------- Nginx ---------- nginx: image: nginx:1.27-alpine restart: unless-stopped @@ -42,4 +78,8 @@ services: depends_on: backend: condition: service_healthy - - frontend \ No newline at end of file + - frontend + +volumes: + mysql_data: + app_data: \ No newline at end of file diff --git a/update.sh b/update.sh new file mode 100644 index 0000000..b00c25f --- /dev/null +++ b/update.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# ============================================================ +# AI Knowledge Link — 一键更新脚本 +# 在服务器项目目录执行:bash update.sh +# +# 功能: +# 1. 拉取最新代码 +# 2. 重新构建镜像 +# 3. 重启服务(数据库不丢失) +# 4. 执行数据库迁移(如有新迁移) +# ============================================================ + +set -e + +echo "=== AI Knowledge Link 更新 ===" +echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" + +# 1. 拉取最新代码 +echo "[1/4] 拉取最新代码..." +git pull + +# 2. 重新构建 +echo "[2/4] 重新构建镜像..." +docker compose build + +# 3. 重启服务 +echo "[3/4] 重启服务..." +docker compose up -d + +# 4. 数据库迁移 +echo "[4/4] 执行数据库迁移..." +sleep 5 +docker compose exec -T backend alembic upgrade head + +# 5. 完成 +echo "" +echo "更新完成!$(date '+%H:%M:%S')" +echo "服务状态:" +docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"