Files
kite/backend/config.py
T
curo1305 3ed6dd494f feat(03-02): extend StorageBackend ABC and MinIOBackend with presigned PUT and stat_object
- Add generate_presigned_put_url and stat_object abstract methods to StorageBackend ABC
- Extend MinIOBackend with dual client (self._client internal + self._public_client public)
- MinIOBackend.__init__ accepts optional public_endpoint param (RESEARCH.md Finding 3)
- generate_presigned_put_url uses self._public_client for browser-resolvable URLs
- stat_object uses self._client.stat_object and returns .size (authoritative, T-03-05)
- get_storage_backend() passes public_endpoint=settings.minio_public_endpoint
- config.py adds minio_public_endpoint field (RESEARCH.md Finding 3)
- docker-compose.yml: MINIO_API_CORS_ALLOW_ORIGIN on minio service (T-03-09)
- docker-compose.yml: MINIO_PUBLIC_ENDPOINT on backend service
- docker-compose.yml: new celery-beat service (RESEARCH.md Finding 10)
2026-05-23 13:52:16 +02:00

95 lines
3.1 KiB
Python

from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Phase 1 Pydantic Settings — reads all Phase 1 env vars from environment or .env file."""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
env_list_separator=",",
)
# Data directory — used only for the flat-file settings.json path (Phase 1)
data_dir: str = "/app/data"
# PostgreSQL
database_url: str = "postgresql+psycopg://docuvault_app:changeme_app@postgres:5432/docuvault"
database_migrate_url: str = "postgresql+psycopg://docuvault_migrate:changeme_migrate@postgres:5432/docuvault"
# MinIO
minio_endpoint: str = "minio:9000"
minio_access_key: str = "docuvault_app"
minio_secret_key: str = "changeme_minio_app"
minio_bucket: str = "docuvault"
# RESEARCH.md Finding 3 — browser-resolvable hostname for presigned URLs.
# Empty string means fall back to minio_endpoint inside MinIOBackend.
minio_public_endpoint: str = ""
# Redis / Celery
redis_url: str = "redis://:changeme_redis@redis:6379/0"
# Security (Phase 2 — documented now, not read by Phase 1 code paths)
secret_key: str = "CHANGEME"
# Auth / JWT (Phase 2)
access_token_expire_minutes: int = 15
refresh_token_expire_days: int = 30
# SMTP (Phase 2 — D-01)
smtp_host: str = ""
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
smtp_from: str = "noreply@docuvault.local"
# Admin bootstrap (Phase 2 — D-04)
admin_email: str = ""
admin_password: str = ""
# CORS (Phase 2 — D-09)
cors_origins: list[str] = ["http://localhost:5173"]
# Frontend URL — used to build password reset links (D-02, D-03)
frontend_url: str = "http://localhost:5173"
settings = Settings()
# SETTINGS_FILE: still flat-file in Phase 1; migrates to users.ai_provider in Phase 2
SETTINGS_FILE = Path(settings.data_dir) / "settings.json"
DEFAULT_SYSTEM_PROMPT = """You are a document classification assistant. When given a document's text content and a list of existing topics, you must:
1. Assign the document to one or more relevant topics from the list.
2. If no existing topics fit well, suggest new topic names.
Return ONLY valid JSON in this exact format, with no additional text or explanation:
{"assigned_topics": ["topic1"], "new_topic_suggestions": ["new topic name"]}
If the document fits no topics and you have no suggestions, return: {"assigned_topics": [], "new_topic_suggestions": []}"""
DEFAULT_SETTINGS = {
"system_prompt": DEFAULT_SYSTEM_PROMPT,
"active_provider": "lmstudio",
"providers": {
"anthropic": {
"api_key": "",
"model": "claude-sonnet-4-6"
},
"openai": {
"api_key": "",
"model": "gpt-4o",
"base_url": None
},
"ollama": {
"base_url": "http://host.docker.internal:11434",
"model": "llama3.2"
},
"lmstudio": {
"base_url": "http://host.docker.internal:1234",
"model": "gemma-4-e4b-it"
}
}
}