feat: implement ProcessManager service for lifecycle management and add UI components for dashboard monitoring
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
<VirtualHost *:{{PORT}}>
|
||||
ServerName {{HOST}}
|
||||
DocumentRoot "{{PATH}}"
|
||||
# Subdirectory Alias for {{HOST}}
|
||||
Alias /{{HOST}} "{{PATH}}"
|
||||
|
||||
<Directory "{{PATH}}">
|
||||
Options Indexes FollowSymLinks ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
|
||||
# --- KRİTİK EKLEME BURASI ---
|
||||
# PHP'ye php.ini dosyasını {{PHP_DIR}} klasöründe aramasını söyler
|
||||
SetEnv PHPRC "{{PHP_DIR}}"
|
||||
# ----------------------------
|
||||
|
||||
# Passing Authorization header to PHP
|
||||
CGIPassAuth On
|
||||
|
||||
@@ -15,11 +19,11 @@
|
||||
|
||||
# PHP Handling via mod_actions and CGI
|
||||
<FilesMatch "\.php$">
|
||||
SetHandler application/x-httpd-php-{{HOST}}
|
||||
SetHandler application/x-httpd-php-alias-{{HOST}}
|
||||
</FilesMatch>
|
||||
|
||||
# Action maps the handler to the binary via the ScriptAlias URL
|
||||
Action application/x-httpd-php-{{HOST}} "{{PHP_BIN_URL}}/php-cgi.exe"
|
||||
Action application/x-httpd-php-alias-{{HOST}} "{{PHP_BIN_URL}}/php-cgi.exe"
|
||||
</Directory>
|
||||
|
||||
# ScriptAlias definition for the project's PHP CGI binary
|
||||
@@ -30,4 +34,3 @@
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
@@ -0,0 +1,33 @@
|
||||
<VirtualHost *:{{PORT}}>
|
||||
ServerName {{HOST}}
|
||||
DocumentRoot "{{PATH}}"
|
||||
|
||||
<Directory "{{PATH}}">
|
||||
Options Indexes FollowSymLinks ExecCGI
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
|
||||
# Passing Authorization header to PHP
|
||||
CGIPassAuth On
|
||||
|
||||
# Directory index handling
|
||||
DirectoryIndex index.php index.html
|
||||
|
||||
# PHP Handling via mod_actions and CGI
|
||||
<FilesMatch "\.php$">
|
||||
SetHandler application/x-httpd-php-{{HOST}}
|
||||
</FilesMatch>
|
||||
|
||||
# Action maps the handler to the binary via the ScriptAlias URL
|
||||
Action application/x-httpd-php-{{HOST}} "{{PHP_BIN_URL}}/php-cgi.exe"
|
||||
</Directory>
|
||||
|
||||
# ScriptAlias definition for the project's PHP CGI binary
|
||||
ScriptAlias {{PHP_BIN_URL}}/ "{{PHP_DIR}}/"
|
||||
|
||||
<Directory "{{PHP_DIR}}">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
+4
-1
@@ -11,5 +11,8 @@
|
||||
"4.4.9": 9009,
|
||||
"3.0.x (latest)": 9010
|
||||
},
|
||||
"mariadb": {}
|
||||
"mariadb": {
|
||||
"11.4.2": 3306,
|
||||
"10.11.8": 3307
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,119 @@
|
||||
[
|
||||
{
|
||||
"service": "php:5.6.40",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "zhj5uo002",
|
||||
"timestamp": 1775188480028
|
||||
},
|
||||
{
|
||||
"service": "php:7.4.33",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "lnxl5gj0x",
|
||||
"timestamp": 1775188480010
|
||||
},
|
||||
{
|
||||
"service": "php:8.2.30",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "d7pzpblst",
|
||||
"timestamp": 1775188479993
|
||||
},
|
||||
{
|
||||
"service": "php:8.5.4",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "9v9w3li68",
|
||||
"timestamp": 1775188479975
|
||||
},
|
||||
{
|
||||
"service": "mariadb:10.11.8",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "q2w07qros",
|
||||
"timestamp": 1775188479689
|
||||
},
|
||||
{
|
||||
"service": "mariadb:11.4.2",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "2p8k4gdxd",
|
||||
"timestamp": 1775188479666
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "pvto9aq7v",
|
||||
"timestamp": 1775188478980
|
||||
},
|
||||
{
|
||||
"service": "nginx",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "cozuqx8yf",
|
||||
"timestamp": 1775188478919
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "n6kxec3vz",
|
||||
"timestamp": 1775187974649
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "exit",
|
||||
"status": "failure",
|
||||
"details": "Exit code: null",
|
||||
"id": "nt1xnmbop",
|
||||
"timestamp": 1775187970713
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "stop",
|
||||
"status": "info",
|
||||
"id": "cf6yzeo2h",
|
||||
"timestamp": 1775187970709
|
||||
},
|
||||
{
|
||||
"service": "php:7.4.33",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "kcijsqygj",
|
||||
"timestamp": 1775187963197
|
||||
},
|
||||
{
|
||||
"service": "php:7.4.33",
|
||||
"action": "exit",
|
||||
"status": "failure",
|
||||
"details": "Exit code: null",
|
||||
"id": "9f03j1ihl",
|
||||
"timestamp": 1775187959015
|
||||
},
|
||||
{
|
||||
"service": "php:7.4.33",
|
||||
"action": "stop",
|
||||
"status": "info",
|
||||
"id": "meezjpt0j",
|
||||
"timestamp": 1775187959002
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "4zl8frca6",
|
||||
"timestamp": 1775187929780
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "exit",
|
||||
"status": "failure",
|
||||
"details": "Exit code: 1",
|
||||
"id": "bjo67q9pj",
|
||||
"timestamp": 1775187928384
|
||||
},
|
||||
{
|
||||
"service": "php:5.6.40",
|
||||
"action": "start",
|
||||
|
||||
@@ -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