5b0269af34
- Add Cloudflare Turnstile integration for login and registration forms - Create turnstile verification utility with server-side token validation - Add turnstile_site_key and turnstile_secret_key fields to nodes table - Implement admin panel UI for configuring Turnstile keys in settings - Update login and register API routes to verify Turnstile tokens - Expose turnstile_site_key via GET /api/node endpoint for frontend - Add admin endpoint to save and update Turnstile configuration - Create remote_likes and remote_reposts tables for federated interaction tracking - Add swarm reply metadata fields (swarm_reply_to_id, swarm_reply_to_content, swarm_reply_to_author) to posts table - Add comprehensive TURNSTILE_SETUP.md documentation with setup instructions and security notes - Create database migration 0007 with schema updates and indexes - Protects against bot registrations and automated attacks on federated nodes
52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { db, nodes } from '@/db';
|
|
import { eq } from 'drizzle-orm';
|
|
|
|
export async function verifyTurnstileToken(token: string, ip?: string): Promise<boolean> {
|
|
try {
|
|
// Get node settings to check if Turnstile is enabled
|
|
const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
|
const node = await db.query.nodes.findFirst({
|
|
where: eq(nodes.domain, domain),
|
|
});
|
|
|
|
// If no secret key is configured, skip verification (Turnstile is disabled)
|
|
if (!node?.turnstileSecretKey) {
|
|
return true;
|
|
}
|
|
|
|
// Verify the token with Cloudflare
|
|
const formData = new FormData();
|
|
formData.append('secret', node.turnstileSecretKey);
|
|
formData.append('response', token);
|
|
if (ip) {
|
|
formData.append('remoteip', ip);
|
|
}
|
|
|
|
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
|
|
const data = await response.json();
|
|
return data.success === true;
|
|
} catch (error) {
|
|
console.error('Turnstile verification error:', error);
|
|
// On error, fail closed (reject the request)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function getTurnstileSiteKey(): Promise<string | null> {
|
|
try {
|
|
const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
|
const node = await db.query.nodes.findFirst({
|
|
where: eq(nodes.domain, domain),
|
|
});
|
|
|
|
return node?.turnstileSiteKey || null;
|
|
} catch (error) {
|
|
console.error('Error fetching Turnstile site key:', error);
|
|
return null;
|
|
}
|
|
}
|