# SonicForge System API — capabilities: frontend gọi 1 lần lúc boot để biết # môi trường (desktop Windows / docker headless) và bật/tắt tính năng tương ứng. from fastapi import APIRouter, HTTPException, Depends from pydantic import BaseModel from typing import Optional from app.core.runtime import capabilities, save_carla_path from app.api.v1.auth import get_current_user, enforce_password_changed router = APIRouter() @router.get("/capabilities") async def get_capabilities(): """Khả năng của môi trường hiện tại (public — cần trước khi đăng nhập). - runtime: "desktop" (server + client cùng 1 máy Windows/macOS) | "headless" (docker server + browser UI) - features.carla_local: có Carla trên máy này → hiện nút "Mở trong Carla" - features.preset_upload: luôn True (upload .vstpreset qua web UI) - features.preview_mode: "quick_render" (pedalboard render clip ngắn — âm thật giống export) | "wasm" (Preview Synth trong browser) """ return capabilities() class CarlaPathRequest(BaseModel): """Định vị Carla (bản portable zip không cài đặt/PATH). Chấp nhận đường dẫn tới carla.exe HOẶC thư mục chứa carla.exe — resolve và lưu config.""" carla_path: str carla_dir: Optional[str] = None # tương thích ngược: tên cũ của carla_path @router.post("/carla-path") async def set_carla_path(req: CarlaPathRequest, current_user: dict = Depends(get_current_user)): """Lưu vị trí carla.exe do user chọn (Plugin Manager → Định vị Carla...). Cần thiết vì bản Carla Windows là bộ file zip portable — không có installer cũng không dùng biến môi trường PATH, nên heuristic không tìm thấy.""" enforce_password_changed(current_user) target = (req.carla_path or req.carla_dir or "").strip() if not target: raise HTTPException(status_code=400, detail="Thiếu đường dẫn Carla") exe = save_carla_path(target) if not exe: raise HTTPException( status_code=400, detail="Không tìm thấy carla.exe trong đường dẫn đã chọn. Hãy chọn " "thư mục chứa carla.exe (bản portable giải nén) hoặc chính file carla.exe.", ) return {"success": True, "carla_path": exe, **capabilities()}