76e9774c65
Adds optional admin auth (enabled when DATABASE_URL is set) with session-cookie login, logout, requireAdmin middleware, login UI, and a skippable integration test (TEST_DATABASE_URL required). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
43 lines
1.1 KiB
JavaScript
43 lines
1.1 KiB
JavaScript
"use strict"
|
|
|
|
async function readJsonBody(req, maxBytes = 1_000_000) {
|
|
return new Promise((resolve, reject) => {
|
|
const chunks = []
|
|
let total = 0
|
|
req.on("data", (chunk) => {
|
|
total += chunk.length
|
|
if (total > maxBytes) {
|
|
req.destroy()
|
|
reject(Object.assign(new Error("request body too large"), { status: 413 }))
|
|
return
|
|
}
|
|
chunks.push(chunk)
|
|
})
|
|
req.on("end", () => {
|
|
try {
|
|
const raw = Buffer.concat(chunks).toString("utf-8")
|
|
resolve(raw ? JSON.parse(raw) : {})
|
|
} catch (err) {
|
|
reject(Object.assign(err, { status: 400 }))
|
|
}
|
|
})
|
|
req.on("error", reject)
|
|
})
|
|
}
|
|
|
|
function sendJson(res, status, body, headers = {}) {
|
|
const json = JSON.stringify(body)
|
|
res.writeHead(status, {
|
|
"Content-Type": "application/json; charset=utf-8",
|
|
"Cache-Control": "no-store",
|
|
...headers
|
|
})
|
|
res.end(json)
|
|
}
|
|
|
|
function openAiError(res, status, message, code) {
|
|
sendJson(res, status, { error: { message, type: code, code } })
|
|
}
|
|
|
|
module.exports = { readJsonBody, sendJson, openAiError }
|