const Database = require("better-sqlite3") const logger = require("../modules/logger") const path = require("node:path") const jwt = require("jsonwebtoken") const fs = require("node:fs") require("colors") const db = new Database(path.join(__dirname, "..", "data", "lobby.db"), { verbose: (message) => logger.log(`[${"SQLite".yellow}] ${message}`) }) const privateKey = fs.readFileSync(path.join(__dirname, "..", "keys", "private.pem")) function initDB() { db.exec(` CREATE TABLE IF NOT EXISTS rooms ( externalGuid TEXT PRIMARY KEY, id TEXT NOT NULL, address TEXT NOT NULL, name TEXT NOT NULL, description TEXT NOT NULL, owner TEXT NOT NULL, port INTEGER NOT NULL, preferredGameName TEXT NOT NULL, preferredGameId INTEGER NOT NULL, maxPlayers INTEGER NOT NULL, netVersion TEXT NOT NULL, hasPassword BOOLEAN NOT NULL DEFAULT 0, password TEXT DEFAULT "" ) `) db.exec(` CREATE TABLE IF NOT EXISTS users ( discordId TEXT NOT NULL, username TEXT NOT NULL PRIMARY KEY, nickname TEXT NOT NULL, avatarUrl TEXT NOT NULL, token TEXT NOT NULL ) `) db.exec(` CREATE TABLE IF NOT EXISTS players ( nickname TEXT NOT NULL, username TEXT NOT NULL, avatarUrl TEXT NOT NULL, roomId TEXT NOT NULL, gameId INTEGER NOT NULL, FOREIGN KEY(roomId) REFERENCES rooms(externalGuid) ) `) } function loginWithTokenAndToken(username, token) { const stmt = db.prepare("SELECT * FROM users WHERE username = ?") const user = stmt.get(username) if (user) { if (user.token == token) { delete user.token return user } else { return { success: false, error: "Invalid token", code: 401 } } } else { return { success: false, error: "No user found with that username.", code: 404 } } } function loginWithToken(token) { try { const payload = jwt.verify(token, privateKey) const stmt = db.prepare("SELECT * FROM users WHERE username = ?") const user = stmt.get(payload.username) if (user) { delete user.token return user } else { return { success: false, error: "No user found with that username.", code: 404 } } } catch (error) { return { success: false, error: "Invalid token.", code: 401 } } } function login(req, res, next) { const xToken = req.headers["x-token"] const xUsername = req.headers["x-username"] const bearer = res.headers["authorization"] const token = bearer && bearer.startsWith("Bearer ") ? bearer.split(" ")[1] : null if (!xToken && !xUsername && !token) { return res.status(401).json({ error: "No token or/and username provided." }) } if (!xToken || !xUsername) { return res.status(422).json({ error: "No token or/and username provided." }) } if (token) { const user = loginWithToken(token) if (user.error) { return res.status(user.code).json({ error: user.error }) } else { req.user = user next() } } else { const user = loginWithTokenAndToken(xUsername, xToken) if (user.error) { return res.status(user.code).json({ error: user.error }) } else { req.user = user next() } } } function getPlayersFromRoom(roomId) { const stmt = db.prepare("SELECT * FROM players WHERE roomId = ?") const players = stmt.all(roomId) if (players) { return { success: true, players: players } } else { return { success: false, error: "No players found in that room.", code: 404 } } } function putPlayerInRoom(username, roomId, nickname, avatarUrl, gameId) { const stmt = db.prepare("INSERT INTO players (username, roomId, nickname, avatarUrl, gameId) VALUES (?, ?, ?, ?, ?)") try { stmt.run(username, roomId, nickname, avatarUrl, gameId) return { success: true } } catch (error) { console.error(error) return { success: false, error: "Failed to add player to the room.", code: 500 } } } function addRoomToLobby(externalGuid, id, address, name, description, owner, port, preferredGameName, preferredGameId, maxPlayers, netVersion, hasPassword, password) { const stmt = db.prepare("INSERT INTO rooms (externalGuid, id, address, name, description, owner, port, preferredGameName, preferredGameId, maxPlayers, netVersion, hasPassword, password) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") try { stmt.run(externalGuid, id, address, name, description, owner, port, preferredGameName, preferredGameId, maxPlayers, netVersion, hasPassword, password) return { success: true } } catch (error) { console.error(error) return { success: false, error: "Failed to room to lobby", code: 500 } } } function movePlayerAnotherRoom(username, roomId) { const stmt = db.prepare("UPDATE players SET roomId = ? WHERE username = ?") try { const result = stmt.run(roomId, username) if (result.changes > 0) { return { success: true } } else { return { success: false, error: "Player not found or already in the specified room.", code: 404 } } } catch (error) { return { success: false, error: "Failed to move player to another room.", code: 500 } } } function removePlayer(username, roomId) { const stmt = db.prepare("DELETE FROM players WHERE username = ? AND roomId = ?") try { const result = stmt.run(username, roomId) if (result.changes > 0) { return { success: true } } else { return { success: false, error: "Player not found in the specified room.", code: 404 } } } catch (error) { return { success: false, error: "Failed to remove player from the room.", code: 500 } } } function listRooms() { const stmt = db.prepare("SELECT * FROM rooms") try { const rooms = stmt.all() return { success: true, rooms: rooms, code: 200 } } catch (error) { return { success: false, error: "Failed to retrieve rooms.", code: 500 } } } function deleteRoom(roomId) { const stmt = db.prepare("DELETE FROM rooms WHERE id = ?") try { stmt.run(roomId) return { success: true, code: 204 } } catch (error) { console.error(error) return { success: false, error: "Failed to delete room", code: 500 } } } function getRoom(roomId) { const stmt = db.prepare("SELECT * FROM rooms WHERE id = ?") const room = stmt.get(roomId) if (room) { return { success: true, code: 200, room } } else { return { success: false, error: "No room found with that ID.", code: 404 } } } function updateKeyAndValueForRoom(roomId, key, value) { const stmt = db.prepare(`UPDATE rooms SET ${key} = ? WHERE id = ?`) try { const result = stmt.run(value, roomId) if (result.changes > 0) { return { success: true } } else { return { success: false, error: "Room not found or no changes made.", code: 404 } } } catch (error) { console.error(error) return { success: false, error: "Failed to update room.", code: 500 } } } function addUser(discordId, username, nickname, avatarUrl) { const stmt = db.prepare("INSERT INTO users (discordId, username, nickname, avatarUrl, token) VALUES (?, ?, ?, ?, ?)") try { const token = jwt.sign({ username, nickname, avatarUrl }, privateKey, { algorithm: "RS256", expiresIn: "168h" }) stmt.run(discordId, username, nickname, avatarUrl, token) return { success: true, user: { username, nickname, avatarUrl, token } } } catch (error) { console.error(error) return { success: false, error: "Failed to register user.", code: 500 } } } function removeUser(username) { const stmt = db.prepare("DELETE FROM users WHERE username = ?") try { stmt.run(username) return { success: true, code: 204 } } catch (error) { console.error(error) return { success: false, error: "Failed to delete user", code: 500 } } } function updateUserNickname(nickname, discordId) { const stmt = db.prepare("UPDATE users SET nickname = ? WHERE discordId = ?") try { const result = stmt.run(nickname, discordId) if (result.changes > 0) { return { success: true } } else { return { success: false, error: "Player not found or already in the specified room.", code: 404 } } } catch (error) { return { success: false, error: "Failed to move player to another room.", code: 500 } } } function updateUserUsername(username, discordId) { const stmt = db.prepare("UPDATE users SET username = ? WHERE discordId = ?") try { const result = stmt.run(username, discordId) if (result.changes > 0) { return { success: true } } else { return { success: false, error: "Player not found or already in the specified room.", code: 404 } } } catch (error) { return { success: false, error: "Failed to move player to another room.", code: 500 } } } function updateUserAvatar(avatarUrl, discordId) { const stmt = db.prepare("UPDATE users SET avatarUrl = ? WHERE discordId = ?") try { const result = stmt.run(avatarUrl, discordId) if (result.changes > 0) { return { success: true } } else { return { success: false, error: "Player not found or already in the specified room.", code: 404 } } } catch (error) { return { success: false, error: "Failed to move player to another room.", code: 500 } } } function getUser(userId) { const stmt = db.prepare("SELECT * FROM users WHERE discordId = ?") const user = stmt.get(userId) if (user) { return { success: true, code: 200, user } } else { return { success: false, error: "No user found with that ID.", code: 404 } } } module.exports = { initDB, login, loginWithToken, loginWithTokenAndToken, getPlayersFromRoom, putPlayerInRoom, movePlayerAnotherRoom, removePlayer, listRooms, addRoomToLobby, deleteRoom, getRoom, updateKeyAndValueForRoom, removeUser, addUser, updateUserAvatar, updateUserNickname, updateUserUsername, getUser }