Files
congsh ba6e7669e8 Initial commit: RSS platform phase 1 skeleton with code review fixes
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>
2026-06-15 17:01:57 +08:00

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()