Publish Container / build (push) Successful in 15m46s
- Atomic temp-file + rename for all JSON data writes (no truncated files on crash) - /api/bible-passage: rate limit, range caps (60 verses), bounded cache - Admin webhooks: https-only, block private/internal/Tailscale addresses (checked at registration and delivery), no redirects - Resend webhook token compare is timing-safe - DOMPurify on rendered study notes and broadcast preview - CI runs lint + tests before build - Permissions-Policy and HSTS (production) headers; JSON error handler for /api - Tests for URL safety and atomic writes Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
18 lines
704 B
JavaScript
18 lines
704 B
JavaScript
import { writeFile as fsWriteFile, rename, unlink } from 'node:fs/promises'
|
|
import { randomBytes } from 'node:crypto'
|
|
|
|
// Drop-in replacement for fs.promises.writeFile that never leaves a truncated
|
|
// file behind: the data goes to a sibling temp file which is then renamed over
|
|
// the target (atomic on the same filesystem). A crash mid-write leaves the
|
|
// previous version intact instead of a half-written JSON file.
|
|
export async function writeFile(file, data, options) {
|
|
const tmp = `${file}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`
|
|
try {
|
|
await fsWriteFile(tmp, data, options)
|
|
await rename(tmp, file)
|
|
} catch (err) {
|
|
await unlink(tmp).catch(() => {})
|
|
throw err
|
|
}
|
|
}
|