From c7d1c0e18f290749f5a080d309d4c97f27f59df6 Mon Sep 17 00:00:00 2001 From: TheDevRyan <175502913+The-Dev-Ryan@users.noreply.github.com> Date: Fri, 24 Oct 2025 11:53:37 +0100 Subject: [PATCH] Add advanced custom CSS/JS file support per template (#7361) Introduces a new 'customFiles' array in the config schema for granular domain-level CSS and JavaScript customization. Updates webserver logic to inject custom CSS/JS tags based on template scope, replacing static custom.css/custom.js references in all Handlebars views. Also updates meshctrl.js to support the new config property. --- meshcentral-config-schema.json | 35 ++++++++++++ meshctrl.js | 4 +- views/agentinvite.handlebars | 4 +- views/default-mobile.handlebars | 2 + views/default.handlebars | 4 +- views/default3.handlebars | 4 +- views/download.handlebars | 2 + views/download2.handlebars | 2 + views/error404-mobile.handlebars | 2 + views/error404.handlebars | 2 + views/error4042.handlebars | 2 + views/invite.handlebars | 4 +- views/login-mobile.handlebars | 2 + views/login.handlebars | 2 + views/login2.handlebars | 4 +- views/message.handlebars | 2 + views/message2.handlebars | 4 +- views/messenger.handlebars | 4 +- views/mstsc.handlebars | 2 + views/player.handlebars | 4 +- views/sharing-mobile.handlebars | 2 + views/sharing.handlebars | 4 +- views/ssh.handlebars | 2 + views/terms-mobile.handlebars | 2 + views/terms.handlebars | 2 + views/xterm.handlebars | 2 + webserver.js | 92 +++++++++++++++++++++++++++++++- 27 files changed, 176 insertions(+), 21 deletions(-) diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 9d71e2f0..77504a7e 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -1987,6 +1987,41 @@ "customUI": { "type": "object" }, + "customFiles": { + "type": "array", + "description": "Advanced customization system allowing multiple CSS and JavaScript file configurations per domain. Each configuration can target specific templates using the 'scope' property, enabling granular control over where custom files are loaded. All custom files are loaded after the default custom.css and custom.js files.", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique name for this custom files configuration. Used for identification and debugging purposes." + }, + "css": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered list of custom CSS files to load. Files are loaded in the specified order after the default custom.css. Place files in the meshcentral-data/public/styles/ directory. Example: [\"theme.css\", \"overrides.css\", \"mobile.css\"]" + }, + "js": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Ordered list of custom JavaScript files to load. Files are loaded in the specified order after the default custom.js. Place files in the meshcentral-data/public/scripts/ directory. Example: [\"analytics.js\", \"custom-functions.js\", \"ui-enhancements.js\"]" + }, + "scope": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of template names where these custom files should be loaded. If not specified or empty, files will NOT be loaded anywhere. Use 'all' to load on all templates. Available template names: all, default, default3, default-mobile, login, login2, login-mobile, messenger, message, message2, invite, agentinvite, player, xterm, mstsc, ssh, sharing, sharing-mobile, download, download2, terms, terms-mobile, error404, error4042, error404-mobile. Example: [\"default3\", \"login2\", \"player\"] or [\"all\"]" + } + }, + "required": ["name"] + } + }, "consentMessages": { "type": "object", "description": "This section is used to customize user consent prompts, these show up when asking if a remote session is allowed or not.", diff --git a/meshctrl.js b/meshctrl.js index 8a747db8..023fd10e 100644 --- a/meshctrl.js +++ b/meshctrl.js @@ -1195,8 +1195,8 @@ function displayConfigHelp() { } function performConfigOperations(args) { - var domainValues = ['title', 'title2', 'titlepicture', 'trustedcert', 'welcomepicture', 'welcometext', 'userquota', 'meshquota', 'newaccounts', 'usernameisemail', 'newaccountemaildomains', 'newaccountspass', 'newaccountsrights', 'geolocation', 'lockagentdownload', 'userconsentflags', 'Usersessionidletimeout', 'auth', 'ldapoptions', 'ldapusername', 'ldapuserbinarykey', 'ldapuseremail', 'footer', 'certurl', 'loginKey', 'userallowedip', 'agentallowedip', 'agentnoproxy', 'agentconfig', 'orphanagentuser', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording', 'hide']; - var domainObjectValues = ['ldapoptions', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording']; + var domainValues = ['title', 'title2', 'titlepicture', 'trustedcert', 'welcomepicture', 'welcometext', 'userquota', 'meshquota', 'newaccounts', 'usernameisemail', 'newaccountemaildomains', 'newaccountspass', 'newaccountsrights', 'geolocation', 'lockagentdownload', 'userconsentflags', 'Usersessionidletimeout', 'auth', 'ldapoptions', 'ldapusername', 'ldapuserbinarykey', 'ldapuseremail', 'footer', 'certurl', 'loginKey', 'userallowedip', 'agentallowedip', 'agentnoproxy', 'agentconfig', 'orphanagentuser', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording', 'hide', 'customFiles']; + var domainObjectValues = ['ldapoptions', 'httpheaders', 'yubikey', 'passwordrequirements', 'limits', 'amtacmactivation', 'redirects', 'sessionrecording', 'customFiles']; var domainArrayValues = ['newaccountemaildomains', 'newaccountsrights', 'loginkey', 'agentconfig']; var configChange = false; var fs = require('fs'); diff --git a/views/agentinvite.handlebars b/views/agentinvite.handlebars index e6d29910..eb81dfaf 100644 --- a/views/agentinvite.handlebars +++ b/views/agentinvite.handlebars @@ -8,11 +8,11 @@ - + {{{customCSSTags}}} - + {{{customJSTags}}} Agent Installation