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
This commit is contained in:
164 files changed
+27190
No files matched your search
@@ -0,0 +1,37 @@
|
||||
-- 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;
|
||||
Reference in new issue
Block a user