File size: 5,636 Bytes
b491c15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
"""
cache_endpoints.py — Endpoint API per Gestione Cache

Endpoint:
  GET  /api/cache/stats           — Statistiche cache
  POST /api/cache/invalidate      — Invalida un entry
  GET  /api/cache/health          — Health check cache
  POST /api/cache/reset-stats     — Resetta statistiche
"""

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import Optional
import logging

# Import cache manager
try:
    from backend.api.cache_manager import (
        get_cache_stats,
        reset_cache_stats,
        invalidate_cache,
        CacheStrategy,
        CACHE_ENABLED,
    )
except ImportError:
    from cache_manager import (
        get_cache_stats,
        reset_cache_stats,
        invalidate_cache,
        CacheStrategy,
        CACHE_ENABLED,
    )

_logger = logging.getLogger("cache_endpoints")

router = APIRouter(prefix="/api/cache", tags=["cache"])


# ── Modelli Pydantic ──────────────────────────────────────────────────────
class InvalidateCacheRequest(BaseModel):
    strategy: str  # "query", "memory", "embedding", "conversation", "analytics"
    identifier: str


class CacheStatsResponse(BaseModel):
    enabled: bool
    hits: int
    misses: int
    sets: int
    deletes: int
    evictions: int
    hit_rate_percent: float
    total_requests: int


class CacheHealthResponse(BaseModel):
    ok: bool
    cache_enabled: bool
    message: str


# ── Endpoint: Statistiche Cache ────────────────────────────────────────────
@router.get("/stats", response_model=CacheStatsResponse)
async def cache_stats():
    """Ritorna le statistiche del cache layer."""
    try:
        stats = get_cache_stats()
        return CacheStatsResponse(**stats)
    except Exception as exc:
        _logger.error(f"Error fetching cache stats: {exc}")
        raise HTTPException(500, "Error fetching cache stats")


# ── Endpoint: Invalida Cache ──────────────────────────────────────────────
@router.post("/invalidate")
async def invalidate_cache_entry(req: InvalidateCacheRequest):
    """Invalida un entry specifico dalla cache."""
    try:
        # Valida strategy
        try:
            strategy = CacheStrategy(req.strategy)
        except ValueError:
            raise HTTPException(
                400,
                f"Invalid strategy. Must be one of: {', '.join([s.value for s in CacheStrategy])}",
            )
        
        # Invalida
        success = await invalidate_cache(strategy, req.identifier)
        
        return {
            "ok": success,
            "strategy": req.strategy,
            "identifier": req.identifier,
            "message": "Cache entry invalidated" if success else "Failed to invalidate cache entry",
        }
    except HTTPException:
        raise
    except Exception as exc:
        _logger.error(f"Error invalidating cache: {exc}")
        raise HTTPException(500, "Error invalidating cache")


# ── Endpoint: Health Check ────────────────────────────────────────────────
@router.get("/health", response_model=CacheHealthResponse)
async def cache_health():
    """Health check per il cache layer."""
    try:
        stats = get_cache_stats()
        
        return CacheHealthResponse(
            ok=True,
            cache_enabled=CACHE_ENABLED,
            message=f"Cache layer operational. Hit rate: {stats.get('hit_rate_percent', 0):.1f}%",
        )
    except Exception as exc:
        _logger.error(f"Cache health check failed: {exc}")
        return CacheHealthResponse(
            ok=False,
            cache_enabled=CACHE_ENABLED,
            message=f"Cache health check failed: {str(exc)}",
        )


# ── Endpoint: Reset Statistiche ───────────────────────────────────────────
@router.post("/reset-stats")
async def reset_stats():
    """Resetta le statistiche del cache."""
    try:
        reset_cache_stats()
        return {
            "ok": True,
            "message": "Cache statistics reset",
        }
    except Exception as exc:
        _logger.error(f"Error resetting cache stats: {exc}")
        raise HTTPException(500, "Error resetting cache stats")


# ── Endpoint: Info Cache ──────────────────────────────────────────────────
@router.get("/info")
async def cache_info():
    """Ritorna informazioni sulla configurazione del cache."""
    try:
        from cache_manager import (
            CACHE_ENABLED,
            CACHE_TTL_DEFAULT,
            CACHE_MAX_SIZE,
            CACHE_TTL_BY_STRATEGY,
            SUPABASE_A_URL,
            SUPABASE_B_URL,
        )
        
        return {
            "enabled": CACHE_ENABLED,
            "ttl_default_seconds": CACHE_TTL_DEFAULT,
            "max_size": CACHE_MAX_SIZE,
            "ttl_by_strategy": {k.value: v for k, v in CACHE_TTL_BY_STRATEGY.items()},
            "supabase_a_configured": bool(SUPABASE_A_URL),
            "supabase_b_configured": bool(SUPABASE_B_URL),
        }
    except Exception as exc:
        _logger.error(f"Error fetching cache info: {exc}")
        raise HTTPException(500, "Error fetching cache info")