v3: search engine, responsive header with compact user menu

This commit is contained in:
khannurien
2026-03-29 11:56:31 +00:00
parent f0f6472db6
commit cbb3505139
31 changed files with 1206 additions and 178 deletions

View File

@@ -9,26 +9,31 @@ import {
type RichContent,
type User,
} from "./interfaces.ts";
import {
ATTACHMENTS_DIR,
DB_PATH,
ORPHANED_ATTACHMENTS_RETENTION_HOURS,
UNUSED_INVITES_RETENTION_DAYS,
} from "../config.ts";
export const db = new DatabaseSync("api/sql/gerbeur.db");
export const db = new DatabaseSync(DB_PATH);
db.exec("PRAGMA foreign_keys = ON;");
// Purge expired unused invites on startup
db.prepare(
`DELETE FROM invites WHERE used_at IS NULL AND created_at < datetime('now', '-7 days');`,
`DELETE FROM invites WHERE used_at IS NULL AND created_at < datetime('now', '-${UNUSED_INVITES_RETENTION_DAYS} days');`,
).run();
// Prune orphaned attachments (uploaded but never linked to a resource) older than 1 hour
const orphanedAttachments = db.prepare(
`SELECT id FROM attachments WHERE resource_id IS NULL AND created_at < datetime('now', '-1 hour');`,
`SELECT id FROM attachments WHERE resource_id IS NULL AND created_at < datetime('now', '-${ORPHANED_ATTACHMENTS_RETENTION_HOURS} hour');`,
).all() as { id: string }[];
if (orphanedAttachments.length > 0) {
const { ATTACHMENTS_DIR } = await import("../lib/upload.ts");
for (const { id } of orphanedAttachments) {
await Deno.remove(`${ATTACHMENTS_DIR}/${id}`).catch(() => {});
}
db.prepare(
`DELETE FROM attachments WHERE resource_id IS NULL AND created_at < datetime('now', '-1 hour');`,
`DELETE FROM attachments WHERE resource_id IS NULL AND created_at < datetime('now', '-${ORPHANED_ATTACHMENTS_RETENTION_HOURS} hour');`,
).run();
}

View File

@@ -1,21 +1,9 @@
import { VALIDATION } from "../config.ts";
/**
* Backend
*/
// ── Validation constants (shared with frontend via src/config/api.ts) ──────────
export const VALIDATION = {
USERNAME_MIN: 1,
USERNAME_MAX: 32,
PASSWORD_MIN: 8,
PASSWORD_MAX: 128,
DUMP_TITLE_MAX: 200,
DUMP_COMMENT_MAX: 5000,
PLAYLIST_TITLE_MAX: 100,
PLAYLIST_DESCRIPTION_MAX: 2000,
COMMENT_BODY_MAX: 5000,
USER_DESCRIPTION_MAX: 2000,
} as const;
export interface RichContent {
type: string;
url: string;