Compare commits

..

70 Commits

Author SHA1 Message Date
wh1te909
e837c494cb Release 0.101.43 2024-03-25 17:38:24 +00:00
wh1te909
afc40fcbe3 bump version 2024-03-25 17:38:12 +00:00
wh1te909
185f50787b wording 2024-03-23 19:29:12 +00:00
Dan
6c33676f73 Merge pull request #26 from dinger1986/dinger1986-add-confirm-and-info-for-enable-sync
Dinger1986 add confirm and info for enable sync
2024-03-23 12:12:44 -07:00
Dan
0290002444 update wording 2024-03-23 12:11:44 -07:00
wh1te909
fc5195e817 update reqs 2024-03-21 18:19:11 +00:00
Dan
efd5c3dca1 Merge pull request #25 from dinger1986/dinger1986-add-coname-to-initialsetup
Update InitialSetup.vue
2024-03-20 17:08:03 -07:00
Dan
2f438feec2 not needed 2024-03-20 17:07:17 -07:00
Dan
07ae9dfddf make style consistent with the other q-inputs 2024-03-20 17:04:34 -07:00
dinger1986
64575c5f7d Update EditCoreSettings.vue 2024-03-20 21:40:50 +00:00
dinger1986
e0fa339644 Update EditCoreSettings.vue 2024-03-20 21:32:18 +00:00
dinger1986
b72a86e514 Update InitialSetup.vue 2024-03-20 21:21:36 +00:00
wh1te909
62f0414afa fix date filter fixes amidaware/tacticalrmm#1803 2024-03-20 05:35:16 +00:00
wh1te909
200a02b87b update ci and bump version 2024-03-15 19:57:05 +00:00
wh1te909
da5dbeaf0f update reqs 2024-03-15 19:49:24 +00:00
wh1te909
4b6d099f72 add icon for tooltip and don't show in hosted 2024-03-13 01:30:37 +00:00
wh1te909
842661ada6 update browser/node 2024-03-13 01:29:22 +00:00
wh1te909
f5148c87c8 no longer need disable auto login 2024-03-12 05:27:18 +00:00
wh1te909
16164c0bbc update apexcharts 2024-03-10 22:37:17 +00:00
wh1te909
f38ddb840b update reqs 2024-03-08 18:42:40 +00:00
wh1te909
f86fe26ffe fix wording 2024-03-08 18:42:32 +00:00
Dan
162360bf45 Merge pull request #21 from dinger1986/develop
Update TakeControl.vue
2024-03-02 12:43:27 -08:00
dinger1986
612aaa7880 Update TakeControl.vue 2024-02-26 21:51:29 +00:00
wh1te909
e91f3fe53d sync mesh users/perms with trmm amidaware/tacticalrmm#182 2024-02-23 21:30:27 +00:00
wh1te909
f0fe4d64bc same perms 2024-02-23 03:17:35 +00:00
Dan
07cc6aca6a Merge pull request #20 from conlan0/develop
Add Shutdown option to agent action menu
2024-02-22 13:50:14 -08:00
wh1te909
23bf81efbb fix js/typescript monaco support in editor 2024-02-22 20:50:03 +00:00
wh1te909
a55105e5ee format 2024-02-22 20:49:33 +00:00
Dan
5832a426bc Merge pull request #14 from NiceGuyIT/feature/cross-platform-scripting
[Feature] Add cross site scripting
2024-02-21 21:23:26 -08:00
conlan0
38dc709108 Add shutdown url 2024-02-21 21:22:37 -05:00
conlan0
5696d3359b Add Shutdown option 2024-02-21 21:19:20 -05:00
wh1te909
1b4fa84753 update reqs 2024-02-21 01:13:38 +00:00
wh1te909
13f0f117da Release 0.101.40 2024-02-03 01:44:41 +00:00
wh1te909
2db4eeec05 bump version 2024-02-03 01:44:28 +00:00
Dan
fe5e8aa5fe Merge pull request #17 from JordanLukeJones/JordanLukeJones-OTP-1
Update LoginView.vue
2024-02-02 17:40:36 -08:00
Jordan Jones
13e35d24a2 Update LoginView.vue
Add's ability to auto-populate OTP on compatible devices
2024-02-02 09:29:19 +00:00
wh1te909
0b6ae80777 Release 0.101.39 2024-02-02 00:55:23 +00:00
wh1te909
5e0fab88a3 bump version 2024-02-02 00:53:27 +00:00
wh1te909
bf8797264b feat: hide custom fields in summary tab only amidaware/tacticalrmm#1745 2024-01-28 03:22:54 +00:00
wh1te909
14bde967bd feat: add serial number to linux/mac amidaware/tacticalrmm#1683 2024-01-27 02:47:23 +00:00
wh1te909
596ce69789 feat: add from name to email amidaware/tacticalrmm#1726 2024-01-26 00:38:12 +00:00
wh1te909
c5491dcb73 feat: add time and ret code to script test closes amidaware/tacticalrmm#1713 2024-01-26 00:04:58 +00:00
wh1te909
3f6340f0a1 update reqs 2024-01-26 00:04:19 +00:00
wh1te909
351f0870a9 update reqs 2024-01-19 08:09:06 +00:00
Dan
f2638a4c5e Merge pull request #16 from silversword411/develop
Increase user preferences width
2024-01-18 23:14:41 -08:00
silversword411
2bd00d5ca0 Increase user preferences width 2024-01-19 05:31:47 +00:00
wh1te909
00a40dd450 forgot to include 80% 2024-01-17 19:19:00 +00:00
wh1te909
cfe1cb2dbf Release 0.101.38 2023-12-22 17:50:19 +00:00
wh1te909
16fb75b56c bump version 2023-12-22 17:49:31 +00:00
wh1te909
094cf45ce3 add M3 2023-12-22 17:44:55 +00:00
wh1te909
d6984b3da9 use ticket instead of email 2023-12-22 17:44:36 +00:00
sadnub
53fc6f4cde fix folder view 2023-12-12 10:50:32 -05:00
wh1te909
e1dc8050e3 Release 0.101.37 2023-12-01 18:55:37 +00:00
wh1te909
49da10cf0b bump version 2023-12-01 18:53:26 +00:00
wh1te909
a3e10910bf wording 2023-12-01 18:52:01 +00:00
wh1te909
3ff9edc424 update reqs 2023-11-29 22:41:49 +00:00
sadnub
69414d4083 add unsaved changes to new scripts and close and cancel buttons 2023-11-29 10:24:13 -05:00
sadnub
e06b7a7775 add last_seen date to summary tab as a tooltip 2023-11-24 18:34:29 -05:00
sadnub
c006e4d922 make policy tasks sort ascending 2023-11-24 18:19:39 -05:00
sadnub
df6fe0863b change sort order for reports manager 2023-11-24 18:17:29 -05:00
sadnub
d55a29911c add trmm logo to community scripts in dropdown and improve img loading with es6 import 2023-11-24 17:47:07 -05:00
sadnub
d0e49d27fd add prompt for close if using esc in script manager 2023-11-24 17:02:45 -05:00
sadnub
1299bfc93e add trmm icon to builtin scripts 2023-11-24 16:19:33 -05:00
sadnub
be999646d4 make policy status width larger 2023-11-24 15:50:44 -05:00
sadnub
e57d32f122 add onboarding task in ui 2023-11-22 23:37:18 -05:00
wh1te909
3e6365574e Release 0.101.36 2023-11-23 00:03:20 +00:00
wh1te909
08fa8da735 bump version 2023-11-23 00:03:10 +00:00
David Randall
fe8d88497f [Feature] Add cross site scripting 2023-11-12 15:37:55 -05:00
sadnub
4ab31a529e update to node 18 2023-11-11 13:51:48 -05:00
wh1te909
466725d5c2 rework uninstall perm amidaware/tacticalrmm#1673 2023-11-10 01:20:25 +00:00
33 changed files with 1296 additions and 768 deletions

View File

@@ -1,9 +1,9 @@
version: '3.4' version: '3.7'
services: services:
app-dev: app-dev:
container_name: trmm-app-dev container_name: trmm-app-dev
image: node:16-alpine image: node:18-alpine
restart: always restart: always
command: /bin/sh -c "npm install --cache ~/.npm && npm run serve" command: /bin/sh -c "npm install --cache ~/.npm && npm run serve"
user: 1000:1000 user: 1000:1000

View File

@@ -11,11 +11,11 @@ jobs:
name: Build web name: Build web
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: "20.11.1"
- run: touch env-config.js - run: touch env-config.js
@@ -29,6 +29,6 @@ jobs:
run: tar -czvf trmm-web-${{github.ref_name}}.tar.gz dist/ run: tar -czvf trmm-web-${{github.ref_name}}.tar.gz dist/
- name: Release - name: Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v2
with: with:
files: trmm-web-${{github.ref_name}}.tar.gz files: trmm-web-${{github.ref_name}}.tar.gz

1108
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "web", "name": "web",
"version": "0.101.35", "version": "0.101.43",
"private": true, "private": true,
"productName": "Tactical RMM", "productName": "Tactical RMM",
"scripts": { "scripts": {
@@ -10,34 +10,37 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore" "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "1.16.7", "@quasar/extras": "1.16.9",
"apexcharts": "3.44.0", "apexcharts": "3.48.0",
"axios": "1.6.0", "axios": "1.6.8",
"dotenv": "16.3.1", "dotenv": "16.4.5",
"pinia": "^2.1.7",
"qrcode.vue": "3.4.1", "qrcode.vue": "3.4.1",
"quasar": "2.13.0", "quasar": "2.15.1",
"vue": "3.3.8", "vue": "3.4.21",
"vue3-apexcharts": "1.4.4", "vue3-apexcharts": "1.5.2",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
"vue-router": "4.2.5", "vue-router": "4.3.0",
"@vueuse/core": "10.5.0", "@vueuse/core": "10.9.0",
"@vueuse/shared": "10.5.0", "@vueuse/shared": "10.9.0",
"monaco-editor": "0.44.0", "monaco-editor": "0.47.0",
"vuex": "4.1.0", "vuex": "4.1.0",
"yaml": "2.3.4" "xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"yaml": "2.4.1"
}, },
"devDependencies": { "devDependencies": {
"@quasar/cli": "2.3.0", "@quasar/cli": "2.4.0",
"@intlify/unplugin-vue-i18n": "1.4.0", "@intlify/unplugin-vue-i18n": "3.0.1",
"@quasar/app-vite": "1.6.2", "@quasar/app-vite": "1.8.0",
"@types/node": "20.8.10", "@types/node": "20.11.30",
"@typescript-eslint/eslint-plugin": "6.10.0", "@typescript-eslint/eslint-plugin": "7.3.1",
"@typescript-eslint/parser": "6.10.0", "@typescript-eslint/parser": "7.3.1",
"autoprefixer": "10.4.16", "autoprefixer": "10.4.18",
"eslint": "8.53.0", "eslint": "8.57.0",
"eslint-config-prettier": "9.0.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-vue": "8.7.1", "eslint-plugin-vue": "8.7.1",
"prettier": "3.0.3", "prettier": "3.2.5",
"typescript": "5.2.2" "typescript": "5.4.3"
} }
} }

View File

@@ -36,7 +36,7 @@ module.exports = configure(function (/* ctx */) {
// https://github.com/quasarframework/quasar/tree/dev/extras // https://github.com/quasarframework/quasar/tree/dev/extras
extras: [ extras: [
// 'ionicons-v4', "ionicons-v4",
"mdi-v5", "mdi-v5",
"fontawesome-v6", "fontawesome-v6",
// 'eva-icons', // 'eva-icons',
@@ -51,8 +51,8 @@ module.exports = configure(function (/* ctx */) {
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: { build: {
target: { target: {
browser: ["es2021"], browser: ["es2022"],
node: "node16", node: "node20",
}, },
vueRouterMode: "history", // available values: 'hash', 'history' vueRouterMode: "history", // available values: 'hash', 'history'

View File

@@ -191,6 +191,11 @@ export async function agentRebootNow(agent_id) {
return data; return data;
} }
export async function agentShutdown(agent_id) {
const { data } = await axios.post(`${baseUrl}/${agent_id}/shutdown/`);
return data;
}
export async function sendAgentRecoverMesh(agent_id, params = {}) { export async function sendAgentRecoverMesh(agent_id, params = {}) {
const { data } = await axios.post( const { data } = await axios.post(
`${baseUrl}/${agent_id}/meshcentral/recover/`, `${baseUrl}/${agent_id}/meshcentral/recover/`,

BIN
src/assets/trmm_256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -170,7 +170,7 @@
overdueAlert( overdueAlert(
'dashboard', 'dashboard',
props.row, props.row,
props.row.overdue_dashboard_alert props.row.overdue_dashboard_alert,
) )
" "
v-model="props.row.overdue_dashboard_alert" v-model="props.row.overdue_dashboard_alert"
@@ -431,8 +431,8 @@ export default {
return false; return false;
else if (availability === "expired") { else if (availability === "expired") {
let now = new Date(); let now = new Date();
let lastSeen = date.extractDate(row.last_seen, "MM DD YYYY HH:mm"); let last_seen_unix = new Date(row.boot_time * 1000);
let diff = date.getDateDiff(now, lastSeen, "days"); let diff = date.getDateDiff(now, last_seen_unix, "days");
if (diff < 30) return false; if (diff < 30) return false;
} }
} }

View File

@@ -85,10 +85,6 @@
v-model="localRole.can_uninstall_agents" v-model="localRole.can_uninstall_agents"
label="Uninstall Agents" label="Uninstall Agents"
/> />
<q-checkbox
v-model="localRole.can_ping_agents"
label="Ping Agents"
/>
<q-checkbox <q-checkbox
v-model="localRole.can_update_agents" v-model="localRole.can_update_agents"
label="Update Agents" label="Update Agents"
@@ -111,7 +107,7 @@
/> />
<q-checkbox <q-checkbox
v-model="localRole.can_reboot_agents" v-model="localRole.can_reboot_agents"
label="Reboot Agents" label="Shutdown / Reboot Agents"
/> />
<q-checkbox <q-checkbox
v-model="localRole.can_send_wol" v-model="localRole.can_send_wol"
@@ -447,7 +443,6 @@ export default {
can_uninstall_agents: false, can_uninstall_agents: false,
can_update_agents: false, can_update_agents: false,
can_edit_agent: false, can_edit_agent: false,
can_ping_agents: false,
can_manage_procs: false, can_manage_procs: false,
can_view_eventlogs: false, can_view_eventlogs: false,
can_send_cmd: false, can_send_cmd: false,

View File

@@ -176,6 +176,13 @@
</q-menu> </q-menu>
</q-item> </q-item>
<q-item clickable v-close-popup @click="shutdown(agent)">
<q-item-section side>
<q-icon size="xs" name="power" />
</q-item-section>
<q-item-section>Shutdown</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showPolicyAdd(agent)"> <q-item clickable v-close-popup @click="showPolicyAdd(agent)">
<q-item-section side> <q-item-section side>
<q-icon size="xs" name="policy" /> <q-icon size="xs" name="policy" />
@@ -192,9 +199,9 @@
" "
> >
<q-item-section side> <q-item-section side>
<q-icon size="xs" name="integration_instructions" /> <q-icon size="xs" name="analytics" />
</q-item-section> </q-item-section>
<q-item-section>Integrations</q-item-section> <q-item-section>Reporting</q-item-section>
<q-item-section side> <q-item-section side>
<q-icon name="keyboard_arrow_right" /> <q-icon name="keyboard_arrow_right" />
</q-item-section> </q-item-section>
@@ -231,6 +238,7 @@ import { fetchURLActions, runURLAction } from "@/api/core";
import { import {
editAgent, editAgent,
agentRebootNow, agentRebootNow,
agentShutdown,
sendAgentPing, sendAgentPing,
removeAgent, removeAgent,
runRemoteBackground, runRemoteBackground,
@@ -298,7 +306,7 @@ export default {
if (urlActions.value.length === 0) { if (urlActions.value.length === 0) {
notifyWarning( notifyWarning(
"No URL Actions configured. Go to Settings > Global Settings > URL Actions" "No URL Actions configured. Go to Settings > Global Settings > URL Actions",
); );
return; return;
} }
@@ -364,7 +372,7 @@ export default {
notifySuccess( notifySuccess(
`Maintenance mode was ${ `Maintenance mode was ${
agent.maintenance_mode ? "disabled" : "enabled" agent.maintenance_mode ? "disabled" : "enabled"
} on ${agent.hostname}` } on ${agent.hostname}`,
); );
store.commit("setRefreshSummaryTab", true); store.commit("setRefreshSummaryTab", true);
refreshDashboard(); refreshDashboard();
@@ -437,6 +445,32 @@ export default {
}); });
} }
function shutdown(agent) {
$q.dialog({
title:
'Please type <code style="color:red">yes</code> in the box below to confirm shutdown.',
prompt: {
model: "",
type: "text",
isValid: (val) => val === "yes",
},
cancel: true,
ok: { label: "Shutdown", color: "negative" },
persistent: true,
html: true,
}).onOk(async () => {
$q.loading.show();
try {
await agentShutdown(agent.agent_id);
notifySuccess(`${agent.hostname} will now be shutdown`);
$q.loading.hide();
} catch (e) {
$q.loading.hide();
console.error(e);
}
});
}
function showPolicyAdd(agent) { function showPolicyAdd(agent) {
$q.dialog({ $q.dialog({
component: PolicyAdd, component: PolicyAdd,
@@ -505,7 +539,7 @@ export default {
notifySuccess(data); notifySuccess(data);
refreshDashboard( refreshDashboard(
false /* clearTreeSelected */, false /* clearTreeSelected */,
true /* clearSubTable */ true /* clearSubTable */,
); );
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@@ -534,6 +568,7 @@ export default {
runChecks, runChecks,
showRebootLaterModal, showRebootLaterModal,
rebootNow, rebootNow,
shutdown,
showPolicyAdd, showPolicyAdd,
showAgentRecovery, showAgentRecovery,
pingAgent, pingAgent,

View File

@@ -34,7 +34,7 @@
:color="dash_warning_color" :color="dash_warning_color"
class="q-mr-sm" class="q-mr-sm"
> >
<q-tooltip>Agent offline</q-tooltip> <q-tooltip>{{ store.getters.formatDate(summary.last_seen) }}</q-tooltip>
</q-icon> </q-icon>
<q-icon <q-icon
v-else v-else
@@ -43,7 +43,7 @@
:color="dash_positive_color" :color="dash_positive_color"
class="q-mr-sm" class="q-mr-sm"
> >
<q-tooltip>Agent online</q-tooltip> <q-tooltip>{{ store.getters.formatDate(summary.last_seen) }}</q-tooltip>
</q-icon> </q-icon>
<b>{{ summary.hostname }}</b> <b>{{ summary.hostname }}</b>
<span v-if="summary.maintenance_mode"> <span v-if="summary.maintenance_mode">
@@ -267,7 +267,11 @@ export default {
const loading = ref(false); const loading = ref(false);
const serial_number = computed(() => { const serial_number = computed(() => {
if (summary.value.plat === "windows") {
return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber; return summary.value.wmi_detail.bios?.[0]?.[0]?.SerialNumber;
} else {
return summary.value.wmi_detail.serialnumber;
}
}); });
const cpu = computed(() => { const cpu = computed(() => {
@@ -280,7 +284,7 @@ export default {
function diskBarColor(percent) { function diskBarColor(percent) {
if (percent < 80) { if (percent < 80) {
return dash_positive_color.value; return dash_positive_color.value;
} else if (percent > 80 && percent < 95) { } else if (percent >= 80 && percent < 95) {
return dash_warning_color.value; return dash_warning_color.value;
} else { } else {
return dash_negative_color.value; return dash_negative_color.value;
@@ -311,11 +315,11 @@ export default {
const ret = []; const ret = [];
for (const customField of summary.value.custom_fields) { for (const customField of summary.value.custom_fields) {
const definition = customFieldsDefinitions.value.find( const definition = customFieldsDefinitions.value.find(
(def) => def.id === customField.field (def) => def.id === customField.field,
); );
if ( if (
definition && definition &&
!definition.hide_in_ui && !definition.hide_in_summary &&
customField.value?.length > 0 customField.value?.length > 0
) { ) {
ret.push({ ret.push({
@@ -381,6 +385,7 @@ export default {
dash_negative_color, dash_negative_color,
serial_number, serial_number,
cpu, cpu,
store,
// methods // methods
getSummary, getSummary,

View File

@@ -254,7 +254,7 @@ export default {
pagination: { pagination: {
rowsPerPage: 0, rowsPerPage: 0,
sortBy: "name", sortBy: "name",
descending: true, descending: false,
}, },
}; };
}, },
@@ -321,7 +321,7 @@ export default {
runTask(task) { runTask(task) {
if (!task.enabled) { if (!task.enabled) {
this.notifyError( this.notifyError(
"Task cannot be run when it's disabled. Enable it first." "Task cannot be run when it's disabled. Enable it first.",
); );
return; return;
} }

View File

@@ -1,6 +1,6 @@
<template> <template>
<q-dialog ref="dialog" @hide="onHide"> <q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="width: 90vw"> <q-card class="q-dialog-plugin" style="min-width: 70vw">
<q-bar> <q-bar>
{{ title.slice(0, 27) }} {{ title.slice(0, 27) }}
<q-space /> <q-space />

View File

@@ -137,7 +137,7 @@
<q-radio <q-radio
v-model="goarch" v-model="goarch"
:val="GOARCH_ARM64" :val="GOARCH_ARM64"
label="Apple Silicon (M1, M2)" label="Apple Silicon (M1, M2, M3)"
v-show="agentOS === 'darwin'" v-show="agentOS === 'darwin'"
/> />
<q-radio <q-radio

View File

@@ -142,6 +142,11 @@
v-model="localField.hide_in_ui" v-model="localField.hide_in_ui"
color="green" color="green"
/> />
<q-toggle
label="Hide in Summary Tab"
v-model="localField.hide_in_summary"
color="green"
/>
</q-card-section> </q-card-section>
<q-card-actions align="right"> <q-card-actions align="right">
<q-btn flat label="Cancel" v-close-popup /> <q-btn flat label="Cancel" v-close-popup />
@@ -172,6 +177,7 @@ export default {
default_value_bool: false, default_value_bool: false,
default_values_multiple: [], default_values_multiple: [],
hide_in_ui: false, hide_in_ui: false,
hide_in_summary: false,
}, },
modelOptions: [ modelOptions: [
{ label: "Client", value: "client" }, { label: "Client", value: "client" },

View File

@@ -57,6 +57,10 @@
<q-td> <q-td>
<q-icon v-if="props.row.hide_in_ui" name="check" /> <q-icon v-if="props.row.hide_in_ui" name="check" />
</q-td> </q-td>
<!-- hide in summary tab -->
<q-td>
<q-icon v-if="props.row.hide_in_summary" name="check" />
</q-td>
<!-- default value --> <!-- default value -->
<q-td v-if="props.row.type === 'checkbox'"> <q-td v-if="props.row.type === 'checkbox'">
{{ props.row.default_value_bool }} {{ props.row.default_value_bool }}
@@ -123,6 +127,13 @@ export default {
align: "left", align: "left",
sortable: true, sortable: true,
}, },
{
name: "hide_in_summary",
label: "Hide in Summary Tab",
field: "hide_in_summary",
align: "left",
sortable: true,
},
{ {
name: "default_value", name: "default_value",
label: "Default Value", label: "Default Value",

View File

@@ -71,7 +71,7 @@
icon="info" icon="info"
@click=" @click="
openURL( openURL(
'https://quasar.dev/quasar-utils/date-utils#format-for-display' 'https://quasar.dev/quasar-utils/date-utils#format-for-display',
) )
" "
> >
@@ -216,7 +216,7 @@
<div class="text-subtitle2">SMTP Settings</div> <div class="text-subtitle2">SMTP Settings</div>
<q-separator /> <q-separator />
<q-card-section class="row"> <q-card-section class="row">
<div class="col-2">From:</div> <div class="col-2">From email:</div>
<div class="col-4"></div> <div class="col-4"></div>
<q-input <q-input
outlined outlined
@@ -226,6 +226,16 @@
:rules="[(val) => isValidEmail(val) || 'Invalid email']" :rules="[(val) => isValidEmail(val) || 'Invalid email']"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row">
<div class="col-2">From name:</div>
<div class="col-4"></div>
<q-input
outlined
dense
v-model="settings.smtp_from_name"
class="col-6 q-pa-none"
/>
</q-card-section>
<q-card-section class="row"> <q-card-section class="row">
<div class="col-2">Host:</div> <div class="col-2">Host:</div>
<div class="col-4"></div> <div class="col-4"></div>
@@ -379,7 +389,7 @@
<q-tab-panel name="meshcentral"> <q-tab-panel name="meshcentral">
<div class="text-subtitle2">MeshCentral Settings</div> <div class="text-subtitle2">MeshCentral Settings</div>
<q-separator /> <q-separator />
<q-card-section class="row"> <q-card-section class="row" v-if="!hosted">
<div class="col-4">Username:</div> <div class="col-4">Username:</div>
<div class="col-2"></div> <div class="col-2"></div>
<q-input <q-input
@@ -395,7 +405,7 @@
]" ]"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row" v-if="!hosted">
<div class="col-4">Mesh Site:</div> <div class="col-4">Mesh Site:</div>
<div class="col-2"></div> <div class="col-2"></div>
<q-input <q-input
@@ -405,7 +415,7 @@
class="col-6" class="col-6"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row" v-if="!hosted">
<div class="col-4">Mesh Token:</div> <div class="col-4">Mesh Token:</div>
<div class="col-2"></div> <div class="col-2"></div>
<q-input <q-input
@@ -415,7 +425,7 @@
class="col-6" class="col-6"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row" v-if="!hosted">
<div class="col-4">Mesh Device Group Name:</div> <div class="col-4">Mesh Device Group Name:</div>
<div class="col-2"></div> <div class="col-2"></div>
<q-input <q-input
@@ -425,17 +435,58 @@
class="col-6" class="col-6"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row" v-if="!hosted">
<div class="col-4"> <div class="col-4 flex items-center">
Disable Auto Login for Remote Control and Remote background: Sync Mesh Perms with TRMM:
<q-icon
right
name="ion-information-circle-outline"
size="sm"
class="cursor-pointer"
>
<q-tooltip class="text-caption">
It is recommended to keep this option enabled;
otherwise, all TRMM users will have full permissions in
MeshCentral regardless of their permissions in TRMM.
</q-tooltip>
</q-icon>
</div> </div>
<div class="col-2"></div> <div class="col-2"></div>
<q-checkbox <q-checkbox
dense dense
v-model="settings.mesh_disable_auto_login" :model-value="settings.sync_mesh_with_trmm"
@update:model-value="confirmSyncChange"
class="col-6" class="col-6"
/> />
</q-card-section> </q-card-section>
<q-card-section class="row items-center">
<div class="col-4 flex items-center">
Company Name:
<q-icon
name="ion-information-circle-outline"
size="sm"
class="q-ml-sm cursor-pointer"
>
<q-tooltip class="text-caption">
Adding your company name here will append it to the
user's full name that appears when doing a remote
control session, for example: 'John Doe - Amidaware
Inc.'
</q-tooltip>
</q-icon>
</div>
<div class="col-2"></div>
<q-input
dense
outlined
v-model="settings.mesh_company_name"
class="col-6"
>
</q-input>
</q-card-section>
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="customfields"> <q-tab-panel name="customfields">
<CustomFields /> <CustomFields />
@@ -635,6 +686,11 @@ export default {
], ],
}; };
}, },
computed: {
hosted() {
return this.$store.state.hosted;
},
},
methods: { methods: {
openURL(url) { openURL(url) {
openURL(url); openURL(url);
@@ -669,6 +725,19 @@ export default {
})); }));
}); });
}, },
confirmSyncChange(newValue) {
this.$q
.dialog({
title: "Are you sure?",
message:
"This operation may take several minutes to complete in the background and can be very CPU/disk intensive, depending on your hardware and number of agents. Please allow time for the sync to fully complete.",
ok: { label: "Yes", color: "primary" },
cancel: { label: "No", color: "negative" },
})
.onOk(() => {
this.settings.sync_mesh_with_trmm = newValue;
});
},
showResetPatchPolicy() { showResetPatchPolicy() {
this.$q.dialog({ this.$q.dialog({
component: ResetPatchPolicy, component: ResetPatchPolicy,
@@ -711,13 +780,13 @@ export default {
}, },
removeEmail(email) { removeEmail(email) {
const removed = this.settings.email_alert_recipients.filter( const removed = this.settings.email_alert_recipients.filter(
(k) => k !== email (k) => k !== email,
); );
this.settings.email_alert_recipients = removed; this.settings.email_alert_recipients = removed;
}, },
removeSMSNumber(num) { removeSMSNumber(num) {
const removed = this.settings.sms_alert_recipients.filter( const removed = this.settings.sms_alert_recipients.filter(
(k) => k !== num (k) => k !== num,
); );
this.settings.sms_alert_recipients = removed; this.settings.sms_alert_recipients = removed;
}, },

View File

@@ -1,6 +1,6 @@
<template> <template>
<q-dialog ref="dialog" @hide="onHide"> <q-dialog ref="dialog" @hide="onHide">
<q-card class="q-dialog-plugin" style="min-width: 85vh"> <q-card class="q-dialog-plugin" style="min-width: 60vw">
<q-splitter v-model="splitterModel"> <q-splitter v-model="splitterModel">
<template v-slot:before> <template v-slot:before>
<q-tabs dense v-model="tab" vertical class="text-primary"> <q-tabs dense v-model="tab" vertical class="text-primary">
@@ -201,7 +201,7 @@
icon="info" icon="info"
@click=" @click="
openURL( openURL(
'https://quasar.dev/quasar-utils/date-utils#format-for-display' 'https://quasar.dev/quasar-utils/date-utils#format-for-display',
) )
" "
> >
@@ -315,7 +315,7 @@ export default {
this.$axios.get("/core/urlaction/").then((r) => { this.$axios.get("/core/urlaction/").then((r) => {
if (r.data.length === 0) { if (r.data.length === 0) {
this.notifyWarning( this.notifyWarning(
"No URL Actions configured. Go to Settings > Global Settings > URL Actions" "No URL Actions configured. Go to Settings > Global Settings > URL Actions",
); );
return; return;
} }

View File

@@ -2,9 +2,11 @@
<q-dialog <q-dialog
ref="dialogRef" ref="dialogRef"
maximized maximized
no-esc-dismiss
@hide="onDialogHide" @hide="onDialogHide"
@show="loadEditor" @show="loadEditor"
@before-hide="unloadEditor" @before-hide="unloadEditor"
@keydown.esc.stop="closeEditor"
> >
<q-card class="q-dialog-plugin"> <q-card class="q-dialog-plugin">
<q-bar> <q-bar>
@@ -20,7 +22,7 @@
@click="generateScriptOpenAI" @click="generateScriptOpenAI"
/> />
<q-space /> <q-space />
<q-btn dense flat icon="close" v-close-popup> <q-btn dense flat icon="close" @click="closeEditor">
<q-tooltip class="bg-white text-primary">Close</q-tooltip> <q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn> </q-btn>
</q-bar> </q-bar>
@@ -190,7 +192,7 @@
</template> </template>
</tactical-dropdown> </tactical-dropdown>
<q-space /> <q-space />
<q-btn dense flat label="Cancel" v-close-popup /> <q-btn dense flat label="Cancel" @click="closeEditor" />
<q-btn <q-btn
v-if="!readonly" v-if="!readonly"
:loading="loading" :loading="loading"
@@ -220,6 +222,35 @@ import TestScriptModal from "@/components/scripts/TestScriptModal.vue";
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue"; import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
import * as monaco from "monaco-editor"; import * as monaco from "monaco-editor";
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import jsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
// https://github.com/microsoft/monaco-editor/issues/4045#issuecomment-1723787448
self.MonacoEnvironment = {
getWorker: function (workerId, label) {
switch (label) {
case "json":
return new jsonWorker();
case "css":
case "scss":
case "less":
return new cssWorker();
case "html":
case "handlebars":
case "razor":
return new htmlWorker();
case "typescript":
case "javascript":
return new jsWorker();
default:
return new editorWorker();
}
},
};
// types // types
import type { Script } from "@/types/scripts"; import type { Script } from "@/types/scripts";
@@ -294,11 +325,21 @@ const title = computed(() => {
// convert highlighter language to match what ace expects // convert highlighter language to match what ace expects
const lang = computed(() => { const lang = computed(() => {
if (script.shell === "cmd") return "bat"; switch (script.shell) {
else if (script.shell === "powershell") return "powershell"; case "cmd":
else if (script.shell === "python") return "python"; return "bat";
else if (script.shell === "shell") return "shell"; case "powershell":
else return ""; return "powershell";
case "python":
return "python";
case "shell":
case "nushell":
return "shell";
case "deno":
return "typescript";
default:
return "";
}
}); });
async function submit() { async function submit() {
@@ -337,12 +378,7 @@ const scriptEditor = ref<HTMLElement | null>(null);
let editor: monaco.editor.IStandaloneCodeEditor; let editor: monaco.editor.IStandaloneCodeEditor;
function loadEditor() { function loadEditor() {
var modelUri = monaco.Uri.parse("model://new"); // a made up unique URI for our model var model = monaco.editor.createModel(script.script_body, lang.value);
var model = monaco.editor.createModel(
script.script_body,
lang.value,
modelUri,
);
const theme = $q.dark.isActive ? "vs-dark" : "vs-light"; const theme = $q.dark.isActive ? "vs-dark" : "vs-light";
@@ -363,7 +399,23 @@ function loadEditor() {
downloadScript(script.id, { with_snippets: props.readonly }).then((r) => { downloadScript(script.id, { with_snippets: props.readonly }).then((r) => {
script.script_body = r.code; script.script_body = r.code;
editor.setValue(r.code); editor.setValue(r.code);
// need to add this in the download function otherwise the above will trigger an edit
watch(
() => script.script_body,
() => {
edited.value = true;
},
);
}); });
else {
watch(
() => script.script_body,
() => {
edited.value = true;
},
);
}
// watch for changes in language // watch for changes in language
watch(lang, () => { watch(lang, () => {
@@ -394,6 +446,21 @@ function generateScriptOpenAI() {
}); });
} }
// add are you sure prompt to unsaved script
const edited = ref(false);
function closeEditor() {
if (edited.value)
$q.dialog({
title: "You have unsaved changes. Are you sure you want to close?",
cancel: true,
ok: true,
}).onOk(async () => {
unloadEditor();
});
else unloadEditor();
}
// component life cycle hooks // component life cycle hooks
onMounted(async () => { onMounted(async () => {
agentLoading.value = true; agentLoading.value = true;

View File

@@ -175,6 +175,28 @@
> >
<q-tooltip> Shell </q-tooltip> <q-tooltip> Shell </q-tooltip>
</q-icon> </q-icon>
<q-icon
v-else-if="props.node.shell === 'nushell'"
name="mdi-code-greater-than"
color="primary"
>
<q-tooltip> Nushell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.node.shell === 'deno'"
name="mdi-language-typescript"
color="primary"
>
<q-tooltip> Deno </q-tooltip>
</q-icon>
<!-- is community script icon -->
<img
v-if="props.node.script_type === 'builtin'"
class="vertical-middle"
:src="trmmLogo"
style="height: 20px; max-width: 20px"
/>
<span <span
class="q-pl-xs text-weight-bold" class="q-pl-xs text-weight-bold"
@@ -463,6 +485,22 @@
> >
<q-tooltip> Shell </q-tooltip> <q-tooltip> Shell </q-tooltip>
</q-icon> </q-icon>
<q-icon
v-else-if="props.row.shell === 'nushell'"
size="sm"
name="mdi-code-greater-than"
color="primary"
>
<q-tooltip> Nushell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.shell === 'deno'"
size="sm"
name="mdi-language-typescript"
color="primary"
>
<q-tooltip> Deno </q-tooltip>
</q-icon>
</q-td> </q-td>
<!-- supported platforms --> <!-- supported platforms -->
<q-td key="supported_platforms" :props="props"> <q-td key="supported_platforms" :props="props">
@@ -488,6 +526,12 @@
:props="props" :props="props"
:style="{ color: props.row.hidden ? 'grey' : '' }" :style="{ color: props.row.hidden ? 'grey' : '' }"
> >
<!-- is community script icon -->
<img
v-if="props.row.script_type === 'builtin'"
:src="trmmLogo"
style="height: 20px; max-width: 20px"
/>
{{ truncateText(props.row.name, 50) }} {{ truncateText(props.row.name, 50) }}
<q-tooltip <q-tooltip
v-if="props.row.name.length >= 50" v-if="props.row.name.length >= 50"
@@ -550,6 +594,8 @@ import ScriptFormModal from "@/components/scripts/ScriptFormModal.vue";
import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue"; import ScriptSnippets from "@/components/scripts/ScriptSnippets.vue";
import TacticalTable from "@/components/ui/TacticalTable.vue"; import TacticalTable from "@/components/ui/TacticalTable.vue";
import trmmLogo from "@/assets/trmm_256.png";
// static data // static data
const columns = [ const columns = [
{ {
@@ -620,7 +666,7 @@ export default {
// setup vuex store // setup vuex store
const store = useStore(); const store = useStore();
const showCommunityScripts = computed( const showCommunityScripts = computed(
() => store.state.showCommunityScripts () => store.state.showCommunityScripts,
); );
// setup quasar plugins // setup quasar plugins
@@ -721,7 +767,7 @@ export default {
return showCommunityScripts.value return showCommunityScripts.value
? scripts.value.filter((i) => !i.hidden) ? scripts.value.filter((i) => !i.hidden)
: scripts.value.filter( : scripts.value.filter(
(i) => i.script_type !== "builtin" && !i.hidden (i) => i.script_type !== "builtin" && !i.hidden,
); );
} }
}); });
@@ -884,6 +930,7 @@ export default {
loading, loading,
showCommunityScripts, showCommunityScripts,
showHiddenScripts, showHiddenScripts,
trmmLogo,
// computed // computed
visibleScripts, visibleScripts,

View File

@@ -86,6 +86,35 @@ import { notifySuccess } from "@/utils/notify";
// ui imports // ui imports
import * as monaco from "monaco-editor"; import * as monaco from "monaco-editor";
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import jsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
// https://github.com/microsoft/monaco-editor/issues/4045#issuecomment-1723787448
self.MonacoEnvironment = {
getWorker: function (workerId, label) {
switch (label) {
case "json":
return new jsonWorker();
case "css":
case "scss":
case "less":
return new cssWorker();
case "html":
case "handlebars":
case "razor":
return new htmlWorker();
case "typescript":
case "javascript":
return new jsWorker();
default:
return new editorWorker();
}
},
};
// types // types
import type { ScriptSnippet } from "@/types/scripts"; import type { ScriptSnippet } from "@/types/scripts";
@@ -124,11 +153,21 @@ const title = computed(() => {
// convert highlighter language to match what ace expects // convert highlighter language to match what ace expects
const lang = computed(() => { const lang = computed(() => {
if (snippet.shell === "cmd") return "bat"; switch (snippet.shell) {
else if (snippet.shell === "powershell") return "powershell"; case "cmd":
else if (snippet.shell === "python") return "python"; return "bat";
else if (snippet.shell === "shell") return "shell"; case "powershell":
else return ""; return "powershell";
case "python":
return "python";
case "shell":
case "nushell":
return "shell";
case "deno":
return "typescript";
default:
return "";
}
}); });
async function submit() { async function submit() {
@@ -150,8 +189,7 @@ const snippetEditor = ref<HTMLElement | null>(null);
let editor: monaco.editor.IStandaloneCodeEditor; let editor: monaco.editor.IStandaloneCodeEditor;
function loadEditor() { function loadEditor() {
var modelUri = monaco.Uri.parse("model://snippet"); // a made up unique URI for our model var model = monaco.editor.createModel(snippet.code, lang.value);
var model = monaco.editor.createModel(snippet.code, lang.value, modelUri);
const theme = $q.dark.isActive ? "vs-dark" : "vs-light"; const theme = $q.dark.isActive ? "vs-dark" : "vs-light";

View File

@@ -124,6 +124,22 @@
> >
<q-tooltip> Shell </q-tooltip> <q-tooltip> Shell </q-tooltip>
</q-icon> </q-icon>
<q-icon
v-else-if="props.row.shell === 'nushell'"
name="mdi-nushell"
color="primary"
size="sm"
>
<q-tooltip> Nushell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.shell === 'deno'"
name="mdi-typescript"
color="primary"
size="sm"
>
<q-tooltip> Deno </q-tooltip>
</q-icon>
</q-td> </q-td>
<!-- name --> <!-- name -->
<q-td>{{ props.row.name }}</q-td> <q-td>{{ props.row.name }}</q-td>

View File

@@ -8,8 +8,25 @@
<q-tooltip class="bg-white text-primary">Close</q-tooltip> <q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn> </q-btn>
</q-bar> </q-bar>
<q-card-section class="scroll" style="max-height: 70vh; height: 70vh"> <q-card-section style="height: 70vh" class="scroll">
<pre v-if="ret">{{ ret }}</pre> <div>
Run Time:
<code>{{ ret.execution_time }} seconds</code>
<br />Return Code:
<code>{{ ret.retcode }}</code>
<br />
</div>
<br />
<div v-if="ret.stdout">
Standard Output
<q-separator />
<pre>{{ ret.stdout }}</pre>
</div>
<div v-if="ret.stderr">
Standard Error
<q-separator />
<pre>{{ ret.stderr }}</pre>
</div>
<q-inner-loading :showing="loading" /> <q-inner-loading :showing="loading" />
</q-card-section> </q-card-section>
</q-card> </q-card>
@@ -34,7 +51,12 @@ export default {
const { dialogRef, onDialogHide } = useDialogPluginComponent(); const { dialogRef, onDialogHide } = useDialogPluginComponent();
// main run script functionality // main run script functionality
const ret = ref(null); const ret = ref({
execution_time: "",
retcode: "",
stdout: "",
stderr: "",
});
const loading = ref(false); const loading = ref(false);
async function runTestScript() { async function runTestScript() {

View File

@@ -87,6 +87,7 @@
:done="step > 2" :done="step > 2"
:error="!isValidStep2" :error="!isValidStep2"
> >
<div class="scroll" style="max-height: 60vh">
<q-form @submit.prevent="addAction"> <q-form @submit.prevent="addAction">
<div class="row q-pa-sm q-gutter-x-xs items-center"> <div class="row q-pa-sm q-gutter-x-xs items-center">
<div class="text-subtitle2 col-12">Action Type:</div> <div class="text-subtitle2 col-12">Action Type:</div>
@@ -200,7 +201,7 @@
<q-tooltip>Continue task if an action fails</q-tooltip> <q-tooltip>Continue task if an action fails</q-tooltip>
</q-checkbox> </q-checkbox>
</div> </div>
<div class="scroll q-pt-sm" style="height: 40vh; max-height: 40vh"> <div class="q-pt-sm" style="height: 150px">
<draggable <draggable
class="q-list" class="q-list"
handle=".handle" handle=".handle"
@@ -263,6 +264,7 @@
</template> </template>
</draggable> </draggable>
</div> </div>
</div>
</q-step> </q-step>
<q-step :name="3" title="Choose Schedule" :error="!isValidStep3"> <q-step :name="3" title="Choose Schedule" :error="!isValidStep3">
@@ -283,7 +285,7 @@
<q-card-section <q-card-section
v-if=" v-if="
['runonce', 'daily', 'weekly', 'monthly'].includes( ['runonce', 'daily', 'weekly', 'monthly'].includes(
state.task_type state.task_type,
) )
" "
class="row" class="row"
@@ -314,6 +316,22 @@
/> />
</q-card-section> </q-card-section>
<q-card-section
v-if="
state.task_type === 'onboarding' ||
state.task_type === 'runonce'
"
class="row"
>
<span v-if="state.task_type === 'onboarding'"
>This task will run as soon as it's created on the
agent.</span
>
<span v-else-if="state.task_type === 'runonce'"
>Start Time must be in the future for run once tasks.</span
>
</q-card-section>
<!-- daily options --> <!-- daily options -->
<q-card-section v-if="state.task_type === 'daily'" class="row"> <q-card-section v-if="state.task_type === 'daily'" class="row">
<!-- daily interval --> <!-- daily interval -->
@@ -579,7 +597,8 @@
<q-card-section <q-card-section
v-if=" v-if="
state.task_type !== 'checkfailure' && state.task_type !== 'checkfailure' &&
state.task_type !== 'manual' state.task_type !== 'manual' &&
state.task_type !== 'onboarding'
" "
class="row" class="row"
> >
@@ -617,7 +636,7 @@
(val) => (val) =>
convertPeriodToSeconds(val) >= convertPeriodToSeconds(val) >=
convertPeriodToSeconds( convertPeriodToSeconds(
state.task_repetition_interval state.task_repetition_interval,
) || ) ||
'Repetition duration must be greater than repetition interval', 'Repetition duration must be greater than repetition interval',
]" ]"
@@ -712,7 +731,7 @@
@click=" @click="
validateStep( validateStep(
step === 1 ? $refs.taskGeneralForm : undefined, step === 1 ? $refs.taskGeneralForm : undefined,
$refs.stepper $refs.stepper,
) )
" "
color="primary" color="primary"
@@ -769,6 +788,7 @@ const taskTypeOptions = [
{ label: "Monthly", value: "monthly" }, { label: "Monthly", value: "monthly" },
{ label: "Run Once", value: "runonce" }, { label: "Run Once", value: "runonce" },
{ label: "On check failure", value: "checkfailure" }, { label: "On check failure", value: "checkfailure" },
{ label: "Onboarding", value: "onboarding" },
{ label: "Manual", value: "manual" }, { label: "Manual", value: "manual" },
]; ];
@@ -933,7 +953,7 @@ export default {
task.value.actions.push({ task.value.actions.push({
type: "script", type: "script",
name: scriptOptions.value.find( name: scriptOptions.value.find(
(option) => option.value === script.value (option) => option.value === script.value,
).label, ).label,
script: script.value, script: script.value,
timeout: defaultTimeout.value, timeout: defaultTimeout.value,
@@ -1019,13 +1039,13 @@ export default {
// remove milliseconds and Z to work with native date input // remove milliseconds and Z to work with native date input
task.value.run_time_date = formatDateInputField( task.value.run_time_date = formatDateInputField(
task.value.run_time_date, task.value.run_time_date,
true true,
); );
if (task.value.expire_date) if (task.value.expire_date)
task.value.expire_date = formatDateInputField( task.value.expire_date = formatDateInputField(
task.value.expire_date, task.value.expire_date,
true true,
); );
// set task type if monthlydow is being used // set task type if monthlydow is being used
@@ -1069,7 +1089,7 @@ export default {
task.value.monthly_weeks_of_month = []; task.value.monthly_weeks_of_month = [];
task.value.task_instance_policy = 0; task.value.task_instance_policy = 0;
task.value.expire_date = null; task.value.expire_date = null;
} },
); );
// check the collector box when editing task and custom field is set // check the collector box when editing task and custom field is set

View File

@@ -25,13 +25,21 @@
:key="mapOptions ? scope.opt.value : scope.opt" :key="mapOptions ? scope.opt.value : scope.opt"
> >
<q-item-section> <q-item-section>
<q-item-label <q-item-label v-html="mapOptions ? scope.opt.label : scope.opt" />
v-html="mapOptions ? scope.opt.label : scope.opt" </q-item-section>
></q-item-label> <q-item-section
v-if="
(filtered && mapOptions && scope.opt.cat) || scope.opt.img_right
"
side
>
{{ scope.opt.cat || "" }}
<img
v-if="scope.opt.img_right"
:src="scope.opt.img_right"
style="height: 20px; max-width: 20px"
/>
</q-item-section> </q-item-section>
<q-item-section v-if="filtered && mapOptions && scope.opt.cat" side>{{
scope.opt.cat
}}</q-item-section>
</q-item> </q-item>
<q-item-label <q-item-label
v-if="scope.opt.category" v-if="scope.opt.category"
@@ -80,7 +88,7 @@ export default {
if (!props.mapOptions) if (!props.mapOptions)
filteredOptions.value = props.options.filter( filteredOptions.value = props.options.filter(
(v) => v.toLowerCase().indexOf(needle) > -1 (v) => v.toLowerCase().indexOf(needle) > -1,
); );
else else
filteredOptions.value = props.options.filter((v) => { filteredOptions.value = props.options.filter((v) => {

View File

@@ -18,7 +18,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
// specify parameters to filter out community scripts // specify parameters to filter out community scripts
async function getScriptOptions(showCommunityScripts = false) { async function getScriptOptions(showCommunityScripts = false) {
scriptOptions.value = Object.freeze( scriptOptions.value = Object.freeze(
formatScriptOptions(await fetchScripts({ showCommunityScripts })) formatScriptOptions(await fetchScripts({ showCommunityScripts })),
); );
} }
@@ -26,7 +26,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
watch([script, scriptOptions], () => { watch([script, scriptOptions], () => {
if (script.value && scriptOptions.value.length > 0) { if (script.value && scriptOptions.value.length > 0) {
const tmpScript = scriptOptions.value.find( const tmpScript = scriptOptions.value.find(
(i) => i.value === script.value (i) => i.value === script.value,
); );
defaultTimeout.value = tmpScript.timeout; defaultTimeout.value = tmpScript.timeout;
defaultArgs.value = tmpScript.args; defaultArgs.value = tmpScript.args;
@@ -65,4 +65,6 @@ export const shellOptions = [
{ label: "Batch", value: "cmd" }, { label: "Batch", value: "cmd" },
{ label: "Python", value: "python" }, { label: "Python", value: "python" },
{ label: "Shell", value: "shell" }, { label: "Shell", value: "shell" },
{ label: "Nushell", value: "nushell" },
{ label: "Deno", value: "deno" },
]; ];

View File

@@ -32,7 +32,7 @@ For details, see: https://license.tacticalrmm.com/ee
:rows="reportTemplates" :rows="reportTemplates"
:columns="columns" :columns="columns"
:loading="isLoading" :loading="isLoading"
:pagination="{ rowsPerPage: 0, sortBy: 'name', descending: true }" :pagination="{ rowsPerPage: 0, sortBy: 'name', descending: false }"
:filter="search" :filter="search"
row-key="id" row-key="id"
binary-state-sort binary-state-sort

View File

@@ -25,8 +25,8 @@
If you have downgraded or cancelled your sponsorship, please delete If you have downgraded or cancelled your sponsorship, please delete
your token from the Code Signing modal and refresh to get rid of this your token from the Code Signing modal and refresh to get rid of this
banner.<br /><br /> banner.<br /><br />
For any issues or to renew your sponsorship please email For any issues or to renew your sponsorship please open a ticket at
support@amidaware.com<br /><br support.amidaware.com<br /><br
/></span> /></span>
<q-btn <q-btn
color="dark" color="dark"

View File

@@ -1,6 +1,6 @@
import type { AgentPlatformType } from "@/types/agents"; import type { AgentPlatformType } from "@/types/agents";
export type ScriptShellType = "powershell" | "cmd" | "shell" | "python"; export type ScriptShellType = "powershell" | "cmd" | "shell" | "python" | "nushell" | "deno";
export interface Script { export interface Script {
id?: number; id?: number;

View File

@@ -1,5 +1,6 @@
import { date } from "quasar"; import { date } from "quasar";
import { validateTimePeriod } from "@/utils/validation"; import { validateTimePeriod } from "@/utils/validation";
import trmmLogo from "@/assets/trmm_256.png";
// dropdown options formatting // dropdown options formatting
export function removeExtraOptionCategories(array) { export function removeExtraOptionCategories(array) {
@@ -24,7 +25,7 @@ function _formatOptions(
flat = false, flat = false,
allowDuplicates = true, allowDuplicates = true,
appendToOptionObject = {}, appendToOptionObject = {},
} },
) { ) {
if (!flat) if (!flat)
// returns array of options in object format [{label: label, value: 1}] // returns array of options in object format [{label: label, value: 1}]
@@ -64,6 +65,7 @@ export function formatScriptOptions(data) {
data.forEach((script) => { data.forEach((script) => {
if (script.category === cat) { if (script.category === cat) {
tmp.push({ tmp.push({
img_right: script.script_type === "builtin" ? trmmLogo : undefined,
label: script.name, label: script.name,
value: script.id, value: script.id,
timeout: script.default_timeout, timeout: script.default_timeout,
@@ -100,7 +102,7 @@ export function formatScriptOptions(data) {
export function formatAgentOptions( export function formatAgentOptions(
data, data,
flat = false, flat = false,
value_field = "agent_id" value_field = "agent_id",
) { ) {
if (flat) { if (flat) {
// returns just agent hostnames in array // returns just agent hostnames in array
@@ -185,7 +187,7 @@ export function formatSiteOptions(data, flat = false) {
label: "name", label: "name",
flat: flat, flat: flat,
appendToOptionObject: { cat: client.name }, appendToOptionObject: { cat: client.name },
}) }),
); );
}); });
@@ -361,7 +363,7 @@ export function convertToBitArray(number) {
bitArray.push(1); bitArray.push(1);
} else { } else {
bitArray.push( bitArray.push(
parseInt(binary.slice(i), 2) - parseInt(binary.slice(i + 1), 2) parseInt(binary.slice(i), 2) - parseInt(binary.slice(i + 1), 2),
); );
} }
} }

View File

@@ -53,6 +53,26 @@
:options="allTimezones" :options="allTimezones"
/> />
</q-card-section> </q-card-section>
<q-card-section>
<div>
Company name:
<q-icon
name="ion-information-circle-outline"
size="sm"
class="q-ml-sm cursor-pointer"
>
<q-tooltip class="text-caption">
Adding your company name here will append it to the user's
full name that appears when doing a remote control session,
for example: 'John Doe - Amidaware Inc.'
</q-tooltip>
</q-icon>
</div>
<q-input dense outlined v-model="companyname"> </q-input>
</q-card-section>
<q-card-actions align="center"> <q-card-actions align="center">
<q-btn <q-btn
label="Finish" label="Finish"
@@ -86,6 +106,7 @@ export default {
allTimezones: [], allTimezones: [],
timezone: null, timezone: null,
arch: "64", arch: "64",
companyname: "",
}; };
}, },
methods: { methods: {
@@ -95,6 +116,7 @@ export default {
client: this.client, client: this.client,
site: this.site, site: this.site,
timezone: this.timezone, timezone: this.timezone,
companyname: this.companyname,
initialsetup: true, initialsetup: true,
}; };
this.$axios this.$axios

View File

@@ -63,6 +63,7 @@
autofocus autofocus
outlined outlined
v-model="credentials.twofactor" v-model="credentials.twofactor"
autocomplete="one-time-code"
:rules="[ :rules="[
(val) => (val) =>
(val && val.length > 0) || 'This field is required', (val && val.length > 0) || 'This field is required',

View File

@@ -90,7 +90,7 @@ export default {
control.value = data.control; control.value = data.control;
status.value = data.status; status.value = data.status;
useMeta({ useMeta({
title: `${data.hostname} - ${data.client} - ${data.site} | Remote Background`, title: `${data.hostname} - ${data.client} - ${data.site} | Take Control`,
}); });
} catch (e) { } catch (e) {
console.error(e); console.error(e);