You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			98 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			98 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			TypeScript
		
	
| import { Database } from 'bun:sqlite';
 | |
| import { normalizeWhatsAppId } from './utils/whatsapp';
 | |
| import { mkdirSync } from 'fs';
 | |
| import { join } from 'path';
 | |
| import { Migrator } from './db/migrator';
 | |
| 
 | |
| function applyDefaultPragmas(instance: Database): void {
 | |
|   try {
 | |
|     instance.exec(`PRAGMA busy_timeout = 5000;`);
 | |
|     // Intentar activar WAL (si no es soportado, SQLite devolverá 'memory' u otro modo)
 | |
|     instance.query(`PRAGMA journal_mode = WAL`).get();
 | |
|     instance.exec(`PRAGMA synchronous = NORMAL;`);
 | |
|     instance.exec(`PRAGMA wal_autocheckpoint = 1000;`);
 | |
|     // Asegurar claves foráneas siempre activas
 | |
|     instance.exec(`PRAGMA foreign_keys = ON;`);
 | |
|   } catch (e) {
 | |
|     console.warn('[db] No se pudieron aplicar PRAGMAs (WAL, busy_timeout...):', e);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Function to get a database instance. Defaults to 'data/tasks.db'
 | |
| export function getDb(filename: string = 'tasks.db'): Database {
 | |
|   // Try to create data directory if it doesn't exist (ignore if already exists)
 | |
|   try {
 | |
|     mkdirSync('data', { recursive: true });
 | |
|   } catch (err) {
 | |
|     if (err.code !== 'EEXIST') throw err; // Only ignore "already exists" errors
 | |
|   }
 | |
|   const instance = new Database(join('data', filename));
 | |
|   applyDefaultPragmas(instance);
 | |
|   return instance;
 | |
| }
 | |
| 
 | |
| // Default export for the main application database
 | |
| export const db = getDb();
 | |
| 
 | |
|  // Initialize function now accepts a database instance
 | |
| export function initializeDatabase(instance: Database) {
 | |
|   // Aplicar PRAGMAs por defecto (WAL, busy_timeout, FK, etc.)
 | |
|   applyDefaultPragmas(instance);
 | |
| 
 | |
|   // Ejecutar migraciones up-only (sin baseline por defecto). Evitar backup duplicado aquí.
 | |
|   try {
 | |
|     Migrator.migrateToLatest(instance, { withBackup: false, allowBaseline: false });
 | |
|   } catch (e) {
 | |
|     console.error('[initializeDatabase] Error al aplicar migraciones:', e);
 | |
|     throw e;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Ensures a user exists in the database based on their raw WhatsApp ID.
 | |
|  * If the user exists, updates their last_seen timestamp.
 | |
|  * If the user does not exist, creates them.
 | |
|  * Uses the normalizeWhatsAppId utility.
 | |
|  * Stores timestamps with millisecond precision.
 | |
|  * 
 | |
|  * @param rawUserId The raw WhatsApp ID (e.g., '12345@s.whatsapp.net').
 | |
|  * @param instance The database instance to use (defaults to the main db).
 | |
|  * @returns The normalized user ID if successful, otherwise null.
 | |
|  */
 | |
| export function ensureUserExists(rawUserId: string | null | undefined, instance: Database = db): string | null {
 | |
|   const normalizedId = normalizeWhatsAppId(rawUserId);
 | |
| 
 | |
|   if (!normalizedId) {
 | |
|     console.error(`[ensureUserExists] Could not normalize or invalid user ID provided: ${rawUserId}`);
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   try {
 | |
|     // Use strftime for millisecond precision timestamps
 | |
|     const insertStmt = instance.prepare(`
 | |
|       INSERT INTO users (id, first_seen, last_seen) 
 | |
|       VALUES (?, strftime('%Y-%m-%d %H:%M:%f', 'now'), strftime('%Y-%m-%d %H:%M:%f', 'now'))
 | |
|       ON CONFLICT(id) DO NOTHING;
 | |
|     `);
 | |
|     
 | |
|     const updateStmt = instance.prepare(`
 | |
|       UPDATE users 
 | |
|       SET last_seen = strftime('%Y-%m-%d %H:%M:%f', 'now')
 | |
|       WHERE id = ?;
 | |
|     `);
 | |
| 
 | |
|     // Run as transaction for atomicity
 | |
|     instance.transaction(() => {
 | |
|       insertStmt.run(normalizedId);
 | |
|       // Update last_seen even if the user was just inserted or already existed
 | |
|       updateStmt.run(normalizedId); 
 | |
|     })(); // Immediately invoke the transaction
 | |
| 
 | |
|     return normalizedId;
 | |
| 
 | |
|   } catch (error) {
 | |
|     console.error(`[ensureUserExists] Database error for user ID ${normalizedId}:`, error);
 | |
|     return null;
 | |
|   }
 | |
| }
 |