Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion service/private/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@
const proc = `${des_db}.mfs_access_node`;
for (let r of toArray(recipients)) {
if (!oldItems[r.uid]) {
oldItems[r.uid] = await this.db.await_proc(proc, r.uid, nid);

Check warning on line 315 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEb6&open=AaD6jTIEP3PpqJdOHEb6&pullRequest=244
}
}
let nodes = {};
Expand All @@ -323,7 +323,7 @@
if (nodes[s.uid]) {
r = nodes[s.uid];
} else {
r = await this.yp.await_proc(access, s.uid, nid);

Check warning on line 326 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEb7&open=AaD6jTIEP3PpqJdOHEb7&pullRequest=244
}
if (!r || !r.actual_db) continue;
if (r.filetype == Attr.hub && r.actual_hub_id) {
Expand Down Expand Up @@ -409,9 +409,9 @@
case "show":
let nid = node.nid;
let access = `${node.des_db}.mfs_access_node`;
let sockets = await this.yp.await_proc("entity_sockets", {
db_name: node.des_db,
});

Check warning on line 414 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEb-&open=AaD6jTIEP3PpqJdOHEb-&pullRequest=244
nodes = {};
let counts = {};
for (let s of toArray(sockets)) {
Expand All @@ -420,7 +420,7 @@
if (nodes[s.uid]) {
r = nodes[s.uid];
} else {
r = await this.yp.await_proc(access, s.uid, nid);

Check warning on line 423 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEb_&open=AaD6jTIEP3PpqJdOHEb_&pullRequest=244
result.push(r);
}
if (!r || !r.actual_db) continue;
Expand All @@ -441,7 +441,7 @@
counts[s.uid] = c;
}
nodes[s.uid] = r;
await RedisStore.sendData(this.payload(r), s);

Check warning on line 444 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcB&open=AaD6jTIEP3PpqJdOHEcB&pullRequest=244
}
break;

Expand Down Expand Up @@ -469,7 +469,7 @@
dest.hub_id = destinationHubId;
if (!workspaceMoveHubNames.has(destinationHubId)) {
try {
const hub = toArray(await this.yp.await_proc('get_hub', destinationHubId))[0] || {};

Check warning on line 472 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcC&open=AaD6jTIEP3PpqJdOHEcC&pullRequest=244
let profile = {};
try {
profile = isString(hub.profile) ? JSON.parse(hub.profile) : (hub.profile || {});
Expand Down Expand Up @@ -653,13 +653,13 @@
continue;
}
granted.push(node);
let recipients = await this.yp.await_proc("entity_sockets", {
hub_id: n.hub_id
});

Check warning on line 658 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcE&open=AaD6jTIEP3PpqJdOHEcE&pullRequest=244
let proc = `${n.db_name}.mfs_access_node`;
for (let r of toArray(recipients)) {
if (this.heap.oldItems[r.uid]) continue;
node = await this.db.await_proc(proc, r.uid, n.id);

Check warning on line 662 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcF&open=AaD6jTIEP3PpqJdOHEcF&pullRequest=244
if (node && node.privilege) {
this.heap.oldItems[r.uid] = node;
}
Expand Down Expand Up @@ -852,16 +852,16 @@
const nid = node && (node.nid || node.id);
const hubId = node && (node.actual_hub_id || node.hub_id);
if (!nid || !hubId) continue;
const storage = firstRow(await this.yp.await_proc("file_move_entity_storage", hubId));

Check warning on line 855 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcH&open=AaD6jTIEP3PpqJdOHEcH&pullRequest=244
const dbName = storage && storage.db_name;
if (!dbName) continue;
try {
// Handles a file and a folder alike: given a file it returns at most
// that file's own thread, given a folder it returns every thread
// beneath it at any depth.
const threads = toArray(await this.yp.await_proc(
`${dbName}.channel_file_thread_list_in_subtree`, nid
));

Check warning on line 864 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcI&open=AaD6jTIEP3PpqJdOHEcI&pullRequest=244
for (const row of threads) {
if (!row || !row.file_nid || !row.file_thread_id) continue;
const key = String(row.file_nid);
Expand All @@ -877,7 +877,7 @@
// operation touching this thread mid-move. If it fails the thread is
// simply left alone — the file still moves, and a thread left marked
// active over a departed file is recoverable.
const reservation = await this._reserveDirectFileThreadTrash(target);

Check warning on line 880 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcJ&open=AaD6jTIEP3PpqJdOHEcJ&pullRequest=244
if (reservation && !reservation.failed
&& Number(reservation.reserved) === 1) {
target.reserved = true;
Expand Down Expand Up @@ -908,7 +908,7 @@
for (const [key, target] of snapshots) {
if (!target || !target.reserved) continue;
if (movedFileNids && movedFileNids.has(key)) continue;
await this._releaseDirectFileThreadTrash(target);

Check warning on line 911 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcK&open=AaD6jTIEP3PpqJdOHEcK&pullRequest=244
target.reserved = false;
}
}
Expand Down Expand Up @@ -1077,17 +1077,17 @@
for (const row of orphaned) {
// Each event goes to the workspace that still holds that thread, never
// to the one where the file was deleted.
const recipients = await this.yp.await_proc("entity_sockets", row.current_hub_id);

Check warning on line 1080 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcL&open=AaD6jTIEP3PpqJdOHEcL&pullRequest=244
await RedisStore.sendData(this.payload({
lineage_id: row.lineage_id,
access_revision: row.access_revision,
actor: this._fileMoveActor(),
reason: "orphaned",
state: "orphaned",
hub_id: row.current_hub_id,
file_nid: row.current_file_nid,
file_thread_id: row.current_thread_id,
}, { service: "channel.file_thread_access_changed" }), recipients);

Check warning on line 1090 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcM&open=AaD6jTIEP3PpqJdOHEcM&pullRequest=244
}
return orphaned;
} catch (error) {
Expand Down Expand Up @@ -1120,24 +1120,24 @@
// Every thread anywhere that is waiting on this file. A file discussed
// in more than one workspace has one sleeping thread per workspace, and
// a single move can touch several of them at once.
const waiting = toArray(await this.yp.await_proc(
"file_thread_lineage_resolve_holder", sourceHubId, pair.source_file_nid
)).filter((row) => row && row.lineage_id);

Check warning on line 1125 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcN&open=AaD6jTIEP3PpqJdOHEcN&pullRequest=244

for (const lineage of waiting) {
if (String(lineage.current_hub_id) === String(destinationHubId)) {
// This thread's own workspace is where the file is going: its wait
// is over.
await this._markFileThreadMovedBack(
lineage, pair.destination_file_nid, sourceDbName
);

Check warning on line 1133 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcO&open=AaD6jTIEP3PpqJdOHEcO&pullRequest=244
} else {
// Still away from home. Follow the file so the thread's info card
// keeps naming the workspace that actually holds it.
const tracked = firstRow(await this.yp.await_proc(
"file_thread_lineage_track_holder",
lineage.lineage_id, destinationHubId, pair.destination_file_nid
));

Check warning on line 1140 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcP&open=AaD6jTIEP3PpqJdOHEcP&pullRequest=244
if (!tracked || tracked.failed || Number(tracked.tracked) !== 1) {
this.warn("File thread holder tracking failed", {
lineage_id: lineage.lineage_id,
Expand Down Expand Up @@ -1268,15 +1268,15 @@
}

for (const source of values(sourceUpdates)) {
const recipients = await this.yp.await_proc("entity_sockets", source.hub_id);

Check warning on line 1271 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcR&open=AaD6jTIEP3PpqJdOHEcR&pullRequest=244
await RedisStore.sendData(
this.payload(source, { keys: [Attr.nid, Attr.hub_id], service: "media.remove" }),
recipients
);

Check warning on line 1275 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcS&open=AaD6jTIEP3PpqJdOHEcS&pullRequest=244
await RedisStore.sendData(
this.payload({}, { service: "notification.resync" }),
recipients
);

Check warning on line 1279 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcT&open=AaD6jTIEP3PpqJdOHEcT&pullRequest=244
}

// The thread half of the move. Threads are never carried across databases:
Expand All @@ -1299,12 +1299,12 @@
const storage = firstRow(await this.yp.await_proc(
"file_move_entity_storage", source.hub_id
));
await this._applyFileThreadMove(
source.hub_id,
storage && storage.db_name,
destinationHubId,
threadSnapshots
);

Check warning on line 1307 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcV&open=AaD6jTIEP3PpqJdOHEcV&pullRequest=244
}

// A selection can be partly refused (permissions, circular reference), so a
Expand Down Expand Up @@ -1512,13 +1512,13 @@
if (departed.length) {
const recipients = await this.yp.await_proc("entity_sockets", sourceHubId);
for (const node of departed) {
await RedisStore.sendData(
this.payload(
{ nid: node.nid, hub_id: sourceHubId },
{ keys: [Attr.nid, Attr.hub_id], service: "media.remove" }
),
recipients
);

Check warning on line 1521 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcW&open=AaD6jTIEP3PpqJdOHEcW&pullRequest=244
}
await RedisStore.sendData(
this.payload({}, { service: "notification.resync" }),
Expand Down Expand Up @@ -1895,7 +1895,7 @@
let node;
for (let uid of users) {
let user = await this.yp.await_proc("get_visitor", uid);
let privilege = await this.db.await_func(`user_permission`, uid, "*");

Check warning on line 1898 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEca&open=AaD6jTIEP3PpqJdOHEca&pullRequest=244
user.privilege = privilege;
if (!(privilege & Permission.OWNER) && privilege & Permission.READ) {
let profile = JSON.parse(user.profile);
Expand All @@ -1918,23 +1918,23 @@
stringify(md.fullname),
1
);
await this.db.await_proc(
"mfs_set_attr",
node.id,
"metadata",
md
);

Check warning on line 1926 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcc&open=AaD6jTIEP3PpqJdOHEcc&pullRequest=244
for (let id of users) {
if (id == user.id) {
await this.db.await_proc(
"permission_grant",
node.id,
user.id,
0,
Privilege.WRITE,
"system",
`Writable by ${profile.email}`
);

Check warning on line 1937 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcd&open=AaD6jTIEP3PpqJdOHEcd&pullRequest=244
} else {
await this.db.await_proc(
"permission_grant",
Expand Down Expand Up @@ -2059,7 +2059,7 @@
let denied = [];
for (let node of src) {
var proc = `${node.db_name}.mfs_access_node`;
source_node = await this.yp.await_proc(proc, uid, node.id);

Check warning on line 2062 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcf&open=AaD6jTIEP3PpqJdOHEcf&pullRequest=244
if (source_node.permission & node.privilege) {
this.heap.srcgrantlst.push({
nid: source_node.nid,
Expand Down Expand Up @@ -2177,7 +2177,7 @@
mfs_root: row.des_mfs_root,
};
proc = `${row.dest_db_name}.mfs_access_node`;
r = await this.yp.await_proc(proc, this.uid, dest.nid);

Check warning on line 2180 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcg&open=AaD6jTIEP3PpqJdOHEcg&pullRequest=244
r.privilege = r.permission;
show_node.push(r);
copy_node(src, dest, 1);
Expand All @@ -2185,9 +2185,9 @@
case "show":
case "showone":
if (!row.dest_db_name) {
let entity = await this.yp.await_proc('get_entity', this.input.get('recipient_id'))

Check warning on line 2188 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEch&open=AaD6jTIEP3PpqJdOHEch&pullRequest=244
proc = `${entity.db_name}.mfs_access_node`;
r = await this.db.await_proc(proc, this.uid, row.nid);

Check warning on line 2190 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEci&open=AaD6jTIEP3PpqJdOHEci&pullRequest=244
r.privilege = r.permission;
if (r.filetype == Attr.hub) {
r.hub_id = row.nid; // hub_id is inconsistent after trash
Expand All @@ -2196,7 +2196,7 @@
continue;
}
proc = `${row.dest_db_name}.mfs_access_node`;
r = await this.yp.await_proc(proc, this.uid, row.nid);

Check warning on line 2199 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcj&open=AaD6jTIEP3PpqJdOHEcj&pullRequest=244
r.privilege = r.permission;

if (r.filetype == Attr.hub) {
Expand Down Expand Up @@ -2231,20 +2231,20 @@
let sockets = [];

for (var m of show_node) {
const restoredThread = await this._directFileThreadSnapshot(
m.hub_id,
m.nid || m.id,
m.db_name || m.actual_db
);

Check warning on line 2238 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcm&open=AaD6jTIEP3PpqJdOHEcm&pullRequest=244
await this._transitionDirectFileThreadAccess(restoredThread, "active", "direct_restore");

Check warning on line 2239 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcn&open=AaD6jTIEP3PpqJdOHEcn&pullRequest=244
let changelog = await this.changelog_write({ src: m, event: "media.new" });

Check warning on line 2240 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEco&open=AaD6jTIEP3PpqJdOHEco&pullRequest=244
let dest = await this.yp.await_proc("entity_sockets", m.hub_id);

Check warning on line 2241 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcp&open=AaD6jTIEP3PpqJdOHEcp&pullRequest=244
sockets = sockets.concat(dest);
m.args = { ...m.args, changelog };
await RedisStore.sendData(
this.payload(m, { service: "media.restore_into" }),
dest
);

Check warning on line 2247 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcq&open=AaD6jTIEP3PpqJdOHEcq&pullRequest=244
}
await RedisStore.sendData(
this.payload({ rebuild: 1 }, { service: "notification.resync" }),
Expand Down Expand Up @@ -2349,12 +2349,12 @@
for (var node of data) {
db_name = node.db_name;
if (!db_name || db_name == null) {
files = await this.db.await_proc("mfs_purge", node.id);

Check warning on line 2352 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcs&open=AaD6jTIEP3PpqJdOHEcs&pullRequest=244
continue;
}
switch (node.category) {
case Attr.folder:
files = await this.yp.await_proc(db_name + ".mfs_purge", node.id);

Check warning on line 2357 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEct&open=AaD6jTIEP3PpqJdOHEct&pullRequest=244
files = toArray(files);
for (let f of files) {
res.push(f);
Expand All @@ -2372,7 +2372,7 @@
}
}
for (var entity of entities) {
await this.yp.await_proc("entity_delete", entity);

Check warning on line 2375 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcv&open=AaD6jTIEP3PpqJdOHEcv&pullRequest=244
}
return data.concat(res);
}
Expand All @@ -2397,7 +2397,7 @@
if (!nid) continue;
if (entry.category === FOLDER || entry.category === HUB) continue;
try {
await this._markFileThreadOrphaned(entry.hub_id || hubId, nid);

Check warning on line 2400 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcw&open=AaD6jTIEP3PpqJdOHEcw&pullRequest=244
} catch (error) {
this.warn("Unable to orphan thread for purged file", { nid, error });
}
Expand Down Expand Up @@ -2644,11 +2644,11 @@
const directAccessKeys = new Set();
for (const g of granted) {
try {
const hub_db = await this.yp.await_func('get_db_name', g.hub_id);

Check warning on line 2647 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcx&open=AaD6jTIEP3PpqJdOHEcx&pullRequest=244
if (!hub_db) continue;
const attr = await this.yp.await_proc(`${hub_db}.mfs_node_attr`, g.nid);

Check warning on line 2649 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcy&open=AaD6jTIEP3PpqJdOHEcy&pullRequest=244
if (!attr) continue;
const directTarget = await this._directFileThreadSnapshot(g.hub_id, g.nid, hub_db);

Check warning on line 2651 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEcz&open=AaD6jTIEP3PpqJdOHEcz&pullRequest=244
const directKey = directTarget && `${directTarget.hub_id}:${directTarget.file_nid}`;
if (directTarget && !directAccessKeys.has(directKey)) {
directAccessKeys.add(directKey);
Expand Down Expand Up @@ -2699,11 +2699,11 @@
let changelog = await this.changelog_write({ src: data, event: service });
if (isEmpty(data)) {
for (let h of granted) {
recipients = await this.yp.await_proc("entity_sockets", h.hub_id);

Check warning on line 2702 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc2&open=AaD6jTIEP3PpqJdOHEc2&pullRequest=244
await RedisStore.sendData(
this.payload({ ...h, changelog }, { keys, service }),
recipients
);

Check warning on line 2706 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc3&open=AaD6jTIEP3PpqJdOHEc3&pullRequest=244
await RedisStore.sendData(
this.payload({}, { service: "notification.resync" }),
recipients
Expand All @@ -2725,14 +2725,14 @@
this.output.add_data({ changelog });

for (const t of auditTargets) {
await writeAudit(this, {
db: t.hub_db,
uid: this.uid,
action: 'deleted',
category: 'media',
entity_id: t.nid,
log: `${t.filetype === 'hub' ? 'Workspace' : t.filetype === 'folder' ? 'Folder' : 'File'} '${t.filename}' moved to trash`,
});

Check warning on line 2735 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc6&open=AaD6jTIEP3PpqJdOHEc6&pullRequest=244
}

await this._evaluateOverLimitAfterResolve();
Expand Down Expand Up @@ -2865,7 +2865,9 @@
let newItems = {};
let recipients;

/** Renaming hubname must not change other's name*/
/** A hub row lives on the caller's desk: start with the caller's own
* sockets. A hub admin's rename widens this to every member in
* renameHubForMembers; anyone else renames only their own label. */
if (node[FILETYPE] == Attr.hub) {
recipients = await this.yp.await_proc(
"entity_sockets",
Expand Down Expand Up @@ -2893,11 +2895,11 @@
default:
for (let r of toArray(recipients)) {
if (!oldItems[r.uid]) {
oldItems[r.uid] = await this.db.await_proc(
"mfs_access_node",
r.uid,
nid
);

Check warning on line 2902 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc7&open=AaD6jTIEP3PpqJdOHEc7&pullRequest=244
}
}
res = await this.db.await_proc("mfs_rename", nid, filename);
Expand All @@ -2917,12 +2919,22 @@
await this.changelog_write({ src: old, dest: attr });
}
}
if (node[FILETYPE] == Attr.hub) {
// The caller's own rename has already landed; a failure here must not
// turn it into an error, so it degrades to the personal rename.
try {
const members = await this.renameHubForMembers(nid, filename, oldItems, newItems);
if (members) recipients = members;
} catch (e) {
this.warn("media.rename: could not rename the workspace for its members", e);
}
}
for (let r of toArray(recipients)) {
let dest;
if (newItems[r.uid] && newItems[r.uid].filename) {
dest = newItems[r.uid]
} else {
dest = await this.db.await_proc("mfs_access_node", r.uid, nid);

Check warning on line 2937 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc8&open=AaD6jTIEP3PpqJdOHEc8&pullRequest=244
newItems[r.uid] = dest;
}
let model = {
Expand All @@ -2934,7 +2946,7 @@
changelog: this.__changelog
}
};
await RedisStore.sendData(this.payload(model), r);

Check warning on line 2949 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc9&open=AaD6jTIEP3PpqJdOHEc9&pullRequest=244
}
let model;
if (newItems[this.uid]) {
Expand All @@ -2961,6 +2973,52 @@
this.output.data(model);
}

/**
* A hub admin's rename reaches EVERY member of the workspace.
*
* Each member's desk holds its own row for the workspace and the desk lists
* that row's name, so renaming only the caller's row (above) left everyone
* else on the old name. yp.hub_rename_for_members writes yp.hub.name and
* every member's row; each member's node is then read back from THEIR own
* db, because this.db is the caller's desk and answers the caller's name.
*
* Anyone below admin keeps the personal rename: only their own label moves.
*
* @returns the hub's member sockets to push to, or null for a personal rename
*/
async renameHubForMembers(hub_id, filename, oldItems, newItems) {
const hub = firstRow(
await this.yp.await_query("SELECT db_name FROM entity WHERE id=?", hub_id)
);
if (!hub || !hub.db_name) return null;

Check warning on line 2993 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEc-&open=AaD6jTIEP3PpqJdOHEc-&pullRequest=244
const hubDb = hub.db_name;
const privilege = await this.yp.await_func(
`${hubDb}.user_permission`, this.uid, "*"
);
if (!(Number(privilege) & Permission.ADMIN)) return null;

const members = toArray(
await this.yp.await_proc("hub_rename_for_members", hub_id, filename)
);
for (const m of members) {
if (!m || !m.uid || !m.db_name) continue;
// The caller's row was renamed and read back by the main path.
if (m.uid == this.uid && newItems[m.uid]) continue;
const dest = await this.yp.await_proc(
`${m.db_name}.mfs_access_node`, m.uid, hub_id
);

Check warning on line 3009 in service/private/media.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected `await` inside a loop.

See more on https://sonarcloud.io/project/issues?id=drumee_server-team&issues=AaD6jTIEP3PpqJdOHEdA&open=AaD6jTIEP3PpqJdOHEdA&pullRequest=244
if (!dest || !dest.filename) continue;
dest.hub_id = dest.actual_hub_id;
dest.privilege = dest.permission;
dest.home_id = dest.actual_home_id;
newItems[m.uid] = dest;
if (!oldItems[m.uid]) {
oldItems[m.uid] = { ...dest, filename: m.old_filename, fname: m.old_filename };
}
}
return this.yp.await_proc("entity_sockets", hub_id);
}

/**
* Not used
*/
Expand Down
157 changes: 157 additions & 0 deletions test/hub-rename-members.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// media.rename on a WORKSPACE reaches every member when a hub admin does it.
//
// Each member's desk holds its own row for a workspace (media, category hub)
// and the desk lists that row's name. rename() only ever ran mfs_rename on the
// CALLER's desk and pushed to the CALLER's sockets, so every other member kept
// the old name — and the dest it would have pushed them was read from the
// caller's db anyway.
//
// A hub admin's rename now also calls yp.hub_rename_for_members, reads each
// member's node back from THEIR db, and pushes to the hub's sockets. Anyone
// below admin keeps the personal rename.
//
// Run: node --test test/hub-rename-members.test.js
const assert = require("node:assert/strict");
const test = require("node:test");

global.myDrumee = { arch: "pod", useEmail: 0 };
global.verbosity = 0;
global.debug = {};

const { RedisStore, Attr } = require("@drumee/server-essentials");
const MediaPrivate = require("../service/private/media");

const sent = [];
RedisStore.sendData = async (payload, dest) => {
sent.push({ payload, dest });
};

const HUB_ID = "hub-1";
const CALLER = { uid: "u1", db: "db_u1" };
const MEMBER = { uid: "u2", db: "db_u2" };

function node(uid, filename) {
return {
nid: HUB_ID,
id: HUB_ID,
filename,
filetype: Attr.hub,
actual_hub_id: HUB_ID,
actual_home_id: "home-1",
permission: 63,
owner: uid,
};
}

function fakeService({ privilege }) {
const calls = [];
// Per-desk names, as the two databases would hold them.
const names = { [CALLER.uid]: "Old", [MEMBER.uid]: "Old" };
const svc = Object.create(MediaPrivate.prototype);
Object.assign(svc, {
calls,
uid: CALLER.uid,
__changelog: {},
randomString: () => "tag",
source_granted: () => ({ node: { ...node(CALLER.uid, "Old"), hub_id: CALLER.uid } }),
input: { need: () => "New Name", get: () => undefined, use: () => undefined },
exception: { user: (code) => calls.push(["exception", code]) },
hub: { get: (k) => (k === Attr.id ? CALLER.uid : k === Attr.db_name ? CALLER.db : undefined) },
changelog_write: async () => {},
payload: (model) => ({ model }),
output: { data: (d) => (svc.result = d) },
warn: () => {},
db: {
// The CALLER's desk: whatever uid it is asked about, it answers its own row.
await_proc: async (name, ...args) => {
calls.push(["db", name, ...args]);
if (name === "mfs_rename") names[CALLER.uid] = args[1];
if (name === "mfs_access_node") return node(args[0], names[CALLER.uid]);
return {};
},
},
yp: {
await_query: async (sql, id) => {
calls.push(["yp.query", id]);
return [{ db_name: "hub_db" }];
},
await_func: async (name, ...args) => {
calls.push(["yp.func", name, ...args]);
return privilege;
},
await_proc: async (name, ...args) => {
calls.push(["yp", name, ...args]);
if (name === "entity_sockets") {
return args[0] === HUB_ID
? [{ socket_id: "s1", uid: CALLER.uid }, { socket_id: "s2", uid: MEMBER.uid }]
: [{ socket_id: "s1", uid: CALLER.uid }];
}
if (name === "hub_rename_for_members") {
names[MEMBER.uid] = args[1];
return [
{ uid: CALLER.uid, db_name: CALLER.db, old_filename: "New Name", filename: "New Name", changed: 0 },
{ uid: MEMBER.uid, db_name: MEMBER.db, old_filename: "Old", filename: "New Name", changed: 1 },
];
}
if (name === `${MEMBER.db}.mfs_access_node`) return node(args[0], names[MEMBER.uid]);
return {};
},
},
});
return svc;
}

test("hub admin: every member's row is renamed and every member is told", async () => {
sent.length = 0;
const svc = fakeService({ privilege: 63 });
await svc.rename();

assert.ok(svc.calls.some((c) => c[0] === "yp.func" && c[1] === "hub_db.user_permission" && c[2] === CALLER.uid));
assert.ok(svc.calls.some((c) => c[1] === "hub_rename_for_members" && c[2] === HUB_ID && c[3] === "New Name"));

const toMember = sent.find((s) => s.dest.socket_id === "s2");
assert.ok(toMember, "the other member gets a push");
assert.equal(toMember.payload.model.args.dest.filename, "New Name");
assert.equal(toMember.payload.model.args.src.filename, "Old");
assert.equal(toMember.payload.model.filetype, Attr.hub);
// Read from the MEMBER's db, never the caller's.
assert.ok(svc.calls.some((c) => c[1] === `${MEMBER.db}.mfs_access_node` && c[2] === MEMBER.uid));

const toCaller = sent.find((s) => s.dest.socket_id === "s1");
assert.equal(toCaller.payload.model.args.dest.filename, "New Name");
assert.equal(svc.result.args.dest.filename, "New Name");
});

test("member below admin: personal rename only, nobody else touched", async () => {
sent.length = 0;
const svc = fakeService({ privilege: 3 });
await svc.rename();

assert.ok(!svc.calls.some((c) => c[1] === "hub_rename_for_members"));
assert.deepEqual(sent.map((s) => s.dest.socket_id), ["s1"]);
assert.equal(svc.result.args.dest.filename, "New Name");
});

test("not a hub: the shared path never runs", async () => {
sent.length = 0;
const svc = fakeService({ privilege: 63 });
const granted = svc.source_granted;
svc.source_granted = () => ({ node: { ...granted().node, filetype: Attr.folder } });
await svc.rename();

assert.ok(!svc.calls.some((c) => c[0] === "yp.func" || c[1] === "hub_rename_for_members"));
});

test("shared path throws: the caller's rename still answers, personal push only", async () => {
sent.length = 0;
const svc = fakeService({ privilege: 63 });
const proc = svc.yp.await_proc;
svc.yp.await_proc = async (name, ...args) => {
if (name === "hub_rename_for_members") throw new Error("boom");
return proc(name, ...args);
};
await svc.rename();

assert.equal(svc.result.args.dest.filename, "New Name");
assert.deepEqual(sent.map((s) => s.dest.socket_id), ["s1"]);
});
Loading