| """ |
| API routes for version checking and update management. |
| """ |
|
|
| from datetime import datetime |
| from typing import Any |
|
|
| import logfire |
| from fastapi import APIRouter, Depends, Header, HTTPException, Response |
| from pydantic import BaseModel |
|
|
| from ..auth.dependencies import requires_permission |
| from ..auth.permissions import TASK_READ_TEAM |
| from ..config.version import ARCHON_VERSION |
| from ..services.version_service import version_service |
| from ..utils.etag_utils import check_etag, generate_etag |
|
|
|
|
| |
| class ReleaseAsset(BaseModel): |
| """Represents a downloadable asset from a release.""" |
|
|
| name: str |
| size: int |
| download_count: int |
| browser_download_url: str |
| content_type: str |
|
|
|
|
| class VersionCheckResponse(BaseModel): |
| """Version check response with update information.""" |
|
|
| current: str |
| latest: str | None |
| update_available: bool |
| release_url: str | None |
| release_notes: str | None |
| published_at: datetime | None |
| check_error: str | None = None |
| assets: list[dict[str, Any]] | None = None |
| author: str | None = None |
|
|
|
|
| class CurrentVersionResponse(BaseModel): |
| """Simple current version response.""" |
|
|
| version: str |
| timestamp: datetime |
|
|
|
|
| |
| router = APIRouter(prefix="/api/version", tags=["version"]) |
|
|
|
|
| @router.get("/documents", response_model=list[dict[str, Any]]) |
| async def get_document_versions(limit: int = 50, current_user: dict = Depends(requires_permission(TASK_READ_TEAM))): |
| """ |
| Fetch historical versions of project documents. Requires TASK_READ_TEAM. |
| """ |
| try: |
| from ..utils import get_supabase_client |
|
|
| supabase = get_supabase_client() |
|
|
| |
| res = ( |
| supabase.table("archon_document_versions").select("*").order("created_at", desc=True).limit(limit).execute() |
| ) |
|
|
| return res.data or [] |
| except Exception as e: |
| logfire.error(f"Error fetching document versions: {e}") |
| raise HTTPException(status_code=500, detail=str(e)) from e |
|
|
|
|
| @router.get("/check", response_model=VersionCheckResponse) |
| async def check_for_updates(response: Response, if_none_match: str | None = Header(None)): |
| """ |
| Check for available Archon updates. |
| |
| Queries GitHub releases API to determine if a newer version is available. |
| Results are cached for 1 hour to avoid rate limiting. |
| |
| Returns: |
| Version information including current, latest, and update availability |
| """ |
| try: |
| |
| result = await version_service.check_for_updates() |
|
|
| |
| etag = generate_etag(result) |
|
|
| |
| if check_etag(if_none_match, etag): |
| |
| response.status_code = 304 |
| response.headers["ETag"] = f'"{etag}"' |
| response.headers["Cache-Control"] = "no-cache, must-revalidate" |
| return Response(status_code=304) |
| else: |
| |
| response.headers["ETag"] = f'"{etag}"' |
| response.headers["Cache-Control"] = "no-cache, must-revalidate" |
| return VersionCheckResponse(**result) |
|
|
| except Exception as e: |
| logfire.error(f"Error checking for updates: {e}") |
| |
| return VersionCheckResponse( |
| current=ARCHON_VERSION, |
| latest=None, |
| update_available=False, |
| release_url=None, |
| release_notes=None, |
| published_at=None, |
| check_error=str(e), |
| ) |
|
|
|
|
| @router.get("/current", response_model=CurrentVersionResponse) |
| async def get_current_version(): |
| """ |
| Get the current Archon version. |
| |
| Simple endpoint that returns the installed version without checking for updates. |
| """ |
| return CurrentVersionResponse(version=ARCHON_VERSION, timestamp=datetime.now()) |
|
|
|
|
| @router.post("/clear-cache") |
| async def clear_version_cache(): |
| """ |
| Clear the version check cache. |
| |
| Forces the next version check to query GitHub API instead of using cached data. |
| Useful for testing or forcing an immediate update check. |
| """ |
| try: |
| version_service.clear_cache() |
| return {"message": "Version cache cleared successfully", "success": True} |
| except Exception as e: |
| logfire.error(f"Error clearing version cache: {e}") |
| raise HTTPException(status_code=500, detail=f"Failed to clear cache: {str(e)}") from e |
|
|