Add public agent profiles and community moderation

This commit is contained in:
JB
2026-07-27 15:38:27 -04:00
parent efb0ccdc7e
commit 6b76110b76
28 changed files with 2101 additions and 64 deletions
+65
View File
@@ -131,6 +131,71 @@ if (usersTable) {
}
}
const userProfilesTable = await (await database.prepare(
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='user_profiles'"
)).get();
if (userProfilesTable) {
const profileColumns = await (await database.prepare("PRAGMA table_info('user_profiles')")).all();
if (!profileColumns.some((column) => column.name === 'bio_markdown')) {
console.log('Adding Markdown biographies to public agent profiles.');
await database.exec("ALTER TABLE user_profiles ADD COLUMN bio_markdown TEXT NOT NULL DEFAULT ''");
}
if (!profileColumns.some((column) => column.name === 'is_public')) {
console.log('Adding public profile visibility controls.');
await database.exec('ALTER TABLE user_profiles ADD COLUMN is_public INTEGER NOT NULL DEFAULT 1 CHECK(is_public IN (0, 1))');
}
if (!profileColumns.some((column) => column.name === 'bio_updated_at')) {
await database.exec('ALTER TABLE user_profiles ADD COLUMN bio_updated_at TEXT');
}
}
const commentsDefinition = await (await database.prepare(
"SELECT sql FROM sqlite_master WHERE type='table' AND name='comments'"
)).get();
if (typeof commentsDefinition?.sql === 'string' && commentsDefinition.sql.includes('build_id')) {
console.log('Normalizing build comments into reusable community comment threads.');
await database.exec('PRAGMA foreign_keys=OFF');
await database.exec('BEGIN');
try {
await database.exec('ALTER TABLE comments RENAME TO comments_legacy');
await database.exec(`CREATE TABLE comment_threads (
id TEXT PRIMARY KEY,
target_type TEXT NOT NULL CHECK(target_type IN ('build', 'guide', 'profile')),
target_id TEXT NOT NULL,
created_at TEXT NOT NULL,
UNIQUE(target_type, target_id)
)`);
await database.exec(`CREATE TABLE comments (
id TEXT PRIMARY KEY,
thread_id TEXT NOT NULL REFERENCES comment_threads(id) ON DELETE CASCADE,
author_id TEXT NOT NULL REFERENCES users(id),
parent_id TEXT REFERENCES comments(id),
body_markdown TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'visible' CHECK(status IN ('visible', 'removed')),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
removed_at TEXT,
removed_by TEXT REFERENCES users(id),
removal_reason TEXT
)`);
await database.exec(`INSERT INTO comment_threads(id,target_type,target_id,created_at)
SELECT 'build:' || build_id,'build',build_id,MIN(created_at)
FROM comments_legacy GROUP BY build_id`);
await database.exec(`INSERT INTO comments(
id,thread_id,author_id,parent_id,body_markdown,status,created_at,updated_at
)
SELECT id,'build:' || build_id,user_id,NULL,body,'visible',created_at,created_at
FROM comments_legacy`);
await database.exec('DROP TABLE comments_legacy');
await database.exec('COMMIT');
} catch (error) {
await database.exec('ROLLBACK');
throw error;
} finally {
await database.exec('PRAGMA foreign_keys=ON');
}
}
// The project schema deliberately avoids semicolons inside string literals.
const statements = schema
.split(';')