import { BaseCommand, args, flags } from '@adonisjs/core/ace' import { CommandOptions } from '@adonisjs/core/types/ace' import db from '@adonisjs/lucid/services/db' import NntpService from '#services/NntpService' import BackfillService from '#services/BackfillService' import { isSpotGroup } from '#services/SpotService' /** * Indexes a group's older headers (CONCEPT.md D32): from where live * indexing began back to N days ago, header-only and behind live indexing. * index:scheduler queues the ranges; this only sets where to go. */ export default class GroupBackfill extends BaseCommand { public static commandName = 'group:backfill' public static description = "Indexes groups' older headers (header-only, behind live indexing)." public static options: CommandOptions = { startApp: true, } @args.spread({ description: 'Groups (default: every active binary group)', required: false }) declare names?: string[] @flags.number({ description: 'How many days back', default: 21 }) declare days: number @flags.boolean({ description: 'Stop backfilling these groups (ranges already queued still run)' }) declare stop: boolean public async run() { const names = this.names?.length ? this.names : (await db.from('groups').where('active', true).orderBy('name').select('name')).map((g) => g.name as string).filter((n) => !isSpotGroup(n)) try { for (const name of names) { if (this.stop) { await BackfillService.stop(name) this.logger.info(`${name}: backfill stopped.`) continue } const range = await BackfillService.start(name, this.days) this.logger.info( range ? `${name}: backfilling articles ${range.from}–${range.to} (${(range.to - range.from).toLocaleString('en')} articles, ${this.days} days).` : `${name}: nothing to backfill.` ) } } finally { await NntpService.shutdown() } } }