fix(invites): код приглашения не подменяется слабым при отказе crypto/rand

Вместо предсказуемого запасного кода ручка возвращает 500: приглашение —
это доступ к серверу, маскировать отказ источника энтропии нельзя.
This commit is contained in:
2026-09-19 23:33:59 +03:00
parent 61a84bfeae
commit 63bf1e63b5
5 changed files with 84 additions and 13 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ export function Menu({ open, label, onClose, children, anchorRef, className = ''
if (!open) {
return;
}
const onPointerDown = (event: MouseEvent): void => {
const onPointerDown = (event: Event): void => {
const target = event.target;
if (!(target instanceof Node)) {
return;
+68
View File
@@ -183,6 +183,7 @@
"empty": "This server has no visible channels yet.",
"select": "Open channel {{name}}",
"add": "Create a channel",
"settings": "Server settings",
"create": {
"title": "New channel",
"name": "Channel name",
@@ -214,6 +215,13 @@
"user": {
"panelLabel": "User panel",
"settings": "User settings",
"menu": {
"label": "User menu",
"open": "Open the user menu",
"account": "Account settings",
"guilds": "Server settings",
"status": "Status"
},
"logout": "Sign out",
"loggingOut": "Signing out…",
"statusLabel": "Status: {{status}}",
@@ -240,6 +248,11 @@
"settings": {
"title": "Settings",
"back": "Back to app",
"switcher": {
"label": "Settings sections",
"account": "Server settings",
"servers": "My servers"
},
"tabs": {
"label": "Settings sections",
"profile": "Profile",
@@ -331,6 +344,61 @@
"memberCount": "{{count}} members",
"adminBadge": "admin",
"mainBadge": "main"
},
"servers": {
"pickerLabel": "My servers",
"empty": "You have no servers yet.",
"emptyTitle": "No servers yet",
"emptyHint": "Join the instance main server or create your own — its settings will show up here.",
"select": "Open settings of the server {{name}}",
"notFound": "The server was not found among the ones you can access.",
"readOnlyHint": "Only the owner and members with ADMINISTRATOR or MANAGE_GUILD can edit this.",
"general": {
"title": "General",
"description": "Every member sees the server name and description.",
"name": "Server name",
"guildDescription": "Description",
"descriptionPlaceholder": "What this server is about",
"saved": "Server settings saved."
},
"nickname": {
"title": "My profile on this server",
"description": "The nickname is visible on this server only.",
"label": "Nickname",
"placeholder": "Leave empty to reset the nickname",
"current": "Current nickname:",
"none": "not set",
"saved": "Nickname saved."
},
"members": {
"title": "Members",
"description": "The first {{limit}} members of the server.",
"empty": "No members found.",
"roles": "Roles: {{roles}}",
"more": "The full member list with roles arrives in the next phases."
},
"roles": {
"title": "Roles",
"description": "Role colour and position. Role management arrives in the next phases.",
"empty": "This server has no roles yet.",
"position": "Position {{position}}",
"default": "default"
},
"danger": {
"title": "Danger zone",
"description": "These actions cannot be undone.",
"leaveOwnerHint": "The owner cannot leave their own server: transfer ownership or delete the server first.",
"leaveHint": "Leave the server and lose access to its channels.",
"leave": "Leave server",
"leaveConfirmTitle": "Leave the server?",
"leaveConfirmBody": "You will leave “{{name}}”. You can come back with an invite.",
"leaveConfirm": "Leave",
"deleteHint": "Delete the server together with its channels, roles and members.",
"delete": "Delete server",
"deleteConfirmTitle": "Delete the server?",
"deleteConfirmBody": "“{{name}}” will be deleted permanently.",
"deleteConfirm": "Delete"
}
}
}
}
@@ -30,7 +30,7 @@ export function GuildMembersSection({
return (
<SettingsSection
title={t('settings.servers.members.title')}
description={t('settings.servers.members.description', { count: PREVIEW_LIMIT })}
description={t('settings.servers.members.description', { limit: PREVIEW_LIMIT })}
>
{isPending ? (
<SkeletonLines className="mt-4" rows={4} />
@@ -65,7 +65,7 @@ export function GuildMembersSection({
</span>
</span>
<Badge>
{t('settings.servers.members.roles', { count: member.role_ids.length })}
{t('settings.servers.members.roles', { roles: member.role_ids.length })}
</Badge>
</li>
);
@@ -90,17 +90,18 @@ export default function ServersSettingsPage() {
});
const guild = detail.data;
const sessionGuild = sessionGuilds.find((item) => item.id === activeId) ?? null;
const myPermissions = guild?.my_permissions ?? active?.my_permissions ?? [];
const ownerId = guild?.owner_id ?? active?.owner_id ?? '';
const canManage = canManageGuildSettings(myPermissions, ownerId, currentUser.data?.id);
const currentNickname = useMemo(() => {
const fromSnapshot = active?.my_nickname;
const fromSnapshot = sessionGuild?.my_nickname;
if (fromSnapshot !== undefined && fromSnapshot !== '') {
return fromSnapshot;
}
const mine = members.data?.find((member) => member.user_id === currentUser.data?.id);
return mine?.nickname ?? '';
}, [active, members.data, currentUser.data?.id]);
}, [sessionGuild, members.data, currentUser.data?.id]);
return (
<div className="flex flex-col gap-5">