#!/usr/bin/env bash set -e # ══════════════════════════════════════════════════════════════ # 题库PLUS 服务端安装脚本 # 支持 Arch Linux (pacman) / Debian-Ubuntu (apt) # ══════════════════════════════════════════════════════════════ RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' echo "========================================" echo " 题库PLUS 服务端安装" echo "========================================" echo "" # ── 检测 ────────────────────────────────────────────────── command_exists() { command -v "$1" &> /dev/null; } # Python >= 3.10 if command_exists python3; then PY_VER=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') echo -e "${GREEN}✓${NC} Python $PY_VER" else echo -e "${RED}✗${NC} Python 3.10+ 未安装" echo " 请先安装 Python: https://www.python.org/downloads/" exit 1 fi # pandoc if command_exists pandoc; then echo -e "${GREEN}✓${NC} pandoc $(pandoc --version | head -1 | cut -d' ' -f2)" else echo -e "${YELLOW}⚠${NC} pandoc 未安装" if command_exists pacman; then echo " 安装: sudo pacman -S pandoc" elif command_exists apt; then echo " 安装: sudo apt install pandoc" else echo " 请手动安装 pandoc: https://pandoc.org/installing.html" fi echo " pandoc 用于 docx→HTML 渲染,如不安装则题目渲染功能不可用" fi # 中文字体 FONT_OK=0 for font in "宋体" "SimSun" "Noto Serif CJK SC" "WenQuanYi Micro Hei"; do if fc-list | grep -qi "$font"; then FONT_OK=1 break fi done if [ "$FONT_OK" -eq 1 ]; then echo -e "${GREEN}✓${NC} 中文字体" else echo -e "${YELLOW}⚠${NC} 未检测到中文字体" if command_exists pacman; then echo " 安装: sudo pacman -S noto-fonts-cjk" elif command_exists apt; then echo " 安装: sudo apt install fonts-noto-cjk" fi fi echo "" # ── 虚拟环境 ────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" if [ ! -d ".venv" ]; then echo "创建 Python 虚拟环境..." python3 -m venv .venv echo -e "${GREEN}✓${NC} 虚拟环境已创建" fi source .venv/bin/activate echo "安装 Python 依赖..." pip install -r requirements.txt -q echo -e "${GREEN}✓${NC} Python 依赖已安装" echo "" # ── 配置文件 ────────────────────────────────────────────── if [ ! -f "config.yaml" ]; then cp config.yaml.example config.yaml 2>/dev/null || true echo -e "${GREEN}✓${NC} config.yaml 已就绪" else echo -e "${GREEN}✓${NC} config.yaml 已存在" fi # ── 数据目录 ────────────────────────────────────────────── mkdir -p data/problems data/papers data/backups data/uploads echo -e "${GREEN}✓${NC} 数据目录已创建" # ── 知识点编码 ──────────────────────────────────────────── if [ ! -f "src/knowledge_map.json" ]; then if [ -f "src/知识点树.md" ]; then echo "" echo "构建知识点编码..." .venv/bin/python tiku-server knowledge build echo -e "${GREEN}✓${NC} 知识点编码已构建" else echo -e "${YELLOW}⚠${NC} src/知识点树.md 不存在,跳过知识点构建" echo " 如需知识点编码,请从现有题库复制该文件后运行:" echo " ./tiku-server knowledge build" fi fi # ── Token ───────────────────────────────────────────────── TOKEN=$(grep -oP 'token:\s*"\K[^"]+' config.yaml 2>/dev/null || true) if [ -z "$TOKEN" ]; then echo "" echo "生成 API Token..." .venv/bin/python tiku-server token regerate 2>/dev/null || { # 如果 CLI token 命令不存在,手动生成 TOKEN=$(python3 -c "import secrets; print(secrets.token_hex(16))") sed -i "s/token: \"\"/token: \"$TOKEN\"/" config.yaml 2>/dev/null || true echo -e "${GREEN}✓${NC} Token: $TOKEN" } fi echo "" echo "========================================" echo -e "${GREEN} 安装完成!${NC}" echo "========================================" echo "" echo "API Token: $(grep -oP 'token:\s*"\K[^"]+' config.yaml 2>/dev/null || echo '(查看 config.yaml)')" echo "" echo "启动命令:" echo " cd $SCRIPT_DIR && source .venv/bin/activate" echo " nohup ./tiku-server serve --restart > tiku.log 2>&1 &" echo "" echo " 或使用 systemd (下方创建)" echo "" echo "访问: http://localhost:8765" echo "" # ── systemd 服务(可选)─────────────────────────────────── read -p "是否创建 systemd 服务实现开机自启?[y/N] " -r if [[ $REPLY =~ ^[Yy]$ ]]; then SERVICE_FILE="/etc/systemd/system/tiku-server.service" echo "[Unit] Description=题库PLUS 服务端 After=network.target [Service] Type=simple User=$USER WorkingDirectory=$SCRIPT_DIR ExecStart=$SCRIPT_DIR/.venv/bin/python $SCRIPT_DIR/tiku-server serve Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target" | sudo tee "$SERVICE_FILE" > /dev/null sudo systemctl daemon-reload sudo systemctl enable tiku-server echo -e "${GREEN}✓${NC} systemd 服务已创建" echo " 启动: sudo systemctl start tiku-server" echo " 状态: sudo systemctl status tiku-server" fi echo "" # ── Flutter Web(可选)──────────────────────────────────── if command_exists flutter; then echo "" read -p "检测到 Flutter SDK,是否构建 Web 客户端?[y/N] " -r if [[ $REPLY =~ ^[Yy]$ ]]; then FLUTTER_DIR="$SCRIPT_DIR/../tiku-client" if [ -d "$FLUTTER_DIR" ]; then cd "$FLUTTER_DIR" echo "构建 Flutter Web..." flutter build web --release cp -r build/web/* "$SCRIPT_DIR/web/" echo -e "${GREEN}✓${NC} Flutter Web 已部署" cd "$SCRIPT_DIR" else echo -e "${YELLOW}⚠${NC} Flutter 客户端目录不存在: $FLUTTER_DIR" fi fi fi