Adds forgot/reset password flow with dev-mode email logging, multiple named/revocable API keys per user (replacing the single legacy key model while keeping it working), per-tool-call usage analytics recorded from the MCP call_tool handler, and a separate admin API (auth/queries/routes) for managing all users and viewing system-wide analytics — authenticated via a hardcoded operator identity, fully isolated from regular user auth. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
54 lines
1.5 KiB
Python
54 lines
1.5 KiB
Python
from __future__ import annotations
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
# Server
|
|
host: str = "0.0.0.0"
|
|
port: int = 8000
|
|
mcp_server_name: str = "mt-odoo-mcp"
|
|
mcp_log_level: str = "INFO"
|
|
|
|
# Public URL (used in API responses so clients know where to connect)
|
|
public_url: str = "http://localhost:8000"
|
|
|
|
# CORS — comma-separated list of allowed frontend origins
|
|
cors_origins: str = "http://localhost:3000"
|
|
|
|
# Security
|
|
secret_key: str = "change-me"
|
|
encryption_key: str = "change-me"
|
|
|
|
# Database
|
|
database_path: str = "./mt_odoo_mcp.db"
|
|
|
|
# Delete confirmation token TTL (seconds)
|
|
confirm_token_ttl: int = 300
|
|
|
|
# Audit
|
|
audit_log_file: str = "./audit.jsonl"
|
|
audit_log_enabled: bool = True
|
|
|
|
# Password reset email — in dev mode the reset link is logged instead of emailed
|
|
smtp_dev_mode: bool = True
|
|
smtp_host: str = ""
|
|
smtp_port: int = 587
|
|
smtp_user: str = ""
|
|
smtp_password: str = ""
|
|
smtp_from_email: str = "no-reply@odoomcp.cloud"
|
|
frontend_base_url: str = "http://localhost:3000"
|
|
|
|
# Admin panel — single hardcoded operator identity, not tied to the users table
|
|
admin_email: str = "admin@example.com"
|
|
admin_password: str = "change-me"
|
|
admin_session_ttl_hours: int = 12
|
|
|
|
@property
|
|
def cors_origins_list(self) -> list[str]:
|
|
return [o.strip() for o in self.cors_origins.split(",") if o.strip()]
|
|
|
|
|
|
settings = Settings()
|