/** * Backfill: activate any Gitea bot users that were created before the * provisioner started flipping `active: true` automatically. * * Safe to re-run — bots that are already active stay active. * * Usage: * npx dotenv-cli -e .env.local -- npx tsx scripts/activate-workspace-bots.ts */ import { query } from '../lib/db-postgres'; const GITEA_API_URL = process.env.GITEA_API_URL ?? 'https://git.vibnai.com'; const GITEA_API_TOKEN = process.env.GITEA_API_TOKEN ?? ''; if (!GITEA_API_TOKEN) { console.error('GITEA_API_TOKEN not set — cannot proceed'); process.exit(1); } async function gitea(path: string, init?: RequestInit) { const res = await fetch(`${GITEA_API_URL}/api/v1${path}`, { ...init, headers: { 'Content-Type': 'application/json', Authorization: `token ${GITEA_API_TOKEN}`, ...(init?.headers ?? {}), }, }); if (!res.ok && res.status !== 204) { throw new Error(`gitea ${path} → ${res.status}: ${await res.text()}`); } if (res.status === 204) return null; return res.json(); } async function main() { const rows = await query<{ slug: string; gitea_bot_username: string }>( `SELECT slug, gitea_bot_username FROM vibn_workspaces WHERE gitea_bot_username IS NOT NULL` ); console.log(`Checking ${rows.length} workspace bot(s)…`); let touched = 0; for (const { slug, gitea_bot_username: bot } of rows) { try { const u = (await gitea(`/users/${bot}`)) as { active?: boolean } | null; if (!u) { console.warn(` [${slug}] bot ${bot} not found in Gitea`); continue; } if (u.active) { console.log(` [${slug}] ${bot} already active`); continue; } await gitea(`/admin/users/${bot}`, { method: 'PATCH', body: JSON.stringify({ source_id: 0, login_name: bot, active: true }), }); console.log(` [${slug}] activated ${bot}`); touched++; } catch (err) { console.error(` [${slug}] error:`, err instanceof Error ? err.message : err); } } console.log(`Done — activated ${touched} bot(s).`); } main().catch((err) => { console.error('FAILED:', err); process.exit(1); });