feat(settings): add PATCH env + Coolify redeploy endpoints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
101
web/app.py
101
web/app.py
@@ -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 ──────────────────────────────
|
# ── Settings: Tag → Company Mappings ──────────────────────────────
|
||||||
|
|
||||||
@app.get("/api/settings/paperclip-companies")
|
@app.get("/api/settings/paperclip-companies")
|
||||||
|
|||||||
Reference in New Issue
Block a user