feat: add Melo's stylish UI support for Docker containers (#7475)

* feat: add Melo's stylish UI support for containers * chore: add fallback echo * feat: integrate suggestions * chore: edit Debian Dockerfile as well * chore: bump to absolute paths * chore: rename variable --------- Co-authored-by: DaanSelen <dselen@systemec.nl>

DaanSelen committed Dec 16, 2025 at 15:19 UTC a0a6b4a9e564b229771c54bb171e8b928581dbde
3 files changed +124 -34
docker/Dockerfile
+6 -4
@@ -78,14 +78,16 @@ ENV NODE_ENV="production" \
78 ENV ALLOW_PLUGINS="false" \
79 ALLOW_NEW_ACCOUNTS="false" \
80 ALLOWED_ORIGIN="false" \
81 + HOSTNAME="localhost" \
82 + INSTALL_STYLISHUI="false" \
83 IFRAME="false" \
82 - REGEN_SESSIONKEY="false" \
83 - WEBRTC="false" \
84 LOCAL_SESSION_RECORDING="true" \
85 MINIFY="true" \
86 - HOSTNAME="localhost" \
86 REVERSE_PROXY="" \
87 REVERSE_PROXY_TLS_PORT="443" \
88 + REGEN_SESSIONKEY="false" \
89 + STYLISHUI_FORCE_LATEST="false" \
90 + WEBRTC="false" \
91 TRUSTED_PROXY="" \
92 ARGS=""
93
@@ -208,4 +210,4 @@ VOLUME /opt/meshcentral/meshcentral-backups
210 COPY ./docker/entrypoint.sh /opt/meshcentral/entrypoint.sh
211 COPY ./docker/config.json.template /opt/meshcentral/config.json.template
212
211 -ENTRYPOINT ["bash", "/opt/meshcentral/entrypoint.sh"]
\ No newline at end of file
213 +ENTRYPOINT ["/bin/bash", "/opt/meshcentral/entrypoint.sh"]
docker/Dockerfile-debian
+6 -4
@@ -104,14 +104,16 @@ ENV NODE_ENV="production" \
104 ENV ALLOW_PLUGINS="false" \
105 ALLOW_NEW_ACCOUNTS="false" \
106 ALLOWED_ORIGIN="false" \
107 + HOSTNAME="localhost" \
108 + INSTALL_STYLISHUI="false" \
109 IFRAME="false" \
108 - REGEN_SESSIONKEY="false" \
109 - WEBRTC="false" \
110 LOCAL_SESSION_RECORDING="true" \
111 MINIFY="true" \
112 - HOSTNAME="localhost" \
112 REVERSE_PROXY="" \
113 REVERSE_PROXY_TLS_PORT="443" \
114 + REGEN_SESSIONKEY="false" \
115 + STYLISHUI_FORCE_LATEST="false" \
116 + WEBRTC="false" \
117 TRUSTED_PROXY="" \
118 ARGS=""
119
@@ -235,4 +237,4 @@ VOLUME /opt/meshcentral/meshcentral-backups
237 COPY ./docker/entrypoint.sh /opt/meshcentral/entrypoint.sh
238 COPY ./docker/config.json.template /opt/meshcentral/config.json.template
239
238 -ENTRYPOINT ["bash", "/opt/meshcentral/entrypoint.sh"]
\ No newline at end of file
240 +ENTRYPOINT ["/bin/bash", "/opt/meshcentral/entrypoint.sh"]
docker/entrypoint.sh
+112 -26
@@ -1,6 +1,10 @@
1 #!/bin/bash
2
3 -graceful_shutdown() {
3 +# Origin: https://github.com/Melo-Professional/MeshCentral-Stylish-UI
4 +stylishui_base_url="https://github.com/Melo-Professional/MeshCentral-Stylish-UI/archive/refs"
5 +stylishui_compat="https://raw.githubusercontent.com/Melo-Professional/MeshCentral-Stylish-UI/refs/heads/main/metadata/compat.json"
6 +
7 +function graceful_shutdown() {
8 echo "Received SIGTERM from the container host. Cleaning up..."
9 kill -SIGINT $meshcentral_pid
10
@@ -9,33 +13,18 @@ graceful_shutdown() {
13 }
14 trap graceful_shutdown SIGTERM
15
12 -### Start MeshCentral Docker Container.
13 -
14 -# Make the start more cleared when restarted.
15 -echo "-------------------------------------------------------------"
16 -date
17 -if [ -n "$CONFIG_FILE" ]; then
18 - echo "Config file: $CONFIG_FILE"
19 -else
20 - exit 1
21 -fi
22 -
23 -# Failsafe to create a new config if the expected config is not there.
24 -if [ -f "${CONFIG_FILE}" ]; then
25 - echo "Pre-existing config found, not recreating..."
26 -else
27 - if [ ! -d $(dirname "$CONFIG_FILE") ]; then
28 - echo "Creating meshcentral-data directory..."
29 - mkdir -p /opt/meshcentral/meshcentral-data
16 +function test_url() {
17 + wget --spider $1 &> /dev/null
18 + if [[ $? -eq 0 ]]; then
19 + echo "is ok."
20 + return 0
21 + else
22 + echo "is NOT ok."
23 + return 1
24 fi
25 +}
26
32 - echo "Placing template into the relevant directory: $(dirname $CONFIG_FILE)"
33 - cp /opt/meshcentral/config.json.template "${CONFIG_FILE}"
34 -fi
35 -
36 -if [[ ${DYNAMIC_CONFIG,,} =~ ^(true|yes)$ ]]; then
37 - echo "Using Dynamic Configuration values..."
38 -
27 +function dynamic_config() {
28 # BEGIN DATABASE CONFIGURATION FIELDS
29 USE_MONGODB=${USE_MONGODB,,}
30 if [[ $USE_MONGODB =~ ^(true|yes)$ ]]; then
@@ -269,12 +258,109 @@ if [[ ${DYNAMIC_CONFIG,,} =~ ^(true|yes)$ ]]; then
258 fi
259
260 cat "$CONFIG_FILE"
261 +}
262 +
263 +function install_stylishui() {
264 + # Start by testing if we can determine compatibility
265 + printf "Testing compatibility schema URL..."
266 + if ! test_url $stylishui_compat; then
267 + echo "Compat URL failed."
268 + return 1
269 + fi
270 +
271 + if [[ ${STYLISHUI_FORCE_LATEST,,} =~ ^(true|yes)$ ]]; then
272 + echo "Overriding to main branch..."
273 + full_url="${stylishui_base_url}/heads/main.tar.gz"
274 + else
275 + # Retrieve the values we need to determine compatibility
276 + compat_data=$(curl -fsSL $stylishui_compat)
277 + meshcentral_version=$(jq -r '.version' /opt/meshcentral/meshcentral/package.json)
278 + # Target the StylishUI version we need for our present Meshcentral version
279 + compat_version=$(echo "$compat_data" | jq -r --arg mcv "$meshcentral_version" \
280 + '.compatibility[] | select(.meshcentral==$mcv) | .stylishui')
281 + echo "Data: MeshCentral: $meshcentral_version, matched StylishUI: $compat_version"
282 +
283 + # From the data gathered above, compile the whole URL.
284 + full_url="${stylishui_base_url}/tags/${compat_version}.tar.gz"
285 + fi
286 +
287 + # Test if we can reach the data/content URL on github
288 + printf "Testing content URL..."
289 + if ! test_url $full_url; then
290 + echo "StylishUI URL failed."
291 + return 1
292 + fi
293 +
294 + # Lets download and install the UI
295 + wget -O /tmp/stylishui.tar.gz $full_url > /dev/null
296 + tar -xzf /tmp/stylishui.tar.gz -C /tmp
297 + web_folder=$(find /tmp -name meshcentral-web)
298 +
299 + # Check if we have some integrity
300 + if [[ -z $web_folder ]]; then
301 + echo "Installation failed, cleaning..."
302 + rm /tmp/stylishui*
303 + return 1
304 + fi
305 +
306 + # Looks good!
307 + echo Found extracted contents at: $web_folder
308 + mv ${web_folder}/* /opt/meshcentral/meshcentral-web
309 + rm /tmp/stylishui*
310 +
311 + return 0
312 +}
313 +
314 +### Start MeshCentral Docker Container.
315 +
316 +### BEGIN MAIN CHAIN
317 +
318 +# Make the start more cleared when restarted.
319 +echo "-------------------------------------------------------------"
320 +date
321 +if [ -n "$CONFIG_FILE" ]; then
322 + echo "Config file: $CONFIG_FILE"
323 +else
324 + exit 1
325 +fi
326 +
327 +# Failsafe to create a new config if the expected config is not there.
328 +if [ -f "${CONFIG_FILE}" ]; then
329 + echo "Pre-existing config found, not recreating..."
330 +else
331 + if [ ! -d $(dirname "$CONFIG_FILE") ]; then
332 + echo "Creating meshcentral-data directory..."
333 + mkdir -p /opt/meshcentral/meshcentral-data
334 + fi
335 +
336 + echo "Placing template into the relevant directory: $(dirname $CONFIG_FILE)"
337 + cp /opt/meshcentral/config.json.template "${CONFIG_FILE}"
338 +fi
339 +
340 +if [[ ${DYNAMIC_CONFIG,,} =~ ^(true|yes)$ ]]; then
341 + echo "-------------------------------------------------------------"
342 + echo "Using Dynamic Configuration values..."
343 + dynamic_config
344 + echo "-------------------------------------------------------------"
345 else
346 echo "Leaving config as-is. Dynamic Configuration is off."
347 fi
348
349 +if [[ ${INSTALL_STYLISHUI,,} =~ ^(true|yes)$ ]]; then
350 + echo "-------------------------------------------------------------"
351 + echo "Reached StylishUI install trigger, installing..."
352 + if ! install_stylishui; then
353 + echo "Something fatal happened in the StylishUI install. Skipping..."
354 + else
355 + echo "StylishUI has been installed!"
356 + fi
357 + echo "-------------------------------------------------------------"
358 +fi
359 +
360 # Actually start MeshCentral.
361 node /opt/meshcentral/meshcentral/meshcentral --configfile "${CONFIG_FILE}" "${ARGS}" >> /proc/1/fd/1 &
362 meshcentral_pid=$!
363
364 wait "$meshcentral_pid"
365 +
366 +### END MAIN CHAIN