diff --git a/app.py b/app.py index b0511c7..c888897 100644 --- a/app.py +++ b/app.py @@ -205,6 +205,10 @@ class CreateWorkspaceRequest(BaseModel): organization_id: str | None = None +class CloneWorkspaceRequest(BaseModel): + organization_id: str + + class CreateWebappRequest(BaseModel): name: str url: str @@ -2598,6 +2602,51 @@ def create_workspace(key: str, name: str, description: str | None, organization_ return {"id": str(workspace_id), "key": key, "name": name} +def clone_workspace(source_workspace_id: str, organization_id: str): + """ + "Diesen Standard übernehmen" (GUI-Umbau Phase 3, Nachtrag 11.09.2026): + live gefundene Lücke - Webapps sind an Besitz gebunden (siehe + fetch_sichtbaren_workspace() in der Kundenplattform), ein global + schreibgeschützter Standard-Workspace hat also nie eigene Webapps, + und eine Organisation ohne einen einzigen eigenen Workspace hatte + dadurch gar keinen Weg mehr zu Webapps (anders als das alte, aber + unsichere Verhalten, wo jede Organisation, die einen geteilten + Workspace NUTZTE, dort auch Webapps eintragen konnte - das hätte + Webapps zwischen fremden Organisationen sichtbar gemacht, die + zufällig denselben globalen Standard verwenden). + + Löst das auf, ohne den schreibgeschützten Standard selbst anzutasten: + kopiert die Merkmal-Zusammensetzung in einen neuen, der Organisation + gehörenden Workspace - der Standard bleibt für alle unverändert, + die Kopie ist von Anfang an voll editierbar (inkl. eigener Webapps). + """ + + quelle = fetch_workspace_detail(source_workspace_id) + + if quelle is None: + return None + + with get_database_connection() as conn: + with conn.cursor() as cur: + cur.execute("SELECT key FROM workspaces WHERE organization_id = %s", (organization_id,)) + vergebene_keys = {row[0] for row in cur.fetchall()} + + basis_key = f"{quelle['key']}-eigene-kopie" + neuer_key = basis_key + zaehler = 2 + while neuer_key in vergebene_keys: + neuer_key = f"{basis_key}-{zaehler}" + zaehler += 1 + + neuer_workspace = create_workspace( + neuer_key, f"{quelle['name']} (eigene Kopie)", quelle["description"], organization_id + ) + + for merkmal in quelle["merkmale"]: + add_merkmal_to_workspace(neuer_workspace["id"], merkmal["id"]) + + return neuer_workspace + def fetch_workspace_detail(workspace_id: str): with get_database_connection() as conn: with conn.cursor() as cur: @@ -4177,6 +4226,21 @@ def create_workspace_endpoint(payload: CreateWorkspaceRequest, authorization: st } +@app.post("/api/v1/workspaces/{workspace_id}/clone") +def clone_workspace_endpoint( + workspace_id: str, payload: CloneWorkspaceRequest, authorization: str | None = Header(default=None) +): + if not require_service_token(authorization): + return {"success": False, "error": "unauthorized", "message": "Fehlendes oder ungültiges Service-Token."} + + neuer_workspace = clone_workspace(workspace_id, payload.organization_id) + + if neuer_workspace is None: + return {"success": False, "error": "not_found", "message": "Workspace wurde nicht gefunden."} + + return {"success": True, "workspace": neuer_workspace} + + @app.get("/api/v1/workspaces/{workspace_id}") def get_workspace(workspace_id: str, authorization: str | None = Header(default=None)): if not require_service_token(authorization):