Replace the entire Python/FastAPI backend with a Node.js/TypeScript stack: - Framework: Hono + @hono/node-server - Templates: Nunjucks (.njk) replacing Jinja2 (.html) - ORM: Drizzle ORM with mysql2 (same MariaDB schema, no migrations needed) - Sessions: hono-sessions with CookieStore - CSS: Pico CSS v2 replacing Bootstrap 5 (Bootstrap Icons CDN kept) - Dev: tsx watch; Prod: tsc + node dist/index.js Original Python app preserved in web-python/ as backup. Updated Dockerfile.web and docker-compose.yml for Node.js deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
2.8 KiB
Python
102 lines
2.8 KiB
Python
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Request, Depends
|
|
from fastapi.responses import RedirectResponse
|
|
from web.templates_env import templates
|
|
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")
|
|
|
|
|
|
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)
|