@ufdevsllc/authme2.0
Version:
SDK for license management and remote monitoring with automatic system tracking, license validation, and remote control capabilities
353 lines (307 loc) • 12.3 kB
JavaScript
import DatabaseManager from './database-manager.js';
class DataLogger {
constructor(errorHandler = null) {
this.errorHandler = errorHandler;
this.databaseManager = new DatabaseManager(this.errorHandler);
this.isInitialized = false;
}
/**
* Initialize the data logger with database connection
* @returns {Promise<void>}
*/
async init() {
if (this.isInitialized) {
return;
}
const context = {
component: 'data-logger',
operation: 'initialization'
};
try {
if (this.errorHandler) {
await this.errorHandler.initialize();
}
await this.databaseManager.initMonitoringConnection();
this.isInitialized = true;
if (this.errorHandler) {
await this.errorHandler.logInfo('Data logger initialized successfully', context);
} else {
console.log('Data logger initialized successfully');
}
} catch (error) {
if (this.errorHandler) {
await this.errorHandler.handleError(error, context);
} else {
console.error('Failed to initialize DataLogger:', error.message);
}
throw new Error(`DataLogger initialization failed: ${error.message}`);
}
}
/**
* Validate input parameters for logging operations
* @param {string} collectionName - Name of the collection
* @param {*} data - Data to validate
* @param {string} operation - Operation type
* @throws {Error} If validation fails
*/
_validateInput(collectionName, data, operation) {
// Validate collection name
if (this.errorHandler) {
const collectionValidation = this.errorHandler.validateInput(collectionName, {
type: 'string',
required: true,
minLength: 1,
maxLength: 100,
pattern: /^[a-zA-Z0-9_-]+$/
}, { field: 'collectionName' });
if (!collectionValidation.isValid) {
throw new Error(`Invalid collection name: ${collectionValidation.errors.join(', ')}`);
}
// Validate operation
if (operation) {
const operationValidation = this.errorHandler.validateInput(operation, {
type: 'string',
required: false,
maxLength: 50
}, { field: 'operation' });
if (!operationValidation.isValid) {
throw new Error(`Invalid operation: ${operationValidation.errors.join(', ')}`);
}
}
} else {
// Basic validation without error handler
if (!collectionName || typeof collectionName !== 'string') {
throw new Error('Collection name must be a non-empty string');
}
if (operation && typeof operation !== 'string') {
throw new Error('Operation must be a string');
}
}
// Validate data
if (data === null || data === undefined) {
throw new Error('Data cannot be null or undefined');
}
}
/**
* Ensure the logger is initialized before operations
* @throws {Error} If not initialized
*/
_ensureInitialized() {
if (!this.isInitialized) {
throw new Error('DataLogger not initialized. Call init() first.');
}
if (!this.databaseManager.isMonitoringConnected()) {
throw new Error('Monitoring database not connected');
}
}
/**
* Log general data operations to monitoring database
* @param {string} collectionName - Name of the collection to log to
* @param {Object} data - Data to log
* @param {string} operation - Type of operation (create, update, delete, read)
* @returns {Promise<Object>} Result of the logging operation
*/
async logData(collectionName, data, operation = 'unknown') {
this._validateInput(collectionName, data, operation);
this._ensureInitialized();
const context = {
component: 'data-logger',
operation: 'log-data',
collection: collectionName,
dataOperation: operation
};
try {
const logEntry = {
collectionName,
operation,
data: typeof data === 'object' ? JSON.parse(JSON.stringify(data)) : data,
timestamp: new Date(),
logType: 'general_data'
};
let result;
if (this.errorHandler) {
result = await this.errorHandler.executeWithRetry(
async () => {
return await this.databaseManager.saveUserData('usage_logs', logEntry);
},
context,
{ maxRetries: 3 }
);
await this.errorHandler.logInfo(`Data logged successfully to collection: ${collectionName}`, context);
} else {
result = await this.databaseManager.saveUserData('usage_logs', logEntry);
console.log(`Data logged successfully to collection: ${collectionName}`);
}
return result;
} catch (error) {
if (this.errorHandler) {
await this.errorHandler.handleError(error, context);
} else {
console.error('Error logging data:', error.message);
}
throw new Error(`Failed to log data: ${error.message}`);
}
}
/**
* Log user activity and actions
* @param {string} userId - User identifier
* @param {string} action - Action performed by user
* @param {Object} details - Additional details about the action
* @returns {Promise<Object>} Result of the logging operation
*/
async logUserActivity(userId, action, details = {}) {
if (!userId || typeof userId !== 'string') {
throw new Error('User ID must be a non-empty string');
}
if (!action || typeof action !== 'string') {
throw new Error('Action must be a non-empty string');
}
this._ensureInitialized();
try {
const logEntry = {
userId,
action,
details: typeof details === 'object' ? JSON.parse(JSON.stringify(details)) : details,
timestamp: new Date(),
logType: 'user_activity'
};
const result = await this.databaseManager.saveUserData('usage_logs', logEntry);
return result;
} catch (error) {
console.error('Error logging user activity:', error.message);
throw new Error(`Failed to log user activity: ${error.message}`);
}
}
/**
* Log model-specific operations
* @param {string} modelName - Name of the model
* @param {string} operation - Operation performed (save, update, delete, find)
* @param {Object} data - Data involved in the operation
* @returns {Promise<Object>} Result of the logging operation
*/
async logModelOperation(modelName, operation, data) {
if (!modelName || typeof modelName !== 'string') {
throw new Error('Model name must be a non-empty string');
}
this._validateInput('model_operations', data, operation);
this._ensureInitialized();
try {
const logEntry = {
modelName,
operation,
data: typeof data === 'object' ? JSON.parse(JSON.stringify(data)) : data,
timestamp: new Date(),
logType: 'model_operation'
};
const result = await this.databaseManager.saveUserData('usage_logs', logEntry);
return result;
} catch (error) {
console.error('Error logging model operation:', error.message);
throw new Error(`Failed to log model operation: ${error.message}`);
}
}
/**
* Log multiple operations in batch
* @param {Array} operations - Array of operation objects
* @returns {Promise<Array>} Results of all logging operations
*/
async bulkLog(operations) {
if (!Array.isArray(operations)) {
throw new Error('Operations must be an array');
}
if (operations.length === 0) {
throw new Error('Operations array cannot be empty');
}
this._ensureInitialized();
const results = [];
const errors = [];
for (let i = 0; i < operations.length; i++) {
const op = operations[i];
try {
// Validate operation structure
if (!op || typeof op !== 'object') {
throw new Error(`Operation at index ${i} must be an object`);
}
const { type, ...params } = op;
if (!type || typeof type !== 'string') {
throw new Error(`Operation at index ${i} must have a valid type`);
}
let result;
switch (type) {
case 'logData':
const { collectionName, data, operation } = params;
result = await this.logData(collectionName, data, operation);
break;
case 'logUserActivity':
const { userId, action, details } = params;
result = await this.logUserActivity(userId, action, details);
break;
case 'logModelOperation':
const { modelName, operation: modelOp, data: modelData } = params;
result = await this.logModelOperation(modelName, modelOp, modelData);
break;
default:
throw new Error(`Unknown operation type: ${type}`);
}
results.push({ index: i, success: true, result });
} catch (error) {
const errorInfo = {
index: i,
success: false,
error: error.message,
operation: op
};
errors.push(errorInfo);
results.push(errorInfo);
}
}
// If there were errors, log them but don't throw unless all operations failed
if (errors.length > 0) {
console.warn(`${errors.length} out of ${operations.length} bulk operations failed:`, errors);
// If all operations failed, throw an error
if (errors.length === operations.length) {
throw new Error(`All bulk operations failed. First error: ${errors[0].error}`);
}
}
return results;
}
/**
* Check if the data logger is properly initialized and connected
* @returns {boolean} True if initialized and connected
*/
isReady() {
return this.isInitialized && this.databaseManager.isMonitoringConnected();
}
/**
* Close the data logger and database connections
* @returns {Promise<void>}
*/
async close() {
const context = {
component: 'data-logger',
operation: 'close'
};
try {
if (this.databaseManager) {
await this.databaseManager.closeConnection();
}
this.isInitialized = false;
if (this.errorHandler) {
await this.errorHandler.logInfo('Data logger closed successfully', context);
} else {
console.log('Data logger closed successfully');
}
} catch (error) {
if (this.errorHandler) {
await this.errorHandler.handleError(error, context);
} else {
console.error('Error closing data logger:', error);
}
} finally {
if (this.errorHandler) {
await this.errorHandler.cleanup();
}
}
}
}
export default DataLogger;