import { BaseSchema } from '@adonisjs/lucid/schema' /** * The seed orchestrator (CONCEPT.md D38): nodes that lease article ranges * over HTTPS and submit what they aggregated (D37), and the ranges planned * for them. A range's id is its priority: plans insert newest first. */ export default class extends BaseSchema { async up() { this.schema.createTable('seed_nodes', (table) => { table.increments('id') table.text('name').notNullable().unique() // sha256 of the bearer token; the token itself is shown once, when it's made. table.string('token_hash', 64).notNullable().unique() table.boolean('disabled').notNullable().defaultTo(false) table.timestamp('created_at', { useTz: true }).notNullable().defaultTo(this.now()) table.timestamp('last_seen_at', { useTz: true }).nullable() }) this.schema.createTable('seed_ranges', (table) => { table.bigIncrements('id') table.text('group_name').notNullable() table.bigInteger('start_id').notNullable() table.bigInteger('end_id').notNullable() // First article not reported yet; a re-leased range resumes here. table.bigInteger('next_id').notNullable() table.string('status', 8).notNullable().defaultTo('queued') table.integer('node_id').nullable().references('id').inTable('seed_nodes').onDelete('SET NULL') table.integer('attempts').notNullable().defaultTo(0) table.timestamp('leased_at', { useTz: true }).nullable() table.timestamp('heartbeat_at', { useTz: true }).nullable() table.timestamp('done_at', { useTz: true }).nullable() table.bigInteger('headers').notNullable().defaultTo(0) table.bigInteger('files').notNullable().defaultTo(0) table.bigInteger('fragments').notNullable().defaultTo(0) table.text('error').nullable() table.timestamp('created_at', { useTz: true }).notNullable().defaultTo(this.now()) table.index(['group_name', 'start_id']) }) this.schema.raw(`CREATE INDEX seed_ranges_open_idx ON seed_ranges (id) WHERE status IN ('queued', 'leased')`) } async down() { this.schema.dropTable('seed_ranges') this.schema.dropTable('seed_nodes') } }