Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
193
admin-ui/js/services/audit-service.js
Normal file
193
admin-ui/js/services/audit-service.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Audit Log Service - User action history tracking
|
||||
*/
|
||||
|
||||
import api from '../core/api.js';
|
||||
import logger from '../core/logger.js';
|
||||
|
||||
class AuditService {
|
||||
constructor() {
|
||||
this.cache = {
|
||||
categories: null,
|
||||
actions: null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audit log with filters
|
||||
*/
|
||||
async getAuditLog(filters = {}) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (filters.project_id) params.append('project_id', filters.project_id);
|
||||
if (filters.user_id) params.append('user_id', filters.user_id);
|
||||
if (filters.action) params.append('action', filters.action);
|
||||
if (filters.category) params.append('category', filters.category);
|
||||
if (filters.entity_type) params.append('entity_type', filters.entity_type);
|
||||
if (filters.severity) params.append('severity', filters.severity);
|
||||
if (filters.start_date) params.append('start_date', filters.start_date);
|
||||
if (filters.end_date) params.append('end_date', filters.end_date);
|
||||
if (filters.limit) params.append('limit', filters.limit);
|
||||
if (filters.offset) params.append('offset', filters.offset);
|
||||
|
||||
try {
|
||||
const response = await api.get(`/audit?${params.toString()}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
logger.error('AuditService', 'Failed to fetch audit log', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get audit statistics
|
||||
*/
|
||||
async getStats() {
|
||||
try {
|
||||
return await api.get('/audit/stats');
|
||||
} catch (error) {
|
||||
logger.error('AuditService', 'Failed to fetch audit stats', error);
|
||||
return {
|
||||
by_category: {},
|
||||
by_user: {},
|
||||
total_count: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all categories
|
||||
*/
|
||||
async getCategories() {
|
||||
if (this.cache.categories) {
|
||||
return this.cache.categories;
|
||||
}
|
||||
|
||||
try {
|
||||
this.cache.categories = await api.get('/audit/categories');
|
||||
return this.cache.categories;
|
||||
} catch (error) {
|
||||
logger.error('AuditService', 'Failed to fetch categories', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all actions
|
||||
*/
|
||||
async getActions() {
|
||||
if (this.cache.actions) {
|
||||
return this.cache.actions;
|
||||
}
|
||||
|
||||
try {
|
||||
this.cache.actions = await api.get('/audit/actions');
|
||||
return this.cache.actions;
|
||||
} catch (error) {
|
||||
logger.error('AuditService', 'Failed to fetch actions', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create audit entry
|
||||
*/
|
||||
async logAction(entry) {
|
||||
try {
|
||||
await api.post('/audit', entry);
|
||||
logger.info('AuditService', 'Action logged', { action: entry.action });
|
||||
} catch (error) {
|
||||
logger.error('AuditService', 'Failed to log action', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export audit log
|
||||
*/
|
||||
async export(filters = {}, format = 'json') {
|
||||
const params = new URLSearchParams({ format });
|
||||
|
||||
if (filters.project_id) params.append('project_id', filters.project_id);
|
||||
if (filters.category) params.append('category', filters.category);
|
||||
if (filters.start_date) params.append('start_date', filters.start_date);
|
||||
if (filters.end_date) params.append('end_date', filters.end_date);
|
||||
|
||||
try {
|
||||
const url = `${api.baseUrl}/audit/export?${params.toString()}`;
|
||||
window.open(url, '_blank');
|
||||
logger.info('AuditService', 'Export initiated', { format });
|
||||
} catch (error) {
|
||||
logger.error('AuditService', 'Failed to export', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity badge class
|
||||
*/
|
||||
getSeverityClass(severity) {
|
||||
const classes = {
|
||||
'info': 'badge--primary',
|
||||
'warning': 'badge--warning',
|
||||
'critical': 'badge--destructive'
|
||||
};
|
||||
return classes[severity] || 'badge--secondary';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category icon
|
||||
*/
|
||||
getCategoryIcon(category) {
|
||||
const icons = {
|
||||
'design_system': '🎨',
|
||||
'code': '⚛️',
|
||||
'configuration': '⚙️',
|
||||
'project': '📁',
|
||||
'team': '👥',
|
||||
'storybook': '📚',
|
||||
'other': '📌'
|
||||
};
|
||||
return icons[category] || '📌';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format timestamp
|
||||
*/
|
||||
formatTimestamp(timestamp) {
|
||||
const date = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diff = now - date;
|
||||
|
||||
// Less than 1 minute
|
||||
if (diff < 60000) {
|
||||
return 'Just now';
|
||||
}
|
||||
|
||||
// Less than 1 hour
|
||||
if (diff < 3600000) {
|
||||
const minutes = Math.floor(diff / 60000);
|
||||
return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
||||
}
|
||||
|
||||
// Less than 1 day
|
||||
if (diff < 86400000) {
|
||||
const hours = Math.floor(diff / 3600000);
|
||||
return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
||||
}
|
||||
|
||||
// Less than 7 days
|
||||
if (diff < 604800000) {
|
||||
const days = Math.floor(diff / 86400000);
|
||||
return `${days} day${days > 1 ? 's' : ''} ago`;
|
||||
}
|
||||
|
||||
// Show full date
|
||||
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
const auditService = new AuditService();
|
||||
|
||||
export default auditService;
|
||||
export { AuditService };
|
||||
Reference in New Issue
Block a user