Skip to content
This repository was archived by the owner on Jan 3, 2026. It is now read-only.

Commit 88089c3

Browse files
committed
Replace DetectionAccuracy with AverageScanTimeMs & other improvements
1 parent 15cd5ae commit 88089c3

File tree

11 files changed

+121
-65
lines changed

11 files changed

+121
-65
lines changed

src/app/[locale]/page.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import Image from 'next/image';
88
import Link from 'next/link';
99
import { useChunkedUpload } from '@/hooks/useChunkedUpload';
1010
import StandardFileUpload from '@/components/StandardFileUpload';
11+
import { formatScanTime, computeFileHash } from '@/lib/utils';
1112

1213
interface SystemAnalytics {
1314
totalFilesScanned: number;
1415
totalThreatsDetected: number;
15-
detectionAccuracy: number;
1616
averageScanTimeMs: number;
1717
lastUpdated: string;
1818
totalSafeFiles: number;
@@ -86,13 +86,6 @@ export default function Page() {
8686
};
8787
}, []);
8888

89-
async function computeFileHash(file: File): Promise<string> {
90-
const arrayBuffer = await file.arrayBuffer();
91-
const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
92-
const bytes = Array.from(new Uint8Array(hashBuffer));
93-
const base64 = btoa(String.fromCharCode(...bytes));
94-
return base64;
95-
}
9689

9790
const handleFileSelect = (file: File) => {
9891
setUseChunkedUploadForFile(file.size > CHUNKED_UPLOAD_THRESHOLD);
@@ -413,16 +406,14 @@ export default function Page() {
413406
{t('stats.threatsDetected')}
414407
</div>
415408
</div>
416-
<div className='bg-slate-800/30 backdrop-blur-sm border border-purple-500/20 rounded-xl p-4 md:p-6 hover:bg-slate-800/50 hover:border-green-500/40 transition-all duration-300 hover:scale-105 hover:shadow-lg hover:shadow-green-500/20 group cursor-pointer'>
417-
<div
418-
className={`text-2xl md:text-3xl font-bold mb-2 group-hover:scale-110 transition-transform duration-300 ${systemAnalytics ? (systemAnalytics.detectionAccuracy >= 95 ? 'text-green-400' : systemAnalytics.detectionAccuracy >= 80 ? 'text-yellow-400' : 'text-red-400') : 'text-gray-400'}`}
419-
>
409+
<div className='bg-slate-800/30 backdrop-blur-sm border border-purple-500/20 rounded-xl p-4 md:p-6 hover:bg-slate-800/50 hover:border-blue-500/40 transition-all duration-300 hover:scale-105 hover:shadow-lg hover:shadow-blue-500/20 group cursor-pointer'>
410+
<div className='text-2xl md:text-3xl font-bold text-blue-400 mb-2 group-hover:scale-110 transition-transform duration-300'>
420411
{systemAnalytics
421-
? `${systemAnalytics.detectionAccuracy.toFixed(1)}%`
412+
? formatScanTime(systemAnalytics.averageScanTimeMs, t)
422413
: '...'}
423414
</div>
424415
<div className='text-gray-300 group-hover:text-white transition-colors duration-300 text-sm md:text-base'>
425-
{t('stats.detectionAccuracy')}
416+
{t('stats.averageScanTime')}
426417
</div>
427418
</div>
428419
</div>

src/app/[locale]/privacy/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ export default function PrivacyPage() {
166166
detected vs safe files
167167
</li>
168168
<li>
169-
<strong>Detection accuracy</strong> - Performance metrics of our
170-
analysis
169+
<strong>Scan performance metrics</strong> - Average scan time and
170+
processing statistics
171171
</li>
172172
<li>
173173
<strong>Average scan time</strong> - Service performance metrics
@@ -359,7 +359,7 @@ export default function PrivacyPage() {
359359

360360
<div className='mt-8 pt-6 border-t border-purple-500/30'>
361361
<p className='text-sm text-gray-400'>
362-
<strong>Last updated:</strong> October 26, 2025, 12:30 PM GMT+3
362+
<strong>Last updated:</strong> October 28, 2025, 6:52 PM GMT+3
363363
</p>
364364
</div>
365365
</div>

src/app/[locale]/result/[hash]/page.tsx

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import LanguageSwitcher from '@/components/LanguageSwitcher';
77
import { useChunkedUpload } from '@/hooks/useChunkedUpload';
88
import Image from 'next/image';
99
import Link from 'next/link';
10+
import { formatFileSize, formatScanTime, computeFileHash, getRiskLevel, getRiskColor } from '@/lib/utils';
1011

1112
interface AnalyticsData {
1213
fileName: string;
@@ -359,25 +360,6 @@ export default function ResultPage() {
359360
}
360361
};
361362

362-
const getRiskColor = (score: number) => {
363-
if (score <= 70) return 'text-green-400';
364-
if (score >= 50) return 'text-yellow-400';
365-
return 'text-red-400';
366-
};
367-
368-
const formatFileSize = (bytes: number) => {
369-
if (bytes === 0) return '0 Bytes';
370-
const k = 1024;
371-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
372-
const i = Math.floor(Math.log(bytes) / Math.log(k));
373-
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
374-
};
375-
376-
const getRiskLevel = (score: number) => {
377-
if (score <= 70) return t('results.safe');
378-
if (score >= 50) return t('results.suspicious');
379-
return t('results.unsafe');
380-
};
381363

382364
if (loading) {
383365
return (
@@ -577,7 +559,7 @@ export default function ResultPage() {
577559
</div>
578560
</div>
579561
<div className='text-xs md:text-sm break-words max-w-20 md:max-w-24 mt-2'>
580-
{getRiskLevel(analyticsData.score)}
562+
{getRiskLevel(analyticsData.score, t)}
581563
</div>
582564
</div>
583565
</div>
@@ -586,7 +568,7 @@ export default function ResultPage() {
586568
<div>
587569
<span className='text-gray-400'>{t('results.fileSize')}:</span>
588570
<span className='ml-2 text-white'>
589-
{formatFileSize(analyticsData.fileSizeBytes)}
571+
{formatFileSize(analyticsData.fileSizeBytes, t)}
590572
</span>
591573
</div>
592574
<div>
@@ -717,7 +699,7 @@ export default function ResultPage() {
717699
{dragFile.name}
718700
</p>
719701
<p className='text-sm text-gray-500 mt-1'>
720-
{formatFileSize(dragFile.size)}
702+
{formatFileSize(dragFile.size, t)}
721703
</p>
722704
</div>
723705

src/app/en/privacy/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ export default function PrivacyPage() {
169169
detected vs safe files
170170
</li>
171171
<li>
172-
<strong>Detection accuracy</strong> - Performance metrics of our
173-
analysis
172+
<strong>Scan performance metrics</strong> - Average scan time and
173+
processing statistics
174174
</li>
175175
<li>
176176
<strong>Average scan time</strong> - Service performance metrics
@@ -362,7 +362,7 @@ export default function PrivacyPage() {
362362

363363
<div className='mt-8 pt-6 border-t border-purple-500/30'>
364364
<p className='text-sm text-gray-400'>
365-
<strong>Last updated:</strong> October 26, 2025, 12:30 PM GMT+3
365+
<strong>Last updated:</strong> October 28, 2025, 6:52 PM GMT+3
366366
</p>
367367
</div>
368368
</div>

src/app/en/terms/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ export default function TermsPage() {
130130
<ul className='list-disc list-inside space-y-1 ml-4'>
131131
<li>
132132
<strong>No Guarantees</strong> - We do not guarantee 100%
133-
accuracy of threat detection
133+
effectiveness of threat detection
134134
</li>
135135
<li>
136136
<strong>False Positives</strong> - Legitimate files may be

src/app/ru/privacy/page.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,8 @@ export default function PrivacyPage() {
172172
обнаруженных угроз vs безопасных файлов
173173
</li>
174174
<li>
175-
<strong>Точность обнаружения</strong> - Метрики
176-
производительности нашего анализа
177-
</li>
178-
<li>
179-
<strong>Среднее время сканирования</strong> - Метрики
180-
производительности сервиса
175+
<strong>Метрики производительности сканирования</strong> - Среднее время сканирования и
176+
статистика обработки
181177
</li>
182178
<li>
183179
<strong>Средние оценки безопасности</strong> - Общие тенденции
@@ -367,8 +363,7 @@ export default function PrivacyPage() {
367363

368364
<div className='mt-8 pt-6 border-t border-purple-500/30'>
369365
<p className='text-sm text-gray-400'>
370-
<strong>Последнее обновление:</strong>{' '}
371-
{new Date().toLocaleDateString('ru-RU')}
366+
<strong>Последнее обновление:</strong> 28 октября 2025, 18:52 GMT+3
372367
</p>
373368
</div>
374369
</div>

src/app/ru/terms/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ export default function TermsPage() {
132132
<ul className='list-disc list-inside space-y-1 ml-4'>
133133
<li>
134134
<strong>Нет гарантий</strong> - Мы не гарантируем 100%
135-
точность обнаружения угроз
135+
эффективность обнаружения угроз
136136
</li>
137137
<li>
138138
<strong>Ложные срабатывания</strong> - Законные файлы могут

src/components/StandardFileUpload.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import React, { useRef, useState, useCallback } from 'react';
44
import { useTranslation } from '@/hooks/useTranslation';
5+
import { formatFileSize } from '@/lib/utils';
56

67
interface StandardFileUploadProps {
78
onFileSelect: (file: File) => void;
@@ -43,7 +44,7 @@ export default function StandardFileUpload({
4344
(file: File): string | null => {
4445
if (file.size > maxFileSize) {
4546
return t('errors.fileTooLarge', 'File is too large. Maximum size is {{size}}.', {
46-
size: formatFileSize(maxFileSize),
47+
size: formatFileSize(maxFileSize, t),
4748
});
4849
}
4950

@@ -224,7 +225,7 @@ export default function StandardFileUpload({
224225
<div className='text-xs text-gray-500'>
225226
<p>
226227
{t('upload.maxSize', 'Max file size: {{size}}', {
227-
size: formatFileSize(maxFileSize),
228+
size: formatFileSize(maxFileSize, t),
228229
})}
229230
</p>
230231
<p>
@@ -263,7 +264,7 @@ export default function StandardFileUpload({
263264
{selectedFile.name}
264265
</p>
265266
<p className='text-xs md:text-sm text-gray-500 mt-1'>
266-
{formatFileSize(selectedFile.size)}
267+
{formatFileSize(selectedFile.size, t)}
267268
</p>
268269
</div>
269270

src/lib/utils.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Formats file size in human-readable format with localization
3+
* @param bytes - File size in bytes
4+
* @param t - Translation function
5+
* @returns Formatted file size string
6+
*/
7+
export const formatFileSize = (bytes: number, t: (key: string) => string): string => {
8+
if (bytes === 0) return `0 ${t('fileSizeUnits.bytes')}`;
9+
const k = 1024;
10+
const sizeKeys = ['bytes', 'kb', 'mb', 'gb'];
11+
const i = Math.floor(Math.log(bytes) / Math.log(k));
12+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + t(`fileSizeUnits.${sizeKeys[i]}`);
13+
};
14+
15+
/**
16+
* Formats scan time in human-readable format with localization
17+
* @param milliseconds - Time in milliseconds
18+
* @param t - Translation function
19+
* @returns Formatted time string
20+
*/
21+
export const formatScanTime = (milliseconds: number, t: (key: string) => string): string => {
22+
if (milliseconds < 1000) {
23+
return `${Math.round(milliseconds)}${t('timeUnits.milliseconds')}`;
24+
} else if (milliseconds < 60000) {
25+
return `${(milliseconds / 1000).toFixed(1)}${t('timeUnits.seconds')}`;
26+
} else {
27+
const minutes = Math.floor(milliseconds / 60000);
28+
const seconds = Math.floor((milliseconds % 60000) / 1000);
29+
return `${minutes}${t('timeUnits.minutes')} ${seconds}${t('timeUnits.seconds')}`;
30+
}
31+
};
32+
33+
/**
34+
* Computes SHA-256 hash of a file
35+
* @param file - File object
36+
* @returns Promise<string> - Base64 encoded hash
37+
*/
38+
export const computeFileHash = async (file: File): Promise<string> => {
39+
const arrayBuffer = await file.arrayBuffer();
40+
const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
41+
const bytes = Array.from(new Uint8Array(hashBuffer));
42+
const base64 = btoa(String.fromCharCode(...bytes));
43+
return base64;
44+
};
45+
46+
/**
47+
* Gets risk level based on score
48+
* @param score - Security score (0-100)
49+
* @param t - Translation function
50+
* @returns Risk level string
51+
*/
52+
export const getRiskLevel = (score: number, t: (key: string) => string): string => {
53+
if (score <= 70) return t('results.safe');
54+
if (score >= 50) return t('results.suspicious');
55+
return t('results.unsafe');
56+
};
57+
58+
/**
59+
* Gets risk color class based on score
60+
* @param score - Security score (0-100)
61+
* @returns CSS class string
62+
*/
63+
export const getRiskColor = (score: number): string => {
64+
if (score <= 70) return 'text-green-400';
65+
if (score >= 50) return 'text-yellow-400';
66+
return 'text-red-400';
67+
};

src/locales/en.json

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,19 @@
6060
"safe": "Safe",
6161
"unsafe": "Unsafe",
6262
"riskLevel": "Security level",
63-
"accuracy": "Detection accuracy",
6463
"threatsDetected": "Threats detected",
65-
"detectionAccuracy": "Detection accuracy"
64+
"averageScanTime": "Average scan time"
65+
},
66+
"timeUnits": {
67+
"milliseconds": "ms",
68+
"seconds": "s",
69+
"minutes": "m"
70+
},
71+
"fileSizeUnits": {
72+
"bytes": "Bytes",
73+
"kb": "KB",
74+
"mb": "MB",
75+
"gb": "GB"
6676
},
6777
"features": {
6878
"title": "Safeturned Features",
@@ -138,7 +148,7 @@
138148
"suffix2": "."
139149
},
140150
"analytics": {
141-
"lastUpdated": "Last updated"
151+
"lastUpdated": "Data updated"
142152
},
143153
"terms": {
144154
"title": "Terms of Service",
@@ -178,7 +188,7 @@
178188
"important": "Important Limitations",
179189
"noGuarantees": {
180190
"title": "No Guarantees",
181-
"content": "We do not guarantee 100% accuracy of threat detection"
191+
"content": "We do not guarantee 100% effectiveness of threat detection"
182192
},
183193
"falsePositives": {
184194
"title": "False Positives",
@@ -302,6 +312,6 @@
302312
"title": "12. Contact Information",
303313
"content": "If you have any questions about these Terms of Service, please contact us through our GitHub repository or other official channels."
304314
},
305-
"lastUpdated": "Last updated"
315+
"lastUpdated": "Data updated"
306316
}
307317
}

0 commit comments

Comments
 (0)