refactor(P11+P12): codebase hygiene — CLAUDE.md rewrite, type safety, dead code removal

- Rewrite CLAUDE.md to match current 8-service architecture (was 11, 5 deleted)
- Remove all as-any casts in OrderDetail.tsx (9 casts → 0)
- Add cad_parsed_objects/cad_part_materials to OrderItem interface
- Rename require_admin → require_global_admin across 6 router files (22 calls)
- Remove EXPORT_GLB_PRODUCTION enum + generate_gltf_production_task (dead code)
- Remove worker-thumbnail from ALLOWED_SERVICES, replace Flamenco link
- Delete obsolete PLAN.md (1455 lines) and PLAN_REFACTOR.md (1174 lines)
- Fix digit-only USD prim names with p_ prefix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 07:22:04 +01:00
parent 3dcfa7c0bd
commit 577dd1ca7e
21 changed files with 303 additions and 3229 deletions
-28
View File
@@ -303,34 +303,6 @@ async def generate_gltf_geometry(
return {"status": "queued", "task_id": task.id, "cad_file_id": str(id)}
@router.post("/{id}/generate-gltf-production", status_code=status.HTTP_202_ACCEPTED)
async def generate_gltf_production(
id: uuid.UUID,
user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
"""Queue production GLB export (Blender + PBR materials) from a geometry GLB.
Requires a gltf_geometry MediaAsset to already exist (run generate-gltf-geometry first).
Stores result as a MediaAsset with asset_type='gltf_production'.
"""
if not is_privileged(user):
raise HTTPException(status_code=403, detail="Insufficient permissions")
cad = await _get_cad_file(id, db)
if not cad.stored_path:
raise HTTPException(status_code=404, detail="STEP file not uploaded for this CAD file")
logger.warning(
"generate_gltf_production called for cad %s"
"deprecated: renders now consume usd_master directly",
id,
)
from app.tasks.step_tasks import generate_gltf_production_task
task = generate_gltf_production_task.delay(str(id))
return {"status": "queued", "task_id": task.id, "cad_file_id": str(id)}
@router.post(
"/{id}/regenerate-thumbnail",
status_code=status.HTTP_202_ACCEPTED,
@@ -10,7 +10,7 @@ from app.domains.rendering.schemas import (
GlobalRenderPositionPatch,
GlobalRenderPositionOut,
)
from app.utils.auth import require_admin, get_current_user
from app.utils.auth import require_global_admin, get_current_user
router = APIRouter(prefix="/render-positions/global", tags=["global-render-positions"])
@@ -31,7 +31,7 @@ async def list_global_render_positions(
async def create_global_render_position(
body: GlobalRenderPositionCreate,
db: AsyncSession = Depends(get_db),
_user=Depends(require_admin),
_user=Depends(require_global_admin),
):
"""Create a new global render position (admin only)."""
pos = GlobalRenderPosition(**body.model_dump())
@@ -46,7 +46,7 @@ async def update_global_render_position(
pos_id: uuid.UUID,
body: GlobalRenderPositionPatch,
db: AsyncSession = Depends(get_db),
_user=Depends(require_admin),
_user=Depends(require_global_admin),
):
"""Update a global render position (admin only)."""
result = await db.execute(select(GlobalRenderPosition).where(GlobalRenderPosition.id == pos_id))
@@ -64,7 +64,7 @@ async def update_global_render_position(
async def delete_global_render_position(
pos_id: uuid.UUID,
db: AsyncSession = Depends(get_db),
_user=Depends(require_admin),
_user=Depends(require_global_admin),
):
"""Delete a global render position (admin only)."""
result = await db.execute(select(GlobalRenderPosition).where(GlobalRenderPosition.id == pos_id))
+2 -2
View File
@@ -6,7 +6,7 @@ from sqlalchemy import select
from pydantic import BaseModel
from app.database import get_db
from app.models.template import Template
from app.utils.auth import get_current_user, require_admin
from app.utils.auth import get_current_user, require_global_admin
from app.models.user import User
router = APIRouter(prefix="/templates", tags=["templates"])
@@ -63,7 +63,7 @@ async def get_template(
async def update_template(
template_id: uuid.UUID,
body: TemplateUpdate,
user: User = Depends(require_admin),
user: User = Depends(require_global_admin),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(Template).where(Template.id == template_id))
+8 -8
View File
@@ -17,7 +17,7 @@ from app.models.product import Product
from app.models.user import User
from app.models.worker_config import WorkerConfig
from app.models.system_setting import SystemSetting
from app.utils.auth import get_current_user, require_admin_or_pm, require_admin
from app.utils.auth import get_current_user, require_admin_or_pm, require_global_admin
router = APIRouter(prefix="/worker", tags=["worker"])
@@ -364,7 +364,7 @@ async def cancel_task(task_id: str, user: User = Depends(require_admin_or_pm)):
# ---------------------------------------------------------------------------
class ScaleRequest(BaseModel):
service: str # "render-worker" | "worker" | "worker-thumbnail"
service: str # "render-worker" | "worker"
count: int # 020
@@ -411,7 +411,7 @@ async def scale_workers(
body: ScaleRequest,
user: User = Depends(require_admin_or_pm),
):
"""Scale a Compose service (render-worker, worker, worker-thumbnail) up or down.
"""Scale a Compose service (render-worker, worker) up or down.
Requires the docker socket and compose file to be accessible inside the container
(see docker-compose.yml COMPOSE_PROJECT_DIR env var).
@@ -421,7 +421,7 @@ async def scale_workers(
import subprocess
from fastapi import HTTPException
ALLOWED_SERVICES = {"render-worker", "worker", "worker-thumbnail"}
ALLOWED_SERVICES = {"render-worker", "worker"}
if body.service not in ALLOWED_SERVICES:
raise HTTPException(400, detail=f"service must be one of {ALLOWED_SERVICES}")
if not (0 <= body.count <= 20):
@@ -462,7 +462,7 @@ async def scale_workers(
# ---------------------------------------------------------------------------
@router.post("/probe/gpu", status_code=http_status.HTTP_202_ACCEPTED)
async def trigger_gpu_probe(current_user: User = Depends(require_admin)):
async def trigger_gpu_probe(current_user: User = Depends(require_global_admin)):
"""Queue a GPU probe task on the render-worker."""
from app.tasks.gpu_tasks import probe_gpu
result = probe_gpu.delay()
@@ -471,7 +471,7 @@ async def trigger_gpu_probe(current_user: User = Depends(require_admin)):
@router.get("/probe/gpu/result")
async def get_gpu_probe_result(
current_user: User = Depends(require_admin),
current_user: User = Depends(require_global_admin),
db: AsyncSession = Depends(get_db),
):
"""Return the last GPU probe result from system_settings."""
@@ -622,7 +622,7 @@ class WorkerConfigUpdate(BaseModel):
@router.get("/configs", response_model=list[WorkerConfigOut])
async def list_worker_configs(
user: User = Depends(require_admin),
user: User = Depends(require_global_admin),
db: AsyncSession = Depends(get_db),
):
"""List all worker concurrency configurations (admin only)."""
@@ -644,7 +644,7 @@ async def list_worker_configs(
async def update_worker_config(
queue_name: str,
body: WorkerConfigUpdate,
user: User = Depends(require_admin),
user: User = Depends(require_global_admin),
db: AsyncSession = Depends(get_db),
):
"""Update concurrency settings for a specific queue (admin only)."""