mirror of
https://github.com/kamask-dev/pchelpmaster.git
synced 2026-11-11 05:49:38 +00:00
feat(shared): add pocketbase di client and domain errors
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014hvR7iG75jK5byaBBHdDLT
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
fc55456286
commit
bec31ff02e
+15
-10
@@ -55,7 +55,8 @@ src/app/orders/
|
||||
|
||||
| Папка | Назначение |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `shared/pocketbase/` | URL PocketBase, фабрика клиента, синхронизация cookie, маппинг ошибок |
|
||||
| `shared/pocketbase/` | URL PocketBase, фабрика клиента, синхронизация cookie |
|
||||
| `shared/errors/` | доменные ошибки и `toDomainError` |
|
||||
| `users/` | сущность User (профиль владельца аккаунта), без логики входа |
|
||||
| `auth/` | сессия: вход, регистрация, выход, текущий пользователь, guard'ы, формы входа/регистрации |
|
||||
| `layout/` | каркас приложения: шапка, навигация, меню пользователя |
|
||||
@@ -117,14 +118,18 @@ providers: [
|
||||
|
||||
## 4. Ошибки
|
||||
|
||||
`shared/pocketbase/` переводит `ClientResponseError` в доменные ошибки, которые понимает UI:
|
||||
Доменные ошибки и `toDomainError(error: unknown): Error` живут в `shared/errors/`. `toDomainError` переводит `ClientResponseError` в доменные ошибки, которые понимает UI:
|
||||
|
||||
| Доменная ошибка | Когда |
|
||||
| ------------------- | -------------------------------------------------------------------------------- |
|
||||
| `NotFoundError` | 404 (в том числе «чужая запись»: PocketBase не различает «нет» и «нет доступа») |
|
||||
| `ValidationError` | 400 с ошибками по полям: `fieldErrors: Record<string, string>` (коды PocketBase) |
|
||||
| `UnauthorizedError` | 401/403: сессия недействительна |
|
||||
| `NetworkError` | нет связи / PocketBase недоступен |
|
||||
| Доменная ошибка | Когда |
|
||||
| ------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| `NotFoundError` | 404 (в том числе «чужая запись»: PocketBase не различает «нет» и «нет доступа») |
|
||||
| `ValidationError` | 400 с ошибками по полям: `fieldErrors: Record<string, string>` (коды PocketBase) |
|
||||
| `UnauthorizedError` | 401/403: сессия недействительна |
|
||||
| `NetworkError` | нет связи / PocketBase недоступен (`status === 0`) или запрос прерван |
|
||||
| `InvalidCredentialsError` | неверный email или пароль; создаёт сервис аутентификации, `toDomainError` её не знает |
|
||||
| `UnknownError` | всё остальное, в том числе 400 без ошибок по полям; исходная ошибка — в `cause` |
|
||||
|
||||
Все доменные ошибки наследуют `DomainError`: ошибка, уже являющаяся `DomainError`, возвращается из `toDomainError` как есть. Исходная ошибка PocketBase сохраняется в `cause` у каждой ошибки, которую создаёт `toDomainError`.
|
||||
|
||||
UI показывает ошибки полей рядом с полями, а общие ошибки — в области уведомлений с `role="alert"`.
|
||||
|
||||
@@ -153,7 +158,7 @@ UI показывает ошибки полей рядом с полями, а
|
||||
|
||||
На сервере каждый HTTP-запрос получает **свой экземпляр Angular-приложения и свой root-инжектор**. Всё, что живёт в DI, изолировано. Опасно только то, что живёт **вне DI** — на уровне модуля, общее для всех запросов.
|
||||
|
||||
1. **Нельзя** создавать PocketBase на уровне модуля (`export const pb = new PocketBase(...)`). Экземпляр создаёт только фабрика в `shared/pocketbase/` внутри DI. ESLint-правило запрещает `new PocketBase(` во всех остальных файлах.
|
||||
1. **Нельзя** создавать PocketBase на уровне модуля (`export const pb = new PocketBase(...)`). Экземпляр создаёт только фабрика в `shared/pocketbase/` внутри DI. ESLint запрещает `new PocketBase(` и импорт класса `PocketBase` (в том числе под другим именем и через `pocketbase/*`) во всех остальных файлах; `import type` разрешён.
|
||||
2. **Нельзя** хранить пользовательские данные в переменных модуля, статических полях классов, кэшах вне DI.
|
||||
3. Сервер рендерит страницы с заголовками `Cache-Control: private, no-store` и `Vary: Cookie`: ни браузер, ни прокси не должны отдать одну страницу двум людям.
|
||||
4. На сервере используется глобальный `fetch` без общего состояния; `authStore` — свой у каждого экземпляра PocketBase (не `LocalAuthStore`, а in-memory `BaseAuthStore`).
|
||||
@@ -168,7 +173,7 @@ PocketBase SDK использует `fetch`, а не `HttpClient`, поэтом
|
||||
|
||||
- **Сервер:** `PB_URL` — переменная окружения Node-сервера, внутренний адрес PocketBase для серверного рендера (по умолчанию `http://127.0.0.1:8090`). В браузер не передаётся.
|
||||
- **Браузер:** `<window.location.origin>/pb`. Префикс `/pb` — константа `PB_PUBLIC_PATH` в `shared/pocketbase/`; если путь в nginx поменяется, меняется только она.
|
||||
- Всё это задаётся в одном месте: `shared/pocketbase/` (InjectionToken `PB_URL`, разные провайдеры для сервера и браузера).
|
||||
- Всё это задаётся в одном месте: `shared/pocketbase/`. Клиент `POCKETBASE` создаётся одной и той же фабрикой и в браузере, и на сервере (`providePocketBase()` в `app.config.ts`); различается только токен `PB_URL`: `providePocketBase()` даёт браузерное значение по умолчанию, а `providePbUrlForServer()` в `app.config.server.ts` подменяет его серверным.
|
||||
- **Разработка:** `ng serve` проксирует `/pb/*` → `PB_URL` со срезанием префикса (`proxy.conf.mjs`). E2E-тесты production-сборки используют маленький тестовый фронт-прокси, повторяющий правила nginx.
|
||||
|
||||
### Ожидаемая конфигурация nginx (пример, деплой — вне MVP)
|
||||
|
||||
Reference in New Issue
Block a user