feat(settings): add PATCH env + Coolify redeploy endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-04 06:26:00 +00:00
parent c30c987ec2
commit 2fe73fcce1

View File

@@ -2672,6 +2672,107 @@ async def api_mcp_env():
}
class McpEnvUpdateRequest(BaseModel):
value: Any
@app.patch("/api/settings/mcp/env/{key}")
async def api_mcp_env_update(key: str, req: McpEnvUpdateRequest):
"""Update a non-secret env var in Infisical. Requires redeploy to take effect."""
spec = ENV_CATALOG.get(key)
if spec is None:
raise HTTPException(404, f"Unknown env key: {key}")
if spec.is_secret:
raise HTTPException(400, f"Cannot edit secret: {key}")
if not spec.is_editable:
raise HTTPException(400, f"Read-only: {key}")
try:
coerced = coerce(spec, req.value)
except ValueError as e:
raise HTTPException(400, str(e))
client = _infisical_client()
if client is None:
raise HTTPException(503, "Infisical not configured")
project_id, env, path = _infisical_ctx()
str_value = "true" if coerced is True else (
"false" if coerced is False else str(coerced)
)
try:
# SDK pattern: try update, fall back to create if missing.
# NOTE: exact method may vary by infisical-python version. The
# canonical method is `update_secret_by_name`; if your version
# uses `secrets.update`, replace accordingly.
try:
client.update_secret_by_name(
project_id=project_id,
environment_slug=env,
secret_path=path,
secret_name=key,
secret_value=str_value,
)
except Exception:
client.create_secret_by_name(
project_id=project_id,
environment_slug=env,
secret_path=path,
secret_name=key,
secret_value=str_value,
)
except Exception as e:
logger.exception("infisical_write_failed key=%s", key)
raise HTTPException(502, f"Infisical write failed: {e}")
logger.info(
"mcp_env_update key=%s value=%s",
key,
"[masked]" if spec.is_secret else str_value,
)
return {
"ok": True,
"key": key,
"saved_value": str_value,
"requires_redeploy": True,
"message": "נשמר ב-Infisical. נדרש redeploy כדי שיכנס לתוקף בקונטיינר.",
}
@app.post("/api/settings/mcp/env/redeploy")
async def api_mcp_env_redeploy():
"""Trigger Coolify redeploy of the legal-ai app."""
import httpx
coolify_url = os.environ.get("COOLIFY_URL", "http://158.178.131.193:8000")
coolify_token = os.environ.get("COOLIFY_API_TOKEN", "")
app_uuid = os.environ.get("COOLIFY_APP_UUID", "gyjo0mtw2c42ej3xxvbz8zio")
if not coolify_token:
raise HTTPException(503, "COOLIFY_API_TOKEN not configured")
async with httpx.AsyncClient(timeout=30.0) as http:
try:
resp = await http.post(
f"{coolify_url}/api/v1/deploy",
params={"uuid": app_uuid, "force": "false"},
headers={"Authorization": f"Bearer {coolify_token}"},
)
except Exception as e:
raise HTTPException(502, f"Coolify unreachable: {e}")
if resp.status_code >= 400:
raise HTTPException(
502, f"Coolify deploy failed: {resp.status_code} {resp.text}"
)
data = resp.json() if resp.content else {}
deployment_uuid = (
data.get("deployment_uuid")
or (data.get("deployments") or [{}])[0].get("deployment_uuid")
)
logger.info("mcp_env_redeploy triggered uuid=%s", deployment_uuid)
return {
"ok": True,
"deployment_uuid": deployment_uuid,
"message": "Redeploy הופעל. הקונטיינר יחזור תוך 2-4 דקות.",
}
# ── Settings: Tag → Company Mappings ──────────────────────────────
@app.get("/api/settings/paperclip-companies")