Spaces:
Running
Running
| // Tiny JSON-file persistence layer. No external DB, no config. | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const crypto = require('crypto'); | |
| const DB_PATH = path.join(__dirname, '..', 'data', 'db.json'); | |
| let db = null; | |
| let saveTimer = null; | |
| function uid(len = 12) { | |
| return crypto.randomBytes(len).toString('base64url').slice(0, len); | |
| } | |
| function load() { | |
| if (fs.existsSync(DB_PATH)) { | |
| try { | |
| db = JSON.parse(fs.readFileSync(DB_PATH, 'utf8')); | |
| } catch (e) { | |
| console.error('db corrupt, starting fresh:', e.message); | |
| db = null; | |
| } | |
| } | |
| if (!db) { | |
| db = { users: {}, games: {}, sessions: {}, catalog: [] }; | |
| } | |
| return db; | |
| } | |
| function get() { | |
| if (!db) load(); | |
| return db; | |
| } | |
| function save() { | |
| // Debounced write so rapid studio ops don't hammer the disk. | |
| if (saveTimer) return; | |
| saveTimer = setTimeout(() => { | |
| saveTimer = null; | |
| fs.writeFileSync(DB_PATH, JSON.stringify(db)); | |
| }, 500); | |
| } | |
| function saveNow() { | |
| if (saveTimer) { clearTimeout(saveTimer); saveTimer = null; } | |
| fs.writeFileSync(DB_PATH, JSON.stringify(db)); | |
| } | |
| function findUserByName(username) { | |
| const u = Object.values(get().users).find( | |
| (x) => x.username.toLowerCase() === String(username).toLowerCase() | |
| ); | |
| return u || null; | |
| } | |
| function publicUser(u) { | |
| if (!u) return null; | |
| return { | |
| id: u.id, | |
| username: u.username, | |
| avatar: u.avatar, | |
| created: u.created, | |
| stats: u.stats, | |
| system: !!u.system, | |
| }; | |
| } | |
| module.exports = { get, load, save, saveNow, uid, findUserByName, publicUser }; | |