feat: expose MinIO operator console
This commit is contained in:
@@ -6,7 +6,7 @@ import type { ApiResponse } from '@/api/types'
|
|||||||
* gated by our own admin permissions. The backend registry is
|
* gated by our own admin permissions. The backend registry is
|
||||||
* config/internal_surfaces.php; the surface id must match a key there.
|
* config/internal_surfaces.php; the surface id must match a key there.
|
||||||
*/
|
*/
|
||||||
export type InternalSurface = 'db-studio'
|
export type InternalSurface = 'db-studio' | 'minio-console'
|
||||||
|
|
||||||
export interface InternalSurfaceSession {
|
export interface InternalSurfaceSession {
|
||||||
kind: 'internal_surface_session'
|
kind: 'internal_surface_session'
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export const PERM = {
|
|||||||
// internal operator consoles — `auth:` prefixed, so super-admin only
|
// internal operator consoles — `auth:` prefixed, so super-admin only
|
||||||
// (the `admin` role is synced to admin:% keys and would otherwise inherit it)
|
// (the `admin` role is synced to admin:% keys and would otherwise inherit it)
|
||||||
DB_STUDIO_ACCESS: 'auth:db-studio:access',
|
DB_STUDIO_ACCESS: 'auth:db-studio:access',
|
||||||
|
MINIO_CONSOLE_ACCESS: 'auth:minio-console:access',
|
||||||
// role management — only super-admin holds these
|
// role management — only super-admin holds these
|
||||||
ROLE_READ: 'auth:role:read',
|
ROLE_READ: 'auth:role:read',
|
||||||
ROLE_CREATE: 'auth:role:create',
|
ROLE_CREATE: 'auth:role:create',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Flag,
|
Flag,
|
||||||
FolderTree,
|
FolderTree,
|
||||||
Hash,
|
Hash,
|
||||||
|
HardDrive,
|
||||||
Inbox,
|
Inbox,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
Languages,
|
Languages,
|
||||||
@@ -131,6 +132,12 @@ export const NAV_GROUPS: NavGroup[] = [
|
|||||||
icon: Database,
|
icon: Database,
|
||||||
permission: 'auth:db-studio:access',
|
permission: 'auth:db-studio:access',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '对象存储管理台',
|
||||||
|
to: '/minio-console',
|
||||||
|
icon: HardDrive,
|
||||||
|
permission: 'auth:minio-console:access',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { Route as AuthedIndexRouteImport } from './routes/_authed/index'
|
|||||||
import { Route as AuthedSidRouteImport } from './routes/_authed/sid'
|
import { Route as AuthedSidRouteImport } from './routes/_authed/sid'
|
||||||
import { Route as AuthedRolesRouteImport } from './routes/_authed/roles'
|
import { Route as AuthedRolesRouteImport } from './routes/_authed/roles'
|
||||||
import { Route as AuthedRobotsRouteImport } from './routes/_authed/robots'
|
import { Route as AuthedRobotsRouteImport } from './routes/_authed/robots'
|
||||||
|
import { Route as AuthedMinioConsoleRouteImport } from './routes/_authed/minio-console'
|
||||||
import { Route as AuthedLocalesRouteImport } from './routes/_authed/locales'
|
import { Route as AuthedLocalesRouteImport } from './routes/_authed/locales'
|
||||||
import { Route as AuthedEventReportsRouteImport } from './routes/_authed/event-reports'
|
import { Route as AuthedEventReportsRouteImport } from './routes/_authed/event-reports'
|
||||||
import { Route as AuthedDimzouTranslationsRouteImport } from './routes/_authed/dimzou-translations'
|
import { Route as AuthedDimzouTranslationsRouteImport } from './routes/_authed/dimzou-translations'
|
||||||
@@ -75,6 +76,11 @@ const AuthedRobotsRoute = AuthedRobotsRouteImport.update({
|
|||||||
path: '/robots',
|
path: '/robots',
|
||||||
getParentRoute: () => AuthedRoute,
|
getParentRoute: () => AuthedRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AuthedMinioConsoleRoute = AuthedMinioConsoleRouteImport.update({
|
||||||
|
id: '/minio-console',
|
||||||
|
path: '/minio-console',
|
||||||
|
getParentRoute: () => AuthedRoute,
|
||||||
|
} as any)
|
||||||
const AuthedLocalesRoute = AuthedLocalesRouteImport.update({
|
const AuthedLocalesRoute = AuthedLocalesRouteImport.update({
|
||||||
id: '/locales',
|
id: '/locales',
|
||||||
path: '/locales',
|
path: '/locales',
|
||||||
@@ -213,6 +219,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/dimzou-translations': typeof AuthedDimzouTranslationsRoute
|
'/dimzou-translations': typeof AuthedDimzouTranslationsRoute
|
||||||
'/event-reports': typeof AuthedEventReportsRoute
|
'/event-reports': typeof AuthedEventReportsRoute
|
||||||
'/locales': typeof AuthedLocalesRoute
|
'/locales': typeof AuthedLocalesRoute
|
||||||
|
'/minio-console': typeof AuthedMinioConsoleRoute
|
||||||
'/robots': typeof AuthedRobotsRoute
|
'/robots': typeof AuthedRobotsRoute
|
||||||
'/roles': typeof AuthedRolesRoute
|
'/roles': typeof AuthedRolesRoute
|
||||||
'/sid': typeof AuthedSidRoute
|
'/sid': typeof AuthedSidRoute
|
||||||
@@ -244,6 +251,7 @@ export interface FileRoutesByTo {
|
|||||||
'/dimzou-translations': typeof AuthedDimzouTranslationsRoute
|
'/dimzou-translations': typeof AuthedDimzouTranslationsRoute
|
||||||
'/event-reports': typeof AuthedEventReportsRoute
|
'/event-reports': typeof AuthedEventReportsRoute
|
||||||
'/locales': typeof AuthedLocalesRoute
|
'/locales': typeof AuthedLocalesRoute
|
||||||
|
'/minio-console': typeof AuthedMinioConsoleRoute
|
||||||
'/robots': typeof AuthedRobotsRoute
|
'/robots': typeof AuthedRobotsRoute
|
||||||
'/roles': typeof AuthedRolesRoute
|
'/roles': typeof AuthedRolesRoute
|
||||||
'/sid': typeof AuthedSidRoute
|
'/sid': typeof AuthedSidRoute
|
||||||
@@ -278,6 +286,7 @@ export interface FileRoutesById {
|
|||||||
'/_authed/dimzou-translations': typeof AuthedDimzouTranslationsRoute
|
'/_authed/dimzou-translations': typeof AuthedDimzouTranslationsRoute
|
||||||
'/_authed/event-reports': typeof AuthedEventReportsRoute
|
'/_authed/event-reports': typeof AuthedEventReportsRoute
|
||||||
'/_authed/locales': typeof AuthedLocalesRoute
|
'/_authed/locales': typeof AuthedLocalesRoute
|
||||||
|
'/_authed/minio-console': typeof AuthedMinioConsoleRoute
|
||||||
'/_authed/robots': typeof AuthedRobotsRoute
|
'/_authed/robots': typeof AuthedRobotsRoute
|
||||||
'/_authed/roles': typeof AuthedRolesRoute
|
'/_authed/roles': typeof AuthedRolesRoute
|
||||||
'/_authed/sid': typeof AuthedSidRoute
|
'/_authed/sid': typeof AuthedSidRoute
|
||||||
@@ -313,6 +322,7 @@ export interface FileRouteTypes {
|
|||||||
| '/dimzou-translations'
|
| '/dimzou-translations'
|
||||||
| '/event-reports'
|
| '/event-reports'
|
||||||
| '/locales'
|
| '/locales'
|
||||||
|
| '/minio-console'
|
||||||
| '/robots'
|
| '/robots'
|
||||||
| '/roles'
|
| '/roles'
|
||||||
| '/sid'
|
| '/sid'
|
||||||
@@ -344,6 +354,7 @@ export interface FileRouteTypes {
|
|||||||
| '/dimzou-translations'
|
| '/dimzou-translations'
|
||||||
| '/event-reports'
|
| '/event-reports'
|
||||||
| '/locales'
|
| '/locales'
|
||||||
|
| '/minio-console'
|
||||||
| '/robots'
|
| '/robots'
|
||||||
| '/roles'
|
| '/roles'
|
||||||
| '/sid'
|
| '/sid'
|
||||||
@@ -377,6 +388,7 @@ export interface FileRouteTypes {
|
|||||||
| '/_authed/dimzou-translations'
|
| '/_authed/dimzou-translations'
|
||||||
| '/_authed/event-reports'
|
| '/_authed/event-reports'
|
||||||
| '/_authed/locales'
|
| '/_authed/locales'
|
||||||
|
| '/_authed/minio-console'
|
||||||
| '/_authed/robots'
|
| '/_authed/robots'
|
||||||
| '/_authed/roles'
|
| '/_authed/roles'
|
||||||
| '/_authed/sid'
|
| '/_authed/sid'
|
||||||
@@ -457,6 +469,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AuthedRobotsRouteImport
|
preLoaderRoute: typeof AuthedRobotsRouteImport
|
||||||
parentRoute: typeof AuthedRoute
|
parentRoute: typeof AuthedRoute
|
||||||
}
|
}
|
||||||
|
'/_authed/minio-console': {
|
||||||
|
id: '/_authed/minio-console'
|
||||||
|
path: '/minio-console'
|
||||||
|
fullPath: '/minio-console'
|
||||||
|
preLoaderRoute: typeof AuthedMinioConsoleRouteImport
|
||||||
|
parentRoute: typeof AuthedRoute
|
||||||
|
}
|
||||||
'/_authed/locales': {
|
'/_authed/locales': {
|
||||||
id: '/_authed/locales'
|
id: '/_authed/locales'
|
||||||
path: '/locales'
|
path: '/locales'
|
||||||
@@ -636,6 +655,7 @@ interface AuthedRouteChildren {
|
|||||||
AuthedDimzouTranslationsRoute: typeof AuthedDimzouTranslationsRoute
|
AuthedDimzouTranslationsRoute: typeof AuthedDimzouTranslationsRoute
|
||||||
AuthedEventReportsRoute: typeof AuthedEventReportsRoute
|
AuthedEventReportsRoute: typeof AuthedEventReportsRoute
|
||||||
AuthedLocalesRoute: typeof AuthedLocalesRoute
|
AuthedLocalesRoute: typeof AuthedLocalesRoute
|
||||||
|
AuthedMinioConsoleRoute: typeof AuthedMinioConsoleRoute
|
||||||
AuthedRobotsRoute: typeof AuthedRobotsRoute
|
AuthedRobotsRoute: typeof AuthedRobotsRoute
|
||||||
AuthedRolesRoute: typeof AuthedRolesRoute
|
AuthedRolesRoute: typeof AuthedRolesRoute
|
||||||
AuthedSidRoute: typeof AuthedSidRoute
|
AuthedSidRoute: typeof AuthedSidRoute
|
||||||
@@ -667,6 +687,7 @@ const AuthedRouteChildren: AuthedRouteChildren = {
|
|||||||
AuthedDimzouTranslationsRoute: AuthedDimzouTranslationsRoute,
|
AuthedDimzouTranslationsRoute: AuthedDimzouTranslationsRoute,
|
||||||
AuthedEventReportsRoute: AuthedEventReportsRoute,
|
AuthedEventReportsRoute: AuthedEventReportsRoute,
|
||||||
AuthedLocalesRoute: AuthedLocalesRoute,
|
AuthedLocalesRoute: AuthedLocalesRoute,
|
||||||
|
AuthedMinioConsoleRoute: AuthedMinioConsoleRoute,
|
||||||
AuthedRobotsRoute: AuthedRobotsRoute,
|
AuthedRobotsRoute: AuthedRobotsRoute,
|
||||||
AuthedRolesRoute: AuthedRolesRoute,
|
AuthedRolesRoute: AuthedRolesRoute,
|
||||||
AuthedSidRoute: AuthedSidRoute,
|
AuthedSidRoute: AuthedSidRoute,
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { openInternalSurface } from '@/api/modules/internal-surfaces'
|
||||||
|
import { PERM, requirePermission } from '@/auth/permissions'
|
||||||
|
import { PageHeader } from '@/components/page-header'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@/components/ui/card'
|
||||||
|
import { handleApiError } from '@/lib/errors'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/_authed/minio-console')({
|
||||||
|
beforeLoad: () => requirePermission(PERM.MINIO_CONSOLE_ACCESS),
|
||||||
|
component: MinioConsolePage,
|
||||||
|
})
|
||||||
|
|
||||||
|
function MinioConsolePage() {
|
||||||
|
const [busy, setBusy] = useState(false)
|
||||||
|
|
||||||
|
const open = async () => {
|
||||||
|
setBusy(true)
|
||||||
|
const tab = window.open('about:blank', '_blank')
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await openInternalSurface('minio-console')
|
||||||
|
if (tab) {
|
||||||
|
tab.opener = null
|
||||||
|
tab.location.replace(res.data.url)
|
||||||
|
} else {
|
||||||
|
window.location.assign(res.data.url)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
tab?.close()
|
||||||
|
handleApiError(error)
|
||||||
|
} finally {
|
||||||
|
setBusy(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageHeader
|
||||||
|
title="对象存储管理台"
|
||||||
|
description="在浏览器中检查和管理 MinIO 对象存储"
|
||||||
|
/>
|
||||||
|
<Card className="max-w-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-base">打开管理台</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
将在新标签页中打开。平台入口链接一次性有效、60 秒内过期,会话约
|
||||||
|
30 分钟后失效。
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<p className="text-muted-foreground text-sm">
|
||||||
|
平台权限仅控制管理台入口。打开后仍需使用 MinIO
|
||||||
|
凭据登录;对象删除和权限修改可能影响线上文件访问。
|
||||||
|
</p>
|
||||||
|
<Button onClick={open} disabled={busy}>
|
||||||
|
{busy ? '正在打开…' : '打开对象存储管理台'}
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user