feat: release v1.8.0 — connections dashboard, VK OAuth, sync config, catalog browser
- Connections dashboard with add/remove flow and background health checks - VK OAuth connection with profile info and health monitoring - Sync configuration page with master toggle and filter summary - Catalog browser with store/group/product tables, filter management, CSV export - Alembic migrations for all new tables - run/read_config.py for shell sync script DB integration - CHANGELOG.md updated for v1.8.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,19 +2,33 @@
|
||||
|
||||
## Context
|
||||
|
||||
Users currently access Evotor connection via a dedicated `/evotor` page linked from the navbar. As more integrations are planned, we need a unified **Connections** page where users can see all their connections at a glance, with real-time status indicators powered by background health checks.
|
||||
Users currently access Evotor connection via a dedicated `/evotor` page linked from the navbar. As more integrations are planned, we need a unified **Connections** page where users can manage all their connections: add new ones, view status, edit, and delete. The dashboard starts empty — users explicitly add each connection they need.
|
||||
|
||||
Supported connection types: **Evotor**, **VK** (one per type per user).
|
||||
|
||||
## Data Design
|
||||
|
||||
### Current state (separate models)
|
||||
|
||||
`EvotorConnection` and `VkConnection` remain as-is — they hold service-specific fields (store_id/store_name for Evotor, vk_user_id/first_name/last_name for VK). The connections dashboard reads from both tables.
|
||||
|
||||
No new unified "connection" table needed. The dashboard builds a virtual list by querying both tables. The "add" flow is just a gateway to the existing per-service OAuth pages.
|
||||
|
||||
### Model additions (both `EvotorConnection` and `VkConnection`)
|
||||
|
||||
Already planned:
|
||||
- `is_online` (Boolean, default=False, server_default="0")
|
||||
- `last_checked_at` (DateTime, nullable)
|
||||
|
||||
## Plan
|
||||
|
||||
### 1. Model Changes — `web/models.py`
|
||||
|
||||
Add to `EvotorConnection`:
|
||||
- `is_online` (Boolean, default=False, server_default="0")
|
||||
- `last_checked_at` (DateTime, nullable)
|
||||
Add `is_online` and `last_checked_at` to both `EvotorConnection` and `VkConnection`.
|
||||
|
||||
### 2. Alembic Migration
|
||||
|
||||
Generate migration for the two new columns on `evotor_connections` table.
|
||||
Add health check fields to both connection tables.
|
||||
|
||||
### 3. Config Addition — `web/config.py`
|
||||
|
||||
@@ -22,74 +36,157 @@ Add `HEALTH_CHECK_INTERVAL_SECONDS: int = 600` (10 minutes default).
|
||||
|
||||
### 4. Background Health Checker — `web/health_checker.py` (new)
|
||||
|
||||
- `check_evotor_connection(access_token) -> bool` — async, calls `GET https://api.evotor.ru/stores` with Bearer token, returns True if 200
|
||||
- `run_health_checks()` — queries all `EvotorConnection` rows using its own `SessionLocal()`, checks each, updates `is_online` and `last_checked_at`
|
||||
- `health_check_loop(interval)` — infinite loop with `asyncio.sleep`, calls `run_health_checks()`
|
||||
- `check_evotor_connection(access_token) -> bool` — async, `GET https://api.evotor.ru/stores` with Bearer token
|
||||
- `check_vk_connection(access_token) -> bool` — async, `GET https://api.vk.com/method/users.get` with token
|
||||
- `run_health_checks()` — queries all connection rows, checks each, updates `is_online` and `last_checked_at`
|
||||
- `health_check_loop(interval)` — infinite loop with `asyncio.sleep`
|
||||
|
||||
### 5. Wire Background Task — `web/main.py`
|
||||
|
||||
Add FastAPI lifespan context manager:
|
||||
- On startup: `asyncio.create_task(health_check_loop(...))`
|
||||
- On shutdown: cancel the task
|
||||
- Register new connections router
|
||||
- Register connections router
|
||||
|
||||
### 6. Connections Route — `web/routes/connections.py` (new)
|
||||
|
||||
`GET /connections` — requires auth, builds a list of connection descriptors:
|
||||
**`GET /connections`** — Main dashboard. Requires auth.
|
||||
|
||||
Queries both `EvotorConnection` and `VkConnection` for the current user. Builds a list of available service types and their connection state:
|
||||
|
||||
```python
|
||||
connections = [{
|
||||
"name": "Эвотор",
|
||||
"icon": "bi-shop",
|
||||
"connected": bool,
|
||||
"is_online": bool,
|
||||
"last_checked_at": datetime | None,
|
||||
"details": store_name,
|
||||
"connect_url": "/evotor",
|
||||
"disconnect_url": "/evotor/disconnect",
|
||||
}]
|
||||
SERVICE_TYPES = [
|
||||
{"type": "evotor", "name": "Эвотор", "icon": "bi-shop", "connect_url": "/evotor", "disconnect_url": "/evotor/disconnect"},
|
||||
{"type": "vk", "name": "ВКонтакте", "icon": "bi-chat-dots", "connect_url": "/vk", "disconnect_url": "/vk/disconnect"},
|
||||
]
|
||||
```
|
||||
Future connections just append another dict — template stays generic.
|
||||
|
||||
### 7. Connections Template — `web/templates/connections.html` (new)
|
||||
For each type, attach the connection record (or None). Template renders based on state.
|
||||
|
||||
Card per connection showing:
|
||||
- Icon + name + optional details (store name)
|
||||
- Status: green `bi-circle-fill` (online), red `bi-circle-fill` (offline), grey `bi-circle` (not connected)
|
||||
- Action button: "Подключить" (link to connect_url) or "Отключить" (POST form to disconnect_url)
|
||||
- Card footer with last check timestamp
|
||||
**`GET /connections/add`** — "Add connection" page.
|
||||
|
||||
Shows only service types the user has NOT yet connected:
|
||||
- Card per available type with service name, icon, short description
|
||||
- "Подключить" button linking to the service's OAuth page (`/evotor` or `/vk`)
|
||||
- If all types already connected — message "Все доступные сервисы подключены"
|
||||
- Back link to `/connections`
|
||||
|
||||
**`POST /connections/delete?type=evotor|vk`** — Delete a connection.
|
||||
|
||||
Same as existing disconnect endpoints but accessed from the dashboard. Deletes the connection record, redirects to `/connections`.
|
||||
|
||||
(The existing `/evotor/disconnect` and `/vk/disconnect` routes remain as aliases.)
|
||||
|
||||
### 7. Templates
|
||||
|
||||
**`web/templates/connections.html`** — Dashboard:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Подключения [+ Добавить] │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ Card ─────────────────────────────────────┐ │
|
||||
│ │ 🏪 Эвотор ● (green) │ │
|
||||
│ │ Магазин "Чайная" │ │
|
||||
│ │ Последняя проверка: 06.03.2026 14:30 │ │
|
||||
│ │ │ │
|
||||
│ │ [Настроить] [Отключить] │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Card ─────────────────────────────────────┐ │
|
||||
│ │ 💬 ВКонтакте ● (green) │ │
|
||||
│ │ Иван Иванов │ │
|
||||
│ │ Последняя проверка: 06.03.2026 14:30 │ │
|
||||
│ │ │ │
|
||||
│ │ [Настроить] [Отключить] │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ (Нет подключений — нажмите «Добавить») │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Each connection card:
|
||||
- Icon + service name + status indicator (green/red/grey)
|
||||
- Details line (store name for Evotor, profile name for VK)
|
||||
- Last checked timestamp in card footer
|
||||
- "Настроить" button → links to service page (`/evotor` or `/vk`) for reconnect/details
|
||||
- "Отключить" button → POST to `/connections/delete?type=...` with confirmation
|
||||
|
||||
Empty state: message prompting user to add their first connection.
|
||||
|
||||
**`web/templates/connections_add.html`** — Add connection page:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Добавить подключение │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─ Card ─────────────────────────────────────┐ │
|
||||
│ │ 🏪 Эвотор │ │
|
||||
│ │ Подключите кассу Эвотор для синхронизации │ │
|
||||
│ │ каталога товаров. │ │
|
||||
│ │ [Подключить →] │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Card ─────────────────────────────────────┐ │
|
||||
│ │ 💬 ВКонтакте │ │
|
||||
│ │ Подключите аккаунт ВКонтакте для │ │
|
||||
│ │ публикации товаров в вашу группу. │ │
|
||||
│ │ [Подключить →] │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ← Вернуться к подключениям │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 8. Navbar Update — `web/templates/base.html`
|
||||
|
||||
Replace "Эвотор" link with "Подключения" → `/connections`.
|
||||
|
||||
### 9. Evotor Callback Update — `web/routes/evotor.py`
|
||||
### 9. Evotor/VK Callback Updates
|
||||
|
||||
On successful OAuth callback, set `is_online=True` and `last_checked_at=func.now()`.
|
||||
On successful OAuth callback in both `/evotor/callback` and `/vk/callback`:
|
||||
- Set `is_online=True` and `last_checked_at=now()`
|
||||
- Redirect to `/connections` (already done for Evotor)
|
||||
|
||||
### 10. Evotor Template Back Link — `web/templates/evotor.html`
|
||||
### 10. Evotor/VK Template Back Links
|
||||
|
||||
Change "Вернуться в личный кабинет" → "Вернуться к подключениям" linking to `/connections`.
|
||||
Change back links on `/evotor` and `/vk` pages: "Вернуться к подключениям" → `/connections`.
|
||||
|
||||
### 11. Delete Confirmation
|
||||
|
||||
The "Отключить" button on the dashboard uses a simple JS `confirm()` dialog: "Вы уверены, что хотите отключить {service name}?" before submitting the POST form.
|
||||
|
||||
## Files Summary
|
||||
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `web/models.py` | Modify — add 2 fields |
|
||||
| `web/config.py` | Modify — add interval setting |
|
||||
| `web/main.py` | Modify — lifespan + router |
|
||||
| `web/routes/evotor.py` | Modify — set online on callback |
|
||||
| `web/routes/connections.py` | Create |
|
||||
| `web/health_checker.py` | Create |
|
||||
| `web/templates/connections.html` | Create |
|
||||
| `web/templates/base.html` | Modify — navbar |
|
||||
| `web/templates/evotor.html` | Modify — back link |
|
||||
| `web/models.py` | Modify — add `is_online`, `last_checked_at` to both connection models |
|
||||
| `web/config.py` | Modify — add `HEALTH_CHECK_INTERVAL_SECONDS` |
|
||||
| `web/main.py` | Modify — lifespan + register connections router |
|
||||
| `web/routes/evotor.py` | Modify — set online on callback, redirect to /connections |
|
||||
| `web/routes/vk.py` | Modify — set online on callback, redirect to /connections |
|
||||
| `web/routes/connections.py` | Create — dashboard, add page, delete endpoint |
|
||||
| `web/health_checker.py` | Create — background checks for both Evotor and VK |
|
||||
| `web/templates/connections.html` | Create — dashboard with cards |
|
||||
| `web/templates/connections_add.html` | Create — add connection page |
|
||||
| `web/templates/base.html` | Modify — navbar link |
|
||||
| `web/templates/evotor.html` | Modify — back link to /connections |
|
||||
| `web/templates/vk.html` | Modify — back link to /connections |
|
||||
| Alembic migration | Create |
|
||||
|
||||
## Verification
|
||||
|
||||
1. Run `alembic upgrade head` to apply migration
|
||||
1. Run `alembic upgrade head`
|
||||
2. Start the app, verify background task logs appear
|
||||
3. Visit `/connections` — should show Evotor as disconnected (grey)
|
||||
4. Connect Evotor via `/evotor` — should redirect back, connections page shows green status
|
||||
5. Disconnect — status returns to grey
|
||||
6. Wait for health check interval or trigger manually — verify `is_online` and `last_checked_at` update
|
||||
3. Visit `/connections` — empty state, "Добавить" button visible
|
||||
4. Click "Добавить" → shows Evotor and VK as available services
|
||||
5. Add Evotor → goes through OAuth → returns to `/connections` with green status card
|
||||
6. Add VK → same flow → both connections visible
|
||||
7. Click "Добавить" again → shows "Все доступные сервисы подключены"
|
||||
8. Click "Отключить" on Evotor → confirmation dialog → connection removed → card disappears
|
||||
9. Click "Добавить" → Evotor is available again
|
||||
10. Wait for health check cycle → verify `is_online` and `last_checked_at` update on remaining connections
|
||||
|
||||
Reference in New Issue
Block a user