修改架构,移动端优先。

save
This commit is contained in:
cheney
2026-08-03 18:05:50 +08:00
committed by Cheney
parent 1c07b417ce
commit 08b3655a38
71 changed files with 2524 additions and 290 deletions
+19
View File
@@ -0,0 +1,19 @@
import { Router } from 'express'
import { authRequired } from '../middleware.js'
import { listArticles, getArticle } from '../articles.js'
const router = Router()
router.use(authRequired)
router.get('/articles', (_req, res) => {
res.json({ articles: listArticles() })
})
router.get('/articles/:slug', (req, res) => {
const article = getArticle(req.params.slug)
if (!article) return res.status(404).json({ error: '文章不存在' })
res.json({ article })
})
export default router
+46
View File
@@ -0,0 +1,46 @@
import { Router } from 'express'
import crypto from 'node:crypto'
import { prisma } from '../db.js'
import { authRequired, signToken } from '../middleware.js'
const router = Router()
// 校验密码: 接受 scrypt$salt$hash 格式
function verifyPassword(plain, stored) {
if (!stored || !stored.startsWith('scrypt$')) return false
const parts = stored.split('$')
if (parts.length !== 3) return false
const [, salt, hash] = parts
const computed = crypto.scryptSync(plain, salt, 64).toString('hex')
// 常数时间比较
const a = Buffer.from(computed, 'hex')
const b = Buffer.from(hash, 'hex')
if (a.length !== b.length) return false
return crypto.timingSafeEqual(a, b)
}
router.post('/login', async (req, res) => {
const { email, password } = req.body || {}
if (!email || !password) return res.status(400).json({ error: '邮箱或密码不能为空' })
const user = await prisma.user.findUnique({ where: { email: String(email).toLowerCase() } })
if (!user) return res.status(401).json({ error: '邮箱或密码错误' })
if (!verifyPassword(String(password), user.password)) {
return res.status(401).json({ error: '邮箱或密码错误' })
}
const payload = { sub: user.id, email: user.email, name: user.name }
const token = signToken(payload)
res.json({ token, user: { id: user.id, name: user.name, email: user.email, createdAt: user.createdAt } })
})
router.get('/me', authRequired, async (req, res) => {
const user = await prisma.user.findUnique({ where: { id: req.user.sub } })
if (!user) return res.status(401).json({ error: '用户不存在' })
res.json({ user: { id: user.id, name: user.name, email: user.email, createdAt: user.createdAt } })
})
router.post('/logout', (_req, res) => {
// JWT 无状态, 前端清 token 即可; 这里返回 200 让前端不用特判
res.json({ ok: true })
})
export default router
+30
View File
@@ -0,0 +1,30 @@
import { Router } from 'express'
import { prisma } from '../db.js'
import { authRequired } from '../middleware.js'
const router = Router()
router.use(authRequired)
router.get('/indicators', async (_req, res, next) => {
try {
const rows = await prisma.indicator.findMany({ orderBy: [{ groupName: 'asc' }, { name: 'asc' }] })
res.json({ indicators: rows })
} catch (e) { next(e) }
})
router.get('/strategies', async (_req, res, next) => {
try {
const rows = await prisma.strategy.findMany({ orderBy: { name: 'asc' } })
res.json({ strategies: rows })
} catch (e) { next(e) }
})
router.get('/sow', async (_req, res, next) => {
try {
const rows = await prisma.econ_SowInventory.findMany({ orderBy: { month: 'asc' } })
res.json({ rows })
} catch (e) { next(e) }
})
export default router