Initial commit: auth

This commit is contained in:
jtricerolph 2026-07-01 12:09:54 +00:00
commit 372e71c8f5
11 changed files with 500 additions and 0 deletions

67
src/db.js Normal file
View file

@ -0,0 +1,67 @@
import pg from 'pg'
import { hashPassword } from './jwt.js'
const { Pool } = pg
export const pool = new Pool({ connectionString: process.env.DATABASE_URL })
export async function initDb() {
await pool.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
password_hash TEXT NOT NULL,
offsite_allowed BOOLEAN NOT NULL DEFAULT FALSE,
active BOOLEAN NOT NULL DEFAULT TRUE,
is_admin BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS apps (
id SERIAL PRIMARY KEY,
slug TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
base_path TEXT NOT NULL,
icon TEXT NOT NULL DEFAULT '📋',
theme_color TEXT NOT NULL DEFAULT '#1e3a5f',
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS user_app_perms (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
app_id INTEGER NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (user_id, app_id)
);
`)
// Seed built-in apps
await pool.query(`
INSERT INTO apps (slug, name, description, base_path, icon, theme_color)
VALUES
('noticeboard', 'Noticeboard', 'Staff notices and announcements', '/notices', '📋', '#1e3a5f'),
('kitchen', 'Kitchen Flash', 'Invoice processing and GP tracking', '/kitchen', '🍳', '#e85d04'),
('cashup', 'Cash Up', 'Hotel daily cashing up', '/cashup', '💷', '#6b2d8b'),
('housekeeping','Housekeeping', 'Room status and task management', '/hk', '🛏️', '#2d6a4f'),
('forecasting', 'Forecasting', 'Revenue forecasting and reporting', '/forecast', '📈', '#0077b6'),
('rates', 'Rate Scraper', 'Competitor rate monitoring', '/rates', '🔍', '#7b4f00')
ON CONFLICT (slug) DO NOTHING
`)
// Seed first admin user if table is empty
const { rows } = await pool.query('SELECT COUNT(*) FROM users')
if (parseInt(rows[0].count) === 0) {
const email = process.env.ADMIN_EMAIL
const password = process.env.ADMIN_PASSWORD
if (email && password) {
await pool.query(
`INSERT INTO users (email, name, password_hash, is_admin, offsite_allowed)
VALUES ($1, $2, $3, true, true)`,
[email, 'Admin', await hashPassword(password)]
)
console.log(`Created initial admin: ${email}`)
}
}
}