mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-08-04 22:40:11 +00:00
feat: add functionality to delete invalid invites and update related components
This commit is contained in:
@@ -56,6 +56,7 @@ NodeWarden-compat/
|
|||||||
.codex-upstream/bitwarden-browser/
|
.codex-upstream/bitwarden-browser/
|
||||||
|
|
||||||
.reasonix/
|
.reasonix/
|
||||||
|
.upstream/
|
||||||
|
|
||||||
# Compatibility analysis documents
|
# Compatibility analysis documents
|
||||||
BITWARDEN_COMPATIBILITY_ANALYSIS.md
|
BITWARDEN_COMPATIBILITY_ANALYSIS.md
|
||||||
|
|||||||
+10
-1
@@ -277,12 +277,21 @@ export async function handleAdminDeleteAllInvites(
|
|||||||
env: Env,
|
env: Env,
|
||||||
actorUser: User
|
actorUser: User
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
void request;
|
|
||||||
if (!isAdmin(actorUser)) {
|
if (!isAdmin(actorUser)) {
|
||||||
return errorResponse('Forbidden', 403);
|
return errorResponse('Forbidden', 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
const storage = new StorageService(env.DB);
|
const storage = new StorageService(env.DB);
|
||||||
|
const url = new URL(request.url);
|
||||||
|
if (url.searchParams.get('scope') === 'invalid') {
|
||||||
|
const deleted = await storage.deleteInvalidInvites();
|
||||||
|
await writeAuditLog(storage, actorUser.id, 'admin.invite.delete_invalid', 'invite', null, {
|
||||||
|
deleted,
|
||||||
|
}, request);
|
||||||
|
|
||||||
|
return jsonResponse({ deleted }, 200);
|
||||||
|
}
|
||||||
|
|
||||||
const deleted = await storage.deleteAllInvites();
|
const deleted = await storage.deleteAllInvites();
|
||||||
await writeAuditLog(storage, actorUser.id, 'admin.invite.delete_all', 'invite', null, {
|
await writeAuditLog(storage, actorUser.id, 'admin.invite.delete_all', 'invite', null, {
|
||||||
deleted,
|
deleted,
|
||||||
|
|||||||
@@ -159,6 +159,15 @@ export async function deleteInvite(db: D1Database, code: string): Promise<boolea
|
|||||||
return (result.meta.changes ?? 0) > 0;
|
return (result.meta.changes ?? 0) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteInvalidInvites(db: D1Database): Promise<number> {
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
const result = await db
|
||||||
|
.prepare("DELETE FROM invites WHERE status != 'active' OR expires_at <= ?")
|
||||||
|
.bind(now)
|
||||||
|
.run();
|
||||||
|
return Number(result.meta.changes ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteAllInvites(db: D1Database): Promise<number> {
|
export async function deleteAllInvites(db: D1Database): Promise<number> {
|
||||||
const result = await db.prepare('DELETE FROM invites').run();
|
const result = await db.prepare('DELETE FROM invites').run();
|
||||||
return Number(result.meta.changes ?? 0);
|
return Number(result.meta.changes ?? 0);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
assignInviteUsedBy as assignStoredInviteUsedBy,
|
assignInviteUsedBy as assignStoredInviteUsedBy,
|
||||||
createInvite as createStoredInvite,
|
createInvite as createStoredInvite,
|
||||||
deleteInvite as deleteStoredInvite,
|
deleteInvite as deleteStoredInvite,
|
||||||
|
deleteInvalidInvites as deleteStoredInvalidInvites,
|
||||||
deleteAllInvites as deleteStoredInvites,
|
deleteAllInvites as deleteStoredInvites,
|
||||||
getInvite as findStoredInvite,
|
getInvite as findStoredInvite,
|
||||||
listAuditLogs as listStoredAuditLogs,
|
listAuditLogs as listStoredAuditLogs,
|
||||||
@@ -333,6 +334,10 @@ export class StorageService {
|
|||||||
return deleteStoredInvite(this.db, code);
|
return deleteStoredInvite(this.db, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteInvalidInvites(): Promise<number> {
|
||||||
|
return deleteStoredInvalidInvites(this.db);
|
||||||
|
}
|
||||||
|
|
||||||
async deleteAllInvites(): Promise<number> {
|
async deleteAllInvites(): Promise<number> {
|
||||||
return deleteStoredInvites(this.db);
|
return deleteStoredInvites(this.db);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2036,6 +2036,7 @@ export default function App() {
|
|||||||
onRemoveAllDevices: accountSecurityActions.openRemoveAllDevices,
|
onRemoveAllDevices: accountSecurityActions.openRemoveAllDevices,
|
||||||
onRefreshAdmin: adminActions.refreshAdmin,
|
onRefreshAdmin: adminActions.refreshAdmin,
|
||||||
onCreateInvite: adminActions.createInvite,
|
onCreateInvite: adminActions.createInvite,
|
||||||
|
onDeleteInvalidInvites: adminActions.deleteInvalidInvites,
|
||||||
onDeleteAllInvites: adminActions.deleteAllInvites,
|
onDeleteAllInvites: adminActions.deleteAllInvites,
|
||||||
onToggleUserStatus: adminActions.toggleUserStatus,
|
onToggleUserStatus: adminActions.toggleUserStatus,
|
||||||
onDeleteUser: adminActions.deleteUser,
|
onDeleteUser: adminActions.deleteUser,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface AdminPageProps {
|
|||||||
error: string;
|
error: string;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
onCreateInvite: (hours: number) => Promise<void>;
|
onCreateInvite: (hours: number) => Promise<void>;
|
||||||
|
onDeleteInvalidInvites: () => Promise<void>;
|
||||||
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>;
|
||||||
@@ -134,7 +135,10 @@ export default function AdminPage(props: AdminPageProps) {
|
|||||||
<h3>{t('txt_invites')}</h3>
|
<h3>{t('txt_invites')}</h3>
|
||||||
<div className="actions admin-invites-head-actions">
|
<div className="actions admin-invites-head-actions">
|
||||||
<button type="button" className="btn btn-secondary small" disabled={props.loading} onClick={props.onRefresh}>
|
<button type="button" className="btn btn-secondary small" disabled={props.loading} onClick={props.onRefresh}>
|
||||||
<RefreshCw size={14} className="btn-icon" /> {t('txt_sync')}
|
<RefreshCw size={14} className="btn-icon" /> {t('txt_refresh')}
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn btn-danger small" onClick={() => void props.onDeleteInvalidInvites()}>
|
||||||
|
<Trash2 size={14} className="btn-icon" /> {t('txt_delete_invalid')}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" className="btn btn-danger small" onClick={() => void props.onDeleteAllInvites()}>
|
<button type="button" className="btn btn-danger small" onClick={() => void props.onDeleteAllInvites()}>
|
||||||
<Trash2 size={14} className="btn-icon" /> {t('txt_delete_all')}
|
<Trash2 size={14} className="btn-icon" /> {t('txt_delete_all')}
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ export interface AppMainRoutesProps {
|
|||||||
onRemoveAllDevices: () => void;
|
onRemoveAllDevices: () => void;
|
||||||
onCreateInvite: (hours: number) => Promise<void>;
|
onCreateInvite: (hours: number) => Promise<void>;
|
||||||
onRefreshAdmin: () => void;
|
onRefreshAdmin: () => void;
|
||||||
|
onDeleteInvalidInvites: () => Promise<void>;
|
||||||
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>;
|
||||||
@@ -413,6 +414,7 @@ export default function AppMainRoutes(props: AppMainRoutesProps) {
|
|||||||
error={props.adminError}
|
error={props.adminError}
|
||||||
onRefresh={props.onRefreshAdmin}
|
onRefresh={props.onRefreshAdmin}
|
||||||
onCreateInvite={props.onCreateInvite}
|
onCreateInvite={props.onCreateInvite}
|
||||||
|
onDeleteInvalidInvites={props.onDeleteInvalidInvites}
|
||||||
onDeleteAllInvites={props.onDeleteAllInvites}
|
onDeleteAllInvites={props.onDeleteAllInvites}
|
||||||
onToggleUserStatus={props.onToggleUserStatus}
|
onToggleUserStatus={props.onToggleUserStatus}
|
||||||
onDeleteUser={props.onDeleteUser}
|
onDeleteUser={props.onDeleteUser}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'preact/hooks';
|
import { useMemo } from 'preact/hooks';
|
||||||
import { createInvite, deleteAllInvites, deleteInvite, deleteUser, setUserStatus } from '@/lib/api/admin';
|
import { createInvite, deleteAllInvites, deleteInvalidInvites, 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';
|
||||||
@@ -65,6 +65,26 @@ export default function useAdminActions(options: UseAdminActionsOptions) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async deleteInvalidInvites() {
|
||||||
|
onSetConfirm({
|
||||||
|
title: t('txt_delete_invalid_invites'),
|
||||||
|
message: t('txt_delete_invalid_invites_confirm_message'),
|
||||||
|
danger: true,
|
||||||
|
onConfirm: () => {
|
||||||
|
onSetConfirm(null);
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
await deleteInvalidInvites(authedFetch);
|
||||||
|
await refetchInvites();
|
||||||
|
onNotify('success', t('txt_invalid_invites_deleted'));
|
||||||
|
} catch (error) {
|
||||||
|
onNotify('error', error instanceof Error ? error.message : t('txt_delete_invalid_invites_failed'));
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async deleteAllInvites() {
|
async deleteAllInvites() {
|
||||||
onSetConfirm({
|
onSetConfirm({
|
||||||
title: t('txt_delete_all_invites'),
|
title: t('txt_delete_all_invites'),
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ export async function deleteInvite(authedFetch: AuthedFetch, code: string): Prom
|
|||||||
if (!resp.ok) throw new Error('Delete invite failed');
|
if (!resp.ok) throw new Error('Delete invite failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function deleteInvalidInvites(authedFetch: AuthedFetch): Promise<void> {
|
||||||
|
const resp = await authedFetch('/api/admin/invites?scope=invalid', { method: 'DELETE' });
|
||||||
|
if (!resp.ok) throw new Error('Delete invalid invites failed');
|
||||||
|
}
|
||||||
|
|
||||||
export async function deleteAllInvites(authedFetch: AuthedFetch): Promise<void> {
|
export async function deleteAllInvites(authedFetch: AuthedFetch): Promise<void> {
|
||||||
const resp = await authedFetch('/api/admin/invites', { method: 'DELETE' });
|
const resp = await authedFetch('/api/admin/invites', { method: 'DELETE' });
|
||||||
if (!resp.ok) throw new Error('Delete all invites failed');
|
if (!resp.ok) throw new Error('Delete all invites failed');
|
||||||
|
|||||||
@@ -1127,6 +1127,13 @@ export function createDemoMainRoutesProps(base: AppMainRoutesProps, notify: Noti
|
|||||||
onRefreshAdmin: () => {
|
onRefreshAdmin: () => {
|
||||||
notify('success', t('txt_demo_admin_refreshed'));
|
notify('success', t('txt_demo_admin_refreshed'));
|
||||||
},
|
},
|
||||||
|
onDeleteInvalidInvites: async () => {
|
||||||
|
const now = Date.now();
|
||||||
|
state.setInvites((prev) => prev.filter((invite) => (
|
||||||
|
invite.status === 'active' && (!invite.expiresAt || new Date(invite.expiresAt).getTime() > now)
|
||||||
|
)));
|
||||||
|
notify('success', t('txt_invalid_invites_deleted'));
|
||||||
|
},
|
||||||
onDeleteAllInvites: async () => {
|
onDeleteAllInvites: async () => {
|
||||||
state.setInvites([]);
|
state.setInvites([]);
|
||||||
notify('success', t('txt_all_invites_deleted'));
|
notify('success', t('txt_all_invites_deleted'));
|
||||||
|
|||||||
@@ -582,12 +582,17 @@ 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_invalid": "Delete Invalid",
|
||||||
|
"txt_delete_invalid_invites": "Delete invalid invites",
|
||||||
|
"txt_delete_invalid_invites_confirm_message": "Delete all invalid invite codes? Active, unexpired invite codes will be kept.",
|
||||||
|
"txt_delete_invalid_invites_failed": "Failed to delete invalid invites",
|
||||||
"txt_delete_invite": "Delete invite",
|
"txt_delete_invite": "Delete invite",
|
||||||
"txt_delete_invite_confirm_message": "Delete this invite code? This cannot be undone.",
|
"txt_delete_invite_confirm_message": "Delete this invite code? This cannot be undone.",
|
||||||
"txt_delete_invite_failed": "Failed to delete invite",
|
"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_deleted": "Invite deleted",
|
||||||
|
"txt_invalid_invites_deleted": "Invalid invites 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)",
|
||||||
@@ -1162,6 +1167,7 @@ const en: Record<string, string> = {
|
|||||||
"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": "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_delete_invalid": "Delete invalid 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",
|
||||||
"txt_log_action_admin_user_status": "Change user status",
|
"txt_log_action_admin_user_status": "Change user status",
|
||||||
|
|||||||
@@ -582,12 +582,17 @@ 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_invalid": "Eliminar inválidas",
|
||||||
|
"txt_delete_invalid_invites": "Eliminar invitaciones inválidas",
|
||||||
|
"txt_delete_invalid_invites_confirm_message": "¿Eliminar todos los códigos de invitación inválidos? Se conservarán los códigos activos y no vencidos.",
|
||||||
|
"txt_delete_invalid_invites_failed": "Error al eliminar invitaciones inválidas",
|
||||||
"txt_delete_invite": "Eliminar 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_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_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_deleted": "Invitación eliminada",
|
||||||
|
"txt_invalid_invites_deleted": "Invitaciones inválidas eliminadas",
|
||||||
"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",
|
||||||
@@ -1162,6 +1167,7 @@ const es: Record<string, string> = {
|
|||||||
"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": "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_delete_invalid": "Delete invalid 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",
|
||||||
"txt_log_action_admin_user_status": "Change user status",
|
"txt_log_action_admin_user_status": "Change user status",
|
||||||
|
|||||||
@@ -582,12 +582,17 @@ 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_invalid": "Удалить недействительные",
|
||||||
|
"txt_delete_invalid_invites": "Удалить недействительные приглашения",
|
||||||
|
"txt_delete_invalid_invites_confirm_message": "Удалить все недействительные пригласительные коды? Активные и не истекшие коды будут сохранены.",
|
||||||
|
"txt_delete_invalid_invites_failed": "Не удалось удалить недействительные приглашения",
|
||||||
"txt_delete_invite": "Удалить приглашение",
|
"txt_delete_invite": "Удалить приглашение",
|
||||||
"txt_delete_invite_confirm_message": "Удалить этот пригласительный код? Это действие нельзя отменить.",
|
"txt_delete_invite_confirm_message": "Удалить этот пригласительный код? Это действие нельзя отменить.",
|
||||||
"txt_delete_invite_failed": "Не удалось удалить приглашение",
|
"txt_delete_invite_failed": "Не удалось удалить приглашение",
|
||||||
"txt_invite_code_required": "Пригласительный код (обязательно)",
|
"txt_invite_code_required": "Пригласительный код (обязательно)",
|
||||||
"txt_invite_created": "Приглашение создано",
|
"txt_invite_created": "Приглашение создано",
|
||||||
"txt_invite_deleted": "Приглашение удалено",
|
"txt_invite_deleted": "Приглашение удалено",
|
||||||
|
"txt_invalid_invites_deleted": "Недействительные приглашения удалены",
|
||||||
"txt_invite_revoked": "Приглашение отозвано",
|
"txt_invite_revoked": "Приглашение отозвано",
|
||||||
"txt_revoke_invite_failed": "Не удалось отозвать приглашение",
|
"txt_revoke_invite_failed": "Не удалось отозвать приглашение",
|
||||||
"txt_invite_validity_hours": "Срок действия приглашения (часы)",
|
"txt_invite_validity_hours": "Срок действия приглашения (часы)",
|
||||||
@@ -1162,6 +1167,7 @@ const ru: Record<string, string> = {
|
|||||||
"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": "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_delete_invalid": "Delete invalid 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",
|
||||||
"txt_log_action_admin_user_status": "Change user status",
|
"txt_log_action_admin_user_status": "Change user status",
|
||||||
|
|||||||
@@ -582,12 +582,17 @@ 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_invalid": "删除无效",
|
||||||
|
"txt_delete_invalid_invites": "删除无效邀请码",
|
||||||
|
"txt_delete_invalid_invites_confirm_message": "确定删除所有无效邀请码吗?仍有效且未过期的邀请码会保留。",
|
||||||
|
"txt_delete_invalid_invites_failed": "删除无效邀请码失败",
|
||||||
"txt_delete_invite": "删除邀请码",
|
"txt_delete_invite": "删除邀请码",
|
||||||
"txt_delete_invite_confirm_message": "确定删除该邀请码吗?删除后无法恢复。",
|
"txt_delete_invite_confirm_message": "确定删除该邀请码吗?删除后无法恢复。",
|
||||||
"txt_delete_invite_failed": "删除邀请码失败",
|
"txt_delete_invite_failed": "删除邀请码失败",
|
||||||
"txt_invite_code_required": "邀请码(必填)",
|
"txt_invite_code_required": "邀请码(必填)",
|
||||||
"txt_invite_created": "邀请码已创建",
|
"txt_invite_created": "邀请码已创建",
|
||||||
"txt_invite_deleted": "邀请码已删除",
|
"txt_invite_deleted": "邀请码已删除",
|
||||||
|
"txt_invalid_invites_deleted": "无效邀请码已删除",
|
||||||
"txt_invite_revoked": "邀请码已撤销",
|
"txt_invite_revoked": "邀请码已撤销",
|
||||||
"txt_revoke_invite_failed": "撤销邀请码失败",
|
"txt_revoke_invite_failed": "撤销邀请码失败",
|
||||||
"txt_invite_validity_hours": "邀请码有效期(小时)",
|
"txt_invite_validity_hours": "邀请码有效期(小时)",
|
||||||
@@ -1162,6 +1167,7 @@ const zhCN: Record<string, string> = {
|
|||||||
"txt_log_action_admin_invite_create": "创建邀请",
|
"txt_log_action_admin_invite_create": "创建邀请",
|
||||||
"txt_log_action_admin_invite_delete": "删除邀请",
|
"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_delete_invalid": "删除无效邀请",
|
||||||
"txt_log_action_admin_invite_revoke": "撤销邀请",
|
"txt_log_action_admin_invite_revoke": "撤销邀请",
|
||||||
"txt_log_action_admin_user_delete": "删除用户",
|
"txt_log_action_admin_user_delete": "删除用户",
|
||||||
"txt_log_action_admin_user_status": "修改用户状态",
|
"txt_log_action_admin_user_status": "修改用户状态",
|
||||||
|
|||||||
@@ -582,12 +582,17 @@ 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_invalid": "刪除無效",
|
||||||
|
"txt_delete_invalid_invites": "刪除無效邀請碼",
|
||||||
|
"txt_delete_invalid_invites_confirm_message": "確定刪除所有無效邀請碼嗎?仍有效且未過期的邀請碼會保留。",
|
||||||
|
"txt_delete_invalid_invites_failed": "刪除無效邀請碼失敗",
|
||||||
"txt_delete_invite": "刪除邀請碼",
|
"txt_delete_invite": "刪除邀請碼",
|
||||||
"txt_delete_invite_confirm_message": "確定刪除此邀請碼嗎?刪除後無法復原。",
|
"txt_delete_invite_confirm_message": "確定刪除此邀請碼嗎?刪除後無法復原。",
|
||||||
"txt_delete_invite_failed": "刪除邀請碼失敗",
|
"txt_delete_invite_failed": "刪除邀請碼失敗",
|
||||||
"txt_invite_code_required": "邀請碼(必填)",
|
"txt_invite_code_required": "邀請碼(必填)",
|
||||||
"txt_invite_created": "邀請碼已創建",
|
"txt_invite_created": "邀請碼已創建",
|
||||||
"txt_invite_deleted": "邀請碼已刪除",
|
"txt_invite_deleted": "邀請碼已刪除",
|
||||||
|
"txt_invalid_invites_deleted": "無效邀請碼已刪除",
|
||||||
"txt_invite_revoked": "邀請碼已撤銷",
|
"txt_invite_revoked": "邀請碼已撤銷",
|
||||||
"txt_revoke_invite_failed": "撤銷邀請碼失敗",
|
"txt_revoke_invite_failed": "撤銷邀請碼失敗",
|
||||||
"txt_invite_validity_hours": "邀請碼有效期(小時)",
|
"txt_invite_validity_hours": "邀請碼有效期(小時)",
|
||||||
@@ -1162,6 +1167,7 @@ const zhTW: Record<string, string> = {
|
|||||||
"txt_log_action_admin_invite_create": "建立邀請",
|
"txt_log_action_admin_invite_create": "建立邀請",
|
||||||
"txt_log_action_admin_invite_delete": "刪除邀請",
|
"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_delete_invalid": "刪除無效邀請",
|
||||||
"txt_log_action_admin_invite_revoke": "撤銷邀請",
|
"txt_log_action_admin_invite_revoke": "撤銷邀請",
|
||||||
"txt_log_action_admin_user_delete": "刪除使用者",
|
"txt_log_action_admin_user_delete": "刪除使用者",
|
||||||
"txt_log_action_admin_user_status": "修改使用者狀態",
|
"txt_log_action_admin_user_status": "修改使用者狀態",
|
||||||
|
|||||||
Reference in New Issue
Block a user