Compare commits

...

23 Commits

Author SHA1 Message Date
wh1te909
200a02b87b update ci and bump version 2024-03-15 19:57:05 +00:00
wh1te909
da5dbeaf0f update reqs 2024-03-15 19:49:24 +00:00
wh1te909
4b6d099f72 add icon for tooltip and don't show in hosted 2024-03-13 01:30:37 +00:00
wh1te909
842661ada6 update browser/node 2024-03-13 01:29:22 +00:00
wh1te909
f5148c87c8 no longer need disable auto login 2024-03-12 05:27:18 +00:00
wh1te909
16164c0bbc update apexcharts 2024-03-10 22:37:17 +00:00
wh1te909
f38ddb840b update reqs 2024-03-08 18:42:40 +00:00
wh1te909
f86fe26ffe fix wording 2024-03-08 18:42:32 +00:00
Dan
162360bf45 Merge pull request #21 from dinger1986/develop
Update TakeControl.vue
2024-03-02 12:43:27 -08:00
dinger1986
612aaa7880 Update TakeControl.vue 2024-02-26 21:51:29 +00:00
wh1te909
e91f3fe53d sync mesh users/perms with trmm amidaware/tacticalrmm#182 2024-02-23 21:30:27 +00:00
wh1te909
f0fe4d64bc same perms 2024-02-23 03:17:35 +00:00
Dan
07cc6aca6a Merge pull request #20 from conlan0/develop
Add Shutdown option to agent action menu
2024-02-22 13:50:14 -08:00
wh1te909
23bf81efbb fix js/typescript monaco support in editor 2024-02-22 20:50:03 +00:00
wh1te909
a55105e5ee format 2024-02-22 20:49:33 +00:00
Dan
5832a426bc Merge pull request #14 from NiceGuyIT/feature/cross-platform-scripting
[Feature] Add cross site scripting
2024-02-21 21:23:26 -08:00
conlan0
38dc709108 Add shutdown url 2024-02-21 21:22:37 -05:00
conlan0
5696d3359b Add Shutdown option 2024-02-21 21:19:20 -05:00
wh1te909
1b4fa84753 update reqs 2024-02-21 01:13:38 +00:00
wh1te909
2db4eeec05 bump version 2024-02-03 01:44:28 +00:00
Dan
fe5e8aa5fe Merge pull request #17 from JordanLukeJones/JordanLukeJones-OTP-1
Update LoginView.vue
2024-02-02 17:40:36 -08:00
Jordan Jones
13e35d24a2 Update LoginView.vue
Add's ability to auto-populate OTP on compatible devices
2024-02-02 09:29:19 +00:00
David Randall
fe8d88497f [Feature] Add cross site scripting 2023-11-12 15:37:55 -05:00
16 changed files with 677 additions and 358 deletions

View File

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

704
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "web",
"version": "0.101.39",
"version": "0.101.41",
"private": true,
"productName": "Tactical RMM",
"scripts": {
@@ -11,33 +11,36 @@
},
"dependencies": {
"@quasar/extras": "1.16.9",
"apexcharts": "3.45.2",
"axios": "1.6.7",
"dotenv": "16.4.1",
"apexcharts": "3.47.0",
"axios": "1.6.8",
"dotenv": "16.4.5",
"pinia": "^2.1.7",
"qrcode.vue": "3.4.1",
"quasar": "2.14.3",
"vue": "3.4.15",
"vue3-apexcharts": "1.4.4",
"quasar": "2.15.1",
"vue": "3.4.21",
"vue3-apexcharts": "1.5.2",
"vuedraggable": "4.1.0",
"vue-router": "4.2.5",
"@vueuse/core": "10.7.2",
"@vueuse/shared": "10.7.2",
"monaco-editor": "0.45.0",
"vue-router": "4.3.0",
"@vueuse/core": "10.9.0",
"@vueuse/shared": "10.9.0",
"monaco-editor": "0.47.0",
"vuex": "4.1.0",
"yaml": "2.3.4"
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"yaml": "2.4.1"
},
"devDependencies": {
"@quasar/cli": "2.3.0",
"@intlify/unplugin-vue-i18n": "2.0.0",
"@quasar/app-vite": "1.7.3",
"@types/node": "20.11.6",
"@typescript-eslint/eslint-plugin": "6.19.1",
"@typescript-eslint/parser": "6.19.1",
"autoprefixer": "10.4.17",
"eslint": "8.56.0",
"@quasar/cli": "2.4.0",
"@intlify/unplugin-vue-i18n": "3.0.1",
"@quasar/app-vite": "1.8.0",
"@types/node": "20.11.27",
"@typescript-eslint/eslint-plugin": "7.2.0",
"@typescript-eslint/parser": "7.2.0",
"autoprefixer": "10.4.18",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-vue": "8.7.1",
"prettier": "3.2.4",
"typescript": "5.3.3"
"prettier": "3.2.5",
"typescript": "5.4.2"
}
}

View File

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

View File

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

View File

@@ -107,7 +107,7 @@
/>
<q-checkbox
v-model="localRole.can_reboot_agents"
label="Reboot Agents"
label="Shutdown / Reboot Agents"
/>
<q-checkbox
v-model="localRole.can_send_wol"

View File

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

View File

@@ -389,7 +389,7 @@
<q-tab-panel name="meshcentral">
<div class="text-subtitle2">MeshCentral Settings</div>
<q-separator />
<q-card-section class="row">
<q-card-section class="row" v-if="!hosted">
<div class="col-4">Username:</div>
<div class="col-2"></div>
<q-input
@@ -405,7 +405,7 @@
]"
/>
</q-card-section>
<q-card-section class="row">
<q-card-section class="row" v-if="!hosted">
<div class="col-4">Mesh Site:</div>
<div class="col-2"></div>
<q-input
@@ -415,7 +415,7 @@
class="col-6"
/>
</q-card-section>
<q-card-section class="row">
<q-card-section class="row" v-if="!hosted">
<div class="col-4">Mesh Token:</div>
<div class="col-2"></div>
<q-input
@@ -425,7 +425,7 @@
class="col-6"
/>
</q-card-section>
<q-card-section class="row">
<q-card-section class="row" v-if="!hosted">
<div class="col-4">Mesh Device Group Name:</div>
<div class="col-2"></div>
<q-input
@@ -435,17 +435,45 @@
class="col-6"
/>
</q-card-section>
<q-card-section class="row">
<div class="col-4">
Disable Auto Login for Remote Control and Remote background:
<q-card-section class="row" v-if="!hosted">
<div class="col-4 flex items-center">
Sync MeshCentral Users/Permissions with TRMM:
</div>
<div class="col-2"></div>
<q-checkbox
dense
v-model="settings.mesh_disable_auto_login"
v-model="settings.sync_mesh_with_trmm"
class="col-6"
/>
</q-card-section>
<q-card-section class="row items-center">
<div class="col-4 flex items-center">
Company Name:
<q-icon
name="ion-information-circle-outline"
size="sm"
class="q-ml-sm cursor-pointer"
>
<q-tooltip class="text-caption">
Adding your company name here will append it to the
user's full name that appears when doing a remote
control session, for example: 'John Doe - Amidaware
Inc.'
</q-tooltip>
</q-icon>
</div>
<div class="col-2"></div>
<q-input
dense
outlined
v-model="settings.mesh_company_name"
class="col-6"
>
</q-input>
</q-card-section>
</q-tab-panel>
<q-tab-panel name="customfields">
<CustomFields />
@@ -645,6 +673,11 @@ export default {
],
};
},
computed: {
hosted() {
return this.$store.state.hosted;
},
},
methods: {
openURL(url) {
openURL(url);

View File

@@ -222,6 +222,35 @@ import TestScriptModal from "@/components/scripts/TestScriptModal.vue";
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
import * as monaco from "monaco-editor";
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
import jsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
// https://github.com/microsoft/monaco-editor/issues/4045#issuecomment-1723787448
self.MonacoEnvironment = {
getWorker: function (workerId, label) {
switch (label) {
case "json":
return new jsonWorker();
case "css":
case "scss":
case "less":
return new cssWorker();
case "html":
case "handlebars":
case "razor":
return new htmlWorker();
case "typescript":
case "javascript":
return new jsWorker();
default:
return new editorWorker();
}
},
};
// types
import type { Script } from "@/types/scripts";
@@ -287,8 +316,8 @@ const title = computed(() => {
return props.readonly
? `Viewing ${script.name}`
: props.clone
? `Copying ${script.name}`
: `Editing ${script.name}`;
? `Copying ${script.name}`
: `Editing ${script.name}`;
} else {
return "Adding new script";
}
@@ -296,11 +325,21 @@ const title = computed(() => {
// convert highlighter language to match what ace expects
const lang = computed(() => {
if (script.shell === "cmd") return "bat";
else if (script.shell === "powershell") return "powershell";
else if (script.shell === "python") return "python";
else if (script.shell === "shell") return "shell";
else return "";
switch (script.shell) {
case "cmd":
return "bat";
case "powershell":
return "powershell";
case "python":
return "python";
case "shell":
case "nushell":
return "shell";
case "deno":
return "typescript";
default:
return "";
}
});
async function submit() {
@@ -339,12 +378,7 @@ const scriptEditor = ref<HTMLElement | null>(null);
let editor: monaco.editor.IStandaloneCodeEditor;
function loadEditor() {
var modelUri = monaco.Uri.parse("model://new"); // a made up unique URI for our model
var model = monaco.editor.createModel(
script.script_body,
lang.value,
modelUri,
);
var model = monaco.editor.createModel(script.script_body, lang.value);
const theme = $q.dark.isActive ? "vs-dark" : "vs-light";

View File

@@ -175,6 +175,20 @@
>
<q-tooltip> Shell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.node.shell === 'nushell'"
name="mdi-code-greater-than"
color="primary"
>
<q-tooltip> Nushell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.node.shell === 'deno'"
name="mdi-language-typescript"
color="primary"
>
<q-tooltip> Deno </q-tooltip>
</q-icon>
<!-- is community script icon -->
<img
@@ -471,6 +485,22 @@
>
<q-tooltip> Shell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.shell === 'nushell'"
size="sm"
name="mdi-code-greater-than"
color="primary"
>
<q-tooltip> Nushell </q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.shell === 'deno'"
size="sm"
name="mdi-language-typescript"
color="primary"
>
<q-tooltip> Deno </q-tooltip>
</q-icon>
</q-td>
<!-- supported platforms -->
<q-td key="supported_platforms" :props="props">

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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