Skip to content
61 changes: 55 additions & 6 deletions rootfs/standard/var/pynode/application_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -241,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"])
Expand Down Expand Up @@ -633,25 +635,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")
Expand Down Expand Up @@ -738,6 +754,39 @@ 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(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(f" Stopping '{short_name}'…")
stop_service(short_name)

# Remove App 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(f" Creating storage folder '{data_folder}'…")
create_application_storage_folder(app_data)

# Re-start the service
log_message(f" Starting '{short_name}'…")
start_service(short_name)

return True

######################################################################################
## Bulk Application Actions
######################################################################################
Expand Down
39 changes: 39 additions & 0 deletions rootfs/standard/var/www/mynode/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,45 @@ def api_restart_app():

return "OK"

@mynode_api.route("/api/backup_data_folder")
def api_backup_data_folder():
check_logged_in()

short_name = request.args.get("short_name")
if not short_name:
return "NO_APP_SPECIFIED"
if not is_application_valid(short_name):
return "INVALID_APP_NAME"
if not backup_data_folder(short_name):
return "ERROR"
return "OK"

@mynode_api.route("/api/restore_data_folder")
def api_restore_data_folder():
check_logged_in()

short_name = request.args.get("short_name")
if not short_name:
return "NO_APP_SPECIFIED"
if not is_application_valid(short_name):
return "INVALID_APP_NAME"
if not restore_data_folder(short_name):
return "ERROR"
return "OK"

@mynode_api.route("/api/reset_data_folder")
def api_reset_data_folder():
check_logged_in()

short_name = request.args.get("short_name")
if not short_name:
return "NO_APP_SPECIFIED"
if not is_application_valid(short_name):
return "INVALID_APP_NAME"
if not reset_data_folder(short_name):
return "ERROR"
return "OK"

@mynode_api.route("/api/get_device_info")
def api_get_device_info():
check_logged_in()
Expand Down
49 changes: 49 additions & 0 deletions rootfs/standard/var/www/mynode/static/js/manage_apps.js
Original file line number Diff line number Diff line change
Expand Up @@ -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_data_folder?app='+short_name)
.done(function( data ) {
if (data != "OK") {
alert("Error backing up 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_data_folder?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("Resetting app...");
$('#loading_spinner_overlay').fadeIn();
$.get('/api/reset_data_folder?app='+short_name)
.done(function( data ) {
if (data != "OK") {
alert("Error removing app data: "+data)
}
$('#loading_spinner_overlay').fadeOut();
}
);
}
}
6 changes: 6 additions & 0 deletions rootfs/standard/var/www/mynode/templates/app/generic_app.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@
{% if app.is_enabled %}
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="restart_app_via_api('{{ app.name }}', '{{ app.short_name }}');">Restart</button>

{% if app.is_enabled and app.data_manageable %}
<!-- <button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="backup_data_folder_via_api('{{ app.name }}', '{{ app.short_name }}');">Backup Data</button>
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="restore_data_folder_via_api('{{ app.name }}', '{{ app.short_name }}');">Restore Data</button>
--> <button class="ui-button ui-widget ui-corner-all mynode_button app_page_button" onclick="reset_data_folder_via_api('{{ app.name }}', '{{ app.short_name }}');">Reset Data</button>
{% endif %}

{% for btn in app.app_page_additional_buttons %}
<button class="ui-button ui-widget ui-corner-all mynode_button app_page_button"
{% if btn.href is defined and btn.href != "" %}
Expand Down