wallet-core/packages/web-util/src/serve.ts

126 lines
3.4 KiB
TypeScript
Raw Normal View History

2022-12-06 19:24:36 +01:00
import { Logger } from "@gnu-taler/taler-util";
import chokidar from "chokidar";
2022-12-06 13:21:17 +01:00
import express from "express";
import https from "https";
2023-03-10 05:21:28 +01:00
import http from "http";
2022-12-06 19:24:36 +01:00
import { parse } from "url";
2023-02-16 01:01:18 +01:00
import WebSocket from "ws";
2022-12-06 13:21:17 +01:00
2022-12-06 19:24:36 +01:00
import locahostCrt from "./keys/localhost.crt";
import locahostKey from "./keys/localhost.key";
import storiesHtml from "./stories.html";
2022-12-06 13:21:17 +01:00
import path from "path";
const httpServerOptions = {
key: locahostKey,
2022-12-06 19:24:36 +01:00
cert: locahostCrt,
2022-12-06 13:21:17 +01:00
};
const logger = new Logger("serve.ts");
const PATHS = {
WS: "/ws",
EXAMPLE: "/examples",
APP: "/app",
2022-12-06 19:24:36 +01:00
};
2022-12-06 13:21:17 +01:00
export async function serve(opts: {
2022-12-06 19:24:36 +01:00
folder: string;
port: number;
source?: string;
examplesLocationJs?: string;
examplesLocationCss?: string;
2023-05-05 13:34:21 +02:00
onSourceUpdate?: () => Promise<void>;
2022-12-06 13:21:17 +01:00
}): Promise<void> {
2022-12-06 19:24:36 +01:00
const app = express();
2022-12-06 13:21:17 +01:00
2022-12-06 19:24:36 +01:00
app.use(PATHS.APP, express.static(opts.folder));
2022-12-06 13:21:17 +01:00
2023-05-05 13:34:21 +02:00
const httpServer = http.createServer(app);
const httpPort = opts.port;
const httpsServer = https.createServer(httpServerOptions, app);
const httpsPort = opts.port + 1;
const servers = [httpServer, httpsServer];
logger.info(`Dev server. Endpoints:`);
logger.info(` ${PATHS.APP}: where root application can be tested`);
logger.info(` ${PATHS.EXAMPLE}: where examples can be found and browse`);
logger.info(` ${PATHS.WS}: websocket for live reloading`);
const wss = new WebSocket.Server({ noServer: true });
wss.on("connection", function connection(ws) {
ws.send("welcome");
});
servers.forEach(function addWSHandler(server) {
server.on("upgrade", function upgrade(request, socket, head) {
const { pathname } = parse(request.url || "");
if (pathname === PATHS.WS) {
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit("connection", ws, request);
});
2022-12-06 13:21:17 +01:00
} else {
2023-05-05 13:34:21 +02:00
socket.destroy();
2022-12-06 13:21:17 +01:00
}
2022-12-06 19:24:36 +01:00
});
2023-05-05 13:34:21 +02:00
});
2022-12-06 13:21:17 +01:00
2023-05-05 13:34:21 +02:00
const sendToAllClients = function (data: object): void {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
2023-03-15 11:23:20 +01:00
});
2023-05-05 13:34:21 +02:00
};
const watchingFolder = opts.source ?? opts.folder;
logger.info(`watching ${watchingFolder} for changes`);
chokidar.watch(watchingFolder).on("change", (path, stats) => {
logger.info(`changed: ${path}`);
if (opts.onSourceUpdate) {
sendToAllClients({ type: "file-updated-start", data: { path } });
opts
.onSourceUpdate()
.then((result) => {
sendToAllClients({
type: "file-updated-done",
data: { path, result },
});
})
.catch((error) => {
sendToAllClients({
type: "file-updated-failed",
data: { path, error },
});
});
} else {
sendToAllClients({ type: "file-change", data: { path } });
}
});
if (opts.onSourceUpdate) opts.onSourceUpdate();
app.get(PATHS.EXAMPLE, function (req: any, res: any) {
res.set("Content-Type", "text/html");
res.send(
storiesHtml
.replace(
"__EXAMPLES_JS_FILE_LOCATION__",
opts.examplesLocationJs ?? `.${PATHS.APP}/stories.js`,
)
.replace(
"__EXAMPLES_CSS_FILE_LOCATION__",
opts.examplesLocationCss ?? `.${PATHS.APP}/stories.css`,
),
);
});
logger.info(`Serving ${opts.folder} on ${httpPort}: plain HTTP`);
httpServer.listen(httpPort);
logger.info(`Serving ${opts.folder} on ${httpsPort}: HTTP + TLS`);
httpsServer.listen(httpsPort);
2022-12-06 13:21:17 +01:00
}