M2 Umsetzung
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
.env.local
|
||||
.local/
|
||||
uploads/
|
||||
var/
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
if (!defined('APP_ROOT')) {
|
||||
define('APP_ROOT', dirname(__DIR__));
|
||||
}
|
||||
|
||||
function app_env(string $name, ?string $default = null): ?string
|
||||
{
|
||||
$value = getenv($name);
|
||||
if ($value === false || $value === '') {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function app_is_dev(): bool
|
||||
{
|
||||
return app_env('APP_ENV', 'prod') === 'dev';
|
||||
}
|
||||
|
||||
function app_is_https(): bool
|
||||
{
|
||||
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|
||||
|| (($_SERVER['SERVER_PORT'] ?? null) === '443');
|
||||
}
|
||||
|
||||
function app_start_session(): void
|
||||
{
|
||||
if (PHP_SAPI === 'cli' || session_status() === PHP_SESSION_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sessionPath = app_env('APP_SESSION_PATH', APP_ROOT . '/var/sessions');
|
||||
if ($sessionPath !== null && !is_dir($sessionPath)) {
|
||||
@mkdir($sessionPath, 0700, true);
|
||||
}
|
||||
|
||||
if ($sessionPath !== null && is_dir($sessionPath) && is_writable($sessionPath)) {
|
||||
session_save_path($sessionPath);
|
||||
}
|
||||
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => '',
|
||||
'secure' => app_is_https(),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
|
||||
session_name(app_env('APP_SESSION_NAME', 'kaffeeliste_session') ?? 'kaffeeliste_session');
|
||||
session_start();
|
||||
}
|
||||
|
||||
function app_csrf_token(): string
|
||||
{
|
||||
app_start_session();
|
||||
|
||||
if (!isset($_SESSION) || !is_array($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
if (empty($_SESSION['csrf_token']) || !is_string($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
return $_SESSION['csrf_token'];
|
||||
}
|
||||
|
||||
function app_csrf_field(): string
|
||||
{
|
||||
return '<input type="hidden" name="csrf_token" value="'
|
||||
. htmlspecialchars(app_csrf_token(), ENT_QUOTES, 'UTF-8')
|
||||
. '">';
|
||||
}
|
||||
|
||||
function app_verify_csrf(?string $token): bool
|
||||
{
|
||||
app_start_session();
|
||||
|
||||
if (!isset($_SESSION) || !is_array($_SESSION)) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
return is_string($token)
|
||||
&& isset($_SESSION['csrf_token'])
|
||||
&& is_string($_SESSION['csrf_token'])
|
||||
&& hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
|
||||
function app_require_csrf(): void
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!app_verify_csrf($_POST['csrf_token'] ?? null)) {
|
||||
http_response_code(419);
|
||||
die('CSRF validation failed.');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/app/bootstrap.php';
|
||||
|
||||
app_start_session();
|
||||
|
||||
if ((getenv('APP_DB_DRIVER') ?: '') === 'mysql' && !function_exists('sqlsrv_connect')) {
|
||||
require_once __DIR__ . '/lib/sqlsrv_mysql_compat.php';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
CREATE TABLE IF NOT EXISTS kl_Mitarbeiter (
|
||||
MitarbeiterID INT AUTO_INCREMENT PRIMARY KEY,
|
||||
Name VARCHAR(255) NOT NULL,
|
||||
Email VARCHAR(255) NOT NULL,
|
||||
paypalname VARCHAR(255) NULL,
|
||||
aktiv TINYINT(1) NOT NULL DEFAULT 1,
|
||||
admin TINYINT(1) NOT NULL DEFAULT 0,
|
||||
UNIQUE KEY uq_kl_mitarbeiter_email (Email)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kl_config (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
KostenproStrich DECIMAL(10,2) NOT NULL DEFAULT 0.20,
|
||||
paypaluse TINYINT(1) NOT NULL DEFAULT 0,
|
||||
paypallink VARCHAR(1000) NOT NULL DEFAULT '',
|
||||
strichperweb TINYINT(1) NOT NULL DEFAULT 1
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kl_Einzahlungen (
|
||||
EinzahlungsID INT AUTO_INCREMENT PRIMARY KEY,
|
||||
MitarbeiterID INT NOT NULL,
|
||||
Betrag DECIMAL(10,2) NOT NULL,
|
||||
Datum DATETIME NOT NULL,
|
||||
KEY idx_kl_einzahlungen_mitarbeiter_datum (MitarbeiterID, Datum),
|
||||
CONSTRAINT fk_kl_einzahlungen_mitarbeiter
|
||||
FOREIGN KEY (MitarbeiterID) REFERENCES kl_Mitarbeiter(MitarbeiterID)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kl_Kaffeeverbrauch (
|
||||
VerbrauchID INT AUTO_INCREMENT PRIMARY KEY,
|
||||
MitarbeiterID INT NOT NULL,
|
||||
AnzahlStriche INT NOT NULL,
|
||||
Kosten DECIMAL(10,2) NOT NULL,
|
||||
KostenproStrich DECIMAL(10,2) NOT NULL,
|
||||
Datum DATETIME NOT NULL,
|
||||
Eintragsart INT NULL,
|
||||
KEY idx_kl_verbrauch_mitarbeiter_datum (MitarbeiterID, Datum),
|
||||
CONSTRAINT fk_kl_verbrauch_mitarbeiter
|
||||
FOREIGN KEY (MitarbeiterID) REFERENCES kl_Mitarbeiter(MitarbeiterID)
|
||||
ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kl_hinweise (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
nachricht TEXT NOT NULL,
|
||||
gueltig_bis DATETIME NOT NULL,
|
||||
KEY idx_kl_hinweise_gueltig_bis (gueltig_bis)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CoffeeSurveyVotedEmails (
|
||||
EmailNorm VARCHAR(255) PRIMARY KEY,
|
||||
CreatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CoffeeSurveyResponses (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
Q1_ListEase INT NOT NULL,
|
||||
Q2_ListSatisfaction INT NOT NULL,
|
||||
Q3_CoffeeQuality INT NOT NULL,
|
||||
Q4_WebsiteSatisfaction INT NOT NULL,
|
||||
Q5_Drinks TEXT NOT NULL,
|
||||
Q5_DrinksOther TEXT NULL,
|
||||
Q6_NewVarieties TEXT NULL,
|
||||
Q7_ListProblem VARCHAR(100) NOT NULL,
|
||||
Q7_ListProblemOther TEXT NULL,
|
||||
Q8_Improvements TEXT NOT NULL,
|
||||
Q8_ImprovementsOther TEXT NULL,
|
||||
Q9_BetterIdeas TEXT NULL,
|
||||
CreatedAt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS dev_baseline_metadata (
|
||||
meta_key VARCHAR(100) PRIMARY KEY,
|
||||
meta_value TEXT NOT NULL,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
INSERT INTO kl_config (id, KostenproStrich, paypaluse, paypallink, strichperweb)
|
||||
VALUES (1, 0.20, 0, '', 1)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
KostenproStrich = VALUES(KostenproStrich),
|
||||
paypaluse = VALUES(paypaluse),
|
||||
strichperweb = VALUES(strichperweb);
|
||||
|
||||
INSERT INTO dev_baseline_metadata (meta_key, meta_value)
|
||||
VALUES
|
||||
('baseline_kind', 'reconstructed_mysql_dev'),
|
||||
('legacy_database_status', 'no_ms_sql_legacy_snapshot_available'),
|
||||
('legacy_behavior_source', 'code_and_screenshots'),
|
||||
('visual_reference_path', 'docs/m0/screenshots'),
|
||||
('schema_source', 'database/migrations/0001_legacy_mysql_baseline.sql')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
meta_value = VALUES(meta_value);
|
||||
+15
-2
@@ -24,10 +24,20 @@ export DB_USER="example_user"
|
||||
export DB_PASS="example_password"
|
||||
export DEV_AUTH_EMAIL="admin@test.local"
|
||||
export DEV_AUTH_NAME="Test Admin"
|
||||
# Optional: Standard ist var/sessions im Repository.
|
||||
export APP_SESSION_PATH="/path/to/writable/sessions"
|
||||
```
|
||||
|
||||
## Testdatenbank initialisieren
|
||||
|
||||
Migrationen anwenden:
|
||||
|
||||
```bash
|
||||
php scripts/migrate.php
|
||||
```
|
||||
|
||||
Dev-Admin und Basistestdaten anlegen:
|
||||
|
||||
```bash
|
||||
php scripts/init-mysql-dev.php
|
||||
```
|
||||
@@ -53,6 +63,9 @@ der Port ueber die Ports-Ansicht weitergeleitet werden.
|
||||
## Hinweise
|
||||
|
||||
- Der Dev-Login umgeht AD/LDAP und nutzt `DEV_AUTH_EMAIL`.
|
||||
- Die Tabellen werden aus `database/mysql-dev-schema.sql` angelegt.
|
||||
- Die Tabellen werden ueber `database/migrations/` angelegt.
|
||||
- `database/mysql-dev-schema.sql` bleibt als historische Dev-Schema-Baseline
|
||||
erhalten; der aktive Weg ist `scripts/migrate.php`.
|
||||
- Session-Dateien liegen standardmaessig unter `var/sessions`; `var/` wird von
|
||||
Git ignoriert.
|
||||
- Der Dev-Modus ist nur fuer lokale Tests gedacht, nicht fuer Produktion.
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# M2 Technisches Fundament
|
||||
|
||||
Stand: 2026-07-12
|
||||
|
||||
M2 fuehrt die technischen Grundbausteine fuer den SaaS-Umbau ein, ohne das
|
||||
Legacy-Verhalten oder das bestehende Design zu veraendern.
|
||||
|
||||
## Ziel
|
||||
|
||||
- Versionierte Datenbankmigrationen statt loser Schema-Ausfuehrung.
|
||||
- Zentrales Bootstrap fuer Umgebung, Session und CSRF-Helper.
|
||||
- Bestehende Legacy-Seiten bleiben kompatibel.
|
||||
- Layout-Trennung wird vorbereitet, aber noch nicht in bestehende Seiten
|
||||
hineingezogen.
|
||||
|
||||
## Umgesetzte Bausteine
|
||||
|
||||
### Bootstrap
|
||||
|
||||
- `app/bootstrap.php` definiert zentrale Helfer:
|
||||
- `app_env()`
|
||||
- `app_is_dev()`
|
||||
- `app_start_session()`
|
||||
- `app_csrf_token()`
|
||||
- `app_csrf_field()`
|
||||
- `app_verify_csrf()`
|
||||
- `app_require_csrf()`
|
||||
- `config.php` laedt den Bootstrap und startet die Session mit sicheren
|
||||
Cookie-Optionen.
|
||||
- Sessions werden standardmaessig unter `var/sessions` abgelegt, weil die
|
||||
lokale PHP-Umgebung keinen beschreibbaren Systempfad garantiert. `var/` ist
|
||||
von Git ignoriert. Der Pfad kann ueber `APP_SESSION_PATH` ueberschrieben
|
||||
werden.
|
||||
- CSRF ist bewusst noch nicht global erzwungen. Die vorhandenen POST-Seiten
|
||||
werden spaeter einzeln umgestellt, damit keine Formulare oder Spezialflows
|
||||
brechen.
|
||||
|
||||
### Migrationen
|
||||
|
||||
- `database/migrations/0001_legacy_mysql_baseline.sql` bildet die bisherige
|
||||
MySQL-Dev-Baseline als erste versionierte Migration ab.
|
||||
- `scripts/dev-db.php` verwaltet `schema_migrations` und fuehrt neue
|
||||
Migrationen idempotent aus.
|
||||
- `scripts/migrate.php` ist der direkte Runner fuer Migrationen.
|
||||
- `scripts/init-mysql-dev.php` nutzt ab jetzt ebenfalls die Migrationslogik.
|
||||
|
||||
### Layout
|
||||
|
||||
- `header.php` und `footer.php` bleiben vorerst Legacy-Wrapper.
|
||||
- Die spaetere Trennung in Public-Layout und App-Layout wird erst umgesetzt,
|
||||
wenn die neue Route-/View-Struktur steht.
|
||||
- Die bestehende HTML5-UP-Struktur, Sidebar und Assets bleiben unveraendert.
|
||||
|
||||
## Ausfuehrung
|
||||
|
||||
Mit normaler PHP-CLI:
|
||||
|
||||
```bash
|
||||
php scripts/migrate.php
|
||||
php scripts/init-mysql-dev.php
|
||||
php scripts/check-golden-master.php
|
||||
php scripts/http-smoke.php
|
||||
```
|
||||
|
||||
In der aktuellen lokalen Umgebung:
|
||||
|
||||
```bash
|
||||
LD_LIBRARY_PATH="$PWD/.local/php/usr/lib/x86_64-linux-gnu:$PWD/.local/php/usr/lib/x86_64-linux-gnu/sasl2" \
|
||||
"$PWD/.local/php/usr/bin/php8.3" \
|
||||
-c "$PWD/.local/php-dev.ini" \
|
||||
scripts/migrate.php
|
||||
```
|
||||
|
||||
Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
|
||||
`DB_USER` und `DB_PASS`. `scripts/init-mysql-dev.php` braucht zusaetzlich
|
||||
`DEV_AUTH_EMAIL`.
|
||||
|
||||
## Aktueller Pruefstatus
|
||||
|
||||
- Migration `0001_legacy_mysql_baseline.sql` erfolgreich angewendet.
|
||||
- Zweiter Migrationslauf meldet: Datenbank ist aktuell.
|
||||
- PHP-Syntax fuer Bootstrap, Migrationen, Init-Skript und Config ist sauber.
|
||||
- Session-Start laeuft in der lokalen Dev-Umgebung ohne PHP-Warnings ueber
|
||||
`var/sessions`.
|
||||
- Golden Master weiterhin gruen mit 104 Assertions.
|
||||
- HTTP-Smoke weiterhin gruen mit 14 sicheren Seiten.
|
||||
|
||||
## Bewusste Grenzen
|
||||
|
||||
- Keine Tenant-/User-/Rollen-Tabellen in M2. Diese gehoeren zu M3.
|
||||
- Keine globale CSRF-Erzwingung in M2. Die Absicherung der POST-Seiten erfolgt
|
||||
schrittweise.
|
||||
- Kein Layout-Umbau in M2. Die visuelle Struktur bleibt stabil.
|
||||
- PDF-/Mail-/Jahresprozesse bleiben als M6-Themen offen.
|
||||
|
||||
## Naechste Schritte
|
||||
|
||||
1. POST-Seiten einzeln mit `app_csrf_field()` und `app_require_csrf()`
|
||||
absichern.
|
||||
2. Eine duenne View-/Layout-Struktur vorbereiten, ohne Header/Footer-Markup
|
||||
sofort zu verschieben.
|
||||
3. Danach M3 starten: Tenants, User, Registrierung, Login und Rollen.
|
||||
@@ -282,7 +282,7 @@ Uebersicht:
|
||||
| --- | --- | --- |
|
||||
| M0 | Baseline | Bestand, Sicherheit und Designreferenz sind dokumentiert |
|
||||
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prueft sichere Seiten |
|
||||
| M2 | Technisches Fundament | Public-/App-Struktur, Bootstrap, Session und CSRF stehen |
|
||||
| M2 | Technisches Fundament | Migrationen, Bootstrap, Session und CSRF-Helper stehen |
|
||||
| M3 | SaaS-Basis | Tenants, User, Registrierung, Login und Rollen funktionieren |
|
||||
| M4 | Datenmigration | Legacy-Daten sind tenant-sicher im Zielmodell abgebildet |
|
||||
| M5 | App-Kern | Dashboard, Striche, Einzahlungen, Mitglieder und Liste laufen |
|
||||
@@ -373,19 +373,27 @@ Schritte:
|
||||
- Projektstruktur festlegen: Public-Routes, App-Routes, Views/Templates,
|
||||
Services/Repositories.
|
||||
- Zentrales Bootstrap fuer Config, DB-Verbindung, Session und Fehlerbehandlung.
|
||||
- Layouts trennen: Public-Layout und App-Layout.
|
||||
Begonnen mit `app/bootstrap.php`.
|
||||
- Versionierte Migrationen einfuehren. Begonnen mit
|
||||
`database/migrations/0001_legacy_mysql_baseline.sql` und
|
||||
`scripts/migrate.php`.
|
||||
- Layouts trennen: Public-Layout und App-Layout. Noch offen; Header/Footer
|
||||
bleiben vorerst Legacy-Wrapper.
|
||||
- Bestehende Assets weiterverwenden.
|
||||
- CSRF- und Session-Basis einziehen.
|
||||
- CSRF- und Session-Basis einziehen. Session und CSRF-Helper sind vorhanden;
|
||||
globale Erzwingung erfolgt schrittweise pro POST-Seite.
|
||||
- Konfigurationswerte aus Code in Umgebung oder Settings verschieben.
|
||||
|
||||
Ergebnis:
|
||||
|
||||
- Grundgeruest fuer neue SaaS-App.
|
||||
- Kein fachlicher Rewrite, aber klare Struktur fuer die Migration.
|
||||
- Dokumentation: `docs/m2-technical-foundation.md`.
|
||||
|
||||
Abhaengigkeiten:
|
||||
|
||||
- Entscheidung, ob PHP nativ weitergefuehrt oder ein Framework genutzt wird.
|
||||
- M2 bleibt bewusst ohne Tenant-/User-/Rollen-Tabellen; diese starten in M3.
|
||||
|
||||
### M3: Mandanten, Registrierung und Login
|
||||
|
||||
|
||||
+70
-4
@@ -31,12 +31,78 @@ function dev_pdo(): PDO
|
||||
|
||||
function dev_apply_schema(PDO $pdo): void
|
||||
{
|
||||
$schema = file_get_contents(__DIR__ . '/../database/mysql-dev-schema.sql');
|
||||
if ($schema === false) {
|
||||
fwrite(STDERR, "Could not read schema file.\n");
|
||||
dev_apply_migrations($pdo);
|
||||
}
|
||||
|
||||
function dev_migrations_dir(): string
|
||||
{
|
||||
return __DIR__ . '/../database/migrations';
|
||||
}
|
||||
|
||||
function dev_ensure_migration_table(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec(
|
||||
'CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version VARCHAR(255) PRIMARY KEY,
|
||||
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, true>
|
||||
*/
|
||||
function dev_applied_migrations(PDO $pdo): array
|
||||
{
|
||||
dev_ensure_migration_table($pdo);
|
||||
|
||||
$applied = [];
|
||||
$stmt = $pdo->query('SELECT version FROM schema_migrations ORDER BY version');
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $version) {
|
||||
$applied[(string)$version] = true;
|
||||
}
|
||||
|
||||
return $applied;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string> Applied migration filenames.
|
||||
*/
|
||||
function dev_apply_migrations(PDO $pdo): array
|
||||
{
|
||||
$dir = dev_migrations_dir();
|
||||
$files = glob($dir . '/*.sql');
|
||||
if ($files === false || $files === []) {
|
||||
fwrite(STDERR, "No migration files found in {$dir}.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$pdo->exec($schema);
|
||||
sort($files, SORT_STRING);
|
||||
$applied = dev_applied_migrations($pdo);
|
||||
$newlyApplied = [];
|
||||
$insert = $pdo->prepare('INSERT INTO schema_migrations (version) VALUES (?)');
|
||||
|
||||
foreach ($files as $file) {
|
||||
$version = basename($file);
|
||||
if (isset($applied[$version])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sql = file_get_contents($file);
|
||||
if ($sql === false) {
|
||||
fwrite(STDERR, "Could not read migration file {$version}.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo->exec($sql);
|
||||
$insert->execute([$version]);
|
||||
$newlyApplied[] = $version;
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, "Migration {$version} failed: {$e->getMessage()}\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return $newlyApplied;
|
||||
}
|
||||
|
||||
@@ -2,35 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$required = ['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS', 'DEV_AUTH_EMAIL'];
|
||||
foreach ($required as $name) {
|
||||
if ((getenv($name) ?: '') === '') {
|
||||
fwrite(STDERR, "Missing environment variable: {$name}\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
require __DIR__ . '/dev-db.php';
|
||||
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4',
|
||||
getenv('DB_HOST'),
|
||||
getenv('DB_PORT') ?: '3306',
|
||||
getenv('DB_NAME')
|
||||
);
|
||||
|
||||
$pdo = new PDO($dsn, getenv('DB_USER'), getenv('DB_PASS'), [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
|
||||
$schema = file_get_contents(__DIR__ . '/../database/mysql-dev-schema.sql');
|
||||
if ($schema === false) {
|
||||
fwrite(STDERR, "Could not read schema file.\n");
|
||||
$email = getenv('DEV_AUTH_EMAIL');
|
||||
if ($email === false || trim($email) === '') {
|
||||
fwrite(STDERR, "Missing environment variable: DEV_AUTH_EMAIL\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$pdo->exec($schema);
|
||||
$pdo = dev_pdo();
|
||||
dev_apply_schema($pdo);
|
||||
|
||||
$email = strtolower(trim((string)getenv('DEV_AUTH_EMAIL')));
|
||||
$email = strtolower(trim((string)$email));
|
||||
$name = trim((string)(getenv('DEV_AUTH_NAME') ?: 'Test Admin'));
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
@@ -64,4 +47,3 @@ if ($noticeCount === 0) {
|
||||
}
|
||||
|
||||
echo "MySQL dev schema is ready for {$email}.\n";
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require __DIR__ . '/dev-db.php';
|
||||
|
||||
$pdo = dev_pdo();
|
||||
$applied = dev_apply_migrations($pdo);
|
||||
|
||||
if ($applied === []) {
|
||||
echo "No new migrations. Database is up to date.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
foreach ($applied as $version) {
|
||||
echo "Applied migration: {$version}\n";
|
||||
}
|
||||
|
||||
echo "Applied " . count($applied) . " migration(s).\n";
|
||||
|
||||
Reference in New Issue
Block a user