@chenbihao/pomasa-dashboard 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +107 -16
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -57,6 +57,7 @@ var init_paths = __esm({
|
|
|
57
57
|
// server/setup.ts
|
|
58
58
|
import express from "express";
|
|
59
59
|
import cors from "cors";
|
|
60
|
+
import crypto from "crypto";
|
|
60
61
|
import path7 from "path";
|
|
61
62
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
62
63
|
import { createServer } from "http";
|
|
@@ -147,9 +148,23 @@ function spawnShell(ws, cwd, cols, rows) {
|
|
|
147
148
|
}
|
|
148
149
|
return spawnWithChildProcess(ws, cwd);
|
|
149
150
|
}
|
|
150
|
-
function setupTerminalWebSocket(server) {
|
|
151
|
+
function setupTerminalWebSocket(server, accessToken = null) {
|
|
151
152
|
const wss = new WebSocketServer({ server, path: "/api/terminal" });
|
|
152
|
-
wss.on("connection", (ws) => {
|
|
153
|
+
wss.on("connection", (ws, req) => {
|
|
154
|
+
if (accessToken) {
|
|
155
|
+
const cookieHeader = req.headers.cookie || "";
|
|
156
|
+
const cookies = {};
|
|
157
|
+
for (const pair of cookieHeader.split(";")) {
|
|
158
|
+
const [key, ...rest] = pair.split("=");
|
|
159
|
+
if (key && rest.length) {
|
|
160
|
+
cookies[key.trim()] = rest.join("=").trim();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (cookies.pomasa_access !== accessToken) {
|
|
164
|
+
ws.close(4001, "Unauthorized");
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
153
168
|
let shellProcess = null;
|
|
154
169
|
ws.on("message", (data) => {
|
|
155
170
|
try {
|
|
@@ -975,16 +990,78 @@ var mas_default = router4;
|
|
|
975
990
|
|
|
976
991
|
// server/setup.ts
|
|
977
992
|
var __dirname2 = path7.dirname(fileURLToPath2(import.meta.url));
|
|
978
|
-
function
|
|
993
|
+
function generateToken() {
|
|
994
|
+
return crypto.randomBytes(24).toString("hex");
|
|
995
|
+
}
|
|
996
|
+
function denyAccess(req, res) {
|
|
997
|
+
const accept = req.headers.accept || "";
|
|
998
|
+
if (accept.includes("text/html")) {
|
|
999
|
+
res.status(401).send(`<!DOCTYPE html>
|
|
1000
|
+
<html><head><meta charset="utf-8"><title>POMASA Dashboard</title>
|
|
1001
|
+
<style>
|
|
1002
|
+
body { font-family: -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #f5f5f5; }
|
|
1003
|
+
.box { text-align: center; padding: 2rem; background: #fff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
|
1004
|
+
h2 { color: #333; margin-bottom: 0.5rem; }
|
|
1005
|
+
p { color: #666; }
|
|
1006
|
+
code { background: #eee; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
|
|
1007
|
+
</style></head>
|
|
1008
|
+
<body><div class="box">
|
|
1009
|
+
<h2>\u{1F512} \u9700\u8981\u8BBF\u95EE\u4EE4\u724C</h2>
|
|
1010
|
+
<p>\u8BF7\u4F7F\u7528\u542F\u52A8\u65F6\u8F93\u51FA\u7684\u5E26\u6709 <code>?token=...</code> \u7684 URL \u8BBF\u95EE</p>
|
|
1011
|
+
</div></body></html>`);
|
|
1012
|
+
} else {
|
|
1013
|
+
res.status(401).json({ error: "Unauthorized", message: "Access token required. Use ?token=... in URL." });
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
function createApp(options = {}) {
|
|
1017
|
+
const { token: enableToken = true } = options;
|
|
979
1018
|
const app = express();
|
|
980
1019
|
const server = createServer(app);
|
|
1020
|
+
const accessToken = enableToken ? generateToken() : null;
|
|
981
1021
|
app.use(cors());
|
|
982
1022
|
app.use(express.json({ limit: "10mb" }));
|
|
1023
|
+
app.use((req, _res, next) => {
|
|
1024
|
+
if (!req.cookies) {
|
|
1025
|
+
req.cookies = {};
|
|
1026
|
+
const cookieHeader = req.headers.cookie;
|
|
1027
|
+
if (cookieHeader) {
|
|
1028
|
+
for (const pair of cookieHeader.split(";")) {
|
|
1029
|
+
const [key, ...rest] = pair.split("=");
|
|
1030
|
+
if (key && rest.length) {
|
|
1031
|
+
req.cookies[key.trim()] = rest.join("=").trim();
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
next();
|
|
1037
|
+
});
|
|
1038
|
+
if (accessToken) {
|
|
1039
|
+
app.use((req, res, next) => {
|
|
1040
|
+
const queryToken = req.query.token;
|
|
1041
|
+
if (queryToken) {
|
|
1042
|
+
if (queryToken === accessToken) {
|
|
1043
|
+
res.cookie("pomasa_access", accessToken, {
|
|
1044
|
+
httpOnly: true,
|
|
1045
|
+
sameSite: "lax",
|
|
1046
|
+
path: "/"
|
|
1047
|
+
});
|
|
1048
|
+
const cleanUrl = req.path;
|
|
1049
|
+
return res.redirect(302, cleanUrl);
|
|
1050
|
+
}
|
|
1051
|
+
return denyAccess(req, res);
|
|
1052
|
+
}
|
|
1053
|
+
const cookieToken = req.cookies?.pomasa_access;
|
|
1054
|
+
if (cookieToken === accessToken) {
|
|
1055
|
+
return next();
|
|
1056
|
+
}
|
|
1057
|
+
return denyAccess(req, res);
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
983
1060
|
app.use("/api/projects", projects_default);
|
|
984
1061
|
app.use("/api/framework", framework_default);
|
|
985
1062
|
app.use("/api/fs", filesystem_default);
|
|
986
1063
|
app.use("/api/mas", mas_default);
|
|
987
|
-
setupTerminalWebSocket(server);
|
|
1064
|
+
setupTerminalWebSocket(server, accessToken);
|
|
988
1065
|
const distDir = path7.resolve(__dirname2, "../dist");
|
|
989
1066
|
app.use(express.static(distDir));
|
|
990
1067
|
app.use("/api", (_req, res) => {
|
|
@@ -993,25 +1070,22 @@ function createApp() {
|
|
|
993
1070
|
app.get("/{*splat}", (_req, res) => {
|
|
994
1071
|
res.sendFile(path7.join(distDir, "index.html"));
|
|
995
1072
|
});
|
|
996
|
-
return { app, server };
|
|
1073
|
+
return { app, server, accessToken };
|
|
997
1074
|
}
|
|
998
|
-
function startServer(port2, maxRetries = 10) {
|
|
999
|
-
const { server } = createApp();
|
|
1075
|
+
function startServer(port2, host2 = "0.0.0.0", options = {}, maxRetries = 10) {
|
|
1076
|
+
const { server, accessToken } = createApp(options);
|
|
1000
1077
|
return new Promise((resolve, reject) => {
|
|
1001
1078
|
server.on("error", (err) => {
|
|
1002
1079
|
if (err.code === "EADDRINUSE" && maxRetries > 0) {
|
|
1003
1080
|
console.log(`Port ${port2} in use, trying ${port2 + 1}...`);
|
|
1004
1081
|
server.removeAllListeners("error");
|
|
1005
|
-
startServer(port2 + 1,
|
|
1082
|
+
startServer(port2 + 1, host2, options, maxRetries).then(resolve).catch(reject);
|
|
1006
1083
|
} else {
|
|
1007
1084
|
reject(err);
|
|
1008
1085
|
}
|
|
1009
1086
|
});
|
|
1010
|
-
server.listen(port2, () => {
|
|
1011
|
-
|
|
1012
|
-
console.log(`API at http://localhost:${port2}/api`);
|
|
1013
|
-
console.log(`WebSocket terminal at ws://localhost:${port2}/api/terminal`);
|
|
1014
|
-
resolve(server);
|
|
1087
|
+
server.listen(port2, host2, () => {
|
|
1088
|
+
resolve({ server, accessToken });
|
|
1015
1089
|
});
|
|
1016
1090
|
});
|
|
1017
1091
|
}
|
|
@@ -1030,12 +1104,29 @@ if (portArg !== -1 && process.argv[portArg + 1]) {
|
|
|
1030
1104
|
} else if (process.env.PORT) {
|
|
1031
1105
|
port = parseInt(process.env.PORT, 10);
|
|
1032
1106
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1107
|
+
var host = "0.0.0.0";
|
|
1108
|
+
var hostArg = process.argv.indexOf("--host");
|
|
1109
|
+
if (hostArg !== -1 && process.argv[hostArg + 1]) {
|
|
1110
|
+
host = process.argv[hostArg + 1];
|
|
1111
|
+
} else if (process.env.HOST) {
|
|
1112
|
+
host = process.env.HOST;
|
|
1113
|
+
}
|
|
1114
|
+
var noToken = process.argv.includes("--no-token") || !!process.env.NO_TOKEN;
|
|
1115
|
+
startServer(port, host, { token: !noToken }).then(async ({ accessToken }) => {
|
|
1116
|
+
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
1117
|
+
const baseUrl = `http://${displayHost}:${port}`;
|
|
1118
|
+
const url = accessToken ? `${baseUrl}/?token=${accessToken}` : baseUrl;
|
|
1119
|
+
console.log(`POMASA Dashboard running at ${baseUrl}`);
|
|
1120
|
+
if (accessToken) {
|
|
1121
|
+
console.log(`Access token: ${accessToken}`);
|
|
1122
|
+
console.log(`Full URL (with token): ${url}`);
|
|
1123
|
+
}
|
|
1124
|
+
console.log(`API at ${baseUrl}/api`);
|
|
1125
|
+
console.log(`WebSocket terminal at ws://${displayHost}:${port}/api/terminal`);
|
|
1035
1126
|
try {
|
|
1036
1127
|
const open = (await import("open")).default;
|
|
1037
1128
|
await open(url);
|
|
1038
|
-
console.log(`Opened
|
|
1129
|
+
console.log(`Opened in browser`);
|
|
1039
1130
|
} catch {
|
|
1040
1131
|
console.log(`Please open ${url} in your browser`);
|
|
1041
1132
|
}
|