diff --git a/api/tacticalrmm/agents/models.py b/api/tacticalrmm/agents/models.py index 21ddfc0f2e..4db3d3d9d2 100644 --- a/api/tacticalrmm/agents/models.py +++ b/api/tacticalrmm/agents/models.py @@ -126,6 +126,22 @@ class Meta: def __str__(self) -> str: return self.hostname + def save(self, *args, **kwargs): + # prevent recursion since calling set_alert_template() also calls save() + if not hasattr(self, "_processing_set_alert_template"): + self._processing_set_alert_template = False + + if self.pk and not self._processing_set_alert_template: + orig = Agent.objects.get(pk=self.pk) + mon_type_changed = self.monitoring_type != orig.monitoring_type + site_changed = self.site_id != orig.site_id + if mon_type_changed or site_changed: + self._processing_set_alert_template = True + self.set_alert_template() + self._processing_set_alert_template = False + + super().save(*args, **kwargs) + @property def client(self) -> "Client": return self.site.client @@ -282,7 +298,20 @@ def cpu_model(self) -> List[str]: try: cpus = self.wmi_detail["cpu"] for cpu in cpus: - ret.append([x["Name"] for x in cpu if "Name" in x][0]) + name = [x["Name"] for x in cpu if "Name" in x][0] + lp, nc = "", "" + with suppress(Exception): + lp = [ + x["NumberOfLogicalProcessors"] + for x in cpu + if "NumberOfCores" in x + ][0] + nc = [x["NumberOfCores"] for x in cpu if "NumberOfCores" in x][0] + if lp and nc: + cpu_string = f"{name}, {nc}C/{lp}T" + else: + cpu_string = name + ret.append(cpu_string) return ret except: return ["unknown cpu model"] @@ -413,7 +442,10 @@ def physical_disks(self) -> Sequence[Disk]: @property def serial_number(self) -> str: if self.is_posix: - return "" + try: + return self.wmi_detail["serialnumber"] + except: + return "" try: return self.wmi_detail["bios"][0][0]["SerialNumber"] @@ -507,24 +539,32 @@ def get_agent_policies(self) -> "Dict[str, Optional[Policy]]": ) return { - "agent_policy": self.policy - if self.policy and not self.policy.is_agent_excluded(self) - else None, - "site_policy": site_policy - if (site_policy and not site_policy.is_agent_excluded(self)) - and not self.block_policy_inheritance - else None, - "client_policy": client_policy - if (client_policy and not client_policy.is_agent_excluded(self)) - and not self.block_policy_inheritance - and not self.site.block_policy_inheritance - else None, - "default_policy": default_policy - if (default_policy and not default_policy.is_agent_excluded(self)) - and not self.block_policy_inheritance - and not self.site.block_policy_inheritance - and not self.client.block_policy_inheritance - else None, + "agent_policy": ( + self.policy + if self.policy and not self.policy.is_agent_excluded(self) + else None + ), + "site_policy": ( + site_policy + if (site_policy and not site_policy.is_agent_excluded(self)) + and not self.block_policy_inheritance + else None + ), + "client_policy": ( + client_policy + if (client_policy and not client_policy.is_agent_excluded(self)) + and not self.block_policy_inheritance + and not self.site.block_policy_inheritance + else None + ), + "default_policy": ( + default_policy + if (default_policy and not default_policy.is_agent_excluded(self)) + and not self.block_policy_inheritance + and not self.site.block_policy_inheritance + and not self.client.block_policy_inheritance + else None + ), } def check_run_interval(self) -> int: diff --git a/api/tacticalrmm/alerts/models.py b/api/tacticalrmm/alerts/models.py index f0485a6b67..f008843380 100644 --- a/api/tacticalrmm/alerts/models.py +++ b/api/tacticalrmm/alerts/models.py @@ -169,15 +169,17 @@ def create_or_return_check_alert( assigned_check=check, agent=agent, alert_type=AlertType.CHECK, - severity=check.alert_severity - if check.check_type - not in { - CheckType.MEMORY, - CheckType.CPU_LOAD, - CheckType.DISK_SPACE, - CheckType.SCRIPT, - } - else alert_severity, + severity=( + check.alert_severity + if check.check_type + not in { + CheckType.MEMORY, + CheckType.CPU_LOAD, + CheckType.DISK_SPACE, + CheckType.SCRIPT, + } + else alert_severity + ), message=f"{agent.hostname} has a {check.check_type} check: {check.readable_desc} that failed.", hidden=True, ), diff --git a/api/tacticalrmm/autotasks/models.py b/api/tacticalrmm/autotasks/models.py index c13a89f633..a700bdb99a 100644 --- a/api/tacticalrmm/autotasks/models.py +++ b/api/tacticalrmm/autotasks/models.py @@ -248,16 +248,20 @@ def generate_nats_task_payload(self) -> Dict[str, Any]: "name": self.win_task_name, "overwrite_task": True, "enabled": self.enabled, - "trigger": self.task_type - if self.task_type != TaskType.CHECK_FAILURE - else TaskType.MANUAL, + "trigger": ( + self.task_type + if self.task_type != TaskType.CHECK_FAILURE + else TaskType.MANUAL + ), "multiple_instances": self.task_instance_policy or 0, - "delete_expired_task_after": self.remove_if_not_scheduled - if self.expire_date - else False, - "start_when_available": self.run_asap_after_missed - if self.task_type != TaskType.RUN_ONCE - else True, + "delete_expired_task_after": ( + self.remove_if_not_scheduled if self.expire_date else False + ), + "start_when_available": ( + self.run_asap_after_missed + if self.task_type != TaskType.RUN_ONCE + else True + ), } if self.task_type in ( diff --git a/api/tacticalrmm/core/management/commands/create_natsapi_conf.py b/api/tacticalrmm/core/management/commands/create_natsapi_conf.py index 9d74632f45..3335cd475a 100644 --- a/api/tacticalrmm/core/management/commands/create_natsapi_conf.py +++ b/api/tacticalrmm/core/management/commands/create_natsapi_conf.py @@ -4,7 +4,7 @@ from django.conf import settings from django.core.management.base import BaseCommand -from tacticalrmm.helpers import get_nats_internal_protocol, get_nats_ports +from tacticalrmm.helpers import get_nats_url class Command(BaseCommand): @@ -20,11 +20,9 @@ def handle(self, *args, **kwargs): else: ssl = "disable" - nats_std_port, _ = get_nats_ports() - proto = get_nats_internal_protocol() config = { "key": settings.SECRET_KEY, - "natsurl": f"{proto}://{settings.ALLOWED_HOSTS[0]}:{nats_std_port}", + "natsurl": get_nats_url(), "user": db["USER"], "pass": db["PASSWORD"], "host": db["HOST"], diff --git a/api/tacticalrmm/core/migrations/0039_coresettings_smtp_from_name.py b/api/tacticalrmm/core/migrations/0039_coresettings_smtp_from_name.py new file mode 100644 index 0000000000..70096d7097 --- /dev/null +++ b/api/tacticalrmm/core/migrations/0039_coresettings_smtp_from_name.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.9 on 2024-01-26 00:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0038_alter_coresettings_default_time_zone"), + ] + + operations = [ + migrations.AddField( + model_name="coresettings", + name="smtp_from_name", + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/api/tacticalrmm/core/migrations/0040_customfield_hide_in_summary.py b/api/tacticalrmm/core/migrations/0040_customfield_hide_in_summary.py new file mode 100644 index 0000000000..8eeed5b561 --- /dev/null +++ b/api/tacticalrmm/core/migrations/0040_customfield_hide_in_summary.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.9 on 2024-01-28 02:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0039_coresettings_smtp_from_name"), + ] + + operations = [ + migrations.AddField( + model_name="customfield", + name="hide_in_summary", + field=models.BooleanField(default=False), + ), + ] diff --git a/api/tacticalrmm/core/migrations/0041_auto_20240128_0301.py b/api/tacticalrmm/core/migrations/0041_auto_20240128_0301.py new file mode 100644 index 0000000000..19cd50c831 --- /dev/null +++ b/api/tacticalrmm/core/migrations/0041_auto_20240128_0301.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.9 on 2024-01-28 03:01 + +from django.db import migrations + + +def update_hide_in_summary(apps, schema_editor): + CustomField = apps.get_model("core", "CustomField") + for field in CustomField.objects.filter(hide_in_ui=True): + field.hide_in_summary = True + field.save(update_fields=["hide_in_summary"]) + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0040_customfield_hide_in_summary"), + ] + + operations = [migrations.RunPython(update_hide_in_summary)] diff --git a/api/tacticalrmm/core/models.py b/api/tacticalrmm/core/models.py index 8139b70063..63839c4d59 100644 --- a/api/tacticalrmm/core/models.py +++ b/api/tacticalrmm/core/models.py @@ -1,6 +1,7 @@ import smtplib from contextlib import suppress from email.message import EmailMessage +from email.headerregistry import Address from typing import TYPE_CHECKING, List, Optional, cast import requests @@ -44,6 +45,7 @@ class CoreSettings(BaseAuditModel): smtp_from_email = models.CharField( max_length=255, blank=True, default="from@example.com" ) + smtp_from_name = models.CharField(max_length=255, null=True, blank=True) smtp_host = models.CharField(max_length=255, blank=True, default="smtp.gmail.com") smtp_host_user = models.CharField( max_length=255, blank=True, default="admin@example.com" @@ -207,7 +209,14 @@ def send_mail( try: msg = EmailMessage() msg["Subject"] = subject - msg["From"] = from_address + + if self.smtp_from_name: + msg["From"] = Address( + display_name=self.smtp_from_name, addr_spec=from_address + ) + else: + msg["From"] = from_address + msg["To"] = email_recipients msg.set_content(body) @@ -222,9 +231,16 @@ def send_mail( server.send_message(msg) server.quit() else: - # smtp relay. no auth required - server.send_message(msg) - server.quit() + # gmail smtp relay specific handling. + if self.smtp_host == "smtp-relay.gmail.com": + server.ehlo() + server.starttls() + server.send_message(msg) + server.quit() + else: + # smtp relay. no auth required + server.send_message(msg) + server.quit() except Exception as e: DebugLog.error(message=f"Sending email failed with error: {e}") @@ -298,6 +314,7 @@ class CustomField(BaseAuditModel): default=list, ) hide_in_ui = models.BooleanField(default=False) + hide_in_summary = models.BooleanField(default=False) class Meta: unique_together = (("model", "name"),) diff --git a/api/tacticalrmm/core/tests.py b/api/tacticalrmm/core/tests.py index ac664eb9aa..f60498d33c 100644 --- a/api/tacticalrmm/core/tests.py +++ b/api/tacticalrmm/core/tests.py @@ -14,13 +14,12 @@ from core.utils import get_core_settings, get_meshagent_url # from logs.models import PendingAction -from tacticalrmm.constants import ( +from tacticalrmm.constants import ( # PAAction,; PAStatus, CONFIG_MGMT_CMDS, CustomFieldModel, MeshAgentIdent, - # PAAction, - # PAStatus, ) +from tacticalrmm.helpers import get_nats_hosts, get_nats_url from tacticalrmm.test import TacticalTestCase from .consumers import DashInfo @@ -445,6 +444,38 @@ def test_get_config(self): call_command("get_config", cmd) +class TestNatsUrls(TacticalTestCase): + def setUp(self): + self.setup_coresettings() + + def test_standard_install(self): + self.assertEqual(get_nats_url(), "nats://localhost:4222") + + @override_settings( + NATS_STANDARD_PORT=5000, + USE_NATS_STANDARD=True, + ALLOWED_HOSTS=["api.example.com"], + ) + def test_custom_port_nats_standard(self): + self.assertEqual(get_nats_url(), "tls://api.example.com:5000") + + @override_settings(DOCKER_BUILD=True, ALLOWED_HOSTS=["api.example.com"]) + def test_docker_nats(self): + self.assertEqual(get_nats_url(), "nats://api.example.com:4222") + + @patch.dict("os.environ", {"NATS_CONNECT_HOST": "172.20.4.3"}) + @override_settings(ALLOWED_HOSTS=["api.example.com"]) + def test_custom_connect_host_env(self): + self.assertEqual(get_nats_url(), "nats://172.20.4.3:4222") + + def test_standard_nats_hosts(self): + self.assertEqual(get_nats_hosts(), ("localhost", "localhost", "localhost")) + + @override_settings(DOCKER_BUILD=True, ALLOWED_HOSTS=["api.example.com"]) + def test_docker_nats_hosts(self): + self.assertEqual(get_nats_hosts(), ("0.0.0.0", "0.0.0.0", "api.example.com")) + + class TestCorePermissions(TacticalTestCase): def setUp(self): self.setup_client() diff --git a/api/tacticalrmm/core/views.py b/api/tacticalrmm/core/views.py index 186d07f37e..dccddb498e 100644 --- a/api/tacticalrmm/core/views.py +++ b/api/tacticalrmm/core/views.py @@ -91,9 +91,9 @@ def dashboard_info(request): "show_community_scripts": request.user.show_community_scripts, "dbl_click_action": request.user.agent_dblclick_action, "default_agent_tbl_tab": request.user.default_agent_tbl_tab, - "url_action": request.user.url_action.id - if request.user.url_action - else None, + "url_action": ( + request.user.url_action.id if request.user.url_action else None + ), "client_tree_sort": request.user.client_tree_sort, "client_tree_splitter": request.user.client_tree_splitter, "loading_bar_color": request.user.loading_bar_color, diff --git a/api/tacticalrmm/ee/reporting/custom_filters.py b/api/tacticalrmm/ee/reporting/custom_filters.py index 5ac83e9789..d4f9785b65 100644 --- a/api/tacticalrmm/ee/reporting/custom_filters.py +++ b/api/tacticalrmm/ee/reporting/custom_filters.py @@ -4,7 +4,7 @@ import validators -def as_tz(date_obj, tz, format="%b %d, %I:%M %p"): +def as_tz(date_obj, tz, format="%b %d %Y, %I:%M %p"): return date_obj.astimezone(ZoneInfo(tz)).strftime(format) diff --git a/api/tacticalrmm/ee/reporting/management/commands/generate_json_schemas.py b/api/tacticalrmm/ee/reporting/management/commands/generate_json_schemas.py index 5f1bf4bc45..b9d781f05f 100644 --- a/api/tacticalrmm/ee/reporting/management/commands/generate_json_schemas.py +++ b/api/tacticalrmm/ee/reporting/management/commands/generate_json_schemas.py @@ -3,6 +3,7 @@ This file is subject to the EE License Agreement. For details, see: https://license.tacticalrmm.com/ee """ + import json from typing import TYPE_CHECKING, Any, Dict, List, Tuple diff --git a/api/tacticalrmm/ee/reporting/management/commands/get_webtar_url.py b/api/tacticalrmm/ee/reporting/management/commands/get_webtar_url.py index 7f96fd1f84..626115a528 100644 --- a/api/tacticalrmm/ee/reporting/management/commands/get_webtar_url.py +++ b/api/tacticalrmm/ee/reporting/management/commands/get_webtar_url.py @@ -3,6 +3,7 @@ This file is subject to the EE License Agreement. For details, see: https://license.tacticalrmm.com/ee """ + import urllib.parse from time import sleep from typing import Any, Optional diff --git a/api/tacticalrmm/ee/reporting/tests/test_report_template_views.py b/api/tacticalrmm/ee/reporting/tests/test_report_template_views.py index 44d8e1b871..0d81252c20 100644 --- a/api/tacticalrmm/ee/reporting/tests/test_report_template_views.py +++ b/api/tacticalrmm/ee/reporting/tests/test_report_template_views.py @@ -187,9 +187,11 @@ def test_generate_report_with_dependencies( template=report_template.template_md, template_type=report_template.type, css=report_template.template_css if report_template.template_css else "", - html_template=report_template.template_html.id - if report_template.template_html - else None, + html_template=( + report_template.template_html.id + if report_template.template_html + else None + ), variables=report_template.template_variables, dependencies={"client": 1}, ) diff --git a/api/tacticalrmm/ee/reporting/views.py b/api/tacticalrmm/ee/reporting/views.py index 8b993b3aad..67153ddccc 100644 --- a/api/tacticalrmm/ee/reporting/views.py +++ b/api/tacticalrmm/ee/reporting/views.py @@ -130,9 +130,9 @@ def post(self, request: Request, pk: int) -> Union[FileResponse, Response]: template=template.template_md, template_type=template.type, css=template.template_css or "", - html_template=template.template_html.id - if template.template_html - else None, + html_template=( + template.template_html.id if template.template_html else None + ), variables=template.template_variables, dependencies=request.data["dependencies"], ) diff --git a/api/tacticalrmm/logs/tests.py b/api/tacticalrmm/logs/tests.py index ef1f766688..885b3b79c1 100644 --- a/api/tacticalrmm/logs/tests.py +++ b/api/tacticalrmm/logs/tests.py @@ -152,9 +152,11 @@ def test_get_audit_logs(self): self.assertEqual(resp.status_code, 200) self.assertEqual( len(resp.data["audit_logs"]), # type:ignore - pagination["rowsPerPage"] - if req["count"] > pagination["rowsPerPage"] - else req["count"], + ( + pagination["rowsPerPage"] + if req["count"] > pagination["rowsPerPage"] + else req["count"] + ), ) self.assertEqual(resp.data["total"], req["count"]) # type:ignore diff --git a/api/tacticalrmm/requirements.txt b/api/tacticalrmm/requirements.txt index 3317211f8f..67fef4ddcb 100644 --- a/api/tacticalrmm/requirements.txt +++ b/api/tacticalrmm/requirements.txt @@ -1,46 +1,46 @@ -adrf==0.1.2 +adrf==0.1.3 asgiref==3.7.2 celery==5.3.6 -certifi==2023.11.17 +certifi==2024.2.2 cffi==1.16.0 channels==4.0.0 -channels_redis==4.1.0 -cryptography==41.0.7 -Django==4.2.8 +channels_redis==4.2.0 +cryptography==42.0.2 +Django==4.2.9 django-cors-headers==4.3.1 django-filter==23.5 django-rest-knox==4.2.0 djangorestframework==3.14.0 -drf-spectacular==0.27.0 -hiredis==2.2.3 +drf-spectacular==0.27.1 +hiredis==2.3.2 meshctrl==0.1.15 msgpack==1.0.7 nats-py==2.6.0 packaging==23.2 -psutil==5.9.6 -psycopg[binary]==3.1.16 +psutil==5.9.8 +psycopg[binary]==3.1.17 pycparser==2.21 -pycryptodome==3.19.0 +pycryptodome==3.20.0 pyotp==2.9.0 pyparsing==3.1.1 python-ipware==2.0.1 qrcode==7.4.2 -redis==4.5.5 +redis==5.0.1 requests==2.31.0 six==1.16.0 sqlparse==0.4.4 -twilio==8.10.3 -urllib3==2.1.0 -uvicorn[standard]==0.23.2 +twilio==8.12.0 +urllib3==2.2.0 +uvicorn[standard]==0.27.0 uWSGI==2.0.23 -validators==0.20.0 +validators==0.22.0 vine==5.1.0 websockets==12.0 zipp==3.17.0 -pandas==2.1.4 +pandas==2.2.0 kaleido==0.2.1 -jinja2==3.1.2 -markdown==3.5.1 +jinja2==3.1.3 +markdown==3.5.2 plotly==5.18.0 weasyprint==60.2 ocxsect==0.1.5 \ No newline at end of file diff --git a/api/tacticalrmm/scripts/views.py b/api/tacticalrmm/scripts/views.py index ddd48a0fbf..ab3aab9ec0 100644 --- a/api/tacticalrmm/scripts/views.py +++ b/api/tacticalrmm/scripts/views.py @@ -153,7 +153,7 @@ def post(self, request, agent_id): ) data = { - "func": "runscript", + "func": "runscriptfull", "timeout": request.data["timeout"], "script_args": parsed_args, "payload": { diff --git a/api/tacticalrmm/tacticalrmm/celery.py b/api/tacticalrmm/tacticalrmm/celery.py index 0abcc0fe1a..30b85f2ba0 100644 --- a/api/tacticalrmm/tacticalrmm/celery.py +++ b/api/tacticalrmm/tacticalrmm/celery.py @@ -16,6 +16,7 @@ app.conf.task_track_started = True app.conf.worker_proc_alive_timeout = 30 app.conf.worker_max_tasks_per_child = 2 +app.conf.broker_connection_retry_on_startup = True app.autodiscover_tasks() app.conf.beat_schedule = { diff --git a/api/tacticalrmm/tacticalrmm/helpers.py b/api/tacticalrmm/tacticalrmm/helpers.py index e8aec60303..c3d86fbc17 100644 --- a/api/tacticalrmm/tacticalrmm/helpers.py +++ b/api/tacticalrmm/tacticalrmm/helpers.py @@ -1,3 +1,4 @@ +import os import random import secrets import string @@ -43,10 +44,49 @@ def get_nats_ports() -> tuple[int, int]: def get_nats_internal_protocol() -> str: - if getattr(settings, "TRMM_INSECURE", False): - return "nats" + if getattr(settings, "USE_NATS_STANDARD", False): + return "tls" - return "tls" + return "nats" + + +def get_nats_hosts() -> tuple[str, str, str]: + std_bind_host = "0.0.0.0" + ws_bind_host = "0.0.0.0" + connect_host = settings.ALLOWED_HOSTS[0] + + # standard install + if not settings.DOCKER_BUILD and not getattr(settings, "USE_NATS_STANDARD", False): + std_bind_host, ws_bind_host, connect_host = ( + "localhost", + "localhost", + "localhost", + ) + + # allow customizing all nats hosts + if "NATS_STD_BIND_HOST" in os.environ: + std_bind_host = os.getenv("NATS_STD_BIND_HOST") + elif hasattr(settings, "NATS_STD_BIND_HOST"): + std_bind_host = settings.NATS_STD_BIND_HOST + + if "NATS_WS_BIND_HOST" in os.environ: + ws_bind_host = os.getenv("NATS_WS_BIND_HOST") + elif hasattr(settings, "NATS_WS_BIND_HOST"): + ws_bind_host = settings.NATS_WS_BIND_HOST + + if "NATS_CONNECT_HOST" in os.environ: + connect_host = os.getenv("NATS_CONNECT_HOST") + elif hasattr(settings, "NATS_CONNECT_HOST"): + connect_host = settings.NATS_CONNECT_HOST + + return std_bind_host, ws_bind_host, connect_host + + +def get_nats_url() -> str: + _, _, connect_host = get_nats_hosts() + proto = get_nats_internal_protocol() + port, _ = get_nats_ports() + return f"{proto}://{connect_host}:{port}" def date_is_in_past(*, datetime_obj: "datetime", agent_tz: str) -> bool: @@ -72,10 +112,8 @@ def rand_range(min: int, max: int) -> float: def setup_nats_options() -> dict[str, Any]: - nats_std_port, _ = get_nats_ports() - proto = get_nats_internal_protocol() opts = { - "servers": f"{proto}://{settings.ALLOWED_HOSTS[0]}:{nats_std_port}", + "servers": get_nats_url(), "user": "tacticalrmm", "name": "trmm-django", "password": settings.SECRET_KEY, diff --git a/api/tacticalrmm/tacticalrmm/settings.py b/api/tacticalrmm/tacticalrmm/settings.py index 2efa5d50c3..a35d4a8591 100644 --- a/api/tacticalrmm/tacticalrmm/settings.py +++ b/api/tacticalrmm/tacticalrmm/settings.py @@ -20,26 +20,26 @@ AUTH_USER_MODEL = "accounts.User" # latest release -TRMM_VERSION = "0.17.3" +TRMM_VERSION = "0.17.4" # https://github.com/amidaware/tacticalrmm-web -WEB_VERSION = "0.101.38" +WEB_VERSION = "0.101.40" # bump this version everytime vue code is changed # to alert user they need to manually refresh their browser -APP_VER = "0.0.189" +APP_VER = "0.0.190" # https://github.com/amidaware/rmmagent -LATEST_AGENT_VER = "2.6.1" +LATEST_AGENT_VER = "2.6.2" -MESH_VER = "1.1.9" +MESH_VER = "1.1.20" -NATS_SERVER_VER = "2.10.7" +NATS_SERVER_VER = "2.10.10" # for the update script, bump when need to recreate venv -PIP_VER = "41" +PIP_VER = "42" -SETUPTOOLS_VER = "69.0.2" +SETUPTOOLS_VER = "69.0.3" WHEEL_VER = "0.42.0" AGENT_BASE_URL = "https://agents.tacticalrmm.com" @@ -156,7 +156,7 @@ "BACKEND": "tacticalrmm.cache.TacticalRedisCache", "LOCATION": f"redis://{REDIS_HOST}:6379", "OPTIONS": { - "parser_class": "redis.connection.HiredisParser", + "parser_class": "redis.connection._HiredisParser", "pool_class": "redis.BlockingConnectionPool", "db": "10", }, diff --git a/api/tacticalrmm/tacticalrmm/utils.py b/api/tacticalrmm/tacticalrmm/utils.py index 84d07fc2a4..a69bcc9f05 100644 --- a/api/tacticalrmm/tacticalrmm/utils.py +++ b/api/tacticalrmm/tacticalrmm/utils.py @@ -33,6 +33,7 @@ ) from tacticalrmm.helpers import ( get_certs, + get_nats_hosts, get_nats_internal_protocol, get_nats_ports, notify_error, @@ -206,13 +207,16 @@ def reload_nats() -> None: ) cert_file, key_file = get_certs() + nats_std_host, nats_ws_host, _ = get_nats_hosts() nats_std_port, nats_ws_port = get_nats_ports() config = { "authorization": {"users": users}, "max_payload": 67108864, + "host": nats_std_host, "port": nats_std_port, # internal only "websocket": { + "host": nats_ws_host, "port": nats_ws_port, "no_tls": True, # TLS is handled by nginx, so not needed here }, diff --git a/docker/containers/tactical-meshcentral/dockerfile b/docker/containers/tactical-meshcentral/dockerfile index 8bbd587523..0afde7007c 100644 --- a/docker/containers/tactical-meshcentral/dockerfile +++ b/docker/containers/tactical-meshcentral/dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine +FROM node:20-alpine WORKDIR /home/node/app diff --git a/docker/containers/tactical-nats/dockerfile b/docker/containers/tactical-nats/dockerfile index 0a9b00ce7b..e6bacabff7 100644 --- a/docker/containers/tactical-nats/dockerfile +++ b/docker/containers/tactical-nats/dockerfile @@ -1,4 +1,4 @@ -FROM nats:2.10.3-alpine +FROM nats:2.10.9-alpine ENV TACTICAL_DIR /opt/tactical ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready diff --git a/docker/containers/tactical/entrypoint.sh b/docker/containers/tactical/entrypoint.sh index de773c2b8e..6f1957a15a 100644 --- a/docker/containers/tactical/entrypoint.sh +++ b/docker/containers/tactical/entrypoint.sh @@ -159,7 +159,7 @@ fi if [ "$1" = 'tactical-celery' ]; then check_tactical_ready - celery -A tacticalrmm worker --autoscale=30,5 -l info + celery -A tacticalrmm worker --autoscale=20,2 -l info fi if [ "$1" = 'tactical-celerybeat' ]; then diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ab8e43eed3..af1dc193ae 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -52,7 +52,7 @@ services: container_name: trmm-init image: ${IMAGE_REPO}tactical:${VERSION} restart: on-failure - command: [ "tactical-init" ] + command: ["tactical-init"] environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASS: ${POSTGRES_PASS} @@ -84,8 +84,6 @@ services: restart: always environment: API_HOST: ${API_HOST} - ports: - - "4222:4222" volumes: - tactical_data:/opt/tactical networks: @@ -151,7 +149,7 @@ services: container_name: trmm-backend image: ${IMAGE_REPO}tactical:${VERSION} user: 1000:1000 - command: [ "tactical-backend" ] + command: ["tactical-backend"] restart: always networks: - proxy @@ -167,7 +165,7 @@ services: container_name: trmm-websockets image: ${IMAGE_REPO}tactical:${VERSION} user: 1000:1000 - command: [ "tactical-websockets" ] + command: ["tactical-websockets"] restart: always networks: - proxy @@ -205,7 +203,7 @@ services: container_name: trmm-celery image: ${IMAGE_REPO}tactical:${VERSION} user: 1000:1000 - command: [ "tactical-celery" ] + command: ["tactical-celery"] restart: always networks: - redis @@ -222,7 +220,7 @@ services: container_name: trmm-celerybeat image: ${IMAGE_REPO}tactical:${VERSION} user: 1000:1000 - command: [ "tactical-celerybeat" ] + command: ["tactical-celerybeat"] restart: always networks: - proxy diff --git a/go.mod b/go.mod index e03382d133..2ea465b065 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/amidaware/tacticalrmm -go 1.21.4 +go 1.21.6 require ( github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.9 - github.com/nats-io/nats.go v1.31.0 - github.com/ugorji/go/codec v1.2.11 + github.com/nats-io/nats.go v1.32.0 + github.com/ugorji/go/codec v1.2.12 github.com/wh1te909/trmm-shared v0.0.0-20220227075846-f9f757361139 ) @@ -14,10 +14,10 @@ require github.com/sirupsen/logrus v1.9.3 require ( github.com/klauspost/compress v1.17.2 // indirect - github.com/nats-io/nkeys v0.4.6 // indirect + github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/stretchr/testify v1.7.1 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/sys v0.16.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6563c2e641..861606bbb3 100644 --- a/go.sum +++ b/go.sum @@ -12,10 +12,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E= -github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8= -github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY= -github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= +github.com/nats-io/nats.go v1.32.0 h1:Bx9BZS+aXYlxW08k8Gd3yR2s73pV5XSoAQUyp1Kwvp0= +github.com/nats-io/nats.go v1.32.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= +github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -26,15 +26,15 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/wh1te909/trmm-shared v0.0.0-20220227075846-f9f757361139 h1:PfOl03o+Y+svWrfXAAu1QWUDePu1yqTq0pf4rpnN8eA= github.com/wh1te909/trmm-shared v0.0.0-20220227075846-f9f757361139/go.mod h1:ILUz1utl5KgwrxmNHv0RpgMtKeh8gPAABvK2MiXBqv8= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go index 8334fe94f1..5a8eff70cb 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( ) var ( - version = "3.5.0" + version = "3.5.2" log = logrus.New() ) diff --git a/natsapi/bin/nats-api b/natsapi/bin/nats-api index 1b39336a36..35d79eaf30 100755 Binary files a/natsapi/bin/nats-api and b/natsapi/bin/nats-api differ diff --git a/natsapi/bin/nats-api-arm64 b/natsapi/bin/nats-api-arm64 index c8a9a9180c..feb9cbb259 100755 Binary files a/natsapi/bin/nats-api-arm64 and b/natsapi/bin/nats-api-arm64 differ diff --git a/natsapi/utils.go b/natsapi/utils.go index 8f0ad01864..cdc382b1c6 100644 --- a/natsapi/utils.go +++ b/natsapi/utils.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "os" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" @@ -20,7 +20,7 @@ func GetConfig(cfg string) (db *sqlx.DB, r DjangoConfig, err error) { } } - jret, _ := ioutil.ReadFile(cfg) + jret, _ := os.ReadFile(cfg) err = json.Unmarshal(jret, &r) if err != nil { return