feat: Infrastructure multi-tenant avec isolation par sous-domaine

Une application SaaS éducative nécessite une séparation stricte des données
entre établissements scolaires. L'architecture multi-tenant par sous-domaine
(ecole-alpha.classeo.local) permet cette isolation tout en utilisant une
base de code unique.

Le choix d'une résolution basée sur les sous-domaines plutôt que sur des
headers ou tokens facilite le routage au niveau infrastructure (reverse proxy)
et offre une UX plus naturelle où chaque école accède à "son" URL dédiée.
This commit is contained in:
2026-01-30 23:34:10 +01:00
parent 6da5996340
commit 1fd256346a
71 changed files with 14390 additions and 37 deletions

View File

@@ -52,6 +52,17 @@ RUN echo "opcache.enable=1" >> "$PHP_INI_DIR/conf.d/opcache.ini" \
# =============================================================================
FROM base AS dev
# Install gosu for proper user switching
ENV GOSU_VERSION=1.17
RUN set -eux; \
apk add --no-cache --virtual .gosu-deps dpkg gnupg; \
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
chmod +x /usr/local/bin/gosu; \
gosu --version; \
gosu nobody true; \
apk del --no-network .gosu-deps
# Enable opcache revalidation for dev (zz- prefix loads last alphabetically)
RUN echo "opcache.validate_timestamps=1" >> "$PHP_INI_DIR/conf.d/zz-opcache-dev.ini"
@@ -65,16 +76,40 @@ RUN echo "xdebug.mode=develop,debug,coverage" >> "$PHP_INI_DIR/conf.d/xdebug.ini
ENV SERVER_NAME=:8000
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
# Create entrypoint script for dev (installs deps if needed)
RUN echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh && \
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh && \
echo 'if [ ! -f /app/vendor/autoload.php ]; then' >> /usr/local/bin/docker-entrypoint.sh && \
echo ' echo "Installing Composer dependencies..."' >> /usr/local/bin/docker-entrypoint.sh && \
echo ' composer install --prefer-dist --no-progress --no-interaction' >> /usr/local/bin/docker-entrypoint.sh && \
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh && \
echo 'mkdir -p var/cache var/log && chmod -R 777 var' >> /usr/local/bin/docker-entrypoint.sh && \
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh && \
chmod +x /usr/local/bin/docker-entrypoint.sh
# Entrypoint: detect host UID/GID and run as matching user
# Uses gosu with UID:GID directly (no need to create user in Dockerfile)
COPY --chmod=755 <<'EOF' /usr/local/bin/docker-entrypoint.sh
#!/bin/sh
set -e
# Detect UID/GID from mounted /app directory
HOST_UID=$(stat -c %u /app)
HOST_GID=$(stat -c %g /app)
# If root owns /app, run as root (CI environment or volume not mounted)
if [ "$HOST_UID" = "0" ]; then
# Install dependencies if not present
if [ ! -f /app/vendor/autoload.php ]; then
echo "Installing Composer dependencies..."
composer install --prefer-dist --no-progress --no-interaction
fi
mkdir -p /app/var/cache /app/var/log
exec "$@"
fi
# Ensure directories exist with correct ownership
mkdir -p /app/var/cache /app/var/log /data /config
chown -R "$HOST_UID:$HOST_GID" /app/var /data /config 2>/dev/null || true
# Install Composer dependencies if not present (as host user)
if [ ! -f /app/vendor/autoload.php ]; then
echo "Installing Composer dependencies..."
gosu "$HOST_UID:$HOST_GID" composer install --prefer-dist --no-progress --no-interaction
fi
# Run command as host user via gosu (using UID:GID directly)
exec gosu "$HOST_UID:$HOST_GID" "$@"
EOF
EXPOSE 8000