61a84bfeae
- POST /guilds/{id}/invites (CREATE_INVITES): код из 10 символов, лимит
использований и срок жизни; GET /guilds/{id}/invites (MANAGE_GUILD);
DELETE /invites/{code} — создатель или MANAGE_GUILD;
- GET /invites/{code} — предпросмотр с карточкой сервера, 410 для истёкших и
исчерпанных; POST /invites/{code} — принятие с проверкой лимита участников,
повторное принятие не расходует использование;
- PATCH /guilds/{id} принимает `public`: сервер попадает в каталог и открыт для
входа без приглашения (главный сервер публиковать нельзя);
- GET /guilds/public — каталог (имя, описание, иконка, число участников);
- аудит invite.create/invite.revoke/invite.accept, события INVITE_CREATE и
INVITE_DELETE владельцу сессии;
- тесты: полный жизненный цикл приглашения, исчерпание, отзыв, права,
публичный каталог и вход в публичный сервер.
27 lines
745 B
TypeScript
27 lines
745 B
TypeScript
import { Suspense } from 'react';
|
|
import { RouterProvider } from 'react-router';
|
|
|
|
import { Card } from '@/components/ui/primitives';
|
|
import { SkeletonLines } from '@/components/ui/Skeleton';
|
|
import { router } from '@/router';
|
|
import '@/i18n';
|
|
|
|
/** Заглушка на время загрузки ленивых страниц: скелетон вместо пустоты. */
|
|
export function RouteFallback() {
|
|
return (
|
|
<div className="flex min-h-full items-center justify-center p-6">
|
|
<Card className="w-full max-w-md">
|
|
<SkeletonLines rows={4} />
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function App() {
|
|
return (
|
|
<Suspense fallback={<RouteFallback />}>
|
|
<RouterProvider router={router} />
|
|
</Suspense>
|
|
);
|
|
}
|