from datetime import datetime, timezone from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse, RedirectResponse from sqlalchemy.orm import Session from web.auth.session import get_current_user from web.config import settings from web.database import get_db from web.models.connections import CachedGroup, CachedProduct, CachedStore, SyncConfig, SyncFilter from web.templates_env import templates router = APIRouter() def _get_or_create_sync_config(db: Session, user_id: int) -> SyncConfig: cfg = db.query(SyncConfig).filter_by(user_id=user_id).first() if not cfg: cfg = SyncConfig(user_id=user_id, is_enabled=True) db.add(cfg) db.flush() return cfg def _enabled_store_ids(db: Session, user_id: int) -> set[str] | None: """Return set of enabled store evotor_ids, or None if no filters set (all enabled).""" cfg = db.query(SyncConfig).filter_by(user_id=user_id).first() if not cfg: return None filters = db.query(SyncFilter).filter_by( sync_config_id=cfg.id, entity_type="store", filter_mode="include" ).all() if not filters: return None return {f.entity_id for f in filters} def _enabled_group_ids(db: Session, user_id: int, store_evotor_id: str) -> set[str] | None: """Return set of enabled group evotor_ids for a store, or None if all enabled.""" cfg = db.query(SyncConfig).filter_by(user_id=user_id).first() if not cfg: return None filters = db.query(SyncFilter).filter_by( sync_config_id=cfg.id, entity_type="group", filter_mode="include", parent_entity_id=store_evotor_id, ).all() if not filters: return None return {f.entity_id for f in filters} def _render(request: Request, template: str, ctx: dict) -> HTMLResponse: ctx["request"] = request ctx.setdefault("jivosite_widget_id", settings.JIVOSITE_WIDGET_ID) return templates.TemplateResponse(ctx.pop("request"), template, ctx) @router.get("/catalog/stores") async def catalog_stores(request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) stores = ( db.query(CachedStore) .filter(CachedStore.user_id == user.id) .order_by(CachedStore.name) .all() ) enabled_ids = _enabled_store_ids(db, user.id) return _render(request, "catalog/stores.html", { "user": user, "stores": stores, "enabled_ids": enabled_ids, # None = all enabled, set = explicit list "refresh_interval": settings.CATALOG_REFRESH_INTERVAL_SECONDS, }) @router.get("/catalog/stores/{store_evotor_id}/groups") async def catalog_groups(store_evotor_id: str, request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) store = ( db.query(CachedStore) .filter(CachedStore.user_id == user.id, CachedStore.evotor_id == store_evotor_id) .first() ) if not store: return RedirectResponse("/catalog/stores", 303) groups = ( db.query(CachedGroup) .filter(CachedGroup.user_id == user.id, CachedGroup.store_evotor_id == store_evotor_id) .order_by(CachedGroup.name) .all() ) enabled_ids = _enabled_group_ids(db, user.id, store_evotor_id) return _render(request, "catalog/groups.html", { "user": user, "store": store, "groups": groups, "enabled_ids": enabled_ids, }) @router.get("/catalog/stores/{store_evotor_id}/products") async def catalog_products(store_evotor_id: str, request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) store = ( db.query(CachedStore) .filter(CachedStore.user_id == user.id, CachedStore.evotor_id == store_evotor_id) .first() ) if not store: return RedirectResponse("/catalog/stores", 303) group_id = request.query_params.get("group") q = db.query(CachedProduct).filter( CachedProduct.user_id == user.id, CachedProduct.store_evotor_id == store_evotor_id, ) if group_id: q = q.filter(CachedProduct.group_evotor_id == group_id) products = q.order_by(CachedProduct.name).all() groups = ( db.query(CachedGroup) .filter(CachedGroup.user_id == user.id, CachedGroup.store_evotor_id == store_evotor_id) .order_by(CachedGroup.name) .all() ) return _render(request, "catalog/products.html", { "user": user, "store": store, "products": products, "groups": groups, "group_id": group_id, }) @router.post("/catalog/stores/{store_evotor_id}/toggle") async def catalog_store_toggle(store_evotor_id: str, request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) cfg = _get_or_create_sync_config(db, user.id) # If no filters exist yet, that means all stores are implicitly enabled. # Toggling one store OFF means we create include-filters for all OTHER stores. existing = db.query(SyncFilter).filter_by( sync_config_id=cfg.id, entity_type="store", filter_mode="include" ).all() existing_ids = {f.entity_id for f in existing} if store_evotor_id in existing_ids: # Currently enabled → disable: remove its filter db.query(SyncFilter).filter_by( sync_config_id=cfg.id, entity_type="store", entity_id=store_evotor_id, filter_mode="include", ).delete() else: if not existing_ids: # First toggle: seed include-filters for all OTHER stores all_stores = db.query(CachedStore).filter_by(user_id=user.id).all() now = datetime.now(timezone.utc).replace(tzinfo=None) for s in all_stores: if s.evotor_id == store_evotor_id: continue db.add(SyncFilter( sync_config_id=cfg.id, entity_type="store", entity_id=s.evotor_id, entity_name=s.name, filter_mode="include", created_at=now, )) else: # Re-enable: add its filter back db.add(SyncFilter( sync_config_id=cfg.id, entity_type="store", entity_id=store_evotor_id, filter_mode="include", created_at=datetime.now(timezone.utc).replace(tzinfo=None), )) db.commit() return RedirectResponse("/catalog/stores", 303) @router.post("/catalog/stores/{store_evotor_id}/groups/{group_evotor_id}/toggle") async def catalog_group_toggle( store_evotor_id: str, group_evotor_id: str, request: Request, db: Session = Depends(get_db), ): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) cfg = _get_or_create_sync_config(db, user.id) existing = db.query(SyncFilter).filter_by( sync_config_id=cfg.id, entity_type="group", filter_mode="include", parent_entity_id=store_evotor_id, ).all() existing_ids = {f.entity_id for f in existing} if group_evotor_id in existing_ids: db.query(SyncFilter).filter_by( sync_config_id=cfg.id, entity_type="group", entity_id=group_evotor_id, filter_mode="include", ).delete() else: if not existing_ids: # First toggle: seed include-filters for all OTHER groups in this store all_groups = db.query(CachedGroup).filter_by( user_id=user.id, store_evotor_id=store_evotor_id, ).all() now = datetime.now(timezone.utc).replace(tzinfo=None) for g in all_groups: if g.evotor_id == group_evotor_id: continue db.add(SyncFilter( sync_config_id=cfg.id, entity_type="group", entity_id=g.evotor_id, entity_name=g.name, filter_mode="include", parent_entity_id=store_evotor_id, created_at=now, )) else: db.add(SyncFilter( sync_config_id=cfg.id, entity_type="group", entity_id=group_evotor_id, filter_mode="include", parent_entity_id=store_evotor_id, created_at=datetime.now(timezone.utc).replace(tzinfo=None), )) db.commit() return RedirectResponse(f"/catalog/stores/{store_evotor_id}/groups", 303)