Compare commits

...

8 Commits

Author SHA1 Message Date
lebaudantoine 24bcdbfeb5 wip factorize magic layout value 2026-03-25 18:04:36 +01:00
lebaudantoine 439633e109 wip simplify videoconference layout 2026-03-25 17:49:53 +01:00
lebaudantoine 0d1e276740 wip extract layout related component together in a features 2026-03-25 17:15:01 +01:00
lebaudantoine 74b71f4a9b ♻️(frontend) move reaction-related code into a dedicated feature folder
Group all reaction components, hooks, and logic under a single
feature directory to improve code organization and maintainability.
2026-03-25 16:51:53 +01:00
lebaudantoine 0cd44a8f93 ♻️(frontend) refactor reaction system to unify state and rendering
Use a single store, hook, and portal system to handle both local
and remote emoji reactions.

Improve code quality and reduce duplication through better
factorization of shared logic.
2026-03-25 16:27:53 +01:00
lebaudantoine 15133f9d6b 🧑‍💻(helm) use YAML anchors to simplify Helm values for summary
Reduce duplication by introducing YAML anchors for configurations
shared across multiple services.

Most settings were nearly identical across the three summary
services, making them easier to maintain and update.
2026-03-25 13:43:19 +01:00
lebaudantoine bea1f18ab8 🗑️(helm) remove unused dev Helm values
The dev values are no longer in use and have not been used for over a year.
We primarily rely on the dev-keycloak values, and occasionally
the dev-dinum ones for testing on the Dinum-labeled frontend.

As a result, the unused dev values should be removed to reduce clutter
and simplify maintenance.
2026-03-25 13:29:57 +01:00
lebaudantoine d5a614d2b5 🐛(backend) fix regression in update-participant endpoint
Serialization hardening introduced a breaking change between the
frontend and backend. Adjust the Pydantic model to restore
compatibility.

Reinstate support for can_subscribe_metric, which is passed by
default from the frontend.
2026-03-25 12:20:45 +01:00
29 changed files with 352 additions and 839 deletions
+1
View File
@@ -15,6 +15,7 @@ and this project adheres to
### Fixed
- 🔒️(backend) fix email disclosure in room invitation endpoint #1200
- 🐛(backend) fix regression in update-participant endpoint #1204
## [1.12.0] - 2026-03-24
+4 -11
View File
@@ -303,6 +303,9 @@ class MuteParticipantSerializer(BaseParticipantsManagementSerializer):
)
TrackSource = Literal["SCREEN_SHARE", "SCREEN_SHARE_AUDIO", "CAMERA", "MICROPHONE"]
class ParticipantPermission(BaseModel):
"""Mirror the LiveKit ParticipantPermission protobuf.
@@ -313,9 +316,7 @@ class ParticipantPermission(BaseModel):
can_subscribe: bool | None = None
can_publish: bool | None = None
can_publish_data: bool | None = None
can_publish_sources: list[int] = Field(
default_factory=list
) # TrackSource enum values
can_publish_sources: list[TrackSource] = Field(default_factory=list)
hidden: bool | None = None
recorder: bool | None = None
can_update_metadata: bool | None = None
@@ -366,14 +367,6 @@ class UpdateParticipantSerializer(BaseParticipantsManagementSerializer):
f"Setting the following participant permissions is not allowed: "
f"{', '.join(suspicious_fields)}."
)
if permission.can_subscribe_metrics is not None:
raise serializers.ValidationError(
{
"permission": {
"can_subscribe_metrics": "This permission is not implemented."
}
}
)
return permission
@@ -130,10 +130,11 @@ def test_update_participant_success(mock_livekit_client):
"can_publish": True,
"can_publish_data": True,
"can_publish_sources": [
1,
2,
], # [TrackSource.CAMERA, TrackSource.MICROPHONE]
"CAMERA",
"MICROPHONE",
],
"can_update_metadata": True,
"can_subscribe_metrics": True,
},
"name": "John Doe",
}
@@ -155,8 +156,14 @@ def test_update_participant_success(mock_livekit_client):
{"can_subscribe": True},
{"can_publish": True},
{"can_publish_data": True},
{"can_publish_sources": [1, 2]},
{
"can_publish_sources": [
"CAMERA",
"MICROPHONE",
]
},
{"can_update_metadata": True},
{"can_subscribe_metrics": False},
],
)
def test_update_participant_permission_fields_are_optional(
@@ -264,35 +271,6 @@ def test_update_participant_suspicious_permission_multiple(mock_suspicious):
)
@pytest.mark.parametrize("value", (False, True))
def test_update_participant_unimplemented_can_subscribe_metrics(value):
"""Test update participant raises 400 when can_subscribe_metrics is set."""
client = APIClient()
room = RoomFactory()
user = UserFactory()
UserResourceAccessFactory(
resource=room, user=user, role=random.choice(["administrator", "owner"])
)
client.force_authenticate(user=user)
payload = {
"participant_identity": str(uuid4()),
"permission": {
"can_subscribe": True,
"can_publish": True,
"can_publish_data": True,
"can_update_metadata": False,
"can_subscribe_metrics": value,
},
}
url = reverse("rooms-update-participant", kwargs={"pk": room.id})
response = client.post(url, payload, format="json")
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert "can_subscribe_metrics" in str(response.data)
def test_update_participant_forbidden_without_access():
"""Test update participant returns 403 when user lacks room privileges."""
client = APIClient()
+4
View File
@@ -274,6 +274,10 @@ const config: Config = {
min: { value: 'min-content' },
max: { value: 'max-content' },
fit: { value: 'fit-content' },
// room layout
'room-side-panel': { value: '360px' },
'room-side-panel-margin': { value: '1.5rem' },
'room-control-bar': { value: '80px' },
},
spacing,
}),
@@ -7,9 +7,9 @@ import {
useSwipe,
} from '@livekit/components-react'
import { mergeProps } from '@/utils/mergeProps'
import { PaginationIndicator } from '../controls/PaginationIndicator'
import { useGridLayout } from '../../hooks/useGridLayout'
import { PaginationControl } from '../controls/PaginationControl'
import { PaginationIndicator } from './PaginationIndicator'
import { useGridLayout } from '../hooks/useGridLayout'
import { PaginationControl } from './PaginationControl'
/** @public */
export interface GridLayoutProps
@@ -0,0 +1,72 @@
// RoomContentArea.tsx
import React from 'react'
import { styled } from '@/styled-system/jsx'
import { cva } from '@/styled-system/css'
import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
import { useSidePanel } from '@/features/rooms/livekit/hooks/useSidePanel'
import { Subtitles } from '@/features/subtitle/component/Subtitles'
import { MainNotificationToast } from '@/features/notifications/MainNotificationToast'
const RoomViewport = styled(
'div',
cva({
base: {
position: 'absolute',
maxHeight: '100%',
transition: 'inset .5s cubic-bezier(0.4,0,0.2,1) 5ms',
},
variants: {
isSidePanelOpen: {
true: {
inset: `var(--lk-grid-gap) calc(var(--sizes-room-side-panel) + var(--sizes-room-side-panel-margin) * 2) calc(var(--sizes-room-control-bar) + var(--lk-grid-gap)) 16px`,
},
false: {
inset: `var(--lk-grid-gap) var(--lk-grid-gap) calc(var(--sizes-room-control-bar) + var(--lk-grid-gap))`,
},
},
},
})
)
const TrackAreaContainer = styled(
'div',
cva({
base: {
position: 'relative',
display: 'flex',
width: '100%',
transition: 'height .5s cubic-bezier(0.4,0,0.2,1) 5ms',
},
variants: {
areSubtitlesOpen: {
true: {
height: 'calc(100% - 12rem)',
},
false: {
height: '100%',
},
},
},
})
)
interface RoomContentAreaProps {
children: React.ReactNode
overlay?: React.ReactNode
}
export function RoomContentArea({ children }: RoomContentAreaProps) {
const { isSidePanelOpen } = useSidePanel()
const { areSubtitlesOpen } = useSubtitles()
return (
<RoomViewport isSidePanelOpen={isSidePanelOpen}>
<TrackAreaContainer areSubtitlesOpen={areSubtitlesOpen}>
{children}
</TrackAreaContainer>
<Subtitles />
<MainNotificationToast />
</RoomViewport>
)
}
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react'
import { useCallback, useEffect } from 'react'
import { useRoomContext } from '@livekit/components-react'
import { Participant, RemoteParticipant, RoomEvent } from 'livekit-client'
import { ChatMessage, isMobileBrowser } from '@livekit/components-core'
@@ -10,17 +10,11 @@ import { decodeNotificationDataReceived } from './utils'
import { useNotificationSound } from '@/features/notifications/hooks/useSoundNotification'
import { ToastProvider, toastQueue } from './components/ToastProvider'
import { WaitingParticipantNotification } from './components/WaitingParticipantNotification'
import {
Emoji,
Reaction,
} from '@/features/rooms/livekit/components/controls/ReactionsToggle'
import {
ANIMATION_DURATION,
ReactionPortals,
} from '@/features/rooms/livekit/components/ReactionPortal'
import { layoutStore } from '@/stores/layout'
import { PanelId } from '@/features/rooms/livekit/hooks/useSidePanel'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
import { Emoji } from '@/features/reactions/types'
import { useReactions } from '@/features/reactions/hooks/useReactions'
export const MainNotificationToast = () => {
const room = useRoomContext()
@@ -28,8 +22,7 @@ export const MainNotificationToast = () => {
const { t } = useTranslation('notifications')
const announce = useScreenReaderAnnounce()
const [reactions, setReactions] = useState<Reaction[]>([])
const instanceIdRef = useRef(0)
const { appendReaction } = useReactions()
useEffect(() => {
const handleChatMessage = (
@@ -62,21 +55,13 @@ export const MainNotificationToast = () => {
}
}, [room, triggerNotificationSound, announce, t])
const handleEmoji = (emoji: string, participant: Participant) => {
if (!emoji || !Object.values(Emoji).includes(emoji as Emoji)) return
const id = instanceIdRef.current++
setReactions((prev) => [
...prev,
{
id,
emoji,
participant,
},
])
setTimeout(() => {
setReactions((prev) => prev.filter((instance) => instance.id !== id))
}, ANIMATION_DURATION)
}
const handleEmoji = useCallback(
async (emoji: string, participant: Participant) => {
if (!emoji || !Object.values(Emoji).includes(emoji as Emoji)) return
await appendReaction(emoji as Emoji, participant)
},
[appendReaction]
)
useEffect(() => {
const handleDataReceived = (
@@ -149,7 +134,7 @@ export const MainNotificationToast = () => {
return () => {
room.off(RoomEvent.DataReceived, handleDataReceived)
}
}, [room])
}, [room, handleEmoji])
useEffect(() => {
const showJoinNotification = (participant: Participant) => {
@@ -252,7 +237,6 @@ export const MainNotificationToast = () => {
<Div position="absolute" bottom={0} right={5} zIndex={1000}>
<ToastProvider />
<WaitingParticipantNotification />
<ReactionPortals reactions={reactions} />
</Div>
)
}
@@ -2,19 +2,17 @@ import { createPortal } from 'react-dom'
import { useState, useEffect, useMemo } from 'react'
import { Text } from '@/primitives'
import { css } from '@/styled-system/css'
import { Participant } from 'livekit-client'
import { useTranslation } from 'react-i18next'
import { Reaction } from '@/features/rooms/livekit/components/controls/ReactionsToggle'
import { getEmojiLabel } from '@/features/rooms/livekit/utils/reactionUtils'
import { accessibilityStore } from '@/stores/accessibility'
import { useSnapshot } from 'valtio'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
export const ANIMATION_DURATION = 3000
export const ANIMATION_DISTANCE = 300
export const FADE_OUT_THRESHOLD = 0.7
export const REACTION_SPAWN_WIDTH_RATIO = 0.2
export const INITIAL_POSITION = 200
import { reactionsStore } from '@/stores/reactions'
import { useAnnounceReaction } from '../hooks/useAnnounceReaction'
import { Reaction } from '../types'
import {
ANIMATION_DISTANCE,
ANIMATION_DURATION,
FADE_OUT_THRESHOLD,
INITIAL_POSITION,
REACTION_SPAWN_WIDTH_RATIO,
} from '../constants'
interface FloatingReactionProps {
emoji: string
@@ -79,7 +77,7 @@ export function FloatingReaction({
>
<img
src={`/assets/reactions/${emoji}.png`}
alt={''}
alt=""
className={css({
height: '50px',
})}
@@ -116,14 +114,7 @@ export function FloatingReaction({
)
}
export function ReactionPortal({
emoji,
participant,
}: {
emoji: string
participant: Participant
}) {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const ReactionPortal = ({ reaction }: { reaction: Reaction }) => {
const speed = useMemo(() => Math.random() * 1.5 + 0.5, [])
const scale = useMemo(() => Math.max(Math.random() + 0.5, 1), [])
return createPortal(
@@ -138,51 +129,27 @@ export function ReactionPortal({
})}
>
<FloatingReaction
emoji={emoji}
emoji={reaction.emoji}
speed={speed}
scale={scale}
name={participant?.isLocal ? t('you') : participant.name}
isLocal={participant?.isLocal}
name={reaction.participantName}
isLocal={reaction?.isLocal}
/>
</div>,
document.body
)
}
export const ReactionPortals = ({ reactions }: { reactions: Reaction[] }) => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { announceReactions } = useSnapshot(accessibilityStore)
const [lastAnnouncedId, setLastAnnouncedId] = useState<number | null>(null)
const announce = useScreenReaderAnnounce()
export const ReactionPortals = () => {
const { reactions } = useSnapshot(reactionsStore)
const latestReaction = reactions.at(-1)
const latestReaction =
reactions.length > 0 ? reactions[reactions.length - 1] : undefined
useEffect(() => {
if (!announceReactions) {
return
}
if (!latestReaction) return
const isNewReaction = latestReaction.id !== lastAnnouncedId
if (!isNewReaction) return
const emojiLabel = getEmojiLabel(latestReaction.emoji, t)
const participantName = latestReaction.participant?.isLocal
? t('you')
: latestReaction.participant?.name?.trim() ||
t('someone', { defaultValue: 'Someone' })
announce(t('announce', { name: participantName, emoji: emojiLabel }))
setLastAnnouncedId(latestReaction.id)
}, [announce, latestReaction, lastAnnouncedId, announceReactions, t])
useAnnounceReaction(latestReaction)
return (
<>
{reactions.map((instance) => (
<ReactionPortal
key={instance.id}
emoji={instance.emoji}
participant={instance.participant}
/>
<ReactionPortal key={instance.id} reaction={instance} />
))}
</>
)
@@ -1,16 +1,9 @@
import { useTranslation } from 'react-i18next'
import { RiEmotionLine } from '@remixicon/react'
import { useState, useRef } from 'react'
import { useState } from 'react'
import { css } from '@/styled-system/css'
import { useRoomContext } from '@livekit/components-react'
import { ToggleButton, Button } from '@/primitives'
import { NotificationType } from '@/features/notifications/NotificationType'
import { NotificationPayload } from '@/features/notifications/NotificationPayload'
import {
ANIMATION_DURATION,
ReactionPortals,
} from '@/features/rooms/livekit/components/ReactionPortal'
import { getEmojiLabel } from '@/features/rooms/livekit/utils/reactionUtils'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import {
Popover as RACPopover,
@@ -18,72 +11,21 @@ import {
DialogTrigger,
} from 'react-aria-components'
import { FocusScope } from '@react-aria/focus'
import { Participant } from 'livekit-client'
import useRateLimiter from '@/hooks/useRateLimiter'
// eslint-disable-next-line react-refresh/only-export-components
export enum Emoji {
THUMBS_UP = 'thumbs-up',
THUMBS_DOWN = 'thumbs-down',
CLAP = 'clapping-hands',
HEART = 'red-heart',
LAUGHING = 'face-with-tears-of-joy',
SURPRISED = 'face-with-open-mouth',
CELEBRATION = 'party-popper',
PLEASE = 'folded-hands',
}
export interface Reaction {
id: number
emoji: string
participant: Participant
}
import { useReactions } from '../hooks/useReactions'
import { Emoji } from '../types'
import { getEmojiLabel } from '../utils'
export const ReactionsToggle = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const [reactions, setReactions] = useState<Reaction[]>([])
const instanceIdRef = useRef(0)
const room = useRoomContext()
const [isOpen, setIsOpen] = useState(false)
const { sendReaction } = useReactions()
useRegisterKeyboardShortcut({
id: 'reaction',
handler: () => setIsOpen((prev) => !prev),
})
const sendReaction = async (emoji: string) => {
const encoder = new TextEncoder()
const payload: NotificationPayload = {
type: NotificationType.ReactionReceived,
data: {
emoji: emoji,
},
}
const data = encoder.encode(JSON.stringify(payload))
await room.localParticipant.publishData(data, { reliable: true })
const newReaction = {
id: instanceIdRef.current++,
emoji,
participant: room.localParticipant,
}
setReactions((prev) => [...prev, newReaction])
// Remove this reaction after animation
setTimeout(() => {
setReactions((prev) =>
prev.filter((instance) => instance.id !== newReaction.id)
)
}, ANIMATION_DURATION)
}
const debouncedSendReaction = useRateLimiter({
callback: sendReaction,
maxCalls: 10,
windowMs: 1000,
})
return (
<>
<div className={css({ position: 'relative' })}>
@@ -130,7 +72,7 @@ export const ReactionsToggle = () => {
{Object.values(Emoji).map((emoji, index) => (
<Button
key={index}
onPress={() => debouncedSendReaction(emoji)}
onPress={() => sendReaction(emoji)}
aria-label={t('send', { emoji: getEmojiLabel(emoji, t) })}
variant="primaryTextDark"
size="sm"
@@ -155,7 +97,6 @@ export const ReactionsToggle = () => {
</RACPopover>
</DialogTrigger>
</div>
<ReactionPortals reactions={reactions} />
</>
)
}
@@ -0,0 +1,5 @@
export const ANIMATION_DURATION = 3000
export const ANIMATION_DISTANCE = 300
export const FADE_OUT_THRESHOLD = 0.7
export const REACTION_SPAWN_WIDTH_RATIO = 0.2
export const INITIAL_POSITION = 200
@@ -0,0 +1,26 @@
import { useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useSnapshot } from 'valtio'
import { accessibilityStore } from '@/stores/accessibility'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
import { getEmojiLabel } from '../utils'
import { Reaction } from '../types'
export const useAnnounceReaction = (latestReaction: Reaction | undefined) => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { announceReactions } = useSnapshot(accessibilityStore)
const [lastAnnouncedId, setLastAnnouncedId] = useState<string | null>(null)
const announce = useScreenReaderAnnounce()
useEffect(() => {
if (!announceReactions || !latestReaction) return
if (latestReaction.id === lastAnnouncedId) return
const emojiLabel = getEmojiLabel(latestReaction.emoji, t)
const participantName = latestReaction.participantName
announce(t('announce', { name: participantName, emoji: emojiLabel }))
setLastAnnouncedId(latestReaction.id)
}, [announce, latestReaction, lastAnnouncedId, announceReactions, t])
}
@@ -0,0 +1,56 @@
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { reactionsStore } from '@/stores/reactions'
import { NotificationType } from '@/features/notifications/NotificationType'
import { useNotifyParticipants } from '@/features/notifications'
import useRateLimiter from '@/hooks/useRateLimiter'
import { Participant } from 'livekit-client'
import { Emoji } from '../types'
import { ANIMATION_DURATION } from '../constants'
export const useReactions = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls.reactions' })
const { notifyParticipants } = useNotifyParticipants()
const appendReaction = useCallback(
(emoji: Emoji, participant?: Participant) => {
const newReaction = {
id: `${emoji}-${Date.now()}-${Math.random()}`,
emoji,
participantName: participant
? participant.name || participant.identity
: t('you'),
isLocal: !participant,
}
reactionsStore.reactions.push(newReaction)
setTimeout(() => {
const index = reactionsStore.reactions.findIndex(
(r) => r.id === newReaction.id
)
if (index !== -1) reactionsStore.reactions.splice(index, 1)
}, ANIMATION_DURATION)
},
[t]
)
const sendReaction = async (emoji: Emoji) => {
await notifyParticipants({
type: NotificationType.ReactionReceived,
additionalData: { data: { emoji } },
})
appendReaction(emoji)
}
const debouncedSendReaction = useRateLimiter({
callback: sendReaction,
maxCalls: 10,
windowMs: 1000,
})
return {
sendReaction: debouncedSendReaction,
appendReaction,
}
}
@@ -0,0 +1,17 @@
export enum Emoji {
THUMBS_UP = 'thumbs-up',
THUMBS_DOWN = 'thumbs-down',
CLAP = 'clapping-hands',
HEART = 'red-heart',
LAUGHING = 'face-with-tears-of-joy',
SURPRISED = 'face-with-open-mouth',
CELEBRATION = 'party-popper',
PLEASE = 'folded-hands',
}
export interface Reaction {
id: string
emoji: Emoji
participantName: string
isLocal: boolean
}
@@ -51,17 +51,20 @@ const StyledSidePanel = ({
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
margin: '1.5rem 1.5rem 1.5rem 0',
margin: 'var(--sizes-room-side-panel-margin)',
marginLeft: 0,
padding: 0,
gap: 0,
right: 0,
top: 0,
bottom: '80px',
width: '360px',
bottom: 'var(--sizes-room-control-bar)',
width: 'var(--sizes-room-side-panel)',
transition: '.5s cubic-bezier(.4,0,.2,1) 5ms',
})}
style={{
transform: isClosed ? 'translateX(calc(360px + 1.5rem))' : 'none',
transform: isClosed
? 'translateX(calc(var(--sizes-room-side-panel) + var(--sizes-room-side-panel-margin)))'
: 'none',
}}
aria-hidden={isClosed}
aria-label={ariaLabel}
@@ -3,7 +3,6 @@ import { ControlBarAuxProps } from './ControlBar'
import { css } from '@/styled-system/css'
import { LeaveButton } from '../../components/controls/LeaveButton'
import { Track } from 'livekit-client'
import { ReactionsToggle } from '../../components/controls/ReactionsToggle'
import { HandToggle } from '../../components/controls/HandToggle'
import { ScreenShareToggle } from '../../components/controls/ScreenShareToggle'
import { SubtitlesToggle } from '../../components/controls/SubtitlesToggle'
@@ -15,6 +14,7 @@ import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKey
import { useFullScreen } from '../../hooks/useFullScreen'
import { VideoDeviceControl } from '../../components/controls/Device/VideoDeviceControl'
import { AudioDevicesControl } from '../../components/controls/Device/AudioDevicesControl'
import { ReactionsToggle } from '@/features/reactions/components/ReactionsToggle'
export function DesktopControlBar({
onDeviceError,
@@ -20,13 +20,9 @@ import {
import { useTranslation } from 'react-i18next'
import { ControlBar } from './ControlBar/ControlBar'
import { styled } from '@/styled-system/jsx'
import { cva } from '@/styled-system/css'
import { MainNotificationToast } from '@/features/notifications/MainNotificationToast'
import { FocusLayout } from '../components/FocusLayout'
import { ParticipantTile } from '../components/ParticipantTile'
import { SidePanel } from '../components/SidePanel'
import { useSidePanel } from '../hooks/useSidePanel'
import { RecordingProvider } from '@/features/recording'
import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
import { useConnectionObserver } from '../hooks/useConnectionObserver'
@@ -37,35 +33,13 @@ import { useSettingsDialog } from '@/features/settings'
import { SettingsDialogExtendedKey } from '@/features/settings/type'
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
import { useSubtitles } from '@/features/subtitle/hooks/useSubtitles'
import { Subtitles } from '@/features/subtitle/component/Subtitles'
import { CarouselLayout } from '../components/layout/CarouselLayout'
import { GridLayout } from '../components/layout/GridLayout'
import { IsIdleDisconnectModal } from '../components/IsIdleDisconnectModal'
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
const LayoutWrapper = styled(
'div',
cva({
base: {
position: 'relative',
display: 'flex',
width: '100%',
transition: 'height .5s cubic-bezier(0.4,0,0.2,1) 5ms',
},
variants: {
areSubtitlesOpen: {
true: {
height: 'calc(100% - 12rem)',
},
false: {
height: '100%',
},
},
},
})
)
import { ReactionPortals } from '@/features/reactions/components/ReactionPortals'
import { CarouselLayout } from '@/features/layout/components/CarouselLayout'
import { GridLayout } from '@/features/layout/components/GridLayout'
import { RoomContentArea } from '@/features/layout/components/RoomContentArea'
/**
* @public
@@ -253,9 +227,6 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
])
/* eslint-enable react-hooks/exhaustive-deps */
const { isSidePanelOpen } = useSidePanel()
const { areSubtitlesOpen } = useSubtitles()
const [isShareErrorVisible, setIsShareErrorVisible] = useState(false)
return (
@@ -276,57 +247,35 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
onClose={() => setIsShareErrorVisible(false)}
/>
<IsIdleDisconnectModal />
<div
// todo - extract these magic values into constant
style={{
position: 'absolute',
inset: isSidePanelOpen
? `var(--lk-grid-gap) calc(358px + 3rem) calc(80px + var(--lk-grid-gap)) 16px`
: `var(--lk-grid-gap) var(--lk-grid-gap) calc(80px + var(--lk-grid-gap))`,
transition: 'inset .5s cubic-bezier(0.4,0,0.2,1) 5ms',
maxHeight: '100%',
}}
>
<LayoutWrapper areSubtitlesOpen={areSubtitlesOpen}>
<RoomContentArea>
{!focusTrack ? (
<div
style={{
display: 'flex',
position: 'relative',
width: '100%',
}}
className="lk-grid-layout-wrapper"
style={{ height: 'auto' }}
>
{!focusTrack ? (
<div
className="lk-grid-layout-wrapper"
style={{ height: 'auto' }}
>
<GridLayout tracks={tracks} style={{ padding: 0 }}>
<ParticipantTile />
</GridLayout>
</div>
) : (
<div
className="lk-focus-layout-wrapper"
style={{ height: 'auto' }}
>
<FocusLayoutContainer style={{ padding: 0 }}>
<CarouselLayout
tracks={carouselTracks}
style={{
minWidth: '200px',
}}
>
<ParticipantTile />
</CarouselLayout>
{focusTrack && <FocusLayout trackRef={focusTrack} />}
</FocusLayoutContainer>
</div>
)}
<GridLayout tracks={tracks} style={{ padding: 0 }}>
<ParticipantTile />
</GridLayout>
</div>
</LayoutWrapper>
<Subtitles />
<MainNotificationToast />
</div>
) : (
<div
className="lk-focus-layout-wrapper"
style={{ height: 'auto' }}
>
<FocusLayoutContainer style={{ padding: 0 }}>
<CarouselLayout
tracks={carouselTracks}
style={{
minWidth: '200px',
}}
>
<ParticipantTile />
</CarouselLayout>
{focusTrack && <FocusLayout trackRef={focusTrack} />}
</FocusLayoutContainer>
</div>
)}
</RoomContentArea>
<ControlBar
onDeviceError={(e) => {
console.error(e)
@@ -346,6 +295,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
<ConnectionStateToast />
<RecordingProvider />
<SettingsDialogProvider />
<ReactionPortals />
</div>
)
}
+10
View File
@@ -0,0 +1,10 @@
import { proxy } from 'valtio'
import { Reaction } from '@/features/reactions/types'
type State = {
reactions: Reaction[]
}
export const reactionsStore = proxy<State>({
reactions: [],
})
@@ -1,3 +1,29 @@
_summaryEnvVars: &summaryEnvVars
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000/
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
_summaryImage: &summaryImage
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
image:
repository: localhost:5001/meet-backend
pullPolicy: Always
@@ -142,66 +168,15 @@ posthog:
summary:
replicas: 1
image: *summaryImage
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "uvicorn"
- "summary.main:app"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--reload"
<<: *summaryEnvVars
celeryTranscribe:
replicas: 1
image: *summaryImage
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
<<: *summaryEnvVars
command:
- "celery"
- "-A"
@@ -213,31 +188,9 @@ celeryTranscribe:
celerySummarize:
replicas: 1
image: *summaryImage
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
<<: *summaryEnvVars
command:
- "celery"
- "-A"
@@ -1,3 +1,29 @@
_summaryEnvVars: &summaryEnvVars
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000/
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
_summaryImage: &summaryImage
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
image:
repository: localhost:5001/meet-backend
pullPolicy: Always
@@ -160,69 +186,15 @@ posthog:
summary:
replicas: 1
image: *summaryImage
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000/
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "uvicorn"
- "summary.main:app"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--reload"
<<: *summaryEnvVars
celeryTranscribe:
replicas: 1
image: *summaryImage
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000/
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
<<: *summaryEnvVars
command:
- "celery"
- "-A"
@@ -235,32 +207,9 @@ celeryTranscribe:
celerySummarize:
replicas: 1
image: *summaryImage
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000/
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
WHISPERX_DEFAULT_LANGUAGE: fr
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
<<: *summaryEnvVars
command:
- "celery"
- "-A"
@@ -1,43 +0,0 @@
replicaCount: 1
terminationGracePeriodSeconds: 18000
egress:
log_level: debug
ws_url: ws://livekit-livekit-server:80
insecure: true
enable_chrome_sandbox: true
{{- with .Values.livekit.keys }}
{{- range $key, $value := . }}
api_key: {{ $key }}
api_secret: {{ $value }}
{{- end }}
{{- end }}
redis:
address: redis-master:6379
password: pass
s3:
access_key: meet
secret: password
region: local
bucket: meet-media-storage
endpoint: http://minio:9000
force_path_style: true
loadBalancer:
type: nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
tls:
- hosts:
- livekit-egress.127.0.0.1.nip.io
secretName: livekit-egress-dinum-cert
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 5
nodeSelector: {}
resources: {}
@@ -1,49 +0,0 @@
replicaCount: 1
terminationGracePeriodSeconds: 18000
image:
repository: localhost:5001/meet-livekit
pullPolicy: Always
tag: "latest"
livekit:
log_level: debug
rtc:
use_external_ip: false
port_range_start: 50000
port_range_end: 60000
tcp_port: 7881
redis:
address: redis-master:6379
password: pass
keys:
turn:
enabled: true
udp_port: 443
domain: livekit.127.0.0.1.nip.io
loadBalancerAnnotations: {}
webhook:
api_key:
urls:
- https://meet.127.0.0.1.nip.io/api/v1.0/rooms/webhooks-livekit/
loadBalancer:
type: nginx
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
tls:
- hosts:
- livekit.127.0.0.1.nip.io
secretName: livekit-dinum-cert
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 5
targetCPUUtilizationPercentage: 60
nodeSelector: {}
resources: {}
-290
View File
@@ -1,290 +0,0 @@
secrets:
- name: oidcLogin
itemId: a25effec-eaea-4ce1-9ed8-3a3cc1c734db
field: username
podVariable: OIDC_RP_CLIENT_ID
clusterSecretStore: bitwarden-login-meet
- name: oidcPass
itemId: a25effec-eaea-4ce1-9ed8-3a3cc1c734db
field: password
podVariable: OIDC_RP_CLIENT_SECRET
clusterSecretStore: bitwarden-login-meet
- name: brevoApiKey
itemId: 99107889-6124-4436-97cc-a5193f28443f
field: password
podVariable: BREVO_API_KEY
clusterSecretStore: bitwarden-login-meet
image:
repository: localhost:5001/meet-backend
pullPolicy: Always
tag: "latest"
backend:
replicas: 1
envVars:
DJANGO_CSRF_TRUSTED_ORIGINS: https://meet.127.0.0.1.nip.io,http://meet.127.0.0.1.nip.io
DJANGO_CONFIGURATION: Production
DJANGO_ALLOWED_HOSTS: meet.127.0.0.1.nip.io
DJANGO_SECRET_KEY: {{ .Values.djangoSecretKey }}
DJANGO_SETTINGS_MODULE: meet.settings
DJANGO_SILENCED_SYSTEM_CHECKS: security.W004, security.W008
DJANGO_SUPERUSER_PASSWORD: admin
DJANGO_EMAIL_HOST: "mailcatcher"
DJANGO_EMAIL_PORT: 1025
DJANGO_EMAIL_USE_SSL: False
DJANGO_EMAIL_BRAND_NAME: "La Suite Numérique"
DJANGO_EMAIL_SUPPORT_EMAIL: "test@yopmail.com"
DJANGO_EMAIL_LOGO_IMG: https://meet.127.0.0.1.nip.io/assets/logo-suite-numerique.png
DJANGO_EMAIL_DOMAIN: meet.127.0.0.1.nip.io
DJANGO_EMAIL_APP_BASE_URL: https://meet.127.0.0.1.nip.io
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
OIDC_OP_USER_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/userinfo
OIDC_OP_LOGOUT_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/session/end
OIDC_RP_CLIENT_ID:
secretKeyRef:
name: backend
key: OIDC_RP_CLIENT_ID
OIDC_RP_CLIENT_SECRET:
secretKeyRef:
name: backend
key: OIDC_RP_CLIENT_SECRET
OIDC_RP_SIGN_ALGO: RS256
OIDC_RP_SCOPES: "openid email given_name usual_name"
OIDC_REDIRECT_ALLOWED_HOSTS: https://meet.127.0.0.1.nip.io
OIDC_AUTH_REQUEST_EXTRA_PARAMS: "{'acr_values': 'eidas1'}"
LOGIN_REDIRECT_URL: https://meet.127.0.0.1.nip.io
LOGIN_REDIRECT_URL_FAILURE: https://meet.127.0.0.1.nip.io
LOGOUT_REDIRECT_URL: https://meet.127.0.0.1.nip.io
DB_HOST: postgres
DB_NAME: meet
DB_USER: dinum
DB_PASSWORD: pass
DB_PORT: 5432
REDIS_URL: redis://default:pass@redis-master:6379/1
STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
{{- with .Values.livekit.keys }}
{{- range $key, $value := . }}
LIVEKIT_API_SECRET: {{ $value }}
LIVEKIT_API_KEY: {{ $key }}
{{- end }}
{{- end }}
LIVEKIT_API_URL: https://livekit.127.0.0.1.nip.io/
LIVEKIT_WEBHOOK_EVENTS_FILTER_REGEX: "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
ALLOW_UNREGISTERED_ROOMS: False
FRONTEND_SILENCE_LIVEKIT_DEBUG: False
FRONTEND_SUPPORT: "{'id': '58ea6697-8eba-4492-bc59-ad6562585041'}"
AWS_S3_ENDPOINT_URL: http://minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_REGION_NAME: local
MEDIA_BASE_URL: https://meet.127.0.0.1.nip.io
RECORDING_ENABLE: True
RECORDING_STORAGE_EVENT_ENABLE: True
RECORDING_STORAGE_EVENT_TOKEN: password
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
SUMMARY_SERVICE_API_TOKEN: password
RECORDING_DOWNLOAD_BASE_URL: https://meet.127.0.0.1.nip.io/recording
SIGNUP_NEW_USER_TO_MARKETING_EMAIL: True
BREVO_API_KEY:
secretKeyRef:
name: backend
key: BREVO_API_KEY
BREVO_API_CONTACT_LIST_IDS: 8
ROOM_TELEPHONY_ENABLED: True
SSL_CERT_FILE: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem
migrate:
command:
- "/bin/sh"
- "-c"
- |
python manage.py migrate --no-input
restartPolicy: Never
command:
- "gunicorn"
- "-c"
- "/usr/local/etc/gunicorn/meet.py"
- "meet.wsgi:application"
- "--reload"
createsuperuser:
command:
- "/bin/sh"
- "-c"
- |
python manage.py createsuperuser --email admin@example.com --password admin
restartPolicy: Never
# Extra volume mounts to manage our local custom CA and avoid to set ssl_verify: false
extraVolumeMounts:
- name: certs
mountPath: /app/.venv/lib/python3.13/site-packages/certifi/cacert.pem
subPath: cacert.pem
# Extra volumes to manage our local custom CA and avoid to set ssl_verify: false
extraVolumes:
- name: certs
configMap:
name: certifi
items:
- key: cacert.pem
path: cacert.pem
frontend:
envVars:
VITE_APP_TITLE: "LaSuite Meet"
VITE_PORT: 8080
VITE_HOST: 0.0.0.0
VITE_API_BASE_URL: https://meet.127.0.0.1.nip.io/
replicas: 1
image:
repository: localhost:5001/meet-frontend
pullPolicy: Always
tag: "latest"
ingress:
enabled: true
host: meet.127.0.0.1.nip.io
ingressAdmin:
enabled: true
host: meet.127.0.0.1.nip.io
ingressWebhook:
enabled: true
host: meet.127.0.0.1.nip.io
posthog:
ingress:
enabled: false
ingressAssets:
enabled: false
summary:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "uvicorn"
- "summary.main:app"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--reload"
celeryTranscribe:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "celery"
- "-A"
- "summary.core.celery_worker"
- "worker"
- "--pool=solo"
- "--loglevel=info"
- "-Q transcribe-queue"
celerySummarize:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_S3_SECURE_ACCESS: False
WHISPERX_API_KEY: your-secret-value
WHISPERX_BASE_URL: https://configure-your-url.com
WHISPERX_ASR_MODEL: large-v2
LLM_BASE_URL: https://configure-your-url.com
LLM_API_KEY: your-secret-value
LLM_MODEL: meta-llama/Llama-3.1-8B-Instruct
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
TASK_TRACKER_REDIS_URL: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "celery"
- "-A"
- "summary.core.celery_worker"
- "worker"
- "--pool=solo"
- "--loglevel=info"
- "-Q summarize-queue"
ingressMedia:
enabled: true
host: meet.127.0.0.1.nip.io
annotations:
nginx.ingress.kubernetes.io/auth-url: https://meet.127.0.0.1.nip.io/api/v1.0/recordings/media-auth/
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Amz-Date, X-Amz-Content-SHA256"
nginx.ingress.kubernetes.io/upstream-vhost: minio.meet.svc.cluster.local:9000
nginx.ingress.kubernetes.io/rewrite-target: /meet-media-storage/$1
serviceMedia:
host: minio.meet.svc.cluster.local
port: 9000
-10
View File
@@ -1,10 +0,0 @@
djangoSecretKey: 7K9mQ2xR8pL3vN6tY1sW4jH5cE0zF9bM2qA7uI3oP6rT1wErt12te12
livekit:
keys:
devkey: secret
webhook:
api_key: devkey
livekitApi:
key: devkey
secret: secret
-4
View File
@@ -7,10 +7,6 @@ environments:
values:
- version: 0.0.1
- env.d/dev-dinum/values.secrets.yaml
dev:
values:
- version: 0.0.1
- env.d/dev/values.secrets.yaml
---
repositories: