企業內部新聞彙整與分析系統 - 自動新聞抓取 (Digitimes, 經濟日報, 工商時報) - AI 智慧摘要 (OpenAI/Claude/Ollama) - 群組管理與訂閱通知 - 已清理 Python 快取檔案 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
138 lines
4.3 KiB
Python
138 lines
4.3 KiB
Python
"""
|
||
應用程式設定模組
|
||
使用 Pydantic Settings 管理環境變數
|
||
"""
|
||
from functools import lru_cache
|
||
from typing import Literal
|
||
from pydantic import Field
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""應用程式設定"""
|
||
model_config = SettingsConfigDict(
|
||
env_file=".env",
|
||
env_file_encoding="utf-8",
|
||
case_sensitive=False
|
||
)
|
||
|
||
# 應用程式
|
||
app_name: str = "每日報導APP"
|
||
app_env: Literal["development", "staging", "production"] = "development"
|
||
debug: bool = Field(
|
||
default=False, # 預設為 False,更安全
|
||
description="除錯模式,僅開發環境使用"
|
||
)
|
||
secret_key: str = Field(
|
||
default="change-me-in-production",
|
||
description="應用程式密鑰,生產環境必須透過環境變數設定"
|
||
)
|
||
|
||
# 資料庫
|
||
db_host: str = "localhost"
|
||
db_port: int = 3306
|
||
db_name: str = "daily_news_app"
|
||
db_user: str = "root"
|
||
db_password: str = ""
|
||
|
||
@property
|
||
def database_url(self) -> str:
|
||
if self.db_host == "sqlite":
|
||
return f"sqlite:///{self.db_name}.db"
|
||
return f"mysql+pymysql://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}?charset=utf8mb4"
|
||
|
||
@property
|
||
def async_database_url(self) -> str:
|
||
if self.db_host == "sqlite":
|
||
return f"sqlite+aiosqlite:///{self.db_name}.db"
|
||
return f"mysql+aiomysql://{self.db_user}:{self.db_password}@{self.db_host}:{self.db_port}/{self.db_name}?charset=utf8mb4"
|
||
|
||
# JWT
|
||
jwt_secret_key: str = Field(
|
||
default="change-me",
|
||
description="JWT 簽章密鑰,生產環境必須透過環境變數設定"
|
||
)
|
||
jwt_algorithm: str = "HS256"
|
||
jwt_access_token_expire_minutes: int = Field(
|
||
default=480, # 開發環境預設值
|
||
description="JWT Token 過期時間(分鐘),建議生產環境設為 60-120 分鐘"
|
||
)
|
||
|
||
# LDAP
|
||
ldap_server: str = ""
|
||
ldap_port: int = 389
|
||
ldap_base_dn: str = ""
|
||
ldap_bind_dn: str = ""
|
||
ldap_bind_password: str = ""
|
||
|
||
# LLM
|
||
llm_provider: Literal["gemini", "openai", "ollama"] = "gemini"
|
||
gemini_api_key: str = ""
|
||
gemini_model: str = "gemini-1.5-pro"
|
||
openai_api_key: str = ""
|
||
openai_model: str = "gpt-4o"
|
||
ollama_endpoint: str = "http://localhost:11434"
|
||
ollama_model: str = "llama3"
|
||
|
||
# SMTP
|
||
smtp_host: str = ""
|
||
smtp_port: int = 587
|
||
smtp_username: str = ""
|
||
smtp_password: str = ""
|
||
smtp_from_email: str = ""
|
||
smtp_from_name: str = "每日報導系統"
|
||
|
||
# 爬蟲
|
||
crawl_schedule_time: str = "08:00"
|
||
crawl_request_delay: int = 3
|
||
crawl_max_retries: int = 3
|
||
|
||
# Digitimes
|
||
digitimes_username: str = ""
|
||
digitimes_password: str = ""
|
||
|
||
# 資料保留
|
||
data_retention_days: int = 60
|
||
|
||
# PDF
|
||
pdf_logo_path: str = ""
|
||
pdf_header_text: str = ""
|
||
pdf_footer_text: str = "本報告僅供內部參考使用"
|
||
|
||
# CORS 設定
|
||
cors_origins: list[str] = Field(
|
||
default=["http://localhost:3000", "http://localhost:8000"],
|
||
description="允許的 CORS 來源列表,生產環境必須明確指定,不能使用 *"
|
||
)
|
||
|
||
# 管理員預設密碼
|
||
admin_password: str = Field(
|
||
default="admin123",
|
||
description="管理員預設密碼"
|
||
)
|
||
|
||
|
||
def validate_secrets():
|
||
"""驗證生產環境的密鑰設定"""
|
||
if settings.app_env == "production":
|
||
if settings.secret_key == "change-me-in-production":
|
||
raise ValueError("生產環境必須設定 SECRET_KEY 環境變數")
|
||
if settings.jwt_secret_key == "change-me":
|
||
raise ValueError("生產環境必須設定 JWT_SECRET_KEY 環境變數")
|
||
if len(settings.secret_key) < 32:
|
||
raise ValueError("SECRET_KEY 長度必須至少 32 字元")
|
||
if len(settings.jwt_secret_key) < 32:
|
||
raise ValueError("JWT_SECRET_KEY 長度必須至少 32 字元")
|
||
if settings.jwt_access_token_expire_minutes > 120:
|
||
import warnings
|
||
warnings.warn("生產環境 JWT Token 過期時間建議不超過 120 分鐘")
|
||
|
||
|
||
@lru_cache
|
||
def get_settings() -> Settings:
|
||
"""取得設定實例(快取)"""
|
||
return Settings()
|
||
|
||
|
||
settings = get_settings()
|