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:
102
web/routes/sync.py
Normal file
102
web/routes/sync.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter, Request, Depends
|
||||
from fastapi.responses import RedirectResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from web.auth import get_current_user
|
||||
from web.database import get_db
|
||||
from web.models import User, EvotorConnection, VkConnection, SyncConfig, SyncFilter
|
||||
|
||||
router = APIRouter(prefix="/sync")
|
||||
templates = Jinja2Templates(directory="web/templates")
|
||||
|
||||
|
||||
def _get_or_create_sync_config(db: Session, user_id: int) -> SyncConfig:
|
||||
config = db.query(SyncConfig).filter(SyncConfig.user_id == user_id).first()
|
||||
if not config:
|
||||
config = SyncConfig(user_id=user_id, is_enabled=False)
|
||||
db.add(config)
|
||||
db.commit()
|
||||
db.refresh(config)
|
||||
return config
|
||||
|
||||
|
||||
def _filter_summary(config: SyncConfig) -> dict:
|
||||
stores = [f for f in config.filters if f.entity_type == "store"]
|
||||
groups = [f for f in config.filters if f.entity_type == "group"]
|
||||
products = [f for f in config.filters if f.entity_type == "product"]
|
||||
return {
|
||||
"stores": len(stores),
|
||||
"groups": len(groups),
|
||||
"products": len(products),
|
||||
"total": len(config.filters),
|
||||
}
|
||||
|
||||
|
||||
@router.get("")
|
||||
def sync_page(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
user: User | None = Depends(get_current_user),
|
||||
):
|
||||
if not user:
|
||||
return RedirectResponse("/login", 303)
|
||||
|
||||
evotor = db.query(EvotorConnection).filter(EvotorConnection.user_id == user.id).first()
|
||||
vk = db.query(VkConnection).filter(VkConnection.user_id == user.id).first()
|
||||
config = _get_or_create_sync_config(db, user.id)
|
||||
summary = _filter_summary(config)
|
||||
|
||||
if config.confirmed_at and config.is_enabled:
|
||||
status = "active"
|
||||
elif config.confirmed_at and not config.is_enabled:
|
||||
status = "paused"
|
||||
elif summary["total"] > 0:
|
||||
status = "pending"
|
||||
else:
|
||||
status = "unconfigured"
|
||||
|
||||
return templates.TemplateResponse("sync.html", {
|
||||
"request": request,
|
||||
"user": user,
|
||||
"evotor": evotor,
|
||||
"vk": vk,
|
||||
"config": config,
|
||||
"summary": summary,
|
||||
"status": status,
|
||||
})
|
||||
|
||||
|
||||
@router.post("/toggle")
|
||||
def sync_toggle(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
user: User | None = Depends(get_current_user),
|
||||
):
|
||||
if not user:
|
||||
return RedirectResponse("/login", 303)
|
||||
|
||||
config = _get_or_create_sync_config(db, user.id)
|
||||
config.is_enabled = not config.is_enabled
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse("/sync", 303)
|
||||
|
||||
|
||||
@router.post("/confirm")
|
||||
def sync_confirm(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
user: User | None = Depends(get_current_user),
|
||||
):
|
||||
if not user:
|
||||
return RedirectResponse("/login", 303)
|
||||
|
||||
config = _get_or_create_sync_config(db, user.id)
|
||||
if config.is_enabled and len(config.filters) > 0:
|
||||
config.confirmed_at = datetime.utcnow()
|
||||
db.commit()
|
||||
|
||||
return RedirectResponse("/sync", 303)
|
||||
Reference in New Issue
Block a user