The new endpoint requires title and content, breaking
the original implementation. This hotfix ensures staging works
immediately while I plan an LLM-based solution
for title generation.
It led to an error, with two occurrences of the form's URL being
desynchronized. Fix this minor issue, and refactor the constant
in a constant file to be shared across the app.
Enabled recording feature in production. MinIO needs to trigger a webhook
when a new recording is saved. Secret will be updated in the upcoming commits.
Updated the webhook URL to the definitive version in docs.
Jacques also updated the webhook secret for authentication
against Impress API. Not tested locally.
We want to have a specific responsive menu on mobile browsers.
It also implied to refactor a bit the way the settings modals is opened
because it could be opened from this responsive menu, so in order to achive
that a specific context has been created in order to allow its opening
from any sub component of the control bar.
We want to be able to customize the variant which those toggle uses as
well as being able to trigger an event when the toggle is pressed. This
is going to be useful to close the responsive menu after each clic as
react-aria prevent click event propgation.
According to the documentation:
Dynacast dynamically pauses video layers that are
not being consumed by any subscribers, significantly
reducing publishing CPU and bandwidth usage.
Dynacast will be enabled if SVC codecs (VP9/AV1) are used.
Multi-codec simulcast requires dynacast
My goal is to reduce CPU and bandwidth usage for clients.
Dynacast is enabled both in OpenTalk and LiveKit demo app!
Adaptive stream is a key optimization in large room.
Enabled adaptive stream to automatically manage
the quality of subscribed video tracks, optimizing
for bandwidth and CPU usage.
When video elements are visible, it adjusts
the resolution based on the size of the largest visible
element. If no video elements are visible, it
temporarily pauses the track until they are visible again.
Additionally, introduced support for custom pixel density,
which defaults to `2` for high-density screens
(devicePixelRatio ≥ 3) or `1` for standard screens.
Setting it to `screen` allows the pixel density to match
the actual screen's devicePixelRatio, optimizing video
clarity on ultra-high-definition displays.
This ensures a balance between streaming quality
and resource consumption.
This might also significantly increase the bandwidth
consumed by people streaming on high definition screens.
It needs to be battle tested.
OpenTalk uses a adaptiveStream equals true, while LiveKit
demo app uses 'screen' value. I followed OpenTalk choice,
I was scared 'screen' value creates performance issues
for user with high resolution screen in poor network conditions.
As a mandatory codec in WebRTC specifications, VP8 serves as
the baseline for compatibility, making it the default choice
in LiveKit client configuration.
There is room for optimization.
Newer codecs like VP9 offer significant efficiency gains
compared to VP8, with a 23-33% improvement in compression
efficiency. This translates to better video quality at
the same bitrate or reduced bandwidth usage for the same quality.
VP9 is supported in Safari starting from version 15.0+,
and Firefox offers partial support. However, Firefox lacks
support for VP9's Scalable Video Coding (SVC).
With SVC, participants can send a single VP9 stream
with multiple spatial or temporal layers. This allows receivers
to dynamically adjust video quality by using lower layers when
resolution or bandwidth needs to be reduced, improving
adaptability in heterogeneous network conditions.
Simulcast, by contrast, sends multiple separate streams
at different resolutions. While widely supported in VP8 and VP9,
it consumes more bandwidth compared to SVC.
The configuration added here is based on the LiveKit demo app,
which defaults to VP9 when supported. OpenTalk’s configuration
also recommends VP9.
If a browser does not support VP9, LiveKit falls back to VP8 or
other codecs as needed. Notably, LiveKit disables VP9 encoding for
Firefox due to longstanding issues, but it can still decode VP9
streams and encode VP8 for outgoing streams. This ensures
compatibility with other participants, even in mixed environments
where some browsers use VP9 and others fallback to VP8.
In theory, participants do not all need to switch to a single codec,
as both LiveKit and browsers intelligently handle codec negotiation
on a per-participant basis. This dynamic adaptation ensures seamless
communication without manual intervention.
A similar challenge with codec compatibility was raised
in Jitsi two years ago, check issue #10657.
Before any release, this needs to be battle tested
with Firefox 115 browsers.
Introduced a global state to handle user preferences related to notifications.
The first use case is sound notifications, allowing users to disable them
based on feedback.
Additionally, the sound volume is now stored globally, making it easy to
configure in the future if needed. I've lowered the volume of
the notifications to make them more discreet.
Preferences are persisted in local storage, ensuring they are retained
between meetings.
Add STORAGE_KEYS object to centralize localStorage keys,
ensuring no key overlaps by maintaining a single source
of truth for key declarations across the app.
Might be premature, as only the notification store will be persisted.
Valtio allows state persistence in local storage, which is
necessary for the notification store. In this case, I'll need
to persist a `proxyMap`—a utility provided by Valtio to create
an observable map.
However, since `proxyMap` isn't natively serializable,
I'll need to implement two custom functions: one for serialization
and another for deserialization (revival).
Regarding the file structure, I've named the file `utils/valtio`,
but this can be discussed further. The purpose of this file is
to centralize common utility functions related to Valtio
for better organization and reuse.
I found the item names unclear, so I updated them for better clarity.
I also removed the unnecessary 'lowered' item and added
a TODO comment about handling the message received notification,
which is not yet implemented in the code.
Inspired by Robin's design, I've styled a React Aria
Switch component using our DSFR theme.
This is an initial draft and isn't yet pixel-perfect compared
to Robin's design. It also hasn't been integrated into
the form inputs yet.
Rely on Posthog for a first iteration on the feature flag feature.
This is a pragmatic choice, relying on an external dependency might
not suitable on the longer term, however, compare to the maturity
of our product, this is the best trade off.
Few frontend features rely on Posthog. Posthog is not activated in
dev environment. Offer a hook that encapsulates this logic, and
return a boolean flag.
Configure dev and staging environment to use our self-deployed
models (Whisper and LLM). Secrets need to be updated btw.
Because of outscale LB bug, which timeout after 60s, we need to
connect directly to the svc.
Feedback from one of our users.
I forgot animation can be disabled for accessibility purpose.
Fix it by bypassing the animation if reduce motion is activated.
Oopsie, mybad, I learnt something.
Declare Marianne font. Not sure of my configuration, lmk if
my configuration feel wrong @nvasse @manuhabitela.
It's a requirement in the public sector.
Based on Florian and Stéphanie feedbacks, add the 'Marianne' logo,
and a beta tag on our visio's logo.
I've slightly updated the header. Its responsive is broken in certain
situations.
While hot reloading the stack, I got an error, Crisp not being
defined at the time of the component rendering. Prevent such issue.
I am not 100% sure this commit is useful, WDYT?
Discussed IRL with Robin and Natan. Previous iteration wasn't
appropriated for a visioconference tool.
Try a trade off between nice colors and DSFR. We will probably
continue to iterate on these.
Got an unexpected error while instantiating the openai client
since I installed sentry deps. I've upgraded openai version
without digging much, and it worked again.
Draft a piece of code to try the feature in staging. I'll consolidate this
implementation ASAP, as soon we have a first implementation functional.
What's missing?
- when owners are multiple
- retry when the backend cannot reach the summary service
- factorize the key oneliner, duplicated from the egress service
- optimize SQL query
- unit tests
Share the same project as the microservice one. Will see in the future
if we need a separate sentry account for the backend. Good enough
at this project stage.
This is written in a rush, boostrap the real logic with celery
worker to process meeting recording.
Lack of unit tests is critical, I am not proud of a it. I am
totally in a hurry for the demo. Not sure we will actually keep
this microservice.
Update values for dev and staging environment to enable
recording-related endpoints. A new secret need to be created.
Production values will be added in an upcoming commit.
This commit solves few issues:
- sharing the relevant certificates with minio so when triggering the webhook
notification, the minio pod can verify our backend domain certificates.
- making sure everything spawn in the right namespace (LiveKit and the Egress)
without relying on a dirty fix in the make start-tilt.
all these fixes were made by @rouja, I don't fully understand them yet.
He simplified the stack, removing two Kind nodes to make it lightweight.
thx @rouja.
Quick fix, not optimal. Removed a useless breaking point IMO.
I won't spend too much time on the Header component, I'll
introduce new features needing a heavy layout update.
Needed a trade off between xs and sm breaking points.
Introduce xsm, I am not very satisfied with this naming.
We might refactor xs in xss. Not urgent.
Refactored the feedback alert button into a more discreet and polished banner.
The banner provides additional context about Visio being under construction.
Also updated the link to the feedback form.
Previously, language detection was failing to read browser settings correctly.
Added explicit detection order to ensure localStorage preferences are checked
before falling back to browser language.
I've created a beta form, allowing interested visitors to
register to be a beta user.
This form is yet created with Grist, could be enhance. I'll
investigate using PostHog.
Avoided installing a dependency for such small piece of code.
I've implemented a naive component, which allow users explore
the slide presenting key feature of our app.
User experience should be ok. However, I might need to optimize
image format and loading strategy. First 'raw' iteration, gonna
optimize it in the future.
I found original to hide text decoration on hover. It felt wrong
to me, I proposed a new animation. Please feel free to rework it
or enhance it in the future.
Created a component for an intro slider to showcase app features.
Each slide will include a minimal illustration and concise descriptions.
Work In Progress, Slide logic will be added in upcomming commits.
Oooopsie while merging chat, I forgot to test horizontal scrolling.
It ended up creating a scroll issue. Fixed!
This fix might create a new issue for mobile users, I'll refactor
their layout ASAP.
Streamline user joining a room by pre-fillig name field from
their ProConnect data. Reduces friction and typos in the join flow.
This feature will be mostly used by new users. Recurrent users have
their previous choices persisted in local storage.
Request the necessary scopes from ProConnect service.
Update configurations in every environments.
Note: ask given_name and usual_name scopes to get users' info.
(these scopes should be granted by default by ProConnect when
requesting a client id client secret)
Inspired by @sampaccoud's eee2003 commit on impress, adapt the code to be more
Pythonic. Add basic test coverage for user name synchronization on login. User
name fields now update automatically at each login when new data is available.
Note: current logic doesn't handle the case where a user with existing names
logs in with missing first/last names - should we clear the names then?
Removing a field that was present in the initial form is not a valid update
operation.
Following @sampaccoud's work on impress, add new fields to handle
user names in our application.
@sampaccoud preferred having a full and short names instead of
a basic first and last ones, to follow common good practices, and
avoid having frontend formating names (from my understanding).
Please see commit eee20033 on Impress.
DINUM users face connection issues in bandwidth-constrained environments.
They cannot reach their room, the peer connection timeouts while negotiating
TURN/TLS handshake (from our current understanding).
I am not sure this issue is linked to those parameters, at least try something.
I've protected this endpoint with a feature flag, and an authentication
class, as it will be exposed on the public internet.
I've tried to keep the viewset logic as minimal as possible, I've
to ship smth and will continue iterating on this piece of code.
At some point, abstracting webhook endpoint and authentication class
might be beneficial for the project. YAGNI as of today.
A recording is savable only if it's active or stopped. In other
status (error, already saved, etc.) it won't be possible. I might
iterate on this piece of code. Let's ship it.
When a new file is uploaded to a Minio Bucket, a webhook can be
configured to notify third parties about the event. Basically,
it's a POST call with a payload providing informations on the
event that just happened.
When a recording worker will stop, it will upload its data to a Minio
bucket, which will trigger the webhook.
Try to introduce the minimalest code to parse these events, discard
them whener it's relevant, and extract the recording ID, thus we
know which recording was successfully saved to the Minio bucket.
In the longer runner, it will trigger a callback.
Addressed a `DeprecationWarning` in `RecordingFactory` related to the
`_after_postgeneration` method, which will stop saving the instance after
postgeneration hooks in the next major release. To resolve this,
`skip_postgeneration_save=True` was added to `RecordingFactory.Meta` to
avoid extraneous save calls. Alternatively, if instance saving is needed,
the save call can be moved to postgeneration hooks or by overriding
`after_postgeneration`.
Avoided unnecessary manipulation of the room name to prevent issues when
starting an egress worker. Previously, hyphens were stripped from the room
name, likely inherited from the legacy setup with Jitsi in Magnify, though
the purpose of this change is unclear and might be an undesired legacy
feature.
To ensure accurate room matching during egress worker requests, this update
removes any manipulation of the room name. This approach minimizes the risk
of errors when initiating recordings and maintains the integrity of the
original room name throughout the process.
The LiveKit egress worker interactions are proxied through the backend for
security reasons. Allowing clients to directly use tokens with sufficient
grants to start recordings could lead to misuse, enabling users to spam the
egress worker API and potentially initiate a DDOS attack on the egress
service. To prevent this, only users with room-specific privileges can
initiate recordings.
We make sure only one recording at the time can be made on a room.
The requested recording mode is stored so it can be referenced later when
the recording is saved, triggering a callback action as needed.
A feature flag was also introduced for this capability; while this is a simple
approach, a more robust system for managing feature flags could be valuable
long-term. For now, KISS (Keep It Simple, Stupid) applies.
The viewset endpoints were designed to be as straightforward as possible—
let me know if anything can be improved.
Introducing a new worker service architecture. Sorry for the long commit.
This design adheres to several key principles, primarily the Single
Responsibility Principle. Dependency Injection and composition are
prioritized over inheritance, enhancing modularity and maintainability.
Interactions between the backend and external workers are encapsulated in
classes implementing a common `WorkerService` interface. I chose Protocol
over an abstract class for agility, aligning closely with static typing
without requiring inheritance. Each `WorkerService` implementation can
independently manage recordings according to its specific requirements.
This flexibility ensures that adding a new worker service, such as for
LiveKit, can be done without any change to existing components.
Configuration management is centralized in a single `WorkerServiceConfig`
class, which loads and provides all settings for different worker
implementations, keeping configurations organized and extensible. The worker
service class itself handles accessing relevant configurations as needed,
simplifying the configuration process.
A basic dictionary in Django settings acts as a factory, responsible for
instantiating the correct worker service based on the client's request mode.
This approach aligns with Django development conventions, emphasizing
simplicity. While a full factory class with a builder pattern could provide
future flexibility, YAGNI (You Aren't Gonna Need It) suggests deferring
such complexity until it’s necessary.
At the core of this design is the worker mediator, which decouples worker
service implementations from the Django ORM and manages database state
according to worker state. The mediator is purposefully limited in
responsibility, handling only what’s essential. It doesn’t instantiate
worker services directly; instead, services are injected via composition,
allowing the mediator to manage any object conforming to the `WorkerService`
interface. This setup preserves flexibility and maintains a clear
separation of responsibilities. The factory create worker services,
the mediator runs it.
(sorry for this long commit)
Write the proper ORM code to sanitize the rows in db and avoid
existing users lose access to our app.
Existing duplicate user accounts are merged, and resource accesses
are transferred.
Add test cases for email-based user matching fallback logic:
- String comparison edge cases
- Multiple users with matching email addresses
- Invalid email format handling
Fix will follow in subsequent commit.
When OIDC providers return random values in the "sub" field instead of stable
identifiers, implement email-based user matching as fallback.
Note: Current implementation needs improvement. Tests forthcoming.
Original: @sampaccoud (ff7914f) on Impress
Handle case where user is inactive.
Previously this edge case would cause unexpected behavior.
Related to previous commit that added the test coverage.
Remove redundant wrapper method that duplicates SUB validation logic.
Already validated in parent class, following the pattern established by
@sampaccoud in People and Impress modules.
Handle case where sub value is an empty string instead of None.
Previously this edge case would cause unexpected behavior.
Related to previous commit that added the test coverage.
Add failing test for corner case when sub value is an empty string.
This edge case was discovered by @sampaccoud and was previously untested.
Fix will follow in subsequent commit.
Add missing hint to guide user entering their name while
joining a room.
I introduced this bug while merging @manuhabitela works on
the newly prejoin screen.
Manage Recording in the Django Admin. As of today, I have not enforced
a strict policy to avoid edit on recording rows or even creating new
data point directly from the admin. Will do in the future.
Implements routes to manage recordings within rooms, following the patterns
established in Impress. The viewset exposes targeted endpoints rather than
full CRUD operations, with recordings being created (soon) through
room-specific routes (e.g. room/123/start-recording).
The implementation draws from @sampaccoud's initial work and advices.
Review focus areas:
- Permission implementation choices
- Serializer design and structure
Credit: Initial work by @sampaccoud
The Recording model is introduced to track recording lifecycle within rooms,
while maintaining strict separation of access controls between rooms and
recordings.
Recordings follow the BaseAccess pattern (similar to Documents in Impress),
providing independent access control from room permissions. This ensures that
joining a room doesn't automatically grant access to previous recordings,
allowing for more flexible permission management.
The implementation was driven by TDD, particularly for the get_abilities
function, resulting in reduced nesting levels and improved readability.
The Recording model is deliberately kept minimal to serve as a foundation for
upcoming AI features while maintaining flexibility for future extensions.
I have avoided LiveKit-specific terminology for better abstraction.
Note: Room access control remains unchanged in this commit, pending future
refactor to use BaseAccess pattern (discussed IRL with @sampaccoud).
Renames a Django setting in the Resource base class to use resource-agnostic
terminology. While rooms are currently the only resource type, keeping base
class naming generic improves clarity.
Previously, the Docker Compose filename was hardcoded in _config.sh when used
through utility scripts. In recent commits, I've renamed the filename without
updating this configuration.
Oopsie, running make commands was fine, but running bin scripts
requiring compose failed.
Python module models.py raises too-many-ancestors warning, but linter
still pass, code is rated 10/10.
Inspired from @sampaccoud's commit #eee20032 on impress.
The version is now automatically guessed by Docker Compose. This
commit will fix the warning raised about the uselessness of this
setting.
from @sampaccoud
Egress is already deployed in staging. But, while
working locally on feature relying on Egress, it's not
suitable to test your development or iterate.
Especially I'll need to test the connection between the Egress
and the minio bucket in my next PR.
We faced quite a few issue while starting the whole stack.
Egress didn't want to start. Its connection with the livekit server
while the egress participant was joining the room was not successful.
The Turn part of the livekit server helm chart was activated. We needed
to update few values to in the helm configuration to enabled this turn.
Updated CoreDNS to expose Egress pod. Egress tries connecting to MinIO at
127.0.0.1, where no instance exists. Using minio.127.0.0.1.nip.io resolves
to 127.0.0.1, causing Egress to connect to itself for uploads. The CoreDNS
rewrite directs this to the Ingress IP, correctly routing to MinIO.
Inspired by Impress and @sampaccoud's work.
We use Indie Hoster’s Kubernetes objects in staging and production.
In the "dev" environment, we install the `bitnami/minio` chart to mimic
Indie Hoster’s MinIO setup.
To access the MinIO admin interface in dev, use port forwarding;
the interface runs on port 9001.
LiveKit Helm chart doesn't support namespace parameterization
like MinIO templates. Egress and server deploy to default namespace,
but frontend and backend need all components in the same namespace.
Workaround: force kubectl context to 'meet' before starting Tilt.
Future improvement needed in chart.
Issue #105 have been opened in livekit-helm repo.
Encapsulate all interactions with the layout store concerning the
side panel into a hook. This hook exposes a clear interface.
Each variable has a single responsability, with a clear naming.
Rename useWidget interaction to useSidePanel. Remove LiveKit-specific naming
as we no longer use LiveKit elements in the layout context. This change
improves clarity and reflects the current functionality of the hook.
Properly integrate chat into the sidepanel to improve UX and avoid disruptions.
Implement initial styling based on Google Meet's design, with plans for future
enhancements. Some details remain to be refined, such as preserving newline
characters in the message formatter.
This substantial commit refactors and cleans up a significant legacy component.
Chat notifications will be addressed in a separate PR.
Note: While this is a large commit, it represents a major improvement in user
experience (in my opinion).
Implement smooth animations and DOM persistence for sidepanel.
Side panel state should be kept alive, to match initial LiveKit team
intent.
Temporarily, remove chat component. Chat functionality will be absent
until next commits. It will be reintegrated in the side panel.
When a participant leaves a videoconference, remove all notifications they
authored. It's generally unnecessary to keep notifications about actions from
users who are no longer present, with few exceptions.
As we currently support only two notification types (Joined and Raised Hand),
it's appropriate to close all of them when the author leaves the room.
Changes introduced by LiveKit which deprecated some chat's options.
As we duplicated their code, let's just removed them.
They are not useful, and not in use anywhere.
Introduce a draft chat input component inspired by Google Meet design.
This component is not yet in use and requires further enhancements.
To be improved:
- Avoid sizing in pixels
- Replace hardcoded colors with theme variables
This lays the groundwork for a more interactive chat experience in the future.
Allow more flexibility in side panel behavior. Some panels, such as the chat,
require specific scrolling functionality:
- Header and footer should remain fixed
- Only chat messages should be scrollable
This change enables customization of scroll behavior for different panel types,
improving component reusability.
In object-oriented terms, the previous implementation violated the Liskov
Substitution Principle. Props between these two components (Button and Link)
were not substitutable.
This led to TypeScript errors and increased overall complexity without
significant DX gains. To address this, the LinkButton has been extracted
into a dedicated component.
Fix following warning messages :
- You have not set a value for the SECURE_HSTS_SECONDS setting.
- Your SECURE_SSL_REDIRECT setting is not set to True.
I am not a huge fan of changing the component's behavior based on
a if, but that the only way I found to have something quickly.
Actually, the push to talk feature will always only applies to the
microphone. No other devices will be concerned.
Reuse the active speaker indicator to quickly bootstrap the feature.
Some details should be improved in terms of UI. The UX is quite
decent.
Needed to support push to talk using the spacebar.
Introduce a utility hook. Will be called by the mic toggle
in the upcoming commits.
Inspired by Jitsi code.
React Query refetches stale data on window focus, which triggers
silent login attempts for logged-out users.
If user data becomes stale (after 1 hour), silent login redirects
the user to the auth domain, potentially disconnecting them during calls.
To prevent this, user data is now considered non-stale
during active sessions.
If the user logs in via another tab, a manual page
reload will be required to refresh their session.
Based on feedback from @rouja, I've updated the Helm configuration for PostHog
to use separate ingress resources for each service. Although the documentation
suggests sharing the same ingress, the services have different externalName
values, which conflicts with the use of a vhost in the ingress annotations.
This change ensures proper service redirection by aligning each service with
its own ingress.
Declare the expected support and analytics env variables
expected by the frontend for each environment. To avoid consuming
too much credits from our PostHog free tier plan.
Query API to get the App's configs.
Replacement for env variables, thus with a single image,
we can dynamically update frontend's behavior.
Code inspired by Marsha. Not sure having a single component
returning null is a good idea, to be discussed.
Inspired by Joanie.
In Frontend context, env variables are only available at build time,
not runtime. This is one of the easiest way to pass frontend dynamic
configurations while running.
This commit only exposes the view already existing.
Implement a chat feature that allows users to communicate directly with an
engineer for assistance with issues. We use Crisp for consistency, as it’s
already utilized in all other LaSuite products. Additionally, we need to
configure an environment variable in the frontend for better flexibility.
This is the initial implementation, and session handling will be refined
in future updates.
Decouple the code from the PostHog dependency by wrapping it in a custom hook
and several utility functions. This enhances code maintainability and separation
of concerns.
Fixed an issue where a setting cloned from Joanie was missing
in the Django configuration. Although its value was provided, it was
not applied due to the missing reference in the settings file.
We should use participant attributes, rather than metadata, to store hand
status (raised/lowered). However, the useParticipantInfo hook is currently
not suited for this, as it does not refresh when attributes change.
Be transparent with users: this version lacks quality and is currently
limited to Chrome, which may lead to frustration.
In addition to providing information, we should implement a method to
collect user feedback and reactions.
Simplified the logic to avoid redundancy. Removed unnecessary duplication of
eventGroups, as it's already exported from the core. My bad for the initial
complication — code is now cleaner.
Implemented using MediaPipe, which is not supported on Firefox due to
limitations outlined in issue #38 of the track-processors-js repo.
We decided to release this first version exclusively for Chrome to quickly
offer a solution to users. Future iterations will address broader compatibility.
An informational message will be added to notify users that the feature is
experimental. For now, a text label will be used in place of an icon.
Refactor side panel into a reusable component to display any interactive
content like menus, messages, participant lists, or effects. Establish it
as a core feature of the videoconference tool.
Improve extensibility and better share responsibilities. The next step is to
refactor the chat to render inside the side panel.
Increased all icons size, as they looked a bit small on screen.
Introduce a new component separator, to organize menu items
in section.
Work in progress, added the 'effects' item, which triggers no
action yet.
Recommended dependency to process video tracks, to add a virtual
background, or a blurring effect. Created and maintained by the
core LiveKit's team.
Please note, few issues are known. track-processors-js relies on
MediaPipe (by Google), which is not supported on Firefox …
Quick-win to ship a first draft.
When new secret is added to backend secret, it's not sync at the
beginning of argocd synchronisation and jobs are blocked. Theses new
annotations fix this issue.
Upgraded to livekit/components-react v2.5.4, fixing a bug in v2.5.3 that
caused repeated nesting of persistent user choices, exhausting the quota.
Thanks to Lukas for the quick fix.
PostHog's autocapture is useful, but explicit tracking ensures cleaner, more
focused data. This initial pass targets high-impact actions; we'll iterate as
needed.
Initialize Posthog script with our project's keys.
This key is public, it's not a secret.
Our data is hosted in Europe, followed Tchap integration,
which was reviewed by the ANSI.
Enhanced the share screen button by adding a tooltip and improving contrast for
better accessibility. Created a temporary icon by combining two from Remix, but
it’s bulky and will need refactoring soon.
Improved handling of the disconnect call by properly managing the returned
promise. Simplified the approach by skipping the useDisconnectButton hook.
Updated the button to display only an icon for a cleaner interface.
Ensure notification toasts are cleared when a participant disconnects,
preventing stale notifications from showing if the user quickly rejoins.
This resolves issues with duplicate or outdated notifications appearing.
Previously, I prevented clients from fetching new tokens when data is stale.
It seems totally wrong. Enabling data refresh addresses potential issues
with random user disconnections.
Please note the data refresh when stale was only disabled for people
who created the room.
While in focus, left tiles should be large enough to render
long name and still have a big enough size to get who is in the
tile. Temporary, but functional.
Due to responsiveness issues and awkward display on mobile screens.
Note: Chrome inspector's responsive mode inaccurately triggers mobile
behavior. It uses a temporary function imported from LiveKit.
Toast-related components are reusable. They follow React Aria recommendations.
The UI is heavily inspired by GMeet, we should iterate on it.
Why toast? It gives additional information and quick actions when an event
occurs in the room.
The ToastRegion design should be improved to be more extensible, to move away
from poor practices with conditional rendering.
Added initial toast display when users join, with room for further improvement.
Please read the fixme items.
Inspired by the props disableSpeakingIndicator, used the same
logic for all metadata displayed on hover on a participant's tile.
Why? I'll need to render the joining participant tile without any
metadata, so the participant see who is joining the room.
Absolutely needed some toasts to handle in app notifications,
or feedbacks. React Aria is working on it, and has released a
first beta. No component is available, only hooks.
Added user feedback for actions with explicit text indicating
URL will be copied to clipboard. Modeled after Jitsi's button
behavior for clarity and consistency in user experience.
Based on User's feedback.
Having the dialog position visually in the participant tile is
confusing for the user.
Position the dialog to be explicitly outside of a participant
tile container.
Users' feedbacks. Design is inspired from Whereby, a webinars
product. I am not a huge fan of having the indicator on the
left of the participant's name. It causes a shift of the name
when the participant unmute herself.
This should be discussed with the UX designer.
Users's feedbacks. Raised hands in participants' tiles were not
highlighted enough.
It was discussed adding a colorful border around the participant's
tile (idea inspired by Jitsi). Nonetheless, it conflicts with the
current blue border, indicating when a participant is speaking.
Until this blue border is present, it's not a great idea having a
second border indicating a new level of informations.
@sampacoud requested a yellow-ish/colorful indicator of when a
participant raised her hand, inspired by Jitsi. However, I think
yellow color should be reserved for warning or dangerous action.
I chose to use a white background around the participant's name,
to reinforce the visual indication a participant has her hand
raised, while still being discreet. It's inspired by Gmeet.
The Gmeet-inspired indicator was misleading for users,
at least those not used to GSuite products.
They didn't understand how to mute a participant.
Plus, having the button disabled for the local participant,
was creating confusion. To simplify the UX, have all
the buttons enabled is simpler to understand.
Empirical observations with a few number of users, should be
enhance and challenge in the long run.
'hash' built-in function is randomly seed by Python process.
In staging or production, our backend runs over 3 pods, thus 3
Python processes. For a given identity, it was not prompting
the same hash across all pods.
Why 'hash' is randomly seed? For security reasons, there was
a vulnerability disclosure exploiting key collision. Since Python 3.2,
'hash' is by default randomly seed.
Fixed it! Thx @jonathanperret for your help.
Height was varying when muting/unmuting the mic, so I added a minimum height to
avoid layout shift.
Proof of concept for the future UX: a raised hand would be placed on
the left side of the participant's name.
Having the mic indicator and raised hand side by side is temporary and totally
undesired (it feels super weird). I'll refactor the whole UX of the participant
tile in a dedicated PR.
Encapsulate code responsible for toggling hand,
and determining the hand state based on participant metadata.
This gonna be reused across the codebase.
Inspired from Gmeet. Add a button to lower all hands at once.
It should be quite useful for room moderator/admin.
The UX might feel strange, having this action button at the top of
the list, but I could not figure out a better layout. In terms of
UX I feel this is the best we can provide. However, the UI should
definitely be improved.
Show the raised hand waiting list, allow any participant to
lower other participants' hands.
Inspired from Gmeet. I found it quite clear to have a dedicated
waiting list for raised hands.
Will need it for lower hand action.
Actually, this piece of code should be enhanced. It'll act
as a corner stone of all actions dispatched in the interface.
I'll continue refactoring it later on.
Why? This layout is extensible. This menu will have two sections,
to separate in-room participants from the waiting room ones.
It's copied from Gmeet User Experience.
In-room participants will be divided in sub-lists based on their
states (ex: hand raised, …). This User Experience is extensible
if in the future with support subroom in a room or whatever.
Inspired by Magnify. Rely on participant metadata to determine,
if the local user has raised its hand.
There is a todo item in LiveKit code, useLocalParticipant is not
subscribed to metadata updates events.
The contrast of the toggled button in legacy Style is poor,
it needs to be urgently improved. User can barely distinguish if
their hands are raised.
These changes should be discussed. Needed for the audio selects,
that render super long text values.
By default, centered texts are sometime hard to read.
Removed annoying overlay that blocked the user. (feedback from @spaccoud)
Previous design required one click too many. As this dialog doesn't need
an overlay, I couldn't use the default primitive.
It's a hacky but functional, creating technical debt here.
Closer to the Gmeet design. Added a warning about permissions
since the rooms are public in beta.
Apologies @manuhabitela, this may impact accessibility for users unfamiliar
with the copy icon. Let's conduct user testing.
Messy code file organization, but functions are okay. Functional, but not
well-designed and likely hard to maintain. Shipping it, creating technical
debt here. Will be reused for interactions with LiveKit server API.
Participants need to be room admin to interact with LiveKit server
api. Until we offer private room, with moderation, all participants
will be considered as room admin.
note: room admin doesn't offer permission to record a room. please,
refer to LiveKit documentation to learn more.
This commit focuses on the UX more than the code quality. In facts,
we should design a centralized component/alert service, that we
can trigger anywhere in the codebase, when a user is prompted for
confirmation.
The alert modal with cancel/submit buttons and a description is generic.
Creating technical debt here.
Rapidly, a 'more option' button should be added to the row. Thus,
participants will have access to few more actions (ex: remove from
the room, pin camera, etc…).
This flex layout should be definitive, except the code which is dirty,
but not creating much technical debt here.
First raw iteration.
Tried replicating Gmeet UX and layout. However, in their ux, even if
action button are disabled, some tooltips explain to the user why they
cannot perform the action. With the default Trigger, it's not easily
feasible. As soon as the button is disabled, there is no way to trigger
the tooltip in an uncontrolled manner.
In upcoming commits I'll arrange layout, and call the remote LiveKit
ServerApi, to perform the mute action.
Main update: create a component, which has the responsability of
rendering a Participant in the list. I tried gettig closer to a
cleaner codebase, with small and well-scoped components. WIP.
Formatting participants before rendering the list was way to over
complicated, iterating twice on the list was kinda useless. Fixed!
Few updates:
- Removed capitalizing Participant's name to be consistent with the
avatar. This choices mimics GMeet UX.
- Duplicated isLocal utils function from LiveKit, which is not
exported by their package.
Avatar font size was way too small in the participant list context.
I tried improve it. Few font sizing are visually uncomfortable in
the participant list.
oupsy, heavily copied from Gmeet.
Thx Nathan Vasse for his css animation skills, he crushed it.
This component will be core in the app, and reused in many places.
It was necessary for the participants list.
Upgrading Django to 5.1 created a severe issue.
Migrate and create-superuser jobs were failing.
The issue originated from the third party easy_thumbnail.
Please refer to the issue #641 on their repo.
This is the suggested workaround by @Miketsukami.
It explains why participant initials doesn't update when a participant
changes its name.
Similar issue as the one faced while developing on the participant list.
It needs to be fixed at the root, in the LiveKit repo.
Duplicated source from LiveKit internal hooks (not ideal, but necessary)
to ensure the inner content of the participant placeholder consistently
fits its parent container. Unfortunately, I couldn't find
a simpler solution.
This might lead to some performances issues, as for each tile, some js
would be computing avatar's size, which is much more costly than pure
css…
Note: Gmeet achieves this by generating a temporary placeholder image
with a colored background and initials, allowing for a perfectly round,
responsive avatar without relying on JavaScript.
Default 'transparent' value avoid showing the avatar when
participant's info are not yet available. Might be a bad design
if we follow the single responsability pattern. Please feel free
to refactor it.
Color is stored in participant's metadata.
Using a hash of some participant's info allow to persist the color
for logged-in participant without storing the color in db.
Please feel free to tune the saturation and lightness range.
Code is inspired by a tutorial found online.
Inspired by Gmeet animation (mine is far from perfect).
Some details should be improved, especially to have a smooth
animation stop when the user stops speaking.
Duplicated LiveKit sources to customize the participant tile. It’s needed
to enhance the design and color of the participant placeholder.
More in the next commits.
Inspired by the Google Meet user experience.
A simple UX for scheduling meetings was requested by some users.
This is a quick and basic poc to validate the UX in a production
environment. If it gains traction, it will be refined and consolidated.
Inspired by GMeet user experience. Offer a sound tester button
to our users while configuring their audio output.
uprise.mp3 has a free license. We should credit them in our legal
terms asap.
First draft, it lacks important features, as a visual indicator when
the mic is active, and a trigger to test the audio output.
I made some heuristics (e.g. default output/input, etc..) to ship a
first version of this setting tabs that should work good enough to
create value for our current users.
Please refer to the inline comments.
Introduced a General settings tab, currently allowing language configuration.
Although this commit introduces some duplication, the default settings dialog
will be refactored in future updates.
The General tab is designed to accommodate additional features over time.
User can now update their name in a dedicated Account tab,
inspired by Jitsi design. Removed the username dialog.
Account-related updates will be operated through the advanced
settings dialog, in this dedicated tab.
Inspired by other videoconferencing tools' designs.
Sizes are mixing pixels and rem, it should be harmonized.
Will be used by the advanced settings panels.
Refactored it in a proper component. Initially planned to
use it in the Account Tab, but I changed my mind.
This refactoring is an enhancement, the Avatar props are better
encapsulated.
This component should be the definitive layout for settings.
Why extended? I kept the original one, which is still in-use in the
homepage, and considered this settings dialos as an extended version,
or advanced one.
I hope in a close future, we merge these two dialogs to avoid
maintaining too much code. Not the top prio.
Relevant TabPanel components will be introduced in the upcomming commits.
Even if the list is empty, previous assertion was returning true.
Fixed it, mybad, noob error.
In fact, the participants list should never be empty, as you are
in the meeting to access it.
The 'Box' component has a type 'dialog' which limits the Dialog size.
The default size is insufficient for larger dialogs like
the advanced settings.
To address this temporarily, I'm allowing the wrapper component to set the
Dialog size directly. This isn't ideal, but will enable better flexibility
until we finalize the user experience and dialog requirements.
I am not sure of the new props' naming introduced.
Added a new variant for Tabs and TabList components
to support borderless styles.
By default, both Tabs and TabList include borders.
Using React context, TabList’s children will automatically
inherit a borderless style if specified via props.
However, you can still explicitly apply borders
to individual children, even if the parent
TabList is set to be borderless.
Style react aria components and exposed them.
Tabs are needed for the advanced settings component.
Both orientations, horizontal and vertical, are supported.
Introduce an option to render or omit the title in the Dialog component.
This is necessary for the advanced settings Dialog, which displays its
title in the Tab menu.
This could affect existing component API created by @manuhabitela.
The component will become less opinionated, offering more flexibility
in its usage (I am not 100% convinced it's actually a great idea).
Quick fix! Let's enhance it
Text.tsx export not only a component, but also few types.
This triggers an eslint warning. I don't want refactoring it
without discussing it with @manuhabitela. Silence it.
@manuhabitela introduced Menu and MenuTrigger components. Refactor the
options menu to benefits from his components.
Few details are not perfect yet. wip
it seems panda-css didn't like my way of sharing css code. When sharing
bare objects matching panda-css interface, panda-css didn't understand
those were styles and didn't parse them. Now using actual recipes via
the `cva` helper, panda understands correctly those styles need to be
updated on change.
ps: cleaned a few panda imports that didn't use our "@" alias
this will be used for toggled on/off mic and camera buttons. We want
toggled-on buttons that don't look pressed as it looks a bit weird
(we'll change icons on pressed/unpressed state). And we want red buttons
for when the buttons are not pressed, so adding the "danger" variant
too.
the whole colorpalette stuff needs a bit of work to be clean but it'll
do for now
Refactor the existing Button component to extract a new ToggleButton
component that wraps react aria ToggleButton. This will
be used to toggle cam/mic on/off, or any other controls in the control
bar.
ditch the "PopoverList" component that was basically a poor Menu. Now
dropdown buttons can be made with react aria Menu+MenuItems components
via the Menu+MenuList components.
The difference now is dropdown menus behave better with keyboard (they
use up/down arrows instead of tabs), like selects.
Popovers are there if we need to show any content in a big tooltip, not
for actionable list items.
The current design is inefficient, using two separate approaches to manage
the app state and determine which widget is open. This inconsistency affects
the user experience and overall design quality.
Soon, only one container will be open/close, and its content will be toggle,
using an extensible system.
As a quick fix, I’ve added basic logic to ensure that opening one widget
automatically closes any other open widgets, such as the chat or participants
widget. This is a temporary measure to fix state management until a
more robust solution can be implemented.
This commit refactor the default chat toggle to enhance few details:
- styling (e.g. button's size, icon's style, etc)
- accessibility (e.g. tooltip, aria label, keyboard controls)
- notifications (enhanced contrast on the red dot, feedback from Arnaud)
I faced few challenge while using LiveKit hook, please refer to my commits.
I'll work on a PR on their repo to get it fixed. Duplicating their sources
add overhead.
This commit introduce the most minimal participants list possible. More
controls or information will be added in the upcomming commits.
Responsiveness and accessibility should be functional.
Soon, any right side panel will share the same container to avoid visual
glichts.
Introduced a button to manage the visibility of the participants list.
Encountered refresh issues with the room context where the participant count
in the room metadata does not update in real-time, causing discrepancies
between the actual number of participants and the displayed count. To address
this, I utilized the participants hook, which updates more frequently and
ensures consistency.
Encountered issues extending the current button due to the complexity of the
Button and Link props construction. I’ll need to redesign the component to
enhance its extensibility.
The legacy style adheres to the previous interface but suffers from poor
color contrast. Hex codes were used to match the selected toggle color,
resulting in a difficult color system to maintain. This approach needs refactoring
for better color handling. Legacy styles will soon disappear.
Efforts were made to minimize code for handling the toggle functionality.
Future work should focus on refactoring the button to align with the field
approach for improved developer experience and props validation.
In LiveKit, opening the chat causes the entire page layout, including
participant tiles and controls, to shift left. This disrupts the user
experience, as the chat toggle button moves, requiring users to reposition
their cursor to close the chat.
This behavior also prevents effective organization of the control bar,
especially when trying to maintain a centered section for main controls and
a fixed section for quick-access buttons on the left or right bottom corners.
This fix temporarily locks the controls and participant list in place when
the chat is toggled. The styles will be refactored during the upcoming
redesign.
Updated Django's ALLOWED_HOSTS setting from '*' to the specific host of the
server. Setting ALLOWED_HOSTS to '*' is a security risk as it allows any host
to access the application, potentially exposing it to malicious attacks.
Restricting ALLOWED_HOSTS to the server's host ensures only legitimate
requests are processed.
In a Kubernetes environment, we also needed to whitelist the pod's IP address
to allow health checks to pass. This ensures that Kubernetes liveness and
readiness probes can access the application to verify its health.
Few scripts were duplicated between the scripts and the bin folders.
Reorganize the scripts in a common folder, and align filenames to
follow the same rule.
Updated the liveness and readiness probes interval from every 10 seconds to
every 30 seconds. This change reduces the load on the server by decreasing
the frequency of health checks.
Given the current stability of the application, a 30-second interval is
sufficient to ensure that the application remains responsive and healthy.
Silenced certain Django security warnings because the application is served
behind a reverse proxy. These warnings are not applicable in our deployment
context, where the reverse proxy handles these security concerns.
This change ensures relevant security measures are appropriately managed
while avoiding unnecessary warnings. Any question? asked @rouja.
/!\ actually, this commit is not working, and should be fixed.
Some outdated references to Terraform and OpenStack were missed during
the project quickstart. These are legacy elements inherited from OpenFun.
This commit cleans up the codebase.
LiveKit access tokens are valid for 6 hours after generation. Without a
`staleTime` set, room data becomes stale immediately (0 milliseconds),
causing unnecessary refetches, particularly when users switch focus back
to the call window.
This issue led to an excessive number of "get room" events, as observed
in the analytics. For example, during a meeting, if a user switches tabs
and then returns, the room data would be refetched despite the access
token still being valid.
By adding a `staleTime` aligned with the token's validity, we avoid
unnecessary network requests.
(aligned with Google Meet or Jitsi ux)
This ensures participants join a room with a fresh access token.
Some participants information might be altered after the access token
was generated (e.g. their name).
I wanted the initial data to be passed only once, when you navigate
to the room from the create button, but if you reload the page, then
a new access token will be fetched, and the pre-join screen displayed.
Utilized local storage to persist the updated name. After careful
consideration, persisting all user choices in the backend was deemed overly
complex and unnecessary for the value it would provide.
Opted for a single responsibility approach by leveraging local storage to manage
user choices, aligning with LiveKit's default implementation.
Switched to using query parameters instead of GET requests, enabling the
inclusion of additional parameters when calling the create endpoint via POST.
Simplified the access token generation process by removing redundant calls to
`with_identity` and consolidating token generation steps. This streamlines the
method flow by preparing all necessary data before generating the access token.
The user name is initially set by the backend when generating the LiveKit access
token. By default, if the frontend has not provided a username, the backend uses
the user's email address. This approach isn't ideal, as some users prefer using
their first name.
This update allows local participant to change their username in real-time
during a session. However, these changes are not yet persisted for
future meetings. This persistence feature will be added in upcoming commits.
Reintroduced user information in the settings dialog. This change allows
users to see the account they are logged into, which is important for
account management. An additional feature to update usernames will be
added soon. Apologies to @manuhabitela for the oversight.
Removed the header when inside a room to maximize space for the camera
gallery. Settings are now accessible via the options menu.
It aligns the design with common video conference tools as meet,
or jitsi. @manuhabitela was aligned with this change.
I am not hundred percent sure it respects @manuhabitela's guidelines,
but for the options menu, I would need to trigger a dialog in a controlled way.
I tried to have the minimal impact on the existing settings dialog.
Replaced the existing settings button with a new `OptionsButton` component.
This new component integrates a menu for more actions, providing an
extensible and accessible interface.
Refactoring will follow to enhance code maintainability.
A legacy style was introduced to keep visual coherence with the other controls
from the controls bar. It's temporary, until we redesign the whole bar.
It doesn't match pixel-perfect the legacy styles.
A new public Tchap chan was created. All users, and not only beta users,
can now join it using the link in the menu, and reach us.
Localized video conference controls to match the selected language.
Previously, controls were always in English, causing confusion for French users.
A full refactor of the controls is planned soon.
Same approach as the previous commit. Few elements were not exposed
by the LiveKit package, and I had to duplicate them. Let's see if
we can asap get rid off all this complexity.
Basically, duplicate LiveKit code to start iterating on their
components, not sure wether it's the optimal strategy, but at least
we will be more agile, shipping small features which are lacking.
Added CI job to run linting and formatting checks in the frontend
codebase. Please note, we should cache frontend dependencies,
to avoid re-installing them. Future improvement!
Ran Prettier on the entire codebase to fix formatting issues. My IDE was
previously misconfigured, causing most of these errors. The IDE configuration
has been corrected.
Following @manuhabitela's recommendation, we've decided to switch
from Yarn to npm for managing our Node.js dependencies. This update
aligns all remaining parts of the codebase that were still using
Yarn to now utilize npm.
I added an example in the documentation to ensure we remember to upgrade the
frontend image during the release process. This will help prevent any issues
related to outdated images when deploying new versions.
(I did this mistake while releasing)
Enhanced the error screen to support customizable titles and descriptions.
Allows for more detailed and informative error messages for users.
By default, the error screen displays a standard heading. You can
now pass custom title and body content to provide additional context
and clarity.
Attempt to create a room using a mutation if the fetch query fails.
If the mutation succeeds, update the fetch query data to ensure
it is up-to-date.
I tried to reproduce kind of a ‘get or create’ mechanism.
Due to the removal of the 'onError' prop in React Query, it is recommended
to use useEffect for handling such event propagation.
Regarding status handling, the fetch status takes priority over the mutation.
The mutation should only be called if the fetch has failed.
I'll need this key to sync data when the mutation responds successfully.
This is a preliminary refactoring; the mutation will be added
in upcoming commits.
Enabling logs by default in clients during production feels insecure.
Therefore, I have silenced all LiveKit logs.
This might pose a challenge if we need to debug a user in the future,
but currently, we don’t require these logs.
Additionally, my utils file and function work as intended,
though there is room for improvement.
With unregistered rooms being now forbidden, we need to call create a room
in our Django db to get an access token.
Clicking on the 'Create room' will create a new entry using a Post request.
The output serialized already contains an access token to the LiveKit server,
thus we won't need to run the useQuery hook when navigating to the room.
/!\ this modification now prevent authenticated users to create rooms
by simply navigating to it. It'll be fixed in the upcoming commits.
Require users to create a room in the database
before requesting a LiveKit token.
If user request an access token for a room that doesn't
exist in our db, its request would end in a 404 error.
Ensure that rooms must be registered by a user before they can be accessed.
By default, all created rooms remain public, allowing anonymous users to join
any room created by a logged-in user.
However, anonymous users cannot create rooms themselves.
I have set up distinct namespaces for each environment. You can now push
events to the development namespace without affecting production data.
Please note that these keys are not 'secret'. They will also be configured
in the browser SDK, which is inherently insecure. The documentation does not
specify a secure storage method for these keys.
In this commit, we'll integrate a third-party service to track user events.
We start by using the `identify` method to track sign-ins and sign-ups.
Additionally, we use the `track` method to monitor custom events such as room
creation, access token generation, and logouts. This will provide us with
valuable data on current usage patterns.
The analytics library operates by opening a queue in a separate thread for
posting events, ensuring it remains non-blocking for the API. Let's test
this in a real-world scenario.
Add a new property 'email_anonymized' to the User model,
to allow tracking a user's email without any personal data.
In fact, we're dealing with professional data, thus it shouldn't
be subject to the GDPR, however I prefer taking extra care
when working with potentially first and last names.
I've chosen June, a closed project, for our product analysis. Please note that
this is temporary until we find our product-market fit and achieve
significant traction.
I selected June for several reasons, particularly their focus on pre-product-
market fit (PMF) analysis, which is crucial for us. Their approach will help us
track user engagement and identify the most important features.
Remember, the purpose of this data is not to provide definitive answers about
our product, but to prompt us to ask the right questions and engage with users
to find the answers.
Close issue #91. Each <li> element in a list should have a unique key
to avoid React warnings.
Added unique keys to 'popover' items. Although the 'lang' keys are generated
by i18n dependencies and are expected to be unique, this implementation
ensures keys are unique and not based on indexes.
Removing the __init__.py makes it impossible for Pylint to get the sources
to lint from the root folder. We manually set all the paths pylint will lint.
That's not a big deal, as we'll remove Pylint any soon to rely only on ruff.
I took inspiration from marsha or magnify project.
I removed the now useless bash script to run Pylint command. It saves us
wrapper! Plus, having a lint command running with different option locally
and in the CI was quite a pain.
Locally linter was running on diff files; Fixed! CI and make command has now
the same behavior.
Recent updates in dependencies broke the tests.
I am in a rush, I found a stack overflow discussion mentionning we should
NOT consider the root folder of a Django project as a Python package.
My issue was:
Model class app.core.models.User doesn't declare an explicit app_label
and isn't in an application in INSTALLED_APPS.
Removing the __ini__.py file at the root folder fixed the regression.
Recent updates of dev/ruff and dev/pylint dependencies led
to new linting warnings.
Pylint 3.2.0 introduced a new check `possibly-used-before-assignment`,
which ensures variables are defined regardless of conditional statements.
Some if/else branches were missing defaults. These have been fixed.
Previously each route rendered its whole layout from a to z.
Now each route updates a single common wrapper when the layout changes
between pages.
Also the Loading, Error, UserFetched, QueryAware code is more explicit
and understandable.
This introduces valtio as dependency, allowing us to deal with global
state easily in a svelte/vue way (reactive mutable state). This limits
the boilerplaty-ness of immutable state lib approaches, while keeping
rendering optimization better than homemade react contexts.
we query user at load and keep info for a while, way less than user
session anyway. will need to check if we could get de-sync (like loading
the frontend with backend user session ending in 30 minutes while we
don't check user state until an hour)
at the cost of a not-changeable username, logged in users who create
rooms now skip the join page. I think it's good to have this to test
this way, even if the username choice is not there yet.
it was a bit cumbersome to navigate by paths accross the app as if one
path changes a tiny bit we have to make sure we updated everything
everywhere and it's kind of error-prone and cumbersome to build paths by
hand.
now we have a routes file that describes everything: what is the path to
each route, and how to navigate to them.
We use a new navigateTo helper that helps us. It could be better typed
but i'm a newbie.
the issue was we return the fetchUser promise to react query too early
(before the browser reloaded the page). now we make sure react query
doesn't get the info
This commit is totally work in progress.
@manuhabitela the floor is yours.
If the user is logged-out, try silently log-in her every hour,
to avoid any manual login on her side.
The one hour value has been discussed with @manuhabitela, but there
is no real logic behind it. Could definitely be shorter or longer.
It needs at least to be longer than the average silent login flow
to avoid locking the user-agent in an infinite redirection loop.
Enhanced the authentication URL generation to support 'silent'
and 'returnTo' query parameters.
This allows initiating a silent login and specifying a custom
return URL after auth.
Silent login attempts to re-authenticate the user without interaction,
provided they have an active session, improving UX by reducing manual auth.
It's an essential feature to really feel the SSO in La Suite.
A new query parameter, 'silent', allows the client to initiate a silent login.
In this flow, an extra parameter, 'prompt=none', is passed to the OIDC provider.
The requested flow is persisted in session data to adapt the authentication
callback behavior.
In a silent login flow, an authentication failure should not be considered as a
real failure. Instead, users should be redirected back to the originating view.
A silent login fails when user has no active session.
Why return the 'success_url'? The 'success_url' will redirect the user agent to
the 'returnTo' parameter provided when requesting authentication.
It's necessary to enable a silent login on any URL.
Minimal test coverage has been added for these two custom views to ensure
correct behavior.
mozilla-django-oidc now supports a 'returnTo' parameter for redirecting
to a specificURL upon successful login. if not provided,
it defaults to the settings-defined URL.
This allows initiating the login flow from any views,
enhancing UX by returning users to their previous page.
The 'returnTo' naming can be discussed.
Based on @rouja instructions, try to document the Tilt stack,
to enhance the DX of any newcomers, discovering Meet and trying
to run it on K8s.
Having a shared/common onboarding documentation on Tilt with
Impress, Regie, and Meet would be amazing.
Especially, to document how to install Tilt and its dependencies.
Important: The frontend is not deployed locally using the production
target, and I feel important to document it.
Closes#50.
Adopted LiveKit demo app approach for passing
configurations to LiveKitRoom.
Introduced roomOptions for future customizations (e.g.,
video quality, e2e, codec).
DeviceId is persisted in local storage despite boolean flag.
checkout@v2 uses node12 which will be deprecated soon.
I've aligned CI configurations to use a more recent action,
already in-use in the 'meet.yml' flow.
now clicking on the header homepage link asks for confirmation when on
the route page.
this is quick and dirty, using browser confirm ui, and not making a
difference between join page and conference page, but it'll do for now
- show the header on homepage. Not sure we want any header on this app
actually but I guess he's right since we have one it feels more
consistent to have it everywhere
- show logged in email in header. ditched it because i didn't quite get
the value of showing it all the time in this app but i guess it's better
than nothing
- remove user info from settings. Since they are back in the header, no
need
- use the default react-aria DialogTrigger when we want to build buttons
triggering dialogs
- use custom Dialog component as a wrapper to Dialog content
this better permits us to have a Dialog content component somewhere else
than its trigger button. Mainly did this so that the dialog title is
localized with its content.
they are not really helpful, i'd rather stick to the react-aria wording,
easier to understand when looking at react aria examples, converting
code, etc. Not a great value adding this api in our tiny heads
- improve the Form component to abstract the few things we'll certainly
do all the time (data parsing, action buttons rendering)
- add a Field component, the main way to render form fields. It mainly
wraps react aria components with our styling. The Checkbox component is
a bit tricky to go around some current limitations with react aria
this triggered on a few things we didn't want (labels particularly).
plus, handle the focus ring color via panda, so that it's available in
the JS (will be useful in soon to be commited stuff)
until now, we concluded that is `isLoggedIn` !== true meant the user
wasn't logged in. While it also meant that we are currently loading user
info.
wrap the whole in something that doesn't render anything until we made
the first user request to prevent this behavior.
The Pylint job was failing due to those TODO items. In our make lint
command sequence, Pylint runs first. If it fails, Ruff won't run,
which is quite inconvenient.
I've extracted those TODOs into an issue for further review.
Quick and dirty approach. It works, that's essential.
Frontend can pass a desired username for the user. This would
be the name displayed in the room to other participants.
Usernames don't need to be unique, but user identities do
If no username is passed, API will fall back to a default username.
Why? This serves as a security mechanism. If the API is called
incorrectly by a client, it maintains the previous behavior.
when adding keys by hand, we didn't really know where to add them so
that the i18n:extract command would not move them afterwards. Feels like
this will help.
I guess a CI thing checking if the locales file dont change after a push
would be helpful
- this feels a bit less boilerplaty to read
- puting the characters whitelist outside the function to prevent
creating the var each time (yes, this of super great importance)
Enhanced security by ensuring users are redirected to a 404 error page
if they
pass an incorrect roomId path, either intentionally or unintentionally.
This is
a critical security mechanism that should be included in our MVP.
Let's discuss extracting hardcoded elements, such as lengths or
the separator, into proper constants to improve code maintainability.
I was concerned that this might make the code harder to read, it could
enhance
clarity and reusability in the long term.
I prefer exposing the roomIdRegex from the same location where we
generate IDs.
However, this increases the responsibility of that file. Lmk if you have
any
suggestion for a better organization.
Additionally, the current 404 error page displays a 'Page not found'
message for
invalid room IDs. Should we update this message to 'Invalid room name'
to
provide more context to the user?
UUID-v4 room IDs are long and uninviting. Shorter, custom room IDs
can enhance UX by making URLs easier to share and remember.
While UUID-v4s are typically used in database systems for their low
collision probability, for ephemeral room IDs, the collision risk of e+14
combinations is acceptable.
This aligns room IDs with Google Meet format.
Even if the 'slugify' function is not used anymore, I kept it.
Lmk if you prefer removing it @manuhabitela
this will be better in an options page later i think, as we don't pass
our life changing language and we already have a language detector at
load.
this adds a PopoverList primitive to easily create buttons triggering
popovers containing list of actionable items.
- upload local translation files on push
- make crowdin create a pull request when new translations are made
through the crowdin website (webhook configured on crowdin-end)
- dynamically load locale files for smaller footprint
- have a namespace for each feature. At first I'd figured I'd put each
namespace in its correct feature folder but it's kinda cumbersome to
manage if we want to link that to i18n management services like crowdin…
Updated CI to use "npm" instead of yarn for the frontend project based
on @manuhabitela's recommendations. Also updated the dependencies-related CI
steps that were previously missed.
Heavily inspired from openfun handbook instructions,
https://handbook.openfun.fr/git
Document how release a new version. Might be a common
documentation shared with Impress, Regie and Meet projects.
Let's discuss it.
It's quite important that anyone should know how to release,
as we plan to release every week an enhanced version.
While refactoring 'Impress' to introduce features from 'Magnify',
few unnecessary changes were traced in the database migrations.
Do some clean up before releasing a first version in production.
Deploying LiveKit on Kubernetes is quite challenging when using a private cloud provider.
@rouja faced some issues while configuring the exposed port necessary for the
STUN and TURN servers to work when the user is connected to a network behind a firewall.
@rouja deployed quickly a temporary LiveKit instance on a VM with its own STUN and
TURN servers to avoid using the Google infrastructure.
Soon we will have a proper Python API, that will interact with the Egress
service.
Until this point, I shared how recording data from a meeting. So we could
extract data from the LiveKit server, and use it as sample to build the
AI pipeline.
Please note this documentation is minimal, it's a mini-tutorial.
LiveKit CLI is essential to interact with the running server and its
ecosystem.
I recommend installing it, as you can list rooms, find participant identity,
create egress to record room, etc.
It helped a lot debugging the Egress service, and discovering its features.
LiveKit offers Universal Egress, designed to provide universal exports
of LiveKit sessions or tracks to a file or stream data.
Egress is kept outside of the server to keep the load off the SFU and avoid
impacting real-time audio or video performance/quality.
Followed the "Running Locally" steps from the https://github.com/livekit/egress
repository, but I adapted them to docker-compose.
By default, I chose to run both the LiveKit server and the Egress when you
up the stack. If we see any performance issue, we could only run the LiveKit
server, which is the barebone of the product.
Egress will be usefull only when dealing with recording/exporting data.
Egress service will output file recordings to "./docker/livekit/out"
Note: the Egress service doesn't run as root. You need to update the "/out"
permissions, so all user could write to it.
LiveKit server configuration was the default ones. These configurations
were not connecting to any Redis instance. When running a standalone
LiveKit server, Redis is not needed.
However, when adding other LiveKit ecosystem service, e.g. Egress,
LiveKit server publish jobs to a Redis queue, that are handled by
the Egress workers.
(Precisely, they use Redis Pub/Sub to communicate but I am no expert)
The LiveKit server and the Egress need to be connected to the same
Redis instance. This commit configure the LiveKit server before
adding the Egress service to the compose stack.
- we now have "features" to try to organize code by intent instead of
code type. everything at the root of frontend, not in feature/, is
global
- customized the panda config a bunch to try to begin to have an actual
design system. The idea is to prevent using arbitrary values here and
there in the code, but rather semantic tokens
- changed the userAuth code logic to handle the fact that a 401 on the
users/me call is not really an error per say, but rather an indication
the user is not logged in
ChangeLog won't be any useful before the first release.
Save us time, save the world useless computation, remove the CI steps.
They'll be added back as soon as they are necessary.
Configured Nginx to set caching headers for static assets by adding
a location block to match common static file extensions and set
an expiration time of 30 days.
It should result in faster loading times, reduced bandwidth usage,
and a more efficient and responsive user experience.
Wdyt @manuhabitela?
Configured Nginx to serve index.html for all requests, allowing
the client-side router (Wouter) to manage the routing.
Added a try_files directive to attempt to serve static files first,
falling back to index.html if the requested file is not found.
Added an error_page directive to handle 404 errors by internally
redirecting to index.html without modifying the URL path.
Wouter should make the rest.
It seems appropriate that backend owns the responsability of knowing any
information/configurations of the LiveKit server. Then, it shares those
with the frontend.
Please see my previous commit to understand why environment variables are
not appropriate for deployment in several remove environments.
As of today, the LiveKit server URL is the only configuration exposed
dynamically to the frontend. Thus, it doesn't justify adding a new route
to the API, responsible for exposing configurations (e.g. /configuration).
As the frontend needs to call the backend when it wants to initiate a new
webconference room, let's pass the server URL when retrieving the room's token.
It is relevant, to get both the room location and the keys to open the room in
the same call.
I prefered to be pragmatic, if the need appears any soon, I would refactor
these parts.
Discussed IRL with @manuhabitela. In developpement, we build locally the
Docker image. Thus, we can pass values to the frontend before the npm build
command was called.
Environment variables are great for configuration, and work perfectly in dev
mode, building Docker image on the fly.
However, in other environment (e.g. staging, pre-prod, prod) we'll pull a common
Docker image published in a remote registry. All cited environments should use
the same Docker image to make tests/deployment reproducible between envs.
As the Docker image is not rebuilt on the fly, we cannot easily configure
customized environment variables for each environment.
The API base URL would have a different value for each environment, and would
require a different environment variable.
Inspired by Impress works, if no environment variable is passed for the API URL,
the window origin will be used, and then the API path will be appended.
Frontend and backend are always deployed on the same URL, usually frontend
is at the '/' route, and backend at the '/api/vXX/' route.
If any configuration are required for each remote environment, they would be
retrieved from the API at runtime.
Voila! Don't hesitate to challenge this commit.
@manuhabitela configured panda css to manage project styling.
Panda codegen generates a new folder, 'styled-system' which was not
ingored by Eslint, resulting in ~40 Eslint errors.
Adapted Eslint configurations to ignore this path.
Values for staging, pre-prod, prod environments were adapted to read
the newly introduced LiveKit secrets.
The extra/template/secrets.yaml should be moved to a proper location.
Uncommenting the line left the original commented line in place,
which was misleading because the comment indicated to comment
the next line, which was already commented.
Fixed!
I have updated the staging, pre-prod and production environments.
Done:
- Remove silenced security checks, as SECURE_PROXY_SSL_HEADER is set in prod.
- Rename "impress" to "meet"
- Rename "docs" to "meet"
- Remove unused values (webrtc, ingressWS)
I haven't yet received the definitive DNS configuration from Florian or Olivier.
The hosts meet.numerique.gouv.fr and all meet-*.beta.numerique.gouv.fr are
only hypothetical at this point.
panda needs to generate types to work. We used to generate those after
deps install but it's not that necessary, since we generate them before
running the dev env, and before building the prod build.
This fixes the `npm ci` error in the frontend docker build
the idea is to use react aria, panda-css, react query and wouter as a
base, in addition to livekit react components
this is still mostly wip but it's usable:
- homepage shows a login link to create a room
- before joining a room you are asked to configure your audio/video/user
name
- note that if you directly go to a a conference url it creates it even
if you are not logged in… very secured!
Done:
- Rename all occurrences of "impress" to "meet".
- Update Agent Connect secrets credentials for the dev environment.
- Add new development secrets for LiveKit.
- Remove Minio from the dev stack (no cold storage required).
- Add LiveKit chart to the stack.
- Remove templates and values related to the WebSocket server.
The integration of LiveKit was inspired by an example from the "numerique-gouve/infrastructure" repo.
However, a notable issue persists with LiveKit's default chart: we are unable to override
the namespace, resulting in all LiveKit components running in the default namespace.
thx to @rouja for his help.
The start-kind.sh script was read-only after copying the repository, preventing it from running
the "build-k8s-cluster" make command. Updated permissions to chmod 755.
Configured the frontend to use environment variables (prefixed with "VITE_") for frontend
port and host configuration, which will be overridden in the Helm chart values
to ensure correct values are used in different environments.
Helm requires the frontend port to be 8081 and use the public host,
not the default "localhost" value.
Renamed docker/files/usr/local/etc/gunicorn/impress.py to .../meet.py to match the updated
backend source filenames. This resolves the issue where the Dockerfile was attempting
to copy a non-existent file, causing the build to fail.
Configured the frontend to use environment variables (prefixed with "VITE_") for API
and LiveKit server URLs, which will be overridden in the Helm chart values
to ensure correct URLs are used in different environments.
Inspired by the Docker images from numerique-gouv/people and numerique-gouv/impress
(see commit 1a3b396 in the "people" repository).
Due to the lack of a certified cold storage solution (e.g., S3) for serving static files,
we've containerized the frontend as a temporary deployment solution.
Vite.js static output is served using an Nginx reverse proxy.
I am not quite sure of this commit, please @manuhabitela could you review how I exposed
the static build from vite in my Nginx server? and do the appriopriate fix if necessary.
Resolved minor TypeScript errors in the Proof of Concept (PoC)
that were causing the "npm run build" command to fail.
These fixes were necessary to prepare the frontend for
containerization with Docker.
ASAP, a CI step will prevent these kind of errors.
@rouja secrets management relies on a central repository, which manages
all numerique-gouv secrets.
I have created a new subfolder in its "numerique-gouv/secret" repository
to store all update key/credentials related to the Meet project.
I have updated all references of "Impress" to "Meet" in the tsclient.
Also, I forgot to rename the repository link in the mail package.json,
my bad, it's fixed.
I have created two new repositories on DockerHub, one for the currently
existing backend image, and one for the future frontend image.
I searched-replaced all occurences of "lasuite/impress-frontend" or "lasuite/impress-backend".
One image won't exist anymore, "impress-y-webrtc-signaling", I have
removed the steps building and pushing its image to the DockerHub account.
Performed a minor cleanup:
The CI related to the frontend has been removed temporarily.
It will be reinstated once the frontend development is initiated by @manuhabitela.
A redundant issue template has been removed. Existing templates in
the "ISSUE_TEMPLATE" folder remain in place.
I have updated all references of "Impress" to "Meet".
Migrations were manually updated and not regenerated. Never-mind,
they all will be squashed before the first release.
I have also searched for reference to "Magnify", and replaced them
by "Meet".
While updating the backend sources, I have also fixed other parts of
the project, namely:
- Compose file
- Github documentation and CI
- Makefile commands
I have renamed the Github project's name, from "Impress" to "Meet".
Updated the pyproject urls section, which provides essential metadata
about the project. This updates ensure all links point to the right
github repository.
I have updated all references of "Impress" to "Meet".
Few environment variables were updated, keycloak was including
the realm's name as a base URL for API endpoints.
Update frontend to be the root folder of the frontend sources,
instead of nesting them in a folder named as the application.
We only work on a single frontend app as of today, nesting
sources doesn't add any value, even though the initial organization
was more extensible.
Introduce a utility function to issue a basic LiveKit access token with the minimal
required video grants for videoconferencing.
/!\ This function is naive, and doesn’t handle properly all cases. It’s under construction.
Testing was conducted using the LiveKit connection test tool https://livekit.io/connection-test,
which allows users to input the address of their local LiveKit server and an access token.
** Upcoming improvements? **
- Unit tests should be added.
- User display name should be their full name instead of their email address.
- Anonymous users should be allowed to provide a full name when requesting access to the room.
- Video grants should be adapted based on the room configuration and the user's role.
These improvements will be addressed in future commits.
Nevertheless, with this draft, we should be able to address various situations, including
public rooms, permanent rooms, temporary rooms, logged-in users, and anonymous users.
When starting the LiveKit server using the '--dev' option, the server uses
defaults secret/key pair according to the documentation.
Make sure the Django settings and de facto the environment variable match
these defaults.
Please have a look at the documentation page here:
https://docs.livekit.io/home/self-hosting/local/
LiveKit offers various React components as building blocks to build any
video or audio conference tools.
It also requires to install a LiveKit client, as the users will connect
to the LiveKit server once the backend have issued an Access Token,
directly from their web browsers.
Add livekit-api dependencies. According to the documentation, this Python
package is required while issuing Access Token for a LiveKit server, to
the users.
Introduce CRUD API endpoints for the Rooms and ResourceAccess models.
The code follows the Magnify logic, with the exception that token generation
has been removed and replaced by a TODO item with a mocked value.
Proper integration of LiveKit will be added in future commits.
With the removal of group logic, some complex query sets can be simplified.
Previously, we checked for both direct and indirect access to a room.
Indirect access meant a room was shared with a group, and the user was a
member of that group. I haven’t simplified those query set, as I preferred
isolate changes in dedicated commits.
Additionally, all previous tests are still passing, although tests related
to groups have been removed.
I picked few models from Magnify to build our MVP:
- Resource:
A generic model representing any type of resource. Though currently used only by Room,
it encapsulates a meaningful business logic as an abstract model.
- Room:
The primary object we manipulate, representing a meeting room with access
and permission controls.
- ResourceAccess
Ensures relevant users have the appropriate permissions for a given room.
** What’s different from Magnify ? **
Removed group logic; it will be added later. For now, we rely on the user model's
property to get its groups via desk.
Removed any logic or method related to Jitsi or LiveKit. These servers will be integrated
in the upcomming commits.
Focus on Room-related models to maintain a minimal and functional product (KISS principle)
until we achieve product-market fit (PMF).
Creating simple public and private, permanent and temporary rooms
is sufficient for building our MVP.
The Meeting model in Magnify, which supports recurrence, should be handled by
the collaborative calendar instead.
Adapted the unit test to use Pytest, and linted all the sources using Ruff linter.
(Migrations will be squashed before releasing the MVP)
Based on @mathisbarthere's PR on openfun/Magnify migration to LiveKit.
These configurations might need to be updated.
Please refer to the documentation: https://docs.livekit.io/home/self-hosting/local/
The 'livekit-server --dev' will start a LiveKit in development mode,
the instance will use the following API key/secret pair:
API Key: devkey
API secret: secret
By default LiveKit's signal server binds to `127.0.0.1:7880`, adding the
option `--bind 0.0.0.0 allow other devices on your network to access the
server.
Quick and dirty code to initiate a login or logout flow based on
user's click.
The code is copied/pasted from several sources of Impress. It's dirty,
and meant to be refactored.
It asserts the login and logout are still functional while introducing
new features in the project.
Run the command 'npm create vite@latest' to bootstrap a new frontend project.
Please note, other elements of the project still use yarn, to avoid confusion
let's use npm instead.
Vite was chosen over Next.Js for its simplicity; Next.Js could be kind of a
black box where a lot of magics happen.
This commit introduces a boilerplate inspired by https://github.com/numerique-gouv/impress.
The code has been cleaned to remove unnecessary Impress logic and dependencies.
Changes made:
- Removed Minio, WebRTC, and create bucket from the stack.
- Removed the Next.js frontend (it will be replaced by Vite).
- Cleaned up impress-specific backend logics.
The whole stack remains functional:
- All tests pass.
- Linter checks pass.
- Agent Connexion sources are already set-up.
Why clear out the code?
To adhere to the KISS principle, we aim to maintain a minimalist codebase. Cloning Impress
allowed us to quickly inherit its code quality tools and deployment configurations for staging,
pre-production, and production environments.
What’s broken?
- The tsclient is not functional anymore.
- Some make commands need to be fixed.
- Helm sources are outdated.
- Naming across the project sources are inconsistent (impress, visio, etc.)
- CI is not configured properly.
This list might be incomplete. Let's grind it.
Meet is a simple video and phone conferencing tool, powered by [LiveKit](https://livekit.io/).
Meet is built on top of [Django Rest
Framework](https://www.django-rest-framework.org/) and [Vite.js](https://vitejs.dev/).
## Getting started
### Prerequisite
#### Docker
Make sure you have a recent version of Docker and [Docker
Compose](https://docs.docker.com/compose/install) installed on your laptop:
```bash
$ docker -v
Docker version 20.10.2, build 2291f61
$ docker compose -v
docker compose version 1.27.4, build 40524192
```
> ⚠️ You may need to run the following commands with `sudo` but this can be
> avoided by assigning your user to the `docker` group.
#### LiveKit CLI
Install LiveKit CLI, which provides utilities for interacting with the LiveKit ecosystem (including the server, egress, and more), please follow the instructions available in the [official repository](https://github.com/livekit/livekit-cli).
### Project bootstrap
The easiest way to start working on the project is to use GNU Make:
```bash
$ make bootstrap FLUSH_ARGS='--no-input'
```
Then you can access to the project in development mode by going to http://localhost:3000.
You will be prompted to log in, the default credentials are:
```bash
username: meet
password: meet
```
---
This command builds the `app` container, installs dependencies, performs
database migrations and compile translations. It's a good idea to use this
command each time you are pulling code from the project repository to avoid
dependency-related or migration-related issues.
Your Docker services should now be up and running 🎉
[FIXME] Explain how to run the frontend project.
### Configure LiveKit CLI
For the optimal DX, create a default project named `meet` to use with `livekit-cli` commands:
```bash
$ livekit-cli project add
URL: http://localhost:7880
API Key: devkey
API Secret: secret
Give it a name for later reference: meet
? Make this project default?? [y/N] y
```
Thus, you won't need to pass the project API Key and API Secret for each command.
### Adding content
You can create a basic demo site by running:
```bash
$ make demo
```
Finally, you can check all available Make rules using:
The application is deployed across staging, preprod, and production environments using Kubernetes (K8s).
Reproducing environment conditions locally is crucial for developing new features or debugging issues.
This is facilitated by [Tilt](https://tilt.dev/) ("Kubernetes for Prod, Tilt for Dev"). Tilt enables smart rebuilds and live updates for services running locally in Kubernetes. We defined our services in a Tiltfile located at `bin/Tiltfile`.
#### Getting Started
Make sure you have installed:
- kubectl
- helm
- helmfile
- tilt
To build and start the Kubernetes cluster using Kind:
```shell
$ 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.
You can monitor Tilt's at `http://localhost:10350/`. After Tilt actions finish, you can access the app at `https://meet.127.0.0.1.nip.io/`.
#### Debugging frontend
Tilt deploys the `meet-dev` for the frontend by default, to benefit from Vite.js hot reloading while developing.
To troubleshoot production issues, please modify the Tiltfile, switch frontend's target to `frontend-production`:
LiveKit offers Universal Egress, designed to provide universal exports of LiveKit sessions or tracks to a file or stream data.
It is kept in a separate system to keep the load off the [Single Forwarding Unit (SFU)](https://docs.livekit.io/reference/internals/livekit-sfu/) and avoid impacting real-time audio or video performance/quality.
## Getting started
### Prerequisite
1.**Verify Services**: Ensure the LiveKit server and Egress service are both up and running.
2.**Install CLI**: Confirm that the LiveKit CLI utility is installed on your system.
3.**Set Permissions**: Since the Egress service does not run as the root user, you need to grant write permissions to all users for the output directory. Update the permissions of the `docker/livekit/out` folder before starting the docker-compose stack:
```bash
$ chmod o+w ./docker/livekit/out
```
### Make a recording
LiveKit provides examples for creating Egress requests, which you can find [here](https://github.com/livekit/livekit-cli/tree/main/cmd/livekit-cli/examples). One of these examples has been added to the repository under `docker/livekit/egress-example`.
Follow these steps to start an Egress request:
1.**Create a Room**: Create a room either through the frontend or using the `livekit-cli` command.
2.**Retrieve Room Name**: Get the room's name (e.g., the UUID4 in the URL from the frontend).
3.**Update Configuration**: Edit the `docker/livekit/egress-example/room-composite-file.json` file with your room's name.
4.**Start Egress Request**: Initiate a new Egress request.
Whenever we are cooking a new release (e.g. `4.18.1`) we should follow a standard procedure described below:
1. Create a new branch named: `release/4.18.1`.
2. Bump the release number for backend project, frontend projects, and Helm files:
- for backend, update the version number by hand in `pyproject.toml`,
- for each frontend projects (`src/frontend`, `src/mail`), run `npm version 4.18.1` in their directory. This will update both their `package.json` and `package-lock.json` for you,
- for Helm, update Docker image tag in files located at `src/helm/env.d` for both `preprod` and `production` environments:
```yaml
image:
repository: lasuite/meet-backend
pullPolicy: Always
tag: "v4.18.1" # Replace with your new version number, without forgetting the "v" prefix
...
frontend:
image:
repository: lasuite/meet-frontend
pullPolicy: Always
tag: "v4.18.1" # Replace with your new version number, without forgetting the "v" prefix
```
The new images don't exist _yet_: they will be created automatically later in the process.
3. ~~Update the project's `Changelog` following the [keepachangelog](https://keepachangelog.com/en/0.3.0/) recommendations~~ _we don't keep a changelog yet for now as the project is still in its infancy. Soon™!_
4. Commit your changes with the following format: the 🔖 release emoji, the type of release (patch/minor/patch) and the release version:
```text
🔖(minor) bump release to 4.18.0
```
5. Open a pull request, wait for an approval from your peers and merge it.
6. Checkout and pull changes from the `main` branch to ensure you have the latest updates.
7. Tag and push your commit:
```bash
git tag v4.18.1 && git push origin --tags
```
Doing this triggers the CI and tells it to build the new Docker image versions that you targeted earlier in the Helm files.
8. Ensure the new [backend](https://hub.docker.com/r/lasuite/meet-frontend/tags) and [frontend](https://hub.docker.com/r/lasuite/meet-frontend/tags) image tags are on Docker Hub.
9. The release is now done!
# Deploying
> [!TIP]
> The `staging` platform is deployed automatically with every update of the `main` branch.
Making a new release doesn't publish it automatically in production.
Deployment is done by ArgoCD. ArgoCD checks for the `production` tag and automatically deploys the production platform with the targeted commit.
To publish, we mark the commit we want with the `production` tag. ArgoCD is then notified that the tag has changed. It then deploys the Docker image tags specified in the Helm files of the targeted commit.
('id',models.UUIDField(default=uuid.uuid4,editable=False,help_text='primary key for the record as UUID',primary_key=True,serialize=False,verbose_name='id')),
('created_at',models.DateTimeField(auto_now_add=True,help_text='date and time at which a record was created',verbose_name='created on')),
('updated_at',models.DateTimeField(auto_now=True,help_text='date and time at which a record was last updated',verbose_name='updated on')),
('is_superuser',models.BooleanField(default=False,help_text='Designates that this user has all permissions without explicitly assigning them.',verbose_name='superuser status')),
('id',models.UUIDField(default=uuid.uuid4,editable=False,help_text='primary key for the record as UUID',primary_key=True,serialize=False,verbose_name='id')),
('created_at',models.DateTimeField(auto_now_add=True,help_text='date and time at which a record was created',verbose_name='created on')),
('updated_at',models.DateTimeField(auto_now=True,help_text='date and time at which a record was last updated',verbose_name='updated on')),
('sub',models.CharField(blank=True,help_text='Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ characters only.',max_length=255,null=True,unique=True,validators=[django.core.validators.RegexValidator(message='Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/_ characters.',regex='^[\\w.@+-]+\\Z')],verbose_name='sub')),
('language',models.CharField(choices="(('en-us', 'English'), ('fr-fr', 'French'))",default='en-us',help_text='The language in which the user wants to see the interface.',max_length=10,verbose_name='language')),
('timezone',timezone_field.fields.TimeZoneField(choices_display='WITH_GMT_OFFSET',default='UTC',help_text='The timezone in which the user wants to see times.',use_pytz=False)),
('is_device',models.BooleanField(default=False,help_text='Whether the user is a device or a real user.',verbose_name='device')),
('is_staff',models.BooleanField(default=False,help_text='Whether the user can log into this admin site.',verbose_name='staff status')),
('is_active',models.BooleanField(default=True,help_text='Whether this user should be treated as active. Unselect this instead of deleting accounts.',verbose_name='active')),
('groups',models.ManyToManyField(blank=True,help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',related_name='user_set',related_query_name='user',to='auth.group',verbose_name='groups')),
('user_permissions',models.ManyToManyField(blank=True,help_text='Specific permissions for this user.',related_name='user_set',related_query_name='user',to='auth.permission',verbose_name='user permissions')),
('configuration',models.JSONField(blank=True,default={},help_text='Values for Visio parameters to configure the room.',verbose_name='Visio room configuration')),
],
options={
'verbose_name':'Room',
'verbose_name_plural':'Rooms',
'db_table':'meet_room',
'ordering':('name',),
},
bases=('core.resource',),
),
migrations.CreateModel(
name='ResourceAccess',
fields=[
('id',models.UUIDField(default=uuid.uuid4,editable=False,help_text='primary key for the record as UUID',primary_key=True,serialize=False,verbose_name='id')),
('created_at',models.DateTimeField(auto_now_add=True,help_text='date and time at which a record was created',verbose_name='created on')),
('updated_at',models.DateTimeField(auto_now=True,help_text='date and time at which a record was last updated',verbose_name='updated on')),
constraint=models.UniqueConstraint(fields=('user','resource'),name='resource_access_unique_user_resource',violation_error_message='Resource access with this User and Resource already exists.'),
field=models.JSONField(blank=True,default=dict,help_text='Values for Visio parameters to configure the room.',verbose_name='Visio room configuration'),
field=models.CharField(choices="(('en-us', 'English'), ('fr-fr', 'French'))",default='en-us',help_text='The language in which the user wants to see the interface.',max_length=10,verbose_name='language'),
('id',models.UUIDField(default=uuid.uuid4,editable=False,help_text='primary key for the record as UUID',primary_key=True,serialize=False,verbose_name='id')),
('created_at',models.DateTimeField(auto_now_add=True,help_text='date and time at which a record was created',verbose_name='created on')),
('updated_at',models.DateTimeField(auto_now=True,help_text='date and time at which a record was last updated',verbose_name='updated on')),
('status',models.CharField(choices=[('initiated','Initiated'),('active','Active'),('stopped','Stopped'),('saved','Saved'),('aborted','Aborted'),('failed_to_start','Failed to Start'),('failed_to_stop','Failed to Stop')],default='initiated',max_length=20)),
('worker_id',models.CharField(blank=True,help_text='Enter an identifier for the worker recording.This ID is retained even when the worker stops, allowing for easy tracking.',max_length=255,null=True,verbose_name='Worker ID')),
('id',models.UUIDField(default=uuid.uuid4,editable=False,help_text='primary key for the record as UUID',primary_key=True,serialize=False,verbose_name='id')),
('created_at',models.DateTimeField(auto_now_add=True,help_text='date and time at which a record was created',verbose_name='created on')),
('updated_at',models.DateTimeField(auto_now=True,help_text='date and time at which a record was last updated',verbose_name='updated on')),
constraint=models.UniqueConstraint(condition=models.Q(('user__isnull',False)),fields=('user','recording'),name='unique_recording_user',violation_error_message='This user is already in this recording.'),
),
migrations.AddConstraint(
model_name='recordingaccess',
constraint=models.UniqueConstraint(condition=models.Q(('team__gt','')),fields=('team','recording'),name='unique_recording_team',violation_error_message='This team is already in this recording.'),
),
migrations.AddConstraint(
model_name='recordingaccess',
constraint=models.CheckConstraint(condition=models.Q(models.Q(('team',''),('user__isnull',False)),models.Q(('team__gt',''),('user__isnull',True)),_connector='OR'),name='check_recording_access_either_user_or_team',violation_error_message='Either user or team must be set, not both.'),
field=models.CharField(choices=[('screen_recording','SCREEN_RECORDING'),('transcript','TRANSCRIPT')],default='screen_recording',help_text='Defines the mode of recording being called.',max_length=20,verbose_name='Recording mode'),
field=models.CharField(choices=[('initiated','Initiated'),('active','Active'),('stopped','Stopped'),('saved','Saved'),('aborted','Aborted'),('failed_to_start','Failed to Start'),('failed_to_stop','Failed to Stop'),('notification_succeeded','Notification succeeded')],default='initiated',max_length=50),
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.