create-tinny-backend 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,649 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="light">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6
+ <title>401 | Unauthorized Access</title>
7
+ <link rel="shortcut icon" href="/imgs/logo.png" type="image/x-icon">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
10
+
11
+ <style>
12
+ :root {
13
+ --bg: #f4f6fa;
14
+ --text: #1e293b;
15
+ --text-secondary: #475569;
16
+ --text-muted: #64748b;
17
+ --border: #e2e8f0;
18
+ --card-bg: #ffffff;
19
+ --card-border: #e9eef3;
20
+ --input-focus: #2563eb;
21
+ --heading: #0f172a;
22
+ --shadow-lg: 0 12px 40px rgba(0,0,0,0.1);
23
+ --toggle-bg: #f1f5f9;
24
+ --badge-bg: #dbeafe;
25
+ --badge-text: #1e40af;
26
+ --error-bg: #fee2e2;
27
+ --error-text: #991b1b;
28
+ --success-bg: #dcfce7;
29
+ --success-text: #166534;
30
+ --terminal-bg: #0f172a;
31
+ --terminal-text: #e2e8f0;
32
+ --terminal-border: #1e293b;
33
+ --icon-bg: #f8fafc;
34
+ --code-color: #6d8ecc;
35
+ }
36
+
37
+ [data-theme="dark"] {
38
+ --bg: #1a1a1a;
39
+ --text: #d4d4d4;
40
+ --text-secondary: #a0a0a0;
41
+ --text-muted: #808080;
42
+ --border: #3a3a3a;
43
+ --card-bg: #2a2a2a;
44
+ --card-border: #3a3a3a;
45
+ --input-focus: #60a5fa;
46
+ --heading: #e0e0e0;
47
+ --shadow-lg: 0 12px 40px rgba(0,0,0,0.5);
48
+ --toggle-bg: #333333;
49
+ --badge-bg: #2a2a2a;
50
+ --badge-text: #a0a0a0;
51
+ --error-bg: #3a1a1a;
52
+ --error-text: #fc8181;
53
+ --success-bg: #1a3a2a;
54
+ --success-text: #48bb78;
55
+ --terminal-bg: #0a0a0a;
56
+ --terminal-text: #d4d4d4;
57
+ --terminal-border: #2a2a2a;
58
+ --icon-bg: #222222;
59
+ --code-color: #60a5fa;
60
+ }
61
+
62
+ * {
63
+ margin: 0;
64
+ padding: 0;
65
+ box-sizing: border-box;
66
+ }
67
+
68
+ body {
69
+ background: var(--bg);
70
+ font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
71
+ min-height: 100vh;
72
+ display: flex;
73
+ justify-content: center;
74
+ align-items: center;
75
+ padding: 2rem;
76
+ color: var(--text);
77
+ transition: background 0.3s ease, color 0.3s ease;
78
+ }
79
+
80
+ .container {
81
+ width: 100%;
82
+ max-width: 850px;
83
+ animation: fadeIn 0.6s ease;
84
+ }
85
+
86
+ @keyframes fadeIn {
87
+ from {
88
+ opacity: 0;
89
+ transform: translateY(20px);
90
+ }
91
+ to {
92
+ opacity: 1;
93
+ transform: translateY(0);
94
+ }
95
+ }
96
+
97
+ .auth-card {
98
+ background: var(--card-bg);
99
+ border: 1px solid var(--card-border);
100
+ border-radius: 1.5rem;
101
+ padding: 3rem;
102
+ text-align: center;
103
+ box-shadow: var(--shadow-lg);
104
+ transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
105
+ position: relative;
106
+ overflow: hidden;
107
+ }
108
+
109
+ .auth-card::before {
110
+ content: '';
111
+ position: absolute;
112
+ top: 0;
113
+ left: 0;
114
+ right: 0;
115
+ height: 4px;
116
+ background: linear-gradient(90deg, var(--input-focus), var(--badge-bg));
117
+ opacity: 0.5;
118
+ }
119
+
120
+ .auth-card:hover {
121
+ border-color: var(--input-focus);
122
+ box-shadow: var(--shadow-lg), 0 0 0 1px var(--input-focus);
123
+ }
124
+
125
+ /* Theme Toggle */
126
+ .theme-toggle-wrapper {
127
+ display: flex;
128
+ justify-content: flex-end;
129
+ margin-bottom: 1.5rem;
130
+ }
131
+
132
+ .theme-toggle {
133
+ background: var(--toggle-bg);
134
+ border: 1px solid var(--border);
135
+ color: var(--text);
136
+ width: 40px;
137
+ height: 40px;
138
+ border-radius: 0.8rem;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ cursor: pointer;
143
+ font-size: 1.1rem;
144
+ transition: all 0.2s ease;
145
+ }
146
+
147
+ .theme-toggle:hover {
148
+ background: var(--input-focus);
149
+ color: white;
150
+ border-color: var(--input-focus);
151
+ }
152
+
153
+ /* Icon */
154
+ .auth-icon {
155
+ width: 120px;
156
+ height: 120px;
157
+ margin: 0 auto 1.5rem;
158
+ border-radius: 50%;
159
+ background: var(--icon-bg);
160
+ border: 2px solid var(--border);
161
+ display: flex;
162
+ align-items: center;
163
+ justify-content: center;
164
+ transition: all 0.3s ease;
165
+ }
166
+
167
+ .auth-icon:hover {
168
+ border-color: var(--input-focus);
169
+ box-shadow: 0 0 40px rgba(37, 99, 235, 0.1);
170
+ }
171
+
172
+ .auth-icon i {
173
+ font-size: 3.25rem;
174
+ color: var(--input-focus);
175
+ }
176
+
177
+ /* Code */
178
+ .code {
179
+ font-size: 5rem;
180
+ font-weight: 700;
181
+ color: var(--heading);
182
+ letter-spacing: -3px;
183
+ line-height: 1;
184
+ margin-bottom: 0.75rem;
185
+ }
186
+
187
+ .code span {
188
+ background: linear-gradient(135deg, var(--input-focus), var(--badge-text));
189
+ -webkit-background-clip: text;
190
+ -webkit-text-fill-color: transparent;
191
+ background-clip: text;
192
+ }
193
+
194
+ /* Title */
195
+ .title {
196
+ font-size: 1.8rem;
197
+ font-weight: 600;
198
+ color: var(--heading);
199
+ margin-bottom: 0.75rem;
200
+ }
201
+
202
+ .title i {
203
+ color: var(--input-focus);
204
+ margin-right: 0.5rem;
205
+ }
206
+
207
+ /* Description */
208
+ .description {
209
+ max-width: 550px;
210
+ margin: 0 auto 2rem;
211
+ color: var(--text-secondary);
212
+ line-height: 1.7;
213
+ }
214
+
215
+ .description .badge {
216
+ display: inline-block;
217
+ background: var(--badge-bg);
218
+ color: var(--badge-text);
219
+ padding: 0.2rem 0.6rem;
220
+ border-radius: 30px;
221
+ font-size: 0.7rem;
222
+ font-weight: 700;
223
+ border: 1px solid var(--border);
224
+ }
225
+
226
+ /* Actions */
227
+ .actions {
228
+ display: flex;
229
+ justify-content: center;
230
+ gap: 1rem;
231
+ flex-wrap: wrap;
232
+ margin-bottom: 2rem;
233
+ }
234
+
235
+ .btn {
236
+ text-decoration: none;
237
+ padding: 0.85rem 2rem;
238
+ border-radius: 2.5rem;
239
+ font-size: 0.9rem;
240
+ font-weight: 600;
241
+ transition: all 0.3s ease;
242
+ display: flex;
243
+ align-items: center;
244
+ gap: 0.6rem;
245
+ font-family: 'Inter', sans-serif;
246
+ border: none;
247
+ cursor: pointer;
248
+ }
249
+
250
+ .btn-primary {
251
+ background: var(--input-focus);
252
+ color: #fff;
253
+ }
254
+
255
+ .btn-primary:hover {
256
+ background: var(--badge-text);
257
+ transform: translateY(-2px);
258
+ box-shadow: 0 8px 24px rgba(37, 99, 235, 0.3);
259
+ }
260
+
261
+ .btn-secondary {
262
+ background: var(--toggle-bg);
263
+ border: 1px solid var(--border);
264
+ color: var(--text);
265
+ }
266
+
267
+ .btn-secondary:hover {
268
+ background: var(--border);
269
+ transform: translateY(-2px);
270
+ }
271
+
272
+ /* Terminal */
273
+ .terminal {
274
+ margin-top: 0.5rem;
275
+ background: var(--terminal-bg);
276
+ border: 1px solid var(--terminal-border);
277
+ border-radius: 1rem;
278
+ overflow: hidden;
279
+ transition: background 0.3s ease, border-color 0.3s ease;
280
+ text-align: left;
281
+ }
282
+
283
+ .terminal-header {
284
+ background: var(--border);
285
+ padding: 0.7rem 1rem;
286
+ display: flex;
287
+ align-items: center;
288
+ gap: 0.5rem;
289
+ border-bottom: 1px solid var(--terminal-border);
290
+ transition: background 0.3s ease, border-color 0.3s ease;
291
+ }
292
+
293
+ .dot {
294
+ width: 10px;
295
+ height: 10px;
296
+ border-radius: 50%;
297
+ display: inline-block;
298
+ }
299
+
300
+ .red { background: #f56565; }
301
+ .yellow { background: #ecc94b; }
302
+ .green { background: #48bb78; }
303
+
304
+ .terminal-title {
305
+ margin-left: 0.5rem;
306
+ color: var(--text-muted);
307
+ font-size: 0.8rem;
308
+ font-weight: 500;
309
+ letter-spacing: 0.3px;
310
+ }
311
+
312
+ .terminal-body {
313
+ padding: 1.2rem 1.2rem;
314
+ font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
315
+ font-size: 0.85rem;
316
+ color: var(--terminal-text);
317
+ background: var(--terminal-bg);
318
+ min-height: 150px;
319
+ }
320
+
321
+ .line {
322
+ margin-bottom: 0.5rem;
323
+ display: flex;
324
+ align-items: baseline;
325
+ gap: 0.6rem;
326
+ }
327
+
328
+ .line:last-child {
329
+ margin-bottom: 0;
330
+ }
331
+
332
+ .prompt {
333
+ color: var(--code-color);
334
+ user-select: none;
335
+ }
336
+
337
+ .error {
338
+ color: #fc8181;
339
+ }
340
+
341
+ .success {
342
+ color: #48bb78;
343
+ }
344
+
345
+ .info {
346
+ color: var(--code-color);
347
+ }
348
+
349
+ .dim {
350
+ color: var(--text-muted);
351
+ }
352
+
353
+ .cursor-blink {
354
+ display: inline-block;
355
+ width: 8px;
356
+ height: 16px;
357
+ background: var(--code-color);
358
+ animation: blink 1s step-end infinite;
359
+ vertical-align: text-bottom;
360
+ margin-left: 2px;
361
+ }
362
+
363
+ @keyframes blink {
364
+ 0%, 100% { opacity: 1; }
365
+ 50% { opacity: 0; }
366
+ }
367
+
368
+ /* Decorative line */
369
+ .decorative-line {
370
+ display: flex;
371
+ align-items: center;
372
+ gap: 1rem;
373
+ margin: 1.5rem 0;
374
+ }
375
+
376
+ .decorative-line::before,
377
+ .decorative-line::after {
378
+ content: '';
379
+ flex: 1;
380
+ height: 1px;
381
+ background: linear-gradient(to right, transparent, var(--border), transparent);
382
+ }
383
+
384
+ .decorative-line span {
385
+ color: var(--text-muted);
386
+ font-size: 0.7rem;
387
+ text-transform: uppercase;
388
+ letter-spacing: 0.5px;
389
+ font-weight: 600;
390
+ }
391
+
392
+ /* Footer */
393
+ .footer {
394
+ margin-top: 2rem;
395
+ text-align: center;
396
+ color: var(--text-muted);
397
+ font-size: 0.8rem;
398
+ }
399
+
400
+ .footer a {
401
+ color: var(--input-focus);
402
+ text-decoration: none;
403
+ font-weight: 500;
404
+ transition: color 0.3s ease;
405
+ }
406
+
407
+ .footer a:hover {
408
+ color: var(--badge-text);
409
+ text-decoration: underline;
410
+ }
411
+
412
+ .footer i {
413
+ margin: 0 0.3rem;
414
+ font-size: 0.7rem;
415
+ }
416
+
417
+ @media (max-width: 600px) {
418
+ body {
419
+ padding: 1rem;
420
+ }
421
+
422
+ .auth-card {
423
+ padding: 2rem 1.5rem;
424
+ }
425
+
426
+ .code {
427
+ font-size: 3.5rem;
428
+ }
429
+
430
+ .title {
431
+ font-size: 1.4rem;
432
+ }
433
+
434
+ .auth-icon {
435
+ width: 90px;
436
+ height: 90px;
437
+ }
438
+
439
+ .auth-icon i {
440
+ font-size: 2.5rem;
441
+ }
442
+
443
+ .btn {
444
+ padding: 0.7rem 1.5rem;
445
+ font-size: 0.85rem;
446
+ width: 100%;
447
+ justify-content: center;
448
+ }
449
+
450
+ .actions {
451
+ flex-direction: column;
452
+ gap: 0.8rem;
453
+ }
454
+
455
+ .terminal-body {
456
+ font-size: 0.75rem;
457
+ padding: 1rem;
458
+ }
459
+
460
+ .line {
461
+ flex-wrap: wrap;
462
+ gap: 0.3rem;
463
+ }
464
+ }
465
+
466
+ /* Scrollbar */
467
+ ::-webkit-scrollbar {
468
+ width: 6px;
469
+ height: 6px;
470
+ }
471
+
472
+ ::-webkit-scrollbar-track {
473
+ background: var(--bg);
474
+ }
475
+
476
+ ::-webkit-scrollbar-thumb {
477
+ background: var(--border);
478
+ border-radius: 10px;
479
+ }
480
+
481
+ ::-webkit-scrollbar-thumb:hover {
482
+ background: var(--text-muted);
483
+ }
484
+ </style>
485
+ </head>
486
+ <body>
487
+
488
+ <div class="container">
489
+
490
+ <div class="auth-card">
491
+
492
+ <!-- Theme Toggle -->
493
+ <div class="theme-toggle-wrapper">
494
+ <button class="theme-toggle" id="themeToggle" title="Toggle dark/light mode">
495
+ <i class="fas fa-moon"></i>
496
+ </button>
497
+ </div>
498
+
499
+ <!-- Icon -->
500
+ <div class="auth-icon">
501
+ <i class="fas fa-lock"></i>
502
+ </div>
503
+
504
+ <!-- Code -->
505
+ <div class="code">
506
+ <span>401</span>
507
+ </div>
508
+
509
+ <!-- Title -->
510
+ <h1 class="title">
511
+ <i class="fas fa-exclamation-triangle"></i>
512
+ Unauthorized Access
513
+ </h1>
514
+
515
+ <!-- Description -->
516
+ <p class="description">
517
+ You are not authorized to access this resource.
518
+ Please authenticate with valid credentials or contact
519
+ your administrator if you believe this is an error.
520
+ <br><br>
521
+ <span class="badge"><i class="fas fa-shield-alt"></i> Access Restricted</span>
522
+ </p>
523
+
524
+ <!-- Actions -->
525
+ <div class="actions">
526
+ <a href="/" class="btn btn-primary">
527
+ <i class="fas fa-right-to-bracket"></i>
528
+ Sign In
529
+ </a>
530
+ <a href="/docs" class="btn btn-secondary">
531
+ <i class="fa-regular fa-newspaper"></i>
532
+ Documentation
533
+ </a>
534
+ </div>
535
+
536
+ <!-- Decorative Line -->
537
+ <div class="decorative-line">
538
+ <span>authentication required</span>
539
+ </div>
540
+
541
+ <!-- Terminal -->
542
+ <div class="terminal">
543
+ <div class="terminal-header">
544
+ <span class="dot red"></span>
545
+ <span class="dot yellow"></span>
546
+ <span class="dot green"></span>
547
+ <span class="terminal-title"><i class="fas fa-terminal"></i> auth.log</span>
548
+ </div>
549
+
550
+ <div class="terminal-body">
551
+ <div class="line">
552
+ <span class="prompt">❯</span>
553
+ <span class="info">[INFO]</span>
554
+ <span>Authentication required for protected resource</span>
555
+ </div>
556
+
557
+ <div class="line">
558
+ <span class="prompt">❯</span>
559
+ <span class="info">[INFO]</span>
560
+ <span>Requested endpoint: <span class="dim">/admin/status</span></span>
561
+ </div>
562
+
563
+ <div class="line error">
564
+ <span class="prompt">❯</span>
565
+ <span class="error">[ERROR]</span>
566
+ <span>Access denied — 401 Unauthorized</span>
567
+ </div>
568
+
569
+ <div class="line">
570
+ <span class="prompt">❯</span>
571
+ <span class="info">[INFO]</span>
572
+ <span>Session token: <span class="dim">missing</span></span>
573
+ </div>
574
+
575
+ <div class="line success">
576
+ <span class="prompt">❯</span>
577
+ <span class="success">[READY]</span>
578
+ <span>Awaiting authentication... <span class="cursor-blink"></span></span>
579
+ </div>
580
+ </div>
581
+ </div>
582
+
583
+ <!-- Footer -->
584
+ <div class="footer">
585
+ <i class="fas fa-lock"></i>
586
+ Need help? Check the
587
+ <a href="/docs">documentation</a>
588
+ <i class="fas fa-chevron-right"></i>
589
+ <a href="/">back to home</a>
590
+ </div>
591
+
592
+ </div>
593
+
594
+ </div>
595
+
596
+ <script>
597
+ (function() {
598
+ // ===== THEME TOGGLE =====
599
+ const html = document.documentElement;
600
+ const themeToggle = document.getElementById('themeToggle');
601
+ const savedTheme = localStorage.getItem('tinnybackend-theme');
602
+
603
+ if (savedTheme) {
604
+ html.setAttribute('data-theme', savedTheme);
605
+ } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
606
+ html.setAttribute('data-theme', 'dark');
607
+ }
608
+
609
+ function updateThemeUI(theme) {
610
+ const icon = themeToggle.querySelector('i');
611
+ if (theme === 'dark') {
612
+ icon.classList.remove('fa-moon');
613
+ icon.classList.add('fa-sun');
614
+ } else {
615
+ icon.classList.remove('fa-sun');
616
+ icon.classList.add('fa-moon');
617
+ }
618
+ }
619
+
620
+ updateThemeUI(html.getAttribute('data-theme'));
621
+
622
+ themeToggle.addEventListener('click', () => {
623
+ const currentTheme = html.getAttribute('data-theme');
624
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
625
+ html.setAttribute('data-theme', newTheme);
626
+ localStorage.setItem('tinnybackend-theme', newTheme);
627
+ updateThemeUI(newTheme);
628
+ });
629
+
630
+ if (window.matchMedia) {
631
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
632
+ if (!localStorage.getItem('tinnybackend-theme')) {
633
+ const newTheme = e.matches ? 'dark' : 'light';
634
+ html.setAttribute('data-theme', newTheme);
635
+ updateThemeUI(newTheme);
636
+ }
637
+ });
638
+ }
639
+
640
+ // ===== AUTO-REDIRECT AFTER 10 SECONDS =====
641
+ setTimeout(() => {
642
+ window.location.href = '/';
643
+ }, 10000);
644
+
645
+ })();
646
+ </script>
647
+
648
+ </body>
649
+ </html>