Files
vps-panel/src/app/api/insight/route.ts
T

143 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextRequest, NextResponse } from 'next/server'
import { requireAuth } from '@/lib/auth'
import lighthouse from 'lighthouse'
import * as chromeLauncher from 'chrome-launcher'
export const runtime = 'nodejs'
const OPPORTUNITY_IDS = [
'render-blocking-resources',
'unused-css-rules',
'unused-javascript',
'uses-optimized-images',
'uses-webp-images',
'uses-text-compression',
'uses-responsive-images',
'offscreen-images',
'server-response-time',
'redirects',
'efficient-animated-content',
'legacy-javascript',
'unminified-css',
'unminified-javascript',
]
const DIAGNOSTIC_IDS = [
'total-byte-weight',
'dom-size',
'bootup-time',
'mainthread-work-breakdown',
'font-display',
'third-party-summary',
'critical-request-chains',
]
function pickAudit(audit: any) {
if (!audit) return null
return {
id: audit.id,
title: audit.title,
description: audit.description,
score: audit.score,
scoreDisplayMode: audit.scoreDisplayMode,
displayValue: audit.displayValue ?? null,
numericValue: audit.numericValue ?? null,
}
}
export async function GET(request: NextRequest) {
const authErr = await requireAuth(request)
if (authErr) return authErr
const { searchParams } = new URL(request.url)
const url = searchParams.get('url')
const strategy = searchParams.get('strategy') || 'mobile'
if (!url) {
return NextResponse.json({ error: 'URL gerekli' }, { status: 400 })
}
try {
new URL(url)
} catch {
return NextResponse.json({ error: 'Geçersiz URL formatı' }, { status: 400 })
}
let chrome: chromeLauncher.LaunchedChrome | undefined
try {
chrome = await chromeLauncher.launch({
chromeFlags: ['--headless', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
})
const formFactor = strategy === 'desktop' ? 'desktop' : 'mobile'
const screenEmulation = formFactor === 'desktop'
? { mobile: false, width: 1350, height: 940, deviceScaleFactor: 1, disabled: false }
: { mobile: true, width: 375, height: 812, deviceScaleFactor: 3, disabled: false }
// PSI ile eşleşen throttling değerleri
const throttling = formFactor === 'desktop'
? { rttMs: 40, throughputKbps: 10_240, uploadThroughputKbps: 10_240, cpuSlowdownMultiplier: 1 }
: { rttMs: 150, throughputKbps: 1_638.4, uploadThroughputKbps: 750, cpuSlowdownMultiplier: 4 }
const runnerResult = await Promise.race([
lighthouse(url, {
port: chrome.port,
output: 'json',
logLevel: 'error',
formFactor,
screenEmulation,
throttling,
throttlingMethod: 'simulate',
}),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error('Lighthouse zaman aşımına uğradı')), 60_000)
),
])
if (!runnerResult) throw new Error('Lighthouse sonuç döndürmedi')
const { lhr } = runnerResult
return NextResponse.json({
id: url,
lighthouseResult: {
categories: {
performance: { score: lhr.categories?.performance?.score ?? 0 },
accessibility: { score: lhr.categories?.accessibility?.score ?? 0 },
'best-practices': { score: lhr.categories?.['best-practices']?.score ?? 0 },
seo: { score: lhr.categories?.seo?.score ?? 0 },
},
audits: {
'first-contentful-paint': pickAudit(lhr.audits?.['first-contentful-paint']),
'largest-contentful-paint': pickAudit(lhr.audits?.['largest-contentful-paint']),
'total-blocking-time': pickAudit(lhr.audits?.['total-blocking-time']),
'cumulative-layout-shift': pickAudit(lhr.audits?.['cumulative-layout-shift']),
'speed-index': pickAudit(lhr.audits?.['speed-index']),
'interactive': pickAudit(lhr.audits?.['interactive']),
},
opportunities: OPPORTUNITY_IDS
.map(id => pickAudit(lhr.audits?.[id]))
.filter((a): a is NonNullable<typeof a> => a !== null && a.score !== null && a.score < 0.9),
diagnostics: DIAGNOSTIC_IDS
.map(id => pickAudit(lhr.audits?.[id]))
.filter((a): a is NonNullable<typeof a> => a !== null),
},
})
} catch (error: any) {
console.error('Lighthouse hatası:', error)
return NextResponse.json(
{
error: error.message.includes('zaman aşımı')
? 'Analiz zaman aşımına uğradı, lütfen tekrar deneyin.'
: 'Analiz sırasında hata oluştu. Sunucuda Chrome yüklü olduğundan emin olun.',
details: error?.message || String(error)
},
{ status: 500 }
)
} finally {
await chrome?.kill()
}
}