feat: implement MariaDB management interface for database and user operations
This commit is contained in:
@@ -1,4 +1,116 @@
|
||||
[
|
||||
{
|
||||
"service": "php:5.6.40",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "e224nli4b",
|
||||
"timestamp": 1775131071408
|
||||
},
|
||||
{
|
||||
"service": "php:7.4.33",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "uylwtxbbj",
|
||||
"timestamp": 1775131071388
|
||||
},
|
||||
{
|
||||
"service": "php:8.2.30",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "2s4hcn046",
|
||||
"timestamp": 1775131071369
|
||||
},
|
||||
{
|
||||
"service": "php:8.5.4",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "6jyn1ulp5",
|
||||
"timestamp": 1775131071348
|
||||
},
|
||||
{
|
||||
"service": "mariadb:10.11.8",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "rwjbq8erc",
|
||||
"timestamp": 1775131070886
|
||||
},
|
||||
{
|
||||
"service": "mariadb:11.4.2",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "ckyqrf43d",
|
||||
"timestamp": 1775131070866
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "a91gjnuiz",
|
||||
"timestamp": 1775131070360
|
||||
},
|
||||
{
|
||||
"service": "nginx",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "6hzh7wm5t",
|
||||
"timestamp": 1775131070285
|
||||
},
|
||||
{
|
||||
"service": "php:5.6.40",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "e7dkj5bii",
|
||||
"timestamp": 1775130858539
|
||||
},
|
||||
{
|
||||
"service": "php:7.4.33",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "osp7uacka",
|
||||
"timestamp": 1775130858516
|
||||
},
|
||||
{
|
||||
"service": "php:8.2.30",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "w98vrvad6",
|
||||
"timestamp": 1775130858493
|
||||
},
|
||||
{
|
||||
"service": "php:8.5.4",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "kjbfmzipg",
|
||||
"timestamp": 1775130858466
|
||||
},
|
||||
{
|
||||
"service": "mariadb:11.4.2",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "src638nzz",
|
||||
"timestamp": 1775130857997
|
||||
},
|
||||
{
|
||||
"service": "mariadb:10.11.8",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "l5izsomjp",
|
||||
"timestamp": 1775130857977
|
||||
},
|
||||
{
|
||||
"service": "apache",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "tjho3pnv0",
|
||||
"timestamp": 1775130857478
|
||||
},
|
||||
{
|
||||
"service": "nginx",
|
||||
"action": "start",
|
||||
"status": "success",
|
||||
"id": "0l999aqql",
|
||||
"timestamp": 1775130857409
|
||||
},
|
||||
{
|
||||
"service": "mariadb:10.11.8",
|
||||
"action": "start",
|
||||
|
||||
@@ -276,6 +276,60 @@ export function registerIpcHandlers(): void {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:databases-with-sizes', async (_event, versionId) => {
|
||||
try {
|
||||
return await mariaDbManagerService.getDatabasesWithSizes(versionId)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to get databases:', error)
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:create-db', async (_event, { versionId, dbName }) => {
|
||||
try {
|
||||
return await mariaDbManagerService.createDatabase(versionId, dbName)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to create database:', error)
|
||||
return { success: false, message: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:drop-db', async (_event, { versionId, dbName }) => {
|
||||
try {
|
||||
return await mariaDbManagerService.dropDatabase(versionId, dbName)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to drop database:', error)
|
||||
return { success: false, message: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:users', async (_event, versionId) => {
|
||||
try {
|
||||
return await mariaDbManagerService.getUsers(versionId)
|
||||
} catch (error: any) {
|
||||
console.error('Failed to get users:', error)
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:create-user', async (_event, { versionId, user, host, password }) => {
|
||||
try {
|
||||
return await mariaDbManagerService.createUser(versionId, { user, host, password })
|
||||
} catch (error: any) {
|
||||
console.error('Failed to create user:', error)
|
||||
return { success: false, message: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('mariadb:drop-user', async (_event, { versionId, user, host }) => {
|
||||
try {
|
||||
return await mariaDbManagerService.dropUser(versionId, { user, host })
|
||||
} catch (error: any) {
|
||||
console.error('Failed to drop user:', error)
|
||||
return { success: false, message: error.message }
|
||||
}
|
||||
})
|
||||
|
||||
// CLI & Environment
|
||||
ipcMain.handle('cli:get-config', async () => {
|
||||
const settings = configService.getSettings()
|
||||
|
||||
@@ -215,6 +215,54 @@ export class MariaDbManagerService {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private async getMariaDbConfig(versionId: string) {
|
||||
const version = this.versions.find(v => v.id === versionId)
|
||||
if (!version) throw new Error('Version not found')
|
||||
|
||||
const binaryPath = await this.getMariaDbBinaryPath(version.version)
|
||||
if (!binaryPath) throw new Error('MariaDB binary not found')
|
||||
|
||||
const settings = configService.getSettings()
|
||||
return {
|
||||
binPath: binaryPath,
|
||||
host: '127.0.0.1',
|
||||
port: version.port,
|
||||
rootUser: 'root',
|
||||
rootPass: settings.mariaDbPasswords?.[versionId] || ''
|
||||
}
|
||||
}
|
||||
|
||||
async getDatabasesWithSizes(versionId: string) {
|
||||
const config = await this.getMariaDbConfig(versionId)
|
||||
return await mariaDbOperationService.getDatabasesWithSizes(config)
|
||||
}
|
||||
|
||||
async createDatabase(versionId: string, dbName: string) {
|
||||
const config = await this.getMariaDbConfig(versionId)
|
||||
const result = await mariaDbOperationService.runQuery(config, `CREATE DATABASE IF NOT EXISTS \`${dbName}\` CHARACTER SET utf8 COLLATE utf8_unicode_ci`)
|
||||
return { success: result.success, message: result.success ? 'Database created' : result.error }
|
||||
}
|
||||
|
||||
async dropDatabase(versionId: string, dbName: string) {
|
||||
const config = await this.getMariaDbConfig(versionId)
|
||||
return await mariaDbOperationService.dropDatabase(config, dbName)
|
||||
}
|
||||
|
||||
async getUsers(versionId: string) {
|
||||
const config = await this.getMariaDbConfig(versionId)
|
||||
return await mariaDbOperationService.listUsers(config)
|
||||
}
|
||||
|
||||
async createUser(versionId: string, { user, host, password }: any) {
|
||||
const config = await this.getMariaDbConfig(versionId)
|
||||
return await mariaDbOperationService.createUser(config, user, host, password)
|
||||
}
|
||||
|
||||
async dropUser(versionId: string, { user, host }: any) {
|
||||
const config = await this.getMariaDbConfig(versionId)
|
||||
return await mariaDbOperationService.dropUser(config, user, host)
|
||||
}
|
||||
}
|
||||
|
||||
export const mariaDbManagerService = new MariaDbManagerService()
|
||||
|
||||
@@ -216,6 +216,52 @@ export class MariaDbOperationService {
|
||||
.filter(line => line && line !== 'Database' && !line.match(/^information_schema|performance_schema|mysql|sys$/i))
|
||||
}
|
||||
|
||||
async getDatabasesWithSizes(config: MariaDbConfig): Promise<{ name: string; size: number }[]> {
|
||||
const query = "SELECT table_schema AS 'db', SUM(data_length + index_length) / 1024 / 1024 AS 'size' FROM information_schema.TABLES GROUP BY table_schema";
|
||||
const result = await this.runQuery(config, query)
|
||||
if (!result.success) return []
|
||||
|
||||
const lines = result.output.split('\n').filter(l => l.trim().length > 0)
|
||||
if (lines.length <= 1) return []
|
||||
|
||||
return lines.slice(1).map(line => {
|
||||
const [name, size] = line.split('\t')
|
||||
return { name, size: parseFloat(size) || 0 }
|
||||
}).filter(db => !db.name.match(/^information_schema|performance_schema|mysql|sys$/i))
|
||||
}
|
||||
|
||||
async listUsers(config: MariaDbConfig): Promise<{ user: string; host: string }[]> {
|
||||
const result = await this.runQuery(config, "SELECT user, host FROM mysql.user")
|
||||
if (!result.success) return []
|
||||
|
||||
const lines = result.output.split('\n').filter(l => l.trim().length > 0)
|
||||
if (lines.length <= 1) return []
|
||||
|
||||
return lines.slice(1).map(line => {
|
||||
const [user, host] = line.split('\t')
|
||||
return { user, host }
|
||||
})
|
||||
}
|
||||
|
||||
async createUser(config: MariaDbConfig, user: string, host: string, pass: string): Promise<{ success: boolean; message: string }> {
|
||||
const queries = [
|
||||
`CREATE USER IF NOT EXISTS '${user}'@'${host}' IDENTIFIED BY '${pass}';`,
|
||||
`GRANT ALL PRIVILEGES ON *.* TO '${user}'@'${host}' WITH GRANT OPTION;`,
|
||||
`FLUSH PRIVILEGES;`
|
||||
]
|
||||
|
||||
for (const query of queries) {
|
||||
const result = await this.runQuery(config, query)
|
||||
if (!result.success) return { success: false, message: result.error || 'Query failed' }
|
||||
}
|
||||
return { success: true, message: 'User created successfully' }
|
||||
}
|
||||
|
||||
async dropUser(config: MariaDbConfig, user: string, host: string): Promise<{ success: boolean; message: string }> {
|
||||
const result = await this.runQuery(config, `DROP USER '${user}'@'${host}'`)
|
||||
return { success: result.success, message: result.success ? 'User dropped' : result.error || 'Drop failed' }
|
||||
}
|
||||
|
||||
async exportSql(config: MariaDbConfig, dbName: string, targetPath: string, compress: boolean, onProgress?: (log: string) => void): Promise<{ success: boolean; message: string }> {
|
||||
return new Promise((resolve) => {
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
@@ -86,6 +86,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import { supportedLngs } from './i18n'
|
||||
import MariaDbImportWizard from './components/MariaDbImportWizard'
|
||||
import MariaDbExportWizard from './components/MariaDbExportWizard'
|
||||
import MariaDbDatabaseManager from './components/MariaDbDatabaseManager'
|
||||
import NginxCodeEditor from './components/NginxCodeEditor'
|
||||
import ApacheCodeEditor from './components/ApacheCodeEditor'
|
||||
import PhpExtensionManager from './components/PhpExtensionManager'
|
||||
@@ -1119,9 +1120,15 @@ function App(): JSX.Element {
|
||||
<ListItemText primary={t('common.tools')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton selected={activeTab === 'db_manager'} onClick={() => setActiveTab('db_manager')}>
|
||||
<ListItemIcon><DbIcon color={activeTab === 'db_manager' ? 'primary' : 'inherit'} /></ListItemIcon>
|
||||
<ListItemText primary={t('mariadb_manager.title')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton selected={activeTab === 'db_monitor'} onClick={() => setActiveTab('db_monitor')}>
|
||||
<ListItemIcon><DbIcon color={activeTab === 'db_monitor' ? 'primary' : 'inherit'} /></ListItemIcon>
|
||||
<ListItemIcon><TerminalIcon color={activeTab === 'db_monitor' ? 'primary' : 'inherit'} /></ListItemIcon>
|
||||
<ListItemText primary={t('mariadb_process_monitor.title')} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
@@ -1142,6 +1149,7 @@ function App(): JSX.Element {
|
||||
<Toolbar />
|
||||
<Container maxWidth={false} sx={{ py: 2 }}>
|
||||
{activeTab === 'tuning' && <ServerTuning />}
|
||||
{activeTab === 'db_manager' && <MariaDbDatabaseManager versions={mariaDbVersions} />}
|
||||
{activeTab === 'settings' && (
|
||||
<Box>
|
||||
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
Typography,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
IconButton,
|
||||
Button,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
Stack,
|
||||
Divider,
|
||||
Tooltip,
|
||||
CircularProgress,
|
||||
Alert
|
||||
} from '@mui/material'
|
||||
import {
|
||||
Delete as DeleteIcon,
|
||||
Add as AddIcon,
|
||||
Refresh as RefreshIcon,
|
||||
Storage as DbIcon,
|
||||
Person as UserIcon,
|
||||
Language as HostIcon
|
||||
} from '@mui/icons-material'
|
||||
import Swal from 'sweetalert2'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface DatabaseInfo {
|
||||
name: string
|
||||
size: number
|
||||
}
|
||||
|
||||
interface UserInfo {
|
||||
user: string
|
||||
host: string
|
||||
}
|
||||
|
||||
interface MariaDbDatabaseManagerProps {
|
||||
versions: any[]
|
||||
}
|
||||
|
||||
const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ versions }) => {
|
||||
const { t } = useTranslation()
|
||||
const [selectedVersion, setSelectedVersion] = useState<string>('')
|
||||
const [databases, setDatabases] = useState<DatabaseInfo[]>([])
|
||||
const [users, setUsers] = useState<UserInfo[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const installedVersions = versions.filter(v => v.status === 'installed')
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedVersion && installedVersions.length > 0) {
|
||||
setSelectedVersion(installedVersions[0].id)
|
||||
}
|
||||
}, [installedVersions])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedVersion) {
|
||||
fetchData()
|
||||
}
|
||||
}, [selectedVersion])
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!selectedVersion) return
|
||||
setLoading(true)
|
||||
try {
|
||||
const dbList = await window.api.invoke('mariadb:databases-with-sizes', selectedVersion)
|
||||
const userList = await window.api.invoke('mariadb:users', selectedVersion)
|
||||
setDatabases(dbList || [])
|
||||
setUsers(userList || [])
|
||||
} catch (error) {
|
||||
console.error('Fetch data error:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateDb = async () => {
|
||||
const { value: dbName } = await Swal.fire({
|
||||
title: t('mariadb_manager.create_db_title'),
|
||||
input: 'text',
|
||||
inputLabel: t('mariadb_manager.db_name_label'),
|
||||
showCancelButton: true,
|
||||
background: '#1e1e1e',
|
||||
color: '#fff',
|
||||
inputValidator: (value) => {
|
||||
if (!value) return t('common.error_required')
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
if (dbName) {
|
||||
const result = await window.api.invoke('mariadb:create-db', { versionId: selectedVersion, dbName })
|
||||
if (result.success) {
|
||||
Swal.fire({ icon: 'success', title: t('common.ok'), background: '#1e1e1e', color: '#fff' })
|
||||
fetchData()
|
||||
} else {
|
||||
Swal.fire({ icon: 'error', title: t('common.error'), text: result.message, background: '#1e1e1e', color: '#fff' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteDb = async (dbName: string) => {
|
||||
const result = await Swal.fire({
|
||||
title: t('common.are_you_sure'),
|
||||
text: t('mariadb_manager.delete_db_confirm', { db: dbName }),
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
background: '#1e1e1e',
|
||||
color: '#fff'
|
||||
})
|
||||
|
||||
if (result.isConfirmed) {
|
||||
const res = await window.api.invoke('mariadb:drop-db', { versionId: selectedVersion, dbName })
|
||||
if (res.success) {
|
||||
fetchData()
|
||||
} else {
|
||||
Swal.fire({ icon: 'error', title: t('common.error'), text: res.message, background: '#1e1e1e', color: '#fff' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateUser = async () => {
|
||||
const { value: formValues } = await Swal.fire({
|
||||
title: t('mariadb_manager.create_user_title'),
|
||||
html:
|
||||
`<input id="swal-user" class="swal2-input" placeholder="${t('mariadb_manager.user_label')}">` +
|
||||
`<input id="swal-host" class="swal2-input" placeholder="Host" value="%">` +
|
||||
`<input id="swal-pass" type="password" class="swal2-input" placeholder="${t('mariadb_manager.password_label')}">`,
|
||||
focusConfirm: false,
|
||||
background: '#1e1e1e',
|
||||
color: '#fff',
|
||||
preConfirm: () => {
|
||||
const user = (document.getElementById('swal-user') as HTMLInputElement).value
|
||||
const host = (document.getElementById('swal-host') as HTMLInputElement).value
|
||||
const password = (document.getElementById('swal-pass') as HTMLInputElement).value
|
||||
if (!user || !host) {
|
||||
Swal.showValidationMessage(t('common.error_required'))
|
||||
return null
|
||||
}
|
||||
return { user, host, password }
|
||||
}
|
||||
})
|
||||
|
||||
if (formValues) {
|
||||
const result = await window.api.invoke('mariadb:create-user', { versionId: selectedVersion, ...formValues })
|
||||
if (result.success) {
|
||||
Swal.fire({ icon: 'success', title: t('common.ok'), background: '#1e1e1e', color: '#fff' })
|
||||
fetchData()
|
||||
} else {
|
||||
Swal.fire({ icon: 'error', title: t('common.error'), text: result.message, background: '#1e1e1e', color: '#fff' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteUser = async (user: string, host: string) => {
|
||||
const result = await Swal.fire({
|
||||
title: t('common.are_you_sure'),
|
||||
text: t('mariadb_manager.delete_user_confirm', { user: `${user}@${host}` }),
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
background: '#1e1e1e',
|
||||
color: '#fff'
|
||||
})
|
||||
|
||||
if (result.isConfirmed) {
|
||||
const res = await window.api.invoke('mariadb:drop-user', { versionId: selectedVersion, user, host })
|
||||
if (res.success) {
|
||||
fetchData()
|
||||
} else {
|
||||
Swal.fire({ icon: 'error', title: t('common.error'), text: res.message, background: '#1e1e1e', color: '#fff' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper sx={{ p: 3, mb: 3, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
|
||||
<Stack direction="row" spacing={2} alignItems="center" justifyContent="space-between" sx={{ mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<DbIcon color="primary" />
|
||||
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>{t('mariadb_manager.title')}</Typography>
|
||||
</Box>
|
||||
<Stack direction="row" spacing={2}>
|
||||
<FormControl size="small" sx={{ minWidth: 200 }}>
|
||||
<InputLabel id="version-select-label">{t('mariadb.version')}</InputLabel>
|
||||
<Select
|
||||
labelId="version-select-label"
|
||||
value={selectedVersion}
|
||||
label={t('mariadb.version')}
|
||||
onChange={(e) => setSelectedVersion(e.target.value)}
|
||||
>
|
||||
{installedVersions.map(v => (
|
||||
<MenuItem key={v.id} value={v.id}>MariaDB {v.version}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<Button startIcon={<RefreshIcon />} onClick={fetchData} disabled={loading}>
|
||||
{t('common.refresh')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
{!selectedVersion && (
|
||||
<Alert severity="info" sx={{ mt: 2 }}>{t('mariadb_manager.no_active_version')}</Alert>
|
||||
)}
|
||||
|
||||
{selectedVersion && (
|
||||
<Stack spacing={4}>
|
||||
{/* Databases Section */}
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'primary.light' }}>
|
||||
{t('mariadb_manager.databases_title')}
|
||||
</Typography>
|
||||
<Button variant="contained" size="small" startIcon={<AddIcon />} onClick={handleCreateDb}>
|
||||
{t('mariadb_manager.create_db_btn')}
|
||||
</Button>
|
||||
</Box>
|
||||
<TableContainer component={Paper} variant="outlined" sx={{ bgcolor: 'rgba(0,0,0,0.2)', maxHeight: 400 }}>
|
||||
<Table stickyHeader size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 'bold' }}>{t('mariadb_manager.db_name')}</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 'bold' }}>{t('mariadb_manager.db_size')}</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 'bold', textAlign: 'right' }}>{t('common.actions')}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow><TableCell colSpan={3} align="center"><CircularProgress size={24} sx={{ my: 2 }} /></TableCell></TableRow>
|
||||
) : databases.length === 0 ? (
|
||||
<TableRow><TableCell colSpan={3} align="center" sx={{ py: 3, color: 'text.secondary' }}>{t('common.no_data')}</TableCell></TableRow>
|
||||
) : databases.map(db => (
|
||||
<TableRow key={db.name}>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{db.name}</TableCell>
|
||||
<TableCell>{db.size.toFixed(2)} MB</TableCell>
|
||||
<TableCell align="right">
|
||||
<Tooltip title={t('common.delete')}>
|
||||
<IconButton color="error" size="small" onClick={() => handleDeleteDb(db.name)}>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
|
||||
<Divider sx={{ opacity: 0.1 }} />
|
||||
|
||||
{/* Users Section */}
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||
<Typography variant="subtitle1" sx={{ fontWeight: 800, color: 'secondary.light' }}>
|
||||
{t('mariadb_manager.users_title')}
|
||||
</Typography>
|
||||
<Button variant="contained" color="secondary" size="small" startIcon={<AddIcon />} onClick={handleCreateUser}>
|
||||
{t('mariadb_manager.create_user_btn')}
|
||||
</Button>
|
||||
</Box>
|
||||
<TableContainer component={Paper} variant="outlined" sx={{ bgcolor: 'rgba(0,0,0,0.2)', maxHeight: 400 }}>
|
||||
<Table stickyHeader size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 'bold' }}>{t('mariadb_manager.user_label')}</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 'bold' }}>Host</TableCell>
|
||||
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 'bold', textAlign: 'right' }}>{t('common.actions')}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<TableRow><TableCell colSpan={3} align="center"><CircularProgress size={24} sx={{ my: 2 }} /></TableCell></TableRow>
|
||||
) : users.length === 0 ? (
|
||||
<TableRow><TableCell colSpan={3} align="center" sx={{ py: 3, color: 'text.secondary' }}>{t('common.no_data')}</TableCell></TableRow>
|
||||
) : users.map(user => (
|
||||
<TableRow key={`${user.user}-${user.host}`}>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<UserIcon fontSize="inherit" sx={{ opacity: 0.5 }} />
|
||||
{user.user}
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<HostIcon fontSize="inherit" sx={{ opacity: 0.5 }} />
|
||||
{user.host}
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Tooltip title={t('common.delete')}>
|
||||
<IconButton color="error" size="small" onClick={() => handleDeleteUser(user.user, user.host)}>
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default MariaDbDatabaseManager
|
||||
@@ -322,5 +322,22 @@
|
||||
"timestamp": "Time",
|
||||
"details": "Details"
|
||||
}
|
||||
},
|
||||
"mariadb_manager": {
|
||||
"title": "MariaDB Management",
|
||||
"databases_title": "Databases",
|
||||
"users_title": "Users",
|
||||
"create_db_btn": "New Database",
|
||||
"create_db_title": "Create New Database",
|
||||
"db_name_label": "Database Name",
|
||||
"db_name": "Database",
|
||||
"db_size": "Size",
|
||||
"create_user_btn": "New User",
|
||||
"create_user_title": "Create New User",
|
||||
"user_label": "Username",
|
||||
"password_label": "Password",
|
||||
"delete_db_confirm": "Are you sure you want to delete database '{{db}}'? This action cannot be undone!",
|
||||
"delete_user_confirm": "Are you sure you want to delete user '{{user}}'?",
|
||||
"no_active_version": "Please select an installed MariaDB version first or download one."
|
||||
}
|
||||
}
|
||||
@@ -317,5 +317,22 @@
|
||||
"timestamp": "Zaman",
|
||||
"details": "Detay"
|
||||
}
|
||||
},
|
||||
"mariadb_manager": {
|
||||
"title": "MariaDB Yönetimi",
|
||||
"databases_title": "Veritabanları",
|
||||
"users_title": "Kullanıcılar",
|
||||
"create_db_btn": "Yeni Veritabanı",
|
||||
"create_db_title": "Yeni Veritabanı Oluştur",
|
||||
"db_name_label": "Veritabanı Adı",
|
||||
"db_name": "Veritabanı",
|
||||
"db_size": "Boyut",
|
||||
"create_user_btn": "Yeni Kullanıcı",
|
||||
"create_user_title": "Yeni Kullanıcı Oluştur",
|
||||
"user_label": "Kullanıcı Adı",
|
||||
"password_label": "Şifre",
|
||||
"delete_db_confirm": "'{{db}}' veritabanını silmek istediğinize emin misiniz? Bu işlem geri alınamaz!",
|
||||
"delete_user_confirm": "'{{user}}' kullanıcısını silmek istediğinize emin misiniz?",
|
||||
"no_active_version": "Lütfen önce yüklü bir MariaDB sürümü seçin veya bir sürüm indirin."
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user