05e173b293
Replace the WebView shell with SwiftUI screens, add account-scoped Wiki and TTS APIs with adaptive review and photo scan support, and keep web/iOS pages usable while data loads asynchronously. Co-authored-by: Cursor <cursoragent@cursor.com>
142 lines
4.6 KiB
Bash
Executable File
142 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 在 105 服务器上执行(/root/wordloop)
|
||
set -euo pipefail
|
||
|
||
ROOT="${WORDLOOP_ROOT:-/root/wordloop}"
|
||
cd "$ROOT"
|
||
|
||
MYSQL_HOST="${MYSQL_HOST:?MYSQL_HOST required}"
|
||
MYSQL_PORT="${MYSQL_PORT:-3306}"
|
||
MYSQL_USER="${MYSQL_USER:?MYSQL_USER required}"
|
||
MYSQL_PASSWORD="${MYSQL_PASSWORD:?MYSQL_PASSWORD required}"
|
||
MYSQL_DATABASE="${MYSQL_DATABASE:-wordloop}"
|
||
if [ -z "${WORDLOOP_SECRET_KEY:-}" ] && [ -f "$ROOT/backend/.env" ]; then
|
||
WORDLOOP_SECRET_KEY="$(grep -E '^WORDLOOP_SECRET_KEY=' "$ROOT/backend/.env" | cut -d= -f2- || true)"
|
||
fi
|
||
WORDLOOP_SECRET_KEY="${WORDLOOP_SECRET_KEY:-$(openssl rand -hex 32)}"
|
||
DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-}"
|
||
DEEPSEEK_BASE_URL="${DEEPSEEK_BASE_URL:-https://api.deepseek.com}"
|
||
DEEPSEEK_MODEL="${DEEPSEEK_MODEL:-deepseek-chat}"
|
||
AI_WIKI_DAILY_LLM_LIMIT="${AI_WIKI_DAILY_LLM_LIMIT:-20}"
|
||
TKMIND_TTS_BASE_URL="${TKMIND_TTS_BASE_URL:-http://127.0.0.1:19200}"
|
||
TKMIND_TTS_ENABLED="${TKMIND_TTS_ENABLED:-true}"
|
||
TKMIND_TTS_SPK_ID="${TKMIND_TTS_SPK_ID:-aitong}"
|
||
|
||
echo ">>> 写入 backend/.env"
|
||
cat > "$ROOT/backend/.env" <<EOF
|
||
MYSQL_HOST=${MYSQL_HOST}
|
||
MYSQL_PORT=${MYSQL_PORT}
|
||
MYSQL_USER=${MYSQL_USER}
|
||
MYSQL_PASSWORD=${MYSQL_PASSWORD}
|
||
MYSQL_DATABASE=${MYSQL_DATABASE}
|
||
WORDLOOP_SECRET_KEY=${WORDLOOP_SECRET_KEY}
|
||
DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}
|
||
DEEPSEEK_BASE_URL=${DEEPSEEK_BASE_URL}
|
||
DEEPSEEK_MODEL=${DEEPSEEK_MODEL}
|
||
AI_WIKI_DAILY_LLM_LIMIT=${AI_WIKI_DAILY_LLM_LIMIT}
|
||
TKMIND_TTS_BASE_URL=${TKMIND_TTS_BASE_URL}
|
||
TKMIND_TTS_ENABLED=${TKMIND_TTS_ENABLED}
|
||
TKMIND_TTS_SPK_ID=${TKMIND_TTS_SPK_ID}
|
||
EOF
|
||
chmod 600 "$ROOT/backend/.env"
|
||
|
||
echo ">>> Python 虚拟环境与依赖"
|
||
cd "$ROOT/backend"
|
||
if [ ! -d venv ]; then
|
||
python3 -m venv venv
|
||
fi
|
||
source venv/bin/activate
|
||
pip install -q -r requirements.txt
|
||
|
||
echo ">>> OCR 依赖(iOS 拍照录入走服务端识别)"
|
||
if command -v tesseract >/dev/null 2>&1; then
|
||
echo "tesseract 已安装: $(tesseract --version 2>&1 | head -1)"
|
||
else
|
||
if command -v apt-get >/dev/null 2>&1; then
|
||
apt-get update -qq
|
||
apt-get install -y -qq tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng
|
||
elif command -v yum >/dev/null 2>&1; then
|
||
yum install -y tesseract tesseract-langpack-chi-sim 2>/dev/null || true
|
||
elif command -v dnf >/dev/null 2>&1; then
|
||
dnf install -y tesseract tesseract-langpack-chi-sim 2>/dev/null || true
|
||
fi
|
||
fi
|
||
if ! command -v tesseract >/dev/null 2>&1; then
|
||
echo "警告: 未安装 tesseract,iOS 拍照录入将无法识别照片" >&2
|
||
fi
|
||
|
||
echo ">>> 创建数据库(若不存在)并建表"
|
||
python - <<'PY'
|
||
import pymysql
|
||
from config import get_settings
|
||
|
||
s = get_settings()
|
||
conn = pymysql.connect(
|
||
host=s.mysql_host,
|
||
port=s.mysql_port,
|
||
user=s.mysql_user,
|
||
password=s.mysql_password,
|
||
charset="utf8mb4",
|
||
)
|
||
try:
|
||
with conn.cursor() as cur:
|
||
cur.execute(
|
||
f"CREATE DATABASE IF NOT EXISTS `{s.mysql_database}` "
|
||
"CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
|
||
)
|
||
conn.commit()
|
||
print(f"database `{s.mysql_database}` ready")
|
||
finally:
|
||
conn.close()
|
||
|
||
from database import engine, Base
|
||
from sqlalchemy import text
|
||
import models # noqa: F401
|
||
|
||
with engine.connect() as conn:
|
||
conn.execute(text("SELECT 1"))
|
||
Base.metadata.create_all(bind=engine)
|
||
print("tables ready")
|
||
PY
|
||
|
||
echo ">>> 导入词书目录(沪版 + 商务)"
|
||
cd "$ROOT/backend"
|
||
source venv/bin/activate
|
||
if [ -d "$ROOT/backend/data/books" ] && ls "$ROOT/backend/data/books"/*.json >/dev/null 2>&1; then
|
||
python -m scripts.import_word_books
|
||
else
|
||
echo "未找到 data/books/*.json,跳过词书导入"
|
||
fi
|
||
|
||
echo ">>> 构建前端"
|
||
cd "$ROOT/frontend"
|
||
if ! command -v npm >/dev/null 2>&1; then
|
||
echo "请先安装 Node.js/npm"
|
||
exit 1
|
||
fi
|
||
npm ci --silent
|
||
npm run build
|
||
|
||
echo ">>> Nginx 可读静态目录"
|
||
chmod 755 /root /root/wordloop /root/wordloop/frontend /root/wordloop/frontend/dist 2>/dev/null || true
|
||
|
||
echo ">>> 配置 Nginx + HTTPS (w.tkmind.cn)"
|
||
if command -v nginx >/dev/null 2>&1; then
|
||
cp "$ROOT/deploy/wordloop-locations.inc" /etc/nginx/conf.d/wordloop-locations.inc
|
||
chmod +x "$ROOT/deploy/ssl-w.tkmind.cn.sh"
|
||
bash "$ROOT/deploy/ssl-w.tkmind.cn.sh"
|
||
systemctl enable nginx 2>/dev/null || true
|
||
else
|
||
echo "警告: 未安装 nginx,请手动安装后执行 deploy/ssl-w.tkmind.cn.sh"
|
||
fi
|
||
|
||
echo ">>> 配置 systemd 后端服务"
|
||
cp "$ROOT/deploy/systemd/wordloop-backend-root.service" /etc/systemd/system/wordloop-backend.service
|
||
systemctl daemon-reload
|
||
systemctl enable wordloop-backend
|
||
systemctl restart wordloop-backend
|
||
|
||
echo ">>> 完成"
|
||
systemctl --no-pager status wordloop-backend || true
|
||
curl -sf http://127.0.0.1:18004/ >/dev/null && echo "API: ok" || echo "API: 请检查 journalctl -u wordloop-backend"
|