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' // 默认 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()) // 健康检查 app.get('/api/health', (_req, res) => res.json({ ok: true, ts: Date.now() })) // OTA 接口公开 (检查/下载更新不需要登录), 必须挂载在最前面 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, 由前端路由处理 app.get(/^\/(?!api\/).*/, (_req, res, next) => { const indexHtml = path.join(DIST_DIR, 'index.html') if (!fs.existsSync(indexHtml)) return next() res.sendFile(indexHtml) }) } // 统一错误处理 app.use((err, _req, res, _next) => { console.error('[api error]', err) res.status(500).json({ error: err.message || '服务器内部错误' }) }) 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)'}`) })