radio: private radio tracker (/trackers/radio): frequencies, UBC125XLT banks + CSV, nets, ISS passes over Gouda, listening log, ntfy reminders
All checks were successful
Deploy production / deploy (push) Successful in 1m26s

This commit is contained in:
2026-10-26 16:58:29 +00:00
parent e24ee7f95d
commit 03574a95a7
37 changed files with 48839 additions and 3 deletions

View File

@@ -342,6 +342,32 @@ try {
throw new Error(`plant schema tables/grants are wrong: ${JSON.stringify(plant)}`);
const [{ n: plantEntries }] = await client`select count(*)::int as n from plant.entries`;
if (!plantEntries) throw new Error('plant.entries is empty, expected the seeded factory guide');
// Radio tracker (owner-only): 7 = frequencies, channels, nets, iss_passes, iss_tle, log,
// reminders; the migration seeds Opa's frequencies.
const RADIO_TABLES = 7;
const [radio] = await client`
select
count(*) filter (where has_table_privilege(c.oid, 'select, insert, update, delete')) as app_rw,
count(*) filter (
where exists (
select 1 from aclexplode(c.relacl) a join pg_roles r on r.oid = a.grantee
where r.rolname = 'personal_ro'
)
) as ro_read,
count(*) as tables,
has_schema_privilege('public', 'radio', 'usage') as public_usage
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'radio' and c.relkind = 'r'
`;
if (
Number(radio.tables) !== RADIO_TABLES ||
Number(radio.app_rw) !== RADIO_TABLES ||
Number(radio.ro_read) !== 0 ||
radio.public_usage
)
throw new Error(`radio schema tables/grants are wrong: ${JSON.stringify(radio)}`);
const [{ n: radioFreqs }] = await client`select count(*)::int as n from radio.frequencies`;
if (!radioFreqs) throw new Error('radio.frequencies is empty, expected the seeded frequencies');
// Energy tracker reads the ned-collector tables (SELECT only).
const [energy] = await client`
select count(*) as hours, max(valid_from) as latest from energy.ned_utilization
@@ -351,7 +377,7 @@ try {
if (!Number(energy.hours))
console.warn('Warning: energy.ned_utilization has no hourly data from the last 7 days');
console.log(
`health: ${health.tables} tables, tasks: ${tasks.tables} tables, groceries: ${groceries.tables} tables, stats: ${stats.tables} tables, ai: ${ai.tables} tables, movies: ${movies.tables} tables, worldtodo: ${worldtodo.tables} tables (${worldtodoTasks} tasks), notes: ${notes.tables} tables, math: ${math.tables} tables (${mathChapters} chapters), plant: ${plant.tables} tables (${plantEntries} entries), app read/write, personal_ro none; energy: ${energy.hours} hourly rows, latest ${energy.latest ? new Date(energy.latest).toISOString() : 'none'}`
`health: ${health.tables} tables, tasks: ${tasks.tables} tables, groceries: ${groceries.tables} tables, stats: ${stats.tables} tables, ai: ${ai.tables} tables, movies: ${movies.tables} tables, worldtodo: ${worldtodo.tables} tables (${worldtodoTasks} tasks), notes: ${notes.tables} tables, math: ${math.tables} tables (${mathChapters} chapters), plant: ${plant.tables} tables (${plantEntries} entries), radio: ${radio.tables} tables (${radioFreqs} frequencies), app read/write, personal_ro none; energy: ${energy.hours} hourly rows, latest ${energy.latest ? new Date(energy.latest).toISOString() : 'none'}`
);
const [catalog] = await client`
select