kist
Version:
Lightweight Package Pipeline Processor with Plugin Architecture
139 lines • 5.68 kB
JavaScript
import { __awaiter } from "tslib";
import express from "express";
import rateLimit from "express-rate-limit";
import path from "path";
import { WebSocket, WebSocketServer } from "ws";
import { AbstractProcess } from "../core/abstract/AbstractProcess.js";
import { ConfigStore } from "../core/config/ConfigStore.js";
export class LiveServer extends AbstractProcess {
constructor() {
super();
this.app = express();
this.clients = new Set();
const configStore = ConfigStore.getInstance();
const liveReloadOptions = configStore.get("options.live") || {};
this.port = liveReloadOptions.port || 3000;
this.root = path.resolve(process.cwd(), liveReloadOptions.root || "public");
this.watchPaths = (liveReloadOptions.watchPaths || [
"src/**/*",
"config/**/*",
"pack.yaml",
]).map((p) => path.resolve(process.cwd(), p));
this.ignoredPaths = (liveReloadOptions.ignoredPaths || ["node_modules"]).map((p) => path.resolve(process.cwd(), p));
this.logInitializationDetails();
this.server = this.app.listen(this.port, () => {
this.logInfo(`Live Server running at http://localhost:${this.port}`);
});
this.wss = new WebSocketServer({ server: this.server });
this.setupRateLimiter();
this.setupWebSocketHandlers();
this.setupMiddleware();
}
initializeServer() {
this.server = this.app.listen(this.port, () => {
this.logInfo(`Live Server running at http://localhost:${this.port}`);
});
this.wss = new WebSocketServer({ server: this.server });
}
logInitializationDetails() {
this.logInfo(`LiveServer initialized with port: ${this.port}`);
this.logInfo(`Serving static files from: ${this.root}`);
this.logInfo(`Watching paths: ${JSON.stringify(this.watchPaths)}`);
this.logInfo(`Ignoring paths: ${JSON.stringify(this.ignoredPaths)}`);
}
setupRateLimiter() {
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: "Too many requests from this IP, please try again later.",
});
this.app.use(limiter);
}
setupWebSocketHandlers() {
this.wss.on("connection", (ws) => {
this.logInfo("New WebSocket connection established.");
this.clients.add(ws);
ws.on("message", (message) => {
this.logInfo(`WebSocket message received: ${message.toString()}`);
});
ws.on("close", () => {
this.logInfo("WebSocket connection closed.");
this.clients.delete(ws);
});
ws.on("error", (error) => {
console.error("WebSocket encountered an error:", error);
this.clients.delete(ws);
});
});
}
setupMiddleware() {
this.logInfo(`Resolved public directory: ${this.root}`);
this.logInfo(`Serving static files from: ${this.root}`);
this.app.use(express.static(this.root));
this.app.use(this.injectLiveReloadScript.bind(this));
}
injectLiveReloadScript(req, res, next) {
if (req.url.endsWith(".html")) {
const sanitizedPath = path.join(path.resolve(__dirname, "public"), path.normalize(req.url).replace(/^(\.\.(\/|\\|$))+/g, ""));
res.sendFile(sanitizedPath, (err) => {
if (err) {
console.error("Error sending HTML file:", err);
next(err);
}
else {
res.write(`<script>
const ws = new WebSocket("ws://localhost:${this.port}");
ws.onmessage = (event) => {
if (event.data === "reload") {
this.logInfo("Reloading page...");
window.location.reload();
}
};
</script>`);
res.end();
}
});
}
else {
next();
}
}
reloadClients() {
this.logInfo("Reloading all connected clients...");
this.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send("reload");
}
});
}
shutdown() {
return __awaiter(this, void 0, void 0, function* () {
this.logInfo("Shutting down Live Reload Server...");
this.clients.forEach((client) => client.close());
this.wss.close();
yield new Promise((resolve, reject) => {
this.server.close((err) => {
if (err) {
if (this.isErrnoException(err) &&
err.code === "ERR_SERVER_NOT_RUNNING") {
this.logWarn("Server is not running, skipping shutdown.");
resolve();
}
else {
this.logError("Error shutting down server:", err);
reject(err);
}
}
else {
resolve();
}
});
});
this.logInfo("Live Reload Server has been shut down.");
});
}
isErrnoException(error) {
return typeof error === "object" && error !== null && "code" in error;
}
}
//# sourceMappingURL=LiveServer.js.map