feat: add community publishing and news syndication
This commit is contained in:
+225
-7
@@ -138,14 +138,210 @@ CREATE INDEX IF NOT EXISTS oversight_notification_queue_due_idx ON oversight_not
|
||||
CREATE INDEX IF NOT EXISTS oversight_notification_queue_user_category_idx ON oversight_notification_queue(user_id, oversight_category, delivered_at);
|
||||
CREATE INDEX IF NOT EXISTS avatar_reviews_status_submitted_idx ON avatar_reviews(status, submitted_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS avatar_reviews_user_submitted_idx ON avatar_reviews(user_id, submitted_at DESC);
|
||||
CREATE TABLE IF NOT EXISTS builds (id TEXT PRIMARY KEY, owner_id TEXT NOT NULL REFERENCES users(id), title TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, visibility TEXT NOT NULL DEFAULT 'private', summary TEXT, guide_body TEXT, source_build_id TEXT REFERENCES builds(id), loadout_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS builds (id TEXT PRIMARY KEY, owner_id TEXT NOT NULL REFERENCES users(id), title TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, visibility TEXT NOT NULL DEFAULT 'private' CHECK(visibility IN ('private','unlisted','public')), source_build_id TEXT REFERENCES builds(id), loadout_json TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
|
||||
CREATE INDEX IF NOT EXISTS builds_visibility_updated_idx ON builds(visibility, updated_at DESC);
|
||||
CREATE TABLE IF NOT EXISTS build_items (id TEXT PRIMARY KEY, build_id TEXT NOT NULL REFERENCES builds(id) ON DELETE CASCADE, slot_id TEXT NOT NULL, item_definition_id TEXT NOT NULL, configuration_json TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS ratings (user_id TEXT NOT NULL REFERENCES users(id), build_id TEXT NOT NULL REFERENCES builds(id) ON DELETE CASCADE, value INTEGER NOT NULL CHECK(value BETWEEN 1 AND 5), PRIMARY KEY(user_id, build_id));
|
||||
CREATE TABLE IF NOT EXISTS articles (
|
||||
id TEXT PRIMARY KEY,
|
||||
kind TEXT NOT NULL CHECK(kind IN ('guide','news')),
|
||||
owner_id TEXT NOT NULL REFERENCES users(id),
|
||||
slug TEXT NOT NULL,
|
||||
visibility TEXT NOT NULL DEFAULT 'private' CHECK(visibility IN ('private','unlisted','public')),
|
||||
published_revision_id TEXT REFERENCES article_revisions(id),
|
||||
pending_revision_id TEXT REFERENCES article_revisions(id),
|
||||
moderation_hold INTEGER NOT NULL DEFAULT 0 CHECK(moderation_hold IN (0,1)),
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
published_at TEXT,
|
||||
deleted_at TEXT,
|
||||
deleted_by TEXT REFERENCES users(id),
|
||||
UNIQUE(kind,slug),
|
||||
CHECK(published_revision_id IS NOT NULL OR visibility='private'),
|
||||
CHECK(deleted_at IS NULL OR visibility='private')
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS articles_owner_kind_updated_idx ON articles(owner_id,kind,updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS articles_public_kind_published_idx ON articles(kind,published_at DESC)
|
||||
WHERE visibility='public' AND deleted_at IS NULL AND published_revision_id IS NOT NULL;
|
||||
CREATE TABLE IF NOT EXISTS article_drafts (
|
||||
article_id TEXT PRIMARY KEY REFERENCES articles(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
deck TEXT NOT NULL DEFAULT '',
|
||||
document_json TEXT NOT NULL CHECK(json_valid(document_json)),
|
||||
tags_json TEXT NOT NULL DEFAULT '[]' CHECK(json_valid(tags_json)),
|
||||
channel TEXT,
|
||||
cover_image_id TEXT REFERENCES managed_images(id) ON DELETE SET NULL,
|
||||
cover_alt TEXT NOT NULL DEFAULT '',
|
||||
cover_caption TEXT NOT NULL DEFAULT '',
|
||||
version INTEGER NOT NULL DEFAULT 1 CHECK(version>=1),
|
||||
updated_by TEXT NOT NULL REFERENCES users(id),
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS article_revisions (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL REFERENCES articles(id),
|
||||
revision_number INTEGER NOT NULL CHECK(revision_number>=1),
|
||||
title TEXT NOT NULL CHECK(length(trim(title)) BETWEEN 2 AND 140),
|
||||
deck TEXT NOT NULL DEFAULT '',
|
||||
document_json TEXT NOT NULL CHECK(json_valid(document_json)),
|
||||
plain_text TEXT NOT NULL,
|
||||
channel TEXT,
|
||||
cover_image_id TEXT REFERENCES managed_images(id) ON DELETE SET NULL,
|
||||
cover_alt TEXT NOT NULL DEFAULT '',
|
||||
cover_caption TEXT NOT NULL DEFAULT '',
|
||||
snapshot_hash TEXT NOT NULL CHECK(length(snapshot_hash)=64),
|
||||
created_from_draft_version INTEGER NOT NULL CHECK(created_from_draft_version>=1),
|
||||
created_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TEXT NOT NULL,
|
||||
UNIQUE(article_id,revision_number)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS article_revisions_article_created_idx ON article_revisions(article_id,created_at DESC);
|
||||
CREATE TABLE IF NOT EXISTS article_revision_build_embeds (
|
||||
revision_id TEXT NOT NULL REFERENCES article_revisions(id) ON DELETE CASCADE,
|
||||
embed_key TEXT NOT NULL,
|
||||
ordinal INTEGER NOT NULL CHECK(ordinal>=0),
|
||||
build_id TEXT NOT NULL REFERENCES builds(id),
|
||||
owner_shared INTEGER NOT NULL DEFAULT 0 CHECK(owner_shared IN (0,1)),
|
||||
build_snapshot_json TEXT NOT NULL CHECK(json_valid(build_snapshot_json)),
|
||||
PRIMARY KEY(revision_id,embed_key),
|
||||
UNIQUE(revision_id,ordinal)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS article_revision_build_embeds_build_idx ON article_revision_build_embeds(build_id,revision_id);
|
||||
CREATE TABLE IF NOT EXISTS article_media (
|
||||
article_id TEXT NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
image_id TEXT NOT NULL UNIQUE REFERENCES managed_images(id) ON DELETE CASCADE,
|
||||
uploaded_by TEXT NOT NULL REFERENCES users(id),
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY(article_id,image_id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS article_tags (
|
||||
id TEXT PRIMARY KEY,
|
||||
group_name TEXT NOT NULL CHECK(group_name IN ('topic','activity','difficulty','specialization','focus','weapon','news_channel')),
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
source_type TEXT,
|
||||
source_id TEXT,
|
||||
active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1)),
|
||||
UNIQUE(source_type,source_id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS article_revision_tags (
|
||||
revision_id TEXT NOT NULL REFERENCES article_revisions(id) ON DELETE CASCADE,
|
||||
tag_id TEXT NOT NULL REFERENCES article_tags(id),
|
||||
ordinal INTEGER NOT NULL CHECK(ordinal>=0),
|
||||
PRIMARY KEY(revision_id,tag_id),
|
||||
UNIQUE(revision_id,ordinal)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS article_revision_tags_tag_idx ON article_revision_tags(tag_id,revision_id);
|
||||
CREATE TABLE IF NOT EXISTS article_publication_requests (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL REFERENCES articles(id),
|
||||
revision_id TEXT NOT NULL REFERENCES article_revisions(id),
|
||||
base_published_revision_id TEXT REFERENCES article_revisions(id),
|
||||
requested_visibility TEXT NOT NULL CHECK(requested_visibility IN ('unlisted','public')),
|
||||
status TEXT NOT NULL CHECK(status IN ('scanning','pending_review','approved','rejected','superseded')),
|
||||
scan_id TEXT UNIQUE REFERENCES content_moderation_scans(id),
|
||||
requested_by TEXT NOT NULL REFERENCES users(id),
|
||||
requested_at TEXT NOT NULL,
|
||||
decided_by TEXT REFERENCES users(id),
|
||||
decided_at TEXT,
|
||||
decision_reason TEXT
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS article_publication_requests_active_idx ON article_publication_requests(article_id)
|
||||
WHERE status IN ('scanning','pending_review');
|
||||
CREATE TABLE IF NOT EXISTS guide_curations (
|
||||
article_id TEXT PRIMARY KEY REFERENCES articles(id) ON DELETE CASCADE,
|
||||
pinned_order INTEGER CHECK(pinned_order IS NULL OR pinned_order>=0),
|
||||
featured INTEGER NOT NULL DEFAULT 0 CHECK(featured IN (0,1)),
|
||||
recommended INTEGER NOT NULL DEFAULT 0 CHECK(recommended IN (0,1)),
|
||||
curated_revision_id TEXT NOT NULL REFERENCES article_revisions(id),
|
||||
curated_by TEXT NOT NULL REFERENCES users(id),
|
||||
curated_at TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS guide_curations_discovery_idx ON guide_curations(pinned_order,featured,recommended,curated_at DESC);
|
||||
CREATE TABLE IF NOT EXISTS anonymous_visitors (
|
||||
token_hash TEXT PRIMARY KEY CHECK(length(token_hash)=64),
|
||||
created_at TEXT NOT NULL,
|
||||
last_seen_at TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS anonymous_activity_limits (
|
||||
identity_hash TEXT NOT NULL,
|
||||
action_type TEXT NOT NULL CHECK(action_type IN ('guide_rating','guide_report')),
|
||||
window_started_at TEXT NOT NULL,
|
||||
action_count INTEGER NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(identity_hash,action_type)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS guide_ratings (
|
||||
id TEXT PRIMARY KEY,
|
||||
article_id TEXT NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
|
||||
revision_id TEXT NOT NULL REFERENCES article_revisions(id),
|
||||
rater_user_id TEXT REFERENCES users(id),
|
||||
anonymous_token_hash TEXT REFERENCES anonymous_visitors(token_hash),
|
||||
value INTEGER NOT NULL CHECK(value BETWEEN 1 AND 5),
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
CHECK((rater_user_id IS NOT NULL AND anonymous_token_hash IS NULL) OR (rater_user_id IS NULL AND anonymous_token_hash IS NOT NULL))
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS guide_ratings_user_idx ON guide_ratings(article_id,rater_user_id) WHERE rater_user_id IS NOT NULL;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS guide_ratings_anonymous_idx ON guide_ratings(article_id,anonymous_token_hash) WHERE anonymous_token_hash IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS guide_ratings_article_idx ON guide_ratings(article_id,value);
|
||||
CREATE TABLE IF NOT EXISTS article_search (
|
||||
article_id TEXT PRIMARY KEY REFERENCES articles(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
deck TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
tags TEXT NOT NULL
|
||||
);
|
||||
INSERT OR IGNORE INTO users(id,username,role,created_at) VALUES(
|
||||
'7e2a8a20-9bbd-4f1e-9124-a3fb71f7de55','ubisoft-news-service','user','2026-08-01T00:00:00.000Z'
|
||||
);
|
||||
INSERT OR IGNORE INTO user_profiles(user_id,display_name,is_public,updated_at) VALUES(
|
||||
'7e2a8a20-9bbd-4f1e-9124-a3fb71f7de55','Ubisoft Official News',0,'2026-08-01T00:00:00.000Z'
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS external_news_sources (
|
||||
article_id TEXT PRIMARY KEY REFERENCES articles(id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL CHECK(provider IN ('ubisoft')),
|
||||
external_id TEXT NOT NULL,
|
||||
canonical_url TEXT NOT NULL,
|
||||
source_published_at TEXT NOT NULL,
|
||||
content_hash TEXT NOT NULL CHECK(length(content_hash)=64),
|
||||
first_seen_at TEXT NOT NULL,
|
||||
last_seen_at TEXT NOT NULL,
|
||||
last_synced_at TEXT NOT NULL,
|
||||
UNIQUE(provider,external_id)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS external_news_sources_last_seen_idx
|
||||
ON external_news_sources(provider,last_seen_at DESC);
|
||||
CREATE TABLE IF NOT EXISTS external_news_sync_state (
|
||||
provider TEXT PRIMARY KEY CHECK(provider IN ('ubisoft')),
|
||||
last_attempt_at TEXT,
|
||||
last_success_at TEXT,
|
||||
next_check_at TEXT NOT NULL,
|
||||
consecutive_failures INTEGER NOT NULL DEFAULT 0 CHECK(consecutive_failures>=0),
|
||||
last_error TEXT
|
||||
);
|
||||
INSERT OR IGNORE INTO article_tags(id,group_name,name,slug,source_type,source_id,active) VALUES
|
||||
('guide-builds','topic','Builds','builds','static','builds',1),
|
||||
('guide-pve','activity','PvE','pve','static','pve',1),
|
||||
('guide-pvp','activity','PvP','pvp','static','pvp',1),
|
||||
('guide-dark-zone','activity','Dark Zone','dark-zone','static','dark-zone',1),
|
||||
('guide-conflict','activity','Conflict','conflict','static','conflict',1),
|
||||
('guide-elite','difficulty','Elite','elite','static','elite',1),
|
||||
('guide-heroic','difficulty','Heroic','heroic','static','heroic',1),
|
||||
('guide-shotgun','weapon','Shotgun','shotgun','weapon_category','Shotgun',1),
|
||||
('guide-rifle','weapon','Rifle','rifle','weapon_category','Rifle',1),
|
||||
('guide-mmr','weapon','MMR','mmr','weapon_category','MMR',1),
|
||||
('guide-ar','weapon','AR','ar','weapon_category','AR',1),
|
||||
('guide-lmg','weapon','LMG','lmg','weapon_category','LMG',1),
|
||||
('guide-smg','weapon','SMG','smg','weapon_category','SMG',1),
|
||||
('news-official','news_channel','Official Game News','official-game-news','news_channel','official',1),
|
||||
('news-patch','news_channel','Patch & Data Updates','patch-data-updates','news_channel','patch',1),
|
||||
('news-community','news_channel','Community Reports','community-reports','news_channel','community',1),
|
||||
('news-shd','news_channel','SHD.build Development','shd-build-development','news_channel','shd',1);
|
||||
CREATE TABLE IF NOT EXISTS comment_threads (
|
||||
id TEXT PRIMARY KEY,
|
||||
target_type TEXT NOT NULL CHECK(target_type IN ('build', 'guide', 'profile')),
|
||||
target_type TEXT NOT NULL CHECK(target_type IN ('build', 'guide', 'news', 'profile', 'game_data')),
|
||||
target_id TEXT NOT NULL,
|
||||
pinned_comment_id TEXT REFERENCES comments(id) ON DELETE SET NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
UNIQUE(target_type, target_id)
|
||||
);
|
||||
@@ -267,6 +463,7 @@ CREATE TABLE IF NOT EXISTS content_moderation_scans (
|
||||
author_id TEXT NOT NULL REFERENCES users(id),
|
||||
content_hash TEXT NOT NULL CHECK(length(content_hash)>0),
|
||||
revision_id TEXT REFERENCES moderated_content_revisions(id),
|
||||
article_revision_id TEXT REFERENCES article_revisions(id),
|
||||
policy_version INTEGER NOT NULL CHECK(policy_version>=1),
|
||||
provider TEXT NOT NULL CHECK(length(trim(provider))>0),
|
||||
model TEXT NOT NULL CHECK(length(trim(model))>0),
|
||||
@@ -281,10 +478,10 @@ CREATE TABLE IF NOT EXISTS content_moderation_scans (
|
||||
completed_at TEXT,
|
||||
error_code TEXT,
|
||||
error_message TEXT,
|
||||
UNIQUE(content_type,content_id,content_hash,policy_version,provider,model),
|
||||
CHECK(
|
||||
(content_type IN ('comment','avatar') AND revision_id IS NULL)
|
||||
OR (content_type IN ('profile_bio','guide') AND revision_id IS NOT NULL)
|
||||
(content_type IN ('comment','avatar') AND revision_id IS NULL AND article_revision_id IS NULL)
|
||||
OR (content_type='profile_bio' AND revision_id IS NOT NULL AND article_revision_id IS NULL)
|
||||
OR (content_type='guide' AND revision_id IS NULL AND article_revision_id IS NOT NULL)
|
||||
),
|
||||
CHECK(
|
||||
(status='error' AND error_code IS NOT NULL)
|
||||
@@ -296,6 +493,13 @@ CREATE INDEX IF NOT EXISTS content_moderation_scans_pending_queue_idx
|
||||
WHERE status IN ('review','priority_review');
|
||||
CREATE INDEX IF NOT EXISTS content_moderation_scans_content_idx
|
||||
ON content_moderation_scans(content_type,content_id,created_at DESC);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS content_moderation_scans_direct_unique_idx
|
||||
ON content_moderation_scans(content_type,content_id,content_hash,policy_version,provider,model)
|
||||
WHERE content_type IN ('comment','avatar');
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS content_moderation_scans_profile_unique_idx
|
||||
ON content_moderation_scans(revision_id,policy_version,provider,model) WHERE content_type='profile_bio';
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS content_moderation_scans_guide_unique_idx
|
||||
ON content_moderation_scans(article_revision_id,policy_version,provider,model) WHERE content_type='guide';
|
||||
CREATE TABLE IF NOT EXISTS content_moderation_reviews (
|
||||
id TEXT PRIMARY KEY,
|
||||
scan_id TEXT NOT NULL REFERENCES content_moderation_scans(id),
|
||||
@@ -308,11 +512,14 @@ CREATE INDEX IF NOT EXISTS content_moderation_reviews_scan_created_idx
|
||||
ON content_moderation_reviews(scan_id,created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS content_moderation_reviews_reviewer_created_idx
|
||||
ON content_moderation_reviews(reviewer_id,created_at DESC);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS content_moderation_reviews_scan_unique_idx ON content_moderation_reviews(scan_id);
|
||||
CREATE TABLE IF NOT EXISTS user_content_reports (
|
||||
id TEXT PRIMARY KEY,
|
||||
content_type TEXT NOT NULL CHECK(content_type IN ('comment','profile_bio','guide','avatar')),
|
||||
content_id TEXT NOT NULL,
|
||||
reporter_id TEXT NOT NULL REFERENCES users(id),
|
||||
reporter_id TEXT REFERENCES users(id),
|
||||
anonymous_token_hash TEXT REFERENCES anonymous_visitors(token_hash),
|
||||
article_revision_id TEXT REFERENCES article_revisions(id),
|
||||
category TEXT NOT NULL CHECK(category IN (
|
||||
'spam','harassment','hate','sexual','violence','self_harm','privacy','other'
|
||||
)),
|
||||
@@ -322,6 +529,8 @@ CREATE TABLE IF NOT EXISTS user_content_reports (
|
||||
reviewed_at TEXT,
|
||||
reviewed_by TEXT REFERENCES users(id),
|
||||
resolution_reason TEXT,
|
||||
CHECK((reporter_id IS NOT NULL AND anonymous_token_hash IS NULL) OR (reporter_id IS NULL AND anonymous_token_hash IS NOT NULL)),
|
||||
CHECK((content_type='guide' AND article_revision_id IS NOT NULL) OR (content_type<>'guide' AND article_revision_id IS NULL)),
|
||||
CHECK(
|
||||
(status='pending' AND reviewed_at IS NULL AND reviewed_by IS NULL AND resolution_reason IS NULL)
|
||||
OR (status IN ('resolved','dismissed') AND reviewed_at IS NOT NULL AND reviewed_by IS NOT NULL
|
||||
@@ -335,7 +544,10 @@ CREATE INDEX IF NOT EXISTS user_content_reports_content_idx
|
||||
ON user_content_reports(content_type,content_id,created_at DESC);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS user_content_reports_one_pending_idx
|
||||
ON user_content_reports(reporter_id,content_type,content_id)
|
||||
WHERE status='pending';
|
||||
WHERE status='pending' AND reporter_id IS NOT NULL;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS user_content_reports_one_anonymous_pending_idx
|
||||
ON user_content_reports(anonymous_token_hash,content_type,content_id)
|
||||
WHERE status='pending' AND anonymous_token_hash IS NOT NULL;
|
||||
CREATE TABLE IF NOT EXISTS favorites (user_id TEXT NOT NULL REFERENCES users(id), build_id TEXT NOT NULL REFERENCES builds(id) ON DELETE CASCADE, created_at TEXT NOT NULL, PRIMARY KEY(user_id, build_id));
|
||||
CREATE TABLE IF NOT EXISTS equipment_types (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, category TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS stat_definitions (id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, value_kind TEXT NOT NULL, min_value REAL, max_value REAL, unit TEXT, active INTEGER NOT NULL DEFAULT 1);
|
||||
@@ -453,6 +665,9 @@ INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-images
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-avatars-review','avatars.review','Review and remove custom user avatars');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-community-comments-moderate','community.comments.moderate','Review and moderate public build, guide, and agent-profile comments');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-community-profiles-moderate','community.profiles.moderate','Review and hide public agent-profile biographies');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-community-guides-moderate','community.guides.moderate','Review, hide, and restore public Guide revisions');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-guides-curate','guides.curate','Pin, feature, and recommend published Guides');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-news-manage','news.manage','Create, edit, publish, and remove News articles');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-calculation-publish','calculation.publish','Activate combat calculation revisions used by every loadout');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-ai-mechanic-generate','ai.mechanic.generate','Use the OpenAI-assisted mechanic drafting workflow');
|
||||
INSERT OR IGNORE INTO permissions(id,name,description) VALUES('permission-ai-calculation-generate','ai.calculation.generate','Use the OpenAI-assisted combat calculation revision drafting workflow');
|
||||
@@ -490,6 +705,8 @@ INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moder
|
||||
INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moderator','permission-avatars-review');
|
||||
INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moderator','permission-community-comments-moderate');
|
||||
INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moderator','permission-community-profiles-moderate');
|
||||
INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moderator','permission-community-guides-moderate');
|
||||
INSERT OR IGNORE INTO role_permissions(role_id,permission_id) VALUES('role-moderator','permission-guides-curate');
|
||||
CREATE TABLE IF NOT EXISTS oauth_states (state_hash TEXT PRIMARY KEY, provider TEXT NOT NULL, mode TEXT NOT NULL, user_id TEXT REFERENCES users(id) ON DELETE CASCADE, expires_at TEXT NOT NULL, created_at TEXT NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS data_change_proposals (
|
||||
id TEXT PRIMARY KEY,
|
||||
@@ -499,6 +716,7 @@ CREATE TABLE IF NOT EXISTS data_change_proposals (
|
||||
draft_entity_id TEXT,
|
||||
applied_entity_id TEXT,
|
||||
summary TEXT NOT NULL,
|
||||
contributor_note TEXT,
|
||||
payload_json TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','approved','rejected','failed')),
|
||||
proposed_by TEXT NOT NULL REFERENCES users(id),
|
||||
|
||||
Reference in New Issue
Block a user