mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-08-01 19:18:35 -04:00
refactor(routes): move cleanup domain into routes/cleanup/ subpackage (#5658)
Slice 2g of the route-domain reorganization (#4082/#4071). Moves
cleanup_routes.py into routes/cleanup/, leaving a backward-compat
sys.modules shim at the old path. Pure file reorganization, no behavior
change.
The shim uses sys.modules replacement so string-targeted
monkeypatch.setattr("routes.cleanup_routes.*", ...) in
test_cleanup_owner_scope.py reaches the canonical module.
Canonical module imports only from src/ and stdlib (zero internal
routes/ coupling). Zero source-introspection landmines.
Adds tests/test_cleanup_routes_shim.py to pin the sys.modules shim
contract. Verified: compileall clean; targeted tests pass.
This commit is contained in:
@@ -704,7 +704,7 @@ from routes.diagnostics_routes import setup_diagnostics_routes
|
|||||||
app.include_router(setup_diagnostics_routes(rag_manager, rag_available, research_handler, memory_vector))
|
app.include_router(setup_diagnostics_routes(rag_manager, rag_available, research_handler, memory_vector))
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
from routes.cleanup_routes import setup_cleanup_routes
|
from routes.cleanup.cleanup_routes import setup_cleanup_routes
|
||||||
app.include_router(setup_cleanup_routes(session_manager))
|
app.include_router(setup_cleanup_routes(session_manager))
|
||||||
|
|
||||||
# Personal docs
|
# Personal docs
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Cleanup route domain package (slice 2g, #4082/#4071).
|
||||||
|
|
||||||
|
Contains cleanup_routes.py, migrated from the flat routes/ directory.
|
||||||
|
Backward-compat shim at routes/cleanup_routes.py re-exports from here.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# routes/cleanup_routes.py
|
||||||
|
"""Routes for cleanup operations."""
|
||||||
|
import logging
|
||||||
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
|
from src.cleanup_service import get_cleanup_preview, cleanup_sessions
|
||||||
|
from src.auth_helpers import get_current_user
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def setup_cleanup_routes(session_manager):
|
||||||
|
"""
|
||||||
|
Setup cleanup-related routes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session_manager: SessionManager instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
APIRouter instance with cleanup routes
|
||||||
|
"""
|
||||||
|
router = APIRouter(prefix="/api/cleanup")
|
||||||
|
|
||||||
|
@router.get("/preview")
|
||||||
|
async def cleanup_preview(request: Request):
|
||||||
|
"""
|
||||||
|
Preview what would be cleaned up without making any changes.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response with lists of sessions that would be archived/deleted and estimated space savings
|
||||||
|
"""
|
||||||
|
user = get_current_user(request)
|
||||||
|
try:
|
||||||
|
preview = await get_cleanup_preview(owner=user)
|
||||||
|
return preview
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Cleanup preview failed: {e}")
|
||||||
|
raise HTTPException(500, "Cleanup preview generation failed")
|
||||||
|
|
||||||
|
@router.post("")
|
||||||
|
async def cleanup_endpoint(request: Request):
|
||||||
|
"""
|
||||||
|
Perform cleanup operations:
|
||||||
|
1. Archive inactive sessions (not accessed for 7 days)
|
||||||
|
2. Delete old sessions (archived, not important, not accessed for 14+ days, with fewer than 10 messages)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response with counts of deleted and archived sessions, and space freed
|
||||||
|
"""
|
||||||
|
user = get_current_user(request)
|
||||||
|
try:
|
||||||
|
archived_count, deleted_count, space_freed_mb = await cleanup_sessions(session_manager, owner=user)
|
||||||
|
return {
|
||||||
|
"archived_count": archived_count,
|
||||||
|
"deleted_count": deleted_count,
|
||||||
|
"space_freed_mb": round(space_freed_mb, 2)
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Cleanup failed: {e}")
|
||||||
|
raise HTTPException(500, "Cleanup operation failed")
|
||||||
|
|
||||||
|
return router
|
||||||
+13
-56
@@ -1,60 +1,17 @@
|
|||||||
# routes/cleanup_routes.py
|
"""Backward-compat shim — canonical location is routes/cleanup/cleanup_routes.py.
|
||||||
"""Routes for cleanup operations."""
|
|
||||||
import logging
|
|
||||||
from fastapi import APIRouter, HTTPException, Request
|
|
||||||
from src.cleanup_service import get_cleanup_preview, cleanup_sessions
|
|
||||||
from src.auth_helpers import get_current_user
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
This module is replaced in ``sys.modules`` by the canonical module object so
|
||||||
|
that ``import routes.cleanup_routes``, ``from routes.cleanup_routes import X``,
|
||||||
|
``importlib.import_module("routes.cleanup_routes")``, and the string-targeted
|
||||||
|
``monkeypatch.setattr("routes.cleanup_routes.get_cleanup_preview", ...)`` /
|
||||||
|
``"routes.cleanup_routes.get_current_user"`` / ``"routes.cleanup_routes.
|
||||||
|
cleanup_sessions"`` pattern used by test_cleanup_owner_scope.py all operate
|
||||||
|
on the *same* object the application actually uses. Keeps existing import
|
||||||
|
paths working after slice 2g (#4082/#4071).
|
||||||
|
"""
|
||||||
|
|
||||||
def setup_cleanup_routes(session_manager):
|
import sys as _sys
|
||||||
"""
|
|
||||||
Setup cleanup-related routes.
|
|
||||||
|
|
||||||
Args:
|
from routes.cleanup import cleanup_routes as _canonical # noqa: F401
|
||||||
session_manager: SessionManager instance
|
|
||||||
|
|
||||||
Returns:
|
_sys.modules[__name__] = _canonical
|
||||||
APIRouter instance with cleanup routes
|
|
||||||
"""
|
|
||||||
router = APIRouter(prefix="/api/cleanup")
|
|
||||||
|
|
||||||
@router.get("/preview")
|
|
||||||
async def cleanup_preview(request: Request):
|
|
||||||
"""
|
|
||||||
Preview what would be cleaned up without making any changes.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
JSON response with lists of sessions that would be archived/deleted and estimated space savings
|
|
||||||
"""
|
|
||||||
user = get_current_user(request)
|
|
||||||
try:
|
|
||||||
preview = await get_cleanup_preview(owner=user)
|
|
||||||
return preview
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Cleanup preview failed: {e}")
|
|
||||||
raise HTTPException(500, "Cleanup preview generation failed")
|
|
||||||
|
|
||||||
@router.post("")
|
|
||||||
async def cleanup_endpoint(request: Request):
|
|
||||||
"""
|
|
||||||
Perform cleanup operations:
|
|
||||||
1. Archive inactive sessions (not accessed for 7 days)
|
|
||||||
2. Delete old sessions (archived, not important, not accessed for 14+ days, with fewer than 10 messages)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
JSON response with counts of deleted and archived sessions, and space freed
|
|
||||||
"""
|
|
||||||
user = get_current_user(request)
|
|
||||||
try:
|
|
||||||
archived_count, deleted_count, space_freed_mb = await cleanup_sessions(session_manager, owner=user)
|
|
||||||
return {
|
|
||||||
"archived_count": archived_count,
|
|
||||||
"deleted_count": deleted_count,
|
|
||||||
"space_freed_mb": round(space_freed_mb, 2)
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Cleanup failed: {e}")
|
|
||||||
raise HTTPException(500, "Cleanup operation failed")
|
|
||||||
|
|
||||||
return router
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
"""Regression test for the cleanup route shim (slice 2g, #4082/#4071).
|
||||||
|
|
||||||
|
The backward-compat shim at ``routes/cleanup_routes.py`` uses ``sys.modules``
|
||||||
|
replacement so the legacy import path and the canonical ``routes.cleanup.*``
|
||||||
|
path resolve to the *same* module object. This is required because
|
||||||
|
``test_cleanup_owner_scope.py`` uses string-targeted
|
||||||
|
``monkeypatch.setattr("routes.cleanup_routes.get_cleanup_preview", ...)`` and
|
||||||
|
``monkeypatch.delitem(sys.modules, "routes.cleanup_routes")`` + re-import —
|
||||||
|
for those patches to take effect at runtime, the legacy module object and
|
||||||
|
the canonical one must be identical.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
import routes.cleanup_routes as _shim_cleanup # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
|
def test_legacy_and_canonical_cleanup_module_are_same_object():
|
||||||
|
"""``import routes.cleanup_routes`` must alias the canonical module."""
|
||||||
|
legacy = importlib.import_module("routes.cleanup_routes")
|
||||||
|
canonical = importlib.import_module("routes.cleanup.cleanup_routes")
|
||||||
|
assert legacy is canonical, (
|
||||||
|
"routes.cleanup_routes shim must resolve to the canonical "
|
||||||
|
"routes.cleanup.cleanup_routes module object"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user