This commit is contained in:
@@ -4,16 +4,18 @@ import { DataTableCard, type DataTableColumn, type DataTableFilter } from '../..
|
||||
import { useToast } from '../../components/toast';
|
||||
import { Button, Modal } from '../../components/ui';
|
||||
import { PageTitle } from '../../layout/AppLayout';
|
||||
import { getAdminAppPermissions, isOwnerAdmin, setAdminAppPermissions } from '../../lib/admin-app-permissions';
|
||||
import { isOwnerAdmin, setAdminAppPermissions } from '../../lib/admin-app-permissions';
|
||||
import { listApps } from '../applications/application-api';
|
||||
import type { AppWithComponents } from '../../types/application';
|
||||
import {
|
||||
createAdmin,
|
||||
createAdminSshKey,
|
||||
deleteAdminSshKey,
|
||||
loadAdminAppPermissions,
|
||||
loadAdminRoles,
|
||||
loadAdmins,
|
||||
resetAdminPassword,
|
||||
updateAdminAppPermissions,
|
||||
updateAdminRole,
|
||||
updateAdminStatus,
|
||||
type AdminAccount,
|
||||
@@ -68,6 +70,7 @@ export function AdminManagementPage() {
|
||||
});
|
||||
const [permissionApps, setPermissionApps] = useState<AppWithComponents[]>([]);
|
||||
const [permissionLoading, setPermissionLoading] = useState(false);
|
||||
const [permissionSaving, setPermissionSaving] = useState(false);
|
||||
const loadingVisible = useMinimumLoading(loading);
|
||||
|
||||
const refresh = () => {
|
||||
@@ -123,14 +126,15 @@ export function AdminManagementPage() {
|
||||
|
||||
const openPermissionModal = (admin: AdminAccount) => {
|
||||
setMenuAdmin(null);
|
||||
setPermissionTarget({ admin, appIds: isOwnerAdmin(admin) ? [] : getAdminAppPermissions(admin) });
|
||||
setPermissionTarget({ admin, appIds: [] });
|
||||
setPermissionLoading(true);
|
||||
listApps()
|
||||
.then((apps) => {
|
||||
Promise.all([
|
||||
listApps(),
|
||||
isOwnerAdmin(admin) ? Promise.resolve<string[]>([]) : loadAdminAppPermissions(admin.id)
|
||||
])
|
||||
.then(([apps, appIds]) => {
|
||||
setPermissionApps(apps);
|
||||
if (isOwnerAdmin(admin)) {
|
||||
setPermissionTarget({ admin, appIds: apps.map((app) => app.id) });
|
||||
}
|
||||
setPermissionTarget({ admin, appIds: isOwnerAdmin(admin) ? apps.map((app) => app.id) : appIds });
|
||||
})
|
||||
.catch(() => {
|
||||
setPermissionApps([]);
|
||||
@@ -152,9 +156,18 @@ export function AdminManagementPage() {
|
||||
return;
|
||||
}
|
||||
|
||||
setAdminAppPermissions(permissionTarget.admin, permissionTarget.appIds);
|
||||
setPermissionTarget({ admin: null, appIds: [] });
|
||||
toast({ type: 'success', title: '앱 권한 저장', message: '관리자 앱 권한이 저장되었습니다.' });
|
||||
setPermissionSaving(true);
|
||||
updateAdminAppPermissions(permissionTarget.admin.id, permissionTarget.appIds)
|
||||
.then((result) => {
|
||||
setAdminAppPermissions(permissionTarget.admin as AdminAccount, result.appIds ?? permissionTarget.appIds);
|
||||
setPermissionTarget({ admin: null, appIds: [] });
|
||||
toast({ type: 'success', title: '앱 권한 저장', message: '관리자 앱 권한이 저장되었습니다.' });
|
||||
refresh();
|
||||
})
|
||||
.catch((error) => {
|
||||
toast({ type: 'error', title: '앱 권한 저장 실패', message: error instanceof Error ? error.message : '관리자 앱 권한 저장에 실패했습니다.' });
|
||||
})
|
||||
.finally(() => setPermissionSaving(false));
|
||||
};
|
||||
|
||||
const submitAdmin = () => {
|
||||
@@ -486,9 +499,9 @@ export function AdminManagementPage() {
|
||||
className="max-w-2xl"
|
||||
footer={
|
||||
<>
|
||||
<Button variant="outline" onClick={() => setPermissionTarget({ admin: null, appIds: [] })}>취소</Button>
|
||||
<Button onClick={saveAppPermissions} disabled={permissionLoading || isOwnerAdmin(permissionTarget.admin)}>
|
||||
저장
|
||||
<Button variant="outline" onClick={() => setPermissionTarget({ admin: null, appIds: [] })} disabled={permissionSaving}>취소</Button>
|
||||
<Button onClick={saveAppPermissions} disabled={permissionLoading || permissionSaving || isOwnerAdmin(permissionTarget.admin)}>
|
||||
{permissionSaving ? '저장 중' : '저장'}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export type AdminAccount = {
|
||||
lastLoginAt?: string | null;
|
||||
sessions?: AdminSession[];
|
||||
sshKeys?: AdminSshKey[];
|
||||
appPermissions?: string[];
|
||||
};
|
||||
|
||||
export type AdminRoleOption = {
|
||||
@@ -81,8 +82,17 @@ export type LauncherNotice = {
|
||||
};
|
||||
|
||||
export async function loadAdmins() {
|
||||
const response = await apiRequest<AdminAccount[] | { admins?: AdminAccount[] }>('/admins');
|
||||
return Array.isArray(response) ? response : response.admins ?? [];
|
||||
type RawAdminAccount = Omit<AdminAccount, 'appPermissions'> & {
|
||||
appPermissions?: Array<string | { appId: string }>;
|
||||
};
|
||||
const response = await apiRequest<RawAdminAccount[] | { admins?: RawAdminAccount[] }>('/admins');
|
||||
const admins = Array.isArray(response) ? response : response.admins ?? [];
|
||||
return admins.map((admin) => ({
|
||||
...admin,
|
||||
appPermissions: (admin.appPermissions ?? []).map((permission) => (
|
||||
typeof permission === 'string' ? permission : permission.appId
|
||||
))
|
||||
}));
|
||||
}
|
||||
|
||||
export function loadAdminRoles() {
|
||||
@@ -138,6 +148,18 @@ export function deleteAdminSshKey(adminId: string, keyId: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadAdminAppPermissions(adminId: string) {
|
||||
const response = await apiRequest<{ appIds?: string[] }>(`/admins/${adminId}/app-permissions`);
|
||||
return response.appIds ?? [];
|
||||
}
|
||||
|
||||
export function updateAdminAppPermissions(adminId: string, appIds: string[]) {
|
||||
return apiRequest<{ appIds?: string[] }>(`/admins/${adminId}/app-permissions`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ appIds })
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadUserSessions() {
|
||||
const response = await apiRequest<{ sessions?: UserSession[] }>('/users/sessions');
|
||||
return (response.sessions ?? []).map((session) => ({
|
||||
|
||||
Reference in New Issue
Block a user