Files
infohliadka/pages/api/sources/check.ts

145 lines
4.1 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from 'next'
import sqlite3 from 'sqlite3'
import path from 'path'
import { rateLimit, getRateLimitHeaders } from '../../../lib/rate-limiter'
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<CheckResponse | { error: string }>
) {
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' })
}
const dbPath = path.join(process.cwd(), 'database', 'antihoax.db')
const db = new sqlite3.Database(dbPath)
try {
const sources = await new Promise<any[]>((resolve, reject) => {
db.all(
`SELECT s.*, GROUP_CONCAT(c.name) as categories
FROM sources s
LEFT JOIN source_categories sc ON s.id = sc.source_id
LEFT JOIN categories c ON sc.category_id = c.id
WHERE s.domain = ? AND s.status = 'verified'
GROUP BY s.id`,
[domain],
(err, rows) => {
if (err) reject(err)
else resolve(rows)
}
)
})
if (sources.length === 0) {
return res.status(200).json({
is_problematic: false,
risk_level: 0,
categories: [],
message: 'Stránka nie je v našej databáze problematických zdrojov',
source_count: 0
})
}
const maxRiskLevel = Math.max(...sources.map(s => s.risk_level))
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á'
}
return res.status(200).json({
is_problematic: true,
risk_level: maxRiskLevel,
categories: uniqueCategories,
message,
source_count: sources.length
})
} catch (error) {
console.error('Database error:', error)
return res.status(500).json({ error: 'Internal server error' })
} finally {
db.close()
}
}