@electron-memory/monitor 0.2.1 → 0.2.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/index.js +91 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +92 -13
- package/dist/index.mjs.map +1 -1
- package/dist/ui/index.html +3 -3
- package/package.json +70 -70
package/dist/index.js
CHANGED
|
@@ -1114,6 +1114,7 @@ var path3 = __toESM(require("path"));
|
|
|
1114
1114
|
var import_electron2 = require("electron");
|
|
1115
1115
|
var import_promises = require("fs/promises");
|
|
1116
1116
|
var path2 = __toESM(require("path"));
|
|
1117
|
+
var import_node_url = require("url");
|
|
1117
1118
|
var SCHEME = "emm-dashboard";
|
|
1118
1119
|
var privilegedRegistered = false;
|
|
1119
1120
|
var handlerRegistered = false;
|
|
@@ -1142,8 +1143,60 @@ function isPathInsideRoot(filePath, root) {
|
|
|
1142
1143
|
if (f === r) {
|
|
1143
1144
|
return true;
|
|
1144
1145
|
}
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1146
|
+
const rel = path2.relative(r, f);
|
|
1147
|
+
if (!rel || rel.startsWith("..") || path2.isAbsolute(rel)) {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
return true;
|
|
1151
|
+
}
|
|
1152
|
+
function urlToUiRelativePath(requestUrl) {
|
|
1153
|
+
let u;
|
|
1154
|
+
try {
|
|
1155
|
+
u = new URL(requestUrl);
|
|
1156
|
+
} catch {
|
|
1157
|
+
return "";
|
|
1158
|
+
}
|
|
1159
|
+
let p = "";
|
|
1160
|
+
try {
|
|
1161
|
+
p = decodeURIComponent(u.pathname || "");
|
|
1162
|
+
} catch {
|
|
1163
|
+
p = u.pathname || "";
|
|
1164
|
+
}
|
|
1165
|
+
p = p.replace(/^\/+/, "");
|
|
1166
|
+
const host = (u.hostname || "").toLowerCase();
|
|
1167
|
+
if (p.includes("..")) {
|
|
1168
|
+
return "";
|
|
1169
|
+
}
|
|
1170
|
+
if (host === "electron" || host === "") {
|
|
1171
|
+
return p || "index.html";
|
|
1172
|
+
}
|
|
1173
|
+
if (p) {
|
|
1174
|
+
return `${host}/${p}`.replace(/\\/g, "/");
|
|
1175
|
+
}
|
|
1176
|
+
if (host.includes(".")) {
|
|
1177
|
+
return host;
|
|
1178
|
+
}
|
|
1179
|
+
return "index.html";
|
|
1180
|
+
}
|
|
1181
|
+
function bufferToResponseBody(buf) {
|
|
1182
|
+
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
1183
|
+
}
|
|
1184
|
+
function looksLikeHtml(buf) {
|
|
1185
|
+
let i = 0;
|
|
1186
|
+
if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
|
|
1187
|
+
i = 3;
|
|
1188
|
+
}
|
|
1189
|
+
while (i < buf.length && (buf[i] === 32 || buf[i] === 9 || buf[i] === 10 || buf[i] === 13)) {
|
|
1190
|
+
i += 1;
|
|
1191
|
+
}
|
|
1192
|
+
return i < buf.length && buf[i] === 60;
|
|
1193
|
+
}
|
|
1194
|
+
function corsHeaders() {
|
|
1195
|
+
return {
|
|
1196
|
+
"Access-Control-Allow-Origin": "*",
|
|
1197
|
+
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
|
1198
|
+
"Access-Control-Allow-Headers": "*"
|
|
1199
|
+
};
|
|
1147
1200
|
}
|
|
1148
1201
|
function registerDashboardSchemePrivileged() {
|
|
1149
1202
|
if (privilegedRegistered) {
|
|
@@ -1175,29 +1228,55 @@ function ensureDashboardProtocolHandler(uiRoot) {
|
|
|
1175
1228
|
handlerRegistered = true;
|
|
1176
1229
|
const base = path2.resolve(uiRoot);
|
|
1177
1230
|
import_electron2.protocol.handle(SCHEME, async (request) => {
|
|
1231
|
+
if (request.method === "OPTIONS") {
|
|
1232
|
+
return new Response(null, { status: 204, headers: corsHeaders() });
|
|
1233
|
+
}
|
|
1178
1234
|
try {
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
rel = "index.html";
|
|
1235
|
+
const rel = urlToUiRelativePath(request.url);
|
|
1236
|
+
if (!rel || rel.includes("..")) {
|
|
1237
|
+
return new Response("Bad path", { status: 400, headers: corsHeaders() });
|
|
1183
1238
|
}
|
|
1184
|
-
const filePath = path2.resolve(path2.join(base, rel));
|
|
1239
|
+
const filePath = path2.resolve(path2.join(base, ...rel.split("/")));
|
|
1185
1240
|
if (!isPathInsideRoot(filePath, base)) {
|
|
1186
|
-
return new Response("Forbidden", { status: 403 });
|
|
1241
|
+
return new Response("Forbidden", { status: 403, headers: corsHeaders() });
|
|
1242
|
+
}
|
|
1243
|
+
const fileUrl = (0, import_node_url.pathToFileURL)(filePath).href;
|
|
1244
|
+
let upstream;
|
|
1245
|
+
try {
|
|
1246
|
+
upstream = await import_electron2.net.fetch(fileUrl);
|
|
1247
|
+
} catch {
|
|
1248
|
+
upstream = new Response(null, { status: 599 });
|
|
1249
|
+
}
|
|
1250
|
+
let buf;
|
|
1251
|
+
if (!upstream.ok) {
|
|
1252
|
+
buf = await (0, import_promises.readFile)(filePath);
|
|
1253
|
+
} else {
|
|
1254
|
+
const ab = await upstream.arrayBuffer();
|
|
1255
|
+
buf = Buffer.from(ab);
|
|
1187
1256
|
}
|
|
1188
|
-
const body = await (0, import_promises.readFile)(filePath);
|
|
1189
1257
|
const ext = path2.extname(filePath).toLowerCase();
|
|
1258
|
+
if (ext === ".html" && !looksLikeHtml(buf)) {
|
|
1259
|
+
console.error(
|
|
1260
|
+
"[@electron-memory/monitor] emm-dashboard: not HTML at",
|
|
1261
|
+
filePath,
|
|
1262
|
+
"url=",
|
|
1263
|
+
request.url
|
|
1264
|
+
);
|
|
1265
|
+
return new Response("Invalid dashboard HTML", { status: 500, headers: corsHeaders() });
|
|
1266
|
+
}
|
|
1190
1267
|
const mime = MIME_BY_EXT[ext] || "application/octet-stream";
|
|
1191
|
-
return new Response(
|
|
1268
|
+
return new Response(bufferToResponseBody(buf), {
|
|
1192
1269
|
status: 200,
|
|
1193
1270
|
headers: {
|
|
1194
1271
|
"Content-Type": mime,
|
|
1195
|
-
"Cache-Control": "no-store"
|
|
1272
|
+
"Cache-Control": "no-store",
|
|
1273
|
+
...corsHeaders()
|
|
1196
1274
|
}
|
|
1197
1275
|
});
|
|
1198
1276
|
} catch (err) {
|
|
1199
1277
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1200
|
-
|
|
1278
|
+
console.error("[@electron-memory/monitor] emm-dashboard:", request.url, err);
|
|
1279
|
+
return new Response(msg, { status: 404, headers: corsHeaders() });
|
|
1201
1280
|
}
|
|
1202
1281
|
});
|
|
1203
1282
|
}
|