Compare commits
	
		
			24 Commits
		
	
	
		
			v0.100.0-d
			...
			v0.100.6-d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					0a4b00298d | ||
| 
						 | 
					1eaed284a3 | ||
| 
						 | 
					b278e0bed4 | ||
| 
						 | 
					6ee3df7e4e | ||
| 
						 | 
					7ee87da3b6 | ||
| 
						 | 
					7bce958633 | ||
| 
						 | 
					57963f6d1a | ||
| 
						 | 
					c9d76bdddc | ||
| 
						 | 
					c279a44679 | ||
| 
						 | 
					974ba53926 | ||
| 
						 | 
					021fbbe14f | ||
| 
						 | 
					bbd74c34b7 | ||
| 
						 | 
					dfef0a5b4b | ||
| 
						 | 
					ee687bf559 | ||
| 
						 | 
					627d0e91f1 | ||
| 
						 | 
					bffaba1f60 | ||
| 
						 | 
					fdf28539cb | ||
| 
						 | 
					ac1246c81c | ||
| 
						 | 
					4feed0c65c | ||
| 
						 | 
					197f2f237b | ||
| 
						 | 
					0dc0d010bd | ||
| 
						 | 
					b17aff8c6f | ||
| 
						 | 
					63147ce116 | ||
| 
						 | 
					ba9f93962a | 
@@ -3,3 +3,4 @@ DEV_URL = "https://api.example.com"
 | 
			
		||||
APP_URL = "https://app.example.com"
 | 
			
		||||
DEV_HOST = 0.0.0.0
 | 
			
		||||
DEV_PORT = 80
 | 
			
		||||
USE_HTTPS = false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1814
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1814
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										50
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "web",
 | 
			
		||||
  "version": "0.100.0-dev",
 | 
			
		||||
  "version": "0.100.6-dev",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "productName": "Tactical RMM",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
@@ -10,47 +10,31 @@
 | 
			
		||||
    "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@quasar/extras": "1.14.0",
 | 
			
		||||
    "apexcharts": "3.35.2",
 | 
			
		||||
    "@quasar/extras": "1.15.0",
 | 
			
		||||
    "apexcharts": "3.35.4",
 | 
			
		||||
    "axios": "0.27.2",
 | 
			
		||||
    "dotenv": "16.0.0",
 | 
			
		||||
    "dotenv": "16.0.1",
 | 
			
		||||
    "qrcode.vue": "3.3.3",
 | 
			
		||||
    "quasar": "2.7.1",
 | 
			
		||||
    "vue": "3.2.31",
 | 
			
		||||
    "quasar": "2.7.5",
 | 
			
		||||
    "vue": "3.2.37",
 | 
			
		||||
    "vue3-ace-editor": "2.2.2",
 | 
			
		||||
    "vue3-apexcharts": "1.4.1",
 | 
			
		||||
    "vuedraggable": "4.1.0",
 | 
			
		||||
    "vue-router": "4.0.15",
 | 
			
		||||
    "vue-router": "4.1.2",
 | 
			
		||||
    "vuex": "4.0.2"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@quasar/cli": "^1.3.2",
 | 
			
		||||
    "@intlify/vite-plugin-vue-i18n": "^3.3.1",
 | 
			
		||||
    "@quasar/app-vite": "^1.0.1",
 | 
			
		||||
    "@types/node": "^12.20.21",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^5.10.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^5.10.0",
 | 
			
		||||
    "autoprefixer": "^10.4.2",
 | 
			
		||||
    "eslint": "^8.10.0",
 | 
			
		||||
    "eslint-config-prettier": "^8.1.0",
 | 
			
		||||
    "@intlify/vite-plugin-vue-i18n": "^5.0.1",
 | 
			
		||||
    "@quasar/app-vite": "^1.0.5",
 | 
			
		||||
    "@types/node": "^18.6.1",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^5.30.5",
 | 
			
		||||
    "@typescript-eslint/parser": "^5.30.5",
 | 
			
		||||
    "autoprefixer": "^10.4.7",
 | 
			
		||||
    "eslint": "^8.20.0",
 | 
			
		||||
    "eslint-config-prettier": "^8.5.0",
 | 
			
		||||
    "eslint-plugin-vue": "^8.5.0",
 | 
			
		||||
    "prettier": "^2.5.1",
 | 
			
		||||
    "typescript": "^4.6.4"
 | 
			
		||||
  },
 | 
			
		||||
  "browserslist": [
 | 
			
		||||
    "last 3 Chrome versions",
 | 
			
		||||
    "last 3 Firefox versions",
 | 
			
		||||
    "last 3 Edge versions",
 | 
			
		||||
    "last 2 Safari versions",
 | 
			
		||||
    "last 3 Android versions",
 | 
			
		||||
    "last 3 ChromeAndroid versions",
 | 
			
		||||
    "last 3 FirefoxAndroid versions",
 | 
			
		||||
    "last 2 iOS versions",
 | 
			
		||||
    "last 3 Opera versions"
 | 
			
		||||
  ],
 | 
			
		||||
  "engines": {
 | 
			
		||||
    "node": ">= 12.22.1",
 | 
			
		||||
    "npm": ">= 6.13.4",
 | 
			
		||||
    "yarn": ">= 1.21.1"
 | 
			
		||||
    "prettier": "^2.7.1",
 | 
			
		||||
    "typescript": "^4.7.4"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ module.exports = configure(function (/* ctx */) {
 | 
			
		||||
    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
 | 
			
		||||
    build: {
 | 
			
		||||
      target: {
 | 
			
		||||
        browser: ["es2019", "edge88", "firefox78", "chrome87", "safari13.1"],
 | 
			
		||||
        browser: ["es2021"],
 | 
			
		||||
        node: "node16",
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
@@ -86,7 +86,7 @@ module.exports = configure(function (/* ctx */) {
 | 
			
		||||
 | 
			
		||||
    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
 | 
			
		||||
    devServer: {
 | 
			
		||||
      https: false,
 | 
			
		||||
      https: process.env.USE_HTTPS === "true",
 | 
			
		||||
      open: false, // opens browser window automatically
 | 
			
		||||
      host: process.env.DEV_HOST,
 | 
			
		||||
      port: process.env.DEV_PORT,
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,17 @@ export default function ({ app, router, store }) {
 | 
			
		||||
      return response;
 | 
			
		||||
    },
 | 
			
		||||
    async function (error) {
 | 
			
		||||
      if (error.code && error.code === "ERR_NETWORK") {
 | 
			
		||||
        Notify.create({
 | 
			
		||||
          color: "negative",
 | 
			
		||||
          message: "Backend is offline (network error)",
 | 
			
		||||
          caption:
 | 
			
		||||
            "Open your browser's dev tools and check the console tab for more detailed error messages",
 | 
			
		||||
          timeout: 5000,
 | 
			
		||||
        });
 | 
			
		||||
        return Promise.reject({ ...error });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      let text;
 | 
			
		||||
 | 
			
		||||
      if (!error.response) {
 | 
			
		||||
 
 | 
			
		||||
@@ -142,6 +142,10 @@
 | 
			
		||||
              <q-item clickable v-close-popup @click="clearCache">
 | 
			
		||||
                <q-item-section>Clear Cache</q-item-section>
 | 
			
		||||
              </q-item>
 | 
			
		||||
              <!-- bulk recover agents -->
 | 
			
		||||
              <q-item clickable v-close-popup @click="bulkRecoverAgents">
 | 
			
		||||
                <q-item-section>Recover All Agents</q-item-section>
 | 
			
		||||
              </q-item>
 | 
			
		||||
            </q-list>
 | 
			
		||||
          </q-menu>
 | 
			
		||||
        </q-btn>
 | 
			
		||||
@@ -262,6 +266,20 @@ export default {
 | 
			
		||||
        .get("/core/clearcache/")
 | 
			
		||||
        .then((r) => this.notifySuccess(r.data));
 | 
			
		||||
    },
 | 
			
		||||
    bulkRecoverAgents() {
 | 
			
		||||
      this.$q
 | 
			
		||||
        .dialog({
 | 
			
		||||
          title: "Bulk Recover All Agents?",
 | 
			
		||||
          message:
 | 
			
		||||
            "This will restart the Tactical and Mesh Agent services on all agents",
 | 
			
		||||
          cancel: true,
 | 
			
		||||
        })
 | 
			
		||||
        .onOk(() => {
 | 
			
		||||
          this.$axios
 | 
			
		||||
            .get("/agents/bulkrecovery/")
 | 
			
		||||
            .then((r) => this.notifySuccess(r.data));
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    openHelp(mode) {
 | 
			
		||||
      let url;
 | 
			
		||||
      switch (mode) {
 | 
			
		||||
 
 | 
			
		||||
@@ -61,10 +61,7 @@
 | 
			
		||||
            <q-td key="client" :props="props">{{ props.row.client_name }}</q-td>
 | 
			
		||||
            <q-td key="site" :props="props">{{ props.row.site_name }}</q-td>
 | 
			
		||||
            <q-td key="mon_type" :props="props">{{ props.row.mon_type }}</q-td>
 | 
			
		||||
            <q-td key="arch" :props="props"
 | 
			
		||||
              ><span v-if="props.row.arch === '64'">64 bit</span
 | 
			
		||||
              ><span v-else>32 bit</span></q-td
 | 
			
		||||
            >
 | 
			
		||||
            <q-td key="goarch" :props="props">{{ props.row.goarch }}</q-td>
 | 
			
		||||
            <q-td key="expiry" :props="props">{{
 | 
			
		||||
              formatDate(props.row.expiry)
 | 
			
		||||
            }}</q-td>
 | 
			
		||||
@@ -130,7 +127,13 @@ const columns = [
 | 
			
		||||
    align: "left",
 | 
			
		||||
    sortable: true,
 | 
			
		||||
  },
 | 
			
		||||
  { name: "arch", label: "Arch", field: "arch", align: "left", sortable: true },
 | 
			
		||||
  {
 | 
			
		||||
    name: "goarch",
 | 
			
		||||
    label: "Arch",
 | 
			
		||||
    field: "goarch",
 | 
			
		||||
    align: "left",
 | 
			
		||||
    sortable: true,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: "expiry",
 | 
			
		||||
    label: "Expiry",
 | 
			
		||||
 
 | 
			
		||||
@@ -54,9 +54,9 @@
 | 
			
		||||
        />
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
      <q-card-section>
 | 
			
		||||
        <div class="q-pl-sm">OS</div>
 | 
			
		||||
        <q-radio v-model="state.arch" val="64" label="64 bit" />
 | 
			
		||||
        <q-radio v-model="state.arch" val="32" label="32 bit" />
 | 
			
		||||
        <div class="q-pl-sm">Arch</div>
 | 
			
		||||
        <q-radio v-model="state.goarch" :val="GOARCH_AMD64" label="64 bit" />
 | 
			
		||||
        <q-radio v-model="state.goarch" :val="GOARCH_i386" label="32 bit" />
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
      <q-card-actions align="right">
 | 
			
		||||
        <q-btn dense flat label="Cancel" v-close-popup />
 | 
			
		||||
@@ -84,6 +84,7 @@ import {
 | 
			
		||||
  formatDateInputField,
 | 
			
		||||
  formatDateStringwithTimezone,
 | 
			
		||||
} from "@/utils/format";
 | 
			
		||||
import { GOARCH_AMD64, GOARCH_i386 } from "@/constants/constants";
 | 
			
		||||
 | 
			
		||||
// ui imports
 | 
			
		||||
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
 | 
			
		||||
@@ -108,7 +109,7 @@ export default {
 | 
			
		||||
      power: false,
 | 
			
		||||
      rdp: false,
 | 
			
		||||
      ping: false,
 | 
			
		||||
      arch: "64",
 | 
			
		||||
      goarch: GOARCH_AMD64,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const loading = ref(false);
 | 
			
		||||
@@ -145,6 +146,10 @@ export default {
 | 
			
		||||
      // quasar dialog
 | 
			
		||||
      dialogRef,
 | 
			
		||||
      onDialogHide,
 | 
			
		||||
 | 
			
		||||
      // constants
 | 
			
		||||
      GOARCH_AMD64,
 | 
			
		||||
      GOARCH_i386,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@
 | 
			
		||||
              label="Windows"
 | 
			
		||||
              @update:model-value="
 | 
			
		||||
                installMethod = 'exe';
 | 
			
		||||
                arch = '64';
 | 
			
		||||
                goarch = GOARCH_AMD64;
 | 
			
		||||
              "
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
@@ -48,8 +48,8 @@
 | 
			
		||||
              val="linux"
 | 
			
		||||
              label="Linux"
 | 
			
		||||
              @update:model-value="
 | 
			
		||||
                installMethod = 'linux';
 | 
			
		||||
                arch = 'amd64';
 | 
			
		||||
                installMethod = 'bash';
 | 
			
		||||
                goarch = GOARCH_AMD64;
 | 
			
		||||
              "
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
@@ -102,38 +102,38 @@
 | 
			
		||||
          Arch
 | 
			
		||||
          <div class="q-gutter-sm">
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-model="arch"
 | 
			
		||||
              val="64"
 | 
			
		||||
              v-model="goarch"
 | 
			
		||||
              :val="GOARCH_AMD64"
 | 
			
		||||
              label="64 bit"
 | 
			
		||||
              v-show="agentOS === 'windows'"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-model="arch"
 | 
			
		||||
              val="32"
 | 
			
		||||
              v-model="goarch"
 | 
			
		||||
              :val="GOARCH_i386"
 | 
			
		||||
              label="32 bit"
 | 
			
		||||
              v-show="agentOS === 'windows'"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-model="arch"
 | 
			
		||||
              val="amd64"
 | 
			
		||||
              v-model="goarch"
 | 
			
		||||
              :val="GOARCH_AMD64"
 | 
			
		||||
              label="64 bit"
 | 
			
		||||
              v-show="agentOS !== 'windows'"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-model="arch"
 | 
			
		||||
              val="386"
 | 
			
		||||
              v-model="goarch"
 | 
			
		||||
              :val="GOARCH_i386"
 | 
			
		||||
              label="32 bit"
 | 
			
		||||
              v-show="agentOS !== 'windows'"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-model="arch"
 | 
			
		||||
              val="arm64"
 | 
			
		||||
              v-model="goarch"
 | 
			
		||||
              :val="GOARCH_ARM64"
 | 
			
		||||
              label="ARM 64 bit"
 | 
			
		||||
              v-show="agentOS !== 'windows'"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-model="arch"
 | 
			
		||||
              val="arm"
 | 
			
		||||
              v-model="goarch"
 | 
			
		||||
              :val="GOARCH_ARM32"
 | 
			
		||||
              label="ARM 32 bit (Rasp Pi)"
 | 
			
		||||
              v-show="agentOS !== 'windows'"
 | 
			
		||||
            />
 | 
			
		||||
@@ -177,6 +177,12 @@
 | 
			
		||||
import mixins from "@/mixins/mixins";
 | 
			
		||||
import AgentDownload from "@/components/modals/agents/AgentDownload.vue";
 | 
			
		||||
import { getBaseUrl } from "@/boot/axios";
 | 
			
		||||
import {
 | 
			
		||||
  GOARCH_AMD64,
 | 
			
		||||
  GOARCH_i386,
 | 
			
		||||
  GOARCH_ARM64,
 | 
			
		||||
  GOARCH_ARM32,
 | 
			
		||||
} from "@/constants/constants";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "InstallAgent",
 | 
			
		||||
@@ -187,6 +193,10 @@ export default {
 | 
			
		||||
  },
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      GOARCH_AMD64: GOARCH_AMD64,
 | 
			
		||||
      GOARCH_i386: GOARCH_i386,
 | 
			
		||||
      GOARCH_ARM64: GOARCH_ARM64,
 | 
			
		||||
      GOARCH_ARM32: GOARCH_ARM32,
 | 
			
		||||
      client_options: [],
 | 
			
		||||
      client: null,
 | 
			
		||||
      site: null,
 | 
			
		||||
@@ -198,7 +208,7 @@ export default {
 | 
			
		||||
      showAgentDownload: false,
 | 
			
		||||
      info: {},
 | 
			
		||||
      installMethod: "exe",
 | 
			
		||||
      arch: "64",
 | 
			
		||||
      goarch: GOARCH_AMD64,
 | 
			
		||||
      agentOS: "windows",
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
@@ -239,10 +249,7 @@ export default {
 | 
			
		||||
        .toLowerCase()
 | 
			
		||||
        .replace(/([^a-zA-Z0-9]+)/g, "");
 | 
			
		||||
 | 
			
		||||
      const fileName =
 | 
			
		||||
        this.arch === "64"
 | 
			
		||||
          ? `rmm-${clientStripped}-${siteStripped}-${this.agenttype}.exe`
 | 
			
		||||
          : `rmm-${clientStripped}-${siteStripped}-${this.agenttype}-x86.exe`;
 | 
			
		||||
      const fileName = `trmm-${clientStripped}-${siteStripped}-${this.agenttype}-${this.goarch}.exe`;
 | 
			
		||||
 | 
			
		||||
      const data = {
 | 
			
		||||
        installMethod: this.installMethod,
 | 
			
		||||
@@ -253,10 +260,10 @@ export default {
 | 
			
		||||
        power: this.power ? 1 : 0,
 | 
			
		||||
        rdp: this.rdp ? 1 : 0,
 | 
			
		||||
        ping: this.ping ? 1 : 0,
 | 
			
		||||
        arch: this.arch,
 | 
			
		||||
        goarch: this.goarch,
 | 
			
		||||
        api,
 | 
			
		||||
        fileName,
 | 
			
		||||
        os: this.agentOS,
 | 
			
		||||
        plat: this.agentOS,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      if (this.installMethod === "manual") {
 | 
			
		||||
@@ -264,7 +271,7 @@ export default {
 | 
			
		||||
          this.info = {
 | 
			
		||||
            expires: this.expires,
 | 
			
		||||
            data: r.data,
 | 
			
		||||
            arch: this.arch,
 | 
			
		||||
            goarch: this.goarch,
 | 
			
		||||
          };
 | 
			
		||||
          this.showAgentDownload = true;
 | 
			
		||||
        });
 | 
			
		||||
@@ -289,7 +296,7 @@ export default {
 | 
			
		||||
          });
 | 
			
		||||
      } else if (
 | 
			
		||||
        this.installMethod === "powershell" ||
 | 
			
		||||
        this.installMethod === "linux"
 | 
			
		||||
        this.installMethod === "bash"
 | 
			
		||||
      ) {
 | 
			
		||||
        this.$q.loading.show();
 | 
			
		||||
        let ext = this.installMethod === "powershell" ? "ps1" : "sh";
 | 
			
		||||
@@ -333,7 +340,7 @@ export default {
 | 
			
		||||
        case "manual":
 | 
			
		||||
          text = "Show manual installation instructions";
 | 
			
		||||
          break;
 | 
			
		||||
        case "linux":
 | 
			
		||||
        case "bash":
 | 
			
		||||
          text = "Download linux install script";
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -129,37 +129,37 @@
 | 
			
		||||
      <div class="q-gutter-sm">
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="1"
 | 
			
		||||
          :val="0"
 | 
			
		||||
          label="Monday"
 | 
			
		||||
        />
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="2"
 | 
			
		||||
          :val="1"
 | 
			
		||||
          label="Tuesday"
 | 
			
		||||
        />
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="3"
 | 
			
		||||
          :val="2"
 | 
			
		||||
          label="Wednesday"
 | 
			
		||||
        />
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="4"
 | 
			
		||||
          :val="3"
 | 
			
		||||
          label="Thursday"
 | 
			
		||||
        />
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="5"
 | 
			
		||||
          :val="4"
 | 
			
		||||
          label="Friday"
 | 
			
		||||
        />
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="6"
 | 
			
		||||
          :val="5"
 | 
			
		||||
          label="Saturday"
 | 
			
		||||
        />
 | 
			
		||||
        <q-checkbox
 | 
			
		||||
          v-model="winupdatepolicy.run_time_days"
 | 
			
		||||
          :val="0"
 | 
			
		||||
          :val="6"
 | 
			
		||||
          label="Sunday"
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -63,11 +63,14 @@ export default {
 | 
			
		||||
      loading.value = true;
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await scheduleAgentReboot(props.agent.agent_id, state.value);
 | 
			
		||||
        const ret = await scheduleAgentReboot(
 | 
			
		||||
          props.agent.agent_id,
 | 
			
		||||
          state.value
 | 
			
		||||
        );
 | 
			
		||||
        $q.dialog({
 | 
			
		||||
          title: "Reboot pending",
 | 
			
		||||
          style: "width: 40vw",
 | 
			
		||||
          message: `A reboot has been scheduled for <strong>${state.value.datetime}</strong> on ${props.agent.hostname}.
 | 
			
		||||
          message: `A reboot has been scheduled for <strong>${ret.time}</strong> on ${props.agent.hostname}.
 | 
			
		||||
            <br />It can be cancelled from the Pending Actions menu until the scheduled time.`,
 | 
			
		||||
          html: true,
 | 
			
		||||
        }).onDismiss(onDialogOK);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,11 +8,12 @@
 | 
			
		||||
      </q-btn>
 | 
			
		||||
    </q-bar>
 | 
			
		||||
    <q-separator />
 | 
			
		||||
    <q-banner class="bg-warning">
 | 
			
		||||
    <q-banner class="bg-info">
 | 
			
		||||
      <template v-slot:avatar>
 | 
			
		||||
        <q-icon name="info" />
 | 
			
		||||
      </template>
 | 
			
		||||
      Agents will now automatically self update, this tool is no longer needed.
 | 
			
		||||
      Agents will automatically self update at 35 min past the hour, every hour.
 | 
			
		||||
      Use this tool to manually trigger an agent update cycle.
 | 
			
		||||
    </q-banner>
 | 
			
		||||
    <q-card-section>
 | 
			
		||||
      Select Version
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										211
									
								
								src/components/modals/agents/WebsocketSendCommand.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/components/modals/agents/WebsocketSendCommand.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <q-dialog
 | 
			
		||||
    ref="dialogRef"
 | 
			
		||||
    @hide="onDialogHide"
 | 
			
		||||
    persistent
 | 
			
		||||
    @keydown.esc="onDialogHide"
 | 
			
		||||
  >
 | 
			
		||||
    <q-card
 | 
			
		||||
      class="q-dialog-plugin"
 | 
			
		||||
      :style="{ 'min-width': !ret ? '40vw' : '70vw' }"
 | 
			
		||||
    >
 | 
			
		||||
      <q-bar>
 | 
			
		||||
        Send command on {{ agent.hostname }}
 | 
			
		||||
        <q-space />
 | 
			
		||||
        <q-chip v-if="!wsConnected" color="red" text-color="white" icon="error"
 | 
			
		||||
          >Websocket diconnected!</q-chip
 | 
			
		||||
        >
 | 
			
		||||
        <q-space />
 | 
			
		||||
        <q-btn dense flat icon="close" v-close-popup>
 | 
			
		||||
          <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | 
			
		||||
        </q-btn>
 | 
			
		||||
      </q-bar>
 | 
			
		||||
      <q-form @submit="submit">
 | 
			
		||||
        <q-card-section>
 | 
			
		||||
          <p>Shell</p>
 | 
			
		||||
          <div class="q-gutter-sm">
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-if="agent.plat !== 'windows'"
 | 
			
		||||
              dense
 | 
			
		||||
              v-model="state.shell"
 | 
			
		||||
              val="/bin/bash"
 | 
			
		||||
              label="Bash"
 | 
			
		||||
              @update:model-value="state.custom_shell = null"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-if="agent.plat !== 'windows'"
 | 
			
		||||
              dense
 | 
			
		||||
              v-model="state.shell"
 | 
			
		||||
              val="custom"
 | 
			
		||||
              label="Custom"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-if="agent.plat === 'windows'"
 | 
			
		||||
              dense
 | 
			
		||||
              v-model="state.shell"
 | 
			
		||||
              val="cmd"
 | 
			
		||||
              label="CMD"
 | 
			
		||||
            />
 | 
			
		||||
            <q-radio
 | 
			
		||||
              v-if="agent.plat === 'windows'"
 | 
			
		||||
              dense
 | 
			
		||||
              v-model="state.shell"
 | 
			
		||||
              val="powershell"
 | 
			
		||||
              label="Powershell"
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
        <q-card-section v-if="state.shell === 'custom'">
 | 
			
		||||
          <q-input
 | 
			
		||||
            v-model="state.custom_shell"
 | 
			
		||||
            outlined
 | 
			
		||||
            label="Custom shell"
 | 
			
		||||
            stack-label
 | 
			
		||||
            placeholder="/usr/bin/python3"
 | 
			
		||||
            :rules="[(val) => !!val || '*Required']"
 | 
			
		||||
          />
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
        <q-card-section>
 | 
			
		||||
          <q-input
 | 
			
		||||
            v-model.number="state.timeout"
 | 
			
		||||
            dense
 | 
			
		||||
            outlined
 | 
			
		||||
            type="number"
 | 
			
		||||
            style="max-width: 150px"
 | 
			
		||||
            label="Timeout (seconds)"
 | 
			
		||||
            stack-label
 | 
			
		||||
            :rules="[
 | 
			
		||||
              (val) => !!val || '*Required',
 | 
			
		||||
              (val) => val >= 10 || 'Minimum is 10 seconds',
 | 
			
		||||
              (val) => val <= 3600 || 'Maximum is 3600 seconds',
 | 
			
		||||
            ]"
 | 
			
		||||
          />
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
        <q-card-section>
 | 
			
		||||
          <q-input
 | 
			
		||||
            v-model="state.cmd"
 | 
			
		||||
            outlined
 | 
			
		||||
            label="Command"
 | 
			
		||||
            stack-label
 | 
			
		||||
            :placeholder="cmdPlaceholder(state.shell)"
 | 
			
		||||
            :rules="[(val) => !!val || '*Required']"
 | 
			
		||||
          />
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
        <q-card-actions align="right">
 | 
			
		||||
          <q-btn flat dense push label="Cancel" v-close-popup />
 | 
			
		||||
          <q-btn
 | 
			
		||||
            :loading="loading"
 | 
			
		||||
            :disable="!wsConnected"
 | 
			
		||||
            flat
 | 
			
		||||
            dense
 | 
			
		||||
            push
 | 
			
		||||
            label="Send"
 | 
			
		||||
            color="primary"
 | 
			
		||||
            type="submit"
 | 
			
		||||
          >
 | 
			
		||||
          </q-btn>
 | 
			
		||||
        </q-card-actions>
 | 
			
		||||
        <q-card-section
 | 
			
		||||
          v-if="ret !== null"
 | 
			
		||||
          class="q-pl-md q-pr-md q-pt-none q-ma-none scroll"
 | 
			
		||||
          style="max-height: 50vh"
 | 
			
		||||
        >
 | 
			
		||||
          <pre>{{ ret }}</pre>
 | 
			
		||||
        </q-card-section>
 | 
			
		||||
      </q-form>
 | 
			
		||||
    </q-card>
 | 
			
		||||
  </q-dialog>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
// composition imports
 | 
			
		||||
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
 | 
			
		||||
import { useStore } from "vuex";
 | 
			
		||||
import { useDialogPluginComponent } from "quasar";
 | 
			
		||||
import { cmdPlaceholder } from "@/composables/agents";
 | 
			
		||||
import { getWSUrl } from "@/websocket/channels";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "SendCommand",
 | 
			
		||||
  emits: [...useDialogPluginComponent.emits],
 | 
			
		||||
  props: {
 | 
			
		||||
    agent: !Object,
 | 
			
		||||
  },
 | 
			
		||||
  setup(props) {
 | 
			
		||||
    const store = useStore();
 | 
			
		||||
    // setup quasar dialog plugin
 | 
			
		||||
    const { dialogRef, onDialogHide } = useDialogPluginComponent();
 | 
			
		||||
 | 
			
		||||
    // run command logic
 | 
			
		||||
    const state = ref({
 | 
			
		||||
      shell: props.agent.plat === "windows" ? "cmd" : "/bin/bash",
 | 
			
		||||
      cmd: null,
 | 
			
		||||
      timeout: 30,
 | 
			
		||||
      custom_shell: null,
 | 
			
		||||
      agent_id: props.agent.agent_id,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const loading = ref(false);
 | 
			
		||||
    const ret = ref(null);
 | 
			
		||||
 | 
			
		||||
    // websocket
 | 
			
		||||
    const ws = ref(null);
 | 
			
		||||
    const wsConnected = ref(false);
 | 
			
		||||
 | 
			
		||||
    function setupWS() {
 | 
			
		||||
      const token = computed(() => store.state.token);
 | 
			
		||||
      console.log("Starting send command websocket");
 | 
			
		||||
      let url = getWSUrl("sendcmd", token.value);
 | 
			
		||||
      ws.value = new WebSocket(url);
 | 
			
		||||
      ws.value.onopen = () => {
 | 
			
		||||
        wsConnected.value = true;
 | 
			
		||||
        console.log("Send command websocket connected");
 | 
			
		||||
      };
 | 
			
		||||
      ws.value.onmessage = (e) => {
 | 
			
		||||
        const data = JSON.parse(e.data);
 | 
			
		||||
        ret.value = data.ret;
 | 
			
		||||
        loading.value = false;
 | 
			
		||||
      };
 | 
			
		||||
      ws.value.onclose = () => {
 | 
			
		||||
        console.log("Send command websocket disconnected");
 | 
			
		||||
        wsConnected.value = false;
 | 
			
		||||
      };
 | 
			
		||||
      ws.value.onerror = () => {
 | 
			
		||||
        wsConnected.value = false;
 | 
			
		||||
        console.log("Send command websocket error");
 | 
			
		||||
        ws.value.onclose();
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function submit() {
 | 
			
		||||
      ret.value = null;
 | 
			
		||||
      loading.value = true;
 | 
			
		||||
      ret.value = ws.value.send(JSON.stringify(state.value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      setupWS();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    onBeforeUnmount(() => {
 | 
			
		||||
      ws.value.close();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      // reactive data
 | 
			
		||||
      state,
 | 
			
		||||
      loading,
 | 
			
		||||
      ret,
 | 
			
		||||
      wsConnected,
 | 
			
		||||
 | 
			
		||||
      // methods
 | 
			
		||||
      submit,
 | 
			
		||||
      cmdPlaceholder,
 | 
			
		||||
 | 
			
		||||
      // quasar dialog
 | 
			
		||||
      dialogRef,
 | 
			
		||||
      onDialogHide,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@@ -12,11 +12,15 @@
 | 
			
		||||
        color="positive"
 | 
			
		||||
        class="full-width"
 | 
			
		||||
        @click="doCodeSign"
 | 
			
		||||
        :loading="loading"
 | 
			
		||||
      >
 | 
			
		||||
        <q-tooltip
 | 
			
		||||
          >Force all existing agents to be updated to the code-signed
 | 
			
		||||
          version</q-tooltip
 | 
			
		||||
        >
 | 
			
		||||
        <template v-slot:loading>
 | 
			
		||||
          <q-spinner-facebook />
 | 
			
		||||
        </template>
 | 
			
		||||
      </q-btn>
 | 
			
		||||
    </q-card-section>
 | 
			
		||||
    <q-form @submit.prevent="editToken">
 | 
			
		||||
@@ -33,56 +37,92 @@
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
      <q-card-section class="row items-center">
 | 
			
		||||
        <q-btn label="Save" color="primary" type="submit" />
 | 
			
		||||
        <q-space />
 | 
			
		||||
        <q-btn label="Delete" color="negative" @click="confirmDelete" />
 | 
			
		||||
      </q-card-section>
 | 
			
		||||
    </q-form>
 | 
			
		||||
  </q-card>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import mixins from "@/mixins/mixins";
 | 
			
		||||
import { ref, onMounted } from "vue";
 | 
			
		||||
import { useQuasar } from "quasar";
 | 
			
		||||
import axios from "axios";
 | 
			
		||||
import { notifySuccess } from "@/utils/notify";
 | 
			
		||||
 | 
			
		||||
const endpoint = "/core/codesign/";
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  name: "CodeSign",
 | 
			
		||||
  mixins: [mixins],
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      settings: {
 | 
			
		||||
        token: "",
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    getToken() {
 | 
			
		||||
      this.$axios.get("/core/codesign/").then((r) => {
 | 
			
		||||
        this.settings = r.data;
 | 
			
		||||
  setup() {
 | 
			
		||||
    const $q = useQuasar();
 | 
			
		||||
    const settings = ref({ token: "" });
 | 
			
		||||
    const loading = ref(false);
 | 
			
		||||
 | 
			
		||||
    async function getToken() {
 | 
			
		||||
      try {
 | 
			
		||||
        const { data } = await axios.get(endpoint);
 | 
			
		||||
        settings.value = data;
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error(e);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function deleteToken() {
 | 
			
		||||
      try {
 | 
			
		||||
        await axios.delete(endpoint);
 | 
			
		||||
        notifySuccess("Token was deleted!");
 | 
			
		||||
        await getToken();
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        console.error(e);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function confirmDelete() {
 | 
			
		||||
      $q.dialog({
 | 
			
		||||
        title: "Delete token?",
 | 
			
		||||
        cancel: true,
 | 
			
		||||
        persistent: true,
 | 
			
		||||
      }).onOk(() => {
 | 
			
		||||
        deleteToken();
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
    editToken() {
 | 
			
		||||
      this.$q.loading.show();
 | 
			
		||||
      this.$axios
 | 
			
		||||
        .patch("/core/codesign/", this.settings)
 | 
			
		||||
        .then((r) => {
 | 
			
		||||
          this.$q.loading.hide();
 | 
			
		||||
          this.notifySuccess(r.data);
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          this.$q.loading.hide();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    doCodeSign() {
 | 
			
		||||
      this.$q.loading.show();
 | 
			
		||||
      this.$axios
 | 
			
		||||
        .post("/core/codesign/")
 | 
			
		||||
        .then((r) => {
 | 
			
		||||
          this.$q.loading.hide();
 | 
			
		||||
          this.notifySuccess(r.data);
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          this.$q.loading.hide();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.getToken();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function doCodeSign() {
 | 
			
		||||
      loading.value = true;
 | 
			
		||||
      try {
 | 
			
		||||
        const { data } = await axios.post(endpoint);
 | 
			
		||||
        loading.value = false;
 | 
			
		||||
        notifySuccess(data);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        loading.value = false;
 | 
			
		||||
        console.error(e);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function editToken() {
 | 
			
		||||
      $q.loading.show();
 | 
			
		||||
      try {
 | 
			
		||||
        const { data } = await axios.patch(endpoint, settings.value);
 | 
			
		||||
        $q.loading.hide();
 | 
			
		||||
        notifySuccess(data);
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        $q.loading.hide();
 | 
			
		||||
        console.error(e);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      getToken();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      settings,
 | 
			
		||||
      loading,
 | 
			
		||||
      confirmDelete,
 | 
			
		||||
      doCodeSign,
 | 
			
		||||
      editToken,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
 
 | 
			
		||||
@@ -991,10 +991,16 @@ export default {
 | 
			
		||||
        : [];
 | 
			
		||||
 | 
			
		||||
      // remove milliseconds and Z to work with native date input
 | 
			
		||||
      task.value.run_time_date = formatDateInputField(task.value.run_time_date);
 | 
			
		||||
      task.value.run_time_date = formatDateInputField(
 | 
			
		||||
        task.value.run_time_date,
 | 
			
		||||
        true
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      if (task.value.expire_date)
 | 
			
		||||
        task.value.expire_date = formatDateInputField(task.value.expire_date);
 | 
			
		||||
        task.value.expire_date = formatDateInputField(
 | 
			
		||||
          task.value.expire_date,
 | 
			
		||||
          true
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
      // set task type if monthlydow is being used
 | 
			
		||||
      if (task.value.task_type === "monthlydow") {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								src/constants/constants.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/constants/constants.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
const GOARCH_AMD64 = "amd64";
 | 
			
		||||
const GOARCH_i386 = "386";
 | 
			
		||||
const GOARCH_ARM64 = "arm64";
 | 
			
		||||
const GOARCH_ARM32 = "arm";
 | 
			
		||||
 | 
			
		||||
export { GOARCH_AMD64, GOARCH_i386, GOARCH_ARM64, GOARCH_ARM32 };
 | 
			
		||||
@@ -35,12 +35,7 @@
 | 
			
		||||
          Tactical RMM<span class="text-overline q-ml-sm"
 | 
			
		||||
            >v{{ currentTRMMVersion }}</span
 | 
			
		||||
          >
 | 
			
		||||
          <span
 | 
			
		||||
            class="text-overline q-ml-md"
 | 
			
		||||
            v-if="
 | 
			
		||||
              latestTRMMVersion !== 'error' &&
 | 
			
		||||
              currentTRMMVersion !== latestTRMMVersion
 | 
			
		||||
            "
 | 
			
		||||
          <span class="text-overline q-ml-md" v-if="updateAvailable()"
 | 
			
		||||
            ><q-badge color="warning"
 | 
			
		||||
              ><a :href="latestReleaseURL" target="_blank"
 | 
			
		||||
                >v{{ latestTRMMVersion }} available</a
 | 
			
		||||
@@ -144,7 +139,7 @@ import { ref, computed, onMounted, onBeforeUnmount } from "vue";
 | 
			
		||||
import { useQuasar } from "quasar";
 | 
			
		||||
import { useStore } from "vuex";
 | 
			
		||||
import axios from "axios";
 | 
			
		||||
import { getBaseUrl } from "@/boot/axios";
 | 
			
		||||
import { getWSUrl } from "@/websocket/channels";
 | 
			
		||||
 | 
			
		||||
// ui imports
 | 
			
		||||
import AlertsIcon from "@/components/AlertsIcon.vue";
 | 
			
		||||
@@ -171,6 +166,7 @@ export default {
 | 
			
		||||
    const latestTRMMVersion = computed(() => store.state.latestTRMMVersion);
 | 
			
		||||
    const needRefresh = computed(() => store.state.needrefresh);
 | 
			
		||||
    const user = computed(() => store.state.username);
 | 
			
		||||
    const hosted = computed(() => store.state.hosted);
 | 
			
		||||
 | 
			
		||||
    const latestReleaseURL = computed(() => {
 | 
			
		||||
      return latestTRMMVersion.value
 | 
			
		||||
@@ -184,10 +180,6 @@ export default {
 | 
			
		||||
      }).onOk(() => store.dispatch("getDashInfo"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function wsUrl() {
 | 
			
		||||
      return getBaseUrl().split("://")[1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const serverCount = ref(0);
 | 
			
		||||
    const serverOfflineCount = ref(0);
 | 
			
		||||
    const workstationCount = ref(0);
 | 
			
		||||
@@ -200,13 +192,8 @@ export default {
 | 
			
		||||
      // when ws is closed causing ws to connect with expired token
 | 
			
		||||
      const token = computed(() => store.state.token);
 | 
			
		||||
      console.log("Starting websocket");
 | 
			
		||||
      const proto =
 | 
			
		||||
        process.env.NODE_ENV === "production" || process.env.DOCKER_BUILD
 | 
			
		||||
          ? "wss"
 | 
			
		||||
          : "ws";
 | 
			
		||||
      ws.value = new WebSocket(
 | 
			
		||||
        `${proto}://${wsUrl()}/ws/dashinfo/?access_token=${token.value}`
 | 
			
		||||
      );
 | 
			
		||||
      let url = getWSUrl("dashinfo", token.value);
 | 
			
		||||
      ws.value = new WebSocket(url);
 | 
			
		||||
      ws.value.onopen = () => {
 | 
			
		||||
        console.log("Connected to ws");
 | 
			
		||||
      };
 | 
			
		||||
@@ -242,6 +229,11 @@ export default {
 | 
			
		||||
      }, 60 * 5 * 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function updateAvailable() {
 | 
			
		||||
      if (latestTRMMVersion.value === "error" || hosted.value) return false;
 | 
			
		||||
      return currentTRMMVersion.value !== latestTRMMVersion.value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onMounted(() => {
 | 
			
		||||
      setupWS();
 | 
			
		||||
      store.dispatch("getDashInfo");
 | 
			
		||||
@@ -270,6 +262,7 @@ export default {
 | 
			
		||||
 | 
			
		||||
      // methods
 | 
			
		||||
      showUserPreferences,
 | 
			
		||||
      updateAvailable,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								src/websocket/channels.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/websocket/channels.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
import { getBaseUrl } from "@/boot/axios";
 | 
			
		||||
 | 
			
		||||
export function getWSUrl(path, token) {
 | 
			
		||||
  const url = getBaseUrl().split("://")[1];
 | 
			
		||||
 | 
			
		||||
  const proto =
 | 
			
		||||
    process.env.NODE_ENV === "production" || process.env.DOCKER_BUILD
 | 
			
		||||
      ? "wss"
 | 
			
		||||
      : "ws";
 | 
			
		||||
  return `${proto}://${url}/ws/${path}/?access_token=${token}`;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user