Files
konstruct/packages/shared/shared/config.py
Adolfo Delorenzo f710c9c5fe feat(04-rbac-01): DB migration 006 + RBAC ORM models + config fields
- Migration 006: adds role TEXT+CHECK column to portal_users, backfills
  is_admin -> platform_admin/customer_admin, drops is_admin
- Migration 006: creates user_tenant_roles table (UNIQUE user_id+tenant_id)
- Migration 006: creates portal_invitations table with token_hash, status, expires_at
- PortalUser: replaced is_admin (bool) with role (str, default customer_admin)
- Added UserRole enum (PLATFORM_ADMIN, CUSTOMER_ADMIN, CUSTOMER_OPERATOR)
- Added UserTenantRole ORM model with FK cascade deletes
- Added PortalInvitation ORM model with token_hash unique constraint
- Settings: added invite_secret, smtp_host, smtp_port, smtp_username,
  smtp_password, smtp_from_email fields
2026-03-24 13:49:16 -06:00

226 lines
8.0 KiB
Python

"""
Konstruct shared configuration.
Loads all environment variables via Pydantic Settings with sensible defaults
for local development. All services import from this module.
"""
from __future__ import annotations
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
"""Application settings loaded from environment variables."""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
extra="ignore",
)
# -------------------------------------------------------------------------
# Database
# -------------------------------------------------------------------------
database_url: str = Field(
default="postgresql+asyncpg://konstruct_app:konstruct_dev@localhost:5432/konstruct",
description="Async database URL — must use konstruct_app role, not superuser",
)
database_admin_url: str = Field(
default="postgresql+asyncpg://postgres:postgres_dev@localhost:5432/konstruct",
description="Admin database URL for Alembic migrations (superuser)",
)
# -------------------------------------------------------------------------
# Redis
# -------------------------------------------------------------------------
redis_url: str = Field(
default="redis://localhost:6379/0",
description="Redis connection URL",
)
celery_broker_url: str = Field(
default="redis://localhost:6379/1",
description="Celery broker URL",
)
celery_result_backend: str = Field(
default="redis://localhost:6379/2",
description="Celery result backend URL",
)
# -------------------------------------------------------------------------
# Slack
# -------------------------------------------------------------------------
slack_bot_token: str = Field(
default="",
description="Slack bot token (xoxb-...)",
)
slack_signing_secret: str = Field(
default="",
description="Slack signing secret for webhook verification",
)
slack_app_token: str = Field(
default="",
description="Slack app-level token for Socket Mode (xapp-...)",
)
# -------------------------------------------------------------------------
# WhatsApp
# -------------------------------------------------------------------------
whatsapp_app_secret: str = Field(
default="",
description="WhatsApp app secret for HMAC-SHA256 webhook signature verification",
)
whatsapp_verify_token: str = Field(
default="",
description="WhatsApp webhook verification token (hub.verify_token)",
)
# -------------------------------------------------------------------------
# MinIO / Object Storage
# -------------------------------------------------------------------------
minio_endpoint: str = Field(
default="http://localhost:9000",
description="MinIO endpoint URL (S3-compatible)",
)
minio_access_key: str = Field(
default="minioadmin",
description="MinIO access key",
)
minio_secret_key: str = Field(
default="minioadmin",
description="MinIO secret key",
)
minio_media_bucket: str = Field(
default="konstruct-media",
description="MinIO bucket name for media attachments",
)
# -------------------------------------------------------------------------
# LLM Providers
# -------------------------------------------------------------------------
anthropic_api_key: str = Field(
default="",
description="Anthropic API key",
)
openai_api_key: str = Field(
default="",
description="OpenAI API key",
)
ollama_base_url: str = Field(
default="http://localhost:11434",
description="Ollama inference server base URL",
)
# -------------------------------------------------------------------------
# Auth / Security
# -------------------------------------------------------------------------
auth_secret: str = Field(
default="insecure-dev-secret-change-in-production",
description="Secret key for signing JWT tokens",
)
invite_secret: str = Field(
default="insecure-invite-secret-change-in-production",
description="HMAC secret for signing invite tokens (separate from auth_secret)",
)
# -------------------------------------------------------------------------
# SMTP (for invitation emails)
# -------------------------------------------------------------------------
smtp_host: str = Field(
default="localhost",
description="SMTP server hostname",
)
smtp_port: int = Field(
default=587,
description="SMTP server port",
)
smtp_username: str = Field(
default="",
description="SMTP authentication username",
)
smtp_password: str = Field(
default="",
description="SMTP authentication password",
)
smtp_from_email: str = Field(
default="noreply@konstruct.dev",
description="From address for outbound emails",
)
# -------------------------------------------------------------------------
# Service URLs
# -------------------------------------------------------------------------
gateway_url: str = Field(default="http://localhost:8001")
router_url: str = Field(default="http://localhost:8002")
orchestrator_url: str = Field(default="http://localhost:8003")
llm_pool_url: str = Field(default="http://localhost:8004")
# -------------------------------------------------------------------------
# Encryption
# -------------------------------------------------------------------------
platform_encryption_key: str = Field(
default="",
description="Fernet key for BYO API key encryption (base64-encoded 32-byte key)",
)
platform_encryption_key_previous: str = Field(
default="",
description="Previous Fernet key retained for decryption during rotation window",
)
# -------------------------------------------------------------------------
# Stripe
# -------------------------------------------------------------------------
stripe_secret_key: str = Field(
default="",
description="Stripe secret API key (sk_live_... or sk_test_...)",
)
stripe_webhook_secret: str = Field(
default="",
description="Stripe webhook endpoint signing secret (whsec_...)",
)
stripe_per_agent_price_id: str = Field(
default="",
description="Stripe Price ID for the per-agent monthly subscription plan",
)
portal_url: str = Field(
default="http://localhost:3000",
description="Portal base URL used in Stripe checkout success/cancel redirects",
)
# -------------------------------------------------------------------------
# Slack OAuth
# -------------------------------------------------------------------------
slack_client_id: str = Field(
default="",
description="Slack OAuth app client ID",
)
slack_client_secret: str = Field(
default="",
description="Slack OAuth app client secret",
)
slack_oauth_redirect_uri: str = Field(
default="http://localhost:3000/api/slack/callback",
description="Slack OAuth redirect URI (must match Slack app config)",
)
oauth_state_secret: str = Field(
default="",
description="HMAC secret for signing OAuth state parameters (CSRF protection)",
)
# -------------------------------------------------------------------------
# Application
# -------------------------------------------------------------------------
environment: str = Field(default="development")
log_level: str = Field(default="INFO")
debug: bool = Field(default=False)
default_rate_limit_rpm: int = Field(
default=60,
description="Default requests per minute per tenant",
)
# Module-level singleton — imported by all services
settings = Settings()