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.
308 lines
8.3 KiB
Makefile
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
|