postgres makes me want to kill myself actually, sqlite all the way :333
This commit is contained in:
+68
-56
@@ -1,8 +1,9 @@
|
||||
const fs = require('node:fs')
|
||||
const http = require('node:http')
|
||||
const https = require('node:https')
|
||||
const os = require('node:os')
|
||||
const path = require('node:path')
|
||||
const { Pool } = require('pg')
|
||||
const Database = require('better-sqlite3')
|
||||
|
||||
function loadEnvFile() {
|
||||
const envPath = path.join(__dirname, '.env')
|
||||
@@ -37,7 +38,8 @@ loadEnvFile()
|
||||
const PORT = Number(process.env.PORT || 3001)
|
||||
const API_UPSTREAM = process.env.API_UPSTREAM || 'http://127.0.0.1:6000'
|
||||
const PUBLIC_ORIGIN = process.env.PUBLIC_ORIGIN || ''
|
||||
const UPTIME_DATABASE_URL = process.env.UPTIME_DATABASE_URL || process.env.DATABASE_URL || ''
|
||||
const UPTIME_STORAGE_DIR = process.env.UPTIME_STORAGE_DIR || '~/tsswebstorage'
|
||||
const UPTIME_DATABASE_FILE = process.env.UPTIME_DATABASE_FILE || 'uptime.sqlite'
|
||||
const UPTIME_SAMPLE_INTERVAL_MS = Number(process.env.UPTIME_SAMPLE_INTERVAL_MS || 30 * 60 * 1000)
|
||||
const UPTIME_HISTORY_LIMIT = Number(process.env.UPTIME_HISTORY_LIMIT || 336)
|
||||
const API_CACHE_TTL_MS = Number(process.env.API_CACHE_TTL_MS || 15000)
|
||||
@@ -73,13 +75,7 @@ const jsonHeaders = {
|
||||
|
||||
const apiCache = new Map()
|
||||
const rateLimits = new Map()
|
||||
const uptimePool = UPTIME_DATABASE_URL
|
||||
? new Pool({
|
||||
connectionString: UPTIME_DATABASE_URL,
|
||||
ssl: process.env.PGSSLMODE === 'require' ? { rejectUnauthorized: false } : undefined,
|
||||
})
|
||||
: null
|
||||
let uptimeReady = false
|
||||
let uptimeDb = null
|
||||
let latestUptimeSnapshot = null
|
||||
|
||||
function sendJson(res, status, body, headers = {}) {
|
||||
@@ -131,28 +127,42 @@ function requestJson(url, timeoutMs = 10000) {
|
||||
})
|
||||
}
|
||||
|
||||
async function ensureUptimeSchema() {
|
||||
if (!uptimePool || uptimeReady) return Boolean(uptimePool)
|
||||
function expandHome(filePath) {
|
||||
if (filePath === '~') return os.homedir()
|
||||
if (filePath.startsWith(`~${path.sep}`)) return path.join(os.homedir(), filePath.slice(2))
|
||||
if (filePath.startsWith('~/')) return path.join(os.homedir(), filePath.slice(2))
|
||||
return filePath
|
||||
}
|
||||
|
||||
await uptimePool.query(`
|
||||
function uptimeStoragePath() {
|
||||
return path.resolve(expandHome(UPTIME_STORAGE_DIR))
|
||||
}
|
||||
|
||||
function ensureUptimeDb() {
|
||||
if (uptimeDb) return uptimeDb
|
||||
|
||||
const storageDir = uptimeStoragePath()
|
||||
fs.mkdirSync(storageDir, { recursive: true })
|
||||
|
||||
uptimeDb = new Database(path.join(storageDir, UPTIME_DATABASE_FILE))
|
||||
uptimeDb.pragma('journal_mode = WAL')
|
||||
uptimeDb.exec(`
|
||||
create table if not exists uptime_snapshots (
|
||||
id bigserial primary key,
|
||||
checked_at timestamptz not null default now(),
|
||||
website_ok boolean not null,
|
||||
health_ok boolean not null,
|
||||
tss_ok boolean not null,
|
||||
ok boolean not null,
|
||||
id integer primary key autoincrement,
|
||||
checked_at text not null default (datetime('now')),
|
||||
website_ok integer not null,
|
||||
health_ok integer not null,
|
||||
tss_ok integer not null,
|
||||
ok integer not null,
|
||||
latency_ms integer not null,
|
||||
details jsonb not null default '{}'::jsonb
|
||||
)
|
||||
`)
|
||||
await uptimePool.query(`
|
||||
details text not null default '{}'
|
||||
);
|
||||
|
||||
create index if not exists uptime_snapshots_checked_at_idx
|
||||
on uptime_snapshots (checked_at desc)
|
||||
on uptime_snapshots (checked_at desc);
|
||||
`)
|
||||
|
||||
uptimeReady = true
|
||||
return true
|
||||
return uptimeDb
|
||||
}
|
||||
|
||||
async function takeUptimeSnapshot() {
|
||||
@@ -194,37 +204,42 @@ async function takeUptimeSnapshot() {
|
||||
|
||||
latestUptimeSnapshot = snapshot
|
||||
|
||||
if (uptimePool) {
|
||||
await ensureUptimeSchema()
|
||||
await uptimePool.query(
|
||||
`insert into uptime_snapshots
|
||||
(website_ok, health_ok, tss_ok, ok, latency_ms, details)
|
||||
values ($1, $2, $3, $4, $5, $6)`,
|
||||
[snapshot.website_ok, snapshot.health_ok, snapshot.tss_ok, snapshot.ok, snapshot.latency_ms, snapshot.details],
|
||||
)
|
||||
}
|
||||
const db = ensureUptimeDb()
|
||||
db.prepare(`
|
||||
insert into uptime_snapshots
|
||||
(checked_at, website_ok, health_ok, tss_ok, ok, latency_ms, details)
|
||||
values
|
||||
(@checked_at, @website_ok, @health_ok, @tss_ok, @ok, @latency_ms, @details)
|
||||
`).run({
|
||||
checked_at: snapshot.checked_at,
|
||||
website_ok: snapshot.website_ok ? 1 : 0,
|
||||
health_ok: snapshot.health_ok ? 1 : 0,
|
||||
tss_ok: snapshot.tss_ok ? 1 : 0,
|
||||
ok: snapshot.ok ? 1 : 0,
|
||||
latency_ms: snapshot.latency_ms,
|
||||
details: JSON.stringify(snapshot.details),
|
||||
})
|
||||
|
||||
return snapshot
|
||||
}
|
||||
|
||||
async function uptimeHistory() {
|
||||
if (!uptimePool) {
|
||||
return {
|
||||
configured: false,
|
||||
latest: latestUptimeSnapshot,
|
||||
history: latestUptimeSnapshot ? [latestUptimeSnapshot] : [],
|
||||
}
|
||||
}
|
||||
|
||||
await ensureUptimeSchema()
|
||||
const result = await uptimePool.query(
|
||||
`select checked_at, website_ok, health_ok, tss_ok, ok, latency_ms, details
|
||||
from uptime_snapshots
|
||||
order by checked_at desc
|
||||
limit $1`,
|
||||
[UPTIME_HISTORY_LIMIT],
|
||||
)
|
||||
const history = result.rows.reverse()
|
||||
const db = ensureUptimeDb()
|
||||
const rows = db.prepare(`
|
||||
select checked_at, website_ok, health_ok, tss_ok, ok, latency_ms, details
|
||||
from uptime_snapshots
|
||||
order by checked_at desc
|
||||
limit ?
|
||||
`).all(UPTIME_HISTORY_LIMIT)
|
||||
const history = rows.reverse().map((row) => ({
|
||||
checked_at: row.checked_at,
|
||||
website_ok: Boolean(row.website_ok),
|
||||
health_ok: Boolean(row.health_ok),
|
||||
tss_ok: Boolean(row.tss_ok),
|
||||
ok: Boolean(row.ok),
|
||||
latency_ms: row.latency_ms,
|
||||
details: JSON.parse(row.details || '{}'),
|
||||
}))
|
||||
|
||||
return {
|
||||
configured: true,
|
||||
@@ -490,10 +505,7 @@ http
|
||||
.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`tssbot-web serving http://localhost:${PORT}`)
|
||||
console.log(`proxying API requests to ${API_UPSTREAM}`)
|
||||
console.log(
|
||||
uptimePool
|
||||
? `sampling uptime every ${Math.round(UPTIME_SAMPLE_INTERVAL_MS / 60000)} minutes`
|
||||
: 'uptime database disabled; set DATABASE_URL or UPTIME_DATABASE_URL to persist snapshots',
|
||||
)
|
||||
console.log(`sampling uptime every ${Math.round(UPTIME_SAMPLE_INTERVAL_MS / 60000)} minutes`)
|
||||
console.log(`storing uptime snapshots in ${path.join(uptimeStoragePath(), UPTIME_DATABASE_FILE)}`)
|
||||
startUptimeSampler()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user