T01: core infrastructure + auth + multi-tenant + RLS
- 10 models: tenants, users, user_tenants, roles, sessions, audit_log, deletion_log, notifications, password_reset_tokens, api_tokens - Session-based auth (Redis + PostgreSQL audit trail) - Multi-tenant with ORM-level filtering + PostgreSQL RLS (set_config) - RBAC with roles/permissions + field-level permissions - CSRF protection via Origin header validation - Auth rate limiting (Redis counters with TTL) - CORS with explicit origins (no wildcard) - Health endpoint (no auth required) - Notification service + audit log middleware - 29 tests, 26 ACs, all passing - Coverage: 62% (infrastructure modules pending coverage in later tasks)
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"""Redis cache wrapper."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import redis.asyncio as aioredis
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
|
||||
_cache_redis: aioredis.Redis | None = None
|
||||
|
||||
|
||||
def get_cache() -> aioredis.Redis:
|
||||
"""Get or create the cache Redis client."""
|
||||
global _cache_redis
|
||||
if _cache_redis is None:
|
||||
_cache_redis = aioredis.from_url(get_settings().redis_url, decode_responses=True)
|
||||
return _cache_redis
|
||||
|
||||
|
||||
async def cache_get(key: str) -> Any | None:
|
||||
"""Get a value from cache."""
|
||||
r = get_cache()
|
||||
raw = await r.get(key)
|
||||
if raw is None:
|
||||
return None
|
||||
return json.loads(raw)
|
||||
|
||||
|
||||
async def cache_set(key: str, value: Any, ttl: int = 300) -> None:
|
||||
"""Set a value in cache with TTL."""
|
||||
r = get_cache()
|
||||
await r.setex(key, ttl, json.dumps(value))
|
||||
|
||||
|
||||
async def cache_delete(key: str) -> None:
|
||||
"""Delete a key from cache."""
|
||||
r = get_cache()
|
||||
await r.delete(key)
|
||||
|
||||
|
||||
async def cache_flush_pattern(pattern: str) -> None:
|
||||
"""Delete all keys matching a pattern."""
|
||||
r = get_cache()
|
||||
keys = await r.keys(pattern)
|
||||
if keys:
|
||||
await r.delete(*keys)
|
||||
Reference in New Issue
Block a user