# syntax=docker/dockerfile:1.7
#
# PNLCS - self-contained image for Kubernetes and Docker.
#
# One container runs nginx, PHP-FPM and cron (the Laravel scheduler, under
# flock) under supervisord. Nothing has to be mounted: storage/ lives inside
# the container and starts clean on every restart. See DOCKER.md for what that
# means for uploads, and for the one optional volume that keeps them.

ARG PHP_VERSION=8.4
ARG NODE_VERSION=22

# ── 1. Frontend assets ──────────────────────────────────────────────────────
FROM node:${NODE_VERSION}-alpine AS assets
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --no-audit --no-fund
# Tailwind scans the views and themes for class names, so the whole tree goes in.
COPY . .
RUN npm run build

# ── 2. Runtime ──────────────────────────────────────────────────────────────
FROM php:${PHP_VERSION}-fpm-alpine AS runtime

COPY --from=mlocati/php-extension-installer:2 /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions bcmath gd intl pdo_mysql zip opcache pcntl \
 && apk add --no-cache nginx supervisor su-exec mariadb-client tzdata \
 && mkdir -p /run/nginx

COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer

WORKDIR /var/www/html

# Dependencies first, so a code-only change reuses this layer.
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist --no-interaction --no-progress

COPY . .
COPY --from=assets /app/public/build public/build

RUN composer dump-autoload --optimize --no-dev --no-interaction \
 && php artisan package:discover --ansi \
 && rm /usr/local/bin/composer \
 # Uploaded logos/favicons are written to public/branding: point it into
 # storage/app so one volume (if you ever add one) keeps every upload.
 && rm -rf public/storage public/branding \
 && ln -s ../storage/app/public public/storage \
 && ln -s ../storage/app/branding public/branding \
 # Code stays root-owned; the app may write only where it has to.
 && mkdir -p storage/app/private storage/app/public storage/app/branding \
             storage/framework/cache/data storage/framework/sessions \
             storage/framework/views storage/logs bootstrap/cache \
 && touch .env \
 && chown -R www-data:www-data storage bootstrap/cache themes .env \
 # The base image leaves /var/www/html world-writable (1777); the app must
 # not be able to drop files next to its own code. This also makes the
 # in-app updater (Setup -> Updates) refuse up front: updates ship as images.
 && chown root:root /var/www/html \
 && chmod 755 /var/www/html

COPY docker/nginx.conf        /etc/nginx/nginx.conf
COPY docker/php.ini           /usr/local/etc/php/conf.d/zz-pnlcs.ini
COPY docker/php-fpm.conf      /usr/local/etc/php-fpm.d/zz-pnlcs.conf
COPY docker/supervisord.conf  /etc/supervisord.conf
COPY docker/crontab           /etc/crontabs/www-data
COPY docker/render-env.php    /usr/local/lib/pnlcs/render-env.php
COPY --chmod=0755 docker/entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod 0600 /etc/crontabs/www-data && rm -f /etc/crontabs/root

# PNLCS is MySQL/MariaDB only; Laravel's own default (sqlite) would make a
# deployment that leaves DB_CONNECTION out migrate a file named after DB_DATABASE.
ENV APP_ENV=production \
    APP_DEBUG=false \
    DB_CONNECTION=mysql \
    LOG_CHANNEL=stack \
    LOG_STACK=daily,stderr \
    LOG_LEVEL=warning

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
  CMD wget -q -O /dev/null http://127.0.0.1:8080/up || exit 1

ENTRYPOINT ["/usr/local/bin/docker-entrypoint"]
CMD ["supervisord", "-c", "/etc/supervisord.conf"]
