diff options
Diffstat (limited to 'src/db.rs')
| -rw-r--r-- | src/db.rs | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..7868e3e --- /dev/null +++ b/src/db.rs | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | use rusqlite::Connection; | ||
| 2 | use std::path::Path; | ||
| 3 | use std::sync::{Arc, Mutex}; | ||
| 4 | |||
| 5 | #[derive(Clone)] | ||
| 6 | pub struct Database { | ||
| 7 | conn: Arc<Mutex<Connection>>, | ||
| 8 | } | ||
| 9 | |||
| 10 | // Safe because all access to Connection goes through Mutex::lock() | ||
| 11 | unsafe impl Sync for Database {} | ||
| 12 | |||
| 13 | impl Database { | ||
| 14 | pub fn open(path: &Path) -> Result<Self, rusqlite::Error> { | ||
| 15 | let conn = Connection::open(path)?; | ||
| 16 | conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;")?; | ||
| 17 | let db = Self { | ||
| 18 | conn: Arc::new(Mutex::new(conn)), | ||
| 19 | }; | ||
| 20 | db.migrate()?; | ||
| 21 | Ok(db) | ||
| 22 | } | ||
| 23 | |||
| 24 | fn migrate(&self) -> Result<(), rusqlite::Error> { | ||
| 25 | let conn = self.conn.lock().unwrap(); | ||
| 26 | conn.execute_batch( | ||
| 27 | " | ||
| 28 | CREATE TABLE IF NOT EXISTS accounts ( | ||
| 29 | id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| 30 | username TEXT NOT NULL UNIQUE, | ||
| 31 | password TEXT NOT NULL, | ||
| 32 | is_superuser INTEGER NOT NULL DEFAULT 0, | ||
| 33 | password_change_required INTEGER NOT NULL DEFAULT 0 | ||
| 34 | ); | ||
| 35 | |||
| 36 | CREATE TABLE IF NOT EXISTS sessions ( | ||
| 37 | id TEXT PRIMARY KEY, | ||
| 38 | account_id INTEGER NOT NULL, | ||
| 39 | created_at TEXT NOT NULL DEFAULT (datetime('now')), | ||
| 40 | FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE | ||
| 41 | ); | ||
| 42 | |||
| 43 | CREATE TABLE IF NOT EXISTS instances ( | ||
| 44 | id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| 45 | name TEXT NOT NULL UNIQUE, | ||
| 46 | version TEXT NOT NULL, | ||
| 47 | server_type TEXT NOT NULL CHECK(server_type IN ('VANILLA', 'FORGE', 'FABRIC')), | ||
| 48 | world_source TEXT NOT NULL CHECK(world_source IN ('generate', 'upload')), | ||
| 49 | world_seed TEXT, | ||
| 50 | level_type TEXT NOT NULL DEFAULT 'DEFAULT', | ||
| 51 | has_modpack INTEGER NOT NULL DEFAULT 0, | ||
| 52 | memory_mb INTEGER NOT NULL, | ||
| 53 | difficulty INTEGER NOT NULL DEFAULT 1, | ||
| 54 | gamemode INTEGER NOT NULL DEFAULT 0, | ||
| 55 | max_players INTEGER NOT NULL DEFAULT 20, | ||
| 56 | pvp INTEGER NOT NULL DEFAULT 1, | ||
| 57 | motd TEXT NOT NULL DEFAULT 'A Minecraft Server', | ||
| 58 | rcon_password TEXT NOT NULL, | ||
| 59 | is_active INTEGER NOT NULL DEFAULT 0, | ||
| 60 | container_state TEXT NOT NULL DEFAULT 'stopped', | ||
| 61 | created_at TEXT NOT NULL DEFAULT (datetime('now')), | ||
| 62 | updated_at TEXT NOT NULL DEFAULT (datetime('now')) | ||
| 63 | ); | ||
| 64 | |||
| 65 | CREATE TABLE IF NOT EXISTS backups ( | ||
| 66 | id INTEGER PRIMARY KEY AUTOINCREMENT, | ||
| 67 | instance_id INTEGER NOT NULL, | ||
| 68 | filename TEXT NOT NULL, | ||
| 69 | created_at TEXT NOT NULL DEFAULT (datetime('now')), | ||
| 70 | FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE | ||
| 71 | ); | ||
| 72 | |||
| 73 | CREATE TABLE IF NOT EXISTS whitelist ( | ||
| 74 | instance_id INTEGER NOT NULL, | ||
| 75 | username TEXT NOT NULL, | ||
| 76 | PRIMARY KEY (instance_id, username), | ||
| 77 | FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE | ||
| 78 | ); | ||
| 79 | |||
| 80 | CREATE TABLE IF NOT EXISTS ops ( | ||
| 81 | instance_id INTEGER NOT NULL, | ||
| 82 | username TEXT NOT NULL, | ||
| 83 | PRIMARY KEY (instance_id, username), | ||
| 84 | FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE | ||
| 85 | ); | ||
| 86 | |||
| 87 | CREATE TABLE IF NOT EXISTS bans ( | ||
| 88 | instance_id INTEGER NOT NULL, | ||
| 89 | username TEXT NOT NULL, | ||
| 90 | ban_type TEXT NOT NULL CHECK(ban_type IN ('user', 'ip')), | ||
| 91 | PRIMARY KEY (instance_id, username), | ||
| 92 | FOREIGN KEY (instance_id) REFERENCES instances(id) ON DELETE CASCADE | ||
| 93 | ); | ||
| 94 | ", | ||
| 95 | )?; | ||
| 96 | |||
| 97 | // Migration: add container_state column if missing (for older DBs) | ||
| 98 | let _ = conn.execute_batch( | ||
| 99 | "ALTER TABLE instances ADD COLUMN container_state TEXT NOT NULL DEFAULT 'stopped'", | ||
| 100 | ); | ||
| 101 | Ok(()) | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn conn(&self) -> std::sync::MutexGuard<'_, Connection> { | ||
| 105 | self.conn.lock().unwrap() | ||
| 106 | } | ||
| 107 | } | ||
