Germinal commited on
Commit
f7b8e4c
·
verified ·
1 Parent(s): f34ac80

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +524 -332
index.html CHANGED
@@ -3,400 +3,592 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Pesquisa Forense de Pessoas</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
9
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
10
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
11
  <style>
12
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
 
13
  body {
14
  font-family: 'Inter', sans-serif;
 
15
  }
16
- .loading-spinner {
17
- border: 4px solid rgba(255, 255, 255, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  border-radius: 50%;
19
- border-top: 4px solid #3b82f6;
 
 
 
 
 
20
  width: 40px;
21
  height: 40px;
 
 
 
22
  animation: spin 1s linear infinite;
23
  }
 
24
  @keyframes spin {
25
- 0% { transform: rotate(0deg); }
26
- 100% { transform: rotate(360deg); }
27
  }
28
- .card-hover {
29
- transition: all 0.3s ease;
 
30
  }
31
- .card-hover:hover {
32
- transform: translateY(-5px);
33
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
34
  }
35
  </style>
36
  </head>
37
- <body class="bg-slate-900 text-slate-200 min-h-screen p-4 font-sans">
38
- <div id="root"></div>
39
-
40
- <script type="text/babel">
41
- const PLUGIN_ID = "forensic-person-search-v1";
42
- let authToken = null;
43
-
44
- // Componentes React
45
- const AuthStatus = () => {
46
- const [status, setStatus] = React.useState("Aguardando Autenticação...");
47
- const [isAuthenticated, setIsAuthenticated] = React.useState(false);
48
-
49
- React.useEffect(() => {
50
- window.addEventListener('message', (event) => {
51
- if (event.data.type === 'AUTH_TOKEN') {
52
- authToken = event.data.token;
53
- setStatus("Conectado e Autenticado ✅");
54
- setIsAuthenticated(true);
55
- }
56
- });
57
- }, []);
58
-
59
- return (
60
- <div className="flex justify-between items-center mb-6">
61
- <div className="flex items-center space-x-2">
62
- <div className="w-3 h-3 rounded-full bg-blue-500 animate-pulse"></div>
63
- <span className="text-xs text-slate-400">{status}</span>
64
- </div>
65
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener noreferrer" className="text-xs text-blue-400 hover:text-blue-300 transition-colors">
66
- Built with anycoder
67
- </a>
68
  </div>
69
- );
70
- };
71
-
72
- const SearchForm = ({ onSearch, isLoading }) => {
73
- const [cpf, setCpf] = React.useState("");
74
- const [socialProfile, setSocialProfile] = React.useState("");
75
- const [years, setYears] = React.useState(5);
 
76
 
77
- const handleSubmit = (e) => {
78
- e.preventDefault();
79
- onSearch({ cpf, socialProfile, years });
80
- };
 
 
 
81
 
82
- return (
83
- <form onSubmit={handleSubmit} className="bg-slate-800 p-6 rounded-lg shadow-lg mb-8">
84
- <h2 className="text-xl font-semibold mb-6 text-white">Pesquisa Forense</h2>
85
-
86
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
87
- <div>
88
- <label className="block text-sm font-medium text-slate-300 mb-1">CPF</label>
89
- <input
90
- type="text"
91
- value={cpf}
92
- onChange={(e) => setCpf(e.target.value)}
93
- placeholder="000.000.000-00"
94
- className="w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-md text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
95
- required
96
- />
 
 
 
 
 
 
 
 
 
 
97
  </div>
98
-
99
- <div>
100
- <label className="block text-sm font-medium text-slate-300 mb-1">Perfil Social</label>
101
- <input
102
- type="url"
103
- value={socialProfile}
104
- onChange={(e) => setSocialProfile(e.target.value)}
105
- placeholder="https://rede-social.com/perfil"
106
- className="w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-md text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
107
- required
108
- />
109
  </div>
 
 
 
110
 
111
- <div>
112
- <label className="block text-sm font-medium text-slate-300 mb-1">Anos de História</label>
113
- <select
114
- value={years}
115
- onChange={(e) => setYears(parseInt(e.target.value))}
116
- className="w-full px-3 py-2 bg-slate-700 border border-slate-600 rounded-md text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
117
- >
118
- {[1, 2, 3, 4, 5].map((year) => (
119
- <option key={year} value={year}>{year} ano{year > 1 ? 's' : ''}</option>
120
- ))}
121
- </select>
 
 
 
 
 
 
 
 
 
 
 
 
122
  </div>
123
  </div>
124
 
125
- <button
126
- type="submit"
127
- disabled={isLoading}
128
- className={`w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-md transition-colors flex items-center justify-center ${isLoading ? 'opacity-70 cursor-not-allowed' : ''}`}
129
- >
130
- {isLoading ? (
131
- <>
132
- <div className="loading-spinner mr-2"></div>
133
- Processando...
134
- </>
135
- ) : (
136
- 'Iniciar Pesquisa Forense'
137
- )}
138
- </button>
139
- </form>
140
- );
141
- };
142
-
143
- const ResultsDashboard = ({ results, error }) => {
144
- if (error) {
145
- return (
146
- <div className="bg-red-900/20 border border-red-500 p-4 rounded-lg text-red-300">
147
- <h3 className="font-semibold mb-2">Erro na Pesquisa</h3>
148
- <p>{error}</p>
149
  </div>
150
- );
151
- }
152
 
153
- if (!results) {
154
- return (
155
- <div className="text-center py-12">
156
- <p className="text-slate-400">Insira os dados e clique em "Iniciar Pesquisa Forense" para começar</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  </div>
158
- );
159
- }
160
 
161
- return (
162
- <div className="space-y-6">
163
- {/* Header com informações básicas */}
164
- <div className="bg-slate-800 p-6 rounded-lg shadow-lg">
165
- <div className="flex flex-col md:flex-row justify-between items-start md:items-center">
166
  <div>
167
- <h2 className="text-2xl font-bold text-white mb-2">{results.personalInfo.name || "Desconhecido"}</h2>
168
- <p className="text-slate-300">CPF: {results.personalInfo.cpf || "Não fornecido"}</p>
169
- <p className="text-slate-400 text-sm">Perfil analisado: {results.socialProfile}</p>
 
 
 
 
 
170
  </div>
171
- <div className="mt-4 md:mt-0">
172
- <span className={`px-3 py-1 rounded-full text-xs font-medium ${results.riskLevel === 'ALTO' ? 'bg-red-500' : results.riskLevel === 'MÉDIO' ? 'bg-yellow-500' : 'bg-green-500'}`}>
173
- Nível de Risco: {results.riskLevel || "Não avaliado"}
174
- </span>
175
  </div>
176
  </div>
177
  </div>
 
178
 
179
- {/* Cards de informações */}
180
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
181
- {/* Card de Atividade */}
182
- <div className="bg-slate-800 p-6 rounded-lg shadow-lg card-hover">
183
- <h3 className="text-lg font-semibold text-white mb-4 flex items-center">
184
- <svg className="w-5 h-5 mr-2 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
185
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
186
- </svg>
187
- Atividade Recente
188
- </h3>
189
- <div className="space-y-3">
190
- <div className="flex justify-between text-sm">
191
- <span className="text-slate-400">Posts nos últimos {results.years} anos</span>
192
- <span className="font-medium text-white">{results.activity.postCount || 0}</span>
193
- </div>
194
- <div className="flex justify-between text-sm">
195
- <span className="text-slate-400">Comentários</span>
196
- <span className="font-medium text-white">{results.activity.commentCount || 0}</span>
197
- </div>
198
- <div className="flex justify-between text-sm">
199
- <span className="text-slate-400">Curtidas</span>
200
- <span className="font-medium text-white">{results.activity.likeCount || 0}</span>
201
  </div>
202
  </div>
203
  </div>
204
 
205
- {/* Card de Análise de Sentimento */}
206
- <div className="bg-slate-800 p-6 rounded-lg shadow-lg card-hover">
207
- <h3 className="text-lg font-semibold text-white mb-4 flex items-center">
208
- <svg className="w-5 h-5 mr-2 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
209
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
210
- </svg>
211
- Análise de Sentimento
212
- </h3>
213
- <div className="space-y-3">
214
- <div className="flex items-center justify-between">
215
- <span className="text-slate-400 text-sm">Positivo</span>
216
- <div className="w-2/3 bg-slate-700 rounded-full h-2.5">
217
- <div className="bg-green-500 h-2.5 rounded-full" style={{ width: `${results.sentiment.positive || 0}%` }}></div>
218
- </div>
219
- <span className="text-white text-sm ml-2">{results.sentiment.positive || 0}%</span>
220
- </div>
221
- <div className="flex items-center justify-between">
222
- <span className="text-slate-400 text-sm">Neutro</span>
223
- <div className="w-2/3 bg-slate-700 rounded-full h-2.5">
224
- <div className="bg-blue-500 h-2.5 rounded-full" style={{ width: `${results.sentiment.neutral || 0}%` }}></div>
225
  </div>
226
- <span className="text-white text-sm ml-2">{results.sentiment.neutral || 0}%</span>
227
- </div>
228
- <div className="flex items-center justify-between">
229
- <span className="text-slate-400 text-sm">Negativo</span>
230
- <div className="w-2/3 bg-slate-700 rounded-full h-2.5">
231
- <div className="bg-red-500 h-2.5 rounded-full" style={{ width: `${results.sentiment.negative || 0}%` }}></div>
232
  </div>
233
- <span className="text-white text-sm ml-2">{results.sentiment.negative || 0}%</span>
234
  </div>
235
  </div>
236
  </div>
 
237
 
238
- {/* Card de Conexões */}
239
- <div className="bg-slate-800 p-6 rounded-lg shadow-lg card-hover">
240
- <h3 className="text-lg font-semibold text-white mb-4 flex items-center">
241
- <svg className="w-5 h-5 mr-2 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
242
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
243
- </svg>
244
- Conexões
245
- </h3>
246
- <div className="space-y-3">
247
- <div className="flex justify-between text-sm">
248
- <span className="text-slate-400">Amigos/Seguidores</span>
249
- <span className="font-medium text-white">{results.connections.friends || 0}</span>
250
- </div>
251
- <div className="flex justify-between text-sm">
252
- <span className="text-slate-400">Grupos</span>
253
- <span className="font-medium text-white">{results.connections.groups || 0}</span>
254
- </div>
255
- <div className="flex justify-between text-sm">
256
- <span className="text-slate-400">Páginas Seguidas</span>
257
- <span className="font-medium text-white">{results.connections.pages || 0}</span>
258
  </div>
259
  </div>
260
  </div>
261
- </div>
262
 
263
- {/* Tabela de Posts Importantes */}
264
- <div className="bg-slate-800 p-6 rounded-lg shadow-lg">
265
- <h3 className="text-lg font-semibold text-white mb-4 flex items-center">
266
- <svg className="w-5 h-5 mr-2 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
267
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3h3m-3 4h3m-3 4h3m-3 4h3" />
268
- </svg>
269
- Posts Importantes (Últimos {results.years} anos)
270
- </h3>
271
- <div className="overflow-x-auto">
272
- <table className="w-full text-sm">
273
- <thead className="text-slate-400 border-b border-slate-700">
274
- <tr>
275
- <th className="px-4 py-2 text-left">Data</th>
276
- <th className="px-4 py-2 text-left">Conteúdo</th>
277
- <th className="px-4 py-2 text-left">Sentimento</th>
278
- <th className="px-4 py-2 text-left">Engajamento</th>
279
- </tr>
280
- </thead>
281
- <tbody>
282
- {results.importantPosts && results.importantPosts.length > 0 ? (
283
- results.importantPosts.map((post, index) => (
284
- <tr key={index} className="border-b border-slate-700 hover:bg-slate-700/50 transition-colors">
285
- <td className="px-4 py-3 text-slate-300">{new Date(post.date).toLocaleDateString()}</td>
286
- <td className="px-4 py-3 text-white max-w-xs truncate">{post.content}</td>
287
- <td className="px-4 py-3">
288
- <span className={`px-2 py-1 rounded-full text-xs font-medium ${post.sentiment === 'POSITIVO' ? 'bg-green-500' : post.sentiment === 'NEGATIVO' ? 'bg-red-500' : 'bg-blue-500'}`}>
289
- {post.sentiment}
290
- </span>
291
- </td>
292
- <td className="px-4 py-3 text-white">{post.engagement}</td>
293
- </tr>
294
- ))
295
- ) : (
296
- <tr>
297
- <td colSpan="4" className="px-4 py-6 text-center text-slate-400">Nenhum post importante encontrado</td>
298
- </tr>
299
- )}
300
- </tbody>
301
- </table>
302
  </div>
303
  </div>
 
 
304
 
305
- {/* Resumo da IA */}
306
- <div className="bg-slate-800 p-6 rounded-lg shadow-lg">
307
- <h3 className="text-lg font-semibold text-white mb-4 flex items-center">
308
- <svg className="w-5 h-5 mr-2 text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
309
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
310
- </svg>
311
- Resumo da Análise Forense (IA Gemini)
312
- </h3>
313
- <div className="prose prose-invert max-w-none text-slate-300">
314
- <p className="whitespace-pre-wrap">{results.aiSummary || "Nenhum resumo disponível. A análise da IA Gemini não retornou resultados."}</p>
315
- </div>
 
 
 
 
 
 
 
316
  </div>
317
  </div>
318
- );
319
- };
320
-
321
- const App = () => {
322
- const [results, setResults] = React.useState(null);
323
- const [error, setError] = React.useState(null);
324
- const [isLoading, setIsLoading] = React.useState(false);
325
- const [isAuthenticated, setIsAuthenticated] = React.useState(false);
326
-
327
- React.useEffect(() => {
328
- window.addEventListener('message', (event) => {
329
- if (event.data.type === 'AUTH_TOKEN') {
330
- authToken = event.data.token;
331
- setIsAuthenticated(true);
332
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  });
334
- }, []);
335
 
336
- const handleSearch = async ({ cpf, socialProfile, years }) => {
337
- if (!authToken) {
338
- setError("Token de autenticação não disponível. Por favor, recarregue a página.");
339
- return;
340
  }
341
 
342
- setIsLoading(true);
343
- setError(null);
344
- setResults(null);
345
-
346
- try {
347
- const response = await fetch('/api/client/plugin/ai', {
348
- method: 'POST',
349
- headers: {
350
- 'Content-Type': 'application/json',
351
- 'Authorization': `Bearer ${authToken}`
352
- },
353
- body: JSON.stringify({
354
- plugin_id: PLUGIN_ID,
355
- user_prompt: `Realize uma pesquisa forense detalhada da pessoa com CPF ${cpf} e perfil social ${socialProfile}. Analise os últimos ${years} anos de atividade. Estruture os dados nos seguintes campos:
356
-
357
- 1. personalInfo: {name, cpf, age, location}
358
- 2. activity: {postCount, commentCount, likeCount}
359
- 3. sentiment: {positive, neutral, negative} (percentages)
360
- 4. connections: {friends, groups, pages}
361
- 5. importantPosts: [ {date, content, sentiment, engagement} ] (max 10)
362
- 6. riskLevel: "BAIXO", "MÉDIO" ou "ALTO"
363
- 7. aiSummary: resumo profissional em português (máx 500 palavras) com insights forenses
364
-
365
- Use a API do Gemini para estruturar e analisar as informações. Inclua qualquer comportamento suspeito, padrões de atividade e potenciais riscos identificados.`
366
- })
367
- });
368
-
369
- if (!response.ok) {
370
- throw new Error(`Erro na API: ${response.status} ${response.statusText}`);
371
- }
372
-
373
- const data = await response.json();
374
- setResults({
375
- ...data,
376
- cpf,
377
- socialProfile,
378
- years
379
- });
380
-
381
- } catch (err) {
382
- console.error("Erro na pesquisa:", err);
383
- setError(err.message || "Falha ao processar a pesquisa forense. Tente novamente.");
384
- } finally {
385
- setIsLoading(false);
386
- }
 
 
 
387
  };
 
 
388
 
389
- return (
390
- <div className="max-w-7xl mx-auto">
391
- <AuthStatus />
392
- <SearchForm onSearch={handleSearch} isLoading={isLoading} />
393
- <ResultsDashboard results={results} error={error} />
394
- </div>
395
- );
396
- };
397
 
398
- // Renderizar o aplicativo React
399
- ReactDOM.createRoot(document.getElementById('root')).render(<App />);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  </script>
401
  </body>
402
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Forensic Person Search System</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
 
9
  <style>
10
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
11
+
12
  body {
13
  font-family: 'Inter', sans-serif;
14
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
15
  }
16
+
17
+ .card-glow {
18
+ box-shadow: 0 0 20px rgba(59, 130, 246, 0.1);
19
+ transition: all 0.3s ease;
20
+ }
21
+
22
+ .card-glow:hover {
23
+ transform: translateY(-5px);
24
+ box-shadow: 0 10px 30px rgba(59, 130, 246, 0.2);
25
+ }
26
+
27
+ .gradient-border {
28
+ background: linear-gradient(45deg, #3b82f6, #8b5cf6);
29
+ padding: 1px;
30
+ border-radius: 0.75rem;
31
+ }
32
+
33
+ .gradient-border-content {
34
+ background: #0f172a;
35
+ border-radius: 0.625rem;
36
+ }
37
+
38
+ .timeline-item::before {
39
+ content: '';
40
+ position: absolute;
41
+ left: 10px;
42
+ top: 0;
43
+ bottom: 0;
44
+ width: 2px;
45
+ background: linear-gradient(to bottom, #3b82f6, #8b5cf6);
46
+ }
47
+
48
+ .timeline-dot {
49
+ width: 20px;
50
+ height: 20px;
51
  border-radius: 50%;
52
+ background: linear-gradient(45deg, #3b82f6, #8b5cf6);
53
+ position: absolute;
54
+ left: 5px;
55
+ }
56
+
57
+ .loading-spinner {
58
  width: 40px;
59
  height: 40px;
60
+ border: 4px solid rgba(59, 130, 246, 0.2);
61
+ border-top-color: #3b82f6;
62
+ border-radius: 50%;
63
  animation: spin 1s linear infinite;
64
  }
65
+
66
  @keyframes spin {
67
+ to { transform: rotate(360deg); }
 
68
  }
69
+
70
+ .network-graph {
71
+ background: radial-gradient(circle at center, rgba(59, 130, 246, 0.1) 0%, transparent 70%);
72
  }
73
+
74
+ .risk-meter {
75
+ background: linear-gradient(to right, #ef4444, #f59e0b, #10b981);
76
  }
77
  </style>
78
  </head>
79
+ <body class="min-h-screen p-4 md:p-6">
80
+ <div class="max-w-7xl mx-auto">
81
+ <!-- Header -->
82
+ <header class="flex justify-between items-center mb-8">
83
+ <div class="flex items-center space-x-3">
84
+ <div class="w-10 h-10 bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg flex items-center justify-center">
85
+ <i class="fas fa-search text-white text-lg"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  </div>
87
+ <h1 class="text-2xl font-bold text-white">Forensic Search System</h1>
88
+ </div>
89
+ <div class="text-sm text-slate-400">
90
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="hover:text-blue-400 transition-colors">
91
+ Built with anycoder
92
+ </a>
93
+ </div>
94
+ </header>
95
 
96
+ <!-- Main Content -->
97
+ <div id="app" class="space-y-6">
98
+ <!-- Status Bar -->
99
+ <div id="status" class="text-xs text-slate-500 mb-4 flex items-center space-x-2">
100
+ <span class="w-2 h-2 rounded-full bg-yellow-500 animate-pulse"></span>
101
+ <span>Aguardando Autenticação...</span>
102
+ </div>
103
 
104
+ <!-- Search Form -->
105
+ <div class="gradient-border">
106
+ <div class="gradient-border-content p-6">
107
+ <form id="searchForm" class="space-y-4">
108
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
109
+ <div>
110
+ <label class="block text-sm font-medium text-slate-300 mb-1">CPF</label>
111
+ <input type="text" id="cpf" placeholder="000.000.000-00"
112
+ class="w-full px-4 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
113
+ </div>
114
+ <div>
115
+ <label class="block text-sm font-medium text-slate-300 mb-1">Rede Social</label>
116
+ <select id="socialNetwork" class="w-full px-4 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
117
+ <option value="facebook">Facebook</option>
118
+ <option value="twitter">Twitter/X</option>
119
+ <option value="instagram">Instagram</option>
120
+ <option value="linkedin">LinkedIn</option>
121
+ <option value="tiktok">TikTok</option>
122
+ </select>
123
+ </div>
124
+ <div>
125
+ <label class="block text-sm font-medium text-slate-300 mb-1">Perfil</label>
126
+ <input type="text" id="profileUrl" placeholder="https://social.com/username"
127
+ class="w-full px-4 py-2 bg-slate-800 border border-slate-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
128
+ </div>
129
  </div>
130
+ <div class="flex justify-end">
131
+ <button id="btnSearch" type="submit" class="bg-gradient-to-r from-blue-600 to-purple-600 text-white px-6 py-2 rounded-lg font-medium hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed">
132
+ <i class="fas fa-search mr-2"></i>
133
+ Iniciar Pesquisa Forense
134
+ </button>
 
 
 
 
 
 
135
  </div>
136
+ </form>
137
+ </div>
138
+ </div>
139
 
140
+ <!-- Results Dashboard -->
141
+ <div id="resultsDashboard" class="hidden space-y-6">
142
+ <!-- Summary Cards -->
143
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
144
+ <div class="card-glow bg-slate-800 p-6 rounded-xl">
145
+ <div class="flex items-center space-x-3 mb-4">
146
+ <div class="w-12 h-12 bg-blue-600 rounded-full flex items-center justify-center">
147
+ <i class="fas fa-user text-white"></i>
148
+ </div>
149
+ <div>
150
+ <h3 class="text-lg font-semibold text-white">Perfil Analisado</h3>
151
+ <p id="profileName" class="text-slate-300">-</p>
152
+ </div>
153
+ </div>
154
+ <div class="space-y-2">
155
+ <div class="flex justify-between">
156
+ <span class="text-sm text-slate-400">Postagens Analisadas</span>
157
+ <span id="postsCount" class="text-lg font-bold text-white">0</span>
158
+ </div>
159
+ <div class="flex justify-between">
160
+ <span class="text-sm text-slate-400">Período Coberto</span>
161
+ <span id="periodCovered" class="text-lg font-bold text-white">-</span>
162
+ </div>
163
  </div>
164
  </div>
165
 
166
+ <div class="card-glow bg-slate-800 p-6 rounded-xl">
167
+ <div class="flex items-center space-x-3 mb-4">
168
+ <div class="w-12 h-12 bg-purple-600 rounded-full flex items-center justify-center">
169
+ <i class="fas fa-chart-line text-white"></i>
170
+ </div>
171
+ <div>
172
+ <h3 class="text-lg font-semibold text-white">Atividade</h3>
173
+ <p class="text-slate-300">Métricas de Engajamento</p>
174
+ </div>
175
+ </div>
176
+ <div class="space-y-2">
177
+ <div class="flex justify-between">
178
+ <span class="text-sm text-slate-400">Média Diária</span>
179
+ <span id="dailyAvg" class="text-lg font-bold text-white">0</span>
180
+ </div>
181
+ <div class="flex justify-between">
182
+ <span class="text-sm text-slate-400">Pico de Atividade</span>
183
+ <span id="peakActivity" class="text-lg font-bold text-white">-</span>
184
+ </div>
185
+ </div>
 
 
 
 
186
  </div>
 
 
187
 
188
+ <div class="card-glow bg-slate-800 p-6 rounded-xl">
189
+ <div class="flex items-center space-x-3 mb-4">
190
+ <div class="w-12 h-12 bg-red-600 rounded-full flex items-center justify-center">
191
+ <i class="fas fa-exclamation-triangle text-white"></i>
192
+ </div>
193
+ <div>
194
+ <h3 class="text-lg font-semibold text-white">Riscos Detectados</h3>
195
+ <p class="text-slate-300">Avaliação de Perigo</p>
196
+ </div>
197
+ </div>
198
+ <div class="space-y-2">
199
+ <div class="flex justify-between">
200
+ <span class="text-sm text-slate-400">Nível de Risco</span>
201
+ <span id="riskLevel" class="text-lg font-bold text-red-400">Baixo</span>
202
+ </div>
203
+ <div class="w-full h-2 bg-slate-700 rounded-full mt-2">
204
+ <div id="riskMeter" class="h-full rounded-full risk-meter" style="width: 20%"></div>
205
+ </div>
206
+ </div>
207
  </div>
 
 
208
 
209
+ <div class="card-glow bg-slate-800 p-6 rounded-xl">
210
+ <div class="flex items-center space-x-3 mb-4">
211
+ <div class="w-12 h-12 bg-green-600 rounded-full flex items-center justify-center">
212
+ <i class="fas fa-network-wired text-white"></i>
213
+ </div>
214
  <div>
215
+ <h3 class="text-lg font-semibold text-white">Conexões</h3>
216
+ <p class="text-slate-300">Rede Social</p>
217
+ </div>
218
+ </div>
219
+ <div class="space-y-2">
220
+ <div class="flex justify-between">
221
+ <span class="text-sm text-slate-400">Seguidores</span>
222
+ <span id="followers" class="text-lg font-bold text-white">0</span>
223
  </div>
224
+ <div class="flex justify-between">
225
+ <span class="text-sm text-slate-400">Seguindo</span>
226
+ <span id="following" class="text-lg font-bold text-white">0</span>
 
227
  </div>
228
  </div>
229
  </div>
230
+ </div>
231
 
232
+ <!-- Detailed Analysis -->
233
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
234
+ <!-- Timeline -->
235
+ <div class="lg:col-span-2 space-y-4">
236
+ <div class="gradient-border">
237
+ <div class="gradient-border-content p-6">
238
+ <h2 class="text-xl font-bold text-white mb-4 flex items-center">
239
+ <i class="fas fa-clock mr-2"></i>
240
+ Linha do Tempo
241
+ </h2>
242
+ <div id="timeline" class="space-y-6">
243
+ <!-- Timeline items will be added here -->
 
 
 
 
 
 
 
 
 
 
244
  </div>
245
  </div>
246
  </div>
247
 
248
+ <!-- Content Analysis -->
249
+ <div class="gradient-border">
250
+ <div class="gradient-border-content p-6">
251
+ <h2 class="text-xl font-bold text-white mb-4 flex items-center">
252
+ <i class="fas fa-chart-pie mr-2"></i>
253
+ Análise de Conteúdo
254
+ </h2>
255
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
256
+ <div>
257
+ <h3 class="text-lg font-semibold text-white mb-2">Tópicos Principais</h3>
258
+ <div id="topicsChart" class="h-48 bg-slate-800 rounded-lg flex items-center justify-center">
259
+ <span class="text-slate-400">Gráfico de tópicos</span>
260
+ </div>
 
 
 
 
 
 
 
261
  </div>
262
+ <div>
263
+ <h3 class="text-lg font-semibold text-white mb-2">Sentimento</h3>
264
+ <div id="sentimentChart" class="h-48 bg-slate-800 rounded-lg flex items-center justify-center">
265
+ <span class="text-slate-400">Gráfico de sentimento</span>
266
+ </div>
 
267
  </div>
 
268
  </div>
269
  </div>
270
  </div>
271
+ </div>
272
 
273
+ <!-- Network Graph -->
274
+ <div class="space-y-4">
275
+ <div class="gradient-border">
276
+ <div class="gradient-border-content p-6">
277
+ <h2 class="text-xl font-bold text-white mb-4 flex items-center">
278
+ <i class="fas fa-project-diagram mr-2"></i>
279
+ Rede de Conexões
280
+ </h2>
281
+ <div id="networkGraph" class="h-64 bg-slate-800 rounded-lg network-graph flex items-center justify-center">
282
+ <span class="text-slate-400">Visualização de rede</span>
 
 
 
 
 
 
 
 
 
 
283
  </div>
284
  </div>
285
  </div>
 
286
 
287
+ <!-- Risk Assessment -->
288
+ <div class="gradient-border">
289
+ <div class="gradient-border-content p-6">
290
+ <h2 class="text-xl font-bold text-white mb-4 flex items-center">
291
+ <i class="fas fa-shield-alt mr-2"></i>
292
+ Avaliação de Risco
293
+ </h2>
294
+ <div class="space-y-4">
295
+ <div>
296
+ <h3 class="text-lg font-semibold text-white mb-2">Alertas</h3>
297
+ <div id="riskAlerts" class="space-y-2">
298
+ <!-- Risk alerts will be added here -->
299
+ </div>
300
+ </div>
301
+ <div>
302
+ <h3 class="text-lg font-semibold text-white mb-2">Recomendações</h3>
303
+ <div id="recommendations" class="space-y-2">
304
+ <!-- Recommendations will be added here -->
305
+ </div>
306
+ </div>
307
+ </div>
308
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  </div>
310
  </div>
311
+ </div>
312
+ </div>
313
 
314
+ <!-- Loading State -->
315
+ <div id="loadingState" class="hidden text-center py-12">
316
+ <div class="loading-spinner mx-auto mb-4"></div>
317
+ <h3 class="text-xl font-semibold text-white mb-2">Processando Análise Forense</h3>
318
+ <p class="text-slate-400">Estamos coletando e analisando dados dos últimos 5 anos...</p>
319
+ <div class="mt-4 text-sm text-slate-500">
320
+ <p>Esta operação pode levar alguns minutos dependendo da quantidade de dados.</p>
321
+ <p>Por favor, não feche esta janela.</p>
322
+ </div>
323
+ </div>
324
+
325
+ <!-- Error State -->
326
+ <div id="errorState" class="hidden bg-red-900/20 border border-red-500 rounded-lg p-4 text-red-300">
327
+ <div class="flex items-start space-x-3">
328
+ <i class="fas fa-exclamation-circle text-red-400 mt-1"></i>
329
+ <div>
330
+ <h3 class="font-semibold text-white mb-2">Erro na Análise</h3>
331
+ <p id="errorMessage">Ocorreu um erro ao processar sua solicitação.</p>
332
  </div>
333
  </div>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <script>
339
+ // Configuration
340
+ const PLUGIN_ID = "forensic-search-system-v1";
341
+ let authToken = null;
342
+ let currentAnalysis = null;
343
+
344
+ // DOM Elements
345
+ const statusElement = document.getElementById('status');
346
+ const searchForm = document.getElementById('searchForm');
347
+ const btnSearch = document.getElementById('btnSearch');
348
+ const resultsDashboard = document.getElementById('resultsDashboard');
349
+ const loadingState = document.getElementById('loadingState');
350
+ const errorState = document.getElementById('errorState');
351
+
352
+ // 1. Authentication Handshake
353
+ window.addEventListener('message', (event) => {
354
+ if (event.data.type === 'AUTH_TOKEN') {
355
+ authToken = event.data.token;
356
+ statusElement.innerHTML = `
357
+ <span class="w-2 h-2 rounded-full bg-green-500"></span>
358
+ <span>Autenticado com sucesso</span>
359
+ `;
360
+ btnSearch.disabled = false;
361
+ }
362
+ });
363
+
364
+ // 2. Form Submission
365
+ searchForm.addEventListener('submit', async (e) => {
366
+ e.preventDefault();
367
+
368
+ if (!authToken) {
369
+ showError("Token de autenticação não disponível");
370
+ return;
371
+ }
372
+
373
+ // Get form values
374
+ const cpf = document.getElementById('cpf').value;
375
+ const socialNetwork = document.getElementById('socialNetwork').value;
376
+ const profileUrl = document.getElementById('profileUrl').value;
377
+
378
+ if (!cpf || !profileUrl) {
379
+ showError("Por favor, preencha todos os campos obrigatórios");
380
+ return;
381
+ }
382
+
383
+ // Show loading state
384
+ resultsDashboard.classList.add('hidden');
385
+ loadingState.classList.remove('hidden');
386
+ errorState.classList.add('hidden');
387
+
388
+ try {
389
+ // Call AI Gateway
390
+ const analysis = await callAIGateway(cpf, socialNetwork, profileUrl);
391
+ currentAnalysis = analysis;
392
+ renderResults(analysis);
393
+ } catch (error) {
394
+ showError(error.message || "Falha ao processar a análise");
395
+ }
396
+ });
397
+
398
+ // 3. AI Gateway Call
399
+ async function callAIGateway(cpf, socialNetwork, profileUrl) {
400
+ try {
401
+ const response = await fetch('/api/client/plugin/ai', {
402
+ method: 'POST',
403
+ headers: {
404
+ 'Authorization': `Bearer ${authToken}`,
405
+ 'Content-Type': 'application/json'
406
+ },
407
+ body: JSON.stringify({
408
+ plugin_id: PLUGIN_ID,
409
+ user_prompt: `Analise forensicamente o perfil ${profileUrl} na rede social ${socialNetwork} associado ao CPF ${cpf}. Colete dados dos últimos 5 anos e gere um relatório detalhado com:
410
+ 1. Informações básicas do perfil
411
+ 2. Análise de atividade (postagens, engajamento)
412
+ 3. Linha do tempo de eventos significativos
413
+ 4. Análise de conteúdo (tópicos, sentimento)
414
+ 5. Rede de conexões
415
+ 6. Avaliação de risco
416
+ 7. Alertas e recomendações
417
+
418
+ Formate a resposta como JSON com a seguinte estrutura:
419
+ {
420
+ "profile": {...},
421
+ "activity": {...},
422
+ "timeline": [...],
423
+ "contentAnalysis": {...},
424
+ "network": {...},
425
+ "riskAssessment": {...}
426
+ }`
427
+ })
428
  });
 
429
 
430
+ if (!response.ok) {
431
+ throw new Error(`Erro na API: ${response.status}`);
 
 
432
  }
433
 
434
+ const data = await response.json();
435
+ return JSON.parse(data.response); // Assuming the AI returns JSON in the response field
436
+ } catch (error) {
437
+ console.error("Error calling AI Gateway:", error);
438
+ throw error;
439
+ }
440
+ }
441
+
442
+ // 4. Render Results
443
+ function renderResults(analysis) {
444
+ // Hide loading state
445
+ loadingState.classList.add('hidden');
446
+
447
+ // Populate summary cards
448
+ document.getElementById('profileName').textContent = analysis.profile.name || '-';
449
+ document.getElementById('postsCount').textContent = analysis.activity.postsCount || '0';
450
+ document.getElementById('periodCovered').textContent = analysis.activity.period || '-';
451
+ document.getElementById('dailyAvg').textContent = analysis.activity.dailyAverage || '0';
452
+ document.getElementById('peakActivity').textContent = analysis.activity.peakDate || '-';
453
+
454
+ // Risk assessment
455
+ const riskLevel = analysis.riskAssessment.level || 'Baixo';
456
+ const riskPercentage = getRiskPercentage(riskLevel);
457
+ document.getElementById('riskLevel').textContent = riskLevel;
458
+ document.getElementById('riskMeter').style.width = `${riskPercentage}%`;
459
+
460
+ // Network
461
+ document.getElementById('followers').textContent = analysis.network.followers || '0';
462
+ document.getElementById('following').textContent = analysis.network.following || '0';
463
+
464
+ // Render timeline
465
+ renderTimeline(analysis.timeline);
466
+
467
+ // Render risk alerts and recommendations
468
+ renderAlerts(analysis.riskAssessment.alerts);
469
+ renderRecommendations(analysis.riskAssessment.recommendations);
470
+
471
+ // Show results
472
+ resultsDashboard.classList.remove('hidden');
473
+ }
474
+
475
+ // 5. Helper Functions
476
+ function getRiskPercentage(level) {
477
+ const levels = {
478
+ 'Baixo': 20,
479
+ 'Médio': 50,
480
+ 'Alto': 80,
481
+ 'Crítico': 100
482
  };
483
+ return levels[level] || 20;
484
+ }
485
 
486
+ function renderTimeline(items) {
487
+ const timelineElement = document.getElementById('timeline');
488
+ timelineElement.innerHTML = '';
 
 
 
 
 
489
 
490
+ if (!items || items.length === 0) {
491
+ timelineElement.innerHTML = '<p class="text-slate-400">Nenhum evento significativo encontrado.</p>';
492
+ return;
493
+ }
494
+
495
+ items.forEach(item => {
496
+ const timelineItem = document.createElement('div');
497
+ timelineItem.className = 'relative pl-8 pb-4';
498
+
499
+ timelineItem.innerHTML = `
500
+ <div class="timeline-dot"></div>
501
+ <div class="bg-slate-800 p-4 rounded-lg border border-slate-700">
502
+ <div class="flex justify-between items-start mb-2">
503
+ <h4 class="font-semibold text-white">${item.title}</h4>
504
+ <span class="text-xs text-slate-400">${item.date}</span>
505
+ </div>
506
+ <p class="text-slate-300 mb-3">${item.description}</p>
507
+ ${item.tags ? `
508
+ <div class="flex flex-wrap gap-2">
509
+ ${item.tags.map(tag => `
510
+ <span class="px-2 py-1 bg-slate-700 text-xs rounded-full text-slate-300">${tag}</span>
511
+ `).join('')}
512
+ </div>
513
+ ` : ''}
514
+ </div>
515
+ `;
516
+
517
+ timelineElement.appendChild(timelineItem);
518
+ });
519
+ }
520
+
521
+ function renderAlerts(alerts) {
522
+ const alertsElement = document.getElementById('riskAlerts');
523
+ alertsElement.innerHTML = '';
524
+
525
+ if (!alerts || alerts.length === 0) {
526
+ alertsElement.innerHTML = '<p class="text-slate-400 text-sm">Nenhum alerta encontrado.</p>';
527
+ return;
528
+ }
529
+
530
+ alerts.forEach(alert => {
531
+ const alertElement = document.createElement('div');
532
+ alertElement.className = 'bg-red-900/30 border border-red-500 rounded-lg p-3';
533
+
534
+ alertElement.innerHTML = `
535
+ <div class="flex items-start space-x-3">
536
+ <i class="fas fa-exclamation-triangle text-red-400 mt-1"></i>
537
+ <div>
538
+ <h4 class="font-semibold text-white">${alert.title}</h4>
539
+ <p class="text-red-300 text-sm">${alert.description}</p>
540
+ ${alert.severity ? `
541
+ <span class="text-xs text-red-400 mt-1 block">Severidade: ${alert.severity}</span>
542
+ ` : ''}
543
+ </div>
544
+ </div>
545
+ `;
546
+
547
+ alertsElement.appendChild(alertElement);
548
+ });
549
+ }
550
+
551
+ function renderRecommendations(recommendations) {
552
+ const recElement = document.getElementById('recommendations');
553
+ recElement.innerHTML = '';
554
+
555
+ if (!recommendations || recommendations.length === 0) {
556
+ recElement.innerHTML = '<p class="text-slate-400 text-sm">Nenhuma recomendação.</p>';
557
+ return;
558
+ }
559
+
560
+ recommendations.forEach(rec => {
561
+ const recElementItem = document.createElement('div');
562
+ recElementItem.className = 'bg-blue-900/30 border border-blue-500 rounded-lg p-3';
563
+
564
+ recElementItem.innerHTML = `
565
+ <div class="flex items-start space-x-3">
566
+ <i class="fas fa-lightbulb text-blue-400 mt-1"></i>
567
+ <div>
568
+ <h4 class="font-semibold text-white">${rec.title}</h4>
569
+ <p class="text-blue-300 text-sm">${rec.description}</p>
570
+ </div>
571
+ </div>
572
+ `;
573
+
574
+ recElement.appendChild(recElementItem);
575
+ });
576
+ }
577
+
578
+ function showError(message) {
579
+ loadingState.classList.add('hidden');
580
+ errorState.classList.remove('hidden');
581
+ document.getElementById('errorMessage').textContent = message;
582
+ }
583
+
584
+ // Initialize
585
+ document.addEventListener('DOMContentLoaded', () => {
586
+ // Check if we're in an iframe
587
+ if (window.self !== window.top) {
588
+ // Send ready message to parent
589
+ window.parent.postMessage({ type: 'PLUGIN_READY', pluginId: PLUGIN_ID }, '*');
590
+ }
591
+ });
592
  </script>
593
  </body>
594
  </html>