- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
441 lines
14 KiB
PHP
441 lines
14 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>WebPush Demo - Push-Benachrichtigungen</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.container {
|
|
background: white;
|
|
border-radius: 20px;
|
|
padding: 40px;
|
|
max-width: 600px;
|
|
width: 100%;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
h1 {
|
|
color: #667eea;
|
|
margin-bottom: 10px;
|
|
font-size: 32px;
|
|
}
|
|
|
|
.subtitle {
|
|
color: #666;
|
|
margin-bottom: 30px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.status-card {
|
|
background: #f7fafc;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
margin-bottom: 30px;
|
|
border-left: 4px solid #667eea;
|
|
}
|
|
|
|
.status-label {
|
|
color: #667eea;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.status-value {
|
|
color: #2d3748;
|
|
font-size: 18px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.status-subscribed {
|
|
color: #48bb78;
|
|
}
|
|
|
|
.status-unsubscribed {
|
|
color: #f56565;
|
|
}
|
|
|
|
.button-group {
|
|
display: grid;
|
|
gap: 15px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
button {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border: none;
|
|
padding: 16px 24px;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
|
}
|
|
|
|
button:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
button:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
button:disabled {
|
|
background: #cbd5e0;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
button.secondary {
|
|
background: white;
|
|
color: #667eea;
|
|
border: 2px solid #667eea;
|
|
box-shadow: none;
|
|
}
|
|
|
|
button.secondary:hover {
|
|
background: #f7fafc;
|
|
}
|
|
|
|
button.danger {
|
|
background: linear-gradient(135deg, #f56565 0%, #c53030 100%);
|
|
box-shadow: 0 4px 15px rgba(245, 101, 101, 0.3);
|
|
}
|
|
|
|
button.danger:hover {
|
|
box-shadow: 0 6px 20px rgba(245, 101, 101, 0.4);
|
|
}
|
|
|
|
.info-box {
|
|
background: #edf2f7;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.info-title {
|
|
color: #2d3748;
|
|
font-weight: 600;
|
|
margin-bottom: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.info-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.info-list li {
|
|
color: #4a5568;
|
|
padding: 8px 0;
|
|
padding-left: 24px;
|
|
position: relative;
|
|
}
|
|
|
|
.info-list li:before {
|
|
content: "✓";
|
|
position: absolute;
|
|
left: 0;
|
|
color: #48bb78;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.console-output {
|
|
background: #1a202c;
|
|
color: #e2e8f0;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 13px;
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.console-output .log-entry {
|
|
margin-bottom: 8px;
|
|
opacity: 0;
|
|
animation: fadeIn 0.3s ease forwards;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.console-output .log-success {
|
|
color: #48bb78;
|
|
}
|
|
|
|
.console-output .log-error {
|
|
color: #f56565;
|
|
}
|
|
|
|
.console-output .log-info {
|
|
color: #4299e1;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.container {
|
|
padding: 24px;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 24px;
|
|
}
|
|
|
|
button {
|
|
padding: 14px 20px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🔔 WebPush Demo</h1>
|
|
<p class="subtitle">Teste Push-Benachrichtigungen in deinem Browser</p>
|
|
|
|
<div class="status-card">
|
|
<div class="status-label">Status</div>
|
|
<div class="status-value" id="subscription-status">Prüfe...</div>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button id="btn-init" onclick="initWebPush()">
|
|
🚀 WebPush initialisieren
|
|
</button>
|
|
<button id="btn-subscribe" onclick="subscribe()" disabled>
|
|
✅ Push-Benachrichtigungen aktivieren
|
|
</button>
|
|
<button id="btn-unsubscribe" onclick="unsubscribe()" class="danger" disabled>
|
|
❌ Push-Benachrichtigungen deaktivieren
|
|
</button>
|
|
<button id="btn-test" onclick="sendTestNotification()" class="secondary" disabled>
|
|
🧪 Test-Benachrichtigung senden
|
|
</button>
|
|
</div>
|
|
|
|
<div class="info-box">
|
|
<div class="info-title">
|
|
<span>📋</span>
|
|
<span>Features</span>
|
|
</div>
|
|
<ul class="info-list">
|
|
<li>Service Worker Registration</li>
|
|
<li>VAPID Public Key vom Server</li>
|
|
<li>Push-Subscription Management</li>
|
|
<li>Test-Benachrichtigungen</li>
|
|
<li>Status-Tracking</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="console-output" id="console-output"></div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
// Use the globally available WebPushManager from main.js
|
|
// It's initialized at app startup as window.webPushManager
|
|
let webPush = null;
|
|
|
|
// Console Output
|
|
function log(message, type = 'info') {
|
|
const output = document.getElementById('console-output');
|
|
const entry = document.createElement('div');
|
|
entry.className = `log-entry log-${type}`;
|
|
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
|
|
output.appendChild(entry);
|
|
output.scrollTop = output.scrollHeight;
|
|
}
|
|
|
|
// Update UI
|
|
function updateUI(isSubscribed) {
|
|
const status = document.getElementById('subscription-status');
|
|
const btnSubscribe = document.getElementById('btn-subscribe');
|
|
const btnUnsubscribe = document.getElementById('btn-unsubscribe');
|
|
const btnTest = document.getElementById('btn-test');
|
|
|
|
if (isSubscribed) {
|
|
status.textContent = 'Abonniert';
|
|
status.className = 'status-value status-subscribed';
|
|
btnSubscribe.disabled = true;
|
|
btnUnsubscribe.disabled = false;
|
|
btnTest.disabled = false;
|
|
} else {
|
|
status.textContent = 'Nicht abonniert';
|
|
status.className = 'status-value status-unsubscribed';
|
|
btnSubscribe.disabled = false;
|
|
btnUnsubscribe.disabled = true;
|
|
btnTest.disabled = true;
|
|
}
|
|
}
|
|
|
|
// Initialize WebPush
|
|
window.initWebPush = async function() {
|
|
try {
|
|
log('Initialisiere WebPush Manager...', 'info');
|
|
|
|
// Use the global WebPushManager instance from main.js
|
|
if (!window.webPushManager) {
|
|
throw new Error('WebPushManager nicht verfügbar. Bitte Seite neu laden.');
|
|
}
|
|
|
|
webPush = window.webPushManager;
|
|
|
|
const isSubscribed = await webPush.init();
|
|
updateUI(isSubscribed);
|
|
|
|
document.getElementById('btn-init').disabled = true;
|
|
log('✓ WebPush Manager erfolgreich initialisiert', 'success');
|
|
log(`Browser-Support: Service Worker ✓, Push API ✓`, 'success');
|
|
|
|
} catch (error) {
|
|
log(`✗ Fehler: ${error.message}`, 'error');
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
// Subscribe
|
|
window.subscribe = async function() {
|
|
if (!webPush) {
|
|
log('✗ Bitte zuerst initialisieren', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
log('Fordere Berechtigung an...', 'info');
|
|
const subscription = await webPush.subscribe();
|
|
log('✓ Erfolgreich abonniert!', 'success');
|
|
log(`Endpoint: ${subscription.endpoint.substring(0, 50)}...`, 'info');
|
|
} catch (error) {
|
|
log(`✗ Abonnement fehlgeschlagen: ${error.message}`, 'error');
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
// Unsubscribe
|
|
window.unsubscribe = async function() {
|
|
if (!webPush) {
|
|
log('✗ Bitte zuerst initialisieren', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
log('Deabonniere...', 'info');
|
|
const success = await webPush.unsubscribe();
|
|
if (success) {
|
|
log('✓ Erfolgreich deabonniert!', 'success');
|
|
}
|
|
} catch (error) {
|
|
log(`✗ Deabonnieren fehlgeschlagen: ${error.message}`, 'error');
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
// Send Test Notification
|
|
window.sendTestNotification = async function() {
|
|
if (!webPush) {
|
|
log('✗ Bitte zuerst initialisieren', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
log('Sende Test-Benachrichtigung...', 'info');
|
|
const result = await webPush.sendTestNotification(
|
|
'Test Benachrichtigung',
|
|
'Dies ist eine Test-Benachrichtigung vom WebPush Demo! 🎉'
|
|
);
|
|
log('✓ Test-Benachrichtigung gesendet!', 'success');
|
|
log(`Response: ${JSON.stringify(result)}`, 'info');
|
|
} catch (error) {
|
|
log(`✗ Senden fehlgeschlagen: ${error.message}`, 'error');
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
// Check browser support on load
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// First check: Browser capabilities (synchronous)
|
|
const hasServiceWorker = 'serviceWorker' in navigator;
|
|
const hasPushManager = 'PushManager' in window;
|
|
const hasNotification = 'Notification' in window;
|
|
|
|
console.log('=== WebPush Browser Check ===');
|
|
console.log('Service Worker support:', hasServiceWorker);
|
|
console.log('Push Manager support:', hasPushManager);
|
|
console.log('Notification support:', hasNotification);
|
|
console.log('HTTPS:', location.protocol === 'https:');
|
|
console.log('Browser:', navigator.userAgent.split(' ').slice(-2).join(' '));
|
|
|
|
// Log browser capabilities immediately
|
|
if (!hasServiceWorker) {
|
|
log('✗ Browser unterstützt keine Service Workers', 'error');
|
|
} else {
|
|
log('✓ Service Workers verfügbar', 'success');
|
|
}
|
|
|
|
if (!hasPushManager) {
|
|
log('✗ Browser unterstützt keine Push API', 'error');
|
|
} else {
|
|
log('✓ Push API verfügbar', 'success');
|
|
}
|
|
|
|
if (!hasNotification) {
|
|
log('✗ Browser unterstützt keine Notifications', 'error');
|
|
} else {
|
|
log('✓ Notification API verfügbar', 'success');
|
|
}
|
|
|
|
// Wait for main.js to initialize
|
|
setTimeout(() => {
|
|
console.log('=== WebPushManager Check ===');
|
|
console.log('window.webPushManager:', window.webPushManager);
|
|
|
|
if (!window.webPushManager) {
|
|
log('✗ WebPushManager nicht initialisiert', 'error');
|
|
|
|
if (!hasServiceWorker || !hasPushManager) {
|
|
log('💡 Grund: Browser unterstützt nicht alle erforderlichen APIs', 'info');
|
|
} else {
|
|
log('💡 Grund: main.js hat WebPushManager nicht initialisiert', 'info');
|
|
log('Tipp: Prüfe Browser Console für Fehler in main.js', 'info');
|
|
}
|
|
|
|
document.getElementById('btn-init').disabled = true;
|
|
document.getElementById('subscription-status').textContent = 'Nicht verfügbar';
|
|
document.getElementById('subscription-status').className = 'status-value status-unsubscribed';
|
|
} else {
|
|
log('✓ WebPushManager erfolgreich geladen', 'success');
|
|
log('💡 Klicke auf "WebPush initialisieren" um zu starten', 'info');
|
|
}
|
|
}, 500); // Wait 500ms for main.js to load
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|