浏览代码

moving apps around

Medowar 1 月之前
父节点
当前提交
af7211d2c6
共有 16 个文件被更改,包括 225 次插入285 次删除
  1. 8 0
      .htaccess
  2. 22 12
      README.md
  3. 1 1
      admin/index.php
  4. 1 1
      admin/logout.php
  5. 0 0
      api-docs/index.html
  6. 1 1
      api/v1/readings.php
  7. 1 1
      api/v1/status.php
  8. 0 0
      app.js
  9. 7 0
      data/.htaccess
  10. 7 0
      docs/.htaccess
  11. 15 3
      docs/API.md
  12. 154 265
      docs/DEPLOYMENT.md
  13. 1 1
      index.php
  14. 0 0
      openapi.yaml
  15. 7 0
      src/.htaccess
  16. 0 0
      styles.css

+ 8 - 0
.htaccess

@@ -0,0 +1,8 @@
+Options -Indexes
+DirectoryIndex index.php
+
+<IfModule mod_rewrite.c>
+    RewriteEngine On
+    RewriteRule (^|/)\. - [F,L]
+    RewriteRule (^|/)README\.md$ - [F,L,NC]
+</IfModule>

+ 22 - 12
README.md

@@ -39,21 +39,28 @@ Der Datenfluss ist einfach:
 
 ## Projektstruktur
 
-- `public/`
-  - Webroot der Anwendung
-  - enthaelt Dashboard, Adminpanel, CSS, JavaScript und API-Endpunkte
+Die Anwendung ist jetzt so aufgebaut, dass der gesamte Projektordner direkt als Apache-Unterordner deployt werden kann.
+
+- `index.php`, `admin/`, `api/`, `api-docs/`, `app.js`, `styles.css`, `openapi.yaml`
+  - oeffentliche Runtime-Dateien
 - `src/`
-  - Kernlogik der Anwendung
-  - Konfigurationszugriff, Zustandsberechnung, Alarmierung, Authentifizierung
+  - interne PHP-Kernlogik
+  - wird auf Apache-Hosting per `.htaccess` gesperrt
 - `data/`
   - JSON-Dateien fuer Konfiguration, aktuellen Zustand und Alarmhistorie
+  - liegt im selben Ordner wie die App, ist aber per `.htaccess` gesperrt
+- `docs/`
+  - Markdown-Dokumentation fuer Entwickler
+  - wird auf Apache-Hosting ebenfalls per `.htaccess` gesperrt
 
 Wichtige Dateien:
 
-- `public/index.php`: Dashboard
-- `public/admin/index.php`: Login und Adminpanel
-- `public/api/v1/readings.php`: API fuer eingehende Sensorwerte
-- `public/api/v1/status.php`: Status-API fuer Dashboard und Adminpanel
+- `index.php`: Dashboard
+- `admin/index.php`: Login und Adminpanel
+- `api/v1/readings.php`: API fuer eingehende Sensorwerte
+- `api/v1/status.php`: Status-API fuer Dashboard und Adminpanel
+- `api-docs/index.html`: Swagger UI
+- `openapi.yaml`: OpenAPI-Spec
 - `src/MonitorService.php`: zentrale Orchestrierung fuer Lesen, Berechnen und Status
 - `src/InventoryService.php`: Berechnung von Fuellgrad und Bestand
 - `src/AlertService.php`: Webhook- und Email-Alarmierung
@@ -67,6 +74,7 @@ Fuer den Betrieb wird auf dem Zielsystem benoetigt:
 
 - PHP 8.1 oder neuer empfohlen
 - Schreibrechte auf `data/`
+- Apache mit aktivem `.htaccess`-Support fuer die Produktionsbereitstellung
 - funktionierende Mail-Konfiguration, falls `mail()` fuer Email-Alarme genutzt werden soll
 - Webserver oder PHP Built-in Server
 
@@ -75,7 +83,7 @@ Fuer den Betrieb wird auf dem Zielsystem benoetigt:
 Sobald PHP auf dem Zielsystem installiert ist:
 
 ```bash
-php -S localhost:8000 -t public
+php -S localhost:8000
 ```
 
 Danach ist die Anwendung erreichbar unter:
@@ -83,9 +91,11 @@ Danach ist die Anwendung erreichbar unter:
 - Dashboard: `http://localhost:8000/`
 - Adminpanel: `http://localhost:8000/admin/`
 - Status-API: `http://localhost:8000/api/v1/status.php`
-- Swagger UI: `http://localhost:8000/docs/`
+- Swagger UI: `http://localhost:8000/api-docs/`
 - OpenAPI-Spec: `http://localhost:8000/openapi.yaml`
 
+Hinweis: Der PHP Built-in Server wertet `.htaccess` nicht aus. Die Verzeichnisse `src/`, `data/` und `docs/` werden auf Shared Hosting erst durch Apache geschuetzt.
+
 ## Default-Zugangsdaten
 
 Die Beispiel-Konfiguration bringt absichtlich einfache Startwerte mit:
@@ -171,7 +181,7 @@ Damit repraesentiert `distance_per_unit` die Aenderung des Messwerts pro Flasche
 
 Die API ist jetzt auf drei Ebenen dokumentiert:
 
-- Interaktive Swagger UI unter `http://localhost:8000/docs/`
+- Interaktive Swagger UI unter `http://localhost:8000/api-docs/`
 - Maschinenlesbare OpenAPI-Spec unter `http://localhost:8000/openapi.yaml`
 - Erlaeuternde Referenz in [docs/API.md](docs/API.md)
 

+ 1 - 1
public/admin/index.php → admin/index.php

@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-require_once dirname(__DIR__, 2) . '/src/bootstrap.php';
+require_once dirname(__DIR__) . '/src/bootstrap.php';
 
 $auth = app_admin_auth();
 $auth->start();

+ 1 - 1
public/admin/logout.php → admin/logout.php

@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-require_once dirname(__DIR__, 2) . '/src/bootstrap.php';
+require_once dirname(__DIR__) . '/src/bootstrap.php';
 
 app_admin_auth()->logout();
 app_redirect(app_url('/admin/'));

+ 0 - 0
public/docs/index.html → api-docs/index.html


+ 1 - 1
public/api/v1/readings.php → api/v1/readings.php

@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-require_once dirname(__DIR__, 3) . '/src/bootstrap.php';
+require_once dirname(__DIR__, 2) . '/src/bootstrap.php';
 
 header('Access-Control-Allow-Methods: POST, OPTIONS');
 header('Access-Control-Allow-Headers: Authorization, Content-Type');

+ 1 - 1
public/api/v1/status.php → api/v1/status.php

@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-require_once dirname(__DIR__, 3) . '/src/bootstrap.php';
+require_once dirname(__DIR__, 2) . '/src/bootstrap.php';
 
 if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
     app_json_response([

+ 0 - 0
public/app.js → app.js


+ 7 - 0
data/.htaccess

@@ -0,0 +1,7 @@
+<IfModule mod_authz_core.c>
+    Require all denied
+</IfModule>
+<IfModule !mod_authz_core.c>
+    Order allow,deny
+    Deny from all
+</IfModule>

+ 7 - 0
docs/.htaccess

@@ -0,0 +1,7 @@
+<IfModule mod_authz_core.c>
+    Require all denied
+</IfModule>
+<IfModule !mod_authz_core.c>
+    Order allow,deny
+    Deny from all
+</IfModule>

+ 15 - 3
docs/API.md

@@ -6,12 +6,12 @@ Diese Datei beschreibt die HTTP-Schnittstellen der Anwendung.
 
 Zusatzlich zur textuellen Referenz gibt es jetzt zwei API-Dokumentationsziele in der laufenden Anwendung:
 
-- Swagger UI: `/docs/`
+- Swagger UI: `/api-docs/`
 - OpenAPI-Spec: `/openapi.yaml`
 
-Beim lokalen Start mit `php -S localhost:8000 -t public` sind die URLs:
+Beim lokalen Start mit `php -S localhost:8000` sind die URLs:
 
-- `http://localhost:8000/docs/`
+- `http://localhost:8000/api-docs/`
 - `http://localhost:8000/openapi.yaml`
 
 Die OpenAPI-Datei ist handgeschrieben und dient als maschinenlesbare Quelle fuer die Swagger UI.
@@ -171,6 +171,12 @@ curl -X POST http://localhost:8000/api/v1/readings.php \
   }'
 ```
 
+Bei einer Apache-Unterordner-Installation unter `https://example.com/monitor/` lautet derselbe Endpunkt:
+
+```text
+POST https://example.com/monitor/api/v1/readings.php
+```
+
 ## Endpunkt: `GET /api/v1/status.php`
 
 Liefert den aktuellen Zustand der gesamten Anwendung.
@@ -212,6 +218,12 @@ Beispielstruktur:
 }
 ```
 
+Bei einer Apache-Unterordner-Installation unter `https://example.com/monitor/` lautet der Status-Endpunkt:
+
+```text
+GET https://example.com/monitor/api/v1/status.php
+```
+
 ### Wichtige Felder in `machines[].slots[]`
 
 - `machine_id`

+ 154 - 265
docs/DEPLOYMENT.md

@@ -1,356 +1,245 @@
 # Deployment Guide
 
-This application can run on basic shared webhosting because it uses:
+Diese Anwendung ist jetzt fuer klassisches Apache-Shared-Hosting als einzelner App-Ordner vorbereitet.
 
-- plain PHP
-- no database
-- no Composer dependencies
-- JSON files for persistence
+Zielbild:
 
-The deployment target assumed by this guide is a classic shared hosting account with a document root such as `public_html/`.
+- du laedst genau einen Ordner auf den Webspace hoch
+- dieser Ordner liegt direkt im oeffentlichen Webspace, z. B. unter `public_html/monitor/`
+- die App ist dann unter `https://example.com/monitor/` erreichbar
+- die API liegt automatisch unter `https://example.com/monitor/api/v1/`
+- `src/`, `data/` und die Markdown-Doku bleiben im selben Ordner, sind aber per `.htaccess` gesperrt
 
-## Requirements
+## Voraussetzungen
 
-Required:
+Erforderlich:
 
-- PHP 8.1 or newer recommended
-- PHP sessions enabled
-- permission for PHP to read `src/` and read/write `data/`
-- domain or subdomain pointing to the app
+- Apache-Webhosting mit aktiviertem `.htaccess`-Support
+- PHP 8.1 oder neuer empfohlen
+- PHP-Sessions aktiviert
+- Schreibrechte fuer PHP auf `data/`
 
-Optional but useful:
+Optional:
 
-- working `mail()` configuration for email alerts
-- `allow_url_fopen` enabled for webhook alerts
-- outbound HTTPS requests for webhook alerts
-- browser access to `cdn.jsdelivr.net` if the Swagger UI should load at `/docs/` or `<base-path>/docs/`
+- funktionierende `mail()`-Konfiguration fuer Email-Alarme
+- ausgehende HTTPS-Verbindungen fuer Webhooks
+- Zugriff auf `cdn.jsdelivr.net` fuer die Swagger UI unter `/api-docs/`
 
-Not required:
+## Deploy-Modell
 
-- database
-- Composer
-- Node.js
-- cron job
+Die Anwendung wird nicht mehr in `public/` und private Nachbarordner getrennt deployt. Stattdessen ist der Projektordner selbst das Deploy-Artefakt.
 
-## Base Path And Subfolder Deployments
+Beispiel fuer eine Installation in einem Unterordner:
 
-The application now supports an optional configured base path for deployments below the domain root.
-
-Examples:
-
-- root deployment: `https://monitor.example.com/`
-- root deployment: `https://example.com/`
-- subfolder deployment: `https://example.com/monitor/`
-
-The relevant config value is `app.base_path` in `data/config.json`:
-
-- leave it empty for deployment directly under `/`
-- set it to the URL prefix for subfolder deployments, for example `"/monitor"`
-- the value is normalized on save, so `monitor`, `/monitor`, and `/monitor/` all become `"/monitor"`
-
-This setting is used for generated links, asset URLs, admin redirects, and dashboard API polling.
-
-Important:
+```text
+/home/account/public_html/
+└── monitor/
+    ├── .htaccess
+    ├── index.php
+    ├── app.js
+    ├── styles.css
+    ├── openapi.yaml
+    ├── admin/
+    ├── api/
+    ├── api-docs/
+    ├── data/
+    │   ├── .htaccess
+    │   ├── config.json
+    │   ├── state.json
+    │   └── alert_log.json
+    ├── docs/
+    │   ├── .htaccess
+    │   ├── API.md
+    │   ├── CONFIG.md
+    │   └── DEPLOYMENT.md
+    └── src/
+        └── .htaccess
+```
 
-- `app.base_path` changes URLs, not the required PHP file layout
-- `src/` and `data/` must still stay outside the public web root
-- the public PHP files must still be deployed in a layout where their relative `../src/...` and `../data/...` paths resolve correctly
+Wichtig:
 
-## Where Everything Should Be
+- `index.php`, `admin/`, `api/`, `api-docs/`, `app.js`, `styles.css` und `openapi.yaml` sind oeffentlich
+- `src/`, `data/` und `docs/` liegen im selben Ordner, werden aber durch mitgelieferte `.htaccess`-Dateien blockiert
+- `README.md` und Dotfiles werden ebenfalls ueber die Root-`.htaccess` geblockt
 
-### Preferred layout if the host allows a custom document root
+## Empfohlene URL-Struktur
 
-Keep the repository structure as-is and point the domain document root to `public/`.
+Wenn du den Ordner `monitor/` in `public_html/` hochlaedst, setze in `data/config.json` oder im Adminpanel:
 
-```text
-/home/account/getraenke-monitor/
-├── data/
-│   ├── alert_log.json
-│   ├── config.json
-│   └── state.json
-├── public/              <- document root
-│   ├── admin/
-│   ├── api/
-│   ├── docs/
-│   ├── app.js
-│   ├── index.php
-│   ├── openapi.yaml
-│   └── styles.css
-└── src/
+```json
+{
+  "app": {
+    "base_path": "/monitor"
+  }
+}
 ```
 
-### Recommended layout for basic shared hosting with `public_html/`
-
-If the host does not let you choose `public/` as document root, upload the files like this:
-
-```text
-/home/account/
-├── data/
-│   ├── alert_log.json
-│   ├── config.json
-│   └── state.json
-├── public_html/         <- public contents go here
-│   ├── admin/
-│   ├── api/
-│   ├── docs/
-│   ├── app.js
-│   ├── index.php
-│   ├── openapi.yaml
-│   └── styles.css
-└── src/
-```
+Dann sind die wichtigsten URLs:
 
-Important:
+- Dashboard: `https://example.com/monitor/`
+- Adminpanel: `https://example.com/monitor/admin/`
+- Status-API: `https://example.com/monitor/api/v1/status.php`
+- Readings-API: `https://example.com/monitor/api/v1/readings.php`
+- Swagger UI: `https://example.com/monitor/api-docs/`
+- OpenAPI-Spec: `https://example.com/monitor/openapi.yaml`
 
-- upload the **contents of `public/`** into `public_html/`
-- upload `src/` next to `public_html/`, not inside it
-- upload `data/` next to `public_html/`, not inside it
+Wenn du den Inhalt direkt in den Domain-Root legst, bleibt `base_path` leer.
 
-This exact layout matches how the PHP files resolve `../src/bootstrap.php` and `../data/...`.
+## Upload
 
-## What Must Be Public And What Must Stay Private
+### 1. Projektordner vorbereiten
 
-Public in the web root:
+Du brauchst fuer das Hosting den kompletten Projektordner inklusive:
 
-- `index.php`
-- `styles.css`
-- `app.js`
-- `openapi.yaml`
+- Runtime-Dateien im Root
 - `admin/`
 - `api/`
-- `docs/`
-
-Private, outside the public web root:
-
-- `src/`
-- `data/config.json`
-- `data/state.json`
-- `data/alert_log.json`
-
-Do not place `data/` inside the public web root if you can avoid it. Those files contain credentials, live state, and alert history.
-
-## Deployment Steps
-
-### 1. Prepare the files
-
-From this repository, you need these folders:
-
-- `public/`
+- `api-docs/`
 - `src/`
 - `data/`
+- `docs/`
+- `.htaccess`
 
-You do not need `docs/` on the server for runtime operation. The `docs/` folder in the repository is only Markdown documentation for developers.
-
-### 2. Upload the application
-
-Using FTP, SFTP, or your hosting file manager:
-
-- upload `src/` outside the public web root
-- upload `data/` outside the public web root
-- upload the contents of `public/` into the public web root
+### 2. Ordner hochladen
 
-For a typical shared host that means:
+Lade den kompletten Ordner in den oeffentlichen Webspace hoch, zum Beispiel:
 
-- repo `src/` -> server `~/src/`
-- repo `data/` -> server `~/data/`
-- repo `public/*` -> server `~/public_html/`
+```text
+lokal:  /mein-projekt/automat
+server: /home/account/public_html/monitor
+```
 
-### 3. Set permissions
+Danach muss Apache aus genau diesem Ordner ausliefern.
 
-PHP must be able to write to `data/` and the JSON files in it.
+### 3. Schreibrechte setzen
 
-Typical safe defaults:
+PHP muss in `data/` schreiben koennen.
 
-- directories: `755` or `775`
-- files: `644` or `664`
+Typische Werte:
 
-If the host runs PHP under your own account, `755/644` is often enough. If writes fail, adjust group write permissions first before considering anything more permissive.
+- Verzeichnisse: `755` oder `775`
+- Dateien: `644` oder `664`
 
-The app needs write access for:
+Beschreibbar sein muessen mindestens:
 
 - `data/config.json`
 - `data/state.json`
 - `data/alert_log.json`
 
-### 4. Set the PHP version
-
-In the hosting control panel, select PHP `8.1` or newer for the domain/subdomain.
+### 4. PHP-Version einstellen
 
-### 5. Open the app
+Im Hosting-Panel PHP `8.1` oder neuer aktivieren.
 
-After upload, these URLs should work.
+### 5. Basis-Pfad setzen
 
-If `app.base_path` is empty:
+Falls die App in einem Unterordner wie `monitor/` liegt:
 
-- `/`
-- `/admin/`
-- `/api/v1/status.php`
-- `/docs/`
-- `/openapi.yaml`
+- `app.base_path` auf `"/monitor"` setzen
 
-Examples:
+Falls die App direkt unter der Domain liegt:
 
-- `https://monitor.example.com/`
-- `https://monitor.example.com/admin/`
-- `https://monitor.example.com/api/v1/status.php`
+- `app.base_path` leer lassen
 
-If `app.base_path` is set to `/monitor`:
+Das Feld wird im Adminpanel normalisiert. `monitor`, `/monitor` und `/monitor/` werden zu `"/monitor"`.
 
-- `/monitor/`
-- `/monitor/admin/`
-- `/monitor/api/v1/status.php`
-- `/monitor/docs/`
-- `/monitor/openapi.yaml`
+### 6. Default-Zugangsdaten ersetzen
 
-Examples:
+Nach dem ersten Login sofort aendern:
 
-- `https://example.com/monitor/`
-- `https://example.com/monitor/admin/`
-- `https://example.com/monitor/api/v1/status.php`
+- Admin-Benutzername
+- Admin-Passwort
+- API-Bearer-Token
 
-### 6. Replace the default credentials
+## Apache-Schutz
 
-The example files ship with placeholder credentials. After the first login, immediately change:
+Die Bereitstellung setzt auf die mitgelieferten `.htaccess`-Dateien:
 
-- admin username
-- admin password
-- API bearer token
+- Root `.htaccess`
+  - verhindert Directory Listing
+  - blockiert Dotfiles
+  - blockiert `README.md`
+- `src/.htaccess`
+  - blockiert den Quellcode
+- `data/.htaccess`
+  - blockiert JSON-Daten und Zugangsdaten
+- `docs/.htaccess`
+  - blockiert die internen Markdown-Dateien
 
-You can do that in `/admin/` or `<base-path>/admin/`, depending on the deployment.
+Wenn dein Hosting `.htaccess` ignoriert, ist dieses Deploy-Modell nicht sicher.
 
-### 7. Configure the application
+## Sensor-Clients
 
-In the admin panel, configure:
+Sensoren senden an den Readings-Endpunkt relativ zum `base_path`.
 
-- app name
-- timezone
-- dashboard refresh interval
-- email sender
-- base path
-- webhook targets
-- email recipients
-- machines
-- slots
-- sensor calibration values
-- alert thresholds
-
-For the base path:
-
-- leave it empty when the app is served directly from the domain root
-- set it to the exact URL prefix when the app is served from a subfolder, for example `/monitor`
-
-The JSON structure is documented in [CONFIG.md](./CONFIG.md).
-
-### 8. Connect the sensor clients
-
-Each ESP32 or other sensor client should send readings to:
+Beispiel fuer eine Installation unter `/monitor`:
 
 ```text
-POST https://your-domain.example<base-path>/api/v1/readings.php
-Authorization: Bearer <your-token>
+POST https://example.com/monitor/api/v1/readings.php
+Authorization: Bearer <token>
 Content-Type: application/json
 ```
 
-Examples:
+## Verifikation nach dem Upload
 
-- root deployment: `https://your-domain.example/api/v1/readings.php`
-- subfolder deployment with `app.base_path = "/monitor"`: `https://your-domain.example/monitor/api/v1/readings.php`
+Diese Checks sollten direkt funktionieren:
 
-The request and response format is documented in [API.md](./API.md).
+1. `https://example.com/monitor/` zeigt das Dashboard.
+2. `https://example.com/monitor/admin/` zeigt den Login.
+3. `https://example.com/monitor/api/v1/status.php` liefert JSON.
+4. `https://example.com/monitor/api-docs/` zeigt Swagger UI.
+5. `https://example.com/monitor/openapi.yaml` ist abrufbar.
 
-### 9. Run a deployment check
+Diese Pfade duerfen nicht oeffentlich lesbar sein:
 
-Verify all of the following:
+1. `https://example.com/monitor/data/config.json`
+2. `https://example.com/monitor/src/bootstrap.php`
+3. `https://example.com/monitor/docs/DEPLOYMENT.md`
 
-- dashboard loads without PHP errors
-- admin login works
-- saving config works
-- `data/state.json` updates after a test reading
-- webhook delivery works if configured
-- email delivery works if configured
-- `/api/v1/status.php` returns JSON
-- `/docs/` loads Swagger UI
-- all links, redirects, CSS, and JavaScript requests use the expected base path if deployed in a subfolder
+Sie sollten `403 Forbidden` oder aehnlich blockiert werden.
 
-## First Live Test
+## Lokale Entwicklung
 
-Use one manual API call after deployment:
+Lokal kannst du die App direkt aus dem Projektordner starten:
 
 ```bash
-curl -X POST https://your-domain.example<base-path>/api/v1/readings.php \
-  -H 'Authorization: Bearer YOUR_TOKEN' \
-  -H 'Content-Type: application/json' \
-  -d '{
-    "machine_id": "automat-lobby",
-    "sensor_id": "fach-a1",
-    "distance_mm": 184
-  }'
+php -S localhost:8000
 ```
 
-If that works, the dashboard should shortly show the new state.
-
-## Troubleshooting
-
-### Dashboard loads, but saving config fails
-
-Cause:
-
-- `data/` is not writable by PHP
-
-Check:
-
-- directory permissions
-- file ownership
-- hosting PHP user
-
-### PHP errors about missing `src/bootstrap.php`
-
-Cause:
-
-- `src/` was uploaded into the wrong place
-
-Fix:
-
-- place `src/` next to `public_html/`, not inside it
-
-### API works locally but not on shared hosting
-
-Check:
+Wichtig:
 
-- PHP version
-- whether `Authorization` headers are forwarded by the host
-- whether HTTPS is used
+- der PHP Built-in Server wertet `.htaccess` nicht aus
+- der Schutz von `src/`, `data/` und `docs/` gilt deshalb lokal nicht
+- fuer den Live-Betrieb ist Apache entscheidend
 
-The app already checks both `HTTP_AUTHORIZATION` and `REDIRECT_HTTP_AUTHORIZATION`, which helps on many Apache-based hosts.
+## Fehlerbehebung
 
-### Dashboard loads without CSS/JS, or admin links point to the wrong URL
+### `403` oder `404` auf allen App-URLs
 
-Cause:
+Pruefen:
 
-- `app.base_path` does not match the actual public URL prefix
+- liegt der App-Ordner wirklich im oeffentlichen Webspace
+- liefert Apache aus diesem Ordner aus
+- ist die PHP-Version fuer diesen Ordner aktiv
 
-Fix:
+### Dashboard laedt, aber CSS oder JS fehlt
 
-- leave `app.base_path` empty for deployment at `/`
-- set it to the exact subfolder path for deployments such as `/monitor`
-- save the config again from `/admin/` or update `data/config.json` directly if the admin URL is currently wrong
+Pruefen:
 
-### `/docs/` does not render
+- `app.base_path` stimmt exakt mit dem URL-Unterordner ueberein
+- `app.js` und `styles.css` liegen im Root des App-Ordners
 
-Cause:
+### API funktioniert nicht unter dem Unterordner
 
-- the Swagger UI page loads its assets from `cdn.jsdelivr.net`
+Pruefen:
 
-Fix:
+- `app.base_path` ist z. B. `"/monitor"`
+- der Sensor postet an `/monitor/api/v1/readings.php`
+- das Bearer-Token stimmt
 
-- allow outbound access to the CDN
-- or replace the hosted Swagger assets with local files later
+### `data/config.json` ist im Browser erreichbar
 
-## Recommended Go-Live Checklist
+Dann greift `.htaccess` nicht korrekt. In dem Fall:
 
-- set `app.base_path` correctly for the final public URL
-- keep `src/` and `data/` outside the public web root
-- ensure `data/` is writable
-- replace all demo credentials
-- test one real API reading
-- verify alerts before production use
+- Hosting-Konfiguration fuer `.htaccess` pruefen
+- `AllowOverride` bzw. das entsprechende Hosting-Feature aktivieren
+- die App nicht produktiv betreiben, bis der Zugriff blockiert ist

+ 1 - 1
public/index.php → index.php

@@ -2,7 +2,7 @@
 
 declare(strict_types=1);
 
-require_once dirname(__DIR__) . '/src/bootstrap.php';
+require_once __DIR__ . '/src/bootstrap.php';
 
 $status = app_monitor_service()->getStatus();
 $appName = $status['app']['name'] ?? 'Getraenkeautomat Monitor';

+ 0 - 0
public/openapi.yaml → openapi.yaml


+ 7 - 0
src/.htaccess

@@ -0,0 +1,7 @@
+<IfModule mod_authz_core.c>
+    Require all denied
+</IfModule>
+<IfModule !mod_authz_core.c>
+    Order allow,deny
+    Deny from all
+</IfModule>

+ 0 - 0
public/styles.css → styles.css