feat(P2.1): install index libraries by WARMING the shared cache (not the global dir)
New OSS warm_library hook; /api/libraries/install now calls it (with the requester id for the anon policy) instead of mutating the shared global libraries volume — so that volume stops growing and can be retired (P2.5). Falls back to the legacy arduino-cli global install when no overlay is loaded (OSS self-host parity).
This commit is contained in:
parent
97f390719f
commit
02d396318f
|
|
@ -1,6 +1,7 @@
|
|||
from fastapi import APIRouter, HTTPException, Query
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
from app.api.routes.compile import arduino_cli
|
||||
from app.core.hooks import get_current_user_id, warm_library
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
|
@ -36,11 +37,30 @@ async def search_libraries(q: str = Query(..., description="Search query for lib
|
|||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/install", response_model=InstallResponse)
|
||||
async def install_library(request: InstallLibraryRequest):
|
||||
async def install_library(
|
||||
request: InstallLibraryRequest,
|
||||
requester_id: str | None = Depends(get_current_user_id),
|
||||
):
|
||||
"""
|
||||
Install a specific Arduino library by name.
|
||||
|
||||
On velxio.dev this WARMS the shared content-addressed cache (the global
|
||||
mutable libraries volume is being retired) rather than mutating that volume;
|
||||
the overlay also enforces the anonymous policy. With no overlay (OSS
|
||||
self-host) it falls back to the legacy arduino-cli global install.
|
||||
"""
|
||||
try:
|
||||
# P2.1 — prefer warming the content-addressed cache (no global write).
|
||||
warmed = await warm_library(request.name, request.version, requester_id)
|
||||
if warmed is not None:
|
||||
return InstallResponse(
|
||||
success=bool(warmed.get("success")),
|
||||
error=warmed.get("error"),
|
||||
stdout=warmed.get("stdout"),
|
||||
fallback=warmed.get("fallback"),
|
||||
requested_version=warmed.get("requested_version"),
|
||||
)
|
||||
# OSS / no overlay: legacy global install (self-host parity).
|
||||
spec = f"{request.name}@{request.version}" if request.version else request.name
|
||||
result = await arduino_cli.install_library(spec)
|
||||
if not result["success"]:
|
||||
|
|
|
|||
|
|
@ -202,6 +202,41 @@ async def get_project_owner(project_id: Optional[str]) -> Optional[str]:
|
|||
return None
|
||||
|
||||
|
||||
# ── warm_library ──────────────────────────────────────────────────────────────
|
||||
# "Install" an index library by WARMING the shared content-addressed cache
|
||||
# (install into a throwaway sketchbook -> publish to the cache) instead of
|
||||
# mutating the single shared global libraries volume — so the global dir stops
|
||||
# growing and can be retired. `requester_id` enforces the anon policy (an
|
||||
# anonymous user may only use libraries already referenced by an example/project,
|
||||
# i.e. already cached; warming a fresh uncached lib requires sign-in). Returns a
|
||||
# result dict ({success, error?, ...}) or None when no overlay is loaded -> the
|
||||
# OSS route falls back to its legacy arduino-cli global install (self-host parity).
|
||||
|
||||
WarmLibraryHook = Callable[..., Awaitable[Optional[dict]]]
|
||||
|
||||
_warm_library_hook: Optional[WarmLibraryHook] = None
|
||||
|
||||
|
||||
def register_warm_library(hook: WarmLibraryHook) -> None:
|
||||
"""Install the cache-warm library installer. Called by overlays in register_pro."""
|
||||
global _warm_library_hook
|
||||
_warm_library_hook = hook
|
||||
|
||||
|
||||
async def warm_library(
|
||||
name: str, version: Optional[str] = None, requester_id: Optional[str] = None
|
||||
) -> Optional[dict]:
|
||||
"""Warm an index library into the shared cache. None -> no overlay (the OSS
|
||||
route does its legacy global install). Never raises."""
|
||||
if _warm_library_hook is None:
|
||||
return None
|
||||
try:
|
||||
return await _warm_library_hook(name=name, version=version, requester_id=requester_id)
|
||||
except Exception:
|
||||
logger.exception("warm_library hook failed")
|
||||
return {"success": False, "error": "Library install failed."}
|
||||
|
||||
|
||||
# ── lifespan startup ──────────────────────────────────────────────────────────
|
||||
# Overlays that need to run async setup during FastAPI lifespan (DB init,
|
||||
# table creation, legacy column migrations, etc.) register a coroutine here.
|
||||
|
|
|
|||
Loading…
Reference in New Issue