Files
factorio-signal-exporter/web/migrations/001_initial_schema.js
2026-06-04 11:44:20 +02:00

119 lines
4.7 KiB
JavaScript

/** @type {import('node-pg-migrate').MigrationBuilder} */
exports.up = (pgm) => {
pgm.sql(`CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE`);
pgm.createTable(
'signals',
{
real_time: { type: 'timestamptz', notNull: true },
game_tick: { type: 'bigint', notNull: true },
combinator: { type: 'text', notNull: true },
item_key: { type: 'text', notNull: true },
green: { type: 'integer', notNull: true, default: 0 },
red: { type: 'integer', notNull: true, default: 0 },
logistic: { type: 'integer' },
},
{ ifNotExists: true },
);
pgm.sql(`SELECT create_hypertable('signals', 'real_time', if_not_exists => true)`);
pgm.sql(
`CREATE INDEX IF NOT EXISTS signals_combinator_item_key_real_time_idx ON signals (combinator, item_key, real_time DESC)`,
);
pgm.sql(`CREATE INDEX IF NOT EXISTS signals_game_tick_idx ON signals (game_tick DESC)`);
pgm.sql(`SELECT add_retention_policy('signals', INTERVAL '30 days', if_not_exists => true)`);
pgm.createTable(
'tick_timing',
{
real_time: { type: 'timestamptz', notNull: true },
game_tick: { type: 'bigint', notNull: true },
combinator: { type: 'text', notNull: true },
},
{ ifNotExists: true },
);
pgm.sql(`SELECT create_hypertable('tick_timing', 'real_time', if_not_exists => true)`);
pgm.sql(
`CREATE INDEX IF NOT EXISTS tick_timing_combinator_real_time_idx ON tick_timing (combinator, real_time DESC)`,
);
pgm.sql(`SELECT add_retention_policy('tick_timing', INTERVAL '30 days', if_not_exists => true)`);
pgm.createTable(
'charts',
{
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
title: { type: 'text', notNull: true },
pos_x: { type: 'integer', notNull: true, default: 0 },
pos_y: { type: 'integer', notNull: true, default: 0 },
width: { type: 'integer', notNull: true, default: 2 },
height: { type: 'integer', notNull: true, default: 4 },
signal_type: { type: 'text', notNull: true, default: 'both' },
chart_type: { type: 'text', notNull: true, default: 'signals' },
viz_type: { type: 'text', notNull: true, default: 'line' },
filter_combinators: { type: 'text[]' },
filter_items: { type: 'text[]' },
filter_items_exclude: { type: 'text[]' },
filter_items_regex: { type: 'boolean', notNull: true, default: false },
y_min: { type: 'real' },
y_max: { type: 'real' },
series_limit: { type: 'integer', notNull: true, default: 20 },
order_by: { type: 'text', notNull: true, default: 'time' },
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
},
{ ifNotExists: true },
);
// Use DO blocks so constraints are idempotent on existing DBs
pgm.sql(`DO $$ BEGIN
ALTER TABLE charts ADD CONSTRAINT charts_signal_type_check CHECK (signal_type IN ('green','red','both'));
EXCEPTION WHEN duplicate_object THEN NULL; END $$`);
pgm.sql(`DO $$ BEGIN
ALTER TABLE charts ADD CONSTRAINT charts_chart_type_check CHECK (chart_type IN ('signals','ups'));
EXCEPTION WHEN duplicate_object THEN NULL; END $$`);
pgm.sql(`DO $$ BEGIN
ALTER TABLE charts ADD CONSTRAINT charts_viz_type_check CHECK (viz_type IN ('line','stacked','table'));
EXCEPTION WHEN duplicate_object THEN NULL; END $$`);
pgm.sql(`DO $$ BEGIN
ALTER TABLE charts ADD CONSTRAINT charts_order_by_check CHECK (order_by IN ('time','value_asc','value_desc','abs_desc','delta_asc','delta_desc'));
EXCEPTION WHEN duplicate_object THEN NULL; END $$`);
pgm.createTable(
'alerts',
{
id: { type: 'uuid', primaryKey: true, default: pgm.func('gen_random_uuid()') },
item_key: { type: 'text', notNull: true },
item_key_is_regex: { type: 'boolean', notNull: true, default: false },
combinator: { type: 'text' },
signal_type: { type: 'text', notNull: true, default: 'green' },
condition: { type: 'text', notNull: true },
threshold: { type: 'integer', notNull: true },
active: { type: 'boolean', notNull: true, default: true },
created_at: { type: 'timestamptz', notNull: true, default: pgm.func('now()') },
},
{ ifNotExists: true },
);
pgm.sql(`DO $$ BEGIN
ALTER TABLE alerts ADD CONSTRAINT alerts_signal_type_check CHECK (signal_type IN ('green','red'));
EXCEPTION WHEN duplicate_object THEN NULL; END $$`);
pgm.sql(`DO $$ BEGIN
ALTER TABLE alerts ADD CONSTRAINT alerts_condition_check CHECK (condition IN ('above','below'));
EXCEPTION WHEN duplicate_object THEN NULL; END $$`);
pgm.createTable(
'settings',
{
key: { type: 'text', primaryKey: true },
value: { type: 'text', notNull: true },
},
{ ifNotExists: true },
);
};
exports.down = () => Promise.resolve();