diff --git a/index.html b/index.html index d1d2eb5..bf94a79 100644 --- a/index.html +++ b/index.html @@ -107,13 +107,14 @@ } } - /* Font-Loading Optimierung */ + /* Font-Loading Optimierung - Subset für Latin + German */ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap; - src: url('/fonts/inter-var.woff2') format('woff2-variations'); + src: url('/fonts/inter-subset.woff2') format('woff2-variations'); + unicode-range: U+0020-007E, U+00A0-00FF, U+0100-017F, U+2013-2014, U+2018-201A, U+201C-201E, U+2026, U+20AC; } body { @@ -121,8 +122,8 @@ } - - + + diff --git a/public/fonts/inter-subset.woff2 b/public/fonts/inter-subset.woff2 new file mode 100644 index 0000000..f7e7b8a Binary files /dev/null and b/public/fonts/inter-subset.woff2 differ diff --git a/public/fonts/inter-var.woff2 b/public/fonts/inter-var.woff2 deleted file mode 100644 index 5a8d3e7..0000000 Binary files a/public/fonts/inter-var.woff2 and /dev/null differ diff --git a/scripts/pagespeed-check.js b/scripts/pagespeed-check.js new file mode 100644 index 0000000..62c55e5 --- /dev/null +++ b/scripts/pagespeed-check.js @@ -0,0 +1,185 @@ +#!/usr/bin/env node +/** + * PageSpeed Insights API Check + * Prüft die Performance einer URL mit der Google PageSpeed Insights API + */ + +import https from 'https'; + +// Disable SSL verification for local testing (network proxy issue) +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + +const API_KEY = 'AIzaSyA4qo0LfLy_ps-SyTQTQpFrFRjDnAUn_K4'; +const URL_TO_TEST = 'https://www.damjan-savic.com/'; + +function httpsGet(url) { + return new Promise((resolve, reject) => { + https.get(url, (res) => { + let data = ''; + res.on('data', chunk => data += chunk); + res.on('end', () => { + try { + resolve(JSON.parse(data)); + } catch (e) { + reject(new Error(`JSON Parse Error: ${e.message}`)); + } + }); + }).on('error', reject); + }); +} + +async function runPageSpeedTest(url, strategy = 'mobile') { + const apiUrl = `https://www.pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodeURIComponent(url)}&key=${API_KEY}&strategy=${strategy}&category=performance&category=accessibility&category=best-practices&category=seo`; + + console.log(`\n🔍 Analysiere ${url} (${strategy})...\n`); + console.log('⏳ Bitte warten, die Analyse kann 30-60 Sekunden dauern...\n'); + + try { + const data = await httpsGet(apiUrl); + + if (data.error) { + throw new Error(`API Error: ${data.error.message}`); + } + + return data; + } catch (error) { + console.error('❌ Fehler bei der API-Anfrage:', error.message); + throw error; + } +} + +function formatScore(score) { + const percentage = Math.round(score * 100); + if (percentage >= 90) return `\x1b[32m${percentage}\x1b[0m`; // Grün + if (percentage >= 50) return `\x1b[33m${percentage}\x1b[0m`; // Gelb + return `\x1b[31m${percentage}\x1b[0m`; // Rot +} + +function formatTime(ms) { + if (ms < 1000) return `${Math.round(ms)}ms`; + return `${(ms / 1000).toFixed(1)}s`; +} + +function displayResults(data, strategy) { + const lighthouse = data.lighthouseResult; + const categories = lighthouse.categories; + const audits = lighthouse.audits; + + console.log('═'.repeat(60)); + console.log(`📊 PageSpeed Insights Ergebnis (${strategy.toUpperCase()})`); + console.log('═'.repeat(60)); + + // Scores + console.log('\n📈 SCORES:'); + console.log('─'.repeat(40)); + console.log(` Performance: ${formatScore(categories.performance.score)}`); + console.log(` Barrierefreiheit: ${formatScore(categories.accessibility.score)}`); + console.log(` Best Practices: ${formatScore(categories['best-practices'].score)}`); + console.log(` SEO: ${formatScore(categories.seo.score)}`); + + // Core Web Vitals + console.log('\n⚡ CORE WEB VITALS:'); + console.log('─'.repeat(40)); + + const fcp = audits['first-contentful-paint']; + const lcp = audits['largest-contentful-paint']; + const tbt = audits['total-blocking-time']; + const cls = audits['cumulative-layout-shift']; + const si = audits['speed-index']; + + console.log(` First Contentful Paint (FCP): ${formatTime(fcp.numericValue)}`); + console.log(` Largest Contentful Paint (LCP): ${formatTime(lcp.numericValue)}`); + console.log(` Total Blocking Time (TBT): ${formatTime(tbt.numericValue)}`); + console.log(` Cumulative Layout Shift (CLS): ${cls.displayValue}`); + console.log(` Speed Index: ${formatTime(si.numericValue)}`); + + // Opportunities + const opportunities = Object.values(audits) + .filter(audit => audit.details?.type === 'opportunity' && audit.details?.overallSavingsMs > 0) + .sort((a, b) => (b.details?.overallSavingsMs || 0) - (a.details?.overallSavingsMs || 0)) + .slice(0, 5); + + if (opportunities.length > 0) { + console.log('\n💡 OPTIMIERUNGSMÖGLICHKEITEN:'); + console.log('─'.repeat(40)); + opportunities.forEach(opp => { + const savings = opp.details?.overallSavingsMs; + console.log(` • ${opp.title}`); + if (savings) { + console.log(` Potenzielle Einsparung: ${formatTime(savings)}`); + } + }); + } + + // Diagnostics + const diagnostics = Object.values(audits) + .filter(audit => audit.details?.type === 'table' && audit.score !== null && audit.score < 0.9) + .slice(0, 5); + + if (diagnostics.length > 0) { + console.log('\n🔧 DIAGNOSE:'); + console.log('─'.repeat(40)); + diagnostics.forEach(diag => { + const scoreIcon = diag.score >= 0.5 ? '⚠️' : '❌'; + console.log(` ${scoreIcon} ${diag.title}`); + }); + } + + // Passed Audits Count + const passedAudits = Object.values(audits).filter(a => a.score === 1).length; + const totalAudits = Object.values(audits).filter(a => a.score !== null).length; + + console.log('\n✅ BESTANDENE PRÜFUNGEN:'); + console.log('─'.repeat(40)); + console.log(` ${passedAudits} von ${totalAudits} Prüfungen bestanden`); + + console.log('\n' + '═'.repeat(60)); + + return { + performance: Math.round(categories.performance.score * 100), + accessibility: Math.round(categories.accessibility.score * 100), + bestPractices: Math.round(categories['best-practices'].score * 100), + seo: Math.round(categories.seo.score * 100), + lcp: lcp.numericValue, + fcp: fcp.numericValue, + tbt: tbt.numericValue, + cls: parseFloat(cls.displayValue) + }; +} + +async function main() { + console.log('\n🚀 PageSpeed Insights API Test'); + console.log('================================\n'); + console.log(`URL: ${URL_TO_TEST}`); + console.log(`API Key: ${API_KEY.substring(0, 10)}...`); + + try { + // Mobile Test + const mobileData = await runPageSpeedTest(URL_TO_TEST, 'mobile'); + const mobileResults = displayResults(mobileData, 'mobile'); + + // Desktop Test + console.log('\n\n'); + const desktopData = await runPageSpeedTest(URL_TO_TEST, 'desktop'); + const desktopResults = displayResults(desktopData, 'desktop'); + + // Summary + console.log('\n\n📋 ZUSAMMENFASSUNG:'); + console.log('═'.repeat(60)); + console.log(`\n 📱 Mobile:`); + console.log(` Performance: ${formatScore(mobileResults.performance / 100)}`); + console.log(` LCP: ${formatTime(mobileResults.lcp)}`); + console.log(` TBT: ${formatTime(mobileResults.tbt)}`); + console.log(`\n 🖥️ Desktop:`); + console.log(` Performance: ${formatScore(desktopResults.performance / 100)}`); + console.log(` LCP: ${formatTime(desktopResults.lcp)}`); + console.log(` TBT: ${formatTime(desktopResults.tbt)}`); + console.log('\n' + '═'.repeat(60)); + + } catch (error) { + console.error('\n❌ Test fehlgeschlagen:', error.message); + process.exit(1); + } +} + +main();