Преглед на файлове

Project skeleton: directory layout, config samples, access protection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Medowar преди 1 месец
ревизия
228b9212f1
променени са 9 файла, в които са добавени 145 реда и са изтрити 0 реда
  1. 13 0
      .gitignore
  2. 10 0
      .htaccess
  3. 55 0
      README.md
  4. 1 0
      app/.htaccess
  5. 1 0
      config/.htaccess
  6. 39 0
      config/config.sample.php
  7. 13 0
      config/credentials.sample.php
  8. 1 0
      data/.htaccess
  9. 12 0
      public/media/.htaccess

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+# Instance configuration (create from config/config.sample.php)
+/config/config.php
+/config/credentials.php
+
+# Runtime data
+/data/*
+!/data/.htaccess
+/public/media/*
+!/public/media/.htaccess
+
+# OS / editor noise
+.DS_Store
+Thumbs.db

+ 10 - 0
.htaccess

@@ -0,0 +1,10 @@
+# Repository root .htaccess.
+#
+# Preferred setup: point the document root of your hosting account at public/.
+# If your shared host cannot do that and serves this directory instead,
+# this file routes visitors into public/ and the per-directory .htaccess
+# files in app/, config/ and data/ block direct access to internals.
+
+RewriteEngine On
+RewriteCond %{REQUEST_URI} !^/public/
+RewriteRule ^(.*)$ public/$1 [L]

+ 55 - 0
README.md

@@ -0,0 +1,55 @@
+# Photography Portfolio & Client Galleries
+
+A flat-file PHP website for photographers, built for plain shared webhosting.
+No database, no framework, no build step — upload via FTP/SFTP and it runs.
+
+## Features
+
+- **Landing page** — full-screen hero image with an artist introduction.
+- **Showreel** — the portfolio: selected images displayed full-screen, one per
+  scroll step, on a minimal dark layout. Navigation hides while scrolling.
+- **Client galleries** — whole event galleries (weddings, events, …) hosted on
+  Hetzner Object Storage (S3). Optionally password-protected and/or with an
+  expiry date after which the gallery is hidden. Visitors load images directly
+  from S3 through short-lived presigned URLs — nothing is proxied through the
+  webhost.
+- **Admin backoffice** — a small CMS to edit the front page, manage the
+  showreel, create galleries and bulk-upload images. Uploads go straight from
+  the browser to S3, in full resolution, originals untouched; grid thumbnails
+  are generated in the browser.
+- **Flat-file storage** — all content lives in JSON files; admin credentials
+  live in a PHP config file. Password can be changed online.
+
+## Requirements
+
+- PHP 8.1+ with `curl` and `openssl` (standard on shared hosting)
+- Apache with `.htaccess` support (or any server with the document root
+  pointed at `public/`)
+- A private Hetzner Object Storage bucket (for client galleries)
+
+## Quick start
+
+```bash
+cp config/config.sample.php config/config.php          # fill in S3 + site settings
+cp config/credentials.sample.php config/credentials.php # default login: admin / changeme
+php -S localhost:8080 -t public                         # local preview
+```
+
+Then open http://localhost:8080/admin/, log in, and **change the password
+first** (Settings).
+
+## Documentation
+
+- [docs/SETUP.md](docs/SETUP.md) — deployment, Hetzner bucket + CORS setup
+- [docs/ADMIN-GUIDE.md](docs/ADMIN-GUIDE.md) — using the backoffice
+- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — how it works inside
+
+## Layout
+
+```
+public/    web root (point your domain here)
+app/       PHP library code (not web-accessible)
+config/    static configuration + admin credentials
+data/      flat-file content storage (JSON)
+docs/      documentation
+```

+ 1 - 0
app/.htaccess

@@ -0,0 +1 @@
+Require all denied

+ 1 - 0
config/.htaccess

@@ -0,0 +1 @@
+Require all denied

+ 39 - 0
config/config.sample.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * Site configuration. Copy this file to config.php and fill in your values.
+ * config.php is never committed to git and never rewritten by the application.
+ */
+return [
+    // ---- Site -------------------------------------------------------------
+    'site' => [
+        'name'     => 'Jane Doe Photography',
+        // Base URL of the public site, no trailing slash. Used for links in
+        // shared gallery URLs. Example: 'https://www.example.com'
+        'base_url' => 'https://www.example.com',
+        // IANA timezone used for gallery expiry checks.
+        'timezone' => 'Europe/Berlin',
+    ],
+
+    // ---- Hetzner Object Storage (S3-compatible) ---------------------------
+    // Create the bucket in the Hetzner console, keep it PRIVATE.
+    // Endpoint depends on the location, e.g. fsn1/nbg1/hel1.
+    's3' => [
+        'endpoint'   => 'https://fsn1.your-objectstorage.com',
+        'region'     => 'fsn1',
+        'bucket'     => 'my-photo-galleries',
+        'access_key' => 'CHANGE_ME',
+        'secret_key' => 'CHANGE_ME',
+        // Lifetime of presigned GET URLs (seconds). Gallery pages embed these;
+        // after expiry the browser must reload the page for fresh URLs.
+        'url_ttl'    => 3600,
+    ],
+
+    // ---- Uploads ----------------------------------------------------------
+    'uploads' => [
+        // Longest edge of the client-side generated grid thumbnails (px).
+        // Originals are never touched.
+        'thumb_size'    => 600,
+        // JPEG quality for thumbnails (0.0 - 1.0, used by the browser canvas).
+        'thumb_quality' => 0.8,
+    ],
+];

+ 13 - 0
config/credentials.sample.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Admin credentials. Copy to credentials.php, then set your own password:
+ * generate a hash with:  php -r "echo password_hash('your-password', PASSWORD_DEFAULT), PHP_EOL;"
+ *
+ * The default below is username "admin", password "changeme".
+ * The application rewrites this file when the password is changed online,
+ * so it must remain writable by the web server.
+ */
+return [
+    'username'      => 'admin',
+    'password_hash' => '$2y$10$Y6vXsF0ykl0S8PbW3rSPPeK1ZBCwYpuBABv3JCVSt17c40cA8B4C6',
+];

+ 1 - 0
data/.htaccess

@@ -0,0 +1 @@
+Require all denied

+ 12 - 0
public/media/.htaccess

@@ -0,0 +1,12 @@
+# Media directory: serve images only, never execute scripts.
+Require all denied
+<FilesMatch "\.(?i:jpe?g|png|gif|webp|avif)$">
+    Require all granted
+</FilesMatch>
+
+# Neutralize PHP however the host runs it.
+RemoveHandler .php .phtml .phar
+RemoveType .php .phtml .phar
+<IfModule mod_php.c>
+    php_flag engine off
+</IfModule>