Files
website/scripts/goal-metrics.ts

47 lines
1.9 KiB
TypeScript

// Daily refresh of auto goal metrics (tasks.goals.metric_key):
// 1. trailing-12-month paid Moneybird sales invoices -> tasks.metric_sources
// 2. recompute every auto goal -> tasks.goals.metric_current/metric_detail (+ progress snapshot)
//
// bun --env-file=$HOME/.config/goal-metrics.env scripts/goal-metrics.ts [--dry-run]
// Env: DB_URL (app role), MONEYBIRD_API_TOKEN, MONEYBIRD_ADMINISTRATION_ID. Mode 600, never committed.
// Run by the goal-metrics.timer user unit (deploy/systemd). Page loads never call Moneybird.
import postgres from 'postgres';
import {
SIDE_INCOME_SOURCE,
fetchMoneybirdSideIncome,
persistGoalMetrics,
saveSource
} from '../src/lib/server/goal-metrics/core';
const dry = process.argv.includes('--dry-run');
const { DB_URL, MONEYBIRD_API_TOKEN, MONEYBIRD_ADMINISTRATION_ID } = process.env;
if (!DB_URL) throw new Error('Set DB_URL');
const sql = postgres(DB_URL, { max: 1, connect_timeout: 10, onnotice: () => {} });
let failed = false;
try {
if (!MONEYBIRD_API_TOKEN || !MONEYBIRD_ADMINISTRATION_ID) {
failed = true;
console.error('[goal-metrics] Moneybird env missing; keeping the cached side income');
} else {
try {
const mb = await fetchMoneybirdSideIncome(MONEYBIRD_API_TOKEN, MONEYBIRD_ADMINISTRATION_ID);
console.log(`[goal-metrics] Moneybird: ${mb.invoices} paid invoices ${mb.from}..${mb.to}`);
if (!dry) await saveSource(sql, SIDE_INCOME_SOURCE, mb.monthly, mb, null);
} catch (e) {
failed = true;
const msg = (e as Error).message.slice(0, 300);
console.error(`[goal-metrics] Moneybird failed: ${msg}`);
if (!dry) await saveSource(sql, SIDE_INCOME_SOURCE, null, null, msg);
}
}
if (!dry) {
for (const g of await persistGoalMetrics(sql))
console.log(
`[goal-metrics] goal ${g.id}: ${g.target ? ((g.value / g.target) * 100).toFixed(1) + '%' : 'no target'}`
);
}
} finally {
await sql.end();
}
if (failed) process.exit(1);