UNPKG

@tomkoooo/tbase

Version:

A JavaScript-based socket client for out-of-the-box backend for React

1,473 lines (1,375 loc) 59.8 kB
import { MongoClient } from "mongodb"; import mysql from "mysql2/promise"; import { ObjectId } from "mongodb"; import bcrypt from "bcryptjs"; class Database { async connect(connectionInfo) {} async watchChanges(collectionName, callback, options) {} async execute(method) {} async close() {} constructor(type, connection) { this.type = type; // "mongodb" vagy "mysql" this.connection = connection; // MongoDB db objektum vagy MySQL connection } // Közös segédfüggvény: ObjectId konverzió MongoDB-hez toObjectId(id) { return this.type === "mongodb" ? new ObjectId(id) : id; } //---- Account Scope ---- // Regisztráció (signup) async signUp(payload) { try { const { email, password, isSuper } = payload; const salt = bcrypt.genSaltSync(10); const hashedPassword = bcrypt.hashSync(password, salt); if (this.type === "mongodb") { const existingUser = await this.db .collection("users") .findOne({ email }); if (existingUser) throw new Error("User already exists"); const result = await this.db.collection("users").insertOne({ email, password: hashedPassword, createdAt: new Date(), labels: [], preferencies: {}, verified: false, isSuper: isSuper || false, }); return result.insertedId; } else if (this.type === "mysql") { const [rows] = await this.db.execute( "SELECT * FROM users WHERE email = ?", [email] ); if (rows.length > 0) throw new Error("User already exists"); const [result] = await this.db.execute( "INSERT INTO users (email, password, isSuper) VALUES (?, ?, ?)", [email, hashedPassword, isSuper || false] ); return result.insertId.toString(); } } catch (err) { throw new Error(err.message || "Error during signup"); } } async signInSuper(email, password, isSuper) { try { if (this.type === "mongodb") { const user = await this.db.collection("users").findOne({ email}); if (!user || !bcrypt.compare(password, user.password) || (isSuper !== user.isSuper || !isSuper || !user.isSuper)) { throw new Error("Invalid credentials"); } const csrUser = {_id: user._id, ...user} return { user: csrUser }; } else if (this.type === "mysql") { const [rows] = await this.db.execute( "SELECT * FROM users WHERE email = ?", [email] ); const user = rows[0]; if (!user || !bcrypt.compare(password, user.password)) { throw new Error("Invalid credentials"); } return { user}; } } catch (err) { throw new Error(err.message || "Error during signin"); } } // Bejelentkezés (signin) async signIn(email, password, isSuper) { try { if (this.type === "mongodb") { const user = await this.db.collection("users").findOne({ email}); if (!user || !bcrypt.compare(password, user.password)) { throw new Error("Invalid credentials"); } const csrUser = {_id: user._id, ...user} return { user: csrUser }; } else if (this.type === "mysql") { const [rows] = await this.db.execute( "SELECT * FROM users WHERE email = ?", [email] ); const user = rows[0]; if (!user || !bcrypt.compare(password, user.password)) { throw new Error("Invalid credentials"); } return { user}; } } catch (err) { throw new Error(err.message || "Error during signin"); } } // Felhasználó lekérdezése (getAccount) async getAccount(userId) { try { if (this.type === "mongodb") { const user = await this.db .collection("users") .findOne( { _id: this.toObjectId(userId) }, { projection: { password: 0 } } ); if (!user) throw new Error("User not found"); return { user }; } else if (this.type === "mysql") { const [rows] = await this.db.execute( "SELECT * FROM users WHERE id = ?", [userId] ); const user = rows[0]; if (!user) throw new Error("User not found"); return user; } } catch (err) { throw new Error(err.message || "Error retrieving account"); } } async getSession(token) { try { if (this.type === "mongodb" && this.db) { if (token) { const session = await this.db.collection("sessions").findOne({ token }); if (!session) return null; return { userId: session.userId, token: session.token, data: session.data, createdAt: session.createdAt, updatedAt: session.updatedAt, }; } else { const sessions = await this.db.collection("sessions").find({ userId }).toArray(); return sessions.map((s) => ({ userId: s.userId, token: s.token, data: s.data, createdAt: s.createdAt, updatedAt: s.updatedAt, })); } } else if (this.type === "mysql" && this.db) { if (token) { const [rows] = await this.db.execute( "SELECT * FROM sessions WHERE token = ?", [token] ); if (rows.length === 0) return null; const session = rows[0]; return { userId: session.userId, token: session.token, data: session.data ? JSON.parse(session.data) : null, createdAt: session.createdAt, updatedAt: session.updatedAt, }; } else { const [rows] = await this.db.execute( "SELECT user_id AS userId, token, data, created_at AS createdAt, updated_at AS updatedAt FROM sessions WHERE user_id = ?", [userId] ); return rows.map((r) => ({ userId: r.userId, token: r.token, data: r.data ? JSON.parse(r.data) : null, createdAt: r.createdAt, updatedAt: r.updatedAt, })); } } throw new Error("Database not initialized"); } catch (err) { throw new Error(err.message || "Error getting session"); } } async getSessions(userId) { try { if (this.type === "mongodb" && this.db) { const sessions = await this.db.collection("sessions").find({ userId }).toArray(); if (sessions.length === 0) throw new Error("Sessions not found"); return sessions } else if (this.type === "mysql" && this.db) { const [rows] = await this.db.execute( "SELECT * FROM sessions WHERE user_id = ?", [userId] ); if (rows.length === 0) throw new Error("Sessions not found"); return rows } throw new Error("Database not initialized"); } catch (err) { throw new Error(err.message || "Error retrieving sessions"); } } async setSession(userId, token) { try { if (this.type === "mongodb" && this.db) { await this.db.collection("sessions").insertOne({ userId, token, createdAt: new Date(), updatedAt: new Date(), }); } else if (this.type === "mysql" && this.db) { await this.db.execute( "INSERT INTO sessions (user_id, token) VALUES (?, ?)", [userId, token] ); } else { throw new Error("Database not initialized"); } } catch (err) { throw new Error(err.message || "Error setting session"); } } async killSession(token) { try { if (this.type === "mongodb" && this.db) { console.log("token", token); const result = await this.db.collection("sessions").deleteOne({ token }); if (result.deletedCount === 0) throw new Error("Session not found"); } else if (this.type === "mysql" && this.db) { const [result] = await this.db.execute( "DELETE FROM sessions WHERE token = ?", [token] ); if (result.affectedRows === 0) throw new Error("Session not found"); } else { throw new Error("Database not initialized"); } } catch (err) { throw new Error(err.message || "Error killing session"); } } async killSessions(userId) { try { if (this.type === "mongodb" && this.db) { const result = await this.db.collection("sessions").deleteMany({ userId }); if (result.deletedCount === 0) throw new Error("Session not found"); } else if (this.type === "mysql" && this.db) { const [result] = await this.db.execute( "DELETE FROM sessions WHERE user_id = ? AND token = ?", [userId, token] ); if (result.affectedRows === 0) throw new Error("Session not found"); } else { throw new Error("Database not initialized"); } } catch (err) { throw new Error(err.message || "Error killing session"); } } async changeSession(token, data) { try { if (this.type === "mongodb" && this.db) { const result = await this.db.collection("sessions").updateOne( { token }, { $set: { token: data, updatedAt: new Date() } } ); if (result.matchedCount === 0) throw new Error("Session not found"); } else if (this.type === "mysql" && this.db) { const [result] = await this.db.execute( "UPDATE sessions SET token = ?, WHERE token = ?", [data, token] ); if (result.affectedRows === 0) throw new Error("Session not found"); } else { throw new Error("Database not initialized"); } } catch (err) { throw new Error(err.message || "Error changing session"); } } //---- User Scope ---- //get a specific user async getUser(userId) { try { if (this.type === "mongodb") { const user = await this.db.collection("users").findOne({ _id: this.toObjectId(userId) }); if (!user) throw new Error("User not found"); return { user }; } else if (this.type === "mysql") { const [rows] = await this.db.execute("SELECT * FROM users WHERE id = ?", [userId]); if (rows.length === 0) throw new Error("User not found"); return rows[0]; } } catch (err) { throw new Error(err.message || "Error retrieving user"); } } // get multiple usrs async getUsers(userIds){ try { if (!Array.isArray(userIds) || userIds.length === 0) { throw new Error("userIds must be a non-empty array"); } if (this.type === "mongodb") { const users = await this.db .collection("users") .find({ _id: { $in: userIds.map((id) => this.toObjectId(id)) } }) .toArray(); if (users.length === 0) throw new Error("Users not found"); return users } else if (this.type === "mysql" && this.db) { const placeholders = userIds.map(() => "?").join(", "); const query = `SELECT id AS _id, email, created_at AS createdAt FROM users WHERE id IN (${placeholders})`; const [rows] = await this.db.execute(query, userIds); if (rows.length === 0) throw new Error("Users not found"); return rows; } throw new Error("Database not initialized"); } catch (err) { console.error("Error in getUsers:", err); throw new Error(err instanceof Error ? err.message : "Error retrieving users"); } } async listUsers() { try { if (this.type === "mongodb") { const users = await this.db.collection("users").find({}).toArray(); return users; } else if (this.type === "mysql") { const [rows] = await this.db.execute("SELECT * FROM users"); console.log("Rows:", rows); return rows; } } catch (err) { throw new Error(err.message || "Error listing users"); } } //---- Notification ---- // Store a new subscription async storeSubscription(userId, subscription) { if (this.type === 'mongodb') { const subscriptionDoc = { userId, subscription, createdAt: new Date(), }; const result = await this.db.collection('push_subscriptions').insertOne(subscriptionDoc); console.log(`Stored subscription for ${userId} in MongoDB`); return result.insertedId; } else if (this.type === 'mysql') { const subscriptionStr = JSON.stringify(subscription); const [result] = await this.db.execute( 'INSERT INTO push_subscriptions (user_id, subscription, created_at) VALUES (?, ?, NOW())', [userId, subscriptionStr] ); console.log(`Stored subscription for ${userId} in MySQL`); return result.insertId; } else { throw new Error(`Unsupported DB type: ${this.type}`); } } // Upsert a record (update or insert) async upsert(table, data) { if (this.type === 'mongodb') { const result = await this.db.collection(table).updateOne( { userId: data.userId }, { $set: { subscription: data.subscription, updatedAt: new Date() } }, { upsert: true } ); console.log(`Upserted into ${table} in MongoDB:`, data); return result; } else if (this.type === 'mysql') { const subscriptionStr = JSON.stringify(data.subscription); const [result] = await this.db.execute( `INSERT INTO ${table} (user_id, subscription, created_at) VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE subscription = ?, updated_at = NOW()`, [data.userId, subscriptionStr, subscriptionStr] ); console.log(`Upserted into ${table} in MySQL:`, data); return result; } else { throw new Error(`Unsupported DB type: ${this.type}`); } } // Delete a record async delete(table, query) { if (this.type === 'mongodb') { const result = await this.db.collection(table).deleteOne({ userId: query.userId, subscription: query.subscription, }); console.log(`Deleted from ${table} in MongoDB:`, query); return { deletedCount: result.deletedCount }; } else if (this.type === 'mysql') { const subscriptionStr = JSON.stringify(query.subscription); const [result] = await this.db.execute( `DELETE FROM ${table} WHERE user_id = ? AND subscription = ?`, [query.userId, subscriptionStr] ); console.log(`Deleted from ${table} in MySQL:`, query); return { affectedRows: result.affectedRows }; } else { throw new Error(`Unsupported DB type: ${this.type}`); } } // Find records async find(table, query) { if (this.type === 'mongodb') { const results = await this.db.collection(table).find(query).toArray(); console.log(`Found in ${table} in MongoDB:`, results); return results; } else if (this.type === 'mysql') { let sql = `SELECT * FROM ${table}`; let params = []; if (Object.keys(query).length > 0) { sql += ' WHERE user_id = ?'; params.push(query.userId); } const [rows] = await this.db.execute(sql, params); const parsedRows = rows.map(row => ({ userId: row.user_id, subscription: JSON.parse(row.subscription), createdAt: row.created_at, })); console.log(`Found in ${table} in MySQL:`, parsedRows); return parsedRows; } else { throw new Error(`Unsupported DB type: ${this.type}`); } } // ------ Bucket API ------ // Create a new bucket async createBucket() { try { let bucketId = Math.random().toString(36).substring(2, 15); if (this.type === "mongodb") { this.db.createCollection(`bucket_${bucketId}`); console.log(`Created MongoDB bucket: bucket_${bucketId}`); return `bucket_${bucketId}`; } else if (this.type === "mysql") { let [rows] = await this.db.execute(`SHOW TABLES LIKE 'bucket_${bucketId}'`); while (rows.length > 0) { bucketId = Math.random().toString(36).substring(2, 15); [rows] = await this.db.execute(`SHOW TABLES LIKE 'bucket_${bucketId}'`); } await this.db.execute(` CREATE TABLE bucket_${bucketId} ( id INT AUTO_INCREMENT PRIMARY KEY, file_name VARCHAR(255) NOT NULL, file_type VARCHAR(255) NOT NULL, file_data LONGBLOB NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) `); console.log(`Created MySQL bucket: bucket_${bucketId}`); return `bucket_${bucketId}`; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error creating bucket"); } } // Upload a file to a bucket async uploadFile(bucketId, file) { try { const { name: fileName, type: fileType, data: fileData } = file; // Expecting { name, type, data } structure if (!fileName || !fileType || !fileData) { throw new Error("File name, type, and data are required"); } if (this.type === "mongodb") { const result = await this.db.collection(bucketId).insertOne({ file_name: fileName, file_type: fileType, file_data: Buffer.from(fileData), // Convert to Buffer for MongoDB created_at: new Date(), updated_at: new Date(), }); console.log(`Uploaded file ${fileName} to ${bucketId} in MongoDB`); return result.insertedId; } else if (this.type === "mysql") { const [result] = await this.db.execute(` INSERT INTO ${bucketId} (file_name, file_type, file_data) VALUES (?, ?, ?) `, [fileName, fileType, fileData]); // fileData should be a Buffer or binary string console.log(`Uploaded file ${fileName} to ${bucketId} in MySQL`); return result.insertId; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error uploading file to bucket"); } } // Retrieve a file from a bucket by ID async getFile(bucketId, fileId) { try { if (this.type === "mongodb") { const file = await this.db.collection(bucketId).findOne({ _id: this.toObjectId(fileId) }); if (!file) throw new Error("File not found"); console.log(`Retrieved file ${file.file_name} from ${bucketId} in MongoDB`); return { fileName: file.file_name, fileType: file.file_type, fileData: file.file_data.buffer, // Return Buffer as-is }; } else if (this.type === "mysql") { const [rows] = await this.db.execute(` SELECT file_name, file_type, file_data FROM ${bucketId} WHERE id = ? `, [fileId]); if (rows.length === 0) throw new Error("File not found"); const file = rows[0]; console.log(`Retrieved file ${file.file_name} from ${bucketId} in MySQL`); return { fileName: file.file_name, fileType: file.file_type, fileData: file.file_data, // LONGBLOB as Buffer }; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error retrieving file from bucket"); } } // List all files in a bucket async listFiles(bucketId) { try { if (this.type === "mongodb") { const files = await this.db.collection(bucketId).find().toArray(); console.log(`Listed ${files.length} files in ${bucketId} in MongoDB`); return files.map(file => ({ id: file._id, fileName: file.file_name, fileType: file.file_type, createdAt: file.created_at, updatedAt: file.updated_at, })); } else if (this.type === "mysql") { const [rows] = await this.db.execute(` SELECT id, file_name, file_type, created_at, updated_at FROM ${bucketId} `); console.log(`Listed ${rows.length} files in ${bucketId} in MySQL`); return rows.map(row => ({ id: row.id, fileName: row.file_name, fileType: row.file_type, createdAt: row.created_at, updatedAt: row.updated_at, })); } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error listing files in bucket"); } } // Delete a file from a bucket async deleteFile(bucketId, fileId) { try { if (this.type === "mongodb") { const result = await this.db.collection(bucketId).deleteOne({ _id: this.toObjectId(fileId) }); if (result.deletedCount === 0) throw new Error("File not found"); console.log(`Deleted file ${fileId} from ${bucketId} in MongoDB`); return true; } else if (this.type === "mysql") { const [result] = await this.db.execute(` DELETE FROM ${bucketId} WHERE id = ? `, [fileId]); if (result.affectedRows === 0) throw new Error("File not found"); console.log(`Deleted file ${fileId} from ${bucketId} in MySQL`); return true; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error deleting file from bucket"); } } async listBuckets() { try { if (this.type === "mongodb") { const collections = await this.db.listCollections().toArray(); const buckets = collections .filter((col) => col.name.startsWith("bucket_")) .map((col) => col.name); console.log(`Listed ${buckets.length} buckets in MongoDB`); return buckets; } else if (this.type === "mysql") { const [rows] = await this.db.execute(`SHOW TABLES`); const buckets = rows .map((row) => Object.values(row)[0]) .filter((tableName) => tableName.startsWith("bucket_")); console.log(`Listed ${buckets.length} buckets in MySQL`); return buckets; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error listing buckets"); } } async deleteBucket(bucketId) { try { if (!bucketId.startsWith("bucket_")) { throw new Error("Invalid bucket ID: must start with 'bucket_'"); } if (this.type === "mongodb") { await this.db.collection(bucketId).drop(); console.log(`Deleted bucket ${bucketId} in MongoDB`); return true; } else if (this.type === "mysql") { await this.db.execute(`DROP TABLE ${bucketId}`); console.log(`Deleted bucket ${bucketId} in MySQL`); return true; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error deleting bucket"); } } async renameBucket(oldBucketId, newBucketId) { try { if (!oldBucketId.startsWith("bucket_") || !newBucketId.startsWith("bucket_")) { throw new Error("Invalid bucket ID: must start with 'bucket_'"); } if (oldBucketId === newBucketId) { throw new Error("New bucket ID must be different from the old one"); } if (this.type === "mongodb") { await this.db.collection(oldBucketId).rename(newBucketId); console.log(`Renamed bucket ${oldBucketId} to ${newBucketId} in MongoDB`); return true; } else if (this.type === "mysql") { const [existing] = await this.db.execute(`SHOW TABLES LIKE '${newBucketId}'`); if (existing.length > 0) { throw new Error(`Bucket ${newBucketId} already exists`); } await this.db.execute(` CREATE TABLE ${newBucketId} LIKE ${oldBucketId} `); await this.db.execute(` INSERT INTO ${newBucketId} SELECT * FROM ${oldBucketId} `); await this.db.execute(`DROP TABLE ${oldBucketId}`); console.log(`Renamed bucket ${oldBucketId} to ${newBucketId} in MySQL`); return true; } else { throw new Error("Unsupported database type"); } } catch (err) { throw new Error(err.message || "Error renaming bucket"); } } //---- Permission Scope ---- async createPermission(itemId, requireAction, requireRole = null) { try { if (this.type === "mysql") { const [result] = await this.db.query( `INSERT INTO permissions (item_id, require_action, require_role) VALUES (?, ?, ?)`, [itemId, requireAction, requireRole] ); return { id: result.insertId }; } else if (this.type === "mongodb") { const result = await this.db.collection("permissions").insertOne({ item_id: itemId, require_action: requireAction, require_role: requireRole, created_at: new Date(), updated_at: new Date(), }); return { id: result.insertedId }; } } catch (error) { throw new Error(`Error in createPermission (${this.type}): ${error.message}`); } } // Read a permission by ID async getPermission(permissionId) { try { if (this.type === "mysql") { const [rows] = await this.db.query( `SELECT id, item_id, require_action, require_role FROM permissions WHERE id = ?`, [permissionId] ); if (rows.length === 0) throw new Error("Permission not found"); return rows[0]; } else if (this.type === "mongodb") { const permission = await this.db.collection("permissions").findOne({ _id: permissionId }); if (!permission) throw new Error("Permission not found"); return permission; } } catch (error) { throw new Error(`Error in getPermission (${this.type}): ${error.message}`); } } // Read all permissions (optional filter by item_id) async getPermissions(itemId = null) { try { if (this.type === "mysql") { const [rows] = await this.db.query( `SELECT id, item_id, require_action, require_role FROM permissions WHERE item_id = ? OR ? IS NULL`, [itemId, itemId] ); return rows; } else if (this.type === "mongodb") { const query = itemId ? { item_id: itemId } : {}; const permissions = await this.db.collection("permissions").find(query).toArray(); return permissions; } } catch (error) { throw new Error(`Error in getPermissions (${this.type}): ${error.message}`); } } // Update a permission async updatePermission(permissionId, itemId, requireAction, requireRole = null) { try { if (this.type === "mysql") { const [result] = await this.db.query( `UPDATE permissions SET item_id = ?, require_action = ?, require_role = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, [itemId, requireAction, requireRole, permissionId] ); if (result.affectedRows === 0) throw new Error("Permission not found or no changes made"); return { success: true }; } else if (this.type === "mongodb") { const result = await this.db.collection("permissions").updateOne( { _id: permissionId }, { $set: { item_id: itemId, require_action: requireAction, require_role: requireRole, updated_at: new Date() } } ); if (result.matchedCount === 0) throw new Error("Permission not found or no changes made"); return { success: true }; } } catch (error) { throw new Error(`Error in updatePermission (${this.type}): ${error.message}`); } } // Delete a permission async deletePermission(permissionId) { try { if (this.type === "mysql") { const [result] = await this.db.query( `DELETE FROM permissions WHERE id = ?`, [permissionId] ); if (result.affectedRows === 0) throw new Error("Permission not found"); return { success: true }; } else if (this.type === "mongodb") { const result = await this.db.collection("permissions").deleteOne({ _id: permissionId }); if (result.deletedCount === 0) throw new Error("Permission not found"); return { success: true }; } } catch (error) { throw new Error(`Error in deletePermission (${this.type}): ${error.message}`); } } //---- User Permission Scope ---- // Create a new user permission (add access for a user to a document/route) async createUserPermission(userId, onDoc, permission) { try { if (this.type === "mysql") { const [result] = await this.db.query( `INSERT INTO user_permissions (user_id, onDoc, permission) VALUES (?, ?, ?)`, [userId, onDoc, permission] ); return { id: result.insertId }; } else if (this.type === "mongodb") { const result = await this.db.collection("user_permissions").insertOne({ user_id: userId, onDoc, permission, created_at: new Date(), updated_at: new Date(), }); return { id: result.insertedId }; } } catch (error) { throw new Error(`Error in createUserPermission (${this.type}): ${error.message}`); } } // Read a user permission by ID async getUserPermission(permissionId) { try { if (this.type === "mysql") { const [rows] = await this.db.query( `SELECT id, user_id, onDoc, permission FROM user_permissions WHERE id = ?`, [permissionId] ); if (rows.length === 0) throw new Error("User permission not found"); return rows[0]; } else if (this.type === "mongodb") { const permission = await this.db.collection("user_permissions").findOne({ _id: permissionId }); if (!permission) throw new Error("User permission not found"); return permission; } } catch (error) { throw new Error(`Error in getUserPermission (${this.type}): ${error.message}`); } } // Read all permissions for a specific user (optional filter by onDoc) async getUserPermissions(userId, onDoc = null) { try { if (this.type === "mysql") { const [rows] = await this.db.query( `SELECT id, user_id, onDoc, permission FROM user_permissions WHERE user_id = ? AND (onDoc = ? OR ? IS NULL)`, [userId, onDoc, onDoc] ); return rows; } else if (this.type === "mongodb") { const query = { user_id: userId }; if (onDoc) query.onDoc = onDoc; const permissions = await this.db.collection("user_permissions").find(query).toArray(); return permissions; } } catch (error) { throw new Error(`Error in getUserPermissions (${this.type}): ${error.message}`); } } // Update a user permission async updateUserPermission(permissionId, onDoc, permission) { try { if (this.type === "mysql") { const [result] = await this.db.query( `UPDATE user_permissions SET onDoc = ?, permission = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, [onDoc, permission, permissionId] ); if (result.affectedRows === 0) throw new Error("User permission not found or no changes made"); return { success: true }; } else if (this.type === "mongodb") { const result = await this.db.collection("user_permissions").updateOne( { _id: permissionId }, { $set: { onDoc, permission, updated_at: new Date() } } ); if (result.matchedCount === 0) throw new Error("User permission not found or no changes made"); return { success: true }; } } catch (error) { throw new Error(`Error in updateUserPermission (${this.type}): ${error.message}`); } } // Delete a user permission async deleteUserPermission(permissionId) { try { if (this.type === "mysql") { const [result] = await this.db.query( `DELETE FROM user_permissions WHERE id = ?`, [permissionId] ); if (result.affectedRows === 0) throw new Error("User permission not found"); return { success: true }; } else if (this.type === "mongodb") { const result = await this.db.collection("user_permissions").deleteOne({ _id: permissionId }); if (result.deletedCount === 0) throw new Error("User permission not found"); return { success: true }; } } catch (error) { throw new Error(`Error in deleteUserPermission (${this.type}): ${error.message}`); } } // Check if a user has a specific permission for a document/route async checkUserPermission(userId, onDoc, requiredPermission) { try { if (this.type === "mysql") { const [rows] = await this.db.query( `SELECT permission FROM user_permissions WHERE user_id = ? AND onDoc = ? AND permission = ?`, [userId, onDoc, requiredPermission] ); return rows.length > 0; } else if (this.type === "mongodb") { const permission = await this.db.collection("user_permissions").findOne({ user_id: userId, onDoc, permission: requiredPermission, }); return !!permission; } } catch (error) { throw new Error(`Error in checkUserPermission (${this.type}): ${error.message}`); } } //----- TEAM SCOPE ----- // Team CRUD async createTeam({ name, styling, creatorId }) { try { if (this.type === "mysql") { const [result] = await this.client.execute( "INSERT INTO teams (name, styling) VALUES (?, ?)", [name, styling] ); return { id: result.insertId, name, styling, labels: [] }; } else if (this.type === "mongodb") { const result = await this.db.collection('teams').insertOne({ name, styling, labels: [], created_at: new Date(), updated_at: new Date(), }); return { id: result.insertedId, name, styling, labels: [] }; } } catch (error) { throw new Error(`Error in createTeam (${this.type}): ${error.message}`); } } async getTeam(teamId) { try { if (this.type === "mysql") { const [rows] = await this.client.execute( ` SELECT t.id, t.name, t.styling, t.creator_id, t.labels AS team_labels, tu.user_id, tu.role, tu.labels AS user_labels FROM teams t LEFT JOIN team_users tu ON t.id = tu.team_id WHERE t.id = ? `, [teamId] ); if (!rows.length) { return null; // Ha a csapat nem létezik } const team = { id: rows[0].id, name: rows[0].name, styling: rows[0].styling, creator_id: rows[0].creator_id, labels: rows[0].team_labels ? JSON.parse(rows[0].team_labels) : [], users: [], }; rows.forEach((row) => { if (row.user_id) { team.users.push({ user_id: row.user_id, role: row.role, labels: row.user_labels ? JSON.parse(row.user_labels) : [], }); } }); return team; } else if (this.type === "mongodb") { const team = await this.db .collection("teams") .findOne({ _id: this.toObjectId(teamId) }); console.log("team", team); // Hibakeresés if (!team) { return null; // Ha a csapat nem létezik } const teamMembers = await this.db .collection("team_users") .find({ team_id: teamId }) .toArray(); return { id: team._id.toString(), name: team.name, styling: team.styling || "{}", creator_id: team.creator_id || "", labels: team.labels || [], users: teamMembers.map((member) => ({ user_id: member.user_id, role: member.role, labels: member.labels || [], })), }; } } catch (error) { throw new Error(`Error in getTeam (${this.type}): ${error.message}`); } } async getTeams(userId) { try { if (this.type === "mysql") { const [rows] = await this.client.execute( ` SELECT t.id, t.name, t.styling, t.creator_id, t.labels AS team_labels, tu.user_id, tu.role, tu.labels AS user_labels FROM teams t JOIN team_users tu ON t.id = tu.team_id WHERE tu.user_id = ? `, [userId] ); const teamsMap = {}; rows.forEach((row) => { const teamId = row.id; if (!teamsMap[teamId]) { teamsMap[teamId] = { id: row.id, name: row.name, styling: row.styling, creator_id: row.creator_id, labels: row.team_labels ? JSON.parse(row.team_labels) : [], users: [], }; } teamsMap[teamId].users.push({ user_id: row.user_id, role: row.role, labels: row.user_labels ? JSON.parse(row.user_labels) : [], }); }); return { teams: Object.values(teamsMap) }; } else if (this.type === "mongodb") { console.log("userId", userId); // Lekérjük a team_id-kat, ahol a felhasználó tag const teamIds = await this.db .collection("team_users") .distinct("team_id", { user_id: userId }); console.log("teamIds", teamIds); // Hibakeresés if (!teamIds || teamIds.length === 0) { return { teams: [] }; } // Ellenőrizzük, hogy a teamIds elemei stringek legyenek const validTeamIds = teamIds .filter((id) => id && typeof id === "string") .map((id) => this.toObjectId(id)); if (validTeamIds.length === 0) { console.log("No valid teamIds found"); return { teams: [] }; } // Csapatok lekérdezése const teams = await this.db .collection("teams") .find({ _id: { $in: validTeamIds } }) .toArray(); console.log("teams", teams); // Hibakeresés if (!teams || teams.length === 0) { return { teams: [] }; } // Csapattagok lekérdezése const teamMembers = await this.db .collection("team_users") .find({ team_id: { $in: teamIds } }) .toArray(); console.log("teamMembers", teamMembers); // Hibakeresés const teamsMap = {}; teams.forEach((team) => { // Ellenőrizzük, hogy a team és _id létezik if (team && team._id) { const teamId = team._id.toString(); teamsMap[teamId] = { id: teamId, name: team.name || "", styling: team.styling || "{}", creator_id: team.creator_id || "", labels: team.labels || [], users: [], }; } }); teamMembers.forEach((member) => { // Ellenőrizzük, hogy a member és team_id létezik if (member && member.team_id) { const teamId = member.team_id.toString(); if (teamsMap[teamId]) { teamsMap[teamId].users.push({ user_id: member.user_id || "", role: member.role || "", labels: member.labels || [], }); } } }); return { teams: Object.values(teamsMap) }; } } catch (error) { throw new Error(`Error in getTeams (${this.type}): ${error.message}`); } } async updateTeam(teamId, name, styling, userId) { try { if (this.type === "mysql") { await this.client.execute( "UPDATE teams SET name = ?, styling = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", [name, styling, teamId] ); return { id: teamId, name, styling }; } else if (this.type === "mongodb") { const result = await this.db.collection('teams').findOneAndUpdate( { _id: this.toObjectId(teamId) }, { $set: { name, styling, updated_at: new Date() } }, { returnDocument: 'after' } ); return result.value; } } catch (error) { throw new Error(`Error in updateTeam (${this.type}): ${error.message}`); } } async deleteTeam(teamId, userId) { try { if (this.type === "mysql") { await this.client.execute("DELETE FROM teams WHERE id = ?", [teamId]); return { id: teamId }; } else if (this.type === "mongodb") { await this.db.collection('teams').deleteOne({ _id: this.toObjectId(teamId) }); return { id: teamId }; } } catch (error) { throw new Error(`Error in deleteTeam (${this.type}): ${error.message}`); } } // Team Users CRUD async addTeamUser(teamId, userId, role, addedBy) { try { if (this.type === "mysql") { const [result] = await this.client.execute( "INSERT INTO team_users (team_id, user_id, role) VALUES (?, ?, ?)", [teamId, userId, role] ); return { id: result.insertId, team_id: teamId, user_id: userId, role }; } else if (this.type === "mongodb") { const result = await this.db.collection('team_users').insertOne({ team_id: teamId, user_id: userId, role, labels: [], created_at: new Date(), updated_at: new Date(), }); return { _id: result.insertedId, team_id: teamId, user_id: userId, role }; } } catch (error) { throw new Error(`Error in addTeamUser (${this.type}): ${error.message}`); } } async removeTeamUser(teamId, userId, removedBy) { try { if (this.type === "mysql") { await this.client.execute("DELETE FROM team_users WHERE team_id = ? AND user_id = ?", [teamId, userId]); return { team_id: teamId, user_id: userId }; } else if (this.type === "mongodb") { await this.db.collection('team_users').deleteOne({ team_id: teamId, user_id: userId }); return { team_id: teamId, user_id: userId }; } } catch (error) { throw new Error(`Error in removeTeamUser (${this.type}): ${error.message}`); } } async updateTeamUserRole(teamId, userId, role, updatedBy) { try { if (this.type === "mysql") { await this.client.execute( "UPDATE team_users SET role = ?, updated_at = CURRENT_TIMESTAMP WHERE team_id = ? AND user_id = ?", [role, teamId, userId] ); return { team_id: teamId, user_id: userId, role }; } else if (this.type === "mongodb") { const result = await this.db.collection('team_users').findOneAndUpdate( { team_id: teamId, user_id: userId }, { $set: { role, updated_at: new Date() } }, { returnDocument: 'after' } ); return { team_id: teamId, user_id: userId, role }; } } catch (error) { throw new Error(`Error in updateTeamUserRole (${this.type}): ${error.message}`); } } async updateTeamUserLabels(teamId, userId, labels, updatedBy) { try { if (this.type === "mysql") { await this.client.execute( "UPDATE team_users SET labels = ?, updated_at = CURRENT_TIMESTAMP WHERE team_id = ? AND user_id = ?", [JSON.stringify(labels), teamId, userId] ); return { team_id: teamId, user_id: userId, labels }; } else if (this.type === "mongodb") { const result = await this.db.collection('team_users').findOneAndUpdate( { team_id: teamId, user_id: userId }, { $set: { labels, updated_at: new Date() } }, { returnDocument: 'after' } ); return { team_id: teamId, user_id: userId, labels }; } } catch (error) { throw new Error(`Error in updateTeamUserLabels (${this.type}): ${error.message}`); } } async getTeamUserRole(teamId, userId) { try { if (this.type === "mysql") { const [rows] = await this.client.execute( "SELECT role FROM team_users WHERE team_id = ? AND user_id = ?", [teamId, userId] ); return rows[0]?.role; } else if (this.type === "mongodb") { const result = await this.db.collection('team_users').findOne( { team_id: teamId, user_id: userId }, { projection: { role: 1 } } ); return result?.role; } } catch (error) { throw new Error(`Error in getTeamUserRole (${this.type}): ${error.message}`); } } async listTeams() { try { if (this.type === "mysql") { const [rows] = await this.client.execute("SELECT * FROM teams"); return rows; } else if (this.type === "mongodb") { const result = await this.db.collection('teams').find().toArray(); return result; } } catch (error) { throw new Error(`Error in listTeams (${this.type}): ${error.message}`); } } //---- labels and preferences ---- //---- Labels and Preferences ---- // Labels CRUD Methods // Set (Replace) the entire labels array for a user async setUserLabels(userId, labels) { try { if (this.type === "mysql") { await this.client.execute( "UPDATE users SET labels = ? WHERE id = ?", [JSON.stringify(labels), userId] ); return { user_id: userId, labels }; } else if (this.type === "mongodb") { const result = await this.db.collection('users').findOneAndUpdate( { _id: this.toObjectId(userId) }, { $set: { labels, updated_at: new Date() } }, { returnDocument: 'after' } ); if (!result.value) throw new Error("User not found"); return { user_id: userId, labels }; } } catch (error) { throw new Error(`Error in setUserLabels (${this.type}): ${error.message}`); } } // Retrieve the labels array for a user async getUserLabels(userId) { try { if (this.type === "mysql") { const [rows] = await this.client.execute( "SELECT labels FROM users WHERE id = ?", [userId] ); if (rows.length === 0) throw new Error("User not found"); return rows[0].labels ? JSON.parse(rows[0].labels) : []; } else if (this.type === "mongodb") { const user = await this.db.collection('users').findOne( { _id: this.toObjectId(userId) }, { projection: { labels: 1 } } ); if (!user) throw new Error("User not found"); return user.labels || []; } } catch (error) { throw new Error(`Error in getUserLabels (${this.type}): ${error.message}`); } } // Delete the entire labels array for a user (set to empty array) async deleteUserLabels(userId) { try { if (this.type === "mysql") { await this.client.execute( "UPDATE users SET labels = ? WHERE id = ?", [JSON.stringify([]), userId] ); return { user_id: userId, labels: [] }; } else if (this.type === "mongodb") { const result = await this.db.collection('users').findOneAndUpdate( { _id: this.toObjectId(userId) }, { $set: { labels: [], updated_at: new Date() } }, { returnDocument: 'after' } ); if (!result.value) throw new Error("User not found"); return { user_id: userId, labels: [] }; } } catch (error) { throw new Error(`Error in deleteUserLabels (${this.type}): ${error.message}`); } } // Preferences CRUD Methods // Update or add a specific key-value pair in the preferences object async updateUserPreference(userId, key, value) { try { if (this.type === "mysql") { // First, get the current preferences const [rows] = await this.client.execute( "SELECT preferences FROM users WHERE id = ?", [userId] ); if (rows.length === 0) throw new Error("User not found"); // Parse the current preferences (or initialize as empty object) const currentPrefs = rows[0].preferences ? JSON.parse(rows[0].preferences) : {}; // Update the specific key with the new value currentPrefs[key] = value; // Save the updated preferences await this.client.execute( "UPDATE users SET preferences = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", [JSON.stringify(currentPrefs), userId] ); return { user_id: userId, preferences: currentPrefs }; } else if (this.type === "mongodb") { const result = await this.db.collection('users').findOneAndUpdate( { _id: this.toObjectId(userId) }, { $set: { [`preferences.${key}`]: value, updated_at: new Date() } }, { returnDocument: 'after' } ); if (!result.value) throw new Error("User not found"); return { user_id: userId, preferences: result.value.preferences || {} }; } } catch (error) { throw new Error(`Error in updateUserPreference (${this.type}): ${error.message}`); } } // Delete a specific key from the preferences object async deleteUserPreferenceKey(userId, key) { try { if (this.type === "mysql") { // First, get the current preferences const [rows] = await this.client.execute( "SELECT preferences FROM users WHERE id = ?", [userId] ); if (rows.length === 0) throw new Error("User not found"); // Parse the current preferences (or initialize as empty object) const currentPrefs = rows[0].preferences ? JSON.parse(rows[0].preferences) : {}; // Remove the specific key if (key in currentPrefs) { delete currentPrefs[key]; } // Save the updated preferences await this.client.execute( "UPDATE users SET preferences = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", [JSON.stringify(currentPrefs), userId] ); return { user_id: userId, preferences: currentPrefs }; } else if (this.type === "mongodb") { const result = await this.db.collection('users').findOneAndUpdate( { _id: this.toObjectId(userId) }, { $unset: { [`preferences.${key}`]: "" }, $set: { updated_at: new Date() } }, { returnDocument: 'after' } ); if (!result.value) throw new Error("User not found"); return { user_id: userId, preferences: result.value.preferences || {} }; } } catch (error) { throw new Error(`Error in deleteUserPreferenceKey (${this.type}): ${error.message}`); } } // Retrieve the entire preferences object for a user async getUserPreferences(userId) { try { if (this.type === "mysql") { const [rows] = await this.client.execute( "SELECT preferences FROM