diff --git a/pages/api/domains/risky.ts b/pages/api/domains/risky.ts new file mode 100644 index 0000000..0d6efd1 --- /dev/null +++ b/pages/api/domains/risky.ts @@ -0,0 +1,68 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import sqlite3 from 'sqlite3' +import path from 'path' + +interface RiskyDomain { + domain: string + source_count: number + avg_risk_level: number + max_risk_level: number + categories: string[] +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== 'GET') { + return res.status(405).json({ error: 'Method not allowed' }) + } + + const { limit = '20' } = req.query + + const dbPath = path.join(process.cwd(), 'database', 'antihoax.db') + const db = new sqlite3.Database(dbPath) + + try { + const riskyDomains = await new Promise((resolve, reject) => { + db.all( + `SELECT + s.domain, + COUNT(*) as source_count, + AVG(s.risk_level) as avg_risk_level, + MAX(s.risk_level) as max_risk_level, + GROUP_CONCAT(DISTINCT 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.status = 'verified' + GROUP BY s.domain + HAVING AVG(s.risk_level) >= 3 + ORDER BY avg_risk_level DESC, source_count DESC + LIMIT ?`, + [parseInt(limit as string)], + (err, rows: any[]) => { + if (err) reject(err) + else { + const domains = rows.map(row => ({ + domain: row.domain, + source_count: row.source_count, + avg_risk_level: Math.round(row.avg_risk_level * 10) / 10, + max_risk_level: row.max_risk_level, + categories: row.categories ? row.categories.split(',') : [] + })) + resolve(domains) + } + } + ) + }) + + return res.status(200).json(riskyDomains) + + } catch (error) { + console.error('Database error:', error) + return res.status(500).json({ error: 'Internal server error' }) + } finally { + db.close() + } +} \ No newline at end of file diff --git a/pages/api/stats.ts b/pages/api/stats.ts new file mode 100644 index 0000000..a266085 --- /dev/null +++ b/pages/api/stats.ts @@ -0,0 +1,117 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import sqlite3 from 'sqlite3' +import path from 'path' + +interface PublicStats { + total_sources: number + high_risk_sources: number + categories_breakdown: { [key: string]: number } + recent_additions: number + top_domains: { domain: string; count: number; risk_level: number }[] +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + if (req.method !== 'GET') { + return res.status(405).json({ error: 'Method not allowed' }) + } + + const dbPath = path.join(process.cwd(), 'database', 'antihoax.db') + const db = new sqlite3.Database(dbPath) + + try { + // Get basic counts + const totalSources = await new Promise((resolve, reject) => { + db.get( + "SELECT COUNT(*) as count FROM sources WHERE status = 'verified'", + (err, row: any) => { + if (err) reject(err) + else resolve(row.count) + } + ) + }) + + const highRiskSources = await new Promise((resolve, reject) => { + db.get( + "SELECT COUNT(*) as count FROM sources WHERE status = 'verified' AND risk_level >= 4", + (err, row: any) => { + if (err) reject(err) + else resolve(row.count) + } + ) + }) + + const recentAdditions = await new Promise((resolve, reject) => { + db.get( + "SELECT COUNT(*) as count FROM sources WHERE created_at > datetime('now', '-30 days')", + (err, row: any) => { + if (err) reject(err) + else resolve(row.count) + } + ) + }) + + // Get categories breakdown + const categoriesBreakdown = await new Promise<{ [key: string]: number }>((resolve, reject) => { + db.all( + `SELECT c.name, COUNT(*) as count + FROM categories c + JOIN source_categories sc ON c.id = sc.category_id + JOIN sources s ON sc.source_id = s.id + WHERE s.status = 'verified' + GROUP BY c.id, c.name`, + (err, rows: any[]) => { + if (err) reject(err) + else { + const breakdown: { [key: string]: number } = {} + rows.forEach(row => { + breakdown[row.name] = row.count + }) + resolve(breakdown) + } + } + ) + }) + + // Get top risky domains + const topDomains = await new Promise<{ domain: string; count: number; risk_level: number }[]>((resolve, reject) => { + db.all( + `SELECT domain, COUNT(*) as count, AVG(risk_level) as avg_risk + FROM sources + WHERE status = 'verified' + GROUP BY domain + ORDER BY avg_risk DESC, count DESC + LIMIT 10`, + (err, rows: any[]) => { + if (err) reject(err) + else { + const domains = rows.map(row => ({ + domain: row.domain, + count: row.count, + risk_level: Math.round(row.avg_risk * 10) / 10 + })) + resolve(domains) + } + } + ) + }) + + const stats: PublicStats = { + total_sources: totalSources, + high_risk_sources: highRiskSources, + categories_breakdown: categoriesBreakdown, + recent_additions: recentAdditions, + top_domains: topDomains + } + + return res.status(200).json(stats) + + } catch (error) { + console.error('Database error:', error) + return res.status(500).json({ error: 'Internal server error' }) + } finally { + db.close() + } +} \ No newline at end of file