Skip to content

fix(demo): make the Docker stack actually work end-to-end#42

Merged
bigin merged 5 commits into
masterfrom
feat/demo-image
May 13, 2026
Merged

fix(demo): make the Docker stack actually work end-to-end#42
bigin merged 5 commits into
masterfrom
feat/demo-image

Conversation

@bigin
Copy link
Copy Markdown
Owner

@bigin bigin commented May 13, 2026

33932b7 │ composer-rewrite helper (Build-Fix)
a36af61 │ SCRIPTOR_DEMO_PORT Env-Override
ecdb713 │ nginx user nginx statt www-data
3c706e7 │ try_files ohne $uri/ (Editor-Routing)
7748205 │ Admin-Password bcrypt-hashen im Seed

bigin added 5 commits May 13, 2026 10:23
`composer config repositories.0 '{...}'` doesn't replace index 0 in a
sequential `repositories` array — it adds a SECOND entry keyed `"0"`
(string), leaving the original path-repo at `../imanager` in place.
Modern Composer then fails repository discovery with:

  In PathRepository.php line 163:
    The `url` supplied for the path (../imanager) repository does not exist

Rewrite composer.json directly with a small PHP helper script that
overwrites the `repositories` section with the VCS-only shape we
actually want. Helper is COPYed into /tmp during the build and
removed afterwards.

Verified: iManager's `composer.json` `branch-alias` maps `dev-main`
to `2.0.x-dev`, so Scriptor's `bigins/imanager: 2.0.x-dev` constraint
resolves against the VCS repo's `main` branch as expected.

Cuts directly through the build failure from the first
`docker compose up -d --build` run.
The compose stack hard-coded `8080:80`, which collides with anything
else on the host already on 8080 (local web stacks, other containers).
Make the host side dynamic via an env var that defaults to 8080:

    SCRIPTOR_DEMO_PORT=8090 docker compose up -d --build

The container itself still listens on 80 internally — only the
host-side mapping is configurable. docs/demo.md gets a callout for
the override.
The official `nginx:alpine` image ships with a `nginx` system user; it
does NOT have `www-data`. The previous config said `user www-data;`,
which made nginx die on startup with:

  nginx: [emerg] getpwnam("www-data") failed

…and the web container then restarted in a loop.

The php-fpm side still runs as `www-data` (that's the user from the
`php:fpm-alpine` base). They're two separate containers, so the
user mismatch is fine: nginx only reads the shared volume, and the
Scriptor image leaves files world-readable (0644/0755 defaults).

Added a comment block explaining the asymmetry so the next person
who reads the config doesn't try to "fix" it back to www-data.
…x.php

Scriptor's design is single-entry routing: the root `index.php`
delegates `/<admin_path>/*` (default: `/editor/*`) to the editor in
PHP. With `try_files $uri $uri/ /index.php` and the default
`index index.php` directive, nginx was resolving `/editor/` to the
filesystem directory, then falling through to `editor/index.php`
via the index directive — at which point the `\.php$` deny-rule
caught it and returned 404.

Dropping `$uri/` from try_files makes nginx skip the directory
resolution: `/editor/` is not a file → no `$uri` match → falls
through to `/index.php?$query_string`, which is exactly what
Scriptor's routing expects.

Verified: `/`, `/editor/`, and `/hello-world/` (the seeded example
page) all return 200 with the expected content. Login form on
`/editor/` renders with a fresh CSRF token.
The seed passed `'password' => 'scriptor'` verbatim to
ItemRepository::save(), expecting the registered PasswordFieldType
to bcrypt it. It doesn't — SqliteItemRepository::save() writes the
data column as-is and never invokes the FieldTypeRegistry. Validation
is the host editor's job (Scriptor's PagesModule / ProfileModule
run it before save).

So the seed stored plaintext "scriptor" in the password field and
AuthModule's `password_verify($plaintext, $hashOrPlaintext)` failed
on the next login attempt — bcrypt prefix mismatch.

Hash directly with `password_hash($pw, PASSWORD_BCRYPT)` in the seed
— the same call PasswordFieldType::validate() would have produced.

Verified end-to-end on the demo container:
- DB now stores `$2y$10$…` bcrypt hash.
- POST /editor/ with admin / scriptor returns 302 → /editor/.
- The follow-up GET shows the authenticated dashboard ("Pages",
  "Dashboard", "logout").

Separate follow-up needed in the iManager docs: api/storage.md and
docs/field-types.md both claim ItemRepository::save() validates
through plugins. That's wrong — it doesn't. Will land in a docs PR
on the iManager side.
@bigin bigin merged commit 774319c into master May 13, 2026
@bigin bigin deleted the feat/demo-image branch May 13, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant