cc4c3fcecb
- Backend: Customer/Satellite Models, customer_id auf Server/Job/Audit - Satellite-API: heartbeat, poll (atomares Claiming), logs, result, scan-result, health-report - Auth via X-Api-Key (SHA-256 gehasht) - Job-Queue: pending/claimed/running/success/failed + Stale-Janitor - Batch-Trigger: ein Job pro Server, Satellite arbeitet sequenziell ab - Credentials bleiben lokal: nur symbolische credential_ref zentral - Neues Paket satellite/: Pull-Loop, WinRM/SSH/CAU/Scanner, PyInstaller-tauglich - Frontend: Kunden-Switcher, Satelliten-View, Polling statt WebSocket - Entfernt: WebSocket/Socket.io, Redis, zentrale Credentials, JobRunner - Docs: README/AGENTS/PROMPT auf neue Architektur aktualisiert
106 lines
3.2 KiB
Python
106 lines
3.2 KiB
Python
"""Customer routes (tenant management)."""
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.api.deps import client_ip, get_current_user
|
|
from app.core.database import get_db
|
|
from app.core.exceptions import ConflictError, NotFoundError
|
|
from app.models.customer import Customer
|
|
from app.models.user import User
|
|
from app.schemas.customer import CustomerCreate, CustomerRead, CustomerUpdate
|
|
from app.services.audit import AuditService
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("", response_model=list[CustomerRead])
|
|
async def list_customers(
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: User = Depends(get_current_user),
|
|
) -> list[Customer]:
|
|
result = await db.execute(select(Customer).order_by(Customer.name))
|
|
return list(result.scalars().all())
|
|
|
|
|
|
@router.post("", response_model=CustomerRead, status_code=201)
|
|
async def create_customer(
|
|
payload: CustomerCreate,
|
|
request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
) -> Customer:
|
|
existing = await db.execute(
|
|
select(Customer).where((Customer.name == payload.name) | (Customer.slug == payload.slug))
|
|
)
|
|
if existing.scalar_one_or_none():
|
|
raise ConflictError("Kunde mit diesem Namen oder Slug existiert bereits")
|
|
|
|
customer = Customer(**payload.model_dump())
|
|
db.add(customer)
|
|
await db.flush()
|
|
await AuditService(db).log(
|
|
username=user.username,
|
|
action="customer.create",
|
|
target=customer.name,
|
|
customer_id=customer.id,
|
|
ip_address=client_ip(request),
|
|
)
|
|
return customer
|
|
|
|
|
|
@router.get("/{customer_id}", response_model=CustomerRead)
|
|
async def get_customer(
|
|
customer_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: User = Depends(get_current_user),
|
|
) -> Customer:
|
|
customer = await db.get(Customer, customer_id)
|
|
if not customer:
|
|
raise NotFoundError("Kunde nicht gefunden")
|
|
return customer
|
|
|
|
|
|
@router.patch("/{customer_id}", response_model=CustomerRead)
|
|
async def update_customer(
|
|
customer_id: int,
|
|
payload: CustomerUpdate,
|
|
request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
) -> Customer:
|
|
customer = await db.get(Customer, customer_id)
|
|
if not customer:
|
|
raise NotFoundError("Kunde nicht gefunden")
|
|
for field, value in payload.model_dump(exclude_unset=True).items():
|
|
setattr(customer, field, value)
|
|
await AuditService(db).log(
|
|
username=user.username,
|
|
action="customer.update",
|
|
target=customer.name,
|
|
customer_id=customer.id,
|
|
ip_address=client_ip(request),
|
|
)
|
|
return customer
|
|
|
|
|
|
@router.delete("/{customer_id}", status_code=204)
|
|
async def delete_customer(
|
|
customer_id: int,
|
|
request: Request,
|
|
db: AsyncSession = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
) -> None:
|
|
customer = await db.get(Customer, customer_id)
|
|
if not customer:
|
|
raise NotFoundError("Kunde nicht gefunden")
|
|
await AuditService(db).log(
|
|
username=user.username,
|
|
action="customer.delete",
|
|
target=customer.name,
|
|
customer_id=customer.id,
|
|
ip_address=client_ip(request),
|
|
)
|
|
await db.delete(customer)
|