DISCORD_BUG_REPORT_WEBHOOK is gone, with sendToDiscord and its helpers. Reports still land in DATA_PATH/bug-reports — the disk write always happened first and the webhook was only a ping about it, so nothing about the report is lost. It was a personal notification channel living in deployment config, on a platform whose owner is the only person who files reports. It was also never in .env.example: the setup script wrote a variable nothing documented, which is the same drift as PORT, in the other direction. Note DISCORD_WEBHOOK_URL is a DIFFERENT variable — the notify sidecar's own channel — and is untouched. Then a parity sweep of setup / .env.example / what the code reads, which turned up two leftovers from earlier today: HOME_DIR was still read in six files, each with its own `?? homedir()` fallback. Dead since nothing sets it, but a dead read is worse than none — it reads as a supported override. They take homedir() directly now. user-instance.ts gets a comment on why its line stays where it is: it sits above `process.env.HOME = homeDir`, and homedir() reads $HOME, so a read moved below that assignment would return whichever member was last spawned into. Two of the six had fallback chains ending in process.cwd() and '' — the second would have silently disabled whatever consumed it rather than failing. VAULTWARDEN_URL was uncommented in .env.example among the variables setup writes, though it is a plugin variable setup has never written. Commented out with the other plugin entries. The three files now agree: setup writes PORT, BROWSER_RELAY_PORT and POSTGRES_URL; .env.example lists those plus JWT_SECRET and VAULT_STORE_KEY, which are required by code and deliberately unwritten until the secret store lands. Not typechecked (empty node_modules, frozen installs). Every changed file parses; the setup section was run and writes three variables. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
import { createRouter } from '../../create-router';
|
|
import { mkdir } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { DATA_PATH } from '@@/data-path';
|
|
|
|
// Bug reports land on disk and nowhere else.
|
|
//
|
|
// There was a Discord webhook here until 2026-08-13, behind DISCORD_BUG_REPORT_WEBHOOK. It was a
|
|
// personal notification channel living in deployment config, on a self-hosted platform whose owner is
|
|
// the only person filing reports — and it was never in .env.example, so the setup script wrote a
|
|
// variable nothing documented.
|
|
//
|
|
// Nothing is lost from the report itself: the disk write below always happened first, and the webhook
|
|
// was only a ping about it.
|
|
|
|
export const bugReportRouter = createRouter();
|
|
|
|
bugReportRouter.post('/', async (ctx) => {
|
|
const user = ctx.get('user');
|
|
const body = ctx.get('body') as Record<string, unknown>;
|
|
|
|
const description = body.description as string | undefined;
|
|
const contextRaw = body.context as string | undefined;
|
|
const screenshot = body.screenshot as File | undefined;
|
|
|
|
if (!description?.trim()) {
|
|
return ctx.text('Description is required', 400);
|
|
}
|
|
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
const uuid = crypto.randomUUID().slice(0, 8);
|
|
const dirName = `${timestamp}-${uuid}`;
|
|
const reportDir = join(DATA_PATH, 'bug-reports', dirName);
|
|
|
|
await mkdir(reportDir, { recursive: true });
|
|
|
|
const context = contextRaw ? JSON.parse(contextRaw) : null;
|
|
const report = {
|
|
description: description.trim(),
|
|
context,
|
|
reporter: { id: user.id, email: user.email, name: user.name },
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
|
|
await Bun.write(join(reportDir, 'report.json'), JSON.stringify(report, null, 2));
|
|
|
|
if (screenshot instanceof File) {
|
|
await Bun.write(join(reportDir, 'screenshot.png'), Buffer.from(await screenshot.arrayBuffer()));
|
|
}
|
|
|
|
return ctx.json({ ok: true, id: dirName });
|
|
});
|