Add --host flag, listen on 127.0.0.1 by default
The server previously bound to all interfaces with no auth. Default to loopback so it sits behind a reverse proxy; --host (or JOB_BOARD_HOST) overrides it. Plain HOST is ignored since some environments set it to the machine hostname. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+8
-4
@@ -18,14 +18,14 @@ No runtime dependencies. It uses Bun's built-in SQLite, HTTP server and HTMLRewr
|
||||
|
||||
```sh
|
||||
bun install
|
||||
bun start # http://localhost:3000
|
||||
bun start # http://127.0.0.1:3000
|
||||
bun start --port 8080 # or -p 8080
|
||||
```
|
||||
|
||||
The server scrapes every 24h. On startup it waits out the rest of the 24h since the last scrape (or scrapes right away if none happened in the last 24h).
|
||||
You can also click **Scrape now** in the UI, or run `bun run scrape` once (e.g. from cron).
|
||||
|
||||
Env vars: `PORT` (3000; `--port` overrides it), `SCRAPE_INTERVAL_HOURS` (24), `DB_PATH` (`data/jobs.sqlite`).
|
||||
Env vars: `PORT` (3000; `--port` overrides it), `JOB_BOARD_HOST` (127.0.0.1; `--host` overrides it), `SCRAPE_INTERVAL_HOURS` (24), `DB_PATH` (`data/jobs.sqlite`).
|
||||
|
||||
## Build a binary for a server
|
||||
|
||||
@@ -41,11 +41,15 @@ bun run build:linux-arm64 # bin/job-board-linux-arm64 (e.g. Raspberry Pi, ARM
|
||||
On the server:
|
||||
|
||||
```sh
|
||||
./job-board # web server on :3000, scrapes every 24h
|
||||
./job-board # http://127.0.0.1:3000, scrapes every 24h
|
||||
./job-board --port 8080
|
||||
./job-board --host 0.0.0.0 --port 8080 # listen on all interfaces
|
||||
./job-board scrape # scrape once and exit (exit code 1 if a scraper failed)
|
||||
```
|
||||
|
||||
It listens on loopback (`127.0.0.1`) only by default, so it's meant to sit behind a reverse proxy
|
||||
(nginx, Caddy). There is no login: only use `--host 0.0.0.0` on a trusted network.
|
||||
|
||||
The database goes to `data/jobs.sqlite` next to the binary (override with `DB_PATH`).
|
||||
The binaries target glibc Linux; for Alpine use `--target=bun-linux-x64-musl`, and for old CPUs
|
||||
without AVX2 use `--target=bun-linux-x64-baseline`.
|
||||
@@ -58,7 +62,7 @@ Description=Job board
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/opt/job-board/job-board --port 3000
|
||||
ExecStart=/opt/job-board/job-board --host 127.0.0.1 --port 3000
|
||||
Restart=on-failure
|
||||
User=jobboard
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Entry point (also used by the compiled binary):
|
||||
// job-board [serve] [--port <n>] start the web server (scrapes every 24h)
|
||||
// job-board [serve] [--host <addr>] [--port <n>] start the web server (scrapes every 24h)
|
||||
// job-board scrape run all scrapers once and exit (exit code 1 if any failed)
|
||||
|
||||
export {};
|
||||
|
||||
const USAGE = "Usage: job-board [serve] [--port <n>] | job-board scrape";
|
||||
const USAGE = "Usage: job-board [serve] [--host <addr>] [--port <n>] | job-board scrape\n --host address to listen on (default 127.0.0.1; 0.0.0.0 = all interfaces)\n --port port to listen on (default 3000)";
|
||||
|
||||
function fail(message: string): never {
|
||||
console.error(`${message}\n${USAGE}`);
|
||||
@@ -20,6 +20,10 @@ for (let i = 0; i < args.length; i++) {
|
||||
const port = Number(value);
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65535) fail(`Invalid port "${value ?? ""}".`);
|
||||
process.env.PORT = String(port); // read by server.ts; overrides the PORT env var
|
||||
} else if (arg === "--host" || arg.startsWith("--host=")) {
|
||||
const value = arg.startsWith("--host=") ? arg.slice("--host=".length) : args[++i];
|
||||
if (!value || value.startsWith("-")) fail(`Invalid host "${value ?? ""}".`);
|
||||
process.env.JOB_BOARD_HOST = value; // read by server.ts; overrides the env var
|
||||
} else if (arg === "--help" || arg === "-h") {
|
||||
console.log(USAGE);
|
||||
process.exit(0);
|
||||
|
||||
@@ -3,6 +3,9 @@ import { isScraping, runAllScrapers } from "./scrape";
|
||||
import { scrapers } from "./scrapers";
|
||||
|
||||
const PORT = Number(process.env.PORT ?? 3000);
|
||||
// Loopback only by default; use --host 0.0.0.0 (or JOB_BOARD_HOST) to expose it on the network.
|
||||
// Not plain HOST: some shells/environments set that to the machine hostname.
|
||||
const HOST = process.env.JOB_BOARD_HOST || "127.0.0.1";
|
||||
const SCRAPE_INTERVAL_HOURS = Number(process.env.SCRAPE_INTERVAL_HOURS ?? 24);
|
||||
|
||||
// --- Scheduler: scrape every SCRAPE_INTERVAL_HOURS. On startup, wait out whatever is left of the
|
||||
@@ -205,6 +208,7 @@ function page(url: URL) {
|
||||
const html = (body: string) => new Response(body, { headers: { "content-type": "text/html; charset=utf-8" } });
|
||||
|
||||
Bun.serve({
|
||||
hostname: HOST,
|
||||
port: PORT,
|
||||
routes: {
|
||||
"/": (req) => html(page(new URL(req.url))),
|
||||
@@ -227,4 +231,5 @@ Bun.serve({
|
||||
fetch: () => new Response("Not found", { status: 404 }),
|
||||
});
|
||||
|
||||
console.log(`Job board running at http://localhost:${PORT} (scraping every ${SCRAPE_INTERVAL_HOURS}h)`);
|
||||
const shownHost = HOST.includes(":") ? `[${HOST}]` : HOST;
|
||||
console.log(`Job board running at http://${shownHost}:${PORT} (scraping every ${SCRAPE_INTERVAL_HOURS}h)`);
|
||||
|
||||
Reference in New Issue
Block a user