Add comprehensive backend features and mobile UI improvements

Backend:
- Add 2FA authentication with TOTP support
- Add API keys management system
- Add audit logging for security events
- Add file upload/management system
- Add notifications system with preferences
- Add session management
- Add webhooks integration
- Add analytics endpoints
- Add export functionality
- Add password policy enforcement
- Add new database migrations for core tables

Frontend:
- Add module position system (top/bottom sidebar sections)
- Add search and notifications module configuration tabs
- Add mobile logo replacing hamburger menu
- Center page title absolutely when no tabs present
- Align sidebar footer toggles with navigation items
- Add lighter icon color in dark theme for mobile
- Add API keys management page
- Add notifications page with context
- Add admin analytics and audit logs pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-17 22:27:32 +01:00
parent f698aa4d51
commit 8c4a555b88
76 changed files with 9751 additions and 323 deletions

162
backend/app/api/v1/audit.py Normal file
View File

@@ -0,0 +1,162 @@
"""Audit log API endpoints."""
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, Query, HTTPException, status
from sqlalchemy.orm import Session
from app.dependencies import get_db, get_current_superuser
from app.models.user import User
from app import crud
from app.schemas.audit_log import (
AuditLog,
AuditLogList,
AuditLogFilter,
AuditLogStats
)
router = APIRouter()
@router.get("", response_model=AuditLogList)
def get_audit_logs(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=100),
user_id: Optional[str] = None,
username: Optional[str] = None,
action: Optional[str] = None,
resource_type: Optional[str] = None,
resource_id: Optional[str] = None,
status_filter: Optional[str] = Query(None, alias="status"),
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None,
):
"""
Get paginated audit logs with optional filtering.
Requires superuser authentication.
"""
filters = AuditLogFilter(
user_id=user_id,
username=username,
action=action,
resource_type=resource_type,
resource_id=resource_id,
status=status_filter,
start_date=start_date,
end_date=end_date
)
skip = (page - 1) * page_size
items, total = crud.audit_log.get_multi(
db, skip=skip, limit=page_size, filters=filters
)
total_pages = (total + page_size - 1) // page_size
return AuditLogList(
items=items,
total=total,
page=page,
page_size=page_size,
total_pages=total_pages
)
@router.get("/stats", response_model=AuditLogStats)
def get_audit_stats(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
):
"""
Get audit log statistics.
Requires superuser authentication.
"""
return crud.audit_log.get_stats(db)
@router.get("/actions")
def get_distinct_actions(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
):
"""
Get list of distinct action types for filtering.
"""
return {"actions": crud.audit_log.get_distinct_actions(db)}
@router.get("/resource-types")
def get_distinct_resource_types(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
):
"""
Get list of distinct resource types for filtering.
"""
return {"resource_types": crud.audit_log.get_distinct_resource_types(db)}
@router.get("/user/{user_id}", response_model=AuditLogList)
def get_user_audit_logs(
user_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=100),
):
"""
Get audit logs for a specific user.
Requires superuser authentication.
"""
filters = AuditLogFilter(user_id=user_id)
skip = (page - 1) * page_size
items, total = crud.audit_log.get_multi(
db, skip=skip, limit=page_size, filters=filters
)
total_pages = (total + page_size - 1) // page_size
return AuditLogList(
items=items,
total=total,
page=page,
page_size=page_size,
total_pages=total_pages
)
@router.get("/{log_id}", response_model=AuditLog)
def get_audit_log(
log_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
):
"""
Get a specific audit log entry.
Requires superuser authentication.
"""
log = crud.audit_log.get(db, id=log_id)
if not log:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Audit log not found"
)
return log
@router.delete("/cleanup")
def cleanup_old_logs(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_superuser),
days: int = Query(90, ge=1, le=365),
):
"""
Delete audit logs older than specified days.
Requires superuser authentication.
Default: 90 days.
"""
deleted = crud.audit_log.delete_old(db, days=days)
return {"deleted": deleted, "days_threshold": days}