instaserve 1.1.17 → 1.1.19

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/instaserve CHANGED
@@ -6,6 +6,13 @@ import path from 'node:path'
6
6
  import { pathToFileURL, fileURLToPath } from 'node:url'
7
7
  import { execSync } from 'node:child_process'
8
8
 
9
+ if (process.argv.includes('-v') || process.argv.includes('--version')) {
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
11
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'))
12
+ console.log(packageJson.version)
13
+ process.exit(0)
14
+ }
15
+
9
16
  console.log(chalk.cyan('\nInstaserve - Instant Web Stack\n'))
10
17
  console.log(chalk.yellow('Usage:'))
11
18
  console.log(chalk.green(' npx instaserve [options]'))
@@ -19,12 +26,15 @@ console.log(chalk.green(' -ip <address>') + ' IP address to bind to (defaul
19
26
  console.log(chalk.green(' -public <path>') + ' Public directory path (default: ./public)')
20
27
  console.log(chalk.green(' -api <file>') + ' Path to routes file (default: ./routes.js)')
21
28
  console.log(chalk.green(' -secure') + ' Enable HTTPS (requires cert.pem and key.pem: run "npx instaserve generate-certs")')
29
+ console.log(chalk.green(' -v') + ' Show version number')
22
30
  console.log(chalk.green(' -help') + ' Show this help message\n')
23
31
 
24
32
  if (process.argv.includes('-help')) {
25
33
  process.exit(0)
26
34
  }
27
35
 
36
+
37
+
28
38
  const args = process.argv.slice(2)
29
39
 
30
40
  // Handle generate-routes command
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instaserve",
3
- "version": "1.1.17",
3
+ "version": "1.1.19",
4
4
  "description": "Instant web stack",
5
5
  "main": "module.mjs",
6
6
  "bin": "./instaserve",
@@ -0,0 +1,346 @@
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>Instaserve App</title>
7
+ <!-- Google Fonts for Modern Typography -->
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap" rel="stylesheet">
11
+ <style>
12
+ :root {
13
+ /* HSL Color Palette - Vibrant & Premium */
14
+ --primary: 220 90% 56%; /* #2563eb */
15
+ --primary-dark: 220 90% 46%;
16
+ --bg-color: 220 30% 98%;
17
+ --card-bg: 0 0% 100%;
18
+ --text-main: 220 20% 15%;
19
+ --text-secondary: 220 15% 40%;
20
+ --success: 150 70% 40%;
21
+ --radius-md: 16px;
22
+ --radius-sm: 8px;
23
+ --shadow-soft: 0 10px 40px -10px rgba(37, 99, 235, 0.15);
24
+ --shadow-hover: 0 20px 50px -12px rgba(37, 99, 235, 0.25);
25
+ }
26
+
27
+ /* Dark Mode Support */
28
+ @media (prefers-color-scheme: dark) {
29
+ :root {
30
+ --bg-color: 220 30% 8%;
31
+ --card-bg: 220 25% 12%;
32
+ --text-main: 220 30% 95%;
33
+ --text-secondary: 220 15% 60%;
34
+ --shadow-soft: 0 10px 40px -10px rgba(0, 0, 0, 0.5);
35
+ }
36
+ }
37
+
38
+ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Outfit', sans-serif; }
39
+
40
+ body {
41
+ background-color: hsl(var(--bg-color));
42
+ color: hsl(var(--text-main));
43
+ min-height: 100vh;
44
+ display: flex;
45
+ flex-direction: column;
46
+ align-items: center;
47
+ justify-content: center;
48
+ transition: background-color 0.3s ease, color 0.3s ease;
49
+ overflow-x: hidden;
50
+ position: relative;
51
+ }
52
+
53
+ /* Subtle Background Gradient Orb */
54
+ .bg-orb {
55
+ position: absolute;
56
+ width: 600px;
57
+ height: 600px;
58
+ border-radius: 50%;
59
+ background: radial-gradient(circle, hsla(var(--primary), 0.15), transparent 70%);
60
+ top: -100px;
61
+ left: 50%;
62
+ transform: translateX(-50%);
63
+ z-index: -1;
64
+ pointer-events: none;
65
+ animation: pulse-orb 10s ease-in-out infinite alternate;
66
+ }
67
+
68
+ @keyframes pulse-orb {
69
+ 0% { transform: translateX(-50%) scale(0.9); opacity: 0.8; }
70
+ 100% { transform: translateX(-50%) scale(1.1); opacity: 1; }
71
+ }
72
+
73
+ .container {
74
+ width: 100%;
75
+ max-width: 480px;
76
+ padding: 2rem;
77
+ text-align: center;
78
+ }
79
+
80
+ .auth-card {
81
+ background: hsl(var(--card-bg));
82
+ padding: 3rem 2rem;
83
+ border-radius: var(--radius-md);
84
+ box-shadow: var(--shadow-soft);
85
+ backdrop-filter: blur(10px);
86
+ border: 1px solid hsla(var(--primary), 0.1);
87
+ transform: translateY(0);
88
+ transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease;
89
+ }
90
+
91
+ .auth-card:hover {
92
+ transform: translateY(-5px);
93
+ box-shadow: var(--shadow-hover);
94
+ }
95
+
96
+ h1 {
97
+ font-size: 2.5rem;
98
+ font-weight: 700;
99
+ margin-bottom: 0.5rem;
100
+ background: linear-gradient(135deg, hsl(var(--primary)), hsl(260 80% 60%));
101
+ -webkit-background-clip: text;
102
+ -webkit-text-fill-color: transparent;
103
+ letter-spacing: -0.03em;
104
+ }
105
+
106
+ p.subtitle {
107
+ font-size: 1.1rem;
108
+ color: hsl(var(--text-secondary));
109
+ margin-bottom: 2rem;
110
+ line-height: 1.5;
111
+ }
112
+
113
+ /* User Profile State */
114
+ .profile-view {
115
+ display: none; /* Hidden by default */
116
+ flex-direction: column;
117
+ align-items: center;
118
+ gap: 1rem;
119
+ animation: fadeIn 0.5s ease;
120
+ }
121
+
122
+ .profile-pic {
123
+ width: 80px;
124
+ height: 80px;
125
+ border-radius: 50%;
126
+ object-fit: cover;
127
+ border: 3px solid hsl(var(--primary));
128
+ margin-bottom: 0.5rem;
129
+ box-shadow: 0 4px 12px hsla(var(--primary), 0.3);
130
+ }
131
+
132
+ .welcome-text {
133
+ font-size: 1.5rem;
134
+ font-weight: 600;
135
+ }
136
+
137
+ .status-badge {
138
+ background-color: hsla(var(--success), 0.1);
139
+ color: hsl(var(--success));
140
+ padding: 6px 16px;
141
+ border-radius: 20px;
142
+ font-size: 0.85rem;
143
+ font-weight: 600;
144
+ display: inline-flex;
145
+ align-items: center;
146
+ gap: 6px;
147
+ }
148
+
149
+ .status-badge::before {
150
+ content: '';
151
+ width: 8px;
152
+ height: 8px;
153
+ background-color: currentColor;
154
+ border-radius: 50%;
155
+ display: inline-block;
156
+ }
157
+
158
+ /* Buttons */
159
+ .btn {
160
+ display: inline-block;
161
+ text-decoration: none;
162
+ padding: 14px 28px;
163
+ border-radius: var(--radius-sm);
164
+ font-weight: 600;
165
+ font-size: 1rem;
166
+ transition: all 0.2s ease;
167
+ cursor: pointer;
168
+ border: none;
169
+ width: 100%;
170
+ }
171
+
172
+ .btn-primary {
173
+ background-color: hsl(var(--primary));
174
+ color: white;
175
+ box-shadow: 0 4px 12px hsla(var(--primary), 0.4);
176
+ }
177
+
178
+ .btn-primary:hover {
179
+ background-color: hsl(var(--primary-dark));
180
+ transform: translateY(-2px);
181
+ box-shadow: 0 8px 16px hsla(var(--primary), 0.5);
182
+ }
183
+
184
+ .btn-secondary {
185
+ background-color: hsla(var(--text-main), 0.05);
186
+ color: hsl(var(--text-main));
187
+ margin-top: 1rem;
188
+ }
189
+
190
+ .btn-secondary:hover {
191
+ background-color: hsla(var(--text-main), 0.1);
192
+ }
193
+
194
+ /* Loading/Guest State */
195
+ .guest-view {
196
+ display: none;
197
+ animation: fadeIn 0.5s ease;
198
+ }
199
+
200
+ /* Testing Controls Styles */
201
+ .btn-sm {
202
+ padding: 6px 12px;
203
+ border-radius: 6px;
204
+ border: 1px solid #ddd;
205
+ background: white;
206
+ cursor: pointer;
207
+ font-size: 0.9rem;
208
+ transition: all 0.2s;
209
+ color: #333;
210
+ }
211
+ .btn-sm:hover {
212
+ background: #f9fafb;
213
+ border-color: #ccc;
214
+ }
215
+ .section-title { font-size: 1rem; color: hsl(var(--text-main)); font-weight: 600; margin-top: 1.5rem; text-align: left; }
216
+ .input-field { width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #ddd; margin-bottom: 0.5rem; }
217
+
218
+ @keyframes fadeIn {
219
+ from { opacity: 0; transform: translateY(10px); }
220
+ to { opacity: 1; transform: translateY(0); }
221
+ }
222
+ </style>
223
+ </head>
224
+ <body>
225
+ <div class="bg-orb"></div>
226
+ <div class="container">
227
+ <div class="auth-card">
228
+ <h1>Instaserve</h1>
229
+
230
+ <!-- Guest View -->
231
+ <div id="guest-view" class="guest-view">
232
+ <p class="subtitle">Secure, fast, and instant web stack.<br>Sign in to access your dashboard.</p>
233
+ <a href="/login" class="btn btn-primary">Sign In with Auth0</a>
234
+ <div style="margin-top: 1.5rem; font-size: 0.9rem; color: hsl(var(--text-secondary));">
235
+ Powered by Node.js & SQLite
236
+ </div>
237
+ </div>
238
+
239
+ <!-- Profile View -->
240
+ <div id="profile-view" class="profile-view">
241
+ <img id="user-pic" src="" alt="Profile Picture" class="profile-pic">
242
+ <div class="welcome-text">Hi, <span id="user-name">User</span>!</div>
243
+ <div class="status-badge">Authenticated</div>
244
+
245
+ <!-- Testing Controls -->
246
+ <div style="width: 100%; text-align: left; margin-top: 0.5rem; border-top: 1px solid hsla(var(--text-main), 0.1); padding-top: 1rem;">
247
+ <h3 class="section-title">SQLite KV Store</h3>
248
+ <div style="display: flex; gap: 0.5rem; margin-top: 0.5rem;">
249
+ <input type="text" id="kv-key" placeholder="Key" style="flex: 1; padding: 8px; border-radius: 6px; border: 1px solid #ddd;">
250
+ <input type="text" id="kv-value" placeholder="Value" style="flex: 1; padding: 8px; border-radius: 6px; border: 1px solid #ddd;">
251
+ </div>
252
+ <div style="display: flex; gap: 0.5rem; margin-top: 0.5rem; flex-wrap: wrap;">
253
+ <button onclick="api('POST', '/api', { key: val('kv-key'), value: val('kv-value') })" class="btn-sm">Set</button>
254
+ <button onclick="api('GET', '/api?key=' + val('kv-key'))" class="btn-sm">Get</button>
255
+ <button onclick="api('DELETE', '/api', { key: val('kv-key') })" class="btn-sm">Delete</button>
256
+ <button onclick="api('GET', '/all')" class="btn-sm">List All</button>
257
+ </div>
258
+
259
+ <h3 class="section-title">R2 Storage</h3>
260
+ <div style="margin-top: 0.5rem;">
261
+ <input type="text" id="r2-bucket" placeholder="Bucket Name" class="input-field">
262
+ <input type="file" id="r2-file" style="margin-bottom: 0.5rem; width: 100%;">
263
+ <div style="display: flex; gap: 0.5rem;">
264
+ <button onclick="uploadFile()" class="btn-sm">Upload File</button>
265
+ <button onclick="listFiles()" class="btn-sm">List Files</button>
266
+ </div>
267
+ </div>
268
+
269
+ <div id="output" style="margin-top: 1rem; background: #f3f4f6; padding: 10px; border-radius: 6px; font-family: monospace; font-size: 0.85rem; min-height: 60px; max-height: 200px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; color: #333;">Result output...</div>
270
+ </div>
271
+
272
+ <a href="/logout" class="btn btn-secondary" onclick="localStorage.clear()" style="margin-top: 1.5rem;">Sign Out</a>
273
+ </div>
274
+
275
+ </div>
276
+ </div>
277
+
278
+ <script>
279
+ // Check for user session in localStorage (set by auth0.js callback)
280
+ document.addEventListener('DOMContentLoaded', () => {
281
+ const name = localStorage.getItem('name');
282
+ const pic = localStorage.getItem('pic');
283
+
284
+ if (name) {
285
+ // User is logged in
286
+ document.getElementById('profile-view').style.display = 'flex';
287
+ document.getElementById('user-name').textContent = name;
288
+ if (pic) {
289
+ document.getElementById('user-pic').src = pic;
290
+ }
291
+ } else {
292
+ // User is guest
293
+ document.getElementById('guest-view').style.display = 'block';
294
+ }
295
+ });
296
+
297
+ const val = (id) => document.getElementById(id).value;
298
+ const log = (data) => document.getElementById('output').textContent = typeof data === 'object' ? JSON.stringify(data, null, 2) : data;
299
+
300
+ async function api(method, url, body) {
301
+ try {
302
+ const opts = { method };
303
+ if (body && method !== 'GET') {
304
+ opts.headers = { 'Content-Type': 'application/json' };
305
+ opts.body = JSON.stringify(body);
306
+ }
307
+ const res = await fetch(url, opts);
308
+ // Try parsing JSON, fallback to text if fail or empty
309
+ const text = await res.text();
310
+ try {
311
+ log(JSON.parse(text));
312
+ } catch {
313
+ log(text || 'OK');
314
+ }
315
+ } catch (e) {
316
+ log('Error: ' + e.message);
317
+ }
318
+ }
319
+
320
+ async function uploadFile() {
321
+ const bucket = val('r2-bucket');
322
+ const fileInput = document.getElementById('r2-file');
323
+ const file = fileInput.files[0];
324
+ if (!bucket || !file) return log('Please enter a bucket and select a file.');
325
+
326
+ const reader = new FileReader();
327
+ reader.onload = async () => {
328
+ const base64 = reader.result.split(',')[1];
329
+ await api('POST', '/upload', {
330
+ bucket,
331
+ key: file.name,
332
+ body: base64,
333
+ contentType: file.type
334
+ });
335
+ };
336
+ reader.readAsDataURL(file);
337
+ }
338
+
339
+ async function listFiles() {
340
+ const bucket = val('r2-bucket');
341
+ if (!bucket) return log('Please enter a bucket.');
342
+ await api('POST', '/files', { bucket });
343
+ }
344
+ </script>
345
+ </body>
346
+ </html>
package/routes.js ADDED
@@ -0,0 +1,57 @@
1
+ export default {
2
+ // Middleware functions (prefixed with _) run on every request
3
+ // Return false to continue processing, or a value to use as response
4
+
5
+ // Example: Log all requests
6
+ _log: (req, res, data) => {
7
+ console.log(`${req.method} ${req.url}`)
8
+ return false // Continue to next middleware or route
9
+ },
10
+
11
+ // Example: Basic authentication (commented out)
12
+ // _auth: (req, res, data) => {
13
+ // if (!data.token) {
14
+ // res.writeHead(401)
15
+ // return 'Unauthorized'
16
+ // }
17
+ // return false // Continue if authorized
18
+ // },
19
+
20
+ // Regular route handlers
21
+ hello: async (req, res, data) => {
22
+ await new Promise(r => setTimeout(r, 2000));
23
+ res.setHeader('some', 'head')
24
+ return { message: 'Hello World' }
25
+ },
26
+
27
+ api: (req, res, data) => {
28
+ return { message: 'API endpoint', data }
29
+ },
30
+
31
+ example429: (req, res, data) => {
32
+ return 429; // This will return a status code 429
33
+ },
34
+
35
+ "POST /examplepost": (req, res, data) => {
36
+ return { message: 'Example POST endpoint', data }
37
+ },
38
+
39
+ "GET /sse": (req, res, data) => {
40
+ res.writeHead(200, {
41
+ 'Content-Type': 'text/event-stream',
42
+ 'Cache-Control': 'no-cache',
43
+ 'Connection': 'keep-alive'
44
+ })
45
+ var x = 3;
46
+ const interval = setInterval(() => {
47
+ if (x <= 0) {
48
+ clearInterval(interval)
49
+ res.end()
50
+ return
51
+ }
52
+ res.write(`data: Hello World ${x}\n\n`)
53
+ x--
54
+ }, 1000)
55
+ return "SSE"
56
+ },
57
+ }