TanmayTomar commited on
Commit
056851b
·
verified ·
1 Parent(s): 0cec2c9

Upload 3 files

Browse files
frontend_by_gemini/index.html ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Digital Forensics Toolkit</title>
7
+ <!-- FIXED: Correct path to CSS file -->
8
+ <link rel="stylesheet" href="/static/style.css">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
13
+ </head>
14
+ <body>
15
+ <div class="background-gradient"></div>
16
+
17
+ <header class="animate-on-scroll fade-in">
18
+ <h1>🛡️ Digital Forensics Toolkit</h1>
19
+ <p>An evidence-based platform to analyze text and images for misinformation.</p>
20
+ </header>
21
+
22
+ <main class="container animate-on-scroll slide-up">
23
+ <form id="analysis-form">
24
+ <div class="input-group">
25
+ <label for="text-input">Analyze Text or URL</label>
26
+ <textarea id="text-input" name="text_input" rows="4" placeholder="Paste a news article, claim, or URL here..."></textarea>
27
+ </div>
28
+
29
+ <div class="separator"><span>OR</span></div>
30
+
31
+ <div class="input-group">
32
+ <label for="image-input" class="file-label">
33
+ <i class="fas fa-cloud-upload-alt"></i>
34
+ <span id="file-label-text">Click to Upload an Image</span>
35
+ </label>
36
+ <input type="file" id="image-input" name="image_file" accept="image/png, image/jpeg">
37
+ </div>
38
+
39
+ <button type="submit" class="btn-analyze" id="analyze-btn">
40
+ <span class="btn-text">Analyze</span>
41
+ <i class="fas fa-arrow-right"></i>
42
+ </button>
43
+ </form>
44
+
45
+ <div id="results-container"></div>
46
+
47
+ <div id="loader" class="loader-hidden">
48
+ <div class="scanner-line"></div>
49
+ <p>Analyzing... This may take a moment.</p>
50
+ </div>
51
+ </main>
52
+
53
+ <footer class="animate-on-scroll fade-in">
54
+ <p>A GenAI Hackathon Project</p>
55
+ </footer>
56
+
57
+ <!-- FIXED: Correct path to JS file -->
58
+ <script src="/static/script.js"></script>
59
+ </body>
60
+ </html>
frontend_by_gemini/script.js ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ // --- Scroll-triggered Animations ---
3
+ const observer = new IntersectionObserver((entries) => {
4
+ entries.forEach(entry => {
5
+ if (entry.isIntersecting) {
6
+ entry.target.classList.add('visible');
7
+ }
8
+ });
9
+ }, { threshold: 0.1 });
10
+
11
+ document.querySelectorAll('.animate-on-scroll').forEach(element => {
12
+ observer.observe(element);
13
+ });
14
+
15
+ // --- Form & API Logic ---
16
+ const form = document.getElementById('analysis-form');
17
+ const analyzeBtn = document.getElementById('analyze-btn');
18
+ const loader = document.getElementById('loader');
19
+ const resultsContainer = document.getElementById('results-container');
20
+ const imageInput = document.getElementById('image-input');
21
+ const fileLabelText = document.getElementById('file-label-text');
22
+
23
+ imageInput.addEventListener('change', () => {
24
+ if (imageInput.files.length > 0) {
25
+ fileLabelText.textContent = `File selected: ${imageInput.files[0].name}`;
26
+ } else {
27
+ fileLabelText.textContent = 'Click to Upload an Image';
28
+ }
29
+ });
30
+
31
+ form.addEventListener('submit', async (event) => {
32
+ event.preventDefault();
33
+ const textInput = document.getElementById('text-input').value;
34
+ const imageFile = imageInput.files[0];
35
+
36
+ if (!textInput.trim() && !imageFile) {
37
+ alert("Please provide text or an image.");
38
+ return;
39
+ }
40
+
41
+ loader.classList.remove('loader-hidden');
42
+ resultsContainer.innerHTML = '';
43
+ analyzeBtn.disabled = true;
44
+
45
+ const formData = new FormData();
46
+ // Append data only if it exists to avoid sending empty fields
47
+ if(textInput.trim()) formData.append('text_input', textInput);
48
+ if(imageFile) formData.append('image_file', imageFile);
49
+
50
+ try {
51
+ // FIXED: Use the full, absolute URL for the API endpoint
52
+ const response = await fetch('http://127.0.0.1:8080/analyze', {
53
+ method: 'POST',
54
+ body: formData
55
+ });
56
+
57
+ const result = await response.json();
58
+
59
+ if (!response.ok) {
60
+ throw new Error(result.detail || `HTTP error! Status: ${response.status}`);
61
+ }
62
+
63
+ // FIXED: Directly use the result object as it's the main data
64
+ displayResults(result);
65
+
66
+ } catch (error) {
67
+ console.error('Analysis error:', error);
68
+ resultsContainer.innerHTML = `<div class="result-card error-card"><p><strong>Error:</strong> ${error.message}</p></div>`;
69
+ } finally {
70
+ loader.classList.add('loader-hidden');
71
+ analyzeBtn.disabled = false;
72
+ }
73
+ });
74
+
75
+ // --- Dynamic Result Display Function ---
76
+ function displayResults(data) {
77
+ resultsContainer.innerHTML = '';
78
+ let delay = 0;
79
+
80
+ // This function creates a new card and adds it to the DOM with a delay
81
+ const addCard = (card) => {
82
+ if(card) {
83
+ resultsContainer.appendChild(card);
84
+ setTimeout(() => card.classList.add('visible'), delay);
85
+ delay += 200;
86
+ }
87
+ };
88
+
89
+ // Handle different report types
90
+ if (data.final_verdict) { // Text-only report
91
+ addCard(createTextCard(data));
92
+ } else { // Image report (which can contain text analysis)
93
+ addCard(createImageManipulationCard(data.image_manipulation_report));
94
+ addCard(createReverseImageSearchCard(data.reverse_image_search_report));
95
+ addCard(createImageContentCard(data.in_image_content_report));
96
+ if (data.extracted_text_analysis_report && data.extracted_text_analysis_report.final_verdict) {
97
+ addCard(createExtractedTextCard(data.extracted_text_analysis_report));
98
+ }
99
+ }
100
+ }
101
+
102
+ // --- Helper functions to create HTML for each card ---
103
+ function createTextCard(report) {
104
+ if (!report) return null;
105
+ const card = document.createElement('div');
106
+ card.className = 'result-card animate-on-scroll fade-in';
107
+ let sourcesHtml = '';
108
+ if (report.source && Object.keys(report.source).length > 0) {
109
+ sourcesHtml = Object.entries(report.source).map(([source, url]) =>
110
+ `<div class="evidence-item"><strong>${source}</strong><br><a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a></div>`
111
+ ).join('');
112
+ }
113
+ card.innerHTML = `
114
+ <h3><i class="fas fa-file-alt"></i> Text Analysis Report</h3>
115
+ <p><strong>Verdict:</strong> <span class="verdict--${report.final_verdict?.toLowerCase()}">${report.final_verdict || 'N/A'}</span></p>
116
+ <p><strong>Explanation:</strong> ${report.explanation || 'N/A'}</p>
117
+ ${sourcesHtml ? `<div class="sources-section"><h4>Sources:</h4>${sourcesHtml}</div>` : ''}
118
+ `;
119
+ return card;
120
+ }
121
+
122
+ function createImageManipulationCard(report) {
123
+ if (!report) return null;
124
+ const card = document.createElement('div');
125
+ card.className = 'result-card animate-on-scroll fade-in';
126
+ // FIXED: Use the correct path for the ELA image
127
+ const elaLink = report.ela_image_path ? `<p><strong>ELA Analysis Image:</strong> <a href="/results/${report.ela_image_path}" target="_blank">View ELA Result</a></p>` : '';
128
+ card.innerHTML = `
129
+ <h3><i class="fas fa-shield-alt"></i> Image Manipulation Analysis</h3>
130
+ <p><strong>AI-Generated Score:</strong> ${report.ai_generated_score_percent?.toFixed(2) || '0.00'}%</p>
131
+ <p><strong>Classic Edit Score (ELA):</strong> ${report.classic_edit_score_percent?.toFixed(2) || '0.00'}%</p>
132
+ ${elaLink}
133
+ `;
134
+ return card;
135
+ }
136
+
137
+ function createImageContentCard(report) {
138
+ if (!report) return null;
139
+ const card = document.createElement('div');
140
+ card.className = 'result-card animate-on-scroll fade-in';
141
+ let contentHtml = '';
142
+ if (report['Safe Search']) {
143
+ const ss = report['Safe Search'];
144
+ contentHtml += `<p><strong>Safe Search:</strong> Adult: ${ss.adult}, Violence: ${ss.violence}</p>`;
145
+ }
146
+ if (report['Identified Entities']) {
147
+ contentHtml += `<p><strong>Identified Entities:</strong> ${report['Identified Entities'].join(', ')}</p>`;
148
+ }
149
+ card.innerHTML = `
150
+ <h3><i class="fas fa-search"></i> Image Content Analysis</h3>
151
+ ${contentHtml || '<p>No specific content detected.</p>'}
152
+ `;
153
+ return card;
154
+ }
155
+
156
+ function createReverseImageSearchCard(report) {
157
+ if (!report) return null;
158
+ const card = document.createElement('div');
159
+ card.className = 'result-card animate-on-scroll fade-in';
160
+ let matchesHtml = '<p>No online matches found.</p>';
161
+ if (report['Reverse Image Matches'] && report['Reverse Image Matches'].length > 0) {
162
+ matchesHtml = report['Reverse Image Matches'].map(match =>
163
+ `<div class="evidence-item"><strong>${match.title}</strong><br><a href="${match.url}" target="_blank" rel="noopener noreferrer">${match.url}</a></div>`
164
+ ).join('');
165
+ }
166
+ card.innerHTML = `
167
+ <h3><i class="fas fa-globe"></i> Reverse Image Search</h3>
168
+ ${matchesHtml}
169
+ `;
170
+ return card;
171
+ }
172
+
173
+ function createExtractedTextCard(report) {
174
+ if (!report) return null;
175
+ // Re-use the text card creation logic
176
+ const card = createTextCard(report);
177
+ card.querySelector('h3').innerHTML = `<i class="fas fa-file-text"></i> Extracted Text Analysis`;
178
+ return card;
179
+ }
180
+ });
frontend_by_gemini/style.css ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* --- Variables & Global Styles --- */
2
+ :root {
3
+ --primary-color: #00bfff; /* Deep Sky Blue */
4
+ --accent-color: #ff4757; /* Vibrant Red */
5
+ --dark-bg: #0d0c22; /* Deep Indigo */
6
+ --light-bg-transparent: rgba(26, 26, 46, 0.4);
7
+ --border-color: rgba(255, 255, 255, 0.1);
8
+ --text-color: #f0f0f0;
9
+ --text-muted: #a0a0a0;
10
+ --font-family-heading: 'Poppins', sans-serif;
11
+ --font-family-body: 'Roboto', sans-serif;
12
+ }
13
+
14
+ body {
15
+ font-family: var(--font-family-body);
16
+ background-color: var(--dark-bg);
17
+ color: var(--text-color);
18
+ line-height: 1.6;
19
+ margin: 0;
20
+ padding: 2rem;
21
+ overflow-x: hidden;
22
+ }
23
+
24
+ /* --- Animated Background --- */
25
+ .background-gradient {
26
+ position: fixed;
27
+ top: 0;
28
+ left: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ z-index: -1;
32
+ background: linear-gradient(315deg, var(--primary-color), var(--dark-bg), var(--accent-color));
33
+ background-size: 400% 400%;
34
+ animation: gradient-flow 15s ease infinite;
35
+ }
36
+
37
+ @keyframes gradient-flow {
38
+ 0% { background-position: 0% 50%; }
39
+ 50% { background-position: 100% 50%; }
40
+ 100% { background-position: 0% 50%; }
41
+ }
42
+
43
+
44
+ /* --- Main Container & Glassmorphism --- */
45
+ .container {
46
+ max-width: 800px;
47
+ margin: 2rem auto;
48
+ padding: 2.5rem;
49
+ background: var(--light-bg-transparent);
50
+ backdrop-filter: blur(20px);
51
+ -webkit-backdrop-filter: blur(20px);
52
+ border-radius: 20px;
53
+ border: 1px solid var(--border-color);
54
+ box-shadow: 0 15px 35px rgba(0,0,0,0.4);
55
+ }
56
+
57
+ header {
58
+ text-align: center;
59
+ margin-bottom: 2rem;
60
+ }
61
+ header h1 {
62
+ font-family: var(--font-family-heading);
63
+ color: #fff;
64
+ text-shadow: 0 0 10px var(--primary-color);
65
+ }
66
+ header p {
67
+ color: var(--text-muted);
68
+ }
69
+
70
+ /* --- Form & Upgraded Inputs --- */
71
+ #analysis-form {
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: 1.5rem;
75
+ }
76
+ .input-group label {
77
+ display: block;
78
+ margin-bottom: 0.5rem;
79
+ font-weight: 600;
80
+ }
81
+ textarea {
82
+ width: 100%;
83
+ padding: 0.75rem;
84
+ background-color: rgba(0,0,0,0.2);
85
+ border: 1px solid var(--border-color);
86
+ border-radius: 8px;
87
+ color: var(--text-color);
88
+ font-size: 1rem;
89
+ resize: vertical;
90
+ transition: border-color 0.3s, box-shadow 0.3s;
91
+ }
92
+ textarea:focus {
93
+ outline: none;
94
+ border-color: var(--primary-color);
95
+ box-shadow: 0 0 15px rgba(0, 191, 255, 0.5);
96
+ }
97
+ .separator {
98
+ text-align: center;
99
+ color: var(--text-muted);
100
+ font-weight: 600;
101
+ position: relative;
102
+ }
103
+ .separator span {
104
+ background: #1a1a2e;
105
+ padding: 0 10px;
106
+ }
107
+ .separator::before {
108
+ content: '';
109
+ position: absolute;
110
+ width: 100%;
111
+ height: 1px;
112
+ background: var(--border-color);
113
+ left: 0;
114
+ top: 50%;
115
+ z-index: -1;
116
+ }
117
+
118
+ /* Custom File Input */
119
+ input[type="file"] {
120
+ display: none;
121
+ }
122
+ .file-label {
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ padding: 2rem;
127
+ border: 2px dashed var(--border-color);
128
+ border-radius: 8px;
129
+ cursor: pointer;
130
+ transition: background-color 0.3s, border-color 0.3s;
131
+ }
132
+ .file-label:hover {
133
+ background-color: rgba(0, 191, 255, 0.1);
134
+ border-color: var(--primary-color);
135
+ }
136
+ .file-label i {
137
+ margin-right: 0.75rem;
138
+ font-size: 1.5rem;
139
+ color: var(--primary-color);
140
+ }
141
+
142
+
143
+ /* Upgraded Button */
144
+ .btn-analyze {
145
+ padding: 1rem 1.5rem;
146
+ font-size: 1.1rem;
147
+ font-weight: 600;
148
+ font-family: var(--font-family-heading);
149
+ color: #fff;
150
+ background: linear-gradient(45deg, var(--primary-color), var(--accent-color));
151
+ border: none;
152
+ border-radius: 8px;
153
+ cursor: pointer;
154
+ transition: all 0.3s ease;
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ gap: 0.5rem;
159
+ box-shadow: 0 5px 15px rgba(255, 71, 87, 0.3);
160
+ }
161
+ .btn-analyze:hover {
162
+ transform: translateY(-3px);
163
+ box-shadow: 0 8px 25px rgba(0, 191, 255, 0.5);
164
+ }
165
+ .btn-analyze:disabled {
166
+ background: #555;
167
+ cursor: not-allowed;
168
+ transform: translateY(0);
169
+ box-shadow: none;
170
+ }
171
+
172
+ /* --- Results & Upgraded Loader --- */
173
+ #results-container {
174
+ margin-top: 2rem;
175
+ }
176
+ .loader-hidden {
177
+ display: none;
178
+ }
179
+ #loader {
180
+ text-align: center;
181
+ padding: 2rem;
182
+ }
183
+ #loader p {
184
+ margin-top: 1rem;
185
+ color: var(--primary-color);
186
+ font-weight: 500;
187
+ }
188
+ .scanner-line {
189
+ width: 100%;
190
+ height: 3px;
191
+ background: linear-gradient(90deg, transparent, var(--primary-color), transparent);
192
+ animation: scan 2s infinite;
193
+ border-radius: 3px;
194
+ }
195
+ @keyframes scan {
196
+ 0% { transform: translateX(-100%); }
197
+ 100% { transform: translateX(100%); }
198
+ }
199
+
200
+ /* --- Result Card Styling --- */
201
+ .result-card {
202
+ background: var(--light-bg-transparent);
203
+ padding: 1.5rem;
204
+ border-radius: 12px;
205
+ margin-bottom: 1.5rem;
206
+ border: 1px solid var(--border-color);
207
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
208
+ }
209
+ .error-card {
210
+ border-color: var(--accent-color);
211
+ background: rgba(255, 71, 87, 0.1);
212
+ }
213
+ .result-card h3 {
214
+ margin-bottom: 1rem;
215
+ color: var(--primary-color);
216
+ display: flex;
217
+ align-items: center;
218
+ gap: 0.5rem;
219
+ }
220
+ .result-card p {
221
+ margin-bottom: 0.5rem;
222
+ }
223
+ .result-card .evidence-item {
224
+ background-color: rgba(0,0,0,0.2);
225
+ padding: 0.75rem;
226
+ border-radius: 6px;
227
+ margin-top: 0.5rem;
228
+ border: 1px solid var(--border-color);
229
+ font-size: 0.9rem;
230
+ }
231
+ .result-card .sources-section {
232
+ margin-top: 1rem;
233
+ }
234
+ .result-card .sources-section h4 {
235
+ color: var(--primary-color);
236
+ margin-bottom: 0.5rem;
237
+ font-size: 1rem;
238
+ }
239
+ .result-card .verdict--true {
240
+ color: #4CAF50;
241
+ font-weight: bold;
242
+ }
243
+ .result-card .verdict--false {
244
+ color: #f44336;
245
+ font-weight: bold;
246
+ }
247
+ .result-card .verdict--neutral {
248
+ color: #ff9800;
249
+ font-weight: bold;
250
+ }
251
+ .result-card .verdict--unverifiable {
252
+ color: #9e9e9e;
253
+ font-weight: bold;
254
+ }
255
+
256
+ /* --- Scroll Animations --- */
257
+ .animate-on-scroll {
258
+ opacity: 0;
259
+ transition: opacity 0.8s ease-out, transform 0.8s ease-out;
260
+ }
261
+ .animate-on-scroll.fade-in {
262
+ transform: translateY(30px);
263
+ }
264
+ .animate-on-scroll.slide-up {
265
+ transform: translateY(50px);
266
+ }
267
+ .animate-on-scroll.visible {
268
+ opacity: 1;
269
+ transform: translateY(0);
270
+ }
271
+
272
+
273
+ footer {
274
+ text-align: center;
275
+ margin-top: 3rem;
276
+ color: var(--text-muted);
277
+ font-size: 0.9rem;
278
+ }
279
+
280
+ /* --- Responsive --- */
281
+ @media (max-width: 600px) {
282
+ body { padding: 0.5rem; }
283
+ .container { padding: 1.5rem; margin: 1rem; }
284
+ header h1 { font-size: 1.8rem; }
285
+ }