Ctrl K
ComunidadGuías…

scripts/audit_broken_manifests_cleanup.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/*
 * List versions where a manifest is broken but zip exists.
 * Broken manifests are derived ONLY from audit_storage exports.
 *
 * Criteria:
 * - version_id in missing_manifests.json
 * - version_id NOT in missing_versions.json (zip exists)
 * - manifest_count > 0
 *
 * Usage: bun scripts/audit_broken_manifests_cleanup.ts
 */
import { createClient } from '@supabase/supabase-js'
import type { Database } from '../supabase/functions/_backend/utils/supabase.types.ts'

const ENV_FILE = './internal/cloudflare/.env.prod'
const OUTPUT_DIR = './tmp/unused_versions'
const MISSING_VERSIONS_IN = './tmp/r2_audit/missing_versions.json'
const MISSING_MANIFESTS_IN = './tmp/r2_audit/missing_manifests.json'
const CANDIDATES_OUT = `${OUTPUT_DIR}/broken_manifest_cleanup_candidates.json`
const SUMMARY_OUT = `${OUTPUT_DIR}/broken_manifest_cleanup_summary.json`

const PAGE_SIZE = 1000

function loadEnv(filePath: string) {
  const envText = Bun.file(filePath).text()
  return envText.then((text) => {
    const env: Record<string, string> = {}
    for (const line of text.split('\n')) {
      const trimmed = line.trim()
      if (!trimmed || trimmed.startsWith('#')) continue
      const idx = trimmed.indexOf('=')
      if (idx <= 0) continue
      const key = trimmed.slice(0, idx)
      const value = trimmed.slice(idx + 1)
      env[key] = value
    }
    return env
  })
}

async function loadJsonArray<T>(path: string): Promise<T[] | null> {
  try {
    const text = await Bun.file(path).text()
    return JSON.parse(text)
  }
  catch {
    return null
  }
}

function chunkArray<T>(items: T[], size: number) {
  const chunks: T[][] = []
  for (let i = 0; i < items.length; i += size) {
    chunks.push(items.slice(i, i + size))
  }
  return chunks
}

async function main() {
  const missingVersions = await loadJsonArray<{ id: number | string }>(MISSING_VERSIONS_IN)
  const missingManifests = await loadJsonArray<{ app_version_id: number | string }>(MISSING_MANIFESTS_IN)

  if (!missingVersions || !missingManifests) {
    console.log('Missing audit inputs. Run scripts/audit_storage.ts first.')
    return
  }

  const missingVersionIds = new Set<number>()
  for (const row of missingVersions) {
    const id = Number(row.id)
    if (!Number.isNaN(id)) missingVersionIds.add(id)
  }

  const missingManifestVersionIds = new Set<number>()
  for (const row of missingManifests) {
    const id = Number(row.app_version_id)
    if (!Number.isNaN(id)) missingManifestVersionIds.add(id)
  }

  const candidateIds = Array.from(missingManifestVersionIds).filter(id => !missingVersionIds.has(id))
  if (candidateIds.length === 0) {
    console.log('No candidate versions found (missing manifest but zip present).')
    return
  }

  const env = await loadEnv(ENV_FILE)
  const supabase = createClient<Database>(
    env.SUPABASE_URL,
    env.SUPABASE_SERVICE_ROLE_KEY,
    { auth: { autoRefreshToken: false, persistSession: false, detectSessionInUrl: false } },
  )

  const candidates: Array<{
    id: number
    app_id: string
    owner_org: string
    name: string
    manifest_count: number
  }> = []

  const chunks = chunkArray(candidateIds, PAGE_SIZE)
  for (const chunk of chunks) {
    const { data, error } = await supabase
      .from('app_versions')
      .select('id, app_id, owner_org, name, manifest_count, deleted')
      .in('id', chunk)

    if (error) {
      console.error('Error fetching app_versions:', error)
      process.exit(1)
    }

    for (const row of data ?? []) {
      if (row.deleted) continue
      if (row.app_id.startsWith('com.capdemo')) continue
      const manifestCount = row.manifest_count ?? 0
      if (manifestCount > 0) {
        candidates.push({
          id: row.id,
          app_id: row.app_id,
          owner_org: row.owner_org,
          name: row.name,
          manifest_count: manifestCount,
        })
      }
    }
  }

  const summary = {
    generatedAt: new Date().toISOString(),
    counts: {
      candidates: candidates.length,
    },
  }

  await Bun.write(CANDIDATES_OUT, JSON.stringify(candidates, null, 2))
  await Bun.write(SUMMARY_OUT, JSON.stringify(summary, null, 2))

  console.log(`Candidates: ${candidates.length}`)
  console.log(`Outputs: ${CANDIDATES_OUT}, ${SUMMARY_OUT}`)
}

await main()