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:
mguschin
2026-03-06 16:08:19 +03:00
parent cfc7229daf
commit 9aeef73b10
20 changed files with 2010 additions and 85 deletions

View File

@@ -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