@mjasano/devtunnel 1.2.0 → 1.5.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,274 @@
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>DevTunnel - Login</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ background-color: #0d1117;
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ height: 100vh;
18
+ display: flex;
19
+ justify-content: center;
20
+ align-items: center;
21
+ color: #c9d1d9;
22
+ }
23
+
24
+ .login-container {
25
+ background-color: #161b22;
26
+ border: 1px solid #30363d;
27
+ border-radius: 12px;
28
+ padding: 40px;
29
+ width: 100%;
30
+ max-width: 400px;
31
+ text-align: center;
32
+ }
33
+
34
+ .logo {
35
+ width: 64px;
36
+ height: 64px;
37
+ background: linear-gradient(135deg, #58a6ff, #8b5cf6);
38
+ border-radius: 16px;
39
+ margin: 0 auto 24px;
40
+ }
41
+
42
+ h1 {
43
+ font-size: 24px;
44
+ font-weight: 600;
45
+ margin-bottom: 8px;
46
+ color: #fff;
47
+ }
48
+
49
+ .subtitle {
50
+ color: #8b949e;
51
+ font-size: 14px;
52
+ margin-bottom: 32px;
53
+ }
54
+
55
+ .form-group {
56
+ margin-bottom: 24px;
57
+ }
58
+
59
+ .form-group label {
60
+ display: block;
61
+ text-align: left;
62
+ font-size: 14px;
63
+ font-weight: 500;
64
+ margin-bottom: 8px;
65
+ color: #c9d1d9;
66
+ }
67
+
68
+ .form-group input {
69
+ width: 100%;
70
+ padding: 12px 16px;
71
+ background-color: #0d1117;
72
+ border: 1px solid #30363d;
73
+ border-radius: 8px;
74
+ color: #c9d1d9;
75
+ font-size: 16px;
76
+ text-align: center;
77
+ letter-spacing: 4px;
78
+ }
79
+
80
+ .form-group input:focus {
81
+ outline: none;
82
+ border-color: #58a6ff;
83
+ box-shadow: 0 0 0 3px rgba(56, 139, 253, 0.2);
84
+ }
85
+
86
+ .form-group input::placeholder {
87
+ color: #6e7681;
88
+ letter-spacing: normal;
89
+ }
90
+
91
+ .btn {
92
+ width: 100%;
93
+ padding: 12px 24px;
94
+ background-color: #238636;
95
+ color: #fff;
96
+ border: none;
97
+ border-radius: 8px;
98
+ font-size: 16px;
99
+ font-weight: 500;
100
+ cursor: pointer;
101
+ transition: background-color 0.15s ease;
102
+ }
103
+
104
+ .btn:hover {
105
+ background-color: #2ea043;
106
+ }
107
+
108
+ .btn:disabled {
109
+ background-color: #21262d;
110
+ color: #484f58;
111
+ cursor: not-allowed;
112
+ }
113
+
114
+ .error-message {
115
+ background-color: rgba(248, 81, 73, 0.1);
116
+ border: 1px solid #f85149;
117
+ color: #f85149;
118
+ padding: 12px;
119
+ border-radius: 8px;
120
+ margin-bottom: 24px;
121
+ font-size: 14px;
122
+ display: none;
123
+ }
124
+
125
+ .error-message.show {
126
+ display: block;
127
+ }
128
+
129
+ .loading {
130
+ display: inline-block;
131
+ width: 16px;
132
+ height: 16px;
133
+ border: 2px solid #fff;
134
+ border-radius: 50%;
135
+ border-top-color: transparent;
136
+ animation: spin 0.8s linear infinite;
137
+ margin-right: 8px;
138
+ vertical-align: middle;
139
+ }
140
+
141
+ @keyframes spin {
142
+ to { transform: rotate(360deg); }
143
+ }
144
+
145
+ @media (max-width: 480px) {
146
+ .login-container {
147
+ margin: 16px;
148
+ padding: 24px;
149
+ }
150
+
151
+ .logo {
152
+ width: 48px;
153
+ height: 48px;
154
+ margin-bottom: 16px;
155
+ }
156
+
157
+ h1 {
158
+ font-size: 20px;
159
+ }
160
+
161
+ .subtitle {
162
+ font-size: 13px;
163
+ margin-bottom: 24px;
164
+ }
165
+
166
+ .form-group input {
167
+ padding: 14px 12px;
168
+ font-size: 18px;
169
+ }
170
+
171
+ .btn {
172
+ padding: 14px 24px;
173
+ font-size: 16px;
174
+ }
175
+ }
176
+ </style>
177
+ </head>
178
+ <body>
179
+ <div class="login-container">
180
+ <div class="logo"></div>
181
+ <h1>DevTunnel</h1>
182
+ <p class="subtitle">Enter passcode to continue</p>
183
+
184
+ <div class="error-message" id="error-message"></div>
185
+
186
+ <form id="login-form">
187
+ <div class="form-group">
188
+ <label for="passcode">Passcode</label>
189
+ <input
190
+ type="password"
191
+ id="passcode"
192
+ name="passcode"
193
+ placeholder="Enter passcode"
194
+ autocomplete="off"
195
+ autofocus
196
+ required
197
+ >
198
+ </div>
199
+
200
+ <button type="submit" class="btn" id="submit-btn">
201
+ Login
202
+ </button>
203
+ </form>
204
+ </div>
205
+
206
+ <script>
207
+ const form = document.getElementById('login-form');
208
+ const passcodeInput = document.getElementById('passcode');
209
+ const submitBtn = document.getElementById('submit-btn');
210
+ const errorMessage = document.getElementById('error-message');
211
+
212
+ // Check if already authenticated
213
+ fetch('/api/auth/status')
214
+ .then(res => res.json())
215
+ .then(data => {
216
+ if (data.authenticated) {
217
+ window.location.href = '/';
218
+ }
219
+ });
220
+
221
+ form.addEventListener('submit', async (e) => {
222
+ e.preventDefault();
223
+
224
+ const passcode = passcodeInput.value;
225
+
226
+ if (!passcode) {
227
+ showError('Please enter a passcode');
228
+ return;
229
+ }
230
+
231
+ setLoading(true);
232
+ hideError();
233
+
234
+ try {
235
+ const res = await fetch('/api/auth/login', {
236
+ method: 'POST',
237
+ headers: { 'Content-Type': 'application/json' },
238
+ body: JSON.stringify({ passcode })
239
+ });
240
+
241
+ const data = await res.json();
242
+
243
+ if (data.success) {
244
+ window.location.href = '/';
245
+ } else {
246
+ showError(data.error || 'Invalid passcode');
247
+ passcodeInput.value = '';
248
+ passcodeInput.focus();
249
+ }
250
+ } catch (err) {
251
+ showError('Connection error. Please try again.');
252
+ } finally {
253
+ setLoading(false);
254
+ }
255
+ });
256
+
257
+ function showError(message) {
258
+ errorMessage.textContent = message;
259
+ errorMessage.classList.add('show');
260
+ }
261
+
262
+ function hideError() {
263
+ errorMessage.classList.remove('show');
264
+ }
265
+
266
+ function setLoading(loading) {
267
+ submitBtn.disabled = loading;
268
+ submitBtn.innerHTML = loading
269
+ ? '<span class="loading"></span>Logging in...'
270
+ : 'Login';
271
+ }
272
+ </script>
273
+ </body>
274
+ </html>