diff --git a/development-system/.gitignore b/development-system/.gitignore index 400a09a..2b04e6f 100644 --- a/development-system/.gitignore +++ b/development-system/.gitignore @@ -5,6 +5,7 @@ /public/share /public/system /public/vendor +/public/booking-config.php /system /bin/console /node_modules/ diff --git a/development-system/README.md b/development-system/README.md index d9036d4..47b0c23 100644 --- a/development-system/README.md +++ b/development-system/README.md @@ -58,7 +58,11 @@ http://localhost:8080/perspektivwerk/ Die genaue Contao-, PHP- und Template-Version der öffentlichen Seite ist nicht verlässlich aus dem ausgelieferten HTML ableitbar. Das Setup bildet daher die wahrscheinlichste Komponentenfamilie nach; es ist kein Dump der Produktivinstallation. IONOS/Hosting, Matomo-Konfiguration, Inhalte, Templates und Erweiterungen müssen bei Bedarf separat ergänzt werden. -Die Terminlogik liegt in `public/booking/booking.js` als browsernatives ES-Modul. Für die statische Perspektivwerk-Vorschau liegen identische Kopien unter `public/perspektivwerk/js/`. +Der vollständige, statisch auslieferbare Perspektivwerk-DocumentRoot liegt in +`public/perspektivwerk/`. Er enthält die Buchungs-API unter `booking/` und die +Verwaltung unter `booking-admin/`. Die Vorschau verwendet relative API-URLs und +funktioniert dadurch lokal unter `/perspektivwerk/` sowie auf dem Server direkt +im DocumentRoot. Für einen einfachen Upload der vollständigen statischen Mockup-Seite auf den Testserver steht unter Windows dieses Skript bereit: @@ -67,17 +71,27 @@ Testserver steht unter Windows dieses Skript bereit: scripts\Deploy-Perspektivwerk.bat ``` -Es überträgt `public\perspektivwerk\*` rekursiv nach -`boogie:/html/perspektivwerk/`. Voraussetzung ist ein konfigurierter -OpenSSH-Zugang mit dem Host-Alias `boogie`. +Es überträgt den vollständigen Ordner `public\perspektivwerk` und dessen +benachbarte Konfigurationsdatei `public\booking-config.php` nach +`boogie:html/perspektivwerk/`. Damit entsteht der Server-DocumentRoot unter +`html/perspektivwerk/perspektivwerk/`; die Konfigurationsdatei liegt eine Ebene +darüber und damit außerhalb des DocumentRoots. Anschließend setzt es per SSH +lesbare Dateien und durchsuchbare Verzeichnisse. + +Vor dem ersten Upload `public\booking-config.php.example` nach +`public\booking-config.php` kopieren und dort die Produktiv-`database_url` +eintragen. Die echte Konfigurationsdatei ist von Git ausgeschlossen; sie wird +bei fehlender oder leerer PHP-Umgebungsvariable `DATABASE_URL` verwendet. Die +mit übertragene `.htaccess` verweigert den HTTP-Zugriff auf diese Datei. +Voraussetzung ist ein konfigurierter OpenSSH-Zugang mit dem Host-Alias +`boogie`. ## Datenbankgestützte Buchungsdemo Die Buchungsdemo lädt ihre Termine aus MariaDB und speichert eine Anmeldung -über `public/booking/api.php`. Die API wird unabhängig von Contao direkt durch -Apache ausgeliefert, verwendet aber dieselbe `DATABASE_URL` wie Contao. Dadurch -funktioniert sie aus der Vorschau `/perspektivwerk/` und aus dem Contao- -HTML-Element gleichermaßen. +über `public/perspektivwerk/booking/api.php`. Die API wird unabhängig von +Contao direkt durch Apache ausgeliefert. In der statischen Vorschau liegt sie +relativ unter `/perspektivwerk/booking/api.php`. Vor dem ersten Aufruf müssen die externen Migrationen eingespielt werden: @@ -156,7 +170,7 @@ nach 30 Minuten ab. ## Eigenständiges PHP-Backend Die Testverwaltung läuft unabhängig von Contao unter -. Sie bietet für PW und PS: +. Sie bietet für PW und PS: - Termin anlegen, bearbeiten sowie aktivieren/deaktivieren, - Übersicht und Entfernen von Teilnahmen, @@ -215,34 +229,43 @@ Contao maskiert nicht freigegebene HTML-Tags auch im Inhaltselement **HTML**. Vo Danach den Contao-Cache über **Systemwartung** leeren und das Inhaltselement erneut speichern. Keine globale Freigabe mit `*` verwenden. Wer ein HTML-Inhaltselement bearbeiten darf, kann mit dem freigegebenen `script`-Tag JavaScript auf der Website ausführen; diese Bearbeitungsrechte müssen daher auf vertrauenswürdige Backend-Nutzer beschränkt bleiben. -Die Termin- und Kapazitätsdaten kommen aus `/booking/api.php`; Datenbankzugänge -oder SQL gehören niemals in das HTML oder das Browser-JavaScript. +Die Termin- und Kapazitätsdaten kommen in der statischen Vorschau aus +`/perspektivwerk/booking/api.php`; Datenbankzugänge oder SQL gehören niemals in +das HTML oder das Browser-JavaScript. `public/perspektivwerk/js/booking.js` und die zugehörige CSS-Datei sind die für den statischen Upload bestimmte Kopie. Ein Test stellt sicher, dass beide Asset-Paare gleich bleiben. Nach Änderungen können sie lokal synchronisiert werden mit: ```powershell -Copy-Item public\booking\booking.js public\perspektivwerk\js\booking.js -Force -Copy-Item public\booking\booking.css public\perspektivwerk\js\booking.css -Force +Copy-Item public\perspektivwerk\booking\booking.js public\perspektivwerk\js\booking.js -Force +Copy-Item public\perspektivwerk\booking\booking.css public\perspektivwerk\js\booking.css -Force ``` -Der vorhandene SCP-Upload überträgt weiterhin nur die statische -Perspektivwerk-Seite. Für einen externen Einsatz der Buchungsdemo müssen -zusätzlich `/booking/api.php`, die Migrationen und die sichere -`DATABASE_URL`-Konfiguration auf dem Zielserver bereitgestellt werden. +Der vorhandene SCP-Upload überträgt den vollständigen statischen +Perspektivwerk-DocumentRoot einschließlich Buchungsdemo. Vor dem externen +Einsatz müssen die Migrationen eingespielt und `booking-config.php` mit einer +sicheren Datenbank-URL befüllt sein. ## Statische Perspektivwerk-Seite übertragen -Das Skript [scripts/Deploy-Perspektivwerk.ps1](scripts/Deploy-Perspektivwerk.ps1) überträgt ausschließlich den Inhalt von `public/perspektivwerk/` per SCP und korrigiert anschließend die Lesbarkeit auf dem Server per SSH. Dieser Ordner enthält auch `js/booking.js` und `js/booking.css`. Es lädt weder Contao noch die Datenbank oder andere Projektdateien hoch. +Das Skript [scripts/Deploy-Perspektivwerk.bat](scripts/Deploy-Perspektivwerk.bat) überträgt den Ordner `public/perspektivwerk/` als vollständigen DocumentRoot per SCP und korrigiert anschließend die Lesbarkeit auf dem Server per SSH. Es lädt weder Contao noch Datenbank-Migrationen hoch. -```powershell -.\scripts\Deploy-Perspektivwerk.ps1 +```bat +scripts\Deploy-Perspektivwerk.bat ``` -Der Hostname `boogie` wird aus der lokalen SSH-Konfiguration verwendet. Der vollständige Inhalt wird per SCP-Aufruf nach `/home/boogie/html/perspektivwerk/` übertragen. Anschließend führt ein separater SSH-Aufruf `chmod -R a+rX /home/boogie/html/perspektivwerk` aus. Die Passphrase des SSH-Schlüssels wird daher in der Regel zweimal abgefragt. +Der Hostname `boogie` wird aus der lokalen SSH-Konfiguration verwendet. Der +Ordner landet unter `/home/boogie/html/perspektivwerk/perspektivwerk/`; die +Konfiguration liegt daneben unter `/home/boogie/html/perspektivwerk/booking-config.php`. +Anschließend führt ein separater SSH-Aufruf `chmod -R a+rX +/home/boogie/html/perspektivwerk` aus. Die Passphrase des SSH-Schlüssels wird +daher in der Regel zweimal abgefragt. -Vor dem Upload gibt das Skript den vollständigen Zielpfad aus. Damit lässt sich direkt prüfen, ob `booking.js` unter `/home/boogie/html/perspektivwerk/js/booking.js` landet. Das große `X` in `a+rX` setzt das Ausführungsrecht nur auf Verzeichnissen und auf bereits ausführbaren Dateien; normale HTML-, CSS- und JavaScript-Dateien erhalten kein unnötiges Ausführungsbit. +Vor dem Upload muss die ignorierte `booking-config.php` befüllt sein. Das große +`X` in `a+rX` setzt das Ausführungsrecht nur auf Verzeichnissen und auf bereits +ausführbaren Dateien; normale HTML-, CSS- und JavaScript-Dateien erhalten kein +unnötiges Ausführungsbit. ## Versionierter Contao-Seitenzustand diff --git a/development-system/docker-compose.yml b/development-system/docker-compose.yml index acf4ae3..59e401f 100644 --- a/development-system/docker-compose.yml +++ b/development-system/docker-compose.yml @@ -9,7 +9,7 @@ services: APP_ENV: dev CONTAO_ENVIRONMENT: dev DATABASE_URL: mysql://perspektivwerk:perspektivwerk@db:3306/perspektivwerk?serverVersion=mariadb-10.11.0 - BOOKING_BASE_URL: http://localhost:8080 + BOOKING_BASE_URL: http://localhost:8080/perspektivwerk BOOKING_MAIL_TRANSPORT: database command: - sh diff --git a/development-system/public/.htaccess b/development-system/public/.htaccess index e62760b..3fa2f46 100644 --- a/development-system/public/.htaccess +++ b/development-system/public/.htaccess @@ -1,3 +1,13 @@ + + + Require all denied + + + Order allow,deny + Deny from all + + + RewriteEngine On @@ -44,9 +54,9 @@ # directory request to Contao's front controller. RewriteRule ^perspektivwerk/?$ perspektivwerk/index.html [L] - # The independent booking administration is a plain PHP application, not a - # Contao route. Serve its directory URL directly as well. - RewriteRule ^booking-admin/?$ booking-admin/index.php [L] + # The standalone booking administration lives below the static mockup and + # must not be handled by Contao's front controller. + RewriteRule ^perspektivwerk/booking-admin/?$ perspektivwerk/booking-admin/index.php [L] # If the requested filename exists, simply serve it. # We only want to let Apache serve files and not directories. diff --git a/development-system/public/booking-config.php.example b/development-system/public/booking-config.php.example new file mode 100644 index 0000000..adbfb1c --- /dev/null +++ b/development-system/public/booking-config.php.example @@ -0,0 +1,9 @@ + 'mysql://USER:PASSWORD@database-host:3306/booking_database', +]; diff --git a/development-system/public/booking-admin/index.php b/development-system/public/perspektivwerk/booking-admin/index.php similarity index 100% rename from development-system/public/booking-admin/index.php rename to development-system/public/perspektivwerk/booking-admin/index.php diff --git a/development-system/public/booking/api.php b/development-system/public/perspektivwerk/booking/api.php similarity index 100% rename from development-system/public/booking/api.php rename to development-system/public/perspektivwerk/booking/api.php diff --git a/development-system/public/booking/backend.php b/development-system/public/perspektivwerk/booking/backend.php similarity index 94% rename from development-system/public/booking/backend.php rename to development-system/public/perspektivwerk/booking/backend.php index 9d2e13b..4285b19 100644 --- a/development-system/public/booking/backend.php +++ b/development-system/public/perspektivwerk/booking/backend.php @@ -2,9 +2,25 @@ declare(strict_types=1); +function bookingDatabaseUrl(?string $environmentUrl = null, ?string $configurationFile = null): string +{ + $url = $environmentUrl ?? getenv('DATABASE_URL'); + if (!is_string($url) || trim($url) === '') { + $configurationFile ??= dirname(__DIR__, 2).'/booking-config.php'; + if (is_file($configurationFile)) { + $configuration = require $configurationFile; + $url = is_array($configuration) ? ($configuration['database_url'] ?? '') : ''; + } + } + if (!is_string($url) || trim($url) === '') { + throw new RuntimeException('DATABASE_URL is not configured and the booking configuration file does not contain a database URL.'); + } + return trim($url); +} + function bookingDatabase(): PDO { - $url = getenv('DATABASE_URL'); + $url = bookingDatabaseUrl(); $parts = is_string($url) ? parse_url($url) : false; if (!is_array($parts) || ($parts['scheme'] ?? '') !== 'mysql' || !isset($parts['host'], $parts['path'], $parts['user'])) { throw new RuntimeException('DATABASE_URL is not configured correctly.'); diff --git a/development-system/public/booking/booking.css b/development-system/public/perspektivwerk/booking/booking.css similarity index 100% rename from development-system/public/booking/booking.css rename to development-system/public/perspektivwerk/booking/booking.css diff --git a/development-system/public/booking/booking.js b/development-system/public/perspektivwerk/booking/booking.js similarity index 99% rename from development-system/public/booking/booking.js rename to development-system/public/perspektivwerk/booking/booking.js index 01a8ce7..da950f4 100644 --- a/development-system/public/booking/booking.js +++ b/development-system/public/perspektivwerk/booking/booking.js @@ -153,7 +153,7 @@ function openBookingDialog(container, event, state) { function renderComponent(container) { const project = container.dataset.project === 'produktionsschule' ? 'produktionsschule' : 'perspektivwerk'; const now = new Date(); - const state = { apiUrl: container.dataset.apiUrl || '/booking/api.php', csrfToken: '', events: [], currentMonth: new Date(now.getFullYear(), now.getMonth(), 1), selectedDate: '' }; + const state = { apiUrl: container.dataset.apiUrl || 'booking/api.php', csrfToken: '', events: [], currentMonth: new Date(now.getFullYear(), now.getMonth(), 1), selectedDate: '' }; const fetchEvents = async () => { const from = formatDateKey(state.currentMonth); const to = formatDateKey(new Date(state.currentMonth.getFullYear(), state.currentMonth.getMonth() + 1, 1)); diff --git a/development-system/public/booking/confirm.php b/development-system/public/perspektivwerk/booking/confirm.php similarity index 91% rename from development-system/public/booking/confirm.php rename to development-system/public/perspektivwerk/booking/confirm.php index 12b95ab..c3b2695 100644 --- a/development-system/public/booking/confirm.php +++ b/development-system/public/perspektivwerk/booking/confirm.php @@ -13,7 +13,7 @@ function page(string $title, string $message, bool $success = false): never $safeTitle = htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $safeMessage = htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $class = $success ? 'success' : 'error'; - echo "$safeTitle

$safeTitle

$safeMessage

Zurück zur Terminbuchung

"; + echo "$safeTitle

$safeTitle

$safeMessage

Zurück zur Terminbuchung

"; exit; } diff --git a/development-system/public/booking/ical.php b/development-system/public/perspektivwerk/booking/ical.php similarity index 100% rename from development-system/public/booking/ical.php rename to development-system/public/perspektivwerk/booking/ical.php diff --git a/development-system/public/perspektivwerk/index.html b/development-system/public/perspektivwerk/index.html index f620f5a..d92230b 100644 --- a/development-system/public/perspektivwerk/index.html +++ b/development-system/public/perspektivwerk/index.html @@ -21,10 +21,10 @@
Wir sind für Sie da

Unser Team, bestehend aus sozialpädagogischen, pädagogischen und psychologischen Mitarbeiterinnen und Mitarbeitern, unterstützt Ihren Prozess der beruflichen Orientierung. Wir arbeiten eng mit den Bearbeiterinnen und Beratern der Agentur für Arbeit, dem Jobcenter und weiteren Beratungsstellen zusammen.

Die Teilnahme am PerspektivWerk ist kostenlos. Sie bestimmen, an welchen Angeboten Sie teilnehmen.

Beratungsgespräch im PerspektivWerk

Wer kann zu uns kommen?

Menschen zwischen 15 und 65 Jahren, die sich beruflich neu orientieren möchten, beim Einstieg Unterstützung suchen oder ihre Möglichkeiten gemeinsam klären wollen.

1. Orientierung & Ersteinschätzung

Sozialpädagogische und psychologische Module helfen dabei, persönliche Situation, Motivation, Belastbarkeit und Stärken einzuschätzen.

2. Praktische Erprobung & Belastungstest

Arbeitsproben und praktische Erprobungen machen fachliche, soziale und arbeitsbezogene Kompetenzen sichtbar.

3. Auswertung & Perspektiventwicklung

Wir führen die Ergebnisse zusammen und entwickeln konkrete, realistische nächste Schritte.

-
+
-
+

Sie haben Fragen zu dieser Maßnahme?

Haben Sie Interesse an unserem Angebot, melden Sie sich bei uns. Wir beraten Sie gern vorab zu den weiteren Schritten.

Kontakt: (0176) 60328929 · perspektivwerk@sufw.de · Dresdner Straße 16, 01326 Dresden

diff --git a/development-system/public/perspektivwerk/js/booking.js b/development-system/public/perspektivwerk/js/booking.js index 01a8ce7..da950f4 100644 --- a/development-system/public/perspektivwerk/js/booking.js +++ b/development-system/public/perspektivwerk/js/booking.js @@ -153,7 +153,7 @@ function openBookingDialog(container, event, state) { function renderComponent(container) { const project = container.dataset.project === 'produktionsschule' ? 'produktionsschule' : 'perspektivwerk'; const now = new Date(); - const state = { apiUrl: container.dataset.apiUrl || '/booking/api.php', csrfToken: '', events: [], currentMonth: new Date(now.getFullYear(), now.getMonth(), 1), selectedDate: '' }; + const state = { apiUrl: container.dataset.apiUrl || 'booking/api.php', csrfToken: '', events: [], currentMonth: new Date(now.getFullYear(), now.getMonth(), 1), selectedDate: '' }; const fetchEvents = async () => { const from = formatDateKey(state.currentMonth); const to = formatDateKey(new Date(state.currentMonth.getFullYear(), state.currentMonth.getMonth() + 1, 1)); diff --git a/development-system/scripts/Deploy-Perspektivwerk.bat b/development-system/scripts/Deploy-Perspektivwerk.bat index 34584ee..0677ba1 100644 --- a/development-system/scripts/Deploy-Perspektivwerk.bat +++ b/development-system/scripts/Deploy-Perspektivwerk.bat @@ -1,10 +1,11 @@ @echo off setlocal -set "SOURCE=%~dp0..\public\perspektivwerk\*" +set "SOURCE=%~dp0..\public\perspektivwerk" set "TARGET=boogie:html/perspektivwerk/" +set "CONFIG=%~dp0..\public\booking-config.php" -echo Lade das PerspektivWerk-Mockup nach %TARGET% hoch ... +echo Lade den Perspektivwerk-DocumentRoot und die Konfiguration nach %TARGET% hoch ... scp -r "%SOURCE%" "%TARGET%" if errorlevel 1 ( @@ -12,4 +13,12 @@ if errorlevel 1 ( exit /b 1 ) -echo Upload abgeschlossen. +echo Setze Leseberechtigungen auf %TARGET% ... +ssh boogie "chmod -R a+rX html/perspektivwerk/" + +if errorlevel 1 ( + echo Berechtigungen konnten nicht gesetzt werden. + exit /b 1 +) + +echo Upload abgeschlossen. \ No newline at end of file diff --git a/development-system/tests/Deploy-Perspektivwerk.Tests.ps1 b/development-system/tests/Deploy-Perspektivwerk.Tests.ps1 new file mode 100644 index 0000000..0beefa4 --- /dev/null +++ b/development-system/tests/Deploy-Perspektivwerk.Tests.ps1 @@ -0,0 +1,16 @@ +$projectRoot = Split-Path -Parent $PSScriptRoot +$deployBatchPath = Join-Path $projectRoot 'scripts\Deploy-Perspektivwerk.bat' +$htaccessPath = Join-Path $projectRoot 'public\.htaccess' + +Describe 'Perspektivwerk deployment' { + It 'deploys the complete Perspektivwerk document root and its sibling configuration file' { + $script = Get-Content -LiteralPath $deployBatchPath -Raw + $htaccess = Get-Content -LiteralPath $htaccessPath -Raw + + $script | Should Match 'public\\booking-config\.php' + $script | Should Match 'public\\perspektivwerk"' + $script | Should Match 'boogie:html/perspektivwerk/' + $htaccess | Should Match '' + $htaccess | Should Match 'Require all denied' + } +} diff --git a/development-system/tests/PerspektivwerkRoute.Tests.ps1 b/development-system/tests/PerspektivwerkRoute.Tests.ps1 index 293ee6a..c96162c 100644 --- a/development-system/tests/PerspektivwerkRoute.Tests.ps1 +++ b/development-system/tests/PerspektivwerkRoute.Tests.ps1 @@ -2,8 +2,8 @@ $projectRoot = Split-Path -Parent $PSScriptRoot $mockupPath = Join-Path $projectRoot 'public\perspektivwerk\index.html' $bookingScriptPath = Join-Path $projectRoot 'public\perspektivwerk\js\booking.js' $bookingStylesPath = Join-Path $projectRoot 'public\perspektivwerk\js\booking.css' -$contaoBookingScriptPath = Join-Path $projectRoot 'public\booking\booking.js' -$contaoBookingStylesPath = Join-Path $projectRoot 'public\booking\booking.css' +$deploymentBookingScriptPath = Join-Path $projectRoot 'public\perspektivwerk\booking\booking.js' +$deploymentBookingStylesPath = Join-Path $projectRoot 'public\perspektivwerk\booking\booking.css' $htaccessPath = Join-Path $projectRoot 'public\.htaccess' Describe 'Perspektivwerk static route' { @@ -19,26 +19,28 @@ Describe 'Perspektivwerk static route' { $mockup | Should Match 'href="js/booking\.css"' $mockup | Should Match 'data-sufw-booking data-project="perspektivwerk"' $mockup | Should Match 'data-sufw-booking data-project="produktionsschule"' + $mockup | Should Match 'data-api-url="booking/api\.php"' + $mockup | Should Not Match 'data-api-url="/booking/api\.php"' $mockup.IndexOf('data-project="perspektivwerk"') | Should BeLessThan $mockup.IndexOf('data-project="produktionsschule"') $mockup | Should Match 'src="js/booking\.js"' } - It 'keeps the Contao and static-preview asset copies identical' { - (Test-Path -LiteralPath $contaoBookingScriptPath) | Should Be $true - (Test-Path -LiteralPath $contaoBookingStylesPath) | Should Be $true - (Get-FileHash -LiteralPath $bookingScriptPath).Hash | Should Be (Get-FileHash -LiteralPath $contaoBookingScriptPath).Hash - (Get-FileHash -LiteralPath $bookingStylesPath).Hash | Should Be (Get-FileHash -LiteralPath $contaoBookingStylesPath).Hash + It 'keeps the deployed API assets and static-preview copies identical' { + (Test-Path -LiteralPath $deploymentBookingScriptPath) | Should Be $true + (Test-Path -LiteralPath $deploymentBookingStylesPath) | Should Be $true + (Get-FileHash -LiteralPath $bookingScriptPath).Hash | Should Be (Get-FileHash -LiteralPath $deploymentBookingScriptPath).Hash + (Get-FileHash -LiteralPath $bookingStylesPath).Hash | Should Be (Get-FileHash -LiteralPath $deploymentBookingStylesPath).Hash } It 'keeps the enabled submit button visibly rendered' { - $script = Get-Content -LiteralPath $contaoBookingScriptPath -Raw + $script = Get-Content -LiteralPath $deploymentBookingScriptPath -Raw $script | Should Match "submitButton\.style\.visibility = 'visible'" $script | Should Match "actions\.style\.position = 'sticky'" } It 'defines colours for the booking form action buttons' { - $styles = Get-Content -LiteralPath $contaoBookingStylesPath -Raw + $styles = Get-Content -LiteralPath $deploymentBookingStylesPath -Raw $styles | Should Match '\.sufw-booking__form-actions button\s*\{[^}]*background-color:\s*#08752a' $styles | Should Match '\.sufw-booking__form-actions button\s*\{[^}]*color:\s*#fff' @@ -47,9 +49,12 @@ Describe 'Perspektivwerk static route' { It 'routes /perspektivwerk to the static entry point before the Contao fallback' { $htaccess = Get-Content -LiteralPath $htaccessPath -Raw $staticRouteIndex = $htaccess.IndexOf('RewriteRule ^perspektivwerk/?$ perspektivwerk/index.html [L]') + $adminRouteIndex = $htaccess.IndexOf('RewriteRule ^perspektivwerk/booking-admin/?$ perspektivwerk/booking-admin/index.php [L]') $fallbackIndex = $htaccess.IndexOf('RewriteRule ^ %{ENV:BASE}/index.php [L]') $staticRouteIndex | Should BeGreaterThan -1 $staticRouteIndex | Should BeLessThan $fallbackIndex + $adminRouteIndex | Should BeGreaterThan -1 + $adminRouteIndex | Should BeLessThan $fallbackIndex } } diff --git a/development-system/tests/booking-backend-config.test.php b/development-system/tests/booking-backend-config.test.php new file mode 100644 index 0000000..05b3e6d --- /dev/null +++ b/development-system/tests/booking-backend-config.test.php @@ -0,0 +1,34 @@ + 'mysql://fallback:secret@database:3306/booking'];\n"); + + expectSame( + 'mysql://fallback:secret@database:3306/booking', + bookingDatabaseUrl('', $configFile), + 'The configuration file must be used when DATABASE_URL is empty.' + ); + expectSame( + 'mysql://environment:secret@database:3306/booking', + bookingDatabaseUrl('mysql://environment:secret@database:3306/booking', $configFile), + 'DATABASE_URL must take precedence over the configuration file.' + ); +} finally { + unlink($configFile); +} + +echo "booking backend configuration tests passed\n"; diff --git a/development-system/tests/booking-migrations.test.mjs b/development-system/tests/booking-migrations.test.mjs index 9a70637..aa5505f 100644 --- a/development-system/tests/booking-migrations.test.mjs +++ b/development-system/tests/booking-migrations.test.mjs @@ -13,9 +13,9 @@ test('liefert eine externe, idempotente Anfangsmigration für Termine und Anmeld test('hält die statische Vorschaukopie der Buchungsassets identisch', async () => { const [componentScript, previewScript, componentStyle, previewStyle] = await Promise.all([ - readFile(new URL('../public/booking/booking.js', import.meta.url), 'utf8'), + readFile(new URL('../public/perspektivwerk/booking/booking.js', import.meta.url), 'utf8'), readFile(new URL('../public/perspektivwerk/js/booking.js', import.meta.url), 'utf8'), - readFile(new URL('../public/booking/booking.css', import.meta.url), 'utf8'), + readFile(new URL('../public/perspektivwerk/booking/booking.css', import.meta.url), 'utf8'), readFile(new URL('../public/perspektivwerk/js/booking.css', import.meta.url), 'utf8'), ]); @@ -61,12 +61,8 @@ test('liefert zusätzliche lokale Demo-Termine für Juli und August', async () = }); test('liefert die unabhängige Verwaltung unter booking-admin mit getrennten Bereichen', async () => { - const [routes, admin] = await Promise.all([ - readFile(new URL('../public/.htaccess', import.meta.url), 'utf8'), - readFile(new URL('../public/booking-admin/index.php', import.meta.url), 'utf8'), - ]); + const admin = await readFile(new URL('../public/perspektivwerk/booking-admin/index.php', import.meta.url), 'utf8'); - assert.match(routes, /\^booking-admin\/\?\$ booking-admin\/index\.php/); assert.match(admin, /view === 'registrations'/); assert.match(admin, /view === 'export'/); assert.doesNotMatch(admin, /getenv\(/); diff --git a/development-system/tests/booking.test.mjs b/development-system/tests/booking.test.mjs index 1c297eb..3a075ba 100644 --- a/development-system/tests/booking.test.mjs +++ b/development-system/tests/booking.test.mjs @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { bookingConfirmationMessage, canSubmitBookingForm, formatDateKey, formatEventDuration, getSlotsForDate, requiresIntroductionDetails, validateBookingForm } from '../public/booking/booking.js'; +import { bookingConfirmationMessage, canSubmitBookingForm, formatDateKey, formatEventDuration, getSlotsForDate, requiresIntroductionDetails, validateBookingForm } from '../public/perspektivwerk/booking/booking.js'; const events = [ { id: 'one', project: 'perspektivwerk', startsAt: '2026-09-14 08:00:00', endsAt: '2026-09-18 16:00:00', status: 'available' },