Files
dzinesco 8ed7ae95d1 feat: comprehensive project completion and documentation
- Enhanced event creation wizard with multi-step validation
- Added advanced QR scanning system with offline support
- Implemented comprehensive territory management features
- Expanded analytics with export functionality and KPIs
- Created complete design token system with theme switching
- Added 25+ Playwright test files for comprehensive coverage
- Implemented enterprise-grade permission system
- Enhanced component library with 80+ React components
- Added Firebase integration for deployment
- Completed Phase 3 development goals substantially

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 15:04:37 -06:00

310 lines
11 KiB
JavaScript

"use strict";
/**
* Structured Logger Utility for Firebase Cloud Functions
*
* Provides consistent structured logging with proper data masking
* and performance tracking for scanner operations.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sentry = exports.logger = void 0;
exports.withLogging = withLogging;
const firebase_functions_1 = require("firebase-functions");
const Sentry = __importStar(require("@sentry/node"));
exports.Sentry = Sentry;
// Initialize Sentry for Cloud Functions
const initializeSentry = () => {
// Only initialize if DSN is provided and not a mock
const dsn = process.env.SENTRY_DSN;
if (!dsn || dsn.includes('mock')) {
console.info('Sentry: Skipping initialization (no DSN or mock DSN detected)');
return;
}
Sentry.init({
dsn,
environment: process.env.NODE_ENV || 'production',
tracesSampleRate: 0.1,
integrations: [
// Add Node.js specific integrations
Sentry.httpIntegration(),
Sentry.expressIntegration(),
],
beforeSend: (event, hint) => {
// Filter out noisy errors
if (event.exception?.values?.[0]?.type === 'TypeError' &&
event.exception?.values?.[0]?.value?.includes('fetch')) {
return null;
}
return event;
},
});
};
// Initialize Sentry when module loads
initializeSentry();
/**
* Mask sensitive data in QR codes, tokens, or other sensitive strings
*/
function maskSensitiveData(data) {
if (!data || data.length <= 8) {
return '***';
}
// Show first 4 and last 4 characters, mask the middle
const start = data.substring(0, 4);
const end = data.substring(data.length - 4);
const maskLength = Math.min(data.length - 8, 20); // Cap mask length
const mask = '*'.repeat(maskLength);
return `${start}${mask}${end}`;
}
/**
* Format log context with sensitive data masking
*/
function formatLogContext(context) {
const formatted = {};
// Copy non-sensitive fields directly
const safeCopyFields = ['sessionId', 'accountId', 'orgId', 'eventId', 'ticketTypeId', 'deviceId', 'userId', 'operation'];
for (const field of safeCopyFields) {
if (context[field]) {
formatted[field] = context[field];
}
}
// Mask sensitive fields
if (context.qr) {
formatted.qr_masked = maskSensitiveData(context.qr);
}
if (context.deviceId) {
formatted.device_short = context.deviceId.split('_')[1]?.substring(0, 8) || 'unknown';
}
formatted.timestamp = new Date().toISOString();
return formatted;
}
/**
* Core structured logger class
*/
class StructuredLogger {
/**
* Log scanner verification result with full context
*/
logScannerVerify(data) {
const logData = {
...formatLogContext(data),
result: data.result,
latencyMs: data.latencyMs,
reason: data.reason,
timestamp: data.timestamp || new Date().toISOString(),
};
// Use different log levels based on result
if (data.result === 'valid') {
firebase_functions_1.logger.info('Scanner verification successful', logData);
}
else if (data.result === 'already_scanned') {
firebase_functions_1.logger.warn('Scanner verification - already scanned', logData);
}
else {
firebase_functions_1.logger.warn('Scanner verification failed', logData);
}
// Send to Sentry if it's an error or concerning result
if (data.result === 'invalid' && data.reason !== 'ticket_not_found') {
Sentry.withScope((scope) => {
scope.setTag('feature', 'scanner');
scope.setTag('scanner.result', data.result);
scope.setContext('scanner_verification', logData);
Sentry.captureMessage(`Scanner verification failed: ${data.reason}`, 'warning');
});
}
}
/**
* Log performance metrics for scanner operations
*/
logPerformance(data) {
const logData = {
operation: data.operation,
duration_ms: data.duration,
...(data.context ? formatLogContext(data.context) : {}),
metadata: data.metadata,
timestamp: new Date().toISOString(),
};
firebase_functions_1.logger.info('Performance metric', logData);
// Send slow operations to Sentry
if (data.duration > 5000) { // Operations slower than 5 seconds
Sentry.withScope((scope) => {
scope.setTag('feature', 'performance');
scope.setTag('performance.operation', data.operation);
scope.setContext('performance_metric', logData);
Sentry.captureMessage(`Slow operation: ${data.operation} took ${data.duration}ms`, 'warning');
});
}
}
/**
* Log general information with context
*/
info(message, context, metadata) {
const logData = {
message,
...(context ? formatLogContext(context) : {}),
...metadata,
timestamp: new Date().toISOString(),
};
firebase_functions_1.logger.info(message, logData);
}
/**
* Log warnings with context
*/
warn(message, context, metadata) {
const logData = {
message,
...(context ? formatLogContext(context) : {}),
...metadata,
timestamp: new Date().toISOString(),
};
firebase_functions_1.logger.warn(message, logData);
// Send warnings to Sentry with context
Sentry.withScope((scope) => {
if (context?.operation) {
scope.setTag('operation', context.operation);
}
scope.setContext('warning_context', logData);
Sentry.captureMessage(message, 'warning');
});
}
/**
* Log errors with context and send to Sentry
*/
error(message, error, context, metadata) {
const logData = {
message,
error_message: error?.message,
error_stack: error?.stack,
...(context ? formatLogContext(context) : {}),
...metadata,
timestamp: new Date().toISOString(),
};
firebase_functions_1.logger.error(message, logData);
// Send to Sentry with full context
Sentry.withScope((scope) => {
if (context?.operation) {
scope.setTag('operation', context.operation);
}
if (context?.sessionId) {
scope.setTag('scanner.session', context.sessionId);
}
scope.setContext('error_context', logData);
if (error) {
Sentry.captureException(error);
}
else {
Sentry.captureMessage(message, 'error');
}
});
}
/**
* Log debug information (only in development)
*/
debug(message, context, metadata) {
if (process.env.NODE_ENV !== 'production') {
const logData = {
message,
...(context ? formatLogContext(context) : {}),
...metadata,
timestamp: new Date().toISOString(),
};
firebase_functions_1.logger.debug(message, logData);
}
}
/**
* Capture exception directly to Sentry with context
*/
captureException(error, context) {
Sentry.withScope((scope) => {
if (context) {
scope.setContext('exception_context', formatLogContext(context));
if (context.operation) {
scope.setTag('operation', context.operation);
}
if (context.sessionId) {
scope.setTag('scanner.session', context.sessionId);
}
}
Sentry.captureException(error);
});
}
/**
* Start a performance transaction
*/
startTransaction(name, op) {
return Sentry.startSpan({ name, op }, () => { });
}
/**
* Add breadcrumb for debugging
*/
addBreadcrumb(message, category = 'general', data) {
Sentry.addBreadcrumb({
message,
category,
level: 'info',
data: {
timestamp: new Date().toISOString(),
...data,
},
});
}
}
// Singleton logger instance
exports.logger = new StructuredLogger();
/**
* Middleware wrapper for Cloud Functions to automatically log performance
*/
function withLogging(operationName, fn, contextExtractor) {
return async (...args) => {
const startTime = performance.now();
const context = contextExtractor ? contextExtractor(...args) : undefined;
exports.logger.addBreadcrumb(`Starting operation: ${operationName}`, 'function', context);
try {
const result = await fn(...args);
const duration = performance.now() - startTime;
exports.logger.logPerformance({
operation: operationName,
duration,
context,
});
return result;
}
catch (error) {
const duration = performance.now() - startTime;
exports.logger.error(`Operation failed: ${operationName}`, error, context, { duration });
throw error;
}
};
}
//# sourceMappingURL=logger.js.map