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 ); `); await db.query(` CREATE TABLE IF NOT EXISTS rooms ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT NOT NULL, price NUMERIC(10,2) NOT NULL, photos TEXT[] DEFAULT '{}', active BOOLEAN DEFAULT TRUE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); `); await db.query(` CREATE TABLE IF NOT EXISTS menu_items ( id SERIAL PRIMARY KEY, category VARCHAR(100) NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, price NUMERIC(10,2) NOT NULL, is_daily_special BOOLEAN DEFAULT FALSE, active BOOLEAN DEFAULT TRUE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW() ); `); await db.query(` CREATE TABLE IF NOT EXISTS places ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT NOT NULL, photo VARCHAR(500), distance_km NUMERIC(8,2), visit_time VARCHAR(100), suggestion TEXT, active BOOLEAN DEFAULT TRUE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW() ); `); await db.query(` CREATE TABLE IF NOT EXISTS reviews ( id SERIAL PRIMARY KEY, author_name VARCHAR(255) NOT NULL, rating INTEGER NOT NULL CHECK (rating BETWEEN 1 AND 5), text TEXT NOT NULL, approved BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW() ); `); await db.query(` CREATE TABLE IF NOT EXISTS slides ( id SERIAL PRIMARY KEY, title VARCHAR(255), subtitle TEXT, image_id INTEGER REFERENCES images(id) ON DELETE SET NULL, image_url VARCHAR(500), active BOOLEAN DEFAULT TRUE, sort_order INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT NOW() ); `); await db.query(` CREATE TABLE IF NOT EXISTS room_comments ( id SERIAL PRIMARY KEY, room_id INTEGER REFERENCES rooms(id) ON DELETE CASCADE, photo_url VARCHAR(500), user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, first_name VARCHAR(100) NOT NULL, last_initial VARCHAR(10), text TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW(), deleted_by_admin BOOLEAN DEFAULT FALSE ); `); await db.query(` ALTER TABLE rooms ADD COLUMN IF NOT EXISTS featured_photo VARCHAR(500); `); await db.query(` ALTER TABLE users ADD COLUMN IF NOT EXISTS comments_disabled BOOLEAN DEFAULT FALSE; `); await db.query(` ALTER TABLE users ADD COLUMN IF NOT EXISTS first_name VARCHAR(100); `); await db.query(` ALTER TABLE users ADD COLUMN IF NOT EXISTS last_name VARCHAR(100); `); await db.query(` CREATE TABLE IF NOT EXISTS user_sessions ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, token TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); `); } 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'] ); } const { rows: calendarRows } = await db.query(`SELECT id FROM calendar_events`); if (calendarRows.length === 0) { const thisYear = new Date().getFullYear(); await db.query( `INSERT INTO calendar_events (date, title, type, description, color) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10), ($11, $12, $13, $14, $15), ($16, $17, $18, $19, $20), ($21, $22, $23, $24, $25), ($26, $27, $28, $29, $30), ($31, $32, $33, $34, $35), ($36, $37, $38, $39, $40), ($41, $42, $43, $44, $45), ($46, $47, $48, $49, $50), ($51, $52, $53, $54, $55)`, [ `${thisYear}-01-01`, 'New Year', 'holiday', 'Start the year in the Andes.', '#E8A849', `${thisYear}-02-14`, 'Valentine', 'holiday', 'Special dinner menu.', '#D94B73', `${thisYear}-03-20`, 'Spring Equinox', 'season', 'Blooming highlands.', '#7CB342', `${thisYear}-06-21`, 'Summer Solstice', 'season', 'Longest days of the year.', '#E8A849', `${thisYear}-09-22`, 'Fall Equinox', 'season', 'Harvest colors.', '#A6683C', `${thisYear}-10-31`, 'Halloween', 'holiday', 'Costume dinner party.', '#9C27B0', `${thisYear}-11-02`, 'Day of the Dead', 'holiday', 'Traditional altar and meal.', '#FF9800', `${thisYear}-12-21`, 'Winter Solstice', 'season', 'Cozy fires and clear skies.', '#4FC3F7', `${thisYear}-12-25`, 'Christmas', 'holiday', 'Holiday feast.', '#C62828', `${thisYear}-12-31`, 'New Year Eve', 'holiday', 'Countdown dinner.', '#E8A849', `${thisYear}-06-28`, 'Sunny 22°C', 'weather', 'Typical Otavalo dry season day.', '#FFD54F', ] ); } const { rows: siteRows } = await db.query(`SELECT key FROM config WHERE key = 'tagline'`); if (siteRows.length === 0) { await db.query(`INSERT INTO config (key, value) VALUES ($1, $2)`, ['tagline', 'Hotel · Restaurant · Andean Highlands']); } } export async function resetDatabase() { await db.query('DROP TABLE IF EXISTS room_comments, reviews, places, menu_items, rooms, config, benefits, offers, coupons, reservations, images, users, slides, calendar_events CASCADE'); await initSchema(); await seed(); }