mirror of
https://github.com/suitenumerique/meet.git
synced 2026-10-07 14:00:56 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2ac457c43e | |||
| e9b523b522 | |||
| 70d348337e | |||
| 258722adfd | |||
| 0a6724e7ad | |||
| c1c2d93932 | |||
| 9efb9577c4 | |||
| 2137c6b444 | |||
| aa01733f6b | |||
| 03db669e51 | |||
| 97a73bf4f2 | |||
| 267a2265d9 | |||
| 14507d57a9 |
@@ -88,6 +88,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Create writable /data
|
- name: Create writable /data
|
||||||
run: |
|
run: |
|
||||||
@@ -159,6 +161,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install ffmpeg
|
- name: Install ffmpeg
|
||||||
run: |
|
run: |
|
||||||
@@ -186,6 +190,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: cd src/frontend/ && npm ci --ignore-scripts
|
run: cd src/frontend/ && npm ci --ignore-scripts
|
||||||
@@ -196,6 +202,27 @@ jobs:
|
|||||||
- name: Check format
|
- name: Check format
|
||||||
run: cd src/frontend/ && npm run check
|
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:
|
lint-sdk:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
@@ -206,6 +233,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci --ignore-scripts
|
run: npm ci --ignore-scripts
|
||||||
@@ -227,6 +256,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci --ignore-scripts
|
run: npm ci --ignore-scripts
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.34.0] - 2026-10-07
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- ✨(helm) import environment variables from Secrets and ConfigMaps
|
- ✨(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
|
- 🔧(summary) add setting to control Sentry traces sampling rate
|
||||||
- ✨(frontend) let signed-out visitors start a meeting
|
- ✨(frontend) let signed-out visitors start a meeting
|
||||||
- ✨(backend) expose `allow_unregistered_rooms` in the frontend configuration
|
- ✨(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
|
### Changed
|
||||||
|
|
||||||
@@ -165,6 +169,7 @@ and this project adheres to
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- ✨(any) let any authenticated user manage the lobby on trusted rooms
|
- ✨(any) let any authenticated user manage the lobby on trusted rooms
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- 📱(frontend) collapse mobile control bar items on narrow viewports
|
- 📱(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
|
@$(COMPOSE) up --force-recreate -d celery-summary-summarize
|
||||||
.PHONY: run-summary
|
.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-multi-user-transcriber
|
||||||
@$(MAKE) run-agent-metadata-collector
|
@$(MAKE) run-agent-metadata-collector
|
||||||
.PHONY: run-agents
|
.PHONY: run-agents
|
||||||
@@ -186,7 +186,6 @@ run:
|
|||||||
run: ## start the wsgi (production) and development server
|
run: ## start the wsgi (production) and development server
|
||||||
@$(MAKE) run-backend
|
@$(MAKE) run-backend
|
||||||
@$(MAKE) run-summary
|
@$(MAKE) run-summary
|
||||||
@$(MAKE) run-agents
|
|
||||||
@$(COMPOSE) up --force-recreate -d frontend
|
@$(COMPOSE) up --force-recreate -d frontend
|
||||||
.PHONY: run
|
.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"`
|
test: ## run project tests; pass extra pytest args via ARGS, e.g. `make test ARGS="-vv"`
|
||||||
@args="$(ARGS) $(filter-out $@,$(MAKECMDGOALS))" && \
|
@args="$(ARGS) $(filter-out $@,$(MAKECMDGOALS))" && \
|
||||||
$(MAKE) test-back-parallel ARGS="$${args}" && \
|
$(MAKE) test-back-parallel ARGS="$${args}" && \
|
||||||
$(MAKE) test-summary ARGS="$${args}"
|
$(MAKE) test-summary ARGS="$${args}" && \
|
||||||
|
$(MAKE) test-frontend
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
|
|
||||||
test-back: ## run back-end tests (pass extra pytest args via ARGS)
|
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}
|
bin/pytest-summary $${args}
|
||||||
.PHONY: test-summary
|
.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.
|
makemigrations: ## run django makemigrations for the Meet project.
|
||||||
@echo "$(BOLD)Running makemigrations$(RESET)"
|
@echo "$(BOLD)Running makemigrations$(RESET)"
|
||||||
@$(COMPOSE) up -d postgresql
|
@$(COMPOSE) up -d postgresql
|
||||||
|
|||||||
+76
-25
@@ -16,43 +16,54 @@ the following command inside your docker container:
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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`.
|
**Celery worker required.** Newsletter signup on login
|
||||||
- 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.
|
(`SIGNUP_NEW_USER_TO_MARKETING_EMAIL=True`) is now dispatched as an
|
||||||
- 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.
|
asynchronous Celery task (`lasuite.marketing.tasks.create_or_update_contact`)
|
||||||
- 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.
|
instead of a synchronous call with a 1s timeout. Make sure a Celery worker is
|
||||||
- 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.
|
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.
|
**Configuration changes.** The following environment variables / settings are
|
||||||
* 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.
|
**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`
|
⚠️ The default backend is now a **dummy** (no-op). If you previously used
|
||||||
2. Optionally reclaim the space used by MinIO: `rm -rf data/media && make data/media`
|
Brevo, you must explicitly configure it, otherwise signups are silently dropped:
|
||||||
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
|
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.
|
Migration mapping:
|
||||||
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.
|
|
||||||
|
|
||||||
Also:
|
- `BREVO_API_KEY` → `api_key`
|
||||||
- Signed URLs to transcripts and summaries are now always path-style (`<endpoint>/<bucket>/<key>`), whereas the minio client used virtual-hosted-style URLs
|
- `BREVO_API_CONTACT_LIST_IDS` → `api_contact_list_ids`
|
||||||
- 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_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
|
### Recording encoding settings replaced by a resolution/profile model
|
||||||
|
|
||||||
The `RECORDING_ENCODING_*` settings introduced in v1.16.0 exposed raw encoder
|
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)
|
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.
|
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
|
## v1.30.0
|
||||||
|
|
||||||
### Removing S3 storage-event webhooks for recordings
|
### 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_yaml(local('cd ../src/helm && helmfile -n meet -e ${DEV_ENV:-dev-keycloak} template .'))
|
||||||
|
|
||||||
k8s_resource('garage-cors', resource_deps=['garage'])
|
k8s_resource('dev-backend-garage-cors', resource_deps=['dev-backend-garage'])
|
||||||
k8s_resource('meet-backend', resource_deps=['postgresql', 'garage-cors', 'redis', 'livekit-livekit-server'])
|
k8s_resource('dev-backend-keycloak', resource_deps=['dev-backend-keycloak-pg'])
|
||||||
k8s_resource('meet-celery-backend', resource_deps=['redis'])
|
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-summarize', resource_deps=['redis'])
|
k8s_resource('meet-celery-backend', resource_deps=['dev-backend-redis'])
|
||||||
k8s_resource('meet-celery-summary-backend', resource_deps=['redis'])
|
k8s_resource('meet-celery-summarize', resource_deps=['dev-backend-redis'])
|
||||||
k8s_resource('meet-celery-transcribe-default', resource_deps=['redis'])
|
k8s_resource('meet-celery-summary-backend', resource_deps=['dev-backend-redis'])
|
||||||
k8s_resource('livekit-livekit-server', resource_deps=['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('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
|
# Trigger once on launch
|
||||||
k8s_resource(
|
k8s_resource(
|
||||||
'meet-backend-createsuperuser',
|
'meet-backend-createsuperuser',
|
||||||
|
|||||||
+5
-8
@@ -9,6 +9,8 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:5
|
image: redis:5
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
|
||||||
mailcatcher:
|
mailcatcher:
|
||||||
image: sj26/mailcatcher:latest
|
image: sj26/mailcatcher:latest
|
||||||
@@ -271,11 +273,6 @@ services:
|
|||||||
- ./src/agents:/app
|
- ./src/agents:/app
|
||||||
- /app/.venv
|
- /app/.venv
|
||||||
|
|
||||||
redis-summary:
|
|
||||||
image: redis:5
|
|
||||||
ports:
|
|
||||||
- "6379:6379"
|
|
||||||
|
|
||||||
app-summary-dev:
|
app-summary-dev:
|
||||||
build:
|
build:
|
||||||
context: src/summary
|
context: src/summary
|
||||||
@@ -290,7 +287,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./src/summary:/app
|
- ./src/summary:/app
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis-summary
|
- redis
|
||||||
|
|
||||||
celery-summary-transcribe:
|
celery-summary-transcribe:
|
||||||
container_name: celery-summary-transcribe
|
container_name: celery-summary-transcribe
|
||||||
@@ -304,7 +301,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./src/summary:/app
|
- ./src/summary:/app
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis-summary
|
- redis
|
||||||
- app-summary-dev
|
- app-summary-dev
|
||||||
- garage
|
- garage
|
||||||
develop:
|
develop:
|
||||||
@@ -324,7 +321,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./src/summary:/app
|
- ./src/summary:/app
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis-summary
|
- redis
|
||||||
- app-summary-dev
|
- app-summary-dev
|
||||||
- garage
|
- garage
|
||||||
develop:
|
develop:
|
||||||
|
|||||||
@@ -107,6 +107,37 @@ $ npm i
|
|||||||
$ npm run dev
|
$ 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
|
## Adding Content
|
||||||
|
|||||||
@@ -104,10 +104,11 @@ ROOM_TELEPHONY_ENABLED=True
|
|||||||
# ROOMKIT_ENABLED = True
|
# ROOMKIT_ENABLED = True
|
||||||
# ROOMKIT_SERVER_TO_SERVER_API_TOKEN = ThisIsAnExampleKeyForDevPurposeOnly
|
# ROOMKIT_SERVER_TO_SERVER_API_TOKEN = ThisIsAnExampleKeyForDevPurposeOnly
|
||||||
|
|
||||||
# Metadata
|
# LiveKit agents (opt-in, start them with `make run-agents`)
|
||||||
METADATA_COLLECTOR_ENABLED=True
|
# Metadata (requires the metadata-collector agent)
|
||||||
|
METADATA_COLLECTOR_ENABLED=False
|
||||||
|
|
||||||
# Subtitle
|
# Subtitle (requires the multi-user-transcriber agent)
|
||||||
ROOM_SUBTITLE_ENABLED=False
|
ROOM_SUBTITLE_ENABLED=False
|
||||||
|
|
||||||
FRONTEND_USE_FRENCH_GOV_FOOTER=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_SECRET_ACCESS_KEY="meet-secret-access-key"
|
||||||
AWS_S3_REGION_NAME="local"
|
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_BASE_URL="https://configure-your-url.com"
|
||||||
WHISPERX_ASR_MODEL="large-v2"
|
WHISPERX_ASR_MODEL="large-v2"
|
||||||
WHISPERX_API_KEY="your-secret-key"
|
WHISPERX_API_KEY="your-secret-key"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "agents"
|
name = "agents"
|
||||||
version = "1.33.0"
|
version = "1.34.0"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"livekit-agents==1.7.0",
|
"livekit-agents==1.7.0",
|
||||||
|
|||||||
Generated
+1
-1
@@ -9,7 +9,7 @@ resolution-markers = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "agents"
|
name = "agents"
|
||||||
version = "1.33.0"
|
version = "1.34.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "boto3" },
|
{ name = "boto3" },
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ build-backend = "uv_build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "meet"
|
name = "meet"
|
||||||
version = "1.33.0"
|
version = "1.34.0"
|
||||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1297,7 +1297,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meet"
|
name = "meet"
|
||||||
version = "1.33.0"
|
version = "1.34.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiohttp" },
|
{ name = "aiohttp" },
|
||||||
|
|||||||
Generated
+280
-6
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "meet",
|
"name": "meet",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "meet",
|
"name": "meet",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/atkinson-hyperlegible-next": "5.3.0",
|
"@fontsource-variable/atkinson-hyperlegible-next": "5.3.0",
|
||||||
"@fontsource-variable/lexend": "5.3.0",
|
"@fontsource-variable/lexend": "5.3.0",
|
||||||
@@ -64,7 +64,8 @@
|
|||||||
"typescript-eslint": "8.60.1",
|
"typescript-eslint": "8.60.1",
|
||||||
"vite": "8.0.14",
|
"vite": "8.0.14",
|
||||||
"vite-plugin-static-copy": "4.1.1",
|
"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": {
|
"node_modules/@adobe/react-spectrum": {
|
||||||
@@ -942,9 +943,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.5",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -2506,6 +2507,24 @@
|
|||||||
"tslib": "^2.4.0"
|
"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": {
|
"node_modules/@types/dom-mediacapture-record": {
|
||||||
"version": "1.0.22",
|
"version": "1.0.22",
|
||||||
"resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.22.tgz",
|
"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": {
|
"node_modules/@vue/compiler-core": {
|
||||||
"version": "3.5.25",
|
"version": "3.5.25",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
|
||||||
@@ -3151,6 +3228,16 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/ast-types-flow": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
|
||||||
@@ -4130,6 +4217,16 @@
|
|||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"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": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -5030,6 +5127,13 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/es-object-atoms": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
@@ -5532,6 +5636,16 @@
|
|||||||
"node": ">=18.0.0"
|
"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": {
|
"node_modules/express": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||||
@@ -8647,6 +8761,20 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/on-finished": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
@@ -10220,6 +10348,13 @@
|
|||||||
"node": ">= 0.8"
|
"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": {
|
"node_modules/stop-iteration-iterator": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
||||||
@@ -10526,6 +10661,26 @@
|
|||||||
"xtend": "~4.0.1"
|
"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": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.17",
|
"version": "0.2.17",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||||
@@ -11610,6 +11765,112 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"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": {
|
"node_modules/walk-sync": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
|
||||||
@@ -11794,6 +12055,19 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "meet",
|
"name": "meet",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "panda codegen && vite",
|
"dev": "panda codegen && vite",
|
||||||
"build": "panda codegen && tsc -b && vite build",
|
"build": "panda codegen && tsc -b && vite build",
|
||||||
"build:debug": "VITE_ANALYZE=true npm run build -- --debug",
|
"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": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"lint:fix": "eslint . --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
@@ -71,6 +72,7 @@
|
|||||||
"typescript-eslint": "8.60.1",
|
"typescript-eslint": "8.60.1",
|
||||||
"vite": "8.0.14",
|
"vite": "8.0.14",
|
||||||
"vite-plugin-static-copy": "4.1.1",
|
"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,
|
useSwipe,
|
||||||
} from '@livekit/components-react'
|
} from '@livekit/components-react'
|
||||||
import { mergeProps } from '@/utils/mergeProps'
|
import { mergeProps } from '@/utils/mergeProps'
|
||||||
import { PaginationIndicator } from './PaginationIndicator'
|
|
||||||
import { useGridLayout } from '../hooks/useGridLayout'
|
import { useGridLayout } from '../hooks/useGridLayout'
|
||||||
import { PaginationControl } from './PaginationControl'
|
import { PaginationControl } from './PaginationControl'
|
||||||
import { useSpeakerPromotionTrigger } from '../hooks/useSpeakerPromotionTrigger'
|
import { useSpeakerPromotionTrigger } from '../hooks/useSpeakerPromotionTrigger'
|
||||||
@@ -62,13 +61,7 @@ export function GridLayout({ tracks, ...props }: GridLayoutProps) {
|
|||||||
>
|
>
|
||||||
<TrackLoop tracks={pagination.tracks}>{props.children}</TrackLoop>
|
<TrackLoop tracks={pagination.tracks}>{props.children}</TrackLoop>
|
||||||
{tracks.length > layout.maxTiles && (
|
{tracks.length > layout.maxTiles && (
|
||||||
<>
|
<PaginationControl {...pagination} focusShortcut />
|
||||||
<PaginationIndicator
|
|
||||||
totalPageCount={pagination.totalPageCount}
|
|
||||||
currentPage={pagination.currentPage}
|
|
||||||
/>
|
|
||||||
<PaginationControl pagesContainer={gridEl} {...pagination} />
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,99 +1,147 @@
|
|||||||
import * as React from 'react'
|
|
||||||
import { createInteractingObservable } from '@livekit/components-core'
|
|
||||||
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
|
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
|
||||||
import { Button } from '@/primitives'
|
import { Button } from '@/primitives'
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
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
|
totalPageCount: number
|
||||||
nextPage: () => void
|
nextPage: () => void
|
||||||
prevPage: () => void
|
prevPage: () => void
|
||||||
currentPage: number
|
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({
|
export function PaginationControl({
|
||||||
totalPageCount,
|
totalPageCount,
|
||||||
nextPage,
|
nextPage,
|
||||||
prevPage,
|
prevPage,
|
||||||
currentPage,
|
currentPage,
|
||||||
pagesContainer: connectedElement,
|
placement,
|
||||||
|
focusShortcut = false,
|
||||||
}: PaginationControlProps) {
|
}: PaginationControlProps) {
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'pagination' })
|
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(() => {
|
const focusPagination = useCallback(() => {
|
||||||
let subscription:
|
const target = isLastPage ? prevButtonRef.current : nextButtonRef.current
|
||||||
| ReturnType<ReturnType<typeof createInteractingObservable>['subscribe']>
|
target?.focus()
|
||||||
| undefined
|
}, [isLastPage])
|
||||||
if (connectedElement) {
|
|
||||||
subscription = createInteractingObservable(
|
|
||||||
connectedElement.current,
|
|
||||||
2000
|
|
||||||
).subscribe(setInteractive)
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (subscription) {
|
|
||||||
subscription.unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [connectedElement])
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<nav
|
<div
|
||||||
aria-label={t('label')}
|
role="group"
|
||||||
className={css({
|
aria-label={`${t('label')}, ${pageCount}`}
|
||||||
position: 'absolute',
|
className={paginationToolbar({ placement })}
|
||||||
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}
|
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
isDisabled={currentPage == 1}
|
ref={prevButtonRef}
|
||||||
onPress={prevPage}
|
aria-disabled={isFirstPage}
|
||||||
size="xs"
|
onPress={isFirstPage ? undefined : prevPage}
|
||||||
variant="quaternaryText"
|
size="sm"
|
||||||
|
square
|
||||||
|
variant="primaryTextDark"
|
||||||
|
className={arrowButtonClass}
|
||||||
aria-label={t('previous')}
|
aria-label={t('previous')}
|
||||||
|
tooltip={t('previous')}
|
||||||
>
|
>
|
||||||
<RiArrowLeftSLine />
|
<RiArrowLeftSLine size={20} />
|
||||||
</Button>
|
</Button>
|
||||||
<span
|
<span
|
||||||
role="status"
|
role="status"
|
||||||
className={css({
|
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', {
|
{pageCount}
|
||||||
currentPage,
|
|
||||||
totalPageCount,
|
|
||||||
})}
|
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
isDisabled={currentPage == totalPageCount}
|
ref={nextButtonRef}
|
||||||
onPress={nextPage}
|
aria-disabled={isLastPage}
|
||||||
size="xs"
|
onPress={isLastPage ? undefined : nextPage}
|
||||||
variant="quaternaryText"
|
size="sm"
|
||||||
|
square
|
||||||
|
variant="primaryTextDark"
|
||||||
|
className={arrowButtonClass}
|
||||||
aria-label={t('next')}
|
aria-label={t('next')}
|
||||||
|
tooltip={t('next')}
|
||||||
>
|
>
|
||||||
<RiArrowRightSLine />
|
<RiArrowRightSLine size={20} />
|
||||||
</Button>
|
</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 { styled } from '@/styled-system/jsx'
|
||||||
import { PipFocusLayout } from './PipFocusLayout'
|
import { PipFocusLayout } from './PipFocusLayout'
|
||||||
import { PipGridLayout } from './PipGridLayout'
|
import { PipGridLayout } from './PipGridLayout'
|
||||||
import { PipPagination } from './PipPagination'
|
import { PaginationControl } from '@/features/layout/components/PaginationControl'
|
||||||
import { PipScreenShareLayout } from './PipScreenShareLayout'
|
import { PipScreenShareLayout } from './PipScreenShareLayout'
|
||||||
import { StageFrame } from './StageFrame'
|
import { StageFrame } from './StageFrame'
|
||||||
import { MAX_PIP_TILES } from '../../utils/pipGrid'
|
import { MAX_PIP_TILES } from '../../utils/pipGrid'
|
||||||
@@ -120,12 +120,7 @@ const PaginatedStage = ({
|
|||||||
}) => (
|
}) => (
|
||||||
<StageWrapper>
|
<StageWrapper>
|
||||||
<StageFrame>{children}</StageFrame>
|
<StageFrame>{children}</StageFrame>
|
||||||
<PipPagination
|
<PaginationControl {...pagination} placement="inline" />
|
||||||
totalPageCount={pagination.totalPageCount}
|
|
||||||
currentPage={pagination.currentPage}
|
|
||||||
nextPage={pagination.nextPage}
|
|
||||||
prevPage={pagination.prevPage}
|
|
||||||
/>
|
|
||||||
</StageWrapper>
|
</StageWrapper>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -31,35 +31,20 @@ export const useSidePanel = () => {
|
|||||||
const isSidePanelOpen = !!activePanelId
|
const isSidePanelOpen = !!activePanelId
|
||||||
const isSubPanelOpen = !!activeSubPanelId
|
const isSubPanelOpen = !!activeSubPanelId
|
||||||
|
|
||||||
const toggleAdmin = () => {
|
// Reads the live store, not the render snapshot: shortcut handlers outlive
|
||||||
layoutStore.activePanelId = isAdminOpen ? null : PanelId.ADMIN
|
// toggles that unmount (e.g. inside a closed overflow menu).
|
||||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
const togglePanel = (panelId: PanelId) => {
|
||||||
|
layoutStore.activePanelId =
|
||||||
|
layoutStore.activePanelId === panelId ? null : panelId
|
||||||
|
if (layoutStore.activeSubPanelId) layoutStore.activeSubPanelId = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleParticipants = () => {
|
const toggleAdmin = () => togglePanel(PanelId.ADMIN)
|
||||||
layoutStore.activePanelId = isParticipantsOpen ? null : PanelId.PARTICIPANTS
|
const toggleParticipants = () => togglePanel(PanelId.PARTICIPANTS)
|
||||||
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
|
const toggleChat = () => togglePanel(PanelId.CHAT)
|
||||||
}
|
const toggleEffects = () => togglePanel(PanelId.EFFECTS)
|
||||||
|
const toggleTools = () => togglePanel(PanelId.TOOLS)
|
||||||
const toggleChat = () => {
|
const toggleInfo = () => togglePanel(PanelId.INFO)
|
||||||
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 openTranscript = () => {
|
const openTranscript = () => {
|
||||||
layoutStore.activeSubPanelId = SubPanelId.TRANSCRIPT
|
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 =
|
export type ShortcutId =
|
||||||
| 'open-shortcuts'
|
| 'open-shortcuts'
|
||||||
| 'focus-toolbar'
|
| 'focus-toolbar'
|
||||||
|
| 'focus-pagination'
|
||||||
| 'toggle-microphone'
|
| 'toggle-microphone'
|
||||||
| 'toggle-camera'
|
| 'toggle-camera'
|
||||||
| 'push-to-talk'
|
| 'push-to-talk'
|
||||||
@@ -44,6 +45,11 @@ export const shortcutCatalog: ShortcutDescriptor[] = [
|
|||||||
category: 'navigation',
|
category: 'navigation',
|
||||||
shortcut: { key: 'F2' },
|
shortcut: { key: 'F2' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'focus-pagination',
|
||||||
|
category: 'navigation',
|
||||||
|
shortcut: { key: 'G', ctrlKey: true, shiftKey: true },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'toggle-microphone',
|
id: 'toggle-microphone',
|
||||||
category: 'media',
|
category: 'media',
|
||||||
|
|||||||
@@ -7,12 +7,16 @@ export type useRegisterKeyboardShortcutProps = {
|
|||||||
id?: ShortcutId
|
id?: ShortcutId
|
||||||
handler: () => Promise<void | boolean | undefined> | void
|
handler: () => Promise<void | boolean | undefined> | void
|
||||||
isDisabled?: boolean
|
isDisabled?: boolean
|
||||||
|
// Opt-in: controls rendered inside menus unmount when the menu closes, and
|
||||||
|
// their shortcuts must keep working.
|
||||||
|
unregisterOnUnmount?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRegisterKeyboardShortcut = ({
|
export const useRegisterKeyboardShortcut = ({
|
||||||
id,
|
id,
|
||||||
handler,
|
handler,
|
||||||
isDisabled = false,
|
isDisabled = false,
|
||||||
|
unregisterOnUnmount = false,
|
||||||
}: useRegisterKeyboardShortcutProps) => {
|
}: useRegisterKeyboardShortcutProps) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!id) return
|
if (!id) return
|
||||||
@@ -21,8 +25,14 @@ export const useRegisterKeyboardShortcut = ({
|
|||||||
const formattedKey = formatShortcutKey(descriptor.shortcut)
|
const formattedKey = formatShortcutKey(descriptor.shortcut)
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
keyboardShortcutsStore.shortcuts.delete(formattedKey)
|
keyboardShortcutsStore.shortcuts.delete(formattedKey)
|
||||||
} else {
|
return
|
||||||
keyboardShortcutsStore.shortcuts.set(formattedKey, handler)
|
|
||||||
}
|
}
|
||||||
}, [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": {
|
"actions": {
|
||||||
"open-shortcuts": "Tastenkürzel-Hilfe öffnen",
|
"open-shortcuts": "Tastenkürzel-Hilfe öffnen",
|
||||||
"focus-toolbar": "Fokus auf die untere Symbolleiste",
|
"focus-toolbar": "Fokus auf die untere Symbolleiste",
|
||||||
|
"focus-pagination": "Fokus auf die Teilnehmerseiten",
|
||||||
"toggle-microphone": "Mikrofon umschalten",
|
"toggle-microphone": "Mikrofon umschalten",
|
||||||
"toggle-camera": "Kamera umschalten",
|
"toggle-camera": "Kamera umschalten",
|
||||||
"push-to-talk": "Push-to-talk",
|
"push-to-talk": "Push-to-talk",
|
||||||
|
|||||||
@@ -784,6 +784,7 @@
|
|||||||
"actions": {
|
"actions": {
|
||||||
"open-shortcuts": "Open shortcuts help",
|
"open-shortcuts": "Open shortcuts help",
|
||||||
"focus-toolbar": "Focus bottom toolbar",
|
"focus-toolbar": "Focus bottom toolbar",
|
||||||
|
"focus-pagination": "Focus participant pagination",
|
||||||
"toggle-microphone": "Toggle microphone",
|
"toggle-microphone": "Toggle microphone",
|
||||||
"toggle-camera": "Toggle camera",
|
"toggle-camera": "Toggle camera",
|
||||||
"push-to-talk": "Push-to-talk (hold to unmute)",
|
"push-to-talk": "Push-to-talk (hold to unmute)",
|
||||||
|
|||||||
@@ -783,6 +783,7 @@
|
|||||||
"actions": {
|
"actions": {
|
||||||
"open-shortcuts": "Abrir la ayuda de atajos",
|
"open-shortcuts": "Abrir la ayuda de atajos",
|
||||||
"focus-toolbar": "Poner el foco en la barra de herramientas inferior",
|
"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-microphone": "Activar o desactivar el micrófono",
|
||||||
"toggle-camera": "Activar o desactivar la cámara",
|
"toggle-camera": "Activar o desactivar la cámara",
|
||||||
"push-to-talk": "Pulsar para hablar (mantener para reactivar)",
|
"push-to-talk": "Pulsar para hablar (mantener para reactivar)",
|
||||||
|
|||||||
@@ -784,6 +784,7 @@
|
|||||||
"actions": {
|
"actions": {
|
||||||
"open-shortcuts": "Ouvrir l’aide des raccourcis",
|
"open-shortcuts": "Ouvrir l’aide des raccourcis",
|
||||||
"focus-toolbar": "Mettre le focus sur la barre d’outils du bas",
|
"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-microphone": "Activer ou désactiver le micro",
|
||||||
"toggle-camera": "Activer ou désactiver la caméra",
|
"toggle-camera": "Activer ou désactiver la caméra",
|
||||||
"push-to-talk": "Appuyer pour parler (maintenir pour réactiver)",
|
"push-to-talk": "Appuyer pour parler (maintenir pour réactiver)",
|
||||||
|
|||||||
@@ -784,6 +784,7 @@
|
|||||||
"actions": {
|
"actions": {
|
||||||
"open-shortcuts": "Sneltoetsenhulp openen",
|
"open-shortcuts": "Sneltoetsenhulp openen",
|
||||||
"focus-toolbar": "Focus op de onderste werkbalk",
|
"focus-toolbar": "Focus op de onderste werkbalk",
|
||||||
|
"focus-pagination": "Focus op de paginering van deelnemers",
|
||||||
"toggle-microphone": "Microfoon aan/uit",
|
"toggle-microphone": "Microfoon aan/uit",
|
||||||
"toggle-camera": "Camera aan/uit",
|
"toggle-camera": "Camera aan/uit",
|
||||||
"push-to-talk": "Push-to-talk (ingedrukt houden om te activeren)",
|
"push-to-talk": "Push-to-talk (ingedrukt houden om te activeren)",
|
||||||
|
|||||||
@@ -10,5 +10,5 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"types": ["node"]
|
"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_NAME: summary-microservice
|
||||||
APP_API_TOKEN: password
|
APP_API_TOKEN: password
|
||||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
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_ACCESS_KEY_ID: meet-access-key
|
||||||
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
||||||
AWS_S3_SECURE_ACCESS: False
|
AWS_S3_SECURE_ACCESS: False
|
||||||
@@ -69,9 +69,9 @@ _summaryEnvVars: &summaryEnvVars
|
|||||||
LLM_MODEL: Qwen/Qwen3-Coder-30B-A3B-Instruct
|
LLM_MODEL: Qwen/Qwen3-Coder-30B-A3B-Instruct
|
||||||
WEBHOOK_API_TOKEN: password
|
WEBHOOK_API_TOKEN: password
|
||||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||||
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
|
CELERY_BROKER_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||||
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
|
CELERY_RESULT_BACKEND: redis://user:pass@dev-backend-redis:6379/1
|
||||||
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
|
TASK_TRACKER_REDIS_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||||
IS_RESOLVE_SPEAKER_IDENTITIES_ENABLED: true
|
IS_RESOLVE_SPEAKER_IDENTITIES_ENABLED: true
|
||||||
RESOLVE_SPEAKER_IDENTITIES_DEFAULT_OVERLAP: 0.5
|
RESOLVE_SPEAKER_IDENTITIES_DEFAULT_OVERLAP: 0.5
|
||||||
RESOLVE_SPEAKER_ENABLE_SPLIT_ON_WORDS: true
|
RESOLVE_SPEAKER_ENABLE_SPLIT_ON_WORDS: true
|
||||||
@@ -127,12 +127,21 @@ backend:
|
|||||||
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
|
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
|
||||||
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
||||||
# Databases
|
# Databases
|
||||||
DB_HOST: postgres
|
DB_HOST: dev-backend-postgres
|
||||||
DB_NAME: meet
|
DB_NAME:
|
||||||
DB_USER: dinum
|
secretKeyRef:
|
||||||
DB_PASSWORD: pass
|
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
|
DB_PORT: 5432
|
||||||
REDIS_URL: redis://default:pass@redis-master:6379/1
|
REDIS_URL: redis://user:pass@dev-backend-redis:6379/1
|
||||||
# Static files
|
# Static files
|
||||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||||
# Permissions
|
# Permissions
|
||||||
@@ -163,7 +172,7 @@ backend:
|
|||||||
FRONTEND_TRANSCRIPTION_DESTINATION: "https://docs.numerique.gouv.fr"
|
FRONTEND_TRANSCRIPTION_DESTINATION: "https://docs.numerique.gouv.fr"
|
||||||
FRONTEND_IS_SILENT_LOGIN_ENABLED: False
|
FRONTEND_IS_SILENT_LOGIN_ENABLED: False
|
||||||
# S3 Storage
|
# 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_ACCESS_KEY_ID: meet-access-key
|
||||||
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
||||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
||||||
@@ -186,7 +195,7 @@ backend:
|
|||||||
MEDIA_BASE_URL: https://meet.127.0.0.1.nip.io
|
MEDIA_BASE_URL: https://meet.127.0.0.1.nip.io
|
||||||
FILE_UPLOAD_ENABLED: True
|
FILE_UPLOAD_ENABLED: True
|
||||||
CELERY_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 & Transcription
|
||||||
RECORDING_ENABLE: True
|
RECORDING_ENABLE: True
|
||||||
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v2/async-jobs/transcribe/
|
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v2/async-jobs/transcribe/
|
||||||
@@ -266,11 +275,11 @@ ingressMedia:
|
|||||||
annotations:
|
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-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/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
|
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/$1
|
||||||
|
|
||||||
serviceMedia:
|
serviceMedia:
|
||||||
host: garage.meet.svc.cluster.local
|
host: dev-backend-garage.meet.svc.cluster.local
|
||||||
port: 9000
|
port: 9000
|
||||||
|
|
||||||
# ---- Extra ingress/service for background file uploads ------------
|
# ---- Extra ingress/service for background file uploads ------------
|
||||||
@@ -282,11 +291,11 @@ ingressMediaFiles:
|
|||||||
annotations:
|
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-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/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
|
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/files/$1
|
||||||
|
|
||||||
serviceMediaFiles:
|
serviceMediaFiles:
|
||||||
host: garage.meet.svc.cluster.local
|
host: dev-backend-garage.meet.svc.cluster.local
|
||||||
port: 9000
|
port: 9000
|
||||||
|
|
||||||
# ---- STT Orchestration Microservice Components --------------------
|
# ---- STT Orchestration Microservice Components --------------------
|
||||||
@@ -364,7 +373,7 @@ agentMetadata:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
ENABLE_SILERO_VAD: "false"
|
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_ACCESS_KEY_ID: meet-access-key
|
||||||
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
|
||||||
AWS_S3_SECURE_ACCESS: False
|
AWS_S3_SECURE_ACCESS: False
|
||||||
|
|||||||
@@ -13,14 +13,15 @@ egress:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
redis:
|
redis:
|
||||||
address: redis-master:6379
|
address: dev-backend-redis:6379
|
||||||
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
s3:
|
s3:
|
||||||
access_key: meet-access-key
|
access_key: meet-access-key
|
||||||
secret: meet-secret-access-key
|
secret: meet-secret-access-key
|
||||||
region: local
|
region: local
|
||||||
bucket: meet-media-storage
|
bucket: meet-media-storage
|
||||||
endpoint: http://garage:9000
|
endpoint: http://dev-backend-garage:9000
|
||||||
force_path_style: true
|
force_path_style: true
|
||||||
|
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ livekit:
|
|||||||
port_range_end: 60000
|
port_range_end: 60000
|
||||||
tcp_port: 7881
|
tcp_port: 7881
|
||||||
redis:
|
redis:
|
||||||
address: redis-master:6379
|
address: dev-backend-redis:6379
|
||||||
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
keys:
|
keys:
|
||||||
turn:
|
turn:
|
||||||
|
|||||||
@@ -16,14 +16,15 @@ egress:
|
|||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
redis:
|
redis:
|
||||||
address: redis-master:6379
|
address: dev-backend-redis:6379
|
||||||
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
s3:
|
s3:
|
||||||
access_key: meet-access-key
|
access_key: meet-access-key
|
||||||
secret: meet-secret-access-key
|
secret: meet-secret-access-key
|
||||||
region: local
|
region: local
|
||||||
bucket: meet-media-storage
|
bucket: meet-media-storage
|
||||||
endpoint: http://garage:9000
|
endpoint: http://dev-backend-garage:9000
|
||||||
force_path_style: true
|
force_path_style: true
|
||||||
|
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ livekit:
|
|||||||
port_range_end: 60000
|
port_range_end: 60000
|
||||||
tcp_port: 7881
|
tcp_port: 7881
|
||||||
redis:
|
redis:
|
||||||
address: redis-master:6379
|
address: dev-backend-redis:6379
|
||||||
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
keys:
|
keys:
|
||||||
turn:
|
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
|
- name: livekit
|
||||||
url: https://helm.livekit.io
|
url: https://helm.livekit.io
|
||||||
|
|
||||||
|
- name: dev-backends
|
||||||
|
url: https://suitenumerique.github.io/helm-dev-backend
|
||||||
|
|
||||||
releases:
|
releases:
|
||||||
- name: extra
|
- name: extra
|
||||||
installed: {{ regexMatch "^dev.*" .Environment.Name }}
|
installed: {{ regexMatch "^dev.*" .Environment.Name }}
|
||||||
@@ -27,8 +30,59 @@ releases:
|
|||||||
- enablePermanentRedirect: {{ .Values | get "enablePermanentRedirect" "False"}}
|
- enablePermanentRedirect: {{ .Values | get "enablePermanentRedirect" "False"}}
|
||||||
- oldDomain: {{ .Values | get "oldDomain" "demo.com" }}
|
- oldDomain: {{ .Values | get "oldDomain" "demo.com" }}
|
||||||
- newDomain: {{ .Values | get "newDomain" "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
|
- name: meet
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mail_mjml",
|
"name": "mail_mjml",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mail_mjml",
|
"name": "mail_mjml",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@html-to/text-cli": "0.6.1",
|
"@html-to/text-cli": "0.6.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mail_mjml",
|
"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",
|
"description": "An util to generate html and text django's templates from mjml templates",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "sdk",
|
"name": "sdk",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sdk",
|
"name": "sdk",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./library",
|
"./library",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sdk",
|
"name": "sdk",
|
||||||
"version": "1.33.0",
|
"version": "1.34.0",
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "summary"
|
name = "summary"
|
||||||
version = "1.33.0"
|
version = "1.34.0"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastapi[standard]>=0.105.0",
|
"fastapi[standard]>=0.105.0",
|
||||||
|
|||||||
Generated
+1
-1
@@ -1484,7 +1484,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "summary"
|
name = "summary"
|
||||||
version = "1.33.0"
|
version = "1.34.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "boto3" },
|
{ name = "boto3" },
|
||||||
|
|||||||
Reference in New Issue
Block a user