Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 48s
Forgejo Docker Build / Root app tests (push) Successful in 51s
Forgejo Android APK / Build signed APK (push) Successful in 2m16s
Forgejo Docker Build / Build Docker image (push) Successful in 20s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
The image job key was the request-body hash plus the figure's position — slide index for a deck, reply index for a document. Two generations from the same form produce the same body hash, so figure 4 of the second generation collided with figure 4 of the first on the unique (owner, workflow, idempotency_key). The constraint handed back the existing job, and the new deck displayed the old deck's artwork. The decks are not even the same length, so the reused picture could land on a slide about something else entirely. Keyed on what is being drawn now: the body hash stays, so submitting the identical request twice still dedupes rather than billing twice, and a hash of the prompt (plus layout and shape) is what makes two different pictures two different jobs. Same fix in deckBuild and resourceImages. Also split fileLog out of logger. logger requires the database at module load, so importing it to record a diagnostic pulls in a connection pool — wrong on its own terms, and it hung the whole test suite when imageTool started logging its refusals: a unit test that never touches a database inherited an open pool handle and never exited. logger.file now delegates to fileLog, so there is still one implementation of where a line goes and how it is redacted. With that in place, every image-tool refusal is recorded durably. There are five of them, they want five different fixes, and until now none of them left any trace once the container was replaced. Verified against a mutation: restoring the index-based key fails two of the four collision tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
38 lines
1.7 KiB
JavaScript
38 lines
1.7 KiB
JavaScript
// ============================================================
|
|
// FILE LOG
|
|
// ============================================================
|
|
// Appending a line to the dated log file, and nothing else.
|
|
//
|
|
// Split out of logger.js because logger requires the database at module load,
|
|
// so importing it just to record a diagnostic pulls in a connection pool. That
|
|
// is wrong on its own terms — a note about what happened should not need a
|
|
// database — and it hung the test suite: a unit test that exercised a code path
|
|
// containing a log call inherited an open pool handle and never exited.
|
|
//
|
|
// fs and the redactor only. logger.file delegates here, so there is one
|
|
// implementation of where a line goes and how it is redacted.
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var { redact } = require('./redact');
|
|
|
|
var LOG_DIR = path.join(__dirname, '../../data/logs');
|
|
|
|
function write(level, message, data) {
|
|
try {
|
|
if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
var now = new Date();
|
|
var file = path.join(LOG_DIR, now.toISOString().split('T')[0] + '.log');
|
|
// Defensive redaction: both message and data go through redact() so PHI
|
|
// patterns cannot reach the file if a caller passes a request body, a
|
|
// clinical string, or a stack trace containing transcript text.
|
|
var line = '[' + now.toISOString() + '] [' + level.toUpperCase() + '] ' +
|
|
redact(String(message == null ? '' : message));
|
|
if (data != null) {
|
|
line += ' | ' + redact(typeof data === 'string' ? data : JSON.stringify(data));
|
|
}
|
|
fs.appendFileSync(file, line + '\n');
|
|
if (level === 'error') console.error(line);
|
|
} catch (e) { /* a diagnostic is never worth failing the caller for */ }
|
|
}
|
|
|
|
module.exports = { write, LOG_DIR };
|