Files
2026-09-26 22:46:15 +08:00

55 lines
1.8 KiB
JavaScript

// Storage 服务端入口: 业务 API + 图片 API + 静态托管 (SPA)
// 端口由部署环境决定 (PORT env), 本地缺省 3002。见 部署环境.md
import 'dotenv/config'
import path from 'node:path'
import fs from 'node:fs'
import express from 'express'
import cors from 'cors'
import appRouter from './routes/app.js'
import imagesRouter from './routes/images.js'
import { initDb } from './db.js'
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({ limit: '2mb' }))
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)
// 图片接口 (签名 URL 鉴权), 需在 SPA fallback 之前挂载
app.use('/api', imagesRouter)
if (HAS_DIST) {
app.use(express.static(DIST_DIR, { index: false, maxAge: '1h', etag: true }))
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(`[storage-api] listening on http://127.0.0.1:${PORT}`)
console.log(`[storage-api] static dist: ${HAS_DIST ? DIST_DIR : '(not built, API only)'}`)
})
})
.catch((e) => {
console.error('[db] failed to init DuckDB:', e)
process.exit(1)
})