Compare commits

..

2 Commits

Author SHA1 Message Date
lebaudantoine c7b05a4487 ⚡️(devx) switch devstack to node:22-alpine
The devstack was pulling the full `node:22` image, around 1.6 GB.
Switch to `node:22-alpine`, which is much smaller, to speed up the
devstack bootstrap time and reduce disk usage.
2026-10-02 15:46:34 +02:00
lebaudantoine 971a7295ca ⚡️(devx) use a single Redis image version in the devstack
The devstack was pulling two different versions of the Redis image.
Align everything on a single version to save a few MB of network
bandwidth when bootstrapping the stack.

Late-night minor optimization.
2026-10-01 16:38:31 +02:00
8 changed files with 55 additions and 44 deletions
-1
View File
@@ -15,7 +15,6 @@ and this project adheres to
### Fixed
- ♿️(frontend) make participant pagination readable and keyboard reachable #1703
- 🐛(frontend) enforce recording-mode permissions on the checkboxes
- 🔒️(agents) fix util-linux CVEs reported by Cyberwatch
- 🔒️(backend) fix HIGH CVEs in Django and urllib3
+2 -3
View File
@@ -37,14 +37,13 @@ RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-dev
# ---- mails ----
FROM node:22 AS mail-builder
FROM node:22-alpine AS mail-builder
COPY ./src/mail /mail/app
WORKDIR /mail/app
RUN yarn install --frozen-lockfile && \
yarn build
RUN npm ci --ignore-scripts && npm run build
# ---- static link collector ----
+2 -2
View File
@@ -172,7 +172,7 @@ services:
working_dir: /app
node:
image: node:22
image: node:22-alpine
user: "${DOCKER_USER:-1000}"
environment:
HOME: /tmp
@@ -271,7 +271,7 @@ services:
- /app/.venv
redis-summary:
image: redis
image: redis:5
ports:
- "6379:6379"
@@ -67,7 +67,7 @@ export function GridLayout({ tracks, ...props }: GridLayoutProps) {
totalPageCount={pagination.totalPageCount}
currentPage={pagination.currentPage}
/>
<PaginationControl {...pagination} />
<PaginationControl pagesContainer={gridEl} {...pagination} />
</>
)}
</div>
@@ -1,14 +1,17 @@
import * as React from 'react'
import { createInteractingObservable } from '@livekit/components-core'
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
import { Button } from '@/primitives'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { css } from '@/styled-system/css'
import { Toolbar } from 'react-aria-components'
export interface PaginationControlProps {
totalPageCount: number
nextPage: () => void
prevPage: () => void
currentPage: number
pagesContainer?: React.RefObject<HTMLElement>
}
export function PaginationControl({
@@ -16,53 +19,65 @@ export function PaginationControl({
nextPage,
prevPage,
currentPage,
pagesContainer: connectedElement,
}: PaginationControlProps) {
const { t } = useTranslation('rooms', { keyPrefix: 'pagination' })
const [interactive, setInteractive] = useState(false)
useEffect(() => {
let subscription:
| ReturnType<ReturnType<typeof createInteractingObservable>['subscribe']>
| undefined
if (connectedElement) {
subscription = createInteractingObservable(
connectedElement.current,
2000
).subscribe(setInteractive)
}
return () => {
if (subscription) {
subscription.unsubscribe()
}
}
}, [connectedElement])
if (totalPageCount <= 1) return null
return (
<Toolbar
<nav
aria-label={t('label')}
className={css({
position: 'absolute',
bottom: '1rem',
left: '50%',
zIndex: 2,
transform: 'translateX(-50%)',
display: 'flex',
alignItems: 'center',
gap: '0.125rem',
backgroundColor: 'primaryDark.50',
borderRadius: '2rem',
alignItems: 'stretch',
backgroundColor: 'var(--lk-control-bg)',
borderRadius: 'var(--lk-border-radius)',
transition: 'opacity ease-in-out .15s',
display: 'none',
border: '1px solid',
borderColor: 'primaryDark.200',
padding: '0.375rem',
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.45)',
borderColor: 'primaryDark.100',
overflow: 'hidden',
})}
style={{
display: interactive ? 'flex' : 'none',
}}
data-lk-user-interaction={interactive}
>
<Button
isDisabled={currentPage === 1}
isDisabled={currentPage == 1}
onPress={prevPage}
size="sm"
square
variant="primaryTextDark"
size="xs"
variant="quaternaryText"
aria-label={t('previous')}
tooltip={t('previous')}
>
<RiArrowLeftSLine size={20} />
<RiArrowLeftSLine />
</Button>
<span
role="status"
className={css({
color: 'white',
fontSize: '0.8125rem',
fontWeight: 500,
minWidth: '3.5rem',
textAlign: 'center',
userSelect: 'none',
padding: '0 0.35rem',
whiteSpace: 'nowrap',
padding: '0.25rem 0.5rem',
})}
>
{t('count', {
@@ -71,16 +86,14 @@ export function PaginationControl({
})}
</span>
<Button
isDisabled={currentPage === totalPageCount}
isDisabled={currentPage == totalPageCount}
onPress={nextPage}
size="sm"
square
variant="primaryTextDark"
size="xs"
variant="quaternaryText"
aria-label={t('next')}
tooltip={t('next')}
>
<RiArrowRightSLine size={20} />
<RiArrowRightSLine />
</Button>
</Toolbar>
</nav>
)
}
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -eo pipefail
#!/bin/sh
set -e
# Run html-to-text to convert all html files to text files
DIR_MAILS="../backend/core/templates/mail/"
+1 -1
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/sh
# Run mjml command to convert all mjml templates to html files
DIR_MAILS="../backend/core/templates/mail/html/"
+2 -2
View File
@@ -9,8 +9,8 @@
},
"private": true,
"scripts": {
"build-mjml-to-html": "bash ./bin/mjml-to-html",
"build-html-to-plain-text": "bash ./bin/html-to-plain-text",
"build-mjml-to-html": "sh ./bin/mjml-to-html",
"build-html-to-plain-text": "sh ./bin/html-to-plain-text",
"build": "npm run build-mjml-to-html && npm run build-html-to-plain-text"
},
"volta": {