Skip to content

Commit b8ec080

Browse files
committed
Release 0.101.56
2 parents 5525e51 + ee7967c commit b8ec080

39 files changed

+3148
-643
lines changed

.github/workflows/build-release.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ jobs:
1515

1616
- uses: actions/setup-node@v4
1717
with:
18-
node-version: "20.19.1"
18+
node-version: "20.19.5"
1919

2020
- run: touch env-config.js
2121

22+
- run: sudo npm install -g npm@11.6.1
23+
2224
- name: Install
2325
run: npm install
2426

.github/workflows/frontend-linting.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ jobs:
1313

1414
- uses: actions/setup-node@v4
1515
with:
16-
node-version: "20.19.1"
16+
node-version: "20.19.5"
17+
18+
- run: sudo npm install -g npm@11.6.1
19+
1720
- run: npm install
1821

1922
- name: Run Prettier formatting

package-lock.json

Lines changed: 383 additions & 364 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "web",
3-
"version": "0.101.55",
3+
"version": "0.101.56",
44
"private": true,
55
"productName": "Tactical RMM",
66
"scripts": {
@@ -17,15 +17,15 @@
1717
"@xterm/addon-fit": "0.10.0",
1818
"@xterm/xterm": "5.5.0",
1919
"apexcharts": "3.54.1",
20-
"axios": "1.8.4",
21-
"dompurify": "3.2.5",
20+
"axios": "1.12.2",
21+
"dompurify": "3.2.6",
2222
"dotenv": "16.4.5",
2323
"monaco-editor": "0.50.0",
2424
"pinia": "2.2.6",
2525
"qrcode": "1.5.4",
26-
"quasar": "2.18.1",
27-
"vue": "3.5.13",
28-
"vue-router": "4.4.5",
26+
"quasar": "2.18.5",
27+
"vue": "3.5.22",
28+
"vue-router": "4.5.1",
2929
"vue3-apexcharts": "1.7.0",
3030
"vuedraggable": "4.1.0",
3131
"vuex": "4.1.0",
@@ -43,6 +43,6 @@
4343
"eslint-config-prettier": "9.1.0",
4444
"eslint-plugin-vue": "8.7.1",
4545
"prettier": "3.3.3",
46-
"typescript": "5.6.2"
46+
"typescript": "5.9.2"
4747
}
4848
}

src/api/software.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ export async function installAgentSoftware(agent_id, payload) {
2727
return data;
2828
}
2929

30+
export async function uninstallAgentSoftware(agent_id, payload) {
31+
const { data } = await axios.post(
32+
`${baseUrl}/${agent_id}/uninstall/`,
33+
payload,
34+
);
35+
return data;
36+
}
37+
3038
export async function refreshAgentSoftware(agent_id) {
3139
try {
3240
const { data } = await axios.put(`${baseUrl}/${agent_id}/`);

src/components/accounts/RolesForm.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,14 @@
187187
v-model="localRole.can_manage_customfields"
188188
label="Edit Custom Fields"
189189
/>
190+
<q-checkbox
191+
v-model="localRole.can_view_schedules"
192+
label="List Schedules"
193+
/>
194+
<q-checkbox
195+
v-model="localRole.can_manage_schedules"
196+
label="Manage Schedules"
197+
/>
190198
<q-checkbox
191199
v-if="!hosted"
192200
v-model="localRole.can_use_webterm"
@@ -492,6 +500,8 @@ export default {
492500
can_run_urlactions: false,
493501
can_view_customfields: false,
494502
can_manage_customfields: false,
503+
can_view_schedules: false,
504+
can_manage_schedules: false,
495505
// api key perms
496506
can_list_api_keys: false,
497507
can_manage_api_keys: false,

src/components/agents/AgentActionMenu.vue

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<q-item-section>VNC</q-item-section>
4343
</q-item>
4444

45-
<q-item clickable v-ripple @click="getURLActions">
45+
<q-item clickable v-ripple :disable="urlActions.length === 0">
4646
<q-item-section side>
4747
<q-icon size="xs" name="open_in_new" />
4848
</q-item-section>
@@ -62,7 +62,7 @@
6262
runURLAction({ agent_id: agent.agent_id, action: action.id })
6363
"
6464
>
65-
{{ action.name }}
65+
<q-item-section>{{ action.name }}</q-item-section>
6666
</q-item>
6767
</q-list>
6868
</q-menu>
@@ -82,7 +82,7 @@
8282
<q-item-section>Run Script</q-item-section>
8383
</q-item>
8484

85-
<q-item clickable v-ripple @click="getFavoriteScripts">
85+
<q-item clickable v-ripple :disable="favoriteScripts.length === 0">
8686
<q-item-section side>
8787
<q-icon size="xs" name="star" />
8888
</q-item-section>
@@ -100,7 +100,7 @@
100100
v-close-popup
101101
@click="showRunScript(agent, script.value)"
102102
>
103-
{{ script.label }}
103+
<q-item-section>{{ script.label }}</q-item-section>
104104
</q-item>
105105
</q-list>
106106
</q-menu>
@@ -245,7 +245,7 @@
245245

246246
<script>
247247
// composition imports
248-
import { ref, inject } from "vue";
248+
import { ref, inject, onMounted } from "vue";
249249
import { useStore } from "vuex";
250250
import { useQuasar } from "quasar";
251251
import { fetchURLActions, runURLAction } from "@/api/core";
@@ -263,7 +263,7 @@ import {
263263
import { runAgentUpdateScan, runAgentUpdateInstall } from "@/api/winupdates";
264264
import { runAgentChecks } from "@/api/checks";
265265
import { fetchScripts } from "@/api/scripts";
266-
import { notifySuccess, notifyWarning, notifyError } from "@/utils/notify";
266+
import { notifySuccess, notifyError } from "@/utils/notify";
267267
268268
// ui imports
269269
import PendingActions from "@/components/logs/PendingActions.vue";
@@ -296,7 +296,6 @@ export default {
296296
297297
const urlActions = ref([]);
298298
const favoriteScripts = ref([]);
299-
const menuLoading = ref(false);
300299
301300
function showEditAgent(agent_id) {
302301
$q.dialog({
@@ -317,22 +316,12 @@ export default {
317316
}
318317
319318
async function getURLActions() {
320-
menuLoading.value = true;
321319
try {
322320
urlActions.value = (await fetchURLActions())
323321
.filter((action) => action.action_type === "web")
324322
.sort((a, b) => a.name.localeCompare(b.name));
325-
326-
if (urlActions.value.length === 0) {
327-
notifyWarning(
328-
"No URL Actions configured. Go to Settings > Global Settings > URL Actions",
329-
);
330-
return;
331-
}
332323
} catch (e) {
333324
console.error(e);
334-
} finally {
335-
menuLoading.value = false;
336325
}
337326
}
338327
@@ -358,19 +347,13 @@ export default {
358347
async function getFavoriteScripts() {
359348
favoriteScripts.value = [];
360349
361-
menuLoading.value = true;
362350
try {
363351
const data = await fetchScripts({
364352
showCommunityScripts: store.state.showCommunityScripts,
365353
});
366354
367355
const scripts = data.filter((script) => !!script.favorite);
368356
369-
if (scripts.length === 0) {
370-
notifyWarning("You don't have any scripts favorited!");
371-
return;
372-
}
373-
374357
favoriteScripts.value = scripts
375358
.map((script) => ({
376359
label: script.name,
@@ -490,7 +473,7 @@ export default {
490473
prompt: {
491474
model: "",
492475
type: "text",
493-
isValid: (val) => val === "yes",
476+
isValid: (val) => val.toLowerCase() === "yes",
494477
},
495478
cancel: true,
496479
ok: { label: "Shutdown", color: "negative" },
@@ -565,7 +548,7 @@ export default {
565548
prompt: {
566549
model: "",
567550
type: "text",
568-
isValid: (val) => val === "yes",
551+
isValid: (val) => val.toLowerCase() === "yes",
569552
},
570553
cancel: true,
571554
ok: { label: "Uninstall", color: "negative" },
@@ -585,6 +568,11 @@ export default {
585568
});
586569
}
587570
571+
onMounted(async () => {
572+
await getURLActions();
573+
await getFavoriteScripts();
574+
});
575+
588576
return {
589577
// reactive data
590578
urlActions,

src/components/agents/SoftwareTab.vue

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@
6161
</q-input>
6262
<export-table-btn :data="software" :columns="columns" />
6363
</template>
64+
65+
<template v-slot:body-cell-uninstall="props">
66+
<td>
67+
<q-btn
68+
v-if="props.row.uninstall"
69+
label="Uninstall"
70+
color="primary"
71+
dense
72+
size="sm"
73+
@click="openUninstallSoftware(props.row)"
74+
/>
75+
</td>
76+
</template>
6477
</q-table>
6578
</div>
6679
</template>
@@ -70,11 +83,17 @@
7083
import { ref, computed, watch, onMounted } from "vue";
7184
import { useQuasar } from "quasar";
7285
import { useStore } from "vuex";
73-
import { fetchAgentSoftware, refreshAgentSoftware } from "@/api/software";
86+
import {
87+
fetchAgentSoftware,
88+
refreshAgentSoftware,
89+
uninstallAgentSoftware,
90+
} from "@/api/software";
7491
7592
// ui imports
7693
import InstallSoftware from "@/components/software/InstallSoftware.vue";
94+
import UninstallSoftware from "@/components/software/UninstallSoftware.vue";
7795
import ExportTableBtn from "@/components/ui/ExportTableBtn.vue";
96+
import { notifySuccess } from "@/utils/notify";
7897
7998
// static data
8099
const columns = [
@@ -116,6 +135,13 @@ const columns = [
116135
field: "version",
117136
sortable: false,
118137
},
138+
{
139+
name: "uninstall",
140+
align: "left",
141+
label: "",
142+
field: "uninstall",
143+
sortable: false,
144+
},
119145
];
120146
121147
export default {
@@ -145,7 +171,7 @@ export default {
145171
146172
async function getSoftware() {
147173
loading.value = true;
148-
software.value = await fetchAgentSoftware(selectedAgent.value);
174+
software.value = (await fetchAgentSoftware(selectedAgent.value)) || [];
149175
loading.value = false;
150176
}
151177
@@ -165,6 +191,36 @@ export default {
165191
});
166192
}
167193
194+
function openUninstallSoftware(software) {
195+
$q.dialog({
196+
component: UninstallSoftware,
197+
198+
componentProps: {
199+
softwareName: software.name,
200+
initialUninstallString:
201+
software.uninstall +
202+
(software.uninstall.toLowerCase().includes("msiexec")
203+
? " /qn /norestart"
204+
: ""),
205+
},
206+
}).onOk(async (data) => {
207+
try {
208+
loading.value = true;
209+
const ret = await uninstallAgentSoftware(selectedAgent.value, {
210+
name: software.name,
211+
command: data.uninstallString, // use user supplied value, not the one from db. to prevent db injection attack
212+
run_as_user: data.run_as_user,
213+
timeout: data.timeout,
214+
});
215+
notifySuccess(ret);
216+
} catch (e) {
217+
console.error(e);
218+
} finally {
219+
loading.value = false;
220+
}
221+
});
222+
}
223+
168224
watch(selectedAgent, (newValue) => {
169225
if (newValue) {
170226
getSoftware();
@@ -191,6 +247,7 @@ export default {
191247
// methods
192248
refreshSoftware,
193249
showInstallSoftwareModal,
250+
openUninstallSoftware,
194251
};
195252
},
196253
};

src/components/logs/AuditManager.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ const objectOptions = [
266266
{ value: "urlaction", label: "URL Action" },
267267
{ value: "keystore", label: "Global Key Store" },
268268
{ value: "customfield", label: "Custom Field" },
269+
{ value: "schedule", label: "Schedule" },
270+
{ value: "reportschedule", label: "Report Schedule" },
269271
];
270272
271273
const timeOptions = [
@@ -414,7 +416,7 @@ export default {
414416
agentFilter.value = [props.agent];
415417
search();
416418
}
417-
}
419+
},
418420
);
419421
}
420422
@@ -460,7 +462,7 @@ export default {
460462
tableNoDataText: computed(() =>
461463
searched.value
462464
? "No data found. Try to refine you search"
463-
: "Click search to find audit logs"
465+
: "Click search to find audit logs",
464466
),
465467
466468
// methods

src/components/modals/agents/EditAgent.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
<div class="col-2"></div>
6666
<tactical-dropdown
6767
filterable
68-
clearable
6968
outlined
7069
dense
7170
options-dense
@@ -462,7 +461,11 @@ export default {
462461
r.data.forEach((client) => {
463462
this.siteOptions.push({ category: client.name });
464463
client.sites.forEach((site) =>
465-
this.siteOptions.push({ label: site.name, value: site.id }),
464+
this.siteOptions.push({
465+
label: site.name,
466+
value: site.id,
467+
cat: client.name,
468+
}),
466469
);
467470
});
468471
});

0 commit comments

Comments
 (0)