"""Lizenz-CRUD.""" from datetime import datetime, timedelta, timezone from fastapi import APIRouter, Depends, HTTPException, status from models import LicenseCreate, LicenseResponse from auth import get_current_admin from database import db_dependency import aiosqlite router = APIRouter(prefix="/api/licenses", tags=["licenses"]) @router.get("", response_model=list[LicenseResponse]) async def list_licenses( org_id: int = None, admin: dict = Depends(get_current_admin), db: aiosqlite.Connection = Depends(db_dependency), ): if org_id: cursor = await db.execute( "SELECT * FROM licenses WHERE organization_id = ? ORDER BY created_at DESC", (org_id,), ) else: cursor = await db.execute("SELECT * FROM licenses ORDER BY created_at DESC") return [dict(row) for row in await cursor.fetchall()] @router.post("", response_model=LicenseResponse, status_code=status.HTTP_201_CREATED) async def create_license( data: LicenseCreate, admin: dict = Depends(get_current_admin), db: aiosqlite.Connection = Depends(db_dependency), ): # Org pruefen cursor = await db.execute( "SELECT id FROM organizations WHERE id = ?", (data.organization_id,) ) if not await cursor.fetchone(): raise HTTPException(status_code=404, detail="Organisation nicht gefunden") # Bestehende aktive Lizenz widerrufen await db.execute( "UPDATE licenses SET status = 'revoked' WHERE organization_id = ? AND status = 'active'", (data.organization_id,), ) now = datetime.now(timezone.utc) valid_from = now.isoformat() valid_until = None if data.license_type == "permanent": valid_until = None elif data.duration_days: valid_until = (now + timedelta(days=data.duration_days)).isoformat() elif data.license_type == "trial": valid_until = (now + timedelta(days=14)).isoformat() elif data.license_type == "annual": valid_until = (now + timedelta(days=365)).isoformat() cursor = await db.execute( """INSERT INTO licenses (organization_id, license_type, max_users, valid_from, valid_until, status) VALUES (?, ?, ?, ?, ?, 'active')""", (data.organization_id, data.license_type, data.max_users, valid_from, valid_until), ) await db.commit() cursor = await db.execute("SELECT * FROM licenses WHERE id = ?", (cursor.lastrowid,)) return dict(await cursor.fetchone()) @router.put("/{license_id}/revoke") async def revoke_license( license_id: int, admin: dict = Depends(get_current_admin), db: aiosqlite.Connection = Depends(db_dependency), ): cursor = await db.execute("SELECT * FROM licenses WHERE id = ?", (license_id,)) lic = await cursor.fetchone() if not lic: raise HTTPException(status_code=404, detail="Lizenz nicht gefunden") await db.execute("UPDATE licenses SET status = 'revoked' WHERE id = ?", (license_id,)) await db.commit() return {"ok": True} @router.put("/{license_id}/extend") async def extend_license( license_id: int, days: int = 365, admin: dict = Depends(get_current_admin), db: aiosqlite.Connection = Depends(db_dependency), ): cursor = await db.execute("SELECT * FROM licenses WHERE id = ?", (license_id,)) lic = await cursor.fetchone() if not lic: raise HTTPException(status_code=404, detail="Lizenz nicht gefunden") if lic["valid_until"]: base = datetime.fromisoformat(lic["valid_until"]) else: base = datetime.now(timezone.utc) new_until = (base + timedelta(days=days)).isoformat() await db.execute( "UPDATE licenses SET valid_until = ?, status = 'active' WHERE id = ?", (new_until, license_id), ) await db.commit() return {"ok": True, "valid_until": new_until} @router.get("/expiring") async def get_expiring_licenses( days: int = 30, admin: dict = Depends(get_current_admin), db: aiosqlite.Connection = Depends(db_dependency), ): """Lizenzen die in den naechsten X Tagen ablaufen.""" cursor = await db.execute( """SELECT l.*, o.name as org_name FROM licenses l JOIN organizations o ON o.id = l.organization_id WHERE l.status = 'active' AND l.valid_until IS NOT NULL AND l.valid_until < datetime('now', '+' || ? || ' days') ORDER BY l.valid_until""", (days,), ) return [dict(row) for row in await cursor.fetchall()]