CI / check (push) Has been cancelled
A self-hosted print studio: images at exact physical sizes, sheet layout with crop marks, tiling, 1:1 PDF. Everything else — image generation, delivery, share links, mail, Telegram — is an optional module that is off by default. This is the neutral public twin of a private tool. Compared to that original: - optional modules behind KLARBILD_MODULES; the default install is print-only - English interface with German as a full second language (1,038 strings); the English sentence is the translation key - no built-in accounts — first boot creates one admin from ADMIN_USERNAME / ADMIN_PASSWORD, or generates a password and logs it once - delivery and backup targets are generic SFTP/FTPS endpoints, off by default - English routes, English identifiers, English comments throughout - two compose files: print-only by default, everything in the full one - AGPL-3.0, CI (build, tests, type check, secret scan), CONTRIBUTING, SECURITY 124 tests green, type check clean, verified end to end against a live instance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
38 lines
2.0 KiB
SQL
38 lines
2.0 KiB
SQL
-- Make sessions revocable server-side ---------------------------------------
|
|
-- Until now the session cookie was only HMAC-signed: whoever had it once got
|
|
-- in with it indefinitely — even after the password was reset or the role
|
|
-- was taken away. The very step meant to rescue a hijacked account therefore
|
|
-- did not help.
|
|
--
|
|
-- `session_epoch` is incremented on every password change and every role
|
|
-- change. The cookie carries the value along; once it no longer matches, the
|
|
-- cookie is no longer valid. A counter instead of a timestamp, because it
|
|
-- knows no clock-skew inaccuracies.
|
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS session_epoch integer NOT NULL DEFAULT 0;
|
|
|
|
-- For rename assignment: `renames.ts` checks, for every finished image,
|
|
-- whether a name is already taken — without an index that was a full table scan.
|
|
CREATE INDEX IF NOT EXISTS items_filename_idx ON items(filename);
|
|
|
|
-- Cleanup: both tables grew without bound.
|
|
CREATE INDEX IF NOT EXISTS share_views_at_idx ON share_views(at);
|
|
CREATE INDEX IF NOT EXISTS password_resets_expires_idx ON password_resets(expires_at);
|
|
|
|
-- Counter for distinct visitors per share link. It used to be computed on every
|
|
-- load of the admin page via `count(DISTINCT ip_hash)` over share_views — that
|
|
-- got slow as the table grew, and made the page attackable, since anyone with
|
|
-- the link could generate an unlimited number of rows.
|
|
ALTER TABLE shares ADD COLUMN IF NOT EXISTS visitor integer NOT NULL DEFAULT 0;
|
|
|
|
-- Only one row per link, visitor and hour: without this limit, a recipient
|
|
-- (or an <img> tag on some page) could fill the table without bound and run
|
|
-- the disk out of space.
|
|
--
|
|
-- The hour value is supplied at write time instead of computed in the index:
|
|
-- `date_trunc` on a timestamptz depends on the timezone setting and is
|
|
-- therefore not immutable enough for an index.
|
|
ALTER TABLE share_views ADD COLUMN IF NOT EXISTS hour text;
|
|
CREATE UNIQUE INDEX IF NOT EXISTS share_views_once_per_hour_idx
|
|
ON share_views (share_id, kind, ip_hash, hour)
|
|
WHERE ip_hash IS NOT NULL AND hour IS NOT NULL;
|