feat: rename revokeInvite to deleteInvite and update related functionality

This commit is contained in:
shuaiplus
2026-06-29 11:47:05 +08:00
parent 4378e1b430
commit f82dcc3c17
15 changed files with 71 additions and 39 deletions
+7 -5
View File
@@ -249,7 +249,7 @@ export async function handleAdminListInvites(
} }
// DELETE /api/admin/invites/:code // DELETE /api/admin/invites/:code
export async function handleAdminRevokeInvite( export async function handleAdminDeleteInvite(
request: Request, request: Request,
env: Env, env: Env,
actorUser: User, actorUser: User,
@@ -260,12 +260,14 @@ export async function handleAdminRevokeInvite(
} }
const storage = new StorageService(env.DB); const storage = new StorageService(env.DB);
const revoked = await storage.revokeInvite(code); const deleted = await storage.deleteInvite(code);
if (!revoked) { if (!deleted) {
return errorResponse('Invite not found or already inactive', 404); return errorResponse('Invite not found', 404);
} }
await writeAuditLog(storage, actorUser.id, 'admin.invite.revoke', 'invite', null, null, request); await writeAuditLog(storage, actorUser.id, 'admin.invite.delete', 'invite', null, {
code,
}, request);
return new Response(null, { status: 204 }); return new Response(null, { status: 204 });
} }
+2 -2
View File
@@ -4,7 +4,7 @@ import {
handleAdminCreateInvite, handleAdminCreateInvite,
handleAdminListInvites, handleAdminListInvites,
handleAdminDeleteAllInvites, handleAdminDeleteAllInvites,
handleAdminRevokeInvite, handleAdminDeleteInvite,
handleAdminSetUserStatus, handleAdminSetUserStatus,
handleAdminDeleteUser, handleAdminDeleteUser,
handleAdminListAuditLogs, handleAdminListAuditLogs,
@@ -52,7 +52,7 @@ export async function handleAdminRoute(
const adminInviteMatch = path.match(/^\/api\/admin\/invites\/([^/]+)$/i); const adminInviteMatch = path.match(/^\/api\/admin\/invites\/([^/]+)$/i);
if (adminInviteMatch && method === 'DELETE') { if (adminInviteMatch && method === 'DELETE') {
const inviteCode = decodeURIComponent(adminInviteMatch[1]); const inviteCode = decodeURIComponent(adminInviteMatch[1]);
return handleAdminRevokeInvite(request, env, actorUser, inviteCode); return handleAdminDeleteInvite(request, env, actorUser, inviteCode);
} }
const adminUserStatusMatch = path.match(/^\/api\/admin\/users\/([a-f0-9-]+)\/status$/i); const adminUserStatusMatch = path.match(/^\/api\/admin\/users\/([a-f0-9-]+)\/status$/i);
+3 -4
View File
@@ -151,11 +151,10 @@ export async function revertInviteUsed(db: D1Database, code: string, userId: str
return (result.meta.changes ?? 0) > 0; return (result.meta.changes ?? 0) > 0;
} }
export async function revokeInvite(db: D1Database, code: string): Promise<boolean> { export async function deleteInvite(db: D1Database, code: string): Promise<boolean> {
const now = new Date().toISOString();
const result = await db const result = await db
.prepare("UPDATE invites SET status = 'revoked', updated_at = ? WHERE code = ? AND status = 'active'") .prepare('DELETE FROM invites WHERE code = ?')
.bind(now, code) .bind(code)
.run(); .run();
return (result.meta.changes ?? 0) > 0; return (result.meta.changes ?? 0) > 0;
} }
+3 -3
View File
@@ -24,6 +24,7 @@ import {
clearAuditLogs as clearStoredAuditLogs, clearAuditLogs as clearStoredAuditLogs,
assignInviteUsedBy as assignStoredInviteUsedBy, assignInviteUsedBy as assignStoredInviteUsedBy,
createInvite as createStoredInvite, createInvite as createStoredInvite,
deleteInvite as deleteStoredInvite,
deleteAllInvites as deleteStoredInvites, deleteAllInvites as deleteStoredInvites,
getInvite as findStoredInvite, getInvite as findStoredInvite,
listAuditLogs as listStoredAuditLogs, listAuditLogs as listStoredAuditLogs,
@@ -32,7 +33,6 @@ import {
pruneAuditLogs as pruneStoredAuditLogs, pruneAuditLogs as pruneStoredAuditLogs,
pruneAuditLogsToMax as pruneStoredAuditLogsToMax, pruneAuditLogsToMax as pruneStoredAuditLogsToMax,
revertInviteUsed as revertStoredInviteUsed, revertInviteUsed as revertStoredInviteUsed,
revokeInvite as revokeStoredInvite,
} from './storage-admin-repo'; } from './storage-admin-repo';
import { import {
bulkDeleteFolders as deleteStoredFolders, bulkDeleteFolders as deleteStoredFolders,
@@ -329,8 +329,8 @@ export class StorageService {
return revertStoredInviteUsed(this.db, code, userId); return revertStoredInviteUsed(this.db, code, userId);
} }
async revokeInvite(code: string): Promise<boolean> { async deleteInvite(code: string): Promise<boolean> {
return revokeStoredInvite(this.db, code); return deleteStoredInvite(this.db, code);
} }
async deleteAllInvites(): Promise<number> { async deleteAllInvites(): Promise<number> {
+1 -1
View File
@@ -2039,7 +2039,7 @@ export default function App() {
onDeleteAllInvites: adminActions.deleteAllInvites, onDeleteAllInvites: adminActions.deleteAllInvites,
onToggleUserStatus: adminActions.toggleUserStatus, onToggleUserStatus: adminActions.toggleUserStatus,
onDeleteUser: adminActions.deleteUser, onDeleteUser: adminActions.deleteUser,
onRevokeInvite: adminActions.revokeInvite, onDeleteInvite: adminActions.deleteInvite,
onLoadAuditLogs: (filters: AuditLogFilters) => listAuditLogs(authedFetch, filters), onLoadAuditLogs: (filters: AuditLogFilters) => listAuditLogs(authedFetch, filters),
onLoadAuditLogSettings: () => getAuditLogSettings(authedFetch), onLoadAuditLogSettings: () => getAuditLogSettings(authedFetch),
onSaveAuditLogSettings: (settings: AuditLogSettings) => saveAuditLogSettings(authedFetch, settings), onSaveAuditLogSettings: (settings: AuditLogSettings) => saveAuditLogSettings(authedFetch, settings),
+3 -5
View File
@@ -16,7 +16,7 @@ interface AdminPageProps {
onDeleteAllInvites: () => Promise<void>; onDeleteAllInvites: () => Promise<void>;
onToggleUserStatus: (userId: string, currentStatus: 'active' | 'banned') => Promise<void>; onToggleUserStatus: (userId: string, currentStatus: 'active' | 'banned') => Promise<void>;
onDeleteUser: (userId: string) => Promise<void>; onDeleteUser: (userId: string) => Promise<void>;
onRevokeInvite: (code: string) => Promise<void>; onDeleteInvite: (code: string) => Promise<void>;
} }
export default function AdminPage(props: AdminPageProps) { export default function AdminPage(props: AdminPageProps) {
@@ -184,11 +184,9 @@ export default function AdminPage(props: AdminPageProps) {
> >
<Clipboard size={14} className="btn-icon" /> {t('txt_copy_link')} <Clipboard size={14} className="btn-icon" /> {t('txt_copy_link')}
</button> </button>
{invite.status === 'active' && ( <button type="button" className="btn btn-danger" onClick={() => void props.onDeleteInvite(invite.code)}>
<button type="button" className="btn btn-danger" onClick={() => void props.onRevokeInvite(invite.code)}> <Trash2 size={14} className="btn-icon" /> {t('txt_delete')}
<Trash2 size={14} className="btn-icon" /> {t('txt_revoke')}
</button> </button>
)}
</div> </div>
</td> </td>
</tr> </tr>
+2 -2
View File
@@ -140,7 +140,7 @@ export interface AppMainRoutesProps {
onDeleteAllInvites: () => Promise<void>; onDeleteAllInvites: () => Promise<void>;
onToggleUserStatus: (userId: string, status: 'active' | 'banned') => Promise<void>; onToggleUserStatus: (userId: string, status: 'active' | 'banned') => Promise<void>;
onDeleteUser: (userId: string) => Promise<void>; onDeleteUser: (userId: string) => Promise<void>;
onRevokeInvite: (code: string) => Promise<void>; onDeleteInvite: (code: string) => Promise<void>;
onLoadAuditLogs: (filters: AuditLogFilters) => Promise<AuditLogListResult>; onLoadAuditLogs: (filters: AuditLogFilters) => Promise<AuditLogListResult>;
onLoadAuditLogSettings: () => Promise<AuditLogSettings>; onLoadAuditLogSettings: () => Promise<AuditLogSettings>;
onSaveAuditLogSettings: (settings: AuditLogSettings) => Promise<AuditLogSettings>; onSaveAuditLogSettings: (settings: AuditLogSettings) => Promise<AuditLogSettings>;
@@ -416,7 +416,7 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
onDeleteAllInvites={props.onDeleteAllInvites} onDeleteAllInvites={props.onDeleteAllInvites}
onToggleUserStatus={props.onToggleUserStatus} onToggleUserStatus={props.onToggleUserStatus}
onDeleteUser={props.onDeleteUser} onDeleteUser={props.onDeleteUser}
onRevokeInvite={props.onRevokeInvite} onDeleteInvite={props.onDeleteInvite}
/> />
</Suspense> </Suspense>
</div> </div>
+15 -5
View File
@@ -1,5 +1,5 @@
import { useMemo } from 'preact/hooks'; import { useMemo } from 'preact/hooks';
import { createInvite, deleteAllInvites, deleteUser, revokeInvite, setUserStatus } from '@/lib/api/admin'; import { createInvite, deleteAllInvites, deleteInvite, deleteUser, setUserStatus } from '@/lib/api/admin';
import { t } from '@/lib/i18n'; import { t } from '@/lib/i18n';
import type { AppConfirmState } from '@/components/AppGlobalOverlays'; import type { AppConfirmState } from '@/components/AppGlobalOverlays';
import type { AuthedFetch } from '@/lib/api/shared'; import type { AuthedFetch } from '@/lib/api/shared';
@@ -45,14 +45,24 @@ export default function useAdminActions(options: UseAdminActionsOptions) {
} }
}, },
async revokeInvite(code: string) { async deleteInvite(code: string) {
onSetConfirm({
title: t('txt_delete_invite'),
message: t('txt_delete_invite_confirm_message'),
danger: true,
onConfirm: () => {
onSetConfirm(null);
void (async () => {
try { try {
await revokeInvite(authedFetch, code); await deleteInvite(authedFetch, code);
await refetchInvites(); await refetchInvites();
onNotify('success', t('txt_invite_revoked')); onNotify('success', t('txt_invite_deleted'));
} catch (error) { } catch (error) {
onNotify('error', error instanceof Error ? error.message : t('txt_revoke_invite_failed')); onNotify('error', error instanceof Error ? error.message : t('txt_delete_invite_failed'));
} }
})();
},
});
}, },
async deleteAllInvites() { async deleteAllInvites() {
+2 -2
View File
@@ -24,9 +24,9 @@ export async function createInvite(authedFetch: AuthedFetch, hours: number): Pro
if (!resp.ok) throw new Error('Create invite failed'); if (!resp.ok) throw new Error('Create invite failed');
} }
export async function revokeInvite(authedFetch: AuthedFetch, code: string): Promise<void> { export async function deleteInvite(authedFetch: AuthedFetch, code: string): Promise<void> {
const resp = await authedFetch(`/api/admin/invites/${encodeURIComponent(code)}`, { method: 'DELETE' }); const resp = await authedFetch(`/api/admin/invites/${encodeURIComponent(code)}`, { method: 'DELETE' });
if (!resp.ok) throw new Error('Revoke invite failed'); if (!resp.ok) throw new Error('Delete invite failed');
} }
export async function deleteAllInvites(authedFetch: AuthedFetch): Promise<void> { export async function deleteAllInvites(authedFetch: AuthedFetch): Promise<void> {
+3 -5
View File
@@ -1141,11 +1141,9 @@ export function createDemoMainRoutesProps(base: AppMainRoutesProps, notify: Noti
state.setUsers((prev) => prev.filter((user) => user.id !== userId)); state.setUsers((prev) => prev.filter((user) => user.id !== userId));
notify('success', t('txt_user_deleted')); notify('success', t('txt_user_deleted'));
}, },
onRevokeInvite: async (code) => { onDeleteInvite: async (code) => {
state.setInvites((prev) => prev.map((invite) => ( state.setInvites((prev) => prev.filter((invite) => invite.code !== code));
invite.code === code ? { ...invite, status: 'inactive' } : invite notify('success', t('txt_invite_deleted'));
)));
notify('success', t('txt_invite_revoked'));
}, },
onLoadAuditLogSettings: async () => ({ retentionDays: 90, maxEntries: null }), onLoadAuditLogSettings: async () => ({ retentionDays: 90, maxEntries: null }),
onSaveAuditLogSettings: async (settings) => { onSaveAuditLogSettings: async (settings) => {
+5
View File
@@ -582,8 +582,12 @@ const en: Record<string, string> = {
"txt_identity_details": "Identity Details", "txt_identity_details": "Identity Details",
"txt_ie_browser": "IE Browser", "txt_ie_browser": "IE Browser",
"txt_create_invite_failed": "Failed to create invite", "txt_create_invite_failed": "Failed to create invite",
"txt_delete_invite": "Delete invite",
"txt_delete_invite_confirm_message": "Delete this invite code? This cannot be undone.",
"txt_delete_invite_failed": "Failed to delete invite",
"txt_invite_code_required": "Invite Code (Required)", "txt_invite_code_required": "Invite Code (Required)",
"txt_invite_created": "Invite created", "txt_invite_created": "Invite created",
"txt_invite_deleted": "Invite deleted",
"txt_invite_revoked": "Invite revoked", "txt_invite_revoked": "Invite revoked",
"txt_revoke_invite_failed": "Failed to revoke invite", "txt_revoke_invite_failed": "Failed to revoke invite",
"txt_invite_validity_hours": "Invite validity (hours)", "txt_invite_validity_hours": "Invite validity (hours)",
@@ -1156,6 +1160,7 @@ const en: Record<string, string> = {
"txt_log_action_admin_backup_settings_repair": "Repair backup settings", "txt_log_action_admin_backup_settings_repair": "Repair backup settings",
"txt_log_action_admin_backup_settings_update": "Update backup settings", "txt_log_action_admin_backup_settings_update": "Update backup settings",
"txt_log_action_admin_invite_create": "Create invite", "txt_log_action_admin_invite_create": "Create invite",
"txt_log_action_admin_invite_delete": "Delete invite",
"txt_log_action_admin_invite_delete_all": "Clear invites", "txt_log_action_admin_invite_delete_all": "Clear invites",
"txt_log_action_admin_invite_revoke": "Revoke invite", "txt_log_action_admin_invite_revoke": "Revoke invite",
"txt_log_action_admin_user_delete": "Delete user", "txt_log_action_admin_user_delete": "Delete user",
+5
View File
@@ -582,8 +582,12 @@ const es: Record<string, string> = {
"txt_identity_details": "Detalles de identidad", "txt_identity_details": "Detalles de identidad",
"txt_ie_browser": "Navegador Internet Explorer", "txt_ie_browser": "Navegador Internet Explorer",
"txt_create_invite_failed": "Error al crear invitación", "txt_create_invite_failed": "Error al crear invitación",
"txt_delete_invite": "Eliminar invitación",
"txt_delete_invite_confirm_message": "¿Eliminar este código de invitación? Esta acción no se puede deshacer.",
"txt_delete_invite_failed": "Error al eliminar invitación",
"txt_invite_code_required": "Código de invitación (obligatorio)", "txt_invite_code_required": "Código de invitación (obligatorio)",
"txt_invite_created": "Invitación creada", "txt_invite_created": "Invitación creada",
"txt_invite_deleted": "Invitación eliminada",
"txt_invite_revoked": "Invitación revocada", "txt_invite_revoked": "Invitación revocada",
"txt_revoke_invite_failed": "Error al revocar invitación", "txt_revoke_invite_failed": "Error al revocar invitación",
"txt_invite_validity_hours": "Validez de la invitación en horas", "txt_invite_validity_hours": "Validez de la invitación en horas",
@@ -1156,6 +1160,7 @@ const es: Record<string, string> = {
"txt_log_action_admin_backup_settings_repair": "Repair backup settings", "txt_log_action_admin_backup_settings_repair": "Repair backup settings",
"txt_log_action_admin_backup_settings_update": "Update backup settings", "txt_log_action_admin_backup_settings_update": "Update backup settings",
"txt_log_action_admin_invite_create": "Create invite", "txt_log_action_admin_invite_create": "Create invite",
"txt_log_action_admin_invite_delete": "Delete invite",
"txt_log_action_admin_invite_delete_all": "Clear invites", "txt_log_action_admin_invite_delete_all": "Clear invites",
"txt_log_action_admin_invite_revoke": "Revoke invite", "txt_log_action_admin_invite_revoke": "Revoke invite",
"txt_log_action_admin_user_delete": "Delete user", "txt_log_action_admin_user_delete": "Delete user",
+5
View File
@@ -582,8 +582,12 @@ const ru: Record<string, string> = {
"txt_identity_details": "Данные личности", "txt_identity_details": "Данные личности",
"txt_ie_browser": "IE-браузер", "txt_ie_browser": "IE-браузер",
"txt_create_invite_failed": "Не удалось создать приглашение", "txt_create_invite_failed": "Не удалось создать приглашение",
"txt_delete_invite": "Удалить приглашение",
"txt_delete_invite_confirm_message": "Удалить этот пригласительный код? Это действие нельзя отменить.",
"txt_delete_invite_failed": "Не удалось удалить приглашение",
"txt_invite_code_required": "Пригласительный код (обязательно)", "txt_invite_code_required": "Пригласительный код (обязательно)",
"txt_invite_created": "Приглашение создано", "txt_invite_created": "Приглашение создано",
"txt_invite_deleted": "Приглашение удалено",
"txt_invite_revoked": "Приглашение отозвано", "txt_invite_revoked": "Приглашение отозвано",
"txt_revoke_invite_failed": "Не удалось отозвать приглашение", "txt_revoke_invite_failed": "Не удалось отозвать приглашение",
"txt_invite_validity_hours": "Срок действия приглашения (часы)", "txt_invite_validity_hours": "Срок действия приглашения (часы)",
@@ -1156,6 +1160,7 @@ const ru: Record<string, string> = {
"txt_log_action_admin_backup_settings_repair": "Repair backup settings", "txt_log_action_admin_backup_settings_repair": "Repair backup settings",
"txt_log_action_admin_backup_settings_update": "Update backup settings", "txt_log_action_admin_backup_settings_update": "Update backup settings",
"txt_log_action_admin_invite_create": "Create invite", "txt_log_action_admin_invite_create": "Create invite",
"txt_log_action_admin_invite_delete": "Delete invite",
"txt_log_action_admin_invite_delete_all": "Clear invites", "txt_log_action_admin_invite_delete_all": "Clear invites",
"txt_log_action_admin_invite_revoke": "Revoke invite", "txt_log_action_admin_invite_revoke": "Revoke invite",
"txt_log_action_admin_user_delete": "Delete user", "txt_log_action_admin_user_delete": "Delete user",
+5
View File
@@ -582,8 +582,12 @@ const zhCN: Record<string, string> = {
"txt_identity_details": "身份详情", "txt_identity_details": "身份详情",
"txt_ie_browser": "IE 浏览器", "txt_ie_browser": "IE 浏览器",
"txt_create_invite_failed": "创建邀请码失败", "txt_create_invite_failed": "创建邀请码失败",
"txt_delete_invite": "删除邀请码",
"txt_delete_invite_confirm_message": "确定删除该邀请码吗?删除后无法恢复。",
"txt_delete_invite_failed": "删除邀请码失败",
"txt_invite_code_required": "邀请码(必填)", "txt_invite_code_required": "邀请码(必填)",
"txt_invite_created": "邀请码已创建", "txt_invite_created": "邀请码已创建",
"txt_invite_deleted": "邀请码已删除",
"txt_invite_revoked": "邀请码已撤销", "txt_invite_revoked": "邀请码已撤销",
"txt_revoke_invite_failed": "撤销邀请码失败", "txt_revoke_invite_failed": "撤销邀请码失败",
"txt_invite_validity_hours": "邀请码有效期(小时)", "txt_invite_validity_hours": "邀请码有效期(小时)",
@@ -1156,6 +1160,7 @@ const zhCN: Record<string, string> = {
"txt_log_action_admin_backup_settings_repair": "修复备份设置", "txt_log_action_admin_backup_settings_repair": "修复备份设置",
"txt_log_action_admin_backup_settings_update": "更新备份设置", "txt_log_action_admin_backup_settings_update": "更新备份设置",
"txt_log_action_admin_invite_create": "创建邀请", "txt_log_action_admin_invite_create": "创建邀请",
"txt_log_action_admin_invite_delete": "删除邀请",
"txt_log_action_admin_invite_delete_all": "清空邀请", "txt_log_action_admin_invite_delete_all": "清空邀请",
"txt_log_action_admin_invite_revoke": "撤销邀请", "txt_log_action_admin_invite_revoke": "撤销邀请",
"txt_log_action_admin_user_delete": "删除用户", "txt_log_action_admin_user_delete": "删除用户",
+5
View File
@@ -582,8 +582,12 @@ const zhTW: Record<string, string> = {
"txt_identity_details": "身份詳情", "txt_identity_details": "身份詳情",
"txt_ie_browser": "IE 瀏覽器", "txt_ie_browser": "IE 瀏覽器",
"txt_create_invite_failed": "創建邀請碼失敗", "txt_create_invite_failed": "創建邀請碼失敗",
"txt_delete_invite": "刪除邀請碼",
"txt_delete_invite_confirm_message": "確定刪除此邀請碼嗎?刪除後無法復原。",
"txt_delete_invite_failed": "刪除邀請碼失敗",
"txt_invite_code_required": "邀請碼(必填)", "txt_invite_code_required": "邀請碼(必填)",
"txt_invite_created": "邀請碼已創建", "txt_invite_created": "邀請碼已創建",
"txt_invite_deleted": "邀請碼已刪除",
"txt_invite_revoked": "邀請碼已撤銷", "txt_invite_revoked": "邀請碼已撤銷",
"txt_revoke_invite_failed": "撤銷邀請碼失敗", "txt_revoke_invite_failed": "撤銷邀請碼失敗",
"txt_invite_validity_hours": "邀請碼有效期(小時)", "txt_invite_validity_hours": "邀請碼有效期(小時)",
@@ -1156,6 +1160,7 @@ const zhTW: Record<string, string> = {
"txt_log_action_admin_backup_settings_repair": "修復備份設定", "txt_log_action_admin_backup_settings_repair": "修復備份設定",
"txt_log_action_admin_backup_settings_update": "更新備份設定", "txt_log_action_admin_backup_settings_update": "更新備份設定",
"txt_log_action_admin_invite_create": "建立邀請", "txt_log_action_admin_invite_create": "建立邀請",
"txt_log_action_admin_invite_delete": "刪除邀請",
"txt_log_action_admin_invite_delete_all": "清空邀請", "txt_log_action_admin_invite_delete_all": "清空邀請",
"txt_log_action_admin_invite_revoke": "撤銷邀請", "txt_log_action_admin_invite_revoke": "撤銷邀請",
"txt_log_action_admin_user_delete": "刪除使用者", "txt_log_action_admin_user_delete": "刪除使用者",