diff --git a/src/backend/core/api/feature_flag.py b/src/backend/core/api/feature_flag.py index b137a7ea..92eae28b 100644 --- a/src/backend/core/api/feature_flag.py +++ b/src/backend/core/api/feature_flag.py @@ -1,30 +1,74 @@ -"""Feature flag handler for the Meet core app.""" - +# core/api/feature_flag.py +import logging +import os from functools import wraps +from logging import getLogger from django.conf import settings from django.http import Http404 +logging.basicConfig(level=logging.DEBUG) + +logger = getLogger(__name__) + +from posthog import Posthog + + +class FeatureFlagError(Exception): + """Feature flag management error.""" + class FeatureFlag: - """Check if features are enabled and return error responses.""" + """Feature flag management using Django settings and PostHog.""" FLAGS = { - "recording": "RECORDING_ENABLE", - "storage_event": "RECORDING_STORAGE_EVENT_ENABLE", - "subtitle": "ROOM_SUBTITLE_ENABLED", + "metadata_agent": {"posthog": "is_metadata_agent_enabled"}, + "recording": {"setting": "RECORDING_ENABLE"}, + "storage_event": {"setting": "RECORDING_STORAGE_EVENT_ENABLE"}, + "subtitle": {"setting": "ROOM_SUBTITLE_ENABLED"}, } + _ph_client = None + @classmethod - def flag_is_active(cls, flag_name): + def _get_ph_client(cls): + """Initialize and return PostHog client if configured.""" + if cls._ph_client is not None: + return cls._ph_client + api_key = os.getenv("POSTHOG_API_KEY") + host = os.getenv("POSTHOG_API_HOST", "https://eu.i.posthog.com") + logging.info("PostHog config: api_key=%s, host=%s", api_key, host) + if Posthog and api_key and host: + logging.info("Initializing PostHog client") + cls._ph_client = Posthog(project_api_key=api_key, host=host) + logging.info("PostHog client initialized: %s", bool(cls._ph_client)) + return cls._ph_client + + @classmethod + def flag_is_active(cls, flag_name, *, distinct_id=None, default=False): """Check if a feature flag is active.""" - - setting_name = cls.FLAGS.get(flag_name) - - if setting_name is None: + cfg = cls.FLAGS.get(flag_name) + if not cfg: return False - return getattr(settings, setting_name, False) + setting_name = cfg.get("setting") + if setting_name is not None: + return bool(getattr(settings, setting_name, False)) + + posthog_flag = cfg.get("posthog") + if posthog_flag: + ph = cls._get_ph_client() + if ph and distinct_id: + try: + logger.info( + "Checking PostHog flag %s for id=%s", posthog_flag, distinct_id + ) + return bool(ph.feature_enabled(posthog_flag, distinct_id)) + except FeatureFlagError as e: + logging.error("Error checking feature flag %s: %s", flag_name, e) + return default + return default + return default @classmethod def require(cls, flag_name): diff --git a/src/backend/core/recording/services/recording_events.py b/src/backend/core/recording/services/recording_events.py index d448618a..cc1d52be 100644 --- a/src/backend/core/recording/services/recording_events.py +++ b/src/backend/core/recording/services/recording_events.py @@ -10,6 +10,7 @@ from django.db import DatabaseError, transaction import aiohttp from core import models, utils +from core.api.feature_flag import FeatureFlag from core.services.metadata import MetadataService logging.basicConfig(level=logging.DEBUG) @@ -21,6 +22,19 @@ class RecordingEventsError(Exception): """Recording event handling fails.""" +def get_recording_creator_id(recording: models.Recording) -> str | None: + """Get the user ID of the recording creator (owner).""" + owner = ( + models.RecordingAccess.objects.select_related("user") + .filter( + role=models.RoleChoices.OWNER, + recording_id=recording.id, + ) + .first() + ) + return str(owner.user.id) + + class RecordingEventsService: """Handles recording-related Livekit webhook events.""" @@ -61,9 +75,14 @@ class RecordingEventsService: @staticmethod def handle_egress_started(recording): """Start metadata agent after transaction commit.""" - service = MetadataService() rec_id = recording.id room_id = recording.room_id + creator_id = get_recording_creator_id(recording) + if not FeatureFlag.flag_is_active("metadata_agent", distinct_id=creator_id): + logger.info("Metadata agent disabled by PostHog flag for id=%s", creator_id) + return + + service = MetadataService() logger.info( "Scheduling metadata start for recording=%s room_id=%s", rec_id, room_id diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index 006058c7..2e1caed2 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -48,6 +48,7 @@ dependencies = [ "jsonschema==4.24.0", "markdown==3.8.2", "nested-multipart-parser==1.5.0", + "posthog==6.0.3", "psycopg[binary]==3.2.9", "PyJWT==2.10.1", "python-frontmatter==1.1.0",