Compare commits
	
		
			28 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					cfe1cb2dbf | ||
| 
						 | 
					16fb75b56c | ||
| 
						 | 
					094cf45ce3 | ||
| 
						 | 
					d6984b3da9 | ||
| 
						 | 
					53fc6f4cde | ||
| 
						 | 
					e1dc8050e3 | ||
| 
						 | 
					49da10cf0b | ||
| 
						 | 
					a3e10910bf | ||
| 
						 | 
					3ff9edc424 | ||
| 
						 | 
					69414d4083 | ||
| 
						 | 
					e06b7a7775 | ||
| 
						 | 
					c006e4d922 | ||
| 
						 | 
					df6fe0863b | ||
| 
						 | 
					d55a29911c | ||
| 
						 | 
					d0e49d27fd | ||
| 
						 | 
					1299bfc93e | ||
| 
						 | 
					be999646d4 | ||
| 
						 | 
					e57d32f122 | ||
| 
						 | 
					3e6365574e | ||
| 
						 | 
					08fa8da735 | ||
| 
						 | 
					4ab31a529e | ||
| 
						 | 
					466725d5c2 | ||
| 
						 | 
					5114ff40aa | ||
| 
						 | 
					908b337797 | ||
| 
						 | 
					fea5258903 | ||
| 
						 | 
					5521e4ea3e | ||
| 
						 | 
					6cc01596cb | ||
| 
						 | 
					0694538482 | 
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										768
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										768
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										28
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "web",
 | 
			
		||||
  "version": "0.101.34",
 | 
			
		||||
  "version": "0.101.38",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "productName": "Tactical RMM",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@@ -10,34 +10,34 @@
 | 
			
		||||
    "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@quasar/extras": "1.16.7",
 | 
			
		||||
    "@quasar/extras": "1.16.8",
 | 
			
		||||
    "apexcharts": "3.44.0",
 | 
			
		||||
    "axios": "1.6.0",
 | 
			
		||||
    "axios": "1.6.2",
 | 
			
		||||
    "dotenv": "16.3.1",
 | 
			
		||||
    "qrcode.vue": "3.4.1",
 | 
			
		||||
    "quasar": "2.13.0",
 | 
			
		||||
    "vue": "3.3.7",
 | 
			
		||||
    "vue": "3.3.9",
 | 
			
		||||
    "vue3-apexcharts": "1.4.4",
 | 
			
		||||
    "vuedraggable": "4.1.0",
 | 
			
		||||
    "vue-router": "4.2.5",
 | 
			
		||||
    "@vueuse/core": "10.5.0",
 | 
			
		||||
    "@vueuse/shared": "10.5.0",
 | 
			
		||||
    "@vueuse/core": "10.6.1",
 | 
			
		||||
    "@vueuse/shared": "10.6.1",
 | 
			
		||||
    "monaco-editor": "0.44.0",
 | 
			
		||||
    "vuex": "4.1.0",
 | 
			
		||||
    "yaml": "2.3.3"
 | 
			
		||||
    "yaml": "2.3.4"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@quasar/cli": "2.3.0",
 | 
			
		||||
    "@intlify/unplugin-vue-i18n": "1.4.0",
 | 
			
		||||
    "@intlify/unplugin-vue-i18n": "1.5.0",
 | 
			
		||||
    "@quasar/app-vite": "1.6.2",
 | 
			
		||||
    "@types/node": "20.8.9",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "6.9.0",
 | 
			
		||||
    "@typescript-eslint/parser": "6.9.0",
 | 
			
		||||
    "@types/node": "20.10.0",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "6.13.1",
 | 
			
		||||
    "@typescript-eslint/parser": "6.13.1",
 | 
			
		||||
    "autoprefixer": "10.4.16",
 | 
			
		||||
    "eslint": "8.52.0",
 | 
			
		||||
    "eslint": "8.54.0",
 | 
			
		||||
    "eslint-config-prettier": "9.0.0",
 | 
			
		||||
    "eslint-plugin-vue": "8.7.1",
 | 
			
		||||
    "prettier": "3.0.3",
 | 
			
		||||
    "typescript": "5.2.2"
 | 
			
		||||
    "prettier": "3.1.0",
 | 
			
		||||
    "typescript": "5.3.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/trmm_256.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/trmm_256.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 15 KiB  | 
@@ -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"
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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">
 | 
			
		||||
@@ -311,7 +311,7 @@ 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 &&
 | 
			
		||||
@@ -381,6 +381,7 @@ export default {
 | 
			
		||||
      dash_negative_color,
 | 
			
		||||
      serial_number,
 | 
			
		||||
      cpu,
 | 
			
		||||
      store,
 | 
			
		||||
 | 
			
		||||
      // methods
 | 
			
		||||
      getSummary,
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -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 />
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,14 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <q-dialog
 | 
			
		||||
    ref="dialogRef"
 | 
			
		||||
    persistent
 | 
			
		||||
    @keydown.esc.stop="onDialogHide"
 | 
			
		||||
    :maximized="maximized"
 | 
			
		||||
    maximized
 | 
			
		||||
    no-esc-dismiss
 | 
			
		||||
    @hide="onDialogHide"
 | 
			
		||||
    @show="loadEditor"
 | 
			
		||||
    @before-hide="unloadEditor"
 | 
			
		||||
    @keydown.esc.stop="closeEditor"
 | 
			
		||||
  >
 | 
			
		||||
    <q-card
 | 
			
		||||
      class="q-dialog-plugin"
 | 
			
		||||
      :style="maximized ? '' : 'width: 90vw; max-width: 90vw'"
 | 
			
		||||
    >
 | 
			
		||||
    <q-card class="q-dialog-plugin">
 | 
			
		||||
      <q-bar>
 | 
			
		||||
        <span class="q-pr-sm">{{ title }}</span>
 | 
			
		||||
        <q-btn
 | 
			
		||||
@@ -25,29 +22,7 @@
 | 
			
		||||
          @click="generateScriptOpenAI"
 | 
			
		||||
        />
 | 
			
		||||
        <q-space />
 | 
			
		||||
        <q-btn
 | 
			
		||||
          dense
 | 
			
		||||
          flat
 | 
			
		||||
          icon="minimize"
 | 
			
		||||
          @click="maximized = false"
 | 
			
		||||
          :disable="!maximized"
 | 
			
		||||
        >
 | 
			
		||||
          <q-tooltip v-if="maximized" class="bg-white text-primary"
 | 
			
		||||
            >Minimize</q-tooltip
 | 
			
		||||
          >
 | 
			
		||||
        </q-btn>
 | 
			
		||||
        <q-btn
 | 
			
		||||
          dense
 | 
			
		||||
          flat
 | 
			
		||||
          icon="crop_square"
 | 
			
		||||
          @click="maximized = true"
 | 
			
		||||
          :disable="maximized"
 | 
			
		||||
        >
 | 
			
		||||
          <q-tooltip v-if="!maximized" class="bg-white text-primary"
 | 
			
		||||
            >Maximize</q-tooltip
 | 
			
		||||
          >
 | 
			
		||||
        </q-btn>
 | 
			
		||||
        <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>
 | 
			
		||||
@@ -78,7 +53,7 @@
 | 
			
		||||
            opacity: '0.2',
 | 
			
		||||
          }"
 | 
			
		||||
          class="col-4 q-mb-none q-pb-none"
 | 
			
		||||
          :style="{ height: `${maximized ? '82vh' : '64vh'}` }"
 | 
			
		||||
          :style="{ height: `${$q.screen.height - 106}px` }"
 | 
			
		||||
        >
 | 
			
		||||
          <div class="q-gutter-sm q-pr-sm">
 | 
			
		||||
            <q-input
 | 
			
		||||
@@ -187,7 +162,7 @@
 | 
			
		||||
        <div
 | 
			
		||||
          ref="scriptEditor"
 | 
			
		||||
          class="col-8 q-mb-none q-pb-none"
 | 
			
		||||
          :style="{ height: `${maximized ? '82vh' : '64vh'}` }"
 | 
			
		||||
          :style="{ height: `${$q.screen.height - 106}px` }"
 | 
			
		||||
        ></div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <q-card-actions>
 | 
			
		||||
@@ -217,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"
 | 
			
		||||
@@ -296,7 +271,6 @@ const script: Script = props.script
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
if (props.clone) script.name = `(Copy) ${script.name}`;
 | 
			
		||||
const maximized = ref(false);
 | 
			
		||||
const loading = ref(false);
 | 
			
		||||
const agentLoading = ref(false);
 | 
			
		||||
 | 
			
		||||
@@ -391,7 +365,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, () => {
 | 
			
		||||
@@ -422,6 +412,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -176,6 +176,14 @@
 | 
			
		||||
                <q-tooltip> Shell </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"
 | 
			
		||||
                :style="{ color: props.node.hidden ? 'grey' : '' }"
 | 
			
		||||
@@ -488,6 +496,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 +564,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 +636,7 @@ export default {
 | 
			
		||||
    // setup vuex store
 | 
			
		||||
    const store = useStore();
 | 
			
		||||
    const showCommunityScripts = computed(
 | 
			
		||||
      () => store.state.showCommunityScripts
 | 
			
		||||
      () => store.state.showCommunityScripts,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // setup quasar plugins
 | 
			
		||||
@@ -721,7 +737,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 +900,7 @@ export default {
 | 
			
		||||
      loading,
 | 
			
		||||
      showCommunityScripts,
 | 
			
		||||
      showHiddenScripts,
 | 
			
		||||
      trmmLogo,
 | 
			
		||||
 | 
			
		||||
      // computed
 | 
			
		||||
      visibleScripts,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,12 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <q-dialog
 | 
			
		||||
    ref="dialogRef"
 | 
			
		||||
    persistent
 | 
			
		||||
    @keydown.esc.stop="onDialogHide"
 | 
			
		||||
    :maximized="maximized"
 | 
			
		||||
    maximized
 | 
			
		||||
    @hide="onDialogHide"
 | 
			
		||||
    @show="loadEditor"
 | 
			
		||||
    @before-hide="unloadEditor"
 | 
			
		||||
  >
 | 
			
		||||
    <q-card
 | 
			
		||||
      class="q-dialog-plugin"
 | 
			
		||||
      :style="maximized ? '' : 'width: 70vw; max-width: 90vw'"
 | 
			
		||||
    >
 | 
			
		||||
    <q-card class="q-dialog-plugin">
 | 
			
		||||
      <q-bar>
 | 
			
		||||
        <span class="q-pr-sm">{{ title }}</span>
 | 
			
		||||
        <q-btn
 | 
			
		||||
@@ -25,35 +20,13 @@
 | 
			
		||||
          @click="generateScriptOpenAI"
 | 
			
		||||
        />
 | 
			
		||||
        <q-space />
 | 
			
		||||
        <q-btn
 | 
			
		||||
          dense
 | 
			
		||||
          flat
 | 
			
		||||
          icon="minimize"
 | 
			
		||||
          @click="maximized = false"
 | 
			
		||||
          :disable="!maximized"
 | 
			
		||||
        >
 | 
			
		||||
          <q-tooltip v-if="maximized" class="bg-white text-primary"
 | 
			
		||||
            >Minimize</q-tooltip
 | 
			
		||||
          >
 | 
			
		||||
        </q-btn>
 | 
			
		||||
        <q-btn
 | 
			
		||||
          dense
 | 
			
		||||
          flat
 | 
			
		||||
          icon="crop_square"
 | 
			
		||||
          @click="maximized = true"
 | 
			
		||||
          :disable="maximized"
 | 
			
		||||
        >
 | 
			
		||||
          <q-tooltip v-if="!maximized" class="bg-white text-primary"
 | 
			
		||||
            >Maximize</q-tooltip
 | 
			
		||||
          >
 | 
			
		||||
        </q-btn>
 | 
			
		||||
        <q-btn dense flat icon="close" v-close-popup>
 | 
			
		||||
          <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | 
			
		||||
        </q-btn>
 | 
			
		||||
      </q-bar>
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <q-input
 | 
			
		||||
          :rules="[(val) => !!val || '*Required']"
 | 
			
		||||
          :rules="[(val: string) => !!val || '*Required']"
 | 
			
		||||
          class="q-pa-sm col-4"
 | 
			
		||||
          v-model="snippet.name"
 | 
			
		||||
          label="Name"
 | 
			
		||||
@@ -82,7 +55,7 @@
 | 
			
		||||
 | 
			
		||||
      <div
 | 
			
		||||
        ref="snippetEditor"
 | 
			
		||||
        :style="{ height: `${maximized ? '82vh' : '64vh'}` }"
 | 
			
		||||
        :style="{ height: `${$q.screen.height - 132}px` }"
 | 
			
		||||
      ></div>
 | 
			
		||||
 | 
			
		||||
      <q-card-actions align="right">
 | 
			
		||||
@@ -139,7 +112,6 @@ const openAIEnabled = computed(() => store.state.openAIIntegrationEnabled);
 | 
			
		||||
const snippet: ScriptSnippet = props.snippet
 | 
			
		||||
  ? reactive(Object.assign({}, props.snippet))
 | 
			
		||||
  : reactive({ name: "", code: "", shell: "powershell" });
 | 
			
		||||
const maximized = ref(false);
 | 
			
		||||
const loading = ref(false);
 | 
			
		||||
 | 
			
		||||
const title = computed(() => {
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@
 | 
			
		||||
          :done="step > 2"
 | 
			
		||||
          :error="!isValidStep2"
 | 
			
		||||
        >
 | 
			
		||||
          <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>
 | 
			
		||||
@@ -200,7 +201,7 @@
 | 
			
		||||
                <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">
 | 
			
		||||
            <div class="q-pt-sm" style="height: 150px">
 | 
			
		||||
              <draggable
 | 
			
		||||
                class="q-list"
 | 
			
		||||
                handle=".handle"
 | 
			
		||||
@@ -263,6 +264,7 @@
 | 
			
		||||
                </template>
 | 
			
		||||
              </draggable>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </q-step>
 | 
			
		||||
 | 
			
		||||
        <q-step :name="3" title="Choose Schedule" :error="!isValidStep3">
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ For details, see: https://license.tacticalrmm.com/ee
 | 
			
		||||
          class="q-pr-sm"
 | 
			
		||||
          filled
 | 
			
		||||
          dense
 | 
			
		||||
          style="width: 250px"
 | 
			
		||||
          style="width: 425px"
 | 
			
		||||
          :error="!isNameValid"
 | 
			
		||||
          hide-bottom-space
 | 
			
		||||
        />
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user