From 0638d9abf2f9511114378fbba44c731daf08e35c Mon Sep 17 00:00:00 2001 From: Atlaskor Date: Thu, 13 Nov 2025 15:39:28 +0000 Subject: [PATCH] Update server/src/db.js --- server/src/db.js | 62 ++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/server/src/db.js b/server/src/db.js index a6be4d8..c78753b 100644 --- a/server/src/db.js +++ b/server/src/db.js @@ -19,52 +19,58 @@ export async function query(q, params) { } } -// init DB from schema + seed +// Initialize DB (schema + seed) export async function init() { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); + const schema = fs.readFileSync(path.join(__dirname, 'schema.sql'), 'utf8'); await query(schema); + const seed = fs.readFileSync(path.join(__dirname, 'seed.sql'), 'utf8'); await query(seed); - console.log('DB initialized.'); + + console.log('DB initialized (characters + items + inventory).'); } -// Models (simple) - -export const Users = { +/** + * Characters model + * - username + passhash live here + * - bcrypt provides salt + cost factor per hash + */ +export const Characters = { + // create a new character with username + password async create(username, password) { - const passhash = await bcrypt.hash(password, 10); + // bcrypt automatically generates a unique salt for each hash. + // You can bump the rounds (12, 14, etc.) if you want more work factor. + const saltRounds = 12; + const passhash = await bcrypt.hash(password, saltRounds); + const { rows } = await query( - 'INSERT INTO users (username, passhash) VALUES ($1,$2) RETURNING id, username', + 'INSERT INTO characters (username, passhash) VALUES ($1,$2) RETURNING id, username, x, y, level, xp', [username, passhash] ); - // create character with same name - await query( - 'INSERT INTO characters (user_id, name) VALUES ($1,$2)', - [rows[0].id, username] - ); return rows[0]; }, + + // verify username/password; return character row or null async verify(username, password) { const { rows } = await query( - 'SELECT * FROM users WHERE username=$1', + 'SELECT * FROM characters WHERE username=$1', [username] ); - if (!rows[0]) return null; - const ok = await bcrypt.compare(password, rows[0].passhash); - return ok ? rows[0] : null; - } -}; + const char = rows[0]; + if (!char) return null; -export const Characters = { - async getByUserId(userId) { - const { rows } = await query( - 'SELECT * FROM characters WHERE user_id=$1 LIMIT 1', - [userId] - ); + const ok = await bcrypt.compare(password, char.passhash); + return ok ? char : null; + }, + + async getById(id) { + const { rows } = await query('SELECT * FROM characters WHERE id=$1', [id]); return rows[0] || null; }, + async addXP(id, amount) { await query('UPDATE characters SET xp = xp + $1 WHERE id=$2', [amount, id]); } @@ -78,19 +84,19 @@ export const Inventory = { ); return rows; }, + async add(characterId, itemKey, qty) { - // upsert - const text = ` + const sql = ` INSERT INTO inventory (character_id, item_key, qty) VALUES ($1,$2,$3) ON CONFLICT (character_id, item_key) DO UPDATE SET qty = inventory.qty + EXCLUDED.qty `; - await query(text, [characterId, itemKey, qty]); + await query(sql, [characterId, itemKey, qty]); } }; -// allow manual init: node src/db.js init +// allow: node src/db.js init if (process.argv[2] === 'init') { init() .then(() => process.exit(0))