(all) add organizations, resources, channels, and infra migration (#34)

Add multi-tenant organization model populated from OIDC claims with
org-scoped user discovery, CalDAV principal filtering, and cross-org
isolation at the SabreDAV layer.

Add bookable resource principals (rooms, equipment) with CalDAV
auto-scheduling that handles conflict detection, auto-accept/decline,
and org-scoped booking enforcement. Fixes #14.

Replace CalendarSubscriptionToken with a unified Channel model
supporting CalDAV integration tokens and iCal feed URLs, with
encrypted token storage and role-based access control. Fixes #16.

Migrate task queue from Celery to Dramatiq with async ICS import,
progress tracking, and task status polling endpoint.

Replace nginx with Caddy for both the reverse proxy and frontend
static serving. Switch frontend package manager from yarn/pnpm to
npm and upgrade Node to 24, Next.js to 16, TypeScript to 5.9.

Harden security with fail-closed entitlements, RSVP rate limiting
and token expiry, CalDAV proxy path validation blocking internal
API routes, channel path scope enforcement, and ETag-based
conflict prevention.

Add frontend pages for resource management and integration channel
CRUD, with resource booking in the event modal.

Restructure CalDAV paths to /calendars/users/ and
/calendars/resources/ with nested principal collections in SabreDAV.
This commit is contained in:
Sylvain Zimmer
2026-03-09 09:09:34 +01:00
committed by GitHub
parent cd2b15b3b5
commit 9c18f96090
176 changed files with 26903 additions and 12108 deletions

View File

@@ -1,38 +0,0 @@
#!/usr/bin/env bash
# shellcheck source=bin/_config.sh
source "$(dirname "${BASH_SOURCE[0]}")/_config.sh"
declare diff_from
declare -a paths
declare -a args
# Parse options
for arg in "$@"
do
case $arg in
--diff-only=*)
diff_from="${arg#*=}"
shift
;;
-*)
args+=("$arg")
shift
;;
*)
paths+=("$arg")
shift
;;
esac
done
if [[ -n "${diff_from}" ]]; then
# Run pylint only on modified files located in src/backend
# (excluding deleted files and migration files)
# shellcheck disable=SC2207
paths=($(git diff "${diff_from}" --name-only --diff-filter=d -- src/backend ':!**/migrations/*.py' | grep -E '^src/backend/.*\.py$'))
fi
# Fix docker vs local path when project sources are mounted as a volume
read -ra paths <<< "$(echo "${paths[@]}" | sed "s|src/backend/||g")"
_dc_run --no-deps backend-dev pylint "${paths[@]}" "${args[@]}"

View File

@@ -5,14 +5,27 @@ set -o pipefail # don't ignore exit codes when piping output
echo "-----> Running post-frontend script"
# Move the frontend build to the nginx root and clean up
# Move the frontend build to the app root and clean up
mkdir -p build/
mv src/frontend/apps/calendars/out build/frontend-out
cp src/frontend/apps/calendars/src/features/i18n/translations.json translations.json
mv src/backend/* ./
mv src/nginx/* ./
# Download Caddy binary with checksum verification
CADDY_VERSION="2.11.2"
CADDY_SHA256="94391dfefe1f278ac8f387ab86162f0e88d87ff97df367f360e51e3cda3df56f"
CADDY_TAR="/tmp/caddy.tar.gz"
curl -fsSL -o "$CADDY_TAR" \
"https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_linux_amd64.tar.gz"
echo "${CADDY_SHA256} ${CADDY_TAR}" | sha256sum -c -
tar -xz -C bin/ caddy < "$CADDY_TAR"
rm "$CADDY_TAR"
chmod +x bin/caddy
# Copy Caddyfile (uses {$ENV} vars natively, no ERB needed)
cp src/proxy/Caddyfile ./Caddyfile
echo "3.13" > .python-version
@@ -25,22 +38,25 @@ mkdir -p "$DEB_DIR" "$PHP_PREFIX"
# Hardcoded Launchpad URLs for PHP 8.3.6-0maysync1 (Ubuntu Noble amd64)
# Source: https://launchpad.net/ubuntu/noble/amd64/php8.3-fpm/8.3.6-0maysync1
declare -A PHP_DEBS=(
[php8.3-cli]="http://launchpadlibrarian.net/724872605/php8.3-cli_8.3.6-0maysync1_amd64.deb"
[php8.3-fpm]="http://launchpadlibrarian.net/724872610/php8.3-fpm_8.3.6-0maysync1_amd64.deb"
[php8.3-common]="http://launchpadlibrarian.net/724872606/php8.3-common_8.3.6-0maysync1_amd64.deb"
[php8.3-opcache]="http://launchpadlibrarian.net/724872623/php8.3-opcache_8.3.6-0maysync1_amd64.deb"
[php8.3-readline]="http://launchpadlibrarian.net/724872627/php8.3-readline_8.3.6-0maysync1_amd64.deb"
[php8.3-pgsql]="http://launchpadlibrarian.net/724872624/php8.3-pgsql_8.3.6-0maysync1_amd64.deb"
[php8.3-xml]="http://launchpadlibrarian.net/724872633/php8.3-xml_8.3.6-0maysync1_amd64.deb"
[php8.3-mbstring]="http://launchpadlibrarian.net/724872617/php8.3-mbstring_8.3.6-0maysync1_amd64.deb"
[php8.3-curl]="http://launchpadlibrarian.net/724872607/php8.3-curl_8.3.6-0maysync1_amd64.deb"
[php-common]="http://launchpadlibrarian.net/710804987/php-common_93ubuntu2_all.deb"
# Format: "package_name url sha256"
PHP_DEBS=(
"php8.3-cli http://launchpadlibrarian.net/724872605/php8.3-cli_8.3.6-0maysync1_amd64.deb 8cb7461dd06fb214b30c060b80b1c6f95d1ff5e2656fdadf215e50b9f299f196"
"php8.3-fpm http://launchpadlibrarian.net/724872610/php8.3-fpm_8.3.6-0maysync1_amd64.deb b3a9435025766bcbf6c16199c06481c5196098c084933dfabf8867c982edc2b2"
"php8.3-common http://launchpadlibrarian.net/724872606/php8.3-common_8.3.6-0maysync1_amd64.deb 0e0d0ad9c17add5fb2afcc14c6fffb81c2beb99114108b8ebd0461d910a79dfc"
"php8.3-opcache http://launchpadlibrarian.net/724872623/php8.3-opcache_8.3.6-0maysync1_amd64.deb 13b2662201c57904c1eda9b048b1349acaf3609c7d9e8df5b2d93833a059bdb0"
"php8.3-readline http://launchpadlibrarian.net/724872627/php8.3-readline_8.3.6-0maysync1_amd64.deb 380f8ed79196914ee2eebb68bf518a752204826af1fdb8a0d5c9609c76086b90"
"php8.3-pgsql http://launchpadlibrarian.net/724872624/php8.3-pgsql_8.3.6-0maysync1_amd64.deb b1ed204c980c348d1870cfa88c1b40257621ae5696a2a7f44f861a9d00eb7477"
"php8.3-xml http://launchpadlibrarian.net/724872633/php8.3-xml_8.3.6-0maysync1_amd64.deb 6c6ded219d1966a50108d032b7a522e641765a8a6aa48747483313fa7dafd533"
"php8.3-mbstring http://launchpadlibrarian.net/724872617/php8.3-mbstring_8.3.6-0maysync1_amd64.deb 42c89945eb105c2232ab208b893ef65e9abc8af5c95aa10c507498655ef812c4"
"php8.3-curl http://launchpadlibrarian.net/724872607/php8.3-curl_8.3.6-0maysync1_amd64.deb 95d46a22e6b493ba0b6256cf036a2a37d4b9b5f438968073709845af1c17df4c"
"php-common http://launchpadlibrarian.net/710804987/php-common_93ubuntu2_all.deb 39b15c407700e81ddd62580736feba31b187ffff56f6835dac5fa8f847c42529"
)
for pkg in "${!PHP_DEBS[@]}"; do
for entry in "${PHP_DEBS[@]}"; do
read -r pkg url sha256 <<< "$entry"
echo " Downloading ${pkg}"
curl -fsSL -o "$DEB_DIR/${pkg}.deb" "${PHP_DEBS[$pkg]}"
curl -fsSL -o "$DEB_DIR/${pkg}.deb" "$url"
echo "${sha256} ${DEB_DIR}/${pkg}.deb" | sha256sum -c -
done
for deb in "$DEB_DIR"/*.deb; do
@@ -90,11 +106,14 @@ chmod +x bin/php
echo "-----> PHP version: $("$PHP_PREFIX/usr/bin/php8.3" -n -c "$BUILD_INI" -v | head -1)"
echo "-----> PHP modules: $("$PHP_PREFIX/usr/bin/php8.3" -n -c "$BUILD_INI" -m | tr '\n' ' ')"
# Download Composer and install SabreDAV dependencies
# Download Composer with integrity verification and install SabreDAV dependencies
echo "-----> Installing SabreDAV dependencies"
COMPOSER_VERSION="2.9.5"
COMPOSER_SHA256="c86ce603fe836bf0861a38c93ac566c8f1e69ac44b2445d9b7a6a17ea2e9972a"
curl -fsSL -o bin/composer.phar \
https://getcomposer.org/download/latest-stable/composer.phar
cp -r docker/sabredav sabredav
"https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar"
echo "${COMPOSER_SHA256} bin/composer.phar" | sha256sum -c -
cp -r src/caldav sabredav
cd sabredav
"../$PHP_PREFIX/usr/bin/php8.3" -n -c "$BUILD_INI" ../bin/composer.phar install \
--no-dev --optimize-autoloader --no-interaction

View File

@@ -3,6 +3,11 @@
# Parse DATABASE_URL into PG* vars for PHP and psql
source bin/export_pg_vars.sh
# Set defaults for Caddy env vars
export CALENDARS_FRONTEND_ROOT="${CALENDARS_FRONTEND_ROOT:-/app/build/frontend-out}"
export CALENDARS_FRONTEND_BACKEND_SERVER="${CALENDARS_FRONTEND_BACKEND_SERVER:-localhost:8000}"
export DJANGO_ADMIN_URL="${DJANGO_ADMIN_URL:-admin}"
# Start PHP-FPM for SabreDAV (CalDAV server)
.php/usr/sbin/php-fpm8.3 \
-n -c /app/.php/php.ini \
@@ -12,11 +17,11 @@ source bin/export_pg_vars.sh
# Start the Django backend
gunicorn -b :8000 calendars.wsgi:application --log-file - &
# Start the Nginx server
bin/run &
# Start the Caddy server
bin/caddy run --config Caddyfile --adapter caddyfile &
# if the current shell is killed, also terminate all its children
trap "pkill SIGTERM -P $$" SIGTERM
trap "pkill -SIGTERM -P $$" SIGTERM
# wait for a single child to finish,
wait -n