From 6d1b446b4666d1cf4f397591858704fc5ba03f0f Mon Sep 17 00:00:00 2001 From: tlindi Date: Thu, 29 May 2025 15:39:06 +0300 Subject: [PATCH 01/12] Split create_application_folders make separate functions for install_folder and storage_folder --- .../standard/var/pynode/application_info.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/rootfs/standard/var/pynode/application_info.py b/rootfs/standard/var/pynode/application_info.py index 643f0fa98..7d13c0789 100644 --- a/rootfs/standard/var/pynode/application_info.py +++ b/rootfs/standard/var/pynode/application_info.py @@ -633,25 +633,39 @@ def create_application_user(app_data): if app_data["requires_docker_image_installation"]: add_user_to_group(username, "docker") -def create_application_folders(app_data): - log_message(" Running create_application_folders...") +def create_application_install_folder(app_data): + log_message(" Running create_application_install_folder...") app_folder = app_data["install_folder"] - data_folder = app_data["storage_folder"] # Clear old data (not storage) if os.path.isdir(app_folder): log_message(" App folder exists, deleting...") run_linux_cmd("rm -rf {}".format(app_folder)) - log_message(" Making application folders...") + log_message(" Making application install folder...") run_linux_cmd("mkdir {}".format(app_folder)) - run_linux_cmd("mkdir -p {}".format(data_folder)) # Set folder permissions (always set for now - could check to see if already proper user) - log_message(" Updating folder permissions...") + log_message(" Updating install folder permissions...") run_linux_cmd("chown -R {}:{} {}".format(app_data["linux_user"], app_data["linux_user"], app_folder)) + +def create_application_storage_folder(app_data): + log_message(" Running create_application_storage_folder...") + data_folder = app_data["storage_folder"] + + log_message(" Making application storage_folder...") + run_linux_cmd("mkdir -p {}".format(data_folder)) + + # Set folder permissions (always set for now - could check to see if already proper user) + log_message(" Updating storage folder permissions...") run_linux_cmd("chown -R {}:{} {}".format(app_data["linux_user"], app_data["linux_user"], data_folder)) +def create_application_folders(app_data): + log_message(" Running create_application_folders...") + + create_application_install_folder(app_data) + create_application_storage_folder(app_data) + def create_application_tor_service(app_data): has_ports = False run_linux_cmd("mkdir -p /etc/torrc.d") From 2b409e1eebf1852d8031a83b39f8af3b2181168a Mon Sep 17 00:00:00 2001 From: tlindi Date: Thu, 29 May 2025 22:20:47 +0300 Subject: [PATCH 02/12] Import enable_disable_functions for service stop and start --- rootfs/standard/var/pynode/application_info.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rootfs/standard/var/pynode/application_info.py b/rootfs/standard/var/pynode/application_info.py index 7d13c0789..24b9a71b8 100644 --- a/rootfs/standard/var/pynode/application_info.py +++ b/rootfs/standard/var/pynode/application_info.py @@ -6,6 +6,7 @@ from drive_info import * from systemctl_info import * from utilities import * +from enable_disable_functions import * import copy import json import time From 0ec897d16f5cb3463733c49e50348863bb1d839b Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 23 May 2025 20:16:32 +0300 Subject: [PATCH 03/12] Implement app_data_manageable --- rootfs/standard/var/pynode/application_info.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rootfs/standard/var/pynode/application_info.py b/rootfs/standard/var/pynode/application_info.py index 24b9a71b8..62729eddd 100644 --- a/rootfs/standard/var/pynode/application_info.py +++ b/rootfs/standard/var/pynode/application_info.py @@ -242,6 +242,7 @@ def initialize_application_defaults(app): if not "app_page_show_open_button" in app: app["app_page_show_open_button"] = True if not "app_page_additional_buttons" in app: app["app_page_additional_buttons"] = [] if not "app_page_content" in app: app["app_page_content"] = [] + if not "data_manageable" in app: app["data_manageable"] = False # Update fields that may use variables that need replacing, like {VERSION}, {SHORT_NAME}, etc... app["download_source_url"] = replace_app_info_variables(app, app["download_source_url"]) From 481df87603985851a7dce1575cd7dcafdc542b8e Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 23 May 2025 19:52:58 +0300 Subject: [PATCH 04/12] Implement App data management to API Update api.py --- rootfs/standard/var/www/mynode/api.py | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/rootfs/standard/var/www/mynode/api.py b/rootfs/standard/var/www/mynode/api.py index 8d950d884..881f63585 100644 --- a/rootfs/standard/var/www/mynode/api.py +++ b/rootfs/standard/var/www/mynode/api.py @@ -149,6 +149,46 @@ def api_restart_app(): return "OK" +# @mynode_api.route("/api/restart_app") +@mynode_api.route("/api/backup_app_data") +def api_backup_data_folder(): + check_logged_in() + + app = request.args.get("app") + if not app: + return "NO_APP_SPECIFIED" + if not is_application_valid(app): + return "INVALID_APP_NAME" + if not backup_data_folder(app): + return "ERROR" + return "OK" + +@mynode_api.route("/api/restore_data_folder") +def api_restore_data_folder(): + check_logged_in() + + app = request.args.get("app") + if not app: + return "NO_APP_SPECIFIED" + if not is_application_valid(app): + return "INVALID_APP_NAME" + if not restore_data_folder(app): + return "ERROR" + return "OK" + +@mynode_api.route("/api/reset_data_folder") +def api_reset_data_folder(): + check_logged_in() + + app = request.args.get("app") + if not app: + return "NO_APP_SPECIFIED" + if not is_application_valid(app): + return "INVALID_APP_NAME" + if not reset__data_folder(app): + return "ERROR" + return "OK" + @mynode_api.route("/api/get_device_info") def api_get_device_info(): check_logged_in() From cbdddc40064113ee97a9e527d203575b3040057d Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 23 May 2025 19:15:47 +0300 Subject: [PATCH 05/12] Implement App data management to js --- .../var/www/mynode/static/js/manage_apps.js | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/rootfs/standard/var/www/mynode/static/js/manage_apps.js b/rootfs/standard/var/www/mynode/static/js/manage_apps.js index f7a3b5ee2..a3dc80faa 100644 --- a/rootfs/standard/var/www/mynode/static/js/manage_apps.js +++ b/rootfs/standard/var/www/mynode/static/js/manage_apps.js @@ -127,4 +127,53 @@ function toggleEnabled(short_name, full_name, enable, return_page="") { window.location.href="/toggle-enabled?app="+short_name+r }); } +} + +// ========================================== +// Manage app storage (data_folder) +// ========================================== + +function backup_data_folder_via_api(name, short_name) { + if ( confirm("Are you sure you want to backup "+name+"? This will stop, backup data and start app.") ) { + $('#loading_spinner_message').html("Making backup..."); + $('#loading_spinner_overlay').fadeIn(); + $.get('/api/backup_app_data?app='+short_name) + .done(function( data ) { + if (data != "OK") { + alert("Error backupping app data: "+data) + } + $('#loading_spinner_overlay').fadeOut(); + } + ); + } +} + +function restore_data_folder_via_api(name, short_name) { + if ( confirm("Are you sure you want to restore "+name+"? This will stop, DELETE DATA, restore backup and start app.") ) { + $('#loading_spinner_message').html("Restoring..."); + $('#loading_spinner_overlay').fadeIn(); + $.get('/api/restore_app_data?app='+short_name) + .done(function( data ) { + if (data != "OK") { + alert("Error restoring app data: "+data) + } + $('#loading_spinner_overlay').fadeOut(); + } + ); + } +} + +function reset_data_folder_via_api(name, short_name) { + if ( confirm("Are you sure you want to reset "+name+"? This will stop app, RESET ALL THE APP DATA and start app.") ) { + $('#loading_spinner_message').html("Reseting app..."); + $('#loading_spinner_overlay').fadeIn(); + $.get('/api/remove_app_data?app='+short_name) + .done(function( data ) { + if (data != "OK") { + alert("Error removing app data: "+data) + } + $('#loading_spinner_overlay').fadeOut(); + } + ); + } } \ No newline at end of file From 603288445e850869ece3231b31a4a6133f7fb4aa Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 23 May 2025 20:45:07 +0300 Subject: [PATCH 06/12] Implement data_manageable to template generic_app.html --- .../standard/var/www/mynode/templates/app/generic_app.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rootfs/standard/var/www/mynode/templates/app/generic_app.html b/rootfs/standard/var/www/mynode/templates/app/generic_app.html index f1aad2b6f..3c1d1cdd4 100644 --- a/rootfs/standard/var/www/mynode/templates/app/generic_app.html +++ b/rootfs/standard/var/www/mynode/templates/app/generic_app.html @@ -82,6 +82,12 @@ {% if app.is_enabled %} + {% if app.is_enabled and app.data_manageable %} + + + + {% endif %} + {% for btn in app.app_page_additional_buttons %} {% if app.is_enabled and app.data_manageable %} - - - + + + {% endif %} {% for btn in app.app_page_additional_buttons %} From 41660c4cd31ba61e2eeac58222fa4be42f6351f6 Mon Sep 17 00:00:00 2001 From: tlindi Date: Thu, 29 May 2025 20:56:02 +0300 Subject: [PATCH 08/12] Implement reset_data functionality --- .../standard/var/pynode/application_info.py | 38 +++++++++++++++++++ .../www/mynode/templates/app/generic_app.html | 4 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/rootfs/standard/var/pynode/application_info.py b/rootfs/standard/var/pynode/application_info.py index 62729eddd..f4bf78b3b 100644 --- a/rootfs/standard/var/pynode/application_info.py +++ b/rootfs/standard/var/pynode/application_info.py @@ -754,6 +754,44 @@ def restart_application(short_name): except Exception as e: return False +def backup_data_folder(app_data): + log_message(" Running backup_data_folder...") + +def restore_data_folder(app_data): + log_message(" Running restore_data_folder...") + +def reset_data_folder(app_data): + log_message(" Running reset_data_folder...") + + # If app_data is a string (short name), convert it to a full configuration + # and preserve the original short name. + if isinstance(app_data, str): + original_short_name = app_data # Save the short name for service commands. + app_data = get_application(app_data) + else: + original_short_name = app_data["short_name"] + + data_folder = app_data["storage_folder"] + + # Stop the service before removing data_folder + log_message("Stopping '{}'.".format(original_short_name)) + stop_service(original_short_name) + + # Remove App data_folder + log_message("Removing storage folder '{}' of '{}'...".format(data_folder, original_short_name)) + data_folder = app_data["storage_folder"] + run_linux_cmd("rm -rf {}".format(data_folder)) + + # Re-create the storage folder + log_message("Creating storage folder '{}' of '{}'...".format(data_folder, original_short_name)) + create_application_storage_folder(app_data) + + # Re-start the service + log_message("Starting '{}'.".format(original_short_name)) + start_service(original_short_name) + + return True + ###################################################################################### ## Bulk Application Actions ###################################################################################### diff --git a/rootfs/standard/var/www/mynode/templates/app/generic_app.html b/rootfs/standard/var/www/mynode/templates/app/generic_app.html index 7f01cae05..01a17ed61 100644 --- a/rootfs/standard/var/www/mynode/templates/app/generic_app.html +++ b/rootfs/standard/var/www/mynode/templates/app/generic_app.html @@ -83,9 +83,9 @@ {% if app.is_enabled and app.data_manageable %} - + {% endif %} {% for btn in app.app_page_additional_buttons %} From 8d0560be40e07a7500d7dccdb269960ee63c913a Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 23 May 2025 19:52:58 +0300 Subject: [PATCH 09/12] simplify API of reset Implement simplification per requested at https://github.com/mynodebtc/mynode/pull/945#discussion_r2125608600 --- .../standard/var/pynode/application_info.py | 33 ++++++++----------- rootfs/standard/var/www/mynode/api.py | 25 +++++++------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/rootfs/standard/var/pynode/application_info.py b/rootfs/standard/var/pynode/application_info.py index f4bf78b3b..2fb8cb756 100644 --- a/rootfs/standard/var/pynode/application_info.py +++ b/rootfs/standard/var/pynode/application_info.py @@ -760,35 +760,30 @@ def backup_data_folder(app_data): def restore_data_folder(app_data): log_message(" Running restore_data_folder...") -def reset_data_folder(app_data): - log_message(" Running reset_data_folder...") - - # If app_data is a string (short name), convert it to a full configuration - # and preserve the original short name. - if isinstance(app_data, str): - original_short_name = app_data # Save the short name for service commands. - app_data = get_application(app_data) - else: - original_short_name = app_data["short_name"] - +def reset_data_folder(short_name): + log_message(f" Running reset_data_folder for '{short_name}'...") + + app_data = get_application(short_name) + if not app_data: + log_message(f" ERROR: application '{short_name}' not found") + return False data_folder = app_data["storage_folder"] # Stop the service before removing data_folder - log_message("Stopping '{}'.".format(original_short_name)) - stop_service(original_short_name) + log_message(f" Stopping '{short_name}'…") + stop_service(short_name) # Remove App data_folder - log_message("Removing storage folder '{}' of '{}'...".format(data_folder, original_short_name)) - data_folder = app_data["storage_folder"] - run_linux_cmd("rm -rf {}".format(data_folder)) + log_message(f" Removing storage folder '{data_folder}'…") + run_linux_cmd(f"rm -rf {data_folder}") # Re-create the storage folder - log_message("Creating storage folder '{}' of '{}'...".format(data_folder, original_short_name)) + log_message(f" Creating storage folder '{data_folder}'…") create_application_storage_folder(app_data) # Re-start the service - log_message("Starting '{}'.".format(original_short_name)) - start_service(original_short_name) + log_message(f" Starting '{short_name}'…") + start_service(short_name) return True diff --git a/rootfs/standard/var/www/mynode/api.py b/rootfs/standard/var/www/mynode/api.py index c7e17f5d9..d87848d6b 100644 --- a/rootfs/standard/var/www/mynode/api.py +++ b/rootfs/standard/var/www/mynode/api.py @@ -149,17 +149,16 @@ def api_restart_app(): return "OK" -# @mynode_api.route("/api/restart_app") @mynode_api.route("/api/backup_data_folder") def api_backup_data_folder(): check_logged_in() - app = request.args.get("app") - if not app: + short_name = request.args.get("short_name") + if not short_name: return "NO_APP_SPECIFIED" - if not is_application_valid(app): + if not is_application_valid(short_name): return "INVALID_APP_NAME" - if not backup_data_folder(app): + if not backup_data_folder(short_name): return "ERROR" return "OK" @@ -167,12 +166,12 @@ def api_backup_data_folder(): def api_restore_data_folder(): check_logged_in() - app = request.args.get("app") - if not app: + short_name = request.args.get("short_name") + if not short_name: return "NO_APP_SPECIFIED" - if not is_application_valid(app): + if not is_application_valid(short_name): return "INVALID_APP_NAME" - if not restore_data_folder(app): + if not restore_data_folder(short_name): return "ERROR" return "OK" @@ -180,12 +179,12 @@ def api_restore_data_folder(): def api_reset_data_folder(): check_logged_in() - app = request.args.get("app") - if not app: + short_name = request.args.get("short_name") + if not short_name: return "NO_APP_SPECIFIED" - if not is_application_valid(app): + if not is_application_valid(short_name): return "INVALID_APP_NAME" - if not reset_data_folder(app): + if not reset_data_folder(short_name): return "ERROR" return "OK" From 421ef25d61ec6c75b3c62e918b4628981d8c3952 Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 13 Jun 2025 17:15:58 +0300 Subject: [PATCH 10/12] [FIX] Implement App data management on js --- rootfs/standard/var/www/mynode/static/js/manage_apps.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rootfs/standard/var/www/mynode/static/js/manage_apps.js b/rootfs/standard/var/www/mynode/static/js/manage_apps.js index 75a02f964..c740f7f9a 100644 --- a/rootfs/standard/var/www/mynode/static/js/manage_apps.js +++ b/rootfs/standard/var/www/mynode/static/js/manage_apps.js @@ -137,7 +137,7 @@ function backup_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to backup "+name+"? This will stop, backup data and start app.") ) { $('#loading_spinner_message').html("Making backup..."); $('#loading_spinner_overlay').fadeIn(); - $.get('/api/backup_data_folder?app='+short_name) + $.get('/api/backup_data_folder?short_name=' + short_name) .done(function( data ) { if (data != "OK") { alert("Error backupping app data: "+data) @@ -152,7 +152,7 @@ function restore_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to restore "+name+"? This will stop, DELETE DATA, restore backup and start app.") ) { $('#loading_spinner_message').html("Restoring..."); $('#loading_spinner_overlay').fadeIn(); - $.get('/api/restore_data_folder?app='+short_name) + $.get('/api/restore_data_folder?short_name=' + short_name) .done(function( data ) { if (data != "OK") { alert("Error restoring app data: "+data) @@ -167,7 +167,7 @@ function reset_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to reset "+name+"? This will stop app, RESET ALL THE APP DATA and start app.") ) { $('#loading_spinner_message').html("Reseting app..."); $('#loading_spinner_overlay').fadeIn(); - $.get('/api/reset_data_folder?app='+short_name) + $.get('/api/reset_data_folder?short_name=' + short_name) .done(function( data ) { if (data != "OK") { alert("Error removing app data: "+data) From b3585e2c6fd1bc89f1c234877e4ce745b820ff60 Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 20 Jun 2025 14:15:30 +0300 Subject: [PATCH 11/12] [FIX TYPOS] Update manage_apps.js --- rootfs/standard/var/www/mynode/static/js/manage_apps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rootfs/standard/var/www/mynode/static/js/manage_apps.js b/rootfs/standard/var/www/mynode/static/js/manage_apps.js index c740f7f9a..f351cc908 100644 --- a/rootfs/standard/var/www/mynode/static/js/manage_apps.js +++ b/rootfs/standard/var/www/mynode/static/js/manage_apps.js @@ -140,7 +140,7 @@ function backup_data_folder_via_api(name, short_name) { $.get('/api/backup_data_folder?short_name=' + short_name) .done(function( data ) { if (data != "OK") { - alert("Error backupping app data: "+data) + alert("Error backing up app data: "+data) } $('#loading_spinner_overlay').fadeOut(); } @@ -165,7 +165,7 @@ function restore_data_folder_via_api(name, short_name) { function reset_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to reset "+name+"? This will stop app, RESET ALL THE APP DATA and start app.") ) { - $('#loading_spinner_message').html("Reseting app..."); + $('#loading_spinner_message').html("Resetting app..."); $('#loading_spinner_overlay').fadeIn(); $.get('/api/reset_data_folder?short_name=' + short_name) .done(function( data ) { From 911f437148d2127f0b84ac0ed9285a990680a949 Mon Sep 17 00:00:00 2001 From: tlindi Date: Fri, 20 Jun 2025 14:18:03 +0300 Subject: [PATCH 12/12] Revert "[FIX] Implement App data management on js" This reverts commit 9f01df1af93fd2372668c59532e3f4be5c5ce827. --- rootfs/standard/var/www/mynode/static/js/manage_apps.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rootfs/standard/var/www/mynode/static/js/manage_apps.js b/rootfs/standard/var/www/mynode/static/js/manage_apps.js index f351cc908..4c33e7b17 100644 --- a/rootfs/standard/var/www/mynode/static/js/manage_apps.js +++ b/rootfs/standard/var/www/mynode/static/js/manage_apps.js @@ -137,7 +137,7 @@ function backup_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to backup "+name+"? This will stop, backup data and start app.") ) { $('#loading_spinner_message').html("Making backup..."); $('#loading_spinner_overlay').fadeIn(); - $.get('/api/backup_data_folder?short_name=' + short_name) + $.get('/api/backup_data_folder?app='+short_name) .done(function( data ) { if (data != "OK") { alert("Error backing up app data: "+data) @@ -152,7 +152,7 @@ function restore_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to restore "+name+"? This will stop, DELETE DATA, restore backup and start app.") ) { $('#loading_spinner_message').html("Restoring..."); $('#loading_spinner_overlay').fadeIn(); - $.get('/api/restore_data_folder?short_name=' + short_name) + $.get('/api/restore_data_folder?app='+short_name) .done(function( data ) { if (data != "OK") { alert("Error restoring app data: "+data) @@ -167,7 +167,7 @@ function reset_data_folder_via_api(name, short_name) { if ( confirm("Are you sure you want to reset "+name+"? This will stop app, RESET ALL THE APP DATA and start app.") ) { $('#loading_spinner_message').html("Resetting app..."); $('#loading_spinner_overlay').fadeIn(); - $.get('/api/reset_data_folder?short_name=' + short_name) + $.get('/api/reset_data_folder?app='+short_name) .done(function( data ) { if (data != "OK") { alert("Error removing app data: "+data)