Documentation: - Add comprehensive capacity planning guide (docs/architecture/capacity-planning.md) - Add operations docs: platform-health, capacity-monitoring, image-storage - Link pricing strategy to capacity planning documentation - Update mkdocs.yml with new Operations section Image Upload System: - Add ImageService with WebP conversion and sharded directory structure - Generate multiple size variants (original, 800px, 200px) - Add storage stats endpoint for monitoring - Add Pillow dependency for image processing Platform Health Monitoring: - Add /admin/platform-health page with real-time metrics - Show CPU, memory, disk usage with progress bars - Display capacity thresholds with status indicators - Generate scaling recommendations automatically - Determine infrastructure tier based on usage - Add psutil dependency for system metrics Admin UI: - Add Capacity Monitor to Platform Health section in sidebar - Create platform-health.html template with stats cards - Create platform-health.js for Alpine.js state management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
129 lines
3.5 KiB
JavaScript
129 lines
3.5 KiB
JavaScript
// static/admin/js/platform-health.js
|
|
/**
|
|
* Admin platform health monitoring page logic
|
|
* Displays system metrics, capacity thresholds, and scaling recommendations
|
|
*/
|
|
|
|
const adminPlatformHealthLog = window.LogConfig.loggers.adminPlatformHealth ||
|
|
window.LogConfig.createLogger('adminPlatformHealth', false);
|
|
|
|
adminPlatformHealthLog.info('Loading...');
|
|
|
|
function adminPlatformHealth() {
|
|
adminPlatformHealthLog.info('adminPlatformHealth() called');
|
|
|
|
return {
|
|
// Inherit base layout state
|
|
...data(),
|
|
|
|
// Set page identifier
|
|
currentPage: 'platform-health',
|
|
|
|
// Loading states
|
|
loading: true,
|
|
error: '',
|
|
|
|
// Health data
|
|
health: null,
|
|
|
|
// Auto-refresh interval (30 seconds)
|
|
refreshInterval: null,
|
|
|
|
async init() {
|
|
adminPlatformHealthLog.info('Platform Health init() called');
|
|
|
|
// Guard against multiple initialization
|
|
if (window._adminPlatformHealthInitialized) {
|
|
adminPlatformHealthLog.warn('Already initialized, skipping');
|
|
return;
|
|
}
|
|
window._adminPlatformHealthInitialized = true;
|
|
|
|
// Load initial data
|
|
await this.loadHealth();
|
|
|
|
// Set up auto-refresh every 30 seconds
|
|
this.refreshInterval = setInterval(() => {
|
|
this.loadHealth();
|
|
}, 30000);
|
|
|
|
adminPlatformHealthLog.info('Platform Health initialization complete');
|
|
},
|
|
|
|
/**
|
|
* Clean up on component destroy
|
|
*/
|
|
destroy() {
|
|
if (this.refreshInterval) {
|
|
clearInterval(this.refreshInterval);
|
|
this.refreshInterval = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Load platform health data
|
|
*/
|
|
async loadHealth() {
|
|
this.loading = true;
|
|
this.error = '';
|
|
|
|
try {
|
|
const response = await apiClient.get('/admin/platform/health');
|
|
this.health = response;
|
|
|
|
adminPlatformHealthLog.info('Loaded health data:', {
|
|
status: response.overall_status,
|
|
tier: response.infrastructure_tier
|
|
});
|
|
} catch (error) {
|
|
adminPlatformHealthLog.error('Failed to load health:', error);
|
|
this.error = error.message || 'Failed to load platform health';
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Manual refresh
|
|
*/
|
|
async refresh() {
|
|
await this.loadHealth();
|
|
},
|
|
|
|
/**
|
|
* Format number with locale
|
|
*/
|
|
formatNumber(num) {
|
|
if (num === null || num === undefined) return '0';
|
|
if (typeof num === 'number' && num % 1 !== 0) {
|
|
return num.toFixed(2);
|
|
}
|
|
return new Intl.NumberFormat('en-US').format(num);
|
|
},
|
|
|
|
/**
|
|
* Format storage size
|
|
*/
|
|
formatStorage(gb) {
|
|
if (gb === null || gb === undefined) return '0 GB';
|
|
if (gb < 1) {
|
|
return (gb * 1024).toFixed(0) + ' MB';
|
|
}
|
|
return gb.toFixed(2) + ' GB';
|
|
},
|
|
|
|
/**
|
|
* Format timestamp
|
|
*/
|
|
formatTime(timestamp) {
|
|
if (!timestamp) return 'Unknown';
|
|
try {
|
|
const date = new Date(timestamp);
|
|
return date.toLocaleTimeString();
|
|
} catch (e) {
|
|
return 'Unknown';
|
|
}
|
|
}
|
|
};
|
|
}
|