67a6cbcd9e
Move media favorites from client state to PostgreSQL with server-side search and pagination. - Store favorites keyed by (tenant, user, group, resource) so a resource can belong to multiple groups; the 500-resource cap counts distinct resources, not memberships. - Add favorite-group CRUD and group/global removal routes, with silent cleanup of delisted or invisible resources on read. - Serve favorite resource details server-side, 10 per page, with name/ID search; favorites-page removal is group-scoped, resources-page removal is global. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
2.4 KiB
SQL
58 lines
2.4 KiB
SQL
CREATE TABLE IF NOT EXISTS media_supply_favorite_owners (
|
|
tenant_id BIGINT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
|
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
revision BIGINT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (tenant_id, user_id),
|
|
CONSTRAINT chk_media_supply_favorite_owner_revision CHECK (revision >= 0)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS media_supply_favorite_groups (
|
|
tenant_id BIGINT NOT NULL,
|
|
user_id BIGINT NOT NULL,
|
|
group_key VARCHAR(64) NOT NULL,
|
|
name VARCHAR(64) NOT NULL,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (tenant_id, user_id, group_key),
|
|
CONSTRAINT fk_media_supply_favorite_group_owner
|
|
FOREIGN KEY (tenant_id, user_id)
|
|
REFERENCES media_supply_favorite_owners (tenant_id, user_id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT chk_media_supply_favorite_group_key_not_blank CHECK (BTRIM(group_key) <> ''),
|
|
CONSTRAINT chk_media_supply_favorite_group_name_not_blank CHECK (BTRIM(name) <> '')
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS uk_media_supply_favorite_group_name
|
|
ON media_supply_favorite_groups (tenant_id, user_id, LOWER(name));
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_media_supply_favorite_group_owner_order
|
|
ON media_supply_favorite_groups (tenant_id, user_id, sort_order, created_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS media_supply_favorite_resources (
|
|
tenant_id BIGINT NOT NULL,
|
|
user_id BIGINT NOT NULL,
|
|
group_key VARCHAR(64) NOT NULL,
|
|
resource_id BIGINT NOT NULL REFERENCES supplier_media_resources(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
PRIMARY KEY (tenant_id, user_id, group_key, resource_id),
|
|
CONSTRAINT fk_media_supply_favorite_resource_group
|
|
FOREIGN KEY (tenant_id, user_id, group_key)
|
|
REFERENCES media_supply_favorite_groups (tenant_id, user_id, group_key)
|
|
ON DELETE CASCADE
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_media_supply_favorite_group_page
|
|
ON media_supply_favorite_resources (
|
|
tenant_id,
|
|
user_id,
|
|
group_key,
|
|
created_at DESC,
|
|
resource_id DESC
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_media_supply_favorite_resource_lookup
|
|
ON media_supply_favorite_resources (resource_id, tenant_id, user_id);
|