import { mkdirSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { DatabaseSync } from "node:sqlite"; import { catalogProducts } from "./catalog.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); export const dbPath = join(__dirname, "../data/shop.sqlite"); mkdirSync(dirname(dbPath), { recursive: true }); export const db = new DatabaseSync(dbPath); db.exec(` PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, password_salt TEXT NOT NULL, name TEXT, first_name TEXT NOT NULL, surname TEXT NOT NULL, address TEXT, street_name TEXT, house_number TEXT, zip_code TEXT, city TEXT, birthdate TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS sessions ( token TEXT PRIMARY KEY, user_id INTEGER NOT NULL, created_at TEXT NOT NULL, expires_at TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS products ( id TEXT PRIMARY KEY, slug TEXT NOT NULL, name TEXT NOT NULL, kind TEXT NOT NULL, size_label TEXT NOT NULL, price_cents INTEGER NOT NULL, discovery_credit_cents INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS cart_items ( user_id INTEGER NOT NULL, product_id TEXT NOT NULL, quantity INTEGER NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (user_id, product_id), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES products(id) ); CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, subtotal_cents INTEGER NOT NULL, discount_cents INTEGER NOT NULL, total_cents INTEGER NOT NULL, shipping_address TEXT NOT NULL, payment_method TEXT NOT NULL, created_at TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS order_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, product_id TEXT NOT NULL, quantity INTEGER NOT NULL, unit_price_cents INTEGER NOT NULL, line_total_cents INTEGER NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES products(id) ); CREATE TABLE IF NOT EXISTS discovery_credits ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, order_id INTEGER NOT NULL, amount_cents INTEGER NOT NULL, redeemed_order_id INTEGER, created_at TEXT NOT NULL, redeemed_at TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE, FOREIGN KEY (redeemed_order_id) REFERENCES orders(id) ); CREATE TABLE IF NOT EXISTS sample_credits ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, slug TEXT NOT NULL, order_id INTEGER NOT NULL, amount_cents INTEGER NOT NULL, redeemed_order_id INTEGER, created_at TEXT NOT NULL, redeemed_at TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE, FOREIGN KEY (redeemed_order_id) REFERENCES orders(id) ); CREATE TABLE IF NOT EXISTS notification_preferences ( user_id INTEGER PRIMARY KEY, drops_enabled INTEGER NOT NULL DEFAULT 0, restocks_enabled INTEGER NOT NULL DEFAULT 0, small_batch_enabled INTEGER NOT NULL DEFAULT 0, discovery_enabled INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS product_subscriptions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, product_id TEXT NOT NULL, type TEXT NOT NULL, created_at TEXT NOT NULL, UNIQUE (user_id, product_id, type), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES products(id) ); `); const seedProduct = db.prepare(` INSERT INTO products ( id, slug, name, kind, size_label, price_cents, discovery_credit_cents ) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET slug = excluded.slug, name = excluded.name, kind = excluded.kind, size_label = excluded.size_label, price_cents = excluded.price_cents, discovery_credit_cents = excluded.discovery_credit_cents `); for (const product of catalogProducts) { seedProduct.run( product.id, product.slug, product.name, product.kind, product.size_label, product.price_cents, product.discovery_credit_cents ); } db.exec(` DELETE FROM discovery_credits WHERE id NOT IN ( SELECT MIN(id) FROM discovery_credits GROUP BY user_id ); `); console.log(`SQLite shop database ready at ${dbPath}`);