feat: allow administrator deletion and remove icon source field
This commit is contained in:
1 parent
a5f1236d44
commit
2db0c75498
18 files changed
+367
-27
No files matched your search
@@ -19,6 +19,8 @@ export function AdminPanel({ user, report }: { user: User; report: (e: unknown)
|
||||
[offset, setOffset] = useState(0);
|
||||
const [busy, setBusy] = useState(false),
|
||||
[version, setVersion] = useState(0);
|
||||
const [deleting, setDeleting] = useState<Account | null>(null);
|
||||
const [confirmation, setConfirmation] = useState('');
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setBusy(true);
|
||||
@@ -69,6 +71,26 @@ export function AdminPanel({ user, report }: { user: User; report: (e: unknown)
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
async function remove(e: FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
if (!deleting) return;
|
||||
const f = new FormData(e.currentTarget);
|
||||
setBusy(true);
|
||||
try {
|
||||
await api('/admin/users/' + deleting.id, 'DELETE', {
|
||||
confirmationUsername: confirmation,
|
||||
currentPassword: f.get('currentPassword'),
|
||||
});
|
||||
setDeleting(null);
|
||||
setConfirmation('');
|
||||
setOffset(0);
|
||||
setVersion((v) => v + 1);
|
||||
} catch (e) {
|
||||
report(e);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="admin-panel">
|
||||
<section className="panel">
|
||||
@@ -161,6 +183,18 @@ export function AdminPanel({ user, report }: { user: User; report: (e: unknown)
|
||||
>
|
||||
{account.banned ? tr('解除封禁') : tr('封禁账号')}
|
||||
</button>
|
||||
{account.role === 'admin' && account.id !== user.id && (
|
||||
<button
|
||||
className="danger"
|
||||
disabled={busy}
|
||||
onClick={() => {
|
||||
setDeleting(account);
|
||||
setConfirmation('');
|
||||
}}
|
||||
>
|
||||
{tr('删除管理员')}
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@@ -184,6 +218,57 @@ export function AdminPanel({ user, report }: { user: User; report: (e: unknown)
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
{deleting && (
|
||||
<div className="veil">
|
||||
<section className="modal" role="dialog" aria-modal="true" aria-label={tr('删除管理员')}>
|
||||
<h2>
|
||||
{tr('删除管理员')} · {deleting.username}
|
||||
</h2>
|
||||
<p className="danger-text">
|
||||
{tr(
|
||||
'此操作不可撤销,将删除该账号及其全部财务数据、私有图标、登录会话和 MCP 授权;已发布的共享图标保留。请先由该账号用户保存需要的备份。',
|
||||
)}
|
||||
</p>
|
||||
<p className="muted">
|
||||
{tr('必须保留另一位已完成改密且未封禁的管理员。不能删除当前登录账号。')}
|
||||
</p>
|
||||
<form onSubmit={remove}>
|
||||
<label className="field">
|
||||
<span>{tr('输入要删除的账号名称')}</span>
|
||||
<input
|
||||
required
|
||||
autoComplete="off"
|
||||
value={confirmation}
|
||||
onChange={(e) => setConfirmation(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span>{tr('验证当前管理员密码')}</span>
|
||||
<input
|
||||
name="currentPassword"
|
||||
type="password"
|
||||
required
|
||||
autoComplete="current-password"
|
||||
maxLength={72}
|
||||
/>
|
||||
</label>
|
||||
<div className="actions">
|
||||
<button
|
||||
type="button"
|
||||
className="secondary"
|
||||
disabled={busy}
|
||||
onClick={() => setDeleting(null)}
|
||||
>
|
||||
{tr('取消')}
|
||||
</button>
|
||||
<button className="danger" disabled={busy || confirmation !== deleting.username}>
|
||||
{tr('永久删除账号')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { t as tr } from './i18n';
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { api } from './api';
|
||||
import { useToast } from './Toast';
|
||||
type Icon = { id: string; name: string; shared: boolean; source: string | null };
|
||||
type Icon = { id: string; name: string; shared: boolean };
|
||||
export const iconUrl = (id: string) => '/api/icons/' + encodeURIComponent(id) + '/image';
|
||||
export function IconLibrary({
|
||||
initialId,
|
||||
|
||||
@@ -656,5 +656,15 @@
|
||||
"不能修改自己的系统权限或封禁自己": "You cannot change your own role or ban yourself.",
|
||||
"必须保留至少一个可用管理员": "At least one active administrator must remain.",
|
||||
"管理员权限已变更": "Administrator permissions have changed.",
|
||||
"账号不存在": "User not found."
|
||||
"账号不存在": "User not found.",
|
||||
"删除管理员": "Delete administrator",
|
||||
"此操作不可撤销,将删除该账号及其全部财务数据、私有图标、登录会话和 MCP 授权;已发布的共享图标保留。请先由该账号用户保存需要的备份。": "This cannot be undone. The account, its financial data, private icons, login sessions and MCP grants will be deleted. Published shared icons remain. Ask the account owner to save any required backup first.",
|
||||
"必须保留另一位已完成改密且未封禁的管理员。不能删除当前登录账号。": "Another unbanned administrator who has changed their initial password must remain. You cannot delete your current account.",
|
||||
"输入要删除的账号名称": "Enter the username to delete",
|
||||
"验证当前管理员密码": "Verify your current administrator password",
|
||||
"永久删除账号": "Permanently delete account",
|
||||
"不能删除当前登录的管理员账号": "You cannot delete the currently signed-in administrator.",
|
||||
"仅支持删除管理员账号": "Only administrator accounts can be deleted.",
|
||||
"确认账号名称不一致,请重新核对": "The confirmation username does not match. Check it again.",
|
||||
"请先设置另一位已完成改密且未封禁的管理员": "First set up another unbanned administrator who has changed their initial password."
|
||||
}
|
||||
@@ -656,5 +656,15 @@
|
||||
"不能修改自己的系统权限或封禁自己": "不能修改自己的系統權限或封禁自己",
|
||||
"必须保留至少一个可用管理员": "必須保留至少一個可用管理員",
|
||||
"管理员权限已变更": "管理員權限已變更",
|
||||
"账号不存在": "賬號不存在"
|
||||
"账号不存在": "賬號不存在",
|
||||
"删除管理员": "刪除管理員",
|
||||
"此操作不可撤销,将删除该账号及其全部财务数据、私有图标、登录会话和 MCP 授权;已发布的共享图标保留。请先由该账号用户保存需要的备份。": "此操作不可撤銷,將刪除該賬號及其全部財務數據、私有圖標、登錄會話和 MCP 授權;已發佈的共享圖標保留。請先由該賬號用戶保存需要的備份。",
|
||||
"必须保留另一位已完成改密且未封禁的管理员。不能删除当前登录账号。": "必須保留另一位已完成改密且未封禁的管理員。不能刪除當前登錄賬號。",
|
||||
"输入要删除的账号名称": "輸入要刪除的賬號名稱",
|
||||
"验证当前管理员密码": "驗證當前管理員密碼",
|
||||
"永久删除账号": "永久刪除賬號",
|
||||
"不能删除当前登录的管理员账号": "不能刪除當前登錄的管理員賬號",
|
||||
"仅支持删除管理员账号": "僅支持刪除管理員賬號",
|
||||
"确认账号名称不一致,请重新核对": "確認賬號名稱不一致,請重新核對",
|
||||
"请先设置另一位已完成改密且未封禁的管理员": "請先設置另一位已完成改密且未封禁的管理員"
|
||||
}
|
||||
Reference in new issue
Block a user