From 2fd7591eac1c654c1f894a70fbde65e4f1a71245 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Fri, 12 Jun 2026 15:40:31 +0200 Subject: [PATCH] =?UTF-8?q?docs(09):=20fix=20plan=20checker=20warnings=20?= =?UTF-8?q?=E2=80=94=20ORDER=20BY=20desc,=20closed=20tag,=20resolved=20mar?= =?UTF-8?q?ker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md | 4 ++-- .planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md | 2 +- .planning/phases/09-admin-panel-rearchitecture/09-RESEARCH.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md b/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md index e9cc4d1..a7c848c 100644 --- a/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md +++ b/.planning/phases/09-admin-panel-rearchitecture/09-01-PLAN.md @@ -133,10 +133,10 @@ From backend/api/admin/__init__.py (current state): - `user_count` is `SELECT count(id) FROM users WHERE role = 'user'`. - `total_storage_bytes` is `SELECT sum(used_bytes) FROM quotas` coerced to `0` when NULL. - `doc_status` is `{status: count}` from `SELECT status, count(id) FROM documents GROUP BY status`. - - `recent_audit` is the result of `_build_filtered_query_with_handles(None, None, None, None).limit(10)` mapped via `_audit_to_dict_with_handles`. + - `recent_audit` is the result of `_build_filtered_query_with_handles(None, None, None, None).order_by(AuditLog.created_at.desc()).limit(10)` mapped via `_audit_to_dict_with_handles` — the `.order_by(AuditLog.created_at.desc())` must be chained before `.limit(10)` to guarantee newest-first ordering that the test asserts. - Response never includes raw user records, raw audit rows, password_hash, credentials_enc, totp_secret, api_key_enc, or extracted_text. - Create `backend/api/admin/overview.py`. Imports: `from __future__ import annotations`; `from fastapi import APIRouter, Depends`; `from sqlalchemy import func, select`; `from sqlalchemy.ext.asyncio import AsyncSession`; `from db.models import Document, Quota, User`; `from deps.auth import get_current_admin`; `from deps.db import get_db`; `from api.audit import _build_filtered_query_with_handles, _audit_to_dict_with_handles`. Declare `router = APIRouter()` with NO prefix (single WHY comment: "NO prefix — parent `__init__.py` carries `/api/admin` (D-02)"). Declare `@router.get("/overview")` async function `get_overview(session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin)) -> dict`. Compute user_count via `await session.scalar(select(func.count(User.id)).where(User.role == "user"))` (default to 0 if None). Compute total_storage_bytes via `await session.scalar(select(func.sum(Quota.used_bytes)))` (default 0). Compute doc_status via `(await session.execute(select(Document.status, func.count(Document.id)).group_by(Document.status))).all()` and convert to dict. Compute recent_audit by calling `_build_filtered_query_with_handles(None, None, None, None).limit(10)`, executing, then mapping each row tuple via `_audit_to_dict_with_handles(*row)` — confirm the row unpacking shape against the audit.py source (the existing list endpoint already does this — copy that exact iteration). Return the dict with the four keys. Modify `backend/api/admin/__init__.py`: add `from api.admin.overview import router as overview_router` alongside existing imports (preserve the docstring WHY comment); append `router.include_router(overview_router)` after the existing three `include_router` calls. Then remove the `@pytest.mark.xfail` decorators from all 8 tests in `backend/tests/test_admin_overview.py` so they execute as real tests. + Create `backend/api/admin/overview.py`. Imports: `from __future__ import annotations`; `from fastapi import APIRouter, Depends`; `from sqlalchemy import func, select`; `from sqlalchemy.ext.asyncio import AsyncSession`; `from db.models import Document, Quota, User`; `from deps.auth import get_current_admin`; `from deps.db import get_db`; `from api.audit import _build_filtered_query_with_handles, _audit_to_dict_with_handles`. Declare `router = APIRouter()` with NO prefix (single WHY comment: "NO prefix — parent `__init__.py` carries `/api/admin` (D-02)"). Declare `@router.get("/overview")` async function `get_overview(session: AsyncSession = Depends(get_db), _admin: User = Depends(get_current_admin)) -> dict`. Compute user_count via `await session.scalar(select(func.count(User.id)).where(User.role == "user"))` (default to 0 if None). Compute total_storage_bytes via `await session.scalar(select(func.sum(Quota.used_bytes)))` (default 0). Compute doc_status via `(await session.execute(select(Document.status, func.count(Document.id)).group_by(Document.status))).all()` and convert to dict. Compute recent_audit by calling `_build_filtered_query_with_handles(None, None, None, None).order_by(AuditLog.created_at.desc()).limit(10)`, executing, then mapping each row tuple via `_audit_to_dict_with_handles(*row)` — confirm the row unpacking shape against the audit.py source (the existing list endpoint already does this — copy that exact iteration). Return the dict with the four keys. Modify `backend/api/admin/__init__.py`: add `from api.admin.overview import router as overview_router` alongside existing imports (preserve the docstring WHY comment); append `router.include_router(overview_router)` after the existing three `include_router` calls. Then remove the `@pytest.mark.xfail` decorators from all 8 tests in `backend/tests/test_admin_overview.py` so they execute as real tests. cd backend && pytest tests/test_admin_overview.py -v -x diff --git a/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md b/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md index 6ec0cc5..82d8b43 100644 --- a/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md +++ b/.planning/phases/09-admin-panel-rearchitecture/09-03-PLAN.md @@ -167,7 +167,7 @@ Verify before extracting each file: `grep -E "^import" frontend/src/components/a - ADMIN-08: Four standalone admin view files exist under `frontend/src/views/admin/`. - ADMIN-10: Each view is a self-contained component that the router can mount as a child of `/admin` (router wiring lands in 09-04). - Behavior preserved: every admin CRUD/filter/download interaction continues to work after wiring in 09-04. - + Create `.planning/phases/09-admin-panel-rearchitecture/09-03-SUMMARY.md` when done. List the four new files, any dead-prop removals, and any orphaned-emit-to-fetch replacements made during extraction. diff --git a/.planning/phases/09-admin-panel-rearchitecture/09-RESEARCH.md b/.planning/phases/09-admin-panel-rearchitecture/09-RESEARCH.md index 9b1d79f..2754af9 100644 --- a/.planning/phases/09-admin-panel-rearchitecture/09-RESEARCH.md +++ b/.planning/phases/09-admin-panel-rearchitecture/09-RESEARCH.md @@ -668,7 +668,7 @@ router.include_router(overview_router) --- -## Open Questions +## Open Questions (RESOLVED) 1. **AdminSidebar — separate component or inline in AdminLayout?** - What we know: `AppSidebar.vue` is a separate component imported by `App.vue`. `AuthLayout.vue` has no sidebar.