'use strict'; const path = require('path'); const sqlite3 = require('sqlite3').verbose(); const { open } = require('sqlite'); const target = process.argv[2]; const dbPath = path.resolve(__dirname, '..', 'storage', 'storage.db'); const GROUPS = { all: { tables: ['shulker_items', 'chest_loose_items', 'shulkers', 'chests', 'item_index', 'trades', 'invite_permissions', 'invite_sites', 'maps', 'permissions'], label: 'ALL tables' }, storage: { tables: ['shulker_items', 'chest_loose_items', 'shulkers', 'chests', 'item_index'], label: 'storage tables (chests, shulkers, items, index)' }, permissions: { tables: ['permissions'], label: 'permissions table' }, maps: { tables: ['maps'], label: 'maps table' }, invites: { tables: ['invite_permissions', 'invite_sites'], label: 'invite tables' }, trades: { tables: ['trades'], label: 'trades table' } }; function usage() { console.log('Usage: node scripts/db-reset.js '); console.log('Targets:'); for (const [name, group] of Object.entries(GROUPS)) { console.log(` ${name.padEnd(14)} ${group.label}`); } process.exit(1); } if (!target || !GROUPS[target]) { usage(); } const group = GROUPS[target]; async function main() { console.log(`Database: ${dbPath}`); console.log(`Resetting ${group.label}...`); const db = await open({ filename: dbPath, driver: sqlite3.Database }); try { await db.run('PRAGMA foreign_keys = OFF'); for (const table of group.tables) { console.log(` DROP TABLE IF EXISTS ${table}`); await db.run(`DROP TABLE IF EXISTS ${table}`); } await db.run('PRAGMA foreign_keys = ON'); // Recreate tables and re-insert defaults await recreateTables(db, group); console.log('Done.'); } finally { await db.close(); } } async function recreateTables(db, group) { const tables = group.tables; const recreate = (t) => tables.includes(t); if (recreate('permissions')) { await db.exec(`CREATE TABLE permissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, player_name TEXT UNIQUE NOT NULL, role TEXT DEFAULT 'team' NOT NULL CHECK(role IN ('owner', 'team', 'readonly')), joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`); console.log(' Recreated permissions'); await insertDefaultPermissions(db); } if (recreate('chests')) { await db.exec(`CREATE TABLE chests ( id INTEGER PRIMARY KEY AUTOINCREMENT, pos_x INTEGER NOT NULL, pos_y INTEGER NOT NULL, pos_z INTEGER NOT NULL, chest_type TEXT NOT NULL CHECK(chest_type IN ('single', 'double')), row INTEGER NOT NULL, column INTEGER NOT NULL, category TEXT, last_scan TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(pos_x, pos_y, pos_z) )`); console.log(' Recreated chests'); } if (recreate('shulkers')) { await db.exec(`CREATE TABLE shulkers ( id INTEGER PRIMARY KEY AUTOINCREMENT, chest_id INTEGER NOT NULL, slot INTEGER NOT NULL, shulker_type TEXT DEFAULT 'shulker_box', category TEXT, item_focus TEXT, slot_count INTEGER DEFAULT 0, total_items INTEGER DEFAULT 0, last_scan TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (chest_id) REFERENCES chests(id) ON DELETE CASCADE, UNIQUE(chest_id, slot) )`); console.log(' Recreated shulkers'); } if (recreate('shulker_items')) { await db.exec(`CREATE TABLE shulker_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, shulker_id INTEGER NOT NULL, item_name TEXT NOT NULL, item_id INTEGER NOT NULL, slot INTEGER NOT NULL, count INTEGER NOT NULL, nbt_data TEXT, FOREIGN KEY (shulker_id) REFERENCES shulkers(id) ON DELETE CASCADE, UNIQUE(shulker_id, slot), CHECK(slot >= 0 AND slot <= 26), CHECK(count > 0 AND count <= 64) )`); console.log(' Recreated shulker_items'); } if (recreate('trades')) { await db.exec(`CREATE TABLE trades ( id INTEGER PRIMARY KEY AUTOINCREMENT, player_name TEXT NOT NULL, action TEXT NOT NULL CHECK(action IN ('deposit', 'withdraw')), items TEXT NOT NULL, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`); console.log(' Recreated trades'); } if (recreate('chest_loose_items')) { await db.exec(`CREATE TABLE chest_loose_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, chest_id INTEGER NOT NULL, slot INTEGER NOT NULL, item_name TEXT NOT NULL, item_id INTEGER NOT NULL, count INTEGER NOT NULL, FOREIGN KEY (chest_id) REFERENCES chests(id) ON DELETE CASCADE, UNIQUE(chest_id, slot) )`); console.log(' Recreated chest_loose_items'); } if (recreate('item_index')) { await db.exec(`CREATE TABLE item_index ( id INTEGER PRIMARY KEY AUTOINCREMENT, item_id INTEGER UNIQUE NOT NULL, item_name TEXT NOT NULL, total_count INTEGER DEFAULT 0, shulker_ids TEXT, last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`); console.log(' Recreated item_index'); } if (recreate('maps')) { await db.exec(`CREATE TABLE maps ( id INTEGER PRIMARY KEY AUTOINCREMENT, map_id INTEGER UNIQUE NOT NULL, image_data TEXT, pixel_data TEXT, captured_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`); console.log(' Recreated maps'); } if (recreate('invite_sites')) { await db.exec(`CREATE TABLE invite_sites ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, label TEXT NOT NULL, bot_name TEXT NOT NULL, description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`); console.log(' Recreated invite_sites'); } if (recreate('invite_permissions')) { await db.exec(`CREATE TABLE invite_permissions ( id INTEGER PRIMARY KEY AUTOINCREMENT, site_id INTEGER NOT NULL, player_name TEXT NOT NULL, FOREIGN KEY (site_id) REFERENCES invite_sites(id) ON DELETE CASCADE, UNIQUE(site_id, player_name) )`); console.log(' Recreated invite_permissions'); } } async function insertDefaultPermissions(db) { const conf = require('../conf/base'); const defaultPlayers = conf.storage?.defaultPlayers || []; for (const player of defaultPlayers) { try { await db.run( 'INSERT OR IGNORE INTO permissions (player_name, role) VALUES (?, ?)', [player.name, player.role] ); } catch (e) { console.error(` Error inserting ${player.name}:`, e.message); } } if (defaultPlayers.length > 0) { console.log(` Inserted ${defaultPlayers.length} default players`); } } main().catch(err => { console.error('Fatal:', err); process.exit(1); });