diff --git a/docker-compose.yml b/docker-compose.yml index 129bc9c..e9a6e5a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,9 @@ services: scriptor: condition: service_healthy ports: - - "8080:80" + # Host port can be overridden — e.g. `SCRIPTOR_DEMO_PORT=8090 docker compose up` + # — when 8080 is already taken on the host (ServBay, another container, …). + - "${SCRIPTOR_DEMO_PORT:-8080}:80" volumes: - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro - scriptor-app:/var/www/scriptor:ro diff --git a/docker/Dockerfile b/docker/Dockerfile index 97fe76b..d9d8a1e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -42,17 +42,21 @@ ENV COMPOSER_HOME=/tmp/composer \ WORKDIR /var/www/scriptor -# Bring composer.json in first so the install layer caches independently -# from application source changes. +# Bring composer.json + the repo-rewrite helper in first so the install +# layer caches independently from application source changes. COPY composer.json composer.lock ./ +COPY docker/composer-rewrite.php /tmp/composer-rewrite.php # The repo's composer.json declares a `path` repository at `../imanager` -# for local development. Inside the container that path doesn't exist, so -# we swap repository[0] for a VCS repo pointing at the public iManager -# GitHub repo. Then update (not install) so the lock can be regenerated -# against the VCS source. -RUN composer config repositories.0 '{"type": "vcs", "url": "https://github.com/bigin/imanager"}' \ - && rm -f composer.lock \ +# for local development. Inside the container that path doesn't exist, and +# modern Composer treats a missing path-repo as a fatal error during +# discovery — so we rewrite the `repositories` array to a VCS repo +# pointing at the public iManager GitHub repo (iManager's `main` branch +# is `branch-alias`-mapped to `2.0.x-dev`, which is what composer.json +# requires). Then update (not install) so the lock regenerates against +# the VCS source. +RUN php /tmp/composer-rewrite.php \ + && rm -f composer.lock /tmp/composer-rewrite.php \ && composer update --no-dev --no-interaction --prefer-dist --optimize-autoloader # Application code last so the previous layers (large) stay cached diff --git a/docker/composer-rewrite.php b/docker/composer-rewrite.php new file mode 100644 index 0000000..c96dcad --- /dev/null +++ b/docker/composer-rewrite.php @@ -0,0 +1,28 @@ + 'vcs', 'url' => 'https://github.com/bigin/imanager'], +]; + +$out = json_encode($c, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES) . \PHP_EOL; +file_put_contents($file, $out); +fwrite(\STDOUT, "[composer-rewrite] swapped path repo for VCS repo.\n"); diff --git a/docker/nginx.conf b/docker/nginx.conf index a1015d7..75a732b 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -6,7 +6,12 @@ # the logs and backups dirs) is blocked at the URL layer — defence in # depth on top of the fact that `data/` lives outside the webroot anyway. -user www-data; +# The official nginx:alpine image runs as the `nginx` user — not +# `www-data` like the php-fpm image. They're two separate containers, +# so the user mismatch is fine: nginx only needs read access to the +# shared volume, and the volume's files are world-readable (0644/0755 +# defaults from the Scriptor image). +user nginx; worker_processes auto; error_log /dev/stderr warn; pid /run/nginx.pid; @@ -53,9 +58,15 @@ http { return 404; } - # Front controller. + # Front controller. We deliberately leave `$uri/` OUT of the + # try_files list — Scriptor's `index.php` at the repo root + # handles `/editor/*` internally via PHP delegation. If we let + # nginx resolve `/editor/` to `editor/index.php` (via the + # `index` directive), the request bypasses the front + # controller and hits the editor's index directly, which + # depends on globals the root index.php sets up. location / { - try_files $uri $uri/ /index.php?$query_string; + try_files $uri /index.php?$query_string; } location ~ ^/index\.php(/|$) { diff --git a/docker/seed-demo.php b/docker/seed-demo.php index 215c9c9..d7a8639 100644 --- a/docker/seed-demo.php +++ b/docker/seed-demo.php @@ -88,8 +88,10 @@ } // -- Admin user -- -// PasswordFieldType validates `scriptor` (8 chars, meets default minLength) -// and bcrypts it on save. Do NOT pre-hash here — let the field type do it. +// `ItemRepository::save()` writes `$data` verbatim — it does NOT run +// the registered FieldTypePlugin's validate() (that's the host editor's +// job). So we have to hash the password ourselves here, the same way +// `PasswordFieldType::validate()` would: bcrypt via `password_hash()`. fwrite(\STDOUT, "[seed] creating admin user (admin/scriptor)…\n"); $items->save(new Item( id: null, @@ -99,7 +101,7 @@ data: [ 'role' => 'admin', 'email' => 'admin@example.com', - 'password' => 'scriptor', + 'password' => password_hash('scriptor', \PASSWORD_BCRYPT), ], )); diff --git a/docs/demo.md b/docs/demo.md index 3acc93a..282a5fa 100644 --- a/docs/demo.md +++ b/docs/demo.md @@ -31,6 +31,17 @@ Then open: | **http://localhost:8080/** | The public front. The single seeded page rendered through Scriptor's default theme. | | **http://localhost:8080/editor/** | The editor. Sign in with the credentials below. | +> **Port already in use?** If something else on the host (a local web +> stack, another container) is already on `8080`, override the host +> port via the `SCRIPTOR_DEMO_PORT` env var: +> +> ```bash +> SCRIPTOR_DEMO_PORT=8090 docker compose up -d --build +> ``` +> +> The container itself always listens on `80`; only the host-side +> port is dynamic. + ### Default credentials ```