🚀 Add automatic deployment system

- Add auto-deploy.sh script with full CI/CD pipeline
- Add quick-deploy.sh for fast development deployments
- Add Git post-receive hook for automatic deployment on push
- Add comprehensive deployment documentation
- Add npm scripts for easy deployment management
- Include health checks, logging, and cleanup
- Support for automatic rollback on failures
This commit is contained in:
Dennis Konkol
2025-09-05 19:47:53 +00:00
parent 203a332306
commit b9b3e5308d
32 changed files with 2490 additions and 441 deletions

185
lib/useWebVitals.ts Normal file
View File

@@ -0,0 +1,185 @@
'use client';
import { useEffect } from 'react';
import { trackWebVitals, trackPerformance } from './analytics';
// Web Vitals types
interface Metric {
name: string;
value: number;
delta: number;
id: string;
}
// Simple Web Vitals implementation (since we don't want to add external dependencies)
const getCLS = (onPerfEntry: (metric: Metric) => void) => {
let clsValue = 0;
let sessionValue = 0;
let sessionEntries: PerformanceEntry[] = [];
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!(entry as any).hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
if (sessionValue && entry.startTime - lastSessionEntry.startTime < 1000 && entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += (entry as any).value;
sessionEntries.push(entry);
} else {
sessionValue = (entry as any).value;
sessionEntries = [entry];
}
if (sessionValue > clsValue) {
clsValue = sessionValue;
onPerfEntry({
name: 'CLS',
value: clsValue,
delta: clsValue,
id: `cls-${Date.now()}`,
});
}
}
}
});
observer.observe({ type: 'layout-shift', buffered: true });
};
const getFID = (onPerfEntry: (metric: Metric) => void) => {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
onPerfEntry({
name: 'FID',
value: (entry as any).processingStart - entry.startTime,
delta: (entry as any).processingStart - entry.startTime,
id: `fid-${Date.now()}`,
});
}
});
observer.observe({ type: 'first-input', buffered: true });
};
const getFCP = (onPerfEntry: (metric: Metric) => void) => {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
onPerfEntry({
name: 'FCP',
value: entry.startTime,
delta: entry.startTime,
id: `fcp-${Date.now()}`,
});
}
}
});
observer.observe({ type: 'paint', buffered: true });
};
const getLCP = (onPerfEntry: (metric: Metric) => void) => {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
onPerfEntry({
name: 'LCP',
value: lastEntry.startTime,
delta: lastEntry.startTime,
id: `lcp-${Date.now()}`,
});
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
};
const getTTFB = (onPerfEntry: (metric: Metric) => void) => {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'navigation') {
const navEntry = entry as PerformanceNavigationTiming;
onPerfEntry({
name: 'TTFB',
value: navEntry.responseStart - navEntry.fetchStart,
delta: navEntry.responseStart - navEntry.fetchStart,
id: `ttfb-${Date.now()}`,
});
}
}
});
observer.observe({ type: 'navigation', buffered: true });
};
// Custom hook for Web Vitals tracking
export const useWebVitals = () => {
useEffect(() => {
if (typeof window === 'undefined') return;
// Track Core Web Vitals
getCLS((metric) => {
trackWebVitals({
...metric,
name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB',
url: window.location.pathname,
});
});
getFID((metric) => {
trackWebVitals({
...metric,
name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB',
url: window.location.pathname,
});
});
getFCP((metric) => {
trackWebVitals({
...metric,
name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB',
url: window.location.pathname,
});
});
getLCP((metric) => {
trackWebVitals({
...metric,
name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB',
url: window.location.pathname,
});
});
getTTFB((metric) => {
trackWebVitals({
...metric,
name: metric.name as 'CLS' | 'FID' | 'FCP' | 'LCP' | 'TTFB',
url: window.location.pathname,
});
});
// Track page load performance
const handleLoad = () => {
setTimeout(() => {
trackPerformance({
name: 'page-load-complete',
value: performance.now(),
url: window.location.pathname,
timestamp: Date.now(),
userAgent: navigator.userAgent,
});
}, 0);
};
if (document.readyState === 'complete') {
handleLoad();
} else {
window.addEventListener('load', handleLoad);
}
return () => {
window.removeEventListener('load', handleLoad);
};
}, []);
};