Compare commits

...

58 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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 1270 additions and 768 deletions

View File

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

View File

@@ -11,11 +11,11 @@ jobs:
name: Build web
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: "20.11.1"
- run: touch env-config.js
@@ -29,6 +29,6 @@ jobs:
run: tar -czvf trmm-web-${{github.ref_name}}.tar.gz dist/
- name: Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
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",
"version": "0.101.35",
"version": "0.101.42",
"private": true,
"productName": "Tactical RMM",
"scripts": {
@@ -10,34 +10,37 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
},
"dependencies": {
"@quasar/extras": "1.16.7",
"apexcharts": "3.44.0",
"axios": "1.6.0",
"dotenv": "16.3.1",
"@quasar/extras": "1.16.9",
"apexcharts": "3.48.0",
"axios": "1.6.8",
"dotenv": "16.4.5",
"pinia": "^2.1.7",
"qrcode.vue": "3.4.1",
"quasar": "2.13.0",
"vue": "3.3.8",
"vue3-apexcharts": "1.4.4",
"quasar": "2.15.1",
"vue": "3.4.21",
"vue3-apexcharts": "1.5.2",
"vuedraggable": "4.1.0",
"vue-router": "4.2.5",
"@vueuse/core": "10.5.0",
"@vueuse/shared": "10.5.0",
"monaco-editor": "0.44.0",
"vue-router": "4.3.0",
"@vueuse/core": "10.9.0",
"@vueuse/shared": "10.9.0",
"monaco-editor": "0.47.0",
"vuex": "4.1.0",
"yaml": "2.3.4"
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"yaml": "2.4.1"
},
"devDependencies": {
"@quasar/cli": "2.3.0",
"@intlify/unplugin-vue-i18n": "1.4.0",
"@quasar/app-vite": "1.6.2",
"@types/node": "20.8.10",
"@typescript-eslint/eslint-plugin": "6.10.0",
"@typescript-eslint/parser": "6.10.0",
"autoprefixer": "10.4.16",
"eslint": "8.53.0",
"eslint-config-prettier": "9.0.0",
"@quasar/cli": "2.4.0",
"@intlify/unplugin-vue-i18n": "3.0.1",
"@quasar/app-vite": "1.8.0",
"@types/node": "20.11.30",
"@typescript-eslint/eslint-plugin": "7.3.1",
"@typescript-eslint/parser": "7.3.1",
"autoprefixer": "10.4.18",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-vue": "8.7.1",
"prettier": "3.0.3",
"typescript": "5.2.2"
"prettier": "3.2.5",
"typescript": "5.4.3"
}
}

View File

@@ -36,7 +36,7 @@ module.exports = configure(function (/* ctx */) {
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
"ionicons-v4",
"mdi-v5",
"fontawesome-v6",
// '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
build: {
target: {
browser: ["es2021"],
node: "node16",
browser: ["es2022"],
node: "node20",
},
vueRouterMode: "history", // available values: 'hash', 'history'

View File

@@ -191,6 +191,11 @@ export async function agentRebootNow(agent_id) {
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 = {}) {
const { data } = await axios.post(
`${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(
'dashboard',
props.row,
props.row.overdue_dashboard_alert
props.row.overdue_dashboard_alert,
)
"
v-model="props.row.overdue_dashboard_alert"
@@ -431,8 +431,8 @@ export default {
return false;
else if (availability === "expired") {
let now = new Date();
let lastSeen = date.extractDate(row.last_seen, "MM DD YYYY HH:mm");
let diff = date.getDateDiff(now, lastSeen, "days");
let last_seen_unix = new Date(row.boot_time * 1000);
let diff = date.getDateDiff(now, last_seen_unix, "days");
if (diff < 30) return false;
}
}

View File

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

View File

@@ -176,6 +176,13 @@
</q-menu>
</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-section side>
<q-icon size="xs" name="policy" />
@@ -192,9 +199,9 @@
"
>
<q-item-section side>
<q-icon size="xs" name="integration_instructions" />
<q-icon size="xs" name="analytics" />
</q-item-section>
<q-item-section>Integrations</q-item-section>
<q-item-section>Reporting</q-item-section>
<q-item-section side>
<q-icon name="keyboard_arrow_right" />
</q-item-section>
@@ -231,6 +238,7 @@ import { fetchURLActions, runURLAction } from "@/api/core";
import {
editAgent,
agentRebootNow,
agentShutdown,
sendAgentPing,
removeAgent,
runRemoteBackground,
@@ -298,7 +306,7 @@ export default {
if (urlActions.value.length === 0) {
notifyWarning(
"No URL Actions configured. Go to Settings > Global Settings > URL Actions"
"No URL Actions configured. Go to Settings > Global Settings > URL Actions",
);
return;
}
@@ -364,7 +372,7 @@ export default {
notifySuccess(
`Maintenance mode was ${
agent.maintenance_mode ? "disabled" : "enabled"
} on ${agent.hostname}`
} on ${agent.hostname}`,
);
store.commit("setRefreshSummaryTab", true);
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) {
$q.dialog({
component: PolicyAdd,
@@ -505,7 +539,7 @@ export default {
notifySuccess(data);
refreshDashboard(
false /* clearTreeSelected */,
true /* clearSubTable */
true /* clearSubTable */,
);
} catch (e) {
console.error(e);
@@ -534,6 +568,7 @@ export default {
runChecks,
showRebootLaterModal,
rebootNow,
shutdown,
showPolicyAdd,
showAgentRecovery,
pingAgent,

View File

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

View File

@@ -254,7 +254,7 @@ export default {
pagination: {
rowsPerPage: 0,
sortBy: "name",
descending: true,
descending: false,
},
};
},
@@ -321,7 +321,7 @@ export default {
runTask(task) {
if (!task.enabled) {
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;
}

View File

@@ -1,6 +1,6 @@
<template>
<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>
{{ title.slice(0, 27) }}
<q-space />

View File

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

View File

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

View File

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

View File

@@ -71,7 +71,7 @@
icon="info"
@click="
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>
<q-separator />
<q-card-section class="row">
<div class="col-2">From:</div>
<div class="col-2">From email:</div>
<div class="col-4"></div>
<q-input
outlined
@@ -226,6 +226,16 @@
:rules="[(val) => isValidEmail(val) || 'Invalid email']"
/>
</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">
<div class="col-2">Host:</div>
<div class="col-4"></div>
@@ -379,7 +389,7 @@
<q-tab-panel name="meshcentral">
<div class="text-subtitle2">MeshCentral Settings</div>
<q-separator />
<q-card-section class="row">
<q-card-section class="row" v-if="!hosted">
<div class="col-4">Username:</div>
<div class="col-2"></div>
<q-input
@@ -395,7 +405,7 @@
]"
/>
</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-2"></div>
<q-input
@@ -405,7 +415,7 @@
class="col-6"
/>
</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-2"></div>
<q-input
@@ -415,7 +425,7 @@
class="col-6"
/>
</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-2"></div>
<q-input
@@ -425,17 +435,45 @@
class="col-6"
/>
</q-card-section>
<q-card-section class="row">
<div class="col-4">
Disable Auto Login for Remote Control and Remote background:
<q-card-section class="row" v-if="!hosted">
<div class="col-4 flex items-center">
Sync MeshCentral Users/Permissions with TRMM:
</div>
<div class="col-2"></div>
<q-checkbox
dense
v-model="settings.mesh_disable_auto_login"
v-model="settings.sync_mesh_with_trmm"
class="col-6"
/>
</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 name="customfields">
<CustomFields />
@@ -635,6 +673,11 @@ export default {
],
};
},
computed: {
hosted() {
return this.$store.state.hosted;
},
},
methods: {
openURL(url) {
openURL(url);
@@ -711,13 +754,13 @@ export default {
},
removeEmail(email) {
const removed = this.settings.email_alert_recipients.filter(
(k) => k !== email
(k) => k !== email,
);
this.settings.email_alert_recipients = removed;
},
removeSMSNumber(num) {
const removed = this.settings.sms_alert_recipients.filter(
(k) => k !== num
(k) => k !== num,
);
this.settings.sms_alert_recipients = removed;
},

View File

@@ -1,6 +1,6 @@
<template>
<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">
<template v-slot:before>
<q-tabs dense v-model="tab" vertical class="text-primary">
@@ -201,7 +201,7 @@
icon="info"
@click="
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) => {
if (r.data.length === 0) {
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;
}

View File

@@ -2,9 +2,11 @@
<q-dialog
ref="dialogRef"
maximized
no-esc-dismiss
@hide="onDialogHide"
@show="loadEditor"
@before-hide="unloadEditor"
@keydown.esc.stop="closeEditor"
>
<q-card class="q-dialog-plugin">
<q-bar>
@@ -20,7 +22,7 @@
@click="generateScriptOpenAI"
/>
<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-btn>
</q-bar>
@@ -190,7 +192,7 @@
</template>
</tactical-dropdown>
<q-space />
<q-btn dense flat label="Cancel" v-close-popup />
<q-btn dense flat label="Cancel" @click="closeEditor" />
<q-btn
v-if="!readonly"
:loading="loading"
@@ -220,6 +222,35 @@ import TestScriptModal from "@/components/scripts/TestScriptModal.vue";
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
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
import type { Script } from "@/types/scripts";
@@ -285,8 +316,8 @@ const title = computed(() => {
return props.readonly
? `Viewing ${script.name}`
: props.clone
? `Copying ${script.name}`
: `Editing ${script.name}`;
? `Copying ${script.name}`
: `Editing ${script.name}`;
} else {
return "Adding new script";
}
@@ -294,11 +325,21 @@ const title = computed(() => {
// convert highlighter language to match what ace expects
const lang = computed(() => {
if (script.shell === "cmd") return "bat";
else if (script.shell === "powershell") return "powershell";
else if (script.shell === "python") return "python";
else if (script.shell === "shell") return "shell";
else return "";
switch (script.shell) {
case "cmd":
return "bat";
case "powershell":
return "powershell";
case "python":
return "python";
case "shell":
case "nushell":
return "shell";
case "deno":
return "typescript";
default:
return "";
}
});
async function submit() {
@@ -337,12 +378,7 @@ const scriptEditor = ref<HTMLElement | null>(null);
let editor: monaco.editor.IStandaloneCodeEditor;
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,
modelUri,
);
var model = monaco.editor.createModel(script.script_body, lang.value);
const theme = $q.dark.isActive ? "vs-dark" : "vs-light";
@@ -363,7 +399,23 @@ function loadEditor() {
downloadScript(script.id, { with_snippets: props.readonly }).then((r) => {
script.script_body = 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(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
onMounted(async () => {
agentLoading.value = true;

View File

@@ -175,6 +175,28 @@
>
<q-tooltip> Shell </q-tooltip>
</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
class="q-pl-xs text-weight-bold"
@@ -463,6 +485,22 @@
>
<q-tooltip> Shell </q-tooltip>
</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>
<!-- supported platforms -->
<q-td key="supported_platforms" :props="props">
@@ -488,6 +526,12 @@
:props="props"
: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) }}
<q-tooltip
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 TacticalTable from "@/components/ui/TacticalTable.vue";
import trmmLogo from "@/assets/trmm_256.png";
// static data
const columns = [
{
@@ -620,7 +666,7 @@ export default {
// setup vuex store
const store = useStore();
const showCommunityScripts = computed(
() => store.state.showCommunityScripts
() => store.state.showCommunityScripts,
);
// setup quasar plugins
@@ -721,7 +767,7 @@ export default {
return showCommunityScripts.value
? scripts.value.filter((i) => !i.hidden)
: scripts.value.filter(
(i) => i.script_type !== "builtin" && !i.hidden
(i) => i.script_type !== "builtin" && !i.hidden,
);
}
});
@@ -884,6 +930,7 @@ export default {
loading,
showCommunityScripts,
showHiddenScripts,
trmmLogo,
// computed
visibleScripts,

View File

@@ -86,6 +86,35 @@ import { notifySuccess } from "@/utils/notify";
// ui imports
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
import type { ScriptSnippet } from "@/types/scripts";
@@ -124,11 +153,21 @@ const title = computed(() => {
// convert highlighter language to match what ace expects
const lang = computed(() => {
if (snippet.shell === "cmd") return "bat";
else if (snippet.shell === "powershell") return "powershell";
else if (snippet.shell === "python") return "python";
else if (snippet.shell === "shell") return "shell";
else return "";
switch (snippet.shell) {
case "cmd":
return "bat";
case "powershell":
return "powershell";
case "python":
return "python";
case "shell":
case "nushell":
return "shell";
case "deno":
return "typescript";
default:
return "";
}
});
async function submit() {
@@ -150,8 +189,7 @@ const snippetEditor = ref<HTMLElement | null>(null);
let editor: monaco.editor.IStandaloneCodeEditor;
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, modelUri);
var model = monaco.editor.createModel(snippet.code, lang.value);
const theme = $q.dark.isActive ? "vs-dark" : "vs-light";

View File

@@ -124,6 +124,22 @@
>
<q-tooltip> Shell </q-tooltip>
</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>
<!-- name -->
<q-td>{{ props.row.name }}</q-td>

View File

@@ -8,8 +8,25 @@
<q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-card-section class="scroll" style="max-height: 70vh; height: 70vh">
<pre v-if="ret">{{ ret }}</pre>
<q-card-section style="height: 70vh" class="scroll">
<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-card-section>
</q-card>
@@ -34,7 +51,12 @@ export default {
const { dialogRef, onDialogHide } = useDialogPluginComponent();
// main run script functionality
const ret = ref(null);
const ret = ref({
execution_time: "",
retcode: "",
stdout: "",
stderr: "",
});
const loading = ref(false);
async function runTestScript() {

View File

@@ -87,181 +87,183 @@
:done="step > 2"
:error="!isValidStep2"
>
<q-form @submit.prevent="addAction">
<div class="row q-pa-sm q-gutter-x-xs items-center">
<div class="text-subtitle2 col-12">Action Type:</div>
<q-option-group
class="col-12"
inline
v-model="actionType"
:options="[
{ label: 'Script', value: 'script' },
{ label: 'Command', value: 'cmd' },
]"
/>
<div class="scroll" style="max-height: 60vh">
<q-form @submit.prevent="addAction">
<div class="row q-pa-sm q-gutter-x-xs items-center">
<div class="text-subtitle2 col-12">Action Type:</div>
<q-option-group
class="col-12"
inline
v-model="actionType"
:options="[
{ label: 'Script', value: 'script' },
{ label: 'Command', value: 'cmd' },
]"
/>
<tactical-dropdown
v-if="actionType === 'script'"
class="col-3"
label="Select script"
v-model="script"
:options="scriptOptions"
filled
mapOptions
filterable
/>
<tactical-dropdown
v-if="actionType === 'script'"
class="col-3"
label="Select script"
v-model="script"
:options="scriptOptions"
filled
mapOptions
filterable
/>
<q-select
v-if="actionType === 'script'"
class="col-3"
dense
label="Script Arguments (press Enter after typing each argument)"
filled
v-model="defaultArgs"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-select
v-if="actionType === 'script'"
class="col-3"
dense
label="Script Arguments (press Enter after typing each argument)"
filled
v-model="defaultArgs"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-select
v-if="actionType === 'script'"
class="col-3"
dense
:label="envVarsLabel"
filled
v-model="defaultEnvVars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-select
v-if="actionType === 'script'"
class="col-3"
dense
:label="envVarsLabel"
filled
v-model="defaultEnvVars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-input
v-if="actionType === 'script'"
class="col-2"
filled
dense
v-model.number="defaultTimeout"
type="number"
label="Timeout (seconds)"
/>
<q-input
v-if="actionType === 'script'"
class="col-2"
filled
dense
v-model.number="defaultTimeout"
type="number"
label="Timeout (seconds)"
/>
<q-input
v-if="actionType === 'cmd'"
label="Command"
v-model="command"
<q-input
v-if="actionType === 'cmd'"
label="Command"
v-model="command"
dense
filled
class="col-7"
/>
<q-input
v-if="actionType === 'cmd'"
class="col-2"
filled
dense
v-model.number="defaultTimeout"
type="number"
label="Timeout (seconds)"
/>
<q-option-group
v-if="actionType === 'cmd'"
class="col-2 q-pl-sm"
inline
v-model="shell"
:options="[
{ label: 'Batch', value: 'cmd' },
{ label: 'Powershell', value: 'powershell' },
]"
/>
<q-btn
class="col-1"
type="submit"
style="width: 50px"
flat
dense
icon="add"
color="primary"
/>
</div>
</q-form>
<div class="text-subtitle2 q-pa-sm">
Actions:
<q-checkbox
class="float-right"
label="Continue on Errors"
v-model="state.continue_on_error"
dense
filled
class="col-7"
/>
<q-input
v-if="actionType === 'cmd'"
class="col-2"
filled
dense
v-model.number="defaultTimeout"
type="number"
label="Timeout (seconds)"
/>
<q-option-group
v-if="actionType === 'cmd'"
class="col-2 q-pl-sm"
inline
v-model="shell"
:options="[
{ label: 'Batch', value: 'cmd' },
{ label: 'Powershell', value: 'powershell' },
]"
/>
<q-btn
class="col-1"
type="submit"
style="width: 50px"
flat
dense
icon="add"
color="primary"
/>
>
<q-tooltip>Continue task if an action fails</q-tooltip>
</q-checkbox>
</div>
</q-form>
<div class="text-subtitle2 q-pa-sm">
Actions:
<q-checkbox
class="float-right"
label="Continue on Errors"
v-model="state.continue_on_error"
dense
>
<q-tooltip>Continue task if an action fails</q-tooltip>
</q-checkbox>
</div>
<div class="scroll q-pt-sm" style="height: 40vh; max-height: 40vh">
<draggable
class="q-list"
handle=".handle"
ghost-class="ghost"
v-model="state.actions"
item-key="index"
>
<template v-slot:item="{ index, element }">
<q-item>
<q-item-section avatar>
<q-icon
class="handle"
style="cursor: move"
name="drag_handle"
/>
</q-item-section>
<q-item-section v-if="element.type === 'script'">
<q-item-label>
<q-icon size="sm" name="description" color="primary" />
&nbsp; {{ element.name }}
</q-item-label>
<q-item-label caption>
Arguments: {{ element.script_args }}
</q-item-label>
<q-item-label caption>
Env Vars: {{ element.env_vars }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
</q-item-section>
<q-item-section v-else>
<q-item-label>
<q-icon size="sm" name="terminal" color="primary" />
&nbsp;
<div class="q-pt-sm" style="height: 150px">
<draggable
class="q-list"
handle=".handle"
ghost-class="ghost"
v-model="state.actions"
item-key="index"
>
<template v-slot:item="{ index, element }">
<q-item>
<q-item-section avatar>
<q-icon
size="sm"
:name="
element.shell === 'cmd'
? 'mdi-microsoft-windows'
: 'mdi-powershell'
"
color="primary"
class="handle"
style="cursor: move"
name="drag_handle"
/>
{{ element.command }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon
class="cursor-pointer"
color="negative"
name="close"
@click="removeAction(index)"
/>
</q-item-section>
</q-item>
</template>
</draggable>
</q-item-section>
<q-item-section v-if="element.type === 'script'">
<q-item-label>
<q-icon size="sm" name="description" color="primary" />
&nbsp; {{ element.name }}
</q-item-label>
<q-item-label caption>
Arguments: {{ element.script_args }}
</q-item-label>
<q-item-label caption>
Env Vars: {{ element.env_vars }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
</q-item-section>
<q-item-section v-else>
<q-item-label>
<q-icon size="sm" name="terminal" color="primary" />
&nbsp;
<q-icon
size="sm"
:name="
element.shell === 'cmd'
? 'mdi-microsoft-windows'
: 'mdi-powershell'
"
color="primary"
/>
{{ element.command }}
</q-item-label>
<q-item-label caption>
Timeout: {{ element.timeout }}
</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon
class="cursor-pointer"
color="negative"
name="close"
@click="removeAction(index)"
/>
</q-item-section>
</q-item>
</template>
</draggable>
</div>
</div>
</q-step>
@@ -283,7 +285,7 @@
<q-card-section
v-if="
['runonce', 'daily', 'weekly', 'monthly'].includes(
state.task_type
state.task_type,
)
"
class="row"
@@ -314,6 +316,22 @@
/>
</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 -->
<q-card-section v-if="state.task_type === 'daily'" class="row">
<!-- daily interval -->
@@ -579,7 +597,8 @@
<q-card-section
v-if="
state.task_type !== 'checkfailure' &&
state.task_type !== 'manual'
state.task_type !== 'manual' &&
state.task_type !== 'onboarding'
"
class="row"
>
@@ -617,7 +636,7 @@
(val) =>
convertPeriodToSeconds(val) >=
convertPeriodToSeconds(
state.task_repetition_interval
state.task_repetition_interval,
) ||
'Repetition duration must be greater than repetition interval',
]"
@@ -712,7 +731,7 @@
@click="
validateStep(
step === 1 ? $refs.taskGeneralForm : undefined,
$refs.stepper
$refs.stepper,
)
"
color="primary"
@@ -769,6 +788,7 @@ const taskTypeOptions = [
{ label: "Monthly", value: "monthly" },
{ label: "Run Once", value: "runonce" },
{ label: "On check failure", value: "checkfailure" },
{ label: "Onboarding", value: "onboarding" },
{ label: "Manual", value: "manual" },
];
@@ -933,7 +953,7 @@ export default {
task.value.actions.push({
type: "script",
name: scriptOptions.value.find(
(option) => option.value === script.value
(option) => option.value === script.value,
).label,
script: script.value,
timeout: defaultTimeout.value,
@@ -1019,13 +1039,13 @@ export default {
// remove milliseconds and Z to work with native date input
task.value.run_time_date = formatDateInputField(
task.value.run_time_date,
true
true,
);
if (task.value.expire_date)
task.value.expire_date = formatDateInputField(
task.value.expire_date,
true
true,
);
// set task type if monthlydow is being used
@@ -1069,7 +1089,7 @@ export default {
task.value.monthly_weeks_of_month = [];
task.value.task_instance_policy = 0;
task.value.expire_date = null;
}
},
);
// 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"
>
<q-item-section>
<q-item-label
v-html="mapOptions ? scope.opt.label : scope.opt"
></q-item-label>
<q-item-label v-html="mapOptions ? scope.opt.label : scope.opt" />
</q-item-section>
<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 v-if="filtered && mapOptions && scope.opt.cat" side>{{
scope.opt.cat
}}</q-item-section>
</q-item>
<q-item-label
v-if="scope.opt.category"
@@ -80,7 +88,7 @@ export default {
if (!props.mapOptions)
filteredOptions.value = props.options.filter(
(v) => v.toLowerCase().indexOf(needle) > -1
(v) => v.toLowerCase().indexOf(needle) > -1,
);
else
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
async function getScriptOptions(showCommunityScripts = false) {
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], () => {
if (script.value && scriptOptions.value.length > 0) {
const tmpScript = scriptOptions.value.find(
(i) => i.value === script.value
(i) => i.value === script.value,
);
defaultTimeout.value = tmpScript.timeout;
defaultArgs.value = tmpScript.args;
@@ -65,4 +65,6 @@ export const shellOptions = [
{ label: "Batch", value: "cmd" },
{ label: "Python", value: "python" },
{ 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"
:columns="columns"
:loading="isLoading"
:pagination="{ rowsPerPage: 0, sortBy: 'name', descending: true }"
:pagination="{ rowsPerPage: 0, sortBy: 'name', descending: false }"
:filter="search"
row-key="id"
binary-state-sort

View File

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

View File

@@ -1,6 +1,6 @@
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 {
id?: number;

View File

@@ -1,5 +1,6 @@
import { date } from "quasar";
import { validateTimePeriod } from "@/utils/validation";
import trmmLogo from "@/assets/trmm_256.png";
// dropdown options formatting
export function removeExtraOptionCategories(array) {
@@ -24,7 +25,7 @@ function _formatOptions(
flat = false,
allowDuplicates = true,
appendToOptionObject = {},
}
},
) {
if (!flat)
// returns array of options in object format [{label: label, value: 1}]
@@ -64,6 +65,7 @@ export function formatScriptOptions(data) {
data.forEach((script) => {
if (script.category === cat) {
tmp.push({
img_right: script.script_type === "builtin" ? trmmLogo : undefined,
label: script.name,
value: script.id,
timeout: script.default_timeout,
@@ -100,7 +102,7 @@ export function formatScriptOptions(data) {
export function formatAgentOptions(
data,
flat = false,
value_field = "agent_id"
value_field = "agent_id",
) {
if (flat) {
// returns just agent hostnames in array
@@ -185,7 +187,7 @@ export function formatSiteOptions(data, flat = false) {
label: "name",
flat: flat,
appendToOptionObject: { cat: client.name },
})
}),
);
});
@@ -361,7 +363,7 @@ export function convertToBitArray(number) {
bitArray.push(1);
} else {
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"
/>
</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-btn
label="Finish"
@@ -86,6 +106,7 @@ export default {
allTimezones: [],
timezone: null,
arch: "64",
companyname: "",
};
},
methods: {
@@ -95,6 +116,7 @@ export default {
client: this.client,
site: this.site,
timezone: this.timezone,
companyname: this.companyname,
initialsetup: true,
};
this.$axios

View File

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

View File

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