Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb3f929138 | |||
| a9b16ed150 | |||
| 4b9a7e05ec | |||
| 6e5a0fb947 | |||
| 4232c0a303 | |||
| a5454e48b7 | |||
| 591706f363 | |||
| 1ea84b6e6c | |||
| c0d101a326 | |||
| 64cfcb6c0f | |||
| cdd69b741a | |||
| f5a87cc210 | |||
| 4d4ddb9ee8 | |||
| 70dbf94f7b | |||
| 0ad37ee6de | |||
| ed4f7dcf6c | |||
| c54773008c | |||
| 6848321bcc | |||
| f161a5cf6a | |||
| 51270571bc | |||
| c6fdeaf1e9 | |||
| f1959cbb3a | |||
| 84cea2f658 | |||
| db1fdb9871 | |||
| a8618239d1 | |||
| 0eb283b75a | |||
| 0104cabc5e | |||
| dbfba564c5 | |||
| 4830206bb2 | |||
| e4f30f926c | |||
| fc61f58596 | |||
| e0021dbb80 | |||
| 83914f8307 | |||
| be54709598 | |||
| 5b76ea492b | |||
| 46934a84d1 | |||
| deb9ab1a1d | |||
| d0fd16d7d2 | |||
| 0b8181e5ce | |||
| 8b2365d5f9 | |||
| 2dd16d1f40 | |||
| fa9484b630 | |||
| 1e0e495cd8 | |||
| c270299179 | |||
| d9a84e5f0f | |||
| 0f64d3cf3a | |||
| d1e008a844 | |||
| 3be5a5afc6 | |||
| 31468a5e7c | |||
| b342b9d526 | |||
| f7e7c3ba22 | |||
| 3902b02691 | |||
| 7ce4390740 | |||
| 94d18cffe4 | |||
| ad0c3eea66 | |||
| 459bbf65a8 |
@@ -0,0 +1,32 @@
|
||||
name: Release Charts
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
# depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
|
||||
# see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cleanup
|
||||
run: rm -rf ./src/helm/extra
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v4
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.6.0
|
||||
with:
|
||||
charts_dir: ./src/helm
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -301,7 +301,40 @@ build-k8s-cluster: ## build the kubernetes cluster using kind
|
||||
./bin/start-kind.sh
|
||||
.PHONY: build-k8s-cluster
|
||||
|
||||
install-secret:
|
||||
if kubectl -n meet get secrets bitwarden-cli-visio; then \
|
||||
echo "Secret already present"; \
|
||||
else \
|
||||
echo "Please provide the following information:"; \
|
||||
read -p "Enter your vaultwarden email login: " LOGIN; \
|
||||
read -p "Enter your vaultwarden password: " PASSWORD; \
|
||||
read -p "Enter your vaultwarden server url: " URL; \
|
||||
echo "\nCreate vaultwarden secret"; \
|
||||
echo "apiVersion: v1" > /tmp/secret.yaml; \
|
||||
echo "kind: Secret" >> /tmp/secret.yaml; \
|
||||
echo "metadata:" >> /tmp/secret.yaml; \
|
||||
echo " name: bitwarden-cli-visio" >> /tmp/secret.yaml; \
|
||||
echo " namespace: meet" >> /tmp/secret.yaml; \
|
||||
echo "type: Opaque" >> /tmp/secret.yaml; \
|
||||
echo "stringData:" >> /tmp/secret.yaml; \
|
||||
echo " BW_HOST: $$URL" >> /tmp/secret.yaml; \
|
||||
echo " BW_PASSWORD: $$PASSWORD" >> /tmp/secret.yaml; \
|
||||
echo " BW_USERNAME: $$LOGIN" >> /tmp/secret.yaml; \
|
||||
kubectl -n meet apply -f /tmp/secret.yaml;\
|
||||
rm -f /tmp/secret.yaml; \
|
||||
helm repo add external-secrets https://charts.external-secrets.io; \
|
||||
helm upgrade --install external-secrets \
|
||||
external-secrets/external-secrets \
|
||||
-n meet \
|
||||
--create-namespace \
|
||||
--set installCRDs=true; \
|
||||
fi
|
||||
.PHONY: build-k8s-cluster
|
||||
|
||||
start-tilt: ## start the kubernetes cluster using kind
|
||||
tilt up -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 -f ./bin/Tiltfile
|
||||
.PHONY: build-k8s-cluster
|
||||
|
||||
@@ -118,6 +118,8 @@ $ make build-k8s-cluster
|
||||
Once the Kubernetes cluster is ready, start the application stack locally:
|
||||
```shell
|
||||
$ make start-tilt
|
||||
or
|
||||
$ make start-tilt-keycloak # start stack without Pro Connect, use keycloak
|
||||
```
|
||||
These commands set up and run your application environment using Tilt for local Kubernetes development.
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ docker_build(
|
||||
]
|
||||
)
|
||||
|
||||
k8s_yaml(local('cd ../src/helm && helmfile -n meet -e dev template .'))
|
||||
k8s_yaml(local('cd ../src/helm && helmfile -n meet -e ${DEV_ENV:-dev} template .'))
|
||||
|
||||
migration = '''
|
||||
set -eu
|
||||
|
||||
+405
-405
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
replicaCount: 1
|
||||
terminationGracePeriodSeconds: 18000
|
||||
|
||||
livekit:
|
||||
keys:
|
||||
devkey: secret
|
||||
log_level: debug
|
||||
rtc:
|
||||
use_external_ip: false
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
tcp_port: 7881
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
password: pass
|
||||
turn:
|
||||
enabled: true
|
||||
udp_port: 443
|
||||
domain: livekit.127.0.0.1.nip.io
|
||||
loadBalancerAnnotations: {}
|
||||
|
||||
|
||||
loadBalancer:
|
||||
type: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
tls:
|
||||
- hosts:
|
||||
- livekit.127.0.0.1.nip.io
|
||||
secretName: livekit-dinum-cert
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
targetCPUUtilizationPercentage: 60
|
||||
|
||||
nodeSelector: {}
|
||||
resources: {}
|
||||
@@ -0,0 +1,107 @@
|
||||
image:
|
||||
repository: lasuite/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
backend:
|
||||
replicas: 1
|
||||
envVars:
|
||||
DJANGO_CSRF_TRUSTED_ORIGINS: https://meet.127.0.0.1.nip.io,http://meet.127.0.0.1.nip.io
|
||||
DJANGO_CONFIGURATION: Production
|
||||
DJANGO_ALLOWED_HOSTS: meet.127.0.0.1.nip.io
|
||||
DJANGO_SECRET_KEY: AgoodOrAbadKey
|
||||
DJANGO_SETTINGS_MODULE: meet.settings
|
||||
DJANGO_SILENCED_SYSTEM_CHECKS: security.W004, security.W008
|
||||
DJANGO_SUPERUSER_PASSWORD: admin
|
||||
DJANGO_EMAIL_HOST: "mailcatcher"
|
||||
DJANGO_EMAIL_PORT: 1025
|
||||
DJANGO_EMAIL_USE_SSL: False
|
||||
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/certs
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/auth
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/token
|
||||
OIDC_OP_USER_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/session/end
|
||||
OIDC_RP_CLIENT_ID: meet
|
||||
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email"
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://meet.127.0.0.1.nip.io
|
||||
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
||||
OIDC_VERIFY_SSL: False
|
||||
LOGIN_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
|
||||
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
||||
DB_HOST: postgresql
|
||||
DB_NAME: meet
|
||||
DB_USER: dinum
|
||||
DB_PASSWORD: pass
|
||||
DB_PORT: 5432
|
||||
POSTGRES_DB: meet
|
||||
POSTGRES_USER: dinum
|
||||
POSTGRES_PASSWORD: pass
|
||||
REDIS_URL: redis://default:pass@redis-master:6379/1
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
LIVEKIT_API_SECRET: secret
|
||||
LIVEKIT_API_KEY: devkey
|
||||
LIVEKIT_API_URL: https://livekit.127.0.0.1.nip.io/
|
||||
ALLOW_UNREGISTERED_ROOMS: False
|
||||
FRONTEND_SILENCE_LIVEKIT_DEBUG: False
|
||||
FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041'}"
|
||||
|
||||
|
||||
migrate:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py migrate --no-input &&
|
||||
python manage.py create_demo --force
|
||||
restartPolicy: Never
|
||||
|
||||
command:
|
||||
- "gunicorn"
|
||||
- "-c"
|
||||
- "/usr/local/etc/gunicorn/meet.py"
|
||||
- "meet.wsgi:application"
|
||||
- "--reload"
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py createsuperuser --email admin@example.com --password admin
|
||||
restartPolicy: Never
|
||||
|
||||
frontend:
|
||||
envVars:
|
||||
VITE_PORT: 8080
|
||||
VITE_HOST: 0.0.0.0
|
||||
VITE_API_BASE_URL: https://meet.127.0.0.1.nip.io/
|
||||
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: lasuite/meet-frontend
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: meet.127.0.0.1.nip.io
|
||||
|
||||
ingressAdmin:
|
||||
enabled: true
|
||||
host: meet.127.0.0.1.nip.io
|
||||
|
||||
posthog:
|
||||
ingress:
|
||||
enabled: false
|
||||
ingressAssets:
|
||||
enabled: false
|
||||
|
||||
summary:
|
||||
replicas: 0
|
||||
|
||||
celery:
|
||||
replicas: 0
|
||||
@@ -0,0 +1,7 @@
|
||||
auth:
|
||||
username: dinum
|
||||
password: pass
|
||||
database: meet
|
||||
tls:
|
||||
enabled: true
|
||||
autoGenerated: true
|
||||
@@ -0,0 +1,3 @@
|
||||
auth:
|
||||
password: pass
|
||||
architecture: standalone
|
||||
@@ -0,0 +1,226 @@
|
||||
# Installation on a k8s cluster
|
||||
|
||||
This document is a step-by-step guide that describes how to install Visio on a k8s cluster without AI features.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- k8s cluster with an nginx-ingress controller
|
||||
- an OIDC provider (if you don't have one, we will provide an example)
|
||||
- a LiveKit server (if you don't have one, we will provide an example)
|
||||
- a PostgreSQL server (if you don't have one, we will provide an example)
|
||||
- a Memcached server (if you don't have one, we will provide an example)
|
||||
|
||||
### Test cluster
|
||||
|
||||
If you do not have a test cluster, you can install everything on a local kind cluster. In this case, the simplest way is to use our script **bin/start-kind.sh**.
|
||||
|
||||
To be able to use the script, you will need to install:
|
||||
|
||||
- Docker (https://docs.docker.com/desktop/)
|
||||
- Kind (https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
|
||||
- Mkcert (https://github.com/FiloSottile/mkcert#installation)
|
||||
- Helm (https://helm.sh/docs/intro/quickstart/#install-helm)
|
||||
|
||||
```
|
||||
$ ./bin/start-kind.sh
|
||||
0. Create ca
|
||||
The local CA is already installed in the system trust store! 👍
|
||||
The local CA is already installed in the Firefox and/or Chrome/Chromium trust store! 👍
|
||||
|
||||
|
||||
Created a new certificate valid for the following names 📜
|
||||
- "127.0.0.1.nip.io"
|
||||
- "*.127.0.0.1.nip.io"
|
||||
|
||||
Reminder: X.509 wildcards only go one level deep, so this won't match a.b.127.0.0.1.nip.io ℹ️
|
||||
|
||||
The certificate is at "./127.0.0.1.nip.io+1.pem" and the key at "./127.0.0.1.nip.io+1-key.pem" ✅
|
||||
|
||||
It will expire on 23 March 2027 🗓
|
||||
|
||||
1. Create registry container unless it already exists
|
||||
2. Create kind cluster with containerd registry config dir enabled
|
||||
Creating cluster "visio" ...
|
||||
✓ Ensuring node image (kindest/node:v1.27.3) 🖼
|
||||
✓ Preparing nodes 📦
|
||||
✓ Writing configuration 📜
|
||||
✓ Starting control-plane 🕹️
|
||||
✓ Installing CNI 🔌
|
||||
✓ Installing StorageClass 💾
|
||||
Set kubectl context to "kind-visio"
|
||||
You can now use your cluster with:
|
||||
|
||||
kubectl cluster-info --context kind-visio
|
||||
|
||||
Thanks for using kind! 😊
|
||||
3. Add the registry config to the nodes
|
||||
4. Connect the registry to the cluster network if not already connected
|
||||
5. Document the local registry
|
||||
configmap/local-registry-hosting created
|
||||
Warning: resource configmaps/coredns is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
|
||||
configmap/coredns configured
|
||||
deployment.apps/coredns restarted
|
||||
6. Install ingress-nginx
|
||||
namespace/ingress-nginx created
|
||||
serviceaccount/ingress-nginx created
|
||||
serviceaccount/ingress-nginx-admission created
|
||||
role.rbac.authorization.k8s.io/ingress-nginx created
|
||||
role.rbac.authorization.k8s.io/ingress-nginx-admission created
|
||||
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
|
||||
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
|
||||
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
|
||||
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
|
||||
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
|
||||
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
|
||||
configmap/ingress-nginx-controller created
|
||||
service/ingress-nginx-controller created
|
||||
service/ingress-nginx-controller-admission created
|
||||
deployment.apps/ingress-nginx-controller created
|
||||
job.batch/ingress-nginx-admission-create created
|
||||
job.batch/ingress-nginx-admission-patch created
|
||||
ingressclass.networking.k8s.io/nginx created
|
||||
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
|
||||
secret/mkcert created
|
||||
deployment.apps/ingress-nginx-controller patched
|
||||
7. Setup namespace
|
||||
namespace/meet created
|
||||
Context "kind-visio" modified.
|
||||
secret/mkcert created
|
||||
$ kind get clusters
|
||||
visio
|
||||
$ kubectl -n ingress-nginx get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
ingress-nginx-admission-create-jgnc9 0/1 Completed 0 2m44s
|
||||
ingress-nginx-admission-patch-wrt47 0/1 Completed 0 2m44s
|
||||
ingress-nginx-controller-57c548c4cd-9xwt6 1/1 Running 0 2m44s
|
||||
```
|
||||
When your k8s cluster is ready, you can start the deployment. This cluster is special because it uses the *.127.0.0.1.nip.io domain and mkcert certificates to have full HTTPS support and easy domain name management.
|
||||
|
||||
Please remember that *.127.0.0.1.nip.io will always resolve to 127.0.0.1, except in the k8s cluster where we configure CoreDNS to answer with the ingress-nginx service IP.
|
||||
|
||||
## Preparation
|
||||
|
||||
### What will you use to authenticate your users ?
|
||||
|
||||
Visio uses OIDC, so if you already have an OIDC provider, obtain the necessary information to use it. In the next step, we will see how to configure Django (and thus Visio) to use it. If you do not have a provider, we will show you how to deploy a local Keycloak instance (this is not a production deployment, just a demo).
|
||||
|
||||
```
|
||||
$ kubectl create namespace meet
|
||||
$ kubectl config set-context --current --namespace=meet
|
||||
$ helm install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak -f examples/keycloak.values.yaml
|
||||
$ #wait until
|
||||
$ kubectl get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
keycloak-0 1/1 Running 0 6m48s
|
||||
keycloak-postgresql-0 1/1 Running 0 6m48s
|
||||
```
|
||||
|
||||
From here the important informations you will need are :
|
||||
|
||||
```
|
||||
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/certs
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/auth
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/token
|
||||
OIDC_OP_USER_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/session/end
|
||||
OIDC_RP_CLIENT_ID: meet
|
||||
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email"
|
||||
```
|
||||
|
||||
You can find these values in **examples/keycloak.values.yaml**
|
||||
|
||||
### Find livekit server connexion values
|
||||
|
||||
Visio use livekit for streaming part so if you have a livekit provider, obtain the necessary information to use it. If you do not have a provider, you can install a livekit testing environment as follow :
|
||||
|
||||
Livekit need a redis (and meet too) so we will start by deploying a redis :
|
||||
|
||||
```
|
||||
$ helm install redis oci://registry-1.docker.io/bitnamicharts/redis -f examples/redis.values.yaml
|
||||
$ kubectl get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
keycloak-0 1/1 Running 0 26m
|
||||
keycloak-postgresql-0 1/1 Running 0 26m
|
||||
redis-master-0 1/1 Running 0 35s
|
||||
```
|
||||
When the redis is ready we can deploy livekit-server.
|
||||
|
||||
```
|
||||
$ helm repo add livekit https://helm.livekit.io
|
||||
$ helm repo update
|
||||
$ helm install livekit livekit/livekit-server -f examples/livekit.values.yaml
|
||||
$ kubectl get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
keycloak-0 1/1 Running 0 30m
|
||||
keycloak-postgresql-0 1/1 Running 0 30m
|
||||
livekit-livekit-server-5c5fb87f7f-ct6x5 1/1 Running 0 7s
|
||||
redis-master-0 1/1 Running 0 4m30s
|
||||
$ curl https://livekit.127.0.0.1.nip.io
|
||||
OK
|
||||
```
|
||||
|
||||
From here important informations you will need are :
|
||||
|
||||
```
|
||||
LIVEKIT_API_SECRET: secret
|
||||
LIVEKIT_API_KEY: devkey
|
||||
LIVEKIT_API_URL: https://livekit.127.0.0.1.nip.io/
|
||||
REDIS_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
|
||||
```
|
||||
|
||||
### Find postgresql connexion values
|
||||
|
||||
Visio uses a postgresql db as backend so if you have a provider, obtain the necessary information to use it. If you do not have, you can install a postgresql testing environment as follow:
|
||||
|
||||
```
|
||||
$ helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql -f examples/postgresql.values.yaml
|
||||
$ kubectl get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
keycloak-0 1/1 Running 0 45m
|
||||
keycloak-postgresql-0 1/1 Running 0 45m
|
||||
livekit-livekit-server-5c5fb87f7f-ct6x5 1/1 Running 0 15m
|
||||
postgresql-0 1/1 Running 0 50s
|
||||
redis-master-0 1/1 Running 0 19
|
||||
```
|
||||
From here important informations you will need are :
|
||||
|
||||
```
|
||||
DB_HOST: postgres-postgresql
|
||||
DB_NAME: meet
|
||||
DB_USER: dinum
|
||||
DB_PASSWORD: pass
|
||||
DB_PORT: 5432
|
||||
POSTGRES_DB: meet
|
||||
POSTGRES_USER: dinum
|
||||
POSTGRES_PASSWORD: pass
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
Now you are ready to deploy Visio without AI. AI requiered more dependancies (openai API and a docs deployment to push resumes). To deploy meet you need to provide all previous informations to the helm chart.
|
||||
|
||||
```
|
||||
$ helm repo add meet https://numerique-gouv.github.io/meet/
|
||||
$ helm repo update
|
||||
$ helm install meet meet/meet -f examples/meet.values.yaml
|
||||
```
|
||||
|
||||
## Test your deployment
|
||||
|
||||
In order to test your deployment you have to login to your instance. If you use exclusively our examples you can do :
|
||||
|
||||
```
|
||||
$ kubectl get ingress
|
||||
NAME CLASS HOSTS ADDRESS PORTS AGE
|
||||
keycloak <none> keycloak.127.0.0.1.nip.io localhost 80 58m
|
||||
livekit-livekit-server <none> livekit.127.0.0.1.nip.io localhost 80, 443 106m
|
||||
meet <none> meet.127.0.0.1.nip.io localhost 80, 443 52m
|
||||
meet-admin <none> meet.127.0.0.1.nip.io localhost 80, 443 52m
|
||||
```
|
||||
|
||||
You can use Visio on https://meet.127.0.0.1.nip.io. The provisionning user in keycloak is meet/meet.
|
||||
+1
-1
Submodule secrets updated: 142e7a70b1...2ba12db71d
@@ -145,7 +145,7 @@ class OIDCLogoutCallbackView(MozillaOIDCOIDCLogoutView):
|
||||
|
||||
|
||||
class OIDCAuthenticationCallbackView(MozillaOIDCAuthenticationCallbackView):
|
||||
"""Custom callback view for handling the silent loging flow."""
|
||||
"""Custom callback view for handling the silent login flow."""
|
||||
|
||||
@property
|
||||
def failure_url(self):
|
||||
@@ -162,7 +162,7 @@ class OIDCAuthenticationCallbackView(MozillaOIDCAuthenticationCallbackView):
|
||||
|
||||
|
||||
class OIDCAuthenticationRequestView(MozillaOIDCAuthenticationRequestView):
|
||||
"""Custom authentication view for handling the silent loging flow."""
|
||||
"""Custom authentication view for handling the silent login flow."""
|
||||
|
||||
def get_extra_params(self, request):
|
||||
"""Handle 'prompt' extra parameter for the silent login flow
|
||||
|
||||
@@ -324,8 +324,10 @@ class Base(Configuration):
|
||||
OIDC_AUTHENTICATE_CLASS = "core.authentication.views.OIDCAuthenticationRequestView"
|
||||
OIDC_CALLBACK_CLASS = "core.authentication.views.OIDCAuthenticationCallbackView"
|
||||
OIDC_CREATE_USER = values.BooleanValue(
|
||||
default=True,
|
||||
environ_name="OIDC_CREATE_USER",
|
||||
default=True, environ_name="OIDC_CREATE_USER", environ_prefix=None
|
||||
)
|
||||
OIDC_VERIFY_SSL = values.BooleanValue(
|
||||
default=True, environ_name="OIDC_VERIFY_SSL", environ_prefix=None
|
||||
)
|
||||
OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = values.BooleanValue(
|
||||
default=False,
|
||||
@@ -501,8 +503,10 @@ class Base(Configuration):
|
||||
release=get_release(),
|
||||
integrations=[DjangoIntegration()],
|
||||
)
|
||||
with sentry_sdk.configure_scope() as scope:
|
||||
scope.set_extra("application", "backend")
|
||||
|
||||
# Add the application name to the Sentry scope
|
||||
scope = sentry_sdk.get_global_scope()
|
||||
scope.set_tag("application", "backend")
|
||||
|
||||
|
||||
class Build(Base):
|
||||
|
||||
+14
-14
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "meet"
|
||||
version = "0.1.10"
|
||||
version = "0.1.12"
|
||||
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
@@ -25,20 +25,20 @@ license = { file = "LICENSE" }
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"boto3==1.35.68",
|
||||
"boto3==1.35.76",
|
||||
"Brotli==1.1.0",
|
||||
"celery[redis]==5.4.0",
|
||||
"django-configurations==2.5.1",
|
||||
"django-cors-headers==4.6.0",
|
||||
"django-countries==7.6.1",
|
||||
"django-parler==2.3",
|
||||
"redis==5.2.0",
|
||||
"redis==5.2.1",
|
||||
"django-redis==5.4.0",
|
||||
"django-storages[s3]==1.14.4",
|
||||
"django-timezone-field>=5.1",
|
||||
"django==5.1.3",
|
||||
"django==5.1.4",
|
||||
"djangorestframework==3.15.2",
|
||||
"drf_spectacular==0.27.2",
|
||||
"drf_spectacular==0.28.0",
|
||||
"dockerflow==2024.4.2",
|
||||
"easy_thumbnails==2.10",
|
||||
"factory_boy==3.3.1",
|
||||
@@ -48,16 +48,16 @@ dependencies = [
|
||||
"markdown==3.7",
|
||||
"nested-multipart-parser==1.5.0",
|
||||
"psycopg[binary]==3.2.3",
|
||||
"PyJWT==2.10.0",
|
||||
"PyJWT==2.10.1",
|
||||
"python-frontmatter==1.1.0",
|
||||
"requests==2.32.3",
|
||||
"sentry-sdk==2.19.0",
|
||||
"sentry-sdk==2.19.2",
|
||||
"url-normalize==1.4.3",
|
||||
"WeasyPrint>=60.2",
|
||||
"whitenoise==6.8.2",
|
||||
"mozilla-django-oidc==4.0.1",
|
||||
"livekit-api==0.8.0",
|
||||
"aiohttp==3.11.7",
|
||||
"aiohttp==3.11.10",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -69,20 +69,20 @@ dependencies = [
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"django-extensions==3.2.3",
|
||||
"drf-spectacular-sidecar==2024.11.1",
|
||||
"drf-spectacular-sidecar==2024.12.1",
|
||||
"freezegun==1.5.1",
|
||||
"ipdb==0.13.13",
|
||||
"ipython==8.29.0",
|
||||
"pyfakefs==5.7.1",
|
||||
"ipython==8.30.0",
|
||||
"pyfakefs==5.7.2",
|
||||
"pylint-django==2.6.1",
|
||||
"pylint==3.3.1",
|
||||
"pylint==3.3.2",
|
||||
"pytest-cov==6.0.0",
|
||||
"pytest-django==4.9.0",
|
||||
"pytest==8.3.3",
|
||||
"pytest==8.3.4",
|
||||
"pytest-icdiff==0.9",
|
||||
"pytest-xdist==3.6.1",
|
||||
"responses==0.25.3",
|
||||
"ruff==0.8.0",
|
||||
"ruff==0.8.2",
|
||||
"types-requests==2.32.0.20241016",
|
||||
]
|
||||
|
||||
|
||||
Generated
+5
-5
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "meet",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.12",
|
||||
"dependencies": {
|
||||
"@livekit/components-react": "2.6.9",
|
||||
"@livekit/components-styles": "1.1.4",
|
||||
@@ -7702,9 +7702,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "meet",
|
||||
"private": true,
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.12",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "panda codegen && vite",
|
||||
|
||||
@@ -2,9 +2,7 @@ import { css } from '@/styled-system/css'
|
||||
import { RiErrorWarningLine, RiExternalLinkLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Text, A } from '@/primitives'
|
||||
|
||||
const GRIST_FORM =
|
||||
'https://grist.numerique.gouv.fr/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4'
|
||||
import { GRIST_FORM } from '@/utils/constants'
|
||||
|
||||
export const FeedbackBanner = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@@ -16,11 +16,11 @@ export const MainNotificationToast = () => {
|
||||
if (isMobileBrowser()) {
|
||||
return
|
||||
}
|
||||
triggerNotificationSound(NotificationType.Joined)
|
||||
triggerNotificationSound(NotificationType.ParticipantJoined)
|
||||
toastQueue.add(
|
||||
{
|
||||
participant,
|
||||
type: NotificationType.Joined,
|
||||
type: NotificationType.ParticipantJoined,
|
||||
},
|
||||
{
|
||||
timeout: 5000,
|
||||
@@ -67,7 +67,7 @@ export const MainNotificationToast = () => {
|
||||
const existingToast = toastQueue.visibleToasts.find(
|
||||
(toast) =>
|
||||
toast.content.participant === participant &&
|
||||
toast.content.type === NotificationType.Raised
|
||||
toast.content.type === NotificationType.HandRaised
|
||||
)
|
||||
|
||||
if (existingToast && prevMetadata.raised && !metadata.raised) {
|
||||
@@ -76,11 +76,11 @@ export const MainNotificationToast = () => {
|
||||
}
|
||||
|
||||
if (!existingToast && !prevMetadata.raised && metadata.raised) {
|
||||
triggerNotificationSound(NotificationType.Raised)
|
||||
triggerNotificationSound(NotificationType.HandRaised)
|
||||
toastQueue.add(
|
||||
{
|
||||
participant,
|
||||
type: NotificationType.Raised,
|
||||
type: NotificationType.HandRaised,
|
||||
},
|
||||
{ timeout: 5000 }
|
||||
)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export enum NotificationType {
|
||||
Joined = 'joined',
|
||||
Default = 'default',
|
||||
Raised = 'raised',
|
||||
Lowered = 'lowered',
|
||||
ParticipantJoined = 'participantJoined',
|
||||
HandRaised = 'handRaised',
|
||||
// todo - implement message received notification
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ export function ToastRegion({ state, ...props }: ToastRegionProps) {
|
||||
return (
|
||||
<div {...regionProps} ref={ref} className="toast-region">
|
||||
{state.visibleToasts.map((toast) => {
|
||||
if (toast.content?.type === NotificationType.Joined) {
|
||||
if (toast.content?.type === NotificationType.ParticipantJoined) {
|
||||
return <ToastJoined key={toast.key} toast={toast} state={state} />
|
||||
}
|
||||
if (toast.content?.type === NotificationType.Raised) {
|
||||
if (toast.content?.type === NotificationType.HandRaised) {
|
||||
return <ToastRaised key={toast.key} toast={toast} state={state} />
|
||||
}
|
||||
return <Toast key={toast.key} toast={toast} state={state} />
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import useSound from 'use-sound'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { notificationsStore } from '@/stores/notifications'
|
||||
import { NotificationType } from '@/features/notifications/NotificationType'
|
||||
|
||||
// fixme - handle dynamic audio output changes
|
||||
export const useNotificationSound = () => {
|
||||
const notificationsSnap = useSnapshot(notificationsStore)
|
||||
const [play] = useSound('./sounds/notifications.mp3', {
|
||||
sprite: {
|
||||
joined: [0, 1150],
|
||||
raised: [1400, 180],
|
||||
message: [1580, 300],
|
||||
participantJoined: [0, 1150],
|
||||
handRaised: [1400, 180],
|
||||
messageReceived: [1580, 300],
|
||||
waiting: [2039, 710],
|
||||
success: [2740, 1304],
|
||||
},
|
||||
volume: notificationsSnap.soundNotificationVolume,
|
||||
})
|
||||
const triggerNotificationSound = (type: string) => {
|
||||
play({ id: type })
|
||||
const triggerNotificationSound = (type: NotificationType) => {
|
||||
const isSoundEnabled = notificationsSnap.soundNotifications.get(type)
|
||||
if (isSoundEnabled) play({ id: type })
|
||||
}
|
||||
return { triggerNotificationSound }
|
||||
}
|
||||
|
||||
@@ -65,6 +65,11 @@ export const Conference = ({
|
||||
|
||||
const roomOptions = useMemo((): RoomOptions => {
|
||||
return {
|
||||
adaptiveStream: true,
|
||||
dynacast: true,
|
||||
publishDefaults: {
|
||||
videoCodec: 'vp9',
|
||||
},
|
||||
videoCaptureDefaults: {
|
||||
deviceId: userConfig.videoDeviceId ?? undefined,
|
||||
},
|
||||
|
||||
@@ -5,8 +5,12 @@ import { css } from '@/styled-system/css'
|
||||
import { ToggleButton } from '@/primitives'
|
||||
import { chatStore } from '@/stores/chat'
|
||||
import { useSidePanel } from '../../hooks/useSidePanel'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
|
||||
export const ChatToggle = () => {
|
||||
export const ChatToggle = ({
|
||||
onPress,
|
||||
...props
|
||||
}: Partial<ToggleButtonProps>) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls.chat' })
|
||||
|
||||
const chatSnap = useSnapshot(chatStore)
|
||||
@@ -27,8 +31,12 @@ export const ChatToggle = () => {
|
||||
aria-label={t(tooltipLabel)}
|
||||
tooltip={t(tooltipLabel)}
|
||||
isSelected={isChatOpen}
|
||||
onPress={() => toggleChat()}
|
||||
onPress={(e) => {
|
||||
toggleChat()
|
||||
onPress?.(e)
|
||||
}}
|
||||
data-attr={`controls-chat-${tooltipLabel}`}
|
||||
{...props}
|
||||
>
|
||||
<RiChat1Line />
|
||||
</ToggleButton>
|
||||
|
||||
+2
-11
@@ -1,16 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiMore2Line } from '@remixicon/react'
|
||||
import { Button, Menu } from '@/primitives'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { OptionsMenuItems } from '@/features/rooms/livekit/components/controls/Options/OptionsMenuItems'
|
||||
import { SettingsDialogExtended } from '@/features/settings/components/SettingsDialogExtended'
|
||||
|
||||
export type DialogState = 'username' | 'settings' | null
|
||||
|
||||
export const OptionsButton = () => {
|
||||
const { t } = useTranslation('rooms')
|
||||
const [dialogOpen, setDialogOpen] = useState<DialogState>(null)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu>
|
||||
@@ -22,12 +17,8 @@ export const OptionsButton = () => {
|
||||
>
|
||||
<RiMore2Line />
|
||||
</Button>
|
||||
<OptionsMenuItems onOpenDialog={setDialogOpen} />
|
||||
<OptionsMenuItems />
|
||||
</Menu>
|
||||
<SettingsDialogExtended
|
||||
isOpen={dialogOpen === 'settings'}
|
||||
onOpenChange={(v) => !v && setDialogOpen(null)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
+6
-9
@@ -5,20 +5,17 @@ import {
|
||||
} from '@remixicon/react'
|
||||
import { MenuItem, Menu as RACMenu, Section } from 'react-aria-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import { DialogState } from './OptionsButton'
|
||||
import { Separator } from '@/primitives/Separator'
|
||||
import { useSidePanel } from '../../../hooks/useSidePanel'
|
||||
import { menuRecipe } from '@/primitives/menuRecipe.ts'
|
||||
import { useSettingsDialog } from '../SettingsDialogContext'
|
||||
import { GRIST_FORM } from '@/utils/constants'
|
||||
|
||||
// @todo try refactoring it to use MenuList component
|
||||
export const OptionsMenuItems = ({
|
||||
onOpenDialog,
|
||||
}: {
|
||||
onOpenDialog: Dispatch<SetStateAction<DialogState>>
|
||||
}) => {
|
||||
export const OptionsMenuItems = () => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
||||
const { toggleEffects } = useSidePanel()
|
||||
const { setDialogOpen } = useSettingsDialog()
|
||||
return (
|
||||
<RACMenu
|
||||
style={{
|
||||
@@ -38,7 +35,7 @@ export const OptionsMenuItems = ({
|
||||
<Separator />
|
||||
<Section>
|
||||
<MenuItem
|
||||
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
|
||||
href={GRIST_FORM}
|
||||
target="_blank"
|
||||
className={menuRecipe({ icon: true }).item}
|
||||
>
|
||||
@@ -47,7 +44,7 @@ export const OptionsMenuItems = ({
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className={menuRecipe({ icon: true }).item}
|
||||
onAction={() => onOpenDialog('settings')}
|
||||
onAction={() => setDialogOpen(true)}
|
||||
>
|
||||
<RiSettings3Line size={20} />
|
||||
{t('settings')}
|
||||
|
||||
+10
-2
@@ -4,8 +4,12 @@ import { ToggleButton } from '@/primitives'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { useParticipants } from '@livekit/components-react'
|
||||
import { useSidePanel } from '../../../hooks/useSidePanel'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
|
||||
export const ParticipantsToggle = () => {
|
||||
export const ParticipantsToggle = ({
|
||||
onPress,
|
||||
...props
|
||||
}: ToggleButtonProps) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls.participants' })
|
||||
|
||||
/**
|
||||
@@ -33,8 +37,12 @@ export const ParticipantsToggle = () => {
|
||||
aria-label={t(tooltipLabel)}
|
||||
tooltip={t(tooltipLabel)}
|
||||
isSelected={isParticipantsOpen}
|
||||
onPress={() => toggleParticipants()}
|
||||
onPress={(e) => {
|
||||
toggleParticipants()
|
||||
onPress?.(e)
|
||||
}}
|
||||
data-attr={`controls-participants-${tooltipLabel}`}
|
||||
{...props}
|
||||
>
|
||||
<RiGroupLine />
|
||||
</ToggleButton>
|
||||
|
||||
@@ -4,13 +4,21 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useTrackToggle, UseTrackToggleProps } from '@livekit/components-react'
|
||||
import { Track } from 'livekit-client'
|
||||
import React from 'react'
|
||||
import { type ButtonRecipeProps } from '@/primitives/buttonRecipe'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
|
||||
export const ScreenShareToggle = (
|
||||
props: Omit<
|
||||
UseTrackToggleProps<Track.Source.ScreenShare>,
|
||||
'source' | 'captureOptions'
|
||||
>
|
||||
) => {
|
||||
type Props = Omit<
|
||||
UseTrackToggleProps<Track.Source.ScreenShare>,
|
||||
'source' | 'captureOptions'
|
||||
> &
|
||||
Pick<NonNullable<ButtonRecipeProps>, 'variant'> &
|
||||
ToggleButtonProps
|
||||
|
||||
export const ScreenShareToggle = ({
|
||||
variant = 'primaryDark',
|
||||
onPress,
|
||||
...props
|
||||
}: Props) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls.screenShare' })
|
||||
const { buttonProps, enabled } = useTrackToggle({
|
||||
...props,
|
||||
@@ -26,18 +34,16 @@ export const ScreenShareToggle = (
|
||||
<ToggleButton
|
||||
isSelected={enabled}
|
||||
square
|
||||
variant="primaryDark"
|
||||
variant={variant}
|
||||
tooltip={t(tooltipLabel)}
|
||||
onPress={(e) =>
|
||||
onPress={(e) => {
|
||||
buttonProps.onClick?.(
|
||||
e as unknown as React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
)
|
||||
}
|
||||
style={{
|
||||
maxWidth: '46px',
|
||||
maxHeight: '46px',
|
||||
onPress?.(e)
|
||||
}}
|
||||
data-attr={`controls-screenshare-${tooltipLabel}`}
|
||||
{...props}
|
||||
>
|
||||
<Div position="relative">
|
||||
<RiRectangleLine size={28} />
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { SettingsDialogExtended } from '@/features/settings/components/SettingsDialogExtended'
|
||||
import React, { createContext, useContext, useState } from 'react'
|
||||
|
||||
const SettingsDialogContext = createContext<
|
||||
| {
|
||||
dialogOpen: boolean
|
||||
setDialogOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
}
|
||||
| undefined
|
||||
>(undefined)
|
||||
|
||||
export const SettingsDialogProvider: React.FC<{
|
||||
children: React.ReactNode
|
||||
}> = ({ children }) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
return (
|
||||
<SettingsDialogContext.Provider value={{ dialogOpen, setDialogOpen }}>
|
||||
{children}
|
||||
<SettingsDialogExtended
|
||||
isOpen={dialogOpen}
|
||||
onOpenChange={(v) => !v && setDialogOpen(false)}
|
||||
/>
|
||||
</SettingsDialogContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components
|
||||
export const useSettingsDialog = () => {
|
||||
const context = useContext(SettingsDialogContext)
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'useSettingsDialog must be used within a SettingsDialogProvider'
|
||||
)
|
||||
}
|
||||
return context
|
||||
}
|
||||
@@ -3,8 +3,9 @@ import { RiQuestionLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Crisp } from 'crisp-sdk-web'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
|
||||
export const SupportToggle = () => {
|
||||
export const SupportToggle = ({ onPress, ...props }: ToggleButtonProps) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls' })
|
||||
const [isOpened, setIsOpened] = useState($crisp.is('chat:opened'))
|
||||
|
||||
@@ -32,8 +33,16 @@ export const SupportToggle = () => {
|
||||
tooltip={t('support')}
|
||||
aria-label={t('support')}
|
||||
isSelected={isOpened}
|
||||
onPress={() => (isOpened ? Crisp.chat.close() : Crisp.chat.open())}
|
||||
onPress={(e) => {
|
||||
if (isOpened) {
|
||||
Crisp.chat.close()
|
||||
} else {
|
||||
Crisp.chat.open()
|
||||
}
|
||||
onPress?.(e)
|
||||
}}
|
||||
data-attr="controls-support"
|
||||
{...props}
|
||||
>
|
||||
<RiQuestionLine />
|
||||
</ToggleButton>
|
||||
|
||||
@@ -4,8 +4,13 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useSidePanel } from '../../hooks/useSidePanel'
|
||||
import { useHasTranscriptAccess } from '../../hooks/useHasTranscriptAccess'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { ToggleButtonProps } from '@/primitives/ToggleButton'
|
||||
|
||||
export const TranscriptToggle = () => {
|
||||
export const TranscriptToggle = ({
|
||||
variant = 'primaryDark',
|
||||
onPress,
|
||||
...props
|
||||
}: ToggleButtonProps) => {
|
||||
const { t } = useTranslation('rooms', { keyPrefix: 'controls.transcript' })
|
||||
|
||||
const { isTranscriptOpen, toggleTranscript } = useSidePanel()
|
||||
@@ -24,11 +29,15 @@ export const TranscriptToggle = () => {
|
||||
>
|
||||
<ToggleButton
|
||||
square
|
||||
variant="primaryTextDark"
|
||||
variant={variant}
|
||||
aria-label={t(tooltipLabel)}
|
||||
tooltip={t(tooltipLabel)}
|
||||
isSelected={isTranscriptOpen}
|
||||
onPress={() => toggleTranscript()}
|
||||
onPress={(e) => {
|
||||
toggleTranscript()
|
||||
onPress?.(e)
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<RiBardLine />
|
||||
</ToggleButton>
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
import { Track } from 'livekit-client'
|
||||
import * as React from 'react'
|
||||
|
||||
import { supportsScreenSharing } from '@livekit/components-core'
|
||||
|
||||
import { usePersistentUserChoices } from '@livekit/components-react'
|
||||
|
||||
import { StartMediaButton } from '../components/controls/StartMediaButton'
|
||||
import { OptionsButton } from '../components/controls/Options/OptionsButton'
|
||||
import { ParticipantsToggle } from '../components/controls/Participants/ParticipantsToggle'
|
||||
import { ChatToggle } from '../components/controls/ChatToggle'
|
||||
import { HandToggle } from '../components/controls/HandToggle'
|
||||
import { SelectToggleDevice } from '../components/controls/SelectToggleDevice'
|
||||
import { LeaveButton } from '../components/controls/LeaveButton'
|
||||
import { ScreenShareToggle } from '../components/controls/ScreenShareToggle'
|
||||
import { SupportToggle } from '../components/controls/SupportToggle'
|
||||
import { TranscriptToggle } from '../components/controls/TranscriptToggle'
|
||||
|
||||
import { css } from '@/styled-system/css'
|
||||
|
||||
/** @public */
|
||||
export type ControlBarControls = {
|
||||
microphone?: boolean
|
||||
camera?: boolean
|
||||
chat?: boolean
|
||||
screenShare?: boolean
|
||||
leave?: boolean
|
||||
settings?: boolean
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onDeviceError?: (error: { source: Track.Source; error: Error }) => void
|
||||
variation?: 'minimal' | 'verbose' | 'textOnly'
|
||||
controls?: ControlBarControls
|
||||
/**
|
||||
* If `true`, the user's device choices will be persisted.
|
||||
* This will enable the user to have the same device choices when they rejoin the room.
|
||||
* @defaultValue true
|
||||
* @alpha
|
||||
*/
|
||||
saveUserChoices?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The `ControlBar` prefab gives the user the basic user interface to control their
|
||||
* media devices (camera, microphone and screen share), open the `Chat` and leave the room.
|
||||
*
|
||||
* @remarks
|
||||
* This component is build with other LiveKit components like `TrackToggle`,
|
||||
* `DeviceSelectorButton`, `DisconnectButton` and `StartAudio`.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <LiveKitRoom>
|
||||
* <ControlBar />
|
||||
* </LiveKitRoom>
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export function ControlBar({
|
||||
saveUserChoices = true,
|
||||
onDeviceError,
|
||||
}: ControlBarProps) {
|
||||
const browserSupportsScreenSharing = supportsScreenSharing()
|
||||
|
||||
const {
|
||||
saveAudioInputEnabled,
|
||||
saveVideoInputEnabled,
|
||||
saveAudioInputDeviceId,
|
||||
saveVideoInputDeviceId,
|
||||
} = usePersistentUserChoices({ preventSave: !saveUserChoices })
|
||||
|
||||
const microphoneOnChange = React.useCallback(
|
||||
(enabled: boolean, isUserInitiated: boolean) =>
|
||||
isUserInitiated ? saveAudioInputEnabled(enabled) : null,
|
||||
[saveAudioInputEnabled]
|
||||
)
|
||||
|
||||
const cameraOnChange = React.useCallback(
|
||||
(enabled: boolean, isUserInitiated: boolean) =>
|
||||
isUserInitiated ? saveVideoInputEnabled(enabled) : null,
|
||||
[saveVideoInputEnabled]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
width: '100vw',
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
padding: '1.125rem',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
flex: '1 1 33%',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
marginLeft: '0.5rem',
|
||||
})}
|
||||
></div>
|
||||
<div
|
||||
className={css({
|
||||
flex: '1 1 33%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
gap: '0.65rem',
|
||||
})}
|
||||
>
|
||||
<SelectToggleDevice
|
||||
source={Track.Source.Microphone}
|
||||
onChange={microphoneOnChange}
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.Microphone, error })
|
||||
}
|
||||
onActiveDeviceChange={(deviceId) =>
|
||||
saveAudioInputDeviceId(deviceId ?? '')
|
||||
}
|
||||
/>
|
||||
<SelectToggleDevice
|
||||
source={Track.Source.Camera}
|
||||
onChange={cameraOnChange}
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.Camera, error })
|
||||
}
|
||||
onActiveDeviceChange={(deviceId) =>
|
||||
saveVideoInputDeviceId(deviceId ?? '')
|
||||
}
|
||||
/>
|
||||
{browserSupportsScreenSharing && (
|
||||
<ScreenShareToggle
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.ScreenShare, error })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<HandToggle />
|
||||
<OptionsButton />
|
||||
<LeaveButton />
|
||||
<StartMediaButton />
|
||||
</div>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flex: '1 1 33%',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
paddingRight: '0.25rem',
|
||||
})}
|
||||
>
|
||||
<ChatToggle />
|
||||
<ParticipantsToggle />
|
||||
<TranscriptToggle />
|
||||
<SupportToggle />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Track } from 'livekit-client'
|
||||
import * as React from 'react'
|
||||
import { usePersistentUserChoices } from '@livekit/components-react'
|
||||
|
||||
import { MobileControlBar } from './MobileControlBar'
|
||||
import { DesktopControlBar } from './DesktopControlBar'
|
||||
import { SettingsDialogProvider } from '../../components/controls/SettingsDialogContext'
|
||||
import { useIsMobile } from '@/utils/useIsMobile'
|
||||
|
||||
/** @public */
|
||||
export type ControlBarControls = {
|
||||
microphone?: boolean
|
||||
camera?: boolean
|
||||
chat?: boolean
|
||||
screenShare?: boolean
|
||||
leave?: boolean
|
||||
settings?: boolean
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
onDeviceError?: (error: { source: Track.Source; error: Error }) => void
|
||||
variation?: 'minimal' | 'verbose' | 'textOnly'
|
||||
controls?: ControlBarControls
|
||||
/**
|
||||
* If `true`, the user's device choices will be persisted.
|
||||
* This will enable the user to have the same device choices when they rejoin the room.
|
||||
* @defaultValue true
|
||||
* @alpha
|
||||
*/
|
||||
saveUserChoices?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The `ControlBar` prefab gives the user the basic user interface to control their
|
||||
* media devices (camera, microphone and screen share), open the `Chat` and leave the room.
|
||||
*
|
||||
* @remarks
|
||||
* This component is build with other LiveKit components like `TrackToggle`,
|
||||
* `DeviceSelectorButton`, `DisconnectButton` and `StartAudio`.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <LiveKitRoom>
|
||||
* <ControlBar />
|
||||
* </LiveKitRoom>
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export function ControlBar({
|
||||
saveUserChoices = true,
|
||||
onDeviceError,
|
||||
}: ControlBarProps) {
|
||||
const {
|
||||
saveAudioInputEnabled,
|
||||
saveVideoInputEnabled,
|
||||
saveAudioInputDeviceId,
|
||||
saveVideoInputDeviceId,
|
||||
} = usePersistentUserChoices({ preventSave: !saveUserChoices })
|
||||
|
||||
const microphoneOnChange = React.useCallback(
|
||||
(enabled: boolean, isUserInitiated: boolean) =>
|
||||
isUserInitiated ? saveAudioInputEnabled(enabled) : null,
|
||||
[saveAudioInputEnabled]
|
||||
)
|
||||
|
||||
const cameraOnChange = React.useCallback(
|
||||
(enabled: boolean, isUserInitiated: boolean) =>
|
||||
isUserInitiated ? saveVideoInputEnabled(enabled) : null,
|
||||
[saveVideoInputEnabled]
|
||||
)
|
||||
|
||||
const barProps = {
|
||||
onDeviceError,
|
||||
microphoneOnChange,
|
||||
cameraOnChange,
|
||||
saveAudioInputDeviceId,
|
||||
saveVideoInputDeviceId,
|
||||
}
|
||||
|
||||
const isMobile = useIsMobile()
|
||||
|
||||
return (
|
||||
<SettingsDialogProvider>
|
||||
{isMobile ? (
|
||||
<MobileControlBar {...barProps} />
|
||||
) : (
|
||||
<DesktopControlBar {...barProps} />
|
||||
)}
|
||||
</SettingsDialogProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export interface ControlBarAuxProps {
|
||||
onDeviceError: ControlBarProps['onDeviceError']
|
||||
microphoneOnChange: (
|
||||
enabled: boolean,
|
||||
isUserInitiated: boolean
|
||||
) => void | null
|
||||
cameraOnChange: (enabled: boolean, isUserInitiated: boolean) => void | null
|
||||
saveAudioInputDeviceId: (deviceId: string) => void
|
||||
saveVideoInputDeviceId: (deviceId: string) => void
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { supportsScreenSharing } from '@livekit/components-core'
|
||||
import { ControlBarAuxProps } from './ControlBar'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { LeaveButton } from '../../components/controls/LeaveButton'
|
||||
import { SelectToggleDevice } from '../../components/controls/SelectToggleDevice'
|
||||
import { Track } from 'livekit-client'
|
||||
import { HandToggle } from '../../components/controls/HandToggle'
|
||||
import { ScreenShareToggle } from '../../components/controls/ScreenShareToggle'
|
||||
import { OptionsButton } from '../../components/controls/Options/OptionsButton'
|
||||
import { StartMediaButton } from '../../components/controls/StartMediaButton'
|
||||
import { ChatToggle } from '../../components/controls/ChatToggle'
|
||||
import { ParticipantsToggle } from '../../components/controls/Participants/ParticipantsToggle'
|
||||
import { SupportToggle } from '../../components/controls/SupportToggle'
|
||||
import { TranscriptToggle } from '../../components/controls/TranscriptToggle'
|
||||
|
||||
export function DesktopControlBar({
|
||||
onDeviceError,
|
||||
microphoneOnChange,
|
||||
cameraOnChange,
|
||||
saveAudioInputDeviceId,
|
||||
saveVideoInputDeviceId,
|
||||
}: ControlBarAuxProps) {
|
||||
const browserSupportsScreenSharing = supportsScreenSharing()
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={css({
|
||||
width: '100vw',
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
padding: '1.125rem',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
flex: '1 1 33%',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
marginLeft: '0.5rem',
|
||||
})}
|
||||
></div>
|
||||
<div
|
||||
className={css({
|
||||
flex: '1 1 33%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
gap: '0.65rem',
|
||||
})}
|
||||
>
|
||||
<SelectToggleDevice
|
||||
source={Track.Source.Microphone}
|
||||
onChange={microphoneOnChange}
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.Microphone, error })
|
||||
}
|
||||
onActiveDeviceChange={(deviceId) =>
|
||||
saveAudioInputDeviceId(deviceId ?? '')
|
||||
}
|
||||
/>
|
||||
<SelectToggleDevice
|
||||
source={Track.Source.Camera}
|
||||
onChange={cameraOnChange}
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.Camera, error })
|
||||
}
|
||||
onActiveDeviceChange={(deviceId) =>
|
||||
saveVideoInputDeviceId(deviceId ?? '')
|
||||
}
|
||||
/>
|
||||
{browserSupportsScreenSharing && (
|
||||
<ScreenShareToggle
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.ScreenShare, error })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<HandToggle />
|
||||
<OptionsButton />
|
||||
<LeaveButton />
|
||||
<StartMediaButton />
|
||||
</div>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flex: '1 1 33%',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
paddingRight: '0.25rem',
|
||||
})}
|
||||
>
|
||||
<ChatToggle />
|
||||
<ParticipantsToggle />
|
||||
<TranscriptToggle />
|
||||
<SupportToggle />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
import { supportsScreenSharing } from '@livekit/components-core'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ControlBarAuxProps } from './ControlBar'
|
||||
import React from 'react'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { LeaveButton } from '../../components/controls/LeaveButton'
|
||||
import { SelectToggleDevice } from '../../components/controls/SelectToggleDevice'
|
||||
import { Track } from 'livekit-client'
|
||||
import { HandToggle } from '../../components/controls/HandToggle'
|
||||
import { Button } from '@/primitives/Button'
|
||||
import {
|
||||
RiAccountBoxLine,
|
||||
RiMegaphoneLine,
|
||||
RiMore2Line,
|
||||
RiSettings3Line,
|
||||
} from '@remixicon/react'
|
||||
import { ScreenShareToggle } from '../../components/controls/ScreenShareToggle'
|
||||
import { ChatToggle } from '../../components/controls/ChatToggle'
|
||||
import { ParticipantsToggle } from '../../components/controls/Participants/ParticipantsToggle'
|
||||
import { SupportToggle } from '../../components/controls/SupportToggle'
|
||||
import { useSidePanel } from '../../hooks/useSidePanel'
|
||||
import { LinkButton } from '@/primitives'
|
||||
import { useSettingsDialog } from '../../components/controls/SettingsDialogContext'
|
||||
import { ResponsiveMenu } from './ResponsiveMenu'
|
||||
import { TranscriptToggle } from '../../components/controls/TranscriptToggle'
|
||||
|
||||
export function MobileControlBar({
|
||||
onDeviceError,
|
||||
microphoneOnChange,
|
||||
cameraOnChange,
|
||||
saveAudioInputDeviceId,
|
||||
saveVideoInputDeviceId,
|
||||
}: ControlBarAuxProps) {
|
||||
const { t } = useTranslation('rooms')
|
||||
const [isMenuOpened, setIsMenuOpened] = React.useState(false)
|
||||
const browserSupportsScreenSharing = supportsScreenSharing()
|
||||
const { toggleEffects } = useSidePanel()
|
||||
const { setDialogOpen } = useSettingsDialog()
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={css({
|
||||
width: '100vw',
|
||||
display: 'flex',
|
||||
position: 'absolute',
|
||||
padding: '1.125rem',
|
||||
justifyContent: 'center',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '422px',
|
||||
})}
|
||||
>
|
||||
<LeaveButton />
|
||||
<SelectToggleDevice
|
||||
source={Track.Source.Microphone}
|
||||
onChange={microphoneOnChange}
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.Microphone, error })
|
||||
}
|
||||
onActiveDeviceChange={(deviceId) =>
|
||||
saveAudioInputDeviceId(deviceId ?? '')
|
||||
}
|
||||
/>
|
||||
<SelectToggleDevice
|
||||
source={Track.Source.Camera}
|
||||
onChange={cameraOnChange}
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.Camera, error })
|
||||
}
|
||||
onActiveDeviceChange={(deviceId) =>
|
||||
saveVideoInputDeviceId(deviceId ?? '')
|
||||
}
|
||||
/>
|
||||
<HandToggle />
|
||||
<Button
|
||||
square
|
||||
variant="primaryDark"
|
||||
aria-label={t('options.buttonLabel')}
|
||||
tooltip={t('options.buttonLabel')}
|
||||
onPress={() => setIsMenuOpened(true)}
|
||||
>
|
||||
<RiMore2Line />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<ResponsiveMenu
|
||||
isOpened={isMenuOpened}
|
||||
onClosed={() => setIsMenuOpened(false)}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
flexGrow: 1,
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(100px, 1fr))',
|
||||
gridGap: '1rem',
|
||||
'& > *': {
|
||||
alignSelf: 'center',
|
||||
justifySelf: 'center',
|
||||
},
|
||||
})}
|
||||
>
|
||||
{browserSupportsScreenSharing && (
|
||||
<ScreenShareToggle
|
||||
onDeviceError={(error) =>
|
||||
onDeviceError?.({ source: Track.Source.ScreenShare, error })
|
||||
}
|
||||
variant="primaryTextDark"
|
||||
description={true}
|
||||
onPress={() => setIsMenuOpened(false)}
|
||||
/>
|
||||
)}
|
||||
<ChatToggle
|
||||
description={true}
|
||||
onPress={() => setIsMenuOpened(false)}
|
||||
/>
|
||||
<ParticipantsToggle
|
||||
description={true}
|
||||
onPress={() => setIsMenuOpened(false)}
|
||||
/>
|
||||
<TranscriptToggle
|
||||
description={true}
|
||||
onPress={() => setIsMenuOpened(false)}
|
||||
/>
|
||||
<SupportToggle
|
||||
description={true}
|
||||
onPress={() => setIsMenuOpened(false)}
|
||||
/>
|
||||
<Button
|
||||
onPress={() => {
|
||||
toggleEffects()
|
||||
setIsMenuOpened(false)
|
||||
}}
|
||||
variant="primaryTextDark"
|
||||
aria-label={t('options.items.effects')}
|
||||
tooltip={t('options.items.effects')}
|
||||
description={true}
|
||||
>
|
||||
<RiAccountBoxLine size={20} />
|
||||
</Button>
|
||||
<LinkButton
|
||||
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
|
||||
variant="primaryTextDark"
|
||||
tooltip={t('options.items.feedbacks')}
|
||||
aria-label={t('options.items.feedbacks')}
|
||||
description={true}
|
||||
target="_blank"
|
||||
onPress={() => setIsMenuOpened(false)}
|
||||
>
|
||||
<RiMegaphoneLine size={20} />
|
||||
</LinkButton>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setDialogOpen(true)
|
||||
setIsMenuOpened(false)
|
||||
}}
|
||||
variant="primaryTextDark"
|
||||
aria-label={t('options.items.settings')}
|
||||
tooltip={t('options.items.settings')}
|
||||
description={true}
|
||||
>
|
||||
<RiSettings3Line size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ResponsiveMenu>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { css } from '@/styled-system/css'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import { Dialog, Modal, ModalOverlay } from 'react-aria-components'
|
||||
|
||||
interface ResponsiveMenuProps extends PropsWithChildren {
|
||||
isOpened: boolean
|
||||
onClosed: () => void
|
||||
}
|
||||
|
||||
export function ResponsiveMenu({
|
||||
isOpened,
|
||||
onClosed,
|
||||
children,
|
||||
}: ResponsiveMenuProps) {
|
||||
return (
|
||||
<ModalOverlay
|
||||
isDismissable
|
||||
isOpen={isOpened}
|
||||
onOpenChange={(isOpened) => {
|
||||
if (!isOpened) {
|
||||
onClosed()
|
||||
}
|
||||
}}
|
||||
className={css({
|
||||
width: '100vw',
|
||||
height: 'var(--visual-viewport-height)',
|
||||
zIndex: 100,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-end',
|
||||
display: 'flex',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
padding: '1.5rem 1.5rem 1rem 1.5rem',
|
||||
boxSizing: 'border-box',
|
||||
})}
|
||||
>
|
||||
<Modal
|
||||
className={css({
|
||||
backgroundColor: 'primaryDark.200',
|
||||
borderRadius: '20px',
|
||||
flexGrow: 1,
|
||||
padding: '1.5rem',
|
||||
'&[data-entering]': {
|
||||
animation: 'slide-full 200ms',
|
||||
},
|
||||
'&[data-exiting]': {
|
||||
animation: 'slide-full 200ms reverse',
|
||||
},
|
||||
})}
|
||||
>
|
||||
<Dialog>{children}</Dialog>
|
||||
</Modal>
|
||||
</ModalOverlay>
|
||||
)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
useCreateLayoutContext,
|
||||
} from '@livekit/components-react'
|
||||
|
||||
import { ControlBar } from './ControlBar'
|
||||
import { ControlBar } from './ControlBar/ControlBar'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
import { cva } from '@/styled-system/css'
|
||||
import { MainNotificationToast } from '@/features/notifications/MainNotificationToast'
|
||||
|
||||
@@ -6,12 +6,14 @@ import { Heading } from 'react-aria-components'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiAccountCircleLine,
|
||||
RiNotification3Line,
|
||||
RiSettings3Line,
|
||||
RiSpeakerLine,
|
||||
} from '@remixicon/react'
|
||||
import { AccountTab } from './tabs/AccountTab'
|
||||
import { GeneralTab } from '@/features/settings/components/tabs/GeneralTab.tsx'
|
||||
import { AudioTab } from '@/features/settings/components/tabs/AudioTab.tsx'
|
||||
import { NotificationsTab } from './tabs/NotificationsTab'
|
||||
import { GeneralTab } from './tabs/GeneralTab'
|
||||
import { AudioTab } from './tabs/AudioTab'
|
||||
import { useSize } from '@/features/rooms/livekit/hooks/useResizeObserver'
|
||||
import { useRef } from 'react'
|
||||
|
||||
@@ -81,12 +83,17 @@ export const SettingsDialogExtended = (props: SettingsDialogExtended) => {
|
||||
<RiSettings3Line />
|
||||
{isWideScreen && t('tabs.general')}
|
||||
</Tab>
|
||||
<Tab icon highlight id="4">
|
||||
<RiNotification3Line />
|
||||
{isWideScreen && t('tabs.notifications')}
|
||||
</Tab>
|
||||
</TabList>
|
||||
</div>
|
||||
<div className={tabPanelContainerStyle}>
|
||||
<AccountTab id="1" onOpenChange={props.onOpenChange} />
|
||||
<AudioTab id="2" />
|
||||
<GeneralTab id="3" />
|
||||
<NotificationsTab id="4" />
|
||||
</div>
|
||||
</Tabs>
|
||||
</Dialog>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { TabPanel, TabPanelProps } from '@/primitives/Tabs'
|
||||
import { H, Switch } from '@/primitives'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { notificationsStore } from '@/stores/notifications'
|
||||
|
||||
export type NotificationsTabProps = Pick<TabPanelProps, 'id'>
|
||||
|
||||
export const NotificationsTab = ({ id }: NotificationsTabProps) => {
|
||||
const { t } = useTranslation('settings', { keyPrefix: 'notifications' })
|
||||
const notificationsSnap = useSnapshot(notificationsStore)
|
||||
return (
|
||||
<TabPanel padding={'md'} flex id={id}>
|
||||
<H lvl={2}>{t('heading')}</H>
|
||||
<ul
|
||||
className={css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1rem',
|
||||
})}
|
||||
>
|
||||
{Array.from(notificationsSnap.soundNotifications).map(
|
||||
([key, value]) => (
|
||||
<li key={key}>
|
||||
<Switch
|
||||
aria-label={`${t(`actions.${value ? 'disable' : 'enable'}`)} ${t('label')} "${t(`items.${key}`)}"`}
|
||||
isSelected={value}
|
||||
onChange={(v) => {
|
||||
notificationsStore.soundNotifications.set(key, v)
|
||||
}}
|
||||
>
|
||||
{t(`items.${key}`)}
|
||||
</Switch>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</TabPanel>
|
||||
)
|
||||
}
|
||||
@@ -19,6 +19,19 @@
|
||||
},
|
||||
"permissionsRequired": ""
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "",
|
||||
"label": "",
|
||||
"actions": {
|
||||
"disable": "",
|
||||
"enable": ""
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "",
|
||||
"handRaised": "",
|
||||
"messageReceived": ""
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"heading": ""
|
||||
},
|
||||
@@ -30,6 +43,7 @@
|
||||
"tabs": {
|
||||
"account": "",
|
||||
"audio": "",
|
||||
"general": ""
|
||||
"general": "",
|
||||
"notifications": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,19 @@
|
||||
},
|
||||
"permissionsRequired": "Permissions required"
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "Sound notifications",
|
||||
"label": "sound notifications for",
|
||||
"actions": {
|
||||
"disable": "Disable",
|
||||
"enable": "Enable"
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "Participant joined",
|
||||
"handRaised": "Hand raised",
|
||||
"messageReceived": "Message received"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"heading": "Settings"
|
||||
},
|
||||
@@ -30,6 +43,7 @@
|
||||
"tabs": {
|
||||
"account": "Profile",
|
||||
"audio": "Audio",
|
||||
"general": "General"
|
||||
"general": "General",
|
||||
"notifications": "Notifications"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,19 @@
|
||||
},
|
||||
"permissionsRequired": "Autorisations nécessaires"
|
||||
},
|
||||
"notifications": {
|
||||
"heading": "Notifications sonores",
|
||||
"label": "la notification sonore pour",
|
||||
"actions": {
|
||||
"disable": "Désactiver",
|
||||
"enable": "Activer"
|
||||
},
|
||||
"items": {
|
||||
"participantJoined": "Un nouveau participant",
|
||||
"handRaised": "Une main levée",
|
||||
"messageReceived": "Un message reçu"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"heading": "Paramètres"
|
||||
},
|
||||
@@ -30,6 +43,7 @@
|
||||
"tabs": {
|
||||
"account": "Profile",
|
||||
"audio": "Audio",
|
||||
"general": "Général"
|
||||
"general": "Général",
|
||||
"notifications": "Notifications"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ import {
|
||||
import { type RecipeVariantProps } from '@/styled-system/css'
|
||||
import { buttonRecipe, type ButtonRecipe } from './buttonRecipe'
|
||||
import { TooltipWrapper, type TooltipWrapperProps } from './TooltipWrapper'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export type ButtonProps = RecipeVariantProps<ButtonRecipe> &
|
||||
RACButtonsProps &
|
||||
TooltipWrapperProps
|
||||
TooltipWrapperProps & {
|
||||
// Use tooltip as description below the button.
|
||||
description?: boolean
|
||||
}
|
||||
|
||||
export const Button = ({
|
||||
tooltip,
|
||||
@@ -22,7 +26,10 @@ export const Button = ({
|
||||
<RACButton
|
||||
className={buttonRecipe(variantProps)}
|
||||
{...(componentProps as RACButtonsProps)}
|
||||
/>
|
||||
>
|
||||
{componentProps.children as ReactNode}
|
||||
{props.description && <span>{tooltip}</span>}
|
||||
</RACButton>
|
||||
</TooltipWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,14 @@ import { Link, LinkProps } from 'react-aria-components'
|
||||
import { type RecipeVariantProps } from '@/styled-system/css'
|
||||
import { buttonRecipe, type ButtonRecipe } from './buttonRecipe'
|
||||
import { TooltipWrapper, type TooltipWrapperProps } from './TooltipWrapper'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
type LinkButtonProps = RecipeVariantProps<ButtonRecipe> &
|
||||
LinkProps &
|
||||
TooltipWrapperProps
|
||||
TooltipWrapperProps & {
|
||||
// Use tooltip as description below the button.
|
||||
description?: boolean
|
||||
}
|
||||
|
||||
export const LinkButton = ({
|
||||
tooltip,
|
||||
@@ -16,7 +20,12 @@ export const LinkButton = ({
|
||||
|
||||
return (
|
||||
<TooltipWrapper tooltip={tooltip} tooltipType={tooltipType}>
|
||||
<Link className={buttonRecipe(variantProps)} {...componentProps} />
|
||||
<Link className={buttonRecipe(variantProps)} {...componentProps}>
|
||||
<>
|
||||
{componentProps.children as ReactNode}
|
||||
{props.description && <span>{tooltip}</span>}
|
||||
</>
|
||||
</Link>
|
||||
</TooltipWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
Switch as RACSwitch,
|
||||
SwitchProps as RACSwitchProps,
|
||||
} from 'react-aria-components'
|
||||
import { styled } from '@/styled-system/jsx'
|
||||
import { StyledVariantProps } from '@/styled-system/types'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export const StyledSwitch = styled(RACSwitch, {
|
||||
base: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.571rem',
|
||||
color: 'black',
|
||||
forcedColorAdjust: 'none',
|
||||
'& .indicator': {
|
||||
width: '2.6rem',
|
||||
height: '1.563rem',
|
||||
border: '0.125rem solid',
|
||||
borderColor: 'primary.800',
|
||||
borderRadius: '1.143rem',
|
||||
transition: 'all 200ms, outline 200ms',
|
||||
_before: {
|
||||
content: '""',
|
||||
display: 'block',
|
||||
margin: '0.125rem',
|
||||
width: '1.063rem',
|
||||
height: '1.063rem',
|
||||
borderRadius: '1.063rem',
|
||||
background: 'primary.800',
|
||||
transition: 'all 200ms',
|
||||
},
|
||||
},
|
||||
'&[data-selected] .indicator': {
|
||||
borderColor: 'primary.800',
|
||||
background: 'primary.800',
|
||||
_before: {
|
||||
background: 'white',
|
||||
transform: 'translateX(100%)',
|
||||
},
|
||||
},
|
||||
'&[data-disabled] .indicator': {
|
||||
borderColor: 'primary.200',
|
||||
background: 'transparent',
|
||||
_before: {
|
||||
background: 'primary.200',
|
||||
},
|
||||
},
|
||||
'&[data-focus-visible] .indicator': {
|
||||
outline: '2px solid!',
|
||||
outlineColor: 'focusRing!',
|
||||
outlineOffset: '2px!',
|
||||
},
|
||||
},
|
||||
variants: {},
|
||||
})
|
||||
|
||||
export type SwitchProps = StyledVariantProps<typeof StyledSwitch> &
|
||||
RACSwitchProps & { children: ReactNode }
|
||||
|
||||
/**
|
||||
* Styled RAC Switch.
|
||||
*/
|
||||
export const Switch = ({ children, ...props }: SwitchProps) => (
|
||||
<StyledSwitch {...props}>
|
||||
<div className="indicator" />
|
||||
{children}
|
||||
</StyledSwitch>
|
||||
)
|
||||
@@ -1,9 +1,17 @@
|
||||
import {
|
||||
ToggleButton as RACToggleButton,
|
||||
ToggleButtonProps,
|
||||
ToggleButtonProps as RACToggleButtonProps,
|
||||
} from 'react-aria-components'
|
||||
import { type ButtonRecipeProps, buttonRecipe } from './buttonRecipe'
|
||||
import { TooltipWrapper, TooltipWrapperProps } from './TooltipWrapper'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export type ToggleButtonProps = RACToggleButtonProps &
|
||||
ButtonRecipeProps &
|
||||
TooltipWrapperProps & {
|
||||
// Use tooltip as description below the button.
|
||||
description?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* React aria ToggleButton with our button styles, that can take a tooltip if needed
|
||||
@@ -12,14 +20,20 @@ export const ToggleButton = ({
|
||||
tooltip,
|
||||
tooltipType,
|
||||
...props
|
||||
}: ToggleButtonProps & ButtonRecipeProps & TooltipWrapperProps) => {
|
||||
}: ToggleButtonProps) => {
|
||||
const [variantProps, componentProps] = buttonRecipe.splitVariantProps(props)
|
||||
|
||||
return (
|
||||
<TooltipWrapper tooltip={tooltip} tooltipType={tooltipType}>
|
||||
<RACToggleButton
|
||||
{...componentProps}
|
||||
className={buttonRecipe(variantProps)}
|
||||
/>
|
||||
>
|
||||
<>
|
||||
{componentProps.children as ReactNode}
|
||||
{props.description && <span>{tooltip}</span>}
|
||||
</>
|
||||
</RACToggleButton>
|
||||
</TooltipWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { type RecipeVariantProps, cva } from '@/styled-system/css'
|
||||
|
||||
export type ButtonRecipe = typeof buttonRecipe
|
||||
|
||||
export type ButtonRecipeProps = RecipeVariantProps<ButtonRecipe>
|
||||
|
||||
export const buttonRecipe = cva({
|
||||
base: {
|
||||
display: 'flex',
|
||||
@@ -222,6 +218,16 @@ export const buttonRecipe = cva({
|
||||
shySelected: {
|
||||
true: {},
|
||||
},
|
||||
description: {
|
||||
true: {
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem',
|
||||
'& span': {
|
||||
fontSize: '13px',
|
||||
textAlign: 'center',
|
||||
},
|
||||
},
|
||||
},
|
||||
// if the button is next to other ones to make a "button group", tell where the button is to handle radius
|
||||
groupPosition: {
|
||||
left: {
|
||||
@@ -255,3 +261,7 @@ export const buttonRecipe = cva({
|
||||
variant: 'primary',
|
||||
},
|
||||
})
|
||||
|
||||
export type ButtonRecipe = typeof buttonRecipe
|
||||
|
||||
export type ButtonRecipeProps = RecipeVariantProps<ButtonRecipe>
|
||||
|
||||
@@ -29,3 +29,4 @@ export { ToggleButton } from './ToggleButton'
|
||||
export { Ul } from './Ul'
|
||||
export { VerticallyOffCenter } from './VerticallyOffCenter'
|
||||
export { TextArea } from './TextArea'
|
||||
export { Switch } from './Switch'
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { proxy, subscribe } from 'valtio'
|
||||
import { proxyMap } from 'valtio/utils'
|
||||
import { deserializeToProxyMap, serializeProxyMap } from '@/utils/valtio'
|
||||
import { STORAGE_KEYS } from '@/utils/storageKeys'
|
||||
import { NotificationType } from '@/features/notifications/NotificationType'
|
||||
|
||||
type State = {
|
||||
soundNotifications: Map<NotificationType, boolean>
|
||||
soundNotificationVolume: number
|
||||
}
|
||||
|
||||
const DEFAULT_STATE: State = {
|
||||
soundNotifications: proxyMap(
|
||||
new Map([
|
||||
[NotificationType.ParticipantJoined, true],
|
||||
[NotificationType.HandRaised, true],
|
||||
])
|
||||
),
|
||||
soundNotificationVolume: 0.1,
|
||||
}
|
||||
|
||||
function getNotificationsState(): State {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEYS.NOTIFICATIONS)
|
||||
if (!stored) return DEFAULT_STATE
|
||||
const parsed = JSON.parse(stored, deserializeToProxyMap)
|
||||
return parsed || DEFAULT_STATE
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
'[NotificationsStore] Failed to parse stored settings:',
|
||||
error
|
||||
)
|
||||
return DEFAULT_STATE
|
||||
}
|
||||
}
|
||||
|
||||
export const notificationsStore = proxy<State>(getNotificationsState())
|
||||
|
||||
subscribe(notificationsStore, () => {
|
||||
localStorage.setItem(
|
||||
STORAGE_KEYS.NOTIFICATIONS,
|
||||
JSON.stringify(notificationsStore, serializeProxyMap)
|
||||
)
|
||||
})
|
||||
@@ -29,3 +29,12 @@ body,
|
||||
body:has(.lk-video-conference) #crisp-chatbox > div > a {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@keyframes slide-full {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const GRIST_FORM =
|
||||
'https://grist.numerique.gouv.fr/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4' as const
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Object containing all localStorage keys used across the app
|
||||
*/
|
||||
export const STORAGE_KEYS = {
|
||||
NOTIFICATIONS: 'app_notification_settings',
|
||||
} as const
|
||||
@@ -0,0 +1,20 @@
|
||||
import { isMobileBrowser } from '@livekit/components-core'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export const useIsMobile = () => {
|
||||
const [isMobile, setIsMobile] = useState(isMobileBrowser())
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsMobile(isMobileBrowser())
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return isMobile
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { proxyMap } from 'valtio/utils'
|
||||
|
||||
/**
|
||||
* Serializes Map objects into a JSON-friendly format while preserving valtio proxyMap compatibility
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function serializeProxyMap(_: string, value: any) {
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
dataType: 'Map',
|
||||
value: Array.from(value.entries()),
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom JSON reviver function for deserializing Map objects and wrapping them in valtio proxyMap
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function deserializeToProxyMap(_: string, value: any) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (value.dataType === 'Map') {
|
||||
return proxyMap(new Map(value.value))
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
replicaCount: 1
|
||||
terminationGracePeriodSeconds: 18000
|
||||
|
||||
egress:
|
||||
log_level: debug
|
||||
ws_url: ws://livekit-livekit-server:80
|
||||
insecure: true
|
||||
enable_chrome_sandbox: true
|
||||
{{- with .Values.livekit.keys }}
|
||||
{{- range $key, $value := . }}
|
||||
api_key: {{ $key }}
|
||||
api_secret: {{ $value }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
password: pass
|
||||
s3:
|
||||
access_key: meet
|
||||
secret: password
|
||||
region: local
|
||||
bucket: meet-media-storage
|
||||
endpoint: http://minio:9000
|
||||
force_path_style: true
|
||||
|
||||
loadBalancer:
|
||||
type: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
tls:
|
||||
- hosts:
|
||||
- livekit-egress.127.0.0.1.nip.io
|
||||
secretName: livekit-egress-dinum-cert
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
|
||||
nodeSelector: {}
|
||||
resources: {}
|
||||
@@ -0,0 +1,40 @@
|
||||
replicaCount: 1
|
||||
terminationGracePeriodSeconds: 18000
|
||||
|
||||
livekit:
|
||||
log_level: debug
|
||||
rtc:
|
||||
use_external_ip: false
|
||||
port_range_start: 50000
|
||||
port_range_end: 60000
|
||||
tcp_port: 7881
|
||||
redis:
|
||||
address: redis-master:6379
|
||||
password: pass
|
||||
keys:
|
||||
turn:
|
||||
enabled: true
|
||||
udp_port: 443
|
||||
domain: livekit.127.0.0.1.nip.io
|
||||
loadBalancerAnnotations: {}
|
||||
|
||||
|
||||
loadBalancer:
|
||||
type: nginx
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
tls:
|
||||
- hosts:
|
||||
- livekit.127.0.0.1.nip.io
|
||||
secretName: livekit-dinum-cert
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
targetCPUUtilizationPercentage: 60
|
||||
|
||||
nodeSelector: {}
|
||||
resources: {}
|
||||
@@ -0,0 +1,182 @@
|
||||
image:
|
||||
repository: localhost:5001/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
backend:
|
||||
replicas: 1
|
||||
envVars:
|
||||
DJANGO_CSRF_TRUSTED_ORIGINS: https://meet.127.0.0.1.nip.io,http://meet.127.0.0.1.nip.io
|
||||
DJANGO_CONFIGURATION: Production
|
||||
DJANGO_ALLOWED_HOSTS: meet.127.0.0.1.nip.io
|
||||
DJANGO_SECRET_KEY: {{ .Values.djangoSecretKey }}
|
||||
DJANGO_SETTINGS_MODULE: meet.settings
|
||||
DJANGO_SILENCED_SYSTEM_CHECKS: security.W004, security.W008
|
||||
DJANGO_SUPERUSER_PASSWORD: admin
|
||||
DJANGO_EMAIL_HOST: "mailcatcher"
|
||||
DJANGO_EMAIL_PORT: 1025
|
||||
DJANGO_EMAIL_USE_SSL: False
|
||||
OIDC_OP_JWKS_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/certs
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/auth
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/token
|
||||
OIDC_OP_USER_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://keycloak.127.0.0.1.nip.io/realms/meet/protocol/openid-connect/session/end
|
||||
OIDC_RP_CLIENT_ID: meet
|
||||
OIDC_RP_CLIENT_SECRET: ThisIsAnExampleKeyForDevPurposeOnly
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email"
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://meet.127.0.0.1.nip.io
|
||||
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
||||
OIDC_VERIFY_SSL: False
|
||||
LOGIN_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
|
||||
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
|
||||
DB_HOST: postgres-postgresql
|
||||
DB_NAME: meet
|
||||
DB_USER: dinum
|
||||
DB_PASSWORD: pass
|
||||
DB_PORT: 5432
|
||||
POSTGRES_DB: meet
|
||||
POSTGRES_USER: dinum
|
||||
POSTGRES_PASSWORD: pass
|
||||
REDIS_URL: redis://default:pass@redis-master:6379/1
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
{{- with .Values.livekit.keys }}
|
||||
{{- range $key, $value := . }}
|
||||
LIVEKIT_API_SECRET: {{ $value }}
|
||||
LIVEKIT_API_KEY: {{ $key }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
LIVEKIT_API_URL: https://livekit.127.0.0.1.nip.io/
|
||||
ALLOW_UNREGISTERED_ROOMS: False
|
||||
FRONTEND_SILENCE_LIVEKIT_DEBUG: False
|
||||
FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041'}"
|
||||
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: meet
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
||||
AWS_S3_REGION_NAME: local
|
||||
RECORDING_ENABLE: True
|
||||
RECORDING_VERIFY_SSL: False
|
||||
RECORDING_STORAGE_EVENT_ENABLE: True
|
||||
RECORDING_STORAGE_EVENT_TOKEN: password
|
||||
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
|
||||
SUMMARY_SERVICE_API_TOKEN: password
|
||||
|
||||
|
||||
migrate:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py migrate --no-input &&
|
||||
python manage.py create_demo --force
|
||||
restartPolicy: Never
|
||||
|
||||
command:
|
||||
- "gunicorn"
|
||||
- "-c"
|
||||
- "/usr/local/etc/gunicorn/meet.py"
|
||||
- "meet.wsgi:application"
|
||||
- "--reload"
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py createsuperuser --email admin@example.com --password admin
|
||||
restartPolicy: Never
|
||||
|
||||
frontend:
|
||||
envVars:
|
||||
VITE_PORT: 8080
|
||||
VITE_HOST: 0.0.0.0
|
||||
VITE_API_BASE_URL: https://meet.127.0.0.1.nip.io/
|
||||
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: localhost:5001/meet-frontend
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: meet.127.0.0.1.nip.io
|
||||
|
||||
ingressAdmin:
|
||||
enabled: true
|
||||
host: meet.127.0.0.1.nip.io
|
||||
|
||||
posthog:
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
ingressAssets:
|
||||
enabled: false
|
||||
|
||||
summary:
|
||||
replicas: 1
|
||||
envVars:
|
||||
APP_NAME: summary-microservice
|
||||
APP_API_TOKEN: password
|
||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
||||
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: meet
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
OPENAI_API_KEY: password
|
||||
OPENAI_BASE_URL: https://albertine.beta.numerique.gouv.fr/v1
|
||||
OPENAI_ASR_MODEL: openai/whisper-large-v3
|
||||
OPENAI_LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
|
||||
AWS_S3_SECURE_ACCESS: False
|
||||
WEBHOOK_API_TOKEN: password
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
|
||||
|
||||
image:
|
||||
repository: localhost:5001/meet-summary
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
command:
|
||||
- "uvicorn"
|
||||
- "summary.main:app"
|
||||
- "--host"
|
||||
- "0.0.0.0"
|
||||
- "--port"
|
||||
- "8000"
|
||||
- "--reload"
|
||||
|
||||
celery:
|
||||
replicas: 1
|
||||
envVars:
|
||||
APP_NAME: summary-microservice
|
||||
APP_API_TOKEN: password
|
||||
AWS_STORAGE_BUCKET_NAME: meet-media-storage
|
||||
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
|
||||
AWS_S3_ACCESS_KEY_ID: meet
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
OPENAI_API_KEY: password
|
||||
OPENAI_BASE_URL: https://albertine.beta.numerique.gouv.fr/v1
|
||||
OPENAI_ASR_MODEL: openai/whisper-large-v3
|
||||
OPENAI_LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
|
||||
AWS_S3_SECURE_ACCESS: False
|
||||
WEBHOOK_API_TOKEN: password
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
|
||||
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
|
||||
|
||||
image:
|
||||
repository: localhost:5001/meet-summary
|
||||
pullPolicy: Always
|
||||
tag: "latest"
|
||||
|
||||
command:
|
||||
- "celery"
|
||||
- "-A"
|
||||
- "summary.core.celery_worker"
|
||||
- "worker"
|
||||
- "--pool=solo"
|
||||
- "--loglevel=info"
|
||||
@@ -0,0 +1,10 @@
|
||||
djangoSecretKey: u!vbjDW71aru&OZA%NZQi0x
|
||||
livekit:
|
||||
keys:
|
||||
devkey: secret
|
||||
livekitApi:
|
||||
key: devkey
|
||||
secret: secret
|
||||
oidc:
|
||||
clientId: meet
|
||||
clientSecret: ThisIsAnExampleKeyForDevPurposeOnly
|
||||
@@ -1,103 +0,0 @@
|
||||
djangoSecretKey: ENC[AES256_GCM,data:p+9m8eNB/dKMXAdfL0cVCg1uKhAv+YLrM+jjajvRYmOZZ9qbiikuFv0dyDp32va/M9w=,iv:ijUztg7ta6BBTsKs+IIfJMFdN0DfzyAKoxlfY8lisPg=,tag:B+uW6akIV0iI2LdMQotrpw==,type:str]
|
||||
oidc:
|
||||
clientId: ENC[AES256_GCM,data:rHzKkQwFQ7hV6kOBBP60RK41NBKVMUs4dMcZavMQ8gCu9ust,iv:8vviSb+XIKS/zjBIScfmWu0VJ8lXCQZ8p7BxuvJtA2w=,tag:k8vn8I/qxKLE/+JNTDj4Jw==,type:str]
|
||||
clientSecret: ENC[AES256_GCM,data:dOYJoG2PStlOMIJPi2exPzsqlxis73iTkcBMvjr8DBr2isWzstpbexscsog7Tuyelw4tpzrJKzC5BTTwJ+xioQ==,iv:oqkLRTPB8+qR0AHvjyNVfHRmoeGrkUvZjrTsWBjIeBc=,tag:hryfmSeqkdWCN9U38jxXlA==,type:str]
|
||||
#ENC[AES256_GCM,data:ua1td/VBXGIHDgAw/bm8XnWIRLmgeJKX9dP7g/rNv3jVsXHw6T+iDXxMWpLXNicAZ/RTymdntlwLwsH47r70Z4icEPsjps0yOZ+X734vaL9wVH9IsyFwCihtyck94kgY4CyC7DI=,iv:iGHYu+2aPaI28PQWFheVVuge8BPWLw1VB7Afsz7eLtI=,tag:pfkXsS+/QmHb3kHS/ONHCA==,type:comment]
|
||||
livekit:
|
||||
keys:
|
||||
devkey: ENC[AES256_GCM,data:5RnAMGm3,iv:bY4n8op2KFlXRqzV9h3QwoC3Bws2aEoN1GFxPlrrVBw=,tag:lA+b/6poVRzeJW6Bu8V29A==,type:str]
|
||||
livekitApi:
|
||||
key: ENC[AES256_GCM,data:JP7KkPms,iv:LlIJ62IRyGf8fByl6abSZv1to2FUc90laC0oL5HFJK4=,tag:2aLMQ79GlDOaiurh8unO0Q==,type:str]
|
||||
secret: ENC[AES256_GCM,data:kGDJo1lh,iv:dnI1OuvZGOJZEKZwzoigXqViqYCw/6H7Y0sVXH/p5RA=,tag:G1IB0mc8zuKEmkxrfyImrQ==,type:str]
|
||||
sops:
|
||||
kms: []
|
||||
gcp_kms: []
|
||||
azure_kv: []
|
||||
hc_vault: []
|
||||
age:
|
||||
- recipient: age15fyxdwmg5mvldtqqus87xspuws2u0cpvwheehrtvkexj4tnsqqysw6re2x
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByR3IybDN3eGx4amYzZkFt
|
||||
OW5VV3FQN3dkSmZBL0JwUE1qSzNLYmRTc1RjCkVCQ2ZmaHk2SFRJaXdMd0VMZUlP
|
||||
b0VQeDVUTDBEZzhBQnhrS2RybzYvL1UKLS0tIG1CbllhWGpsOWx4WEkya0NLeUlC
|
||||
WmRScW9MVkxQLzRxdk85WTZ4U2E0aUUKTpOPYQXutU0xYLih7SNYoQgO+PSEIERL
|
||||
HLz+C7iV+Fj1/M7JrgiGxTB8wJoKMo7IhJ8AjxaAdxR4Q1TgUpQkPw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age16hnlml8yv4ynwy0seer57g8qww075crd0g7nsundz3pj4wk7m3vqftszg7
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aE15QkRsNmg2UTkxaWNF
|
||||
T3NZY2RqSDd0WlRKOHYxWFE2R3J5SGJhRjJNClNIcEFwOEtoSmRWQjdaSm1ZSnlj
|
||||
amhNci9tRDl2Qlp4dlBGZFYzTGxYdm8KLS0tIDZZWTYxQmVqOEZQaTNOODFGWUhn
|
||||
cXpJL3poT3dpYjZKWTN6dGpOV3kxT2sKozsOz+cSYJdZ0C2L6QCf/VSU9DnOz6ae
|
||||
lqV5MMzSl1Jf8ETpqt+PhvvWz+MLCAkIriT9yf6R29DQifCacB7XOA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1plkp8td6zzfcavjusmsfrlk54t9vn8jjxm8zaz7cmnr7kzl2nfnsd54hwg
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBySkpOYWxjQVZRbGtkNXlt
|
||||
OTRKTDlrNjNMenU3V0hPeXYyRnhGVU1mMmhNCmhJTi9ZQzB3ekpSR0k1VDFiNExu
|
||||
dW9TQkI3Vy9LOXhQaEExZHMyM25xZlEKLS0tIGRYTkpzbjIvL1FMS2lYYXl4dDVZ
|
||||
U040akh0Z1ZYVmdjS3k2ZjFRK2VRNGMKqSCnviWARWTkZXeht+sdOYKAxylYYyZK
|
||||
uXYE3nBaXGosIqmTf6deVqCIY+m0mH/J4UMcbH+faMV4pWmVr2JAxg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age12g6f5fse25tgrwweleh4jls3qs52hey2edh759smulwmk5lnzadslu2cp3
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJbUhzZStoUVBHUkZLWlE3
|
||||
NWNiRkJMdXhUVXRNZTFCMUljVzIxY3BVMFQ4ClpmOGhqeUZiaG1HcU5zdndmWE5y
|
||||
Ym5OTmoyVVVsb2Ywa3loRTVNZzdlVjQKLS0tIHNEWVV3Mkk2VGVzR3diQW5Ccm1a
|
||||
MVNUYjZCME9rQWFUaWNycEh5THQyTTAKTBnoF76mJ/GoCIq4TsmV+luYbiWnx0+I
|
||||
BEISvqsr9gbT0z8kfdo/htPoKHZmnyevZhRhd2AMZdKixYvQMX9sjA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1tl80n23wq6zxegupwn70ew0yp225ua5v4dk800x7g2w6pvlxz46qk592pa
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDWEZkODBNOGw2WFdncjJ0
|
||||
TTVzRHlEa1AzaTF4V2hYR3hFRGg2cnBzYmowCmp3WDJ3bEZoTlFYL2hoZ3hhTVU1
|
||||
WnQyYk03K2xmSk00dS92OHNNZnRIL2cKLS0tIEVrbjY4enJBZzdQMjRCRmwwVlRI
|
||||
OHVOMm9NTGdJbnZ2aXYxdi9OdWpkVE0K4b1Hu6rOHVtfH601aXb/uTGYjNMh6yW/
|
||||
LetO+HKk+VEzXHntObK2k/4mTl5I0+OP5H8+PR0jdIUZDpr79iEbgQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1qy04neuzwpasmvljqrcvhwnf0kz5cpyteze38c8avp0czewskasszv9pyw
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqVE9iMmUwTXE2SHZNdG5P
|
||||
Vi9XQ1Jkc1VDamFlakpkZk45ODZ2YnkwYkVBCnNrbktIdkV4UGltcHBUUHlXbjdx
|
||||
Z0QwM3ZKbGI1cDBjL2g2cjdKdElOQjAKLS0tIGxrcTJDa1BWVWcxUS80MmxIMWZH
|
||||
YjBRMDZJZWlmN1FNaXV5c04yVWtleE0K+nGNyFzqSotFP7My/kUnAgxXGu/ji50K
|
||||
OGVLYgNvU48rCGck3r9ZrKY1HpQdAY8UMQXECsuO4HgdirNjiZ97Zg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age18fgn6j2vwwswqcpv9xpcehq8mrf9zs2sglwkamp3tzwx8d9jq9jsrskrk9
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4ZXZud0dqb0dkQ0E3NnE4
|
||||
SXB0ZENjQk1mb1BHU2R1bW0waDhTYy9OZldVCjVnRTV5d1c3Q2NzcEVRQ3BoL09I
|
||||
T1RPQ3hHT3Y2NFNzWG9EdGM2STR2STgKLS0tIHBvL3RhREFNTVdwUGk3S1B4NWJL
|
||||
TnZpblF1SDdGRlVXM0dEdFAzT1FEMUUK6L8gTv5gt6++A3B7PHyWl+xtBUc8bC6G
|
||||
53xoJvyyBpaov3HgUAdrN9VHubfEJmrBGgN7DngGgwYPtlhV87M7/w==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1hm2hsfgjezpsc3k0y5w5feq9t8vl3seq04qjhgt6ztd6403wfvpsgxu09m
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlaXY1VmtDejcwTmUxRVZT
|
||||
YURhMkVPaHNvb0sxT0FYL0pvN3hqclNNcXdRCmxWV3FGeDZTM1VVMVRyalpkVnFJ
|
||||
OGU3Wk9wVVAvejVTdjc1MENPcy9Qc1kKLS0tIGpJQXhZVzV3REc2SFlFSXg0dUo5
|
||||
bjRBaGtJdUFmVUkxeGgwbGYwWjRnNEkKYwzwZ9oOo+C6XD57rkUTO6QADZKzYfSF
|
||||
cFJ7fX0NyZbzxLncyofWa+dlLWLZ3KohIP0doAFngRm+RVsUEVqY5A==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
- recipient: age1hnhuzj96ktkhpyygvmz0x9h8mfvssz7ss6emmukags644mdhf4msajk93r
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1aFNsL2xvWmI4UTAxREc4
|
||||
NFF3bC9qRTBqS3JrM3B0ZjE5bEtjR0diT0VjClhFNStFU3RydnhvcG9CSmhYM3V4
|
||||
VjZ5c0JQZjRoQXh1R2UyeDMyd2NFMEEKLS0tIDNwWUNzZmlrNGZPbERTeFpoUkxO
|
||||
QnZTWWFMemk5djVNWFRaekVMRkMyUjgKt4dw4BOm3J1Ig6U58NbSjzJbWi3ak/Zq
|
||||
8PX5IW7tq1q5+Qd3adqv3cd9S2aVpqjHyN34fxagmuwfvYXVyQ2GDg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2024-10-02T07:30:09Z"
|
||||
mac: ENC[AES256_GCM,data:BdEiR/7AiTz9eppAGOAarFzUJYEfCZzb0lg8LXaHiXe74B5Ob7Ai+XuBBX+x9QPIFzbLZgVveVSrqymW0wAH9Dv5R+e4spDf5KKdRCr9RADfCXNjYC0N9grZVerM70Ic51Lc1kKDnB2mon01W5Sa77Ei29Jo988yvM8AOlXFvr4=,iv:p7PCazxKNv7YcGX7Kpp2L8wXEFaJO8FajEXcVMzmmWQ=,tag:WJKZOkFZSof6IhcXqc60uQ==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.9.0
|
||||
@@ -1,3 +1,14 @@
|
||||
secrets:
|
||||
- name: oidcLogin
|
||||
itemId: a25effec-eaea-4ce1-9ed8-3a3cc1c734db
|
||||
field: username
|
||||
podVariable: OIDC_RP_CLIENT_ID
|
||||
clusterSecretStore: bitwarden-login-visio
|
||||
- name: oidcPass
|
||||
itemId: a25effec-eaea-4ce1-9ed8-3a3cc1c734db
|
||||
field: password
|
||||
podVariable: OIDC_RP_CLIENT_SECRET
|
||||
clusterSecretStore: bitwarden-login-visio
|
||||
image:
|
||||
repository: localhost:5001/meet-backend
|
||||
pullPolicy: Always
|
||||
@@ -21,8 +32,14 @@ backend:
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
|
||||
OIDC_OP_USER_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/session/end
|
||||
OIDC_RP_CLIENT_ID: {{ .Values.oidc.clientId }}
|
||||
OIDC_RP_CLIENT_SECRET: {{ .Values.oidc.clientSecret }}
|
||||
OIDC_RP_CLIENT_ID:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: OIDC_RP_CLIENT_ID
|
||||
OIDC_RP_CLIENT_SECRET:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: OIDC_RP_CLIENT_SECRET
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email given_name usual_name"
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://meet.127.0.0.1.nip.io
|
||||
@@ -125,6 +142,9 @@ summary:
|
||||
AWS_S3_ACCESS_KEY_ID: meet
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
OPENAI_API_KEY: password
|
||||
OPENAI_BASE_URL: https://albertine.beta.numerique.gouv.fr/v1
|
||||
OPENAI_ASR_MODEL: openai/whisper-large-v3
|
||||
OPENAI_LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
|
||||
AWS_S3_SECURE_ACCESS: False
|
||||
WEBHOOK_API_TOKEN: password
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
@@ -155,6 +175,9 @@ celery:
|
||||
AWS_S3_ACCESS_KEY_ID: meet
|
||||
AWS_S3_SECRET_ACCESS_KEY: password
|
||||
OPENAI_API_KEY: password
|
||||
OPENAI_BASE_URL: https://albertine.beta.numerique.gouv.fr/v1
|
||||
OPENAI_ASR_MODEL: openai/whisper-large-v3
|
||||
OPENAI_LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
|
||||
AWS_S3_SECURE_ACCESS: False
|
||||
WEBHOOK_API_TOKEN: password
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
djangoSecretKey: u!vbjDW71aru&OZA%NZQi0x
|
||||
livekit:
|
||||
keys:
|
||||
devkey: secret
|
||||
livekitApi:
|
||||
key: devkey
|
||||
secret: secret
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../../../secrets/numerique-gouv/meet/env/production/secrets.enc.yaml
|
||||
@@ -1,173 +0,0 @@
|
||||
image:
|
||||
repository: lasuite/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "v0.1.10"
|
||||
|
||||
backend:
|
||||
migrateJobAnnotations:
|
||||
argocd.argoproj.io/hook: PostSync
|
||||
argocd.argoproj.io/hook-delete-policy: HookSucceeded
|
||||
envVars:
|
||||
DJANGO_CSRF_TRUSTED_ORIGINS: https://visio.numerique.gouv.fr,https://meet.numerique.gouv.fr
|
||||
DJANGO_CONFIGURATION: Production
|
||||
DJANGO_ALLOWED_HOSTS: visio.numerique.gouv.fr,meet.numerique.gouv.fr
|
||||
DJANGO_SECRET_KEY:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: DJANGO_SECRET_KEY
|
||||
DJANGO_SETTINGS_MODULE: meet.settings
|
||||
DJANGO_SILENCED_SYSTEM_CHECKS: security.W004, security.W008
|
||||
DJANGO_SUPERUSER_EMAIL:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: DJANGO_SUPERUSER_EMAIL
|
||||
DJANGO_SUPERUSER_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: DJANGO_SUPERUSER_PASSWORD
|
||||
DJANGO_EMAIL_HOST: "snap-mail.numerique.gouv.fr"
|
||||
DJANGO_EMAIL_PORT: 465
|
||||
DJANGO_EMAIL_USE_SSL: True
|
||||
DJANGO_SENTRY_DSN: https://5aead03f03505da5130af6d642c42faf@sentry.incubateur.net/202
|
||||
OIDC_OP_JWKS_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/jwks
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/authorize
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/token
|
||||
OIDC_OP_USER_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/session/end
|
||||
OIDC_RP_CLIENT_ID:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: OIDC_RP_CLIENT_ID
|
||||
OIDC_RP_CLIENT_SECRET:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: OIDC_RP_CLIENT_SECRET
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email given_name usual_name"
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://visio.numerique.gouv.fr
|
||||
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
||||
LOGIN_REDIRECT_URL: https://visio.numerique.gouv.fr
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://visio.numerique.gouv.fr
|
||||
LOGOUT_REDIRECT_URL: https://visio.numerique.gouv.fr
|
||||
DB_HOST:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: host
|
||||
DB_NAME:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: database
|
||||
DB_USER:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: username
|
||||
DB_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: password
|
||||
DB_PORT:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: port
|
||||
POSTGRES_USER:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: username
|
||||
POSTGRES_DB:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: database
|
||||
POSTGRES_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: password
|
||||
REDIS_URL:
|
||||
secretKeyRef:
|
||||
name: redis.redis.libre.sh
|
||||
key: url
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
LIVEKIT_API_SECRET:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_SECRET
|
||||
LIVEKIT_API_KEY:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_KEY
|
||||
LIVEKIT_API_URL: https://livekit-preprod.beta.numerique.gouv.fr
|
||||
ALLOW_UNREGISTERED_ROOMS: False
|
||||
FRONTEND_SILENCE_LIVEKIT_DEBUG: False
|
||||
FRONTEND_ANALYTICS: "{'id': 'phc_RPYko028Oqtj0c9exLIWwrlrjLxSdxT0ntW0Lam4iom', 'host': 'https://product.visio.numerique.gouv.fr'}"
|
||||
FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041'}"
|
||||
AWS_S3_ENDPOINT_URL:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: url
|
||||
AWS_S3_ACCESS_KEY_ID:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: accessKey
|
||||
AWS_S3_SECRET_ACCESS_KEY:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: secretKey
|
||||
AWS_STORAGE_BUCKET_NAME:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: bucket
|
||||
AWS_S3_REGION_NAME: local
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py createsuperuser --email $DJANGO_SUPERUSER_EMAIL --password $DJANGO_SUPERUSER_PASSWORD
|
||||
restartPolicy: Never
|
||||
|
||||
frontend:
|
||||
image:
|
||||
repository: lasuite/meet-frontend
|
||||
pullPolicy: Always
|
||||
tag: "v0.1.10"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: visio.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
|
||||
ingressAdmin:
|
||||
enabled: true
|
||||
host: visio.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy.beta.numerique.gouv.fr/oauth2/start
|
||||
nginx.ingress.kubernetes.io/auth-url: https://oauth2-proxy.beta.numerique.gouv.fr/oauth2/auth
|
||||
|
||||
posthog:
|
||||
ingress:
|
||||
enabled: true
|
||||
host: product.visio.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: eu.i.posthog.com
|
||||
nginx.ingress.kubernetes.io/backend-protocol: https
|
||||
|
||||
ingressAssets:
|
||||
enabled: true
|
||||
host: product.visio.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: eu-assets.i.posthog.com
|
||||
nginx.ingress.kubernetes.io/backend-protocol: https
|
||||
|
||||
summary:
|
||||
replicas: 0
|
||||
|
||||
celery:
|
||||
replicas: 0
|
||||
@@ -1 +0,0 @@
|
||||
../../../../secrets/numerique-gouv/meet/env/staging/secrets.enc.yaml
|
||||
@@ -1,294 +0,0 @@
|
||||
image:
|
||||
repository: lasuite/meet-backend
|
||||
pullPolicy: Always
|
||||
tag: "main"
|
||||
|
||||
backend:
|
||||
migrateJobAnnotations:
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
argocd.argoproj.io/hook-delete-policy: HookSucceeded
|
||||
envVars:
|
||||
DJANGO_CSRF_TRUSTED_ORIGINS: http://visio-staging.beta.numerique.gouv.fr,https://meet-staging.beta.numerique.gouv.fr
|
||||
DJANGO_CONFIGURATION: Production
|
||||
DJANGO_ALLOWED_HOSTS: visio-staging.beta.numerique.gouv.fr
|
||||
DJANGO_SECRET_KEY:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: DJANGO_SECRET_KEY
|
||||
DJANGO_SETTINGS_MODULE: meet.settings
|
||||
DJANGO_SUPERUSER_EMAIL:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: DJANGO_SUPERUSER_EMAIL
|
||||
DJANGO_SUPERUSER_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: DJANGO_SUPERUSER_PASSWORD
|
||||
DJANGO_EMAIL_HOST: "snap-mail.numerique.gouv.fr"
|
||||
DJANGO_EMAIL_PORT: 465
|
||||
DJANGO_EMAIL_USE_SSL: True
|
||||
DJANGO_SENTRY_DSN: https://5aead03f03505da5130af6d642c42faf@sentry.incubateur.net/202
|
||||
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
|
||||
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
|
||||
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
|
||||
OIDC_OP_USER_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/userinfo
|
||||
OIDC_OP_LOGOUT_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/session/end
|
||||
OIDC_RP_CLIENT_ID:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: OIDC_RP_CLIENT_ID
|
||||
OIDC_RP_CLIENT_SECRET:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: OIDC_RP_CLIENT_SECRET
|
||||
OIDC_RP_SIGN_ALGO: RS256
|
||||
OIDC_RP_SCOPES: "openid email given_name usual_name"
|
||||
OIDC_REDIRECT_ALLOWED_HOSTS: https://visio-staging.beta.numerique.gouv.fr
|
||||
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
|
||||
LOGIN_REDIRECT_URL: https://visio-staging.beta.numerique.gouv.fr
|
||||
LOGIN_REDIRECT_URL_FAILURE: https://visio-staging.beta.numerique.gouv.fr
|
||||
LOGOUT_REDIRECT_URL: https://visio-staging.beta.numerique.gouv.fr
|
||||
DB_HOST:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: host
|
||||
DB_NAME:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: database
|
||||
DB_USER:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: username
|
||||
DB_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: password
|
||||
DB_PORT:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: port
|
||||
POSTGRES_USER:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: username
|
||||
POSTGRES_DB:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: database
|
||||
POSTGRES_PASSWORD:
|
||||
secretKeyRef:
|
||||
name: postgresql.postgres.libre.sh
|
||||
key: password
|
||||
REDIS_URL:
|
||||
secretKeyRef:
|
||||
name: redis.redis.libre.sh
|
||||
key: url
|
||||
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
LIVEKIT_API_SECRET:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_SECRET
|
||||
LIVEKIT_API_KEY:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: LIVEKIT_API_KEY
|
||||
LIVEKIT_API_URL: https://livekit-staging.beta.numerique.gouv.fr
|
||||
ALLOW_UNREGISTERED_ROOMS: False
|
||||
FRONTEND_ANALYTICS: "{'id': 'phc_RPYko028Oqtj0c9exLIWwrlrjLxSdxT0ntW0Lam4iom', 'host': 'https://product.visio-staging.beta.numerique.gouv.fr'}"
|
||||
FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041'}"
|
||||
AWS_S3_ENDPOINT_URL:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: url
|
||||
AWS_S3_ACCESS_KEY_ID:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: accessKey
|
||||
AWS_S3_SECRET_ACCESS_KEY:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: secretKey
|
||||
AWS_STORAGE_BUCKET_NAME:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: bucket
|
||||
AWS_S3_REGION_NAME: local
|
||||
RECORDING_ENABLE: True
|
||||
RECORDING_STORAGE_EVENT_ENABLE: True
|
||||
RECORDING_STORAGE_EVENT_TOKEN:
|
||||
secretKeyRef:
|
||||
name: backend
|
||||
key: RECORDING_STORAGE_EVENT_TOKEN
|
||||
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
|
||||
SUMMARY_SERVICE_API_TOKEN:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: APP_API_TOKEN
|
||||
|
||||
createsuperuser:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py createsuperuser --email $DJANGO_SUPERUSER_EMAIL --password $DJANGO_SUPERUSER_PASSWORD
|
||||
restartPolicy: Never
|
||||
|
||||
frontend:
|
||||
image:
|
||||
repository: lasuite/meet-frontend
|
||||
pullPolicy: Always
|
||||
tag: "main"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
host: visio-staging.beta.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
tls:
|
||||
enabled: true
|
||||
additional:
|
||||
- secretName: transitional-tls
|
||||
hosts:
|
||||
- {{ .Values.newDomain }}
|
||||
|
||||
ingressAdmin:
|
||||
enabled: true
|
||||
host: visio-staging.beta.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/auth-signin: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/start
|
||||
nginx.ingress.kubernetes.io/auth-url: https://oauth2-proxy-preprod.beta.numerique.gouv.fr/oauth2/auth
|
||||
tls:
|
||||
enabled: true
|
||||
additional:
|
||||
- secretName: transitional-tls
|
||||
hosts:
|
||||
- {{ .Values.newDomain }}
|
||||
|
||||
posthog:
|
||||
ingress:
|
||||
enabled: true
|
||||
host: product.visio-staging.beta.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: eu.i.posthog.com
|
||||
nginx.ingress.kubernetes.io/backend-protocol: https
|
||||
|
||||
ingressAssets:
|
||||
enabled: true
|
||||
host: product.visio-staging.beta.numerique.gouv.fr
|
||||
className: nginx
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/upstream-vhost: eu-assets.i.posthog.com
|
||||
nginx.ingress.kubernetes.io/backend-protocol: https
|
||||
|
||||
summary:
|
||||
replicas: 1
|
||||
envVars:
|
||||
APP_NAME: summary-microservice
|
||||
APP_API_TOKEN:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: APP_API_TOKEN
|
||||
AWS_S3_ENDPOINT_URL:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: url
|
||||
AWS_S3_ACCESS_KEY_ID:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: accessKey
|
||||
AWS_S3_SECRET_ACCESS_KEY:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: secretKey
|
||||
AWS_STORAGE_BUCKET_NAME:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: bucket
|
||||
AWS_S3_REGION_NAME: local
|
||||
OPENAI_API_KEY:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: OPENAI_API_KEY
|
||||
WEBHOOK_API_TOKEN:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: WEBHOOK_API_TOKEN
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
CELERY_BROKER_URL:
|
||||
secretKeyRef:
|
||||
name: redis-summary.redis.libre.sh
|
||||
key: url
|
||||
CELERY_RESULT_BACKEND:
|
||||
secretKeyRef:
|
||||
name: redis-summary.redis.libre.sh
|
||||
key: url
|
||||
|
||||
image:
|
||||
repository: lasuite/meet-summary
|
||||
pullPolicy: Always
|
||||
tag: "main"
|
||||
|
||||
celery:
|
||||
replicas: 1
|
||||
envVars:
|
||||
APP_NAME: summary-microservice
|
||||
APP_API_TOKEN:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: APP_API_TOKEN
|
||||
AWS_S3_ENDPOINT_URL:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: endpoint
|
||||
AWS_S3_ACCESS_KEY_ID:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: accessKey
|
||||
AWS_S3_SECRET_ACCESS_KEY:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: secretKey
|
||||
AWS_STORAGE_BUCKET_NAME:
|
||||
secretKeyRef:
|
||||
name: meet-media-storage.bucket.libre.sh
|
||||
key: bucket
|
||||
AWS_S3_REGION_NAME: local
|
||||
OPENAI_API_KEY:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: OPENAI_API_KEY
|
||||
WEBHOOK_API_TOKEN:
|
||||
secretKeyRef:
|
||||
name: summary
|
||||
key: WEBHOOK_API_TOKEN
|
||||
WEBHOOK_URL: https://www.mock-impress.com/webhook/
|
||||
CELERY_BROKER_URL:
|
||||
secretKeyRef:
|
||||
name: redis-summary.redis.libre.sh
|
||||
key: url
|
||||
CELERY_RESULT_BACKEND:
|
||||
secretKeyRef:
|
||||
name: redis-summary.redis.libre.sh
|
||||
key: url
|
||||
SENTRY_IS_ENABLED: True
|
||||
SENTRY_DSN: https://5aead03f03505da5130af6d642c42faf@sentry.incubateur.net/202
|
||||
|
||||
image:
|
||||
repository: lasuite/meet-summary
|
||||
pullPolicy: Always
|
||||
tag: "main"
|
||||
|
||||
command:
|
||||
- "celery"
|
||||
- "-A"
|
||||
- "summary.core.celery_worker"
|
||||
- "worker"
|
||||
- "--pool=solo"
|
||||
- "--loglevel=info"
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ClusterSecretStore
|
||||
metadata:
|
||||
name: bitwarden-login-visio
|
||||
namespace: {{ $.Release.Namespace | quote }}
|
||||
spec:
|
||||
provider:
|
||||
webhook:
|
||||
url: "http://bitwarden-cli-visio.meet.svc.cluster.local:8087/object/item/{{`{{ .remoteRef.key }}`}}"
|
||||
headers:
|
||||
Content-Type: application/json
|
||||
result:
|
||||
jsonPath: "$.data.login.{{`{{ .remoteRef.property }}`}}"
|
||||
@@ -0,0 +1,28 @@
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: backend
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
refreshInterval: "1m"
|
||||
target:
|
||||
name: backend
|
||||
deletionPolicy: Delete
|
||||
template:
|
||||
type: Opaque
|
||||
data:
|
||||
{{- range .Values.secrets }}
|
||||
{{ .podVariable }}: |-
|
||||
{{`{{`}} {{ print "." .name }} {{`}}`}}
|
||||
{{- end }}
|
||||
data:
|
||||
{{- range .Values.secrets }}
|
||||
- secretKey: {{ .name }}
|
||||
sourceRef:
|
||||
storeRef:
|
||||
name: {{ .clusterSecretStore }}
|
||||
kind: ClusterSecretStore
|
||||
remoteRef:
|
||||
key: {{ .itemId }}
|
||||
property: {{ .field }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bitwarden-cli-visio
|
||||
namespace: {{ $.Release.Namespace | quote }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: bitwarden-cli
|
||||
app.kubernetes.io/name: bitwarden-cli
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: bitwarden-cli
|
||||
app.kubernetes.io/instance: bitwarden-cli
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: bitwarden-cli
|
||||
app.kubernetes.io/instance: bitwarden-cli
|
||||
spec:
|
||||
containers:
|
||||
- name: bitwarden-cli
|
||||
image: lasuite/vaultwarden-api:0.1
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: BW_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: bitwarden-cli-visio
|
||||
key: BW_HOST
|
||||
- name: BW_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: bitwarden-cli-visio
|
||||
key: BW_USERNAME
|
||||
- name: BW_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: bitwarden-cli-visio
|
||||
key: BW_PASSWORD
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8087
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- wget
|
||||
- -q
|
||||
- http://127.0.0.1:8087/sync?force=true
|
||||
- --post-data=''
|
||||
initialDelaySeconds: 20
|
||||
failureThreshold: 3
|
||||
timeoutSeconds: 10
|
||||
periodSeconds: 120
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8087
|
||||
initialDelaySeconds: 20
|
||||
failureThreshold: 3
|
||||
timeoutSeconds: 1
|
||||
periodSeconds: 10
|
||||
startupProbe:
|
||||
tcpSocket:
|
||||
port: 8087
|
||||
initialDelaySeconds: 10
|
||||
failureThreshold: 30
|
||||
timeoutSeconds: 1
|
||||
periodSeconds: 5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: bitwarden-cli-visio
|
||||
namespace: {{ $.Release.Namespace | quote }}
|
||||
labels:
|
||||
app.kubernetes.io/instance: bitwarden-cli
|
||||
app.kubernetes.io/name: bitwarden-cli
|
||||
annotations:
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8087
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: bitwarden-cli
|
||||
app.kubernetes.io/instance: bitwarden-cli
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Redis
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
disableAuth: false
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Postgres
|
||||
metadata:
|
||||
name: postgresql
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
database: meet
|
||||
@@ -1,55 +0,0 @@
|
||||
{{ if .Values.addRedirect }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
{{ if .Values.enablePermanentRedirect }}
|
||||
nginx.ingress.kubernetes.io/permanent-redirect: "https://{{ .Values.newDomain }}$request_uri"
|
||||
nginx.ingress.kubernetes.io/permanent-redirect-code: "308"
|
||||
{{ end }}
|
||||
name: temporary-redirect
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: {{ .Values.oldDomain }}
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: meet-frontend
|
||||
port:
|
||||
number: 80
|
||||
path: /
|
||||
pathType: Prefix
|
||||
- backend:
|
||||
service:
|
||||
name: meet-backend
|
||||
port:
|
||||
number: 80
|
||||
path: /api
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.oldDomain }}
|
||||
secretName: transitional-tls
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: transitional-tls
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
dnsNames:
|
||||
- {{ .Values.newDomain }}
|
||||
- {{ .Values.oldDomain }}
|
||||
issuerRef:
|
||||
group: cert-manager.io
|
||||
kind: ClusterIssuer
|
||||
name: {{ index .Values.ingress.annotations "cert-manager.io/cluster-issuer" }}
|
||||
secretName: transitional-tls
|
||||
usages:
|
||||
- digital signature
|
||||
- key encipherment
|
||||
{{ end }}
|
||||
@@ -1,8 +0,0 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Bucket
|
||||
metadata:
|
||||
name: meet-media-storage
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
provider: data
|
||||
versioned: true
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: core.libre.sh/v1alpha1
|
||||
kind: Redis
|
||||
metadata:
|
||||
name: redis-summary
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
spec:
|
||||
disableAuth: false
|
||||
+62
-27
@@ -1,27 +1,12 @@
|
||||
environments:
|
||||
dev-keycloak:
|
||||
values:
|
||||
- version: 0.0.1
|
||||
- env.d/{{ .Environment.Name }}/values.secrets.yaml
|
||||
dev:
|
||||
values:
|
||||
- version: 0.0.1
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
staging:
|
||||
values:
|
||||
- version: 0.0.1
|
||||
addRedirect: True
|
||||
enablePermanentRedirect: True
|
||||
oldDomain: meet-staging.beta.numerique.gouv.fr
|
||||
newDomain: visio-staging.beta.numerique.gouv.fr
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
production:
|
||||
values:
|
||||
- version: 0.0.1
|
||||
addRedirect: True
|
||||
enablePermanentRedirect: True
|
||||
oldDomain: meet.numerique.gouv.fr
|
||||
newDomain: visio.numerique.gouv.fr
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
- env.d/{{ .Environment.Name }}/values.secrets.yaml
|
||||
|
||||
repositories:
|
||||
- name: bitnami
|
||||
@@ -32,7 +17,8 @@ repositories:
|
||||
|
||||
releases:
|
||||
- name: postgres
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name | toYaml }}
|
||||
missingFileHandler: Warn
|
||||
namespace: {{ .Namespace }}
|
||||
chart: bitnami/postgresql
|
||||
version: 13.1.5
|
||||
@@ -45,9 +31,50 @@ releases:
|
||||
enabled: true
|
||||
autoGenerated: true
|
||||
|
||||
- name: minio
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
- name: keycloak
|
||||
installed: {{ eq .Environment.Name "dev-keycloak" | toYaml }}
|
||||
missingFileHandler: Warn
|
||||
namespace: {{ .Namespace }}
|
||||
chart: bitnami/keycloak
|
||||
version: 17.3.6
|
||||
values:
|
||||
- postgresql:
|
||||
auth:
|
||||
username: keycloak
|
||||
password: keycloak
|
||||
database: keycloak
|
||||
- extraEnvVars:
|
||||
- name: KEYCLOAK_EXTRA_ARGS
|
||||
value: "--import-realm"
|
||||
- name: KC_HOSTNAME_URL
|
||||
value: https://keycloak.127.0.0.1.nip.io
|
||||
- extraVolumes:
|
||||
- name: import
|
||||
configMap:
|
||||
name: meet-keycloak
|
||||
- extraVolumeMounts:
|
||||
- name: import
|
||||
mountPath: /opt/bitnami/keycloak/data/import/
|
||||
- auth:
|
||||
adminUser: su
|
||||
adminPassword: su
|
||||
- proxy: edge
|
||||
- ingress:
|
||||
enabled: true
|
||||
hostname: keycloak.127.0.0.1.nip.io
|
||||
- extraDeploy:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: meet-keycloak
|
||||
data:
|
||||
meet.json: |
|
||||
{{ readFile "../../docker/auth/realm.json" | replace "http://localhost:3200" "https://meet.127.0.0.1.nip.io" | indent 14 }}
|
||||
|
||||
- name: minio
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name | toYaml }}
|
||||
namespace: {{ .Namespace }}
|
||||
missingFileHandler: Warn
|
||||
chart: bitnami/minio
|
||||
version: 12.10.10
|
||||
values:
|
||||
@@ -75,7 +102,8 @@ releases:
|
||||
name: mkcert
|
||||
|
||||
- name: redis
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name | toYaml }}
|
||||
missingFileHandler: Warn
|
||||
namespace: {{ .Namespace }}
|
||||
chart: bitnami/redis
|
||||
version: 18.19.2
|
||||
@@ -85,7 +113,8 @@ releases:
|
||||
architecture: standalone
|
||||
|
||||
- name: extra
|
||||
installed: {{ ne .Environment.Name "dev" | toYaml }}
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name | toYaml }}
|
||||
missingFileHandler: Warn
|
||||
namespace: {{ .Namespace }}
|
||||
chart: ./extra
|
||||
secrets:
|
||||
@@ -100,26 +129,32 @@ releases:
|
||||
- name: meet
|
||||
version: {{ .Values.version }}
|
||||
namespace: {{ .Namespace }}
|
||||
missingFileHandler: Warn
|
||||
chart: ./meet
|
||||
values:
|
||||
- env.d/{{ .Environment.Name }}/values.meet.yaml.gotmpl
|
||||
- env.d/{{ .Environment.Name }}/values.secrets.yaml
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
|
||||
- name: livekit
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name | toYaml }}
|
||||
missingFileHandler: Warn
|
||||
namespace: {{ .Namespace }}
|
||||
chart: livekit/livekit-server
|
||||
values:
|
||||
- env.d/{{ .Environment.Name }}/values.livekit.yaml.gotmpl
|
||||
- env.d/{{ .Environment.Name }}/values.secrets.yaml
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
|
||||
- name: livekit-egress
|
||||
installed: {{ eq .Environment.Name "dev" | toYaml }}
|
||||
installed: {{ regexMatch "^dev.*" .Environment.Name | toYaml }}
|
||||
missingFileHandler: Warn
|
||||
namespace: {{ .Namespace }}
|
||||
chart: livekit/egress
|
||||
values:
|
||||
- env.d/{{ .Environment.Name }}/values.egress.yaml.gotmpl
|
||||
- env.d/{{ .Environment.Name }}/values.secrets.yaml
|
||||
secrets:
|
||||
- env.d/{{ .Environment.Name }}/secrets.enc.yaml
|
||||
|
||||
@@ -19,7 +19,6 @@ spec:
|
||||
{{- with .Values.backend.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
|
||||
labels:
|
||||
{{- include "meet.common.selectorLabels" (list . $component) | nindent 8 }}
|
||||
spec:
|
||||
|
||||
@@ -19,7 +19,6 @@ spec:
|
||||
{{- with .Values.celery.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
|
||||
labels:
|
||||
{{- include "meet.common.selectorLabels" (list . $component) | nindent 8 }}
|
||||
spec:
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: backend
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install,pre-upgrade
|
||||
"helm.sh/hook-weight": "-5"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation
|
||||
stringData:
|
||||
DJANGO_SUPERUSER_EMAIL: {{ .Values.djangoSuperUserEmail }}
|
||||
DJANGO_SUPERUSER_PASSWORD: {{ .Values.djangoSuperUserPass }}
|
||||
DJANGO_SECRET_KEY: {{ .Values.djangoSecretKey }}
|
||||
OIDC_RP_CLIENT_ID: {{ .Values.oidc.clientId }}
|
||||
OIDC_RP_CLIENT_SECRET: {{ .Values.oidc.clientSecret }}
|
||||
LIVEKIT_API_SECRET: {{ .Values.livekitApi.secret }}
|
||||
LIVEKIT_API_KEY: {{ .Values.livekitApi.key }}
|
||||
RECORDING_STORAGE_EVENT_TOKEN: {{ .Values.recordingStorageEventToken }}
|
||||
@@ -19,7 +19,6 @@ spec:
|
||||
{{- with .Values.summary.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }}
|
||||
labels:
|
||||
{{- include "meet.common.selectorLabels" (list . $component) | nindent 8 }}
|
||||
spec:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: summary
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
annotations:
|
||||
"helm.sh/hook": pre-install,pre-upgrade
|
||||
"helm.sh/hook-weight": "-5"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation
|
||||
stringData:
|
||||
APP_API_TOKEN: {{ .Values.appApiToken }}
|
||||
OPENAI_API_KEY: {{ .Values.openaiApiKey }}
|
||||
WEBHOOK_API_TOKEN: {{ .Values.webhookApiToken }}
|
||||
|
||||
@@ -128,6 +128,16 @@ backend:
|
||||
- "--no-input"
|
||||
restartPolicy: Never
|
||||
|
||||
## @param backend.createsuperuser.command backend migrate command
|
||||
## @param backend.createsuperuser.restartPolicy backend migrate job restart policy
|
||||
createsuperuser:
|
||||
command:
|
||||
- "/bin/sh"
|
||||
- "-c"
|
||||
- |
|
||||
python manage.py createsuperuser --email $DJANGO_SUPERUSER_EMAIL --password $DJANGO_SUPERUSER_PASSWORD
|
||||
restartPolicy: Never
|
||||
|
||||
## @param backend.probes.liveness.path [nullable] Configure path for backend HTTP liveness probe
|
||||
## @param backend.probes.liveness.targetPort [nullable] Configure port for backend HTTP liveness probe
|
||||
## @param backend.probes.liveness.initialDelaySeconds [nullable] Configure initial delay for backend liveness probe
|
||||
|
||||
Generated
+5
-4
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.12",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@html-to/text-cli": "0.5.4",
|
||||
@@ -344,8 +344,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"license": "MIT",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mail_mjml",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.12",
|
||||
"description": "An util to generate html and text django's templates from mjml templates",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -8,16 +8,16 @@ dependencies = [
|
||||
"pydantic>=2.5.0",
|
||||
"pydantic-settings>=2.1.0",
|
||||
"celery==5.4.0",
|
||||
"redis==4.5.4",
|
||||
"minio==7.2.9",
|
||||
"openai==1.55.3",
|
||||
"redis==5.2.1",
|
||||
"minio==7.2.12",
|
||||
"openai==1.57.1",
|
||||
"requests==2.32.3",
|
||||
"sentry-sdk[fastapi, celery]==2.19.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"ruff==0.7.4",
|
||||
"ruff==0.8.2",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Celery workers."""
|
||||
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
@@ -28,6 +29,7 @@ celery = Celery(
|
||||
)
|
||||
|
||||
if settings.sentry_dsn and settings.sentry_is_enabled:
|
||||
|
||||
@signals.celeryd_init.connect
|
||||
def init_sentry(**_kwargs):
|
||||
"""Initialize sentry."""
|
||||
@@ -100,15 +102,20 @@ def process_audio_transcribe_summarize(filename: str, email: str, sub: str):
|
||||
api_key=settings.openai_api_key, base_url=settings.openai_base_url
|
||||
)
|
||||
|
||||
logger.debug("Querying transcription …")
|
||||
with open(temp_file_path, "rb") as audio_file:
|
||||
transcription = openai_client.audio.transcriptions.create(
|
||||
model=settings.openai_asr_model, file=audio_file
|
||||
)
|
||||
try:
|
||||
logger.debug("Querying transcription …")
|
||||
with open(temp_file_path, "rb") as audio_file:
|
||||
transcription = openai_client.audio.transcriptions.create(
|
||||
model=settings.openai_asr_model, file=audio_file
|
||||
)
|
||||
|
||||
transcription = transcription.text
|
||||
transcription = transcription.text
|
||||
|
||||
logger.debug("Transcription: \n %s", transcription)
|
||||
logger.debug("Transcription: \n %s", transcription)
|
||||
finally:
|
||||
if os.path.exists(temp_file_path):
|
||||
os.remove(temp_file_path)
|
||||
logger.debug("Temporary file removed: %s", temp_file_path)
|
||||
|
||||
instructions = get_instructions(transcription)
|
||||
summary_response = openai_client.chat.completions.create(
|
||||
@@ -118,8 +125,10 @@ def process_audio_transcribe_summarize(filename: str, email: str, sub: str):
|
||||
summary = summary_response.choices[0].message.content
|
||||
logger.debug("Summary: \n %s", summary)
|
||||
|
||||
# fixme - generate a title using LLM
|
||||
data = {
|
||||
"summary": summary,
|
||||
"title": "Votre résumé",
|
||||
"content": summary,
|
||||
"email": email,
|
||||
"sub": sub,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user