// The transport half of the admin surface — kept apart from admin.js so that file stays pure.
//
// ⛔ SMALL ON PURPOSE. An operator page posts two fields; pulling in a form parser (or reusing the
// multipart one favela-surface needs for pictures) would be more moving parts than the thing it serves.
// Bodies are bounded because a listener that buffers whatever it is handed is a memory bug with a form
// in front of it, even on loopback.
const MAX_BODY = 64 * 1024
export function parseForm (req) {
return new Promise((resolve) => {
if (req.method !== 'POST') return resolve({})
let size = 0
const chunks = []
req.on('data', (c) => {
size += c.length
// ⚠ Stop COLLECTING but keep draining: destroying the stream mid-body makes the browser show a
// transport error instead of the page, for a form that was merely too big.
if (size <= MAX_BODY) chunks.push(c)
})
req.on('end', () => {
const body = Buffer.concat(chunks).toString('utf8')
const out = {}
for (const pair of body.split('&')) {
if (!pair) continue
const i = pair.indexOf('=')
const k = i === -1 ? pair : pair.slice(0, i)
const v = i === -1 ? '' : pair.slice(i + 1)
try { out[decodeURIComponent(k.replace(/\+/g, ' '))] = decodeURIComponent(v.replace(/\+/g, ' ')) } catch { /* skip a malformed pair */ }
}
resolve(out)
})
req.on('error', () => resolve({}))
})
}
export default { parseForm }