droplyvictor89 commited on
Commit
c6f7418
·
verified ·
1 Parent(s): e1184a4

Upload 2 files

Browse files
Files changed (2) hide show
  1. index-3.html +1343 -0
  2. sw.js +198 -0
index-3.html ADDED
@@ -0,0 +1,1343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ro">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <link rel="icon" type="image/png" href="/logo.png">
7
+ <title>DROPLY! v2.1</title>
8
+ <style>
9
+ /* --- DESIGN SISTEMIC --- */
10
+ :root {
11
+ --bg: #000;
12
+ --card: #0a0a0a;
13
+ --accent: #fff;
14
+ --border: #1a1a1a;
15
+ --text-muted: #666;
16
+ --danger: #ff4444;
17
+ --terminal: #00ff41;
18
+ --success: #00ff00;
19
+ }
20
+
21
+ * { box-sizing: border-box; }
22
+
23
+ body {
24
+ background-color: var(--bg);
25
+ color: var(--accent);
26
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
27
+ margin: 0;
28
+ display: flex;
29
+ height: 100vh;
30
+ overflow: hidden;
31
+ }
32
+
33
+ /* --- SIDEBAR --- */
34
+ nav {
35
+ width: 240px;
36
+ background: #050505;
37
+ border-right: 1px solid var(--border);
38
+ display: none;
39
+ flex-direction: column;
40
+ padding: 20px 0;
41
+ flex-shrink: 0;
42
+ }
43
+
44
+ .nav-logo {
45
+ padding: 0 25px 30px;
46
+ font-size: 20px;
47
+ font-weight: 900;
48
+ letter-spacing: 2px;
49
+ }
50
+ .nav-version {
51
+ padding: 0 25px 10px;
52
+ font-size: 9px;
53
+ color: var(--terminal);
54
+ font-family: monospace;
55
+ }
56
+
57
+ .nav-item {
58
+ padding: 15px 25px;
59
+ cursor: pointer;
60
+ color: var(--text-muted);
61
+ text-transform: uppercase;
62
+ font-size: 11px;
63
+ font-weight: bold;
64
+ transition: 0.3s;
65
+ border-left: 3px solid transparent;
66
+ }
67
+
68
+ .nav-item:hover, .nav-item.active {
69
+ color: #fff;
70
+ background: #111;
71
+ border-left-color: #fff;
72
+ }
73
+
74
+ /* --- ZONA PRINCIPALĂ --- */
75
+ main {
76
+ flex-grow: 1;
77
+ overflow-y: auto;
78
+ padding: 40px;
79
+ display: flex;
80
+ flex-direction: column;
81
+ align-items: center;
82
+ }
83
+
84
+ .section {
85
+ width: 100%;
86
+ max-width: 600px;
87
+ display: none;
88
+ animation: fadeIn 0.4s ease;
89
+ }
90
+ .section.active { display: block; }
91
+
92
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
93
+
94
+ /* --- CARDURI & INPUTS --- */
95
+ .card {
96
+ background: var(--card);
97
+ border: 1px solid var(--border);
98
+ padding: 25px;
99
+ border-radius: 4px;
100
+ margin-bottom: 20px;
101
+ word-wrap: break-word;
102
+ overflow-wrap: break-word;
103
+ }
104
+
105
+ h2 {
106
+ font-size: 16px;
107
+ margin-bottom: 20px;
108
+ text-transform: uppercase;
109
+ border-left: 3px solid #fff;
110
+ padding-left: 10px;
111
+ }
112
+
113
+ label {
114
+ display: block;
115
+ font-size: 10px;
116
+ color: var(--text-muted);
117
+ margin-bottom: 8px;
118
+ text-transform: uppercase;
119
+ }
120
+
121
+ input, textarea, select {
122
+ width: 100%;
123
+ background: #000;
124
+ border: 1px solid var(--border);
125
+ color: #fff;
126
+ padding: 12px;
127
+ margin-bottom: 15px;
128
+ border-radius: 2px;
129
+ font-size: 14px;
130
+ box-sizing: border-box;
131
+ outline: none;
132
+ font-family: inherit;
133
+ }
134
+
135
+ input:focus, textarea:focus, select:focus { border-color: var(--terminal); }
136
+
137
+ button {
138
+ background: #fff;
139
+ color: #000;
140
+ border: none;
141
+ padding: 15px;
142
+ font-weight: bold;
143
+ text-transform: uppercase;
144
+ cursor: pointer;
145
+ width: 100%;
146
+ transition: 0.2s;
147
+ font-size: 12px;
148
+ }
149
+ button:hover { background: #ccc; }
150
+
151
+ .btn-secondary {
152
+ background: transparent;
153
+ color: #fff;
154
+ border: 1px solid #333;
155
+ }
156
+ .btn-secondary:hover { background: #111; }
157
+
158
+ .btn-danger {
159
+ background: var(--danger);
160
+ color: #fff;
161
+ }
162
+ .btn-danger:hover { background: #cc0000; }
163
+
164
+ .btn-small {
165
+ width: auto;
166
+ padding: 5px 10px;
167
+ font-size: 10px;
168
+ margin-left: 5px;
169
+ }
170
+
171
+ /* --- TERMINAL LOGS --- */
172
+ #terminalBox {
173
+ background: #000;
174
+ border: 1px solid #111;
175
+ color: var(--terminal);
176
+ font-family: 'Courier New', monospace;
177
+ font-size: 11px;
178
+ padding: 15px;
179
+ height: 150px;
180
+ overflow-y: auto;
181
+ margin-top: 15px;
182
+ border-radius: 4px;
183
+ line-height: 1.5;
184
+ }
185
+
186
+ /* --- MESAJE & GRUP --- */
187
+ .msg-card, .group-msg {
188
+ border-bottom: 1px solid #111;
189
+ padding: 10px 0;
190
+ word-wrap: break-word;
191
+ overflow-wrap: break-word;
192
+ word-break: break-word;
193
+ position: relative;
194
+ }
195
+
196
+ .msg-actions {
197
+ position: absolute;
198
+ top: 10px;
199
+ right: 0;
200
+ }
201
+
202
+ .group-header {
203
+ font-size: 10px;
204
+ color: var(--terminal);
205
+ margin-bottom: 2px;
206
+ }
207
+ .group-time {
208
+ font-size: 9px;
209
+ color: #444;
210
+ }
211
+
212
+ /* --- CONTACTE & LISTE --- */
213
+ .item-list {
214
+ border-bottom: 1px solid #111;
215
+ padding: 10px 0;
216
+ display: flex;
217
+ justify-content: space-between;
218
+ align-items: center;
219
+ }
220
+ .item-info { font-size: 13px; }
221
+
222
+ /* --- BADGES --- */
223
+ .badge {
224
+ background: #111;
225
+ border: 1px solid var(--terminal);
226
+ color: var(--terminal);
227
+ padding: 5px 10px;
228
+ font-size: 10px;
229
+ display: inline-block;
230
+ margin-bottom: 15px;
231
+ cursor: pointer;
232
+ }
233
+ .badge.active { background: var(--terminal); color: #000; }
234
+
235
+ .admin-badge {
236
+ background: #ff0000;
237
+ color: #fff;
238
+ border: none;
239
+ }
240
+
241
+ /* --- NOTIFICĂRI --- */
242
+ #notify {
243
+ position: fixed;
244
+ top: 20px;
245
+ right: 20px;
246
+ background: #fff;
247
+ color: #000;
248
+ padding: 10px 20px;
249
+ font-weight: bold;
250
+ z-index: 1000;
251
+ display: none;
252
+ border-radius: 4px;
253
+ box-shadow: 0 4px 12px rgba(255,255,255,0.2);
254
+ }
255
+
256
+ /* Warning Banner */
257
+ #warningBanner {
258
+ position: fixed;
259
+ top: 0;
260
+ left: 0;
261
+ right: 0;
262
+ background: #ff0000;
263
+ color: #fff;
264
+ padding: 15px;
265
+ text-align: center;
266
+ font-weight: bold;
267
+ z-index: 2000;
268
+ display: none;
269
+ animation: pulse 1s infinite;
270
+ }
271
+
272
+ @keyframes pulse {
273
+ 0%, 100% { opacity: 1; }
274
+ 50% { opacity: 0.7; }
275
+ }
276
+
277
+ /* --- MODAL POPUP --- */
278
+ .modal {
279
+ display: none;
280
+ position: fixed;
281
+ top: 0;
282
+ left: 0;
283
+ right: 0;
284
+ bottom: 0;
285
+ background: rgba(0,0,0,0.9);
286
+ z-index: 1500;
287
+ align-items: center;
288
+ justify-content: center;
289
+ }
290
+
291
+ .modal.active { display: flex; }
292
+
293
+ .modal-content {
294
+ background: var(--card);
295
+ border: 1px solid var(--border);
296
+ padding: 30px;
297
+ max-width: 500px;
298
+ width: 90%;
299
+ max-height: 80vh;
300
+ overflow-y: auto;
301
+ position: relative;
302
+ border-radius: 4px;
303
+ }
304
+
305
+ .modal-close {
306
+ position: absolute;
307
+ top: 15px;
308
+ right: 15px;
309
+ font-size: 24px;
310
+ cursor: pointer;
311
+ color: var(--text-muted);
312
+ }
313
+ .modal-close:hover { color: #fff; }
314
+
315
+ /* --- PING INDICATOR --- */
316
+ .ping-indicator {
317
+ display: inline-block;
318
+ width: 8px;
319
+ height: 8px;
320
+ border-radius: 50%;
321
+ background: var(--success);
322
+ margin-right: 5px;
323
+ animation: blink 2s infinite;
324
+ }
325
+
326
+ @keyframes blink {
327
+ 0%, 100% { opacity: 1; }
328
+ 50% { opacity: 0.3; }
329
+ }
330
+
331
+ /* --- RESPONSIVE --- */
332
+ @media (max-width: 768px) {
333
+ body { flex-direction: column; }
334
+ nav {
335
+ width: 100%;
336
+ height: auto;
337
+ flex-direction: row;
338
+ overflow-x: auto;
339
+ border-right: none;
340
+ border-bottom: 1px solid var(--border);
341
+ }
342
+ .nav-logo, .nav-version { display: none; }
343
+ main { padding: 20px; }
344
+ .section { max-width: 100%; }
345
+ }
346
+ </style>
347
+ </head>
348
+ <body>
349
+
350
+ <audio id="notifSound" src="/notification.mp3" preload="auto"></audio>
351
+
352
+ <div id="notify">ACȚIUNE REUȘITĂ</div>
353
+ <div id="warningBanner">
354
+ ⚠️ ATENȚIE! Serverul DROPLY! intră în mentenanță. Ne cerem scuze pentru inconvenient! ⚠️
355
+ </div>
356
+
357
+ <!-- MODAL pentru vizualizare mesaj inbox -->
358
+ <div id="messageModal" class="modal">
359
+ <div class="modal-content">
360
+ <span class="modal-close" onclick="closeMessageModal()">&times;</span>
361
+ <h2 id="modalFrom">De la:</h2>
362
+ <div id="modalTime" style="font-size: 11px; color: #666; margin-bottom: 15px;"></div>
363
+ <div id="modalContent" style="margin-bottom: 15px; white-space: pre-wrap;"></div>
364
+ <div id="modalFile"></div>
365
+ </div>
366
+ </div>
367
+
368
+ <nav id="sidebar">
369
+ <div class="nav-logo">DROPLY!</div>
370
+ <div class="nav-version">v2.1 STABLE</div>
371
+ <div class="nav-item active" onclick="showSection('home')">Dashboard</div>
372
+ <div class="nav-item" onclick="showSection('inbox')">Inbox</div>
373
+ <div class="nav-item" onclick="showSection('group')">Grup</div>
374
+ <div class="nav-item" onclick="showSection('send')">Trimite</div>
375
+ <div class="nav-item" onclick="showSection('contacts')">Agendă</div>
376
+ <div class="nav-item" onclick="showSection('settings')">Setări</div>
377
+ <div class="nav-item" id="adminNavItem" style="display:none; color: #ff0000;" onclick="showSection('admin')">Admin Panel</div>
378
+ <div class="nav-item" style="margin-top: auto; color: var(--danger);" onclick="logout()">Ieșire</div>
379
+ </nav>
380
+
381
+ <main> <!-- SECȚIUNE LOGIN -->
382
+ <div id="authSection" class="section active">
383
+ <div class="card">
384
+ <h2>Acces DROPLY! v2.1</h2>
385
+ <label>Email</label>
386
+ <input type="text" id="loginEmail" placeholder="Ex: user@droply.md">
387
+ <label>Parolă</label>
388
+ <input type="password" id="loginPass">
389
+ <button onclick="login()">Conectare</button>
390
+ </div>
391
+ <div class="card">
392
+ <h2>Creează Cont DROPLY!</h2>
393
+ <input type="text" id="regUser" placeholder="Username">
394
+ <input type="password" id="regPass" placeholder="Parolă">
395
+ <button class="btn-secondary" onclick="register()">Generează Cont</button>
396
+ </div>
397
+ </div>
398
+
399
+ <!-- DASHBOARD -->
400
+ <div id="home" class="section">
401
+ <div class="card">
402
+ <h2 id="userDisplay">Salut!</h2>
403
+ <div id="idBadge" style="font-family: monospace; color: #555;">ID: 0670...</div>
404
+ <div id="adminBadgeDisplay" style="display:none; margin-top: 10px;">
405
+ <span class="badge admin-badge">ADMIN ACCESS</span>
406
+ </div>
407
+
408
+ <h2 style="margin-top: 30px; border-left: 3px solid var(--terminal);">
409
+ <span class="ping-indicator"></span>Live System Logs
410
+ </h2>
411
+ <div id="terminalBox">
412
+ [SYSTEM] Waiting for authentication...
413
+ </div>
414
+
415
+ <button style="margin-top: 20px;" onclick="showAbout()">📋 About v2.1</button>
416
+ </div>
417
+ </div>
418
+
419
+ <!-- INBOX -->
420
+ <div id="inbox" class="section">
421
+ <div class="card">
422
+ <h2>Inbox & AI</h2>
423
+
424
+ <div id="saraToggle" class="badge" onclick="toggleSara()">SARA AI: OFF</div>
425
+
426
+ <div id="inboxList" style="max-height: 400px; overflow-y: auto;">
427
+ Inbox gol. ┐('~` )┌
428
+ </div>
429
+ </div>
430
+ </div>
431
+
432
+ <!-- GRUP GLOBAL -->
433
+ <div id="group" class="section">
434
+ <div class="card">
435
+ <h2>Grup Global DROPLY!</h2>
436
+
437
+ <div id="groupAccessInfo" style="margin-bottom: 15px; padding: 10px; background: #111; border-left: 3px solid var(--terminal);">
438
+ <div id="groupMemberText" style="display:none;">
439
+ ✅ Ai acces la grup
440
+ </div>
441
+ <div id="groupNonMemberText" style="display:none;">
442
+ ⚠️ Nu ai acces la grup. Poți vizualiza mesajele dar nu poți trimite.
443
+ <button style="margin-top: 10px;" onclick="requestGroupInvite()">Cere Invitație</button>
444
+ </div>
445
+ </div>
446
+
447
+ <div id="groupList" style="max-height: 300px; overflow-y: auto; margin-bottom: 15px;"></div>
448
+
449
+ <div id="groupInputSection">
450
+ <textarea id="groupMsgInput" placeholder="Scrie mesaj..." style="height: 80px;"></textarea>
451
+ <button onclick="sendGroup()">Trimite în Grup</button>
452
+ </div>
453
+ </div>
454
+ </div>
455
+
456
+ <!-- TRIMITE MESAJ PRIVAT -->
457
+ <div id="send" class="section">
458
+ <div class="card">
459
+ <h2>Trimite Mesaj Privat</h2>
460
+ <label>ID Destinatar</label>
461
+ <input type="text" id="targetDest" placeholder="Ex: a1b2c3d4e5f6">
462
+ <label>Mesaj</label>
463
+ <textarea id="targetMsg" placeholder="Scrie mesajul..." style="height: 100px;"></textarea>
464
+ <label>Fișier Atașat (Opțional)</label>
465
+ <input type="file" id="targetFile">
466
+ <button onclick="sendMail()">📤 Trimite</button>
467
+
468
+ <button class="btn-secondary" style="margin-top: 15px;" onclick="reportBug()">
469
+ 🐛 Raportare Bug
470
+ </button>
471
+ </div>
472
+ </div>
473
+
474
+ <!-- AGENDĂ CONTACTE -->
475
+ <div id="contacts" class="section">
476
+ <div class="card">
477
+ <h2>Agendă Contacte</h2>
478
+ <input type="text" id="cName" placeholder="Nume contact">
479
+ <input type="text" id="cId" placeholder="ID utilizator">
480
+ <button onclick="saveContact()">Salvează Contact</button>
481
+
482
+ <h2 style="margin-top: 30px;">Lista Contacte</h2>
483
+ <div id="contactList"></div>
484
+ </div>
485
+ </div>
486
+
487
+ <!-- SETĂRI -->
488
+ <div id="settings" class="section">
489
+ <div class="card">
490
+ <h2>Setări Cont</h2>
491
+
492
+ <label>Vizibilitate Profil</label>
493
+ <select id="setVis">
494
+ <option value="public">Public</option>
495
+ <option value="private">Privat</option>
496
+ </select>
497
+
498
+ <label>Cod Secret (pentru profil privat)</label>
499
+ <input type="text" id="setSecret" placeholder="Ex: ABC123">
500
+
501
+ <label>Notificări Push</label>
502
+ <select id="setNotif">
503
+ <option value="1">Activate</option>
504
+ <option value="0">Dezactivate</option>
505
+ </select>
506
+
507
+ <button onclick="updateSettings()">💾 Salvează Setări</button>
508
+ </div>
509
+
510
+ <div class="card">
511
+ <h2>Resetare Parolă</h2>
512
+ <label>Parolă Veche</label>
513
+ <input type="password" id="oldPass" placeholder="Parolă actuală">
514
+ <label>Parolă Nouă</label>
515
+ <input type="password" id="newPass" placeholder="Parolă nouă">
516
+ <button onclick="changePassword()">🔒 Schimbă Parola</button>
517
+ </div>
518
+
519
+ <div class="card">
520
+ <h2>Server Status</h2>
521
+ <div style="display: flex; align-items: center; margin-bottom: 15px;">
522
+ <span class="ping-indicator"></span>
523
+ <span id="serverPing">Checking...</span>
524
+ </div>
525
+ <button onclick="checkPing()">🔄 Verifică Ping</button>
526
+ </div>
527
+ </div>
528
+
529
+ <!-- ADMIN PANEL -->
530
+ <div id="admin" class="section">
531
+ <div class="card">
532
+ <h2 style="color: #ff0000;">🔐 Admin Panel</h2>
533
+
534
+ <h2 style="margin-top: 20px;">Utilizatori Conectați</h2>
535
+ <div id="adminUsersList" style="max-height: 200px; overflow-y: auto;"></div>
536
+
537
+ <button onclick="loadAdminData()" style="margin-top: 15px;">🔄 Refresh Date</button>
538
+ </div>
539
+
540
+ <div class="card">
541
+ <h2>Cereri Invitații Grup</h2>
542
+ <div id="adminInvitesList" style="max-height: 200px; overflow-y: auto;"></div>
543
+ </div>
544
+
545
+ <div class="card">
546
+ <h2 style="color: #ff0000;">⚠️ Maintenance Mode</h2>
547
+ <p style="font-size: 12px; color: #666; margin-bottom: 15px;">
548
+ Această acțiune va trimite un warning către TOȚI utilizatorii și va închide serverul în 60 de secunde.
549
+ </p>
550
+ <button class="btn-danger" onclick="triggerMaintenance()">
551
+ 🚨 ACTIVEAZĂ MENTENANȚĂ
552
+ </button>
553
+ </div>
554
+ </div>
555
+
556
+ </main>
557
+
558
+ <script>
559
+ let lastMsgCount = 0;
560
+ let isUserLogged = false;
561
+ let saraEnabled = false;
562
+ let currentUserData = null;
563
+ let serviceWorkerRegistration = null;
564
+
565
+ // ===== SERVICE WORKER & NOTIFICĂRI ÎMBUNĂTĂȚITE =====
566
+
567
+ if ('serviceWorker' in navigator) {
568
+ navigator.serviceWorker.register('/sw.js')
569
+ .then(registration => {
570
+ console.log('✅ Service Worker înregistrat:', registration);
571
+ serviceWorkerRegistration = registration;
572
+ addLog('SYSTEM: Service Worker active');
573
+
574
+ // Cere permisiune pentru notificări
575
+ requestNotificationPermission();
576
+
577
+ // Ascultă mesaje de la Service Worker
578
+ navigator.serviceWorker.addEventListener('message', event => {
579
+ console.log('📩 Message from SW:', event.data);
580
+
581
+ if (event.data.type === 'PLAY_SOUND') {
582
+ playNotificationSound();
583
+ }
584
+
585
+ if (event.data.type === 'CHECK_INBOX') {
586
+ refreshInbox();
587
+ }
588
+ });
589
+ })
590
+ .catch(err => {
591
+ console.error('❌ Service Worker eșuat:', err);
592
+ addLog('ERROR: Service Worker failed');
593
+ });
594
+ }
595
+
596
+ // Funcție pentru cerere permisiune notificări
597
+ async function requestNotificationPermission() {
598
+ if (!('Notification' in window)) {
599
+ console.log('Browser-ul nu suportă notificări');
600
+ addLog('WARNING: Browser does not support notifications');
601
+ return false;
602
+ }
603
+
604
+ if (Notification.permission === 'granted') {
605
+ console.log('✅ Notificări deja permise');
606
+ addLog('SYSTEM: Notifications enabled');
607
+ return true;
608
+ }
609
+
610
+ if (Notification.permission !== 'denied') {
611
+ const permission = await Notification.requestPermission();
612
+ if (permission === 'granted') {
613
+ console.log('✅ Permisiune notificări acordată');
614
+ addLog('SYSTEM: Notifications enabled');
615
+
616
+ // Test notificare
617
+ new Notification('DROPLY! v2.1', {
618
+ body: 'Notificările funcționează! 🎉',
619
+ icon: '/logo.png',
620
+ tag: 'test-notification'
621
+ });
622
+ return true;
623
+ } else {
624
+ console.log('❌ Permisiune notificări refuzată');
625
+ addLog('WARNING: Notification permission denied');
626
+ return false;
627
+ }
628
+ }
629
+
630
+ return false;
631
+ }
632
+
633
+ // Funcție pentru redare sunet notificare
634
+ function playNotificationSound() {
635
+ try {
636
+ const audio = document.getElementById('notifSound');
637
+ if (audio) {
638
+ audio.currentTime = 0;
639
+ audio.play().catch(e => {
640
+ console.log('Autoplay blocat:', e);
641
+ });
642
+ }
643
+ } catch (e) {
644
+ console.error('Eroare sunet:', e);
645
+ }
646
+ }
647
+
648
+ // Funcție pentru trimitere notificare către Service Worker
649
+ function sendNotificationToSW(from, content) {
650
+ if (serviceWorkerRegistration && serviceWorkerRegistration.active) {
651
+ serviceWorkerRegistration.active.postMessage({
652
+ type: 'NEW_MESSAGE',
653
+ from: from,
654
+ content: content
655
+ });
656
+ }
657
+ }
658
+
659
+ // Auto-login
660
+ window.addEventListener('DOMContentLoaded', async () => {
661
+ const sessionToken = localStorage.getItem('droply_session_token');
662
+
663
+ if (sessionToken) {
664
+ addLog('SYSTEM: Checking saved session...');
665
+
666
+ try {
667
+ const response = await fetch('/api/check_session', {
668
+ method: 'POST',
669
+ headers: {'Content-Type': 'application/json'},
670
+ body: JSON.stringify({session_token: sessionToken})
671
+ });
672
+
673
+ const data = await response.json();
674
+
675
+ if (data.success) {
676
+ performLogin(data);
677
+ } else {
678
+ localStorage.removeItem('droply_session_token');
679
+ addLog('SYSTEM: Session expired, please login.');
680
+ }
681
+ } catch(e) {
682
+ addLog('ERROR: Failed to check session');
683
+ }
684
+ } else {
685
+ addLog('SYSTEM: Waiting for authentication...');
686
+ }
687
+
688
+ // Check pentru shutdown warning
689
+ setInterval(checkShutdown, 5000);
690
+ });
691
+
692
+ // ===== FUNCȚII UTILITARE =====
693
+
694
+ function showSection(id) {
695
+ document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
696
+ document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
697
+ document.getElementById(id).classList.add('active');
698
+
699
+ const items = document.querySelectorAll('.nav-item');
700
+ items.forEach(item => {
701
+ if(item.innerText.toLowerCase().includes(id) ||
702
+ (id === 'home' && item.innerText.includes('Dashboard')) ||
703
+ (id === 'admin' && item.innerText.includes('Admin'))) {
704
+ item.classList.add('active');
705
+ }
706
+ });
707
+
708
+ addLog(`Navigated to: ${id.toUpperCase()}`);
709
+
710
+ // Dacă e admin panel, încarcă datele
711
+ if(id === 'admin' && currentUserData && currentUserData.is_admin) {
712
+ loadAdminData();
713
+ }
714
+ }
715
+
716
+ function addLog(text) {
717
+ const terminal = document.getElementById('terminalBox');
718
+ const time = new Date().toLocaleTimeString();
719
+ terminal.innerHTML += `<div>[${time}] ${text}</div>`;
720
+ terminal.scrollTop = terminal.scrollHeight;
721
+ }
722
+
723
+ function showNotify(text) {
724
+ const n = document.getElementById('notify');
725
+ n.innerText = text;
726
+ n.style.display = 'block';
727
+ setTimeout(() => n.style.display = 'none', 3000);
728
+ }
729
+
730
+ function logout() {
731
+ localStorage.removeItem('droply_session_token');
732
+ location.reload();
733
+ }
734
+
735
+ // ===== LOGIN & REGISTER =====
736
+
737
+ async function login() {
738
+ const email = document.getElementById('loginEmail').value;
739
+ const pass = document.getElementById('loginPass').value;
740
+
741
+ const r = await fetch('/api/login', {
742
+ method: 'POST',
743
+ headers: {'Content-Type': 'application/json'},
744
+ body: JSON.stringify({email: email, password: pass})
745
+ });
746
+
747
+ const d = await r.json();
748
+
749
+ if(d.success) {
750
+ localStorage.setItem('droply_session_token', d.session_token);
751
+ performLogin(d);
752
+ } else {
753
+ addLog(`FAILED: Login attempt for ${email}`);
754
+ alert("Eroare la logare!");
755
+ }
756
+ }
757
+
758
+ function performLogin(data) {
759
+ isUserLogged = true;
760
+ currentUserData = data.user;
761
+
762
+ document.getElementById('authSection').classList.remove('active');
763
+ document.getElementById('sidebar').style.display = 'flex';
764
+ document.getElementById('home').classList.add('active');
765
+ document.getElementById('userDisplay').innerText = data.email;
766
+ document.getElementById('idBadge').innerText = "ID: " + data.user.id;
767
+ document.getElementById('setVis').value = data.user.visibility;
768
+ document.getElementById('setSecret').value = data.user.secret_code;
769
+ document.getElementById('setNotif').value = data.user.notif_enabled;
770
+
771
+ // Sara AI
772
+ if(data.user.sara_ai) {
773
+ saraEnabled = true;
774
+ document.getElementById('saraToggle').classList.add('active');
775
+ document.getElementById('saraToggle').innerText = "SARA AI: ONLINE";
776
+ }
777
+
778
+ // Admin access
779
+ if(data.user.is_admin) {
780
+ document.getElementById('adminNavItem').style.display = 'block';
781
+ document.getElementById('adminBadgeDisplay').style.display = 'block';
782
+ addLog('ADMIN: Full access granted');
783
+ }
784
+
785
+ // Group access
786
+ if(data.user.is_group_member) {
787
+ document.getElementById('groupMemberText').style.display = 'block';
788
+ document.getElementById('groupNonMemberText').style.display = 'none';
789
+ } else {
790
+ document.getElementById('groupMemberText').style.display = 'none';
791
+ document.getElementById('groupNonMemberText').style.display = 'block';
792
+ document.getElementById('groupInputSection').style.display = 'none';
793
+ }
794
+
795
+ addLog(`SUCCESS: Session started for ${data.email}`);
796
+ addLog(`UID IDENTIFIED: ${data.user.id}`);
797
+
798
+ // Auto-refresh
799
+ setInterval(() => {
800
+ refreshInbox();
801
+ refreshGroup();
802
+ }, 500);
803
+
804
+ refreshInbox();
805
+ loadContacts();
806
+ checkPing();
807
+ }
808
+
809
+ async function register() {
810
+ const r = await fetch('/api/register', {
811
+ method: 'POST',
812
+ headers: {'Content-Type': 'application/json'},
813
+ body: JSON.stringify({
814
+ username: document.getElementById('regUser').value,
815
+ password: document.getElementById('regPass').value
816
+ })
817
+ });
818
+ const d = await r.json();
819
+ if(d.success) {
820
+ alert("Cont creat! Email: " + d.email + " | ID: " + d.uid);
821
+ addLog(`IDENTITY GENERATED: ${d.uid}`);
822
+ } else {
823
+ alert("Eroare: " + d.error);
824
+ }
825
+ }
826
+
827
+ // ===== SARA AI =====
828
+
829
+ function toggleSara() {
830
+ saraEnabled = !saraEnabled;
831
+ const btn = document.getElementById('saraToggle');
832
+ if(saraEnabled) {
833
+ btn.classList.add('active');
834
+ btn.innerText = "SARA AI: ONLINE";
835
+ addLog("SARA AI: Activated auto-responder.");
836
+ updateSettings();
837
+ } else {
838
+ btn.classList.remove('active');
839
+ btn.innerText = "SARA AI: OFF";
840
+ addLog("SARA AI: Deactivated.");
841
+ updateSettings();
842
+ }
843
+ }
844
+
845
+
846
+ // ===== INBOX =====
847
+
848
+ async function refreshInbox() {
849
+ if(!isUserLogged) return;
850
+
851
+ try {
852
+ const r = await fetch('/api/get_inbox');
853
+ const list = await r.json();
854
+
855
+ // Verificare mesaje noi
856
+ if(list.length > lastMsgCount) {
857
+ if(lastMsgCount !== 0 && currentUserData && currentUserData.notif_enabled) {
858
+ const lastMessage = list[list.length-1];
859
+
860
+ // Redăm sunetul
861
+ playNotificationSound();
862
+
863
+ // Arătăm banner-ul
864
+ showNotify("MESAJ NOU!");
865
+
866
+ // Log în terminal
867
+ addLog(`INCOMING: New message from ${lastMessage.from}`);
868
+
869
+ // Browser notification (funcționează și când tab-ul e în fundal)
870
+ if('Notification' in window && Notification.permission === 'granted') {
871
+ // Verificăm dacă documentul e vizibil
872
+ if (document.hidden) {
873
+ // Tab-ul e în fundal, folosim Service Worker
874
+ sendNotificationToSW(lastMessage.from, lastMessage.content);
875
+ } else {
876
+ // Tab-ul e activ, notificare directă
877
+ new Notification('DROPLY! - Mesaj Nou', {
878
+ body: `De la: ${lastMessage.from}\n${lastMessage.content.substring(0, 50)}${lastMessage.content.length > 50 ? '...' : ''}`,
879
+ icon: '/logo.png',
880
+ badge: '/logo.png',
881
+ vibrate: [200, 100, 200],
882
+ tag: 'new-message-' + Date.now(),
883
+ requireInteraction: false
884
+ });
885
+ }
886
+ }
887
+ }
888
+ lastMsgCount = list.length;
889
+ }
890
+
891
+ const container = document.getElementById('inboxList');
892
+ container.innerHTML = list.length ? "" : "Inbox gol. ┐('~` )┌";
893
+
894
+ list.reverse().forEach(m => {
895
+ const div = document.createElement('div');
896
+ div.className = "msg-card";
897
+ div.innerHTML = `
898
+ <div class="msg-actions">
899
+ <button class="btn-small btn-danger" onclick="deleteMessage(${m.id})">🗑️</button>
900
+ </div>
901
+ <small style="cursor: pointer;" onclick="openMessageModal(${m.id}, '${m.from}', '${m.timestamp}', \`${m.content.replace(/`/g, '\\`')}\`, '${m.file || ''}')">
902
+ ${m.from} | ${m.timestamp}
903
+ </small><br>
904
+ <div style="cursor: pointer; margin-top: 5px;" onclick="openMessageModal(${m.id}, '${m.from}', '${m.timestamp}', \`${m.content.replace(/`/g, '\\`')}\`, '${m.file || ''}')">
905
+ ${m.content.substring(0, 50)}${m.content.length > 50 ? '...' : ''}
906
+ </div>
907
+ `;
908
+ container.appendChild(div);
909
+ });
910
+ } catch (e) {
911
+ console.error("Refresh inbox error:", e);
912
+ }
913
+ }
914
+
915
+ function openMessageModal(id, from, time, content, file) {
916
+ document.getElementById('modalFrom').innerText = `De la: ${from}`;
917
+ document.getElementById('modalTime').innerText = time;
918
+ document.getElementById('modalContent').innerText = content;
919
+
920
+ if(file) {
921
+ document.getElementById('modalFile').innerHTML = `
922
+ <a href="/download/${file}" target="_blank" download style="color:#fff; background:#222; padding:8px; text-decoration:none; display:inline-block; border-radius:2px;">
923
+ 📎 Descarcă: ${file}
924
+ </a>
925
+ `;
926
+ } else {
927
+ document.getElementById('modalFile').innerHTML = '';
928
+ }
929
+
930
+ document.getElementById('messageModal').classList.add('active');
931
+ }
932
+
933
+ function closeMessageModal() {
934
+ document.getElementById('messageModal').classList.remove('active');
935
+ }
936
+
937
+ async function deleteMessage(msgId) {
938
+ if(!confirm('Ștergi acest mesaj?')) return;
939
+
940
+ await fetch('/api/delete_message', {
941
+ method: 'POST',
942
+ headers: {'Content-Type': 'application/json'},
943
+ body: JSON.stringify({message_id: msgId})
944
+ });
945
+
946
+ showNotify("MESAJ ȘTERS");
947
+ refreshInbox();
948
+ }
949
+
950
+ // ===== SEND MAIL =====
951
+
952
+ async function sendMail() {
953
+ const fd = new FormData();
954
+ const dest = document.getElementById('targetDest').value;
955
+ fd.append('destinatar', dest);
956
+ fd.append('mesaj', document.getElementById('targetMsg').value);
957
+ const file = document.getElementById('targetFile').files[0];
958
+ if(file) fd.append('fisier', file);
959
+
960
+ addLog(`SENDING: Data packet to ${dest}...`);
961
+
962
+ const r = await fetch('/api/send_mail', { method: 'POST', body: fd });
963
+ const d = await r.json();
964
+
965
+ if(d.success) {
966
+ showNotify("TRIMIS!");
967
+ document.getElementById('targetMsg').value = "";
968
+ document.getElementById('targetFile').value = "";
969
+ addLog(`SUCCESS: Packet delivered to ${dest}`);
970
+ } else {
971
+ addLog(`ERROR: ${d.error}`);
972
+ alert("Eroare: " + d.error);
973
+ }
974
+ }
975
+
976
+ function reportBug() {
977
+ const now = new Date();
978
+ const dateStr = `${now.getDate()}/${now.getMonth()+1}/${now.getFullYear()}`;
979
+ const timeStr = `${now.getHours()}:${String(now.getMinutes()).padStart(2, '0')}`;
980
+
981
+ document.getElementById('targetDest').value = 'admin001';
982
+ document.getElementById('targetMsg').value = `Raportare Bug: [${dateStr} | Ora ${timeStr}]\n\n`;
983
+
984
+ showSection('send');
985
+ showNotify("Completează raportarea!");
986
+ addLog("BUG REPORT: Template loaded");
987
+ }
988
+
989
+ // ===== GRUP =====
990
+
991
+ async function sendGroup() {
992
+ const msg = document.getElementById('groupMsgInput').value;
993
+ if(!msg) return;
994
+
995
+ const r = await fetch('/api/group/send', {
996
+ method: 'POST',
997
+ headers: {'Content-Type': 'application/json'},
998
+ body: JSON.stringify({content: msg})
999
+ });
1000
+
1001
+ const d = await r.json();
1002
+
1003
+ if(d.success) {
1004
+ document.getElementById('groupMsgInput').value = "";
1005
+ refreshGroup();
1006
+ } else {
1007
+ alert("Eroare: " + d.error);
1008
+ }
1009
+ }
1010
+
1011
+ async function refreshGroup() {
1012
+ if(!isUserLogged) return;
1013
+
1014
+ try {
1015
+ const r = await fetch('/api/group/get');
1016
+ const msgs = await r.json();
1017
+ const box = document.getElementById('groupList');
1018
+
1019
+ box.innerHTML = msgs.map(m => `
1020
+ <div class="group-msg">
1021
+ <div class="group-header">
1022
+ ${m.user} <span class="group-time">[${m.timestamp}]</span>
1023
+ ${currentUserData && currentUserData.is_admin ?
1024
+ `<button class="btn-small btn-danger" onclick="deleteGroupMessage(${m.id})" style="float:right;">🗑️</button>`
1025
+ : ''}
1026
+ </div>
1027
+ <div style="color:#fff;">${m.content}</div>
1028
+ </div>
1029
+ `).join('');
1030
+ } catch (e) {
1031
+ console.error("Group refresh error:", e);
1032
+ }
1033
+ }
1034
+
1035
+ async function deleteGroupMessage(msgId) {
1036
+ if(!confirm('Ștergi acest mesaj din grup?')) return;
1037
+
1038
+ await fetch('/api/group/delete', {
1039
+ method: 'POST',
1040
+ headers: {'Content-Type': 'application/json'},
1041
+ body: JSON.stringify({message_id: msgId})
1042
+ });
1043
+
1044
+ showNotify("MESAJ ȘTERS");
1045
+ refreshGroup();
1046
+ }
1047
+
1048
+ async function requestGroupInvite() {
1049
+ const r = await fetch('/api/group/request_invite', { method: 'POST' });
1050
+ const d = await r.json();
1051
+
1052
+ if(d.success) {
1053
+ showNotify("CERERE TRIMISĂ!");
1054
+ addLog("GROUP: Invitation request sent to admin");
1055
+ } else {
1056
+ alert("Eroare: " + d.error);
1057
+ }
1058
+ }
1059
+
1060
+ // ===== CONTACTE =====
1061
+
1062
+ function saveContact() {
1063
+ const name = document.getElementById('cName').value;
1064
+ const id = document.getElementById('cId').value;
1065
+ if(!name || !id) return;
1066
+
1067
+ let contacts = JSON.parse(localStorage.getItem('droply_contacts') || '[]');
1068
+ contacts.push({name, id});
1069
+ localStorage.setItem('droply_contacts', JSON.stringify(contacts));
1070
+
1071
+ document.getElementById('cName').value = "";
1072
+ document.getElementById('cId').value = "";
1073
+ loadContacts();
1074
+ showNotify("CONTACT SALVAT");
1075
+ addLog(`CONTACT SAVED: ${name} (${id})`);
1076
+ }
1077
+
1078
+ function loadContacts() {
1079
+ const contacts = JSON.parse(localStorage.getItem('droply_contacts') || '[]');
1080
+ const container = document.getElementById('contactList');
1081
+ container.innerHTML = "";
1082
+
1083
+ contacts.forEach((c, index) => {
1084
+ const div = document.createElement('div');
1085
+ div.className = "item-list";
1086
+ div.innerHTML = `
1087
+ <div class="item-info"><b>${c.name}</b><br><small>${c.id}</small></div>
1088
+ <div>
1089
+ <button class="btn-small" onclick="useContact('${c.id}')">USE</button>
1090
+ <button class="btn-small btn-danger" onclick="delContact(${index})">X</button>
1091
+ </div>
1092
+ `;
1093
+ container.appendChild(div);
1094
+ });
1095
+ }
1096
+
1097
+ function useContact(id) {
1098
+ document.getElementById('targetDest').value = id;
1099
+ showSection('send');
1100
+ }
1101
+
1102
+ function delContact(i) {
1103
+ let contacts = JSON.parse(localStorage.getItem('droply_contacts') || '[]');
1104
+ contacts.splice(i, 1);
1105
+ localStorage.setItem('droply_contacts', JSON.stringify(contacts));
1106
+ loadContacts();
1107
+ addLog(`CONTACT DELETED`);
1108
+ }
1109
+
1110
+
1111
+ // ===== SETĂRI =====
1112
+
1113
+ async function updateSettings() {
1114
+ const notif = parseInt(document.getElementById('setNotif').value);
1115
+
1116
+ await fetch('/api/update_settings', {
1117
+ method: 'POST',
1118
+ headers: {'Content-Type': 'application/json'},
1119
+ body: JSON.stringify({
1120
+ visibility: document.getElementById('setVis').value,
1121
+ secret_code: document.getElementById('setSecret').value,
1122
+ sara_ai: saraEnabled,
1123
+ notif_enabled: notif
1124
+ })
1125
+ });
1126
+
1127
+ // Actualizăm currentUserData
1128
+ if(currentUserData) {
1129
+ currentUserData.notif_enabled = notif;
1130
+ }
1131
+
1132
+ showNotify("SETĂRI SALVATE");
1133
+ addLog(`SETTINGS: Profile updated. Sara AI: ${saraEnabled}, Notif: ${notif ? 'ON' : 'OFF'}`);
1134
+ }
1135
+
1136
+ async function changePassword() {
1137
+ const oldPass = document.getElementById('oldPass').value;
1138
+ const newPass = document.getElementById('newPass').value;
1139
+
1140
+ if(!oldPass || !newPass) {
1141
+ alert("Completează ambele câmpuri!");
1142
+ return;
1143
+ }
1144
+
1145
+ const r = await fetch('/api/change_password', {
1146
+ method: 'POST',
1147
+ headers: {'Content-Type': 'application/json'},
1148
+ body: JSON.stringify({old_password: oldPass, new_password: newPass})
1149
+ });
1150
+
1151
+ const d = await r.json();
1152
+
1153
+ if(d.success) {
1154
+ showNotify("PAROLĂ SCHIMBATĂ!");
1155
+ document.getElementById('oldPass').value = "";
1156
+ document.getElementById('newPass').value = "";
1157
+ addLog("SECURITY: Password changed successfully");
1158
+ } else {
1159
+ alert("Eroare: " + d.error);
1160
+ }
1161
+ }
1162
+
1163
+ async function checkPing() {
1164
+ try {
1165
+ const start = Date.now();
1166
+ const r = await fetch('/api/server_ping');
1167
+ const ping = Date.now() - start;
1168
+
1169
+ document.getElementById('serverPing').innerText = `Server Online - Ping: ${ping}ms`;
1170
+ addLog(`PING: Server responded in ${ping}ms`);
1171
+ } catch(e) {
1172
+ document.getElementById('serverPing').innerText = 'Server Offline';
1173
+ addLog('PING: Server not responding');
1174
+ }
1175
+ }
1176
+
1177
+ // ===== ADMIN PANEL =====
1178
+
1179
+ async function loadAdminData() {
1180
+ if(!currentUserData || !currentUserData.is_admin) return;
1181
+
1182
+ // Load users
1183
+ const rUsers = await fetch('/api/admin/get_users');
1184
+ const dUsers = await rUsers.json();
1185
+
1186
+ if(dUsers.success) {
1187
+ const container = document.getElementById('adminUsersList');
1188
+ container.innerHTML = '';
1189
+
1190
+ dUsers.users.forEach(u => {
1191
+ const div = document.createElement('div');
1192
+ div.className = 'item-list';
1193
+ div.innerHTML = `
1194
+ <div class="item-info">
1195
+ <b>${u.email}</b><br>
1196
+ <small>ID: ${u.id} | IP: ${u.ip}</small>
1197
+ </div>
1198
+ `;
1199
+ container.appendChild(div);
1200
+ });
1201
+
1202
+ addLog(`ADMIN: Loaded ${dUsers.users.length} users`);
1203
+ }
1204
+
1205
+ // Load group invitations
1206
+ const rInvites = await fetch('/api/admin/get_invitations');
1207
+ const dInvites = await rInvites.json();
1208
+
1209
+ if(dInvites.success) {
1210
+ const container = document.getElementById('adminInvitesList');
1211
+ container.innerHTML = '';
1212
+
1213
+ if(dInvites.invitations.length === 0) {
1214
+ container.innerHTML = '<p style="color:#666; font-size:12px;">Nu există cereri pendinte</p>';
1215
+ } else {
1216
+ dInvites.invitations.forEach(inv => {
1217
+ const div = document.createElement('div');
1218
+ div.className = 'item-list';
1219
+ div.innerHTML = `
1220
+ <div class="item-info">
1221
+ <b>${inv.user_email}</b><br>
1222
+ <small>${inv.timestamp}</small>
1223
+ </div>
1224
+ <button class="btn-small" onclick="approveInvite(${inv.id})">✅ Aprobă</button>
1225
+ `;
1226
+ container.appendChild(div);
1227
+ });
1228
+ }
1229
+ }
1230
+ }
1231
+
1232
+ async function approveInvite(inviteId) {
1233
+ const r = await fetch('/api/admin/approve_invitation', {
1234
+ method: 'POST',
1235
+ headers: {'Content-Type': 'application/json'},
1236
+ body: JSON.stringify({invitation_id: inviteId})
1237
+ });
1238
+
1239
+ const d = await r.json();
1240
+
1241
+ if(d.success) {
1242
+ showNotify("INVITAȚIE APROBATĂ!");
1243
+ loadAdminData();
1244
+ addLog("ADMIN: Group invitation approved");
1245
+ }
1246
+ }
1247
+
1248
+ async function triggerMaintenance() {
1249
+ if(!confirm('ATENȚIE! Această acțiune va închide serverul în 60 de secunde! Continui?')) {
1250
+ return;
1251
+ }
1252
+
1253
+ const r = await fetch('/api/admin/broadcast_warning', { method: 'POST' });
1254
+ const d = await r.json();
1255
+
1256
+ if(d.success) {
1257
+ showNotify("⚠️ MAINTENANCE ACTIVAT!");
1258
+ addLog("ADMIN: Maintenance mode activated - shutdown in 60s");
1259
+
1260
+ // Arătăm banner-ul
1261
+ document.getElementById('warningBanner').style.display = 'block';
1262
+
1263
+ // Countdown
1264
+ let seconds = 60;
1265
+ const countdown = setInterval(() => {
1266
+ seconds--;
1267
+ document.getElementById('warningBanner').innerText =
1268
+ `⚠️ ATENȚIE! Serverul se închide în ${seconds} secunde! ⚠️`;
1269
+
1270
+ if(seconds <= 0) {
1271
+ clearInterval(countdown);
1272
+ document.getElementById('warningBanner').innerText =
1273
+ '🔴 SERVER OFFLINE - Mentenanță în curs...';
1274
+ }
1275
+ }, 1000);
1276
+ }
1277
+ }
1278
+
1279
+ async function checkShutdown() {
1280
+ try {
1281
+ const r = await fetch('/api/check_shutdown');
1282
+ const d = await r.json();
1283
+
1284
+ if(d.shutdown_scheduled && !document.getElementById('warningBanner').style.display) {
1285
+ document.getElementById('warningBanner').style.display = 'block';
1286
+ addLog("WARNING: Server shutdown scheduled!");
1287
+ }
1288
+ } catch(e) {
1289
+ // Server might be down
1290
+ }
1291
+ }
1292
+
1293
+ // ===== ABOUT =====
1294
+
1295
+ function showAbout() {
1296
+ alert(`
1297
+ ╔════════════════════════════════════╗
1298
+ ║ DROPLY! v2.1 - CHANGELOG ║
1299
+ ╚════════════════════════════════════╝
1300
+
1301
+ ✨ FUNCȚII NOI v2.1:
1302
+
1303
+ 🔐 SETĂRI:
1304
+ ✅ Resetare parolă din setări
1305
+ ✅ Activare/Dezactivare notificări push
1306
+ ✅ Verificare ping server
1307
+
1308
+ 👥 GRUP:
1309
+ ✅ Acces controlat de admin
1310
+ ✅ Cerere invitație pentru grup
1311
+ ✅ Ștergere mesaje (doar admin)
1312
+ ✅ Vizualizare mesaje pentru non-membri
1313
+
1314
+ 📧 INBOX:
1315
+ ✅ Pop-up detaliat pentru mesaje
1316
+ ✅ Ștergere mesaje din inbox
1317
+ ✅ Vizualizare fișiere atașate
1318
+
1319
+ 📤 TRIMITERE:
1320
+ ✅ Simplificare (fără cod secret)
1321
+ ✅ Buton raportare bug rapid
1322
+
1323
+ 🔧 ADMIN PANEL (doar admin):
1324
+ ✅ Vizualizare utilizatori + IP
1325
+ ✅ Aprobare invitații grup
1326
+ ✅ Warning broadcast + shutdown
1327
+
1328
+ 🎯 ÎMBUNĂTĂȚIRI:
1329
+ ✅ Auto-login persistent
1330
+ ✅ Notificări browser
1331
+ ✅ Service Worker pentru fundal
1332
+ ✅ UI mai clean și responsive
1333
+
1334
+ ═══════════════════════════════════
1335
+
1336
+ 📝 Admin: victor89@admindroply.md
1337
+ 🔑 Parolă: 122012
1338
+ `);
1339
+ }
1340
+
1341
+ </script>
1342
+ </body>
1343
+ </html>
sw.js ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Service Worker pentru DROPLY! v2.1 - Notificări Complete
2
+ const CACHE_NAME = 'droply-v2.1';
3
+ const urlsToCache = [
4
+ '/',
5
+ '/logo.png',
6
+ '/notification.mp3'
7
+ ];
8
+
9
+ let lastMessageCount = 0;
10
+
11
+ // Instalare Service Worker
12
+ self.addEventListener('install', event => {
13
+ console.log('[SW] Installing...');
14
+ event.waitUntil(
15
+ caches.open(CACHE_NAME)
16
+ .then(cache => {
17
+ console.log('[SW] Caching files');
18
+ return cache.addAll(urlsToCache);
19
+ })
20
+ );
21
+ self.skipWaiting();
22
+ });
23
+
24
+ // Activare Service Worker
25
+ self.addEventListener('activate', event => {
26
+ console.log('[SW] Activating...');
27
+ event.waitUntil(
28
+ caches.keys().then(cacheNames => {
29
+ return Promise.all(
30
+ cacheNames.map(cacheName => {
31
+ if (cacheName !== CACHE_NAME) {
32
+ console.log('[SW] Deleting old cache:', cacheName);
33
+ return caches.delete(cacheName);
34
+ }
35
+ })
36
+ );
37
+ })
38
+ );
39
+ self.clients.claim();
40
+ });
41
+
42
+ // Interceptare cereri - Nu bloca API-urile
43
+ self.addEventListener('fetch', event => {
44
+ // Nu cache-uim API calls
45
+ if (event.request.url.includes('/api/')) {
46
+ return;
47
+ }
48
+
49
+ event.respondWith(
50
+ caches.match(event.request)
51
+ .then(response => response || fetch(event.request))
52
+ .catch(() => caches.match('/'))
53
+ );
54
+ });
55
+
56
+ // Primire notificări push
57
+ self.addEventListener('push', event => {
58
+ console.log('[SW] Push notification received');
59
+ const data = event.data ? event.data.json() : {};
60
+ const title = data.title || 'DROPLY!';
61
+ const options = {
62
+ body: data.body || 'Mesaj nou primit!',
63
+ icon: '/logo.png',
64
+ badge: '/logo.png',
65
+ vibrate: [200, 100, 200],
66
+ tag: 'droply-notification-' + Date.now(),
67
+ requireInteraction: false,
68
+ data: {
69
+ url: '/'
70
+ }
71
+ };
72
+
73
+ event.waitUntil(
74
+ self.registration.showNotification(title, options)
75
+ );
76
+ });
77
+
78
+ // Click pe notificare
79
+ self.addEventListener('notificationclick', event => {
80
+ console.log('[SW] Notification clicked');
81
+ event.notification.close();
82
+
83
+ event.waitUntil(
84
+ clients.matchAll({ type: 'window', includeUncontrolled: true })
85
+ .then(clientList => {
86
+ // Dacă avem deja o fereastră deschisă, o aducem în față
87
+ for (let client of clientList) {
88
+ if (client.url.includes(self.location.origin) && 'focus' in client) {
89
+ return client.focus();
90
+ }
91
+ }
92
+ // Altfel deschidem una nouă
93
+ if (clients.openWindow) {
94
+ return clients.openWindow('/');
95
+ }
96
+ })
97
+ );
98
+ });
99
+
100
+ // Message handler - primește mesaje de la pagina principală
101
+ self.addEventListener('message', event => {
102
+ console.log('[SW] Message received:', event.data);
103
+
104
+ if (event.data && event.data.type === 'CHECK_MESSAGES') {
105
+ checkForNewMessages();
106
+ }
107
+
108
+ if (event.data && event.data.type === 'NEW_MESSAGE') {
109
+ showNotification(event.data.from, event.data.content);
110
+ }
111
+ });
112
+
113
+ // Funcție pentru afișare notificare
114
+ async function showNotification(from, content) {
115
+ const title = 'DROPLY! - Mesaj Nou';
116
+ const options = {
117
+ body: `De la: ${from}\n${content.substring(0, 50)}${content.length > 50 ? '...' : ''}`,
118
+ icon: '/logo.png',
119
+ badge: '/logo.png',
120
+ vibrate: [200, 100, 200, 100, 200],
121
+ tag: 'droply-message-' + Date.now(),
122
+ requireInteraction: false,
123
+ actions: [
124
+ {
125
+ action: 'open',
126
+ title: 'Deschide'
127
+ },
128
+ {
129
+ action: 'close',
130
+ title: 'Închide'
131
+ }
132
+ ],
133
+ data: {
134
+ url: '/',
135
+ from: from
136
+ }
137
+ };
138
+
139
+ try {
140
+ await self.registration.showNotification(title, options);
141
+ console.log('[SW] Notification shown');
142
+
143
+ // Încercăm să redăm sunetul
144
+ const clients = await self.clients.matchAll({ includeUncontrolled: true, type: 'window' });
145
+ clients.forEach(client => {
146
+ client.postMessage({
147
+ type: 'PLAY_SOUND'
148
+ });
149
+ });
150
+ } catch (error) {
151
+ console.error('[SW] Error showing notification:', error);
152
+ }
153
+ }
154
+
155
+ // Verificare periodică pentru mesaje noi (se apelează din pagina principală)
156
+ async function checkForNewMessages() {
157
+ try {
158
+ console.log('[SW] Checking for new messages...');
159
+
160
+ // Obținem toate client-urile (tab-urile) deschise
161
+ const clients = await self.clients.matchAll({ includeUncontrolled: true, type: 'window' });
162
+
163
+ // Trimitem mesaj către toate tab-urile să verifice inbox-ul
164
+ clients.forEach(client => {
165
+ client.postMessage({
166
+ type: 'CHECK_INBOX'
167
+ });
168
+ });
169
+ } catch (error) {
170
+ console.error('[SW] Error checking messages:', error);
171
+ }
172
+ }
173
+
174
+ // Background Sync pentru mesaje în așteptare
175
+ self.addEventListener('sync', event => {
176
+ console.log('[SW] Background sync:', event.tag);
177
+
178
+ if (event.tag === 'sync-messages') {
179
+ event.waitUntil(checkForNewMessages());
180
+ }
181
+ });
182
+
183
+ // Periodic Background Sync (pentru verificare automată când app-ul e închis)
184
+ // NOTĂ: Funcționează doar pe Android cu Chrome
185
+ self.addEventListener('periodicsync', event => {
186
+ console.log('[SW] Periodic sync:', event.tag);
187
+
188
+ if (event.tag === 'check-new-messages') {
189
+ event.waitUntil(checkForNewMessages());
190
+ }
191
+ });
192
+
193
+ // Handler pentru acțiuni notificare
194
+ self.addEventListener('notificationclose', event => {
195
+ console.log('[SW] Notification closed:', event.notification.tag);
196
+ });
197
+
198
+ console.log('[SW] Service Worker loaded successfully');