Files
klarbild-oss/migrations/015_session_epoch.sql
T
tillandClaude Opus 5 e2481139a1
CI / check (push) Has been cancelled
Klarbild 1.0.0 — first public release
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
2026-09-15 14:52:41 +00:00

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;