Compare commits

...

22 Commits

Author SHA1 Message Date
wh1te909
8403ac0e93 Release 0.101.16 2023-03-22 17:00:29 +00:00
wh1te909
c2f21b70dd bump version 2023-03-22 16:59:35 +00:00
wh1te909
520145e0e3 bump dev ver 2023-03-21 18:49:28 +00:00
wh1te909
6a132187a2 fix phantom column fixes amidaware/tacticalrmm#1264 2023-03-20 14:08:58 +00:00
wh1te909
a63a9ccd76 update reqs 2023-03-20 01:28:54 +00:00
wh1te909
ff1eb791db feat: increase size of notes text box closes amidaware/tacticalrmm#1407 2023-03-10 05:31:50 +00:00
wh1te909
13bd88b979 feat: bulk run checks by client or site amidaware/tacticalrmm@7d017f9494 2023-03-10 00:22:12 +00:00
wh1te909
5b0c244920 update reqs 2023-03-10 00:20:51 +00:00
wh1te909
0318a17cac update reqs 2023-03-05 20:49:03 +00:00
wh1te909
b7a91563b0 Release 0.101.13 2023-01-18 20:05:20 +00:00
wh1te909
75296ed8ee bump version 2023-01-18 20:04:26 +00:00
wh1te909
09bee45b2f update reqs and dev version 2023-01-16 23:03:30 +00:00
wh1te909
3573c48872 formatting 2023-01-16 08:38:34 +00:00
wh1te909
784841c221 syntax typo 2023-01-16 08:37:15 +00:00
wh1te909
ed788a1861 update reqs 2023-01-16 08:35:07 +00:00
wh1te909
ab19afca16 Release 0.101.11 2022-12-21 18:44:46 +00:00
wh1te909
bd6b08505a bump version 2022-12-21 18:44:21 +00:00
wh1te909
acd64f25f2 add ui for self reset amidaware/tacticalrmm#1378 2022-12-20 23:38:19 +00:00
wh1te909
087be2c232 update reqs 2022-12-20 23:35:44 +00:00
wh1te909
91a3272843 format 2022-12-20 23:35:32 +00:00
wh1te909
6e64f0a11b formatting 2022-12-20 21:02:55 +00:00
wh1te909
8f34f76a1d fix edit apikey amidaware/tacticalrmm#1369 2022-12-08 23:27:26 +00:00
20 changed files with 2393 additions and 4048 deletions

View File

@@ -5,7 +5,7 @@
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"editorconfig.editorconfig", "editorconfig.editorconfig",
"vue.volar", "vue.volar",
"wayou.vscode-todo-highlight", "wayou.vscode-todo-highlight"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": [
"octref.vetur", "octref.vetur",

View File

@@ -4,7 +4,7 @@
"editor.formatOnSave": true, "editor.formatOnSave": true,
"[vue][javascript][typescript][javascriptreact]": { "[vue][javascript][typescript][javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": ["source.fixAll.eslint"], "editor.codeActionsOnSave": ["source.fixAll.eslint"]
}, },
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"], "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
@@ -15,7 +15,7 @@
"**/node_modules/": true, "**/node_modules/": true,
"/node_modules/**": true, "/node_modules/**": true,
"**/env/": true, "**/env/": true,
"/env/**": true, "/env/**": true
} }
} }
} }

View File

@@ -1,24 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<head> <title><%= productName %></title>
<title>
<%= productName %>
</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
<meta name="description" content="<%= productDescription %>" /> <meta name="description" content="<%= productDescription %>" />
<meta name="format-detection" content="telephone=no" /> <meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" /> <meta name="msapplication-tap-highlight" content="no" />
<meta name="viewport" <meta
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>" /> name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>"
/>
<link rel="icon" type="image/ico" href="favicon.ico" /> <link rel="icon" type="image/ico" href="favicon.ico" />
<script src="/env-config.js"></script> <script src="/env-config.js"></script>
</head> </head>
<body> <body>
<!-- quasar:entry-point --> <!-- quasar:entry-point -->
</body> </body>
</html> </html>

6045
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "web", "name": "web",
"version": "0.101.9", "version": "0.101.16",
"private": true, "private": true,
"productName": "Tactical RMM", "productName": "Tactical RMM",
"scripts": { "scripts": {
@@ -10,13 +10,13 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore" "format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "1.15.6", "@quasar/extras": "1.15.11",
"apexcharts": "3.35.5", "apexcharts": "3.37.1",
"axios": "0.27.2", "axios": "1.3.4",
"dotenv": "16.0.3", "dotenv": "16.0.3",
"qrcode.vue": "3.3.3", "qrcode.vue": "3.3.4",
"quasar": "2.10.2", "quasar": "2.11.8",
"vue": "3.2.45", "vue": "3.2.47",
"vue3-ace-editor": "2.2.2", "vue3-ace-editor": "2.2.2",
"vue3-apexcharts": "1.4.1", "vue3-apexcharts": "1.4.1",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
@@ -24,17 +24,17 @@
"vuex": "4.1.0" "vuex": "4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@quasar/cli": "^1.3.2", "@quasar/cli": "^2.0.0",
"@intlify/vite-plugin-vue-i18n": "^6.0.3", "@intlify/unplugin-vue-i18n": "^0.9.2",
"@quasar/app-vite": "^1.1.3", "@quasar/app-vite": "^1.2.1",
"@types/node": "^18.11.10", "@types/node": "^18.15.3",
"@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.45.0", "@typescript-eslint/parser": "^5.55.0",
"autoprefixer": "10.4.13", "autoprefixer": "10.4.14",
"eslint": "8.28.0", "eslint": "8.36.0",
"eslint-config-prettier": "8.5.0", "eslint-config-prettier": "8.7.0",
"eslint-plugin-vue": "8.7.1", "eslint-plugin-vue": "8.7.1",
"prettier": "2.8.0", "prettier": "2.8.4",
"typescript": "4.9.3" "typescript": "4.9.5"
} }
} }

View File

@@ -4,18 +4,18 @@
module.exports = { module.exports = {
plugins: [ plugins: [
// https://github.com/postcss/autoprefixer // https://github.com/postcss/autoprefixer
require('autoprefixer')({ require("autoprefixer")({
overrideBrowserslist: [ overrideBrowserslist: [
'last 4 Chrome versions', "last 4 Chrome versions",
'last 4 Firefox versions', "last 4 Firefox versions",
'last 4 Edge versions', "last 4 Edge versions",
'last 4 Safari versions', "last 4 Safari versions",
'last 4 Android versions', "last 4 Android versions",
'last 4 ChromeAndroid versions', "last 4 ChromeAndroid versions",
'last 4 FirefoxAndroid versions', "last 4 FirefoxAndroid versions",
'last 4 iOS versions' "last 4 iOS versions",
] ],
}) }),
// https://github.com/elchininet/postcss-rtlcss // https://github.com/elchininet/postcss-rtlcss
// If you want to support RTL css, then // If you want to support RTL css, then
@@ -23,5 +23,5 @@ module.exports = {
// 2. optionally set quasar.config.js > framework > lang to an RTL language // 2. optionally set quasar.config.js > framework > lang to an RTL language
// 3. uncomment the following line: // 3. uncomment the following line:
// require('postcss-rtlcss') // require('postcss-rtlcss')
] ],
} };

View File

@@ -12,6 +12,25 @@ export async function fetchUsers(params = {}) {
} }
} }
export async function resetPass(pass) {
const payload = { password: pass };
try {
const { data } = await axios.put(`${baseUrl}/resetpw/`, payload);
return data;
} catch (e) {
console.error(e);
}
}
export async function resetTwoFactor() {
try {
const { data } = await axios.put(`${baseUrl}/reset2fa/`);
return data;
} catch (e) {
console.error(e);
}
}
// role api function // role api function
export async function fetchRoles(params = {}) { export async function fetchRoles(params = {}) {
try { try {

View File

@@ -374,16 +374,10 @@ export default {
"make_model", "make_model",
"physical_disks", "physical_disks",
]; ];
// quasar filter only does visible columns so this is a hack to add hidden columns we want to filter // quasar filter only does visible columns so this is a hack to add hidden columns we want to filter
for (const elem of hiddenFields) { // originally I was modifying cols directly but this led to phantom colum so doing it this way now
if (!cols.find((o) => o.name === elem)) { // https://github.com/amidaware/tacticalrmm/issues/1264
cols.push({ const allColumns = [...cols, ...hiddenFields.map((field) => ({ field }))];
name: elem,
field: elem,
});
}
}
const lowerTerms = terms ? terms.toLowerCase() : ""; const lowerTerms = terms ? terms.toLowerCase() : "";
let advancedFilter = false; let advancedFilter = false;
@@ -437,7 +431,7 @@ export default {
} }
// Normal text filter // Normal text filter
return cols.some((col) => { return allColumns.some((col) => {
const val = cellValue(col, row) + ""; const val = cellValue(col, row) + "";
const haystack = const haystack =
val === "undefined" || val === "null" ? "" : val.toLowerCase(); val === "undefined" || val === "null" ? "" : val.toLowerCase();

View File

@@ -0,0 +1,75 @@
<template>
<q-dialog ref="dialogRef" @hide="onDialogHide">
<q-card class="q-dialog-plugin" style="width: 60vw">
<q-card-section class="row">
<div class="col-3">New password:</div>
<div class="col-9">
<q-input
outlined
dense
v-model="pass"
:type="isPwd ? 'password' : 'text'"
:rules="[(val) => !!val || '*Required']"
>
<template v-slot:append>
<q-icon
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
<div class="col-3">Confirm password:</div>
<div class="col-9">
<q-input
outlined
dense
v-model="pass2"
:type="isPwd ? 'password' : 'text'"
:rules="[(val) => val === pass || 'Passwords do not match']"
>
<template v-slot:append>
<q-icon
:name="isPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="isPwd = !isPwd"
/>
</template>
</q-input>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
color="primary"
label="Reset"
@click="onOKClick"
:disable="!pass || pass !== pass2"
/>
<q-btn color="negative" label="Cancel" @click="onDialogCancel" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup>
import { ref } from "vue";
import { useDialogPluginComponent } from "quasar";
import { resetPass } from "@/api/accounts";
import { notifySuccess } from "@/utils/notify";
const pass = ref("");
const pass2 = ref("");
const isPwd = ref(true);
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
useDialogPluginComponent();
async function onOKClick() {
const ret = await resetPass(pass.value);
notifySuccess(ret);
onDialogOK();
}
</script>

View File

@@ -166,7 +166,7 @@ export default {
type: "textarea", type: "textarea",
isValid: (val) => !!val, isValid: (val) => !!val,
}, },
style: "width: 30vw; max-width: 50vw;", style: "width: 90vw; max-width: 90vw",
ok: { label: "Add" }, ok: { label: "Add" },
cancel: true, cancel: true,
}).onOk(async () => { }).onOk(async () => {
@@ -193,7 +193,7 @@ export default {
type: "textarea", type: "textarea",
isValid: (val) => !!val, isValid: (val) => !!val,
}, },
style: "width: 30vw; max-width: 50vw;", style: "width: 90vw; max-width: 90vw",
ok: { label: "Save" }, ok: { label: "Save" },
cancel: true, cancel: true,
}).onOk(async (data) => { }).onOk(async (data) => {

View File

@@ -42,10 +42,9 @@ export default {
const tabHeight = computed(() => store.state.tabHeight); const tabHeight = computed(() => store.state.tabHeight);
function copyValueToClip(val) { function copyValueToClip(val) {
copyToClipboard(val) copyToClipboard(val).then(() => {
.then(() => {
notifySuccess("Copied to clipboard"); notifySuccess("Copied to clipboard");
}) });
} }
return { return {

View File

@@ -128,7 +128,7 @@ import { useDialogPluginComponent } from "quasar";
import { useCheckModal } from "@/composables/checks"; import { useCheckModal } from "@/composables/checks";
import { useScriptDropdown } from "@/composables/scripts"; import { useScriptDropdown } from "@/composables/scripts";
import { validateRetcode } from "@/utils/validation"; import { validateRetcode } from "@/utils/validation";
import { envVarsLabel } from "@/constants/constants" import { envVarsLabel } from "@/constants/constants";
// ui imports // ui imports
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue"; import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
@@ -146,8 +146,13 @@ export default {
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent(); const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
// setup script dropdown // setup script dropdown
const { script, scriptOptions, defaultTimeout, defaultArgs, defaultEnvVars } = const {
useScriptDropdown(props.check ? props.check.script : undefined, { script,
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
} = useScriptDropdown(props.check ? props.check.script : undefined, {
onMount: true, onMount: true,
}); });

View File

@@ -122,7 +122,7 @@ export default {
try { try {
const result = props.APIKey const result = props.APIKey
? await editAPIKey(data) ? await editAPIKey(data.id, data)
: await saveAPIKey(data); : await saveAPIKey(data);
onDialogOK(); onDialogOK();
notifySuccess(result); notifySuccess(result);

View File

@@ -89,7 +89,8 @@
<p class="text-italic"> <p class="text-italic">
Note: the auth token above will be valid for {{ info.expires }} hours. Note: the auth token above will be valid for {{ info.expires }} hours.
</p> </p>
<q-btn v-if="info.plat === 'windows'" <q-btn
v-if="info.plat === 'windows'"
type="a" type="a"
:href="info.data.url" :href="info.data.url"
color="primary" color="primary"

View File

@@ -221,8 +221,15 @@ export default {
const { dialogRef, onDialogHide } = useDialogPluginComponent(); const { dialogRef, onDialogHide } = useDialogPluginComponent();
// setup dropdowns // setup dropdowns
const { script, scriptOptions, defaultTimeout, defaultArgs, defaultEnvVars, syntax, link } = const {
useScriptDropdown(props.script, { script,
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
syntax,
link,
} = useScriptDropdown(props.script, {
onMount: true, onMount: true,
filterByPlatform: props.agent.plat, filterByPlatform: props.agent.plat,
}); });

View File

@@ -836,8 +836,13 @@ export default {
const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent(); const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
// setup dropdowns // setup dropdowns
const { script, scriptOptions, defaultTimeout, defaultArgs, defaultEnvVars } = const {
useScriptDropdown(undefined, { script,
scriptOptions,
defaultTimeout,
defaultArgs,
defaultEnvVars,
} = useScriptDropdown(undefined, {
onMount: true, onMount: true,
}); });

View File

@@ -30,7 +30,7 @@ export function useScriptDropdown(setScript = null, { onMount = false } = {}) {
); );
defaultTimeout.value = tmpScript.timeout; defaultTimeout.value = tmpScript.timeout;
defaultArgs.value = tmpScript.args; defaultArgs.value = tmpScript.args;
defaultEnvVars.value = tmpScript.env_vars, defaultEnvVars.value = tmpScript.env_vars;
syntax.value = tmpScript.syntax; syntax.value = tmpScript.syntax;
link.value = link.value =
tmpScript.script_type === "builtin" tmpScript.script_type === "builtin"

View File

@@ -7,4 +7,4 @@ export const runAsUserToolTip =
"Run in the context of the logged in user. If no user is logged in, the script will not run and an error will be returned."; "Run in the context of the logged in user. If no user is logged in, the script will not run and an error will be returned.";
export const envVarsLabel = export const envVarsLabel =
"Environment vars (press Enter after typing each key=value pair)" "Environment vars (press Enter after typing each key=value pair)";

View File

@@ -20,9 +20,14 @@
class="bg-yellow text-black text-center" class="bg-yellow text-black text-center"
> >
<q-icon size="xl" name="warning" /> <q-icon size="xl" name="warning" />
<span><br />Your code signing token is no longer valid.<br/><br/> <span
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/> ><br />Your code signing token is no longer valid.<br /><br />
For any issues or to renew your sponsorship please email support@amidaware.com<br/><br/></span> 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
/></span>
<q-btn <q-btn
color="dark" color="dark"
icon="refresh" icon="refresh"
@@ -135,6 +140,32 @@
<q-item-label>Preferences</q-item-label> <q-item-label>Preferences</q-item-label>
</q-item-section> </q-item-section>
</q-item> </q-item>
<q-item clickable>
<q-item-section>Account</q-item-section>
<q-item-section side>
<q-icon name="keyboard_arrow_right" />
</q-item-section>
<q-menu anchor="top end" self="top start">
<q-list>
<q-item
clickable
v-ripple
@click="resetPassword"
v-close-popup
>
<q-item-section>
<q-item-label>Reset Password</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-ripple @click="reset2FA" v-close-popup>
<q-item-section>
<q-item-label>Reset 2FA</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-item>
<q-item to="/expired" exact> <q-item to="/expired" exact>
<q-item-section> <q-item-section>
<q-item-label>Logout</q-item-label> <q-item-label>Logout</q-item-label>
@@ -156,10 +187,13 @@ import { useQuasar } from "quasar";
import { useStore } from "vuex"; import { useStore } from "vuex";
import axios from "axios"; import axios from "axios";
import { getWSUrl } from "@/websocket/channels"; import { getWSUrl } from "@/websocket/channels";
import { resetTwoFactor } from "@/api/accounts";
import { notifySuccess } from "@/utils/notify";
// ui imports // ui imports
import AlertsIcon from "@/components/AlertsIcon.vue"; import AlertsIcon from "@/components/AlertsIcon.vue";
import UserPreferences from "@/components/modals/coresettings/UserPreferences.vue"; import UserPreferences from "@/components/modals/coresettings/UserPreferences.vue";
import ResetPass from "@/components/accounts/ResetPass.vue";
export default { export default {
name: "MainLayout", name: "MainLayout",
@@ -197,6 +231,26 @@ export default {
}).onOk(() => store.dispatch("getDashInfo")); }).onOk(() => store.dispatch("getDashInfo"));
} }
function resetPassword() {
$q.dialog({
component: ResetPass,
});
}
function reset2FA() {
$q.dialog({
title: "Reset 2FA",
message: "Are you sure you would like to reset your 2FA token?",
cancel: true,
persistent: true,
}).onOk(async () => {
try {
const ret = await resetTwoFactor();
notifySuccess(ret, 3000);
} catch {}
});
}
const serverCount = ref(0); const serverCount = ref(0);
const serverOfflineCount = ref(0); const serverOfflineCount = ref(0);
const workstationCount = ref(0); const workstationCount = ref(0);
@@ -281,6 +335,8 @@ export default {
// methods // methods
showUserPreferences, showUserPreferences,
resetPassword,
reset2FA,
updateAvailable, updateAvailable,
}; };
}, },

View File

@@ -173,6 +173,18 @@
</q-menu> </q-menu>
</q-item> </q-item>
<!-- Bulk Run Checks -->
<q-item
clickable
v-close-popup
@click="runChecks(props.node)"
>
<q-item-section side>
<q-icon name="fas fa-check-double" />
</q-item-section>
<q-item-section>Run Checks</q-item-section>
</q-item>
<q-separator></q-separator> <q-separator></q-separator>
<q-item clickable v-close-popup> <q-item clickable v-close-popup>
@@ -690,6 +702,17 @@ export default {
}) })
.onOk(() => this.$store.dispatch("refreshDashboard")); .onOk(() => this.$store.dispatch("refreshDashboard"));
}, },
runChecks(node) {
const target = node.children ? "client" : "site";
this.$axios
.post(`/checks/${target}/${node.id}/csbulkrun/`)
.then((r) => {
this.notifySuccess(r.data);
})
.catch((e) => {
console.error(e);
});
},
showToggleMaintenance(node) { showToggleMaintenance(node) {
let data = { let data = {
id: node.id, id: node.id,