import type { NextApiRequest, NextApiResponse } from 'next' import { db, schema } from '../../../lib/db/connection' import { eq, and, sql } from 'drizzle-orm' import { rateLimit, getRateLimitHeaders } from '../../../lib/rate-limiter' import { cache, getCacheKey } from '../../../lib/cache' type CheckResponse = { is_problematic: boolean risk_level: number categories: string[] message: string source_count: number } function extractDomain(url: string): string { try { // Clean up the URL first let cleanUrl = url.trim() if (!cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://')) { cleanUrl = 'https://' + cleanUrl } const urlObj = new URL(cleanUrl) let domain = urlObj.hostname.toLowerCase() // Remove common prefixes domain = domain.replace(/^www\./, '') domain = domain.replace(/^m\./, '') domain = domain.replace(/^mobile\./, '') domain = domain.replace(/^amp\./, '') // Handle subdomains for known patterns - extract main domain for common TLDs if (domain.includes('.')) { const parts = domain.split('.') if (parts.length > 2) { const tld = parts[parts.length - 1] const sld = parts[parts.length - 2] // Extract main domain for common TLDs if (['com', 'org', 'net', 'sk', 'cz', 'hu', 'pl', 'eu', 'info'].includes(tld)) { domain = `${sld}.${tld}` } } } return domain } catch { return '' } } export default async function handler( req: NextApiRequest, res: NextApiResponse ) { if (req.method !== 'GET') { return res.status(405).json({ error: 'Method not allowed' }) } // Rate limiting const clientIp = req.headers['x-forwarded-for'] || req.connection?.remoteAddress || 'unknown' const rateLimitResult = rateLimit(clientIp.toString()) const headers = getRateLimitHeaders(rateLimitResult) Object.entries(headers).forEach(([key, value]) => { res.setHeader(key, value) }) if (!rateLimitResult.allowed) { return res.status(429).json({ error: 'Too many requests' }) } const { url } = req.query if (!url || typeof url !== 'string') { return res.status(400).json({ error: 'URL parameter is required' }) } const domain = extractDomain(url) if (!domain) { return res.status(400).json({ error: 'Invalid URL format' }) } // Check cache first const cacheKey = getCacheKey('domain_check', domain) const cachedResult = cache.get(cacheKey) if (cachedResult) { return res.status(200).json(cachedResult) } try { const sources = await db .select({ id: schema.sources.id, riskLevel: schema.sources.riskLevel, categories: sql`string_agg(${schema.categories.name}, ',')` }) .from(schema.sources) .leftJoin(schema.sourceCategories, eq(schema.sources.id, schema.sourceCategories.sourceId)) .leftJoin(schema.categories, eq(schema.sourceCategories.categoryId, schema.categories.id)) .where( and( eq(schema.sources.domain, domain), eq(schema.sources.status, 'verified') ) ) .groupBy(schema.sources.id, schema.sources.riskLevel) let result: CheckResponse if (sources.length === 0) { result = { is_problematic: false, risk_level: 0, categories: [], message: 'Stránka nie je v našej databáze problematických zdrojov', source_count: 0 } } else { const maxRiskLevel = Math.max(...sources.map(s => s.riskLevel || 0)) const allCategories = sources .map(s => s.categories) .filter(Boolean) .join(',') .split(',') .filter(Boolean) const uniqueCategories = Array.from(new Set(allCategories)) let message = '' if (maxRiskLevel >= 4) { message = 'VYSOKÉ RIZIKO: Táto stránka šíri nebezpečné obsahy' } else if (maxRiskLevel >= 3) { message = 'STREDNÉ RIZIKO: Táto stránka môže obsahovať problematické informácie' } else { message = 'NÍZKE RIZIKO: Táto stránka je označená ako problematická' } result = { is_problematic: true, risk_level: maxRiskLevel, categories: uniqueCategories, message, source_count: sources.length } } // Cache the result (5 minutes for non-problematic, 15 minutes for problematic) const cacheTtl = result.is_problematic ? 900 : 300 cache.set(cacheKey, result, cacheTtl) return res.status(200).json(result) } catch (error) { console.error('Database error:', error) return res.status(500).json({ error: 'Internal server error' }) } }