feat: add MariaDB tuning support and internationalization for application UI
This commit is contained in:
@@ -16,6 +16,7 @@ innodb_doublewrite=0
|
|||||||
innodb_lock_wait_timeout=600
|
innodb_lock_wait_timeout=600
|
||||||
innodb_strict_mode=0
|
innodb_strict_mode=0
|
||||||
innodb_default_row_format=DYNAMIC
|
innodb_default_row_format=DYNAMIC
|
||||||
|
{{EXTRA_CONFIG}}
|
||||||
|
|
||||||
[client]
|
[client]
|
||||||
port={{PORT}}
|
port={{PORT}}
|
||||||
|
|||||||
+53
-1
@@ -484,11 +484,13 @@ export function registerIpcHandlers(): void {
|
|||||||
const sanitizedServiceName = serviceName.replace(/:/g, '_')
|
const sanitizedServiceName = serviceName.replace(/:/g, '_')
|
||||||
const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`)
|
const logFile = path.join(configService.getBasePath(), 'logs', `${sanitizedServiceName}_error.log`)
|
||||||
|
|
||||||
|
const tuning = settings.mariaDbTuning?.[dbId] || ''
|
||||||
const configPath = await configService.generateConfig('mariadb.ini.template', `mariadb_${dbId || 'default'}.ini`, {
|
const configPath = await configService.generateConfig('mariadb.ini.template', `mariadb_${dbId || 'default'}.ini`, {
|
||||||
PORT: dbPort,
|
PORT: dbPort,
|
||||||
DATADIR: dataDir.replace(/\\/g, '/'),
|
DATADIR: dataDir.replace(/\\/g, '/'),
|
||||||
SOCKET: `/tmp/${sanitizedServiceName}.sock`,
|
SOCKET: `/tmp/${sanitizedServiceName}.sock`,
|
||||||
LOG_FILE: logFile.replace(/\\/g, '/')
|
LOG_FILE: logFile.replace(/\\/g, '/'),
|
||||||
|
EXTRA_CONFIG: tuning
|
||||||
})
|
})
|
||||||
|
|
||||||
const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false)
|
const success = await processManager.startService(serviceName, binPath, ['--defaults-file=' + configPath], false)
|
||||||
@@ -628,6 +630,56 @@ export function registerIpcHandlers(): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('mariadb:config:get', async (_event, versionId: string) => {
|
||||||
|
const settings = configService.getSettings()
|
||||||
|
const tuning = settings.mariaDbTuning?.[versionId] || ''
|
||||||
|
return { content: tuning }
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('mariadb:config:save', async (_event, { versionId, content }) => {
|
||||||
|
const settings = configService.getSettings()
|
||||||
|
const mariaDbTuning = settings.mariaDbTuning || {}
|
||||||
|
mariaDbTuning[versionId] = content
|
||||||
|
configService.updateSettings({ ...settings, mariaDbTuning })
|
||||||
|
|
||||||
|
const serviceName = `mariadb:${versionId.replace('mariadb-', '')}`
|
||||||
|
if (processManager.isServiceRunning(serviceName)) {
|
||||||
|
await processManager.stopService(serviceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, message: 'Tuning configuration saved.' }
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('php:versions:all', async () => {
|
||||||
|
return phpManagerService.getVersions()
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('php:config:get', async (_event, version: string) => {
|
||||||
|
const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`)
|
||||||
|
if (fs.existsSync(phpIniPath)) {
|
||||||
|
return { content: fs.readFileSync(phpIniPath, 'utf8') }
|
||||||
|
}
|
||||||
|
return { content: '' }
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('php:config:save', async (_event, { version, content }) => {
|
||||||
|
const phpIniPath = path.join(configService.getBasePath(), 'generated_configs', `php_${version}.ini`)
|
||||||
|
// Add manual edit marker to prevent overwriting
|
||||||
|
let finalContent = content
|
||||||
|
if (!finalContent.includes('; MANUAL_EDIT')) {
|
||||||
|
finalContent = `; MANUAL_EDIT\n${finalContent}`
|
||||||
|
}
|
||||||
|
fs.writeFileSync(phpIniPath, finalContent, 'utf8')
|
||||||
|
|
||||||
|
// Restart PHP if running
|
||||||
|
const serviceName = `php:${version}`
|
||||||
|
if (processManager.isServiceRunning(serviceName)) {
|
||||||
|
await processManager.stopService(serviceName)
|
||||||
|
// It will be started again later or manually
|
||||||
|
}
|
||||||
|
return { success: true }
|
||||||
|
})
|
||||||
|
|
||||||
// Apache Versions
|
// Apache Versions
|
||||||
ipcMain.handle('apache:versions', async () => {
|
ipcMain.handle('apache:versions', async () => {
|
||||||
return await apacheManagerService.getVersions()
|
return await apacheManagerService.getVersions()
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export class ConfigService {
|
|||||||
language: 'tr',
|
language: 'tr',
|
||||||
cliEnvEnabled: true,
|
cliEnvEnabled: true,
|
||||||
defaultPhpVersion: '',
|
defaultPhpVersion: '',
|
||||||
defaultMariaDbVersion: ''
|
defaultMariaDbVersion: '',
|
||||||
|
mariaDbTuning: {} as Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ import {
|
|||||||
Launch as LaunchIcon,
|
Launch as LaunchIcon,
|
||||||
Handyman as ToolsIcon,
|
Handyman as ToolsIcon,
|
||||||
Restore as ResetIcon,
|
Restore as ResetIcon,
|
||||||
Search as SearchIcon
|
Search as SearchIcon,
|
||||||
|
Tune as TuningIcon
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import Swal from 'sweetalert2'
|
import Swal from 'sweetalert2'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@@ -88,6 +89,7 @@ import MariaDbExportWizard from './components/MariaDbExportWizard'
|
|||||||
import NginxCodeEditor from './components/NginxCodeEditor'
|
import NginxCodeEditor from './components/NginxCodeEditor'
|
||||||
import ApacheCodeEditor from './components/ApacheCodeEditor'
|
import ApacheCodeEditor from './components/ApacheCodeEditor'
|
||||||
import PhpExtensionManager from './components/PhpExtensionManager'
|
import PhpExtensionManager from './components/PhpExtensionManager'
|
||||||
|
import ServerTuning from './components/ServerTuning'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -1105,6 +1107,12 @@ function App(): JSX.Element {
|
|||||||
<ListItemText primary={t('projects.title')} />
|
<ListItemText primary={t('projects.title')} />
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
<ListItem disablePadding>
|
||||||
|
<ListItemButton selected={activeTab === 'tuning'} onClick={() => setActiveTab('tuning')}>
|
||||||
|
<ListItemIcon><TuningIcon color={activeTab === 'tuning' ? 'primary' : 'inherit'} /></ListItemIcon>
|
||||||
|
<ListItemText primary={t('tuning.title')} />
|
||||||
|
</ListItemButton>
|
||||||
|
</ListItem>
|
||||||
<ListItem disablePadding>
|
<ListItem disablePadding>
|
||||||
<ListItemButton selected={activeTab === 'tools'} onClick={() => setActiveTab('tools')}>
|
<ListItemButton selected={activeTab === 'tools'} onClick={() => setActiveTab('tools')}>
|
||||||
<ListItemIcon><ToolsIcon color={activeTab === 'tools' ? 'primary' : 'inherit'} /></ListItemIcon>
|
<ListItemIcon><ToolsIcon color={activeTab === 'tools' ? 'primary' : 'inherit'} /></ListItemIcon>
|
||||||
@@ -1133,6 +1141,7 @@ function App(): JSX.Element {
|
|||||||
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
|
||||||
<Toolbar />
|
<Toolbar />
|
||||||
<Container maxWidth={false} sx={{ py: 2 }}>
|
<Container maxWidth={false} sx={{ py: 2 }}>
|
||||||
|
{activeTab === 'tuning' && <ServerTuning />}
|
||||||
{activeTab === 'settings' && (
|
{activeTab === 'settings' && (
|
||||||
<Box>
|
<Box>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,426 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
Grid,
|
||||||
|
Paper,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
Slider,
|
||||||
|
TextField,
|
||||||
|
Stack,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
Alert,
|
||||||
|
CircularProgress,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardActionArea,
|
||||||
|
Chip
|
||||||
|
} from '@mui/material'
|
||||||
|
import {
|
||||||
|
Tune as TuneIcon,
|
||||||
|
Close as CloseIcon,
|
||||||
|
Save as SaveIcon,
|
||||||
|
Code as PhpIcon,
|
||||||
|
Storage as DbIcon,
|
||||||
|
InfoOutlined as InfoIcon
|
||||||
|
} from '@mui/icons-material'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
interface TuningParam {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
description: string
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
step: number
|
||||||
|
unit: string
|
||||||
|
type: 'php' | 'mariadb'
|
||||||
|
transform?: (val: number) => string
|
||||||
|
parse?: (val: string) => number
|
||||||
|
}
|
||||||
|
|
||||||
|
const PHP_PARAMS: TuningParam[] = [
|
||||||
|
{
|
||||||
|
key: 'memory_limit',
|
||||||
|
label: 'memory_limit',
|
||||||
|
description: 'tuning.parameters.memory_limit_desc',
|
||||||
|
min: 64,
|
||||||
|
max: 4096,
|
||||||
|
step: 64,
|
||||||
|
unit: 'M',
|
||||||
|
type: 'php',
|
||||||
|
transform: (v) => `${v}M`,
|
||||||
|
parse: (v) => parseInt(v.replace('M', '')) || 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'max_execution_time',
|
||||||
|
label: 'max_execution_time',
|
||||||
|
description: 'tuning.parameters.max_execution_time_desc',
|
||||||
|
min: 30,
|
||||||
|
max: 3600,
|
||||||
|
step: 30,
|
||||||
|
unit: 's',
|
||||||
|
type: 'php',
|
||||||
|
transform: (v) => `${v}`,
|
||||||
|
parse: (v) => parseInt(v) || 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'post_max_size',
|
||||||
|
label: 'post_max_size',
|
||||||
|
description: 'tuning.parameters.post_max_size_desc',
|
||||||
|
min: 8,
|
||||||
|
max: 2048,
|
||||||
|
step: 8,
|
||||||
|
unit: 'M',
|
||||||
|
type: 'php',
|
||||||
|
transform: (v) => `${v}M`,
|
||||||
|
parse: (v) => parseInt(v.replace('M', '')) || 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'upload_max_filesize',
|
||||||
|
label: 'upload_max_filesize',
|
||||||
|
description: 'tuning.parameters.upload_max_filesize_desc',
|
||||||
|
min: 2,
|
||||||
|
max: 2048,
|
||||||
|
step: 2,
|
||||||
|
unit: 'M',
|
||||||
|
type: 'php',
|
||||||
|
transform: (v) => `${v}M`,
|
||||||
|
parse: (v) => parseInt(v.replace('M', '')) || 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'max_input_vars',
|
||||||
|
label: 'max_input_vars',
|
||||||
|
description: 'tuning.parameters.max_input_vars_desc',
|
||||||
|
min: 1000,
|
||||||
|
max: 100000,
|
||||||
|
step: 1000,
|
||||||
|
unit: '',
|
||||||
|
type: 'php',
|
||||||
|
transform: (v) => `${v}`,
|
||||||
|
parse: (v) => parseInt(v) || 1000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const MARIADB_PARAMS: TuningParam[] = [
|
||||||
|
{
|
||||||
|
key: 'max_connections',
|
||||||
|
label: 'max_connections',
|
||||||
|
description: 'tuning.parameters.max_connections_desc',
|
||||||
|
min: 10,
|
||||||
|
max: 5000,
|
||||||
|
step: 10,
|
||||||
|
unit: '',
|
||||||
|
type: 'mariadb',
|
||||||
|
transform: (v) => `${v}`,
|
||||||
|
parse: (v) => parseInt(v) || 151
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'innodb_buffer_pool_size',
|
||||||
|
label: 'innodb_buffer_pool_size',
|
||||||
|
description: 'tuning.parameters.innodb_buffer_pool_size_desc',
|
||||||
|
min: 128,
|
||||||
|
max: 16384,
|
||||||
|
step: 128,
|
||||||
|
unit: 'M',
|
||||||
|
type: 'mariadb',
|
||||||
|
transform: (v) => `${v}M`,
|
||||||
|
parse: (v) => {
|
||||||
|
if (v.endsWith('G')) return parseInt(v) * 1024
|
||||||
|
return parseInt(v) || 128
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'key_buffer_size',
|
||||||
|
label: 'key_buffer_size',
|
||||||
|
description: 'tuning.parameters.key_buffer_size_desc',
|
||||||
|
min: 8,
|
||||||
|
max: 1024,
|
||||||
|
step: 8,
|
||||||
|
unit: 'M',
|
||||||
|
type: 'mariadb',
|
||||||
|
transform: (v) => `${v}M`,
|
||||||
|
parse: (v) => parseInt(v) || 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'max_allowed_packet',
|
||||||
|
label: 'max_allowed_packet',
|
||||||
|
description: 'tuning.parameters.max_allowed_packet_desc',
|
||||||
|
min: 1,
|
||||||
|
max: 1024,
|
||||||
|
step: 1,
|
||||||
|
unit: 'M',
|
||||||
|
type: 'mariadb',
|
||||||
|
transform: (v) => `${v}M`,
|
||||||
|
parse: (v) => {
|
||||||
|
if (v.endsWith('G')) return parseInt(v) * 1024
|
||||||
|
return parseInt(v) || 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function ServerTuning() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [phpVersions, setPhpVersions] = useState<any[]>([])
|
||||||
|
const [mariaDbVersions, setMariaDbVersions] = useState<any[]>([])
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [selectedVersion, setSelectedVersion] = useState<any>(null)
|
||||||
|
const [configContent, setConfigContent] = useState('')
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||||
|
const [saving, setSaving] = useState(false)
|
||||||
|
const [tuningValues, setTuningValues] = useState<Record<string, number>>({})
|
||||||
|
|
||||||
|
const fetchVersions = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
if (window.api) {
|
||||||
|
const phps = await window.api.getPhpVersions()
|
||||||
|
const dbs = await window.api.getMariaDbVersions()
|
||||||
|
setPhpVersions(phps.filter((v: any) => v.status === 'installed'))
|
||||||
|
setMariaDbVersions(dbs.filter((v: any) => v.status === 'installed'))
|
||||||
|
}
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchVersions()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleOpenTuning = async (version: any, type: 'php' | 'mariadb') => {
|
||||||
|
setSelectedVersion({ ...version, type })
|
||||||
|
let content = ''
|
||||||
|
if (type === 'php') {
|
||||||
|
const res = await window.api.invoke('php:config:get', version.version)
|
||||||
|
content = res.content
|
||||||
|
} else {
|
||||||
|
const res = await window.api.invoke('mariadb:config:get', version.id)
|
||||||
|
content = res.content
|
||||||
|
}
|
||||||
|
setConfigContent(content)
|
||||||
|
|
||||||
|
// Parse current values
|
||||||
|
const params = type === 'php' ? PHP_PARAMS : MARIADB_PARAMS
|
||||||
|
const values: Record<string, number> = {}
|
||||||
|
params.forEach(p => {
|
||||||
|
const regex = type === 'php'
|
||||||
|
? new RegExp(`^\\s*${p.key}\\s*=\\s*(.+)$`, 'm')
|
||||||
|
: new RegExp(`^\\s*${p.key}\\s*=\\s*(.+)$`, 'm')
|
||||||
|
|
||||||
|
const match = content.match(regex)
|
||||||
|
if (match && p.parse) {
|
||||||
|
values[p.key] = p.parse(match[1].trim())
|
||||||
|
} else {
|
||||||
|
values[p.key] = p.min // Default to min if not found
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setTuningValues(values)
|
||||||
|
setIsModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
setSaving(true)
|
||||||
|
let updatedContent = configContent
|
||||||
|
const params = selectedVersion.type === 'php' ? PHP_PARAMS : MARIADB_PARAMS
|
||||||
|
|
||||||
|
params.forEach(p => {
|
||||||
|
const newValue = p.transform ? p.transform(tuningValues[p.key]) : tuningValues[p.key].toString()
|
||||||
|
const regex = new RegExp(`^(\\s*${p.key}\\s*=\\s*)(.+)$`, 'm')
|
||||||
|
|
||||||
|
if (updatedContent.match(regex)) {
|
||||||
|
updatedContent = updatedContent.replace(regex, `$1${newValue}`)
|
||||||
|
} else {
|
||||||
|
updatedContent += `\n${p.key}=${newValue}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (selectedVersion.type === 'php') {
|
||||||
|
await window.api.invoke('php:config:save', { version: selectedVersion.version, content: updatedContent })
|
||||||
|
} else {
|
||||||
|
await window.api.invoke('mariadb:config:save', { versionId: selectedVersion.id, content: updatedContent })
|
||||||
|
}
|
||||||
|
|
||||||
|
setSaving(false)
|
||||||
|
setIsModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 10 }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box sx={{ mb: 4 }}>
|
||||||
|
<Typography variant="h4" sx={{ fontWeight: 800, mb: 1, color: 'primary.light' }}>
|
||||||
|
{t('tuning.title')}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1" sx={{ color: 'rgba(255,255,255,0.6)' }}>
|
||||||
|
{t('tuning.desc')}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Grid container spacing={4}>
|
||||||
|
{/* PHP Section */}
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Paper sx={{ p: 3, height: '100%', bgcolor: 'rgba(255,255,255,0.02)', borderRadius: 4, border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3, gap: 2 }}>
|
||||||
|
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: 'primary.main', display: 'flex' }}>
|
||||||
|
<PhpIcon sx={{ color: 'white' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>PHP Tuning</Typography>
|
||||||
|
<Chip label={phpVersions.length} size="small" variant="outlined" />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Stack spacing={2}>
|
||||||
|
{phpVersions.map(v => (
|
||||||
|
<Card key={v.id} sx={{ bgcolor: 'rgba(255,255,255,0.03)', backgroundImage: 'none', border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
|
<CardActionArea onClick={() => handleOpenTuning(v, 'php')}>
|
||||||
|
<CardContent sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>PHP {v.version}</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">php.ini optimization</Typography>
|
||||||
|
</Box>
|
||||||
|
<Button variant="contained" size="small" startIcon={<TuneIcon />}>
|
||||||
|
{t('tuning.tune_btn')}
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* MariaDB Section */}
|
||||||
|
<Grid item xs={12} md={6}>
|
||||||
|
<Paper sx={{ p: 3, height: '100%', bgcolor: 'rgba(255,255,255,0.02)', borderRadius: 4, border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3, gap: 2 }}>
|
||||||
|
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: 'secondary.main', display: 'flex' }}>
|
||||||
|
<DbIcon sx={{ color: 'white' }} />
|
||||||
|
</Box>
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB Tuning</Typography>
|
||||||
|
<Chip label={mariaDbVersions.length} size="small" variant="outlined" />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Stack spacing={2}>
|
||||||
|
{mariaDbVersions.map(v => (
|
||||||
|
<Card key={v.id} sx={{ bgcolor: 'rgba(255,255,255,0.03)', backgroundImage: 'none', border: '1px solid rgba(255,255,255,0.05)' }}>
|
||||||
|
<CardActionArea onClick={() => handleOpenTuning(v, 'mariadb')}>
|
||||||
|
<CardContent sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB {v.version}</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">Performance variables</Typography>
|
||||||
|
</Box>
|
||||||
|
<Button variant="contained" color="secondary" size="small" startIcon={<TuneIcon />}>
|
||||||
|
{t('tuning.tune_btn')}
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Tuning Modal */}
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onClose={() => !saving && setIsModalOpen(false)}
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
bgcolor: '#1a1d21',
|
||||||
|
backgroundImage: 'none',
|
||||||
|
color: '#fff',
|
||||||
|
borderRadius: 4,
|
||||||
|
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' }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||||
|
<TuneIcon color={selectedVersion?.type === 'php' ? 'primary' : 'secondary'} />
|
||||||
|
<Typography variant="h6" sx={{ fontWeight: 800 }}>
|
||||||
|
{selectedVersion?.type === 'php' ? `PHP ${selectedVersion?.version}` : `MariaDB ${selectedVersion?.version}`} Tuning
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<IconButton onClick={() => setIsModalOpen(false)} sx={{ color: 'rgba(255,255,255,0.5)' }}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Box sx={{ pt: 2 }}>
|
||||||
|
<Alert severity="info" sx={{ mb: 4, bgcolor: 'rgba(2, 136, 209, 0.1)', color: 'info.light', border: '1px solid rgba(2, 136, 209, 0.2)' }}>
|
||||||
|
{t('tuning.restart_notice')}
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<Stack spacing={5}>
|
||||||
|
{(selectedVersion?.type === 'php' ? PHP_PARAMS : MARIADB_PARAMS).map(p => (
|
||||||
|
<Box key={p.key}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1, alignItems: 'center' }}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>{p.label}</Typography>
|
||||||
|
<Tooltip title={t(p.description)}>
|
||||||
|
<InfoIcon sx={{ fontSize: 16, color: 'rgba(255,255,255,0.3)', cursor: 'help' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<TextField
|
||||||
|
size="small"
|
||||||
|
value={tuningValues[p.key]}
|
||||||
|
onChange={(e) => setTuningValues({ ...tuningValues, [p.key]: parseInt(e.target.value) || p.min })}
|
||||||
|
InputProps={{
|
||||||
|
endAdornment: <Typography variant="caption" sx={{ ml: 1, opacity: 0.5 }}>{p.unit}</Typography>
|
||||||
|
} as any}
|
||||||
|
sx={{ width: 120, '& .MuiOutlinedInput-root': { height: 32 } }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Slider
|
||||||
|
value={tuningValues[p.key]}
|
||||||
|
min={p.min}
|
||||||
|
max={p.max}
|
||||||
|
step={p.step}
|
||||||
|
onChange={(_e, v) => setTuningValues({ ...tuningValues, [p.key]: v as number })}
|
||||||
|
sx={{
|
||||||
|
color: selectedVersion?.type === 'php' ? 'primary.main' : 'secondary.main',
|
||||||
|
'& .MuiSlider-thumb': {
|
||||||
|
width: 14,
|
||||||
|
height: 14,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mt: -1, display: 'block' }}>
|
||||||
|
{t(p.description)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions sx={{ p: 3, borderTop: '1px solid rgba(255,255,255,0.1)' }}>
|
||||||
|
<Button onClick={() => setIsModalOpen(false)} disabled={saving}>
|
||||||
|
{t('common.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color={selectedVersion?.type === 'php' ? 'primary' : 'secondary'}
|
||||||
|
onClick={handleSave}
|
||||||
|
startIcon={saving ? <CircularProgress size={20} color="inherit" /> : <SaveIcon />}
|
||||||
|
disabled={saving}
|
||||||
|
sx={{ px: 4, borderRadius: 2 }}
|
||||||
|
>
|
||||||
|
{saving ? t('tuning.saving') : t('common.save')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -276,5 +276,34 @@
|
|||||||
"info": "Info",
|
"info": "Info",
|
||||||
"progress": "Progress"
|
"progress": "Progress"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"tuning": {
|
||||||
|
"title": "Server Tuning",
|
||||||
|
"desc": "Optimize performance of PHP and MariaDB servers with key resource limits.",
|
||||||
|
"php_total": "PHP Instances",
|
||||||
|
"mariadb_total": "MariaDB Instances",
|
||||||
|
"tune_btn": "TUNE",
|
||||||
|
"saving": "Applying changes...",
|
||||||
|
"restart_notice": "Some changes require a service restart to take effect.",
|
||||||
|
"parameters": {
|
||||||
|
"memory_limit": "Memory Limit",
|
||||||
|
"memory_limit_desc": "Maximum amount of memory a script may consume.",
|
||||||
|
"max_execution_time": "Max Execution Time",
|
||||||
|
"max_execution_time_desc": "Maximum time in seconds a script is allowed to run.",
|
||||||
|
"post_max_size": "Post Max Size",
|
||||||
|
"post_max_size_desc": "Maximum size of POST data that PHP will accept.",
|
||||||
|
"upload_max_filesize": "Upload Max Filesize",
|
||||||
|
"upload_max_filesize_desc": "Maximum size of an uploaded file.",
|
||||||
|
"max_input_vars": "Max Input Vars",
|
||||||
|
"max_input_vars_desc": "How many input variables may be accepted.",
|
||||||
|
"max_connections": "Max Connections",
|
||||||
|
"max_connections_desc": "Maximum number of simultaneous client connections.",
|
||||||
|
"innodb_buffer_pool_size": "InnoDB Buffer Pool",
|
||||||
|
"innodb_buffer_pool_size_desc": "Crucial for performance. Holds indexes and data from memory.",
|
||||||
|
"key_buffer_size": "Key Buffer Size",
|
||||||
|
"key_buffer_size_desc": "Index blocks for MyISAM tables (shared by all threads).",
|
||||||
|
"max_allowed_packet": "Max Allowed Packet",
|
||||||
|
"max_allowed_packet_desc": "Maximum size of one packet or any generated/intermediate string."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -271,5 +271,34 @@
|
|||||||
"info": "Bilgi",
|
"info": "Bilgi",
|
||||||
"progress": "İlerleme"
|
"progress": "İlerleme"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"tuning": {
|
||||||
|
"title": "Sunucu Optimizasyonu",
|
||||||
|
"desc": "PHP ve MariaDB sunucularının performansını kritik kaynak sınırlarıyla optimize edin.",
|
||||||
|
"php_total": "PHP Örneği",
|
||||||
|
"mariadb_total": "MariaDB Örneği",
|
||||||
|
"tune_btn": "AYARLA",
|
||||||
|
"saving": "Değişiklikler uygulanıyor...",
|
||||||
|
"restart_notice": "Bazı değişikliklerin geçerli olması için servislerin yeniden başlatılması gerekir.",
|
||||||
|
"parameters": {
|
||||||
|
"memory_limit": "Bellek Limiti",
|
||||||
|
"memory_limit_desc": "Bir betiğin tüketebileceği maksimum bellek miktarı.",
|
||||||
|
"max_execution_time": "Maksimum Çalışma Süresi",
|
||||||
|
"max_execution_time_desc": "Bir betiğin çalışmasına izin verilen maksimum süre (saniye).",
|
||||||
|
"post_max_size": "Post Maksimum Boyutu",
|
||||||
|
"post_max_size_desc": "PHP'nin kabul edeceği maksimum POST verisi boyutu.",
|
||||||
|
"upload_max_filesize": "Dosya Yükleme Limiti",
|
||||||
|
"upload_max_filesize_desc": "Yüklenebilecek maksimum dosya boyutu.",
|
||||||
|
"max_input_vars": "Maksimum Girdi Değişkeni",
|
||||||
|
"max_input_vars_desc": "Kaç tane girdi değişkeninin (GET/POST/Cookie) kabul edilebileceği.",
|
||||||
|
"max_connections": "Maksimum Bağlantı",
|
||||||
|
"max_connections_desc": "Eşzamanlı maksimum istemci bağlantısı sayısı.",
|
||||||
|
"innodb_buffer_pool_size": "InnoDB Tampon Havuzu",
|
||||||
|
"innodb_buffer_pool_size_desc": "Performans için kritiktir. İndeksleri ve verileri bellekte tutar.",
|
||||||
|
"key_buffer_size": "Anahtar Tampon Boyutu",
|
||||||
|
"key_buffer_size_desc": "MyISAM tabloları için indeks blokları (tüm iş parçacıkları tarafından paylaşılır).",
|
||||||
|
"max_allowed_packet": "Maksimum Paket Boyutu",
|
||||||
|
"max_allowed_packet_desc": "Bir paketin veya oluşturulan herhangi bir dizenin maksimum boyutu."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user