Add database integration, auth API, admin/user portals, and deploy-db.sh

This commit is contained in:
2026-06-27 16:40:58 -05:00
parent 26559d4cf8
commit cc59177a74
25 changed files with 1423 additions and 118 deletions
+151
View File
@@ -0,0 +1,151 @@
import { db } from './db';
import bcrypt from 'bcryptjs';
export async function initSchema() {
await db.query(`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'user')),
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS images (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
data TEXT NOT NULL,
uploaded_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS reservations (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
date DATE NOT NULL,
time TIME NOT NULL,
guests INTEGER NOT NULL,
table_name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS coupons (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
code VARCHAR(50) NOT NULL,
discount VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS offers (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS benefits (
id SERIAL PRIMARY KEY,
text VARCHAR(500) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
`);
await db.query(`
CREATE TABLE IF NOT EXISTS config (
key VARCHAR(100) PRIMARY KEY,
value TEXT NOT NULL
);
`);
}
export async function seed() {
const { rows: adminRows } = await db.query(`SELECT id FROM users WHERE username = 'admin'`);
if (adminRows.length === 0) {
const adminHash = bcrypt.hashSync('admin', 10);
const userHash = bcrypt.hashSync('user', 10);
await db.query(
`INSERT INTO users (username, password_hash, role) VALUES ($1, $2, $3), ($4, $5, $6)`,
['admin', adminHash, 'admin', 'user', userHash, 'user']
);
}
const { rows: offerRows } = await db.query(`SELECT id FROM offers`);
if (offerRows.length === 0) {
await db.query(
`INSERT INTO offers (title, description) VALUES
($1, $2), ($3, $4)`,
[
'Summer Wine Weekend', 'Complimentary wine tasting with every two-night stay.',
'Sunset Dinner', 'Three-course menu on the terrace every Friday.',
]
);
}
const { rows: benefitRows } = await db.query(`SELECT id FROM benefits`);
if (benefitRows.length === 0) {
await db.query(
`INSERT INTO benefits (text) VALUES
($1), ($2), ($3), ($4)`,
[
'Priority booking for peak weekends',
'Late checkout on availability',
'Free welcome cocktail',
'Member-only events and tastings',
]
);
}
const { rows: configRows } = await db.query(`SELECT key FROM config WHERE key = 'wifi_password'`);
if (configRows.length === 0) {
await db.query(
`INSERT INTO config (key, value) VALUES ($1, $2)`,
['wifi_password', 'LaHuascaGuest2026']
);
}
const { rows: userResRows } = await db.query(`SELECT id FROM reservations WHERE user_id = (SELECT id FROM users WHERE username = 'user')`);
if (userResRows.length === 0) {
await db.query(
`INSERT INTO reservations (user_id, date, time, guests, table_name)
SELECT id, $2, $3, $4, $5 FROM users WHERE username = $1`,
['user', '2026-07-12', '19:00', 4, 'Terrace 3']
);
await db.query(
`INSERT INTO reservations (user_id, date, time, guests, table_name)
SELECT id, $2, $3, $4, $5 FROM users WHERE username = $1`,
['user', '2026-07-25', '20:30', 2, 'Barrel 7']
);
}
const { rows: userCouponRows } = await db.query(`SELECT id FROM coupons WHERE user_id = (SELECT id FROM users WHERE username = 'user')`);
if (userCouponRows.length === 0) {
await db.query(
`INSERT INTO coupons (user_id, code, discount)
SELECT id, $2, $3 FROM users WHERE username = $1`,
['user', 'WELCOME20', '20% off your next stay']
);
await db.query(
`INSERT INTO coupons (user_id, code, discount)
SELECT id, $2, $3 FROM users WHERE username = $1`,
['user', 'COFFEE5', '$5 off any coffee tasting']
);
}
}
export async function resetDatabase() {
await db.query('DROP TABLE IF EXISTS config, benefits, offers, coupons, reservations, images, users CASCADE');
await initSchema();
await seed();
}