from datetime import datetime, timezone from fastapi import APIRouter, Depends, HTTPException, Header, Request, status from passlib.context import CryptContext from sqlalchemy.orm import Session from database import get_db from models.site import Site from schemas.site import HeartbeatRequest, HeartbeatResponse router = APIRouter() _pwd = CryptContext(schemes=["bcrypt"], deprecated="auto") @router.post("/", response_model=HeartbeatResponse) def heartbeat( body: HeartbeatRequest, request: Request, x_site_id: str = Header(..., alias="X-Site-ID"), x_site_key: str = Header(..., alias="X-Site-Key"), db: Session = Depends(get_db), ): site = db.query(Site).filter(Site.site_id == x_site_id).first() if not site or not _pwd.verify(x_site_key, site.secret_key_hash): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid site credentials") now = datetime.now(timezone.utc) site.last_seen_at = now site.last_seen_ip = request.client.host if request.client else None db.commit() licensed = site.is_active and (site.license_expires_at.replace(tzinfo=timezone.utc) > now) return HeartbeatResponse( licensed=licensed, locked=site.is_locked, lock_reason=site.lock_reason, expires_at=site.license_expires_at, )