diff --git a/.changeset/clean-melons-shine.md b/.changeset/clean-melons-shine.md new file mode 100644 index 00000000..50d5dc3c --- /dev/null +++ b/.changeset/clean-melons-shine.md @@ -0,0 +1,5 @@ +--- +"@nodesecure/server": minor +--- + +Add buildServer wsPort option diff --git a/bin/index.js b/bin/index.js index b8c341d7..d230b632 100755 --- a/bin/index.js +++ b/bin/index.js @@ -73,7 +73,8 @@ prog .command("open [json]") .describe(i18n.getTokenSync("cli.commands.open.desc")) .option("-p, --port", i18n.getTokenSync("cli.commands.open.option_port"), process.env.PORT) - .option("-f, --fresh-start", i18n.getTokenSync("cli.commands.open.option_fresh_start"), process.env.PORT) + .option("--ws-port", i18n.getTokenSync("cli.commands.open.option_ws_port"), process.env.WS_PORT) + .option("-f, --fresh-start", i18n.getTokenSync("cli.commands.open.option_fresh_start"), false) .option("-d, --developer", i18n.getTokenSync("cli.commands.open.option_developer"), false) .action(commands.http.start); diff --git a/docs/cli/open.md b/docs/cli/open.md index e3c6a2d8..beba42cf 100644 --- a/docs/cli/open.md +++ b/docs/cli/open.md @@ -17,5 +17,6 @@ $ nsecure open [json] | Name | Shortcut | Default Value | Description | |---|---|---|---| | `--port` | `-p` | `process.env.PORT` | Specify the port on which the HTTP server should run. | +| `--ws-port` | N/A | `process.env.WS_PORT` | Specify the port on which the WebSocket server should run. | | `--fresh-start` | `-f` | `false` | Open the UI with no initial package. Also, the app will use a dedicated cache. | | `--developer` | `-d` | `false` | Launch the server in developer mode, enabling automatic HTML component refresh. | diff --git a/i18n/english.js b/i18n/english.js index 68500b23..9391d679 100644 --- a/i18n/english.js +++ b/i18n/english.js @@ -43,6 +43,7 @@ const cli = { open: { desc: "Run an HTTP Server with a given nsecure JSON file", option_port: "Define the running port", + option_ws_port: "Define the WebSocket server port", option_fresh_start: "Launch the server from scratch, ignoring any existing payload file", option_developer: "Launch the server in developer mode, enabling automatic HTML component refresh" }, diff --git a/i18n/french.js b/i18n/french.js index 79922565..58d79131 100644 --- a/i18n/french.js +++ b/i18n/french.js @@ -43,6 +43,7 @@ const cli = { open: { desc: "Démarre un serveur HTTP avec un fichier .json nsecure donné", option_port: "Port à utiliser", + option_ws_port: "Port du serveur WebSocket", option_fresh_start: "Lance le serveur à partir de zéro, en ignorant tout fichier de payload existant", option_developer: "Lance le serveur en mode développeur, permettant le rafraîchissement automatique des composants HTML" }, diff --git a/public/main.js b/public/main.js index 1c000409..c3201b0d 100644 --- a/public/main.js +++ b/public/main.js @@ -160,7 +160,7 @@ document.addEventListener("DOMContentLoaded", async() => { ); onSettingsSaved(window.settings.config); - const socket = new WebSocketClient(`ws://${window.location.hostname}:1338`); + const socket = new WebSocketClient(`ws://${window.location.hostname}:${window.__WS_PORT__}`); socket.addEventListener("PAYLOAD", onSocketPayload); socket.addEventListener("INIT", onSocketInitOrReload); socket.addEventListener("RELOAD", onSocketInitOrReload); diff --git a/src/commands/http.js b/src/commands/http.js index 1296ecac..100b9acd 100644 --- a/src/commands/http.js +++ b/src/commands/http.js @@ -28,6 +28,7 @@ export async function start( ) { const port = Number(options.port); const httpPort = Number.isNaN(port) ? 0 : port; + const wsPort = Number(options["ws-port"]) || void 0; const freshStart = Boolean(options.f); const enableDeveloperMode = Boolean(options.developer); @@ -59,6 +60,7 @@ export async function start( scanType: options.scanType, projectRootDir: kProjectRootDir, componentsDir: kComponentsDir, + wsPort, i18n: { english, french @@ -74,7 +76,8 @@ export async function start( new WebSocketServerInstanciator({ cache, - logger + logger, + port: wsPort }); for (const eventName of ["SIGINT", "SIGTERM"]) { diff --git a/views/index.html b/views/index.html index 66cef677..d4608d50 100644 --- a/views/index.html +++ b/views/index.html @@ -9,6 +9,7 @@ + NodeSecure diff --git a/workspaces/server/src/ALS.ts b/workspaces/server/src/ALS.ts index 1c028b1f..4abca48c 100644 --- a/workspaces/server/src/ALS.ts +++ b/workspaces/server/src/ALS.ts @@ -19,6 +19,7 @@ export interface AsyncStoreContext { french: NestedStringRecord; }; viewBuilder: ViewBuilder; + wsPort: number; reporter?: typeof report; } diff --git a/workspaces/server/src/ViewBuilder.class.ts b/workspaces/server/src/ViewBuilder.class.ts index 70599961..c06935ab 100644 --- a/workspaces/server/src/ViewBuilder.class.ts +++ b/workspaces/server/src/ViewBuilder.class.ts @@ -72,13 +72,14 @@ export class ViewBuilder { return HTMLStr; } - async render(): Promise { + async render(wsPort: number): Promise { const i18nLangName = await i18n.getLocalLang(); const HTMLStr = await this.#build(); const templateStr = zup(HTMLStr)({ lang: i18n.getTokenSync("lang"), i18nLangName, + wsPort, token: (tokenName: string) => i18n.getTokenSync(`ui.${tokenName}`) }); diff --git a/workspaces/server/src/endpoints/root.ts b/workspaces/server/src/endpoints/root.ts index 1476d9d8..8d75d6c6 100644 --- a/workspaces/server/src/endpoints/root.ts +++ b/workspaces/server/src/endpoints/root.ts @@ -13,9 +13,9 @@ export async function get( res: ServerResponse ) { try { - const { viewBuilder } = context.getStore()!; + const { viewBuilder, wsPort } = context.getStore()!; - const templateStr = await viewBuilder.render(); + const templateStr = await viewBuilder.render(wsPort); res.writeHead(200, { "Content-Type": "text/html" diff --git a/workspaces/server/src/index.ts b/workspaces/server/src/index.ts index 466efa29..6b691ab3 100644 --- a/workspaces/server/src/index.ts +++ b/workspaces/server/src/index.ts @@ -18,12 +18,16 @@ import { type NestedStringRecord } from "./ALS.ts"; +// CONSTANTS +export const DEFAULT_WS_PORT = 1338; + export interface BuildServerOptions { hotReload?: boolean; runFromPayload?: boolean; scanType?: "cwd" | "from"; projectRootDir: string; componentsDir: string; + wsPort?: number; i18n: { english: NestedStringRecord; french: NestedStringRecord; @@ -49,6 +53,7 @@ export async function buildServer( scanType = "from", projectRootDir, componentsDir, + wsPort = DEFAULT_WS_PORT, i18n, reporter } = options; @@ -61,6 +66,7 @@ export async function buildServer( const store: AsyncStoreContext = { i18n, viewBuilder, + wsPort, cache, reporter }; diff --git a/workspaces/server/src/websocket/index.ts b/workspaces/server/src/websocket/index.ts index 425a8e5d..dd3b068e 100644 --- a/workspaces/server/src/websocket/index.ts +++ b/workspaces/server/src/websocket/index.ts @@ -13,10 +13,12 @@ import type { WebSocketContext, WebSocketMessage } from "./websocket.types.ts"; +import { DEFAULT_WS_PORT } from "../index.ts"; export interface WebSocketServerInstanciatorOptions { logger: Logger; cache: PayloadCache; + port?: number; } export class WebSocketServerInstanciator { @@ -29,7 +31,7 @@ export class WebSocketServerInstanciator { this.#logger = options.logger; this.#cache = options.cache; const websocket = new WebSocketServer({ - port: 1338 + port: Number(options.port) || DEFAULT_WS_PORT }); websocket.on("connection", this.onConnectionHandler.bind(this)); } diff --git a/workspaces/server/test/httpServer.test.ts b/workspaces/server/test/httpServer.test.ts index 9095eb69..55eaacba 100644 --- a/workspaces/server/test/httpServer.test.ts +++ b/workspaces/server/test/httpServer.test.ts @@ -340,6 +340,47 @@ describe("httpServer without options", () => { }); }); +describe("httpServer wsPort option", () => { + const wsPort = 1335; + let httpServer: Server; + + before(async() => { + await i18n.extendFromSystemPath( + path.join(import.meta.dirname, "..", "..", "..", "i18n") + ); + + ({ httpServer } = await buildServer(JSON_PATH, { + projectRootDir: kProjectRootDir, + componentsDir: kComponentsDir, + reporter: async() => Buffer.from("fake-pdf-data"), + i18n: { + english: { + ui: {} + }, + french: { + ui: {} + } + }, + wsPort + })); + httpServer.listen(kHttpPort); + await once(httpServer, "listening"); + enableDestroy(httpServer); + }, { timeout: 5000 }); + + after(async() => { + httpServer.destroy(); + }); + + test("'/' should return index.html content", async() => { + const result = await get(kHttpURL); + + assert.equal(result.statusCode, 200); + assert.equal(result.headers["content-type"], "text/html"); + assert.ok(result.data.includes(`window.__WS_PORT__ = Number("${wsPort}")`)); + }); +}); + /** * HELPERS */