import fs from 'fs'; import { verbose } from "sqlite3"; import { validate as validateEmailAddress } from 'email-validator'; import Database from "./index"; const sqlite3 = verbose(); if (!fs.existsSync('./data')) { fs.mkdirSync('./data'); } const db = new sqlite3.Database('./data/chat.sqlite'); // interface ChatRow { // id: string; // user_id: string; // title: string; // } // interface MessageRow { // id: string; // user_id: string; // chat_id: string; // data: any; // } // interface ShareRow { // id: string; // user_id: string; // created_at: Date; // } export class SQLiteAdapter extends Database { public async initialize() { db.serialize(() => { db.run(`CREATE TABLE IF NOT EXISTS authentication ( id TEXT PRIMARY KEY, email TEXT, password_hash BLOB, salt BLOB )`); db.run(`CREATE TABLE IF NOT EXISTS chats ( id TEXT PRIMARY KEY, user_id TEXT, title TEXT )`); db.run(`CREATE TABLE IF NOT EXISTS messages ( id TEXT PRIMARY KEY, user_id TEXT, chat_id TEXT, data TEXT )`); db.run(`CREATE TABLE IF NOT EXISTS shares ( id TEXT PRIMARY KEY, user_id TEXT, created_at DATETIME )`); }); } public createUser(email: string, passwordHash: Buffer, salt: Buffer): Promise { return new Promise((resolve, reject) => { if (!validateEmailAddress(email)) { reject(new Error('invalid email address')); return; } db.run(`INSERT INTO authentication (id, email, password_hash, salt) VALUES (?, ?, ?, ?)`, [email, email, passwordHash, salt], (err) => { if (err) { reject(err); console.log(`[database:sqlite] failed to create user ${email}`); } else { resolve(); console.log(`[database:sqlite] created user ${email}`); } }); }); } public async getUser(email: string): Promise { return new Promise((resolve, reject) => { db.get(`SELECT * FROM authentication WHERE email = ?`, [email], (err: any, row: any) => { if (err) { reject(err); console.log(`[database:sqlite] failed to get user ${email}`); } else { resolve({ ...row, passwordHash: Buffer.from(row.password_hash), salt: Buffer.from(row.salt), }); console.log(`[database:sqlite] retrieved user ${email}`); } }); }); } public async getChats(userID: string): Promise { return new Promise((resolve, reject) => { db.all(`SELECT * FROM chats WHERE user_id = ?`, [userID], (err: any, rows: any) => { if (err) { reject(err); console.log(`[database:sqlite] failed to get chats for user ${userID}`); } else { resolve(rows); console.log(`[database:sqlite] retrieved ${rows.length} chats for user ${userID}`); } }); }); } public async getMessages(userID: string): Promise { return new Promise((resolve, reject) => { db.all(`SELECT * FROM messages WHERE user_id = ?`, [userID], (err: any, rows: any) => { if (err) { reject(err); console.log(`[database:sqlite] failed to get messages for user ${userID}`); } else { resolve(rows.map((row: any) => { row.data = JSON.parse(row.data); return row; })); console.log(`[database:sqlite] retrieved ${rows.length} messages for user ${userID}`); } }); }); } public async insertMessages(userID: string, messages: any[]): Promise { return new Promise((resolve, reject) => { db.serialize(() => { const stmt = db.prepare(`INSERT OR IGNORE INTO messages (id, user_id, chat_id, data) VALUES (?, ?, ?, ?)`); messages.forEach((message) => { stmt.run(message.id, userID, message.chatID, JSON.stringify(message)); }); stmt.finalize(); console.log(`[database:sqlite] inserted ${messages.length} messages`); resolve(); }); }); } public async createShare(userID: string|null, id: string): Promise { return new Promise((resolve, reject) => { db.run(`INSERT INTO shares (id, user_id, created_at) VALUES (?, ?, ?)`, [id, userID, new Date()], (err) => { if (err) { reject(err); console.log(`[database:sqlite] failed to create share ${id}`); } else { resolve(true); console.log(`[database:sqlite] created share ${id}`) } }); }); } public async setTitle(userID: string, chatID: string, title: string): Promise { return new Promise((resolve, reject) => { db.run(`INSERT OR IGNORE INTO chats (id, user_id, title) VALUES (?, ?, ?)`, [chatID, userID, title], (err) => { if (err) { reject(err); console.log(`[database:sqlite] failed to set title for chat ${chatID}`); } else { resolve(); console.log(`[database:sqlite] set title for chat ${chatID}`) } }); }); } }