mbkauthe 1.1.14 → 1.1.16

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.
package/.env.example CHANGED
@@ -6,7 +6,6 @@ mbkautheVar='{
6
6
  "MBKAUTH_TWO_FA_ENABLE": "false",
7
7
  "COOKIE_EXPIRE_TIME": 2,
8
8
  "DOMAIN": "yourdomain.com",
9
- "layout": false,
10
9
  "loginRedirectURL": "/admin"
11
10
  }'
12
11
 
package/README.md CHANGED
@@ -91,7 +91,6 @@ mbkautheVar='{
91
91
  "MBKAUTH_TWO_FA_ENABLE": "false",
92
92
  "COOKIE_EXPIRE_TIME": 2,
93
93
  "DOMAIN": "yourdomain.com",
94
- "layout": false,
95
94
  "loginRedirectURL": "/admin"
96
95
  }'
97
96
  ```
package/index.js CHANGED
@@ -49,6 +49,7 @@ app.engine("handlebars", engine({
49
49
  defaultLayout: false,
50
50
  partialsDir: [
51
51
  path.join(__dirname, "node_modules/mbkauthe/views"),
52
+ path.join(__dirname, "node_modules/mbkauthe/views/Error"),
52
53
  ],
53
54
  }));
54
55
 
package/lib/main.js CHANGED
@@ -333,7 +333,7 @@ router.post("/mbkauthe/api/logout", async (req, res) => {
333
333
 
334
334
 
335
335
  router.get("/mbkauthe/login", LoginLimit, (req, res) => {
336
- return res.render("loginmbkauthe", {
336
+ return res.render("loginmbkauthe.handlebars", {
337
337
  layout: false,
338
338
  customURL: mbkautheVar.loginRedirectURL || '/home',
339
339
  userLoggedIn: !!req.session?.user,
@@ -36,9 +36,13 @@ async function validateSession(req, res, next) {
36
36
  if (!req.session.user) {
37
37
  console.log("User not authenticated");
38
38
  console.log(req.session.user);
39
- return res.render("templates/Error/NotLoggedIn.handlebars", {
40
- layout: mbkautheVar.layout === true ? true : false,
41
- currentUrl: req.originalUrl,
39
+ return res.render("Error/dError.handlebars", {
40
+ layout: false,
41
+ code: 401,
42
+ error: "Not Logged In",
43
+ message: "You Are Not Logged In. Please Log In To Continue.",
44
+ pagename: "Login",
45
+ page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`,
42
46
  });
43
47
  }
44
48
 
@@ -55,9 +59,13 @@ async function validateSession(req, res, next) {
55
59
  res.clearCookie("mbkauthe.sid", cookieOptions);
56
60
  res.clearCookie("sessionId", cookieOptions);
57
61
  res.clearCookie("username", cookieOptions);
58
- return res.render("templates/Error/SessionExpire.handlebars", {
59
- layout: mbkautheVar.layout === true ? true : false,
60
- currentUrl: req.originalUrl,
62
+ return res.render("Error/dError.handlebars", {
63
+ layout: false,
64
+ code: 401,
65
+ error: "Session Expired",
66
+ message: "Your Session Has Expired. Please Log In Again.",
67
+ pagename: "Login",
68
+ page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`,
61
69
  });
62
70
  }
63
71
 
@@ -68,9 +76,13 @@ async function validateSession(req, res, next) {
68
76
  res.clearCookie("mbkauthe.sid", cookieOptions);
69
77
  res.clearCookie("sessionId", cookieOptions);
70
78
  res.clearCookie("username", cookieOptions);
71
- return res.render("templates/Error/AccountInactive.handlebars", {
72
- layout: mbkautheVar.layout === true ? true : false,
73
- currentUrl: req.originalUrl,
79
+ return res.render("Error/dError.handlebars", {
80
+ layout: false,
81
+ code: 401,
82
+ error: "Account Inactive",
83
+ message: "Your Account Is Inactive. Please Contact Support.",
84
+ pagename: "Support",
85
+ page: "https://mbktechstudio.com/Support",
74
86
  });
75
87
  }
76
88
 
@@ -83,9 +95,13 @@ async function validateSession(req, res, next) {
83
95
  res.clearCookie("mbkauthe.sid", cookieOptions);
84
96
  res.clearCookie("sessionId", cookieOptions);
85
97
  res.clearCookie("username", cookieOptions);
86
- return res.render("templates/Error/Error.handlebars", {
87
- layout: mbkautheVar.layout === true ? true : false,
88
- error: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`,
98
+ return res.render("Error/dError.handlebars", {
99
+ layout: false,
100
+ code: 401,
101
+ error: "Unauthorized",
102
+ message: `You Are Not Authorized To Use The Application \"${mbkautheVar.APP_NAME}\"`,
103
+ pagename: "Home",
104
+ page: `/${mbkautheVar.loginRedirectURL}`
89
105
  });
90
106
  }
91
107
  }
@@ -103,9 +119,13 @@ const checkRolePermission = (requiredRole) => {
103
119
  if (!req.session || !req.session.user || !req.session.user.id) {
104
120
  console.log("User not authenticated");
105
121
  console.log(req.session);
106
- return res.render("templates/Error/NotLoggedIn.handlebars", {
107
- layout: mbkautheVar.layout === true ? true : false,
108
- currentUrl: req.originalUrl,
122
+ return res.render("Error/dError.handlebars", {
123
+ layout: false,
124
+ code: 401,
125
+ error: "Not Logged In",
126
+ message: "You Are Not Logged In. Please Log In To Continue.",
127
+ pagename: "Login",
128
+ page: `/mbkauthe/login?redirect=${encodeURIComponent(req.originalUrl)}`,
109
129
  });
110
130
  }
111
131
 
@@ -124,10 +144,13 @@ const checkRolePermission = (requiredRole) => {
124
144
 
125
145
  const userRole = result.rows[0].Role;
126
146
  if (userRole !== requiredRole) {
127
- return res.render("templates/Error/AccessDenied.handlebars", {
128
- layout: mbkautheVar.layout === true ? true : false,
129
- currentRole: userRole,
130
- requiredRole: requiredRole,
147
+ return res.render("Error/dError.handlebars", {
148
+ layout: false,
149
+ code: 403,
150
+ error: "Access Denied",
151
+ message: `You do not have permission to access this resource. Required role: ${requiredRole}`,
152
+ pagename: "Home",
153
+ page: `/${mbkautheVar.loginRedirectURL}`
131
154
  });
132
155
  }
133
156
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mbkauthe",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "MBKTechStudio's reusable authentication system for Node.js applications.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,723 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>{{code}} - {{error}}</title>
8
+ <link rel="icon" type="image/x-icon" href="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg">
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
10
+ <style>
11
+ :root {
12
+ --primary: #4361ee;
13
+ --primary-dark: #3a0ca3;
14
+ --primary-light: rgba(67, 97, 238, 0.1);
15
+ --secondary: #f72585;
16
+ --secondary-light: rgba(247, 37, 133, 0.1);
17
+ --dark: #121212;
18
+ --dark-light: #1e1e1e;
19
+ --darker: #0a0a0a;
20
+ --light: #f8f9fa;
21
+ --lighter: #ffffff;
22
+ --gray: #cccccc;
23
+ --gray-dark: #888888;
24
+ --success: #4cc9f0;
25
+ --warning: #f8961e;
26
+ --danger: #ef233c;
27
+ --gradient: linear-gradient(135deg, var(--primary), var(--secondary));
28
+ --glass: rgba(30, 30, 30, 0.5);
29
+ --glass-border: rgba(255, 255, 255, 0.1);
30
+ --transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.1);
31
+ --shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.1);
32
+ --shadow-md: 0 8px 30px rgba(0, 0, 0, 0.2);
33
+ --shadow-lg: 0 15px 40px rgba(0, 0, 0, 0.3);
34
+ --radius-sm: 8px;
35
+ --radius-md: 12px;
36
+ --radius-lg: 16px;
37
+ --radius-xl: 24px;
38
+ }
39
+
40
+ * {
41
+ margin: 0;
42
+ padding: 0;
43
+ box-sizing: border-box;
44
+ font-family: 'Poppins', sans-serif;
45
+ }
46
+
47
+ body {
48
+ background: var(--dark);
49
+ color: var(--light);
50
+ line-height: 1.6;
51
+ overflow-x: hidden;
52
+ min-height: 100vh;
53
+ display: flex;
54
+ flex-direction: column;
55
+ }
56
+
57
+ header {
58
+ position: fixed;
59
+ top: 0;
60
+ left: 0;
61
+ width: 100%;
62
+ z-index: 1000;
63
+ transition: var(--transition);
64
+ background: linear-gradient(to bottom, rgba(10, 10, 10, 0.9), rgba(10, 10, 10, 0.7));
65
+ backdrop-filter: blur(10px);
66
+ border-bottom: 1px solid var(--glass-border);
67
+ }
68
+
69
+ nav {
70
+ padding: 10px 5%;
71
+ max-width: 1400px;
72
+ margin: 0 auto;
73
+ }
74
+
75
+ .navbar {
76
+ display: flex;
77
+ justify-content: space-between;
78
+ align-items: center;
79
+ }
80
+
81
+ .logo {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 10px;
85
+ }
86
+
87
+ .logo img {
88
+ height: 30px;
89
+ width: auto;
90
+ transition: var(--transition);
91
+ }
92
+
93
+ .logo:hover img {
94
+ transform: rotate(15deg);
95
+ }
96
+
97
+ .logo-text {
98
+ font-size: 1.8rem;
99
+ font-weight: 700;
100
+ background: var(--gradient);
101
+ -webkit-background-clip: text;
102
+ background-clip: text;
103
+ color: transparent;
104
+ }
105
+
106
+ .login-container {
107
+ flex: 1;
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ padding: 120px 5% 80px;
112
+ position: relative;
113
+ overflow: hidden;
114
+ background: radial-gradient(circle at 70% 20%, rgba(67, 97, 238, 0.15), transparent 60%);
115
+ }
116
+
117
+ .login-box {
118
+ background: var(--dark-light);
119
+ border-radius: var(--radius-xl);
120
+ padding: 2rem;
121
+ width: 100%;
122
+ max-width: 500px;
123
+ box-shadow: var(--shadow-lg);
124
+ border: .125rem solid var(--glass-border);
125
+ position: relative;
126
+ z-index: 2;
127
+ transition: var(--transition);
128
+ }
129
+
130
+ .login-box:hover {
131
+ box-shadow: var(--shadow-lg);
132
+ }
133
+
134
+ .login-title {
135
+ text-align: center;
136
+ margin-bottom: 2rem;
137
+ font-size: 2rem;
138
+ position: relative;
139
+ color: var(--lighter);
140
+ }
141
+
142
+ .login-title::after {
143
+ content: '';
144
+ position: absolute;
145
+ bottom: -10px;
146
+ left: 50%;
147
+ transform: translateX(-50%);
148
+ width: 80px;
149
+ height: 4px;
150
+ background: var(--gradient);
151
+ border-radius: 2px;
152
+ }
153
+
154
+ .form-group {
155
+ position: relative;
156
+ margin-bottom: 1rem;
157
+ }
158
+
159
+ .form-input {
160
+ width: 100%;
161
+ padding: 12px 20px;
162
+ background: var(--darker);
163
+ border: 2px solid var(--glass-border);
164
+ border-radius: var(--radius-sm);
165
+ color: var(--light);
166
+ font-size: 1rem;
167
+ transition: var(--transition);
168
+ }
169
+
170
+ .form-input:focus {
171
+ outline: none;
172
+ border-color: var(--primary);
173
+ box-shadow: 0 0 0 3px var(--primary-light);
174
+ }
175
+
176
+ .form-label {
177
+ position: absolute;
178
+ top: 15px;
179
+ left: 20px;
180
+ color: var(--gray);
181
+ transition: var(--transition);
182
+ pointer-events: none;
183
+ }
184
+
185
+ .form-input:focus+.form-label,
186
+ .form-input:not(:placeholder-shown)+.form-label {
187
+ top: -10px;
188
+ left: 15px;
189
+ font-size: 0.8rem;
190
+ background: var(--dark-light);
191
+ padding: 0 5px;
192
+ color: var(--primary);
193
+ }
194
+
195
+ .input-icon {
196
+ position: absolute;
197
+ right: 20px;
198
+ top: 50%;
199
+ transform: translateY(-50%);
200
+ color: var(--gray);
201
+ cursor: pointer;
202
+ transition: var(--transition);
203
+ }
204
+
205
+ .input-icon:hover {
206
+ color: var(--primary);
207
+ }
208
+
209
+ .btn-login {
210
+ width: 100%;
211
+ padding: 10px;
212
+ border-radius: var(--radius-sm);
213
+ background: var(--primary);
214
+ color: white;
215
+ font-weight: 600;
216
+ font-size: 1rem;
217
+ border: none;
218
+ cursor: pointer;
219
+ transition: var(--transition);
220
+ box-shadow: var(--shadow-sm);
221
+ }
222
+
223
+ .btn-login:hover {
224
+ background: var(--primary-dark);
225
+ box-shadow: var(--shadow-md);
226
+ }
227
+
228
+ .btn-login:disabled {
229
+ background: var(--gray-dark);
230
+ cursor: not-allowed;
231
+ transform: none;
232
+ box-shadow: none;
233
+ }
234
+
235
+ .login-links {
236
+ display: flex;
237
+ justify-content: space-between;
238
+ margin-top: 1.5rem;
239
+ font-size: 0.9rem;
240
+ }
241
+
242
+ .login-link {
243
+ color: var(--gray);
244
+ transition: var(--transition);
245
+ }
246
+
247
+ .login-link:hover {
248
+ color: var(--primary);
249
+ }
250
+
251
+ .terms-info {
252
+ margin-top: 2rem;
253
+ font-size: 0.8rem;
254
+ color: var(--gray);
255
+ text-align: center;
256
+ }
257
+
258
+ .terms-link {
259
+ color: var(--primary);
260
+ font-weight: 500;
261
+ }
262
+
263
+ .token-container {
264
+ animation: fadeInUp 0.4s ease-out;
265
+ }
266
+
267
+ .token-container.disable {
268
+ display: none;
269
+ }
270
+
271
+ .token-container.enable {
272
+ display: block;
273
+ }
274
+
275
+ .ai-element {
276
+ position: absolute;
277
+ opacity: 0.1;
278
+ z-index: 1;
279
+ animation: float 6s ease-in-out infinite;
280
+ }
281
+
282
+ .ai-element:nth-child(1) {
283
+ top: 20%;
284
+ left: 10%;
285
+ font-size: 5rem;
286
+ animation-delay: 0s;
287
+ }
288
+
289
+ .ai-element:nth-child(2) {
290
+ top: 60%;
291
+ left: 80%;
292
+ font-size: 4rem;
293
+ animation-delay: 1s;
294
+ }
295
+
296
+ .ai-element:nth-child(3) {
297
+ top: 30%;
298
+ left: 70%;
299
+ font-size: 3rem;
300
+ animation-delay: 2s;
301
+ }
302
+
303
+ .ai-element:nth-child(4) {
304
+ top: 80%;
305
+ left: 20%;
306
+ font-size: 6rem;
307
+ animation-delay: 3s;
308
+ }
309
+
310
+ @keyframes float {
311
+
312
+ 0%,
313
+ 100% {
314
+ transform: translateY(0) rotate(0deg);
315
+ }
316
+
317
+ 50% {
318
+ transform: translateY(-20px) rotate(5deg);
319
+ }
320
+ }
321
+
322
+ @keyframes fadeInUp {
323
+ from {
324
+ opacity: 0;
325
+ transform: translateY(20px);
326
+ }
327
+
328
+ to {
329
+ opacity: 1;
330
+ transform: translateY(0);
331
+ }
332
+ }
333
+
334
+ @media (max-width: 768px) {
335
+ .login-box {
336
+ padding: 2rem;
337
+ }
338
+
339
+ .login-title {
340
+ font-size: 1.8rem;
341
+ }
342
+ }
343
+
344
+ @media (max-width: 576px) {
345
+ .login-box {
346
+ padding: 1.5rem;
347
+ border-radius: var(--radius-lg);
348
+ }
349
+
350
+ .login-links {
351
+ flex-direction: column;
352
+ gap: 0.5rem;
353
+ align-items: center;
354
+ }
355
+ }
356
+
357
+ .remember-me {
358
+ display: flex;
359
+ align-items: center;
360
+ justify-content: center;
361
+ width: 100%;
362
+ margin: 1rem 0;
363
+ }
364
+
365
+ .remember-me input[type="checkbox"] {
366
+ appearance: none;
367
+ -webkit-appearance: none;
368
+ width: 18px;
369
+ height: 18px;
370
+ border: 2px solid var(--glass-border);
371
+ border-radius: var(--radius-sm);
372
+ margin-right: 10px;
373
+ cursor: pointer;
374
+ position: relative;
375
+ transition: var(--transition);
376
+ }
377
+
378
+ .remember-me input[type="checkbox"]:checked {
379
+ background-color: var(--primary);
380
+ border-color: var(--primary);
381
+ }
382
+
383
+ .remember-me input[type="checkbox"]:checked::after {
384
+ content: '\f00c';
385
+ font-family: 'Font Awesome 6 Free';
386
+ font-weight: 900;
387
+ position: absolute;
388
+ top: 50%;
389
+ left: 50%;
390
+ transform: translate(-50%, -50%);
391
+ color: white;
392
+ font-size: 10px;
393
+ }
394
+
395
+ .remember-me label {
396
+ color: var(--gray);
397
+ cursor: pointer;
398
+ transition: var(--transition);
399
+ font-size: 0.9rem;
400
+ }
401
+
402
+ .remember-me label:hover {
403
+ color: var(--light);
404
+ }
405
+
406
+ .WarningboxInfo {
407
+ background: var(--dark-light);
408
+ border: 0.5px solid var(--warning);
409
+ border-left: 4px solid var(--warning);
410
+ padding: 0.75rem 1rem;
411
+ border-radius: var(--radius-sm);
412
+ color: var(--warning);
413
+ font-size: 0.9rem;
414
+ font-weight: 500;
415
+ margin-top: 1rem;
416
+ text-align: center;
417
+ box-shadow: var(--shadow-sm);
418
+ }
419
+
420
+ .status-container {
421
+ flex: 1;
422
+ display: flex;
423
+ align-items: center;
424
+ justify-content: center;
425
+ padding: 120px 5% 80px;
426
+ position: relative;
427
+ overflow: hidden;
428
+ background: radial-gradient(circle at 70% 20%, rgba(67, 97, 238, 0.15), transparent 60%);
429
+ }
430
+
431
+ .status-box {
432
+ background: var(--dark-light);
433
+ border-radius: var(--radius-xl);
434
+ padding: 2rem;
435
+ width: 100%;
436
+ max-width: 600px;
437
+ box-shadow: var(--shadow-lg);
438
+ border: .125rem solid var(--glass-border);
439
+ position: relative;
440
+ z-index: 2;
441
+ transition: var(--transition);
442
+ }
443
+
444
+ .status-box:hover {
445
+ box-shadow: var(--shadow-lg);
446
+ transform: scale(1.05);
447
+ }
448
+
449
+ .status-code {
450
+ text-align: center;
451
+ margin-bottom: 1rem;
452
+ font-size: 2.5rem;
453
+ font-weight: 700;
454
+ color: var(--success);
455
+ position: relative;
456
+ }
457
+
458
+ .status-title {
459
+ text-align: center;
460
+ margin-bottom: 1rem;
461
+ font-size: 1.5rem;
462
+ color: var(--lighter);
463
+ }
464
+
465
+ .status-code::after {
466
+ content: '';
467
+ position: absolute;
468
+ bottom: -10px;
469
+ left: 50%;
470
+ transform: translateX(-50%);
471
+ width: 100px;
472
+ height: 4px;
473
+ background: var(--gradient);
474
+ border-radius: 2px;
475
+ }
476
+
477
+ .status-content {
478
+ margin-bottom: 1.5rem;
479
+ text-align: center;
480
+ }
481
+
482
+ .status-message {
483
+ font-size: 1.2rem;
484
+ margin: 0.5rem 0;
485
+ color: var(--warning);
486
+ }
487
+
488
+ .status-links {
489
+ display: flex;
490
+ justify-content: center;
491
+ margin-top: 1rem;
492
+ }
493
+
494
+ .status-link {
495
+ color: var(--primary);
496
+ text-decoration: none;
497
+ transition: var(--transition);
498
+ margin: 0 15px;
499
+ font-size: 1rem;
500
+ font-weight: 600;
501
+ }
502
+
503
+ .status-link:hover {
504
+ text-decoration: underline;
505
+ color: var(--primary-dark);
506
+ }
507
+
508
+ .details-wrapper {
509
+ margin-top: 1rem;
510
+ background: var(--dark-light);
511
+ border-radius: var(--radius-md);
512
+ padding: 1.5rem;
513
+ box-shadow: var(--shadow-md);
514
+ cursor: pointer;
515
+ transition: var(--transition);
516
+ }
517
+
518
+ .details-header {
519
+ display: flex;
520
+ justify-content: space-between;
521
+ align-items: center;
522
+ color: var(--lighter);
523
+ font-size: 1.2rem;
524
+ }
525
+
526
+ .details-header i {
527
+ transition: var(--transition);
528
+ }
529
+
530
+ .details-wrapper:hover i {
531
+ transform: rotate(180deg);
532
+ }
533
+
534
+ .error-details-wrapper {
535
+ display: none;
536
+ margin-top: 1rem;
537
+ }
538
+
539
+ .details-wrapper.active .error-details-wrapper {
540
+ display: block;
541
+ }
542
+
543
+ .error-details {
544
+ width: 100%;
545
+ height: 150px;
546
+ background: var(--darker);
547
+ border: 1px solid var(--glass-border);
548
+ border-radius: var(--radius-sm);
549
+ color: var(--light);
550
+ padding: 1rem;
551
+ resize: none;
552
+ font-size: 0.9rem;
553
+ }
554
+
555
+ .copy-btn {
556
+ margin-top: 0.5rem;
557
+ background: var(--primary);
558
+ color: var(--lighter);
559
+ border: none;
560
+ border-radius: var(--radius-sm);
561
+ padding: 0.75rem 1.5rem;
562
+ cursor: pointer;
563
+ transition: var(--transition);
564
+ font-size: 1rem;
565
+ }
566
+
567
+ .copy-btn:hover {
568
+ background: var(--primary-dark);
569
+ }
570
+
571
+ .status-element {
572
+ position: absolute;
573
+ opacity: 0.1;
574
+ z-index: 1;
575
+ animation: float 6s ease-in-out infinite;
576
+ }
577
+
578
+ .status-element:nth-child(1) {
579
+ top: 20%;
580
+ left: 10%;
581
+ font-size: 5rem;
582
+ animation-delay: 0s;
583
+ }
584
+
585
+ .status-element:nth-child(2) {
586
+ top: 60%;
587
+ left: 80%;
588
+ font-size: 4rem;
589
+ animation-delay: 1s;
590
+ }
591
+
592
+ .status-element:nth-child(3) {
593
+ top: 30%;
594
+ left: 70%;
595
+ font-size: 3rem;
596
+ animation-delay: 2s;
597
+ }
598
+
599
+ .status-element:nth-child(4) {
600
+ top: 80%;
601
+ left: 20%;
602
+ font-size: 6rem;
603
+ animation-delay: 3s;
604
+ }
605
+
606
+ .status-element:nth-child(5) {
607
+ top: 50%;
608
+ left: 40%;
609
+ font-size: 4.5rem;
610
+ animation-delay: 4s;
611
+ }
612
+
613
+ .status-element:nth-child(6) {
614
+ top: 70%;
615
+ left: 60%;
616
+ font-size: 5.5rem;
617
+ animation-delay: 5s;
618
+ }
619
+
620
+ @keyframes float {
621
+
622
+ 0%,
623
+ 100% {
624
+ transform: translateY(0) rotate(0deg);
625
+ }
626
+
627
+ 50% {
628
+ transform: translateY(-20px) rotate(5deg);
629
+ }
630
+ }
631
+ </style>
632
+ </head>
633
+
634
+ <body>
635
+ <header>
636
+ <nav>
637
+ <div class="navbar">
638
+ <a class="logo">
639
+ <img src="https://mbktechstudio.com/Assets/Images/Icon/dgicon.svg" alt="MBK Tech Studio Logo">
640
+ <span class="logo-text">MBK Authe</span>
641
+ </a>
642
+ </div>
643
+ </nav>
644
+ </header>
645
+
646
+
647
+ <section class="status-container">
648
+
649
+ <i class="fas fa-server status-element"></i>
650
+ <i class="fas fa-database status-element"></i>
651
+ <i class="fas fa-network-wired status-element"></i>
652
+ <i class="fas fa-code status-element"></i>
653
+ <i class="fas fa-cloud status-element"></i>
654
+ <i class="fas fa-shield-alt status-element"></i>
655
+
656
+ <div class="status-box">
657
+ <h1 class="status-code">{{code}}</h1>
658
+ <h1 class="status-title">{{error}}</h1>
659
+
660
+ <div class="status-content">
661
+ <p class="status-message">{{message}}</p>
662
+ </div>
663
+
664
+ <div class="status-links">
665
+ <a href="{{page}}" class="status-link">Go to {{pagename}} page</a>
666
+ </div>
667
+
668
+ {{#if details}}
669
+ <div class="details-wrapper" onclick="toggleDetailsDropdown(this)">
670
+ <div class="details-header">
671
+ <span>Show Error Details</span>
672
+ <i class="fas fa-chevron-down"></i>
673
+ </div>
674
+ <div class="error-details-wrapper">
675
+ <textarea class="error-details" id="errorDetails" readonly>{{details}}</textarea>
676
+ <button class="copy-btn" type="button" aria-label="Copy error details"
677
+ onclick="copyErrorDetails(this); event.stopPropagation();">
678
+ <i class="fas fa-copy"></i> Copy
679
+ </button>
680
+ </div>
681
+ </div>
682
+ {{/if}}
683
+
684
+ </div>
685
+ </section>
686
+ <script>
687
+ function toggleDetailsDropdown(element) {
688
+ const errorDetailsWrapper = element.querySelector('.error-details-wrapper');
689
+ const icon = element.querySelector('.details-header i');
690
+
691
+ if (errorDetailsWrapper.style.display === 'block') {
692
+ errorDetailsWrapper.style.display = 'none';
693
+ icon.style.transform = 'rotate(0deg)';
694
+ } else {
695
+ errorDetailsWrapper.style.display = 'block';
696
+ icon.style.transform = 'rotate(180deg)';
697
+ }
698
+ }
699
+
700
+ function copyErrorDetails(button) {
701
+ const details = button.previousElementSibling.value;
702
+ navigator.clipboard.writeText(details).then(() => {
703
+ button.textContent = 'Copied!';
704
+ setTimeout(() => {
705
+ button.textContent = 'Copy';
706
+ }, 2000);
707
+ }).catch(err => {
708
+ console.error('Failed to copy: ', err);
709
+ });
710
+ }
711
+
712
+ document.addEventListener('DOMContentLoaded', () => {
713
+ const detailsWrappers = document.querySelectorAll('.details-wrapper');
714
+ detailsWrappers.forEach(wrapper => {
715
+ wrapper.addEventListener('click', () => {
716
+ wrapper.classList.toggle('active');
717
+ });
718
+ });
719
+ });
720
+ </script>
721
+ </body>
722
+
723
+ </html>