feat(db): retention schema for account deletion (migration 00007)
Additive/expand-contract: new append-only retained_identities table (the legal dossier of every detached credential, keyed by account_id, TTL'd by detached_at) plus nullable accounts columns last_login_at/last_login_ip (cold-load stamp) and deleted_at/deleted_display_name (tombstone + retained real name). Regenerates the go-jet models for accounts + retained_identities only.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
-- Account deletion as legal retention (not erasure). Two additive pieces.
|
||||
--
|
||||
-- retained_identities is an append-only journal of every credential detached from an
|
||||
-- account — on unlink, email change, or account deletion (reason). It preserves the legal
|
||||
-- dossier (which email/vk/tg was linked, and when) while the live identities row is
|
||||
-- removed, so the (kind, external_id) frees for a new account to reuse. No unique
|
||||
-- constraint and no kind CHECK: the same credential may recur across accounts and events,
|
||||
-- and the log stays robust to future identity kinds. detached_at drives the retention TTL.
|
||||
--
|
||||
-- accounts gains: last_login_at / last_login_ip (stamped on a cold app-load, throttled),
|
||||
-- deleted_at (the tombstone marker), and deleted_display_name (the real name retained for
|
||||
-- the admin dossier after the live display_name is scrubbed to the anonymised label).
|
||||
--
|
||||
-- Expand-contract: everything is additive (a new table plus nullable columns), so a
|
||||
-- backend image rollback stays DB-safe — older code simply ignores them. The accounts
|
||||
-- table shape changes, so its generated go-jet model is regenerated.
|
||||
|
||||
-- +goose Up
|
||||
CREATE TABLE backend.retained_identities (
|
||||
retained_id uuid NOT NULL,
|
||||
account_id uuid NOT NULL,
|
||||
kind text NOT NULL,
|
||||
external_id text NOT NULL,
|
||||
confirmed boolean DEFAULT false NOT NULL,
|
||||
linked_at timestamp with time zone NOT NULL,
|
||||
detached_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
reason text NOT NULL,
|
||||
CONSTRAINT retained_identities_pkey PRIMARY KEY (retained_id),
|
||||
CONSTRAINT retained_identities_reason_chk
|
||||
CHECK ((reason = ANY (ARRAY['unlink'::text, 'change'::text, 'delete'::text])))
|
||||
);
|
||||
CREATE INDEX retained_identities_account_id_idx ON backend.retained_identities (account_id);
|
||||
CREATE INDEX retained_identities_detached_at_idx ON backend.retained_identities (detached_at);
|
||||
|
||||
ALTER TABLE backend.accounts
|
||||
ADD COLUMN last_login_at timestamp with time zone,
|
||||
ADD COLUMN last_login_ip text,
|
||||
ADD COLUMN deleted_at timestamp with time zone,
|
||||
ADD COLUMN deleted_display_name text;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE backend.accounts
|
||||
DROP COLUMN last_login_at,
|
||||
DROP COLUMN last_login_ip,
|
||||
DROP COLUMN deleted_at,
|
||||
DROP COLUMN deleted_display_name;
|
||||
DROP TABLE backend.retained_identities;
|
||||
Reference in New Issue
Block a user