Compare commits

..

2 Commits

Author SHA1 Message Date
fheslouin ca6abed0fc (helm) add resources requests to pods 2026-03-31 10:59:01 +02:00
fheslouin b6daa6d53f 🔒(helm) add pod and container securityContext
This commit aim at adding a securityContext for
pod and container in Deployment and Job,
it include livekit pods as well

It adds 2 values :
- podSecurityContext : for pods
- securityContext : for containers

Please note that `celeryBackend` Deployment does
not have any values defined in `values.meet.yaml` at the moment.
2026-03-27 11:43:38 +01:00
78 changed files with 642 additions and 789 deletions
+2
View File
@@ -223,6 +223,8 @@ jobs:
DB_PORT: 5432
REDIS_URL: redis://localhost:6379/1
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
LIVEKIT_API_SECRET: secret
LIVEKIT_API_KEY: devkey
AWS_S3_ENDPOINT_URL: http://localhost:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
+1 -5
View File
@@ -8,17 +8,13 @@ and this project adheres to
## [Unreleased]
## [1.13.0] - 2026-03-31
### Changed
- ⬆️(dependencies) update python dependencies
- ♿️(frontend) add explicit region for call controls #1216
- ♿️(frontend) improve accessibility of the reaction toolbar #1216
- ♿️(frontend) enhance sidepanel navigation accessibility #1216
### Fixed
- ⚡️(helm) Add pod and container securityContext #4805
- 🔒️(backend) fix email disclosure in room invitation endpoint #1200
- 🐛(backend) fix regression in update-participant endpoint #1204
+5 -5
View File
@@ -73,8 +73,7 @@ create-env-files: \
env.d/development/crowdin \
env.d/development/postgresql \
env.d/development/kc_postgresql \
env.d/development/summary \
env.d/development/kube-secret
env.d/development/summary
.PHONY: create-env-files
bootstrap: ## Prepare Docker images for the project
@@ -266,9 +265,6 @@ env.d/development/kc_postgresql:
env.d/development/summary:
cp -n env.d/development/summary.dist env.d/development/summary
env.d/development/kube-secret:
cp -n env.d/development/kube-secret.dist env.d/development/kube-secret
# -- Internationalization
env.d/development/crowdin:
@@ -363,6 +359,10 @@ install-external-secrets: ## install the kubernetes secrets from Vaultwarden
./bin/install-external-secrets.sh
.PHONY: build-k8s-cluster
start-tilt: ## start the kubernetes cluster using kind
tilt up --namespace=meet -f ./bin/Tiltfile
.PHONY: build-k8s-cluster
start-tilt-keycloak: ## start the kubernetes cluster using kind, without Pro Connect for authentication, use keycloak
DEV_ENV=dev-keycloak tilt up --namespace=meet -f ./bin/Tiltfile
.PHONY: build-k8s-cluster
+2 -8
View File
@@ -2,7 +2,7 @@ load('ext://uibutton', 'cmd_button', 'bool_input', 'location')
load('ext://namespace', 'namespace_create', 'namespace_inject')
namespace_create('meet')
DEV_ENV = os.getenv('DEV_ENV', 'dev-keycloak')
DEV_ENV = os.getenv('DEV_ENV', 'dev')
if DEV_ENV == 'dev-dinum':
update_settings(suppress_unused_image_warnings=["localhost:5001/meet-frontend-generic:latest"])
@@ -95,13 +95,7 @@ docker_build(
)
clean_old_images('localhost:5001/meet-livekit')
load('ext://secret', 'secret_yaml_generic')
k8s_yaml(secret_yaml_generic(
name="secret-dev",
from_env_file="../env.d/development/kube-secret"
))
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} template .'))
k8s_resource('minio-bucket', resource_deps=['minio'])
k8s_resource('meet-backend', resource_deps=['postgresql', 'minio', 'redis', 'livekit-livekit-server'])
-4
View File
@@ -1,4 +0,0 @@
WHISPERX_BASE_URL=https://configure-your-url.com
WHISPERX_API_KEY=<key>
LLM_BASE_URL=https://configure-your-url.com
LLM_API_KEY=<key>
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "agents"
version = "1.13.0"
version = "1.12.0"
requires-python = ">=3.12"
dependencies = [
"livekit-agents==1.4.5",
@@ -205,7 +205,7 @@ def test_start_subtitle_wrong_signature(settings, mock_livekit_token):
"""Test that tokens signed with incorrect signature are rejected."""
settings.ROOM_SUBTITLE_ENABLED = True
settings.LIVEKIT_CONFIGURATION["api_secret"] = "wrong-secret-padded-to-32-bytes!!"
settings.LIVEKIT_CONFIGURATION["api_secret"] = "wrong-secret"
room = RoomFactory()
client = APIClient()
@@ -47,7 +47,7 @@ def mock_livekit_config(settings):
"""Mock LiveKit configuration."""
settings.LIVEKIT_CONFIGURATION = {
"api_key": "test_api_key",
"api_secret": "test_api_secret_padded_to_32bytes!",
"api_secret": "test_api_secret",
"url": "https://test-livekit.example.com/",
}
return settings.LIVEKIT_CONFIGURATION
@@ -713,7 +713,7 @@ def test_api_rooms_token_invalid_signature(mock_rs_authenticate, settings):
}
token = jwt.encode(
payload,
"invalid-private-key-padded-to-32b!",
"invalid-private-key",
algorithm=settings.APPLICATION_JWT_ALG,
)
+1 -7
View File
@@ -981,13 +981,7 @@ class Test(Base):
USE_SWAGGER = True
EXTERNAL_API_ENABLED = True
LIVEKIT_CONFIGURATION = {
"api_key": "devkey-padded-for-minimum-len!-livekit",
"api_secret": "secret-key-padded-for-minimum-len!-livekit",
"url": "http://127.0.0.1.nip.io:7880",
}
APPLICATION_JWT_SECRET_KEY = "secret-key-padded-for-minimum-len!-application" # noqa:S105
APPLICATION_JWT_SECRET_KEY = "devKey" # noqa:S105
APPLICATION_JWT_AUDIENCE = "Test inc."
CELERY_TASK_ALWAYS_EAGER = True
+2 -2
View File
@@ -7,7 +7,7 @@ build-backend = "uv_build"
[project]
name = "meet"
version = "1.13.0"
version = "1.12.0"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -55,7 +55,7 @@ dependencies = [
"PyJWT==2.12.1",
"python-frontmatter==1.1.0",
"python-magic==0.4.27",
"requests==2.33.0",
"requests==2.32.5",
"sentry-sdk==2.54.0",
"whitenoise==6.12.0",
"mozilla-django-oidc==5.0.2",
+49 -49
View File
@@ -474,55 +474,55 @@ wheels = [
[[package]]
name = "cryptography"
version = "46.0.6"
version = "46.0.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" }
sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" },
{ url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" },
{ url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" },
{ url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" },
{ url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" },
{ url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" },
{ url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" },
{ url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" },
{ url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" },
{ url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" },
{ url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" },
{ url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" },
{ url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" },
{ url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" },
{ url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" },
{ url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" },
{ url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" },
{ url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" },
{ url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" },
{ url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" },
{ url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" },
{ url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" },
{ url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" },
{ url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" },
{ url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" },
{ url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" },
{ url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" },
{ url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" },
{ url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" },
{ url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" },
{ url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" },
{ url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" },
{ url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" },
{ url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" },
{ url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" },
{ url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" },
{ url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" },
{ url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" },
{ url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" },
{ url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" },
{ url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" },
{ url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" },
{ url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" },
{ url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" },
{ url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" },
{ url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" },
{ url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" },
{ url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" },
{ url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" },
{ url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" },
{ url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" },
{ url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" },
{ url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" },
{ url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" },
{ url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" },
{ url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" },
{ url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" },
{ url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" },
{ url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" },
{ url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" },
{ url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" },
{ url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" },
{ url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" },
{ url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" },
{ url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" },
{ url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" },
{ url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" },
{ url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" },
{ url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" },
{ url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" },
{ url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" },
{ url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" },
{ url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" },
{ url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" },
{ url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" },
{ url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" },
{ url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" },
{ url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" },
{ url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" },
{ url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" },
{ url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" },
{ url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" },
{ url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" },
{ url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" },
]
[[package]]
@@ -1157,7 +1157,7 @@ wheels = [
[[package]]
name = "meet"
version = "1.13.0"
version = "1.12.0"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
@@ -1255,7 +1255,7 @@ requires-dist = [
{ name = "python-frontmatter", specifier = "==1.1.0" },
{ name = "python-magic", specifier = "==0.4.27" },
{ name = "redis", specifier = "==5.2.1" },
{ name = "requests", specifier = "==2.33.0" },
{ name = "requests", specifier = "==2.32.5" },
{ name = "sentry-sdk", specifier = "==2.54.0" },
{ name = "whitenoise", specifier = "==6.12.0" },
]
@@ -1965,7 +1965,7 @@ wheels = [
[[package]]
name = "requests"
version = "2.33.0"
version = "2.32.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
@@ -1973,9 +1973,9 @@ dependencies = [
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/64/8860370b167a9721e8956ae116825caff829224fbca0ca6e7bf8ddef8430/requests-2.33.0.tar.gz", hash = "sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652", size = 134232, upload-time = "2026-03-25T15:10:41.586Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" },
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
+5 -1
View File
@@ -1,5 +1,7 @@
FROM node:20-alpine AS frontend-deps
USER node
WORKDIR /home/frontend/
COPY ./src/frontend/package.json ./package.json
@@ -8,7 +10,7 @@ COPY ./src/frontend/package-lock.json ./package-lock.json
RUN npm ci
COPY .dockerignore ./.dockerignore
COPY ./src/frontend/ .
COPY --chown=node:node ./src/frontend/ .
### ---- Front-end builder image ----
FROM frontend-deps AS meet
@@ -17,6 +19,8 @@ WORKDIR /home/frontend
FROM frontend-deps AS meet-dev
USER node
WORKDIR /home/frontend
EXPOSE 8080
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "meet",
"version": "1.13.0",
"version": "1.12.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "meet",
"version": "1.13.0",
"version": "1.12.0",
"dependencies": {
"@fontsource-variable/material-symbols-outlined": "5.2.34",
"@fontsource/material-icons-outlined": "5.2.6",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "meet",
"private": true,
"version": "1.13.0",
"version": "1.12.0",
"type": "module",
"scripts": {
"dev": "panda codegen && vite",
-5
View File
@@ -274,11 +274,6 @@ const config: Config = {
min: { value: 'min-content' },
max: { value: 'max-content' },
fit: { value: 'fit-content' },
// room layout
'room-side-panel': { value: '360px' },
'room-side-panel-margin': { value: '1.5rem' },
'room-control-bar': { value: '80px' },
'room-reaction-toolbar-height': { value: '42px' },
},
spacing,
}),
@@ -1,44 +0,0 @@
import { cva, RecipeVariantProps } from '@/styled-system/css'
import { useTranslation } from 'react-i18next'
const controlBarRegion = cva({
base: {
display: 'flex',
alignItems: 'center',
flex: '1 1 33%',
justifyContent: 'center',
gap: '0.65rem',
},
variants: {
mobile: {
true: {
justifyContent: 'space-between',
width: '330px',
},
},
},
defaultVariants: {
mobile: false,
},
})
export type ControlBarRegionProps = React.HTMLAttributes<HTMLDivElement> &
RecipeVariantProps<typeof controlBarRegion>
export function ControlBarRegion({
children,
mobile,
...props
}: ControlBarRegionProps) {
const { t } = useTranslation('rooms')
return (
<div
role="region"
aria-label={t('controls.region')}
className={controlBarRegion({ mobile })}
{...props}
>
{children}
</div>
)
}
@@ -1,86 +0,0 @@
// RoomContentArea.tsx
import React from 'react'
import { styled } from '@/styled-system/jsx'
import { cva } from '@/styled-system/css'
import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
import { Subtitles } from '@/features/subtitle/component/Subtitles'
import { MainNotificationToast } from '@/features/notifications/MainNotificationToast'
import { useReactionsToolbar } from '@/features/reactions/hooks/useReactionsToolbar'
const RoomViewport = styled(
'div',
cva({
base: {
position: 'absolute',
maxHeight: '100%',
transition:
'inset .5s cubic-bezier(0.4,0,0.2,1) 5ms, padding .5s cubic-bezier(0.4,0,0.2,1) 5ms',
},
variants: {
isSidePanelOpen: {
true: {
inset: `var(--lk-grid-gap) calc(var(--sizes-room-side-panel) + var(--sizes-room-side-panel-margin) * 2) calc(var(--sizes-room-control-bar)) 16px`,
},
false: {
inset: `var(--lk-grid-gap) var(--lk-grid-gap) calc(var(--sizes-room-control-bar))`,
},
},
isReactionToolbarOpen: {
true: {
paddingBottom:
'calc(var(--sizes-room-reaction-toolbar-height) + calc(var(--lk-grid-gap) / 2))',
},
false: {
paddingBottom: '0',
},
},
},
})
)
const TrackAreaContainer = styled(
'div',
cva({
base: {
position: 'relative',
display: 'flex',
width: '100%',
transition: 'height .5s cubic-bezier(0.4,0,0.2,1) 5ms',
},
variants: {
areSubtitlesOpen: {
true: {
height: 'calc(100% - 12rem)',
},
false: {
height: '100%',
},
},
},
})
)
interface RoomContentAreaProps {
children: React.ReactNode
}
export function RoomContentArea({ children }: RoomContentAreaProps) {
const { isSidePanelOpen } = useSidePanel()
const { areSubtitlesOpen } = useSubtitles()
const { isOpen: isReactionToolbarOpen } = useReactionsToolbar()
return (
<RoomViewport
isSidePanelOpen={isSidePanelOpen}
isReactionToolbarOpen={isReactionToolbarOpen}
>
<TrackAreaContainer areSubtitlesOpen={areSubtitlesOpen}>
{children}
</TrackAreaContainer>
<Subtitles />
<MainNotificationToast />
</RoomViewport>
)
}
@@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useRoomContext } from '@livekit/components-react'
import { Participant, RemoteParticipant, RoomEvent } from 'livekit-client'
import { ChatMessage, isMobileBrowser } from '@livekit/components-core'
@@ -10,11 +10,17 @@ import { decodeNotificationDataReceived } from './utils'
import { useNotificationSound } from '@/features/notifications/hooks/useSoundNotification'
import { ToastProvider, toastQueue } from './components/ToastProvider'
import { WaitingParticipantNotification } from './components/WaitingParticipantNotification'
import {
Emoji,
Reaction,
} from '@/features/rooms/livekit/components/controls/ReactionsToggle'
import {
ANIMATION_DURATION,
ReactionPortals,
} from '@/features/rooms/livekit/components/ReactionPortal'
import { layoutStore } from '@/stores/layout'
import { PanelId } from '@/features/rooms/livekit/hooks/useSidePanel'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
import { Emoji } from '@/features/reactions/types'
import { useReactions } from '@/features/reactions/hooks/useReactions'
export const MainNotificationToast = () => {
const room = useRoomContext()
@@ -22,7 +28,8 @@ export const MainNotificationToast = () => {
const { t } = useTranslation('notifications')
const announce = useScreenReaderAnnounce()
const { appendReaction } = useReactions()
const [reactions, setReactions] = useState<Reaction[]>([])
const instanceIdRef = useRef(0)
useEffect(() => {
const handleChatMessage = (
@@ -55,13 +62,21 @@ export const MainNotificationToast = () => {
}
}, [room, triggerNotificationSound, announce, t])
const handleEmoji = useCallback(
(emoji: string, participant: Participant) => {
if (!emoji || !Object.values(Emoji).includes(emoji as Emoji)) return
appendReaction(emoji as Emoji, participant)
},
[appendReaction]
)
const handleEmoji = (emoji: string, participant: Participant) => {
if (!emoji || !Object.values(Emoji).includes(emoji as Emoji)) return
const id = instanceIdRef.current++
setReactions((prev) => [
...prev,
{
id,
emoji,
participant,
},
])
setTimeout(() => {
setReactions((prev) => prev.filter((instance) => instance.id !== id))
}, ANIMATION_DURATION)
}
useEffect(() => {
const handleDataReceived = (
@@ -134,7 +149,7 @@ export const MainNotificationToast = () => {
return () => {
room.off(RoomEvent.DataReceived, handleDataReceived)
}
}, [room, handleEmoji])
}, [room])
useEffect(() => {
const showJoinNotification = (participant: Participant) => {
@@ -237,6 +252,7 @@ export const MainNotificationToast = () => {
<Div position="absolute" bottom={0} right={5} zIndex={1000}>
<ToastProvider />
<WaitingParticipantNotification />
<ReactionPortals reactions={reactions} />
</Div>
)
}
@@ -1,33 +0,0 @@
import { useTranslation } from 'react-i18next'
import { RiEmotionLine } from '@remixicon/react'
import { ToggleButton } from '@/primitives'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import { useReactionsToolbar } from '../hooks/useReactionsToolbar'
export const ReactionsToggle = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { isOpen, toggle } = useReactionsToolbar()
useRegisterKeyboardShortcut({
id: 'reaction',
handler: toggle,
})
return (
<ToggleButton
id="reactions-toggle"
data-attr="reactions-toggle"
square
variant="primaryDark"
aria-label={t('button')}
aria-expanded={isOpen}
tooltip={t('button')}
isSelected={isOpen}
onChange={toggle}
>
<RiEmotionLine />
</ToggleButton>
)
}
@@ -1,31 +0,0 @@
import { useTranslation } from 'react-i18next'
import { css } from '@/styled-system/css'
import { Emoji } from '../../types'
import { useReactions } from '../../hooks/useReactions'
import { Button } from '@/primitives'
export const ReactionButton = ({ emoji }: { emoji: Emoji }) => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { sendReaction } = useReactions()
return (
<Button
onPress={() => sendReaction(emoji)}
aria-label={t(`emojis.${emoji}`)}
variant="primaryTextDark"
size="sm"
round
data-attr={`send-reaction-${emoji}`}
>
<img
src={`/assets/reactions/${emoji}.png`}
alt=""
className={css({
width: '28px',
height: '28px',
pointerEvents: 'none',
userSelect: 'none',
})}
/>
</Button>
)
}
@@ -1,153 +0,0 @@
import { FocusScope, useFocusManager } from '@react-aria/focus'
import { useReactionsToolbar } from '../../hooks/useReactionsToolbar'
import { ReactionButton } from './ReactionButton'
import { Emoji } from '../../types'
import { styled } from '@/styled-system/jsx'
import { layoutStore } from '@/stores/layout'
import { getFirstControlBarFocusable } from '@/utils/dom'
import { useIsMobile } from '@/utils/useIsMobile'
import { useEffect, useRef, useState } from 'react'
import { useDelayUnmount } from '@/hooks/useDelayUnmount'
import { useTranslation } from 'react-i18next'
const Container = styled('div', {
base: {
display: 'flex',
justifyContent: 'center',
position: 'absolute',
bottom: 'var(--sizes-room-control-bar)',
left: 0,
right: 0,
pointerEvents: 'none',
},
})
const StyledStrip = styled('div', {
base: {
display: 'flex',
gap: '0.2rem',
borderRadius: '21px',
padding: '0.15rem',
backgroundColor: 'primaryDark.100',
opacity: 0,
transform: 'translateY(3.25rem)',
transition: 'opacity, transform',
transitionDuration: '0.5s',
transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',
pointerEvents: 'none',
},
variants: {
isVisible: {
true: {
opacity: 1,
transform: 'translateY(0)',
pointerEvents: 'auto',
},
},
desktopOffset: {
true: {
// Ideally this value should be calculated dynamically in JavaScript to keep
// the reaction toolbar perfectly centered relative to the reaction toggle.
// However, for simplicity and to follow a pragmatic 80/20 approach,
// this value is currently hardcoded in CSS.
marginRight: '30px',
},
},
},
})
const Strip = ({ children }: { children: React.ReactNode }) => {
const { isOpen } = useReactionsToolbar()
const isMobile = useIsMobile()
const ref = useRef<HTMLDivElement>(null)
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
if (isOpen) {
// defer one frame so the browser paints opacity:0 first
const id = requestAnimationFrame(() => setIsVisible(true))
return () => cancelAnimationFrame(id)
} else {
setIsVisible(false)
}
}, [isOpen])
return (
<StyledStrip
ref={ref}
aria-hidden={!isOpen}
isVisible={isVisible}
desktopOffset={!isMobile}
>
{children}
</StyledStrip>
)
}
const KeyboardNavigation = ({ children }: { children: React.ReactNode }) => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const focusManager = useFocusManager()
const onFocus = (e: React.FocusEvent<HTMLDivElement>) => {
const comingFromOutside = !e.currentTarget.contains(e.relatedTarget)
if (comingFromOutside) {
focusManager?.focusFirst()
}
}
const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
switch (e.key) {
case 'ArrowRight':
focusManager?.focusNext({ wrap: true })
break
case 'ArrowLeft':
focusManager?.focusPrevious({ wrap: true })
break
case 'Escape':
e.preventDefault()
document.getElementById('reactions-toggle')?.focus()
layoutStore.showReactionsToolbar = false
break
case 'Tab':
if (!e.shiftKey) {
e.preventDefault()
getFirstControlBarFocusable('control-bar')?.focus()
}
break
}
}
return (
<div
role="toolbar"
aria-label={t('toolbar')}
onKeyDown={onKeyDown}
onFocus={onFocus}
>
{children}
</div>
)
}
export const ReactionsToolbar = () => {
const { isOpen } = useReactionsToolbar()
const shouldMount = useDelayUnmount(isOpen, 300)
if (!shouldMount) return null
return (
<Container>
{/* eslint-disable-next-line jsx-a11y/no-autofocus*/}
<FocusScope autoFocus>
<KeyboardNavigation>
<Strip>
{Object.values(Emoji).map((emoji) => (
<ReactionButton key={emoji} emoji={emoji} />
))}
</Strip>
</KeyboardNavigation>
</FocusScope>
</Container>
)
}
@@ -1,5 +0,0 @@
export const ANIMATION_DURATION = 3000
export const ANIMATION_DISTANCE = 300
export const FADE_OUT_THRESHOLD = 0.7
export const REACTION_SPAWN_WIDTH_RATIO = 0.2
export const INITIAL_POSITION = 200
@@ -1,26 +0,0 @@
import { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useSnapshot } from 'valtio'
import { accessibilityStore } from '@/stores/accessibility'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
import { getEmojiLabel } from '../utils'
import { Reaction } from '../types'
export const useAnnounceReaction = (latestReaction: Reaction | undefined) => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { announceReactions } = useSnapshot(accessibilityStore)
const [lastAnnouncedId, setLastAnnouncedId] = useState<string | null>(null)
const announce = useScreenReaderAnnounce()
useEffect(() => {
if (!announceReactions || !latestReaction) return
if (latestReaction.id === lastAnnouncedId) return
const emojiLabel = getEmojiLabel(latestReaction.emoji, t)
const participantName = latestReaction.participantName
announce(t('announce', { name: participantName, emoji: emojiLabel }))
setLastAnnouncedId(latestReaction.id)
}, [announce, latestReaction, lastAnnouncedId, announceReactions, t])
}
@@ -1,56 +0,0 @@
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { reactionsStore } from '@/stores/reactions'
import { NotificationType } from '@/features/notifications/NotificationType'
import { useNotifyParticipants } from '@/features/notifications'
import useRateLimiter from '@/hooks/useRateLimiter'
import { Participant } from 'livekit-client'
import { Emoji } from '../types'
import { ANIMATION_DURATION } from '../constants'
export const useReactions = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { notifyParticipants } = useNotifyParticipants()
const appendReaction = useCallback(
(emoji: Emoji, participant?: Participant) => {
const newReaction = {
id: `${emoji}-${Date.now()}-${Math.random()}`,
emoji,
participantName: participant
? participant.name || participant.identity
: t('you'),
isLocal: !participant,
}
reactionsStore.reactions.push(newReaction)
setTimeout(() => {
const index = reactionsStore.reactions.findIndex(
(r) => r.id === newReaction.id
)
if (index !== -1) reactionsStore.reactions.splice(index, 1)
}, ANIMATION_DURATION)
},
[t]
)
const sendReaction = async (emoji: Emoji) => {
appendReaction(emoji)
await notifyParticipants({
type: NotificationType.ReactionReceived,
additionalData: { data: { emoji } },
})
}
const debouncedSendReaction = useRateLimiter({
callback: sendReaction,
maxCalls: 10,
windowMs: 1000,
})
return {
sendReaction: debouncedSendReaction,
appendReaction,
}
}
@@ -1,13 +0,0 @@
import { useSnapshot } from 'valtio'
import { layoutStore } from '@/stores/layout'
export const useReactionsToolbar = () => {
const layoutSnap = useSnapshot(layoutStore)
return {
isOpen: layoutSnap.showReactionsToolbar,
toggle: () => {
layoutStore.showReactionsToolbar = !layoutSnap.showReactionsToolbar
},
}
}
@@ -1,17 +0,0 @@
export enum Emoji {
THUMBS_UP = 'thumbs-up',
THUMBS_DOWN = 'thumbs-down',
CLAP = 'clapping-hands',
HEART = 'red-heart',
LAUGHING = 'face-with-tears-of-joy',
SURPRISED = 'face-with-open-mouth',
CELEBRATION = 'party-popper',
PLEASE = 'folded-hands',
}
export interface Reaction {
id: string
emoji: Emoji
participantName: string
isLocal: boolean
}
@@ -32,7 +32,6 @@ export const AdminToggle = ({
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isAdminOpen}
aria-expanded={isAdminOpen}
onPress={(e) => {
toggleAdmin()
onPress?.(e)
@@ -2,17 +2,19 @@ import { createPortal } from 'react-dom'
import { useState, useEffect, useMemo } from 'react'
import { Text } from '@/primitives'
import { css } from '@/styled-system/css'
import { Participant } from 'livekit-client'
import { useTranslation } from 'react-i18next'
import { Reaction } from '@/features/rooms/livekit/components/controls/ReactionsToggle'
import { getEmojiLabel } from '@/features/rooms/livekit/utils/reactionUtils'
import { accessibilityStore } from '@/stores/accessibility'
import { useSnapshot } from 'valtio'
import { reactionsStore } from '@/stores/reactions'
import { useAnnounceReaction } from '../hooks/useAnnounceReaction'
import { Reaction } from '../types'
import {
ANIMATION_DISTANCE,
ANIMATION_DURATION,
FADE_OUT_THRESHOLD,
INITIAL_POSITION,
REACTION_SPAWN_WIDTH_RATIO,
} from '../constants'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
export const ANIMATION_DURATION = 3000
export const ANIMATION_DISTANCE = 300
export const FADE_OUT_THRESHOLD = 0.7
export const REACTION_SPAWN_WIDTH_RATIO = 0.2
export const INITIAL_POSITION = 200
interface FloatingReactionProps {
emoji: string
@@ -77,7 +79,7 @@ export function FloatingReaction({
>
<img
src={`/assets/reactions/${emoji}.png`}
alt=""
alt={''}
className={css({
height: '50px',
})}
@@ -114,7 +116,14 @@ export function FloatingReaction({
)
}
const ReactionPortal = ({ reaction }: { reaction: Reaction }) => {
export function ReactionPortal({
emoji,
participant,
}: {
emoji: string
participant: Participant
}) {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const speed = useMemo(() => Math.random() * 1.5 + 0.5, [])
const scale = useMemo(() => Math.max(Math.random() + 0.5, 1), [])
return createPortal(
@@ -129,27 +138,51 @@ const ReactionPortal = ({ reaction }: { reaction: Reaction }) => {
})}
>
<FloatingReaction
emoji={reaction.emoji}
emoji={emoji}
speed={speed}
scale={scale}
name={reaction.participantName}
isLocal={reaction.isLocal}
name={participant?.isLocal ? t('you') : participant.name}
isLocal={participant?.isLocal}
/>
</div>,
document.body
)
}
export const ReactionPortals = () => {
const { reactions } = useSnapshot(reactionsStore)
const latestReaction = reactions.at(-1)
export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { announceReactions } = useSnapshot(accessibilityStore)
const [lastAnnouncedId, setLastAnnouncedId] = useState<number | null>(null)
const announce = useScreenReaderAnnounce()
useAnnounceReaction(latestReaction)
const latestReaction =
reactions.length > 0 ? reactions[reactions.length - 1] : undefined
useEffect(() => {
if (!announceReactions) {
return
}
if (!latestReaction) return
const isNewReaction = latestReaction.id !== lastAnnouncedId
if (!isNewReaction) return
const emojiLabel = getEmojiLabel(latestReaction.emoji, t)
const participantName = latestReaction.participant?.isLocal
? t('you')
: latestReaction.participant?.name?.trim() ||
t('someone', { defaultValue: 'Someone' })
announce(t('announce', { name: participantName, emoji: emojiLabel }))
setLastAnnouncedId(latestReaction.id)
}, [announce, latestReaction, lastAnnouncedId, announceReactions, t])
return (
<>
{reactions.map((instance) => (
<ReactionPortal key={instance.id} reaction={instance} />
<ReactionPortal
key={instance.id}
emoji={instance.emoji}
participant={instance.participant}
/>
))}
</>
)
@@ -14,7 +14,6 @@ import { Admin } from './Admin'
import { Tools } from './Tools'
import { Info } from './Info'
import { HStack } from '@/styled-system/jsx'
import { useReactionsToolbar } from '@/features/reactions/hooks/useReactionsToolbar'
type StyledSidePanelProps = {
title: string
@@ -26,7 +25,6 @@ type StyledSidePanelProps = {
isSubmenu: boolean
onBack: () => void
backButtonLabel: string
isReactionToolbarOpen?: boolean
}
const StyledSidePanel = ({
@@ -35,7 +33,6 @@ const StyledSidePanel = ({
children,
onClose,
isClosed,
isReactionToolbarOpen,
closeButtonTooltip,
isSubmenu = false,
onBack,
@@ -54,23 +51,17 @@ const StyledSidePanel = ({
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
margin: 'var(--sizes-room-side-panel-margin)',
marginLeft: 0,
marginBottom: 0,
margin: '1.5rem 1.5rem 1.5rem 0',
padding: 0,
gap: 0,
right: 0,
top: 0,
width: 'var(--sizes-room-side-panel)',
bottom: '80px',
width: '360px',
transition: '.5s cubic-bezier(.4,0,.2,1) 5ms',
})}
style={{
transform: isClosed
? 'translateX(calc(var(--sizes-room-side-panel) + var(--sizes-room-side-panel-margin)))'
: 'none',
bottom: isReactionToolbarOpen
? 'calc( var(--sizes-room-control-bar) + var(--sizes-room-reaction-toolbar-height) + calc(var(--lk-grid-gap) / 2))'
: 'var(--sizes-room-control-bar)',
transform: isClosed ? 'translateX(calc(360px + 1.5rem))' : 'none',
}}
aria-hidden={isClosed}
aria-label={ariaLabel}
@@ -160,8 +151,6 @@ export const SidePanel = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
const title = t(`heading.${activeSubPanelId || activePanelId}`)
const { isOpen: isReactionToolbarOpen } = useReactionsToolbar()
return (
<StyledSidePanel
title={title}
@@ -175,7 +164,6 @@ export const SidePanel = () => {
})}
isClosed={!isSidePanelOpen}
isSubmenu={isSubPanelOpen}
isReactionToolbarOpen={isReactionToolbarOpen}
backButtonLabel={t('backToTools')}
onBack={() => (layoutStore.activeSubPanelId = null)}
>
@@ -37,7 +37,6 @@ export const ChatToggle = ({
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isChatOpen}
aria-expanded={isChatOpen}
onPress={(e) => {
toggleChat()
onPress?.(e)
@@ -27,7 +27,6 @@ export const InfoToggle = ({
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isInfoOpen}
aria-expanded={isInfoOpen}
onPress={(e) => {
toggleInfo()
onPress?.(e)
@@ -48,7 +48,6 @@ export const ParticipantsToggle = ({
count: announcedCount,
})}.`}
isSelected={isParticipantsOpen}
aria-expanded={isParticipantsOpen}
onPress={(e) => {
toggleParticipants()
onPress?.(e)
@@ -77,7 +76,6 @@ export const ParticipantsToggle = ({
zIndex: 1,
userSelect: 'none',
})}
aria-hidden={true}
>
{numParticipants < 100 ? (
numParticipants || 1
@@ -0,0 +1,161 @@
import { useTranslation } from 'react-i18next'
import { RiEmotionLine } from '@remixicon/react'
import { useState, useRef } from 'react'
import { css } from '@/styled-system/css'
import { useRoomContext } from '@livekit/components-react'
import { ToggleButton, Button } from '@/primitives'
import { NotificationType } from '@/features/notifications/NotificationType'
import { NotificationPayload } from '@/features/notifications/NotificationPayload'
import {
ANIMATION_DURATION,
ReactionPortals,
} from '@/features/rooms/livekit/components/ReactionPortal'
import { getEmojiLabel } from '@/features/rooms/livekit/utils/reactionUtils'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import {
Popover as RACPopover,
Dialog,
DialogTrigger,
} from 'react-aria-components'
import { FocusScope } from '@react-aria/focus'
import { Participant } from 'livekit-client'
import useRateLimiter from '@/hooks/useRateLimiter'
// eslint-disable-next-line react-refresh/only-export-components
export enum Emoji {
THUMBS_UP = 'thumbs-up',
THUMBS_DOWN = 'thumbs-down',
CLAP = 'clapping-hands',
HEART = 'red-heart',
LAUGHING = 'face-with-tears-of-joy',
SURPRISED = 'face-with-open-mouth',
CELEBRATION = 'party-popper',
PLEASE = 'folded-hands',
}
export interface Reaction {
id: number
emoji: string
participant: Participant
}
export const ReactionsToggle = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const [reactions, setReactions] = useState<Reaction[]>([])
const instanceIdRef = useRef(0)
const room = useRoomContext()
const [isOpen, setIsOpen] = useState(false)
useRegisterKeyboardShortcut({
id: 'reaction',
handler: () => setIsOpen((prev) => !prev),
})
const sendReaction = async (emoji: string) => {
const encoder = new TextEncoder()
const payload: NotificationPayload = {
type: NotificationType.ReactionReceived,
data: {
emoji: emoji,
},
}
const data = encoder.encode(JSON.stringify(payload))
await room.localParticipant.publishData(data, { reliable: true })
const newReaction = {
id: instanceIdRef.current++,
emoji,
participant: room.localParticipant,
}
setReactions((prev) => [...prev, newReaction])
// Remove this reaction after animation
setTimeout(() => {
setReactions((prev) =>
prev.filter((instance) => instance.id !== newReaction.id)
)
}, ANIMATION_DURATION)
}
const debouncedSendReaction = useRateLimiter({
callback: sendReaction,
maxCalls: 10,
windowMs: 1000,
})
return (
<>
<div className={css({ position: 'relative' })}>
<DialogTrigger isOpen={isOpen} onOpenChange={setIsOpen}>
<ToggleButton
square
variant="primaryDark"
aria-label={t('button')}
tooltip={t('button')}
isSelected={isOpen}
onChange={setIsOpen}
>
<RiEmotionLine />
</ToggleButton>
<RACPopover
placement="top"
offset={8}
isNonModal
shouldCloseOnInteractOutside={() => false}
className={css({
borderRadius: '8px',
padding: '0.35rem',
backgroundColor: 'primaryDark.50',
'&[data-entering]': {
animation: 'fade 200ms ease',
},
'&[data-exiting]': {
animation: 'fade 200ms ease-in reverse',
},
})}
>
<Dialog className={css({ outline: 'none' })}>
{/* eslint-disable-next-line jsx-a11y/no-autofocus -- FocusScope autoFocus is programmatic focus for overlays, not the HTML autofocus attribute */}
<FocusScope contain autoFocus restoreFocus>
<div
role="toolbar"
aria-orientation="horizontal"
aria-label={t('button')}
className={css({
display: 'flex',
gap: '0.5rem',
})}
>
{Object.values(Emoji).map((emoji, index) => (
<Button
key={index}
onPress={() => debouncedSendReaction(emoji)}
aria-label={t('send', { emoji: getEmojiLabel(emoji, t) })}
variant="primaryTextDark"
size="sm"
square
data-attr={`send-reaction-${emoji}`}
>
<img
src={`/assets/reactions/${emoji}.png`}
alt=""
className={css({
width: '28px',
height: '28px',
pointerEvents: 'none',
userSelect: 'none',
})}
/>
</Button>
))}
</div>
</FocusScope>
</Dialog>
</RACPopover>
</DialogTrigger>
</div>
<ReactionPortals reactions={reactions} />
</>
)
}
@@ -34,7 +34,6 @@ export const ToolsToggle = ({
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isToolsOpen}
aria-expanded={isToolsOpen}
onPress={(e) => {
toggleTools()
onPress?.(e)
@@ -7,9 +7,9 @@ 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 { PaginationIndicator } from '../controls/PaginationIndicator'
import { useGridLayout } from '../../hooks/useGridLayout'
import { PaginationControl } from '../controls/PaginationControl'
/** @public */
export interface GridLayoutProps
@@ -4,8 +4,6 @@ import * as React from 'react'
import { MobileControlBar } from './MobileControlBar'
import { DesktopControlBar } from './DesktopControlBar'
import { useIsMobile } from '@/utils/useIsMobile'
import { ReactionsToolbar } from '@/features/reactions/components/toolbar/ReactionsToolbar'
import { css } from '@/styled-system/css'
export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
onDeviceError?: (error: { source: Track.Source; error: Error }) => void
@@ -18,32 +16,10 @@ export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
export function ControlBar({ onDeviceError }: ControlBarProps) {
const isMobile = useIsMobile()
return (
<div
className={css({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
})}
>
<ReactionsToolbar />
<div
id="control-bar"
className={css({
zIndex: 100,
})}
>
{isMobile ? (
<MobileControlBar onDeviceError={onDeviceError} />
) : (
<DesktopControlBar onDeviceError={onDeviceError} />
)}
</div>
</div>
)
if (isMobile) {
return <MobileControlBar onDeviceError={onDeviceError} />
}
return <DesktopControlBar onDeviceError={onDeviceError} />
}
export type ControlBarAuxProps = Pick<ControlBarProps, 'onDeviceError'>
@@ -3,6 +3,7 @@ import { ControlBarAuxProps } from './ControlBar'
import { css } from '@/styled-system/css'
import { LeaveButton } from '../../components/controls/LeaveButton'
import { Track } from 'livekit-client'
import { ReactionsToggle } from '../../components/controls/ReactionsToggle'
import { HandToggle } from '../../components/controls/HandToggle'
import { ScreenShareToggle } from '../../components/controls/ScreenShareToggle'
import { SubtitlesToggle } from '../../components/controls/SubtitlesToggle'
@@ -14,8 +15,6 @@ import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKey
import { useFullScreen } from '../../hooks/useFullScreen'
import { VideoDeviceControl } from '../../components/controls/Device/VideoDeviceControl'
import { AudioDevicesControl } from '../../components/controls/Device/AudioDevicesControl'
import { ReactionsToggle } from '@/features/reactions/components/ReactionsToggle'
import { ControlBarRegion } from '@/features/layout/components/ControlBarRegion'
export function DesktopControlBar({
onDeviceError,
@@ -49,7 +48,11 @@ export function DesktopControlBar({
className={css({
width: '100vw',
display: 'flex',
position: 'absolute',
padding: '1.125rem',
bottom: 0,
left: 0,
right: 0,
})}
>
<div
@@ -62,7 +65,15 @@ export function DesktopControlBar({
marginLeft: '0.5rem',
})}
/>
<ControlBarRegion>
<div
className={css({
flex: '1 1 33%',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
gap: '0.65rem',
})}
>
<AudioDevicesControl
onDeviceError={(error) =>
onDeviceError?.({ source: Track.Source.Microphone, error })
@@ -86,7 +97,7 @@ export function DesktopControlBar({
<OptionsButton />
<LeaveButton />
<StartMediaButton />
</ControlBarRegion>
</div>
<MoreOptions parentElement={desktopControlBarEl} />
</div>
)
@@ -25,7 +25,6 @@ import { useConfig } from '@/api/useConfig'
import { AudioDevicesControl } from '../../components/controls/Device/AudioDevicesControl'
import { VideoDeviceControl } from '../../components/controls/Device/VideoDeviceControl'
import { useSettingsDialog } from '@/features/settings/hook/useSettingsDialog'
import { ControlBarRegion } from '@/features/layout/components/ControlBarRegion'
export function MobileControlBar({
onDeviceError,
@@ -44,11 +43,21 @@ export function MobileControlBar({
className={css({
width: '100vw',
display: 'flex',
position: 'absolute',
padding: '1.125rem',
justifyContent: 'center',
bottom: 0,
left: 0,
right: 0,
})}
>
<ControlBarRegion mobile>
<div
className={css({
display: 'flex',
justifyContent: 'space-between',
width: '330px',
})}
>
<LeaveButton />
<AudioDevicesControl
onDeviceError={(error) =>
@@ -73,7 +82,7 @@ export function MobileControlBar({
>
<RiMore2Line />
</Button>
</ControlBarRegion>
</div>
</div>
<ResponsiveMenu
isOpened={isMenuOpened}
@@ -73,7 +73,7 @@ export const MoreOptions = ({
}) => {
const { width: parentWidth } = useSize(parentElement)
return (
<nav
<div
className={css({
display: 'flex',
justifyContent: 'flex-end',
@@ -88,6 +88,6 @@ export const MoreOptions = ({
) : (
<LateralMenu />
)}
</nav>
</div>
)
}
@@ -20,9 +20,13 @@ import {
import { useTranslation } from 'react-i18next'
import { ControlBar } from './ControlBar/ControlBar'
import { styled } from '@/styled-system/jsx'
import { cva } from '@/styled-system/css'
import { MainNotificationToast } from '@/features/notifications/MainNotificationToast'
import { FocusLayout } from '../components/FocusLayout'
import { ParticipantTile } from '../components/ParticipantTile'
import { SidePanel } from '../components/SidePanel'
import { useSidePanel } from '../hooks/useSidePanel'
import { RecordingProvider } from '@/features/recording'
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
import { useConnectionObserver } from '../hooks/useConnectionObserver'
@@ -33,13 +37,35 @@ import { useSettingsDialog } from '@/features/settings'
import { SettingsDialogExtendedKey } from '@/features/settings/type'
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
import { Subtitles } from '@/features/subtitle/component/Subtitles'
import { CarouselLayout } from '../components/layout/CarouselLayout'
import { GridLayout } from '../components/layout/GridLayout'
import { IsIdleDisconnectModal } from '../components/IsIdleDisconnectModal'
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
import { ReactionPortals } from '@/features/reactions/components/ReactionPortals'
import { CarouselLayout } from '@/features/layout/components/CarouselLayout'
import { GridLayout } from '@/features/layout/components/GridLayout'
import { RoomContentArea } from '@/features/layout/components/RoomContentArea'
const LayoutWrapper = styled(
'div',
cva({
base: {
position: 'relative',
display: 'flex',
width: '100%',
transition: 'height .5s cubic-bezier(0.4,0,0.2,1) 5ms',
},
variants: {
areSubtitlesOpen: {
true: {
height: 'calc(100% - 12rem)',
},
false: {
height: '100%',
},
},
},
})
)
/**
* @public
@@ -227,6 +253,9 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
])
/* eslint-enable react-hooks/exhaustive-deps */
const { isSidePanelOpen } = useSidePanel()
const { areSubtitlesOpen } = useSubtitles()
const [isShareErrorVisible, setIsShareErrorVisible] = useState(false)
return (
@@ -247,35 +276,57 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
onClose={() => setIsShareErrorVisible(false)}
/>
<IsIdleDisconnectModal />
<RoomContentArea>
{!focusTrack ? (
<div
// todo - extract these magic values into constant
style={{
position: 'absolute',
inset: isSidePanelOpen
? `var(--lk-grid-gap) calc(358px + 3rem) calc(80px + var(--lk-grid-gap)) 16px`
: `var(--lk-grid-gap) var(--lk-grid-gap) calc(80px + var(--lk-grid-gap))`,
transition: 'inset .5s cubic-bezier(0.4,0,0.2,1) 5ms',
maxHeight: '100%',
}}
>
<LayoutWrapper areSubtitlesOpen={areSubtitlesOpen}>
<div
className="lk-grid-layout-wrapper"
style={{ height: 'auto' }}
style={{
display: 'flex',
position: 'relative',
width: '100%',
}}
>
<GridLayout tracks={tracks} style={{ padding: 0 }}>
<ParticipantTile />
</GridLayout>
</div>
) : (
<div
className="lk-focus-layout-wrapper"
style={{ height: 'auto' }}
>
<FocusLayoutContainer style={{ padding: 0 }}>
<CarouselLayout
tracks={carouselTracks}
style={{
minWidth: '200px',
}}
{!focusTrack ? (
<div
className="lk-grid-layout-wrapper"
style={{ height: 'auto' }}
>
<ParticipantTile />
</CarouselLayout>
{focusTrack && <FocusLayout trackRef={focusTrack} />}
</FocusLayoutContainer>
<GridLayout tracks={tracks} style={{ padding: 0 }}>
<ParticipantTile />
</GridLayout>
</div>
) : (
<div
className="lk-focus-layout-wrapper"
style={{ height: 'auto' }}
>
<FocusLayoutContainer style={{ padding: 0 }}>
<CarouselLayout
tracks={carouselTracks}
style={{
minWidth: '200px',
}}
>
<ParticipantTile />
</CarouselLayout>
{focusTrack && <FocusLayout trackRef={focusTrack} />}
</FocusLayoutContainer>
</div>
)}
</div>
)}
</RoomContentArea>
</LayoutWrapper>
<Subtitles />
<MainNotificationToast />
</div>
<ControlBar
onDeviceError={(e) => {
console.error(e)
@@ -295,7 +346,6 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
<ConnectionStateToast />
<RecordingProvider />
<SettingsDialogProvider />
<ReactionPortals />
</div>
)
}
@@ -1,8 +1,7 @@
import { useTranslation } from 'react-i18next'
import { Emoji } from './types'
export const getEmojiLabel = (
emoji: string | Emoji,
emoji: string,
t: ReturnType<typeof useTranslation>['t']
) => {
const emojiLabels: Record<string, string> = {
@@ -1,19 +0,0 @@
import { useEffect, useState } from 'react'
export const useDelayUnmount = (isMounted: boolean, delayTime: number) => {
const [shouldRender, setShouldRender] = useState(false)
useEffect(() => {
let timeoutId: ReturnType<typeof setTimeout> | undefined
if (isMounted && !shouldRender) {
setShouldRender(true)
} else if (!isMounted && shouldRender) {
timeoutId = setTimeout(() => setShouldRender(false), delayTime)
}
return () => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId)
}
}
}, [isMounted, delayTime, shouldRender])
return shouldRender
}
+1 -2
View File
@@ -166,7 +166,6 @@
"countdownAnnouncement": "Das Meeting endet in {{duration}}."
},
"controls": {
"region": "Anrufsteuerung",
"microphone": "Mikrofon",
"camera": "Kamera",
"chat": {
@@ -216,7 +215,7 @@
"moreOptions": "Weitere Optionen",
"reactions": {
"button": "Reaktion senden",
"toolbar": "Reaktion senden",
"send": "Reaktion {{emoji}} senden",
"announce": "{{name}} : {{emoji}}",
"you": "du",
"emojis": {
+1 -2
View File
@@ -166,7 +166,6 @@
"countdownAnnouncement": "Call ends in {{duration}}."
},
"controls": {
"region": "Call controls",
"microphone": "Microphone",
"camera": "Camera",
"chat": {
@@ -216,7 +215,7 @@
"moreOptions": "More options",
"reactions": {
"button": "Send reaction",
"toolbar": "Send reaction",
"send": "Send reaction {{emoji}}",
"announce": "{{name}} : {{emoji}}",
"you": "you",
"emojis": {
+1 -2
View File
@@ -166,7 +166,6 @@
"countdownAnnouncement": "L'appel se termine dans {{duration}}."
},
"controls": {
"region": "Commandes pour les appels",
"microphone": "Microphone",
"camera": "Camera",
"chat": {
@@ -216,7 +215,7 @@
"moreOptions": "Plus d'options",
"reactions": {
"button": "Envoyer une réaction",
"toolbar": "Envoyer une réaction",
"send": "Envoyer la réaction {{emoji}}",
"announce": "{{name}} : {{emoji}}",
"you": "vous",
"emojis": {
+1 -2
View File
@@ -166,7 +166,6 @@
"countdownAnnouncement": "Het gesprek eindigt over {{duration}}."
},
"controls": {
"region": "Gespreksbesturing",
"microphone": "Microfoon",
"camera": "Camera",
"chat": {
@@ -216,7 +215,7 @@
"moreOptions": "Meer opties",
"reactions": {
"button": "Stuur reactie",
"toolbar": "Stuur reactie",
"send": "Stuur reactie {{emoji}}",
"announce": "{{name}} : {{emoji}}",
"you": "U",
"emojis": {
@@ -44,13 +44,6 @@ export const buttonRecipe = cva({
paddingY: 'var(--square-padding)',
},
},
round: {
true: {
borderRadius: '50%',
paddingX: 'var(--square-padding)',
paddingY: 'var(--square-padding)',
},
},
variant: {
primary: {
backgroundColor: 'primary.800',
-2
View File
@@ -10,7 +10,6 @@ type State = {
showSubtitles: boolean
activePanelId: PanelId | null
activeSubPanelId: SubPanelId | null
showReactionsToolbar: boolean
}
export const layoutStore = proxy<State>({
@@ -19,5 +18,4 @@ export const layoutStore = proxy<State>({
showSubtitles: false,
activePanelId: null,
activeSubPanelId: null,
showReactionsToolbar: false,
})
-10
View File
@@ -1,10 +0,0 @@
import { proxy } from 'valtio'
import { Reaction } from '@/features/reactions/types'
type State = {
reactions: Reaction[]
}
export const reactionsStore = proxy<State>({
reactions: [],
})
-6
View File
@@ -1,6 +0,0 @@
export const getFirstControlBarFocusable = (id: string): HTMLElement | null =>
document
.getElementById(id)
?.querySelector(
'input, select, textarea, button, object, a, area[href], [tabindex]'
) ?? null
+62
View File
@@ -0,0 +1,62 @@
.podSecurityContext: &podSecurityContext
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
.securityContext: &securityContext
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
frontend:
podSecurityContext: *podSecurityContext
securityContext: *securityContext
resources:
requests:
cpu: "250m"
memory: "128Mi"
backend:
podSecurityContext: *podSecurityContext
securityContext: *securityContext
resources:
requests:
cpu: "250m"
memory: "600Mi"
summary:
podSecurityContext: *podSecurityContext
securityContext: *securityContext
resources:
requests:
cpu: "250m"
memory: "128Mi"
celeryTranscribe:
podSecurityContext: *podSecurityContext
securityContext: *securityContext
resources:
requests:
cpu: "100m"
memory: "500Mi"
celerySummarize:
podSecurityContext: *podSecurityContext
securityContext: *securityContext
resources:
requests:
cpu: "100m"
memory: "128Mi"
agents:
podSecurityContext: *podSecurityContext
securityContext: *securityContext
resources:
requests:
cpu: "100m"
memory: "256Mi"
@@ -23,6 +23,18 @@ egress:
endpoint: http://minio:9000
force_path_style: true
podSecurityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
loadBalancer:
type: nginx
annotations:
@@ -40,4 +52,7 @@ autoscaling:
maxReplicas: 5
nodeSelector: {}
resources: {}
resources:
requests:
cpu: 5000m
memory: 1024Mi
@@ -28,6 +28,16 @@ livekit:
urls:
- https://meet.127.0.0.1.nip.io/api/v1.0/rooms/webhooks-livekit/
podSecurityContext:
fsGroup: 2000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
loadBalancer:
type: nginx
@@ -6,24 +6,12 @@ _summaryEnvVars: &summaryEnvVars
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY:
secretKeyRef:
name: secret-dev
key: WHISPERX_API_KEY
WHISPERX_BASE_URL:
secretKeyRef:
name: secret-dev
key: WHISPERX_BASE_URL
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL:
secretKeyRef:
name: secret-dev
key: LLM_BASE_URL
LLM_API_KEY:
secretKeyRef:
name: secret-dev
key: LLM_API_KEY
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
@@ -174,7 +162,7 @@ ingressWebhook:
posthog:
ingress:
enabled: false
ingressAssets:
enabled: false
@@ -26,6 +26,18 @@ egress:
endpoint: http://minio:9000
force_path_style: true
podSecurityContext:
fsGroup: 1000
fsGroupChangePolicy: "OnRootMismatch"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
loadBalancer:
type: nginx
annotations:
@@ -43,4 +55,7 @@ autoscaling:
maxReplicas: 5
nodeSelector: {}
resources: {}
resources:
requests:
cpu: 5000m
memory: 1024Mi
@@ -28,6 +28,16 @@ livekit:
urls:
- https://meet.127.0.0.1.nip.io/api/v1.0/rooms/webhooks-livekit/
podSecurityContext:
fsGroup: 2000
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
loadBalancer:
type: nginx
@@ -47,4 +57,4 @@ autoscaling:
targetCPUUtilizationPercentage: 60
nodeSelector: {}
resources: {}
resources: {}
@@ -6,24 +6,12 @@ _summaryEnvVars: &summaryEnvVars
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY:
secretKeyRef:
name: secret-dev
key: WHISPERX_API_KEY
WHISPERX_BASE_URL:
secretKeyRef:
name: secret-dev
key: WHISPERX_BASE_URL
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL:
secretKeyRef:
name: secret-dev
key: LLM_BASE_URL
LLM_API_KEY:
secretKeyRef:
name: secret-dev
key: LLM_API_KEY
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
@@ -124,7 +112,6 @@ backend:
CELERY_ENABLED: True
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
migrate:
command:
- "/bin/sh"
@@ -192,7 +179,7 @@ ingressWebhook:
posthog:
ingress:
enabled: false
ingressAssets:
enabled: false
+3
View File
@@ -37,6 +37,7 @@ releases:
missingFileHandler: Warn
chart: ./meet
values:
- env.d/common.yaml.gotmpl
- env.d/{{ .Environment.Name }}/values.meet.yaml.gotmpl
- env.d/{{ .Environment.Name }}/values.secrets.yaml
secrets:
@@ -48,6 +49,7 @@ releases:
namespace: {{ .Namespace }}
chart: livekit/livekit-server
values:
- env.d/common.yaml.gotmpl
- env.d/{{ .Environment.Name }}/values.livekit.yaml.gotmpl
- env.d/{{ .Environment.Name }}/values.secrets.yaml
secrets:
@@ -59,6 +61,7 @@ releases:
namespace: {{ .Namespace }}
chart: livekit/egress
values:
- env.d/common.yaml.gotmpl
- env.d/{{ .Environment.Name }}/values.egress.yaml.gotmpl
- env.d/{{ .Environment.Name }}/values.secrets.yaml
secrets:
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.agents.shareProcessNamespace }}
{{- with .Values.agents.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.agents.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.backend.shareProcessNamespace }}
{{- with .Values.backend.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.backend.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -30,6 +30,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.backend.shareProcessNamespace }}
{{- with .Values.backend.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.backend.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -30,6 +30,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.backend.shareProcessNamespace }}
{{- with .Values.backend.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.backend.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.celeryBackend.shareProcessNamespace }}
{{- with .Values.celeryBackend.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.celeryBackend.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.celerySummarize.shareProcessNamespace }}
{{- with .Values.celerySummarize.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.celerySummarize.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.celeryTranscribe.shareProcessNamespace }}
{{- with .Values.celeryTranscribe.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.celeryTranscribe.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.frontend.shareProcessNamespace }}
{{- with .Values.frontend.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.frontend.sidecars }}
{{- toYaml . | nindent 8 }}
@@ -31,6 +31,10 @@ spec:
- name: {{ include "meet.secret.dockerconfigjson.name" (dict "fullname" (include "meet.fullname" .) "imageCredentials" $.Values.image.credentials) }}
{{- end }}
shareProcessNamespace: {{ .Values.summary.shareProcessNamespace }}
{{- with .Values.summary.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
{{- with .Values.summary.sidecars }}
{{- toYaml . | nindent 8 }}
+28 -7
View File
@@ -216,9 +216,12 @@ backend:
ttlSecondsAfterFinished: 30
backoffLimit: 2
## @param backend.securityContext Configure backend Pod security context
## @param backend.securityContext Configure backend Container security context
securityContext: null
## @param backend.podSecurityContext Configure backend Pod security context
podSecurityContext: null
## @param backend.envVars Configure backend container environment variables
## @extra backend.envVars.BY_VALUE Example environment variable by setting value directly
## @extra backend.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
@@ -359,9 +362,12 @@ frontend:
## @param frontend.sidecars Add sidecars containers to frontend deployment
sidecars: []
## @param frontend.securityContext Configure frontend Pod security context
## @param frontend.securityContext Configure frontend Container security context
securityContext: null
## @param frontend.podSecurityContext Configure frontend Pod security context
podSecurityContext: null
## @param frontend.envVars Configure frontend container environment variables
## @extra frontend.envVars.BY_VALUE Example environment variable by setting value directly
## @extra frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
@@ -533,9 +539,12 @@ summary:
## @param summary.migrateJobAnnotations Annotations for the migrate job
migrateJobAnnotations: {}
## @param summary.securityContext Configure summary Pod security context
## @param summary.securityContext Configure summary Container security context
securityContext: null
## @param summary.podSecurityContext Configure summary Pod security context
podSecurityContext: null
## @param summary.envVars Configure summary container environment variables
## @extra summary.envVars.BY_VALUE Example environment variable by setting value directly
## @extra summary.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
@@ -646,9 +655,12 @@ celeryBackend:
## @param celeryBackend.migrateJobAnnotations Annotations for the migrate job
migrateJobAnnotations: {}
## @param celeryBackend.securityContext Configure celeryBackend Pod security context
## @param celeryBackend.securityContext Configure celeryBackend Container security context
securityContext: null
## @param celeryBackend.podSecurityContext Configure celeryBackend Pod security context
podSecurityContext: null
## @param celeryBackend.envVars Configure celeryBackend container environment variables
## @extra celeryBackend.envVars.BY_VALUE Example environment variable by setting value directly
## @extra celeryBackend.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
@@ -755,9 +767,12 @@ celeryTranscribe:
## @param celeryTranscribe.migrateJobAnnotations Annotations for the migrate job
migrateJobAnnotations: {}
## @param celeryTranscribe.securityContext Configure celeryTranscribe Pod security context
## @param celeryTranscribe.securityContext Configure celeryTranscribe Container security context
securityContext: null
## @param celeryTranscribe.podSecurityContext Configure celeryTranscribe Pod security context
podSecurityContext: null
## @param celeryTranscribe.envVars Configure celeryTranscribe container environment variables
## @extra celeryTranscribe.envVars.BY_VALUE Example environment variable by setting value directly
## @extra celeryTranscribe.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
@@ -864,9 +879,12 @@ celerySummarize:
## @param celerySummarize.migrateJobAnnotations Annotations for the migrate job
migrateJobAnnotations: {}
## @param celerySummarize.securityContext Configure celerySummarize Pod security context
## @param celerySummarize.securityContext Configure celerySummarize Container security context
securityContext: null
## @param celerySummarize.podSecurityContext Configure celerySummarize Pod security context
podSecurityContext: null
## @param celerySummarize.envVars Configure celerySummarize container environment variables
## @extra celerySummarize.envVars.BY_VALUE Example environment variable by setting value directly
## @extra celerySummarize.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
@@ -963,9 +981,12 @@ agents:
## @param agents.sidecars Add sidecars containers to agents deployment
sidecars: []
## @param agents.securityContext Configure agents Pod security context
## @param agents.securityContext Configure agents Container security context
securityContext: null
## @param agents.podSecurityContext Configure agents Pod security context
podSecurityContext: null
## @param agents.envVars Configure agents container environment variables
## @extra agents.envVars.BY_VALUE Example environment variable by setting value directly
## @extra agents.envVars.FROM_CONFIGMAP.configMapKeyRef.name Name of a ConfigMap when configuring env vars from a ConfigMap
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mail_mjml",
"version": "1.13.0",
"version": "1.12.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mail_mjml",
"version": "1.13.0",
"version": "1.12.0",
"license": "MIT",
"dependencies": {
"@html-to/text-cli": "0.5.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mail_mjml",
"version": "1.13.0",
"version": "1.12.0",
"description": "An util to generate html and text django's templates from mjml templates",
"type": "module",
"dependencies": {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "sdk",
"version": "1.13.0",
"version": "1.12.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sdk",
"version": "1.13.0",
"version": "1.12.0",
"license": "ISC",
"workspaces": [
"./library",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "sdk",
"version": "1.13.0",
"version": "1.12.0",
"author": "",
"license": "ISC",
"description": "",
+2 -2
View File
@@ -1,7 +1,7 @@
[project]
name = "summary"
version = "1.13.0"
version = "1.12.0"
dependencies = [
"fastapi[standard]>=0.105.0",
"uvicorn>=0.24.0",
@@ -13,7 +13,7 @@ dependencies = [
"mutagen==1.47.0",
"openai==2.28.0",
"posthog==7.9.12",
"requests==2.33.0",
"requests==2.32.5",
"sentry-sdk[fastapi, celery]==2.54.0",
"langfuse==4.0.0"
]