Compare commits

..

3 Commits

Author SHA1 Message Date
wh1te909
a8a171ba2c Release 0.100.5 2022-07-10 00:00:08 +00:00
wh1te909
24a63f477e Release 0.100.4 2022-07-07 16:38:14 +00:00
wh1te909
ddeb6293a1 init 2022-05-17 20:46:22 +00:00
22 changed files with 702 additions and 1386 deletions

View File

@@ -1,7 +0,0 @@
COMPOSE_PROJECT_NAME=trmm
IMAGE_REPO=tacticalrmm/
VERSION=latest
# DEV SETTINGS
APP_PORT=443
DOCKER_NETWORK=172.21.0.0/24

View File

@@ -1,26 +0,0 @@
version: '3.4'
services:
app-dev:
container_name: trmm-app-dev
image: node:16-alpine
restart: always
command: /bin/sh -c "npm install --cache ~/.npm && npm run serve"
user: 1000:1000
working_dir: /workspace/web
volumes:
- ..:/workspace:cached
ports:
- "8080:443"
networks:
dev:
aliases:
- tactical-frontend
networks:
dev:
driver: bridge
ipam:
driver: default
config:
- subnet: ${DOCKER_NETWORK}

1
.gitignore vendored
View File

@@ -33,4 +33,3 @@ yarn-error.log*
*.sln
.env
/public/env-config.js

1761
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.1-dev",
"version": "0.100.5",
"private": true,
"productName": "Tactical RMM",
"scripts": {
@@ -10,31 +10,31 @@
"format": "prettier --write \"**/*.{js,ts,vue,,html,md,json}\" --ignore-path .gitignore"
},
"dependencies": {
"@quasar/extras": "1.15.5",
"apexcharts": "3.35.5",
"@quasar/extras": "1.14.2",
"apexcharts": "3.35.3",
"axios": "0.27.2",
"dotenv": "16.0.3",
"dotenv": "16.0.1",
"qrcode.vue": "3.3.3",
"quasar": "2.10.0",
"vue": "3.2.41",
"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.1.5",
"vuex": "4.1.0"
"vue-router": "4.1.1",
"vuex": "4.0.2"
},
"devDependencies": {
"@quasar/cli": "^1.3.2",
"@intlify/vite-plugin-vue-i18n": "^6.0.3",
"@quasar/app-vite": "^1.1.3",
"@types/node": "^18.11.0",
"@typescript-eslint/eslint-plugin": "^5.40.0",
"@typescript-eslint/parser": "^5.38.0",
"autoprefixer": "^10.4.11",
"eslint": "^8.25.0",
"@intlify/vite-plugin-vue-i18n": "^3.4.0",
"@quasar/app-vite": "^1.0.5",
"@types/node": "^18.0.3",
"@typescript-eslint/eslint-plugin": "^5.30.5",
"@typescript-eslint/parser": "^5.30.5",
"autoprefixer": "^10.4.7",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-vue": "^8.5.0",
"prettier": "^2.7.1",
"typescript": "^4.8.4"
"typescript": "^4.7.4"
}
}
}

View File

@@ -196,14 +196,6 @@
>
<q-tooltip>Linux</q-tooltip>
</q-icon>
<q-icon
v-else-if="props.row.plat === 'darwin'"
name="mdi-apple"
size="sm"
color="primary"
>
<q-tooltip>macOS</q-tooltip>
</q-icon>
</q-td>
<q-td key="checks-status" :props="props">
@@ -364,27 +356,6 @@ export default {
},
methods: {
filterTable(rows, terms, cols, cellValue) {
const hiddenFields = [
"version",
"operating_system",
"public_ip",
"cpu_model",
"graphics",
"local_ips",
"make_model",
"physical_disks",
];
// quasar filter only does visible columns so this is a hack to add hidden columns we want to filter
for (const elem of hiddenFields) {
if (!cols.find((o) => o.name === elem)) {
cols.push({
name: elem,
field: elem,
});
}
}
const lowerTerms = terms ? terms.toLowerCase() : "";
let advancedFilter = false;
let availability = null;

View File

@@ -310,10 +310,9 @@ export default {
}
function showUpdateDetails(update) {
const color = $q.dark.isActive ? "white" : "";
let support_urls = "";
update.more_info_urls.forEach((u) => {
support_urls += `<a style='color: ${color}' href='${u}' target='_blank'>${u}</a><br/>`;
support_urls += `<a href='${u}' target='_blank'>${u}</a><br/>`;
});
let cats = update.categories.join(", ");
$q.dialog({

View File

@@ -7,17 +7,6 @@
<q-badge color="primary" class="q-ml-sm text-caption">{{
v
}}</q-badge>
<q-btn
v-if="!!v"
size="sm"
class="q-ml-xs"
flat
round
icon="content_copy"
@click="copyValueToClip(v)"
>
<q-tooltip>Copy to Clipboard</q-tooltip>
</q-btn>
</div>
</div>
<q-separator v-if="info.length > 1" />
@@ -26,8 +15,6 @@
</template>
<script>
import { copyToClipboard } from "quasar";
import { notifySuccess } from "@/utils/notify";
// composition imports
import { computed } from "vue";
import { useStore } from "vuex";
@@ -41,17 +28,9 @@ export default {
const store = useStore();
const tabHeight = computed(() => store.state.tabHeight);
function copyValueToClip(val) {
copyToClipboard(val)
.then(() => {
notifySuccess("Copied to clipboard");
})
}
return {
tabHeight,
uid,
copyValueToClip,
};
},
};

View File

@@ -10,13 +10,10 @@
</q-card-actions>
</q-card-section>
<q-card-section>
<p v-if="info.plat === 'windows'" class="text-subtitle1">
<p class="text-subtitle1">
Download the agent then run the following command from an elevated
command prompt on the device you want to add.
</p>
<p v-else-if="info.plat === 'darwin'" class="text-subtitle1">
Run the following command from a terminal
</p>
<p>
<q-field outlined :color="$q.dark.isActive ? 'white' : 'black'">
<code>{{ info.data.cmd }}</code>
@@ -40,7 +37,7 @@
</q-badge>
<span>Do not popup any message boxes during install</span>
</div>
<div v-if="info.plat === 'windows'" class="q-pa-xs q-gutter-xs">
<div class="q-pa-xs q-gutter-xs">
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
<code
>-local-mesh "C:\\&lt;some folder or
@@ -49,7 +46,7 @@
</q-badge>
<span> To skip downloading the Mesh Agent during the install.</span>
</div>
<div v-if="info.plat === 'windows'" class="q-pa-xs q-gutter-xs">
<div class="q-pa-xs q-gutter-xs">
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
<code
>-meshdir "C:\Program Files\Your Company Name\Mesh Agent"</code
@@ -66,7 +63,7 @@
</q-badge>
<span>Don't install the mesh agent</span>
</div>
<div v-if="info.plat === 'windows'" class="q-pa-xs q-gutter-xs">
<div class="q-pa-xs q-gutter-xs">
<q-badge class="text-caption q-mr-xs" color="grey" text-color="black">
<code>-cert "C:\\&lt;some folder or path&gt;\\ca.pem"</code>
</q-badge>
@@ -89,12 +86,12 @@
<p class="text-italic">
Note: the auth token above will be valid for {{ info.expires }} hours.
</p>
<q-btn v-if="info.plat === 'windows'"
<q-btn
type="a"
:href="info.data.url"
color="primary"
label="Download Agent"
></q-btn>
/>
</q-card-section>
</q-card>
</template>

View File

@@ -135,11 +135,6 @@
:rules="[(val) => !!val || '*Required']"
/>
</q-card-section>
<q-card-section v-if="supportsRunAsUser()" class="q-pt-none">
<q-checkbox v-model="state.run_as_user" label="Run As User">
<q-tooltip>{{ runAsUserToolTip }}</q-tooltip>
</q-checkbox>
</q-card-section>
<q-card-section v-if="mode === 'script' || mode === 'command'">
<q-input
@@ -208,7 +203,6 @@ import { runBulkAction } from "@/api/agents";
import { notifySuccess } from "@/utils/notify";
import { cmdPlaceholder } from "@/composables/agents";
import { removeExtraOptionCategories } from "@/utils/format";
import { runAsUserToolTip } from "@/constants/constants";
// ui imports
import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
@@ -223,7 +217,6 @@ const monTypeOptions = [
const osTypeOptions = [
{ label: "Windows", value: "windows" },
{ label: "Linux", value: "linux" },
{ label: "macOS", value: "darwin" },
{ label: "All", value: "all" },
];
@@ -307,7 +300,6 @@ export default {
script,
timeout: defaultTimeout,
args: defaultArgs,
run_as_user: false,
});
const loading = ref(false);
@@ -324,7 +316,6 @@ export default {
() => state.value.osType,
(newValue) => {
state.value.custom_shell = null;
state.value.run_as_user = false;
if (newValue === "windows") {
state.value.shell = "cmd";
@@ -346,13 +337,6 @@ export default {
loading.value = false;
}
const supportsRunAsUser = () => {
const modes = ["script", "command"];
return (
state.value.osType === "windows" && modes.includes(state.value.mode)
);
};
// set modal title and caption
const modalTitle = computed(() => {
return props.mode === "command"
@@ -403,7 +387,6 @@ export default {
osTypeOptions,
targetOptions,
patchModeOptions,
runAsUserToolTip,
//computed
modalTitle,
@@ -411,7 +394,6 @@ export default {
//methods
submit,
cmdPlaceholder,
supportsRunAsUser,
// quasar dialog plugin
dialogRef,

View File

@@ -465,51 +465,8 @@ export default {
});
},
editAgent() {
// TODO we need to fix the serializer to not send this stuff
const toRemove = [
"created_by",
"created_time",
"modified_by",
"modified_time",
"all_timezones",
"timezone",
"wmi_detail",
"services",
"status",
"cpu_model",
"local_ips",
"make_model",
"physical_disks",
"graphics",
"checks",
"patches_last_installed",
"last_seen",
"applied_policies",
"effective_patch_policy",
"version",
"operating_system",
"plat",
"goarch",
"hostname",
"public_ip",
"total_ram",
"disks",
"boot_time",
"logged_in_username",
"last_logged_in_user",
"needs_reboot",
"choco_installed",
"policy",
"mesh_node_id",
"block_policy_inheritance",
"maintenance_mode",
"alert_template",
"client",
"site_name",
];
for (const elem of toRemove) {
delete this.agent[elem];
}
delete this.agent.all_timezones;
delete this.agent.timezone;
// only send the timezone data if it has changed
// this way django will keep the db column as null and inherit from the global setting
@@ -546,7 +503,7 @@ export default {
else if (day === 0) result += "Sun, ";
}
return result.trimEnd(",");
return result.trimRight(",");
},
},
mounted() {

View File

@@ -52,15 +52,6 @@
goarch = GOARCH_AMD64;
"
/>
<q-radio
v-model="agentOS"
val="darwin"
label="macOS"
@update:model-value="
installMethod = 'mac';
goarch = GOARCH_AMD64;
"
/>
</div>
</q-card-section>
<q-card-section>
@@ -114,37 +105,37 @@
v-model="goarch"
:val="GOARCH_AMD64"
label="64 bit"
v-show="agentOS === 'windows' || agentOS === 'linux'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_AMD64"
label="Intel 64 bit"
v-show="agentOS === 'darwin'"
v-show="agentOS === 'windows'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_i386"
label="32 bit"
v-show="agentOS !== 'darwin'"
v-show="agentOS === 'windows'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_AMD64"
label="64 bit"
v-show="agentOS !== 'windows'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_i386"
label="32 bit"
v-show="agentOS !== 'windows'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_ARM64"
label="ARM 64 bit"
v-show="agentOS === 'linux'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_ARM64"
label="Apple Silicon (M1, M2)"
v-show="agentOS === 'darwin'"
v-show="agentOS !== 'windows'"
/>
<q-radio
v-model="goarch"
:val="GOARCH_ARM32"
label="ARM 32 bit (Rasp Pi)"
v-show="agentOS === 'linux'"
v-show="agentOS !== 'windows'"
/>
</div>
</q-card-section>
@@ -275,13 +266,12 @@ export default {
plat: this.agentOS,
};
if (this.installMethod === "manual" || this.installMethod === "mac") {
if (this.installMethod === "manual") {
this.$axios.post("/agents/installer/", data).then((r) => {
this.info = {
expires: this.expires,
data: r.data,
goarch: this.goarch,
plat: this.agentOS,
};
this.showAgentDownload = true;
});
@@ -353,9 +343,6 @@ export default {
case "bash":
text = "Download linux install script";
break;
case "mac":
text = "Show installation instructions";
break;
}
return text;

View File

@@ -129,37 +129,37 @@
<div class="q-gutter-sm">
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="0"
:val="1"
label="Monday"
/>
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="1"
:val="2"
label="Tuesday"
/>
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="2"
:val="3"
label="Wednesday"
/>
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="3"
:val="4"
label="Thursday"
/>
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="4"
:val="5"
label="Friday"
/>
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="5"
:val="6"
label="Saturday"
/>
<q-checkbox
v-model="winupdatepolicy.run_time_days"
:val="6"
:val="0"
label="Sunday"
/>
</div>

View File

@@ -63,14 +63,11 @@ export default {
loading.value = true;
try {
const ret = await scheduleAgentReboot(
props.agent.agent_id,
state.value
);
await scheduleAgentReboot(props.agent.agent_id, state.value);
$q.dialog({
title: "Reboot pending",
style: "width: 40vw",
message: `A reboot has been scheduled for <strong>${ret.time}</strong> on ${props.agent.hostname}.
message: `A reboot has been scheduled for <strong>${state.value.datetime}</strong> on ${props.agent.hostname}.
<br />It can be cancelled from the Pending Actions menu until the scheduled time.`,
html: true,
}).onDismiss(onDialogOK);

View File

@@ -128,11 +128,6 @@
/>
<q-checkbox v-model="state.save_all_output" label="Save all output" />
</q-card-section>
<q-card-section v-if="agent.plat === 'windows'">
<q-checkbox v-model="state.run_as_user" label="Run As User">
<q-tooltip>{{ runAsUserToolTip }}</q-tooltip>
</q-checkbox>
</q-card-section>
<q-card-section>
<q-input
v-model.number="state.timeout"
@@ -178,7 +173,6 @@ import { useScriptDropdown } from "@/composables/scripts";
import { useCustomFieldDropdown } from "@/composables/core";
import { runScript } from "@/api/agents";
import { notifySuccess } from "@/utils/notify";
import { runAsUserToolTip } from "@/constants/constants";
import {
formatScriptSyntax,
removeExtraOptionCategories,
@@ -226,7 +220,6 @@ export default {
script,
args: defaultArgs,
timeout: defaultTimeout,
run_as_user: false,
});
const ret = ref(null);
@@ -280,7 +273,6 @@ export default {
// non-reactive data
outputOptions,
runAsUserToolTip,
//methods
formatScriptSyntax,

View File

@@ -51,11 +51,6 @@
/>
</div>
</q-card-section>
<q-card-section v-if="agent.plat === 'windows'">
<q-checkbox v-model="state.run_as_user" label="Run As User">
<q-tooltip>{{ runAsUserToolTip }}</q-tooltip>
</q-checkbox>
</q-card-section>
<q-card-section v-if="state.shell === 'custom'">
<q-input
v-model="state.custom_shell"
@@ -122,7 +117,6 @@ import { ref } from "vue";
import { useDialogPluginComponent } from "quasar";
import { sendAgentCommand } from "@/api/agents";
import { cmdPlaceholder } from "@/composables/agents";
import { runAsUserToolTip } from "@/constants/constants";
export default {
name: "SendCommand",
@@ -140,7 +134,6 @@ export default {
cmd: null,
timeout: 30,
custom_shell: null,
run_as_user: false,
});
const loading = ref(false);
@@ -163,9 +156,6 @@ export default {
loading,
ret,
// non reactivete data
runAsUserToolTip,
// methods
submit,
cmdPlaceholder,

View File

@@ -128,18 +128,6 @@
:rules="[(val) => val >= 5 || 'Minimum is 5']"
hide-bottom-space
/>
<q-checkbox
v-model="formScript.run_as_user"
label="Run As User (Windows only)"
>
<q-tooltip
>Setting this value on the script model will always override any
'Run As User' checkboxes in the UI and force this script to
always be 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. Not supported on Windows Server.
</q-tooltip>
</q-checkbox>
<q-input
label="Syntax"
type="textarea"
@@ -265,7 +253,6 @@ export default {
default_timeout: 90,
args: [],
script_body: "",
run_as_user: false,
});
if (props.clone) script.value.name = `(Copy) ${script.value.name}`;

View File

@@ -44,7 +44,6 @@ export default {
timeout: props.script.default_timeout,
args: props.script.args,
shell: props.script.shell,
run_as_user: props.script.run_as_user,
};
try {
ret.value = await testScript(props.agent, data);

View File

@@ -991,16 +991,10 @@ export default {
: [];
// remove milliseconds and Z to work with native date input
task.value.run_time_date = formatDateInputField(
task.value.run_time_date,
true
);
task.value.run_time_date = formatDateInputField(task.value.run_time_date);
if (task.value.expire_date)
task.value.expire_date = formatDateInputField(
task.value.expire_date,
true
);
task.value.expire_date = formatDateInputField(task.value.expire_date);
// set task type if monthlydow is being used
if (task.value.task_type === "monthlydow") {

View File

@@ -37,5 +37,4 @@ export function cmdPlaceholder(shell) {
export const agentPlatformOptions = [
{ value: "windows", label: "Windows" },
{ value: "linux", label: "Linux" },
{ value: "darwin", label: "macOS" },
];

View File

@@ -3,13 +3,4 @@ const GOARCH_i386 = "386";
const GOARCH_ARM64 = "arm64";
const GOARCH_ARM32 = "arm";
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. Not supported on Windows Server.";
export {
GOARCH_AMD64,
GOARCH_i386,
GOARCH_ARM64,
GOARCH_ARM32,
runAsUserToolTip,
};
export { GOARCH_AMD64, GOARCH_i386, GOARCH_ARM64, GOARCH_ARM32 };

View File

@@ -285,7 +285,7 @@ export function formatDateInputField(isoDateString, noTimezone = false) {
if (noTimezone) {
isoDateString = isoDateString.replace("Z", "");
}
return date.formatDate(isoDateString, "YYYY-MM-DDTHH:mm");
return date.formatDate(isoDateString, "YYYY-MM-DDTHH:mm:ss");
}
// converts a local date string "YYYY-MM-DDTHH:mm:ss" to an iso date string with the local timezone