Files
calendars/Makefile
Sylvain Zimmer 9c18f96090 (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.
2026-03-09 09:09:34 +01:00

308 lines
8.3 KiB
Makefile

# Note to developers:
#
# While editing this file, please respect the following statements:
#
# 1. Every variable should be defined in the ad hoc VARIABLES section with a
# relevant subsection
# 2. Every new rule should be defined in the ad hoc RULES section with a
# relevant subsection depending on the targeted service
# 3. Rules should be sorted alphabetically within their section
# 4. When a rule has multiple dependencies, you should:
# - duplicate the rule name to add the help string (if required)
# - write one dependency per line to increase readability and diffs
# 5. .PHONY rule statement should be written after the corresponding rule
# ==============================================================================
# VARIABLES
BOLD := \033[1m
RESET := \033[0m
GREEN := \033[1;32m
BLUE := \033[1;34m
# -- Docker
# Get the current user ID to use for docker run and docker exec commands
DOCKER_UID = $(shell id -u)
DOCKER_GID = $(shell id -g)
DOCKER_USER = $(DOCKER_UID):$(DOCKER_GID)
COMPOSE = DOCKER_USER=$(DOCKER_USER) docker compose
COMPOSE_EXEC = $(COMPOSE) exec
COMPOSE_EXEC_APP = $(COMPOSE_EXEC) backend-dev
COMPOSE_RUN = $(COMPOSE) run --rm
COMPOSE_RUN_APP = $(COMPOSE_RUN) backend-dev
COMPOSE_RUN_APP_NO_DEPS = $(COMPOSE_RUN) --no-deps backend-dev
# -- Backend
MANAGE = $(COMPOSE_RUN_APP) python manage.py
MANAGE_EXEC = $(COMPOSE_EXEC_APP) python manage.py
PSQL_E2E = ./bin/postgres_e2e
# ==============================================================================
# RULES
default: help
data/media:
@mkdir -p data/media
data/static:
@mkdir -p data/static
# -- Project
create-env-files: ## Create empty .local env files for local development
create-env-files: \
env.d/development/postgresql.local \
env.d/development/keycloak.local \
env.d/development/backend.local \
env.d/development/frontend.local \
env.d/development/caldav.local
.PHONY: create-env-files
env.d/development/%.local:
@echo "# Local development overrides for $(notdir $*)" > $@
@echo "# Add your local-specific environment variables below:" >> $@
@echo "# Example: DJANGO_DEBUG=True" >> $@
@echo "" >> $@
create-docker-network: ## create the docker network if it doesn't exist
@docker network create lasuite-network || true
.PHONY: create-docker-network
bootstrap: ## Prepare the project for local development
bootstrap: \
data/media \
data/static \
create-env-files \
build \
create-docker-network \
migrate \
migrate-caldav \
start
.PHONY: bootstrap
update: ## Update the project with latest changes
@$(MAKE) data/media
@$(MAKE) data/static
@$(MAKE) create-env-files
@$(MAKE) build
@$(MAKE) migrate
@$(MAKE) migrate-caldav
@$(MAKE) install-frozen-front
.PHONY: update
# -- Docker/compose
build: cache ?= # --no-cache
build: ## build the project containers
@$(COMPOSE) build $(cache)
.PHONY: build
down: ## stop and remove containers, networks, images, and volumes
@$(COMPOSE) down
rm -rf data/postgresql.*
.PHONY: down
logs: ## display all services logs (follow mode)
@$(COMPOSE) logs -f
.PHONY: logs
start: ## start all development services
@$(COMPOSE) up --force-recreate -d worker-dev frontend-dev
.PHONY: start
start-back: ## start backend services only (for local frontend development)
@$(COMPOSE) up --force-recreate -d worker-dev
.PHONY: start-back
status: ## an alias for "docker compose ps"
@$(COMPOSE) ps
.PHONY: status
stop: ## stop all development services
@$(COMPOSE) stop
.PHONY: stop
restart: ## restart all development services
restart: \
stop \
start
.PHONY: restart
migrate-caldav: ## Initialize CalDAV server database schema
@echo "$(BOLD)Initializing CalDAV server database schema...$(RESET)"
@$(COMPOSE) run --rm caldav /usr/local/bin/init-database.sh
@echo "$(GREEN)CalDAV server initialized$(RESET)"
.PHONY: migrate-caldav
# -- Linters
lint: ## run all linters
lint: \
lint-back \
lint-front
.PHONY: lint
lint-back: ## run back-end linters (with auto-fix)
lint-back: \
format-back \
check-back \
analyze-back
.PHONY: lint-back
format-back: ## format back-end python sources with ruff
@$(COMPOSE_RUN_APP_NO_DEPS) ruff format .
.PHONY: format-back
check-back: ## check back-end python sources with ruff
@$(COMPOSE_RUN_APP_NO_DEPS) ruff check . --fix
.PHONY: check-back
analyze-back: ## lint all back-end python sources with pylint
@$(COMPOSE_RUN_APP_NO_DEPS) pylint .
.PHONY: analyze-back
lint-front: ## run the frontend linter
@$(COMPOSE) run --rm frontend-dev sh -c "cd apps/calendars && npm run lint"
.PHONY: lint-front
typecheck-front: ## run the frontend type checker
@$(COMPOSE) run --rm frontend-dev sh -c "cd apps/calendars && npx tsc --noEmit"
.PHONY: typecheck-front
# -- Tests
test: ## run all tests
test: \
test-back-parallel \
test-front
.PHONY: test
test-back: ## run back-end tests
@echo "$(BOLD)Running tests...$(RESET)"
@args="$(filter-out $@,$(MAKECMDGOALS))" && \
bin/pytest $${args:-${1}}
.PHONY: test-back
test-back-parallel: ## run all back-end tests in parallel
@args="$(filter-out $@,$(MAKECMDGOALS))" && \
bin/pytest -n auto $${args:-${1}}
.PHONY: test-back-parallel
test-front: ## run the frontend tests
@args="$(filter-out $@,$(MAKECMDGOALS))" && \
$(COMPOSE) run --rm frontend-dev sh -c "cd apps/calendars && npm test -- $${args:-${1}}"
.PHONY: test-front
# -- E2E Tests
bootstrap-e2e: ## bootstrap the backend for e2e tests, without frontend
bootstrap-e2e: \
data/media \
data/static \
create-env-files \
build \
create-docker-network \
start-back-e2e
.PHONY: bootstrap-e2e
clear-db-e2e: ## quickly clears the database for e2e tests
$(PSQL_E2E) -c "$$(cat bin/clear_db_e2e.sql)"
.PHONY: clear-db-e2e
start-back-e2e: ## start the backend for e2e tests
@$(MAKE) stop
rm -rf data/postgresql.e2e
@ENV_OVERRIDE=e2e $(MAKE) start-back
@ENV_OVERRIDE=e2e $(MAKE) migrate
.PHONY: start-back-e2e
test-e2e: ## run the e2e tests, example: make test-e2e -- --project chromium --headed
@$(MAKE) start-back-e2e
@args="$(filter-out $@,$(MAKECMDGOALS))" && \
cd src/frontend/apps/e2e && npm test $${args:-${1}}
.PHONY: test-e2e
# -- Backend
makemigrations: ## run django makemigrations
@echo "$(BOLD)Running makemigrations$(RESET)"
@$(COMPOSE) up -d postgresql
@$(MANAGE) makemigrations
.PHONY: makemigrations
migrate: ## run django migrations
@echo "$(BOLD)Running migrations$(RESET)"
@$(COMPOSE) up -d postgresql
@$(MANAGE) migrate
.PHONY: migrate
superuser: ## Create an admin superuser with password "admin"
@echo "$(BOLD)Creating a Django superuser$(RESET)"
@$(MANAGE) createsuperuser --email admin@example.com --password admin
.PHONY: superuser
shell-back: ## open a shell in the backend container
@$(COMPOSE) run --rm --build backend-dev /bin/sh
.PHONY: shell-back
exec-back: ## open a shell in the running backend-dev container
@$(COMPOSE) exec backend-dev /bin/sh
.PHONY: exec-back
shell-back-django: ## connect to django shell
@$(MANAGE) shell
.PHONY: shell-back-django
back-lock: ## regenerate the uv.lock file
@echo "$(BOLD)Regenerating uv.lock$(RESET)"
@docker run --rm -v $(PWD)/src/backend:/app -w /app ghcr.io/astral-sh/uv:python3.13-alpine uv lock
.PHONY: back-lock
# -- Database
shell-db: ## connect to database shell
@$(COMPOSE) exec backend-dev python manage.py dbshell
.PHONY: shell-db
reset-db: FLUSH_ARGS ?=
reset-db: ## flush database
@echo "$(BOLD)Flush database$(RESET)"
@$(MANAGE) flush $(FLUSH_ARGS)
.PHONY: reset-db
demo: ## flush db then create a demo
@$(MAKE) reset-db
@$(MANAGE) create_demo
.PHONY: demo
# -- Frontend
install-front: ## install the frontend dependencies
@$(COMPOSE) run --rm frontend-dev sh -c "npm install"
.PHONY: install-front
install-frozen-front: ## install frontend dependencies from lockfile
@echo "Installing frontend dependencies..."
@$(COMPOSE) run --rm frontend-dev sh -c "npm ci"
.PHONY: install-frozen-front
shell-front: ## open a shell in the frontend container
@$(COMPOSE) run --rm frontend-dev /bin/sh
.PHONY: shell-front
# -- Misc
clean: ## restore repository state as it was freshly cloned
git clean -idx
.PHONY: clean
clean-media: ## remove all media files
rm -rf data/media/*
.PHONY: clean-media
help:
@echo "$(BOLD)calendar Makefile"
@echo "Please use 'make $(BOLD)target$(RESET)' where $(BOLD)target$(RESET) is one of:"
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(GREEN)%-30s$(RESET) %s\n", $$1, $$2}'
.PHONY: help