@0x2e8/phantom-ai-crawler 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,376 @@
1
+ <!DOCTYPE html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>🎭 Phantom AI - Dashboard</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
9
+ <style>
10
+ * { font-family: 'Inter', sans-serif; }
11
+ .mono { font-family: 'JetBrains Mono', monospace; }
12
+ .glow { box-shadow: 0 0 20px rgba(139, 92, 246, 0.3); }
13
+ .status-red { background: linear-gradient(135deg, #ef4444, #dc2626); }
14
+ .status-yellow { background: linear-gradient(135deg, #f59e0b, #d97706); }
15
+ .status-green { background: linear-gradient(135deg, #10b981, #059669); }
16
+ .pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
17
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }
18
+ .hidden { display: none !important; }
19
+ </style>
20
+ </head>
21
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
22
+ <!-- Auth Check -->
23
+ <div id="auth-check" class="min-h-screen flex items-center justify-center">
24
+ <div class="text-center">
25
+ <div class="text-6xl mb-4 animate-pulse">🎭</div>
26
+ <div class="text-xl text-gray-400">Verificando autenticação...</div>
27
+ </div>
28
+ </div>
29
+
30
+ <!-- Main Dashboard -->
31
+ <div id="dashboard" class="hidden max-w-7xl mx-auto p-6">
32
+ <!-- Header -->
33
+ <header class="mb-8 flex items-center justify-between">
34
+ <div class="flex items-center gap-4">
35
+ <div class="text-4xl">🎭</div>
36
+ <div>
37
+ <h1 class="text-3xl font-bold bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">Phantom AI</h1>
38
+ <p class="text-gray-400 text-sm">Adaptive Web Crawler with MCP</p>
39
+ </div>
40
+ </div>
41
+ <div class="flex gap-4">
42
+ <div class="bg-gray-800 rounded-lg px-4 py-2 border border-gray-700">
43
+ <span class="text-xs text-gray-400">API Status</span>
44
+ <div id="api-status" class="flex items-center gap-2">
45
+ <div class="w-2 h-2 rounded-full bg-green-500"></div>
46
+ <span class="text-sm">Connected</span>
47
+ </div>
48
+ </div>
49
+ <a href="/settings.html" class="bg-gray-800 hover:bg-gray-700 border border-gray-700 rounded-lg px-4 py-2 transition">⚙️ Settings</a>
50
+ <button onclick="logout()" class="bg-red-900/50 hover:bg-red-900 border border-red-700 rounded-lg px-4 py-2 transition text-red-400">🚪 Logout</button>
51
+ </div>
52
+ </header>
53
+
54
+ <!-- Add Target -->
55
+ <section class="bg-gray-800 rounded-xl p-6 border border-gray-700 mb-6">
56
+ <h2 class="text-lg font-semibold mb-4">➕ Add Target</h2>
57
+ <div class="flex gap-4">
58
+ <input type="url" id="target-url" placeholder="https://example.com" class="flex-1 bg-gray-900 border border-gray-600 rounded-lg px-4 py-2 focus:border-purple-500 focus:outline-none">
59
+ <select id="target-type" class="bg-gray-900 border border-gray-600 rounded-lg px-4 py-2 focus:border-purple-500 focus:outline-none">
60
+ <option value="web">Web</option>
61
+ <option value="api">API</option>
62
+ </select>
63
+ <button onclick="addTarget()" class="bg-purple-600 hover:bg-purple-700 px-6 py-2 rounded-lg font-medium transition">Add</button>
64
+ </div>
65
+ </section>
66
+
67
+ <!-- Targets Grid -->
68
+ <div id="targets-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
69
+ <!-- Targets will be loaded here -->
70
+ </div>
71
+
72
+ <!-- MCP Analysis Log -->
73
+ <section class="mt-8 bg-gray-800 rounded-xl p-6 border border-gray-700">
74
+ <h2 class="text-lg font-semibold mb-4">🧠 MCP Analysis Log</h2>
75
+ <div id="mcp-logs" class="space-y-2 max-h-64 overflow-y-auto">
76
+ <div class="text-gray-500 italic">Select a target and click "MCP" to see analysis...</div>
77
+ </div>
78
+ </section>
79
+ </div>
80
+
81
+ <!-- Auth Modal for Target -->
82
+ <div id="auth-modal" class="hidden fixed inset-0 bg-black/80 flex items-center justify-center z-50">
83
+ <div class="bg-gray-800 rounded-xl p-6 border border-gray-700 max-w-md w-full mx-4">
84
+ <h3 class="text-xl font-bold mb-4">🔐 Authenticate</h3>
85
+ <input type="hidden" id="auth-target-id">
86
+ <div class="space-y-4">
87
+ <div>
88
+ <label class="block text-sm text-gray-400 mb-1">Username</label>
89
+ <input type="text" id="auth-username" class="w-full bg-gray-900 border border-gray-600 rounded-lg px-3 py-2 focus:border-purple-500 focus:outline-none">
90
+ </div>
91
+ <div>
92
+ <label class="block text-sm text-gray-400 mb-1">Password</label>
93
+ <input type="password" id="auth-password" class="w-full bg-gray-900 border border-gray-600 rounded-lg px-3 py-2 focus:border-purple-500 focus:outline-none">
94
+ </div>
95
+ <div>
96
+ <label class="block text-sm text-gray-400 mb-1">Auth Endpoint (optional)</label>
97
+ <input type="text" id="auth-endpoint" placeholder="/login" class="w-full bg-gray-900 border border-gray-600 rounded-lg px-3 py-2 focus:border-purple-500 focus:outline-none">
98
+ </div>
99
+ <div class="flex gap-3">
100
+ <button onclick="submitAuth()" class="flex-1 bg-blue-600 hover:bg-blue-700 py-2 rounded-lg font-medium transition">Authenticate</button>
101
+ <button onclick="closeAuthModal()" class="px-4 bg-gray-700 hover:bg-gray-600 py-2 rounded-lg transition">Cancel</button>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+
107
+ <script>
108
+ const API_URL = 'http://localhost:4000';
109
+ let activeCrawls = new Set();
110
+
111
+ // Verificar autenticação ao carregar
112
+ async function checkAuth() {
113
+ const token = localStorage.getItem('phantom_token');
114
+ if (!token) {
115
+ window.location.href = '/';
116
+ return;
117
+ }
118
+
119
+ try {
120
+ const res = await fetch(`${API_URL}/api/targets`, {
121
+ headers: { 'Authorization': `Bearer ${token}` }
122
+ });
123
+
124
+ if (res.status === 401) {
125
+ localStorage.removeItem('phantom_token');
126
+ window.location.href = '/';
127
+ return;
128
+ }
129
+
130
+ document.getElementById('auth-check').classList.add('hidden');
131
+ document.getElementById('dashboard').classList.remove('hidden');
132
+ fetchTargets();
133
+ } catch (error) {
134
+ document.getElementById('auth-check').innerHTML = `
135
+ <div class="text-red-400 text-xl">❌ API Error</div>
136
+ <div class="text-gray-500 text-sm mt-2">${error.message}</div>
137
+ `;
138
+ }
139
+ }
140
+
141
+ function getAuthHeaders() {
142
+ return { 'Authorization': `Bearer ${localStorage.getItem('phantom_token')}` };
143
+ }
144
+
145
+ async function fetchTargets() {
146
+ try {
147
+ const res = await fetch(`${API_URL}/api/targets`, {
148
+ headers: getAuthHeaders()
149
+ });
150
+ const targets = await res.json();
151
+ renderTargets(targets);
152
+ } catch (err) {
153
+ console.error('Failed to fetch targets:', err);
154
+ }
155
+ }
156
+
157
+ function renderTargets(targets) {
158
+ const grid = document.getElementById('targets-grid');
159
+ if (!targets.length) {
160
+ grid.innerHTML = '<div class="col-span-full text-center text-gray-500 py-12">No targets yet. Add one above!</div>';
161
+ return;
162
+ }
163
+
164
+ grid.innerHTML = targets.map(t => {
165
+ const statusColor = t.greenLightStatus === 'GREEN' ? 'status-green' :
166
+ t.greenLightStatus === 'YELLOW' ? 'status-yellow' : 'status-red';
167
+ const isCrawling = activeCrawls.has(t.id);
168
+ const canAuth = t.greenLightStatus === 'GREEN' && !t.isAuthenticated;
169
+
170
+ return `
171
+ <div class="bg-gray-800 rounded-xl p-5 border border-gray-700 hover:border-gray-600 transition">
172
+ <div class="flex items-center justify-between mb-3">
173
+ <div class="flex items-center gap-2">
174
+ <span class="text-lg">${t.type === 'api' ? '🔌' : '🌐'}</span>
175
+ <span class="font-medium truncate">${t.url.replace(/^https?:\/\//, '')}</span>
176
+ </div>
177
+ <div class="w-3 h-3 rounded-full ${statusColor}"></div>
178
+ </div>
179
+
180
+ <div class="mb-4">
181
+ <div class="flex justify-between text-sm mb-1">
182
+ <span class="text-gray-400">Trust Score</span>
183
+ <span class="font-bold ${t.trustScore >= 80 ? 'text-green-400' : t.trustScore >= 50 ? 'text-yellow-400' : 'text-red-400'}">${t.trustScore}%</span>
184
+ </div>
185
+ <div class="w-full bg-gray-700 rounded-full h-2">
186
+ <div class="${statusColor} h-2 rounded-full transition-all" style="width: ${t.trustScore}%"></div>
187
+ </div>
188
+ </div>
189
+
190
+ ${t.isAuthenticated ? `
191
+ <div class="mb-4 p-3 bg-green-900/30 border border-green-700 rounded-lg">
192
+ <div class="flex items-center gap-2 text-green-400">
193
+ <span>🔐</span>
194
+ <span class="font-medium">Session Authenticated</span>
195
+ </div>
196
+ ${t.authUsername ? `<div class="text-green-300 text-xs mt-1">User: ${t.authUsername}</div>` : ''}
197
+ </div>
198
+ ` : ''}
199
+
200
+ <div class="flex flex-wrap gap-2">
201
+ <button onclick="startCrawl('${t.id}', '${t.url}')" ${isCrawling ? 'disabled' : ''}
202
+ class="flex-1 min-w-[120px] ${isCrawling ? 'bg-gray-700 cursor-not-allowed' : 'bg-green-600 hover:bg-green-700'} text-white py-2 rounded-lg font-medium transition flex items-center justify-center gap-2">
203
+ ${isCrawling ? '⏳ Crawling...' : '▶️ Start Crawl'}
204
+ </button>
205
+
206
+ <button onclick="analyzeTarget('${t.id}')"
207
+ class="flex-1 min-w-[120px] bg-purple-600 hover:bg-purple-700 text-white py-2 rounded-lg font-medium transition">
208
+ 🧠 MCP
209
+ </button>
210
+
211
+ ${canAuth ? `
212
+ <button onclick="openAuthModal('${t.id}')"
213
+ class="flex-1 min-w-[120px] bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-lg font-medium transition flex items-center justify-center gap-2">
214
+ 🔐 Auth
215
+ </button>
216
+ ` : ''}
217
+
218
+ <button onclick="showDetails('${t.id}')" class="px-4 bg-gray-700 hover:bg-gray-600 rounded-lg transition">👁️</button>
219
+ </div>
220
+
221
+ ${isCrawling ? `
222
+ <div class="mt-3 p-3 bg-gray-900 rounded-lg mono text-xs">
223
+ <div class="flex items-center gap-2 text-green-400 mb-2">
224
+ <div class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div>
225
+ Crawling via Caido proxy...
226
+ </div>
227
+ </div>
228
+ ` : ''}
229
+ </div>
230
+ `;
231
+ }).join('');
232
+ }
233
+
234
+ async function addTarget() {
235
+ const url = document.getElementById('target-url').value;
236
+ const type = document.getElementById('target-type').value;
237
+ if (!url) return;
238
+ try {
239
+ await fetch(`${API_URL}/api/targets`, {
240
+ method: 'POST',
241
+ headers: { ...getAuthHeaders(), 'Content-Type': 'application/json' },
242
+ body: JSON.stringify({ url, type })
243
+ });
244
+ document.getElementById('target-url').value = '';
245
+ fetchTargets();
246
+ } catch (err) {
247
+ alert('Failed to add target');
248
+ }
249
+ }
250
+
251
+ async function startCrawl(targetId, url) {
252
+ if (activeCrawls.has(targetId)) return;
253
+ activeCrawls.add(targetId);
254
+ fetchTargets();
255
+
256
+ try {
257
+ await fetch(`${API_URL}/api/crawl/${targetId}`, {
258
+ method: 'POST',
259
+ headers: { ...getAuthHeaders(), 'Content-Type': 'application/json' },
260
+ body: JSON.stringify({ url })
261
+ });
262
+
263
+ setTimeout(() => {
264
+ activeCrawls.delete(targetId);
265
+ fetchTargets();
266
+ }, 30000);
267
+ } catch (err) {
268
+ activeCrawls.delete(targetId);
269
+ fetchTargets();
270
+ }
271
+ }
272
+
273
+ function openAuthModal(targetId) {
274
+ document.getElementById('auth-target-id').value = targetId;
275
+ document.getElementById('auth-modal').classList.remove('hidden');
276
+ }
277
+
278
+ function closeAuthModal() {
279
+ document.getElementById('auth-modal').classList.add('hidden');
280
+ }
281
+
282
+ async function submitAuth() {
283
+ const targetId = document.getElementById('auth-target-id').value;
284
+ const username = document.getElementById('auth-username').value;
285
+ const password = document.getElementById('auth-password').value;
286
+ const endpoint = document.getElementById('auth-endpoint').value;
287
+
288
+ if (!username || !password) {
289
+ alert('Username and password required');
290
+ return;
291
+ }
292
+
293
+ closeAuthModal();
294
+
295
+ try {
296
+ await fetch(`${API_URL}/api/auth/${targetId}`, {
297
+ method: 'POST',
298
+ headers: { ...getAuthHeaders(), 'Content-Type': 'application/json' },
299
+ body: JSON.stringify({ username, password, endpoint })
300
+ });
301
+
302
+ addMcpLog(targetId, {
303
+ title: 'Authentication Initiated',
304
+ description: `Authenticating as ${username}...`,
305
+ eventType: 'auth'
306
+ });
307
+
308
+ setTimeout(fetchTargets, 2000);
309
+ } catch (err) {
310
+ console.error('Auth failed:', err);
311
+ }
312
+ }
313
+
314
+ async function analyzeTarget(targetId) {
315
+ try {
316
+ const res = await fetch(`${API_URL}/api/mcp/analyze/${targetId}`, {
317
+ method: 'POST',
318
+ headers: getAuthHeaders()
319
+ });
320
+ const data = await res.json();
321
+ addMcpLog(targetId, data);
322
+ } catch (err) {
323
+ console.error('MCP analysis failed:', err);
324
+ }
325
+ }
326
+
327
+ function addMcpLog(targetId, data) {
328
+ const container = document.getElementById('mcp-logs');
329
+ if (container.children[0]?.classList.contains('italic')) {
330
+ container.innerHTML = '';
331
+ }
332
+
333
+ const timestamp = new Date().toLocaleTimeString();
334
+ const entry = document.createElement('div');
335
+ entry.className = 'bg-gray-900 rounded-lg p-3 border-l-4 border-purple-500';
336
+ entry.innerHTML = `
337
+ <div class="flex items-center justify-between mb-2">
338
+ <span class="text-purple-400 font-semibold">🧠 MCP Analysis</span>
339
+ <span class="text-gray-500 text-xs">${timestamp}</span>
340
+ </div>
341
+ <div class="text-gray-300 mb-2">Target: ${data.target || targetId}</div>
342
+ ${data.cdn ? `<div class="text-gray-400 text-xs mb-1">CDN: ${data.cdn}</div>` : ''}
343
+ ${data.securityLevel ? `<div class="text-gray-400 text-xs mb-1">Security: ${data.securityLevel}</div>` : ''}
344
+ ${data.recommendations ? `
345
+ <div class="mt-2 text-xs">
346
+ <div class="text-gray-500 mb-1">Recommendations:</div>
347
+ ${data.recommendations.map(r => `<div class="text-green-400">• ${r}</div>`).join('')}
348
+ </div>
349
+ ` : ''}
350
+ `;
351
+ container.insertBefore(entry, container.firstChild);
352
+ }
353
+
354
+ async function showDetails(targetId) {
355
+ try {
356
+ const res = await fetch(`${API_URL}/api/targets/${targetId}`, {
357
+ headers: getAuthHeaders()
358
+ });
359
+ const data = await res.json();
360
+ alert(`Target: ${data.url}\nTrust: ${data.trustScore}%\nAuth: ${data.isAuthenticated ? '✅ ' + data.authUsername : '❌'}\nEvents: ${data._count?.learningEvents || 0}`);
361
+ } catch (err) {
362
+ console.error('Failed to fetch details:', err);
363
+ }
364
+ }
365
+
366
+ function logout() {
367
+ localStorage.removeItem('phantom_token');
368
+ window.location.href = '/';
369
+ }
370
+
371
+ // Iniciar
372
+ checkAuth();
373
+ </script>
374
+ </body>
375
+ </html>
376
+