feat: implement IPC handlers for service lifecycle management and configuration generation
This commit is contained in:
+66
-4
@@ -374,14 +374,23 @@ export function registerIpcHandlers(): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('projects:add', async (_event, project) => {
|
ipcMain.handle('projects:add', async (_event, project) => {
|
||||||
return projectService.addProject(project)
|
const result = await projectService.addProject(project)
|
||||||
|
// Trigger Nginx reload
|
||||||
|
await ipcMain.emit('services:reload', null, 'nginx')
|
||||||
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('projects:remove', async (_event, id) => {
|
ipcMain.handle('projects:remove', async (_event, id) => {
|
||||||
return projectService.removeProject(id)
|
const result = projectService.removeProject(id)
|
||||||
|
// Trigger Nginx reload
|
||||||
|
await ipcMain.emit('services:reload', null, 'nginx')
|
||||||
|
return result
|
||||||
})
|
})
|
||||||
ipcMain.handle('projects:update', async (_event, project) => {
|
ipcMain.handle('projects:update', async (_event, project) => {
|
||||||
return projectService.updateProject(project)
|
const result = projectService.updateProject(project)
|
||||||
|
// Trigger Nginx reload
|
||||||
|
await ipcMain.emit('services:reload', null, 'nginx')
|
||||||
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
// Config management
|
// Config management
|
||||||
@@ -390,7 +399,60 @@ export function registerIpcHandlers(): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('config:save', async (_event, config: any) => {
|
ipcMain.handle('config:save', async (_event, config: any) => {
|
||||||
return { success: true, settings: configService.updateSettings(config) }
|
const settings = configService.updateSettings(config)
|
||||||
|
return { success: true, settings }
|
||||||
|
})
|
||||||
|
|
||||||
|
// Nginx Config Management
|
||||||
|
ipcMain.handle('nginx:config:get', async () => {
|
||||||
|
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
return fs.readFileSync(configPath, 'utf8')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('nginx:config:save', async (_event, content) => {
|
||||||
|
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||||||
|
fs.writeFileSync(configPath, content)
|
||||||
|
// Reload Nginx after save
|
||||||
|
await ipcMain.emit('services:reload', null, 'nginx')
|
||||||
|
return { success: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('nginx:config:test', async () => {
|
||||||
|
try {
|
||||||
|
const binDir = path.join(configService.getBasePath(), 'bin', 'nginx')
|
||||||
|
const nginxVersions = fs.readdirSync(binDir).filter(f => fs.statSync(path.join(binDir, f)).isDirectory())
|
||||||
|
const activeVersion = nginxVersions[0]
|
||||||
|
if (!activeVersion) throw new Error('Nginx not found')
|
||||||
|
|
||||||
|
const nginxPrefix = path.join(binDir, activeVersion)
|
||||||
|
const binPath = path.join(nginxPrefix, 'nginx.exe')
|
||||||
|
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'nginx.conf')
|
||||||
|
|
||||||
|
const success = await processManager.runOnce(binPath, ['-t', '-p', nginxPrefix, '-c', configPath], 'nginx')
|
||||||
|
return { success, message: success ? 'Configuration test successful' : 'Configuration test failed' }
|
||||||
|
} catch (error: any) {
|
||||||
|
return { success: false, message: error.message }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Project Nginx Config
|
||||||
|
ipcMain.handle('project:nginx:get', async (_event, host) => {
|
||||||
|
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects', `${host}.conf`)
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
return fs.readFileSync(configPath, 'utf8')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('project:nginx:save', async (_event, host, content) => {
|
||||||
|
const configPath = path.join(configService.getBasePath(), 'generated_configs', 'projects', `${host}.conf`)
|
||||||
|
fs.writeFileSync(configPath, content)
|
||||||
|
// Reload Nginx after save
|
||||||
|
await ipcMain.emit('services:reload', null, 'nginx')
|
||||||
|
return { success: true }
|
||||||
})
|
})
|
||||||
|
|
||||||
// phpMyAdmin
|
// phpMyAdmin
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ const api = {
|
|||||||
const listener = (_event, msg) => callback(msg)
|
const listener = (_event, msg) => callback(msg)
|
||||||
electronAPI.ipcRenderer.on('mysql:import:log', listener)
|
electronAPI.ipcRenderer.on('mysql:import:log', listener)
|
||||||
return () => electronAPI.ipcRenderer.removeListener('mysql:import:log', listener)
|
return () => electronAPI.ipcRenderer.removeListener('mysql:import:log', listener)
|
||||||
}
|
},
|
||||||
|
invoke: (channel: string, ...args: any[]) => electronAPI.ipcRenderer.invoke(channel, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use `contextBridge` APIs to expose Electron APIs to
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
|
|||||||
+207
-4
@@ -132,6 +132,11 @@ function App(): JSX.Element {
|
|||||||
const [pmaLoading, setPmaLoading] = useState(false)
|
const [pmaLoading, setPmaLoading] = useState(false)
|
||||||
const [pmaProgress, setPmaProgress] = useState(0)
|
const [pmaProgress, setPmaProgress] = useState(0)
|
||||||
const [isMySqlWizardOpen, setIsMySqlWizardOpen] = useState(false)
|
const [isMySqlWizardOpen, setIsMySqlWizardOpen] = useState(false)
|
||||||
|
const [isNginxConfigOpen, setIsNginxConfigOpen] = useState(false)
|
||||||
|
const [nginxConfig, setNginxConfig] = useState('')
|
||||||
|
const [isProjectNginxConfigOpen, setIsProjectNginxConfigOpen] = useState(false)
|
||||||
|
const [projectNginxConfig, setProjectNginxConfig] = useState('')
|
||||||
|
const [selectedProjectHost, setSelectedProjectHost] = useState('')
|
||||||
|
|
||||||
const fetchStatus = async () => {
|
const fetchStatus = async () => {
|
||||||
if (window.api && window.api.getServiceStatus) {
|
if (window.api && window.api.getServiceStatus) {
|
||||||
@@ -442,9 +447,74 @@ function App(): JSX.Element {
|
|||||||
|
|
||||||
const handleRemoveProject = async (id: string) => {
|
const handleRemoveProject = async (id: string) => {
|
||||||
if (!window.api) return
|
if (!window.api) return
|
||||||
await window.api.removeProject(id)
|
const result = await Swal.fire({
|
||||||
setNotification({ open: true, message: 'Proje silindi.', severity: 'success' })
|
title: 'Emin misiniz?',
|
||||||
fetchProjects()
|
text: "Bu projeyi silmek istediğinize emin misiniz? Nginx ayarları otomatik olarak güncellenecektir.",
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#ff4444',
|
||||||
|
cancelButtonColor: '#3085d6',
|
||||||
|
confirmButtonText: 'Evet, sil!',
|
||||||
|
cancelButtonText: 'İptal',
|
||||||
|
background: '#1e1e1e',
|
||||||
|
color: '#fff'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
await window.api.removeProject(id)
|
||||||
|
setNotification({ open: true, message: 'Proje silindi. Nginx yeniden yüklendi.', severity: 'success' })
|
||||||
|
fetchProjects()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenNginxConfig = async () => {
|
||||||
|
if (window.api && window.api.invoke) {
|
||||||
|
const config = await window.api.invoke('nginx:config:get')
|
||||||
|
setNginxConfig(config)
|
||||||
|
setIsNginxConfigOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSaveNginxConfig = async () => {
|
||||||
|
if (window.api && window.api.invoke) {
|
||||||
|
const result = await window.api.invoke('nginx:config:save', nginxConfig)
|
||||||
|
if (result.success) {
|
||||||
|
setNotification({ open: true, message: 'Nginx yapılandırması kaydedildi ve yeniden yüklendi.', severity: 'success' })
|
||||||
|
setIsNginxConfigOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTestNginxConfig = async () => {
|
||||||
|
if (window.api && window.api.invoke) {
|
||||||
|
const result = await window.api.invoke('nginx:config:test')
|
||||||
|
Swal.fire({
|
||||||
|
title: result.success ? 'Başarılı' : 'Hata',
|
||||||
|
text: result.message,
|
||||||
|
icon: result.success ? 'success' : 'error',
|
||||||
|
background: '#1e1e1e',
|
||||||
|
color: '#fff'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenProjectNginxConfig = async (host: string) => {
|
||||||
|
if (window.api && window.api.invoke) {
|
||||||
|
const config = await window.api.invoke('project:nginx:get', host)
|
||||||
|
setProjectNginxConfig(config)
|
||||||
|
setSelectedProjectHost(host)
|
||||||
|
setIsProjectNginxConfigOpen(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSaveProjectNginxConfig = async () => {
|
||||||
|
if (window.api && window.api.invoke) {
|
||||||
|
const result = await window.api.invoke('project:nginx:save', selectedProjectHost, projectNginxConfig)
|
||||||
|
if (result.success) {
|
||||||
|
setNotification({ open: true, message: `${selectedProjectHost} için Nginx yapılandırması kaydedildi.`, severity: 'success' })
|
||||||
|
setIsProjectNginxConfigOpen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownloadPhp = async (id: string) => {
|
const handleDownloadPhp = async (id: string) => {
|
||||||
@@ -814,6 +884,14 @@ function App(): JSX.Element {
|
|||||||
<IconButton onClick={() => handleOpenLogs('nginx')} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
|
<IconButton onClick={() => handleOpenLogs('nginx')} title="Hata Logları" size="small" sx={{ bgcolor: 'rgba(255,255,255,0.05)' }}>
|
||||||
<TerminalIcon fontSize="small" />
|
<TerminalIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
onClick={handleOpenNginxConfig}
|
||||||
|
title="Nginx Yapılandırması"
|
||||||
|
size="small"
|
||||||
|
sx={{ bgcolor: 'rgba(255,255,255,0.05)', '&:hover': { color: 'primary.main' } }}
|
||||||
|
>
|
||||||
|
<SettingsIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.api.invoke('services:reload', 'nginx').then((res: any) => {
|
window.api.invoke('services:reload', 'nginx').then((res: any) => {
|
||||||
@@ -1055,6 +1133,11 @@ function App(): JSX.Element {
|
|||||||
<EditIcon fontSize="small" />
|
<EditIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip title="Nginx Ayarları">
|
||||||
|
<IconButton size="small" onClick={() => handleOpenProjectNginxConfig(project.host)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
|
||||||
|
<TerminalIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
<Tooltip title="Sil">
|
<Tooltip title="Sil">
|
||||||
<IconButton size="small" onClick={() => handleRemoveProject(project.id)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#ff4444' } }}>
|
<IconButton size="small" onClick={() => handleRemoveProject(project.id)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#ff4444' } }}>
|
||||||
<DeleteIcon fontSize="small" />
|
<DeleteIcon fontSize="small" />
|
||||||
@@ -1309,7 +1392,127 @@ function App(): JSX.Element {
|
|||||||
onClose={() => setIsMySqlWizardOpen(false)}
|
onClose={() => setIsMySqlWizardOpen(false)}
|
||||||
mySqlVersions={mySqlVersions}
|
mySqlVersions={mySqlVersions}
|
||||||
/>
|
/>
|
||||||
</Box>
|
|
||||||
|
<Dialog
|
||||||
|
open={isNginxConfigOpen}
|
||||||
|
onClose={() => setIsNginxConfigOpen(false)}
|
||||||
|
maxWidth="md"
|
||||||
|
fullWidth
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
bgcolor: '#1e1e1e',
|
||||||
|
color: '#fff',
|
||||||
|
borderRadius: 3,
|
||||||
|
border: '1px solid rgba(255,255,255,0.1)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Typography variant="h6">Nginx Yapılandırması (nginx.conf)</Typography>
|
||||||
|
<IconButton onClick={() => setIsNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers sx={{ borderColor: 'rgba(255,255,255,0.1)' }}>
|
||||||
|
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mb: 2, display: 'block' }}>
|
||||||
|
Dikkat: Hatalı yapılandırma Nginx'in çalışmasını durdurabilir. Değişiklik yapmadan önce test edin.
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={20}
|
||||||
|
value={nginxConfig}
|
||||||
|
onChange={(e) => setNginxConfig(e.target.value)}
|
||||||
|
sx={{
|
||||||
|
'& .MuiInputBase-root': {
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: 'rgba(255,255,255,0.1)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={{ p: 2, gap: 1 }}>
|
||||||
|
<Button
|
||||||
|
onClick={handleTestNginxConfig}
|
||||||
|
variant="outlined"
|
||||||
|
startIcon={<RefreshIcon />}
|
||||||
|
sx={{ color: '#fff', borderColor: 'rgba(255,255,255,0.3)' }}
|
||||||
|
>
|
||||||
|
Yapılandırmayı Test Et
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => setIsNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.6)' }}>
|
||||||
|
İptal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleSaveNginxConfig}
|
||||||
|
variant="contained"
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
>
|
||||||
|
Kaydet ve Yeniden Yükle
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={isProjectNginxConfigOpen}
|
||||||
|
onClose={() => setIsProjectNginxConfigOpen(false)}
|
||||||
|
maxWidth="md"
|
||||||
|
fullWidth
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
bgcolor: '#1e1e1e',
|
||||||
|
color: '#fff',
|
||||||
|
borderRadius: 3,
|
||||||
|
border: '1px solid rgba(255,255,255,0.1)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Typography variant="h6">{selectedProjectHost} Nginx Yapılandırması</Typography>
|
||||||
|
<IconButton onClick={() => setIsProjectNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers sx={{ borderColor: 'rgba(255,255,255,0.1)' }}>
|
||||||
|
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mb: 2, display: 'block' }}>
|
||||||
|
İpucu: Bu yapılandırma sadece {selectedProjectHost} projesi için geçerlidir.
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
multiline
|
||||||
|
rows={20}
|
||||||
|
value={projectNginxConfig}
|
||||||
|
onChange={(e) => setProjectNginxConfig(e.target.value)}
|
||||||
|
sx={{
|
||||||
|
'& .MuiInputBase-root': {
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: '0.875rem',
|
||||||
|
color: '#fff'
|
||||||
|
},
|
||||||
|
'& .MuiOutlinedInput-notchedOutline': {
|
||||||
|
borderColor: 'rgba(255,255,255,0.1)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={{ p: 2, gap: 1 }}>
|
||||||
|
<Button onClick={() => setIsProjectNginxConfigOpen(false)} sx={{ color: 'rgba(255,255,255,0.6)' }}>
|
||||||
|
İptal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleSaveProjectNginxConfig}
|
||||||
|
variant="contained"
|
||||||
|
startIcon={<SaveIcon />}
|
||||||
|
>
|
||||||
|
Kaydet ve Yeniden Yükle
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</Box >
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user