751097b347
Security:
- Refuse startup with default secret_key in production (was just logging)
- Settings endpoint now requires admin role
- Password validation on initial setup
- DOM-based HTML sanitizer replaces regex in template previews
- Add *.log to .gitignore
Performance & reliability:
- Token refresh deduplication prevents race condition on concurrent 401s
- Theme media query listener registered once (no leak)
- IconPicker uses $derived instead of function call per render
- Snackbar uses single-batch state update instead of while loop
- Replace 11 inline hover handlers with CSS :hover in layout
Architecture - receivers-only:
- Delivery endpoints (chat_id, email, url, room_id, topic) now stored
exclusively in TargetReceiver rows, never in target.config
- Migration extracts existing delivery fields to receiver rows
- Notifier and dispatcher remove all config fallbacks
- Frontend targets page shows receivers list per target with
add/remove/toggle/test per receiver
- Single-receiver test endpoint: POST /targets/{id}/receivers/{id}/test
Code quality:
- Extract AuthLayout.svelte from login/setup (150 lines CSS dedup)
- Split telegram-bots page (754→51 lines + 3 tab components)
- Split notification-trackers page (547→432 lines + 4 components)
- Deduplicate _send_reply into shared handler.send_reply()
- Add locale column to template models, replace name-based detection
- Fix delete_notification_tracker dead protection check
- Fix check_telegram_bot query (filter by type, remove bogus OR)
- Add graceful scheduler shutdown in lifespan
- Consistent /bots?tab=telegram URLs across all nav links
i18n:
- Error page, chat actions, target types, provider types internationalized
- All new receiver UI strings in EN + RU
47 lines
1.3 KiB
Python
47 lines
1.3 KiB
Python
"""Server configuration from environment variables."""
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from pydantic_settings import BaseSettings
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings loaded from environment variables."""
|
|
|
|
data_dir: Path = Path("/data")
|
|
database_url: str = ""
|
|
|
|
secret_key: str = "change-me-in-production"
|
|
|
|
def model_post_init(self, __context: Any) -> None:
|
|
if self.secret_key == "change-me-in-production" and not self.debug:
|
|
raise ValueError(
|
|
"SECURITY: Cannot start with default secret_key in production. "
|
|
"Set NOTIFY_BRIDGE_SECRET_KEY environment variable."
|
|
)
|
|
|
|
access_token_expire_minutes: int = 60
|
|
refresh_token_expire_days: int = 30
|
|
|
|
host: str = "0.0.0.0"
|
|
port: int = 8420
|
|
debug: bool = False
|
|
|
|
anthropic_api_key: str = ""
|
|
ai_model: str = "claude-sonnet-4-20250514"
|
|
ai_max_tokens: int = 1024
|
|
|
|
telegram_webhook_secret: str = ""
|
|
|
|
model_config = {"env_prefix": "NOTIFY_BRIDGE_"}
|
|
|
|
@property
|
|
def effective_database_url(self) -> str:
|
|
if self.database_url:
|
|
return self.database_url
|
|
db_path = self.data_dir / "notify_bridge.db"
|
|
return f"sqlite+aiosqlite:///{db_path}"
|
|
|
|
|
|
settings = Settings()
|