Files
deskhub/DeskHub-Webseite/tools/demo-video/seed_demo.php
T
2026-10-23 17:50:23 +00:00

121 lines
13 KiB
PHP

<?php
/**
* tools/demo-video/seed_demo.php
*
* Baut eine eigene Demo-Installation (SQLite, alles erfunden) in ein Temp-Verzeichnis, damit Screenshots und Video keine echten Kundendaten zeigen.
* php tools/demo-video/seed_demo.php [Zielordner]
* Danach: php -S 127.0.0.1:8097 -t <Zielordner> (Zugang: lena@demo.example / Demo-Passwort-1234)
*/
$root = dirname(__DIR__, 2);
$tmp = $argv[1] ?? (sys_get_temp_dir() . '/deskhub-demo');
function rrm(string $d): void { if (!is_dir($d)) return; foreach (scandir($d) as $e) { if ($e === '.' || $e === '..') continue; is_dir("$d/$e") ? rrm("$d/$e") : @unlink("$d/$e"); } @rmdir($d); }
rrm($tmp);
mkdir($tmp, 0755, true);
$skip = '#^(config/config\.php|database/[^/]+\.sqlite[^/]*|backups|uploads|tests|tools|plugins/[^/]+|docs|\.git)(/|$)#';
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST) as $f) {
$rel = str_replace('\\', '/', substr($f->getPathname(), strlen($root) + 1));
if (preg_match($skip, $rel) && !preg_match('#^(backups|uploads|plugins)/(\.htaccess|README\.md)$#', $rel)) continue;
if ($f->isDir()) { @mkdir("$tmp/$rel", 0755, true); continue; }
@mkdir(dirname("$tmp/$rel"), 0755, true);
copy($f->getPathname(), "$tmp/$rel");
}
@mkdir("$tmp/uploads"); @mkdir("$tmp/config");
file_put_contents("$tmp/config/config.php", '<?php return ' . var_export([
'app_key' => bin2hex(random_bytes(32)),
'db' => ['driver' => 'sqlite', 'sqlite_path' => "$tmp/database/deskhub.sqlite"],
'site' => ['name' => 'Muster Support', 'url' => 'http://127.0.0.1:8097', 'admin_email' => 'lena@demo.example', 'timezone' => 'Europe/Berlin'],
'uploads' => ['max_size_bytes' => 10485760, 'allowed_extensions' => ['png'], 'path' => "$tmp/uploads"],
], true) . ';');
putenv('DESKHUB_CONFIG_JSON'); putenv('DESKHUB_SQLITE_PATH');
require_once "$root/database/sqlite_schema.php";
require_once "$root/database/migrate.php";
date_default_timezone_set('Europe/Berlin');
$p = new PDO("sqlite:$tmp/database/deskhub.sqlite");
$p->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$p->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$p->exec('PRAGMA foreign_keys = ON');
$p->exec(convert_schema_to_sqlite(file_get_contents("$root/database/schema.sql")));
run_migrations($p, true);
$ts = fn(string $rel) => date('Y-m-d H:i:s', strtotime($rel));
$dept = fn(string $n) => (int) $p->query("SELECT id FROM departments WHERE name = " . $p->quote($n))->fetchColumn();
$prio = fn(string $n) => (int) $p->query("SELECT id FROM priorities WHERE name = " . $p->quote($n))->fetchColumn();
// Mitarbeiter
$p->exec('DELETE FROM staff');
$ins = $p->prepare('INSERT INTO staff (name, email, password_hash, role, department_id, is_active, created_at) VALUES (?,?,?,?,?,1,?)');
$staff = [];
foreach ([['Lena Hartmann', 'lena@demo.example', 'admin', 'Allgemeiner Support'], ['Tim Roth', 'tim@demo.example', 'agent', 'Allgemeiner Support'], ['Mira Schulz', 'mira@demo.example', 'agent', 'Technik']] as [$n, $e, $r, $d]) {
$ins->execute([$n, $e, password_hash('Demo-Passwort-1234', PASSWORD_DEFAULT), $r, $dept($d), $ts('-30 days')]);
$staff[$n] = (int) $p->lastInsertId();
}
$av = $p->prepare("INSERT OR REPLACE INTO staff_availability (staff_id, status, updated_at) VALUES (?,?,?)");
$av->execute([$staff['Lena Hartmann'], 'available', $ts('now')]);
$av->execute([$staff['Tim Roth'], 'busy', $ts('now')]);
$av->execute([$staff['Mira Schulz'], 'break', $ts('now')]);
// Einstellungen
$set = $p->prepare('INSERT OR REPLACE INTO settings (setting_key, setting_value) VALUES (?, ?)');
foreach (['assistant_enabled' => '1', 'live_chat_enabled' => '0', 'kb_enabled' => '1', 'display_name' => 'Muster Support', 'assistant_name' => 'Mia, die digitale Assistentin',
'assistant_greeting' => 'Hallo! Ich bin Mia. Ich beantworte Fragen zu Lieferung, Rechnung und Konto oder erstelle ein Ticket für dich.', 'assistant_department_id' => (string) $dept('Allgemeiner Support'),
'assistant_priority_id' => (string) $prio('Normal'), 'assistant_synonyms' => "Rechnung, Beleg, Quittung\nKündigung, kündigen, Vertrag beenden",
'remind_enabled' => '1', 'remind_after_days' => '3', 'auto_close_enabled' => '1', 'auto_close_days' => '7'] as $k => $v) $set->execute([$k, $v]);
// Wissensdatenbank
$p->exec("DELETE FROM kb_articles"); $p->exec("DELETE FROM kb_categories");
$p->exec("INSERT INTO kb_categories (name, slug, sort_order) VALUES ('Konto', 'konto', 1), ('Bestellung und Versand', 'versand', 2), ('Rechnungen', 'rechnungen', 3)");
$cat = fn(string $s) => (int) $p->query("SELECT id FROM kb_categories WHERE slug = " . $p->quote($s))->fetchColumn();
$art = $p->prepare('INSERT INTO kb_articles (category_id, title, slug, body, is_published, views, created_at, updated_at) VALUES (?,?,?,?,1,?,?,?)');
foreach ([
['konto', 'Passwort zurücksetzen', 'passwort-zuruecksetzen', '<p>Klicke auf der Anmeldeseite auf <b>Passwort vergessen</b>. Wir senden dir einen Link per E-Mail, mit dem du ein neues Passwort festlegst. Der Link gilt eine Stunde.</p>', 128],
['konto', 'E-Mail-Adresse ändern', 'email-aendern', '<p>Unter <b>Mein Profil</b> kannst du deine E-Mail-Adresse ändern. Zur Sicherheit bestätigst du die neue Adresse per Link.</p>', 41],
['versand', 'Wie lange dauert der Versand?', 'versandzeiten', '<p>Bestellungen kommen in der Regel in <b>2 bis 3 Werktagen</b> an. Bei Auslandsversand dauert es bis zu 7 Werktage.</p>', 203],
['versand', 'Meine Sendung ist nicht angekommen', 'sendung-fehlt', '<p>Prüfe zuerst die Sendungsverfolgung. Kommt das Paket nach 5 Werktagen nicht an, erstelle bitte ein Ticket mit der Bestellnummer.</p>', 77],
['rechnungen', 'Rechnungen herunterladen', 'rechnungen', '<p>Alle Rechnungen findest du im Kundenbereich unter <b>Rechnungen</b>. Dort lädst du sie als PDF herunter.</p>', 156],
] as [$c, $t, $s, $b, $v]) $art->execute([$cat($c), $t, $s, $b, $v, $ts('-20 days'), $ts('-5 days')]);
// zusätzliche Antworten des Assistenten
$ans = $p->prepare('INSERT INTO assistant_answers (title, keywords, answer, is_active, hits, created_at, updated_at) VALUES (?,?,?,?,?,?,?)');
foreach ([
['Wie sind eure Öffnungszeiten?', 'geöffnet, Uhrzeit, erreichbar', "Wir sind Montag bis Freitag von 9 bis 17 Uhr für dich da.\nAußerhalb dieser Zeit beantwortet dich Mia oder du erstellst ein Ticket.", 1, 34],
['Kann ich eine Bestellung stornieren?', 'Storno, abbrechen, stornieren', 'Solange die Bestellung noch nicht versendet ist, kannst du sie im Kundenbereich unter „Bestellungen“ stornieren.', 1, 19],
['Wo finde ich meine Rechnung?', 'Beleg, Quittung', 'Deine Rechnungen findest du im Kundenbereich unter „Rechnungen“ als PDF.', 1, 52],
] as $a) $ans->execute([$a[0], $a[1], $a[2], $a[3], $a[4], $ts('-15 days'), $ts('-2 days')]);
// Fragen-Log
$lg = $p->prepare('INSERT INTO assistant_log (question, result, source, matched_title, ticket_number, helpful, created_at) VALUES (?,?,?,?,?,?,?)');
foreach ([
['Wie lange dauert der Versand?', 'kb', 'kb', 'Wie lange dauert der Versand?', null, 1, '-2 hours'], ['Wo finde ich meine Rechnung?', 'custom', 'custom', 'Wo finde ich meine Rechnung?', null, 1, '-3 hours'],
['Wann seid ihr erreichbar?', 'custom', 'custom', 'Wie sind eure Öffnungszeiten?', null, 1, '-5 hours'], ['Passwort vergessen was tun', 'kb', 'kb', 'Passwort zurücksetzen', null, null, '-1 day'],
['Wie kann ich meinen Vertrag kündigen?', 'none', null, null, null, null, '-1 day'], ['Wie kann ich meinen Vertrag kündigen?', 'none', null, null, null, null, '-2 days'],
['Gibt es Rabatt für Schulen?', 'none', null, null, null, null, '-2 days'], ['Mein Paket ist beschädigt', 'kb', 'kb', 'Meine Sendung ist nicht angekommen', null, 0, '-3 days'],
['Ich brauche Hilfe von einem Menschen', 'ticket_request', null, null, null, null, '-3 days'], ['Mein Paket ist beschädigt', 'ticket_created', null, null, '260001', null, '-3 days'],
] as [$q, $r, $s, $m, $tn, $h, $when]) $lg->execute([$q, $r, $s, $m, $tn, $h, $ts($when)]);
// Tickets
$tk = $p->prepare('INSERT INTO tickets (ticket_number, subject, requester_name, requester_email, department_id, priority_id, status, source, assigned_staff_id, created_at, updated_at, resolution_due_at, snoozed_until, snoozed_by) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
$th = $p->prepare('INSERT INTO ticket_threads (ticket_id, author_type, staff_id, message, is_internal_note, created_at) VALUES (?,?,?,?,?,?)');
$defs = [
['260101', 'Rechnung für März fehlt', 'Erika Muster', 'Buchhaltung', 'Hoch', 'open', 'Lena Hartmann', '-3 hours', '+5 hours', [['customer', null, 'Guten Tag, in meinem Kundenbereich fehlt die Rechnung für März. Können Sie mir diese bitte zuschicken? Danke!', 0, '-3 hours'], ['staff', 'Lena Hartmann', 'Hallo Frau Muster, ich schaue gleich nach und melde mich noch heute bei Ihnen.', 0, '-2 hours'], ['staff', 'Lena Hartmann', 'Intern: Rechnung wurde nicht automatisch erzeugt, bitte Buchhaltung prüfen lassen.', 1, '-90 minutes']]],
['260102', 'Passwort zurücksetzen funktioniert nicht', 'Jonas Weber', 'Allgemeiner Support', 'Dringend', 'open', 'Tim Roth', '-26 hours', '-2 hours', [['customer', null, 'Der Link zum Zurücksetzen meines Passworts kommt nie an. Ich habe schon dreimal gedrückt.', 0, '-26 hours']]],
['260103', 'Lieferstatus meiner Bestellung', 'Sophie Klein', 'Allgemeiner Support', 'Normal', 'answered', 'Tim Roth', '-30 hours', '+20 hours', [['customer', null, 'Wo ist meine Bestellung 48211? Sie sollte gestern kommen.', 0, '-30 hours'], ['staff', 'Tim Roth', 'Hallo Sophie, Ihre Bestellung ist beim Zusteller und kommt morgen. Hier die Sendungsnummer: 0034 5521 8890.', 0, '-28 hours']]],
['260104', 'Frage zur Kündigung', 'Markus Lehmann', 'Allgemeiner Support', 'Normal', 'open', null, '-45 minutes', '+23 hours', [['customer', null, 'Wie kann ich meinen Vertrag zum Monatsende kündigen?', 0, '-45 minutes']]],
['260105', 'App stürzt beim Start ab', 'Lea Wagner', 'Technik', 'Hoch', 'open', 'Mira Schulz', '-5 hours', '+3 hours', [['customer', null, 'Seit dem letzten Update stürzt die App auf meinem Handy direkt nach dem Start ab (Android 14).', 0, '-5 hours'], ['staff', 'Mira Schulz', 'Danke für den Hinweis. Wir können das reproduzieren und arbeiten an einem Fix.', 0, '-4 hours']]],
['260106', 'Rückruf gewünscht', 'Dirk Neumann', 'Allgemeiner Support', 'Niedrig', 'open', 'Lena Hartmann', '-2 days', '+1 day', [['customer', null, 'Bitte rufen Sie mich nächste Woche wegen meines Angebots zurück.', 0, '-2 days']], '+1 day 08:00'],
['259901', 'Passwort vergessen: Link kommt nicht an', 'Nina Fischer', 'Allgemeiner Support', 'Normal', 'resolved', 'Tim Roth', '-12 days', null, [['customer', null, 'Ich habe mein Passwort vergessen und bekomme keine E-Mail.', 0, '-12 days'], ['staff', 'Tim Roth', 'Der Link war im Spam-Ordner. Ich habe ihn erneut gesendet, jetzt klappt es.', 0, '-12 days']]],
['259902', 'Rechnung als PDF anfordern', 'Paul Schmitt', 'Buchhaltung', 'Normal', 'closed', 'Lena Hartmann', '-20 days', null, [['customer', null, 'Bitte schicken Sie mir die Rechnung Nr. 5521 als PDF.', 0, '-20 days'], ['staff', 'Lena Hartmann', 'Die Rechnung ist angehängt. Viele Grüße', 0, '-20 days']]],
['259903', 'Paket beschädigt angekommen', 'Clara Braun', 'Allgemeiner Support', 'Hoch', 'closed', 'Tim Roth', '-25 days', null, [['customer', null, 'Der Karton war aufgerissen.', 0, '-25 days'], ['staff', 'Tim Roth', 'Das tut uns leid, Ersatz ist unterwegs.', 0, '-24 days']]],
['259904', 'Kündigung zum Jahresende', 'Felix Vogel', 'Allgemeiner Support', 'Normal', 'closed', 'Lena Hartmann', '-40 days', null, [['customer', null, 'Ich möchte kündigen.', 0, '-40 days'], ['staff', 'Lena Hartmann', 'Kündigung bestätigt.', 0, '-39 days']]],
];
foreach ($defs as $d) {
[$no, $subject, $name, $dn, $pn, $status, $assignee, $created, $due, $threads] = $d;
$snooze = $d[10] ?? null;
$tk->execute([$no, $subject, $name, strtolower(str_replace(' ', '.', $name)) . '@kunde.example', $dept($dn), $prio($pn), $status, 'web', $assignee ? $staff[$assignee] : null,
$ts($created), $ts($threads[count($threads) - 1][4]), $due ? $ts($due) : null, $snooze ? $ts($snooze) : null, $snooze ? $staff['Lena Hartmann'] : null]);
$id = (int) $p->lastInsertId();
foreach ($threads as [$who, $sn, $msg, $note, $when]) $th->execute([$id, $who, $sn ? $staff[$sn] : null, $msg, $note, $ts($when)]);
if ($snooze) $p->prepare('INSERT INTO ticket_followers (ticket_id, staff_id, created_at) VALUES (?,?,?)')->execute([$id, $staff['Lena Hartmann'], $ts('-1 day')]);
}
$p->prepare('INSERT INTO ticket_followers (ticket_id, staff_id, created_at) VALUES ((SELECT id FROM tickets WHERE ticket_number = ?),?,?)')->execute(['260102', $staff['Lena Hartmann'], $ts('-20 hours')]);
echo "Demo bereit: $tmp\n";