From 771f9a6c0fd271d5e0b056c96f03003340381bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20Sz=C3=A1sz?= Date: Fri, 7 Aug 2026 11:57:31 +0100 Subject: [PATCH] Dockerised FrameTrail --- .dockerignore | 10 ++++++ CLAUDE.md | 2 ++ Dockerfile | 76 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 8 +++++ compose.yaml | 16 ++++++++++ docker-entrypoint.sh | 15 +++++++++ docs/DEPLOYMENT.md | 18 +++++++++++ 7 files changed, 145 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 compose.yaml create mode 100755 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..06929b3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.github +.gitignore +docs +examples +CONTRIBUTING.md +CLAUDE.md +build +node_modules +scripts/node_modules diff --git a/CLAUDE.md b/CLAUDE.md index 8322a9e..c4a48e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -249,6 +249,8 @@ The build script: 5. Generates clean HTML entry points that load only the two bundles 6. Copies `_server/`, `.htaccess`, `favico.png`, `LICENSE.md` +**Docker:** `Dockerfile` + `compose.yaml` in the repo root run this same build in a throwaway Node stage and serve the output via PHP + Apache (`docker compose up -d`). See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#option-1-server-deployment-php) for build args (e.g. `WITH_FFMPEG`) and volume details. + ### CI/CD - **Build verification** (`.github/workflows/build.yml`): Runs on every push to `main`/`develop` and every PR. Builds and verifies output. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7f75cfc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,76 @@ +# syntax=docker/dockerfile:1 +# +# Multi-stage build: compiles the minified JS/CSS bundle from source in a +# throwaway Node stage (mirrors scripts/build.sh / .github/workflows/build.yml), +# then serves it from a plain PHP + Apache runtime with no build tooling included. +# +# Build: docker build -t frametrail . +# Run: docker run -p 8080:80 -v frametrail_data:/var/www/html/_data frametrail + +######################################## +# Stage 1: build minified JS/CSS bundle +######################################## +FROM node:20-alpine AS build + +RUN apk add --no-cache bash + +WORKDIR /src +COPY . . + +RUN npm install -g terser csso-cli \ + && bash scripts/build.sh + +######################################## +# Stage 2: runtime (PHP + Apache) +######################################## +FROM php:8.3-apache + +# ffmpeg adds ~500MB (codecs/libs) for a feature (server-side video transcoding + +# thumbnail/scrub-sprite generation) the app detects at runtime and silently skips +# if absent. Off by default for a lean image; opt in with --build-arg WITH_FFMPEG=true. +ARG WITH_FFMPEG=false + +# gd -> image thumbnails (src/_server/files.php) +# curl -> oEmbed / OpenGraph resource previews +RUN apt-get update && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libcurl4-openssl-dev \ + $( [ "$WITH_FFMPEG" = "true" ] && echo ffmpeg ) \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" gd curl \ + && a2enmod rewrite \ + && printf '\n AllowOverride All\n\n' \ + >> /etc/apache2/apache2.conf \ + # gd.so / curl.so link against these at runtime. Re-installing them by name + # marks them "manually installed" so the --auto-remove purge below doesn't + # treat them as orphaned deps of the -dev packages and remove them too. + && apt-get install -y --no-install-recommends \ + libfreetype6 libjpeg62-turbo libpng16-16t64 libcurl4t64 \ + && apt-get purge -y --auto-remove \ + libfreetype6-dev libjpeg62-turbo-dev libpng-dev libcurl4-openssl-dev \ + && rm -rf /var/lib/apt/lists/* \ + # Fail the build loudly (rather than shipping a broken image) if a future base + # image bump renames the runtime lib packages above and breaks extension loading. + && php -m | grep -qx gd && php -m | grep -qx curl + +WORKDIR /var/www/html +COPY --from=build /src/build/ ./ + +# Install dir + _data/ must be writable by the web server: FrameTrail's setup +# wizard creates _data/ on first run and writes into it on every save/upload. +RUN chown -R www-data:www-data /var/www/html + +# _data/ doesn't exist yet at this point (it's created by the setup wizard), so +# this chown never reaches it once a volume/bind-mount is placed there at +# `docker run`/`compose up` time — mounting always overrides build-time state +# with a fresh, root-owned directory. docker-entrypoint.sh fixes ownership at +# container start instead, where the mounted path actually exists. +COPY docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +EXPOSE 80 + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["apache2-foreground"] diff --git a/README.md b/README.md index 49b22be..5647579 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,14 @@ FrameTrail works in three modes with different capabilities. For public deployments, use Apache (`.htaccess` included) or nginx with PHP-FPM. No PHP installed? Use [XAMPP](https://www.apachefriends.org/) (Windows) or [MAMP](https://www.mamp.info/) (Mac/Windows). +**Or use Docker** — no PHP install needed: + +```bash +docker compose up -d +``` + +Open `http://localhost:8080` and follow the setup wizard. The image builds the minified bundle from source in a throwaway Node stage and serves it via PHP + Apache, so no build tooling ends up in the final image; `_data/` persists in a named volume across restarts. Pass `--build-arg WITH_FFMPEG=true` (or uncomment the `args:` in `compose.yaml`) to include FFmpeg for server-side video transcoding and thumbnail generation — it's off by default to keep the image lean (~500MB smaller). + ### Option 2: Local Folder Mode 1. Download and extract FrameTrail diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..d149b88 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,16 @@ +services: + frametrail: + build: + context: . + # args: + # WITH_FFMPEG: "true" # adds ffmpeg for server-side transcoding (~500MB larger) + ports: + - "8080:80" + volumes: + # All app state (accounts, hypervideos, annotations, uploads) lives here — + # no database. Back this up / migrate between installs by copying it. + - frametrail_data:/var/www/html/_data + restart: unless-stopped + +volumes: + frametrail_data: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..17b375d --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +# _data/ doesn't exist in the image (FrameTrail's setup wizard creates it on +# first run), so a fresh volume/bind-mount is initialized root-owned by Docker. +# The chown baked into the image at build time never touches it, since it's +# applied before any volume is ever mounted over the path. Fix ownership here, +# at container start, once — skip it once it's already correct so a large +# _data/ doesn't pay a recursive chown on every restart. +mkdir -p /var/www/html/_data +if [ "$(stat -c '%U' /var/www/html/_data)" != "www-data" ]; then + chown -R www-data:www-data /var/www/html/_data +fi + +exec docker-php-entrypoint "$@" diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 458880f..e8c45b7 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -41,6 +41,24 @@ Then open `http://localhost:8080`. No Apache, no XAMPP needed if PHP is installe - `.htaccess` — Apache rewrite rules - `favico.png`, `README.md`, `LICENSE.md` +**Or use Docker:** + +```bash +git clone https://github.com/OpenHypervideo/FrameTrail.git +cd FrameTrail +docker compose up -d +``` + +Open `http://localhost:8080` and follow the setup wizard. The `Dockerfile` builds the minified bundle from source in a throwaway Node stage (mirrors `scripts/build.sh`) and serves it via PHP + Apache — no Node or build tooling ends up in the final image. `_data/` persists in the named `frametrail_data` volume across container restarts/upgrades. + +FFmpeg (server-side video transcoding + thumbnail/scrub-sprite generation) is left out by default to keep the image lean (~500MB smaller); the app detects its absence at runtime and skips those features gracefully. To include it: + +```bash +docker compose build --build-arg WITH_FFMPEG=true +``` + +or uncomment the `args:` block in `compose.yaml`. + ### Option 2: Local Folder Mode (No Server) Single-user editing without a server. Uses the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) to read and write files directly on disk. File uploads (images, video, audio) are supported, but server-side capabilities like media transcoding and user management are not available.