feat: add logging, marketplace, and admin enhancements
Database & Migrations: - Add application_logs table migration for hybrid cloud logging - Add companies table migration and restructure vendor relationships Logging System: - Implement hybrid logging system (database + file) - Add log_service for centralized log management - Create admin logs page with filtering and viewing capabilities - Add init_log_settings.py script for log configuration - Enhance core logging with database integration Marketplace Integration: - Add marketplace admin page with product management - Create marketplace vendor page with product listings - Implement marketplace.js for both admin and vendor interfaces - Add marketplace integration documentation Admin Enhancements: - Add imports management page and functionality - Create settings page for admin configuration - Add vendor themes management page - Enhance vendor detail and edit pages - Improve code quality dashboard and violation details - Add logs viewing and management - Update icons guide and shared icon system Architecture & Documentation: - Document frontend structure and component architecture - Document models structure and relationships - Add vendor-in-token architecture documentation - Add vendor RBAC (role-based access control) documentation - Document marketplace integration patterns - Update architecture patterns documentation Infrastructure: - Add platform static files structure (css, img, js) - Move architecture_scan.py to proper models location - Update model imports and registrations - Enhance exception handling - Update dependency injection patterns UI/UX: - Improve vendor edit interface - Update admin user interface - Enhance page templates documentation - Add vendor marketplace interface
This commit is contained in:
173
static/admin/js/logs.js
Normal file
173
static/admin/js/logs.js
Normal file
@@ -0,0 +1,173 @@
|
||||
// static/admin/js/logs.js
|
||||
|
||||
const logsLog = window.LogConfig?.loggers?.logs || console;
|
||||
|
||||
function adminLogs() {
|
||||
// Get base data
|
||||
const baseData = typeof data === 'function' ? data() : {};
|
||||
|
||||
return {
|
||||
// Inherit base layout functionality from init-alpine.js
|
||||
...baseData,
|
||||
|
||||
// Logs-specific state
|
||||
currentPage: 'logs',
|
||||
loading: true,
|
||||
error: null,
|
||||
successMessage: null,
|
||||
logSource: 'database',
|
||||
logs: [],
|
||||
totalLogs: 0,
|
||||
stats: {
|
||||
total_count: 0,
|
||||
warning_count: 0,
|
||||
error_count: 0,
|
||||
critical_count: 0
|
||||
},
|
||||
selectedLog: null,
|
||||
filters: {
|
||||
level: '',
|
||||
module: '',
|
||||
search: '',
|
||||
skip: 0,
|
||||
limit: 50
|
||||
},
|
||||
logFiles: [],
|
||||
selectedFile: '',
|
||||
fileContent: null,
|
||||
|
||||
async init() {
|
||||
logsLog.info('=== LOGS PAGE INITIALIZING ===');
|
||||
await this.loadStats();
|
||||
await this.loadLogs();
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
this.error = null;
|
||||
this.successMessage = null;
|
||||
await this.loadStats();
|
||||
if (this.logSource === 'database') {
|
||||
await this.loadLogs();
|
||||
} else {
|
||||
await this.loadFileLogs();
|
||||
}
|
||||
},
|
||||
|
||||
async loadStats() {
|
||||
try {
|
||||
const data = await apiClient.get('/admin/logs/statistics?days=7');
|
||||
this.stats = data;
|
||||
logsLog.info('Log statistics loaded:', this.stats);
|
||||
} catch (error) {
|
||||
logsLog.error('Failed to load log statistics:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async loadLogs() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
if (this.filters.level) params.append('level', this.filters.level);
|
||||
if (this.filters.module) params.append('module', this.filters.module);
|
||||
if (this.filters.search) params.append('search', this.filters.search);
|
||||
params.append('skip', this.filters.skip);
|
||||
params.append('limit', this.filters.limit);
|
||||
|
||||
const data = await apiClient.get(`/admin/logs/database?${params}`);
|
||||
this.logs = data.logs;
|
||||
this.totalLogs = data.total;
|
||||
logsLog.info(`Loaded ${this.logs.length} logs (total: ${this.totalLogs})`);
|
||||
} catch (error) {
|
||||
logsLog.error('Failed to load logs:', error);
|
||||
this.error = error.response?.data?.detail || 'Failed to load logs';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadFileLogs() {
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const data = await apiClient.get('/admin/logs/files');
|
||||
this.logFiles = data.files;
|
||||
|
||||
if (this.logFiles.length > 0 && !this.selectedFile) {
|
||||
this.selectedFile = this.logFiles[0].filename;
|
||||
await this.loadFileContent();
|
||||
}
|
||||
logsLog.info(`Loaded ${this.logFiles.length} log files`);
|
||||
} catch (error) {
|
||||
logsLog.error('Failed to load log files:', error);
|
||||
this.error = error.response?.data?.detail || 'Failed to load log files';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async loadFileContent() {
|
||||
if (!this.selectedFile) return;
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const data = await apiClient.get(`/admin/logs/files/${this.selectedFile}?lines=500`);
|
||||
this.fileContent = data;
|
||||
logsLog.info(`Loaded file content for ${this.selectedFile}`);
|
||||
} catch (error) {
|
||||
logsLog.error('Failed to load file content:', error);
|
||||
this.error = error.response?.data?.detail || 'Failed to load file content';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async downloadLogFile() {
|
||||
if (!this.selectedFile) return;
|
||||
|
||||
try {
|
||||
const token = localStorage.getItem('admin_token');
|
||||
// Note: window.open bypasses apiClient, so we need the full path
|
||||
window.open(`/api/v1/admin/logs/files/${this.selectedFile}/download?token=${token}`, '_blank');
|
||||
} catch (error) {
|
||||
logsLog.error('Failed to download log file:', error);
|
||||
this.error = 'Failed to download log file';
|
||||
}
|
||||
},
|
||||
|
||||
resetFilters() {
|
||||
this.filters = {
|
||||
level: '',
|
||||
module: '',
|
||||
search: '',
|
||||
skip: 0,
|
||||
limit: 50
|
||||
};
|
||||
this.loadLogs();
|
||||
},
|
||||
|
||||
nextPage() {
|
||||
this.filters.skip += this.filters.limit;
|
||||
this.loadLogs();
|
||||
},
|
||||
|
||||
previousPage() {
|
||||
this.filters.skip = Math.max(0, this.filters.skip - this.filters.limit);
|
||||
this.loadLogs();
|
||||
},
|
||||
|
||||
showLogDetail(log) {
|
||||
this.selectedLog = log;
|
||||
},
|
||||
|
||||
formatTimestamp(timestamp) {
|
||||
return new Date(timestamp).toLocaleString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
logsLog.info('Logs module loaded');
|
||||
Reference in New Issue
Block a user