import Surreal from 'surrealdb'; class Database { constructor() { this.client = null; this.connected = false; } async connect() { const url = process.env.SURREAL_URL; const user = process.env.SURREAL_USER; const pass = process.env.SURREAL_PASS; if (!url) { console.warn('[DB] No SURREAL_URL set — running in memory-only mode'); this.connected = false; return; } try { if (!this.client) { this.client = new Surreal(); } await this.client.connect(url); await this.client.signin({ username: user, password: pass }); await this.client.use({ namespace: 'kalbot', database: 'kalbot' }); this.connected = true; console.log('[DB] Connected to SurrealDB'); } catch (e) { console.error('[DB] Connection failed:', e.message); this.connected = false; } } _normalizeQueryResult(raw) { if (!Array.isArray(raw)) return [[]]; return raw.map((entry) => { if (Array.isArray(entry)) return entry; if (entry && typeof entry === 'object' && 'result' in entry) { return Array.isArray(entry.result) ? entry.result : [entry.result]; } return []; }); } async _handleTokenExpiration(e) { if (e.message && e.message.toLowerCase().includes('token has expired')) { console.log('[DB] Session token expired! Attempting to re-authenticate...'); this.connected = false; await this.connect(); return this.connected; // Returns true if reconnection was successful } return false; } async query(sql, vars = {}) { if (!this.connected) return [[]]; try { const raw = await this.client.query(sql, vars); return this._normalizeQueryResult(raw); } catch (e) { // Check if it's an expiration issue, if so, reconnect and retry once if (await this._handleTokenExpiration(e)) { try { const retryRaw = await this.client.query(sql, vars); return this._normalizeQueryResult(retryRaw); } catch (retryErr) { console.error('[DB] Query retry error:', retryErr.message); return [[]]; } } console.error('[DB] Query error:', e.message); return [[]]; } } async create(table, data) { if (!this.connected) return null; try { return await this.client.create(table, data); } catch (e) { if (await this._handleTokenExpiration(e)) { try { return await this.client.create(table, data); } catch (retryErr) { console.error('[DB] Create retry error:', retryErr.message); return null; } } console.error('[DB] Create error:', e.message); return null; } } async select(table) { if (!this.connected) return []; try { return await this.client.select(table); } catch (e) { if (await this._handleTokenExpiration(e)) { try { return await this.client.select(table); } catch (retryErr) { console.error('[DB] Select retry error:', retryErr.message); return []; } } console.error('[DB] Select error:', e.message); return []; } } } export const db = new Database();