From fd03c16eca085423267c163137b28ccb60de8db0 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Wed, 25 Feb 2026 00:45:13 +0100
Subject: [PATCH] feat: multi-compose rebuild (Seafile), cancel endpoint, schedule router, project descriptor

---
 app/routers/services.py |   71 ++++++++++-------------------------
 1 files changed, 21 insertions(+), 50 deletions(-)

diff --git a/app/routers/services.py b/app/routers/services.py
index 7cdad19..56ca932 100644
--- a/app/routers/services.py
+++ b/app/routers/services.py
@@ -1,41 +1,17 @@
-import os
+import sys
 from typing import Any
 
-import yaml
 from fastapi import APIRouter, Depends, HTTPException, Query
 
 from app.auth import verify_token
 from app.ops_runner import run_command
 
+sys.path.insert(0, "/opt/infrastructure")
+from toolkit.descriptor import find as find_project  # noqa: E402
+
 router = APIRouter()
 
 _DOCKER = "docker"
-_REGISTRY_PATH = os.environ.get(
-    "REGISTRY_PATH",
-    "/opt/infrastructure/servers/hetzner-vps/registry.yaml",
-)
-
-# ---------------------------------------------------------------------------
-# Registry-based name prefix lookup (cached)
-# ---------------------------------------------------------------------------
-_prefix_cache: dict[str, str] | None = None
-
-
-def _load_prefixes() -> dict[str, str]:
-    """Load project -> name_prefix mapping from the ops registry."""
-    global _prefix_cache
-    if _prefix_cache is not None:
-        return _prefix_cache
-
-    try:
-        with open(_REGISTRY_PATH) as f:
-            data = yaml.safe_load(f)
-        _prefix_cache = {}
-        for proj_name, cfg in data.get("projects", {}).items():
-            _prefix_cache[proj_name] = cfg.get("name_prefix", proj_name)
-        return _prefix_cache
-    except Exception:
-        return {}
 
 
 # ---------------------------------------------------------------------------
@@ -76,33 +52,28 @@
     """
     Resolve the actual Docker container name from project/env/service.
 
-    Uses the ops registry name_prefix mapping and tries patterns in order:
-      1. {env}-{prefix}-{service}  (mdf, seriousletter: dev-mdf-mysql-UUID)
-      2. {prefix}-{service}        (ringsaday: ringsaday-website-UUID, coolify: coolify-db)
-      3. {prefix}-{env}            (ringsaday: ringsaday-dev-UUID)
-      4. exact {prefix}            (coolify infra: coolify)
+    Loads the project descriptor and expands container_prefix for the given
+    env (e.g. "{env}-mdf" -> "dev-mdf"), then tries:
+      1. {expanded_prefix}-{service}   e.g. dev-mdf-wordpress
+      2. exact match on expanded_prefix (infra containers with no service suffix)
     """
-    prefixes = _load_prefixes()
-    prefix = prefixes.get(project, project)
+    desc = find_project(project)
+    if desc is None:
+        raise HTTPException(
+            status_code=404,
+            detail=f"Project '{project}' not found",
+        )
 
-    # Pattern 1: {env}-{prefix}-{service}
-    hit = await _find_by_prefix(f"{env}-{prefix}-{service}")
+    expanded_prefix = desc.container_prefix_for(env)
+
+    # Pattern 1: {expanded_prefix}-{service}
+    hit = await _find_by_prefix(f"{expanded_prefix}-{service}")
     if hit:
         return hit
 
-    # Pattern 2: {prefix}-{service}
-    hit = await _find_by_prefix(f"{prefix}-{service}")
-    if hit:
-        return hit
-
-    # Pattern 3: {prefix}-{env}
-    hit = await _find_by_prefix(f"{prefix}-{env}")
-    if hit:
-        return hit
-
-    # Pattern 4: exact match when service == prefix (e.g., coolify)
-    if service == prefix:
-        hit = await _find_exact(prefix)
+    # Pattern 2: exact match on prefix (infrastructure containers, e.g. "coolify")
+    if service == expanded_prefix or service == desc.name:
+        hit = await _find_exact(expanded_prefix)
         if hit:
             return hit
 

--
Gitblit v1.3.1