Files

138 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 }