|
9 | 9 |
|
10 | 10 | import crypto from 'node:crypto'; |
11 | 11 | import fs from 'node:fs'; |
| 12 | +import net from 'node:net'; |
12 | 13 | import path from 'node:path'; |
13 | 14 | import { fileURLToPath } from 'node:url'; |
14 | 15 | import { createServer } from 'javascript-solid-server/src/server.js'; |
15 | 16 |
|
16 | 17 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
17 | 18 | const at = (p) => path.join(__dirname, p); |
18 | 19 |
|
19 | | -const PORT = Number(process.env.PORT || 3240); |
| 20 | +// Is `port` bindable on 0.0.0.0 right now? (A quick listen-then-close probe.) |
| 21 | +function portFree(port) { |
| 22 | + return new Promise((resolve) => { |
| 23 | + const probe = net.createServer(); |
| 24 | + probe.once('error', () => resolve(false)); |
| 25 | + probe.listen(port, '0.0.0.0', () => probe.close(() => resolve(true))); |
| 26 | + }); |
| 27 | +} |
| 28 | + |
| 29 | +// An OS-assigned free port (bind to 0, read it back, release). |
| 30 | +function freePort() { |
| 31 | + return new Promise((resolve, reject) => { |
| 32 | + const probe = net.createServer(); |
| 33 | + probe.once('error', reject); |
| 34 | + probe.listen(0, '0.0.0.0', () => { |
| 35 | + const { port } = probe.address(); |
| 36 | + probe.close(() => resolve(port)); |
| 37 | + }); |
| 38 | + }); |
| 39 | +} |
| 40 | + |
| 41 | +// Prefer the requested port; if it's busy, hop to a free one and say so. |
| 42 | +// (The plugin configs bake the origin in before listen, so the port has to |
| 43 | +// be settled up front — a retry-on-EADDRINUSE loop around listen() can't fix |
| 44 | +// the loopback/baseUrl already handed to every plugin.) |
| 45 | +async function pickPort(preferred) { |
| 46 | + if (await portFree(preferred)) return preferred; |
| 47 | + const hopped = await freePort(); |
| 48 | + console.warn(`port ${preferred} is in use — hopping to ${hopped} (set PORT= to pin one)`); |
| 49 | + return hopped; |
| 50 | +} |
| 51 | + |
| 52 | +const PORT = await pickPort(Number(process.env.PORT || 3240)); |
20 | 53 | const PUBLIC_URL = (process.env.PUBLIC_URL || `http://localhost:${PORT}`).replace(/\/+$/, ''); |
21 | 54 | const DATA = process.env.DATA || path.join(__dirname, 'data'); |
22 | 55 | const PODS = path.join(DATA, 'pods'); |
|
0 commit comments