From 6831470bf51d43cda40fc03f2cad3b775ff44a1c Mon Sep 17 00:00:00 2001 From: Julian Gruber Date: Mon, 20 Mar 2023 14:45:31 +0100 Subject: [PATCH 1/2] add http server. closes #58 --- README.md | 22 ++++-- bin/station.js | 18 ++++- commands/station.js | 15 ++++- package-lock.json | 158 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + test/test.js | 30 +++++++++ 6 files changed, 239 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4a5ba3f6..4a88f283 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,18 @@ $ station logs --follow ... ``` +### `$ station --listen` + +Open HTTP API. + +Routes: + +- `/` 404 (Placeholder route) + +### `$ station --listen --port` + +Set HTTP API port. Default: `7834` + ### `$ station --help` Show help. @@ -145,12 +157,14 @@ $ station --help Usage: station [options] Commands: - station Start Station [default] - station metrics Show metrics - station activity Show activity log - station logs [module] Show module logs + station Start Station [default] + station metrics Show metrics + station activity Show activity log + station logs [module] Show module logs Options: + -l, --listen Open HTTP API [boolean] + -p, --port HTTP API port [number] [default: 7834] -v, --version Show version number [boolean] -h, --help Show help [boolean] ``` diff --git a/bin/station.js b/bin/station.js index 63b06032..eb113e29 100755 --- a/bin/station.js +++ b/bin/station.js @@ -22,7 +22,23 @@ await fs.mkdir(paths.moduleLogs, { recursive: true }) yargs(hideBin(process.argv)) .usage('Usage: $0 [options]') - .command('$0', 'Start Station', () => {}, commands.station) + .command( + '$0', + 'Start Station', + yargs => yargs + .option('listen', { + alias: 'l', + type: 'boolean', + description: 'Open HTTP API' + }) + .option('port', { + alias: 'p', + type: 'number', + default: 7834, + description: 'HTTP API port' + }), + commands.station + ) .command('metrics', 'Show metrics', () => {}, commands.metrics) .commands('activity', 'Show activity log', () => {}, commands.activity) .command('logs [module]', 'Show module logs', () => {}, commands.logs) diff --git a/commands/station.js b/commands/station.js index 1deae961..818b345b 100644 --- a/commands/station.js +++ b/commands/station.js @@ -6,10 +6,17 @@ import { createMetricsStream } from '../lib/metrics.js' import { createActivityStream } from '../lib/activity.js' import lockfile from 'proper-lockfile' import { maybeCreateFile } from '../lib/util.js' +import http from 'node:http' +import { once } from 'node:events' const { FIL_WALLET_ADDRESS } = process.env -export const station = async () => { +const handler = (req, res) => { + res.statusCode = 404 + res.end(http.STATUS_CODES[404]) +} + +export const station = async ({ listen, port }) => { if (!FIL_WALLET_ADDRESS) { console.error('FIL_WALLET_ADDRESS required') process.exit(1) @@ -24,6 +31,12 @@ export const station = async () => { process.exit(1) } + if (listen) { + const server = http.createServer(handler) + server.listen(port).unref() + await once(server, 'listening') + } + await saturnNode.start({ FIL_WALLET_ADDRESS, storagePath: join(paths.moduleStorage, 'saturn-L2-node'), diff --git a/package-lock.json b/package-lock.json index 1d6f1c8f..94f43c65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,9 @@ "station": "bin/station.js" }, "devDependencies": { + "get-port": "^6.1.2", "gunzip-maybe": "^1.4.2", + "node-fetch": "^3.3.1", "np": "^7.6.3", "prettier": "^2.8.4", "standard": "^17.0.0", @@ -1967,6 +1969,15 @@ "node": ">=8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/date-fns": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", @@ -2954,6 +2965,29 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -3146,6 +3180,18 @@ "node": ">=8.0.0" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -3277,6 +3323,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz", + "integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -5761,6 +5819,43 @@ "node": ">=6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -10726,6 +10821,15 @@ "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", "dev": true }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -12407,6 +12511,12 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true + }, "date-fns": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", @@ -13145,6 +13255,16 @@ "reusify": "^1.0.4" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -13290,6 +13410,15 @@ "signal-exit": "^3.0.2" } }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "requires": { + "fetch-blob": "^3.1.2" + } + }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -13379,6 +13508,12 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-port": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-6.1.2.tgz", + "integrity": "sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==", + "dev": true + }, "get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -15211,6 +15346,23 @@ } } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, + "node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "dev": true, + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -18716,6 +18868,12 @@ } } }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 3c18183f..3fdac99b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "release": "np" }, "devDependencies": { + "get-port": "^6.1.2", "gunzip-maybe": "^1.4.2", + "node-fetch": "^3.3.1", "np": "^7.6.3", "prettier": "^2.8.4", "standard": "^17.0.0", diff --git a/test/test.js b/test/test.js index f1913ca5..6a6bc939 100644 --- a/test/test.js +++ b/test/test.js @@ -7,6 +7,8 @@ import fs from 'node:fs/promises' import { randomUUID } from 'node:crypto' import { once } from 'node:events' import { getPaths } from '../lib/paths.js' +import fetch from 'node-fetch' +import getPort from 'get-port' const __dirname = dirname(fileURLToPath(import.meta.url)) const station = join(__dirname, '..', 'bin', 'station.js') @@ -218,6 +220,34 @@ test('Lockfile', async t => { throw new Error('did not throw') }) +test('HTTP API', async t => { + await t.test('Default port', async t => { + const XDG_STATE_HOME = join(tmpdir(), randomUUID()) + const ps = execa( + station, + ['--listen'], + { env: { XDG_STATE_HOME, FIL_WALLET_ADDRESS } } + ) + await once(ps.stdout, 'data') + const res = await fetch('http://127.0.0.1:7834') + t.equal(res.status, 404) + ps.kill() + }) + await t.test('Custom port', async t => { + const XDG_STATE_HOME = join(tmpdir(), randomUUID()) + const port = await getPort() + const ps = execa( + station, + ['--listen', `--port=${port}`], + { env: { XDG_STATE_HOME, FIL_WALLET_ADDRESS } } + ) + await once(ps.stdout, 'data') + const res = await fetch(`http://127.0.0.1:${port}`) + t.equal(res.status, 404) + ps.kill() + }) +}) + test('Update modules', async t => { await execa(join(__dirname, '..', 'scripts', 'update-modules.js')) }) From aa4335d1928f5378af421c9e752ced414f2ae4ef Mon Sep 17 00:00:00 2001 From: Julian Gruber Date: Mon, 20 Mar 2023 18:28:04 +0100 Subject: [PATCH 2/2] move port to environment variable --- README.md | 10 ++++++---- bin/station.js | 6 ------ commands/station.js | 4 ++-- test/test.js | 4 ++-- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4a88f283..b74f96c3 100644 --- a/README.md +++ b/README.md @@ -140,13 +140,15 @@ $ station logs --follow Open HTTP API. -Routes: +This command has the following additional configuration in addition to common +the configuration options described in +[Common Configuration](#common-configuration): -- `/` 404 (Placeholder route) +- `PORT` _(number)_: The port to listen on. Defaults to `7834`. -### `$ station --listen --port` +Routes: -Set HTTP API port. Default: `7834` +- `/` 404 (Placeholder route) ### `$ station --help` diff --git a/bin/station.js b/bin/station.js index eb113e29..7e55afa1 100755 --- a/bin/station.js +++ b/bin/station.js @@ -30,12 +30,6 @@ yargs(hideBin(process.argv)) alias: 'l', type: 'boolean', description: 'Open HTTP API' - }) - .option('port', { - alias: 'p', - type: 'number', - default: 7834, - description: 'HTTP API port' }), commands.station ) diff --git a/commands/station.js b/commands/station.js index 818b345b..ebf88ae5 100644 --- a/commands/station.js +++ b/commands/station.js @@ -9,7 +9,7 @@ import { maybeCreateFile } from '../lib/util.js' import http from 'node:http' import { once } from 'node:events' -const { FIL_WALLET_ADDRESS } = process.env +const { FIL_WALLET_ADDRESS, PORT = 7834 } = process.env const handler = (req, res) => { res.statusCode = 404 @@ -33,7 +33,7 @@ export const station = async ({ listen, port }) => { if (listen) { const server = http.createServer(handler) - server.listen(port).unref() + server.listen(PORT).unref() await once(server, 'listening') } diff --git a/test/test.js b/test/test.js index 6a6bc939..3455f2e3 100644 --- a/test/test.js +++ b/test/test.js @@ -238,8 +238,8 @@ test('HTTP API', async t => { const port = await getPort() const ps = execa( station, - ['--listen', `--port=${port}`], - { env: { XDG_STATE_HOME, FIL_WALLET_ADDRESS } } + ['--listen'], + { env: { XDG_STATE_HOME, FIL_WALLET_ADDRESS, PORT: port } } ) await once(ps.stdout, 'data') const res = await fetch(`http://127.0.0.1:${port}`)