mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-08-04 22:40:11 +00:00
feat: rename revokeInvite to deleteInvite and update related functionality
This commit is contained in:
@@ -249,7 +249,7 @@ export async function handleAdminListInvites(
|
||||
}
|
||||
|
||||
// DELETE /api/admin/invites/:code
|
||||
export async function handleAdminRevokeInvite(
|
||||
export async function handleAdminDeleteInvite(
|
||||
request: Request,
|
||||
env: Env,
|
||||
actorUser: User,
|
||||
@@ -260,12 +260,14 @@ export async function handleAdminRevokeInvite(
|
||||
}
|
||||
|
||||
const storage = new StorageService(env.DB);
|
||||
const revoked = await storage.revokeInvite(code);
|
||||
if (!revoked) {
|
||||
return errorResponse('Invite not found or already inactive', 404);
|
||||
const deleted = await storage.deleteInvite(code);
|
||||
if (!deleted) {
|
||||
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 });
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import {
|
||||
handleAdminCreateInvite,
|
||||
handleAdminListInvites,
|
||||
handleAdminDeleteAllInvites,
|
||||
handleAdminRevokeInvite,
|
||||
handleAdminDeleteInvite,
|
||||
handleAdminSetUserStatus,
|
||||
handleAdminDeleteUser,
|
||||
handleAdminListAuditLogs,
|
||||
@@ -52,7 +52,7 @@ export async function handleAdminRoute(
|
||||
const adminInviteMatch = path.match(/^\/api\/admin\/invites\/([^/]+)$/i);
|
||||
if (adminInviteMatch && method === 'DELETE') {
|
||||
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);
|
||||
|
||||
@@ -151,11 +151,10 @@ export async function revertInviteUsed(db: D1Database, code: string, userId: str
|
||||
return (result.meta.changes ?? 0) > 0;
|
||||
}
|
||||
|
||||
export async function revokeInvite(db: D1Database, code: string): Promise<boolean> {
|
||||
const now = new Date().toISOString();
|
||||
export async function deleteInvite(db: D1Database, code: string): Promise<boolean> {
|
||||
const result = await db
|
||||
.prepare("UPDATE invites SET status = 'revoked', updated_at = ? WHERE code = ? AND status = 'active'")
|
||||
.bind(now, code)
|
||||
.prepare('DELETE FROM invites WHERE code = ?')
|
||||
.bind(code)
|
||||
.run();
|
||||
return (result.meta.changes ?? 0) > 0;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
clearAuditLogs as clearStoredAuditLogs,
|
||||
assignInviteUsedBy as assignStoredInviteUsedBy,
|
||||
createInvite as createStoredInvite,
|
||||
deleteInvite as deleteStoredInvite,
|
||||
deleteAllInvites as deleteStoredInvites,
|
||||
getInvite as findStoredInvite,
|
||||
listAuditLogs as listStoredAuditLogs,
|
||||
@@ -32,7 +33,6 @@ import {
|
||||
pruneAuditLogs as pruneStoredAuditLogs,
|
||||
pruneAuditLogsToMax as pruneStoredAuditLogsToMax,
|
||||
revertInviteUsed as revertStoredInviteUsed,
|
||||
revokeInvite as revokeStoredInvite,
|
||||
} from './storage-admin-repo';
|
||||
import {
|
||||
bulkDeleteFolders as deleteStoredFolders,
|
||||
@@ -329,8 +329,8 @@ export class StorageService {
|
||||
return revertStoredInviteUsed(this.db, code, userId);
|
||||
}
|
||||
|
||||
async revokeInvite(code: string): Promise<boolean> {
|
||||
return revokeStoredInvite(this.db, code);
|
||||
async deleteInvite(code: string): Promise<boolean> {
|
||||
return deleteStoredInvite(this.db, code);
|
||||
}
|
||||
|
||||
async deleteAllInvites(): Promise<number> {
|
||||
|
||||
+1
-1
@@ -2039,7 +2039,7 @@ export default function App() {
|
||||
onDeleteAllInvites: adminActions.deleteAllInvites,
|
||||
onToggleUserStatus: adminActions.toggleUserStatus,
|
||||
onDeleteUser: adminActions.deleteUser,
|
||||
onRevokeInvite: adminActions.revokeInvite,
|
||||
onDeleteInvite: adminActions.deleteInvite,
|
||||
onLoadAuditLogs: (filters: AuditLogFilters) => listAuditLogs(authedFetch, filters),
|
||||
onLoadAuditLogSettings: () => getAuditLogSettings(authedFetch),
|
||||
onSaveAuditLogSettings: (settings: AuditLogSettings) => saveAuditLogSettings(authedFetch, settings),
|
||||
|
||||
@@ -16,7 +16,7 @@ interface AdminPageProps {
|
||||
onDeleteAllInvites: () => Promise<void>;
|
||||
onToggleUserStatus: (userId: string, currentStatus: 'active' | 'banned') => Promise<void>;
|
||||
onDeleteUser: (userId: string) => Promise<void>;
|
||||
onRevokeInvite: (code: string) => Promise<void>;
|
||||
onDeleteInvite: (code: string) => Promise<void>;
|
||||
}
|
||||
|
||||
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')}
|
||||
</button>
|
||||
{invite.status === 'active' && (
|
||||
<button type="button" className="btn btn-danger" onClick={() => void props.onRevokeInvite(invite.code)}>
|
||||
<Trash2 size={14} className="btn-icon" /> {t('txt_revoke')}
|
||||
</button>
|
||||
)}
|
||||
<button type="button" className="btn btn-danger" onClick={() => void props.onDeleteInvite(invite.code)}>
|
||||
<Trash2 size={14} className="btn-icon" /> {t('txt_delete')}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -140,7 +140,7 @@ export interface AppMainRoutesProps {
|
||||
onDeleteAllInvites: () => Promise<void>;
|
||||
onToggleUserStatus: (userId: string, status: 'active' | 'banned') => Promise<void>;
|
||||
onDeleteUser: (userId: string) => Promise<void>;
|
||||
onRevokeInvite: (code: string) => Promise<void>;
|
||||
onDeleteInvite: (code: string) => Promise<void>;
|
||||
onLoadAuditLogs: (filters: AuditLogFilters) => Promise<AuditLogListResult>;
|
||||
onLoadAuditLogSettings: () => Promise<AuditLogSettings>;
|
||||
onSaveAuditLogSettings: (settings: AuditLogSettings) => Promise<AuditLogSettings>;
|
||||
@@ -416,7 +416,7 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
|
||||
onDeleteAllInvites={props.onDeleteAllInvites}
|
||||
onToggleUserStatus={props.onToggleUserStatus}
|
||||
onDeleteUser={props.onDeleteUser}
|
||||
onRevokeInvite={props.onRevokeInvite}
|
||||
onDeleteInvite={props.onDeleteInvite}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 type { AppConfirmState } from '@/components/AppGlobalOverlays';
|
||||
import type { AuthedFetch } from '@/lib/api/shared';
|
||||
@@ -45,14 +45,24 @@ export default function useAdminActions(options: UseAdminActionsOptions) {
|
||||
}
|
||||
},
|
||||
|
||||
async revokeInvite(code: string) {
|
||||
try {
|
||||
await revokeInvite(authedFetch, code);
|
||||
await refetchInvites();
|
||||
onNotify('success', t('txt_invite_revoked'));
|
||||
} catch (error) {
|
||||
onNotify('error', error instanceof Error ? error.message : t('txt_revoke_invite_failed'));
|
||||
}
|
||||
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 {
|
||||
await deleteInvite(authedFetch, code);
|
||||
await refetchInvites();
|
||||
onNotify('success', t('txt_invite_deleted'));
|
||||
} catch (error) {
|
||||
onNotify('error', error instanceof Error ? error.message : t('txt_delete_invite_failed'));
|
||||
}
|
||||
})();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
async deleteAllInvites() {
|
||||
|
||||
@@ -24,9 +24,9 @@ export async function createInvite(authedFetch: AuthedFetch, hours: number): Pro
|
||||
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' });
|
||||
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> {
|
||||
|
||||
@@ -1141,11 +1141,9 @@ export function createDemoMainRoutesProps(base: AppMainRoutesProps, notify: Noti
|
||||
state.setUsers((prev) => prev.filter((user) => user.id !== userId));
|
||||
notify('success', t('txt_user_deleted'));
|
||||
},
|
||||
onRevokeInvite: async (code) => {
|
||||
state.setInvites((prev) => prev.map((invite) => (
|
||||
invite.code === code ? { ...invite, status: 'inactive' } : invite
|
||||
)));
|
||||
notify('success', t('txt_invite_revoked'));
|
||||
onDeleteInvite: async (code) => {
|
||||
state.setInvites((prev) => prev.filter((invite) => invite.code !== code));
|
||||
notify('success', t('txt_invite_deleted'));
|
||||
},
|
||||
onLoadAuditLogSettings: async () => ({ retentionDays: 90, maxEntries: null }),
|
||||
onSaveAuditLogSettings: async (settings) => {
|
||||
|
||||
@@ -582,8 +582,12 @@ const en: Record<string, string> = {
|
||||
"txt_identity_details": "Identity Details",
|
||||
"txt_ie_browser": "IE Browser",
|
||||
"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_created": "Invite created",
|
||||
"txt_invite_deleted": "Invite deleted",
|
||||
"txt_invite_revoked": "Invite revoked",
|
||||
"txt_revoke_invite_failed": "Failed to revoke invite",
|
||||
"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_update": "Update backup settings",
|
||||
"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_revoke": "Revoke invite",
|
||||
"txt_log_action_admin_user_delete": "Delete user",
|
||||
|
||||
@@ -582,8 +582,12 @@ const es: Record<string, string> = {
|
||||
"txt_identity_details": "Detalles de identidad",
|
||||
"txt_ie_browser": "Navegador Internet Explorer",
|
||||
"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_created": "Invitación creada",
|
||||
"txt_invite_deleted": "Invitación eliminada",
|
||||
"txt_invite_revoked": "Invitación revocada",
|
||||
"txt_revoke_invite_failed": "Error al revocar invitación",
|
||||
"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_update": "Update backup settings",
|
||||
"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_revoke": "Revoke invite",
|
||||
"txt_log_action_admin_user_delete": "Delete user",
|
||||
|
||||
@@ -582,8 +582,12 @@ const ru: Record<string, string> = {
|
||||
"txt_identity_details": "Данные личности",
|
||||
"txt_ie_browser": "IE-браузер",
|
||||
"txt_create_invite_failed": "Не удалось создать приглашение",
|
||||
"txt_delete_invite": "Удалить приглашение",
|
||||
"txt_delete_invite_confirm_message": "Удалить этот пригласительный код? Это действие нельзя отменить.",
|
||||
"txt_delete_invite_failed": "Не удалось удалить приглашение",
|
||||
"txt_invite_code_required": "Пригласительный код (обязательно)",
|
||||
"txt_invite_created": "Приглашение создано",
|
||||
"txt_invite_deleted": "Приглашение удалено",
|
||||
"txt_invite_revoked": "Приглашение отозвано",
|
||||
"txt_revoke_invite_failed": "Не удалось отозвать приглашение",
|
||||
"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_update": "Update backup settings",
|
||||
"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_revoke": "Revoke invite",
|
||||
"txt_log_action_admin_user_delete": "Delete user",
|
||||
|
||||
@@ -582,8 +582,12 @@ const zhCN: Record<string, string> = {
|
||||
"txt_identity_details": "身份详情",
|
||||
"txt_ie_browser": "IE 浏览器",
|
||||
"txt_create_invite_failed": "创建邀请码失败",
|
||||
"txt_delete_invite": "删除邀请码",
|
||||
"txt_delete_invite_confirm_message": "确定删除该邀请码吗?删除后无法恢复。",
|
||||
"txt_delete_invite_failed": "删除邀请码失败",
|
||||
"txt_invite_code_required": "邀请码(必填)",
|
||||
"txt_invite_created": "邀请码已创建",
|
||||
"txt_invite_deleted": "邀请码已删除",
|
||||
"txt_invite_revoked": "邀请码已撤销",
|
||||
"txt_revoke_invite_failed": "撤销邀请码失败",
|
||||
"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_update": "更新备份设置",
|
||||
"txt_log_action_admin_invite_create": "创建邀请",
|
||||
"txt_log_action_admin_invite_delete": "删除邀请",
|
||||
"txt_log_action_admin_invite_delete_all": "清空邀请",
|
||||
"txt_log_action_admin_invite_revoke": "撤销邀请",
|
||||
"txt_log_action_admin_user_delete": "删除用户",
|
||||
|
||||
@@ -582,8 +582,12 @@ const zhTW: Record<string, string> = {
|
||||
"txt_identity_details": "身份詳情",
|
||||
"txt_ie_browser": "IE 瀏覽器",
|
||||
"txt_create_invite_failed": "創建邀請碼失敗",
|
||||
"txt_delete_invite": "刪除邀請碼",
|
||||
"txt_delete_invite_confirm_message": "確定刪除此邀請碼嗎?刪除後無法復原。",
|
||||
"txt_delete_invite_failed": "刪除邀請碼失敗",
|
||||
"txt_invite_code_required": "邀請碼(必填)",
|
||||
"txt_invite_created": "邀請碼已創建",
|
||||
"txt_invite_deleted": "邀請碼已刪除",
|
||||
"txt_invite_revoked": "邀請碼已撤銷",
|
||||
"txt_revoke_invite_failed": "撤銷邀請碼失敗",
|
||||
"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_update": "更新備份設定",
|
||||
"txt_log_action_admin_invite_create": "建立邀請",
|
||||
"txt_log_action_admin_invite_delete": "刪除邀請",
|
||||
"txt_log_action_admin_invite_delete_all": "清空邀請",
|
||||
"txt_log_action_admin_invite_revoke": "撤銷邀請",
|
||||
"txt_log_action_admin_user_delete": "刪除使用者",
|
||||
|
||||
Reference in New Issue
Block a user