diff --git a/app.py b/app.py index 19b2d80..8b7703f 100644 --- a/app.py +++ b/app.py @@ -61,6 +61,10 @@ class AuftragSelectRequest(BaseModel): optionen: dict = {} +class CreateOrganizationRequest(BaseModel): + name: str + + def write_log(entry: dict) -> None: with LOG_PATH.open("a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n") @@ -599,6 +603,60 @@ def fetch_devices_for_organization(organization_id: str): for device_id, hostname, fingerprint, last_checkin in cur.fetchall() ] +def fetch_organizations(): + with get_database_connection() as conn: + with conn.cursor() as cur: + cur.execute( + "SELECT id, name, created_at FROM organizations ORDER BY name" + ) + return [ + {"id": str(org_id), "name": name, "created_at": created_at.isoformat()} + for org_id, name, created_at in cur.fetchall() + ] + +def create_organization(name: str): + organization_id = uuid4() + + with get_database_connection() as conn: + with conn.cursor() as cur: + cur.execute( + "INSERT INTO organizations (id, name) VALUES (%s, %s)", + (organization_id, name), + ) + + return {"id": str(organization_id), "name": name} + +def fetch_activation_codes(organization_id: str): + with get_database_connection() as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT code, active, created_at + FROM activation_codes + WHERE organization_id = %s + ORDER BY created_at DESC + """, + (organization_id,), + ) + return [ + {"code": code, "active": active, "created_at": created_at.isoformat()} + for code, active, created_at in cur.fetchall() + ] + +def create_activation_code(organization_id: str): + code = "-".join( + secrets.token_hex(3).upper()[i:i + 3] for i in range(0, 6, 3) + ) + + with get_database_connection() as conn: + with conn.cursor() as cur: + cur.execute( + "INSERT INTO activation_codes (code, organization_id) VALUES (%s, %s)", + (code, organization_id), + ) + + return {"code": code, "active": True} + @app.get("/health") def health(): return { @@ -904,6 +962,63 @@ def get_organization_devices( return {"success": True, "devices": fetch_devices_for_organization(organization_id)} +@app.get("/api/v1/organizations") +def list_organizations(authorization: str | None = Header(default=None)): + if not require_service_token(authorization): + return { + "success": False, + "error": "unauthorized", + "message": "Fehlendes oder ungültiges Service-Token.", + } + + return {"success": True, "organizations": fetch_organizations()} + + +@app.post("/api/v1/organizations") +def create_organization_endpoint( + payload: CreateOrganizationRequest, + authorization: str | None = Header(default=None), +): + if not require_service_token(authorization): + return { + "success": False, + "error": "unauthorized", + "message": "Fehlendes oder ungültiges Service-Token.", + } + + return {"success": True, "organization": create_organization(payload.name)} + + +@app.get("/api/v1/organizations/{organization_id}/activation-codes") +def list_activation_codes( + organization_id: str, + authorization: str | None = Header(default=None), +): + if not require_service_token(authorization): + return { + "success": False, + "error": "unauthorized", + "message": "Fehlendes oder ungültiges Service-Token.", + } + + return {"success": True, "activation_codes": fetch_activation_codes(organization_id)} + + +@app.post("/api/v1/organizations/{organization_id}/activation-codes") +def create_activation_code_endpoint( + organization_id: str, + authorization: str | None = Header(default=None), +): + if not require_service_token(authorization): + return { + "success": False, + "error": "unauthorized", + "message": "Fehlendes oder ungültiges Service-Token.", + } + + return {"success": True, "activation_code": create_activation_code(organization_id)} + + @app.get("/api/v1/devices/{device_id}/auftragskatalog") def get_device_auftragskatalog( device_id: str,