Files
flowdeck/app/config.py
T
bruno fc8548194a
FlowDeck CI / lint (push) Failing after 1m32s
FlowDeck CI / test (push) Failing after 27m50s
FlowDeck CI / docker (push) Skipped
feat(templates): refonte complète des templates façon Notion + vues/agents-skills
Templates (v7.71.x) :

- registre unifié \	emplates\ (migrations 48-49) + TemplateService.instantiate unique (UI, API v2, agent, scheduler)

- sélecteur (pilule page vide, menu •••, commande /template), gestionnaire /templates, menu New ▾, From template, base inline dans un document

- 141 presets système (59 pages, 42 bases, 15 blocs, 25 lignes), titre auto depuis le template, variables title réservée

- récurrences RRULE + scheduler dédupliqué, agent apply_template/list_templates, API /api/templates + /api/v2/fd-templates

- correctifs : bouton Templates, centrage fenêtre, filtres CSP, flux de création, variable title

- tests : tests/test_fd_templates.py (19) et e2e/templates_picker.spec.js (8)

Inclut le travail déjà présent dans le working tree (vues Notion : view_query/view_aggregate/form_projection/geocoding, property_types, database_table, docs agents-skills) et ignore .playwright-mcp/.
2026-10-10 18:52:19 -04:00

175 lines
7.3 KiB
Python

"""FlowDeck — Configuration via pydantic-settings."""
from __future__ import annotations
import os
from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
@property
def data_dir(self) -> str:
"""Racine des fichiers (avatars, uploads…).
Pas un champ : la lecture est faite à chaque accès parce que les tests
monkeypatchent `FLOWDECK_DATA_DIR` en cours de vie (A42 — les 9 copies
de `Path(os.environ.get("FLOWDECK_DATA_DIR", "/data"))` vont ici).
"""
return os.environ.get("FLOWDECK_DATA_DIR", "/data")
model_config = SettingsConfigDict(
env_file=".env", env_file_encoding="utf-8", extra="ignore"
)
# Gitea
gitea_url: str = "https://git.dracodev.net"
gitea_token: str = "change-me"
gitea_oauth_client_id: str = "test-id"
gitea_oauth_client_secret: str = "test-secret"
gitea_webhook_secret: str = ""
# GitHub
github_oauth_client_id: str = ""
github_oauth_client_secret: str = ""
# OAuth2 — empty = dynamic per-request redirect URI (Host + X-Forwarded-*),
# set this ONLY to pin an exact URI (must be registered in Gitea/GitHub)
oauth_redirect_uri: str = ""
# Webhook
webhook_base_url: str = "http://localhost:8080"
# App
app_secret_key: str = "change-me-to-random"
app_host: str = "0.0.0.0"
app_port: int = 8080
log_level: str = "INFO"
default_lang: str = "fr"
# Rate limiting
rate_limit_enabled: bool = True
rate_limit_requests: int = 60 # per minute
# Public API v2 (v6.3.0)
public_api_insecure_ok: bool = False # if True, fd-public-key is accepted (dev only)
api_v2_rate_limit_per_token: int = 300 # req/min per token for /api/v2
# Database
database_url: str = "sqlite:////data/flowdeck.db"
# Sync
sync_interval: int = 60
gitea_cache_ttl: int = 30
# Backup (v5.2.0) — scheduled daily snapshot of the SQLite file
backup_enabled: bool = True
backup_dir: str = "/data/backups"
backup_interval_hours: int = 24
backup_keep: int = 30
# Forge projects sync (v5.2.0) — periodic refresh of `projects` table
project_sync_enabled: bool = True
project_sync_interval_hours: int = 1
# Reminders (v5.8.0) — background scan for due date reminders
reminders_enabled: bool = True
reminder_scan_interval_seconds: int = 60
# Webhooks outbound (v6.4.0) — retry of failed deliveries
webhook_retry_enabled: bool = True
webhook_retry_interval_seconds: int = 60
# Email / SMTP notifications (v4.9.0) — optional. If smtp_host is empty,
# email notifications are skipped (only in-app notifications are delivered).
smtp_host: str = ""
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
smtp_from: str = "FlowDeck <[email protected]>"
smtp_use_tls: bool = True
app_base_url: str = "http://localhost:8080"
# SSO / SAML + OIDC (v6.7.0) — bootstrap fallback ONLY: as soon as an admin
# saves a configuration in Settings → Admin → SSO / Enterprise, the
# `sso_config` table wins (see app/services/sso_provisioning.py).
sso_provider: str = "" # 'saml' | 'oidc' | '' (disabled)
sso_name: str = "Company SSO" # button label on the login page
sso_entity_id: str = "" # SAML: IdP entity id
sso_sso_url: str = "" # SAML: IdP SSO URL (HTTP-Redirect)
sso_slo_url: str = "" # SAML: IdP Single Logout URL
sso_x509_certificate: str = "" # SAML: IdP signing certificate (PEM)
sso_issuer_url: str = "" # OIDC: issuer identifier
sso_client_id: str = "" # OIDC: client id
sso_client_secret: str = "" # OIDC: client secret (env only)
sso_scope: str = "openid profile email"
sso_attribute_mapping: str = "" # JSON, defaults per provider
sso_groups_mapping: str = "[]" # JSON [{sso_group, workspace_role, workspace_id}]
sso_auto_provision: bool = True
sso_only: bool = False # refuse local login when true
sso_sign_requests: bool = False # sign AuthnRequest / LogoutRequest
sso_default_workspace_id: int = 0
# FlowDeck Agent (v4.10.0) — multi-LLM. Empty keys → offline/mock mode
# (deterministic rule-based planner so the agent works without any API key).
agent_enabled: bool = True
llm_provider: str = "offline" # any id from llm_client.PROVIDERS
# (openai, anthropic, mistral, cohere,
# google, groq, deepseek, openrouter,
# nvidia, together, perplexity, xai,
# qwencloud, minimax, morph, fireworks,
# cerebras, sambanova, chutes, xiaomi,
# sealion, sensenova, ollama, offline)
llm_model: str = "gpt-4o"
llm_api_key: str = ""
llm_api_base: str = "" # custom base URL (Ollama, OpenRouter, ...)
agent_max_iterations: int = 12
agent_max_tokens_budget: int = 500000
agent_run_timeout_seconds: int = 300
# ── Connecteurs OAuth (v7.56.0) — Google / Microsoft 365 ──
# Client OAuth2 à créer dans les consoles : Google Cloud (OAuth client web)
# et Entra ID (application web). Le redirect URI est dérivé d'APP_BASE_URL.
google_client_id: str = ""
google_client_secret: str = ""
ms_client_id: str = ""
ms_client_secret: str = ""
# ── Mémoire de l'agent (v7.54.0) ──
# État initial du toggle « Mémoire » à la création d'une conversation ;
# le basculement se fait ensuite par PATCH /api/agent/conversations/{id}.
agent_memory_default: bool = True
# ── Web tools de l'agent (web_search / fetch_url / search_code) ──
# `web_search_provider` : « exa » (recherche sémantique + snippets, clé
# requise) ou « duckduckgo » (repli sans compte, parsing HTML — dégradé).
web_search_provider: str = "exa"
exa_api_key: str = ""
github_token: str = "" # PAT GitHub : 30 req/min au lieu de 10
web_fetch_max_chars: int = 20000 # texte renvoyé par fetch_url
# ── Vues Notion : géocodage + plafonds (§8.2, §13.4, §20) ──
GEOCODER_PROVIDER: str = "none" # none | nominatim | custom
GEOCODER_ENDPOINT: str = "https://nominatim.openstreetmap.org"
VIEW_ROWS_MAX: int = 1000
MAP_POINTS_MAX: int = 100
TIMELINE_LOAD_MAX: int = 200
CHART_MAX_SUBGROUPS: int = 50
@property
def db_path(self) -> Path:
if self.database_url == "sqlite:///:memory:":
return Path(":memory:") # Special SQLite in-memory
if self.database_url.startswith("sqlite:///"):
p = self.database_url.replace("sqlite:///", "", 1)
# On Windows, don't prepend / if path starts with a drive letter
import re
if re.match(r'^[a-zA-Z]:', p):
return Path(p)
# A26 : `sqlite:////data/flowdeck.db` donne p='/data/…' — un simple
# concat '/'+'/' → '//data/…' = chemin UNC sous Windows. On normalise.
return Path("/" + p.lstrip("/"))
return Path("/data/flowdeck.db")
settings = Settings()