Spaces:
Paused
Paused
File size: 3,910 Bytes
bcf46c3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | import React, { useState, useEffect, useRef } from 'react';
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
import { initLocalEngine, routeInference } from '../utils/inferenceRouter';
import HCaptcha from '@hcaptcha/react-hcaptcha';
import { supabase, useAuth, GATEWAY_URL, LEMON_CHECKOUT_URL } from '../context';
import Dashboard from './Dashboard';
export default function IntegrationsSection({ user }) {
const [webhookUrl, setWebhookUrl] = useState('');
const [webhookSecret, setWebhookSecret] = useState(localStorage.getItem('opticparse_webhook_secret') || '');
const [isSaving, setIsSaving] = useState(false);
const { user: authUser } = useAuth(); // Need auth for backend
useEffect(() => {
const fetchWebhook = async () => {
try {
const res = await fetch('https://opticparse-python-sg.onrender.com/api/settings/webhook', {
headers: {
'Authorization': `Bearer ${authUser?.id}` // Simulating auth header using user ID or real token
}
});
if (res.ok) {
const data = await res.json();
if (data.webhook_url) setWebhookUrl(data.webhook_url);
}
} catch (e) {
console.error(e);
}
};
if (authUser) fetchWebhook();
}, [authUser]);
const handleSave = async () => {
setIsSaving(true);
try {
await fetch('https://opticparse-python-sg.onrender.com/api/settings/webhook', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authUser?.id}`
},
body: JSON.stringify({ url: webhookUrl })
});
localStorage.setItem('opticparse_webhook_secret', webhookSecret);
alert('Integration settings saved!');
} catch (e) {
console.error(e);
alert('Failed to save settings');
}
setIsSaving(false);
};
const testWebhook = async () => {
if (!webhookUrl) return alert('Please enter a webhook URL first.');
try {
const res = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: "Hello from OpticParse & PhishVision! Your webhook integration is working." })
});
if (res.ok) alert('Test webhook sent successfully!');
else alert(`Webhook failed with status: ${res.status}`);
} catch (e) {
alert(`Webhook error: ${e.message}`);
}
};
return (
<div className="card mt-2 animate-in">
<h2>Integrations & Webhooks</h2>
<p style={{color: 'var(--muted)', fontSize: '0.9rem', marginBottom: '1rem'}}>
Configure webhooks to automatically push scan results to Discord or Slack when a background Cron Job (Watch/Monitor) finishes.
</p>
<div style={{ marginTop: '1rem', display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
<label style={{ fontSize: '0.85rem', fontWeight: 'bold' }}>Global Webhook URL (Discord / Slack)</label>
<input
type="url"
placeholder="https://discord.com/api/webhooks/..."
value={webhookUrl}
onChange={e => setWebhookUrl(e.target.value)}
style={{ width: '100%', padding: '0.75rem', borderRadius: '4px', border: '1px solid var(--border)', background: '#1a1a24', color: 'white' }}
/>
<div style={{ display: 'flex', gap: '0.5rem', marginTop: '0.5rem' }}>
<button className="btn btn-primary" onClick={handleSave}>Save Webhook</button>
<button className="btn btn-outline" onClick={testWebhook}>Test Webhook</button>
</div>
</div>
<div style={{ marginTop: '1rem', fontSize: '0.8rem', color: 'var(--muted)' }}>
Note: When you create a new Monitor via the API Docs, this URL will automatically be injected if you use the Dashboard interface in the future.
</div>
</div>
);
}
|