Files
vault-dash/app/services/cache.py
Bu5hm4nn 00a68bc767 Initial commit: Vault Dashboard for options hedging
- FastAPI + NiceGUI web application
- QuantLib-based Black-Scholes pricing with Greeks
- Protective put, laddered, and LEAPS strategies
- Real-time WebSocket updates
- TradingView-style charts via Lightweight-Charts
- Docker containerization
- GitLab CI/CD pipeline for VPS deployment
- VPN-only access configuration
2026-03-21 19:21:40 +01:00

73 lines
2.2 KiB
Python

"""Redis-backed caching utilities with graceful fallback support."""
from __future__ import annotations
import json
import logging
from datetime import datetime
from typing import Any
logger = logging.getLogger(__name__)
try:
from redis.asyncio import Redis
except ImportError: # pragma: no cover - optional dependency
Redis = None
class CacheService:
"""Small async cache wrapper around Redis."""
def __init__(self, url: str | None, default_ttl: int = 300) -> None:
self.url = url
self.default_ttl = default_ttl
self._client: Redis | None = None
self._enabled = bool(url and Redis)
@property
def enabled(self) -> bool:
return self._enabled and self._client is not None
async def connect(self) -> None:
if not self._enabled:
if self.url and Redis is None:
logger.warning("Redis URL configured but redis package is not installed; cache disabled")
return
try:
self._client = Redis.from_url(self.url, decode_responses=True)
await self._client.ping()
logger.info("Connected to Redis cache")
except Exception as exc: # pragma: no cover - network dependent
logger.warning("Redis unavailable, cache disabled: %s", exc)
self._client = None
async def close(self) -> None:
if self._client is None:
return
await self._client.aclose()
self._client = None
async def get_json(self, key: str) -> dict[str, Any] | list[Any] | None:
if self._client is None:
return None
value = await self._client.get(key)
if value is None:
return None
return json.loads(value)
async def set_json(self, key: str, value: Any, ttl: int | None = None) -> None:
if self._client is None:
return
payload = json.dumps(value, default=self._json_default)
await self._client.set(key, payload, ex=ttl or self.default_ttl)
@staticmethod
def _json_default(value: Any) -> str:
if isinstance(value, datetime):
return value.isoformat()
raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable")