feat: implement UI components for PHP extension management, server tuning, and MariaDB database operations

This commit is contained in:
Ümit Tunç
2026-04-03 14:23:22 +03:00
parent 499fdef661
commit 2413c19393
9 changed files with 234 additions and 39 deletions
+176
View File
@@ -1,4 +1,180 @@
[ [
{
"service": "php:5.6.40",
"action": "start",
"status": "success",
"id": "lswj9e3dr",
"timestamp": 1775215366381
},
{
"service": "php:7.4.33",
"action": "start",
"status": "success",
"id": "ygy1lszgv",
"timestamp": 1775215366364
},
{
"service": "php:8.2.30",
"action": "start",
"status": "success",
"id": "uafoj8ms0",
"timestamp": 1775215366347
},
{
"service": "php:8.5.4",
"action": "start",
"status": "success",
"id": "9om7zwoix",
"timestamp": 1775215366330
},
{
"service": "mariadb:10.11.8",
"action": "start",
"status": "success",
"id": "iauegprtk",
"timestamp": 1775215365870
},
{
"service": "mariadb:11.4.2",
"action": "start",
"status": "success",
"id": "ub17l46ie",
"timestamp": 1775215365851
},
{
"service": "apache",
"action": "start",
"status": "success",
"id": "9s4gcc69m",
"timestamp": 1775215365334
},
{
"service": "nginx",
"action": "start",
"status": "success",
"id": "ejkeubl89",
"timestamp": 1775215365279
},
{
"service": "mariadb:10.11.8",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "15n8prhqk",
"timestamp": 1775215363028
},
{
"service": "mariadb:11.4.2",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "shkq1j2z6",
"timestamp": 1775215363020
},
{
"service": "php:8.5.4",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "ymicfo8vr",
"timestamp": 1775215363013
},
{
"service": "php:8.2.30",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "lj6mzphe2",
"timestamp": 1775215363006
},
{
"service": "php:5.6.40",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "jviowcog6",
"timestamp": 1775215363000
},
{
"service": "php:7.4.33",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "hrec85n0o",
"timestamp": 1775215362994
},
{
"service": "apache",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "ylz7lqxot",
"timestamp": 1775215362970
},
{
"service": "nginx",
"action": "exit",
"status": "failure",
"details": "Exit code: 1",
"id": "gt4zol9k7",
"timestamp": 1775215362967
},
{
"service": "php:5.6.40",
"action": "start",
"status": "success",
"id": "4561oqfbq",
"timestamp": 1775215165402
},
{
"service": "php:7.4.33",
"action": "start",
"status": "success",
"id": "rx3hglow7",
"timestamp": 1775215165361
},
{
"service": "php:8.2.30",
"action": "start",
"status": "success",
"id": "w0lba1icw",
"timestamp": 1775215165316
},
{
"service": "php:8.5.4",
"action": "start",
"status": "success",
"id": "at8cycpe4",
"timestamp": 1775215165295
},
{
"service": "mariadb:10.11.8",
"action": "start",
"status": "success",
"id": "68cfbobjp",
"timestamp": 1775215165182
},
{
"service": "mariadb:11.4.2",
"action": "start",
"status": "success",
"id": "e04xqdiud",
"timestamp": 1775215164897
},
{
"service": "apache",
"action": "start",
"status": "success",
"id": "q2ak9offn",
"timestamp": 1775215164330
},
{
"service": "nginx",
"action": "start",
"status": "success",
"id": "r19c607j0",
"timestamp": 1775215137764
},
{ {
"service": "php:5.6.40", "service": "php:5.6.40",
"action": "start", "action": "start",
+30 -10
View File
@@ -476,14 +476,27 @@ function App(): JSX.Element {
if ((status[s]?.status || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: { status: 'starting' } })) if ((status[s]?.status || 'stopped') === 'stopped') setStatus(prev => ({ ...prev, [s]: { status: 'starting' } }))
} }
await Promise.all([ const results = await Promise.all([
...criticalServices.filter(s => status[s]?.status !== 'running').map(s => window.api?.startService(s)), ...criticalServices.filter(s => status[s]?.status !== 'running').map(s => window.api?.startService(s).then((r: any) => ({ service: s, ...r }))),
...phpToStart.filter(s => (status[s]?.status || 'stopped') !== 'running').map(s => window.api?.startService(s)), ...phpToStart.filter(s => (status[s]?.status || 'stopped') !== 'running').map(s => window.api?.startService(s).then((r: any) => ({ service: s, ...r }))),
...mariaDbToStart.filter(s => (status[s]?.status || 'stopped') !== 'running').map(s => window.api?.startService(s)) ...mariaDbToStart.filter(s => (status[s]?.status || 'stopped') !== 'running').map(s => window.api?.startService(s).then((r: any) => ({ service: s, ...r })))
]) ])
const errors = results.filter(r => r && !r.success)
if (errors.length > 0) {
const errorList = errors.map(e => `<b>${e.service}:</b> ${e.message}`).join('<br/>')
Swal.fire({
title: t('common.error'),
html: `<div style="text-align: left; font-size: 0.9rem;">${errorList}</div>`,
icon: 'error',
background: '#1e1e1e',
color: '#fff'
})
} else {
setNotification({ open: true, message: t('dashboard.all_started'), severity: 'success' })
}
fetchStatus() fetchStatus()
setNotification({ open: true, message: t('dashboard.all_started'), severity: 'success' })
} }
const handleStopAll = async () => { const handleStopAll = async () => {
@@ -498,12 +511,19 @@ function App(): JSX.Element {
const result = await window.api.invoke('services:stop-all') const result = await window.api.invoke('services:stop-all')
if (!result.success) {
Swal.fire({
title: t('common.error'),
text: result.message,
icon: 'error',
background: '#1e1e1e',
color: '#fff'
})
} else {
setNotification({ open: true, message: t('dashboard.all_stopped'), severity: 'success' })
}
fetchStatus() fetchStatus()
setNotification({
open: true,
message: result.success ? t('dashboard.all_stopped') : result.message,
severity: result.success ? 'success' : 'error'
})
} }
const formatMemory = (bytes?: number) => { const formatMemory = (bytes?: number) => {
@@ -304,12 +304,12 @@ export default function DashboardDetails({
</Tooltip> </Tooltip>
)} )}
<Button <Button
variant="contained" variant="outlined"
color={isRunning ? 'error' : 'success'} color={isRunning ? 'error' : 'success'}
disabled={isTransitioning} disabled={isTransitioning}
startIcon={isTransitioning ? <CircularProgress size={20} color="inherit" /> : isRunning ? <StopIcon /> : <StartIcon />} startIcon={isTransitioning ? <CircularProgress size={20} color="inherit" /> : isRunning ? <StopIcon /> : <StartIcon />}
onClick={() => onToggleService(serviceId)} onClick={() => onToggleService(serviceId)}
sx={{ borderRadius: 3, px: 4, height: 48, fontWeight: 800, fontSize: '1rem', boxShadow: isRunning ? '0 4px 12px rgba(244, 67, 54, 0.2)' : '0 4px 12px rgba(76, 175, 80, 0.2)' }} sx={{ borderRadius: 3, px: 4, height: 48, fontWeight: 800, fontSize: '1rem', boxShadow: 'none' }}
> >
{isStarting ? t('common.starting') : (isStopping ? t('common.stopping') : (isRunning ? t('common.stop') : t('common.start')))} {isStarting ? t('common.starting') : (isStopping ? t('common.stopping') : (isRunning ? t('common.stop') : t('common.start')))}
</Button> </Button>
@@ -378,7 +378,7 @@ export default function DashboardDetails({
{activeTab === 'settings_live' ? 'php.ini (Aktif Konfigürasyon)' : getTemplateName()} {activeTab === 'settings_live' ? 'php.ini (Aktif Konfigürasyon)' : getTemplateName()}
</Typography> </Typography>
<Button <Button
variant="contained" variant="outlined"
size="large" size="large"
startIcon={isLoadingConfig ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />} startIcon={isLoadingConfig ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />}
disabled={isLoadingConfig} disabled={isLoadingConfig}
@@ -414,7 +414,7 @@ export default function DashboardDetails({
{getProjectTemplateNames()[0]} {getProjectTemplateNames()[0]}
</Typography> </Typography>
<Button <Button
variant="contained" variant="outlined"
size="large" size="large"
startIcon={<SaveIcon />} startIcon={<SaveIcon />}
onClick={() => saveConfig(getProjectTemplateNames()[0], projectConfigContent)} onClick={() => saveConfig(getProjectTemplateNames()[0], projectConfigContent)}
@@ -222,7 +222,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
<Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'primary.light' }}> <Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'primary.light' }}>
{t('mariadb_manager.databases_title')} {t('mariadb_manager.databases_title')}
</Typography> </Typography>
<Button variant="contained" size="small" startIcon={<AddIcon />} onClick={handleCreateDb}> <Button variant="outlined" size="small" startIcon={<AddIcon />} onClick={handleCreateDb}>
{t('mariadb_manager.create_db_btn')} {t('mariadb_manager.create_db_btn')}
</Button> </Button>
</Box> </Box>
@@ -266,7 +266,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
<Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'secondary.light' }}> <Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'secondary.light' }}>
{t('mariadb_manager.users_title')} {t('mariadb_manager.users_title')}
</Typography> </Typography>
<Button variant="contained" color="secondary" size="small" startIcon={<AddIcon />} onClick={handleCreateUser}> <Button variant="outlined" color="secondary" size="small" startIcon={<AddIcon />} onClick={handleCreateUser}>
{t('mariadb_manager.create_user_btn')} {t('mariadb_manager.create_user_btn')}
</Button> </Button>
</Box> </Box>
@@ -210,7 +210,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
), ),
}} }}
/> />
<Button variant="contained" onClick={handleSelectSavePath} sx={{ minWidth: '120px' }}>{t('common.select')}</Button> <Button variant="outlined" onClick={handleSelectSavePath} sx={{ minWidth: '120px' }}>{t('common.select')}</Button>
</Box> </Box>
</Box> </Box>
) )
@@ -293,18 +293,18 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
<Button onClick={prevStep} disabled={loading}>{t('common.back')}</Button> <Button onClick={prevStep} disabled={loading}>{t('common.back')}</Button>
)} )}
{activeStep === 0 && ( {activeStep === 0 && (
<Button variant="contained" onClick={handleStep1Next} disabled={loading}> <Button variant="outlined" onClick={handleStep1Next} disabled={loading}>
{loading ? <CircularProgress size={24} /> : t('common.next')} {loading ? <CircularProgress size={24} /> : t('common.next')}
</Button> </Button>
)} )}
{activeStep === 1 && ( {activeStep === 1 && (
<Button variant="contained" onClick={nextStep} disabled={!selectedDb}>{t('common.next')}</Button> <Button variant="outlined" onClick={nextStep} disabled={!selectedDb}>{t('common.next')}</Button>
)} )}
{activeStep === 2 && ( {activeStep === 2 && (
<Button variant="contained" onClick={nextStep} disabled={!savePath}>{t('common.next')}</Button> <Button variant="outlined" onClick={nextStep} disabled={!savePath}>{t('common.next')}</Button>
)} )}
{activeStep === 3 && !exportResult && ( {activeStep === 3 && !exportResult && (
<Button variant="contained" color="primary" onClick={startExport} disabled={loading}> <Button variant="outlined" color="primary" onClick={startExport} disabled={loading}>
{loading ? <CircularProgress size={24} /> : t('mariadb.export.btn_start')} {loading ? <CircularProgress size={24} /> : t('mariadb.export.btn_start')}
</Button> </Button>
)} )}
@@ -188,7 +188,7 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
), ),
}} }}
/> />
<Button variant="contained" onClick={handleSelectFile} sx={{ minWidth: '120px' }}>{t('common.select')}</Button> <Button variant="outlined" onClick={handleSelectFile} sx={{ minWidth: '120px' }}>{t('common.select')}</Button>
</Box> </Box>
</Box> </Box>
) )
@@ -337,18 +337,18 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
<Button onClick={prevStep} disabled={loading}>{t('common.back')}</Button> <Button onClick={prevStep} disabled={loading}>{t('common.back')}</Button>
)} )}
{activeStep === 0 && ( {activeStep === 0 && (
<Button variant="contained" onClick={nextStep} disabled={!filePath}>{t('common.next')}</Button> <Button variant="outlined" onClick={nextStep} disabled={!filePath}>{t('common.next')}</Button>
)} )}
{activeStep === 1 && ( {activeStep === 1 && (
<Button variant="contained" onClick={handleStep2Next}>{t('common.next')}</Button> <Button variant="outlined" onClick={handleStep2Next}>{t('common.next')}</Button>
)} )}
{activeStep === 2 && ( {activeStep === 2 && (
<Button variant="contained" onClick={handleStep3Next} disabled={loading}> <Button variant="outlined" onClick={handleStep3Next} disabled={loading}>
{loading ? <CircularProgress size={24} /> : t('mariadb.import.btn_summary')} {loading ? <CircularProgress size={24} /> : t('mariadb.import.btn_summary')}
</Button> </Button>
)} )}
{activeStep === 3 && !importResult && ( {activeStep === 3 && !importResult && (
<Button variant="contained" color="primary" onClick={startImport} disabled={loading}> <Button variant="outlined" color="primary" onClick={startImport} disabled={loading}>
{loading ? <CircularProgress size={24} /> : t('mariadb.import.btn_start')} {loading ? <CircularProgress size={24} /> : t('mariadb.import.btn_start')}
</Button> </Button>
)} )}
@@ -266,7 +266,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
<Button onClick={fetchExtensions} startIcon={<RefreshIcon />} sx={{ mr: 'auto' }}> <Button onClick={fetchExtensions} startIcon={<RefreshIcon />} sx={{ mr: 'auto' }}>
{t('common.refresh').toUpperCase()} {t('common.refresh').toUpperCase()}
</Button> </Button>
<Button onClick={onClose} variant="contained" color="primary"> <Button onClick={onClose} variant="outlined" color="primary">
{t('common.close').toUpperCase()} {t('common.close').toUpperCase()}
</Button> </Button>
</DialogActions> </DialogActions>
@@ -331,7 +331,6 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
<PhpIniCodeEditor <PhpIniCodeEditor
value={rawConfig} value={rawConfig}
onChange={setRawConfig} onChange={setRawConfig}
searchTerm={rawSearchTerm}
/> />
</DialogContent> </DialogContent>
<DialogActions sx={{ p: 2, bgcolor: 'rgba(0,0,0,0.2)' }}> <DialogActions sx={{ p: 2, bgcolor: 'rgba(0,0,0,0.2)' }}>
@@ -340,7 +339,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
</Button> </Button>
<Button <Button
onClick={handleSaveRaw} onClick={handleSaveRaw}
variant="contained" variant="outlined"
color="primary" color="primary"
startIcon={isSavingRaw ? <CircularProgress size={18} color="inherit" /> : <SaveIcon />} startIcon={isSavingRaw ? <CircularProgress size={18} color="inherit" /> : <SaveIcon />}
disabled={isSavingRaw} disabled={isSavingRaw}
@@ -242,7 +242,7 @@ export default function ProjectDetails({
<Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap' }}> <Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap' }}>
<Button <Button
variant="contained" variant="outlined"
size="medium" size="medium"
disabled={isTransitioning} disabled={isTransitioning}
startIcon={isTransitioning ? <CircularProgress size={18} color="inherit" /> : (isRunning ? <StopIcon /> : <StartIcon />)} startIcon={isTransitioning ? <CircularProgress size={18} color="inherit" /> : (isRunning ? <StopIcon /> : <StartIcon />)}
@@ -257,7 +257,7 @@ export default function ProjectDetails({
} }
} }
}} }}
sx={{ px: 3, height: 44, borderRadius: 2.5, fontWeight: 800, boxShadow: isRunning ? '0 4px 12px rgba(244, 67, 54, 0.2)' : '0 4px 12px rgba(76, 175, 80, 0.2)' }} sx={{ px: 3, height: 44, borderRadius: 2.5, fontWeight: 800, boxShadow: 'none' }}
> >
{isTransitioning ? t('common.processing') : (isRunning ? t('projects.stop_site').toUpperCase() : t('projects.start_site').toUpperCase())} {isTransitioning ? t('common.processing') : (isRunning ? t('projects.stop_site').toUpperCase() : t('projects.start_site').toUpperCase())}
</Button> </Button>
@@ -284,14 +284,14 @@ export default function ProjectDetails({
{!isHostMapped && ( {!isHostMapped && (
<Button <Button
variant="contained" variant="outlined"
size="medium" size="medium"
color="warning" color="warning"
onClick={() => { onClick={() => {
const finalHost = p.host.includes('.') ? p.host : `${p.host}.local`; const finalHost = p.host.includes('.') ? p.host : `${p.host}.local`;
onAddHost(finalHost); onAddHost(finalHost);
}} }}
sx={{ px: 3, height: 44, borderRadius: 2.5, fontWeight: 800, boxShadow: '0 4px 12px rgba(255, 152, 0, 0.2)' }} sx={{ px: 3, height: 44, borderRadius: 2.5, fontWeight: 800, boxShadow: 'none' }}
> >
HOSTS DOSYASINA EKLE HOSTS DOSYASINA EKLE
</Button> </Button>
@@ -407,12 +407,12 @@ export default function ProjectDetails({
<Box sx={{ pt: 1, display: 'flex', justifyContent: 'flex-start' }}> <Box sx={{ pt: 1, display: 'flex', justifyContent: 'flex-start' }}>
<Button <Button
variant="contained" variant="outlined"
size="large" size="large"
startIcon={isSaving ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />} startIcon={isSaving ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />}
onClick={handleProjectUpdate} onClick={handleProjectUpdate}
disabled={isSaving} disabled={isSaving}
sx={{ px: 6, py: 1.5, borderRadius: 3, fontWeight: 800, fontSize: '1rem', background: 'linear-gradient(45deg, #2196F3 30%, #21CBF3 90%)', boxShadow: '0 8px 20px rgba(33, 150, 243, 0.3)' }} sx={{ px: 6, py: 1.5, borderRadius: 3, fontWeight: 800, fontSize: '1rem', color: 'primary.main', border: '1px solid rgba(33, 150, 243, 0.5)', '&:hover': { bgcolor: 'rgba(33, 150, 243, 0.05)', borderColor: 'primary.main' } }}
> >
{t('common.save').toUpperCase()} {t('common.save').toUpperCase()}
</Button> </Button>
@@ -518,7 +518,7 @@ export default function ProjectDetails({
{t('common.refresh')} {t('common.refresh')}
</Button> </Button>
<Button <Button
variant="contained" variant="outlined"
size="small" size="small"
color="primary" color="primary"
startIcon={isConfigSaving ? <CircularProgress size={16} color="inherit" /> : <SaveIcon />} startIcon={isConfigSaving ? <CircularProgress size={16} color="inherit" /> : <SaveIcon />}
+3 -3
View File
@@ -287,7 +287,7 @@ export default function ServerTuning() {
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>PHP {v.version}</Typography> <Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>PHP {v.version}</Typography>
<Typography variant="caption" color="text.secondary">php.ini optimization</Typography> <Typography variant="caption" color="text.secondary">php.ini optimization</Typography>
</Box> </Box>
<Button variant="contained" size="small" startIcon={<TuneIcon />}> <Button variant="outlined" size="small" startIcon={<TuneIcon />}>
{t('tuning.tune_btn')} {t('tuning.tune_btn')}
</Button> </Button>
</CardContent> </CardContent>
@@ -318,7 +318,7 @@ export default function ServerTuning() {
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB {v.version}</Typography> <Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB {v.version}</Typography>
<Typography variant="caption" color="text.secondary">Performance variables</Typography> <Typography variant="caption" color="text.secondary">Performance variables</Typography>
</Box> </Box>
<Button variant="contained" color="secondary" size="small" startIcon={<TuneIcon />}> <Button variant="outlined" color="secondary" size="small" startIcon={<TuneIcon />}>
{t('tuning.tune_btn')} {t('tuning.tune_btn')}
</Button> </Button>
</CardContent> </CardContent>
@@ -410,7 +410,7 @@ export default function ServerTuning() {
{t('common.cancel')} {t('common.cancel')}
</Button> </Button>
<Button <Button
variant="contained" variant="outlined"
color={selectedVersion?.type === 'php' ? 'primary' : 'secondary'} color={selectedVersion?.type === 'php' ? 'primary' : 'secondary'}
onClick={handleSave} onClick={handleSave}
startIcon={saving ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />} startIcon={saving ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />}