scripts/restore_account.ts
Rama main · réplica de lectura del repositorio original.
/// <reference lib="deno.ns" />
import type { Database } from '../supabase/functions/_backend/utils/supabase.types.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js'
import { load } from 'https://deno.land/std@0.224.0/dotenv/mod.ts'
const accountId = 'THE ACCOUNT ID TO RESTORE'
// Load environment variables from .env.prod
// Running the script: deno run --allow-read --allow-net --allow-env restore_account.ts
const envPath = new URL('../internal/cloudflare/.env.prod', import.meta.url).pathname
const env = await load({ envPath })
const supabaseUrl = env.SUPABASE_URL
const supabaseServiceRoleKey = env.SUPABASE_SERVICE_ROLE_KEY
if (!supabaseUrl || !supabaseServiceRoleKey) {
console.error('Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY in .env.prod')
Deno.exit(1)
}
interface ApiKey {
id: number
created_at: string
user_id: string
key: string | null
updated_at: string
name: string
expires_at: string | null
key_hash: string | null
}
interface RemovedData {
email: string
apikeys: ApiKey[] | null
}
interface ToDeleteAccount {
id: number
account_id: string
removal_date: string
removed_data: RemovedData
created_at: string
}
async function main() {
const supabase = createClient<Database>(supabaseUrl, supabaseServiceRoleKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
detectSessionInUrl: false,
},
})
// Get all accounts pending deletion
const { data: accountsToRestore, error: fetchError } = await supabase
.from('to_delete_accounts')
.select('*')
.eq('account_id', accountId)
if (fetchError) {
console.error('Error fetching accounts to restore:', fetchError)
Deno.exit(1)
}
if (!accountsToRestore || accountsToRestore.length === 0) {
console.log('No accounts found in to_delete_accounts table')
Deno.exit(0)
}
if (accountsToRestore.length > 1) {
console.error('More than one account found in to_delete_accounts table')
Deno.exit(1)
}
console.log(`Found ${accountsToRestore.length} account(s) to restore`)
for (const account of accountsToRestore as unknown as ToDeleteAccount[]) {
console.log(`\nProcessing account: ${account.account_id}`)
console.log(` Email: ${account.removed_data?.email || 'unknown'}`)
const apikeys = account.removed_data?.apikeys
if (!apikeys || apikeys.length === 0) {
console.log(' No API keys to restore for this account')
}
else {
console.log(` Found ${apikeys.length} API key(s) to restore`)
for (const apikey of apikeys) {
if (!apikey.key && !apikey.key_hash) {
console.log(` Skipping API key "${apikey.name}" - no key value or hash to restore`)
continue
}
// Check if this apikey already exists by visible key or hash.
let existingKeyQuery = supabase
.from('apikeys')
.select('id')
existingKeyQuery = apikey.key
? existingKeyQuery.eq('key', apikey.key)
: existingKeyQuery.eq('key_hash', apikey.key_hash)
const { data: existingKey } = await existingKeyQuery.single()
if (existingKey) {
console.log(` Skipping API key "${apikey.name}" - already exists`)
continue
}
// Insert the API key back
const { error: insertError } = await supabase
.from('apikeys')
.insert({
user_id: apikey.user_id,
key: apikey.key,
name: apikey.name,
key_hash: apikey.key_hash,
expires_at: apikey.expires_at,
})
if (insertError) {
console.error(` Error restoring API key "${apikey.name}":`, insertError)
}
else {
console.log(` Restored API key: "${apikey.name}" (RBAC bindings must be reassigned)`)
}
}
}
// Remove the account from to_delete_accounts
const { error: deleteError } = await supabase
.from('to_delete_accounts')
.delete()
.eq('id', account.id)
if (deleteError) {
console.error(` Error removing account from to_delete_accounts:`, deleteError)
}
else {
console.log(` Removed account ${account.account_id} from to_delete_accounts`)
}
}
console.log('\nRestore complete!')
}
await main()