docs: инфраструктура VM Сервисы + архитектура pulse-api и pulse-web

This commit is contained in:
Cosmo
2026-04-02 10:43:44 +00:00
parent d4f3a10d0d
commit 20e1f3fa6c
31 changed files with 1843 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
# pulse-web — Архитектура
**Репозиторий:** `https://git.digital-home.site/daniil/pulse-web`
**URL:** `https://pulse.digital-home.site`
**Dev:** `http://192.168.31.60:5174`
**Storybook:** `http://192.168.31.60:6006`
## Общая архитектура
React SPA (Single Page Application):
```
src/
api/ ← API-функции (axios, по доменам)
store/ ← State management (Zustand)
contexts/ ← React Context (тема)
pages/ ← Страницы (маршруты)
components/ ← Переиспользуемые компоненты
main.jsx ← Точка входа (BrowserRouter + ThemeContext)
App.jsx ← Роутер (Routes/Route)
index.css ← Глобальные стили (Tailwind)
```
**Стек:**
- Framework: React 18
- Bundler: Vite 5
- Роутинг: React Router DOM 6
- State: Zustand 4
- HTTP: Axios (с interceptors для JWT refresh)
- UI: Tailwind CSS 3
- Анимации: Framer Motion 11
- Иконки: Lucide React
- Графики: Recharts 2
- Утилиты дат: date-fns 3
- Компонент-либа: Storybook 8
- Тесты: Vitest + Testing Library
## Структура папок
```
pulse-web/
├── src/
│ ├── api/
│ │ ├── client.js # Axios instance + JWT interceptor
│ │ ├── auth.js # (в store/auth.js)
│ │ ├── tasks.js # tasksApi: list, today, create, complete...
│ │ ├── habits.js # habitsApi: list, create, log, stats...
│ │ ├── finance.js # financeApi: categories, transactions, summary
│ │ ├── savings.js # savingsApi: categories, transactions, stats
│ │ └── profile.js # profileApi: get, update
│ ├── store/
│ │ └── auth.js # useAuthStore (Zustand): user, login, logout
│ ├── contexts/
│ │ └── ThemeContext.jsx # ThemeProvider: light/dark, localStorage
│ ├── pages/
│ │ ├── Home.jsx # Главная страница (дашборд)
│ │ ├── Tracker.jsx # Трекер (таб: привычки/задачи/статистика)
│ │ ├── Habits.jsx # Страница привычек (встраивается в Tracker)
│ │ ├── Tasks.jsx # Страница задач (встраивается в Tracker)
│ │ ├── Stats.jsx # Статистика (встраивается в Tracker)
│ │ ├── Finance.jsx # Финансы (таб: обзор/транзакции/аналитика/категории)
│ │ ├── Savings.jsx # Накопления
│ │ ├── Settings.jsx # Настройки пользователя
│ │ ├── Login.jsx # Вход
│ │ ├── Register.jsx # Регистрация
│ │ ├── ForgotPassword.jsx # Сброс пароля
│ │ ├── ResetPassword.jsx # Новый пароль (по токену из email)
│ │ └── VerifyEmail.jsx # Подтверждение email
│ ├── components/
│ │ ├── Navigation.jsx # Нижняя навигация (fixed bottom)
│ │ ├── CreateTaskModal.jsx
│ │ ├── EditTaskModal.jsx
│ │ ├── CreateHabitModal.jsx
│ │ ├── EditHabitModal.jsx
│ │ ├── LogHabitModal.jsx
│ │ └── finance/
│ │ ├── FinanceDashboard.jsx # Обзор месяца
│ │ ├── TransactionList.jsx # Список транзакций
│ │ ├── FinanceAnalytics.jsx # Графики трендов
│ │ ├── CategoriesManager.jsx # Управление категориями
│ │ └── AddTransactionModal.jsx
│ ├── App.jsx # Routes + ProtectedRoute/PublicRoute
│ ├── main.jsx # ReactDOM.render + Providers
│ └── index.css # Tailwind + кастомные стили
├── public/
├── package.json
├── vite.config.js
├── tailwind.config.js
├── nginx.conf
└── Dockerfile
```
## Страницы / Роуты
| Путь | Компонент | Защита | Описание |
|------|-----------|--------|----------|
| `/login` | `Login` | Public only | Форма входа |
| `/register` | `Register` | Public only | Форма регистрации |
| `/forgot-password` | `ForgotPassword` | Public only | Запрос сброса пароля |
| `/verify-email` | `VerifyEmail` | Нет | Подтверждение email по токену |
| `/reset-password` | `ResetPassword` | Нет | Установка нового пароля |
| `/` | `Home` | Protected | Главная: дашборд |
| `/tracker` | `Tracker` | Protected | Трекер привычек/задач/статистики |
| `/habits` | → `/tracker` | Protected | Редирект на трекер |
| `/tasks` | → `/tracker` | Protected | Редирект на трекер |
| `/stats` | → `/tracker` | Protected | Редирект на трекер |
| `/savings` | `Savings` | Protected | Накопления |
| `/settings` | `Settings` | Protected | Настройки аккаунта |
| `*` | → `/` | — | Любой неизвестный → главная |
### Tracker (вкладки)
`/tracker` содержит 3 вкладки:
1. **Привычки**`Habits` компонент
2. **Задачи**`Tasks` компонент
3. **Статистика**`Stats` компонент
### Finance (вкладки)
`/finance` (доступна через роутер если добавить) содержит 4 вкладки:
1. **Обзор**`FinanceDashboard`
2. **Транзакции**`TransactionList`
3. **Аналитика**`FinanceAnalytics`
4. **Категории**`CategoriesManager`
> ⚠️ Страница Finance рендерится, но **нет роута** в App.jsx. Доступ только если добавить `<Route path="/finance">`.
## Основные компоненты
| Компонент | Файл | Назначение |
|-----------|------|------------|
| `Navigation` | components/Navigation.jsx | Нижняя навбар (Главная, Трекер, Накопления, Настройки). Показывает Finance только для `user.id === 1` (owner) |
| `ProtectedRoute` | App.jsx | Редиректит на `/login` если не авторизован |
| `PublicRoute` | App.jsx | Редиректит на `/` если уже авторизован |
| `FinanceDashboard` | finance/ | Обзор месяца: баланс, доходы/расходы, по категориям, дневной график |
| `TransactionList` | finance/ | Список транзакций с фильтрацией |
| `FinanceAnalytics` | finance/ | Recharts: тренды по месяцам |
| `CategoriesManager` | finance/ | CRUD категорий |
| `AddTransactionModal` | finance/ | Модал добавления транзакции |
| `CreateTaskModal` | components/ | Создание задачи |
| `EditTaskModal` | components/ | Редактирование задачи |
| `CreateHabitModal` | components/ | Создание привычки |
| `LogHabitModal` | components/ | Отметка привычки |
## API вызовы
### Axios Client (`src/api/client.js`)
```js
const api = axios.create({ baseURL: VITE_API_URL })
// Request interceptor: добавляет Bearer токен из localStorage
api.interceptors.request Authorization: Bearer <access_token>
// Response interceptor: при 401 делает refresh и повторяет запрос
api.interceptors.response POST /auth/refresh обновляет tokens в localStorage
```
### API модули
```js
// tasks.js
tasksApi.list(completed?) GET /tasks
tasksApi.today() GET /tasks/today
tasksApi.create(data) POST /tasks
tasksApi.complete(id) POST /tasks/{id}/complete
tasksApi.uncomplete(id) POST /tasks/{id}/uncomplete
tasksApi.update(id, data) PUT /tasks/{id}
tasksApi.delete(id) DELETE /tasks/{id}
// habits.js
habitsApi.list() GET /habits
habitsApi.create(data) POST /habits
habitsApi.log(id, data) POST /habits/{id}/log
habitsApi.stats() GET /habits/stats
habitsApi.habitStats(id) GET /habits/{id}/stats
// finance.js
financeApi.getCategories() GET /finance/categories
financeApi.createCategory(data) POST /finance/categories
financeApi.getTransactions(m, y) GET /finance/transactions?month=&year=
financeApi.createTransaction(data) POST /finance/transactions
financeApi.getSummary(m, y) GET /finance/summary
financeApi.getAnalytics() GET /finance/analytics
// savings.js
savingsApi.getCategories() GET /savings/categories
savingsApi.createCategory(data) POST /savings/categories
savingsApi.getTransactions() GET /savings/transactions
savingsApi.getStats() GET /savings/stats
// profile.js
profileApi.get() GET /profile
profileApi.update(data) PUT /profile
```
## State Management
**Zustand** — единственный store: `src/store/auth.js`
```js
useAuthStore = {
user: null | User,
isLoading: boolean,
isAuthenticated: boolean,
initialize() // Вызывается в App.jsx useEffect: GET /auth/me
login(email, password) // POST /auth/login, сохраняет tokens
register(email, username, password)
logout() // Чистит localStorage + state
}
```
**React Context:**
- `ThemeContext` (`contexts/ThemeContext.jsx`) — dark/light mode, сохраняется в localStorage
**Нет Redux / React Query** — данные загружаются локально в компонентах через `useState + useEffect`.
## Основные зависимости
| Пакет | Версия | Назначение |
|-------|--------|------------|
| react | 18.2 | UI framework |
| react-router-dom | 6.22 | Роутинг |
| zustand | 4.5 | State management |
| axios | 1.6 | HTTP клиент |
| tailwindcss | 3.4 | CSS utility framework |
| framer-motion | 11 | Анимации |
| lucide-react | 0.312 | Иконки |
| recharts | 2.12 | Графики |
| date-fns | 3.3 | Утилиты дат |
| clsx | 2.1 | Условные классы |
| storybook | 8.5 | UI компонент-браузер (dev) |
| vitest | 4 | Тесты |
## Конфигурация
| Env переменная | Описание | Default |
|---------------|----------|---------|
| `VITE_API_URL` | URL backend API | `https://api.digital-home.site` |
## Где искать что
| Задача | Файл |
|--------|------|
| **Новая страница** | `src/pages/NewPage.jsx` + роут в `App.jsx` + ссылка в `Navigation.jsx` |
| **Новый компонент** | `src/components/NewComponent.jsx` |
| **Новый API вызов** | `src/api/<domain>.js` (добавить метод) |
| **Финансы: UI** | `src/pages/Finance.jsx`, `src/components/finance/` |
| **Финансы: API** | `src/api/finance.js` |
| **Привычки: UI** | `src/pages/Habits.jsx`, `CreateHabitModal`, `LogHabitModal` |
| **Привычки: API** | `src/api/habits.js` |
| **Задачи: UI** | `src/pages/Tasks.jsx`, `CreateTaskModal`, `EditTaskModal` |
| **Задачи: API** | `src/api/tasks.js` |
| **Авторизация** | `src/store/auth.js`, `src/pages/Login.jsx` |
| **Глобальные стили** | `src/index.css` + `tailwind.config.js` |
| **Темная тема** | `src/contexts/ThemeContext.jsx` |
| **Нижняя навигация** | `src/components/Navigation.jsx` |
## Заметки
- `Finance` страница не добавлена в роутер и навигацию для обычных пользователей — только для owner (id=1)
- Legacy роуты `/habits`, `/tasks`, `/stats` → редиректят на `/tracker`
- Токены хранятся в `localStorage`: `access_token`, `refresh_token`
- Auto-refresh токена при 401 ответе (в axios interceptor)