feat: implement backend database management API and frontend service integration

This commit is contained in:
Ümit Tunç
2026-04-24 12:59:55 +03:00
parent 853a730210
commit 5c0744d5d1
5 changed files with 101 additions and 3 deletions
@@ -176,4 +176,15 @@ class SchemaController extends Controller
return Response::json(['error' => $e->getMessage()], 400);
}
}
public function tablesMetadata(Request $request, $database)
{
try {
$request->merge(['database' => $database]);
$this->initializeDriver($request);
return Response::json($this->databaseService->getTablesMetadata($database));
} catch (\Exception $e) {
return Response::json(['error' => $e->getMessage()], 400);
}
}
}
+8
View File
@@ -138,4 +138,12 @@ class DatabaseService
{
return $this->getDriver()->truncateTable($table);
}
/**
* Get metadata for all tables in a database.
*/
public function getTablesMetadata(string $database): array
{
return $this->getDriver()->getTablesMetadata($database);
}
}
+1
View File
@@ -12,6 +12,7 @@ Route::prefix('schema')->group(function () {
Route::get('/databases', [SchemaController::class, 'databases']);
Route::get('/tables/{database}', [SchemaController::class, 'tables']);
Route::get('/metadata/{database}', [SchemaController::class, 'metadata']);
Route::get('/metadata/{database}/tables', [SchemaController::class, 'tablesMetadata']);
Route::get('/metadata/{database}/{table}', [SchemaController::class, 'tableMetadata']);
Route::post('/truncate/{table}', [SchemaController::class, 'truncate']);
Route::get('/{table}', [SchemaController::class, 'schema']);
+80 -3
View File
@@ -325,6 +325,83 @@ const MainContent: React.FC = () => {
);
};
const DatabaseTablesGrid = ({ database }: { database: string }) => {
const [tableRows, setTableRows] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const { setActiveTable } = useAppStore();
useEffect(() => {
const fetchTablesMeta = async () => {
setLoading(true);
try {
const res = await SchemaService.getTablesMetadata(database);
setTableRows(res.data.map((r: any, i: number) => ({ id: r.name || i, ...r })));
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
};
fetchTablesMeta();
}, [database]);
const columns: GridColDef[] = [
{
field: 'name',
headerName: 'Table Name',
flex: 1,
minWidth: 200,
renderCell: (params) => (
<Button
size="small"
startIcon={<TableChart fontSize="small" />}
onClick={() => setActiveTable(params.value)}
sx={{ textTransform: 'none', fontWeight: 600, color: 'primary.main' }}
>
{params.value}
</Button>
)
},
{ field: 'engine', headerName: 'Engine', width: 120 },
{ field: 'rows', headerName: 'Rows', type: 'number', width: 120 },
{
field: 'data_length',
headerName: 'Data Size',
width: 130,
valueFormatter: (value) => `${(Number(value) / 1024 / 1024).toFixed(2)} MB`
},
{
field: 'index_length',
headerName: 'Index Size',
width: 130,
valueFormatter: (value) => `${(Number(value) / 1024 / 1024).toFixed(2)} MB`
},
{ field: 'collation', headerName: 'Collation', width: 180 },
{ field: 'comment', headerName: 'Comment', flex: 1, minWidth: 150 },
];
return (
<Paper sx={{
flexGrow: 1,
borderRadius: 3,
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
border: 1,
borderColor: 'divider',
boxShadow: '0 4px 12px rgba(0,0,0,0.05)'
}}>
<DataGrid
rows={tableRows}
columns={columns}
loading={loading}
density="comfortable"
sx={{ border: 'none' }}
/>
</Paper>
);
};
return (
<Box sx={{ flexGrow: 1, p: 3, bgcolor: 'background.default', display: 'flex', flexDirection: 'column', width: '100%', minWidth: 0, overflow: 'hidden', gap: 2 }}>
{/* Database Navigation Tabs */}
@@ -365,9 +442,9 @@ const MainContent: React.FC = () => {
{dbTab === 'tables' && (
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', minWidth: 0, overflow: 'hidden' }}>
{!activeTable ? (
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 2, opacity: 0.5 }}>
<TableChart sx={{ fontSize: 64 }} />
<Typography variant="h6">Select a table from the explorer to view data</Typography>
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', gap: 2 }}>
<Typography variant="h6" sx={{ fontWeight: 800 }}>Database Tables: {activeDatabase}</Typography>
<DatabaseTablesGrid database={activeDatabase} />
</Box>
) : (
<>
+1
View File
@@ -26,6 +26,7 @@ export const SchemaService = {
getDatabases: () => api.get('/schema/databases'),
getTables: (db: string) => api.get(`/schema/tables/${db}`, { params: { database: db } }),
getDatabaseMetadata: (db: string) => api.get(`/schema/metadata/${db}`, { params: { database: db } }),
getTablesMetadata: (db: string) => api.get(`/schema/metadata/${db}/tables`, { params: { database: db } }),
getTableMetadata: (db: string, table: string) => api.get(`/schema/metadata/${db}/${table}`, { params: { database: db } }),
getTableSchema: (table: string, database?: string) => api.get(`/schema/${table}`, { params: { database } }),
getTableData: (table: string, params: any) => api.get(`/schema/${table}/data`, { params }),