# syntax=docker/dockerfile:1.7 # ---------- node stage: build Tailwind + JS bundle ---------- FROM node:20-alpine AS assets WORKDIR /app COPY package.json package-lock.json* ./ RUN npm install --no-audit --no-fund COPY tailwind.config.js postcss.config.js ./ COPY resources ./resources RUN mkdir -p public/assets \ && npx tailwindcss -i resources/css/app.css -o public/assets/app.css --minify \ && npx esbuild resources/js/app.js --bundle --minify --target=es2020 --outfile=public/assets/app.js # ---------- composer stage ---------- FROM composer:2 AS deps WORKDIR /app COPY composer.json composer.lock* ./ RUN composer install --no-dev --no-interaction --no-scripts --no-progress --optimize-autoloader # ---------- runtime ---------- FROM dunglas/frankenphp:1-php8.3-alpine ENV PHP_INI_SCAN_DIR=/usr/local/etc/php/conf.d RUN apk add --no-cache \ icu-dev \ oniguruma-dev \ bash \ && install-php-extensions \ mbstring \ intl \ opcache WORKDIR /app COPY --from=deps /app/vendor ./vendor COPY . ./ COPY --from=assets /app/public/assets ./public/assets COPY docker/Caddyfile /etc/Caddyfile COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh # SEC_REVIEW F18: drop root. /app stays root-owned and world-readable; # the UI has no per-container persistent volume so only /home/app's # XDG dirs need to be app-writable. PHP sessions live under /tmp by # default which is world-writable, so no extra path setup is needed # for the BFF session store. RUN addgroup -S -g 1000 app \ && adduser -S -u 1000 -G app -h /home/app app \ && chmod +x /usr/local/bin/entrypoint.sh \ && mkdir -p /home/app/.config /home/app/.local/share \ && chown -R app:app /home/app ENV XDG_CONFIG_HOME=/home/app/.config \ XDG_DATA_HOME=/home/app/.local/share USER app EXPOSE 8080 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["ui"]