140 lines
4.1 KiB
Python
140 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
from sqlmodel import Session
|
|
|
|
from ..db.session import get_session
|
|
from ..services.audit_logger import log_event
|
|
from ..services.policy_engine import evaluate_policy
|
|
from ..services.privatemuse_artifact_installer import (
|
|
ArtifactInstallError,
|
|
install,
|
|
validate_request,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/privatemuse/artifacts")
|
|
|
|
|
|
class ArtifactInstallRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
artifact_type: str = Field(pattern="^(lora|checkpoint|unet|vae|clip|gguf)$")
|
|
source_url: str = Field(min_length=9, max_length=2048)
|
|
sha256: str = Field(pattern="^[0-9a-f]{64}$")
|
|
filename: str = Field(min_length=13, max_length=192)
|
|
source_id: str = Field(min_length=1, max_length=240)
|
|
license: str = Field(min_length=1, max_length=240)
|
|
reason: str = Field(min_length=1, max_length=1000)
|
|
|
|
|
|
@router.post("/preflight")
|
|
def preflight(
|
|
payload: ArtifactInstallRequest,
|
|
session: Session = Depends(get_session),
|
|
):
|
|
action = "privatemuse.comfyui.install_artifact"
|
|
target = "privatemuse/comfyui"
|
|
decision = evaluate_policy(
|
|
session,
|
|
action=action,
|
|
target=target,
|
|
source="manual",
|
|
)
|
|
try:
|
|
normalized = validate_request(payload.model_dump())
|
|
except ArtifactInstallError as exc:
|
|
raise HTTPException(422, str(exc)) from exc
|
|
return {
|
|
"ready": decision["decision"] != "blocked",
|
|
"policy": decision,
|
|
"artifact": {
|
|
"artifact_type": normalized["artifact_type"],
|
|
"filename": normalized["filename"],
|
|
"sha256": normalized["sha256"],
|
|
"source_id": normalized["source_id"],
|
|
"source_host": normalized["source_url"].split("/")[2],
|
|
"license": normalized["license"],
|
|
},
|
|
}
|
|
|
|
|
|
@router.post("/install")
|
|
def install_artifact(
|
|
payload: ArtifactInstallRequest,
|
|
session: Session = Depends(get_session),
|
|
):
|
|
action = "privatemuse.comfyui.install_artifact"
|
|
target = "privatemuse/comfyui"
|
|
decision = evaluate_policy(
|
|
session,
|
|
action=action,
|
|
target=target,
|
|
source="manual",
|
|
)
|
|
if decision["decision"] != "allowed":
|
|
raise HTTPException(
|
|
409,
|
|
"Artifact installation requires an explicitly allowed policy/approved execution context.",
|
|
)
|
|
model_root = Path(
|
|
os.environ.get(
|
|
"APPOPS_PRIVATEMUSE_COMFYUI_MODEL_ROOT",
|
|
"/mnt/privatemuse-comfyui-models",
|
|
)
|
|
)
|
|
receipt_root = Path(
|
|
os.environ.get(
|
|
"APPOPS_PRIVATEMUSE_ARTIFACT_RECEIPT_ROOT",
|
|
"/data/privatemuse-artifact-receipts",
|
|
)
|
|
)
|
|
try:
|
|
result = install(
|
|
payload.model_dump(),
|
|
model_root=model_root,
|
|
receipt_root=receipt_root,
|
|
)
|
|
except ArtifactInstallError as exc:
|
|
log_event(
|
|
session,
|
|
source="action",
|
|
identity="manual",
|
|
action_id=action,
|
|
target=target,
|
|
risk="high",
|
|
policy_decision="allowed",
|
|
reason=payload.reason,
|
|
result="failed",
|
|
redacted_metadata={
|
|
"filename": payload.filename,
|
|
"sha256": payload.sha256,
|
|
"source_id": payload.source_id,
|
|
},
|
|
)
|
|
raise HTTPException(409, str(exc)) from exc
|
|
audit = log_event(
|
|
session,
|
|
source="action",
|
|
identity="manual",
|
|
action_id=action,
|
|
target=target,
|
|
risk="high",
|
|
policy_decision="allowed",
|
|
reason=payload.reason,
|
|
result="success",
|
|
after_state={
|
|
"status": result["status"],
|
|
"sha256": result["sha256"],
|
|
"bytes": result["bytes"],
|
|
},
|
|
redacted_metadata={
|
|
"filename": payload.filename,
|
|
"source_id": payload.source_id,
|
|
"license": payload.license,
|
|
},
|
|
)
|
|
return {**result, "auditId": audit.id}
|