@mjasano/devtunnel 1.2.0 → 1.4.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,242 @@
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
+ </style>
145
+ </head>
146
+ <body>
147
+ <div class="login-container">
148
+ <div class="logo"></div>
149
+ <h1>DevTunnel</h1>
150
+ <p class="subtitle">Enter passcode to continue</p>
151
+
152
+ <div class="error-message" id="error-message"></div>
153
+
154
+ <form id="login-form">
155
+ <div class="form-group">
156
+ <label for="passcode">Passcode</label>
157
+ <input
158
+ type="password"
159
+ id="passcode"
160
+ name="passcode"
161
+ placeholder="Enter passcode"
162
+ autocomplete="off"
163
+ autofocus
164
+ required
165
+ >
166
+ </div>
167
+
168
+ <button type="submit" class="btn" id="submit-btn">
169
+ Login
170
+ </button>
171
+ </form>
172
+ </div>
173
+
174
+ <script>
175
+ const form = document.getElementById('login-form');
176
+ const passcodeInput = document.getElementById('passcode');
177
+ const submitBtn = document.getElementById('submit-btn');
178
+ const errorMessage = document.getElementById('error-message');
179
+
180
+ // Check if already authenticated
181
+ fetch('/api/auth/status')
182
+ .then(res => res.json())
183
+ .then(data => {
184
+ if (data.authenticated) {
185
+ window.location.href = '/';
186
+ }
187
+ });
188
+
189
+ form.addEventListener('submit', async (e) => {
190
+ e.preventDefault();
191
+
192
+ const passcode = passcodeInput.value;
193
+
194
+ if (!passcode) {
195
+ showError('Please enter a passcode');
196
+ return;
197
+ }
198
+
199
+ setLoading(true);
200
+ hideError();
201
+
202
+ try {
203
+ const res = await fetch('/api/auth/login', {
204
+ method: 'POST',
205
+ headers: { 'Content-Type': 'application/json' },
206
+ body: JSON.stringify({ passcode })
207
+ });
208
+
209
+ const data = await res.json();
210
+
211
+ if (data.success) {
212
+ window.location.href = '/';
213
+ } else {
214
+ showError(data.error || 'Invalid passcode');
215
+ passcodeInput.value = '';
216
+ passcodeInput.focus();
217
+ }
218
+ } catch (err) {
219
+ showError('Connection error. Please try again.');
220
+ } finally {
221
+ setLoading(false);
222
+ }
223
+ });
224
+
225
+ function showError(message) {
226
+ errorMessage.textContent = message;
227
+ errorMessage.classList.add('show');
228
+ }
229
+
230
+ function hideError() {
231
+ errorMessage.classList.remove('show');
232
+ }
233
+
234
+ function setLoading(loading) {
235
+ submitBtn.disabled = loading;
236
+ submitBtn.innerHTML = loading
237
+ ? '<span class="loading"></span>Logging in...'
238
+ : 'Login';
239
+ }
240
+ </script>
241
+ </body>
242
+ </html>