feat: Organisationen + Aktivierungscodes verwalten (Kundenplattform-Admin)
Neue Endpoints GET/POST /api/v1/organizations und GET/POST
/api/v1/organizations/{id}/activation-codes für den neuen Kunden-
Management-Bereich der Kundenplattform. Aktivierungscodes werden
serverseitig generiert (kein manuelles Ausdenken nötig).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
1e9020c5ba
commit
e043233aea
115
app.py
115
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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user