Files
Wardrobe/server/index.js
T
jif2.zhang c1b630a7ef
Android Build & Publish / build-android (push) Failing after 6m55s
Docker Build and Push / build-image (push) Successful in 2m38s
refactor: 数据层迁往服务端 (Express+DuckDB), APP 变瘦客户端只留缓存
- server: 新增 DuckDB 数据层 (db.js) 与鉴权 (auth.js: 验证码+token), 挂载 /api 业务路由
  (衣物/搭配/日历/统计/MCP), 错误统一中间件
- 前端 src/api/index.ts 全部业务调用改为 HTTP (Bearer token), 客户端不再持有数据库
- 删除 Rust 数据层 (db.rs/auth.rs/mcp.rs/models.rs), Cargo.toml 裁剪 duckdb/chrono 等依赖
- Dockerfile: 基镜像 alpine→slim (@duckdb/node-api glibc 预编译), DB 落 /app/data 卷
- docker-build.yml: 移除 prune --volumes, 挂具名卷 wardrobe-data 持久化
- Landing/Profile 文案改为云端同步口径; 退出登录调服务端会话销毁
2026-09-15 10:53:58 +08:00

62 lines
2.2 KiB
JavaScript

import 'dotenv/config'
import path from 'node:path'
import fs from 'node:fs'
import express from 'express'
import cors from 'cors'
import otaRouter from './routes/ota.js'
import appRouter from './routes/app.js'
import { initDb } from './db.js'
// 默认 3002: 与 iboard (3001) 同机部署时不冲突
const PORT = Number(process.env.PORT) || 3002
const DIST_DIR = path.join(process.cwd(), 'dist')
const HAS_DIST = fs.existsSync(DIST_DIR)
const app = express()
app.use(cors())
app.use(express.json())
// 健康检查 (容器活性探针统一用 /healthz, 保留 /api/health 兼容旧引用)
app.get('/healthz', (_req, res) => res.json({ ok: true, ts: Date.now() }))
app.get('/api/health', (_req, res) => res.json({ ok: true, ts: Date.now() }))
// 业务接口 (客户端数据读写, 登录鉴权)
app.use('/api', appRouter)
// OTA 接口公开 (检查/下载更新不需要登录), 必须挂载在最后以防 /api 路由冲突
app.use('/api', otaRouter)
// 静态资源 (Vite build 产物)
if (HAS_DIST) {
app.use(express.static(DIST_DIR, { index: false, maxAge: '1h', etag: true }))
// SPA fallback: 非 /api/* 的请求一律返回 index.html, 由前端路由处理
// 必须 no-cache: 否则浏览器/代理缓存旧 index.html, 引用的仍是旧哈希资源,
// 与部署的 dist 产生"版本不一致"(web 用户刷新也拿不到新页面)
app.get(/^\/(?!api\/).*/, (_req, res, next) => {
const indexHtml = path.join(DIST_DIR, 'index.html')
if (!fs.existsSync(indexHtml)) return next()
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
res.sendFile(indexHtml)
})
}
// 统一错误处理
app.use((err, _req, res, _next) => {
console.error('[api error]', err)
const code = err && err.status ? Number(err.status) : 500
if (code >= 500) console.error(err.stack || err)
res.status(code).json({ error: (err && err.message) || '服务器内部错误' })
})
initDb()
.then(() => {
app.listen(PORT, () => {
console.log(`[wardrobe-api] listening on http://127.0.0.1:${PORT}`)
console.log(`[wardrobe-api] static dist: ${HAS_DIST ? DIST_DIR : '(not built, API only)'}`)
})
})
.catch((e) => {
console.error('[db] failed to init DuckDB:', e)
process.exit(1)
})