- Implement tests to ensure the case tree contains parent and child branches while preventing cycles. - Verify that the timeline UI utilizes tree filters and cursor paging. - Confirm that the relation tree is displayed as a separate tab with appropriate search filters and inline module content. Enhance time tracking with prepaid card usage alerts - Create a migration to track alerts for prepaid card usage at 80% and 100%. - Implement triggers to generate cases when usage thresholds are reached, ensuring proper notifications and case management. Introduce audit logging for time entry edits - Modify the sync log to include time entry updates in the audit stream without altering existing records. Establish tracking for vTiger case imports and exports - Create tables to track cases imported from vTiger into Hub and exports from Hub to vTiger, ensuring idempotency and proper reference management.
419 lines
16 KiB
Python
419 lines
16 KiB
Python
"""
|
|
Configuration Module
|
|
Handles environment variables and application settings
|
|
"""
|
|
|
|
import os
|
|
from typing import List
|
|
from pydantic import field_validator
|
|
from pydantic_settings import BaseSettings
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Application settings loaded from environment variables"""
|
|
|
|
# Database
|
|
DATABASE_URL: str = "postgresql://bmc_hub:bmc_hub@localhost:5432/bmc_hub"
|
|
|
|
# Public website content administration API (HTTPS on Plesk)
|
|
WEBSITE_CONTENT_API_URL: str = "https://isp.bmcnetworks.dk/api/admin-content.php"
|
|
WEBSITE_CONTENT_API_HOST_HEADER: str = "ct.bmcnetworks.dk"
|
|
WEBSITE_CONTENT_API_TOKEN: str = ""
|
|
WEBSITE_CONTENT_API_TIMEOUT_SECONDS: int = 20
|
|
# Deprecated direct-MySQL settings retained temporarily for .env compatibility.
|
|
WEBSITE_CONTENT_MYSQL_HOST: str = ""
|
|
WEBSITE_CONTENT_MYSQL_PORT: int = 3306
|
|
WEBSITE_CONTENT_MYSQL_DATABASE: str = ""
|
|
WEBSITE_CONTENT_MYSQL_USER: str = ""
|
|
WEBSITE_CONTENT_MYSQL_PASSWORD: str = ""
|
|
WEBSITE_CONTENT_MYSQL_POOL_SIZE: int = 5
|
|
WEBSITE_CONTENT_MYSQL_CONNECT_TIMEOUT: int = 8
|
|
WEBSITE_CONTENT_LOGO_BASE_URL: str = ""
|
|
|
|
# API
|
|
API_HOST: str = "0.0.0.0"
|
|
API_PORT: int = 8000
|
|
API_RELOAD: bool = False
|
|
ENABLE_RELOAD: bool = False # Added to match docker-compose.yml
|
|
HUB_BASE_URL: str = "https://hub.bmcnetworks.dk"
|
|
|
|
# Non-interactive service token for Apple Configurator/cfgutil provisioning.
|
|
# Leave empty to disable the endpoint rather than accepting unauthenticated calls.
|
|
MOBILE_RECORDER_PROVISIONING_TOKEN: str = ""
|
|
|
|
# Elnet supplier lookup
|
|
ELNET_API_BASE_URL: str = "https://api.elnet.greenpowerdenmark.dk/api"
|
|
ELNET_TIMEOUT_SECONDS: int = 12
|
|
|
|
# API Gateway (Product catalog)
|
|
APIGW_BASE_URL: str = "https://apigateway.bmcnetworks.dk"
|
|
APIGATEWAY_URL: str = ""
|
|
APIGW_TOKEN: str = ""
|
|
APIGW_TIMEOUT_SECONDS: int = 12
|
|
|
|
# FirmaAPI (CVR company data)
|
|
FIRMAAPI_BASE_URL: str = "https://firmaapi.dk/api/v1"
|
|
FIRMAAPI_API_KEY: str = ""
|
|
FIRMAAPI_TIMEOUT_SECONDS: int = 12
|
|
|
|
# Security
|
|
SECRET_KEY: str = "dev-secret-key-change-in-production"
|
|
JWT_SECRET_KEY: str = "dev-jwt-secret-key-change-in-production"
|
|
COOKIE_SECURE: bool = False
|
|
COOKIE_SAMESITE: str = "lax"
|
|
ALLOWED_ORIGINS: List[str] = ["http://localhost:8000", "http://localhost:3000"]
|
|
CORS_ORIGINS: str = "http://localhost:8000,http://localhost:3000"
|
|
|
|
# Shadow Admin (emergency access)
|
|
SHADOW_ADMIN_ENABLED: bool = False
|
|
SHADOW_ADMIN_USERNAME: str = "shadowadmin"
|
|
SHADOW_ADMIN_PASSWORD: str = ""
|
|
SHADOW_ADMIN_TOTP_SECRET: str = ""
|
|
SHADOW_ADMIN_EMAIL: str = "shadowadmin@bmcnetworks.dk"
|
|
SHADOW_ADMIN_FULL_NAME: str = "Shadow Administrator"
|
|
|
|
# 2FA grace period (hours) before re-prompting
|
|
TWO_FA_GRACE_HOURS: int = 24
|
|
AUTH_DISABLE_2FA: bool = False
|
|
|
|
# Logging
|
|
LOG_LEVEL: str = "INFO"
|
|
LOG_FILE: str = "logs/app.log"
|
|
|
|
# e-conomic Integration
|
|
ECONOMIC_ENABLED: bool = False
|
|
ECONOMIC_API_URL: str = "https://restapi.e-conomic.com"
|
|
ECONOMIC_APP_SECRET_TOKEN: str = ""
|
|
ECONOMIC_AGREEMENT_GRANT_TOKEN: str = ""
|
|
ECONOMIC_READ_ONLY: bool = True
|
|
ECONOMIC_DRY_RUN: bool = True
|
|
|
|
# Manual migration centre
|
|
MIGRATION_CENTER_READ_ONLY: bool = False
|
|
MIGRATION_CENTER_STALE_AFTER_DAYS: int = 7
|
|
MIGRATION_CENTER_VTIGER_LOCK_FIELD: str = ""
|
|
MIGRATION_CENTER_SIMPLY_LOCK_FIELD: str = ""
|
|
|
|
# Nextcloud Integration
|
|
NEXTCLOUD_READ_ONLY: bool = True
|
|
NEXTCLOUD_DRY_RUN: bool = True
|
|
NEXTCLOUD_TIMEOUT_SECONDS: int = 15
|
|
NEXTCLOUD_CACHE_TTL_SECONDS: int = 300
|
|
NEXTCLOUD_ENCRYPTION_KEY: str = ""
|
|
|
|
# Links / Endpoints Module
|
|
LINKS_MODULE_ENABLED: bool = False
|
|
LINKS_READ_ONLY: bool = True
|
|
LINKS_DRY_RUN: bool = True
|
|
LINKS_DEAD_LINK_CHECK_ENABLED: bool = True
|
|
LINKS_DEAD_LINK_CHECK_INTERVAL_MINUTES: int = 60
|
|
LINKS_CHECK_TIMEOUT_SECONDS: int = 5
|
|
|
|
# Vaultwarden (Bitwarden-compatible)
|
|
VAULTWARDEN_BASE_URL: str = ""
|
|
VAULTWARDEN_API_TOKEN: str = ""
|
|
|
|
# Wiki.js Integration
|
|
WIKI_BASE_URL: str = "https://wiki.bmcnetworks.dk"
|
|
WIKI_API_TOKEN: str = ""
|
|
WIKI_API_KEY: str = ""
|
|
WIKI_TIMEOUT_SECONDS: int = 12
|
|
WIKI_READ_ONLY: bool = True
|
|
|
|
# Ollama LLM
|
|
OLLAMA_ENDPOINT: str = "http://172.16.31.195:11434"
|
|
OLLAMA_MODEL: str = "llama3.2"
|
|
# Primary company CVR used to prevent AI invoice extraction from treating
|
|
# the buyer (BMC) as the supplier.
|
|
OWN_CVR: str = "29522790"
|
|
|
|
# Email System Configuration
|
|
# IMAP Settings
|
|
IMAP_SERVER: str = ""
|
|
IMAP_PORT: int = 993
|
|
IMAP_USERNAME: str = ""
|
|
IMAP_PASSWORD: str = ""
|
|
IMAP_USE_SSL: bool = True
|
|
IMAP_FOLDER: str = "INBOX"
|
|
IMAP_TEST_FOLDER: str = ""
|
|
IMAP_READ_ONLY: bool = True
|
|
|
|
# Microsoft Graph API (alternative to IMAP)
|
|
USE_GRAPH_API: bool = False
|
|
GRAPH_TENANT_ID: str = ""
|
|
GRAPH_CLIENT_ID: str = ""
|
|
GRAPH_CLIENT_SECRET: str = ""
|
|
GRAPH_USER_EMAIL: str = ""
|
|
|
|
# Email Processing Settings
|
|
EMAIL_TO_TICKET_ENABLED: bool = False
|
|
EMAIL_RULES_ENABLED: bool = True
|
|
EMAIL_RULES_AUTO_PROCESS: bool = False
|
|
EMAIL_AI_ENABLED: bool = False
|
|
EMAIL_AUTO_CLASSIFY: bool = True # Enable classification by default (uses keywords if AI disabled)
|
|
EMAIL_AI_CONFIDENCE_THRESHOLD: float = 0.7
|
|
EMAIL_REQUIRE_MANUAL_APPROVAL: bool = True # Phase 1: human approval before case creation/routing
|
|
EMAIL_AUTO_CREATE_CASES_FROM_EMAIL: bool = False
|
|
EMAIL_MAX_FETCH_PER_RUN: int = 50
|
|
EMAIL_PROCESS_ALLOW_FOLDER_OVERRIDE: bool = True
|
|
EMAIL_PROCESS_INTERVAL_MINUTES: int = 5
|
|
EMAIL_WORKFLOWS_ENABLED: bool = True
|
|
EMAIL_WORKFLOW_AUTORUN_ENABLED: bool = False
|
|
EMAIL_MAX_UPLOAD_SIZE_MB: int = 50 # Max file size for email uploads
|
|
ALLOWED_EXTENSIONS: List[str] = ["pdf", "jpg", "jpeg", "png", "gif", "doc", "docx", "xls", "xlsx", "zip"] # Allowed file extensions for uploads
|
|
|
|
@field_validator("ALLOWED_EXTENSIONS", mode="before")
|
|
@classmethod
|
|
def parse_allowed_extensions(cls, v):
|
|
"""Handle both list and comma-separated string (e.g. from .env: .pdf,.jpg,...)"""
|
|
if isinstance(v, str):
|
|
# Split comma-separated and strip whitespace + leading dots
|
|
return [ext.strip().lstrip('.').lower() for ext in v.split(',') if ext.strip()]
|
|
if isinstance(v, list):
|
|
# Fix case where pydantic already wrapped entire CSV as single list element
|
|
if len(v) == 1 and ',' in str(v[0]):
|
|
return [ext.strip().lstrip('.').lower() for ext in str(v[0]).split(',') if ext.strip()]
|
|
return [ext.strip().lstrip('.').lower() for ext in v if ext]
|
|
return v
|
|
|
|
# vTiger Cloud Integration
|
|
VTIGER_ENABLED: bool = False
|
|
VTIGER_URL: str = ""
|
|
VTIGER_USERNAME: str = ""
|
|
VTIGER_API_KEY: str = ""
|
|
# VTiger Cases custom checkbox/flag used after a case has been imported into Hub.
|
|
VTIGER_CASE_HUBBED_FIELD: str = "cf_cases_hubbet"
|
|
|
|
# Data Consistency Settings
|
|
VTIGER_SYNC_ENABLED: bool = True
|
|
ECONOMIC_SYNC_ENABLED: bool = True
|
|
AUTO_CHECK_CONSISTENCY: bool = True
|
|
|
|
# Time Tracking Module Settings
|
|
TIMETRACKING_DEFAULT_HOURLY_RATE: float = 1200.00
|
|
TIMETRACKING_AUTO_ROUND: bool = True
|
|
TIMETRACKING_ROUND_INCREMENT: float = 0.5
|
|
TIMETRACKING_ROUND_METHOD: str = "up" # "up", "down", "nearest"
|
|
|
|
# Customer economic defaults
|
|
CUSTOMER_DEFAULT_MARGIN_PERCENT: float = 20.0
|
|
CUSTOMER_DEFAULT_INVOICE_FEE: float = 49.0
|
|
CUSTOMER_DEFAULT_HOURLY_RATE: float = 1200.0
|
|
|
|
# Time Tracking Module Safety Flags
|
|
TIMETRACKING_VTIGER_READ_ONLY: bool = True
|
|
TIMETRACKING_VTIGER_DRY_RUN: bool = True
|
|
TIMETRACKING_ECONOMIC_READ_ONLY: bool = True
|
|
TIMETRACKING_ECONOMIC_DRY_RUN: bool = True
|
|
TIMETRACKING_EXPORT_TYPE: str = "draft" # "draft" or "booked"
|
|
TIMETRACKING_ECONOMIC_LAYOUT: int = 19 # e-conomic invoice layout number (default: 19 = Danish standard)
|
|
TIMETRACKING_ECONOMIC_PRODUCT: str = "1000" # e-conomic product number for time entries (default: 1000)
|
|
|
|
# Global Ordre Module Safety Flags
|
|
ORDRE_ECONOMIC_READ_ONLY: bool = True
|
|
ORDRE_ECONOMIC_DRY_RUN: bool = True
|
|
ORDRE_ECONOMIC_LAYOUT: int = 19
|
|
ORDRE_ECONOMIC_PRODUCT: str = "1000"
|
|
|
|
# Simply-CRM (Old vTiger On-Premise)
|
|
OLD_VTIGER_URL: str = ""
|
|
OLD_VTIGER_USERNAME: str = ""
|
|
OLD_VTIGER_API_KEY: str = ""
|
|
|
|
# Simply-CRM (Separate System)
|
|
SIMPLYCRM_URL: str = ""
|
|
SIMPLYCRM_USERNAME: str = ""
|
|
SIMPLYCRM_API_KEY: str = ""
|
|
SIMPLYCRM_TICKET_MODULE: str = "Tickets"
|
|
SIMPLYCRM_TICKET_COMMENT_MODULE: str = "ModComments"
|
|
SIMPLYCRM_TICKET_COMMENT_RELATION_FIELD: str = "related_to"
|
|
SIMPLYCRM_TICKET_EMAIL_MODULE: str = "Emails"
|
|
SIMPLYCRM_TICKET_EMAIL_RELATION_FIELD: str = "parent_id"
|
|
SIMPLYCRM_TICKET_EMAIL_FALLBACK_RELATION_FIELD: str = "related_to"
|
|
ARCHIVED_VTIGER_SYNC_ENABLED: bool = True
|
|
ARCHIVED_VTIGER_SYNC_INTERVAL_MINUTES: int = 30
|
|
ARCHIVED_VTIGER_SYNC_LIMIT: int = 5000
|
|
ARCHIVED_VTIGER_SYNC_INCLUDE_MESSAGES: bool = False
|
|
PROJECT_CT_ARCHIVE_SYNC_ENABLED: bool = True
|
|
PROJECT_CT_ARCHIVE_SYNC_INTERVAL_MINUTES: int = 360
|
|
|
|
# Backup System Configuration
|
|
BACKUP_ENABLED: bool = True
|
|
BACKUP_STORAGE_PATH: str = "/app/data/backups"
|
|
BACKUP_DRY_RUN: bool = False
|
|
BACKUP_READ_ONLY: bool = False
|
|
BACKUP_RESTORE_DRY_RUN: bool = True # SAFETY: Test restore uden at overskrive data
|
|
BACKUP_RETENTION_DAYS: int = 30
|
|
BACKUP_RETENTION_MONTHLY: int = 12
|
|
BACKUP_MAX_SIZE_GB: int = 100
|
|
STORAGE_WARNING_THRESHOLD_PCT: int = 80
|
|
DB_DAILY_FORMAT: str = "dump" # Compressed format for daily backups
|
|
DB_MONTHLY_FORMAT: str = "sql" # Plain SQL for monthly backups
|
|
BACKUP_INCLUDE_UPLOADS: bool = True # Include uploads/ in file backups
|
|
BACKUP_INCLUDE_LOGS: bool = True # Include logs/ in file backups
|
|
BACKUP_INCLUDE_DATA: bool = True # Include data/ in file backups
|
|
UPLOAD_DIR: str = "uploads" # Upload directory path
|
|
|
|
# Bug report capture
|
|
BUG_REPORT_ENABLED: bool = True
|
|
BUG_REPORT_HOTKEY: str = "Ctrl+Shift+B"
|
|
BUG_REPORT_MAX_PER_HOUR: int = 12
|
|
BUG_REPORT_DEFAULT_CUSTOMER_ID: int = 1
|
|
BUG_REPORT_AUTO_ASSIGN_USER_ID: int | None = 1
|
|
BUG_REPORT_MAX_SCREENSHOT_BYTES: int = 8 * 1024 * 1024
|
|
BUG_REPORT_MAX_ATTACHMENT_BYTES: int = 20 * 1024 * 1024
|
|
|
|
# Offsite Backup Settings (SFTP)
|
|
OFFSITE_ENABLED: bool = False
|
|
OFFSITE_WEEKLY_DAY: str = "sunday"
|
|
OFFSITE_RETRY_DELAY_HOURS: int = 1
|
|
OFFSITE_RETRY_MAX_ATTEMPTS: int = 3
|
|
SFTP_HOST: str = ""
|
|
SFTP_PORT: int = 22
|
|
SFTP_USER: str = ""
|
|
SFTP_PASSWORD: str = ""
|
|
SFTP_REMOTE_PATH: str = "/backups"
|
|
SSH_KEY_PATH: str = ""
|
|
|
|
# Mattermost Notifications
|
|
MATTERMOST_WEBHOOK_URL: str = ""
|
|
MATTERMOST_ENABLED: bool = False
|
|
MATTERMOST_CHANNEL: str = ""
|
|
|
|
# Email Sending (SMTP) Configuration
|
|
EMAIL_SMTP_HOST: str = ""
|
|
EMAIL_SMTP_PORT: int = 587
|
|
EMAIL_SMTP_USER: str = ""
|
|
EMAIL_SMTP_PASSWORD: str = ""
|
|
EMAIL_SMTP_USE_TLS: bool = True
|
|
EMAIL_SMTP_FROM_ADDRESS: str = "noreply@bmcnetworks.dk"
|
|
EMAIL_SMTP_FROM_NAME: str = "BMC Hub"
|
|
|
|
# Reminder System Configuration
|
|
REMINDERS_ENABLED: bool = False
|
|
REMINDERS_EMAIL_ENABLED: bool = False
|
|
REMINDERS_MATTERMOST_ENABLED: bool = False
|
|
REMINDERS_DRY_RUN: bool = True # SAFETY: Log without sending if true
|
|
REMINDERS_CHECK_INTERVAL_MINUTES: int = 5
|
|
REMINDERS_MAX_PER_USER_PER_HOUR: int = 5
|
|
REMINDERS_QUEUE_BATCH_SIZE: int = 10
|
|
|
|
# AnyDesk Remote Support Integration
|
|
ANYDESK_API_URL: str = "https://v1.api.anydesk.com:8081" # AnyDesk REST API base URL
|
|
ANYDESK_LICENSE_ID: str = ""
|
|
ANYDESK_API_TOKEN: str = "" # API Password (HMAC-SHA1, not Bearer) from my.anydesk.com
|
|
ANYDESK_PASSWORD: str = "" # Alias for ANYDESK_API_TOKEN
|
|
ANYDESK_READ_ONLY: bool = True # SAFETY: Prevent API calls if true
|
|
ANYDESK_DRY_RUN: bool = True # SAFETY: Log without executing API calls
|
|
ANYDESK_TIMEOUT_SECONDS: int = 30
|
|
ANYDESK_AUTO_START_SESSION: bool = True # Auto-start session when requested
|
|
ANYDESK_LOCAL_SESSIONS_URL: str = "http://localhost:8001/anydesk/sessions"
|
|
ANYDESK_LOCAL_SYNC_ENABLED: bool = True
|
|
ANYDESK_LOCAL_SYNC_INTERVAL_MINUTES: int = 15
|
|
ANYDESK_LOCAL_SYNC_TIMEOUT_SECONDS: int = 20
|
|
ANYDESK_LOCAL_SYNC_DRY_RUN: bool = False
|
|
|
|
# Telefoni (Yealink) Integration
|
|
TELEFONI_SHARED_SECRET: str = "" # If set, required as ?token=...
|
|
TELEFONI_IP_WHITELIST: str = "172.16.31.0/24" # CSV of IPs/CIDRs, e.g. "192.168.1.0/24,10.0.0.10"
|
|
|
|
# Mission Control webhooks
|
|
MISSION_WEBHOOK_TOKEN: str = ""
|
|
|
|
# ESET Integration
|
|
ESET_ENABLED: bool = False
|
|
ESET_API_URL: str = "https://eu.device-management.eset.systems"
|
|
ESET_IAM_URL: str = "https://eu.business-account.iam.eset.systems"
|
|
ESET_INCIDENTS_URL: str = "https://eu.incident-management.eset.systems"
|
|
ESET_USERNAME: str = ""
|
|
ESET_PASSWORD: str = ""
|
|
ESET_OAUTH_CLIENT_ID: str = ""
|
|
ESET_OAUTH_CLIENT_SECRET: str = ""
|
|
ESET_OAUTH_SCOPE: str = ""
|
|
ESET_READ_ONLY: bool = True
|
|
ESET_TIMEOUT_SECONDS: int = 30
|
|
ESET_SYNC_ENABLED: bool = True
|
|
ESET_SYNC_INTERVAL_MINUTES: int = 120
|
|
ESET_INCIDENTS_ENABLED: bool = True
|
|
|
|
# SMS Integration (CPSMS)
|
|
SMS_API_KEY: str = ""
|
|
SMS_USERNAME: str = ""
|
|
SMS_SENDER: str = "BMC Networks"
|
|
SMS_WEBHOOK_SECRET: str = ""
|
|
|
|
# FedEx Integration
|
|
FEDEX_ENABLED: bool = False
|
|
FEDEX_READ_ONLY: bool = True
|
|
FEDEX_DRY_RUN: bool = True
|
|
FEDEX_API_KEY: str = ""
|
|
FEDEX_API_SECRET: str = ""
|
|
FEDEX_ACCOUNT_NUMBER: str = ""
|
|
FEDEX_BASE_URL: str = ""
|
|
FEDEX_TIMEOUT_SECONDS: int = 20
|
|
|
|
# Shipmondo Integration
|
|
SHIPMONDO_ENABLED: bool = False
|
|
SHIPMONDO_READ_ONLY: bool = True
|
|
SHIPMONDO_DRY_RUN: bool = True
|
|
SHIPMONDO_API_BASE_URL: str = "https://app.shipmondo.com/api/public/v3"
|
|
SHIPMONDO_API_USER: str = ""
|
|
SHIPMONDO_API_KEY: str = ""
|
|
SHIPMONDO_TIMEOUT_SECONDS: int = 30
|
|
SHIPMONDO_SENDER_NAME: str = "BMC Networks"
|
|
SHIPMONDO_SENDER_ATTENTION: str = ""
|
|
SHIPMONDO_SENDER_ADDRESS1: str = ""
|
|
SHIPMONDO_SENDER_ADDRESS2: str = ""
|
|
SHIPMONDO_SENDER_POSTAL_CODE: str = ""
|
|
SHIPMONDO_SENDER_CITY: str = ""
|
|
SHIPMONDO_SENDER_COUNTRY_CODE: str = "DK"
|
|
SHIPMONDO_SENDER_EMAIL: str = ""
|
|
SHIPMONDO_SENDER_PHONE: str = ""
|
|
|
|
# ALSO Cloud Marketplace Integration
|
|
ALSO_ENABLED: bool = False
|
|
ALSO_READ_ONLY: bool = True
|
|
ALSO_DRY_RUN: bool = True
|
|
ALSO_API_BASE_URL: str = ""
|
|
ALSO_API_KEY: str = ""
|
|
ALSO_API_SECRET: str = ""
|
|
ALSO_TIMEOUT_SECONDS: int = 20
|
|
|
|
# Bottom bar module
|
|
BOTTOM_BAR_ENABLED: bool = False
|
|
|
|
# Dev-only shortcuts
|
|
DEV_ALLOW_ARCHIVED_IMPORT: bool = False
|
|
|
|
# Deployment Configuration (used by Docker/Podman)
|
|
POSTGRES_USER: str = "bmc_hub"
|
|
POSTGRES_PASSWORD: str = "bmc_hub"
|
|
POSTGRES_DB: str = "bmc_hub"
|
|
POSTGRES_PORT: int = 5432
|
|
CONTAINER_RUNTIME: str = "docker"
|
|
RELEASE_VERSION: str = "latest"
|
|
GITEA_URL: str = "https://g.bmcnetworks.dk"
|
|
GITHUB_TOKEN: str = ""
|
|
GITHUB_REPO: str = "ct/bmc_hub"
|
|
|
|
# Whisper Transcription
|
|
WHISPER_ENABLED: bool = True
|
|
WHISPER_API_URL: str = "http://172.16.31.115:5000/transcribe"
|
|
WHISPER_TIMEOUT: int = 300
|
|
WHISPER_SUPPORTED_FORMATS: List[str] = [".mp3", ".wav", ".m4a", ".ogg"]
|
|
|
|
@field_validator('*', mode='before')
|
|
@classmethod
|
|
def strip_whitespace(cls, v):
|
|
"""Strip leading/trailing whitespace from string values"""
|
|
if isinstance(v, str):
|
|
return v.strip()
|
|
return v
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
case_sensitive = True
|
|
|
|
|
|
settings = Settings()
|