SETUP.md 4.6 KB

Setup & Deployment

1. Requirements

  • Shared webhosting with PHP 8.1 or newer, curl and openssl extensions (both are standard), and Apache .htaccess support.
  • A Hetzner Object Storage bucket (any S3-compatible storage works).
  • No database, no Composer, no build step.

2. Hetzner Object Storage

  1. In the Hetzner Cloud Console create a Bucket (e.g. location fsn1). Set visibility to private — visitors get access only through short-lived presigned URLs.
  2. Create S3 credentials (Security → S3 credentials) and note the access key and secret key.

No bucket CORS rule is needed: uploads are proxied through the webhost (same-origin) and gallery images load via <img> presigned GET URLs, which browsers don't subject to CORS. Keep the bucket private.

2a. PHP upload limits

Uploads stream through PHP one image per request, so the total gallery size is irrelevant — but each single image must fit the host's limits. Ensure upload_max_filesize and post_max_size are at least as large as your biggest original (e.g. 200M for RAW files); on shared hosting set these in .user.ini or php.ini:

upload_max_filesize = 200M
post_max_size       = 200M

max_execution_time is lifted per upload request in code, but if your host caps it at the web-server level (e.g. Apache/FPM request timeout), raise that too for large files.

3. Configuration

cp config/config.sample.php config/config.php
cp config/credentials.sample.php config/credentials.php

Edit config/config.php:

Key Meaning
site.name Fallback site title
site.base_url Public base URL, used for gallery share links
site.timezone Timezone for expiry checks, e.g. Europe/Berlin
s3.endpoint https://<location>.your-objectstorage.com
s3.region The location, e.g. fsn1
s3.bucket Bucket name
s3.access_key / s3.secret_key S3 credentials
s3.url_ttl Lifetime of presigned view URLs in seconds
uploads.thumb_size Longest edge of grid thumbnails (browser-generated)

The default admin login is admin / changemechange it in the admin Settings page immediately after the first login.

4. Uploading to the webhost

Upload the contents of this folder into your account's document root (usually public_html/, htdocs/ or www/) via FTP/SFTP. The site's home page, index.php, sits directly in the document root — there is no separate web-root subfolder to configure.

The application internals (app/, config/, data/, docs/) live inside the document root but are blocked from the web by the root .htaccess (plus a deny-all .htaccess inside each of app/, config/, data/ as a fallback).

Verify after deploying — each of these must return 403 Forbidden, never their contents:

  • https://your-domain.com/config/config.php
  • https://your-domain.com/config/credentials.php
  • https://your-domain.com/data/site.json

If they don't, your host ignores .htaccess — move app/, config/ and data/ above the document root and adjust the paths, or contact support.

Writable directories

The PHP process must be able to write to:

  • data/ (and data/galleries/) — flat-file content
  • media/ — hero + showreel images
  • config/ — only for the online password change

On typical shared hosting (suEXEC/FPM running as your user) this already works; otherwise chmod 755 the directories (or 775/777 as a last resort).

5. First-deploy smoke test

  1. Open /admin/, log in, change the password (Settings).
  2. Front page: set your name and intro text, upload a hero image → check the landing page.
  3. Showreel: upload 2–3 images → check /showreel.php, scroll behavior, and that navigation hides when scrolling down.
  4. Galleries: create a test gallery with password and an expiry date of today, upload a handful of images. Then:
    • the upload list shows done for each file (webhost → S3 working),
    • the gallery page asks for the password and then shows images (presigned GETs working),
    • images load from your-objectstorage.com, not from your domain,
    • tomorrow the gallery shows "not available" (expiry working).
  5. Delete the test gallery — the S3 objects are removed as well.

6. Local development

php -S localhost:8080 router.php

router.php reproduces the .htaccess protection for the PHP built-in server (which does not read .htaccess); it is only used locally.

Everything except real S3 traffic works without credentials; gallery pages render presigned URLs that simply won't resolve until real keys are configured.