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.
  3. Configure CORS on the bucket so the admin browser may upload directly to S3 and so gallery images load on your domain. Hetzner supports the standard S3 CORS API; using s3cmd, aws s3api, or any S3 GUI client (e.g. Cyberduck), apply:

    {
     "CORSRules": [
       {
         "AllowedOrigins": ["https://www.your-domain.com"],
         "AllowedMethods": ["GET", "PUT"],
         "AllowedHeaders": ["*"],
         "MaxAgeSeconds": 3600
       }
     ]
    }
    

With aws CLI:

   aws s3api put-bucket-cors \
     --endpoint-url https://fsn1.your-objectstorage.com \
     --bucket my-photo-galleries \
     --cors-configuration file://cors.json

Replace the origin with your real domain (and add http://localhost:8080 temporarily if you want to test uploads locally).

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 (S3 + CORS 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.