Ship native iOS app, Wiki/TTS backend, and skeleton loading UX.
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>
This commit is contained in:
@@ -10,7 +10,17 @@ 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
|
||||
@@ -20,6 +30,13 @@ 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"
|
||||
|
||||
@@ -31,6 +48,23 @@ 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
|
||||
|
||||
@@ -40,6 +40,12 @@ ssh "$SSH_TARGET" \
|
||||
MYSQL_USER='${MYSQL_USER}' \
|
||||
MYSQL_PASSWORD='${MYSQL_PASSWORD}' \
|
||||
MYSQL_DATABASE='${MYSQL_DATABASE}' \
|
||||
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}' \
|
||||
bash /root/wordloop/deploy/install-production.sh"
|
||||
|
||||
echo ">>> 发布完成: http://120.26.184.105/ (Cloudflare 请将 w 解析到该 IP)"
|
||||
|
||||
@@ -5,3 +5,15 @@ MYSQL_USER=boot
|
||||
MYSQL_PASSWORD=@Abc888888
|
||||
MYSQL_DATABASE=wordloop
|
||||
# SSH_TARGET=root@120.26.184.105
|
||||
|
||||
# Wiki(DeepSeek)
|
||||
DEEPSEEK_API_KEY=
|
||||
DEEPSEEK_BASE_URL=https://api.deepseek.com
|
||||
DEEPSEEK_MODEL=deepseek-chat
|
||||
AI_WIKI_DAILY_LLM_LIMIT=20
|
||||
|
||||
# 单词朗读(105 同机 TTS)
|
||||
# 生产机与 CosyVoice 同机部署,走本机 19200
|
||||
TKMIND_TTS_BASE_URL=http://127.0.0.1:19200
|
||||
TKMIND_TTS_ENABLED=true
|
||||
TKMIND_TTS_SPK_ID=aitong
|
||||
|
||||
@@ -18,7 +18,10 @@ RDS_USER="${RDS_USER:-boot}"
|
||||
RDS_PASS="${RDS_PASSWORD:-@Abc888888}"
|
||||
RDS_DB="${RDS_DATABASE:-wordloop}"
|
||||
|
||||
TABLES="users user_settings words quiz_records dictionary_entries"
|
||||
# 仅同步全局离线词典(无用户外键)。
|
||||
# 词书目录请走 publish.sh -> install-production.sh 内的 import_word_books(按 slug upsert,避免 DROP 破坏学习关联)。
|
||||
# 绝不包含 users / user_settings / words / quiz_records / wiki_* 等账号学习数据表。
|
||||
TABLES="dictionary_entries"
|
||||
DUMP="$(mktemp /tmp/wordloop-sync.XXXXXX.sql.gz)"
|
||||
|
||||
cleanup() { rm -f "${DUMP%.gz}" "$DUMP" 2>/dev/null || true; }
|
||||
@@ -42,7 +45,7 @@ ssh "$SSH_TARGET" "cd /root/wordloop/backend && source venv/bin/activate && pyth
|
||||
from sqlalchemy import text
|
||||
from database import engine
|
||||
with engine.connect() as c:
|
||||
for t in ['users','words','dictionary_entries','user_settings','quiz_records']:
|
||||
for t in ['users','words','word_books','word_book_entries','dictionary_entries','quiz_records']:
|
||||
print(f\"{t}:\", c.execute(text(f'SELECT COUNT(*) FROM {t}')).scalar())
|
||||
PY"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user