Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot] dfadac1023 ⬆️(dependencies) update GitHub Actions dependencies 2026-10-05 01:30:00 +00:00
35 changed files with 765 additions and 615 deletions
+13 -44
View File
@@ -43,7 +43,7 @@ jobs:
services:
postgres:
image: postgres:16
image: postgres:18
env:
POSTGRES_DB: meet
POSTGRES_USER: dinum
@@ -53,7 +53,7 @@ jobs:
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
redis:
image: redis:5
image: redis:7
ports:
- 6379:6379
# Set health checks to wait until redis has started
@@ -87,9 +87,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Create writable /data
run: |
@@ -117,11 +115,11 @@ jobs:
timeout 30 sh -c 'until docker exec garage /garage health; do sleep 1; done'
- name: Install Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
python-version: "3.14"
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Install the dependencies
run: uv sync --locked --all-extras
@@ -160,9 +158,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install ffmpeg
run: |
@@ -170,12 +166,12 @@ jobs:
sudo apt-get install -y ffmpeg
- name: Install Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
python-version: "3.14"
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Install the project
run: uv sync --locked --all-extras
@@ -189,9 +185,7 @@ jobs:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install dependencies
run: cd src/frontend/ && npm ci --ignore-scripts
@@ -202,27 +196,6 @@ jobs:
- name: Check format
run: cd src/frontend/ && npm run check
test-front:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
- name: Install Node.js
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version: "24"
- name: Install dependencies
run: cd src/frontend/ && npm ci --ignore-scripts
- name: Run tests
run: cd src/frontend/ && npm test
lint-sdk:
runs-on: ubuntu-latest
permissions:
@@ -232,9 +205,7 @@ jobs:
working-directory: src/sdk/library
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install dependencies
run: npm ci --ignore-scripts
@@ -255,9 +226,7 @@ jobs:
working-directory: src/sdk/library
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install dependencies
run: npm ci --ignore-scripts
+1 -4
View File
@@ -16,8 +16,6 @@ and this project adheres to
- 🔧(summary) add setting to control Sentry traces sampling rate
- ✨(frontend) let signed-out visitors start a meeting
- ✨(backend) expose `allow_unregistered_rooms` in the frontend configuration
- ✅(frontend) add vitest so the frontend can carry unit tests
- ♿️(frontend) make participant pagination readable and keyboard reachable #1775
### Changed
@@ -95,7 +93,7 @@ and this project adheres to
### Fixed
- 🐛(helm) probe liveness on **lbheartbeat** and readiness on **heartbeat**
- 🐛(helm) probe liveness on __lbheartbeat__ and readiness on __heartbeat__
- 🐛(helm) render periodSeconds and failureThreshold on probes
- 🐛(backend) report the app release to Sentry instead of "NA"
- 🐛(frontend) play the waiting room notification sound on every arrival
@@ -167,7 +165,6 @@ and this project adheres to
### Added
- ✨(any) let any authenticated user manage the lobby on trusted rooms
### Changed
- 📱(frontend) collapse mobile control bar items on narrow viewports
+1 -6
View File
@@ -260,8 +260,7 @@ lint-pylint: ## lint back-end python sources with pylint only on changed files f
test: ## run project tests; pass extra pytest args via ARGS, e.g. `make test ARGS="-vv"`
@args="$(ARGS) $(filter-out $@,$(MAKECMDGOALS))" && \
$(MAKE) test-back-parallel ARGS="$${args}" && \
$(MAKE) test-summary ARGS="$${args}" && \
$(MAKE) test-frontend
$(MAKE) test-summary ARGS="$${args}"
.PHONY: test
test-back: ## run back-end tests (pass extra pytest args via ARGS)
@@ -279,10 +278,6 @@ test-summary: ## run summary tests (pass extra pytest args via ARGS)
bin/pytest-summary $${args}
.PHONY: test-summary
test-frontend: ## run the frontend unit tests
cd $(PATH_FRONT) && npm test
.PHONY: test-frontend
makemigrations: ## run django makemigrations for the Meet project.
@echo "$(BOLD)Running makemigrations$(RESET)"
@$(COMPOSE) up -d postgresql
+8 -9
View File
@@ -104,16 +104,15 @@ k8s_yaml(secret_yaml_generic(
k8s_yaml(local('cd ../src/helm && helmfile -n meet -e ${DEV_ENV:-dev-keycloak} template .'))
k8s_resource('dev-backend-garage-cors', resource_deps=['dev-backend-garage'])
k8s_resource('dev-backend-keycloak', resource_deps=['dev-backend-keycloak-pg'])
k8s_resource('meet-backend', resource_deps=['dev-backend-postgres', 'dev-backend-garage-cors', 'dev-backend-redis', 'dev-backend-keycloak', 'livekit-livekit-server'])
k8s_resource('meet-celery-backend', resource_deps=['dev-backend-redis'])
k8s_resource('meet-celery-summarize', resource_deps=['dev-backend-redis'])
k8s_resource('meet-celery-summary-backend', resource_deps=['dev-backend-redis'])
k8s_resource('meet-celery-transcribe-default', resource_deps=['dev-backend-redis'])
k8s_resource('livekit-livekit-server', resource_deps=['dev-backend-redis'])
k8s_resource('garage-cors', resource_deps=['garage'])
k8s_resource('meet-backend', resource_deps=['postgresql', 'garage-cors', 'redis', 'livekit-livekit-server'])
k8s_resource('meet-celery-backend', resource_deps=['redis'])
k8s_resource('meet-celery-summarize', resource_deps=['redis'])
k8s_resource('meet-celery-summary-backend', resource_deps=['redis'])
k8s_resource('meet-celery-transcribe-default', resource_deps=['redis'])
k8s_resource('livekit-livekit-server', resource_deps=['redis'])
k8s_resource('livekit-livekit-server-test-connection', resource_deps=['livekit-livekit-server'])
k8s_resource('livekit-egress', resource_deps=['livekit-livekit-server'])
k8s_resource('keycloak', resource_deps=['kc-postgresql'])
# Trigger once on launch
k8s_resource(
'meet-backend-createsuperuser',
+8 -5
View File
@@ -9,8 +9,6 @@ services:
redis:
image: redis:5
ports:
- "6379:6379"
mailcatcher:
image: sj26/mailcatcher:latest
@@ -273,6 +271,11 @@ services:
- ./src/agents:/app
- /app/.venv
redis-summary:
image: redis:5
ports:
- "6379:6379"
app-summary-dev:
build:
context: src/summary
@@ -287,7 +290,7 @@ services:
volumes:
- ./src/summary:/app
depends_on:
- redis
- redis-summary
celery-summary-transcribe:
container_name: celery-summary-transcribe
@@ -301,7 +304,7 @@ services:
volumes:
- ./src/summary:/app
depends_on:
- redis
- redis-summary
- app-summary-dev
- garage
develop:
@@ -321,7 +324,7 @@ services:
volumes:
- ./src/summary:/app
depends_on:
- redis
- redis-summary
- app-summary-dev
- garage
develop:
-4
View File
@@ -9,10 +9,6 @@ AWS_S3_ACCESS_KEY_ID="meet-access-key"
AWS_S3_SECRET_ACCESS_KEY="meet-secret-access-key"
AWS_S3_REGION_NAME="local"
CELERY_BROKER_URL="redis://redis:6379/2"
CELERY_RESULT_BACKEND="redis://redis:6379/2"
TASK_TRACKER_REDIS_URL="redis://redis:6379/2"
WHISPERX_BASE_URL="https://configure-your-url.com"
WHISPERX_ASR_MODEL="large-v2"
WHISPERX_API_KEY="your-secret-key"
+4 -278
View File
@@ -64,8 +64,7 @@
"typescript-eslint": "8.60.1",
"vite": "8.0.14",
"vite-plugin-static-copy": "4.1.1",
"vite-plugin-svgr": "5.2.0",
"vitest": "5.0.2"
"vite-plugin-svgr": "5.2.0"
}
},
"node_modules/@adobe/react-spectrum": {
@@ -943,9 +942,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
"integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
@@ -2507,24 +2506,6 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/chai": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*",
"assertion-error": "^2.0.1"
}
},
"node_modules/@types/deep-eql": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/dom-mediacapture-record": {
"version": "1.0.22",
"resolved": "https://registry.npmjs.org/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.22.tgz",
@@ -2805,64 +2786,6 @@
}
}
},
"node_modules/@vitest/mocker": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.2.tgz",
"integrity": "sha512-Z5FS00Q1SJHkB35xATsmWGdQ5WA1/0MV3CDjqyv7GavHv1OfOj145MNfHOlHk7QLes21dKFDHr8EO2zvL+9WGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "0.3.31",
"@vitest/spy": "5.0.2",
"estree-walker": "^3.0.3",
"magic-string": "^1.2.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"msw": "^2.4.9",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"msw": {
"optional": true
},
"vite": {
"optional": true
}
}
},
"node_modules/@vitest/mocker/node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
},
"node_modules/@vitest/mocker/node_modules/magic-string": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.2.tgz",
"integrity": "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.6.0"
}
},
"node_modules/@vitest/spy": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.2.tgz",
"integrity": "sha512-Ijc7T1nT9efNb5LxvjaBrEqw3f/QwUv5EE0nKqZxgqsaV/FxAAZ8baGylA8X/Z2oS4Lp+K74Jr6dTJsDKxJDeg==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.25",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.25.tgz",
@@ -3228,16 +3151,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -4217,16 +4130,6 @@
],
"license": "CC-BY-4.0"
},
"node_modules/chai": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -5127,13 +5030,6 @@
"node": ">= 0.4"
}
},
"node_modules/es-module-lexer": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz",
"integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==",
"dev": true,
"license": "MIT"
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -5636,16 +5532,6 @@
"node": ">=18.0.0"
}
},
"node_modules/expect-type": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/express": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
@@ -8761,20 +8647,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/obug": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz",
"integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==",
"dev": true,
"funding": [
"https://github.com/sponsors/sxzz",
"https://opencollective.com/debug"
],
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -10348,13 +10220,6 @@
"node": ">= 0.8"
}
},
"node_modules/std-env": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz",
"integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==",
"dev": true,
"license": "MIT"
},
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -10661,26 +10526,6 @@
"xtend": "~4.0.1"
}
},
"node_modules/tinybench": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.0.tgz",
"integrity": "sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/tinyexec": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz",
"integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
@@ -11765,112 +11610,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vitest": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.2.tgz",
"integrity": "sha512-7MQrx9pDv5aHiUcovIb/70Ys3tgtkUVgCtledvKdCmEO+/1Dicq5ZqoSxOW034m03oqC+oHOKui2dM6qtMLoJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/mocker": "5.0.2",
"chai": "^6.2.2",
"es-module-lexer": "^2.3.2",
"expect-type": "^1.4.0",
"magic-string": "^1.2.3",
"obug": "^2.1.4",
"picomatch": "^4.0.7",
"std-env": "^4.2.0",
"tinybench": "^6.1.4",
"tinyexec": "^1.3.0",
"tinyglobby": "^0.2.17",
"why-is-node-running": "^3.2.1"
},
"bin": {
"vitest": "vitest.mjs"
},
"engines": {
"node": "^22.12.0 || ^24.0.0 || >=26.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
"@opentelemetry/api": "^1.9.0",
"@types/node": "^22.0.0 || >=24.0.0",
"@vitest/browser-playwright": "5.0.2",
"@vitest/browser-preview": "5.0.2",
"@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0",
"@vitest/coverage-istanbul": "5.0.2",
"@vitest/coverage-v8": "5.0.2",
"@vitest/ui": "5.0.2",
"happy-dom": "*",
"jsdom": "*",
"vite": "^6.4.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@edge-runtime/vm": {
"optional": true
},
"@opentelemetry/api": {
"optional": true
},
"@types/node": {
"optional": true
},
"@vitest/browser-playwright": {
"optional": true
},
"@vitest/browser-preview": {
"optional": true
},
"@vitest/browser-webdriverio": {
"optional": true
},
"@vitest/coverage-istanbul": {
"optional": true
},
"@vitest/coverage-v8": {
"optional": true
},
"@vitest/ui": {
"optional": true
},
"happy-dom": {
"optional": true
},
"jsdom": {
"optional": true
},
"vite": {
"optional": false
}
}
},
"node_modules/vitest/node_modules/magic-string": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.2.tgz",
"integrity": "sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.6.0"
}
},
"node_modules/vitest/node_modules/picomatch": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/walk-sync": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
@@ -12055,19 +11794,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/why-is-node-running": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.2.tgz",
"integrity": "sha512-NKUzAelcoCXhXL4dJzKIwXeR8iEVqsA0Lq6Vnd0UXvgaKbzVo4ZTHROF2Jidrv+SgxOQ03fMinnNhzZATxOD3A==",
"dev": true,
"license": "MIT",
"bin": {
"why-is-node-running": "cli.js"
},
"engines": {
"node": ">=20.11"
}
},
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+1 -3
View File
@@ -7,7 +7,6 @@
"dev": "panda codegen && vite",
"build": "panda codegen && tsc -b && vite build",
"build:debug": "VITE_ANALYZE=true npm run build -- --debug",
"test": "panda codegen && vitest run",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --fix",
"preview": "vite preview",
@@ -72,7 +71,6 @@
"typescript-eslint": "8.60.1",
"vite": "8.0.14",
"vite-plugin-static-copy": "4.1.1",
"vite-plugin-svgr": "5.2.0",
"vitest": "5.0.2"
"vite-plugin-svgr": "5.2.0"
}
}
@@ -7,6 +7,7 @@ import {
useSwipe,
} from '@livekit/components-react'
import { mergeProps } from '@/utils/mergeProps'
import { PaginationIndicator } from './PaginationIndicator'
import { useGridLayout } from '../hooks/useGridLayout'
import { PaginationControl } from './PaginationControl'
import { useSpeakerPromotionTrigger } from '../hooks/useSpeakerPromotionTrigger'
@@ -61,7 +62,13 @@ export function GridLayout({ tracks, ...props }: GridLayoutProps) {
>
<TrackLoop tracks={pagination.tracks}>{props.children}</TrackLoop>
{tracks.length > layout.maxTiles && (
<PaginationControl {...pagination} focusShortcut />
<>
<PaginationIndicator
totalPageCount={pagination.totalPageCount}
currentPage={pagination.currentPage}
/>
<PaginationControl pagesContainer={gridEl} {...pagination} />
</>
)}
</div>
)
@@ -1,147 +1,99 @@
import * as React from 'react'
import { createInteractingObservable } from '@livekit/components-core'
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
import { Button } from '@/primitives'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { css, cva, type RecipeVariantProps } from '@/styled-system/css'
import { useCallback, useRef } from 'react'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import { css } from '@/styled-system/css'
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
> & {
export interface PaginationControlProps {
totalPageCount: number
nextPage: () => void
prevPage: () => void
currentPage: number
// The shortcut listens on the main window only, so a single instance may own it.
focusShortcut?: boolean
pagesContainer?: React.RefObject<HTMLElement>
}
const arrowButtonClass = css({
_disabled: {
cursor: 'default',
backgroundColor: 'transparent !important',
'& svg': {
opacity: 0.35,
},
_focusVisible: {
outline: '2px solid',
outlineColor: 'focusRing',
outlineOffset: '2px',
},
},
})
export function PaginationControl({
totalPageCount,
nextPage,
prevPage,
currentPage,
placement,
focusShortcut = false,
pagesContainer: connectedElement,
}: PaginationControlProps) {
const { t } = useTranslation('rooms', { keyPrefix: 'pagination' })
const prevButtonRef = useRef<HTMLButtonElement>(null)
const nextButtonRef = useRef<HTMLButtonElement>(null)
const isSinglePage = totalPageCount <= 1
const isFirstPage = currentPage <= 1
const isLastPage = currentPage >= totalPageCount
const [interactive, setInteractive] = useState(false)
const focusPagination = useCallback(() => {
const target = isLastPage ? prevButtonRef.current : nextButtonRef.current
target?.focus()
}, [isLastPage])
useEffect(() => {
let subscription:
| ReturnType<ReturnType<typeof createInteractingObservable>['subscribe']>
| undefined
if (connectedElement) {
subscription = createInteractingObservable(
connectedElement.current,
2000
).subscribe(setInteractive)
}
return () => {
if (subscription) {
subscription.unsubscribe()
}
}
}, [connectedElement])
useRegisterKeyboardShortcut({
id: focusShortcut ? 'focus-pagination' : undefined,
handler: focusPagination,
isDisabled: isSinglePage,
unregisterOnUnmount: true,
})
if (isSinglePage) return null
const pageCount = t('count', { currentPage, totalPageCount })
if (totalPageCount <= 1) return null
return (
<div
role="group"
aria-label={`${t('label')}, ${pageCount}`}
className={paginationToolbar({ placement })}
<nav
aria-label={t('label')}
className={css({
position: 'absolute',
bottom: '1rem',
left: '50%',
transform: 'translateX(-50%)',
alignItems: 'stretch',
backgroundColor: 'var(--lk-control-bg)',
borderRadius: 'var(--lk-border-radius)',
transition: 'opacity ease-in-out .15s',
display: 'none',
border: '1px solid',
borderColor: 'primaryDark.100',
overflow: 'hidden',
})}
style={{
display: interactive ? 'flex' : 'none',
}}
data-lk-user-interaction={interactive}
>
<Button
ref={prevButtonRef}
aria-disabled={isFirstPage}
onPress={isFirstPage ? undefined : prevPage}
size="sm"
square
variant="primaryTextDark"
className={arrowButtonClass}
isDisabled={currentPage == 1}
onPress={prevPage}
size="xs"
variant="quaternaryText"
aria-label={t('previous')}
tooltip={t('previous')}
>
<RiArrowLeftSLine size={20} />
<RiArrowLeftSLine />
</Button>
<span
role="status"
className={css({
color: 'white',
fontSize: '0.8125rem',
fontWeight: 500,
minWidth: '3.5rem',
textAlign: 'center',
userSelect: 'none',
padding: '0 0.35rem',
whiteSpace: 'nowrap',
padding: '0.25rem 0.5rem',
})}
>
{pageCount}
{t('count', {
currentPage,
totalPageCount,
})}
</span>
<Button
ref={nextButtonRef}
aria-disabled={isLastPage}
onPress={isLastPage ? undefined : nextPage}
size="sm"
square
variant="primaryTextDark"
className={arrowButtonClass}
isDisabled={currentPage == totalPageCount}
onPress={nextPage}
size="xs"
variant="quaternaryText"
aria-label={t('next')}
tooltip={t('next')}
>
<RiArrowRightSLine size={20} />
<RiArrowRightSLine />
</Button>
</div>
</nav>
)
}
@@ -0,0 +1,30 @@
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>
)
})
@@ -0,0 +1,96 @@
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { styled } from '@/styled-system/jsx'
interface PipPaginationProps {
totalPageCount: number
currentPage: number
nextPage: () => void
prevPage: () => void
}
export const PipPagination = ({
totalPageCount,
currentPage,
nextPage,
prevPage,
}: PipPaginationProps) => {
const { t } = useTranslation('rooms', { keyPrefix: 'pagination' })
if (totalPageCount <= 1) return null
return (
<Nav aria-label={t('label')}>
<ArrowButton
type="button"
onClick={prevPage}
disabled={currentPage === 1}
aria-label={t('previous')}
>
<RiArrowLeftSLine size={18} />
</ArrowButton>
<Counter role="status">
{t('count', { currentPage, totalPageCount })}
</Counter>
<ArrowButton
type="button"
onClick={nextPage}
disabled={currentPage === totalPageCount}
aria-label={t('next')}
>
<RiArrowRightSLine size={18} />
</ArrowButton>
</Nav>
)
}
const Nav = styled('nav', {
base: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
marginTop: '1rem',
flexShrink: 0,
},
})
const ArrowButton = styled('button', {
base: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: '1.75rem',
height: '1.75rem',
borderRadius: '4px',
border: 'none',
cursor: 'pointer',
color: 'white',
backgroundColor: 'primaryDark.100',
transition: 'opacity 0.15s, background-color 0.15s',
'&:hover:not(:disabled)': {
backgroundColor: 'primaryDark.75',
},
'&:focus-visible': {
outline: '2px solid',
outlineColor: 'white',
outlineOffset: '2px',
},
'&:disabled': {
opacity: 0.3,
cursor: 'default',
},
},
})
const Counter = styled('span', {
base: {
fontSize: '0.75rem',
color: 'white',
opacity: 0.8,
whiteSpace: 'nowrap',
padding: '0 0.25rem',
minWidth: '3rem',
textAlign: 'center',
},
})
@@ -4,7 +4,7 @@ import { RoomEvent, Track } from 'livekit-client'
import { styled } from '@/styled-system/jsx'
import { PipFocusLayout } from './PipFocusLayout'
import { PipGridLayout } from './PipGridLayout'
import { PaginationControl } from '@/features/layout/components/PaginationControl'
import { PipPagination } from './PipPagination'
import { PipScreenShareLayout } from './PipScreenShareLayout'
import { StageFrame } from './StageFrame'
import { MAX_PIP_TILES } from '../../utils/pipGrid'
@@ -120,7 +120,12 @@ const PaginatedStage = ({
}) => (
<StageWrapper>
<StageFrame>{children}</StageFrame>
<PaginationControl {...pagination} placement="inline" />
<PipPagination
totalPageCount={pagination.totalPageCount}
currentPage={pagination.currentPage}
nextPage={pagination.nextPage}
prevPage={pagination.prevPage}
/>
</StageWrapper>
)
@@ -31,20 +31,35 @@ export const useSidePanel = () => {
const isSidePanelOpen = !!activePanelId
const isSubPanelOpen = !!activeSubPanelId
// Reads the live store, not the render snapshot: shortcut handlers outlive
// toggles that unmount (e.g. inside a closed overflow menu).
const togglePanel = (panelId: PanelId) => {
layoutStore.activePanelId =
layoutStore.activePanelId === panelId ? null : panelId
if (layoutStore.activeSubPanelId) layoutStore.activeSubPanelId = null
const toggleAdmin = () => {
layoutStore.activePanelId = isAdminOpen ? null : PanelId.ADMIN
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
}
const toggleAdmin = () => togglePanel(PanelId.ADMIN)
const toggleParticipants = () => togglePanel(PanelId.PARTICIPANTS)
const toggleChat = () => togglePanel(PanelId.CHAT)
const toggleEffects = () => togglePanel(PanelId.EFFECTS)
const toggleTools = () => togglePanel(PanelId.TOOLS)
const toggleInfo = () => togglePanel(PanelId.INFO)
const toggleParticipants = () => {
layoutStore.activePanelId = isParticipantsOpen ? null : PanelId.PARTICIPANTS
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
}
const toggleChat = () => {
layoutStore.activePanelId = isChatOpen ? null : PanelId.CHAT
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
}
const toggleEffects = () => {
layoutStore.activePanelId = isEffectsOpen ? null : PanelId.EFFECTS
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
}
const toggleTools = () => {
layoutStore.activePanelId = isToolsOpen ? null : PanelId.TOOLS
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
}
const toggleInfo = () => {
layoutStore.activePanelId = isInfoOpen ? null : PanelId.INFO
if (layoutSnap.activeSubPanelId) layoutStore.activeSubPanelId = null
}
const openTranscript = () => {
layoutStore.activeSubPanelId = SubPanelId.TRANSCRIPT
@@ -1,14 +0,0 @@
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,7 +7,6 @@ export type ShortcutCategory = 'navigation' | 'media' | 'interaction'
export type ShortcutId =
| 'open-shortcuts'
| 'focus-toolbar'
| 'focus-pagination'
| 'toggle-microphone'
| 'toggle-camera'
| 'push-to-talk'
@@ -45,11 +44,6 @@ export const shortcutCatalog: ShortcutDescriptor[] = [
category: 'navigation',
shortcut: { key: 'F2' },
},
{
id: 'focus-pagination',
category: 'navigation',
shortcut: { key: 'G', ctrlKey: true, shiftKey: true },
},
{
id: 'toggle-microphone',
category: 'media',
@@ -7,16 +7,12 @@ export type useRegisterKeyboardShortcutProps = {
id?: ShortcutId
handler: () => Promise<void | boolean | undefined> | void
isDisabled?: boolean
// Opt-in: controls rendered inside menus unmount when the menu closes, and
// their shortcuts must keep working.
unregisterOnUnmount?: boolean
}
export const useRegisterKeyboardShortcut = ({
id,
handler,
isDisabled = false,
unregisterOnUnmount = false,
}: useRegisterKeyboardShortcutProps) => {
useEffect(() => {
if (!id) return
@@ -25,14 +21,8 @@ export const useRegisterKeyboardShortcut = ({
const formattedKey = formatShortcutKey(descriptor.shortcut)
if (isDisabled) {
keyboardShortcutsStore.shortcuts.delete(formattedKey)
return
} else {
keyboardShortcutsStore.shortcuts.set(formattedKey, handler)
}
keyboardShortcutsStore.shortcuts.set(formattedKey, handler)
if (!unregisterOnUnmount) return
return () => {
if (keyboardShortcutsStore.shortcuts.get(formattedKey) === handler) {
keyboardShortcutsStore.shortcuts.delete(formattedKey)
}
}
}, [handler, id, isDisabled, unregisterOnUnmount])
}, [handler, id, isDisabled])
}
-1
View File
@@ -784,7 +784,6 @@
"actions": {
"open-shortcuts": "Tastenkürzel-Hilfe öffnen",
"focus-toolbar": "Fokus auf die untere Symbolleiste",
"focus-pagination": "Fokus auf die Teilnehmerseiten",
"toggle-microphone": "Mikrofon umschalten",
"toggle-camera": "Kamera umschalten",
"push-to-talk": "Push-to-talk",
-1
View File
@@ -784,7 +784,6 @@
"actions": {
"open-shortcuts": "Open shortcuts help",
"focus-toolbar": "Focus bottom toolbar",
"focus-pagination": "Focus participant pagination",
"toggle-microphone": "Toggle microphone",
"toggle-camera": "Toggle camera",
"push-to-talk": "Push-to-talk (hold to unmute)",
-1
View File
@@ -783,7 +783,6 @@
"actions": {
"open-shortcuts": "Abrir la ayuda de atajos",
"focus-toolbar": "Poner el foco en la barra de herramientas inferior",
"focus-pagination": "Poner el foco en la paginación de participantes",
"toggle-microphone": "Activar o desactivar el micrófono",
"toggle-camera": "Activar o desactivar la cámara",
"push-to-talk": "Pulsar para hablar (mantener para reactivar)",
-1
View File
@@ -784,7 +784,6 @@
"actions": {
"open-shortcuts": "Ouvrir l’aide des raccourcis",
"focus-toolbar": "Mettre le focus sur la barre d’outils du bas",
"focus-pagination": "Mettre le focus sur la pagination des participants",
"toggle-microphone": "Activer ou désactiver le micro",
"toggle-camera": "Activer ou désactiver la caméra",
"push-to-talk": "Appuyer pour parler (maintenir pour réactiver)",
-1
View File
@@ -784,7 +784,6 @@
"actions": {
"open-shortcuts": "Sneltoetsenhulp openen",
"focus-toolbar": "Focus op de onderste werkbalk",
"focus-pagination": "Focus op de paginering van deelnemers",
"toggle-microphone": "Microfoon aan/uit",
"toggle-camera": "Camera aan/uit",
"push-to-talk": "Push-to-talk (ingedrukt houden om te activeren)",
+1 -1
View File
@@ -10,5 +10,5 @@
"noEmit": true,
"types": ["node"]
},
"include": ["vite.config.ts", "vitest.config.ts"]
"include": ["vite.config.ts"]
}
-8
View File
@@ -1,8 +0,0 @@
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,
},
})
+16 -25
View File
@@ -24,7 +24,7 @@ _summaryEnvVars: &summaryEnvVars
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: http://dev-backend-garage.meet.svc.cluster.local:9000/
AWS_S3_ENDPOINT_URL: http://garage.meet.svc.cluster.local:9000/
AWS_S3_ACCESS_KEY_ID: meet-access-key
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
AWS_S3_SECURE_ACCESS: False
@@ -69,9 +69,9 @@ _summaryEnvVars: &summaryEnvVars
LLM_MODEL: Qwen/Qwen3-Coder-30B-A3B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://user:pass@dev-backend-redis:6379/1
CELERY_RESULT_BACKEND: redis://user:pass@dev-backend-redis:6379/1
TASK_TRACKER_REDIS_URL: redis://user:pass@dev-backend-redis:6379/1
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
IS_RESOLVE_SPEAKER_IDENTITIES_ENABLED: true
RESOLVE_SPEAKER_IDENTITIES_DEFAULT_OVERLAP: 0.5
RESOLVE_SPEAKER_ENABLE_SPLIT_ON_WORDS: true
@@ -127,21 +127,12 @@ backend:
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
# Databases
DB_HOST: dev-backend-postgres
DB_NAME:
secretKeyRef:
name: dev-backend-postgres
key: database
DB_USER:
secretKeyRef:
name: dev-backend-postgres
key: username
DB_PASSWORD:
secretKeyRef:
name: dev-backend-postgres
key: password
DB_HOST: postgres
DB_NAME: meet
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
REDIS_URL: redis://user:pass@dev-backend-redis:6379/1
REDIS_URL: redis://default:pass@redis-master:6379/1
# Static files
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
# Permissions
@@ -172,7 +163,7 @@ backend:
FRONTEND_TRANSCRIPTION_DESTINATION: "https://docs.numerique.gouv.fr"
FRONTEND_IS_SILENT_LOGIN_ENABLED: False
# S3 Storage
AWS_S3_ENDPOINT_URL: http://dev-backend-garage.meet.svc.cluster.local:9000
AWS_S3_ENDPOINT_URL: http://garage.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet-access-key
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
AWS_STORAGE_BUCKET_NAME: meet-media-storage
@@ -195,7 +186,7 @@ backend:
MEDIA_BASE_URL: https://meet.127.0.0.1.nip.io
FILE_UPLOAD_ENABLED: True
CELERY_ENABLED: True
CELERY_BROKER_URL: redis://user:pass@dev-backend-redis:6379/1
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
# Recording & Transcription
RECORDING_ENABLE: True
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v2/async-jobs/transcribe/
@@ -275,11 +266,11 @@ ingressMedia:
annotations:
nginx.ingress.kubernetes.io/auth-url: https://meet.127.0.0.1.nip.io/api/v1.0/recordings/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: dev-backend-garage.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/upstream-vhost: garage.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/$1
serviceMedia:
host: dev-backend-garage.meet.svc.cluster.local
host: garage.meet.svc.cluster.local
port: 9000
# ---- Extra ingress/service for background file uploads ------------
@@ -291,11 +282,11 @@ ingressMediaFiles:
annotations:
nginx.ingress.kubernetes.io/auth-url: https://meet.127.0.0.1.nip.io/api/v1.0/files/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: dev-backend-garage.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/upstream-vhost: garage.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/files/$1
serviceMediaFiles:
host: dev-backend-garage.meet.svc.cluster.local
host: garage.meet.svc.cluster.local
port: 9000
# ---- STT Orchestration Microservice Components --------------------
@@ -373,7 +364,7 @@ agentMetadata:
{{- end }}
{{- end }}
ENABLE_SILERO_VAD: "false"
AWS_S3_ENDPOINT_URL: dev-backend-garage.meet.svc.cluster.local:9000
AWS_S3_ENDPOINT_URL: garage.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet-access-key
AWS_S3_SECRET_ACCESS_KEY: meet-secret-access-key
AWS_S3_SECURE_ACCESS: False
@@ -13,15 +13,14 @@ egress:
{{- end }}
{{- end }}
redis:
address: dev-backend-redis:6379
username: user
address: redis-master:6379
password: pass
s3:
access_key: meet-access-key
secret: meet-secret-access-key
region: local
bucket: meet-media-storage
endpoint: http://dev-backend-garage:9000
endpoint: http://garage:9000
force_path_style: true
loadBalancer:
@@ -14,8 +14,7 @@ livekit:
port_range_end: 60000
tcp_port: 7881
redis:
address: dev-backend-redis:6379
username: user
address: redis-master:6379
password: pass
keys:
turn:
@@ -16,15 +16,14 @@ egress:
{{- end }}
{{- end }}
redis:
address: dev-backend-redis:6379
username: user
address: redis-master:6379
password: pass
s3:
access_key: meet-access-key
secret: meet-secret-access-key
region: local
bucket: meet-media-storage
endpoint: http://dev-backend-garage:9000
endpoint: http://garage:9000
force_path_style: true
loadBalancer:
@@ -14,8 +14,7 @@ livekit:
port_range_end: 60000
tcp_port: 7881
redis:
address: dev-backend-redis:6379
username: user
address: redis-master:6379
password: pass
keys:
turn:
+172
View File
@@ -0,0 +1,172 @@
---
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
@@ -0,0 +1,61 @@
---
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
+104
View File
@@ -0,0 +1,104 @@
---
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
@@ -0,0 +1,70 @@
---
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
+65
View File
@@ -0,0 +1,65 @@
---
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
+2 -56
View File
@@ -13,9 +13,6 @@ repositories:
- name: livekit
url: https://helm.livekit.io
- name: dev-backends
url: https://suitenumerique.github.io/helm-dev-backend
releases:
- name: extra
installed: {{ regexMatch "^dev.*" .Environment.Name }}
@@ -30,59 +27,8 @@ releases:
- enablePermanentRedirect: {{ .Values | get "enablePermanentRedirect" "False"}}
- oldDomain: {{ .Values | get "oldDomain" "demo.com" }}
- newDomain: {{ .Values | get "newDomain" "demo.com" }}
- 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
- realm: |
{{ readFile "../../docker/auth/realm.json" | replace "http://localhost:3200" "https://meet.127.0.0.1.nip.io" | indent 8 }}
- name: meet