Initial commit – TimeMaster Zeiterfassung & HR-Tool
Stand: agent-06 (Audit-Log), agent-05 (Krankmeldung), agent-07 Phase 1 (Personalnummer), Busylight-Pull-Integration, TOTP/2FA, Abwesenheiten, Zeiterfassung, Kiosk-Grundgerüst. Migrations 0001–0023 deployed auf 192.168.1.137 + .164. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
SMTP-Konfiguration pro Firma.
|
||||
Nur COMPANY_ADMIN / SUPER_ADMIN darf lesen und schreiben.
|
||||
"""
|
||||
import base64
|
||||
import hashlib
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.dependencies import require_role
|
||||
from app.models.smtp_config import SmtpConfig
|
||||
from app.models.user import User, UserRole
|
||||
from app.schemas.smtp import SmtpConfigOut, SmtpConfigSave, SmtpTestRequest
|
||||
from app.services.email_service import email_service
|
||||
|
||||
router = APIRouter(prefix="/smtp", tags=["SMTP-Konfiguration"])
|
||||
|
||||
_admin_roles = (UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN)
|
||||
|
||||
|
||||
def _encrypt(plain: str) -> str:
|
||||
from app.core.config import settings
|
||||
from cryptography.fernet import Fernet
|
||||
key = hashlib.sha256(settings.secret_key.encode()).digest()
|
||||
f = Fernet(base64.urlsafe_b64encode(key))
|
||||
return f.encrypt(plain.encode()).decode()
|
||||
|
||||
|
||||
async def _get_config(company_id: uuid.UUID, db: AsyncSession) -> SmtpConfig | None:
|
||||
return await db.scalar(select(SmtpConfig).where(SmtpConfig.company_id == company_id))
|
||||
|
||||
|
||||
@router.get("/config", response_model=SmtpConfigOut | None)
|
||||
async def get_smtp_config(
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
cfg = await _get_config(current_user.company_id, db)
|
||||
if not cfg:
|
||||
return None
|
||||
return SmtpConfigOut.model_validate(cfg)
|
||||
|
||||
|
||||
@router.post("/config", response_model=SmtpConfigOut)
|
||||
async def save_smtp_config(
|
||||
data: SmtpConfigSave,
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Erstellt oder überschreibt die SMTP-Konfiguration der Firma."""
|
||||
cfg = await _get_config(current_user.company_id, db)
|
||||
|
||||
if cfg is None:
|
||||
cfg = SmtpConfig(company_id=current_user.company_id, id=uuid.uuid4())
|
||||
db.add(cfg)
|
||||
|
||||
cfg.host = data.host
|
||||
cfg.port = data.port
|
||||
cfg.use_tls = data.use_tls
|
||||
cfg.use_starttls = data.use_starttls
|
||||
cfg.username = data.username
|
||||
cfg.from_email = data.from_email
|
||||
cfg.from_name = data.from_name
|
||||
cfg.is_enabled = data.is_enabled
|
||||
|
||||
if data.password is not None:
|
||||
cfg.password_encrypted = _encrypt(data.password)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(cfg)
|
||||
return SmtpConfigOut.model_validate(cfg)
|
||||
|
||||
|
||||
@router.post("/test")
|
||||
async def test_smtp(
|
||||
data: SmtpTestRequest,
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Sendet eine Test-E-Mail mit der aktuellen Konfiguration."""
|
||||
cfg = await _get_config(current_user.company_id, db)
|
||||
if not cfg:
|
||||
raise HTTPException(status_code=404, detail="Keine SMTP-Konfiguration vorhanden.")
|
||||
try:
|
||||
await email_service.send_test(cfg, data.to)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=502, detail=f"SMTP-Fehler: {exc}")
|
||||
return {"message": f"Test-E-Mail an {data.to} verschickt."}
|
||||
Reference in New Issue
Block a user