@kakuzu_aon/apkz 1.0.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,1017 @@
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>APKZ - Advanced APK Analysis Platform</title>
7
+ <style>
8
+ :root {
9
+ --primary-color: #667eea;
10
+ --secondary-color: #764ba2;
11
+ --accent-color: #f093fb;
12
+ --success-color: #4ade80;
13
+ --warning-color: #fbbf24;
14
+ --error-color: #f87171;
15
+ --info-color: #60a5fa;
16
+ --bg-primary: #0f172a;
17
+ --bg-secondary: #1e293b;
18
+ --bg-tertiary: #334155;
19
+ --text-primary: #f8fafc;
20
+ --text-secondary: #cbd5e1;
21
+ --text-muted: #64748b;
22
+ --border-color: #475569;
23
+ --shadow-color: rgba(0, 0, 0, 0.5);
24
+ --gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
25
+ --gradient-secondary: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
26
+ --gradient-success: linear-gradient(135deg, #4ade80 0%, #22c55e 100%);
27
+ --gradient-warning: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
28
+ --gradient-error: linear-gradient(135deg, #f87171 0%, #dc2626 100%);
29
+ }
30
+
31
+ * {
32
+ margin: 0;
33
+ padding: 0;
34
+ box-sizing: border-box;
35
+ }
36
+
37
+ body {
38
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
39
+ background: var(--bg-primary);
40
+ min-height: 100vh;
41
+ color: var(--text-primary);
42
+ transition: all 0.3s ease;
43
+ }
44
+
45
+ .container {
46
+ max-width: 1200px;
47
+ margin: 0 auto;
48
+ padding: 20px;
49
+ }
50
+
51
+ .header {
52
+ text-align: center;
53
+ color: var(--text-primary);
54
+ margin-bottom: 30px;
55
+ animation: fadeInDown 0.8s ease;
56
+ }
57
+
58
+ .header h1 {
59
+ font-size: 3rem;
60
+ margin-bottom: 10px;
61
+ text-shadow: 2px 2px 4px var(--shadow-color);
62
+ background: var(--gradient-primary);
63
+ -webkit-background-clip: text;
64
+ -webkit-text-fill-color: transparent;
65
+ background-clip: text;
66
+ }
67
+
68
+ .header p {
69
+ font-size: 1.2rem;
70
+ opacity: 0.9;
71
+ color: var(--text-secondary);
72
+ }
73
+
74
+ .main-card {
75
+ background: var(--bg-secondary);
76
+ border: 1px solid var(--border-color);
77
+ border-radius: 20px;
78
+ padding: 30px;
79
+ box-shadow: 0 20px 40px var(--shadow-color);
80
+ animation: fadeInUp 0.8s ease;
81
+ backdrop-filter: blur(10px);
82
+ -webkit-backdrop-filter: blur(10px);
83
+ }
84
+
85
+ .upload-area {
86
+ border: 3px dashed var(--border-color);
87
+ border-radius: 15px;
88
+ padding: 40px;
89
+ text-align: center;
90
+ margin-bottom: 30px;
91
+ transition: all 0.3s ease;
92
+ cursor: pointer;
93
+ background: var(--bg-tertiary);
94
+ position: relative;
95
+ overflow: hidden;
96
+ }
97
+
98
+ .upload-area::before {
99
+ content: '';
100
+ position: absolute;
101
+ top: 0;
102
+ left: 0;
103
+ right: 0;
104
+ bottom: 0;
105
+ background: var(--gradient-primary);
106
+ opacity: 0;
107
+ transition: opacity 0.3s ease;
108
+ pointer-events: none;
109
+ }
110
+
111
+ .upload-area:hover {
112
+ border-color: var(--primary-color);
113
+ background: var(--bg-secondary);
114
+ transform: translateY(-2px);
115
+ box-shadow: 0 25px 50px var(--shadow-color);
116
+ }
117
+
118
+ .upload-area:hover::before {
119
+ opacity: 0.1;
120
+ }
121
+
122
+ .upload-area.dragover {
123
+ border-color: var(--accent-color);
124
+ background: var(--bg-secondary);
125
+ transform: scale(1.02);
126
+ box-shadow: 0 30px 60px var(--shadow-color);
127
+ }
128
+
129
+ .upload-area.dragover::before {
130
+ opacity: 0.2;
131
+ }
132
+
133
+ .upload-icon {
134
+ font-size: 4rem;
135
+ margin-bottom: 20px;
136
+ background: var(--gradient-primary);
137
+ -webkit-background-clip: text;
138
+ -webkit-text-fill-color: transparent;
139
+ background-clip: text;
140
+ }
141
+
142
+ .upload-text {
143
+ font-size: 1.2rem;
144
+ color: var(--text-primary);
145
+ margin-bottom: 10px;
146
+ font-weight: 600;
147
+ }
148
+
149
+ .upload-hint {
150
+ font-size: 0.9rem;
151
+ color: var(--text-muted);
152
+ }
153
+
154
+ .file-input {
155
+ display: none;
156
+ }
157
+
158
+ .features {
159
+ display: grid;
160
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
161
+ gap: 20px;
162
+ margin-top: 30px;
163
+ }
164
+
165
+ .feature-card {
166
+ background: var(--bg-tertiary);
167
+ border: 1px solid var(--border-color);
168
+ border-radius: 10px;
169
+ padding: 20px;
170
+ text-align: center;
171
+ transition: all 0.3s ease;
172
+ position: relative;
173
+ overflow: hidden;
174
+ }
175
+
176
+ .feature-card::before {
177
+ content: '';
178
+ position: absolute;
179
+ top: 0;
180
+ left: 0;
181
+ right: 0;
182
+ height: 3px;
183
+ background: var(--gradient-primary);
184
+ transform: scaleX(0);
185
+ transition: transform 0.3s ease;
186
+ }
187
+
188
+ .feature-card:hover {
189
+ transform: translateY(-5px);
190
+ box-shadow: 0 15px 30px var(--shadow-color);
191
+ background: var(--bg-secondary);
192
+ }
193
+
194
+ .feature-card:hover::before {
195
+ transform: scaleX(1);
196
+ }
197
+
198
+ .feature-icon {
199
+ font-size: 2rem;
200
+ margin-bottom: 10px;
201
+ background: var(--gradient-primary);
202
+ -webkit-background-clip: text;
203
+ -webkit-text-fill-color: transparent;
204
+ background-clip: text;
205
+ }
206
+
207
+ .feature-title {
208
+ font-size: 1.1rem;
209
+ font-weight: 600;
210
+ margin-bottom: 5px;
211
+ color: var(--text-primary);
212
+ }
213
+
214
+ .feature-desc {
215
+ color: var(--text-muted);
216
+ font-size: 0.9rem;
217
+ }
218
+
219
+ .jobs-section {
220
+ margin-top: 30px;
221
+ display: none;
222
+ }
223
+
224
+ .jobs-header {
225
+ display: flex;
226
+ justify-content: space-between;
227
+ align-items: center;
228
+ margin-bottom: 20px;
229
+ }
230
+
231
+ .jobs-title {
232
+ font-size: 1.5rem;
233
+ font-weight: 600;
234
+ color: var(--text-primary);
235
+ }
236
+
237
+ .clear-btn {
238
+ background: var(--gradient-error);
239
+ color: white;
240
+ border: none;
241
+ padding: 8px 16px;
242
+ border-radius: 5px;
243
+ cursor: pointer;
244
+ font-size: 0.9rem;
245
+ transition: all 0.3s ease;
246
+ }
247
+
248
+ .clear-btn:hover {
249
+ transform: translateY(-2px);
250
+ box-shadow: 0 5px 15px var(--shadow-color);
251
+ }
252
+
253
+ .jobs-list {
254
+ display: grid;
255
+ gap: 15px;
256
+ }
257
+
258
+ .job-card {
259
+ background: var(--bg-tertiary);
260
+ border: 1px solid var(--border-color);
261
+ border-radius: 10px;
262
+ padding: 20px;
263
+ display: flex;
264
+ justify-content: space-between;
265
+ align-items: center;
266
+ transition: all 0.3s ease;
267
+ position: relative;
268
+ overflow: hidden;
269
+ }
270
+
271
+ .job-card::before {
272
+ content: '';
273
+ position: absolute;
274
+ top: 0;
275
+ left: 0;
276
+ width: 4px;
277
+ height: 100%;
278
+ background: var(--gradient-primary);
279
+ transform: scaleY(0);
280
+ transition: transform 0.3s ease;
281
+ }
282
+
283
+ .job-card:hover {
284
+ box-shadow: 0 10px 25px var(--shadow-color);
285
+ background: var(--bg-secondary);
286
+ transform: translateX(5px);
287
+ }
288
+
289
+ .job-card:hover::before {
290
+ transform: scaleY(1);
291
+ }
292
+
293
+ .job-info {
294
+ flex: 1;
295
+ }
296
+
297
+ .job-name {
298
+ font-weight: 600;
299
+ margin-bottom: 5px;
300
+ color: var(--text-primary);
301
+ }
302
+
303
+ .job-status {
304
+ font-size: 0.9rem;
305
+ color: var(--text-muted);
306
+ }
307
+
308
+ .job-progress {
309
+ margin-top: 10px;
310
+ }
311
+
312
+ .progress-bar {
313
+ background: var(--bg-primary);
314
+ border-radius: 10px;
315
+ height: 8px;
316
+ overflow: hidden;
317
+ position: relative;
318
+ }
319
+
320
+ .progress-fill {
321
+ height: 100%;
322
+ background: var(--gradient-primary);
323
+ border-radius: 10px;
324
+ transition: width 0.3s ease;
325
+ position: relative;
326
+ overflow: hidden;
327
+ }
328
+
329
+ .progress-fill::after {
330
+ content: '';
331
+ position: absolute;
332
+ top: 0;
333
+ left: 0;
334
+ right: 0;
335
+ bottom: 0;
336
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
337
+ animation: shimmer 2s infinite;
338
+ }
339
+
340
+ .job-actions {
341
+ display: flex;
342
+ gap: 10px;
343
+ align-items: center;
344
+ }
345
+
346
+ .status-badge {
347
+ padding: 5px 12px;
348
+ border-radius: 20px;
349
+ font-size: 0.8rem;
350
+ font-weight: 600;
351
+ text-transform: uppercase;
352
+ transition: all 0.3s ease;
353
+ }
354
+
355
+ .status-pending {
356
+ background: var(--gradient-warning);
357
+ color: white;
358
+ }
359
+
360
+ .status-running {
361
+ background: var(--gradient-primary);
362
+ color: white;
363
+ animation: pulse 2s infinite;
364
+ }
365
+
366
+ .status-completed {
367
+ background: var(--gradient-success);
368
+ color: white;
369
+ }
370
+
371
+ .status-failed {
372
+ background: var(--gradient-error);
373
+ color: white;
374
+ }
375
+
376
+ .download-btn {
377
+ background: var(--gradient-primary);
378
+ color: white;
379
+ border: none;
380
+ padding: 8px 16px;
381
+ border-radius: 5px;
382
+ cursor: pointer;
383
+ font-size: 0.9rem;
384
+ text-decoration: none;
385
+ transition: all 0.3s ease;
386
+ }
387
+
388
+ .download-btn:hover {
389
+ transform: translateY(-2px);
390
+ box-shadow: 0 5px 15px var(--shadow-color);
391
+ }
392
+
393
+ .download-btn:disabled {
394
+ background: var(--bg-tertiary);
395
+ color: var(--text-muted);
396
+ cursor: not-allowed;
397
+ transform: none;
398
+ box-shadow: none;
399
+ }
400
+
401
+ .results-modal {
402
+ display: none;
403
+ position: fixed;
404
+ top: 0;
405
+ left: 0;
406
+ width: 100%;
407
+ height: 100%;
408
+ background: rgba(0, 0, 0, 0.8);
409
+ z-index: 1000;
410
+ justify-content: center;
411
+ align-items: center;
412
+ backdrop-filter: blur(5px);
413
+ -webkit-backdrop-filter: blur(5px);
414
+ }
415
+
416
+ .modal-content {
417
+ background: var(--bg-secondary);
418
+ border: 1px solid var(--border-color);
419
+ border-radius: 15px;
420
+ padding: 30px;
421
+ max-width: 800px;
422
+ max-height: 80vh;
423
+ overflow-y: auto;
424
+ position: relative;
425
+ box-shadow: 0 25px 50px var(--shadow-color);
426
+ }
427
+
428
+ .modal-header {
429
+ display: flex;
430
+ justify-content: space-between;
431
+ align-items: center;
432
+ margin-bottom: 20px;
433
+ }
434
+
435
+ .modal-title {
436
+ font-size: 1.5rem;
437
+ font-weight: 600;
438
+ color: var(--text-primary);
439
+ }
440
+
441
+ .close-btn {
442
+ background: none;
443
+ border: none;
444
+ font-size: 1.5rem;
445
+ cursor: pointer;
446
+ color: var(--text-muted);
447
+ transition: all 0.3s ease;
448
+ }
449
+
450
+ .close-btn:hover {
451
+ color: var(--text-primary);
452
+ transform: rotate(90deg);
453
+ }
454
+
455
+ @keyframes fadeInDown {
456
+ from {
457
+ opacity: 0;
458
+ transform: translateY(-20px);
459
+ }
460
+ to {
461
+ opacity: 1;
462
+ transform: translateY(0);
463
+ }
464
+ }
465
+
466
+ @keyframes fadeInUp {
467
+ from {
468
+ opacity: 0;
469
+ transform: translateY(20px);
470
+ }
471
+ to {
472
+ opacity: 1;
473
+ transform: translateY(0);
474
+ }
475
+ }
476
+
477
+ @keyframes shimmer {
478
+ 0% { transform: translateX(-100%); }
479
+ 100% { transform: translateX(100%); }
480
+ }
481
+
482
+ @keyframes pulse {
483
+ 0%, 100% { opacity: 1; }
484
+ 50% { opacity: 0.7; }
485
+ }
486
+
487
+ .socket-status {
488
+ position: fixed;
489
+ top: 20px;
490
+ right: 20px;
491
+ background: var(--bg-tertiary);
492
+ border: 1px solid var(--border-color);
493
+ padding: 10px 15px;
494
+ border-radius: 20px;
495
+ font-size: 0.9rem;
496
+ color: var(--text-primary);
497
+ box-shadow: 0 5px 15px var(--shadow-color);
498
+ transition: all 0.3s ease;
499
+ }
500
+
501
+ .socket-status.connected {
502
+ background: var(--gradient-success);
503
+ color: white;
504
+ border: none;
505
+ }
506
+
507
+ /* Theme Toggle */
508
+ .theme-toggle {
509
+ position: fixed;
510
+ top: 20px;
511
+ left: 20px;
512
+ background: var(--bg-tertiary);
513
+ border: 1px solid var(--border-color);
514
+ padding: 10px 15px;
515
+ border-radius: 20px;
516
+ font-size: 0.9rem;
517
+ color: var(--text-primary);
518
+ cursor: pointer;
519
+ box-shadow: 0 5px 15px var(--shadow-color);
520
+ transition: all 0.3s ease;
521
+ }
522
+
523
+ .theme-toggle:hover {
524
+ background: var(--bg-secondary);
525
+ transform: translateY(-2px);
526
+ }
527
+
528
+ /* Light Theme */
529
+ body.light-theme {
530
+ --bg-primary: #f8fafc;
531
+ --bg-secondary: #ffffff;
532
+ --bg-tertiary: #f1f5f9;
533
+ --text-primary: #1e293b;
534
+ --text-secondary: #475569;
535
+ --text-muted: #94a3b8;
536
+ --border-color: #e2e8f0;
537
+ --shadow-color: rgba(0, 0, 0, 0.1);
538
+ }
539
+
540
+ /* Scrollbar Styling */
541
+ ::-webkit-scrollbar {
542
+ width: 8px;
543
+ }
544
+
545
+ ::-webkit-scrollbar-track {
546
+ background: var(--bg-tertiary);
547
+ }
548
+
549
+ ::-webkit-scrollbar-thumb {
550
+ background: var(--gradient-primary);
551
+ border-radius: 4px;
552
+ }
553
+
554
+ ::-webkit-scrollbar-thumb:hover {
555
+ background: var(--gradient-secondary);
556
+ }
557
+
558
+ /* Responsive Design */
559
+ @media (max-width: 768px) {
560
+ .header h1 {
561
+ font-size: 2rem;
562
+ }
563
+
564
+ .features {
565
+ grid-template-columns: 1fr;
566
+ }
567
+
568
+ .job-card {
569
+ flex-direction: column;
570
+ align-items: flex-start;
571
+ gap: 15px;
572
+ }
573
+
574
+ .job-actions {
575
+ width: 100%;
576
+ justify-content: space-between;
577
+ }
578
+ }
579
+ </style>
580
+ </head>
581
+ <body>
582
+ <div class="container">
583
+ <div class="header">
584
+ <h1>🔍 APKZ</h1>
585
+ <p>Advanced APK Reverse Engineering & Security Analysis Platform</p>
586
+ </div>
587
+
588
+ <div class="main-card">
589
+ <div class="upload-area" id="uploadArea">
590
+ <div class="upload-icon">📱</div>
591
+ <div class="upload-text">Drop APK file here or click to browse</div>
592
+ <div class="upload-hint">Supports .apk files up to 100MB</div>
593
+ <input type="file" id="fileInput" class="file-input" accept=".apk">
594
+ </div>
595
+
596
+ <div class="features">
597
+ <div class="feature-card">
598
+ <div class="feature-icon">🔍</div>
599
+ <div class="feature-title">Vulnerability Scanning</div>
600
+ <div class="feature-desc">OWASP Top 10 compliance with CVSS scoring</div>
601
+ </div>
602
+ <div class="feature-card">
603
+ <div class="feature-icon">🔧</div>
604
+ <div class="feature-title">APK Modification</div>
605
+ <div class="feature-desc">Interactive editing with real-time preview</div>
606
+ </div>
607
+ <div class="feature-card">
608
+ <div class="feature-icon">🔍</div>
609
+ <div class="feature-title">Obfuscation Analysis</div>
610
+ <div class="feature-desc">Code protection and reverse engineering detection</div>
611
+ </div>
612
+ <div class="feature-card">
613
+ <div class="feature-icon">🌐</div>
614
+ <div class="feature-title">Network Analysis</div>
615
+ <div class="feature-desc">Extract URLs, endpoints, and API keys</div>
616
+ </div>
617
+ <div class="feature-card">
618
+ <div class="feature-icon">📊</div>
619
+ <div class="feature-title">Batch Processing</div>
620
+ <div class="feature-desc">Process multiple APKs simultaneously</div>
621
+ </div>
622
+ <div class="feature-card">
623
+ <div class="feature-icon">📱</div>
624
+ <div class="feature-title">Device Integration</div>
625
+ <div class="feature-desc">Install directly on connected devices</div>
626
+ </div>
627
+ </div>
628
+
629
+ <div class="jobs-section" id="jobsSection">
630
+ <div class="jobs-header">
631
+ <div class="jobs-title">📋 Analysis Jobs</div>
632
+ <button class="clear-btn" onclick="clearJobs()">Clear All</button>
633
+ </div>
634
+ <div class="jobs-list" id="jobsList"></div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+
639
+ <div class="theme-toggle" id="themeToggle" onclick="toggleTheme()">
640
+ 🌙 Dark Mode
641
+ </div>
642
+
643
+ <div class="socket-status" id="socketStatus">
644
+ 🔌 Connecting...
645
+ </div>
646
+
647
+ <div class="results-modal" id="resultsModal">
648
+ <div class="modal-content">
649
+ <div class="modal-header">
650
+ <div class="modal-title">Analysis Results</div>
651
+ <button class="close-btn" onclick="closeModal()">&times;</button>
652
+ </div>
653
+ <div id="resultsContent"></div>
654
+ </div>
655
+ </div>
656
+
657
+ <script src="/socket.io/socket.io.js"></script>
658
+ <script>
659
+ class APKZWebInterface {
660
+ constructor() {
661
+ this.socket = null;
662
+ this.jobs = new Map();
663
+ this.isDarkMode = true;
664
+ this.init();
665
+ }
666
+
667
+ init() {
668
+ this.setupEventListeners();
669
+ this.connectWebSocket();
670
+ this.loadThemePreference();
671
+ }
672
+
673
+ setupEventListeners() {
674
+ const uploadArea = document.getElementById('uploadArea');
675
+ const fileInput = document.getElementById('fileInput');
676
+
677
+ // Upload area events
678
+ uploadArea.addEventListener('click', () => fileInput.click());
679
+
680
+ // Prevent default drag behaviors
681
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
682
+ uploadArea.addEventListener(eventName, this.preventDefaults.bind(this), false);
683
+ document.body.addEventListener(eventName, this.preventDefaults.bind(this), false);
684
+ });
685
+
686
+ // Highlight drop area when item is dragged over it
687
+ ['dragenter', 'dragover'].forEach(eventName => {
688
+ uploadArea.addEventListener(eventName, this.highlight.bind(this), false);
689
+ });
690
+
691
+ ['dragleave', 'drop'].forEach(eventName => {
692
+ uploadArea.addEventListener(eventName, this.unhighlight.bind(this), false);
693
+ });
694
+
695
+ // Handle dropped files
696
+ uploadArea.addEventListener('drop', this.handleDrop.bind(this), false);
697
+
698
+ // Handle file input change
699
+ fileInput.addEventListener('change', (e) => {
700
+ this.handleFileUpload(e.target.files[0]);
701
+ });
702
+ }
703
+
704
+ // Helper functions for drag and drop
705
+ preventDefaults(e) {
706
+ e.preventDefault();
707
+ e.stopPropagation();
708
+ }
709
+
710
+ highlight(e) {
711
+ document.getElementById('uploadArea').classList.add('dragover');
712
+ }
713
+
714
+ unhighlight(e) {
715
+ document.getElementById('uploadArea').classList.remove('dragover');
716
+ }
717
+
718
+ handleDrop(e) {
719
+ const dt = e.dataTransfer;
720
+ const files = dt.files;
721
+ const uploadArea = document.getElementById('uploadArea');
722
+
723
+ uploadArea.classList.remove('dragover');
724
+
725
+ if (files.length > 0) {
726
+ this.handleFileUpload(files[0]);
727
+ }
728
+ }
729
+
730
+ connectWebSocket() {
731
+ this.socket = io();
732
+
733
+ this.socket.on('connect', () => {
734
+ this.updateSocketStatus('Connected', true);
735
+ console.log('Connected to APKZ server');
736
+ });
737
+
738
+ this.socket.on('disconnect', () => {
739
+ this.updateSocketStatus('Disconnected', false);
740
+ console.log('Disconnected from APKZ server');
741
+ });
742
+
743
+ this.socket.on('job-update', (data) => {
744
+ this.updateJobProgress(data);
745
+ });
746
+ }
747
+
748
+ updateSocketStatus(text, connected) {
749
+ const status = document.getElementById('socketStatus');
750
+ status.textContent = text;
751
+ status.className = connected ? 'socket-status connected' : 'socket-status';
752
+ }
753
+
754
+ async handleFileUpload(file) {
755
+ if (!file) return;
756
+
757
+ if (!file.name.endsWith('.apk')) {
758
+ this.showNotification('Please upload an APK file', 'error');
759
+ return;
760
+ }
761
+
762
+ if (file.size > 100 * 1024 * 1024) {
763
+ this.showNotification('File size must be less than 100MB', 'error');
764
+ return;
765
+ }
766
+
767
+ const formData = new FormData();
768
+ formData.append('apk', file);
769
+
770
+ try {
771
+ this.showNotification('Uploading APK...', 'info');
772
+ const response = await fetch('/api/upload', {
773
+ method: 'POST',
774
+ body: formData
775
+ });
776
+
777
+ const result = await response.json();
778
+
779
+ if (result.success) {
780
+ this.showNotification('Upload successful!', 'success');
781
+ this.startAnalysis(result.filename, result.path);
782
+ } else {
783
+ this.showNotification('Upload failed: ' + result.error, 'error');
784
+ }
785
+ } catch (error) {
786
+ this.showNotification('Upload error: ' + error.message, 'error');
787
+ }
788
+ }
789
+
790
+ async startAnalysis(filename, filepath) {
791
+ const options = {
792
+ vulnerability: true,
793
+ obfuscation: true,
794
+ network: true
795
+ };
796
+
797
+ try {
798
+ this.showNotification('Starting analysis...', 'info');
799
+ const response = await fetch('/api/analyze', {
800
+ method: 'POST',
801
+ headers: {
802
+ 'Content-Type': 'application/json'
803
+ },
804
+ body: JSON.stringify({
805
+ filepath: filepath,
806
+ options: options
807
+ })
808
+ });
809
+
810
+ const result = await response.json();
811
+
812
+ if (result.success) {
813
+ this.addJob(result.jobId, filename, 'pending');
814
+ this.socket.emit('join-job', result.jobId);
815
+ } else {
816
+ this.showNotification('Analysis failed: ' + result.error, 'error');
817
+ }
818
+ } catch (error) {
819
+ this.showNotification('Analysis error: ' + error.message, 'error');
820
+ }
821
+ }
822
+
823
+ addJob(jobId, filename, status) {
824
+ const job = {
825
+ id: jobId,
826
+ filename: filename,
827
+ status: status,
828
+ progress: 0,
829
+ results: null,
830
+ createdAt: new Date()
831
+ };
832
+
833
+ this.jobs.set(jobId, job);
834
+ this.renderJob(job);
835
+ this.showJobsSection();
836
+ }
837
+
838
+ renderJob(job) {
839
+ const jobsList = document.getElementById('jobsList');
840
+ let jobCard = document.getElementById(`job-${job.id}`);
841
+
842
+ if (!jobCard) {
843
+ jobCard = document.createElement('div');
844
+ jobCard.id = `job-${job.id}`;
845
+ jobCard.className = 'job-card';
846
+ jobsList.appendChild(jobCard);
847
+ }
848
+
849
+ const statusClass = `status-${job.status}`;
850
+ const progressWidth = job.progress || 0;
851
+
852
+ jobCard.innerHTML = `
853
+ <div class="job-info">
854
+ <div class="job-name">${job.filename}</div>
855
+ <div class="job-status">Status: ${job.status}</div>
856
+ <div class="job-progress">
857
+ <div class="progress-bar">
858
+ <div class="progress-fill" style="width: ${progressWidth}%"></div>
859
+ </div>
860
+ </div>
861
+ </div>
862
+ <div class="job-actions">
863
+ <span class="status-badge ${statusClass}">${job.status}</span>
864
+ ${job.status === 'completed' ?
865
+ `<button class="download-btn" onclick="downloadResults('${job.id}', 'json')">JSON</button>
866
+ <button class="download-btn" onclick="downloadResults('${job.id}', 'html')">HTML</button>` :
867
+ '<button class="download-btn" disabled>Processing...</button>'
868
+ }
869
+ </div>
870
+ `;
871
+ }
872
+
873
+ updateJobProgress(data) {
874
+ const job = this.jobs.get(data.jobId);
875
+ if (job) {
876
+ job.status = data.status;
877
+ job.progress = data.progress;
878
+ this.renderJob(job);
879
+
880
+ if (data.status === 'completed' && data.results) {
881
+ job.results = data.results;
882
+ this.showNotification('Analysis completed!', 'success');
883
+ }
884
+ }
885
+ }
886
+
887
+ showJobsSection() {
888
+ const section = document.getElementById('jobsSection');
889
+ section.style.display = 'block';
890
+ }
891
+
892
+ clearJobs() {
893
+ this.jobs.clear();
894
+ document.getElementById('jobsList').innerHTML = '';
895
+ document.getElementById('jobsSection').style.display = 'none';
896
+ }
897
+
898
+ async downloadResults(jobId, format) {
899
+ const job = this.jobs.get(jobId);
900
+ if (!job || !job.results) return;
901
+
902
+ try {
903
+ const response = await fetch(`/api/download/${jobId}/${format}`);
904
+ const blob = await response.blob();
905
+
906
+ const url = window.URL.createObjectURL(blob);
907
+ const a = document.createElement('a');
908
+ a.href = url;
909
+ a.download = `${job.filename}_results.${format}`;
910
+ document.body.appendChild(a);
911
+ a.click();
912
+ document.body.removeChild(a);
913
+ window.URL.revokeObjectURL(url);
914
+ } catch (error) {
915
+ this.showNotification('Download error: ' + error.message, 'error');
916
+ }
917
+ }
918
+
919
+ closeModal() {
920
+ document.getElementById('resultsModal').style.display = 'none';
921
+ document.getElementById('resultsContent').innerHTML = '';
922
+ }
923
+
924
+ toggleTheme() {
925
+ this.isDarkMode = !this.isDarkMode;
926
+ document.body.classList.toggle('light-theme');
927
+ this.updateThemeToggle();
928
+ this.saveThemePreference();
929
+ }
930
+
931
+ updateThemeToggle() {
932
+ const toggle = document.getElementById('themeToggle');
933
+ toggle.textContent = this.isDarkMode ? '🌙 Dark Mode' : '☀️ Light Mode';
934
+ }
935
+
936
+ saveThemePreference() {
937
+ localStorage.setItem('apkz-theme', this.isDarkMode ? 'dark' : 'light');
938
+ }
939
+
940
+ loadThemePreference() {
941
+ const savedTheme = localStorage.getItem('apkz-theme');
942
+ if (savedTheme === 'light') {
943
+ this.isDarkMode = false;
944
+ document.body.classList.add('light-theme');
945
+ this.updateThemeToggle();
946
+ }
947
+ }
948
+
949
+ showNotification(message, type = 'info') {
950
+ // Create notification element
951
+ const notification = document.createElement('div');
952
+ notification.className = `notification notification-${type}`;
953
+ notification.textContent = message;
954
+ notification.style.cssText = `
955
+ position: fixed;
956
+ top: 80px;
957
+ right: 20px;
958
+ background: var(--gradient-${type === 'error' ? 'error' : type === 'success' ? 'success' : 'primary'});
959
+ color: white;
960
+ padding: 12px 20px;
961
+ border-radius: 8px;
962
+ font-size: 0.9rem;
963
+ z-index: 1001;
964
+ animation: slideInRight 0.3s ease;
965
+ box-shadow: 0 5px 15px var(--shadow-color);
966
+ `;
967
+
968
+ document.body.appendChild(notification);
969
+
970
+ // Remove after 3 seconds
971
+ setTimeout(() => {
972
+ notification.style.animation = 'slideOutRight 0.3s ease';
973
+ setTimeout(() => {
974
+ document.body.removeChild(notification);
975
+ }, 300);
976
+ }, 3000);
977
+ }
978
+ }
979
+
980
+ // Add notification animations
981
+ const style = document.createElement('style');
982
+ style.textContent = `
983
+ @keyframes slideInRight {
984
+ from {
985
+ transform: translateX(100%);
986
+ opacity: 0;
987
+ }
988
+ to {
989
+ transform: translateX(0);
990
+ opacity: 1;
991
+ }
992
+ }
993
+
994
+ @keyframes slideOutRight {
995
+ from {
996
+ transform: translateX(0);
997
+ opacity: 1;
998
+ }
999
+ to {
1000
+ transform: translateX(100%);
1001
+ opacity: 0;
1002
+ }
1003
+ }
1004
+ `;
1005
+ document.head.appendChild(style);
1006
+
1007
+ // Initialize the interface
1008
+ const apkz = new APKZWebInterface();
1009
+
1010
+ // Global functions for onclick handlers
1011
+ window.clearJobs = () => apkz.clearJobs();
1012
+ window.downloadResults = (jobId, format) => apkz.downloadResults(jobId, format);
1013
+ window.closeModal = () => apkz.closeModal();
1014
+ window.toggleTheme = () => apkz.toggleTheme();
1015
+ </script>
1016
+ </body>
1017
+ </html>