CREATE TABLE boards ( id INTEGER PRIMARY KEY AUTOINCREMENT, public_id TEXT NOT NULL UNIQUE, project_id INTEGER NOT NULL, title TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', icon TEXT NOT NULL DEFAULT '', show_description INTEGER NOT NULL DEFAULT 0, card_properties TEXT NOT NULL DEFAULT '[]', is_template INTEGER NOT NULL DEFAULT 0, created_by INTEGER NOT NULL, updated_by INTEGER NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, delete_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users(id), FOREIGN KEY (updated_by) REFERENCES users(id) ); CREATE TABLE blocks ( id INTEGER PRIMARY KEY AUTOINCREMENT, public_id TEXT NOT NULL UNIQUE, board_id INTEGER NOT NULL, parent_id INTEGER NOT NULL DEFAULT 0, created_by INTEGER NOT NULL, updated_by INTEGER NOT NULL, type TEXT NOT NULL, title TEXT NOT NULL DEFAULT '', fields TEXT NOT NULL DEFAULT '{}', completed_at INTEGER NOT NULL DEFAULT 0, completed_by INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, delete_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users(id), FOREIGN KEY (updated_by) REFERENCES users(id) ); CREATE TABLE blocks_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, block_id INTEGER NOT NULL, public_id TEXT NOT NULL, board_id INTEGER NOT NULL, parent_id INTEGER NOT NULL DEFAULT 0, created_by INTEGER NOT NULL, updated_by INTEGER NOT NULL, type TEXT NOT NULL, title TEXT NOT NULL DEFAULT '', fields TEXT NOT NULL DEFAULT '{}', completed_at INTEGER NOT NULL DEFAULT 0, completed_by INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, delete_at INTEGER NOT NULL DEFAULT 0, insert_at INTEGER NOT NULL ); CREATE TABLE board_members ( board_id INTEGER NOT NULL, user_id INTEGER NOT NULL, role TEXT NOT NULL DEFAULT 'writer', created_at INTEGER NOT NULL, PRIMARY KEY (board_id, user_id), FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE block_comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, public_id TEXT NOT NULL UNIQUE, block_id INTEGER NOT NULL, content TEXT NOT NULL, created_by INTEGER NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, delete_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (block_id) REFERENCES blocks(id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users(id) ); CREATE TABLE block_properties ( id INTEGER PRIMARY KEY AUTOINCREMENT, block_id INTEGER NOT NULL UNIQUE, status TEXT NOT NULL DEFAULT '', card_type TEXT NOT NULL DEFAULT '', priority TEXT NOT NULL DEFAULT '', due_date TEXT NOT NULL DEFAULT '', assignee_id INTEGER, sprint TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', FOREIGN KEY (block_id) REFERENCES blocks(id) ON DELETE CASCADE, FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL ); CREATE TABLE board_field_values ( id INTEGER PRIMARY KEY AUTOINCREMENT, public_id TEXT NOT NULL UNIQUE, board_id INTEGER NOT NULL REFERENCES boards(id) ON DELETE CASCADE, field TEXT NOT NULL, value TEXT NOT NULL, label TEXT NOT NULL, color TEXT NOT NULL DEFAULT '', display_order INTEGER NOT NULL DEFAULT 0, start_date TEXT NOT NULL DEFAULT '', end_date TEXT NOT NULL DEFAULT '', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, UNIQUE(board_id, field, value) ); CREATE TABLE block_checklist_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, public_id TEXT NOT NULL UNIQUE, block_id INTEGER NOT NULL, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0, display_order INTEGER NOT NULL DEFAULT 0, created_by INTEGER NOT NULL, updated_by INTEGER NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, delete_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (block_id) REFERENCES blocks(id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users(id), FOREIGN KEY (updated_by) REFERENCES users(id) ); CREATE TABLE block_attachments ( id INTEGER PRIMARY KEY AUTOINCREMENT, public_id TEXT NOT NULL UNIQUE, block_id INTEGER NOT NULL, filename TEXT NOT NULL, content_type TEXT NOT NULL DEFAULT 'application/octet-stream', size INTEGER NOT NULL DEFAULT 0, storage_path TEXT NOT NULL, created_by INTEGER NOT NULL, created_at INTEGER NOT NULL, delete_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (block_id) REFERENCES blocks(id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users(id) ); CREATE TABLE block_assignees ( block_id INTEGER NOT NULL, user_id INTEGER NOT NULL, assigned_at INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (block_id, user_id), FOREIGN KEY (block_id) REFERENCES blocks(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS auth_provider_group_mappings ( id INTEGER PRIMARY KEY AUTOINCREMENT, auth_provider_id INTEGER NOT NULL, group_id INTEGER NOT NULL, claim_value TEXT NOT NULL, created_at INTEGER NOT NULL DEFAULT 0, updated_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (auth_provider_id) REFERENCES auth_providers(id) ON DELETE CASCADE, FOREIGN KEY (group_id) REFERENCES user_groups(id) ON DELETE CASCADE, UNIQUE (auth_provider_id, claim_value, group_id) ); ALTER TABLE repos ADD COLUMN description TEXT NOT NULL DEFAULT ''; ALTER TABLE users ADD COLUMN avatar_storage_path TEXT NOT NULL DEFAULT ''; ALTER TABLE users ADD COLUMN avatar_content_type TEXT NOT NULL DEFAULT ''; ALTER TABLE users ADD COLUMN avatar_updated_at INTEGER NOT NULL DEFAULT 0; ALTER TABLE auth_providers ADD COLUMN oidc_end_session_url TEXT NOT NULL DEFAULT ''; ALTER TABLE auth_providers ADD COLUMN group_sync_mode TEXT NOT NULL DEFAULT 'off'; ALTER TABLE auth_providers ADD COLUMN oidc_post_logout_redirect INTEGER NOT NULL DEFAULT 0; ALTER TABLE sessions ADD COLUMN oidc_id_token TEXT NOT NULL DEFAULT ''; INSERT OR IGNORE INTO block_properties (block_id, status, description) SELECT bl.id, COALESCE(json_extract(bl.fields, '$.status'), ''), COALESCE(json_extract(bl.fields, '$.description'), '') FROM blocks bl WHERE bl.type = 'card' AND bl.delete_at = 0; INSERT OR IGNORE INTO block_assignees (block_id, user_id, assigned_at) SELECT block_id, assignee_id, strftime('%s', 'now') FROM block_properties WHERE assignee_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_boards_project ON boards(project_id, delete_at); CREATE INDEX IF NOT EXISTS idx_blocks_board ON blocks(board_id, delete_at); CREATE INDEX IF NOT EXISTS idx_blocks_parent ON blocks(parent_id); CREATE INDEX IF NOT EXISTS idx_blocks_type ON blocks(board_id, type, delete_at); CREATE INDEX IF NOT EXISTS idx_blocks_history_block ON blocks_history(block_id, insert_at DESC); CREATE INDEX IF NOT EXISTS idx_board_members_user ON board_members(user_id); CREATE INDEX IF NOT EXISTS idx_block_comments_block ON block_comments(block_id, delete_at); CREATE INDEX IF NOT EXISTS idx_block_properties_block ON block_properties(block_id); CREATE INDEX IF NOT EXISTS idx_board_field_values_board_field ON board_field_values(board_id, field); CREATE INDEX IF NOT EXISTS idx_block_checklist_items_block ON block_checklist_items(block_id, delete_at, display_order); CREATE INDEX IF NOT EXISTS idx_block_attachments_block ON block_attachments(block_id, delete_at, created_at DESC); CREATE INDEX IF NOT EXISTS idx_block_assignees_user ON block_assignees(user_id, block_id); CREATE INDEX IF NOT EXISTS idx_auth_provider_group_mappings_provider ON auth_provider_group_mappings(auth_provider_id); CREATE INDEX IF NOT EXISTS idx_auth_provider_group_mappings_claim ON auth_provider_group_mappings(auth_provider_id, claim_value);