docs: инфраструктура VM Сервисы + архитектура pulse-api и pulse-web
This commit is contained in:
263
Инфраструктура/pulse-web.md
Normal file
263
Инфраструктура/pulse-web.md
Normal 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)
|
||||
Reference in New Issue
Block a user