Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot] 50fb093d39 ⬆️(dependencies) update django to v5.2.15 2026-07-01 01:35:02 +00:00
31 changed files with 40 additions and 695 deletions
-9
View File
@@ -8,8 +8,6 @@ and this project adheres to
## [Unreleased]
## [1.22.0] - 2026-07-03
### Added
- ✨(frontend) cap and paginate tiles in picture-in-picture #1383
@@ -18,19 +16,12 @@ and this project adheres to
- 🧱(helm) run clean files command as cronjob
- ✨(backend) add fallback to save recordings without S3/MinIO webhooks
- 🩹(frontend) enable screen share button in PiP #1458
- 🐛(backend) support unencoded S3 notification object keys #1455
### Changed
- ✨(summary) generalized stt api call #1420
- ♻️(env) refactor env variables handling
- 🚸(frontend) use "Advanced" instead of "Premium" in the sidepanel
- ♿️(frontend) make fullscreen share warning keyboard accessible #1459
### Fixed
- 🛂(backend) reject user access tokens on the API
- 🩹(helm) fix Helm ingress rendering when passing multiple hosts
## [1.21.0] - 2026-06-15
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agents"
version = "1.22.0"
version = "1.21.0"
requires-python = ">=3.12"
dependencies = [
"livekit-agents==1.5.13",
+1 -1
View File
@@ -9,7 +9,7 @@ resolution-markers = [
[[package]]
name = "agents"
version = "1.22.0"
version = "1.21.0"
source = { virtual = "." }
dependencies = [
{ name = "livekit-agents" },
-59
View File
@@ -1,59 +0,0 @@
"""
Pluggable analytics.
Usage anywhere in the codebase:
from core import analytics
analytics.capture(request.user, "room_created", {"room_id": str(room.pk)})
The concrete backend is resolved lazily from Django settings, so swapping
PostHog for anything else is a configuration change, not a code change.
"""
from functools import lru_cache
from typing import Any
from django.conf import settings
from django.utils.module_loading import import_string
from .base import AnalyticsBackend, NoOpAnalytics
from .events import AnalyticsEvent
__all__ = [
"get_analytics",
"identify",
"capture",
"AnalyticsBackend",
"AnalyticsEvent",
]
@lru_cache(maxsize=1)
def get_analytics() -> AnalyticsBackend:
"""Instantiate the configured backend once per process."""
dotted_path = getattr(settings, "ANALYTICS_BACKEND", None)
options = getattr(settings, "ANALYTICS_BACKEND_SETTINGS", {}) or {}
if not dotted_path:
return NoOpAnalytics()
backend_class = import_string(dotted_path)
return backend_class(**options)
# Convenience module-level shortcuts
analytics_instance = get_analytics()
def identify(user, properties: dict[str, Any] | None = None) -> None:
"""Associate traits with an identified user."""
analytics_instance.identify(user, properties)
def capture(
user, event: AnalyticsEvent, properties: dict[str, Any] | None = None
) -> None:
"""Record an event performed by an identified user."""
analytics_instance.capture(user, event, properties)
-50
View File
@@ -1,50 +0,0 @@
"""Analytics backend protocol and default no-op implementation."""
from typing import Any, Protocol
from ..models import User
from .events import AnalyticsEvent
class AnalyticsBackend(Protocol):
"""
Interface every analytics backend must implement.
Backends are instantiated once (singleton) with the kwargs declared in
settings.ANALYTICS_BACKEND_SETTINGS, e.g.:
ANALYTICS_BACKEND = "core.analytics.posthog.PostHogAnalytics"
ANALYTICS_BACKEND_SETTINGS = {"api_key": "...", "host": "..."}
"""
def __init__(self, **kwargs: Any) -> None: ...
def identify(self, user: User, properties: dict[str, Any] | None = None) -> None:
"""Associate traits (email, name, ...) with an identified user."""
def capture(
self,
user: User,
event: AnalyticsEvent,
properties: dict[str, Any] | None = None,
) -> None:
"""Record an event performed by an identified user."""
def shutdown(self) -> None:
"""Flush pending events. Called on process exit."""
class NoOpAnalytics:
"""Default backend: silently discards everything."""
def __init__(self, **kwargs: Any) -> None:
"""No-op: accepts and ignores any backend settings kwargs."""
def identify(self, user: User, properties=None) -> None:
"""No-op: discards identify calls."""
def capture(self, user, event, properties=None) -> None:
"""No-op: discards captured events."""
def shutdown(self) -> None:
"""No-op: nothing to flush."""
-10
View File
@@ -1,10 +0,0 @@
"""Catalog of all analytics events emitted by the backend."""
from enum import StrEnum
class AnalyticsEvent(StrEnum):
"""All trackable events. Values are the wire names sent to the provider."""
# Rooms
ROOM_CREATED = "room_created"
-74
View File
@@ -1,74 +0,0 @@
"""PostHog implementation of the analytics backend protocol."""
import logging
from typing import Any
from posthog import Posthog
from ..models import User
from .events import AnalyticsEvent
logger = logging.getLogger(__name__)
class PostHogAnalytics:
"""Send events to PostHog, keyed on the user's primary key (UUID)."""
def __init__(
self,
*,
api_key: str,
host: str = "https://eu.i.posthog.com",
**kwargs: Any,
) -> None:
# The SDK batches and sends in a background thread by default,
# so calls below never block the request/response cycle.
self._client = Posthog(
project_api_key=api_key,
host=host,
**kwargs,
)
@staticmethod
def _distinct_id(user: User) -> str | None:
"""Return the PostHog distinct_id for a user, or None if anonymous."""
if user is None or not getattr(user, "is_authenticated", False):
return None
return str(user.pk)
def identify(self, user: User, properties: dict[str, Any] | None = None) -> None:
"""Associate traits (email, name, ...) with an identified user."""
distinct_id = self._distinct_id(user)
if distinct_id is None:
return
try:
self._client.set(
distinct_id=distinct_id,
properties=properties or {},
)
except Exception: # pylint: disable=broad-exception-caught
logger.exception("PostHog identify failed")
def capture(
self,
user: User,
event: AnalyticsEvent,
properties: dict[str, Any] | None = None,
) -> None:
"""Record an event performed by an identified user."""
distinct_id = self._distinct_id(user)
if distinct_id is None:
return
try:
self._client.capture(
distinct_id=distinct_id,
event=str(event),
properties=properties or {},
)
except Exception: # pylint: disable=broad-exception-caught
logger.exception("PostHog capture failed for event %s", event)
def shutdown(self) -> None:
"""Flush pending events. Called on process exit."""
self._client.shutdown()
+1 -11
View File
@@ -35,7 +35,7 @@ from rest_framework import (
)
from rest_framework.settings import api_settings
from core import analytics, enums, models, utils
from core import enums, models, utils
from core.api.filters import ListFileFilter
from core.enums import MEDIA_STORAGE_URL_PATTERN
from core.recording.enums import FileExtension
@@ -308,16 +308,6 @@ class RoomViewSet(
if callback_id := self.request.data.get("callback_id"):
RoomCreation().persist_callback_state(callback_id, room)
analytics.capture(
self.request.user,
analytics.AnalyticsEvent.ROOM_CREATED,
{
"room_id": str(room.pk),
"access_level": room.access_level,
"from_callback": bool(self.request.data.get("callback_id")),
},
)
def perform_update(self, serializer):
"""Persist the room update, then sync metadata to LiveKit."""
@@ -9,7 +9,6 @@ from django.utils.translation import gettext_lazy as _
from lasuite.oidc_login.backends import (
OIDCAuthenticationBackend as LaSuiteOIDCAuthenticationBackend,
)
from rest_framework.authentication import SessionAuthentication
from core.models import User
from core.services.marketing import (
@@ -97,17 +96,3 @@ class OIDCAuthenticationBackend(LaSuiteOIDCAuthenticationBackend):
"Multiple user accounts share a common email."
) from e
return None
class SessionAuthenticationWith401(SessionAuthentication):
"""
Identical to DRF's SessionAuthentication, but returns a WWW-Authenticate
header so unauthenticated requests get a 401 instead of a 403.
The scheme is deliberately NOT 'Basic' — that would trigger the browser's
native login popup. 'Session' is ignored by the browser's auth UI but is
still truthy, so DRF keeps the status at 401.
"""
def authenticate_header(self, request):
return "Session"
+3 -19
View File
@@ -19,7 +19,7 @@ from rest_framework import (
status as drf_status,
)
from core import analytics, api, models
from core import api, models
from core.api.feature_flag import FeatureFlag
from core.services.jwt_token import JwtTokenService
@@ -194,26 +194,10 @@ class RoomViewSet(
role=models.RoleChoices.OWNER,
)
auth_method = type(self.request.successful_authenticator).__name__
client_id = (self.request.auth or {}).get("client_id", "unknown")
# Log for auditing
logger.info(
"Room created via application: room_id=%s, user_id=%s, client_id=%s, auth_method=%s",
"Room created via application: room_id=%s, user_id=%s, client_id=%s",
room.id,
self.request.user.id,
client_id,
auth_method,
)
analytics.capture(
self.request.user,
analytics.AnalyticsEvent.ROOM_CREATED,
{
"room_id": str(room.pk),
"access_level": room.access_level,
"client_id": client_id,
"external_api": True,
"auth_method": auth_method,
},
getattr(self.request.auth, "client_id", "unknown"),
)
@@ -6,7 +6,6 @@ import re
from dataclasses import dataclass
from functools import lru_cache
from typing import Any, Dict, Optional, Protocol
from urllib.parse import quote
from django.conf import settings
from django.utils.module_loading import import_string
@@ -165,9 +164,6 @@ class S3Parser(BaseS3Parser):
if not filepath:
raise ParsingEventDataError("Missing object key name")
filetype, _ = mimetypes.guess_type(filepath)
# Normalize raw S3-compatible object keys without re-encoding
# already encoded AWS S3 notification keys.
filepath = quote(filepath, safe="%+")
return StorageEvent(
filepath=filepath,
filetype=filetype,
@@ -1,263 +0,0 @@
"""
Unit tests for PostHogAnalytics.
"""
# pylint: disable=redefined-outer-name,unused-argument,protected-access
from unittest.mock import patch
from django.contrib.auth.models import AnonymousUser
import pytest
from core.analytics.events import AnalyticsEvent
from core.analytics.posthog import PostHogAnalytics
from core.factories import UserFactory
pytestmark = pytest.mark.django_db
# ==============================
# __init__
# ==============================
@patch("core.analytics.posthog.Posthog")
def test_init_constructs_posthog_client_with_api_key_and_host(mock_posthog_cls):
"""Should forward api_key and host to the Posthog SDK constructor."""
PostHogAnalytics(api_key="my-key", host="https://custom.i.posthog.com")
mock_posthog_cls.assert_called_once_with(
project_api_key="my-key",
host="https://custom.i.posthog.com",
)
@patch("core.analytics.posthog.Posthog")
def test_init_defaults_to_eu_host(mock_posthog_cls):
"""Should default host to the EU PostHog cloud when not specified."""
PostHogAnalytics(api_key="my-key")
_, kwargs = mock_posthog_cls.call_args
assert kwargs["host"] == "https://eu.i.posthog.com"
@patch("core.analytics.posthog.Posthog")
def test_init_forwards_extra_kwargs_to_client(mock_posthog_cls):
"""Should pass through arbitrary extra kwargs (e.g. debug, disabled) to the SDK."""
PostHogAnalytics(api_key="my-key", debug=True, disabled=False)
_, kwargs = mock_posthog_cls.call_args
assert kwargs["debug"] is True
assert kwargs["disabled"] is False
# ==============================
# _distinct_id
# ==============================
@patch("core.analytics.posthog.Posthog")
def test_distinct_id_returns_none_for_none_user(mock_posthog_cls):
"""Should return None when user is None."""
backend = PostHogAnalytics(api_key="test-api-key")
assert backend._distinct_id(None) is None
@patch("core.analytics.posthog.Posthog")
def test_distinct_id_returns_none_for_anonymous_user(mock_posthog_cls):
"""Should return None when user.is_authenticated is falsy."""
backend = PostHogAnalytics(api_key="test-api-key")
assert backend._distinct_id(AnonymousUser()) is None
@patch("core.analytics.posthog.Posthog")
def test_distinct_id_returns_none_when_attribute_missing(mock_posthog_cls):
"""Should return None when the user object has no is_authenticated attribute at all."""
backend = PostHogAnalytics(api_key="test-api-key")
assert backend._distinct_id(object()) is None
@patch("core.analytics.posthog.Posthog")
def test_distinct_id_returns_stringified_pk_for_authenticated_user(mock_posthog_cls):
"""Should return str(user.pk) for an authenticated user."""
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
assert backend._distinct_id(user) == str(user.pk)
# ==============================
# identify
# ==============================
@patch("core.analytics.posthog.Posthog")
def test_identify_noop_for_anonymous_user(mock_posthog_cls):
"""Should not call the SDK when the user is anonymous."""
backend = PostHogAnalytics(api_key="test-api-key")
backend.identify(AnonymousUser(), {"email": "a@example.com"})
mock_posthog_cls.return_value.set.assert_not_called()
@patch("core.analytics.posthog.Posthog")
def test_identify_noop_for_none_user(mock_posthog_cls):
"""Should not call the SDK when user is None."""
backend = PostHogAnalytics(api_key="test-api-key")
backend.identify(None, {"email": "a@example.com"})
mock_posthog_cls.return_value.set.assert_not_called()
@patch("core.analytics.posthog.Posthog")
def test_identify_sends_set_properties_for_authenticated_user(mock_posthog_cls):
"""Should call capture with event=$identify and properties wrapped in $set."""
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
backend.identify(user, {"email": "a@example.com", "name": "A"})
mock_posthog_cls.return_value.set.assert_called_once_with(
distinct_id=str(user.pk),
properties={"email": "a@example.com", "name": "A"},
)
@patch("core.analytics.posthog.Posthog")
def test_identify_defaults_properties_to_empty_dict(mock_posthog_cls):
"""Should send an empty $set payload when properties is None."""
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
backend.identify(user, None)
mock_posthog_cls.return_value.set.assert_called_once_with(
distinct_id=str(user.pk),
properties={},
)
@patch("core.analytics.posthog.Posthog")
def test_identify_swallows_sdk_exceptions(mock_posthog_cls):
"""Should log and not raise when the SDK call fails."""
mock_posthog_cls.return_value.set.side_effect = RuntimeError("network down")
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
# Must not propagate.
backend.identify(user, {"email": "a@example.com"})
# ==============================
# capture
# ==============================
@patch("core.analytics.posthog.Posthog")
def test_capture_noop_for_anonymous_user(mock_posthog_cls):
"""Should not call the SDK when the user is anonymous."""
backend = PostHogAnalytics(api_key="test-api-key")
backend.capture(AnonymousUser(), AnalyticsEvent.ROOM_CREATED, {"room_id": "1"})
mock_posthog_cls.return_value.capture.assert_not_called()
@patch("core.analytics.posthog.Posthog")
def test_capture_noop_for_none_user(mock_posthog_cls):
"""Should not call the SDK when user is None."""
backend = PostHogAnalytics(api_key="test-api-key")
backend.capture(None, AnalyticsEvent.ROOM_CREATED, {"room_id": "1"})
mock_posthog_cls.return_value.capture.assert_not_called()
@patch("core.analytics.posthog.Posthog")
def test_capture_sends_event_and_properties_for_authenticated_user(mock_posthog_cls):
"""Should call capture with the distinct_id, event name, and properties."""
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
backend.capture(user, AnalyticsEvent.ROOM_CREATED, {"room_id": "room-1"})
mock_posthog_cls.return_value.capture.assert_called_once_with(
distinct_id=str(user.pk),
event="room_created",
properties={"room_id": "room-1"},
)
@patch("core.analytics.posthog.Posthog")
def test_capture_serializes_event_enum_to_plain_string(mock_posthog_cls):
"""Should send the wire string, not the AnalyticsEvent enum member, to the SDK."""
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
backend.capture(user, AnalyticsEvent.ROOM_CREATED)
_, kwargs = mock_posthog_cls.return_value.capture.call_args
assert kwargs["event"] == "room_created"
assert isinstance(
kwargs["event"], str
) # not AnalyticsEvent, not StrEnum subclass leaking through
@patch("core.analytics.posthog.Posthog")
def test_capture_defaults_properties_to_empty_dict(mock_posthog_cls):
"""Should send an empty properties dict when properties is None."""
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
backend.capture(user, AnalyticsEvent.ROOM_CREATED, None)
mock_posthog_cls.return_value.capture.assert_called_once_with(
distinct_id=str(user.pk),
event="room_created",
properties={},
)
@patch("core.analytics.posthog.Posthog")
def test_capture_swallows_sdk_exceptions(mock_posthog_cls):
"""Should log and not raise when the SDK call fails."""
mock_posthog_cls.return_value.capture.side_effect = RuntimeError("network down")
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
# Must not propagate.
backend.capture(user, AnalyticsEvent.ROOM_CREATED, {"room_id": "1"})
@patch("core.analytics.posthog.Posthog")
def test_capture_logs_the_failing_event_name_on_exception(mock_posthog_cls, caplog):
"""Should log which event failed, to aid debugging without crashing the caller."""
mock_posthog_cls.return_value.capture.side_effect = RuntimeError("network down")
backend = PostHogAnalytics(api_key="test-api-key")
user = UserFactory()
with caplog.at_level("ERROR"):
backend.capture(user, AnalyticsEvent.ROOM_CREATED)
assert any("PostHog capture failed" in record.message for record in caplog.records)
# ==============================
# shutdown
# ==============================
@patch("core.analytics.posthog.Posthog")
def test_shutdown_flushes_the_client(mock_posthog_cls):
"""Should delegate to the SDK's shutdown to flush pending events."""
backend = PostHogAnalytics(api_key="test-api-key")
backend.shutdown()
mock_posthog_cls.return_value.shutdown.assert_called_once()
@@ -360,75 +360,6 @@ def test_s3_parse_unrecognized_extension(s3_parser):
s3_parser.parse(event_with_unknown_ext)
def test_s3_parser_keeps_encoded_filepath_compatible(settings):
"""Test S3 parser keeps already encoded object keys compatible."""
settings.RECORDING_OUTPUT_FOLDER = "recordings"
recording_id = "80ae9fe5-639a-438b-b86e-9e3dd2d55f4d"
parser = S3Parser(bucket_name="recordings-bucket")
data = {
"Records": [
{
"s3": {
"bucket": {"name": "recordings-bucket"},
"object": {
"key": f"recordings%2F{recording_id}.mp4",
},
}
}
]
}
assert parser.get_recording_id(data) == recording_id
def test_s3_parser_accepts_unencoded_filepath(settings):
"""Test S3 parser accepts raw object keys with slash separators."""
settings.RECORDING_OUTPUT_FOLDER = "recordings"
recording_id = "80ae9fe5-639a-438b-b86e-9e3dd2d55f4d"
parser = S3Parser(bucket_name="recordings-bucket")
data = {
"Records": [
{
"s3": {
"bucket": {"name": "recordings-bucket"},
"object": {
"key": f"recordings/{recording_id}.mp4",
},
}
}
]
}
assert parser.get_recording_id(data) == recording_id
def test_s3_parser_preserves_plus_signs_in_encoded_filepath(settings):
"""Test S3 parser preserves plus signs in already encoded object keys."""
settings.RECORDING_OUTPUT_FOLDER = "recordings"
recording_id = "80ae9fe5-639a-438b-b86e-9e3dd2d55f4d"
parser = S3Parser(bucket_name="recordings-bucket")
data = {
"Records": [
{
"s3": {
"bucket": {"name": "recordings-bucket"},
"object": {
"key": f"folder+name%2Frecordings%2F{recording_id}.mp4",
},
}
}
]
}
assert parser.get_recording_id(data) == recording_id
def test_s3_get_recording_id_success(s3_parser, valid_s3_event):
"""Test successful extraction of recording ID from S3 event."""
recording_id = s3_parser.get_recording_id(valid_s3_event)
+2 -9
View File
@@ -323,7 +323,8 @@ class Base(Configuration):
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"core.authentication.backends.SessionAuthenticationWith401",
"mozilla_django_oidc.contrib.drf.OIDCAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_PARSER_CLASSES": [
"rest_framework.parsers.JSONParser",
@@ -761,14 +762,6 @@ class Base(Configuration):
None, environ_name="RECORDING_DOWNLOAD_BASE_URL", environ_prefix=None
)
# Analytics
ANALYTICS_BACKEND = values.Value(
None, environ_name="ANALYTICS_BACKEND", environ_prefix=None
)
ANALYTICS_BACKEND_SETTINGS = values.DictValue(
{}, environ_name="ANALYTICS_BACKEND_SETTINGS", environ_prefix=None
)
# Marketing and communication settings
SIGNUP_NEW_USER_TO_MARKETING_EMAIL = values.BooleanValue(
False, # When enabled, new users are automatically added to mailing list.
+2 -3
View File
@@ -7,7 +7,7 @@ build-backend = "uv_build"
[project]
name = "meet"
version = "1.22.0"
version = "1.21.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -40,7 +40,7 @@ dependencies = [
"django-storages[s3]==1.14.6",
"django-timezone-field>=5.1",
"django-pydantic-field==0.5.4",
"django==5.2.14",
"django==5.2.15",
"djangorestframework==3.17.1",
"drf_spectacular==0.29.0",
"dockerflow==2026.3.4",
@@ -50,7 +50,6 @@ dependencies = [
"jsonschema==4.26.0",
"markdown==3.10.2",
"nested-multipart-parser==1.6.0",
"posthog==7.16.1",
"psycopg[binary]==3.3.4",
"pydantic==2.13.4",
"PyJWT==2.13.0",
+8 -43
View File
@@ -161,15 +161,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
]
[[package]]
name = "backoff"
version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001, upload-time = "2022-10-05T19:19:32.061Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148, upload-time = "2022-10-05T19:19:30.546Z" },
]
[[package]]
name = "billiard"
version = "4.2.4"
@@ -578,15 +569,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" },
]
[[package]]
name = "distro"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" },
]
[[package]]
name = "dj-database-url"
version = "3.1.2"
@@ -601,16 +583,16 @@ wheels = [
[[package]]
name = "django"
version = "5.2.14"
version = "5.2.15"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref" },
{ name = "sqlparse" },
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/65/95/95f7faa0950867afaa0bef2460c6263afd6a2c78cc9434046ed28160b015/django-5.2.14.tar.gz", hash = "sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2", size = 10895118, upload-time = "2026-05-05T13:57:31.104Z" }
sdist = { url = "https://files.pythonhosted.org/packages/2b/e3/31722f7284c9f43333daff9aee9184678e4487adcb5506af0db8cea09ce1/django-5.2.15.tar.gz", hash = "sha256:5154a9bf84ac01dde011e367f355c07dbb329532e06810dcf3ef2af269e236e7", size = 10873669, upload-time = "2026-06-03T13:03:35.892Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/44/f172870cf87aa25afef48fb72adba89ee8b77fcab6f3b23d240b923f1528/django-5.2.14-py3-none-any.whl", hash = "sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76", size = 8311320, upload-time = "2026-05-05T13:57:25.795Z" },
{ url = "https://files.pythonhosted.org/packages/92/b5/38140b1643c00d5c46ce69c78e6980fd285aee223100319631bedee4f5e7/django-5.2.15-py3-none-any.whl", hash = "sha256:0eb4a9bb1853a35b0286dbc6d916bd352c8c2687195a7f2d6f80cefd840e4970", size = 8311957, upload-time = "2026-06-03T13:03:31.329Z" },
]
[[package]]
@@ -1084,14 +1066,14 @@ wheels = [
[[package]]
name = "joserfc"
version = "1.6.8"
version = "1.6.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5d/ac/d4fd5b30f82900eac60d765f179f0ba005825ac462cc8ced6e13ec685ab3/joserfc-1.6.8.tar.gz", hash = "sha256:878620c553a6ebdd76ccdc356782fee3f735f21a356d079a546b42a4670ace5f", size = 232930, upload-time = "2026-05-27T03:22:37.819Z" }
sdist = { url = "https://files.pythonhosted.org/packages/de/c6/de8fdbdfa75c8ca04fead38a82d573df8a82906e984c349d58665f459558/joserfc-1.6.4.tar.gz", hash = "sha256:34ce5f499bfcc5e9ad4cc75077f9278ab3227b71da9aaf28f9ab705f8a560d3c", size = 231866, upload-time = "2026-04-13T13:15:40.632Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/8c/5cdce2cf3ce8155849baf9a5e2ce77e89dc87ec3bdb38259e5d85fbc45bd/joserfc-1.6.8-py3-none-any.whl", hash = "sha256:22fb31a69094a5e6f44632002a9df2c30c941fc6c8ce1b037e92c03de954cf9f", size = 70927, upload-time = "2026-05-27T03:22:35.796Z" },
{ url = "https://files.pythonhosted.org/packages/b6/f7/210b27752e972edb36d239315b08d3eb6b14824cc4a590da2337d195260b/joserfc-1.6.4-py3-none-any.whl", hash = "sha256:3e4a22b509b41908989237a045e25c8308d5fd47ab96bdae2dd8057c6451003a", size = 70464, upload-time = "2026-04-13T13:15:39.259Z" },
]
[[package]]
@@ -1202,7 +1184,7 @@ wheels = [
[[package]]
name = "meet"
version = "1.22.0"
version = "1.21.0"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
@@ -1234,7 +1216,6 @@ dependencies = [
{ name = "mozilla-django-oidc" },
{ name = "nested-multipart-parser" },
{ name = "phonenumbers" },
{ name = "posthog" },
{ name = "psycopg", extra = ["binary"] },
{ name = "pydantic" },
{ name = "pyjwt" },
@@ -1275,7 +1256,7 @@ requires-dist = [
{ name = "brotli", specifier = "==1.2.0" },
{ name = "celery", extras = ["redis"], specifier = "==5.6.3" },
{ name = "dj-database-url", specifier = "==3.1.2" },
{ name = "django", specifier = "==5.2.14" },
{ name = "django", specifier = "==5.2.15" },
{ name = "django-configurations", specifier = "==2.5.1" },
{ name = "django-cors-headers", specifier = "==4.9.0" },
{ name = "django-countries", specifier = "==8.2.0" },
@@ -1298,7 +1279,6 @@ requires-dist = [
{ name = "mozilla-django-oidc", specifier = "==5.0.2" },
{ name = "nested-multipart-parser", specifier = "==1.6.0" },
{ name = "phonenumbers", specifier = "==9.0.31" },
{ name = "posthog", specifier = "==7.16.1" },
{ name = "psycopg", extras = ["binary"], specifier = "==3.3.4" },
{ name = "pydantic", specifier = "==2.13.4" },
{ name = "pyjwt", specifier = "==2.13.0" },
@@ -1548,21 +1528,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "posthog"
version = "7.16.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backoff" },
{ name = "distro" },
{ name = "requests" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b4/4f/a954175c862a3565d02c3f627874d85f18313472a0c4b08f45d84aaf3315/posthog-7.16.1.tar.gz", hash = "sha256:3619d3c619ad01f36c6d465e084950882417c63021eb3cfacacb23f900ec52d4", size = 226343, upload-time = "2026-05-27T18:46:20.129Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3e/28/0f840699a1d0db3c1e5483c6208f0804a51f21ccfa34e6aa356161606adc/posthog-7.16.1-py3-none-any.whl", hash = "sha256:fd5aa4510033f3b039fda2fbfce45f493d140d4782f681e69639793dda317d67", size = 264231, upload-time = "2026-05-27T18:46:17.933Z" },
]
[[package]]
name = "pprintpp"
version = "0.4.0"
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "meet",
"version": "1.22.0",
"version": "1.21.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "meet",
"version": "1.22.0",
"version": "1.21.0",
"dependencies": {
"@fontsource-variable/atkinson-hyperlegible-next": "5.2.6",
"@fontsource-variable/lexend": "5.2.11",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "meet",
"private": true,
"version": "1.22.0",
"version": "1.21.0",
"type": "module",
"scripts": {
"dev": "panda codegen && vite",
-1
View File
@@ -9,7 +9,6 @@ export interface ApiConfig {
analytics?: {
id: string
host: string
flags_api_host?: string
}
support?: {
id: string
@@ -28,16 +28,10 @@ export const terminateAnalyticsSession = async () => {
export type useAnalyticsProps = {
id?: string
host?: string
flags_api_host?: string
isDisabled?: boolean
}
export const useAnalytics = ({
id,
host,
flags_api_host,
isDisabled,
}: useAnalyticsProps) => {
export const useAnalytics = ({ id, host, isDisabled }: useAnalyticsProps) => {
const [location] = useLocation()
const { user } = useUser()
@@ -45,13 +39,9 @@ export const useAnalytics = ({
if (!id || !host || isDisabled) return
getPosthog().then((ph) => {
if (ph.__loaded) return
ph.init(id, {
api_host: host,
flags_api_host: flags_api_host,
person_profiles: 'always',
})
ph.init(id, { api_host: host, person_profiles: 'always' })
})
}, [id, host, flags_api_host, isDisabled])
}, [id, host, isDisabled])
useEffect(() => {
if (!user) return
@@ -1,19 +1,12 @@
import { useTranslation } from 'react-i18next'
import { styled } from '@/styled-system/jsx'
import { useLocalParticipant } from '@livekit/components-react'
export const StageFrame = ({ children }: { children: React.ReactNode }) => {
const { t } = useTranslation('rooms', {
keyPrefix: 'pictureInPicture',
})
const { localParticipant } = useLocalParticipant()
return (
<Container
role="region"
aria-label={t('stage')}
{...(!localParticipant.isScreenShareEnabled ? { inert: '' } : {})}
>
<Container role="region" aria-label={t('stage')} {...{ inert: '' }}>
{children}
</Container>
)
@@ -1,6 +1,6 @@
import { css } from '@/styled-system/css'
import { Button, Text } from '@/primitives'
import { useCallback, useMemo, useRef } from 'react'
import { useMemo, useRef } from 'react'
import { screenSharePreferenceStore } from '@/stores/screenSharePreferences'
import { useSnapshot } from 'valtio'
import { useLocalParticipant } from '@livekit/components-react'
@@ -61,19 +61,9 @@ export const FullScreenShareWarning = ({
await localParticipant.setScreenShareEnabled(false, {}, {})
}
const handleDismissWarning = useCallback(() => {
const handleDismissWarning = () => {
screenSharePreferenceStore.enabled = false
}, [])
const handleKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
e.stopPropagation()
handleDismissWarning()
}
},
[handleDismissWarning]
)
}
if (!shouldShowWarning) return null
@@ -108,7 +98,6 @@ export const FullScreenShareWarning = ({
})}
>
<Text
role="alert"
style={{
color: 'white',
flexBasis: '55%',
@@ -128,14 +117,11 @@ export const FullScreenShareWarning = ({
})}
>
<Button
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus
variant="tertiary"
size="sm"
style={{
height: 'fit-content',
}}
onKeyDown={handleKeyDown}
onPress={async () => {
await handleStopScreenShare()
}}
@@ -148,7 +134,6 @@ export const FullScreenShareWarning = ({
style={{
height: 'fit-content',
}}
onKeyDown={handleKeyDown}
onPress={() => handleDismissWarning()}
>
{t('ignore')}
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: v2
type: application
name: meet
version: 0.0.26
version: 0.0.25
+2 -2
View File
@@ -72,11 +72,11 @@ spec:
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ include "meet.posthog.fullname" $ }}-proxy
name: {{ include "meet.posthog.fullname" . }}-proxy
port:
number: {{ $.Values.posthog.service.port }}
{{- else }}
serviceName: {{ include "meet.posthog.fullname" $ }}-proxy
serviceName: {{ include "meet.posthog.fullname" . }}-proxy
servicePort: {{ $.Values.posthog.service.port }}
{{- end }}
{{- with $.Values.posthog.assetsService.customBackends }}
@@ -72,11 +72,11 @@ spec:
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ include "meet.posthog.fullname" $ }}-assets-proxy
name: {{ include "meet.posthog.fullname" . }}-assets-proxy
port:
number: {{ $.Values.posthog.assetsService.service.port }}
{{- else }}
serviceName: {{ include "meet.posthog.fullname" $ }}-assets-proxy
serviceName: {{ include "meet.posthog.fullname" . }}-assets-proxy
servicePort: {{ $.Values.posthog.assetsService.service.port }}
{{- end }}
{{- with $.Values.posthog.assetsService.customBackends }}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mail_mjml",
"version": "1.22.0",
"version": "1.21.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mail_mjml",
"version": "1.22.0",
"version": "1.21.0",
"license": "MIT",
"dependencies": {
"@html-to/text-cli": "0.5.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.22.0",
"version": "1.21.0",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "sdk",
"version": "1.22.0",
"version": "1.21.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sdk",
"version": "1.22.0",
"version": "1.21.0",
"license": "ISC",
"workspaces": [
"./library",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "sdk",
"version": "1.22.0",
"version": "1.21.0",
"author": "",
"license": "ISC",
"description": "",
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "summary"
version = "1.22.0"
version = "1.21.0"
dependencies = [
"fastapi[standard]>=0.105.0",
"uvicorn>=0.24.0",