@decentnetwork/beagle 0.1.38 → 0.1.39
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/desktop/app.js +1 -1
- package/dist/server.js +42 -11
- package/package.json +1 -1
package/dist/desktop/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
window.__DK_UI_VERSION="0.1.
|
|
1
|
+
window.__DK_UI_VERSION="0.1.38";
|
|
2
2
|
const ICON_PATHS = {
|
|
3
3
|
// ---- tab bar (the four must feel like one set) ----
|
|
4
4
|
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
package/dist/server.js
CHANGED
|
@@ -305,6 +305,39 @@ async function agentnetRestart() {
|
|
|
305
305
|
}
|
|
306
306
|
return { ok: false, error: lastErr };
|
|
307
307
|
}
|
|
308
|
+
// ── saved-files locations ───────────────────────────────────────────────────
|
|
309
|
+
// The daemon (often running as root) owns downloads/ — on such machines this
|
|
310
|
+
// user-level process gets EACCES writing there. Files WE save (sent copies,
|
|
311
|
+
// browser-WebRTC receives) then go to a sibling `sent/` dir we own; readers
|
|
312
|
+
// (open / download / reveal) look in downloads first, then sent.
|
|
313
|
+
function sentDirFor(downloadsDir) {
|
|
314
|
+
return join(dirname(downloadsDir), "sent");
|
|
315
|
+
}
|
|
316
|
+
async function pickWritableSaveDir(downloadsDir) {
|
|
317
|
+
const fsp = await import("node:fs/promises");
|
|
318
|
+
try {
|
|
319
|
+
await fsp.mkdir(downloadsDir, { recursive: true });
|
|
320
|
+
await fsp.access(downloadsDir, (await import("node:fs")).constants.W_OK);
|
|
321
|
+
return downloadsDir;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
const alt = sentDirFor(downloadsDir);
|
|
325
|
+
await fsp.mkdir(alt, { recursive: true });
|
|
326
|
+
return alt;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/** Resolve a saved file by name across downloads/ and sent/, with a
|
|
330
|
+
* path-traversal guard. Returns null when absent from both. */
|
|
331
|
+
function findSavedFile(downloadsDir, name) {
|
|
332
|
+
if (!name)
|
|
333
|
+
return null;
|
|
334
|
+
for (const dir of [downloadsDir, sentDirFor(downloadsDir)]) {
|
|
335
|
+
const f = join(dir, name);
|
|
336
|
+
if (f.startsWith(dir) && existsSync(f))
|
|
337
|
+
return f;
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
308
341
|
/** True only when the request came from the local machine. Used to gate the
|
|
309
342
|
* "Sign in with Decent" routes so binding the UI to a LAN IP can't expose
|
|
310
343
|
* identity signing to other hosts. The popup always runs in the local user's
|
|
@@ -977,8 +1010,7 @@ export function startBeagleServer(opts) {
|
|
|
977
1010
|
const keep = !!opts.downloadsDir;
|
|
978
1011
|
let destDir;
|
|
979
1012
|
if (keep) {
|
|
980
|
-
destDir = opts.downloadsDir;
|
|
981
|
-
await fsp.mkdir(destDir, { recursive: true });
|
|
1013
|
+
destDir = await pickWritableSaveDir(opts.downloadsDir);
|
|
982
1014
|
}
|
|
983
1015
|
else {
|
|
984
1016
|
destDir = await fsp.mkdtemp(join(os.tmpdir(), "agentnet-up-"));
|
|
@@ -1036,16 +1068,15 @@ export function startBeagleServer(opts) {
|
|
|
1036
1068
|
return;
|
|
1037
1069
|
}
|
|
1038
1070
|
const fs = await import("node:fs");
|
|
1039
|
-
const
|
|
1040
|
-
await fsp.mkdir(opts.downloadsDir, { recursive: true });
|
|
1071
|
+
const saveDir = await pickWritableSaveDir(opts.downloadsDir);
|
|
1041
1072
|
let finalName = safe;
|
|
1042
|
-
let finalPath = join(
|
|
1073
|
+
let finalPath = join(saveDir, finalName);
|
|
1043
1074
|
const dot = safe.lastIndexOf(".");
|
|
1044
1075
|
const base = dot > 0 ? safe.slice(0, dot) : safe;
|
|
1045
1076
|
const ext = dot > 0 ? safe.slice(dot) : "";
|
|
1046
1077
|
for (let i = 1; existsSync(finalPath); i++) {
|
|
1047
1078
|
finalName = `${base}-${i}${ext}`;
|
|
1048
|
-
finalPath = join(
|
|
1079
|
+
finalPath = join(saveDir, finalName);
|
|
1049
1080
|
}
|
|
1050
1081
|
try {
|
|
1051
1082
|
await new Promise((resolve2, reject) => {
|
|
@@ -1088,8 +1119,8 @@ export function startBeagleServer(opts) {
|
|
|
1088
1119
|
}
|
|
1089
1120
|
const body = await readBody(req);
|
|
1090
1121
|
const name = (typeof body.name === "string" ? body.name : "").replace(/[/\\]/g, "");
|
|
1091
|
-
const file =
|
|
1092
|
-
if (!
|
|
1122
|
+
const file = findSavedFile(opts.downloadsDir, name);
|
|
1123
|
+
if (!file) {
|
|
1093
1124
|
sendJson(res, 404, { ok: false, error: "not found" });
|
|
1094
1125
|
return;
|
|
1095
1126
|
}
|
|
@@ -1105,9 +1136,9 @@ export function startBeagleServer(opts) {
|
|
|
1105
1136
|
}
|
|
1106
1137
|
const q = new URL(req.url || "", "http://x").searchParams;
|
|
1107
1138
|
const name = (q.get("name") || "").replace(/[/\\]/g, "");
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
if (!
|
|
1139
|
+
// Looks in downloads/ then sent/ (traversal-guarded in the helper).
|
|
1140
|
+
const file = findSavedFile(opts.downloadsDir, name);
|
|
1141
|
+
if (!file) {
|
|
1111
1142
|
res.writeHead(404);
|
|
1112
1143
|
res.end("not found");
|
|
1113
1144
|
return;
|
package/package.json
CHANGED