mirror of
https://github.com/suitenumerique/meet.git
synced 2026-10-07 14:00:56 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a5fd2c999 | |||
| e9b523b522 | |||
| 70d348337e | |||
| 258722adfd | |||
| 0a6724e7ad | |||
| c1c2d93932 | |||
| 9efb9577c4 | |||
| 2137c6b444 | |||
| aa01733f6b | |||
| 03db669e51 | |||
| 97a73bf4f2 | |||
| 267a2265d9 | |||
| 14507d57a9 |
+44
-13
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18
|
||||
image: postgres:16
|
||||
env:
|
||||
POSTGRES_DB: meet
|
||||
POSTGRES_USER: dinum
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# needed because the postgres container does not provide a healthcheck
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
redis:
|
||||
image: redis:7
|
||||
image: redis:5
|
||||
ports:
|
||||
- 6379:6379
|
||||
# Set health checks to wait until redis has started
|
||||
@@ -87,7 +87,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Create writable /data
|
||||
run: |
|
||||
@@ -115,11 +117,11 @@ jobs:
|
||||
timeout 30 sh -c 'until docker exec garage /garage health; do sleep 1; done'
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.14"
|
||||
python-version: "3.13"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
||||
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
||||
- name: Install the dependencies
|
||||
run: uv sync --locked --all-extras
|
||||
|
||||
@@ -158,7 +160,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install ffmpeg
|
||||
run: |
|
||||
@@ -166,12 +170,12 @@ jobs:
|
||||
sudo apt-get install -y ffmpeg
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
||||
with:
|
||||
python-version: "3.14"
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
||||
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --locked --all-extras
|
||||
@@ -185,7 +189,9 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd src/frontend/ && npm ci --ignore-scripts
|
||||
@@ -196,6 +202,27 @@ jobs:
|
||||
- name: Check format
|
||||
run: cd src/frontend/ && npm run check
|
||||
|
||||
test-front:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: "24"
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd src/frontend/ && npm ci --ignore-scripts
|
||||
|
||||
- name: Run tests
|
||||
run: cd src/frontend/ && npm test
|
||||
|
||||
lint-sdk:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@@ -205,7 +232,9 @@ jobs:
|
||||
working-directory: src/sdk/library
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
@@ -226,7 +255,9 @@ jobs:
|
||||
working-directory: src/sdk/library
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
@@ -8,6 +8,8 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.34.0] - 2026-10-07
|
||||
|
||||
### Added
|
||||
|
||||
- ✨(helm) import environment variables from Secrets and ConfigMaps
|
||||
@@ -16,6 +18,8 @@ and this project adheres to
|
||||
- 🔧(summary) add setting to control Sentry traces sampling rate
|
||||
- ✨(frontend) let signed-out visitors start a meeting
|
||||
- ✨(backend) expose `allow_unregistered_rooms` in the frontend configuration
|
||||
- ✅(frontend) add vitest so the frontend can carry unit tests
|
||||
- ♿️(frontend) make participant pagination readable and keyboard reachable #1775
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -165,6 +169,7 @@ and this project adheres to
|
||||
### Added
|
||||
|
||||
- ✨(any) let any authenticated user manage the lobby on trusted rooms
|
||||
|
||||
### Changed
|
||||
|
||||
- 📱(frontend) collapse mobile control bar items on narrow viewports
|
||||
|
||||
@@ -169,7 +169,7 @@ run-summary: ## start only the summary application and all needed services
|
||||
@$(COMPOSE) up --force-recreate -d celery-summary-summarize
|
||||
.PHONY: run-summary
|
||||
|
||||
run-agents: ## start the multi-user-transcriber agent
|
||||
run-agents: ## start the LiveKit agents (opt-in, see docs/developping_locally.md)
|
||||
@$(MAKE) run-agent-multi-user-transcriber
|
||||
@$(MAKE) run-agent-metadata-collector
|
||||
.PHONY: run-agents
|
||||
@@ -186,7 +186,6 @@ run:
|
||||
run: ## start the wsgi (production) and development server
|
||||
@$(MAKE) run-backend
|
||||
@$(MAKE) run-summary
|
||||
@$(MAKE) run-agents
|
||||
@$(COMPOSE) up --force-recreate -d frontend
|
||||
.PHONY: run
|
||||
|
||||
@@ -260,7 +259,8 @@ lint-pylint: ## lint back-end python sources with pylint only on changed files f
|
||||
test: ## run project tests; pass extra pytest args via ARGS, e.g. `make test ARGS="-vv"`
|
||||
@args="$(ARGS) $(filter-out $@,$(MAKECMDGOALS))" && \
|
||||
$(MAKE) test-back-parallel ARGS="$${args}" && \
|
||||
$(MAKE) test-summary ARGS="$${args}"
|
||||
$(MAKE) test-summary ARGS="$${args}" && \
|
||||
$(MAKE) test-frontend
|
||||
.PHONY: test
|
||||
|
||||
test-back: ## run back-end tests (pass extra pytest args via ARGS)
|
||||
@@ -278,6 +278,10 @@ test-summary: ## run summary tests (pass extra pytest args via ARGS)
|
||||
bin/pytest-summary $${args}
|
||||
.PHONY: test-summary
|
||||
|
||||
test-frontend: ## run the frontend unit tests
|
||||
cd $(PATH_FRONT) && npm test
|
||||
.PHONY: test-frontend
|
||||
|
||||
makemigrations: ## run django makemigrations for the Meet project.
|
||||
@echo "$(BOLD)Running makemigrations$(RESET)"
|
||||
@$(COMPOSE) up -d postgresql
|
||||
|
||||
+76
-25
@@ -16,43 +16,54 @@ the following command inside your docker container:
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Purging inactive rooms
|
||||
### Marketing / Brevo integration now uses `django-lasuite`
|
||||
|
||||
Rooms now keep track of the last time they were started (`last_started_at`), fed by LiveKit's `room_started` webhook. A new `purge_inactive_rooms` management command permanently deletes the rooms that have not been started for `ROOM_INACTIVITY_DELETION_DAYS` days. See [the room purge documentation](docs/features/room-purge.md).
|
||||
The in-house marketing service (`core.services.marketing`) has been removed and
|
||||
replaced by the shared implementation from `django-lasuite`
|
||||
(`lasuite.marketing`). This fixes a bug where updating a user's contact on
|
||||
Brevo overwrote their list memberships, removing lists set by other
|
||||
La Suite products. Existing lists are now preserved and merged.
|
||||
|
||||
- The feature is **disabled by default**: nothing is deleted unless you set `ROOM_INACTIVITY_DELETION_DAYS`.
|
||||
- The migration marks every existing room as started at the time of the upgrade, so no existing room can be purged before a full inactivity period has elapsed after upgrading.
|
||||
- Rooms holding a saved recording their users may still access are kept: any saved recording, or, when `RECORDING_EXPIRATION_DAYS` is set, a saved recording created within that window.
|
||||
- Inactivity is measured from LiveKit's `room_started` webhook: if it is not delivered to your backend, rooms in daily use look inactive and get purged.
|
||||
- When a room is purged, all it's configuration and access rights are also deleted. Its slug becomes available again and can be reused when a meeting is created from that same URL.
|
||||
**Celery worker required.** Newsletter signup on login
|
||||
(`SIGNUP_NEW_USER_TO_MARKETING_EMAIL=True`) is now dispatched as an
|
||||
asynchronous Celery task (`lasuite.marketing.tasks.create_or_update_contact`)
|
||||
instead of a synchronous call with a 1s timeout. Make sure a Celery worker is
|
||||
running alongside the backend, otherwise contacts will never be pushed to Brevo.
|
||||
|
||||
* With `ALLOW_UNREGISTERED_ROOMS=false`, only an authenticated user can navigate to a previously existing link after the room has been purged. Doing so recreates the room in the database with a fresh configuration, with that user associated with it and granted admin rights.
|
||||
* With `ALLOW_UNREGISTERED_ROOMS=true`, any user can reopen the purged room by navigating to the same URL. In that case, the room is created dynamically and no corresponding room entry is persisted in the database.
|
||||
**Configuration changes.** The following environment variables / settings are
|
||||
**removed** and no longer read:
|
||||
|
||||
### Local development: MinIO replaced by Garage
|
||||
- `MARKETING_SERVICE_CLASS`
|
||||
- `BREVO_API_KEY`
|
||||
- `BREVO_API_CONTACT_LIST_IDS`
|
||||
- `BREVO_API_CONTACT_ATTRIBUTES` (previous default: `{"VISIO_USER": True}`)
|
||||
- `BREVO_API_TIMEOUT`
|
||||
|
||||
The development stacks now use [Garage](https://garagehq.deuxfleurs.fr/) instead of MinIO as S3 storage. Garage keeps its own format in `data/media/meta` and `data/media/data` and cannot read what MinIO left there, so local recordings and files will be lost.
|
||||
They are replaced by a single `LASUITE_MARKETING` setting, configured through:
|
||||
|
||||
To migrate a local environment:
|
||||
| Variable | Default | Description |
|
||||
| ------------------------------ | ------------------------------------------------ | -------------------------------------------- |
|
||||
| `LASUITE_MARKETING_BACKEND` | `lasuite.marketing.backends.dummy.DummyBackend` | Backend class path |
|
||||
| `LASUITE_MARKETING_PARAMETERS` | `{}` | Keyword arguments passed to the backend |
|
||||
|
||||
1. Stop the stack and remove its containers, including the former `minio` one: `docker compose down --remove-orphans`
|
||||
2. Optionally reclaim the space used by MinIO: `rm -rf data/media && make data/media`
|
||||
3. In your `env.d/development/*` files, replace `minio:9000` by `garage:9000`, the `meet` / `password` credentials by `meet-access-key` / `meet-secret-access-key`, and add `AWS_S3_REGION_NAME=local` (or delete these files and run `make create-env-files`)
|
||||
4. Run `make create-env-files` to generate `env.d/development/garage`, which holds a random RPC secret for Garage.
|
||||
5. Rebuild the images, since the summary and agent images now install boto3 instead of minio
|
||||
⚠️ The default backend is now a **dummy** (no-op). If you previously used
|
||||
Brevo, you must explicitly configure it, otherwise signups are silently dropped:
|
||||
|
||||
### Summary service and metadata collector: boto3 replaces the minio client
|
||||
LASUITE_MARKETING_BACKEND=lasuite.marketing.backends.brevo.BrevoBackend
|
||||
LASUITE_MARKETING_PARAMETERS={"api_key": "<your-brevo-api-key>", "api_contact_list_ids": [1, 2], "api_contact_attributes": {"VISIO_USER": True}}
|
||||
|
||||
The summary service and the metadata collector agent now talk to S3 through boto3 instead of the minio client, with the same settings.
|
||||
Requests are now signed for `AWS_S3_REGION_NAME` as-is. When it is not set, the region is no longer looked up from the bucket: boto3 falls back to `AWS_DEFAULT_REGION`, then to `us-east-1`. If you left `AWS_S3_REGION_NAME` unset, set it to your provider's region before upgrading, or providers that check the signing region will reject the transcripts, summaries and meeting metadata uploads, as well as their signed URLs.
|
||||
Migration mapping:
|
||||
|
||||
Also:
|
||||
- Signed URLs to transcripts and summaries are now always path-style (`<endpoint>/<bucket>/<key>`), whereas the minio client used virtual-hosted-style URLs
|
||||
- The metadata collector now accepts `AWS_S3_ENDPOINT_URL` with or without a scheme, like the summary service: the scheme always follows `AWS_S3_SECURE_ACCESS`.
|
||||
- `BREVO_API_KEY` → `api_key`
|
||||
- `BREVO_API_CONTACT_LIST_IDS` → `api_contact_list_ids`
|
||||
- `BREVO_API_CONTACT_ATTRIBUTES` → `api_contact_attributes` (re-add
|
||||
`{"VISIO_USER": True}` if you relied on the old default)
|
||||
- `BREVO_API_TIMEOUT` → no equivalent (the request runs in a background task)
|
||||
|
||||
### Helm chart: media services default to Garage
|
||||
Note: `BREVO_API_KEY` used to support being read from a secret file; the API key
|
||||
now lives inside `LASUITE_MARKETING_PARAMETERS`, so adapt how you inject that
|
||||
secret (e.g. build the whole variable from your secret store).
|
||||
|
||||
The `meet` chart now defaults `serviceMedia.host` and `serviceMediaFiles.host` to `garage.meet.svc.cluster.local`, and the `upstream-vhost` annotation of `ingressMedia` and `ingressMediaFiles` to `garage.meet.svc.cluster.local:9000`. If you relied on the former `minio.meet.svc.cluster.local` defaults, set these values explicitly to your S3 service before upgrading, or recordings and files stop being served under `/media`.
|
||||
### Recording encoding settings replaced by a resolution/profile model
|
||||
|
||||
The `RECORDING_ENCODING_*` settings introduced in v1.16.0 exposed raw encoder
|
||||
@@ -175,6 +186,46 @@ Before enabling it:
|
||||
See [docs/features/recording.md](docs/features/recording.md#tuning-recording-encoding)
|
||||
for the full setting reference, the shipped profile table and the tuning caveats.
|
||||
|
||||
## v1.33.0
|
||||
|
||||
### Purging inactive rooms
|
||||
|
||||
Rooms now keep track of the last time they were started (`last_started_at`), fed by LiveKit's `room_started` webhook. A new `purge_inactive_rooms` management command permanently deletes the rooms that have not been started for `ROOM_INACTIVITY_DELETION_DAYS` days. See [the room purge documentation](docs/features/room-purge.md).
|
||||
|
||||
- The feature is **disabled by default**: nothing is deleted unless you set `ROOM_INACTIVITY_DELETION_DAYS`.
|
||||
- The migration marks every existing room as started at the time of the upgrade, so no existing room can be purged before a full inactivity period has elapsed after upgrading.
|
||||
- Rooms holding a saved recording their users may still access are kept: any saved recording, or, when `RECORDING_EXPIRATION_DAYS` is set, a saved recording created within that window.
|
||||
- Inactivity is measured from LiveKit's `room_started` webhook: if it is not delivered to your backend, rooms in daily use look inactive and get purged.
|
||||
- When a room is purged, all it's configuration and access rights are also deleted. Its slug becomes available again and can be reused when a meeting is created from that same URL.
|
||||
|
||||
* With `ALLOW_UNREGISTERED_ROOMS=false`, only an authenticated user can navigate to a previously existing link after the room has been purged. Doing so recreates the room in the database with a fresh configuration, with that user associated with it and granted admin rights.
|
||||
* With `ALLOW_UNREGISTERED_ROOMS=true`, any user can reopen the purged room by navigating to the same URL. In that case, the room is created dynamically and no corresponding room entry is persisted in the database.
|
||||
|
||||
### Local development: MinIO replaced by Garage
|
||||
|
||||
The development stacks now use [Garage](https://garagehq.deuxfleurs.fr/) instead of MinIO as S3 storage. Garage keeps its own format in `data/media/meta` and `data/media/data` and cannot read what MinIO left there, so local recordings and files will be lost.
|
||||
|
||||
To migrate a local environment:
|
||||
|
||||
1. Stop the stack and remove its containers, including the former `minio` one: `docker compose down --remove-orphans`
|
||||
2. Optionally reclaim the space used by MinIO: `rm -rf data/media && make data/media`
|
||||
3. In your `env.d/development/*` files, replace `minio:9000` by `garage:9000`, the `meet` / `password` credentials by `meet-access-key` / `meet-secret-access-key`, and add `AWS_S3_REGION_NAME=local` (or delete these files and run `make create-env-files`)
|
||||
4. Run `make create-env-files` to generate `env.d/development/garage`, which holds a random RPC secret for Garage.
|
||||
5. Rebuild the images, since the summary and agent images now install boto3 instead of minio
|
||||
|
||||
### Summary service and metadata collector: boto3 replaces the minio client
|
||||
|
||||
The summary service and the metadata collector agent now talk to S3 through boto3 instead of the minio client, with the same settings.
|
||||
Requests are now signed for `AWS_S3_REGION_NAME` as-is. When it is not set, the region is no longer looked up from the bucket: boto3 falls back to `AWS_DEFAULT_REGION`. If you left `AWS_S3_REGION_NAME` unset, set it to your provider's region before upgrading, or providers that check the signing region will reject the transcripts, summaries and meeting metadata uploads, as well as their signed URLs.
|
||||
|
||||
Also:
|
||||
- Signed URLs to transcripts and summaries are now always path-style (`<endpoint>/<bucket>/<key>`), whereas the minio client used virtual-hosted-style URLs
|
||||
- The metadata collector now accepts `AWS_S3_ENDPOINT_URL` with or without a scheme, like the summary service: the scheme always follows `AWS_S3_SECURE_ACCESS`.
|
||||
|
||||
### Helm chart: media services default to Garage
|
||||
|
||||
The `meet` chart now defaults `serviceMedia.host` and `serviceMediaFiles.host` to `garage.meet.svc.cluster.local`, and the `upstream-vhost` annotation of `ingressMedia` and `ingressMediaFiles` to `garage.meet.svc.cluster.local:9000`. If you relied on the former `minio.meet.svc.cluster.local` defaults, set these values explicitly to your S3 service before upgrading, or recordings and files stop being served under `/media`.
|
||||
|
||||
## v1.30.0
|
||||
|
||||
### Removing S3 storage-event webhooks for recordings
|
||||
|
||||
+9
-8
@@ -104,15 +104,16 @@ k8s_yaml(secret_yaml_generic(
|
||||
|
||||
k8s_yaml(local('cd ../src/helm && helmfile -n meet -e ${DEV_ENV:-dev-keycloak} template .'))
|
||||
|
||||
k8s_resource('garage-cors', resource_deps=['garage'])
|
||||
k8s_resource('meet-backend', resource_deps=['postgresql', 'garage-cors', 'redis', 'livekit-livekit-server'])
|
||||
k8s_resource('meet-celery-backend', resource_deps=['redis'])
|
||||
k8s_resource('meet-celery-summarize', resource_deps=['redis'])
|
||||
k8s_resource('meet-celery-summary-backend', resource_deps=['redis'])
|
||||
k8s_resource('meet-celery-transcribe-default', resource_deps=['redis'])
|
||||
k8s_resource('livekit-livekit-server', resource_deps=['redis'])
|
||||
k8s_resource('dev-backend-garage-cors', resource_deps=['dev-backend-garage'])
|
||||
k8s_resource('dev-backend-keycloak', resource_deps=['dev-backend-keycloak-pg'])
|
||||
k8s_resource('meet-backend', resource_deps=['dev-backend-postgres', 'dev-backend-garage-cors', 'dev-backend-redis', 'dev-backend-keycloak', 'livekit-livekit-server'])
|
||||
k8s_resource('meet-celery-backend', resource_deps=['dev-backend-redis'])
|
||||
k8s_resource('meet-celery-summarize', resource_deps=['dev-backend-redis'])
|
||||
k8s_resource('meet-celery-summary-backend', resource_deps=['dev-backend-redis'])
|
||||
k8s_resource('meet-celery-transcribe-default', resource_deps=['dev-backend-redis'])
|
||||
k8s_resource('livekit-livekit-server', resource_deps=['dev-backend-redis'])
|
||||
k8s_resource('livekit-livekit-server-test-connection', resource_deps=['livekit-livekit-server'])
|
||||
k8s_resource('keycloak', resource_deps=['kc-postgresql'])
|
||||
k8s_resource('livekit-egress', resource_deps=['livekit-livekit-server'])
|
||||
# Trigger once on launch
|
||||
k8s_resource(
|
||||
'meet-backend-createsuperuser',
|
||||
|
||||
+5
-8
@@ -9,6 +9,8 @@ services:
|
||||
|
||||
redis:
|
||||
image: redis:5
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
mailcatcher:
|
||||
image: sj26/mailcatcher:latest
|
||||
@@ -271,11 +273,6 @@ services:
|
||||
- ./src/agents:/app
|
||||
- /app/.venv
|
||||
|
||||
redis-summary:
|
||||
image: redis:5
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
app-summary-dev:
|
||||
build:
|
||||
context: src/summary
|
||||
@@ -290,7 +287,7 @@ services:
|
||||
volumes:
|
||||
- ./src/summary:/app
|
||||
depends_on:
|
||||
- redis-summary
|
||||
- redis
|
||||
|
||||
celery-summary-transcribe:
|
||||
container_name: celery-summary-transcribe
|
||||
@@ -304,7 +301,7 @@ services:
|
||||
volumes:
|
||||
- ./src/summary:/app
|
||||
depends_on:
|
||||
- redis-summary
|
||||
- redis
|
||||
- app-summary-dev
|
||||
- garage
|
||||
develop:
|
||||
@@ -324,7 +321,7 @@ services:
|
||||
volumes:
|
||||
- ./src/summary:/app
|
||||
depends_on:
|
||||
- redis-summary
|
||||
- redis
|
||||
- app-summary-dev
|
||||
- garage
|
||||
develop:
|
||||
|
||||
@@ -107,6 +107,37 @@ $ npm i
|
||||
$ npm run dev
|
||||
```
|
||||
|
||||
### LiveKit agents (optional)
|
||||
|
||||
The LiveKit agents are not started by `make run`. Each one runs its own
|
||||
container and stays connected to LiveKit, which costs CPU and memory you
|
||||
don't need unless you work on the features they power. Start them only
|
||||
when you need them.
|
||||
|
||||
| Agent | Feature | Make command | Setting in `env.d/development/common` |
|
||||
|---|---|---|---|
|
||||
| `metadata-collector-dev` | Recording metadata (used to identify speakers in transcripts) | `make run-agent-metadata-collector` | `METADATA_COLLECTOR_ENABLED=True` |
|
||||
| `multi-user-transcriber-dev` | Live subtitles | `make run-agent-multi-user-transcriber` | `ROOM_SUBTITLE_ENABLED=True` |
|
||||
|
||||
To start both at once:
|
||||
```shellscript
|
||||
$ make run-agents
|
||||
```
|
||||
|
||||
Then set the matching settings to `True` and restart the backend so it
|
||||
picks them up:
|
||||
```shellscript
|
||||
$ make run-backend
|
||||
```
|
||||
|
||||
The multi-user transcriber also needs a speech-to-text provider. Configure
|
||||
`STT_PROVIDER` and its credentials in
|
||||
`env.d/development/multi_user_transcriber`.
|
||||
|
||||
Keep the settings and the agents in sync: if a setting is `True` while its
|
||||
agent is stopped, the backend still dispatches jobs to it and the feature
|
||||
fails silently.
|
||||
|
||||
---
|
||||
|
||||
## Adding Content
|
||||
|
||||
@@ -104,10 +104,11 @@ ROOM_TELEPHONY_ENABLED=True
|
||||
# ROOMKIT_ENABLED = True
|
||||
# ROOMKIT_SERVER_TO_SERVER_API_TOKEN = ThisIsAnExampleKeyForDevPurposeOnly
|
||||
|
||||
# Metadata
|
||||
METADATA_COLLECTOR_ENABLED=True
|
||||
# LiveKit agents (opt-in, start them with `make run-agents`)
|
||||
# Metadata (requires the metadata-collector agent)
|
||||
METADATA_COLLECTOR_ENABLED=False
|
||||
|
||||
# Subtitle
|
||||
# Subtitle (requires the multi-user-transcriber agent)
|
||||
ROOM_SUBTITLE_ENABLED=False
|
||||
|
||||
FRONTEND_USE_FRENCH_GOV_FOOTER=False
|
||||
|
||||
@@ -9,6 +9,10 @@ AWS_S3_ACCESS_KEY_ID="meet-access-key"
|
||||
AWS_S3_SECRET_ACCESS_KEY="meet-secret-access-key"
|
||||
AWS_S3_REGION_NAME="local"
|
||||
|
||||
CELERY_BROKER_URL="redis://redis:6379/2"
|
||||
CELERY_RESULT_BACKEND="redis://redis:6379/2"
|
||||
TASK_TRACKER_REDIS_URL="redis://redis:6379/2"
|
||||
|
||||
WHISPERX_BASE_URL="https://configure-your-url.com"
|
||||
WHISPERX_ASR_MODEL="large-v2"
|
||||
WHISPERX_API_KEY="your-secret-key"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "agents"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"livekit-agents==1.7.0",
|
||||
|
||||
Generated
+1
-1
@@ -9,7 +9,7 @@ resolution-markers = [
|
||||
|
||||
[[package]]
|
||||
name = "agents"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "boto3" },
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "uv_build"
|
||||
|
||||
[project]
|
||||
name = "meet"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
|
||||
Generated
+1
-1
@@ -1297,7 +1297,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "meet"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
Generated
+280
-6
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meet",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/atkinson-hyperlegible-next": "5.3.0",
|
||||
"@fontsource-variable/lexend": "5.3.0",
|
||||
@@ -64,7 +64,8 @@
|
||||
"typescript-eslint": "8.60.1",
|
||||
"vite": "8.0.14",
|
||||
"vite-plugin-static-copy": "4.1.1",
|
||||
"vite-plugin-svgr": "5.2.0"
|
||||
"vite-plugin-svgr": "5.2.0",
|
||||
"vitest": "5.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/react-spectrum": {
|
||||
@@ -942,9 +943,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
|
||||
"integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -2506,6 +2507,24 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chai": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
|
||||
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/deep-eql": "*",
|
||||
"assertion-error": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/deep-eql": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
||||
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/dom-mediacapture-record": {
|
||||
"version": "1.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.22.tgz",
|
||||
@@ -2786,6 +2805,64 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.2.tgz",
|
||||
"integrity": "sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.31",
|
||||
"@vitest/spy": "5.0.2",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^1.2.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"msw": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker/node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker/node_modules/magic-string": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.2.tgz",
|
||||
"integrity": "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.2.tgz",
|
||||
"integrity": "sha512-Ijc7T1nT9efNb5LxvjaBrEqw3f/QwUv5EE0nKqZxgqsaV/FxAAZ8baGylA8X/Z2oS4Lp+K74Jr6dTJsDKxJDeg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.25",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
|
||||
@@ -3151,6 +3228,16 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ast-types-flow": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
|
||||
@@ -4130,6 +4217,16 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -5030,6 +5127,13 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz",
|
||||
"integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
@@ -5532,6 +5636,16 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
|
||||
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
@@ -8647,6 +8761,20 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz",
|
||||
"integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/sxzz",
|
||||
"https://opencollective.com/debug"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -10220,6 +10348,13 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz",
|
||||
"integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stop-iteration-iterator": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
||||
@@ -10526,6 +10661,26 @@
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.0.tgz",
|
||||
"integrity": "sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
|
||||
"integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
@@ -11610,6 +11765,112 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.2.tgz",
|
||||
"integrity": "sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/mocker": "5.0.2",
|
||||
"chai": "^6.2.2",
|
||||
"es-module-lexer": "^2.3.2",
|
||||
"expect-type": "^1.4.0",
|
||||
"magic-string": "^1.2.3",
|
||||
"obug": "^2.1.4",
|
||||
"picomatch": "^4.0.7",
|
||||
"std-env": "^4.2.0",
|
||||
"tinybench": "^6.1.4",
|
||||
"tinyexec": "^1.3.0",
|
||||
"tinyglobby": "^0.2.17",
|
||||
"why-is-node-running": "^3.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"vitest": "vitest.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^22.12.0 || ^24.0.0 || >=26.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "5.0.2",
|
||||
"@vitest/browser-preview": "5.0.2",
|
||||
"@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0",
|
||||
"@vitest/coverage-istanbul": "5.0.2",
|
||||
"@vitest/coverage-v8": "5.0.2",
|
||||
"@vitest/ui": "5.0.2",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.4.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-playwright": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-preview": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-webdriverio": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/coverage-istanbul": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/coverage-v8": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/ui": {
|
||||
"optional": true
|
||||
},
|
||||
"happy-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"jsdom": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/magic-string": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.2.tgz",
|
||||
"integrity": "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest/node_modules/picomatch": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/walk-sync": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
|
||||
@@ -11794,6 +12055,19 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/why-is-node-running": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.2.tgz",
|
||||
"integrity": "sha512-NKUzAelcoCXhXL4dJzKIwXeR8iEVqsA0Lq6Vnd0UXvgaKbzVo4ZTHROF2Jidrv+SgxOQ03fMinnNhzZATxOD3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"why-is-node-running": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"private": true,
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "panda codegen && vite",
|
||||
"build": "panda codegen && tsc -b && vite build",
|
||||
"build:debug": "VITE_ANALYZE=true npm run build -- --debug",
|
||||
"test": "panda codegen && vitest run",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"preview": "vite preview",
|
||||
@@ -71,6 +72,7 @@
|
||||
"typescript-eslint": "8.60.1",
|
||||
"vite": "8.0.14",
|
||||
"vite-plugin-static-copy": "4.1.1",
|
||||
"vite-plugin-svgr": "5.2.0"
|
||||
"vite-plugin-svgr": "5.2.0",
|
||||
"vitest": "5.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
useSwipe,
|
||||
} from '@livekit/components-react'
|
||||
import { mergeProps } from '@/utils/mergeProps'
|
||||
import { PaginationIndicator } from './PaginationIndicator'
|
||||
import { useGridLayout } from '../hooks/useGridLayout'
|
||||
import { PaginationControl } from './PaginationControl'
|
||||
import { useSpeakerPromotionTrigger } from '../hooks/useSpeakerPromotionTrigger'
|
||||
@@ -62,13 +61,7 @@ export function GridLayout({ tracks, ...props }: GridLayoutProps) {
|
||||
>
|
||||
<TrackLoop tracks={pagination.tracks}>{props.children}</TrackLoop>
|
||||
{tracks.length > layout.maxTiles && (
|
||||
<>
|
||||
<PaginationIndicator
|
||||
totalPageCount={pagination.totalPageCount}
|
||||
currentPage={pagination.currentPage}
|
||||
/>
|
||||
<PaginationControl pagesContainer={gridEl} {...pagination} />
|
||||
</>
|
||||
<PaginationControl {...pagination} focusShortcut />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,99 +1,147 @@
|
||||
import * as React from 'react'
|
||||
import { createInteractingObservable } from '@livekit/components-core'
|
||||
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||
import { Button } from '@/primitives'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { css, cva, type RecipeVariantProps } from '@/styled-system/css'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
|
||||
|
||||
export interface PaginationControlProps {
|
||||
const paginationToolbar = cva({
|
||||
base: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.125rem',
|
||||
backgroundColor: 'primaryDark.50',
|
||||
borderRadius: '2rem',
|
||||
border: '1px solid',
|
||||
borderColor: 'primaryDark.200',
|
||||
padding: '0.375rem',
|
||||
},
|
||||
variants: {
|
||||
placement: {
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
bottom: '1rem',
|
||||
left: '50%',
|
||||
zIndex: 2,
|
||||
transform: 'translateX(-50%)',
|
||||
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.45)',
|
||||
},
|
||||
inline: {
|
||||
alignSelf: 'center',
|
||||
flexShrink: 0,
|
||||
marginTop: '1rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
placement: 'overlay',
|
||||
},
|
||||
})
|
||||
|
||||
export type PaginationControlProps = RecipeVariantProps<
|
||||
typeof paginationToolbar
|
||||
> & {
|
||||
totalPageCount: number
|
||||
nextPage: () => void
|
||||
prevPage: () => void
|
||||
currentPage: number
|
||||
pagesContainer?: React.RefObject<HTMLElement>
|
||||
// The shortcut listens on the main window only, so a single instance may own it.
|
||||
focusShortcut?: boolean
|
||||
}
|
||||
|
||||
const arrowButtonClass = css({
|
||||
_disabled: {
|
||||
cursor: 'default',
|
||||
backgroundColor: 'transparent !important',
|
||||
'& svg': {
|
||||
opacity: 0.35,
|
||||
},
|
||||
_focusVisible: {
|
||||
outline: '2px solid',
|
||||
outlineColor: 'focusRing',
|
||||
outlineOffset: '2px',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export function PaginationControl({
|
||||
totalPageCount,
|
||||
nextPage,
|
||||
prevPage,
|
||||
currentPage,
|
||||
pagesContainer: connectedElement,
|
||||
placement,
|
||||
focusShortcut = false,
|
||||
}: PaginationControlProps) {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'pagination' })
|
||||
const [interactive, setInteractive] = useState(false)
|
||||
const prevButtonRef = useRef<HTMLButtonElement>(null)
|
||||
const nextButtonRef = useRef<HTMLButtonElement>(null)
|
||||
const isSinglePage = totalPageCount <= 1
|
||||
const isFirstPage = currentPage <= 1
|
||||
const isLastPage = currentPage >= totalPageCount
|
||||
|
||||
useEffect(() => {
|
||||
let subscription:
|
||||
| ReturnType<ReturnType<typeof createInteractingObservable>['subscribe']>
|
||||
| undefined
|
||||
if (connectedElement) {
|
||||
subscription = createInteractingObservable(
|
||||
connectedElement.current,
|
||||
2000
|
||||
).subscribe(setInteractive)
|
||||
}
|
||||
return () => {
|
||||
if (subscription) {
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
}
|
||||
}, [connectedElement])
|
||||
const focusPagination = useCallback(() => {
|
||||
const target = isLastPage ? prevButtonRef.current : nextButtonRef.current
|
||||
target?.focus()
|
||||
}, [isLastPage])
|
||||
|
||||
if (totalPageCount <= 1) return null
|
||||
useRegisterKeyboardShortcut({
|
||||
id: focusShortcut ? 'focus-pagination' : undefined,
|
||||
handler: focusPagination,
|
||||
isDisabled: isSinglePage,
|
||||
unregisterOnUnmount: true,
|
||||
})
|
||||
|
||||
if (isSinglePage) return null
|
||||
|
||||
const pageCount = t('count', { currentPage, totalPageCount })
|
||||
|
||||
return (
|
||||
<nav
|
||||
aria-label={t('label')}
|
||||
className={css({
|
||||
position: 'absolute',
|
||||
bottom: '1rem',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
alignItems: 'stretch',
|
||||
backgroundColor: 'var(--lk-control-bg)',
|
||||
borderRadius: 'var(--lk-border-radius)',
|
||||
transition: 'opacity ease-in-out .15s',
|
||||
display: 'none',
|
||||
border: '1px solid',
|
||||
borderColor: 'primaryDark.100',
|
||||
overflow: 'hidden',
|
||||
})}
|
||||
style={{
|
||||
display: interactive ? 'flex' : 'none',
|
||||
}}
|
||||
data-lk-user-interaction={interactive}
|
||||
<div
|
||||
role="group"
|
||||
aria-label={`${t('label')}, ${pageCount}`}
|
||||
className={paginationToolbar({ placement })}
|
||||
>
|
||||
<Button
|
||||
isDisabled={currentPage == 1}
|
||||
onPress={prevPage}
|
||||
size="xs"
|
||||
variant="quaternaryText"
|
||||
ref={prevButtonRef}
|
||||
aria-disabled={isFirstPage}
|
||||
onPress={isFirstPage ? undefined : prevPage}
|
||||
size="sm"
|
||||
square
|
||||
variant="primaryTextDark"
|
||||
className={arrowButtonClass}
|
||||
aria-label={t('previous')}
|
||||
tooltip={t('previous')}
|
||||
>
|
||||
<RiArrowLeftSLine />
|
||||
<RiArrowLeftSLine size={20} />
|
||||
</Button>
|
||||
<span
|
||||
role="status"
|
||||
className={css({
|
||||
padding: '0.25rem 0.5rem',
|
||||
color: 'white',
|
||||
fontSize: '0.8125rem',
|
||||
fontWeight: 500,
|
||||
minWidth: '3.5rem',
|
||||
textAlign: 'center',
|
||||
userSelect: 'none',
|
||||
padding: '0 0.35rem',
|
||||
whiteSpace: 'nowrap',
|
||||
})}
|
||||
>
|
||||
{t('count', {
|
||||
currentPage,
|
||||
totalPageCount,
|
||||
})}
|
||||
{pageCount}
|
||||
</span>
|
||||
<Button
|
||||
isDisabled={currentPage == totalPageCount}
|
||||
onPress={nextPage}
|
||||
size="xs"
|
||||
variant="quaternaryText"
|
||||
ref={nextButtonRef}
|
||||
aria-disabled={isLastPage}
|
||||
onPress={isLastPage ? undefined : nextPage}
|
||||
size="sm"
|
||||
square
|
||||
variant="primaryTextDark"
|
||||
className={arrowButtonClass}
|
||||
aria-label={t('next')}
|
||||
tooltip={t('next')}
|
||||
>
|
||||
<RiArrowRightSLine />
|
||||
<RiArrowRightSLine size={20} />
|
||||
</Button>
|
||||
</nav>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import * as React from 'react'
|
||||
|
||||
export interface PaginationIndicatorProps {
|
||||
totalPageCount: number
|
||||
currentPage: number
|
||||
}
|
||||
|
||||
export const PaginationIndicator: (
|
||||
props: PaginationIndicatorProps & React.RefAttributes<HTMLDivElement>
|
||||
) => React.ReactNode = /* @__PURE__ */ React.forwardRef<
|
||||
HTMLDivElement,
|
||||
PaginationIndicatorProps
|
||||
>(function PaginationIndicator(
|
||||
{ totalPageCount, currentPage }: PaginationIndicatorProps,
|
||||
ref
|
||||
) {
|
||||
const bubbles = new Array(totalPageCount).fill('').map((_, index) => {
|
||||
if (index + 1 === currentPage) {
|
||||
return <span data-lk-active key={index} />
|
||||
} else {
|
||||
return <span key={index} />
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div ref={ref} className="lk-pagination-indicator" aria-hidden="true">
|
||||
{bubbles}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
@@ -1,96 +0,0 @@
|
||||
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
|
||||
interface PipPaginationProps {
|
||||
totalPageCount: number
|
||||
currentPage: number
|
||||
nextPage: () => void
|
||||
prevPage: () => void
|
||||
}
|
||||
|
||||
export const PipPagination = ({
|
||||
totalPageCount,
|
||||
currentPage,
|
||||
nextPage,
|
||||
prevPage,
|
||||
}: PipPaginationProps) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'pagination' })
|
||||
|
||||
if (totalPageCount <= 1) return null
|
||||
|
||||
return (
|
||||
<Nav aria-label={t('label')}>
|
||||
<ArrowButton
|
||||
type="button"
|
||||
onClick={prevPage}
|
||||
disabled={currentPage === 1}
|
||||
aria-label={t('previous')}
|
||||
>
|
||||
<RiArrowLeftSLine size={18} />
|
||||
</ArrowButton>
|
||||
<Counter role="status">
|
||||
{t('count', { currentPage, totalPageCount })}
|
||||
</Counter>
|
||||
<ArrowButton
|
||||
type="button"
|
||||
onClick={nextPage}
|
||||
disabled={currentPage === totalPageCount}
|
||||
aria-label={t('next')}
|
||||
>
|
||||
<RiArrowRightSLine size={18} />
|
||||
</ArrowButton>
|
||||
</Nav>
|
||||
)
|
||||
}
|
||||
|
||||
const Nav = styled('nav', {
|
||||
base: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '0.25rem',
|
||||
marginTop: '1rem',
|
||||
flexShrink: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const ArrowButton = styled('button', {
|
||||
base: {
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '1.75rem',
|
||||
height: '1.75rem',
|
||||
borderRadius: '4px',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
color: 'white',
|
||||
backgroundColor: 'primaryDark.100',
|
||||
transition: 'opacity 0.15s, background-color 0.15s',
|
||||
'&:hover:not(:disabled)': {
|
||||
backgroundColor: 'primaryDark.75',
|
||||
},
|
||||
'&:focus-visible': {
|
||||
outline: '2px solid',
|
||||
outlineColor: 'white',
|
||||
outlineOffset: '2px',
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.3,
|
||||
cursor: 'default',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const Counter = styled('span', {
|
||||
base: {
|
||||
fontSize: '0.75rem',
|
||||
color: 'white',
|
||||
opacity: 0.8,
|
||||
whiteSpace: 'nowrap',
|
||||
padding: '0 0.25rem',
|
||||
minWidth: '3rem',
|
||||
textAlign: 'center',
|
||||
},
|
||||
})
|
||||
@@ -4,7 +4,7 @@ import { RoomEvent, Track } from 'livekit-client'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
import { PipFocusLayout } from './PipFocusLayout'
|
||||
import { PipGridLayout } from './PipGridLayout'
|
||||
import { PipPagination } from './PipPagination'
|
||||
import { PaginationControl } from '@/features/layout/components/PaginationControl'
|
||||
import { PipScreenShareLayout } from './PipScreenShareLayout'
|
||||
import { StageFrame } from './StageFrame'
|
||||
import { MAX_PIP_TILES } from '../../utils/pipGrid'
|
||||
@@ -120,12 +120,7 @@ const PaginatedStage = ({
|
||||
}) => (
|
||||
<StageWrapper>
|
||||
<StageFrame>{children}</StageFrame>
|
||||
<PipPagination
|
||||
totalPageCount={pagination.totalPageCount}
|
||||
currentPage={pagination.currentPage}
|
||||
nextPage={pagination.nextPage}
|
||||
prevPage={pagination.prevPage}
|
||||
/>
|
||||
<PaginationControl {...pagination} placement="inline" />
|
||||
</StageWrapper>
|
||||
)
|
||||
|
||||
|
||||
@@ -31,35 +31,20 @@ export const useSidePanel = () => {
|
||||
const isSidePanelOpen = !!activePanelId
|
||||
const isSubPanelOpen = !!activeSubPanelId
|
||||
|
||||
const toggleAdmin = () => {
|
||||
layoutStore.activePanelId = isAdminOpen ? null : PanelId.ADMIN
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
// Reads the live store, not the render snapshot: shortcut handlers outlive
|
||||
// toggles that unmount (e.g. inside a closed overflow menu).
|
||||
const togglePanel = (panelId: PanelId) => {
|
||||
layoutStore.activePanelId =
|
||||
layoutStore.activePanelId === panelId ? null : panelId
|
||||
if (layoutStore.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const toggleParticipants = () => {
|
||||
layoutStore.activePanelId = isParticipantsOpen ? null : PanelId.PARTICIPANTS
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const toggleChat = () => {
|
||||
layoutStore.activePanelId = isChatOpen ? null : PanelId.CHAT
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const toggleEffects = () => {
|
||||
layoutStore.activePanelId = isEffectsOpen ? null : PanelId.EFFECTS
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const toggleTools = () => {
|
||||
layoutStore.activePanelId = isToolsOpen ? null : PanelId.TOOLS
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
|
||||
const toggleInfo = () => {
|
||||
layoutStore.activePanelId = isInfoOpen ? null : PanelId.INFO
|
||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||
}
|
||||
const toggleAdmin = () => togglePanel(PanelId.ADMIN)
|
||||
const toggleParticipants = () => togglePanel(PanelId.PARTICIPANTS)
|
||||
const toggleChat = () => togglePanel(PanelId.CHAT)
|
||||
const toggleEffects = () => togglePanel(PanelId.EFFECTS)
|
||||
const toggleTools = () => togglePanel(PanelId.TOOLS)
|
||||
const toggleInfo = () => togglePanel(PanelId.INFO)
|
||||
|
||||
const openTranscript = () => {
|
||||
layoutStore.activeSubPanelId = SubPanelId.TRANSCRIPT
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { normalizeRoomId } from './isRoomValid'
|
||||
|
||||
describe('normalizeRoomId', () => {
|
||||
it('lowercases and re-inserts the hyphens of a ten-letter id', () => {
|
||||
expect(normalizeRoomId('ABCDEFGHIJ')).toBe('abc-defg-hij')
|
||||
expect(normalizeRoomId('abc-defghij')).toBe('abc-defg-hij')
|
||||
})
|
||||
|
||||
it('returns any other input unchanged', () => {
|
||||
expect(normalizeRoomId('abc-def')).toBe('abc-def')
|
||||
expect(normalizeRoomId('Not-A-Room')).toBe('Not-A-Room')
|
||||
})
|
||||
})
|
||||
@@ -7,6 +7,7 @@ export type ShortcutCategory = 'navigation' | 'media' | 'interaction'
|
||||
export type ShortcutId =
|
||||
| 'open-shortcuts'
|
||||
| 'focus-toolbar'
|
||||
| 'focus-pagination'
|
||||
| 'toggle-microphone'
|
||||
| 'toggle-camera'
|
||||
| 'push-to-talk'
|
||||
@@ -44,6 +45,11 @@ export const shortcutCatalog: ShortcutDescriptor[] = [
|
||||
category: 'navigation',
|
||||
shortcut: { key: 'F2' },
|
||||
},
|
||||
{
|
||||
id: 'focus-pagination',
|
||||
category: 'navigation',
|
||||
shortcut: { key: 'G', ctrlKey: true, shiftKey: true },
|
||||
},
|
||||
{
|
||||
id: 'toggle-microphone',
|
||||
category: 'media',
|
||||
|
||||
@@ -7,12 +7,16 @@ export type useRegisterKeyboardShortcutProps = {
|
||||
id?: ShortcutId
|
||||
handler: () => Promise<void | boolean | undefined> | void
|
||||
isDisabled?: boolean
|
||||
// Opt-in: controls rendered inside menus unmount when the menu closes, and
|
||||
// their shortcuts must keep working.
|
||||
unregisterOnUnmount?: boolean
|
||||
}
|
||||
|
||||
export const useRegisterKeyboardShortcut = ({
|
||||
id,
|
||||
handler,
|
||||
isDisabled = false,
|
||||
unregisterOnUnmount = false,
|
||||
}: useRegisterKeyboardShortcutProps) => {
|
||||
useEffect(() => {
|
||||
if (!id) return
|
||||
@@ -21,8 +25,14 @@ export const useRegisterKeyboardShortcut = ({
|
||||
const formattedKey = formatShortcutKey(descriptor.shortcut)
|
||||
if (isDisabled) {
|
||||
keyboardShortcutsStore.shortcuts.delete(formattedKey)
|
||||
} else {
|
||||
keyboardShortcutsStore.shortcuts.set(formattedKey, handler)
|
||||
return
|
||||
}
|
||||
}, [handler, id, isDisabled])
|
||||
keyboardShortcutsStore.shortcuts.set(formattedKey, handler)
|
||||
if (!unregisterOnUnmount) return
|
||||
return () => {
|
||||
if (keyboardShortcutsStore.shortcuts.get(formattedKey) === handler) {
|
||||
keyboardShortcutsStore.shortcuts.delete(formattedKey)
|
||||
}
|
||||
}
|
||||
}, [handler, id, isDisabled, unregisterOnUnmount])
|
||||
}
|
||||
|
||||
@@ -784,6 +784,7 @@
|
||||
"actions": {
|
||||
"open-shortcuts": "Tastenkürzel-Hilfe öffnen",
|
||||
"focus-toolbar": "Fokus auf die untere Symbolleiste",
|
||||
"focus-pagination": "Fokus auf die Teilnehmerseiten",
|
||||
"toggle-microphone": "Mikrofon umschalten",
|
||||
"toggle-camera": "Kamera umschalten",
|
||||
"push-to-talk": "Push-to-talk",
|
||||
|
||||
@@ -784,6 +784,7 @@
|
||||
"actions": {
|
||||
"open-shortcuts": "Open shortcuts help",
|
||||
"focus-toolbar": "Focus bottom toolbar",
|
||||
"focus-pagination": "Focus participant pagination",
|
||||
"toggle-microphone": "Toggle microphone",
|
||||
"toggle-camera": "Toggle camera",
|
||||
"push-to-talk": "Push-to-talk (hold to unmute)",
|
||||
|
||||
@@ -783,6 +783,7 @@
|
||||
"actions": {
|
||||
"open-shortcuts": "Abrir la ayuda de atajos",
|
||||
"focus-toolbar": "Poner el foco en la barra de herramientas inferior",
|
||||
"focus-pagination": "Poner el foco en la paginación de participantes",
|
||||
"toggle-microphone": "Activar o desactivar el micrófono",
|
||||
"toggle-camera": "Activar o desactivar la cámara",
|
||||
"push-to-talk": "Pulsar para hablar (mantener para reactivar)",
|
||||
|
||||
@@ -784,6 +784,7 @@
|
||||
"actions": {
|
||||
"open-shortcuts": "Ouvrir l’aide des raccourcis",
|
||||
"focus-toolbar": "Mettre le focus sur la barre d’outils du bas",
|
||||
"focus-pagination": "Mettre le focus sur la pagination des participants",
|
||||
"toggle-microphone": "Activer ou désactiver le micro",
|
||||
"toggle-camera": "Activer ou désactiver la caméra",
|
||||
"push-to-talk": "Appuyer pour parler (maintenir pour réactiver)",
|
||||
|
||||
@@ -784,6 +784,7 @@
|
||||
"actions": {
|
||||
"open-shortcuts": "Sneltoetsenhulp openen",
|
||||
"focus-toolbar": "Focus op de onderste werkbalk",
|
||||
"focus-pagination": "Focus op de paginering van deelnemers",
|
||||
"toggle-microphone": "Microfoon aan/uit",
|
||||
"toggle-camera": "Camera aan/uit",
|
||||
"push-to-talk": "Push-to-talk (ingedrukt houden om te activeren)",
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
"noEmit": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
"include": ["vite.config.ts", "vitest.config.ts"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
// Kept apart from vite.config.ts so the tests load none of the build plugins.
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
tsconfigPaths: true,
|
||||
},
|
||||
})
|
||||
@@ -24,7 +24,7 @@ _summaryEnvVars: &summaryEnvVars
|
||||
APP_NAME: summary-microservice
|
||||
APP_API_TOKEN: password
|
||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
||||
AWS_S3_ENDPOINT_URL: http://garage.meet.svc.cluster.local:9000/
|
||||
AWS_S3_ENDPOINT_URL: http://dev-backend-garage.meet.svc.cluster.local:9000/
|
||||
AWS_S3_ACCESS_KEY_ID: meet-access-key
|
||||
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
||||
AWS_S3_SECURE_ACCESS: False
|
||||
@@ -69,9 +69,9 @@ _summaryEnvVars: &summaryEnvVars
|
||||
LLM_MODEL: Qwen/Qwen3-Coder-30B-A3B-Instruct
|
||||
WEBHOOK_API_TOKEN: password
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
|
||||
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_BROKER_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||
CELERY_RESULT_BACKEND: redis://user:pass@dev-backend-redis:6379/1
|
||||
TASK_TRACKER_REDIS_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||
IS_RESOLVE_SPEAKER_IDENTITIES_ENABLED: true
|
||||
RESOLVE_SPEAKER_IDENTITIES_DEFAULT_OVERLAP: 0.5
|
||||
RESOLVE_SPEAKER_ENABLE_SPLIT_ON_WORDS: true
|
||||
@@ -127,12 +127,21 @@ backend:
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
|
||||
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
||||
# Databases
|
||||
DB_HOST: postgres
|
||||
DB_NAME: meet
|
||||
DB_USER: dinum
|
||||
DB_PASSWORD: pass
|
||||
DB_HOST: dev-backend-postgres
|
||||
DB_NAME:
|
||||
secretKeyRef:
|
||||
name: dev-backend-postgres
|
||||
key: database
|
||||
DB_USER:
|
||||
secretKeyRef:
|
||||
name: dev-backend-postgres
|
||||
key: username
|
||||
DB_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: dev-backend-postgres
|
||||
key: password
|
||||
DB_PORT: 5432
|
||||
REDIS_URL: redis://default:pass@redis-master:6379/1
|
||||
REDIS_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||
# Static files
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
# Permissions
|
||||
@@ -163,7 +172,7 @@ backend:
|
||||
FRONTEND_TRANSCRIPTION_DESTINATION: "https://docs.numerique.gouv.fr"
|
||||
FRONTEND_IS_SILENT_LOGIN_ENABLED: False
|
||||
# S3 Storage
|
||||
AWS_S3_ENDPOINT_URL: http://garage.meet.svc.cluster.local:9000
|
||||
AWS_S3_ENDPOINT_URL: http://dev-backend-garage.meet.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: meet-access-key
|
||||
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
||||
@@ -186,7 +195,7 @@ backend:
|
||||
MEDIA_BASE_URL: https://meet.127.0.0.1.nip.io
|
||||
FILE_UPLOAD_ENABLED: True
|
||||
CELERY_ENABLED: True
|
||||
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_BROKER_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||
# Recording & Transcription
|
||||
RECORDING_ENABLE: True
|
||||
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v2/async-jobs/transcribe/
|
||||
@@ -266,11 +275,11 @@ ingressMedia:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-url: https://meet.127.0.0.1.nip.io/api/v1.0/recordings/media-auth/
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: garage.meet.svc.cluster.local:9000
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: dev-backend-garage.meet.svc.cluster.local:9000
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/$1
|
||||
|
||||
serviceMedia:
|
||||
host: garage.meet.svc.cluster.local
|
||||
host: dev-backend-garage.meet.svc.cluster.local
|
||||
port: 9000
|
||||
|
||||
# ---- Extra ingress/service for background file uploads ------------
|
||||
@@ -282,11 +291,11 @@ ingressMediaFiles:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-url: https://meet.127.0.0.1.nip.io/api/v1.0/files/media-auth/
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: garage.meet.svc.cluster.local:9000
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: dev-backend-garage.meet.svc.cluster.local:9000
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/files/$1
|
||||
|
||||
serviceMediaFiles:
|
||||
host: garage.meet.svc.cluster.local
|
||||
host: dev-backend-garage.meet.svc.cluster.local
|
||||
port: 9000
|
||||
|
||||
# ---- STT Orchestration Microservice Components --------------------
|
||||
@@ -364,7 +373,7 @@ agentMetadata:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ENABLE_SILERO_VAD: "false"
|
||||
AWS_S3_ENDPOINT_URL: garage.meet.svc.cluster.local:9000
|
||||
AWS_S3_ENDPOINT_URL: dev-backend-garage.meet.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: meet-access-key
|
||||
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
||||
AWS_S3_SECURE_ACCESS: False
|
||||
|
||||
@@ -13,14 +13,15 @@ egress:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
address: dev-backend-redis:6379
|
||||
username: user
|
||||
password: pass
|
||||
s3:
|
||||
access_key: meet-access-key
|
||||
secret: meet-secret-access-key
|
||||
region: local
|
||||
bucket: meet-media-storage
|
||||
endpoint: http://garage:9000
|
||||
endpoint: http://dev-backend-garage:9000
|
||||
force_path_style: true
|
||||
|
||||
loadBalancer:
|
||||
|
||||
@@ -14,7 +14,8 @@ livekit:
|
||||
port_range_end: 60000
|
||||
tcp_port: 7881
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
address: dev-backend-redis:6379
|
||||
username: user
|
||||
password: pass
|
||||
keys:
|
||||
turn:
|
||||
|
||||
@@ -16,14 +16,15 @@ egress:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
address: dev-backend-redis:6379
|
||||
username: user
|
||||
password: pass
|
||||
s3:
|
||||
access_key: meet-access-key
|
||||
secret: meet-secret-access-key
|
||||
region: local
|
||||
bucket: meet-media-storage
|
||||
endpoint: http://garage:9000
|
||||
endpoint: http://dev-backend-garage:9000
|
||||
force_path_style: true
|
||||
|
||||
loadBalancer:
|
||||
|
||||
@@ -14,7 +14,8 @@ livekit:
|
||||
port_range_end: 60000
|
||||
tcp_port: 7881
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
address: dev-backend-redis:6379
|
||||
username: user
|
||||
password: pass
|
||||
keys:
|
||||
turn:
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: garage
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: 10m
|
||||
spec:
|
||||
rules:
|
||||
- host: "garage.127.0.0.1.nip.io"
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: garage
|
||||
port:
|
||||
number: 9000
|
||||
tls:
|
||||
- hosts:
|
||||
- garage.127.0.0.1.nip.io
|
||||
secretName: meet-tls
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: garage
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
ports:
|
||||
- name: client
|
||||
port: 9000
|
||||
protocol: TCP
|
||||
targetPort: 9000
|
||||
selector:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: garage
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: garage-config
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
data:
|
||||
garage.toml: |
|
||||
metadata_dir = "/var/lib/garage/meta"
|
||||
data_dir = "/var/lib/garage/data"
|
||||
db_engine = "lmdb"
|
||||
|
||||
replication_factor = 1
|
||||
|
||||
rpc_bind_addr = "127.0.0.1:3901"
|
||||
rpc_public_addr = "127.0.0.1:3901"
|
||||
|
||||
[s3_api]
|
||||
api_bind_addr = "[::]:9000"
|
||||
# Clients must sign their requests for this region (AWS_S3_REGION_NAME)
|
||||
s3_region = "local"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: garage-dev
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
type: Opaque
|
||||
data:
|
||||
GARAGE_RPC_SECRET: {{ printf "%s/garage-rpc-secret" .Release.Namespace | sha256sum | b64enc }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: garage
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: garage
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: garage
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: garage
|
||||
spec:
|
||||
containers:
|
||||
- name: garage
|
||||
command:
|
||||
- /garage
|
||||
- server
|
||||
- --single-node
|
||||
- --default-bucket
|
||||
env:
|
||||
- name: GARAGE_RPC_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: garage-dev
|
||||
key: GARAGE_RPC_SECRET
|
||||
- name: GARAGE_DEFAULT_ACCESS_KEY
|
||||
value: meet-access-key
|
||||
- name: GARAGE_DEFAULT_SECRET_KEY
|
||||
value: meet-secret-access-key
|
||||
- name: GARAGE_DEFAULT_BUCKET
|
||||
value: meet-media-storage
|
||||
image: "dxflrs/garage:v2.4.1"
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
name: client
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /garage
|
||||
- health
|
||||
volumeMounts:
|
||||
- mountPath: /etc/garage.toml
|
||||
name: config
|
||||
subPath: garage.toml
|
||||
- mountPath: /var/lib/garage
|
||||
name: data
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: garage-config
|
||||
- name: data
|
||||
emptyDir:
|
||||
---
|
||||
# Garage denies cross-origin requests by default: allow the frontend to upload
|
||||
# files straight to the bucket
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: garage-cors
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: aws-cli
|
||||
image: amazon/aws-cli:2.37.1
|
||||
env:
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
value: meet-access-key
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
value: meet-secret-access-key
|
||||
- name: AWS_DEFAULT_REGION
|
||||
value: local
|
||||
- name: AWS_ENDPOINT_URL
|
||||
value: http://garage:9000
|
||||
- name: BUCKET
|
||||
value: meet-media-storage
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
deadline=$(($(date +%s) + 300))
|
||||
until aws s3api head-bucket --bucket="$BUCKET" --cli-connect-timeout=5; do
|
||||
if [ "$(date +%s)" -ge "$deadline" ]; then
|
||||
echo "Bucket $BUCKET still unavailable on $AWS_ENDPOINT_URL" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting for bucket $BUCKET on $AWS_ENDPOINT_URL"
|
||||
sleep 5
|
||||
done
|
||||
exec aws s3api put-bucket-cors --bucket="$BUCKET" \
|
||||
--cors-configuration='{"CORSRules": [{"AllowedOrigins": ["https://meet.127.0.0.1.nip.io"], "AllowedMethods": ["GET", "HEAD", "PUT"], "AllowedHeaders": ["*"], "ExposeHeaders": ["ETag"]}]}'
|
||||
restartPolicy: Never
|
||||
backoffLimit: 3
|
||||
@@ -1,61 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kc-postgres
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
ports:
|
||||
- name: tcp-postgresql
|
||||
port: 5432
|
||||
protocol: TCP
|
||||
targetPort: tcp-postgresql
|
||||
selector:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: kc-postgresql
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: kc-postgresql
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: kc-postgresql
|
||||
serviceName: "kc-postgres"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: kc-postgresql
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 10
|
||||
containers:
|
||||
- name: pg
|
||||
image: postgres:16-alpine
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: tcp-postgresql
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: pass
|
||||
- name: POSTGRES_USER
|
||||
value: dinum
|
||||
- name: POSTGRES_DB
|
||||
value: keycloak
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: [ "ReadWriteOnce" ]
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: keycloak
|
||||
spec:
|
||||
rules:
|
||||
- host: "keycloak.127.0.0.1.nip.io"
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: keycloak
|
||||
port:
|
||||
number: 8080
|
||||
tls:
|
||||
- hosts:
|
||||
- keycloak.127.0.0.1.nip.io
|
||||
secretName: meet-tls
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
ports:
|
||||
- name: tcp-keycloak
|
||||
port: 8080
|
||||
protocol: TCP
|
||||
targetPort: tcp-keycloak
|
||||
selector:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: keycloak
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: realm
|
||||
data:
|
||||
meet.json: |
|
||||
{{ .Values.realm | indent 4 }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: keycloak
|
||||
serviceName: "keycloak"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: keycloak
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 10
|
||||
containers:
|
||||
- name: keycloak
|
||||
image: quay.io/keycloak/keycloak:20.0.1
|
||||
args:
|
||||
- start-dev
|
||||
- --features=preview
|
||||
- --import-realm
|
||||
- --proxy=edge
|
||||
- --hostname=keycloak.127.0.0.1.nip.io
|
||||
- --hostname-strict=false
|
||||
- --hostname-strict-https=false
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: tcp-keycloak
|
||||
env:
|
||||
- name: KEYCLOAK_ADMIN
|
||||
value: admin
|
||||
- name: KEYCLOAK_ADMIN_PASSWORD
|
||||
value: admin
|
||||
- name: PROXY_ADDRESS_FORWARDING
|
||||
value: 'true'
|
||||
- name: KC_DB_URL_HOST
|
||||
value: kc_postgresql
|
||||
- name: KC_DB_URL_DATABASE
|
||||
value: keycloak
|
||||
- name: KC_DB_PASSWORD
|
||||
value: pass
|
||||
- name: KC_DB_USERNAME
|
||||
value: dinum
|
||||
- name: KC_DB_SCHEMA
|
||||
value: public
|
||||
volumeMounts:
|
||||
- name: realm
|
||||
mountPath: "/opt/keycloak/data/import"
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: realm
|
||||
configMap:
|
||||
name: realm
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
ports:
|
||||
- name: tcp-postgresql
|
||||
port: 5432
|
||||
protocol: TCP
|
||||
targetPort: tcp-postgresql
|
||||
selector:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: postgresql
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: postgresql
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: postgresql
|
||||
serviceName: "postgres"
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: postgresql
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 10
|
||||
containers:
|
||||
- name: pg
|
||||
image: postgres:16-alpine
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: [ "pg_isready", "-U", "dinum", "-d", "meet", "-h", "127.0.0.1" ]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: [ "pg_isready", "-U", "dinum", "-d", "meet", "-h", "127.0.0.1" ]
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: tcp-postgresql
|
||||
env:
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: pass
|
||||
- name: POSTGRES_USER
|
||||
value: dinum
|
||||
- name: POSTGRES_DB
|
||||
value: meet
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: [ "ReadWriteOnce" ]
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -1,65 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-master
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
ports:
|
||||
- name: tcp-redis
|
||||
port: 6379
|
||||
protocol: TCP
|
||||
targetPort: tcp-redis
|
||||
selector:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: redis
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: redis
|
||||
data:
|
||||
redis.conf: |
|
||||
bind 0.0.0.0
|
||||
port 6379
|
||||
user default on >pass ~* &* +@all
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: redis
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: redis
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: extra
|
||||
app.kubernetes.io/name: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
args:
|
||||
- redis-server
|
||||
- /usr/local/etc/redis/redis.conf
|
||||
image: "redis:8.2-alpine"
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
name: tcp-redis
|
||||
volumeMounts:
|
||||
- name: redis
|
||||
mountPath: "/usr/local/etc/redis"
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: redis
|
||||
configMap:
|
||||
name: redis
|
||||
@@ -13,6 +13,9 @@ repositories:
|
||||
- name: livekit
|
||||
url: https://helm.livekit.io
|
||||
|
||||
- name: dev-backends
|
||||
url: https://suitenumerique.github.io/helm-dev-backend
|
||||
|
||||
releases:
|
||||
- name: extra
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name }}
|
||||
@@ -27,8 +30,59 @@ releases:
|
||||
- enablePermanentRedirect: {{ .Values | get "enablePermanentRedirect" "False"}}
|
||||
- oldDomain: {{ .Values | get "oldDomain" "demo.com" }}
|
||||
- newDomain: {{ .Values | get "newDomain" "demo.com" }}
|
||||
- realm: |
|
||||
{{ readFile "../../docker/auth/realm.json" | replace "http://localhost:3200" "https://meet.127.0.0.1.nip.io" | indent 8 }}
|
||||
|
||||
- name: dev-backend
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name }}
|
||||
namespace: {{ .Namespace }}
|
||||
chart: dev-backends/dev-backend
|
||||
version: 0.0.14
|
||||
values:
|
||||
- postgres:
|
||||
enabled: true
|
||||
username: dinum
|
||||
password: pass
|
||||
database: meet
|
||||
size: 1Gi
|
||||
- redis:
|
||||
enabled: true
|
||||
username: user
|
||||
password: pass
|
||||
- garage:
|
||||
enabled: true
|
||||
accessKey: meet-access-key
|
||||
secretKey: meet-secret-access-key
|
||||
bucket: meet-media-storage
|
||||
region: local
|
||||
persistence: false
|
||||
ingress:
|
||||
enabled: true
|
||||
hostname: garage.127.0.0.1.nip.io
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: 10m
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: meet-tls
|
||||
cors:
|
||||
allowedOrigins:
|
||||
- https://meet.127.0.0.1.nip.io
|
||||
- keycloak:
|
||||
enabled: true
|
||||
hostname: keycloak.127.0.0.1.nip.io
|
||||
username: admin
|
||||
password: admin
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: meet-tls
|
||||
db:
|
||||
username: dinum
|
||||
password: pass
|
||||
database: keycloak
|
||||
size: 1Gi
|
||||
realm:
|
||||
name: meet
|
||||
username: meet
|
||||
password: meet
|
||||
email: meet@example.com
|
||||
|
||||
|
||||
- name: meet
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mail_mjml",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@html-to/text-cli": "0.6.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"description": "An util to generate html and text django's templates from mjml templates",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "sdk",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"license": "ISC",
|
||||
"workspaces": [
|
||||
"./library",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sdk",
|
||||
"version": "1.33.0",
|
||||
"version": "1.34.0",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
[project]
|
||||
name = "summary"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.105.0",
|
||||
|
||||
Generated
+1
-1
@@ -1484,7 +1484,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "summary"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "boto3" },
|
||||
|
||||
Reference in New Issue
Block a user