ba6e7669e8
Features: - FastAPI + SQLAlchemy 2.0 async + PostgreSQL/pgvector + Redis backend - Vue 3 + TypeScript + Element Plus frontend - JWT auth with access/refresh tokens and revocation - Admin/member RBAC - RSS feed CRUD and article listing - Settings management with Fernet encryption for sensitive values - Redis distributed lock service - Alembic initial migration - Docker Compose development environment Fixes from code review: - Fix DB session leak in dependency injection - Restrict registration to admin only - Add default admin password warning - Implement JWT refresh tokens and jti blacklist - Strengthen password policy - Use func.count for pagination totals - Replace NullPool with AsyncAdaptedQueuePool - Remove init_db from lifespan to enforce alembic migrations - Add request_id middleware and logging filter - Fix vite.config.ts env loading - Add frontend token refresh interceptor - Add Vue error handler Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
1.7 KiB
Python
71 lines
1.7 KiB
Python
"""Application configuration."""
|
|
from pathlib import Path
|
|
|
|
from pydantic import Field
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings loaded from environment variables."""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
extra="ignore",
|
|
)
|
|
|
|
# Database
|
|
DATABASE_URL: str = "postgresql+asyncpg://rss:rss@postgres:5432/rss_platform"
|
|
|
|
# Redis
|
|
REDIS_URL: str = "redis://redis:6379/0"
|
|
|
|
# JWT
|
|
SECRET_KEY: str = Field(..., min_length=32)
|
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 15
|
|
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
|
|
|
# AI
|
|
AI_DEFAULT_PROVIDER: str = "openai"
|
|
AI_DEFAULT_MODEL: str = "gpt-4o-mini"
|
|
|
|
# Storage
|
|
STORAGE_TYPE: str = "minio"
|
|
MINIO_ENDPOINT: str = "minio:9000"
|
|
MINIO_ACCESS_KEY: str = "minioadmin"
|
|
MINIO_SECRET_KEY: str = "minioadmin"
|
|
MINIO_BUCKET: str = "rss-platform"
|
|
|
|
# CORS
|
|
CORS_ALLOWED_ORIGINS: str = ""
|
|
|
|
# Default admin
|
|
DEFAULT_ADMIN_USERNAME: str = "admin"
|
|
DEFAULT_ADMIN_PASSWORD: str = "admin"
|
|
|
|
# Sensitive settings encryption
|
|
SETTINGS_ENCRYPTION_KEY: str = ""
|
|
|
|
# Logging
|
|
LOG_LEVEL: str = "INFO"
|
|
|
|
# RSS Fetching
|
|
FETCH_CONCURRENCY: int = 10
|
|
FETCH_TIMEOUT: int = 30
|
|
DEFAULT_FETCH_INTERVAL: int = 60
|
|
MIN_FETCH_INTERVAL: int = 15
|
|
|
|
# Ports (for reference)
|
|
BACKEND_PORT: int = 8000
|
|
FRONTEND_PORT: int = 5173
|
|
|
|
@property
|
|
def cors_origins(self) -> list[str]:
|
|
"""Parse CORS_ALLOWED_ORIGINS into list."""
|
|
if not self.CORS_ALLOWED_ORIGINS:
|
|
return []
|
|
return [origin.strip() for origin in self.CORS_ALLOWED_ORIGINS.split(",") if origin.strip()]
|
|
|
|
|
|
settings = Settings()
|