chore: 初始提交 - 移除所有硬编码凭据,统一从环境变量读取
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
const express = require('express')
|
||||
const Database = require('better-sqlite3')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const dataDir = path.join(process.cwd(), 'data')
|
||||
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
|
||||
const dbPath = path.join(dataDir, 'dlt_draws.db')
|
||||
const db = new Database(dbPath)
|
||||
|
||||
// Initialize Tables
|
||||
db.exec(`CREATE TABLE IF NOT EXISTS draws (
|
||||
issue TEXT PRIMARY KEY,
|
||||
date TEXT,
|
||||
front TEXT,
|
||||
back TEXT,
|
||||
pool INTEGER DEFAULT 0,
|
||||
created_at TEXT DEFAULT (datetime('now'))
|
||||
);`)
|
||||
|
||||
// Auth Helpers
|
||||
const readCookieCfg = () => {
|
||||
const cfgPath = path.join(process.cwd(), 'public', 'tools', 'dlt_draws', 'assets', 'auth_config.json')
|
||||
let name = 'dlt_draws_gate'
|
||||
let maxAge = 90 * 24 * 3600 * 1000
|
||||
try {
|
||||
if (fs.existsSync(cfgPath)) {
|
||||
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'))
|
||||
if (cfg.cookieName) name = String(cfg.cookieName)
|
||||
const days = Number(cfg.max_age_days || 0)
|
||||
if (Number.isFinite(days) && days > 0) maxAge = Math.min(days, 365) * 24 * 3600 * 1000
|
||||
}
|
||||
} catch {}
|
||||
return { name, maxAge }
|
||||
}
|
||||
const parseCookie = (cookieStr) => {
|
||||
const out = {}
|
||||
String(cookieStr || '').split(/;\s*/).forEach(p => { const idx = p.indexOf('='); if (idx > 0) out[p.slice(0, idx)] = decodeURIComponent(p.slice(idx + 1)) })
|
||||
return out
|
||||
}
|
||||
|
||||
const checkAuth = (req, res, next) => {
|
||||
try {
|
||||
const cookies = parseCookie(req.headers.cookie || '')
|
||||
const { name, maxAge } = readCookieCfg()
|
||||
if (cookies[name] === '1' || cookies['nav_gate'] === '1') {
|
||||
res.cookie(name, '1', { httpOnly: true, sameSite: 'lax', maxAge })
|
||||
return next()
|
||||
}
|
||||
} catch {}
|
||||
return res.status(401).json({ error: 'unauthorized' })
|
||||
}
|
||||
|
||||
// Helpers
|
||||
const pad2 = (n) => String(n).padStart(2, '0')
|
||||
const arrToCsv = (arr) => (Array.isArray(arr) ? arr : String(arr).split(/[,,\s]+/).filter(Boolean))
|
||||
.map((x) => pad2(parseInt(x, 10)))
|
||||
.join(',')
|
||||
const csvToArr = (csv) => String(csv).split(/[,,\s]+/).filter(Boolean).map((x) => parseInt(x, 10))
|
||||
|
||||
const bindRoutes = (app) => {
|
||||
// Routes prefixed with /api/dlt
|
||||
app.get('/auth/handshake', (req, res) => {
|
||||
try {
|
||||
const cookies = parseCookie(req.headers.cookie || '')
|
||||
const { name } = readCookieCfg()
|
||||
if (cookies[name] === '1' || cookies['nav_gate'] === '1') return res.json({ ok: true })
|
||||
return res.status(401).json({ error: 'unauthorized' })
|
||||
} catch (e) {
|
||||
res.status(500).json({ ok: false, error: String(e.message || e) })
|
||||
}
|
||||
})
|
||||
app.get('/auth/token', (req, res) => {
|
||||
const cookies = parseCookie(req.headers.cookie || '')
|
||||
const { name } = readCookieCfg()
|
||||
if (cookies[name] === '1' || cookies['nav_gate'] === '1') return res.json({ ok: true })
|
||||
return res.status(401).json({ error: 'unauthorized' })
|
||||
})
|
||||
|
||||
app.get('/api/dlt/health', (req, res) => res.json({ status: 'ok' }))
|
||||
|
||||
app.get('/api/dlt/draws', checkAuth, (req, res) => {
|
||||
const limit = Math.max(0, parseInt(req.query.limit || '0', 10) || 0)
|
||||
const base = `SELECT issue, date, front, back, pool FROM draws ORDER BY CAST(issue AS INTEGER) ASC`
|
||||
const sql = limit ? `${base} LIMIT ?` : base
|
||||
try {
|
||||
const rows = db.prepare(sql).all(limit ? [limit] : [])
|
||||
res.json(rows.map((r) => ({
|
||||
issue: r.issue,
|
||||
date: r.date,
|
||||
front: csvToArr(r.front),
|
||||
back: csvToArr(r.back),
|
||||
pool: r.pool || 0
|
||||
})))
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/api/dlt/draws/merge', checkAuth, (req, res) => {
|
||||
const items = Array.isArray(req.body.rows) ? req.body.rows : []
|
||||
if (!items.length) return res.status(400).json({ error: 'rows empty' })
|
||||
let inserted = 0
|
||||
try {
|
||||
const insert = db.transaction((rows) => {
|
||||
let count = 0
|
||||
const stmt = db.prepare('INSERT OR IGNORE INTO draws(issue,date,front,back,pool) VALUES (?,?,?,?,?)')
|
||||
for (const it of rows) {
|
||||
const issue = String(it.issue || '').trim()
|
||||
const date = String(it.date || '').trim()
|
||||
const front = arrToCsv(it.front || it.frontArr || [])
|
||||
const back = arrToCsv(it.back || it.backArr || [])
|
||||
const pool = Number(it.pool || 0)
|
||||
if (!issue || csvToArr(front).length !== 5 || csvToArr(back).length !== 2) continue
|
||||
const info = stmt.run(issue, date, front, back, pool)
|
||||
count += info.changes
|
||||
}
|
||||
return count
|
||||
})
|
||||
inserted = insert(items)
|
||||
res.json({ inserted })
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/api/dlt/draws/clear', checkAuth, (req, res) => {
|
||||
try {
|
||||
db.prepare('DELETE FROM draws').run()
|
||||
res.json({ cleared: true })
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { bindRoutes }
|
||||
Reference in New Issue
Block a user