- Add is_blocked to core.users (002_core_tables.sql) - Add is_active to activity.sessions for soft deletes (005_activity_tables.sql) - Implement UserRepo: List, GetByID, GetByName, Create, Update, Delete - Implement MimeRepo: List, GetByID, GetByName - Implement SessionRepo: Create, GetByTokenHash, ListByUser, UpdateLastActivity, Delete, DeleteByUserID - Session deletes are soft (SET is_active = false); is_active is a SQL-only filter, not mapped to the domain type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
83 lines
3.4 KiB
SQL
83 lines
3.4 KiB
SQL
-- +goose Up
|
|
|
|
CREATE TABLE activity.action_types (
|
|
id smallint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
name varchar(64) NOT NULL,
|
|
|
|
CONSTRAINT uni__action_types__name UNIQUE (name)
|
|
);
|
|
|
|
CREATE TABLE activity.sessions (
|
|
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
token_hash text NOT NULL, -- hashed session token
|
|
user_id smallint NOT NULL REFERENCES core.users(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
user_agent varchar(256) NOT NULL,
|
|
started_at timestamptz NOT NULL DEFAULT statement_timestamp(),
|
|
expires_at timestamptz,
|
|
last_activity timestamptz NOT NULL DEFAULT statement_timestamp(),
|
|
is_active boolean NOT NULL DEFAULT true,
|
|
|
|
CONSTRAINT uni__sessions__token_hash UNIQUE (token_hash)
|
|
);
|
|
|
|
CREATE TABLE activity.file_views (
|
|
file_id uuid NOT NULL REFERENCES data.files(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
user_id smallint NOT NULL REFERENCES core.users(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
viewed_at timestamptz NOT NULL DEFAULT statement_timestamp(),
|
|
|
|
PRIMARY KEY (file_id, viewed_at, user_id)
|
|
);
|
|
|
|
CREATE TABLE activity.pool_views (
|
|
pool_id uuid NOT NULL REFERENCES data.pools(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
user_id smallint NOT NULL REFERENCES core.users(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
viewed_at timestamptz NOT NULL DEFAULT statement_timestamp(),
|
|
|
|
PRIMARY KEY (pool_id, viewed_at, user_id)
|
|
);
|
|
|
|
CREATE TABLE activity.tag_uses (
|
|
tag_id uuid NOT NULL REFERENCES data.tags(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
user_id smallint NOT NULL REFERENCES core.users(id)
|
|
ON UPDATE CASCADE ON DELETE CASCADE,
|
|
used_at timestamptz NOT NULL DEFAULT statement_timestamp(),
|
|
is_included boolean NOT NULL, -- true=included in filter, false=excluded
|
|
|
|
PRIMARY KEY (tag_id, used_at, user_id)
|
|
);
|
|
|
|
CREATE TABLE activity.audit_log (
|
|
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
user_id smallint NOT NULL REFERENCES core.users(id)
|
|
ON UPDATE CASCADE ON DELETE RESTRICT,
|
|
action_type_id smallint NOT NULL REFERENCES activity.action_types(id)
|
|
ON UPDATE CASCADE ON DELETE RESTRICT,
|
|
object_type_id smallint REFERENCES core.object_types(id)
|
|
ON UPDATE CASCADE ON DELETE RESTRICT,
|
|
object_id uuid,
|
|
details jsonb, -- action-specific payload
|
|
performed_at timestamptz NOT NULL DEFAULT statement_timestamp()
|
|
);
|
|
|
|
COMMENT ON TABLE activity.action_types IS 'Reference: types of auditable user actions';
|
|
COMMENT ON TABLE activity.sessions IS 'Active user sessions';
|
|
COMMENT ON TABLE activity.file_views IS 'File view history';
|
|
COMMENT ON TABLE activity.pool_views IS 'Pool view history';
|
|
COMMENT ON TABLE activity.tag_uses IS 'Tag usage in filters';
|
|
COMMENT ON TABLE activity.audit_log IS 'Unified audit trail for all user actions';
|
|
|
|
-- +goose Down
|
|
|
|
DROP TABLE IF EXISTS activity.audit_log;
|
|
DROP TABLE IF EXISTS activity.tag_uses;
|
|
DROP TABLE IF EXISTS activity.pool_views;
|
|
DROP TABLE IF EXISTS activity.file_views;
|
|
DROP TABLE IF EXISTS activity.sessions;
|
|
DROP TABLE IF EXISTS activity.action_types;
|