feat: implement ProcessManager service for lifecycle management and add UI components for dashboard monitoring
This commit is contained in:
@@ -6,6 +6,7 @@ import pidusage from 'pidusage'
|
||||
import { configService } from './ConfigService'
|
||||
import { serviceEventLogger } from './ServiceEventLogger'
|
||||
import { t } from '../utils/i18n'
|
||||
import { portMappingService } from './PortMappingService'
|
||||
|
||||
export class ProcessManager {
|
||||
private processes: Map<string, ChildProcess> = new Map()
|
||||
@@ -341,12 +342,13 @@ export class ProcessManager {
|
||||
return !!(child && child.pid)
|
||||
}
|
||||
|
||||
async getStatuses(): Promise<Record<string, { status: string; cpu?: number; memory?: number }>> {
|
||||
const statuses: Record<string, { status: string; cpu?: number; memory?: number }> = {}
|
||||
async getStatuses(): Promise<Record<string, { status: string; cpu?: number; memory?: number, port?: string | number }>> {
|
||||
const statuses: Record<string, { status: string; cpu?: number; memory?: number, port?: string | number }> = {}
|
||||
|
||||
// Add all tracked states first
|
||||
for (const [name, state] of this.states.entries()) {
|
||||
statuses[name] = { status: state }
|
||||
const port = this.getServicePort(name)
|
||||
statuses[name] = { status: state, port }
|
||||
}
|
||||
|
||||
const entries = Array.from(this.processes.entries())
|
||||
@@ -358,7 +360,8 @@ export class ProcessManager {
|
||||
statuses[name] = {
|
||||
status: currentState === 'stopping' ? 'stopping' : 'running',
|
||||
cpu: Math.round(stats.cpu * 10) / 10,
|
||||
memory: stats.memory
|
||||
memory: stats.memory,
|
||||
port: this.getServicePort(name)
|
||||
}
|
||||
if (currentState !== 'stopping' && currentState !== 'starting') {
|
||||
this.states.set(name, 'running')
|
||||
@@ -381,12 +384,27 @@ export class ProcessManager {
|
||||
// Ensure defaults are present
|
||||
const defaultServices = ['nginx', 'apache', 'mariadb']
|
||||
defaultServices.forEach(s => {
|
||||
if (!statuses[s]) statuses[s] = { status: this.states.get(s) || 'stopped' }
|
||||
if (!statuses[s]) statuses[s] = { status: this.states.get(s) || 'stopped', port: this.getServicePort(s) }
|
||||
})
|
||||
|
||||
return statuses
|
||||
}
|
||||
|
||||
private getServicePort(name: string): string | number | undefined {
|
||||
const settings = configService.getSettings()
|
||||
if (name === 'nginx') return settings.nginxPort
|
||||
if (name === 'apache') return settings.apachePort
|
||||
if (name.startsWith('php:')) {
|
||||
const version = name.split(':')[1]
|
||||
return portMappingService.getPhpPort(version)
|
||||
}
|
||||
if (name.startsWith('mariadb:')) {
|
||||
const version = name.split(':')[1]
|
||||
return portMappingService.getMariaDbPort(version)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
private async checkErrorLogs(name: string) {
|
||||
if (!this.logDir) return
|
||||
const logPaths: Record<string, string> = {
|
||||
|
||||
@@ -69,8 +69,10 @@ export default function DashboardDetails({
|
||||
if (!serviceId) return null;
|
||||
if (serviceId === 'nginx') return 'nginx';
|
||||
if (serviceId === 'apache') return 'apache';
|
||||
if (serviceId.startsWith('php:')) return 'php';
|
||||
if (serviceId.startsWith('mariadb:')) return 'mariadb';
|
||||
if (serviceId.includes(':') || serviceId.includes('-')) {
|
||||
const type = serviceId.includes(':') ? serviceId.split(':')[0] : serviceId.split('-')[0];
|
||||
return type.toLowerCase();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -181,6 +183,24 @@ export default function DashboardDetails({
|
||||
}
|
||||
|
||||
const currentStatus = status[serviceId] || { status: 'stopped' };
|
||||
|
||||
const getPort = () => {
|
||||
if (currentStatus.port) return currentStatus.port;
|
||||
// Fallback for stopped services or missing data
|
||||
if (serviceId === 'nginx') return settings.nginxPort;
|
||||
if (serviceId === 'apache') return settings.apachePort;
|
||||
if (serviceType === 'php') {
|
||||
const version = serviceId.includes(':') ? serviceId.split(':')[1] : serviceId.split('-')[1];
|
||||
return version ? '...' : settings.phpPort;
|
||||
}
|
||||
if (serviceType === 'mariadb' || serviceType === 'mysql') {
|
||||
const version = serviceId.includes(':') ? serviceId.split(':')[1] : serviceId.split('-')[1];
|
||||
if (version && settings.mariaDbPorts && settings.mariaDbPorts[version]) return settings.mariaDbPorts[version];
|
||||
if (version) return '...';
|
||||
return settings.mariaDbPort;
|
||||
}
|
||||
return 'n/a';
|
||||
};
|
||||
const isRunning = currentStatus.status === 'running';
|
||||
|
||||
const serviceProjects = projects.filter(p => {
|
||||
@@ -247,13 +267,13 @@ export default function DashboardDetails({
|
||||
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'rgba(255,255,255,0.05)', px: 3 }}>
|
||||
<Tabs value={activeTab} onChange={(_e, v) => setActiveTab(v)}>
|
||||
<Tab label={t('common.overview', 'Genel Bakış')} />
|
||||
<Tab label={t('dashboard.service_settings', 'Servis Ayarları')} />
|
||||
<Tab label={t('dashboard.overview')} />
|
||||
<Tab label={t('dashboard.service_settings')} />
|
||||
{(serviceId === 'nginx' || serviceId === 'apache' || serviceType === 'php') && (
|
||||
<Tab label={t('dashboard.project_settings', 'Proje Ayarları')} />
|
||||
<Tab label={t('dashboard.project_settings')} />
|
||||
)}
|
||||
<Tab label={t('diagnostics.logs', 'Hata Logları')} />
|
||||
<Tab label={t('projects.title', 'Projeler')} />
|
||||
<Tab label={t('diagnostics.logs')} />
|
||||
<Tab label={t('projects.title')} />
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
@@ -261,35 +281,26 @@ export default function DashboardDetails({
|
||||
{activeTab === 0 && (
|
||||
<Grid container spacing={4}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 2, color: 'primary.main' }}>SYSTEM STATS</Typography>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 2, color: 'primary.main' }}>{t('dashboard.system_stats')}</Typography>
|
||||
<Paper sx={{ p: 3, bgcolor: 'rgba(0,0,0,0.2)', borderRadius: 3, border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||
<ResourceMonitor cpu={currentStatus.cpu} memory={currentStatus.memory} />
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 2, color: 'primary.main' }}>SERVICE INFO</Typography>
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 800, mb: 2, color: 'primary.main' }}>{t('dashboard.service_info')}</Typography>
|
||||
<Paper sx={{ p: 3, bgcolor: 'rgba(0,0,0,0.2)', borderRadius: 3, border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="body2" sx={{ opacity: 0.5 }}>Port</Typography>
|
||||
<Typography variant="body2" sx={{ opacity: 0.5 }}>{t('common.port')}</Typography>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||||
{currentStatus.port || (() => {
|
||||
if (serviceId === 'nginx') return settings.nginxPort;
|
||||
if (serviceId === 'apache') return settings.apachePort;
|
||||
if (serviceType === 'php') return settings.phpPort;
|
||||
if (serviceType === 'mariadb') {
|
||||
const verId = serviceId.split(':')[1];
|
||||
return (settings.mariaDbPorts && settings.mariaDbPorts[verId]) || settings.mariaDbPort;
|
||||
}
|
||||
return 'n/a';
|
||||
})()}
|
||||
{getPort()}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Divider sx={{ borderColor: 'rgba(255,255,255,0.05)' }} />
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography variant="body2" sx={{ opacity: 0.5 }}>Status</Typography>
|
||||
<Typography variant="body2" sx={{ opacity: 0.5 }}>{t('common.status')}</Typography>
|
||||
<Typography variant="body2" sx={{ fontWeight: 600, color: isRunning ? 'success.main' : 'error.main' }}>
|
||||
{currentStatus.status.toUpperCase()}
|
||||
{(isRunning ? t('common.running') : t('common.stopped')).toUpperCase()}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
Typography,
|
||||
List,
|
||||
ListItemButton,
|
||||
Chip,
|
||||
CircularProgress
|
||||
} from '@mui/material'
|
||||
import {
|
||||
@@ -62,6 +61,11 @@ export default function DashboardSidebar({
|
||||
<Typography variant="body2" noWrap sx={{ fontWeight: 600 }}>
|
||||
{label}
|
||||
</Typography>
|
||||
{isRunning && status[serviceKey] && (
|
||||
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', display: 'block', fontSize: '0.65rem', mt: -0.5 }}>
|
||||
Port: {status[serviceKey].port || '?'} | CPU: {Math.round(status[serviceKey].cpu || 0)}% | RAM: {Math.round((status[serviceKey].memory || 0) / (1024 * 1024))}MB
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
<Box sx={{
|
||||
width: 10,
|
||||
|
||||
@@ -56,7 +56,13 @@
|
||||
"start_all": "Alle starten",
|
||||
"stop_all": "Alle stoppen",
|
||||
"all_started": "Alle Dienste wurden gestartet.",
|
||||
"all_stopped": "Alle Dienste wurden gestoppt."
|
||||
"all_stopped": "Alle Dienste wurden gestoppt.",
|
||||
"overview": "Übersicht",
|
||||
"service_settings": "Service-Einstellungen",
|
||||
"project_settings": "Projekt-Einstellungen",
|
||||
"select_service": "Bitte wählen Sie einen Dienst aus",
|
||||
"system_stats": "SYSTEMSTATISTIKEN",
|
||||
"service_info": "SERVICEINFO"
|
||||
},
|
||||
"projects": {
|
||||
"title": "Meine Projekte",
|
||||
|
||||
@@ -61,7 +61,13 @@
|
||||
"start_all": "Start All",
|
||||
"stop_all": "Stop All",
|
||||
"all_started": "All services started.",
|
||||
"all_stopped": "All services stopped."
|
||||
"all_stopped": "All services stopped.",
|
||||
"overview": "Overview",
|
||||
"service_settings": "Service Settings",
|
||||
"project_settings": "Project Settings",
|
||||
"select_service": "Please select a service",
|
||||
"system_stats": "SYSTEM STATS",
|
||||
"service_info": "SERVICE INFO"
|
||||
},
|
||||
"projects": {
|
||||
"title": "My Projects",
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
"reset_confirm_text": "{{service}} verilerini sıfırlamak istiyor musunuz? Bu işlem tüm verileri siler ve temiz kurulum yapar.",
|
||||
"reset_btn": "Evet, Sıfırla",
|
||||
"operating": "İşlem devam ediyor...",
|
||||
"please_wait": "Lütfen bekleyin..."
|
||||
"please_wait": "Lütfen bekleyin...",
|
||||
"status": "Durum"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Özet",
|
||||
@@ -61,7 +62,13 @@
|
||||
"start_all": "Tümünü Başlat",
|
||||
"stop_all": "Tümünü Durdur",
|
||||
"all_started": "Tüm servisler başlatıldı.",
|
||||
"all_stopped": "Tüm servisler durduruldu."
|
||||
"all_stopped": "Tüm servisler durduruldu.",
|
||||
"overview": "Genel Bakış",
|
||||
"service_settings": "Servis Ayarları",
|
||||
"project_settings": "Proje Ayarları",
|
||||
"select_service": "Lütfen bir servis seçin",
|
||||
"system_stats": "SİSTEM İSTATİSTİKLERİ",
|
||||
"service_info": "SERVİS BİLGİLERİ"
|
||||
},
|
||||
"projects": {
|
||||
"title": "Projelerim",
|
||||
|
||||
Reference in New Issue
Block a user