diff --git a/.github/workflows/meet.yml b/.github/workflows/meet.yml index da5c14d9..e7960ac0 100644 --- a/.github/workflows/meet.yml +++ b/.github/workflows/meet.yml @@ -305,7 +305,6 @@ jobs: working-directory: src/summary env: - V1_TENANT_ID: 'test-tenant' AUTHORIZED_TENANTS: '[{"id": "test-tenant", "api_key": "test-api-token", "webhook_url": "https://example.com/webhook", "webhook_api_key": "test-webhook-api-key"}]' AWS_STORAGE_BUCKET_NAME: "http://meet-media-storage" AWS_S3_ENDPOINT_URL: "minio:9000" diff --git a/env.d/development/common.dist b/env.d/development/common.dist index 862f226b..87145ba5 100644 --- a/env.d/development/common.dist +++ b/env.d/development/common.dist @@ -64,8 +64,9 @@ ALLOW_UNREGISTERED_ROOMS=False RECORDING_ENABLE=True RECORDING_STORAGE_EVENT_ENABLE=True RECORDING_STORAGE_EVENT_TOKEN=password -SUMMARY_SERVICE_ENDPOINT=http://app-summary-dev:8000/api/v1/tasks/ +SUMMARY_SERVICE_ENDPOINT=http://app-summary-dev:8000/api/v2/async-jobs/transcribe/ SUMMARY_SERVICE_API_TOKEN=password +SUMMARY_SERVICE_WEBHOOK_API_TOKEN=webhook-password RECORDING_DOWNLOAD_BASE_URL=http://localhost:3000/recording # Recording encoding (LiveKit Egress advanced options). diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 3d80d6c5..92360a8d 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -39,7 +39,10 @@ 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 -from core.recording.event.authentication import StorageEventAuthentication +from core.recording.event.authentication import ( + RecordingProcessWebhookAuthentication, + StorageEventAuthentication, +) from core.recording.event.exceptions import ( InvalidBucketError, InvalidFilepathError, @@ -995,6 +998,47 @@ class RecordingViewSet( {"message": "Event processed."}, ) + @decorators.action( + detail=False, + methods=["post"], + url_path="external-process-hook", + authentication_classes=[RecordingProcessWebhookAuthentication], + ) + def on_external_process_event_received(self, request, pk=None): # pylint: disable=unused-argument + """Handle incoming external process events for recordings.""" + logger.debug("Processing external process event %s", request.data) + data = request.data + if not data.get("job_id"): + raise drf_exceptions.ValidationError(detail="No job_id provided") + + job_id = data["job_id"] + try: + recording = models.Recording.objects.get(external_process_id=job_id) + except models.Recording.DoesNotExist as e: + logger.warning("No recording found for job_id %s: %s", job_id, e) + recording = None + + changed = False + if recording and data.get("type") == "transcript": + if data.get("status") == "success": + logger.info("External process received for recording %s", job_id) + recording.status = ( + models.RecordingStatusChoices.EXTERNAL_PROCESS_SUCCESSFUL + ) + changed = True + if data.get("status") == "failure": + recording.status = models.RecordingStatusChoices.EXTERNAL_PROCESS_FAILED + changed = True + + if changed and recording: + recording.save() + else: + logger.info("No changes to save for external process id %s", job_id) + + return drf_response.Response( + {"message": "Event processed."}, + ) + def _auth_get_original_url(self, request): """ Extracts and parses the original URL from the "HTTP_X_ORIGINAL_URL" header. diff --git a/src/backend/core/migrations/0019_recording_external_process_id_alter_recording_status.py b/src/backend/core/migrations/0019_recording_external_process_id_alter_recording_status.py new file mode 100644 index 00000000..6f62d205 --- /dev/null +++ b/src/backend/core/migrations/0019_recording_external_process_id_alter_recording_status.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.14 on 2026-05-26 14:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0018_rename_active_application_is_active'), + ] + + operations = [ + migrations.AddField( + model_name='recording', + name='external_process_id', + field=models.CharField(blank=True, help_text='ID of the external process associated with the recording.', max_length=255, null=True, unique=True, verbose_name='External Process ID'), + ), + migrations.AlterField( + model_name='recording', + name='status', + field=models.CharField(choices=[('initiated', 'Initiated'), ('active', 'Active'), ('stopped', 'Stopped'), ('saved', 'Saved'), ('aborted', 'Aborted'), ('failed_to_start', 'Failed to Start'), ('failed_to_stop', 'Failed to Stop'), ('notification_succeeded', 'Notification succeeded'), ('external_process_successful', 'External process successful'), ('external_process_failed', 'External process failed')], default='initiated', max_length=50), + ), + ] diff --git a/src/backend/core/models.py b/src/backend/core/models.py index 45fb271f..1c487461 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -60,6 +60,11 @@ class RecordingStatusChoices(models.TextChoices): FAILED_TO_START = "failed_to_start", _("Failed to Start") FAILED_TO_STOP = "failed_to_stop", _("Failed to Stop") NOTIFICATION_SUCCEEDED = "notification_succeeded", _("Notification succeeded") + EXTERNAL_PROCESS_SUCCESSFUL = ( + "external_process_successful", + _("External process successful"), + ) + EXTERNAL_PROCESS_FAILED = "external_process_failed", _("External process failed") @classmethod def is_final(cls, status): @@ -73,6 +78,8 @@ class RecordingStatusChoices(models.TextChoices): cls.STOPPED, cls.SAVED, cls.ABORTED, + cls.EXTERNAL_PROCESS_SUCCESSFUL, + cls.EXTERNAL_PROCESS_FAILED, cls.FAILED_TO_START, cls.FAILED_TO_STOP, } @@ -591,6 +598,14 @@ class Recording(BaseModel): verbose_name=_("Recording options"), help_text=_("Recording options"), ) + external_process_id = models.CharField( + max_length=255, + null=True, + blank=True, + unique=True, + verbose_name=_("External Process ID"), + help_text=_("ID of the external process associated with the recording."), + ) class Meta: db_table = "meet_recording" @@ -646,6 +661,8 @@ class Recording(BaseModel): return self.status in { RecordingStatusChoices.NOTIFICATION_SUCCEEDED, RecordingStatusChoices.SAVED, + RecordingStatusChoices.EXTERNAL_PROCESS_SUCCESSFUL, + RecordingStatusChoices.EXTERNAL_PROCESS_FAILED, } @property diff --git a/src/backend/core/recording/event/authentication.py b/src/backend/core/recording/event/authentication.py index 28551468..9ffdab58 100644 --- a/src/backend/core/recording/event/authentication.py +++ b/src/backend/core/recording/event/authentication.py @@ -14,9 +14,9 @@ logger = logging.getLogger(__name__) class MachineUser: """Represent a non-interactive system user for automated storage operations.""" - def __init__(self) -> None: + def __init__(self, username: str = "storage_event_user") -> None: self.pk = None - self.username = "storage_event_user" + self.username = username self.is_active = True @property @@ -91,3 +91,29 @@ class StorageEventAuthentication(BaseAuthentication): def authenticate_header(self, request): """Return the WWW-Authenticate header value.""" return f"{self.TOKEN_TYPE} realm='Storage event API'" + + +class RecordingProcessWebhookAuthentication(BaseAuthentication): + """ + Custom authentication class for recording process webhook requests. + Validates the API key in the Authorization header. + """ + + def authenticate(self, request): + """ + Authenticate the request and return a two-tuple of (user, token). + """ + logger.info("Authentificating") + authorization_header: str = request.headers.get("Authorization") or "" + + if not secrets.compare_digest( + authorization_header.removeprefix("Bearer "), + settings.SUMMARY_SERVICE_WEBHOOK_API_TOKEN, + ): + logger.warning( + "Authentication failed: Bad Authorization header (ip: %s)", + request.META.get("REMOTE_ADDR"), + ) + raise AuthenticationFailed() + + return MachineUser("external_process_user"), None diff --git a/src/backend/core/recording/event/notification.py b/src/backend/core/recording/event/notification.py index 2d389c4c..2c8a0208 100644 --- a/src/backend/core/recording/event/notification.py +++ b/src/backend/core/recording/event/notification.py @@ -1,8 +1,10 @@ """Service to notify external services when a new recording is ready.""" import asyncio +import json import logging import smtplib +import warnings from datetime import datetime, timezone from django.conf import settings @@ -17,6 +19,7 @@ from asgiref.sync import async_to_sync from livekit import api as livekit_api from core import models, utils +from core.utils import generate_download_s3_file_url logger = logging.getLogger(__name__) @@ -215,18 +218,45 @@ class NotificationService: )(recording.worker_id) payload = { + # Legacy V1 params to avoid a breaking change "owner_id": str(owner_access.user.id), "recording_filename": recording.key, "metadata_filename": metadata_filename, "email": owner_access.user.email, "sub": owner_access.user.sub, "room": recording.room.name, - "language": recording.options.get("language"), "owner_timezone": str(owner_access.user.timezone), "download_link": f"{get_recording_download_base_url()}/{recording.id}", - "context_language": owner_access.user.language, "recording_start_at": (started_at.isoformat() if started_at else None), "recording_end_at": (ended_at.isoformat() if ended_at else None), + # V2 params + "user_sub": owner_access.user.sub, + "user_email": owner_access.user.email, + "cloud_storage_url": generate_download_s3_file_url( + recording.key, expires_in=60 * 60 * 24, override_domain=False + ), + "language": recording.options.get("language", "fr"), + "context_language": owner_access.user.language, + "push_to_docs_config": { + "user_email": owner_access.user.email, + "title": "TODO", + "download_link": f"{get_recording_download_base_url()}/{recording.id}", + # For now the feature flag logic is handled on summary side + "auto_create_summary": True, + }, + "metadata": ( + { + "cloud_storage_url": generate_download_s3_file_url( + metadata_filename, + expires_in=60 * 60 * 24, + override_domain=False, + ), + "started_at": started_at.isoformat(), + "ended_at": ended_at.isoformat(), + } + if (started_at and ended_at) + else None + ), } headers = { @@ -242,6 +272,31 @@ class NotificationService: timeout=30, ) response.raise_for_status() + is_v2_implementation = False + try: + response_json = response.json() + # We do not require a job_id to avoid a breaking change + if job_id := response_json.get("job_id"): + recording.external_process_id = job_id + recording.save() + is_v2_implementation = True + except json.JSONDecodeError: + pass + + if not is_v2_implementation: + warnings.warn( + "You are likely using your own implementation for the summary / " + "transcribe service." + "We have released a new version and API contract for that service, " + "we recommend checking it out" + # pylint: disable=line-too-long + "https://github.com/suitenumerique/meet/blob/19c2a378e7e66652afbfa3e749badd697e3917ac/src/summary/summary/api/route/tasks_v2.py " + "and mimicking it's behavior. We will remove the legacy " + "handling in a future version.", + DeprecationWarning, + stacklevel=2, + ) + except requests.RequestException as exc: logger.exception( "Summary service error for recording %s. URL: %s. Exception: %s", diff --git a/src/backend/core/tests/recording/test_api_recordings_external_process_hook.py b/src/backend/core/tests/recording/test_api_recordings_external_process_hook.py new file mode 100644 index 00000000..7969293e --- /dev/null +++ b/src/backend/core/tests/recording/test_api_recordings_external_process_hook.py @@ -0,0 +1,134 @@ +""" +Test recordings API endpoints: external process hook. +""" + +# pylint: disable=redefined-outer-name,unused-argument + +import pytest + +from ...factories import RecordingFactory +from ...models import RecordingStatusChoices + +pytestmark = pytest.mark.django_db + + +@pytest.fixture +def external_process_settings(settings): + """Configure authentication token for the external process webhook.""" + settings.SUMMARY_SERVICE_WEBHOOK_API_TOKEN = "testWebhookToken" + return settings + + +def test_external_process_event_missing_authorization_header( + external_process_settings, client +): + """Requests without authorization must be rejected.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-1", "type": "transcript", "status": "success"}, + ) + + assert response.status_code == 403 + + +def test_external_process_event_wrong_bearer_token(external_process_settings, client): + """Requests with invalid bearer token must be rejected.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-1", "type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer wrongToken", + ) + + assert response.status_code == 403 + + +def test_external_process_event_missing_job_id(external_process_settings, client): + """Payload without job_id must fail validation.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 400 + assert "No job_id provided" in str(response.json()) + + +def test_external_process_event_success_updates_recording_status( + external_process_settings, client +): + """A successful transcript process should update recording status.""" + recording = RecordingFactory( + status=RecordingStatusChoices.SAVED, + external_process_id="job-123", + ) + + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-123", "type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + recording.refresh_from_db() + assert recording.status == RecordingStatusChoices.EXTERNAL_PROCESS_SUCCESSFUL + + +def test_external_process_event_failure_updates_recording_status( + external_process_settings, client +): + """A failing transcript process should update recording status.""" + recording = RecordingFactory( + status=RecordingStatusChoices.SAVED, + external_process_id="job-456", + ) + + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-456", "type": "transcript", "status": "failure"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + recording.refresh_from_db() + assert recording.status == RecordingStatusChoices.EXTERNAL_PROCESS_FAILED + + +def test_external_process_event_unknown_recording_is_ignored( + external_process_settings, client +): + """Unknown job_id should not fail the webhook processing.""" + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "missing-job", "type": "transcript", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + +def test_external_process_event_non_transcript_event_does_not_change_status( + external_process_settings, client +): + """Only transcript events should update recording status.""" + recording = RecordingFactory( + status=RecordingStatusChoices.SAVED, + external_process_id="job-789", + ) + + response = client.post( + "/api/v1.0/recordings/external-process-hook/", + {"job_id": "job-789", "type": "thumbnail", "status": "success"}, + HTTP_AUTHORIZATION="Bearer testWebhookToken", + ) + + assert response.status_code == 200 + assert response.json() == {"message": "Event processed."} + + recording.refresh_from_db() + assert recording.status == RecordingStatusChoices.SAVED diff --git a/src/backend/core/utils.py b/src/backend/core/utils.py index cbacbe1e..e992bf95 100644 --- a/src/backend/core/utils.py +++ b/src/backend/core/utils.py @@ -455,3 +455,38 @@ def generate_upload_policy(file): ) return policy + + +def generate_download_s3_file_url( + key, *, expires_in: int, override_domain: bool = True +): + """ + Generate a S3 signed download url for a given key. + """ + + # This settings should be used if the backend application and the frontend application + # can't connect to the object storage with the same domain. This is the case in the + # docker compose stack used in development. The frontend application will use localhost + # to connect to the object storage while the backend application will use the object storage + # service name declared in the docker compose stack. + # This is needed because the domain name is used to compute the signature. So it can't be + # changed dynamically by the frontend application. + if settings.AWS_S3_DOMAIN_REPLACE and override_domain: + s3_client = boto3.client( + "s3", + aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID, + aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY, + endpoint_url=settings.AWS_S3_DOMAIN_REPLACE, + config=botocore.client.Config( + region_name=settings.AWS_S3_REGION_NAME, + signature_version=settings.AWS_S3_SIGNATURE_VERSION, + ), + ) + else: + s3_client = default_storage.connection.meta.client + + return s3_client.generate_presigned_url( + ClientMethod="get_object", + Params={"Bucket": default_storage.bucket_name, "Key": key}, + ExpiresIn=expires_in, + ) diff --git a/src/backend/locale/de_DE/LC_MESSAGES/django.po b/src/backend/locale/de_DE/LC_MESSAGES/django.po index 5ca75a87..7c3adaa2 100644 --- a/src/backend/locale/de_DE/LC_MESSAGES/django.po +++ b/src/backend/locale/de_DE/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-05-26 16:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -88,17 +88,17 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Ersteller bin ich" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "" "Sie müssen Administrator oder Eigentümer eines Raums sein, um Zugriffe " "hinzuzufügen." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "Diese Dateiendung ist nicht erlaubt." -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1228 msgid "You have reached the maximum number of files for this type." msgstr "Sie haben die maximale Anzahl an Dateien dieses Typs erreicht." @@ -146,51 +146,59 @@ msgstr "Stopp fehlgeschlagen" msgid "Notification succeeded" msgstr "Benachrichtigung erfolgreich" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "" + +#: core/models.py:67 +msgid "External process failed" +msgstr "" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "BILDSCHIRMAUFZEICHNUNG" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSKRIPT" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Öffentlicher Zugriff" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Vertrauenswürdiger Zugriff" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Eingeschränkter Zugriff" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "ID" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "Primärschlüssel des Eintrags als UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "erstellt am" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "Datum und Uhrzeit der Erstellung eines Eintrags" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "aktualisiert am" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "Datum und Uhrzeit der letzten Aktualisierung eines Eintrags" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -198,11 +206,11 @@ msgstr "" "Geben Sie einen gültigen Sub ein. Dieser Wert darf nur Buchstaben, Zahlen " "und die Zeichen @/./+/-/_ enthalten." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "Sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -210,55 +218,55 @@ msgstr "" "Optional für ausstehende Benutzer; erforderlich nach Kontoaktivierung. " "Maximal 255 Zeichen. Nur Buchstaben, Zahlen und @/./+/-/_ Zeichen erlaubt." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "Identitäts-E-Mail-Adresse" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "Administrator-E-Mail-Adresse" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "Vollständiger Name" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "Kurzname" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "Sprache" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "Die Sprache, in der der Benutzer die Oberfläche sehen möchte." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "Die Zeitzone, in der der Benutzer die Zeiten sehen möchte." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "Gerät" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Ob es sich um ein Gerät oder einen echten Benutzer handelt." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "Mitarbeiterstatus" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Ob der Benutzer sich bei dieser Admin-Seite anmelden kann." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "aktiv" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -266,66 +274,66 @@ msgstr "" "Ob dieser Benutzer als aktiv behandelt werden soll. Deaktivieren Sie dies " "anstelle des Löschens des Kontos." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "Benutzer" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "Benutzer" -#: core/models.py:272 +#: core/models.py:279 msgid "Resource" msgstr "Ressource" -#: core/models.py:273 +#: core/models.py:280 msgid "Resources" msgstr "Ressourcen" -#: core/models.py:331 +#: core/models.py:338 msgid "Resource access" msgstr "Ressourcenzugriff" -#: core/models.py:332 +#: core/models.py:339 msgid "Resource accesses" msgstr "Ressourcenzugriffe" -#: core/models.py:338 +#: core/models.py:345 msgid "Resource access with this User and Resource already exists." msgstr "" "Ein Ressourcenzugriff mit diesem Benutzer und dieser Ressource existiert " "bereits." -#: core/models.py:394 +#: core/models.py:402 msgid "Visio room configuration" msgstr "Visio-Raumkonfiguration" -#: core/models.py:395 +#: core/models.py:403 msgid "Values for Visio parameters to configure the room." msgstr "Werte für Visio-Parameter zur Konfiguration des Raums." -#: core/models.py:402 +#: core/models.py:410 msgid "Room PIN code" msgstr "PIN-Code für den Raum" -#: core/models.py:403 +#: core/models.py:411 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "" "Eindeutiger n-stelliger Code, der diesen Raum im Telephonmodus identifiziert." -#: core/models.py:409 core/models.py:563 +#: core/models.py:417 core/models.py:571 msgid "Room" msgstr "Raum" -#: core/models.py:410 +#: core/models.py:418 msgid "Rooms" msgstr "Räume" -#: core/models.py:574 +#: core/models.py:582 msgid "Worker ID" msgstr "Worker-ID" -#: core/models.py:576 +#: core/models.py:584 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -334,141 +342,149 @@ msgstr "" "erhalten, auch wenn der Worker stoppt, was ein einfaches Nachverfolgen " "ermöglicht." -#: core/models.py:584 +#: core/models.py:592 msgid "Recording mode" msgstr "Aufzeichnungsmodus" -#: core/models.py:585 +#: core/models.py:593 msgid "Defines the mode of recording being called." msgstr "Definiert den aufgerufenen Aufzeichnungsmodus." -#: core/models.py:590 core/models.py:591 +#: core/models.py:598 core/models.py:599 msgid "Recording options" msgstr "Aufnahmeoptionen" -#: core/models.py:597 +#: core/models.py:606 +msgid "External Process ID" +msgstr "" + +#: core/models.py:607 +msgid "ID of the external process associated with the recording." +msgstr "" + +#: core/models.py:613 msgid "Recording" msgstr "Aufzeichnung" -#: core/models.py:598 +#: core/models.py:614 msgid "Recordings" msgstr "Aufzeichnungen" -#: core/models.py:706 +#: core/models.py:724 msgid "Recording/user relation" msgstr "Beziehung Aufzeichnung/Benutzer" -#: core/models.py:707 +#: core/models.py:725 msgid "Recording/user relations" msgstr "Beziehungen Aufzeichnung/Benutzer" -#: core/models.py:713 +#: core/models.py:731 msgid "This user is already in this recording." msgstr "Dieser Benutzer ist bereits Teil dieser Aufzeichnung." -#: core/models.py:719 +#: core/models.py:737 msgid "This team is already in this recording." msgstr "Dieses Team ist bereits Teil dieser Aufzeichnung." -#: core/models.py:725 +#: core/models.py:743 msgid "Either user or team must be set, not both." msgstr "Entweder Benutzer oder Team muss festgelegt werden, nicht beides." -#: core/models.py:742 +#: core/models.py:760 msgid "Create rooms" msgstr "Räume erstellen" -#: core/models.py:743 +#: core/models.py:761 msgid "List rooms" msgstr "Räume auflisten" -#: core/models.py:744 +#: core/models.py:762 msgid "Retrieve room details" msgstr "Raumdetails abrufen" -#: core/models.py:745 +#: core/models.py:763 msgid "Update rooms" msgstr "Räume aktualisieren" -#: core/models.py:746 +#: core/models.py:764 msgid "Delete rooms" msgstr "Räume löschen" -#: core/models.py:759 +#: core/models.py:777 msgid "Application name" msgstr "Anwendungsname" -#: core/models.py:760 +#: core/models.py:778 msgid "Descriptive name for this application." msgstr "Beschreibender Name für diese Anwendung." -#: core/models.py:770 +#: core/models.py:788 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "" "Beim Speichern gehasht. Jetzt kopieren, wenn dies ein neues Geheimnis ist." -#: core/models.py:781 +#: core/models.py:799 msgid "Application" msgstr "Anwendung" -#: core/models.py:782 +#: core/models.py:800 msgid "Applications" msgstr "Anwendungen" -#: core/models.py:805 +#: core/models.py:823 msgid "Enter a valid domain" msgstr "Geben Sie eine gültige Domain ein" -#: core/models.py:808 +#: core/models.py:826 msgid "Domain" msgstr "Domain" -#: core/models.py:809 +#: core/models.py:827 msgid "Email domain this application can act on behalf of." msgstr "E-Mail-Domain, im Namen der diese Anwendung handeln kann." -#: core/models.py:821 +#: core/models.py:839 msgid "Application domain" msgstr "Anwendungsdomain" -#: core/models.py:822 +#: core/models.py:840 msgid "Application domains" msgstr "Anwendungsdomains" -#: core/models.py:840 +#: core/models.py:858 msgid "Pending" msgstr "Ausstehend" -#: core/models.py:848 +#: core/models.py:866 msgid "Ready" msgstr "Bereit" -#: core/models.py:854 +#: core/models.py:872 msgid "Background image" msgstr "Hintergrundbild" -#: core/models.py:866 +#: core/models.py:884 msgid "title" msgstr "Titel" -#: core/models.py:890 +#: core/models.py:908 msgid "Malware detection info when the analysis status is unsafe." msgstr "" "Informationen zur Malware-Erkennung, wenn der Analyse-Status unsicher ist." -#: core/models.py:895 +#: core/models.py:913 msgid "File" msgstr "Datei" -#: core/models.py:896 +#: core/models.py:914 msgid "Files" msgstr "Dateien" -#: core/models.py:1000 +#: core/models.py:1018 msgid "This file is already hard deleted." msgstr "Diese Datei wurde bereits endgültig gelöscht." -#: core/models.py:1010 +#: core/models.py:1028 #, fuzzy #| msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted." @@ -476,7 +492,7 @@ msgstr "" "Um eine Datei endgültig zu löschen, muss sie zuvor weich gelöscht worden " "sein." -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:124 msgid "Your recording is ready" msgstr "Ihre Aufzeichnung ist bereit" @@ -576,7 +592,9 @@ msgstr "So speichern Sie diese Aufzeichnung dauerhaft:" #: core/templates/mail/html/screen_recording.html:208 #: core/templates/mail/text/screen_recording.txt:13 -msgid "Click the \"Open\" link below " +#, fuzzy +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open\" button below " msgstr "Klicken Sie auf den Link „Öffnen\" unten " #: core/templates/mail/html/screen_recording.html:209 diff --git a/src/backend/locale/en_US/LC_MESSAGES/django.po b/src/backend/locale/en_US/LC_MESSAGES/django.po index 70c973b9..902536e3 100644 --- a/src/backend/locale/en_US/LC_MESSAGES/django.po +++ b/src/backend/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-05-26 16:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -88,15 +88,15 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Creator is me" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "You must be administrator or owner of a room to add accesses to it." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "This file extension is not allowed." -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1228 msgid "You have reached the maximum number of files for this type." msgstr "You have reached the maximum number of files for this type." @@ -144,51 +144,59 @@ msgstr "Failed to Stop" msgid "Notification succeeded" msgstr "Notification succeeded" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "" + +#: core/models.py:67 +msgid "External process failed" +msgstr "" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "SCREEN_RECORDING" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSCRIPT" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Public Access" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Trusted Access" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Restricted Access" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "id" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "primary key for the record as UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "created on" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "date and time at which a record was created" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "updated on" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "date and time at which a record was last updated" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -196,11 +204,11 @@ msgstr "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -208,55 +216,55 @@ msgstr "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "identity email address" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "admin email address" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "full name" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "short name" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "language" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "The language in which the user wants to see the interface." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "The timezone in which the user wants to see times." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "device" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Whether the user is a device or a real user." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "staff status" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Whether the user can log into this admin site." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "active" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -264,63 +272,63 @@ msgstr "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "user" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "users" -#: core/models.py:272 +#: core/models.py:279 msgid "Resource" msgstr "Resource" -#: core/models.py:273 +#: core/models.py:280 msgid "Resources" msgstr "Resources" -#: core/models.py:331 +#: core/models.py:338 msgid "Resource access" msgstr "Resource access" -#: core/models.py:332 +#: core/models.py:339 msgid "Resource accesses" msgstr "Resource accesses" -#: core/models.py:338 +#: core/models.py:345 msgid "Resource access with this User and Resource already exists." msgstr "Resource access with this User and Resource already exists." -#: core/models.py:394 +#: core/models.py:402 msgid "Visio room configuration" msgstr "Visio room configuration" -#: core/models.py:395 +#: core/models.py:403 msgid "Values for Visio parameters to configure the room." msgstr "Values for Visio parameters to configure the room." -#: core/models.py:402 +#: core/models.py:410 msgid "Room PIN code" msgstr "Room PIN code" -#: core/models.py:403 +#: core/models.py:411 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "Unique n-digit code that identifies this room in telephony mode." -#: core/models.py:409 core/models.py:563 +#: core/models.py:417 core/models.py:571 msgid "Room" msgstr "Room" -#: core/models.py:410 +#: core/models.py:418 msgid "Rooms" msgstr "Rooms" -#: core/models.py:574 +#: core/models.py:582 msgid "Worker ID" msgstr "Worker ID" -#: core/models.py:576 +#: core/models.py:584 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -328,151 +336,159 @@ msgstr "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." -#: core/models.py:584 +#: core/models.py:592 msgid "Recording mode" msgstr "Recording mode" -#: core/models.py:585 +#: core/models.py:593 msgid "Defines the mode of recording being called." msgstr "Defines the mode of recording being called." -#: core/models.py:590 core/models.py:591 +#: core/models.py:598 core/models.py:599 msgid "Recording options" msgstr "Recording options" -#: core/models.py:597 +#: core/models.py:606 +msgid "External Process ID" +msgstr "" + +#: core/models.py:607 +msgid "ID of the external process associated with the recording." +msgstr "" + +#: core/models.py:613 msgid "Recording" msgstr "Recording" -#: core/models.py:598 +#: core/models.py:614 msgid "Recordings" msgstr "Recordings" -#: core/models.py:706 +#: core/models.py:724 msgid "Recording/user relation" msgstr "Recording/user relation" -#: core/models.py:707 +#: core/models.py:725 msgid "Recording/user relations" msgstr "Recording/user relations" -#: core/models.py:713 +#: core/models.py:731 msgid "This user is already in this recording." msgstr "This user is already in this recording." -#: core/models.py:719 +#: core/models.py:737 msgid "This team is already in this recording." msgstr "This team is already in this recording." -#: core/models.py:725 +#: core/models.py:743 msgid "Either user or team must be set, not both." msgstr "Either user or team must be set, not both." -#: core/models.py:742 +#: core/models.py:760 #, fuzzy #| msgid "created on" msgid "Create rooms" msgstr "Create rooms" -#: core/models.py:743 +#: core/models.py:761 msgid "List rooms" msgstr "List rooms" -#: core/models.py:744 +#: core/models.py:762 msgid "Retrieve room details" msgstr "Retrieve room details" -#: core/models.py:745 +#: core/models.py:763 #, fuzzy #| msgid "updated on" msgid "Update rooms" msgstr "Update rooms" -#: core/models.py:746 +#: core/models.py:764 msgid "Delete rooms" msgstr "Delete rooms" -#: core/models.py:759 +#: core/models.py:777 msgid "Application name" msgstr "Application name" -#: core/models.py:760 +#: core/models.py:778 msgid "Descriptive name for this application." msgstr "Descriptive name for this application." -#: core/models.py:770 +#: core/models.py:788 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "Hashed on Save. Copy it now if this is a new secret." -#: core/models.py:781 +#: core/models.py:799 msgid "Application" msgstr "Application" -#: core/models.py:782 +#: core/models.py:800 msgid "Applications" msgstr "Applications" -#: core/models.py:805 +#: core/models.py:823 msgid "Enter a valid domain" msgstr "Enter a valid domain" -#: core/models.py:808 +#: core/models.py:826 msgid "Domain" msgstr "Domain" -#: core/models.py:809 +#: core/models.py:827 msgid "Email domain this application can act on behalf of." msgstr "Email domain this application can act on behalf of." -#: core/models.py:821 +#: core/models.py:839 msgid "Application domain" msgstr "Application domain" -#: core/models.py:822 +#: core/models.py:840 msgid "Application domains" msgstr "Application domains" -#: core/models.py:840 +#: core/models.py:858 #, fuzzy #| msgid "Recording" msgid "Pending" msgstr "Pending" -#: core/models.py:848 +#: core/models.py:866 msgid "Ready" msgstr "Ready" -#: core/models.py:854 +#: core/models.py:872 msgid "Background image" msgstr "Background image" -#: core/models.py:866 +#: core/models.py:884 msgid "title" msgstr "title" -#: core/models.py:890 +#: core/models.py:908 msgid "Malware detection info when the analysis status is unsafe." msgstr "Malware detection info when the analysis status is unsafe." -#: core/models.py:895 +#: core/models.py:913 msgid "File" msgstr "File" -#: core/models.py:896 +#: core/models.py:914 msgid "Files" msgstr "Files" -#: core/models.py:1000 +#: core/models.py:1018 #, fuzzy #| msgid "This user is already in this recording." msgid "This file is already hard deleted." msgstr "This file is already hard deleted." -#: core/models.py:1010 +#: core/models.py:1028 msgid "To hard delete a file, it must first be soft deleted." msgstr "To hard delete a file, it must first be soft deleted." -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:124 msgid "Your recording is ready" msgstr "Your recording is ready" @@ -572,7 +588,9 @@ msgstr "To keep this recording permanently:" #: core/templates/mail/html/screen_recording.html:208 #: core/templates/mail/text/screen_recording.txt:13 -msgid "Click the \"Open\" link below " +#, fuzzy +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open\" button below " msgstr "Click the \"Open\" link below " #: core/templates/mail/html/screen_recording.html:209 diff --git a/src/backend/locale/fr_FR/LC_MESSAGES/django.po b/src/backend/locale/fr_FR/LC_MESSAGES/django.po index cff45d0a..b8b09754 100644 --- a/src/backend/locale/fr_FR/LC_MESSAGES/django.po +++ b/src/backend/locale/fr_FR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-05-26 16:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: antoine.lebaud@mail.numerique.gouv.fr\n" "Language-Team: LANGUAGE \n" @@ -89,17 +89,17 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Je suis le créateur" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "" "Vous devez être administrateur ou propriétaire d'une salle pour y ajouter " "des accès." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "Cette extension n'est pas autorisée" -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1228 msgid "You have reached the maximum number of files for this type." msgstr "Vous avez atteint le nombre maximum de fichiers de ce type" @@ -147,53 +147,61 @@ msgstr "Échec à l'arrêt" msgid "Notification succeeded" msgstr "Notification réussie" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "" + +#: core/models.py:67 +msgid "External process failed" +msgstr "" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "ENREGISTREMENT_ÉCRAN" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSCRIPTION" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Accès public" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Accès de confiance" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Accès restreint" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "id" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "clé primaire pour l'enregistrement sous forme d'UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "créé le" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "date et heure auxquelles un enregistrement a été créé" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "mis à jour le" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "" "date et heure auxquelles un enregistrement a été mis à jour pour la dernière " "fois" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -201,11 +209,11 @@ msgstr "" "Entrez un sub valide. Cette valeur ne peut contenir que des lettres, des " "chiffres et les caractères @/./+/-/_." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -213,55 +221,55 @@ msgstr "" "Optionnel pour les utilisateurs en attente ; requis lors de l'activation du " "compte. 255 caractères maximum. Lettres, chiffres et @/./+/-/_ uniquement." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "adresse e-mail d'identité" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "adresse e-mail d'administrateur" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "nom complet" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "nom court" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "langue" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "La langue dans laquelle l'utilisateur souhaite voir l'interface." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "appareil" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Si l'utilisateur est un appareil ou un utilisateur réel." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "statut du personnel" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Si l'utilisateur peut se connecter à ce site d'administration." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "actif" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -269,65 +277,65 @@ msgstr "" "Si cet utilisateur doit être traité comme actif. Désélectionnez cette option " "au lieu de supprimer des comptes." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "utilisateur" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "utilisateurs" -#: core/models.py:272 +#: core/models.py:279 msgid "Resource" msgstr "Ressource" -#: core/models.py:273 +#: core/models.py:280 msgid "Resources" msgstr "Ressources" -#: core/models.py:331 +#: core/models.py:338 msgid "Resource access" msgstr "Accès aux ressources" -#: core/models.py:332 +#: core/models.py:339 msgid "Resource accesses" msgstr "Accès aux ressources" -#: core/models.py:338 +#: core/models.py:345 msgid "Resource access with this User and Resource already exists." msgstr "" "L'accès à la ressource avec cet utilisateur et cette ressource existe déjà." -#: core/models.py:394 +#: core/models.py:402 msgid "Visio room configuration" msgstr "Configuration de la salle de visioconférence" -#: core/models.py:395 +#: core/models.py:403 msgid "Values for Visio parameters to configure the room." msgstr "Valeurs des paramètres de visioconférence pour configurer la salle." -#: core/models.py:402 +#: core/models.py:410 msgid "Room PIN code" msgstr "Code PIN de la salle" -#: core/models.py:403 +#: core/models.py:411 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "" "Code unique à n chiffres qui identifie cette salle en mode téléphonique." -#: core/models.py:409 core/models.py:563 +#: core/models.py:417 core/models.py:571 msgid "Room" msgstr "Salle" -#: core/models.py:410 +#: core/models.py:418 msgid "Rooms" msgstr "Salles" -#: core/models.py:574 +#: core/models.py:582 msgid "Worker ID" msgstr "ID du Worker" -#: core/models.py:576 +#: core/models.py:584 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -335,150 +343,158 @@ msgstr "" "Entrez un identifiant pour l'enregistrement du Worker. Cet identifiant est " "conservé même lorsque le Worker s'arrête, permettant un suivi facile." -#: core/models.py:584 +#: core/models.py:592 msgid "Recording mode" msgstr "Mode d'enregistrement" -#: core/models.py:585 +#: core/models.py:593 msgid "Defines the mode of recording being called." msgstr "Définit le mode d'enregistrement appelé." -#: core/models.py:590 core/models.py:591 +#: core/models.py:598 core/models.py:599 msgid "Recording options" msgstr "Options d'enregistrement" -#: core/models.py:597 +#: core/models.py:606 +msgid "External Process ID" +msgstr "" + +#: core/models.py:607 +msgid "ID of the external process associated with the recording." +msgstr "" + +#: core/models.py:613 msgid "Recording" msgstr "Enregistrement" -#: core/models.py:598 +#: core/models.py:614 msgid "Recordings" msgstr "Enregistrements" -#: core/models.py:706 +#: core/models.py:724 msgid "Recording/user relation" msgstr "Relation enregistrement/utilisateur" -#: core/models.py:707 +#: core/models.py:725 msgid "Recording/user relations" msgstr "Relations enregistrement/utilisateur" -#: core/models.py:713 +#: core/models.py:731 msgid "This user is already in this recording." msgstr "Cet utilisateur est déjà dans cet enregistrement." -#: core/models.py:719 +#: core/models.py:737 msgid "This team is already in this recording." msgstr "Cette équipe est déjà dans cet enregistrement." -#: core/models.py:725 +#: core/models.py:743 msgid "Either user or team must be set, not both." msgstr "Soit l'utilisateur, soit l'équipe doit être défini, pas les deux." -#: core/models.py:742 +#: core/models.py:760 msgid "Create rooms" msgstr "Créer des salles" -#: core/models.py:743 +#: core/models.py:761 msgid "List rooms" msgstr "Lister les salles" -#: core/models.py:744 +#: core/models.py:762 msgid "Retrieve room details" msgstr "Afficher les détails d’une salle" -#: core/models.py:745 +#: core/models.py:763 msgid "Update rooms" msgstr "Mettre à jour les salles" -#: core/models.py:746 +#: core/models.py:764 msgid "Delete rooms" msgstr "Supprimer les salles" -#: core/models.py:759 +#: core/models.py:777 msgid "Application name" msgstr "Nom de l’application" -#: core/models.py:760 +#: core/models.py:778 msgid "Descriptive name for this application." msgstr "Nom descriptif de cette application." -#: core/models.py:770 +#: core/models.py:788 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "" "Haché lors de l’enregistrement. Copiez-le maintenant s’il s’agit d’un " "nouveau secret." -#: core/models.py:781 +#: core/models.py:799 msgid "Application" msgstr "Application" -#: core/models.py:782 +#: core/models.py:800 msgid "Applications" msgstr "Applications" -#: core/models.py:805 +#: core/models.py:823 msgid "Enter a valid domain" msgstr "Saisissez un domaine valide" -#: core/models.py:808 +#: core/models.py:826 msgid "Domain" msgstr "Domaine" -#: core/models.py:809 +#: core/models.py:827 msgid "Email domain this application can act on behalf of." msgstr "Domaine de messagerie au nom duquel cette application peut agir." -#: core/models.py:821 +#: core/models.py:839 msgid "Application domain" msgstr "Domaine d’application" -#: core/models.py:822 +#: core/models.py:840 msgid "Application domains" msgstr "Domaines d’application" -#: core/models.py:840 +#: core/models.py:858 msgid "Pending" msgstr "En attente" -#: core/models.py:848 +#: core/models.py:866 msgid "Ready" msgstr "Prêt" -#: core/models.py:854 +#: core/models.py:872 msgid "Background image" msgstr "Image de fond" -#: core/models.py:866 +#: core/models.py:884 msgid "title" msgstr "Titre" -#: core/models.py:890 +#: core/models.py:908 msgid "Malware detection info when the analysis status is unsafe." msgstr "" "Information concernant la détection de Malware cand le statut n'est pas sain" -#: core/models.py:895 +#: core/models.py:913 msgid "File" msgstr "Fichier" -#: core/models.py:896 +#: core/models.py:914 msgid "Files" msgstr "Fichiers" -#: core/models.py:1000 +#: core/models.py:1018 #, fuzzy #| msgid "This user is already in this recording." msgid "This file is already hard deleted." msgstr "Ce fichier a été supprimé." -#: core/models.py:1010 +#: core/models.py:1028 msgid "To hard delete a file, it must first be soft deleted." msgstr "" "Pour supprimer définitivement un fichier il doit d'abord avoir été marqué " "comme supprimé (soft delete)" -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:124 msgid "Your recording is ready" msgstr "Votre enregistrement est prêt" @@ -578,7 +594,9 @@ msgstr "Pour conserver cet enregistrement de façon permanente :" #: core/templates/mail/html/screen_recording.html:208 #: core/templates/mail/text/screen_recording.txt:13 -msgid "Click the \"Open\" link below " +#, fuzzy +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open\" button below " msgstr "Cliquez sur le lien \"Ouvrir\" ci-dessous " #: core/templates/mail/html/screen_recording.html:209 diff --git a/src/backend/locale/nl_NL/LC_MESSAGES/django.po b/src/backend/locale/nl_NL/LC_MESSAGES/django.po index 78328812..295e2950 100644 --- a/src/backend/locale/nl_NL/LC_MESSAGES/django.po +++ b/src/backend/locale/nl_NL/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-04-21 14:09+0000\n" +"POT-Creation-Date: 2026-05-26 16:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -88,16 +88,16 @@ msgstr "Scopes" msgid "Creator is me" msgstr "Maker ben ik" -#: core/api/serializers.py:88 +#: core/api/serializers.py:89 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "" "Je moet beheerder of eigenaar van een ruimte zijn om toegang toe te voegen." -#: core/api/serializers.py:509 +#: core/api/serializers.py:534 msgid "This file extension is not allowed." msgstr "Deze bestandsextensie is niet toegestaan." -#: core/api/viewsets.py:1090 +#: core/api/viewsets.py:1228 msgid "You have reached the maximum number of files for this type." msgstr "Het maximale aantal bestanden voor dit type is bereikt." @@ -145,51 +145,59 @@ msgstr "Stoppen mislukt" msgid "Notification succeeded" msgstr "Notificatie geslaagd" -#: core/models.py:89 +#: core/models.py:65 +msgid "External process successful" +msgstr "" + +#: core/models.py:67 +msgid "External process failed" +msgstr "" + +#: core/models.py:96 msgid "SCREEN_RECORDING" msgstr "SCHERM_OPNAME" -#: core/models.py:90 +#: core/models.py:97 msgid "TRANSCRIPT" msgstr "TRANSCRIPT" -#: core/models.py:96 +#: core/models.py:103 msgid "Public Access" msgstr "Openbare toegang" -#: core/models.py:97 +#: core/models.py:104 msgid "Trusted Access" msgstr "Vertrouwde toegang" -#: core/models.py:98 +#: core/models.py:105 msgid "Restricted Access" msgstr "Beperkte toegang" -#: core/models.py:110 +#: core/models.py:117 msgid "id" msgstr "id" -#: core/models.py:111 +#: core/models.py:118 msgid "primary key for the record as UUID" msgstr "primaire sleutel voor het record als UUID" -#: core/models.py:117 +#: core/models.py:124 msgid "created on" msgstr "aangemaakt op" -#: core/models.py:118 +#: core/models.py:125 msgid "date and time at which a record was created" msgstr "datum en tijd waarop een record werd aangemaakt" -#: core/models.py:123 +#: core/models.py:130 msgid "updated on" msgstr "bijgewerkt op" -#: core/models.py:124 +#: core/models.py:131 msgid "date and time at which a record was last updated" msgstr "datum en tijd waarop een record voor het laatst werd bijgewerkt" -#: core/models.py:144 +#: core/models.py:151 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -197,11 +205,11 @@ msgstr "" "Voer een geldige sub in. Deze waarde mag alleen letters, cijfers en @/./+/-/" "_ tekens bevatten." -#: core/models.py:150 +#: core/models.py:157 msgid "sub" msgstr "sub" -#: core/models.py:152 +#: core/models.py:159 msgid "" "Optional for pending users; required upon account activation. 255 characters " "or fewer. Letters, numbers, and @/./+/-/_ characters only." @@ -209,55 +217,55 @@ msgstr "" "Optioneel voor gebruikers in afwachting; vereist bij accountactivering. " "Maximum 255 tekens. Alleen letters, cijfers en @/./+/-/_ toegestaan." -#: core/models.py:161 +#: core/models.py:168 msgid "identity email address" msgstr "identiteit e-mailadres" -#: core/models.py:166 +#: core/models.py:173 msgid "admin email address" msgstr "beheerder e-mailadres" -#: core/models.py:168 +#: core/models.py:175 msgid "full name" msgstr "volledige naam" -#: core/models.py:170 +#: core/models.py:177 msgid "short name" msgstr "korte naam" -#: core/models.py:176 +#: core/models.py:183 msgid "language" msgstr "taal" -#: core/models.py:177 +#: core/models.py:184 msgid "The language in which the user wants to see the interface." msgstr "De taal waarin de gebruiker de interface wil zien." -#: core/models.py:183 +#: core/models.py:190 msgid "The timezone in which the user wants to see times." msgstr "De tijdzone waarin de gebruiker tijden wil zien." -#: core/models.py:186 +#: core/models.py:193 msgid "device" msgstr "apparaat" -#: core/models.py:188 +#: core/models.py:195 msgid "Whether the user is a device or a real user." msgstr "Of de gebruiker een apparaat is of een echte gebruiker." -#: core/models.py:191 +#: core/models.py:198 msgid "staff status" msgstr "personeelsstatus" -#: core/models.py:193 +#: core/models.py:200 msgid "Whether the user can log into this admin site." msgstr "Of de gebruiker kan inloggen op deze beheersite." -#: core/models.py:196 +#: core/models.py:203 msgid "active" msgstr "actief" -#: core/models.py:199 +#: core/models.py:206 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -265,64 +273,64 @@ msgstr "" "Of deze gebruiker als actief moet worden behandeld. Deselecteer dit in " "plaats van accounts te verwijderen." -#: core/models.py:212 +#: core/models.py:219 msgid "user" msgstr "gebruiker" -#: core/models.py:213 +#: core/models.py:220 msgid "users" msgstr "gebruikers" -#: core/models.py:272 +#: core/models.py:279 msgid "Resource" msgstr "Bron" -#: core/models.py:273 +#: core/models.py:280 msgid "Resources" msgstr "Bronnen" -#: core/models.py:331 +#: core/models.py:338 msgid "Resource access" msgstr "Brontoegang" -#: core/models.py:332 +#: core/models.py:339 msgid "Resource accesses" msgstr "Brontoegangsrechten" -#: core/models.py:338 +#: core/models.py:345 msgid "Resource access with this User and Resource already exists." msgstr "Brontoegang met deze gebruiker en bron bestaat al." -#: core/models.py:394 +#: core/models.py:402 msgid "Visio room configuration" msgstr "Visio-ruimteconfiguratie" -#: core/models.py:395 +#: core/models.py:403 msgid "Values for Visio parameters to configure the room." msgstr "Waarden voor Visio-parameters om de ruimte te configureren." -#: core/models.py:402 +#: core/models.py:410 msgid "Room PIN code" msgstr "Pincode van de kamer" -#: core/models.py:403 +#: core/models.py:411 msgid "Unique n-digit code that identifies this room in telephony mode." msgstr "" "Unieke n-cijferige code die deze kamer identificeert in telefonie-modus." -#: core/models.py:409 core/models.py:563 +#: core/models.py:417 core/models.py:571 msgid "Room" msgstr "Ruimte" -#: core/models.py:410 +#: core/models.py:418 msgid "Rooms" msgstr "Ruimtes" -#: core/models.py:574 +#: core/models.py:582 msgid "Worker ID" msgstr "Worker ID" -#: core/models.py:576 +#: core/models.py:584 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -330,140 +338,148 @@ msgstr "" "Voer een identificatie in voor de worker-opname. Deze ID blijft behouden, " "zelfs wanneer de worker stopt, waardoor eenvoudige tracking mogelijk is." -#: core/models.py:584 +#: core/models.py:592 msgid "Recording mode" msgstr "Opnamemodus" -#: core/models.py:585 +#: core/models.py:593 msgid "Defines the mode of recording being called." msgstr "Definieert de modus van opname die wordt aangeroepen." -#: core/models.py:590 core/models.py:591 +#: core/models.py:598 core/models.py:599 msgid "Recording options" msgstr "Opnameopties" -#: core/models.py:597 +#: core/models.py:606 +msgid "External Process ID" +msgstr "" + +#: core/models.py:607 +msgid "ID of the external process associated with the recording." +msgstr "" + +#: core/models.py:613 msgid "Recording" msgstr "Opname" -#: core/models.py:598 +#: core/models.py:614 msgid "Recordings" msgstr "Opnames" -#: core/models.py:706 +#: core/models.py:724 msgid "Recording/user relation" msgstr "Opname/gebruiker-relatie" -#: core/models.py:707 +#: core/models.py:725 msgid "Recording/user relations" msgstr "Opname/gebruiker-relaties" -#: core/models.py:713 +#: core/models.py:731 msgid "This user is already in this recording." msgstr "Deze gebruiker is al in deze opname." -#: core/models.py:719 +#: core/models.py:737 msgid "This team is already in this recording." msgstr "Dit team is al in deze opname." -#: core/models.py:725 +#: core/models.py:743 msgid "Either user or team must be set, not both." msgstr "Ofwel gebruiker of team moet worden ingesteld, niet beide." -#: core/models.py:742 +#: core/models.py:760 msgid "Create rooms" msgstr "Ruimtes aanmaken" -#: core/models.py:743 +#: core/models.py:761 msgid "List rooms" msgstr "Ruimtes weergeven" -#: core/models.py:744 +#: core/models.py:762 msgid "Retrieve room details" msgstr "Details van een ruimte ophalen" -#: core/models.py:745 +#: core/models.py:763 msgid "Update rooms" msgstr "Ruimtes bijwerken" -#: core/models.py:746 +#: core/models.py:764 msgid "Delete rooms" msgstr "Ruimtes verwijderen" -#: core/models.py:759 +#: core/models.py:777 msgid "Application name" msgstr "Naam van de applicatie" -#: core/models.py:760 +#: core/models.py:778 msgid "Descriptive name for this application." msgstr "Beschrijvende naam voor deze applicatie." -#: core/models.py:770 +#: core/models.py:788 msgid "Hashed on Save. Copy it now if this is a new secret." msgstr "" "Wordt gehasht bij het opslaan. Kopieer het nu als dit een nieuw geheim is." -#: core/models.py:781 +#: core/models.py:799 msgid "Application" msgstr "Applicatie" -#: core/models.py:782 +#: core/models.py:800 msgid "Applications" msgstr "Applicaties" -#: core/models.py:805 +#: core/models.py:823 msgid "Enter a valid domain" msgstr "Voer een geldig domein in" -#: core/models.py:808 +#: core/models.py:826 msgid "Domain" msgstr "Domein" -#: core/models.py:809 +#: core/models.py:827 msgid "Email domain this application can act on behalf of." msgstr "E-maildomein namens welke deze applicatie kan handelen." -#: core/models.py:821 +#: core/models.py:839 msgid "Application domain" msgstr "Applicatiedomein" -#: core/models.py:822 +#: core/models.py:840 msgid "Application domains" msgstr "Applicatiedomeinen" -#: core/models.py:840 +#: core/models.py:858 msgid "Pending" msgstr "In afwachting" -#: core/models.py:848 +#: core/models.py:866 msgid "Ready" msgstr "Klaar" -#: core/models.py:854 +#: core/models.py:872 msgid "Background image" msgstr "Achtergrondafbeelding" -#: core/models.py:866 +#: core/models.py:884 msgid "title" msgstr "Titel" -#: core/models.py:890 +#: core/models.py:908 msgid "Malware detection info when the analysis status is unsafe." msgstr "Informatie over malwaredetectie wanneer de analysestatus onveilig is." -#: core/models.py:895 +#: core/models.py:913 msgid "File" msgstr "Bestand" -#: core/models.py:896 +#: core/models.py:914 msgid "Files" msgstr "Bestanden" -#: core/models.py:1000 +#: core/models.py:1018 msgid "This file is already hard deleted." msgstr "Dit bestand is al definitief verwijderd." -#: core/models.py:1010 +#: core/models.py:1028 #, fuzzy #| msgid "To hard delete a file, it must first be soft deleted." msgid "To hard delete a file, it must first be soft deleted." @@ -471,7 +487,7 @@ msgstr "" "Om een bestand definitief te verwijderen, moet het eerst zacht verwijderd " "zijn." -#: core/recording/event/notification.py:116 +#: core/recording/event/notification.py:124 msgid "Your recording is ready" msgstr "Je opname is klaar" @@ -571,7 +587,9 @@ msgstr "Om deze opname permanent te bewaren:" #: core/templates/mail/html/screen_recording.html:208 #: core/templates/mail/text/screen_recording.txt:13 -msgid "Click the \"Open\" link below " +#, fuzzy +#| msgid "Click the \"Open\" link below " +msgid "Click the \"Open\" button below " msgstr "Klik op de \"Openen\"-link hieronder " #: core/templates/mail/html/screen_recording.html:209 diff --git a/src/backend/meet/settings.py b/src/backend/meet/settings.py index a855a20d..c17b7f9e 100755 --- a/src/backend/meet/settings.py +++ b/src/backend/meet/settings.py @@ -744,6 +744,9 @@ class Base(Configuration): SUMMARY_SERVICE_API_TOKEN = SecretFileValue( None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None ) + SUMMARY_SERVICE_WEBHOOK_API_TOKEN = SecretFileValue( + None, environ_name="SUMMARY_SERVICE_WEBHOOK_API_TOKEN", environ_prefix=None + ) SCREEN_RECORDING_BASE_URL = values.Value( None, environ_name="SCREEN_RECORDING_BASE_URL", environ_prefix=None ) diff --git a/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl b/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl index 124608c8..c3c3db4e 100644 --- a/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-dinum/values.meet.yaml.gotmpl @@ -105,8 +105,9 @@ backend: RECORDING_ENABLE: True RECORDING_STORAGE_EVENT_ENABLE: True RECORDING_STORAGE_EVENT_TOKEN: password - SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/ + SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v2/async-jobs/transcribe/ SUMMARY_SERVICE_API_TOKEN: password + SUMMARY_SERVICE_WEBHOOK_API_TOKEN: webhook-password RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording ROOM_TELEPHONY_ENABLED: True SSL_CERT_FILE: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem diff --git a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl index e0ac87ff..32453223 100644 --- a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl @@ -6,7 +6,26 @@ _summaryEnvVars: &summaryEnvVars AWS_S3_ACCESS_KEY_ID: meet AWS_S3_SECRET_ACCESS_KEY: password AWS_S3_SECURE_ACCESS: False - AUTHORIZED_TENANTS: '[{"id": "dictaphone", "api_key": "dictaphone_token", "webhook_url": "http://dictaphone-backend.dictaphone.svc.cluster.local/api/v1.0/ai-jobs/webhook/", "webhook_api_key": "token_summary"}]' + # Note: setting allowed_push_to_docs false because it won't work in dev mode + AUTHORIZED_TENANTS: > + [ + { + "id": "dictaphone", + "api_key": "dictaphone_token", + "webhook_url": "http://dictaphone-backend.dictaphone.svc.cluster.local/api/v1.0/ai-jobs/webhook/", + "webhook_api_key": "token_summary", + "allowed_push_to_docs": false + }, + { + "id": "visio", + "api_key": "password", + "webhook_url": "https://meet.127.0.0.1.nip.io/api/v1.0/recordings/external-process-hook/", + "webhook_api_key": "webhook-password", + "allowed_push_to_docs": false + } + ] + SSL_CERT_FILE: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem + IS_DOCS_INTEGRATION_ENABLED: false WHISPERX_API_KEY: secretKeyRef: name: secret-dev @@ -109,8 +128,9 @@ backend: RECORDING_ENABLE: True RECORDING_STORAGE_EVENT_ENABLE: True RECORDING_STORAGE_EVENT_TOKEN: password - SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/ + SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v2/async-jobs/transcribe/ SUMMARY_SERVICE_API_TOKEN: password + SUMMARY_SERVICE_WEBHOOK_API_TOKEN: webhook-password RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording ROOM_TELEPHONY_ENABLED: True ROOM_TELEPHONY_DEFAULT_COUNTRY: 'FR' @@ -258,6 +278,23 @@ celerySummaryBackend: - "-Q" - "call-webhook-queue-v2" + + # Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false + extraVolumeMounts: + - name: certs + mountPath: /usr/local/lib/python3.13/site-packages/certifi/cacert.pem + subPath: cacert.pem + + # Extra volumes to manage our local custom CA and avoid to set ssl_verify: false + extraVolumes: + - name: certs + configMap: + name: certifi + items: + - key: cacert.pem + path: cacert.pem + + agentMetadata: replicas: 1 envVars: diff --git a/src/summary/summary/core/file_service.py b/src/summary/summary/core/file_service.py index a534823e..cc3abe43 100644 --- a/src/summary/summary/core/file_service.py +++ b/src/summary/summary/core/file_service.py @@ -155,12 +155,6 @@ class FileService: raise ValueError("Invalid cloud_storage_url") extension = Path(urlparse(cloud_storage_url).path).suffix.lower() - if extension not in self._allowed_extensions: - logger.warning( - "Invalid file extension '%s' from cloud_storage_url", extension - ) - raise ValueError(f"Invalid file extension '{extension}'") - try: with requests.get( cloud_storage_url, diff --git a/src/summary/tests/conftest.py b/src/summary/tests/conftest.py index 6971ed5c..f24f8db5 100644 --- a/src/summary/tests/conftest.py +++ b/src/summary/tests/conftest.py @@ -11,7 +11,6 @@ from summary.main import app def get_settings_override(): """Return settings for tests.""" return Settings( - v1_tenant_id="test-tenant", authorized_tenants=( AuthorizedTenant( webhook_url="https://example.com/webhook",