feat: initialize electron main process with tray support and implement core IPC service handlers
This commit is contained in:
+2
-2
@@ -46,8 +46,8 @@ function createTray(): void {
|
||||
|
||||
function createWindow(): void {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
width: 1400,
|
||||
height: 900,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
icon: join(__dirname, '../../resources/icon.png'),
|
||||
|
||||
+48
-48
@@ -256,6 +256,54 @@ async function restartApache(): Promise<{ success: boolean; message: string }> {
|
||||
}
|
||||
|
||||
export function registerIpcHandlers(): void {
|
||||
ipcMain.handle('mariadb:processlist', async (_event, versionId) => {
|
||||
try {
|
||||
return await mariaDbManagerService.getProcessList(versionId)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to get process list:', error)
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:kill-process', async (_event, { versionId, processId }) => {
|
||||
try {
|
||||
await mariaDbManagerService.killProcess(versionId, processId)
|
||||
return { success: true }
|
||||
} catch (error: any) {
|
||||
console.error('Failed to kill process:', error)
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
// CLI & Environment
|
||||
ipcMain.handle('cli:get-config', async () => {
|
||||
const settings = configService.getSettings()
|
||||
return {
|
||||
enabled: settings.cliEnvEnabled !== false,
|
||||
defaultPhp: settings.defaultPhpVersion,
|
||||
defaultMariaDb: settings.defaultMariaDbVersion,
|
||||
binDir: cliBinService.getBinDir(),
|
||||
aliases: cliBinService.getAllAliases()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('cli:set-config', async (_event, config: any) => {
|
||||
const settings = configService.getSettings()
|
||||
configService.updateSettings({
|
||||
...settings,
|
||||
cliEnvEnabled: config.enabled,
|
||||
defaultPhpVersion: config.defaultPhp,
|
||||
defaultMariaDbVersion: config.defaultMariaDb
|
||||
})
|
||||
await cliAliasService.generateAliases()
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
ipcMain.handle('cli:sync', async () => {
|
||||
await cliAliasService.generateAliases()
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
// Service management
|
||||
ipcMain.handle('services:reload', async (_event, serviceName: string) => {
|
||||
if (serviceName === 'nginx') return await reloadNginx()
|
||||
@@ -1042,52 +1090,4 @@ export function registerIpcHandlers(): void {
|
||||
rootPass: rootPass || ''
|
||||
}, dbName, savePath, compress, (msg) => log(msg))
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:processlist', async (_event, versionId) => {
|
||||
try {
|
||||
return await mariaDbManagerService.getProcessList(versionId)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to get process list:', error)
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:kill-process', async (_event, { versionId, processId }) => {
|
||||
try {
|
||||
await mariaDbManagerService.killProcess(versionId, processId)
|
||||
return { success: true }
|
||||
} catch (error: any) {
|
||||
console.error('Failed to kill process:', error)
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
// CLI & Environment
|
||||
ipcMain.handle('cli:get-config', async () => {
|
||||
const settings = configService.getSettings()
|
||||
return {
|
||||
enabled: settings.cliEnvEnabled !== false,
|
||||
defaultPhp: settings.defaultPhpVersion,
|
||||
defaultMariaDb: settings.defaultMariaDbVersion,
|
||||
binDir: cliBinService.getBinDir(),
|
||||
aliases: cliBinService.getAllAliases()
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('cli:set-config', async (_event, config: any) => {
|
||||
const settings = configService.getSettings()
|
||||
configService.updateSettings({
|
||||
...settings,
|
||||
cliEnvEnabled: config.enabled,
|
||||
defaultPhpVersion: config.defaultPhp,
|
||||
defaultMariaDbVersion: config.defaultMariaDb
|
||||
})
|
||||
await cliAliasService.generateAliases()
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
ipcMain.handle('cli:sync', async () => {
|
||||
await cliAliasService.generateAliases()
|
||||
return { success: true }
|
||||
})
|
||||
}
|
||||
|
||||
+195
-5
@@ -1,4 +1,7 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import Prism from 'prismjs'
|
||||
import 'prismjs/themes/prism-tomorrow.css'
|
||||
import 'prismjs/components/prism-sql'
|
||||
import logo from './assets/logo.svg'
|
||||
import {
|
||||
Box,
|
||||
@@ -169,9 +172,10 @@ function App(): JSX.Element {
|
||||
|
||||
// MariaDB Process List State
|
||||
const [processList, setProcessList] = useState<any[]>([])
|
||||
const [isProcessListOpen, setIsProcessListOpen] = useState(false)
|
||||
const [selectedMariaDbId, setSelectedMariaDbId] = useState<string | null>(null)
|
||||
const [isProcessListLoading, setIsProcessListLoading] = useState(false)
|
||||
const [selectedMariaDbId, setSelectedMariaDbId] = useState<string>('')
|
||||
const [isProcessListOpen, setIsProcessListOpen] = useState(false)
|
||||
const [selectedQuery, setSelectedQuery] = useState<string | null>(null)
|
||||
const [isNginxConfigOpen, setIsNginxConfigOpen] = useState(false)
|
||||
const [nginxConfig, setNginxConfig] = useState('')
|
||||
const [isApacheConfigOpen, setIsApacheConfigOpen] = useState(false)
|
||||
@@ -193,6 +197,25 @@ function App(): JSX.Element {
|
||||
return { filePath, line, host }
|
||||
}
|
||||
|
||||
// Auto-refresh for Process Monitor
|
||||
useEffect(() => {
|
||||
if (activeTab === 'db_monitor' && selectedMariaDbId) {
|
||||
fetchProcessList(selectedMariaDbId)
|
||||
const interval = setInterval(() => {
|
||||
fetchProcessList(selectedMariaDbId)
|
||||
}, 3000)
|
||||
return () => clearInterval(interval)
|
||||
}
|
||||
return () => { }
|
||||
}, [activeTab, selectedMariaDbId])
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab === 'db_monitor' && !selectedMariaDbId && mariaDbVersions.length > 0) {
|
||||
const installed = mariaDbVersions.find(v => v.status === 'installed')
|
||||
if (installed) setSelectedMariaDbId(installed.id)
|
||||
}
|
||||
}, [activeTab, mariaDbVersions])
|
||||
|
||||
const fetchStatus = async () => {
|
||||
if (window.api && window.api.getServiceStatus) {
|
||||
const currentStatus = await window.api.getServiceStatus()
|
||||
@@ -882,8 +905,7 @@ function App(): JSX.Element {
|
||||
|
||||
const handleOpenProcessList = async (id: string) => {
|
||||
setSelectedMariaDbId(id)
|
||||
setIsProcessListOpen(true)
|
||||
fetchProcessList(id)
|
||||
setActiveTab('db_monitor')
|
||||
}
|
||||
|
||||
const fetchProcessList = async (id: string) => {
|
||||
@@ -923,6 +945,67 @@ function App(): JSX.Element {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
open={!!selectedQuery}
|
||||
onClose={() => setSelectedQuery(null)}
|
||||
maxWidth="lg"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
bgcolor: '#1a1d21',
|
||||
backgroundImage: 'none',
|
||||
color: '#fff',
|
||||
borderRadius: 2,
|
||||
border: '1px solid rgba(255,255,255,0.1)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 800 }}>{t('mariadb_process_monitor.query_details')}</Typography>
|
||||
<IconButton onClick={() => setSelectedQuery(null)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: 0 }}>
|
||||
<Box sx={{ p: 3, maxHeight: '70vh', overflow: 'auto' }}>
|
||||
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>
|
||||
<code
|
||||
className="language-sql"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: Prism.highlight(
|
||||
(selectedQuery || '')
|
||||
.replace(/\\r\\n/g, '\n')
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\\t/g, ' '),
|
||||
Prism.languages.sql,
|
||||
'sql'
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</pre>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ borderTop: '1px solid rgba(255,255,255,0.1)', p: 2 }}>
|
||||
<Button
|
||||
startIcon={<ContentCopyIcon />}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(selectedQuery || '')
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: t('common.copied'),
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('common.copy')}
|
||||
</Button>
|
||||
<Button onClick={() => setSelectedQuery(null)}>{t('common.close')}</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
<GlobalStyles styles={{
|
||||
'.swal2-container': {
|
||||
zIndex: '9999 !important'
|
||||
@@ -985,6 +1068,11 @@ function App(): JSX.Element {
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Tooltip title={t('mariadb_process_monitor.title')}>
|
||||
<IconButton color="inherit" onClick={() => setActiveTab('db_monitor')} sx={{ '&:hover': { color: 'primary.main' } }}>
|
||||
<TerminalIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<IconButton color="inherit" onClick={() => setActiveTab('settings')} sx={{ ml: 1 }}>
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
@@ -1023,6 +1111,12 @@ function App(): JSX.Element {
|
||||
<ListItemText primary={t('common.tools')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton selected={activeTab === 'db_monitor'} onClick={() => setActiveTab('db_monitor')}>
|
||||
<ListItemIcon><DbIcon color={activeTab === 'db_monitor' ? 'primary' : 'inherit'} /></ListItemIcon>
|
||||
<ListItemText primary={t('mariadb_process_monitor.title')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
<Divider />
|
||||
<List>
|
||||
@@ -1038,7 +1132,7 @@ function App(): JSX.Element {
|
||||
|
||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||
<Toolbar />
|
||||
<Container maxWidth="md">
|
||||
<Container maxWidth={false} sx={{ py: 2 }}>
|
||||
{activeTab === 'settings' && (
|
||||
<Box>
|
||||
|
||||
@@ -1657,6 +1751,102 @@ function App(): JSX.Element {
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'db_monitor' && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Paper sx={{ p: 3, bgcolor: 'rgba(255,255,255,0.03)', borderRadius: 2 }}>
|
||||
<Box sx={{ mb: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Box>
|
||||
<Typography variant="h5" sx={{ fontWeight: 800, color: '#fff', mb: 1 }}>
|
||||
{t('mariadb_process_monitor.title')}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.5)' }}>
|
||||
{t('mariadb_process_monitor.auto_refresh_desc')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}>
|
||||
<FormControl size="small" variant="outlined" sx={{ minWidth: 200 }}>
|
||||
<InputLabel sx={{ color: 'rgba(255,255,255,0.5)' }}>{t('settings.mariadb_versions')}</InputLabel>
|
||||
<Select
|
||||
value={selectedMariaDbId}
|
||||
onChange={(e) => setSelectedMariaDbId(e.target.value)}
|
||||
label={t('settings.mariadb_versions')}
|
||||
sx={{
|
||||
color: '#fff',
|
||||
'.MuiOutlinedInput-notchedOutline': { borderColor: 'rgba(255,255,255,0.1)' }
|
||||
}}
|
||||
>
|
||||
{mariaDbVersions.filter(v => v.status === 'installed').map(v => (
|
||||
<MenuItem key={v.id} value={v.id}>MariaDB {v.version}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{isProcessListLoading && <CircularProgress size={20} sx={{ color: '#39A7FF' }} />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<TableContainer component={Paper} variant="outlined" sx={{ bgcolor: 'rgba(0,0,0,0.2)', borderColor: 'rgba(255,255,255,0.05)', maxHeight: '60vh' }}>
|
||||
<Table stickyHeader size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>ID</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>USER</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>HOST</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>DB</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>COMMAND</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>TIME</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>STATE</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>INFO</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem' }}>PROGRESS</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, color: 'rgba(255,255,255,0.4)', fontSize: '0.75rem', textAlign: 'right' }}>ACTION</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{processList.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={10} align="center" sx={{ py: 4, color: 'rgba(255,255,255,0.3)', borderColor: 'rgba(255,255,255,0.05)' }}>
|
||||
{t('common.no_data')}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
processList.map((proc) => (
|
||||
<TableRow key={proc.Id} hover sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: 'rgba(255,255,255,0.7)', fontFamily: 'monospace' }}>{proc.Id}</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: '#39A7FF' }}>{proc.User}</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: 'rgba(255,255,255,0.7)' }}>{proc.Host}</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: proc.db ? '#ff9f43' : 'rgba(255,255,255,0.3)' }}>{proc.db || 'system'}</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: '#33d9b2' }}>{proc.Command}</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: 'rgba(255,255,255,0.7)' }}>{proc.Time}s</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: proc.State === 'Query' ? '#39A7FF' : 'rgba(255,255,255,0.5)' }}>{proc.State || '-'}</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: 'rgba(255,255,255,0.7)', maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
||||
<Tooltip title={t('mariadb_process_monitor.click_to_view_sql')}>
|
||||
<Box
|
||||
onClick={() => setSelectedQuery(proc.Info)}
|
||||
sx={{
|
||||
cursor: proc.Info ? 'pointer' : 'default',
|
||||
'&:hover': { color: proc.Info ? '#39A7FF' : 'inherit' },
|
||||
transition: 'color 0.2s'
|
||||
}}
|
||||
>
|
||||
{proc.Info || '-'}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: '#39A7FF' }}>{proc.Progress}%</TableCell>
|
||||
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', textAlign: 'right' }}>
|
||||
<IconButton size="small" color="error" onClick={() => handleKillProcess(selectedMariaDbId, proc.Id)}>
|
||||
<DeleteIcon fontSize="inherit" />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{activeTab === 'tools' && (
|
||||
<Box>
|
||||
<Typography variant="h5" sx={{ mb: 3, fontWeight: 'medium' }}>
|
||||
|
||||
@@ -257,10 +257,13 @@
|
||||
}
|
||||
},
|
||||
"mariadb_process_monitor": {
|
||||
"title": "MariaDB Process List",
|
||||
"title": "MariaDB Process Monitor",
|
||||
"auto_refresh_desc": "The process list is automatically refreshed every 3 seconds.",
|
||||
"kill_btn": "KILL",
|
||||
"kill_confirm": "Are you sure you want to kill this process?",
|
||||
"refresh_btn": "Refresh",
|
||||
"click_to_view_sql": "Click to View SQL Query",
|
||||
"query_details": "Query Details",
|
||||
"no_processes": "No active processes found.",
|
||||
"columns": {
|
||||
"id": "ID",
|
||||
|
||||
@@ -252,10 +252,13 @@
|
||||
}
|
||||
},
|
||||
"mariadb_process_monitor": {
|
||||
"title": "MariaDB Süreç Listesi (Process List)",
|
||||
"title": "MariaDB Süreç İzleyici",
|
||||
"auto_refresh_desc": "Süreç listesi her 3 saniyede bir otomatik olarak yenilenir.",
|
||||
"kill_btn": "KILL",
|
||||
"kill_confirm": "Bu süreci sonlandırmak istediğinizden emin misiniz?",
|
||||
"refresh_btn": "Yenile",
|
||||
"click_to_view_sql": "SQL Sorgusunu Görüntülemek İçin Tıklayın",
|
||||
"query_details": "Sorgu Detayları",
|
||||
"no_processes": "Aktif süreç bulunamadı.",
|
||||
"columns": {
|
||||
"id": "ID",
|
||||
|
||||
Reference in New Issue
Block a user