Add news feature with CRUD operations and real-time notifications
- Implemented news management functionality, allowing admins to create, read, update, and delete news posts. - Introduced a new SignalR event for broadcasting news updates to all connected clients. - Updated API documentation to include new endpoints for news management. - Enhanced frontend with a dedicated news page and admin interface for managing news posts. - Added necessary localization for news-related terms in both Russian and English.
This commit is contained in:
@@ -101,6 +101,20 @@ status, createdAt }`. `expiresAt` всегда `null` (лимиты по сро
|
||||
```
|
||||
Значение `OsPlatform` в C#/JSON — `IOS` (не `iOS`).
|
||||
|
||||
## News — лента новостей
|
||||
|
||||
| Метод | Путь | Роль | Тело запроса | Тело ответа |
|
||||
| ------ | ------------------------ | ----- | ------------------------ | ------------- |
|
||||
| GET | `/api/news` | user | query: `page, pageSize` | `PagedList<NewsPostDto>` |
|
||||
| GET | `/api/admin/news` | admin | query: `page, pageSize` | `PagedList<NewsPostDto>` |
|
||||
| POST | `/api/admin/news` | admin | `{ title, body }` | `NewsPostDto` |
|
||||
| PUT | `/api/admin/news/{id}` | admin | `{ title, body }` | `NewsPostDto` |
|
||||
| DELETE | `/api/admin/news/{id}` | admin | — | `204 No Content` |
|
||||
|
||||
Нет черновиков/отложенной публикации — `POST` сразу видна всем аутентифицированным пользователям
|
||||
и триггерит SignalR-событие `newsPublished` (см. ниже). `NewsPostDto`:
|
||||
`{ id, title, body, createdAt, updatedAt }`.
|
||||
|
||||
## Activation (пользователь)
|
||||
|
||||
| Метод | Путь | Роль | Тело запроса | Тело ответа |
|
||||
@@ -190,6 +204,7 @@ totalConfigs, activeConfigs, totalUsedUpBytes, totalUsedDownBytes }` — счи
|
||||
| `nodeStatusChanged` | `{ nodeId, status, lastSyncAt }` | `admins` |
|
||||
| `activationRequested` | `{ requestId, userId, userName, comment, createdAt }` | `admins` |
|
||||
| `userActivated` | `{ userId }` | владельцу |
|
||||
| `newsPublished` | `{ id, title, createdAt }` | все (broadcast) |
|
||||
|
||||
### Client → Server
|
||||
Клиент только слушает; группировка по пользователю происходит на сервере при подключении, по
|
||||
|
||||
@@ -189,7 +189,9 @@ POST /api/configs
|
||||
(события нод/системы/активации) — пользователь при подключении добавляется в свою `user:{userId}`
|
||||
и, если он админ, дополнительно в `admins`.
|
||||
- **События сервер→клиент**: `configTrafficUpdated`, `configStatusChanged`, `nodeStatusChanged`,
|
||||
`activationRequested`, `userActivated` — точные payload'ы см. [api-design.md](api-design.md#signalr--hub-hubspanel).
|
||||
`activationRequested`, `userActivated`, `newsPublished` — точные payload'ы см.
|
||||
[api-design.md](api-design.md#signalr--hub-hubspanel). `newsPublished` — единственное
|
||||
широковещательное событие (`Clients.All`), а не по группе — новости видны всем без исключения.
|
||||
- Пуш выполняет `SignalRRealtimeNotifier` (порт `IRealtimeNotifier`, реализация в `Api/Hubs/`),
|
||||
вызываемый из хендлеров и фоновых сервисов — Application-слой не зависит от SignalR напрямую.
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ backend/
|
||||
Common/ # Entity (единственный базовый класс — без AggregateRoot/IDomainEvent)
|
||||
Activation/ # ActivationRequest, ActivationStatus
|
||||
Apps/ # ClientApp, OsPlatform
|
||||
News/ # NewsPost
|
||||
Audit/ # AuditLog, AuditSource
|
||||
Configs/ # VpnConfig, ConfigStatus, TrafficSample
|
||||
Inbounds/ # Inbound, VpnProtocol
|
||||
@@ -29,6 +30,7 @@ backend/
|
||||
Admin/
|
||||
Activation/ # ListActivationRequestsQuery, Approve/RejectActivationCommand
|
||||
Apps/ # CRUD ClientApp
|
||||
News/ # CRUD NewsPost
|
||||
Audit/ # ListAuditLogsQuery
|
||||
Inbounds/ # ListInbounds, PublishInbound
|
||||
Nodes/ # RegisterNode, UpdateNode, DeleteNode, SyncNode, ProbeNode, ListNodes
|
||||
@@ -37,6 +39,7 @@ backend/
|
||||
Users/ # ListUsers, BlockUser/UnblockUser, ChangeUserRole, ResetUserPassword,
|
||||
# ForceRevokeConfig, GetUserConfigs
|
||||
Apps/ # ListAppsQuery (по ОС, для юзера)
|
||||
News/ # ListNewsQuery (пагинировано, для юзера)
|
||||
Auth/
|
||||
ChangePassword/, DeleteMyAccount/, Login/, Logout/, Me/, Refresh/, Register/
|
||||
Configs/
|
||||
@@ -59,7 +62,7 @@ backend/
|
||||
Telegram/ # TelegramNotifier, TelegramOptions
|
||||
DependencyInjection.cs # AddInfrastructure(...)
|
||||
PnvPanel.Api/
|
||||
Endpoints/ # 12 файлов, см. backend-conventions.md ниже и api-design.md
|
||||
Endpoints/ # 14 файлов, см. backend-conventions.md ниже и api-design.md
|
||||
Hubs/ # PanelHub, SignalRRealtimeNotifier (реализация IRealtimeNotifier — здесь,
|
||||
# не в Infrastructure, т.к. нужен IHubContext<PanelHub>)
|
||||
Telegram/ # TelegramBotHostedService, PnvBotUpdateHandler, TelegramNotifier
|
||||
|
||||
@@ -21,6 +21,7 @@ AppUser (Identity) [+ IsActivated, IsBlocked, TelegramUserId, SubscriptionToken
|
||||
VpnConfig ─*─ TrafficSample (история трафика; пишется TrafficSyncService)
|
||||
AuditLog (append-only журнал действий; ссылается на ActorId/TargetId)
|
||||
ClientApp (каталог приложений-клиентов; группируется по OperatingSystem)
|
||||
NewsPost (лента новостей; публикуется админом, видна всем аутентифицированным пользователям)
|
||||
```
|
||||
|
||||
## Сущности
|
||||
@@ -143,6 +144,21 @@ ClientApp (каталог приложений-клиен
|
||||
Управляется админом (CRUD). Пользователю отдаётся только `IsEnabled`, сгруппировано по `OperatingSystem`.
|
||||
Стартовый набор сидируется из [`seed/client-apps.json`](../seed/client-apps.json), если таблица пуста.
|
||||
|
||||
### NewsPost — новости для пользователей
|
||||
Публикуются админом немедленно, видны всем залогиненным пользователям в хронологической ленте.
|
||||
|
||||
| Поле | Тип | Заметки |
|
||||
| ----------- | ----------------- | ------------------------------------------------------- |
|
||||
| `Id` | `Guid` | PK |
|
||||
| `Title` | `string` | Заголовок |
|
||||
| `Body` | `string` | Markdown-текст |
|
||||
| `CreatedAt` | `DateTimeOffset` | Момент публикации (= момент создания, нет черновиков) |
|
||||
| `UpdatedAt` | `DateTimeOffset?` | Момент последней правки (опц.) |
|
||||
|
||||
Нет статуса черновик/запланировано — публикация мгновенная. Нет видимости по ролям — доступно
|
||||
всем аутентифицированным пользователям. Realtime-оповещение о новом посте — `newsPublished`
|
||||
(SignalR, широковещательно всем подключенным клиентам), см. [architecture.md](architecture.md#realtime-signalr).
|
||||
|
||||
### AuditLog — журнал действий
|
||||
Аудит значимых действий (прежде всего админских) для расследований и прозрачности.
|
||||
|
||||
|
||||
+12
-5
@@ -23,6 +23,7 @@ SPA на **React 19 + Vite + TypeScript**. Общается с бэком по R
|
||||
| Формы | react-hook-form + zod |
|
||||
| Realtime | @microsoft/signalr |
|
||||
| QR-коды | qrcode.react (рендерит QR из готовой строки на клиенте) |
|
||||
| Markdown | react-markdown + remark-gfm (лента новостей; без rehype-raw — сырой HTML не рендерится) |
|
||||
| Типы API | openapi-typescript (`pnpm gen:api`) — генерирует `schema.gen.ts` для сверки; фичи импортируют руками написанный `shared/api/types.ts` |
|
||||
| i18n | react-i18next (RU + EN) |
|
||||
| Линт | oxlint (не ESLint) |
|
||||
@@ -44,19 +45,20 @@ frontend/
|
||||
index.css # Tailwind v4 (@import), без отдельной styles/-папки
|
||||
routes/ # файловый роутинг TanStack Router
|
||||
__root.tsx # шапка (лого, нав, переключатель языка/темы), Outlet
|
||||
index.tsx, login.tsx, register.tsx, dashboard.tsx, instructions.tsx, settings.tsx
|
||||
index.tsx, login.tsx, register.tsx, dashboard.tsx, instructions.tsx, news.tsx, settings.tsx
|
||||
admin.tsx # layout админки (вкладки) + Outlet
|
||||
admin/
|
||||
index.tsx, activation.tsx, users.tsx, roles.tsx, nodes.tsx, apps.tsx, audit.tsx
|
||||
index.tsx, activation.tsx, users.tsx, roles.tsx, nodes.tsx, apps.tsx, news.tsx, audit.tsx
|
||||
features/
|
||||
auth/ # api.ts, store.ts (zustand), guards.ts, LoginForm.tsx, RegisterForm.tsx
|
||||
activation/ # api.ts, ActivationGate.tsx (экран "запросить активацию")
|
||||
configs/ # api.ts, ConfigCard.tsx, CreateConfigDialog.tsx, SubscriptionCard.tsx
|
||||
apps/ # api.ts, AppsCatalog.tsx (для /instructions)
|
||||
news/ # api.ts, NewsFeed.tsx (для /news)
|
||||
telegram/ # api.ts, TelegramLoginButton.tsx
|
||||
settings/ # ChangePasswordForm.tsx, TelegramLinkCard.tsx, DeleteAccountSection.tsx
|
||||
admin/
|
||||
users/, roles/, activation/, nodes/, inbounds/, apps/, audit/, stats/ # api.ts + диалоги CRUD в каждой
|
||||
users/, roles/, activation/, nodes/, inbounds/, apps/, news/, audit/, stats/ # api.ts + диалоги CRUD в каждой
|
||||
theme/
|
||||
ThemeProvider.tsx # React Context + localStorage (`pnv-theme`), НЕ zustand
|
||||
shared/
|
||||
@@ -69,7 +71,7 @@ frontend/
|
||||
realtime/
|
||||
connection.ts, RealtimeProvider.tsx
|
||||
ui/
|
||||
button.tsx, input.tsx, label.tsx, card.tsx, dialog.tsx, select.tsx, badge.tsx,
|
||||
button.tsx, input.tsx, textarea.tsx, label.tsx, card.tsx, dialog.tsx, select.tsx, badge.tsx,
|
||||
progress.tsx, toast-store.tsx, toaster.tsx
|
||||
index.html
|
||||
vite.config.ts
|
||||
@@ -93,6 +95,10 @@ frontend/
|
||||
- **Страница инструкций** (`/instructions`): статичные шаги + каталог приложений (`GET /api/apps`),
|
||||
сгруппированный по ОС и показан вкладками (по одной ОС за раз); клик по приложению открывает
|
||||
ссылку на скачивание.
|
||||
- **Лента новостей** (`/news`): хронологический список постов админа (заголовок + Markdown-тело,
|
||||
рендерится через `react-markdown` + `remark-gfm`), пагинация (`GET /api/news`), живое обновление
|
||||
по SignalR (`newsPublished`, широковещательно всем). Админка (`/admin/news`): CRUD, обычный
|
||||
`<textarea>` с переключателем предпросмотра Markdown вместо WYSIWYG-редактора.
|
||||
- **Настройки** (`/settings`): смена пароля, привязка/отвязка Telegram (`TelegramLinkCard`),
|
||||
удаление аккаунта с подтверждением (`DeleteAccountSection`).
|
||||
- **Админка** (`/admin/*`): вкладки — обзор (карточки статистики, без графиков), запросы активации,
|
||||
@@ -132,7 +138,8 @@ frontend/
|
||||
|
||||
- Одно SignalR-подключение к `/hubs/panel` с JWT (`RealtimeProvider`, `shared/realtime/connection.ts`).
|
||||
- Обработчики `configTrafficUpdated`/`configStatusChanged`/`nodeStatusChanged`/`activationRequested`/
|
||||
`userActivated` точечно инвалидируют/обновляют кэш TanStack Query — UI обновляется без перезагрузки.
|
||||
`userActivated`/`newsPublished` точечно инвалидируют/обновляют кэш TanStack Query — UI обновляется
|
||||
без перезагрузки.
|
||||
|
||||
## Скрипты
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ PnvPanel **не заменяет** Xray/3x-ui — он оркестрирует
|
||||
- Telegram-бот: ссылка на сайт, просмотр конфигов, привязка/регистрация через Telegram и passwordless-вход.
|
||||
- Светлая/тёмная тема сайта.
|
||||
- Страница инструкций по подключению + каталог приложений по ОС (админ ведёт, юзер видит сгруппировано).
|
||||
- Лента новостей: админ публикует Markdown-посты, все пользователи видят их живой лентой (SignalR).
|
||||
- Единый Docker-образ (фронт+бек) + PostgreSQL в docker-compose.
|
||||
|
||||
**Не реализовано:**
|
||||
|
||||
Reference in New Issue
Block a user