170 lines
4.9 KiB
JavaScript
170 lines
4.9 KiB
JavaScript
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}`);
|