Files
CollegeAIcenter/deploy.sh
T
selfrelease 8809a28e9c feat: 新增汇报PPT与生成脚本,修复tsconfig弃用告警
- 新增医科高校智能学习中心汇报PPT(27页,面向高校管理者与广电领导)
- 新增 pptxgenjs 生成脚本 scripts/gen-pptx.js
- 新增 SYSTEM-OVERVIEW.md 系统功能全景文档
- 新增 deploy.sh 部署脚本
- 补充 rate-limit guard、ai-stream controller、exam-prep 仓储等后端模块
- 补充 exam-recommendations、markdown、mock-exam、offline-indicator 前端组件
- tsconfig.json: 显式设置 rootDir=./src,加 ignoreDeprecations=6.0 静音 baseUrl 弃用告警

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-10 12:13:52 +08:00

165 lines
5.8 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
#
# College AI Center — 一键部署脚本
#
# 用法:
# ./deploy.sh
#
# 作用:把本地代码同步到远程服务器,在服务器上安装依赖、构建前后端、
# 配置 PostgreSQL 数据库、systemd 服务与 nginx(域名 mc.all8ai.top),并启动。
#
# 与服务器上已有的 k12 应用互不影响(使用独立端口 3100/4100、独立目录、
# 名称匹配的 nginx server_name,不占用 default_server)。
#
# 依赖(本机):sshpass、rsync、ssh
# ---------------------------------------------------------------------------
set -euo pipefail
# ====== 可配置参数 ======
SERVER_IP="${SERVER_IP:-101.96.196.218}"
SERVER_USER="${SERVER_USER:-root}"
SERVER_PASS="${SERVER_PASS:-Why_701208}"
REMOTE_DIR="/opt/college-ai-center"
BACKEND_PORT=3100
FRONTEND_PORT=4100
# 数据库
DB_NAME="college_ai_center"
DB_USER="cac_user"
DB_PASS="${DB_PASS:-Cac_$(echo "$SERVER_PASS" | md5sum 2>/dev/null | cut -c1-12 || echo Pg88dev)}"
# 大模型配置(如需启用,部署后在服务器 .env 中填写 LLM_API_KEY
LLM_API_KEY="${LLM_API_KEY:-}"
LLM_BASE_URL="${LLM_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}"
LLM_MODEL="${LLM_MODEL:-qwen-plus}"
JWT_SECRET="${JWT_SECRET:-cac-prod-$(openssl rand -hex 16 2>/dev/null || echo change-me-in-prod)}"
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=20"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ssh_run() { sshpass -p "$SERVER_PASS" ssh $SSH_OPTS "$SERVER_USER@$SERVER_IP" "$@"; }
echo "==> [1/6] 检查本机依赖"
for c in sshpass rsync ssh; do
command -v "$c" >/dev/null 2>&1 || { echo "缺少命令:$c,请先安装(mac: brew install $c"; exit 1; }
done
echo "==> [2/6] 同步代码到 $SERVER_IP:$REMOTE_DIR"
ssh_run "mkdir -p $REMOTE_DIR"
sshpass -p "$SERVER_PASS" rsync -az --delete -e "ssh $SSH_OPTS" \
--exclude 'node_modules' \
--exclude 'web/node_modules' \
--exclude '.next' \
--exclude 'web/.next' \
--exclude 'dist' \
--exclude '.git' \
--exclude 'coverage' \
--exclude '*.log' \
"$SCRIPT_DIR/" "$SERVER_USER@$SERVER_IP:$REMOTE_DIR/"
echo "==> [3/6] 在服务器上准备数据库"
ssh_run "sudo -u postgres psql -tc \"SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'\" | grep -q 1 || sudo -u postgres psql -c \"CREATE ROLE $DB_USER LOGIN PASSWORD '$DB_PASS';\""
ssh_run "sudo -u postgres psql -tc \"SELECT 1 FROM pg_database WHERE datname='$DB_NAME'\" | grep -q 1 || sudo -u postgres psql -c \"CREATE DATABASE $DB_NAME OWNER $DB_USER;\""
ssh_run "sudo -u postgres psql -c \"ALTER DATABASE $DB_NAME OWNER TO $DB_USER;\" >/dev/null 2>&1 || true"
echo "==> [4/6] 写入后端 .env"
ssh_run "cat > $REMOTE_DIR/.env <<EOF
APP_PORT=$BACKEND_PORT
NODE_ENV=production
CORS_ORIGIN=*
DB_ENABLED=true
DB_HOST=127.0.0.1
DB_PORT=5432
DB_USERNAME=$DB_USER
DB_PASSWORD=$DB_PASS
DB_DATABASE=$DB_NAME
DB_SYNCHRONIZE=true
DB_LOGGING=false
JWT_SECRET=$JWT_SECRET
JWT_EXPIRES_IN_SECONDS=7200
LLM_API_KEY=$LLM_API_KEY
LLM_BASE_URL=$LLM_BASE_URL
LLM_MODEL=$LLM_MODEL
LLM_TIMEOUT_MS=30000
AI_RATE_LIMIT_PER_MINUTE=20
AI_RATE_LIMIT_PER_DAY=500
EOF"
echo "==> [5/6] 安装依赖并构建(后端 + 前端)"
ssh_run "set -e; cd $REMOTE_DIR && npm install --no-audit --no-fund && npm run build"
ssh_run "set -e; cd $REMOTE_DIR/web && npm install --no-audit --no-fund && BACKEND_URL=http://127.0.0.1:$BACKEND_PORT npm run build"
echo "==> [6/6] 配置 systemd 服务"
# 后端 systemd
ssh_run "cat > /etc/systemd/system/cac-backend.service <<EOF
[Unit]
Description=College AI Center Backend (NestJS)
After=network.target postgresql.service
[Service]
Type=simple
WorkingDirectory=$REMOTE_DIR
ExecStart=/usr/bin/node dist/main
Restart=always
RestartSec=3
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
EOF"
# 前端 systemd(监听 0.0.0.0:$FRONTEND_PORT,直接对外提供访问)
ssh_run "cat > /etc/systemd/system/cac-frontend.service <<EOF
[Unit]
Description=College AI Center Frontend (Next.js)
After=network.target cac-backend.service
[Service]
Type=simple
WorkingDirectory=$REMOTE_DIR/web
ExecStart=/usr/bin/npm run start
Restart=always
RestartSec=3
Environment=NODE_ENV=production
Environment=BACKEND_URL=http://127.0.0.1:$BACKEND_PORT
Environment=PORT=$FRONTEND_PORT
[Install]
WantedBy=multi-user.target
EOF"
# 若曾配置过本应用的 nginx 站点,移除(改为直接端口访问)
ssh_run "rm -f /etc/nginx/sites-enabled/college-ai-center /etc/nginx/sites-available/college-ai-center; (nginx -t && systemctl reload nginx) >/dev/null 2>&1 || true"
# 若 ufw 启用,则放行端口
ssh_run "if ufw status 2>/dev/null | grep -q 'Status: active'; then ufw allow $BACKEND_PORT/tcp; ufw allow $FRONTEND_PORT/tcp; fi || true"
echo "==> 启动服务"
ssh_run "systemctl daemon-reload && systemctl enable cac-backend cac-frontend >/dev/null 2>&1 && systemctl restart cac-backend cac-frontend"
echo "==> 等待服务就绪"
sleep 6
ssh_run "systemctl is-active cac-backend cac-frontend; echo '--- backend health ---'; curl -s -o /dev/null -w 'backend(api/auth/login probe): %{http_code}\n' -X POST http://127.0.0.1:$BACKEND_PORT/api/auth/login -H 'Content-Type: application/json' -d '{}'; echo '--- frontend ---'; curl -s -o /dev/null -w 'frontend: %{http_code}\n' http://127.0.0.1:$FRONTEND_PORT"
echo ""
echo "============================================================"
echo " 部署完成!"
echo " 前端访问: http://${SERVER_IP}:$FRONTEND_PORT"
echo " 后端 API http://${SERVER_IP}:$BACKEND_PORT/api"
echo " 数据库 $DB_NAME (用户 $DB_USER)"
echo ""
echo " 测试账户:student / mentor / admin(密码同 *123,如 student123"
echo ""
echo " 常用运维:"
echo " systemctl status cac-backend cac-frontend"
echo " journalctl -u cac-backend -f"
echo " journalctl -u cac-frontend -f"
echo "============================================================"