Initial commit: Premier Gunner tracker + StartOS 0.4.0 s9pk package

This commit is contained in:
Keysat
2026-05-31 21:04:48 -05:00
commit 0265699504
67 changed files with 4578 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
import bcrypt from 'bcryptjs';
import { randomBytes } from 'node:crypto';
import { db, getSetting, setSetting } from './db.js';
import { config } from './config.js';
export const COOKIE_NAME = 'pg_session';
// Resolve a stable cookie-signing secret (persisted so sessions survive restarts).
export function getCookieSecret() {
if (config.cookieSecret) return config.cookieSecret;
let secret = getSetting('cookie_secret');
if (!secret) {
secret = randomBytes(32).toString('hex');
setSetting('cookie_secret', secret);
}
return secret;
}
// Resolve the bcrypt password hash. The environment (PG_PASSWORD / PG_PASSWORD_HASH)
// is authoritative: when set, it overwrites the stored hash on every boot so that
// platform-managed password changes (e.g. the StartOS "Set Login Password" action,
// which restarts the service with a new PG_PASSWORD) actually take effect.
export function initPassword() {
if (config.passwordHash) {
setSetting('password_hash', config.passwordHash);
return config.passwordHash;
}
const stored = getSetting('password_hash');
if (config.password) {
// Re-hash only when the password actually changed (bcrypt salts differ each run).
if (!stored || !bcrypt.compareSync(config.password, stored)) {
const hash = bcrypt.hashSync(config.password, 10);
setSetting('password_hash', hash);
// Password changed out from under existing sessions — invalidate them.
if (stored) db.prepare('DELETE FROM sessions').run();
return hash;
}
return stored;
}
if (stored) return stored;
const hash = bcrypt.hashSync('gunner', 10);
setSetting('password_hash', hash);
console.warn('\n⚠ No PG_PASSWORD set — using default password "gunner". Set PG_PASSWORD before deploying.\n');
return hash;
}
export function verifyPassword(plain) {
const hash = config.passwordHash || getSetting('password_hash');
if (!hash) return false;
return bcrypt.compareSync(String(plain || ''), hash);
}
export function setPassword(plain) {
const hash = bcrypt.hashSync(String(plain), 10);
setSetting('password_hash', hash);
// Invalidate existing sessions when the password changes.
db.prepare('DELETE FROM sessions').run();
}
export function createSession() {
const token = randomBytes(32).toString('hex');
const expires = new Date(Date.now() + config.sessionDays * 86400_000).toISOString();
db.prepare('INSERT INTO sessions (token, expires_at) VALUES (?, ?)').run(token, expires);
return token;
}
export function isValidSession(token) {
if (!token) return false;
const row = db.prepare('SELECT expires_at FROM sessions WHERE token = ?').get(token);
if (!row) return false;
if (new Date(row.expires_at).getTime() < Date.now()) {
db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
return false;
}
return true;
}
export function destroySession(token) {
if (token) db.prepare('DELETE FROM sessions WHERE token = ?').run(token);
}
export function cleanupExpiredSessions() {
db.prepare("DELETE FROM sessions WHERE expires_at < datetime('now')").run();
}