refactor: migrate vendor APIs to token-based context and consolidate architecture

## Vendor-in-Token Architecture (Complete Migration)
- Migrate all vendor API endpoints from require_vendor_context() to token_vendor_id
- Update permission dependencies to extract vendor from JWT token
- Add vendor exceptions: VendorAccessDeniedException, VendorOwnerOnlyException,
  InsufficientVendorPermissionsException
- Shop endpoints retain require_vendor_context() for URL-based detection
- Add AUTH-004 architecture rule enforcing vendor context patterns
- Fix marketplace router missing /marketplace prefix

## Exception Pattern Fixes (API-003/API-004)
- Services raise domain exceptions, endpoints let them bubble up
- Add code_quality and content_page exception modules
- Move business logic from endpoints to services (admin, auth, content_page)
- Fix exception handling in admin, shop, and vendor endpoints

## Tailwind CSS Consolidation
- Consolidate CSS to per-area files (admin, vendor, shop, platform)
- Remove shared/cdn-fallback.html and shared/css/tailwind.min.css
- Update all templates to use area-specific Tailwind output files
- Remove Node.js config (package.json, postcss.config.js, tailwind.config.js)

## Documentation & Cleanup
- Update vendor-in-token-architecture.md with completed migration status
- Update architecture-rules.md with new rules
- Move migration docs to docs/development/migration/
- Remove duplicate/obsolete documentation files
- Merge pytest.ini settings into pyproject.toml

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-04 22:24:45 +01:00
parent 76f8a59954
commit 8a367077e1
85 changed files with 21787 additions and 134978 deletions

View File

@@ -18,6 +18,7 @@ from sqlalchemy.orm import Session
from app.api.deps import get_current_admin_api
from app.core.database import get_db
from app.core.logging import reload_log_level
from app.exceptions import ConfirmationRequiredException, ResourceNotFoundException
from app.services.admin_audit_service import admin_audit_service
from app.services.admin_settings_service import admin_settings_service
from app.services.log_service import log_service
@@ -26,8 +27,12 @@ from models.schema.admin import (
ApplicationLogFilters,
ApplicationLogListResponse,
FileLogResponse,
LogCleanupResponse,
LogDeleteResponse,
LogFileListResponse,
LogSettingsResponse,
LogSettingsUpdate,
LogSettingsUpdateResponse,
LogStatistics,
)
@@ -87,7 +92,7 @@ def get_log_statistics(
return log_service.get_log_statistics(db, days)
@router.delete("/database/cleanup")
@router.delete("/database/cleanup", response_model=LogCleanupResponse)
def cleanup_old_logs(
retention_days: int = Query(30, ge=1, le=365),
confirm: bool = Query(False, description="Must be true to confirm cleanup"),
@@ -99,13 +104,8 @@ def cleanup_old_logs(
Requires confirmation parameter.
"""
from fastapi import HTTPException
if not confirm:
raise HTTPException(
status_code=400,
detail="Cleanup requires confirmation parameter: confirm=true",
)
raise ConfirmationRequiredException(operation="cleanup_logs")
deleted_count = log_service.cleanup_old_logs(db, retention_days)
@@ -119,13 +119,13 @@ def cleanup_old_logs(
details={"retention_days": retention_days, "deleted_count": deleted_count},
)
return {
"message": f"Deleted {deleted_count} log entries older than {retention_days} days",
"deleted_count": deleted_count,
}
return LogCleanupResponse(
message=f"Deleted {deleted_count} log entries older than {retention_days} days",
deleted_count=deleted_count,
)
@router.delete("/database/{log_id}")
@router.delete("/database/{log_id}", response_model=LogDeleteResponse)
def delete_log(
log_id: int,
db: Session = Depends(get_db),
@@ -144,7 +144,7 @@ def delete_log(
details={},
)
return {"message": message}
return LogDeleteResponse(message=message)
# ============================================================================
@@ -152,7 +152,7 @@ def delete_log(
# ============================================================================
@router.get("/files")
@router.get("/files", response_model=LogFileListResponse)
def list_log_files(
current_admin: User = Depends(get_current_admin_api),
):
@@ -161,7 +161,7 @@ def list_log_files(
Returns list of log files with size and modification date.
"""
return {"files": log_service.list_log_files()}
return LogFileListResponse(files=log_service.list_log_files())
@router.get("/files/{filename}", response_model=FileLogResponse)
@@ -191,7 +191,6 @@ def download_log_file(
from pathlib import Path
from app.core.config import settings
from fastapi import HTTPException
from fastapi.responses import FileResponse
# Determine log file path
@@ -202,7 +201,7 @@ def download_log_file(
log_file = Path("logs") / filename
if not log_file.exists():
raise HTTPException(status_code=404, detail=f"Log file '{filename}' not found")
raise ResourceNotFoundException(resource_type="LogFile", identifier=filename)
# Log action
from app.core.database import get_db
@@ -267,7 +266,7 @@ def get_log_settings(
)
@router.put("/settings")
@router.put("/settings", response_model=LogSettingsUpdateResponse)
def update_log_settings(
settings_update: LogSettingsUpdate,
db: Session = Depends(get_db),
@@ -335,8 +334,8 @@ def update_log_settings(
details={"updated_fields": updated},
)
return {
"message": "Log settings updated successfully",
"updated_fields": updated,
"note": "Log level changes are applied immediately. File rotation settings require restart.",
}
return LogSettingsUpdateResponse(
message="Log settings updated successfully",
updated_fields=updated,
note="Log level changes are applied immediately. File rotation settings require restart.",
)