feat: add MariaDB schema migration and runner
This commit is contained in:
parent
be38cdc3dc
commit
ed45a84e5a
2 changed files with 176 additions and 0 deletions
133
server/migrations/001_initial_schema.sql
Normal file
133
server/migrations/001_initial_schema.sql
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
username VARCHAR(100) NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
avatar_url VARCHAR(500) DEFAULT NULL,
|
||||
created_at DATETIME DEFAULT NOW(),
|
||||
UNIQUE KEY uq_users_email (email)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS campaigns (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
created_at DATETIME DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS campaign_members (
|
||||
campaign_id INT UNSIGNED NOT NULL,
|
||||
user_id INT UNSIGNED NOT NULL,
|
||||
role ENUM('dm', 'player') NOT NULL,
|
||||
joined_at DATETIME DEFAULT NOW(),
|
||||
PRIMARY KEY (campaign_id, user_id),
|
||||
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS campaign_invites (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
campaign_id INT UNSIGNED NOT NULL,
|
||||
token VARCHAR(64) NOT NULL,
|
||||
created_by INT UNSIGNED NOT NULL,
|
||||
expires_at DATETIME DEFAULT NULL,
|
||||
created_at DATETIME DEFAULT NOW(),
|
||||
UNIQUE KEY uq_invites_token (token),
|
||||
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (created_by) REFERENCES users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS characters (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
campaign_id INT UNSIGNED NOT NULL,
|
||||
user_id INT UNSIGNED DEFAULT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
class VARCHAR(100) NOT NULL DEFAULT 'Fighter',
|
||||
ancestry VARCHAR(100) NOT NULL DEFAULT 'Human',
|
||||
level INT NOT NULL DEFAULT 1,
|
||||
xp INT NOT NULL DEFAULT 0,
|
||||
hp_current INT NOT NULL DEFAULT 0,
|
||||
hp_max INT NOT NULL DEFAULT 0,
|
||||
ac INT NOT NULL DEFAULT 10,
|
||||
alignment VARCHAR(50) NOT NULL DEFAULT 'Neutral',
|
||||
title VARCHAR(255) DEFAULT '',
|
||||
notes TEXT DEFAULT '',
|
||||
background VARCHAR(255) DEFAULT '',
|
||||
deity VARCHAR(255) DEFAULT '',
|
||||
languages VARCHAR(500) DEFAULT '',
|
||||
gp INT NOT NULL DEFAULT 0,
|
||||
sp INT NOT NULL DEFAULT 0,
|
||||
cp INT NOT NULL DEFAULT 0,
|
||||
gear_slots_max INT NOT NULL DEFAULT 10,
|
||||
overrides TEXT DEFAULT '{}',
|
||||
color VARCHAR(100) DEFAULT '',
|
||||
luck_token TINYINT NOT NULL DEFAULT 1,
|
||||
torch_lit_at DATETIME DEFAULT NULL,
|
||||
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS character_stats (
|
||||
character_id INT UNSIGNED NOT NULL,
|
||||
stat_name VARCHAR(10) NOT NULL,
|
||||
value INT NOT NULL DEFAULT 10,
|
||||
PRIMARY KEY (character_id, stat_name),
|
||||
FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS character_gear (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
character_id INT UNSIGNED NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL DEFAULT 'gear',
|
||||
slot_count INT NOT NULL DEFAULT 1,
|
||||
properties TEXT DEFAULT '{}',
|
||||
effects TEXT DEFAULT '{}',
|
||||
game_item_id INT UNSIGNED DEFAULT NULL,
|
||||
FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS character_talents (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
character_id INT UNSIGNED NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
effect TEXT DEFAULT '{}',
|
||||
game_talent_id INT UNSIGNED DEFAULT NULL,
|
||||
FOREIGN KEY (character_id) REFERENCES characters(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS game_items (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
slot_count INT NOT NULL DEFAULT 1,
|
||||
effects TEXT DEFAULT '{}',
|
||||
properties TEXT DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS game_talents (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
source VARCHAR(255) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
effect TEXT DEFAULT '{}'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roll_log (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
campaign_id INT UNSIGNED NOT NULL,
|
||||
character_id INT UNSIGNED DEFAULT NULL,
|
||||
character_name VARCHAR(255) NOT NULL DEFAULT 'Roll',
|
||||
type VARCHAR(50) NOT NULL DEFAULT 'custom',
|
||||
label VARCHAR(255) NOT NULL,
|
||||
dice_expression VARCHAR(255) NOT NULL,
|
||||
rolls TEXT NOT NULL DEFAULT '[]',
|
||||
modifier INT NOT NULL DEFAULT 0,
|
||||
total INT NOT NULL DEFAULT 0,
|
||||
advantage TINYINT NOT NULL DEFAULT 0,
|
||||
disadvantage TINYINT NOT NULL DEFAULT 0,
|
||||
nat20 TINYINT NOT NULL DEFAULT 0,
|
||||
character_color VARCHAR(100) DEFAULT '',
|
||||
created_at DATETIME DEFAULT NOW(),
|
||||
FOREIGN KEY (campaign_id) REFERENCES campaigns(id) ON DELETE CASCADE
|
||||
);
|
||||
43
server/src/migrate.ts
Normal file
43
server/src/migrate.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import type { Pool } from "mysql2/promise";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const MIGRATIONS_DIR = path.join(__dirname, "..", "..", "migrations");
|
||||
|
||||
export async function runMigrations(pool: Pool): Promise<void> {
|
||||
await pool.execute(`
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
filename VARCHAR(255) NOT NULL UNIQUE,
|
||||
run_at DATETIME DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
const files = fs
|
||||
.readdirSync(MIGRATIONS_DIR)
|
||||
.filter((f) => f.endsWith(".sql"))
|
||||
.sort();
|
||||
|
||||
for (const file of files) {
|
||||
const [rows] = await pool.execute<import("mysql2").RowDataPacket[]>(
|
||||
"SELECT id FROM _migrations WHERE filename = ?",
|
||||
[file]
|
||||
);
|
||||
if (rows.length > 0) continue;
|
||||
|
||||
const sql = fs.readFileSync(path.join(MIGRATIONS_DIR, file), "utf-8");
|
||||
const statements = sql
|
||||
.split(";")
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s.length > 0 && !s.startsWith("--"));
|
||||
|
||||
for (const stmt of statements) {
|
||||
await pool.execute(stmt);
|
||||
}
|
||||
|
||||
await pool.execute("INSERT INTO _migrations (filename) VALUES (?)", [file]);
|
||||
console.log(`Migration applied: ${file}`);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue