mirror of
https://github.com/unraid/api.git
synced 2026-01-22 00:19:56 -06:00
1629 lines
52 KiB
XML
1629 lines
52 KiB
XML
<?xml version='1.0' standalone='yes'?>
|
||
|
||
<!DOCTYPE PLUGIN [
|
||
<!ENTITY name "dynamix.unraid.net">
|
||
<!ENTITY launch "Unraid.net">
|
||
<!ENTITY author "limetech">
|
||
<!ENTITY version "{{ plg_version }}">
|
||
<!ENTITY node_api_version "{{ node_api_version }}">
|
||
<!ENTITY node_plugins_version "{{ node_plugins_version }}">
|
||
<!ENTITY env "{{ env }}">
|
||
<!ENTITY pluginURL "https://s3.amazonaws.com/dnld.lime-technology.com/unraid-api/&name;.plg">
|
||
<!ENTITY node-api "https://s3.amazonaws.com/dnld.lime-technology.com/unraid-api/unraid-node-api-&node_api_version;.tgz">
|
||
<!ENTITY plugins "https://s3.amazonaws.com/dnld.lime-technology.com/unraid-api/unraid-plugins-&node_plugins_version;.tgz">
|
||
]>
|
||
|
||
<PLUGIN name="&name;" author="&author;" version="&version;" pluginURL="&pluginURL;" launch="&launch;" support="https://forums.unraid.net" min="6.9.0-beta30" icon="globe">
|
||
|
||
<CHANGES>
|
||
##&name;
|
||
|
||
###&version;
|
||
- initial release
|
||
</CHANGES>
|
||
|
||
<!-- check for plugin update -->
|
||
<FILE Run="/bin/bash" Method="install remove">
|
||
<INLINE>
|
||
if [ -e /etc/rc.d/rc.unraid-api ]; then
|
||
/etc/rc.d/rc.flash_backup stop
|
||
/etc/rc.d/rc.unraid-api uninstall
|
||
rm -f /etc/rc.d/rc.unraid-api
|
||
rm -f /etc/rc.d/rc.flash_backup
|
||
mv -f /usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php- /usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php
|
||
mv -f /usr/local/emhttp/plugins/dynamix/Registration.page- /usr/local/emhttp/plugins/dynamix/Registration.page
|
||
mv -f /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php- /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||
mv -f /usr/local/emhttp/plugins/dynamix/DisplaySettings.page- /usr/local/emhttp/plugins/dynamix/DisplaySettings.page
|
||
rm -rf /boot/config/plugins/Unraid.net
|
||
rm -rf /usr/local/emhttp/plugins/dynamix.unraid.net
|
||
rm -f /usr/local/emhttp/webGui/javascript/vue.min.js
|
||
rm -rf /usr/local/emhttp/webGui/wc
|
||
fi
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<!-- node-api -->
|
||
<FILE Name="/boot/config/plugins/Unraid.net/unraid-node-api.tgz">
|
||
<URL>&node-api;</URL>
|
||
</FILE>
|
||
<!-- plugins -->
|
||
<FILE Name="/boot/config/plugins/Unraid.net/unraid-plugins.tgz">
|
||
<URL>&plugins;</URL>
|
||
</FILE>
|
||
<!-- env -->
|
||
<FILE Name="/boot/config/plugins/Unraid.net/env">
|
||
<INLINE>env="&env;"</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/etc/rc.d/rc.unraid-api" Mode="0755">
|
||
<INLINE>
|
||
<![CDATA[
|
||
#!/bin/bash
|
||
# unraid-api-handler
|
||
source /boot/config/plugins/Unraid.net/env
|
||
downloads=(node-api plugins)
|
||
node_base_directory="/usr/local/bin/node"
|
||
|
||
# Only allow specific envs
|
||
if [ "$env" != "staging" ] && [ "$env" != "production" ]; then
|
||
echo "\"$env\" is an unsupported env. Please use \"staging\" or \"production\"."
|
||
exit 1
|
||
fi
|
||
|
||
status() {
|
||
local node_api_pid=$(pidof node-api | awk '{print $1}')
|
||
if [[ $node_api_pid ]]; then
|
||
echo "Unraid-api is running in $env mode."
|
||
else
|
||
echo "No processes are running."
|
||
fi
|
||
}
|
||
switchenv() {
|
||
# Get current environment from file
|
||
local envFile="/boot/config/plugins/Unraid.net/env"
|
||
local currentEnv=$(source $envFile; echo "$env";)
|
||
|
||
if [[ $currentEnv = "production" ]]; then
|
||
echo "Switching from production to staging"
|
||
echo 'env="staging"' > $envFile
|
||
elif [[ $currentEnv = "staging" ]]; then
|
||
echo "Switching from staging to production"
|
||
echo 'env="production"' > $envFile
|
||
fi
|
||
source $envFile;
|
||
reload
|
||
}
|
||
_start() {
|
||
_stop
|
||
local old_working_directory=$(echo $pwd)
|
||
mkdir -p $node_base_directory
|
||
cd $node_base_directory
|
||
/usr/local/bin/node/node-api/node_modules/pm2/bin/pm2 start $node_base_directory/node-api/ecosystem.config.js --env=$(echo $env) --no-daemon &> /dev/null &
|
||
cd $old_working_directory
|
||
}
|
||
start() {
|
||
_start
|
||
sleep 1
|
||
status
|
||
exit 0
|
||
}
|
||
_node_api_version() {
|
||
# Borrowed from https://gist.github.com/DarrenN/8c6a5b969481725a4413
|
||
local version=$(grep '"version"' $node_base_directory/node-api/package.json | cut -d '"' -f 4)
|
||
echo "v$version"
|
||
}
|
||
_plugins_version() {
|
||
# Borrowed from https://gist.github.com/DarrenN/8c6a5b969481725a4413
|
||
local version=$(grep '"version"' $node_base_directory/plugins/package.json | cut -d '"' -f 4)
|
||
echo "v$version"
|
||
}
|
||
report() {
|
||
cat << EOF
|
||
<-----UNRAID-API-REPORT----->
|
||
Env $env
|
||
Node API $(_node_api_version)
|
||
Official plugins $(_plugins_version)
|
||
Unraid v$(source /etc/unraid-version; echo "$version";)
|
||
</----UNRAID-API-REPORT----->
|
||
EOF
|
||
}
|
||
startdebug() {
|
||
_stop
|
||
local old_working_directory=$(echo $pwd)
|
||
cd $node_base_directory
|
||
/usr/local/bin/node/node-api/node_modules/pm2/bin/pm2 start $node_base_directory/node-api/ecosystem.config.js --env=$(echo $env)-debug --no-daemon
|
||
cd $old_working_directory
|
||
}
|
||
_stop() {
|
||
local node_api_pid=$(pidof node-api | awk '{print $1}')
|
||
if [[ $node_api_pid ]]; then
|
||
local parent_pid=$(cat /proc/$node_api_pid/status | grep PPid | cut -f2)
|
||
if [[ $parent_pid != 1 ]]; then
|
||
kill -9 $parent_pid &> /dev/null
|
||
else
|
||
kill -9 $node_api_pid &> /dev/null
|
||
fi
|
||
fi
|
||
}
|
||
stop() {
|
||
echo "Stopping Unraid-api"
|
||
_stop
|
||
sleep 1
|
||
status
|
||
exit 0
|
||
}
|
||
reload() {
|
||
echo "Reloading Unraid-api"
|
||
_stop
|
||
sleep 1
|
||
_start
|
||
sleep 1
|
||
status
|
||
exit 0
|
||
}
|
||
install() {
|
||
# Stop old process
|
||
_stop
|
||
|
||
# Install node-api and plugins
|
||
for download in ${downloads[@]}; do
|
||
rm -rf $node_base_directory/${download}
|
||
mkdir -p $node_base_directory/${download}
|
||
tar -C $node_base_directory/${download} -xzf /boot/config/plugins/Unraid.net/unraid-${download}.tgz --strip 1
|
||
done
|
||
|
||
# Copy across wc files
|
||
rm -rf /usr/local/emhttp/webGui/wc
|
||
mkdir -p /usr/local/emhttp/webGui/wc
|
||
cp /boot/config/plugins/Unraid.net/wc/* /usr/local/emhttp/webGui/wc
|
||
|
||
# Start new process
|
||
_start
|
||
|
||
# Wait for inital process to boot
|
||
sleep 2
|
||
status
|
||
exit 0
|
||
}
|
||
uninstall() {
|
||
stop
|
||
sleep 1
|
||
for download in ${downloads[@]}; do
|
||
rm -rf $node_base_directory/${download}
|
||
done
|
||
rm -f /var/run/unraid-api.sock
|
||
}
|
||
case "$1" in
|
||
'status')
|
||
status
|
||
;;
|
||
'start')
|
||
start
|
||
;;
|
||
'report')
|
||
report
|
||
;;
|
||
'switch-env')
|
||
switchenv
|
||
;;
|
||
'start-debug')
|
||
startdebug
|
||
;;
|
||
'stop')
|
||
stop
|
||
;;
|
||
'reload')
|
||
reload
|
||
;;
|
||
'install')
|
||
install
|
||
;;
|
||
'uninstall')
|
||
uninstall
|
||
;;
|
||
*)
|
||
echo "usage $0 status|start|report|switch-env|start-debug|stop|reload|install|uninstall"
|
||
esac
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<!-- README FILE -->
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix.unraid.net/README.md">
|
||
<INLINE>
|
||
**Unraid.net**
|
||
|
||
Unraid.net provides access to a set web-based services:
|
||
|
||
* Server status such as online/offline, storage used/available, etc.
|
||
* Links for local and remote access to your server webGUI.
|
||
* Backup and Restore of your USB Flash boot device.
|
||
* much more to come
|
||
|
||
A server is registered using your Unraid Community Forum credentials. Registered servers appear under the My Servers forum sub-menu.
|
||
|
||
This is work in progress. Use this for testing purposes only!
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix.unraid.net/Unraid.net.page">
|
||
<INLINE>
|
||
<![CDATA[
|
||
Menu="ManagementAccess:100"
|
||
Title="Unraid.net"
|
||
Icon="icon-u-globe"
|
||
Tag="globe"
|
||
---
|
||
<?PHP
|
||
/* Copyright 2005-2018, Lime Technology
|
||
* Copyright 2012-2018, Bergware International.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License version 2,
|
||
* as published by the Free Software Foundation.
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in
|
||
* all copies or substantial portions of the Software.
|
||
*/
|
||
?>
|
||
<?
|
||
// more:
|
||
// - if someone deletes the dynamic.cfg or directly edits [remote] section therein, it may be possible
|
||
// that server will appear 'unregistered' in webgui but not on My Servers.
|
||
// - appears someone could register, enable remote access, then unregister, replace the ssl cert, and
|
||
// now can remotely access using url that matches their cert. This is probably a bug here, but we might
|
||
// want to rework this somewhat to explicitly enable this 'feature' (remote access using user-provided cert).
|
||
|
||
$keyfile = @file_get_contents($var['regFILE']);
|
||
if ($keyfile !== false)
|
||
$keyfile = @base64_encode($keyfile);
|
||
|
||
if (empty($remote)) {
|
||
$remote = [
|
||
"apikey" => "",
|
||
"wanaccess" => "no",
|
||
"wanport" => "443"
|
||
];
|
||
}
|
||
if (empty($remote['wanport'])) {
|
||
$remote['wanport'] = 443;
|
||
}
|
||
|
||
$hasCert = $var['USE_SSL']!='no' &&
|
||
file_exists('/boot/config/ssl/certs/certificate_bundle.pem') &&
|
||
preg_match('/[0-9a-f]{40}\.unraid\.net$/', exec('openssl x509 -in /boot/config/ssl/certs/certificate_bundle.pem -subject -noout 2>&1'));
|
||
$isRegistered = !empty($remote['apikey']);
|
||
$boolWebUIAuth = $isRegistered && file_exists('/etc/nginx/htpasswd');
|
||
?>
|
||
<script>
|
||
function registerServer(button) {
|
||
var oldlabel = $.trim($(button).text());
|
||
|
||
var failure = function(data) {
|
||
var status = data.status;
|
||
var obj = data.responseJSON;
|
||
var msg = "Sorry, an error ("+status+") occurred registering this server. " +
|
||
"The error is: "+obj.error+".";
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
swal('Oops',msg,'error');
|
||
};
|
||
|
||
var success = function(data) {
|
||
if (data.apikey) {
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
$.post('/webGui/include/Dispatcher.php',{
|
||
"#cfg": "/boot/config/plugins/dynamix/dynamix.cfg",
|
||
"remote_apikey": data.apikey,
|
||
"remote_wanaccess": $('#wanaccess').val(),
|
||
"remote_wanport": $('#wanport').val()
|
||
},function(data2) {
|
||
<?if(!$isRegistered):?>
|
||
swal({title:"",text:"Your server has been registered",type:"success",allowEscapeKey:false},function(){button.form.submit();});
|
||
<?else:?>
|
||
button.form.submit();
|
||
<?endif?>
|
||
});
|
||
} else {
|
||
failure({"status": 403, "responseJSON": {"error": "Unable to register this Unraid Server"}});
|
||
}
|
||
};
|
||
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> "+oldlabel+"ing");
|
||
$.post("/webGui/include/UpdateDNS.php",{username:$('#ips_username').val(),password:$('#ips_password').val(),externalport:$('#wanport').val(),remoteaccess:$('#wanaccess').val()},success).fail(failure);
|
||
}
|
||
|
||
function unregisterServer(button) {
|
||
var oldlabel = $.trim($(button).text());
|
||
|
||
var failure = function(data) {
|
||
var status = data.status;
|
||
var obj = data.responseJSON;
|
||
var msg = "Sorry, an error ("+status+") occurred unregistering this server. " +
|
||
"The error is: "+obj.error+".";
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
swal('Oops',msg,'error');
|
||
};
|
||
|
||
var success = function(data) {
|
||
$.post('/webGui/include/Dispatcher.php',{
|
||
"#cfg": "/boot/config/plugins/dynamix/dynamix.cfg",
|
||
"remote_apikey": "",
|
||
"remote_wanaccess": $('#wanaccess').val(),
|
||
"remote_wanport": $('#wanport').val()
|
||
});
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
swal({title:"",text:"Your server has been unregistered",type:"success",allowEscapeKey:false},function(){button.form.submit();});
|
||
};
|
||
|
||
swal({title:"Remove Registration",text:"Are you sure you want to unregister your server?",type:'warning',confirmButtonText:'Unregister',showCancelButton:true},function(){
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> "+oldlabel+"ing");
|
||
$.post("https://keys.lime-technology.com/account/server/unregister",{keyfile:"<?=$keyfile?>"},success).fail(failure);
|
||
});
|
||
}
|
||
|
||
function dnsCheckServer(button) {
|
||
var oldlabel = $.trim($(button).text());
|
||
|
||
var failure = function(data) {
|
||
var status = data.status;
|
||
var obj = data.responseJSON;
|
||
var msg = "Sorry, an error ("+status+") occurred checking this server. " +
|
||
"The error is: "+obj.error+".";
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
swal('Oops',msg,'error');
|
||
};
|
||
|
||
var success = function(data) {
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
if (data.status) {
|
||
swal("","Your Unraid Server is reachable from the internet","success");
|
||
} else {
|
||
swal("Oops","This Unraid Server was unreachable from the outside","error");
|
||
}
|
||
};
|
||
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> "+oldlabel+"ing");
|
||
$.post("https://keys.lime-technology.com/account/server/checkdns",{externalport:$('#wanport').val(),keyfile:"<?=$keyfile?>"},success).fail(failure);
|
||
}
|
||
|
||
function changeRemoteAccess(dropdown) {
|
||
if ($(dropdown).val() == 'yes') {
|
||
$("#wanpanel").slideDown('fast');
|
||
} else {
|
||
$("#wanpanel").slideUp('fast');
|
||
}
|
||
}
|
||
|
||
function enableFlashBackup(button) {
|
||
var oldlabel = $.trim($(button).text());
|
||
|
||
var failure = function(data) {
|
||
var status = data.status;
|
||
var obj = data.responseJSON;
|
||
var msg = "Sorry, an error ("+status+") occurred enabling Flash backup. " +
|
||
"The error is: "+obj.error+".";
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
swal({title:"",text:msg,type:"error",allowEscapeKey:false},function(){button.form.submit();});
|
||
};
|
||
|
||
var success = function(data) {
|
||
$(button).prop("disabled", false).html(oldlabel);
|
||
button.form.submit();
|
||
};
|
||
|
||
if (oldlabel == 'Activate') {
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> Activating");
|
||
$.post("/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php",{command:"activate"},success).fail(failure);
|
||
}
|
||
if (oldlabel == 'Deactivate') {
|
||
swal({title:"Deactivate",text:"Are you sure you want pause communication with your remote flash backup on unraid.net?",type:'warning',confirmButtonText:'Deactivate',showCancelButton:true},function(){
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> Deactivating");
|
||
$.post("/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php",{command:"deactivate"},success).fail(failure);
|
||
});
|
||
}
|
||
if (oldlabel == 'Update') {
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> Updating");
|
||
$.post("/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php",{command:"flush"},success).fail(failure);
|
||
}
|
||
if (oldlabel == 'Reinitialize') {
|
||
swal({title:"Reinitialize",text:"Are you sure you want erase all change history in both local and unraid.net remote?",type:'warning',confirmButtonText:'Reinitialize',showCancelButton:true},function(){
|
||
$(button).prop("disabled", true).html("<i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i> Reinitializing");
|
||
$.post("/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php",{command:"reinit"},success).fail(failure);
|
||
});
|
||
}
|
||
if (oldlabel == 'Changes') {
|
||
openBox("/webGui/include/gitstatus.php", "Changes", 600,600, false);
|
||
}
|
||
}
|
||
|
||
function buttonStateReset(newstate) {
|
||
$('#loadingpanel,#inactivespanel,#activepanel,#changespanel,#uptodatepanel').hide();
|
||
|
||
if (newstate['loading'] && newstate['loading']!='') {
|
||
$('#loadingtext').text(newstate['loading']);
|
||
$('#loadingpanel').show();
|
||
return;
|
||
}
|
||
|
||
if (newstate['activated'] == 'true') {
|
||
$('#activepanel').show();
|
||
|
||
if (newstate['uptodate'] == 'true') {
|
||
$('#uptodatepanel').show();
|
||
} else {
|
||
$('#changespanel').show();
|
||
}
|
||
} else {
|
||
$('#inactivespanel').show();
|
||
}
|
||
}
|
||
$.post('/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php',{command:"status"});
|
||
</script>
|
||
|
||
<form id="UnraidNetSettings" markdown="1" name="UnraidNetSettings" method="POST" action="/update.htm" target="progressFrame">
|
||
Unraid.net Status:
|
||
: <unraid-authed registered="<? echo $isRegistered ?>"></unraid-authed>
|
||
|
||
<?if($isRegistered):?>
|
||
Allow Remote Access:
|
||
<?if(!$hasCert):?>
|
||
: <span><i class="fa fa-warning icon warning"></i> Disabled until you Provision an unraid.net SSL Cert and set SSL/TLS to Auto</span>
|
||
<input type="hidden" id="wanaccess" value="no">
|
||
<input type="hidden" id="wanport" value="0">
|
||
<?elseif(!$boolWebUIAuth):?>
|
||
: <span><i class="fa fa-warning icon warning"></i> Disabled until your root user account is password-protected</span>
|
||
<input type="hidden" id="wanaccess" value="no">
|
||
<input type="hidden" id="wanport" value="0">
|
||
<?else:?>
|
||
: <select id="wanaccess" size="1" class="narrow" onchange="changeRemoteAccess(this)">
|
||
<?=mk_option($remote['wanaccess'], "no", "No")?>
|
||
<?=mk_option($remote['wanaccess'], "yes", "Yes")?>
|
||
</select>
|
||
|
||
<div markdown="1" id="wanpanel" style="display:<?=($remote['wanaccess']=='yes'?'block':'none')?>">
|
||
WAN Port:
|
||
: <input type="number" id="wanport" class="trim" min="0" max="65535" value="<?=htmlspecialchars($remote['wanport'])?>"> <button type="button" onclick="dnsCheckServer(this)" style="margin-top: 0">Check</button>
|
||
|
||
> WAN Port is the external TCP port number setup on your router to NAT/Port Forward traffic from the internet to this
|
||
> Unraid server SSL port for secure web traffic.
|
||
</div>
|
||
|
||
|
||
: <button type="button" onclick="registerServer(this)">Apply</button>
|
||
<?endif?>
|
||
<?endif?>
|
||
</form>
|
||
|
||
<?if($isRegistered):?>
|
||
<form markdown="1" name="FlashBackup" method="POST" action="/update.htm" target="progressFrame">
|
||
<div markdown="1" id="loadingpanel">
|
||
Flash backup:
|
||
: <span class='blue p0'><span markdown="1" id="loadingtext">Loading</span> <i class="fa fa-spinner fa-spin" aria-hidden="true"></i></span>
|
||
|
||
</div>
|
||
<div markdown="1" id="inactivespanel" style="display:none">
|
||
Flash backup:
|
||
: <span class='orange p0'>Not activated</span>
|
||
|
||
|
||
: <button type="button" onclick="enableFlashBackup(this)">Activate</button>
|
||
|
||
> Click Activate to set up a local git repo for your local USB Flash boot device and connect to a dedicated remote on unraid.net tied tothis server.
|
||
|
||
</div>
|
||
<div markdown="1" id="uptodatepanel" style="display:none">
|
||
Flash backup:
|
||
: <span class='green p0'>Activated:</span> <span class='green p0'>Up-to-date</span>
|
||
|
||
</div>
|
||
<div markdown="1" id="changespanel" style="display:none">
|
||
Flash backup:
|
||
: <span class='green p0'>Activated:</span> <span class='orange p0'>Not up-to-date</span>
|
||
|
||
|
||
: <button type="button" onclick="enableFlashBackup(this)">Update</button> <button type="button" onclick="enableFlashBackup(this)">Changes</button>
|
||
|
||
> The Not Up-to-date status indicates there are local files which are changed vs. the remote on unraid.net.
|
||
|
||
> Click Update to push changes to the remote.
|
||
|
||
> Click Changes to see what has changed.
|
||
|
||
</div>
|
||
<div markdown="1" id="uptodatepanel" style="display:none">
|
||
|
||
: <button type="button" disabled>Update</button>
|
||
|
||
> The Up-to-date status indicates your local configuration matches that stored on the unraid.net remote.
|
||
|
||
</div>
|
||
<div markdown="1" id="activepanel" style="display:none">
|
||
|
||
: <button type="button" onclick="enableFlashBackup(this)">Deactivate</button> <button type="button" onclick="enableFlashBackup(this)">Reinitialize</button>
|
||
|
||
> Click Deactivate to pause communication with your remote on unraid.net.
|
||
|
||
> Click Reinitialize to erase all change history in both local and unraid.net remote.
|
||
|
||
</div>
|
||
</form>
|
||
<script>
|
||
var flashbackupsub = new NchanSubscriber('/sub/flashbackup');
|
||
flashbackupsub.on('message', function(data) {
|
||
var ini = parseINI(data);
|
||
|
||
console.info('Flash backup state: ', ini['flashbackup']);
|
||
buttonStateReset(ini['flashbackup']);
|
||
});
|
||
$(function() {
|
||
flashbackupsub.start();
|
||
});
|
||
</script>
|
||
<?endif?>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<!-- Preserve in case plugin is removed -->
|
||
<FILE Run="/bin/bash" Method="install">
|
||
<INLINE>
|
||
mv -f /usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php /usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php-
|
||
</INLINE>
|
||
</FILE>
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix/include/UpdateDNS.php">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<?PHP
|
||
/* Copyright 2005-2018, Lime Technology
|
||
* Copyright 2012-2018, Bergware International.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License version 2,
|
||
* as published by the Free Software Foundation.
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in
|
||
* all copies or substantial portions of the Software.
|
||
*/
|
||
?>
|
||
<?
|
||
$cli = php_sapi_name()=='cli';
|
||
|
||
function response_complete($httpcode, $result, $cli_success_msg='') {
|
||
global $cli;
|
||
if ($cli) {
|
||
$json = @json_decode($result,true);
|
||
if (!empty($json['error'])) {
|
||
echo 'Error: '.$json['error'].PHP_EOL;
|
||
exit(1);
|
||
}
|
||
exit($cli_success_msg.PHP_EOL);
|
||
}
|
||
header('Content-Type: application/json');
|
||
http_response_code($httpcode);
|
||
exit((string)$result);
|
||
}
|
||
|
||
// remoteaccess, externalport, (if registering) username, password
|
||
extract(parse_ini_file('/boot/config/plugins/dynamix/dynamix.cfg',true));
|
||
if (empty($remote)) {
|
||
$remote = [
|
||
"apikey" => "",
|
||
"wanaccess" => "no",
|
||
"wanport" => "443"
|
||
];
|
||
}
|
||
if (empty($remote['wanport'])) {
|
||
$remote['wanport'] = 443;
|
||
}
|
||
if ($cli) {
|
||
$remoteaccess = $remote['wanaccess'];
|
||
$externalport = $remote['wanport'];
|
||
$username = '';
|
||
$password = '';
|
||
} else {
|
||
$remoteaccess = $_POST['remoteaccess'];
|
||
$externalport = $_POST['externalport'];
|
||
$username = $_POST['username'];
|
||
$password = $_POST['password'];
|
||
}
|
||
$isRegistered = !empty($remote['apikey']);
|
||
|
||
// protocol, hostname, internalport
|
||
list($protocol, $hostname, $internalport) = explode(":", rtrim(file_get_contents("/var/run/nginx.origin")));
|
||
$hostname = substr($hostname, 2);
|
||
|
||
if (!$isRegistered && empty($username) && !preg_match('/.*\.unraid\.net$/', $hostname)) {
|
||
response_complete(406, '{"error":"Nothing to do"}');
|
||
}
|
||
|
||
// keyfile
|
||
$var = parse_ini_file('/var/local/emhttp/var.ini');
|
||
$keyfile = @file_get_contents($var['regFILE']);
|
||
if ($keyfile === false) {
|
||
response_complete(406, '{"error":"Registration key required"}');
|
||
}
|
||
$keyfile = @base64_encode($keyfile);
|
||
|
||
// internalip
|
||
extract(parse_ini_file('/var/local/emhttp/network.ini',true));
|
||
$internalip = $eth0['IPADDR:0'];
|
||
|
||
$ch = curl_init('https://keys.lime-technology.com/account/server/register');
|
||
curl_setopt($ch, CURLOPT_POST, 1);
|
||
|
||
if (!$isRegistered && empty($username)) {
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||
'internalip' => $internalip,
|
||
'keyfile' => $keyfile
|
||
]);
|
||
} else {
|
||
// servername, servercomment
|
||
$servername = $var['NAME'];
|
||
$servercomment = $var['COMMENT'];
|
||
if (empty($username)) {
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||
'servername' => $servername,
|
||
'servercomment' => $servercomment,
|
||
'protocol' => $protocol,
|
||
'hostname' => $hostname,
|
||
'internalport' => $internalport,
|
||
'internalip' => $internalip,
|
||
'remoteaccess' => $remoteaccess,
|
||
'externalport' => $externalport,
|
||
'keyfile' => $keyfile
|
||
]);
|
||
} else {
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||
'servername' => $servername,
|
||
'servercomment' => $servercomment,
|
||
'protocol' => $protocol,
|
||
'hostname' => $hostname,
|
||
'internalport' => $internalport,
|
||
'internalip' => $internalip,
|
||
'remoteaccess' => $remoteaccess,
|
||
'externalport' => $externalport,
|
||
'username' => $username,
|
||
'password' => $password,
|
||
'keyfile' => $keyfile
|
||
]);
|
||
}
|
||
}
|
||
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
$result = curl_exec($ch);
|
||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
$error = curl_error($ch);
|
||
curl_close($ch);
|
||
|
||
if ($result === false) {
|
||
response_complete(500, '{"error":"'.$error.'"}');
|
||
}
|
||
|
||
response_complete($httpcode, $result, 'success');
|
||
?>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/etc/rc.d/rc.flash_backup" Mode="0755">
|
||
<INLINE>
|
||
<![CDATA[
|
||
#!/bin/bash
|
||
# This file is /etc/rc.d/rc.flash_backup
|
||
# use at queue "f" for flash backup
|
||
QUEUE=" -q f "
|
||
TASKNAME="/etc/rc.d/rc.flash_backup watch"
|
||
TASKACTION="php /usr/local/emhttp/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php update"
|
||
status() {
|
||
local flash_backup_pid=$(pgrep --full "${TASKNAME}")
|
||
if [[ $flash_backup_pid ]]; then
|
||
echo "flash backup monitor is running."
|
||
_hasqueue && echo "changes detected, backup queued"
|
||
exit 0
|
||
else
|
||
if _enabled; then
|
||
echo "flash backup is enabled but the monitor is not running"
|
||
else
|
||
echo "flash backup is disabled so the monitor is disabled"
|
||
fi
|
||
exit 1
|
||
fi
|
||
}
|
||
start() {
|
||
# only run if flash_backup is enabled
|
||
_enabled || exit 1
|
||
# terminate watcher loop/process
|
||
pkill --full "${TASKNAME}" &>/dev/null
|
||
# flush: this will ensure we start with a clean repo
|
||
flush
|
||
# start watcher loop as background process
|
||
exec ${TASKNAME} &>/dev/null &
|
||
exit 0
|
||
}
|
||
stop() {
|
||
# terminate watcher loop/process
|
||
pkill --full "${TASKNAME}" &>/dev/null
|
||
# remove any queued jobs and flush changes
|
||
flush
|
||
exit 0
|
||
}
|
||
reload() {
|
||
stop
|
||
sleep 1
|
||
start
|
||
sleep 1
|
||
status
|
||
exit 0
|
||
}
|
||
flush() {
|
||
# remove any queued jobs
|
||
_removequeue
|
||
# push any changes ad-hoc
|
||
echo ${TASKACTION} | at ${QUEUE} now &>/dev/null
|
||
}
|
||
_watch() {
|
||
# start watcher loop
|
||
while true; do
|
||
if [ "$(git -C /boot status -s)" ]; then
|
||
_hasqueue || ( logger "adding task: ${TASKACTION}" --tag flash_backup; echo ${TASKACTION} | at ${QUEUE} now +1 minute &>/dev/null )
|
||
fi
|
||
sleep 60;
|
||
done
|
||
}
|
||
_hasqueue() {
|
||
# returns false if the queue is empty, true otherwise
|
||
if [ -z "$(atq ${QUEUE})" ]; then
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
_removequeue() {
|
||
# delete any at jobs in queue f
|
||
atq ${QUEUE} | while read line; do
|
||
id=`echo ${line} | cut -d " " -f 1`
|
||
atrm ${id}
|
||
done
|
||
}
|
||
_enabled() {
|
||
local output=$(git -C /boot config --get remote.origin.url)
|
||
if [[ $output == *"backup.unraid.net"* ]]; then
|
||
return 0
|
||
fi
|
||
return 1
|
||
}
|
||
case "$1" in
|
||
'status')
|
||
status
|
||
;;
|
||
'start')
|
||
start
|
||
;;
|
||
'stop')
|
||
stop
|
||
;;
|
||
'reload')
|
||
reload
|
||
;;
|
||
'flush')
|
||
flush
|
||
;;
|
||
'watch')
|
||
_watch
|
||
;;
|
||
*)
|
||
echo "usage $0 status|start|stop|reload|flush"
|
||
esac
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix.unraid.net/include/UpdateFlashBackup.php">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<?PHP
|
||
/* Copyright 2005-2018, Lime Technology
|
||
* Copyright 2012-2018, Bergware International.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License version 2,
|
||
* as published by the Free Software Foundation.
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in
|
||
* all copies or substantial portions of the Software.
|
||
*/
|
||
?>
|
||
<?
|
||
$cli = php_sapi_name()=='cli';
|
||
|
||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||
|
||
if (file_exists('/boot/config/plugins/dynamix/dynamix.cfg')) {
|
||
extract(parse_ini_file('/boot/config/plugins/dynamix/dynamix.cfg',true));
|
||
}
|
||
if (empty($remote)) {
|
||
$remote = [
|
||
"apikey" => "",
|
||
"username" => "",
|
||
"avatar" => "",
|
||
"wanaccess" => "no",
|
||
"wanport" => "443"
|
||
];
|
||
}
|
||
|
||
function response_complete($httpcode, $result, $cli_success_msg='') {
|
||
global $cli;
|
||
|
||
save_flash_backup_state();
|
||
|
||
if ($cli) {
|
||
$json = @json_decode($result,true);
|
||
if (!empty($json['error'])) {
|
||
echo 'Error: '.$json['error'].PHP_EOL;
|
||
exit(1);
|
||
}
|
||
if (!empty($cli_success_msg)) $cli_success_msg .= PHP_EOL;
|
||
exit($cli_success_msg);
|
||
}
|
||
header('Content-Type: application/json');
|
||
http_response_code($httpcode);
|
||
exit((string)$result);
|
||
}
|
||
|
||
function save_flash_backup_state($loading='') {
|
||
global $arrState;
|
||
|
||
$arrState['loading'] = $loading;
|
||
|
||
$text = "[flashbackup]\n";
|
||
foreach ($arrState as $key => $value) {
|
||
if ($value === false) $value = 'false';
|
||
if ($value === true) $value = 'true';
|
||
$text .= "$key=" . $value . "\n";
|
||
}
|
||
file_put_contents('/var/local/emhttp/flashbackup.new', $text);
|
||
rename('/var/local/emhttp/flashbackup.new', '/var/local/emhttp/flashbackup.ini');
|
||
}
|
||
|
||
function load_flash_backup_state() {
|
||
global $remote;
|
||
global $arrState;
|
||
|
||
$arrState = [
|
||
'activated' => false,
|
||
'uptodate' => false,
|
||
'loading' => ''
|
||
];
|
||
|
||
$arrNewState = parse_ini_file("/var/local/emhttp/flashbackup.ini");
|
||
|
||
if ($arrNewState !== false) {
|
||
$arrState = array_merge($arrState, $arrNewState);
|
||
}
|
||
|
||
$arrState['activated'] = empty($arrState['activated']) ? true : false;
|
||
$arrState['uptodate'] = empty($arrState['uptodate']) ? true : false;
|
||
$arrState['registered'] = !empty($remote['apikey']);
|
||
}
|
||
// command
|
||
// init (default)
|
||
// activate
|
||
// status
|
||
// update
|
||
// flush
|
||
// reinit
|
||
// deactivate
|
||
if ($cli) {
|
||
if ($argc > 1) $command = $argv[1];
|
||
if ($argc > 2) $commitmsg = $argv[2];
|
||
} else {
|
||
$command = $_POST['command'];
|
||
$commitmsg = $_POST['commitmsg'];
|
||
}
|
||
if (empty($command)) $command='init';
|
||
if (empty($commitmsg)) $commitmsg='Config change';
|
||
|
||
$loadingMessage = '';
|
||
|
||
switch ($command) {
|
||
case 'activate':
|
||
$loadingMessage = 'Activating';
|
||
break;
|
||
case 'deactivate':
|
||
$loadingMessage = 'Deactivating';
|
||
break;
|
||
case 'update':
|
||
$loadingMessage = 'Updating';
|
||
break;
|
||
case 'reinit':
|
||
$loadingMessage = 'Reinitializing';
|
||
break;
|
||
case 'status':
|
||
$loadingMessage = 'Loading';
|
||
break;
|
||
}
|
||
|
||
load_flash_backup_state();
|
||
|
||
// keyfile
|
||
$var = parse_ini_file("/var/local/emhttp/var.ini");
|
||
$keyfile = @file_get_contents($var['regFILE']);
|
||
if ($keyfile === false) {
|
||
response_complete(406, '{"error":"Registration key required"}');
|
||
}
|
||
$keyfile = @base64_encode($keyfile);
|
||
|
||
// check if activated
|
||
if ($command != 'activate') {
|
||
exec('git -C /boot config --get remote.origin.url', $config_output, $return_var);
|
||
if (($return_var != 0) || (strpos($config_output[0],'backup.unraid.net') === false)) {
|
||
response_complete(406, '{"error":"Not activated"}');
|
||
}
|
||
}
|
||
|
||
// if flush command, invoke our background rc.flash_backup to flush
|
||
if ($command == 'flush') {
|
||
exec('/etc/rc.d/rc.flash_backup flush &>/dev/null');
|
||
response_complete(200, '{}');
|
||
}
|
||
|
||
if (!empty($loadingMessage)) {
|
||
save_flash_backup_state($loadingMessage);
|
||
}
|
||
|
||
// if deactivate command, just remove our origin
|
||
if ($command == 'deactivate') {
|
||
exec('git --git-dir /boot/.git remote remove origin &>/dev/null');
|
||
exec('/etc/rc.d/rc.flash_backup stop &>/dev/null');
|
||
response_complete(200, '{}');
|
||
}
|
||
|
||
// build a list of sha256 hashes of the bzfiles
|
||
$bzfilehashes = [];
|
||
$allbzfiles = ['bzimage','bzfirmware','bzmodules','bzroot','bzroot-gui'];
|
||
foreach ($allbzfiles as $bzfile) {
|
||
$sha256 = trim(@file_get_contents("/boot/$bzfile.sha256"));
|
||
if (strlen($sha256) != 64) {
|
||
response_complete(406, '{"error":"Invalid or missing '.$bzfile.'.sha256 file"}');
|
||
}
|
||
$bzfilehashes[] = $sha256;
|
||
}
|
||
|
||
$ch = curl_init('https://keys.lime-technology.com/backup/flash/activate');
|
||
curl_setopt($ch, CURLOPT_POST, 1);
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||
'keyfile' => $keyfile,
|
||
'version' => $var['version'],
|
||
'bzfiles' => implode(',', $bzfilehashes)
|
||
]);
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
$result = curl_exec($ch);
|
||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
$error = curl_error($ch);
|
||
curl_close($ch);
|
||
|
||
if ($result === false) {
|
||
response_complete(500, '{"error":"'.$error.'"}');
|
||
}
|
||
|
||
$json = json_decode($result, true);
|
||
if (empty($json) || empty($json['ssh_privkey'])) {
|
||
response_complete(406, $result);
|
||
}
|
||
|
||
// save the public and private keys
|
||
if (!file_exists('/root/.ssh')) {
|
||
mkdir('/root/.ssh', 0700);
|
||
}
|
||
|
||
if ($json['ssh_privkey'] != file_get_contents('/root/.ssh/unraidbackup_id_ed25519')) {
|
||
file_put_contents('/root/.ssh/unraidbackup_id_ed25519', $json['ssh_privkey']);
|
||
chmod('/root/.ssh/unraidbackup_id_ed25519', 0600);
|
||
file_put_contents('/root/.ssh/unraidbackup_id_ed25519.pub', $json['ssh_pubkey']);
|
||
chmod('/root/.ssh/unraidbackup_id_ed25519.pub', 0644);
|
||
}
|
||
|
||
// add configuration to use our keys
|
||
if (!file_exists('/root/.ssh/config') || strpos(file_get_contents('/root/.ssh/config'),'Host backup.unraid.net') === false) {
|
||
file_put_contents('/root/.ssh/config', 'Host backup.unraid.net
|
||
IdentityFile ~/.ssh/unraidbackup_id_ed25519
|
||
IdentitiesOnly yes
|
||
', FILE_APPEND);
|
||
chmod('/root/.ssh/config', 0644);
|
||
}
|
||
|
||
// add our server as a known host
|
||
if (!file_exists('/root/.ssh/known_hosts') || strpos(file_get_contents('/root/.ssh/known_hosts'),'backup.unraid.net,54.70.72.154') === false) {
|
||
file_put_contents('/root/.ssh/known_hosts', 'backup.unraid.net,54.70.72.154 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKrKXKQwPZTY25MoveIw7fZ3IoZvvffnItrx6q7nkNriDMr2WAsoxu0DrU2QrSLH5zFF1ibv4tChS1hOpiYObiI='."\n", FILE_APPEND);
|
||
chmod('/root/.ssh/known_hosts', 0644);
|
||
}
|
||
|
||
// blow away existing repo if reinit command
|
||
if ($command == 'reinit') {
|
||
exec('rm -rf /boot/.git');
|
||
}
|
||
|
||
// ensure git repo is setup on the flash drive
|
||
if (!file_exists('/boot/.git/info/exclude')) {
|
||
exec('git init /boot &>/dev/null');
|
||
}
|
||
|
||
// setup a nice git description
|
||
if (!file_exists('/boot/.git/description') || strpos(file_get_contents('/boot/.git/description'),$var['NAME']) === false) {
|
||
file_put_contents('/boot/.git/description', 'Unraid flash drive for '.$var['NAME']."\n");
|
||
}
|
||
|
||
// setup git ignore for files we dont need in the flash backup
|
||
if (strpos(file_get_contents('/boot/.git/info/exclude'),'startup.nsh') === false) {
|
||
file_put_contents('/boot/.git/info/exclude', '# Unraid OS Flash Backup
|
||
|
||
# Blacklist everything
|
||
/*
|
||
|
||
# Whitelist selected root files
|
||
!*.sha256
|
||
!changes.txt
|
||
!license.txt
|
||
!startup.nsh
|
||
|
||
!EFI*/
|
||
EFI*/boot/*
|
||
!EFI*/boot/syslinux.cfg
|
||
|
||
!syslinux/
|
||
syslinux/*
|
||
!syslinux/syslinux.cfg
|
||
!syslinux/syslinux.cfg-
|
||
|
||
# Whitelist entire config directory
|
||
!config/
|
||
# except for selected files
|
||
config/drift
|
||
config/plugins/unRAIDServer.plg
|
||
config/random-seed
|
||
config/shadow
|
||
config/smbpasswd
|
||
config/wireguard/privatekey
|
||
config/plugins/**/*.tgz
|
||
config/plugins/**/*.txz
|
||
config/plugins/**/*.tar.bz2
|
||
');
|
||
}
|
||
|
||
// ensure git user is configured
|
||
exec('git --git-dir /boot/.git config user.email \'gitbot@unraid.net\' &>/dev/null');
|
||
exec('git --git-dir /boot/.git config user.name \'gitbot\' &>/dev/null');
|
||
|
||
// ensure upstream git server is configured and in-sync
|
||
exec('git --git-dir /boot/.git remote add -f -t master -m master origin git@backup.unraid.net:~/flash.git &>/dev/null');
|
||
if ($command != 'reinit') {
|
||
exec('git --git-dir /boot/.git reset origin/master &>/dev/null');
|
||
exec('git --git-dir /boot/.git checkout -B master origin/master &>/dev/null');
|
||
}
|
||
|
||
// establish status
|
||
exec('git -C /boot status --porcelain', $status_output, $return_var);
|
||
$arrState['activated'] = $return_var==0;
|
||
|
||
if ($return_var != 0) {
|
||
$arrState['loading'] = '';
|
||
response_complete(406, '{"error":"'.${status_output[0]}.'"}');
|
||
}
|
||
|
||
$arrState['uptodate'] = empty($status_output);
|
||
|
||
if ($command == 'status') {
|
||
$data = implode("\n", $status_output);
|
||
response_complete($httpcode, '{"data":"'.$data.'"}', $data);
|
||
}
|
||
|
||
if (($command == 'update') || ($command == 'reinit')) {
|
||
// push changes upstream
|
||
if (!empty($status_output)) {
|
||
exec('git -C /boot add -A &>/dev/null');
|
||
if ($command == 'reinit') {
|
||
exec('git -C /boot commit -m \'Initial commit\' &>/dev/null');
|
||
exec('git -C /boot push --force --set-upstream origin master &>/dev/null');
|
||
} else {
|
||
exec('git -C /boot commit -m ' . escapeshellarg($commitmsg) . ' &>/dev/null');
|
||
exec('git -C /boot push --set-upstream origin master &>/dev/null');
|
||
}
|
||
$arrState['uptodate'] = true;
|
||
}
|
||
}
|
||
|
||
if ($command == 'activate') {
|
||
exec('/etc/rc.d/rc.flash_backup start &>/dev/null');
|
||
}
|
||
|
||
response_complete($httpcode, '{}');
|
||
?>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix.unraid.net/include/RegWizard.php">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<style>
|
||
#sb-title {display:none}
|
||
#sb-info {display:none}
|
||
</style>
|
||
<script markdown="0">
|
||
function handleMessage(e) {
|
||
//if (e.origin != "http://child.com") { return; }
|
||
if (e.data.length == 0) { return; }
|
||
|
||
const SAFE_JSON_PARSE = (str) => {
|
||
try {
|
||
return [null, JSON.parse(str)];
|
||
} catch (err) {
|
||
return [err];
|
||
}
|
||
};
|
||
|
||
const [err, data] = SAFE_JSON_PARSE(e.data);
|
||
if (err) return false; // swallow json parse error
|
||
|
||
const HANDLE_LICENSES = (data, e) => {
|
||
if (data.license) {
|
||
$.get('/webGui/include/InstallKey.php', {url: data.license}, function() {
|
||
console.log('New license key installed: ' + data.license);
|
||
const payload = {
|
||
event: 'LICENSE_PINGBACK',
|
||
message: 'New license key installed',
|
||
license: data.license,
|
||
success: true,
|
||
};
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
}).fail(function() {
|
||
console.error('Failed to license new key: ' + data.license);
|
||
const payload = {
|
||
event: 'LICENSE_PINGBACK',
|
||
message: 'Failed to license new key',
|
||
license: data.license,
|
||
success: false,
|
||
};
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
});
|
||
} else {
|
||
console.error('KEY_PURCHASE event fired! but missing license data:', data);
|
||
}
|
||
};
|
||
|
||
switch (data.event) {
|
||
case "CLOSE_SHADOWBOX":
|
||
hideRegWizard();
|
||
break;
|
||
case "SHUTDOWN":
|
||
window.location.href = '/webGui/include/Boot.php';
|
||
break;
|
||
case "REG_WIZARD":
|
||
if (data.apikey) {
|
||
$.post('https://keys.lime-technology.com/ips/details', {'apikey': data.apikey}, function(details) {
|
||
var postargs = {
|
||
'#file': 'dynamix/dynamix.cfg',
|
||
'#section': 'remote',
|
||
apikey: data.apikey,
|
||
regWizTime: Date.now(), // set when signing in the first time and never unset for the sake of displaying Sign In/Up in the UPC without needing to validate guid every time
|
||
};
|
||
if (data.email) {
|
||
postargs['email'] = data.email; // this is also never unset but only "reset" if with instance of sign in
|
||
}
|
||
if (details.username) {
|
||
postargs['username'] = details.username;
|
||
}
|
||
if (details.avatar) {
|
||
postargs['avatar'] = details.avatar;
|
||
}
|
||
$.post('/update.php', postargs, function() {
|
||
console.log('dynamix/dynamix.cfg: Updated apikey under [remote] section');
|
||
// send a ping back to the regwiz
|
||
const payload = { event: 'ACCOUNT_PINGBACK', success: true, type: 'signIn', webGuiPathname: window.location.pathname };
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
}).fail(function() {
|
||
console.error('Failed to update apikey under [remote] section');
|
||
// send a ping back to the regwiz
|
||
const payload = { event: 'ACCOUNT_PINGBACK', success: false, type: 'signIn', webGuiPathname: window.location.pathname };
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
});
|
||
}).fail(function() {
|
||
console.error('Failed to retrieve details from key server');
|
||
// send a ping back to the regwiz
|
||
const payload = { event: 'ACCOUNT_PINGBACK', success: false, type: 'signIn', webGuiPathname: window.location.pathname };
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
});
|
||
}
|
||
// duplicate conditional so we don't get the error from HANDLE_LICENSES()
|
||
if (data.license) HANDLE_LICENSES(data, e);
|
||
break;
|
||
case "KEY_PURCHASE":
|
||
HANDLE_LICENSES(data, e);
|
||
break;
|
||
case "GET_STATE":
|
||
$.get('/plugins/dynamix.unraid.net/include/state.php', function(newstate) {
|
||
e.source.postMessage(newstate, e.origin);
|
||
}).fail(function(err) {
|
||
console.error('Failed to get new state: ' + err);
|
||
});
|
||
break;
|
||
case "SUCCESS_NEW_STATE":
|
||
$.get('/plugins/dynamix.unraid.net/include/state.php', function(newstate) {
|
||
// parse response so we can update the event type string to send back
|
||
const [stateError, stateData] = SAFE_JSON_PARSE(newstate);
|
||
const newPayload = { ...stateData, event: 'DELIVER_NEW_STATE', success: true };
|
||
e.source.postMessage(JSON.stringify(newPayload), e.origin);
|
||
}).fail(function(err) {
|
||
e.source.postMessage(JSON.stringify({ event: 'DELIVER_NEW_STATE', success: false }), e.origin);
|
||
});
|
||
break;
|
||
case "MYSERVERS_UNREGISTER":
|
||
$.post('/update.php', {
|
||
'#file': 'dynamix/dynamix.cfg',
|
||
'#section': 'remote',
|
||
apikey: '',
|
||
avatar: '',
|
||
email: '',
|
||
username: '',
|
||
}, function() {
|
||
console.log('dynamix/dynamix.cfg: Unregistered myservers, cleared apikey under [remote] section');
|
||
// send a ping back to the regwiz
|
||
const payload = { event: 'ACCOUNT_PINGBACK', success: true, type: 'signOut', webGuiPathname: window.location.pathname };
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
}).fail(function() {
|
||
console.error('Failed to unregister');
|
||
// send a ping back to the regwiz
|
||
const payload = { event: 'ACCOUNT_PINGBACK', success: false, type: 'signOut', webGuiPathname: window.location.pathname };
|
||
e.source.postMessage(JSON.stringify(payload), e.origin);
|
||
});
|
||
break;
|
||
case "RELOAD":
|
||
return window.location.reload();
|
||
case "REDIRECT_MAIN":
|
||
return window.location.href = '/Main';
|
||
case "STOP_SENDING_SESSION":
|
||
break;
|
||
case "PREFLIGHT_REQUEST":
|
||
break;
|
||
default:
|
||
console.error('Unhandled event \'' + data.event + '\' fired. data:', data);
|
||
break;
|
||
}
|
||
}
|
||
window.addEventListener('message', handleMessage, false);
|
||
</script>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix.unraid.net/include/state.php">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<?PHP
|
||
/* Copyright 2005-2020, Lime Technology
|
||
* Copyright 2012-2020, Bergware International.
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License version 2,
|
||
* as published by the Free Software Foundation.
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in
|
||
* all copies or substantial portions of the Software.
|
||
*/
|
||
?>
|
||
<?
|
||
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
|
||
|
||
require_once "$docroot/webGui/include/Helpers.php";
|
||
|
||
$var = (array)parse_ini_file('state/var.ini');
|
||
$license_state = strtoupper(empty($var['regCheck']) ? $var['regTy'] : $var['regCheck']);
|
||
$key_contents = str_replace(['+','/','='], ['-','_',''], trim(base64_encode(@file_get_contents($var['regFILE']))));
|
||
|
||
if (file_exists('/boot/config/plugins/dynamix/dynamix.cfg')) {
|
||
extract(parse_ini_file('/boot/config/plugins/dynamix/dynamix.cfg',true));
|
||
}
|
||
if (empty($remote)) {
|
||
$remote = [
|
||
"apikey" => "",
|
||
"username" => "",
|
||
"avatar" => "",
|
||
"wanaccess" => "no",
|
||
"wanport" => "443"
|
||
];
|
||
}
|
||
if (empty($remote['username'])) {
|
||
$remote['username'] = '';
|
||
}
|
||
if (empty($remote['avatar'])) {
|
||
$remote['avatar'] = '';
|
||
}
|
||
|
||
$arr = [];
|
||
$arr['event'] = 'STATE';
|
||
$arr['ts'] = time();
|
||
$arr['guid'] = $var['flashGUID'];
|
||
$arr['state'] = $license_state;
|
||
$arr['keyfile'] = $key_contents;
|
||
$arr['reggen'] = $var['regGen'];
|
||
$arr['flashproduct'] = $var['flashProduct'];
|
||
$arr['flashvendor'] = $var['flashVendor'];
|
||
$arr['servername'] = $var['NAME'];
|
||
$arr['serverip'] = $_SERVER['SERVER_ADDR'];
|
||
$arr['internalip'] = $_SERVER['SERVER_ADDR'];
|
||
$arr['internalport'] = $_SERVER['SERVER_PORT'];
|
||
$arr['protocol'] = $_SERVER['REQUEST_SCHEME'];
|
||
$arr['registered'] = empty($remote['apikey']) || empty($var['regFILE']) ? 0 : 1;
|
||
$arr['username'] = $remote['username'];
|
||
$arr['avatar'] = $remote['avatar'];
|
||
|
||
echo json_encode($arr);
|
||
?>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/webGui/javascript/vue.js">
|
||
<URL>https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js</URL>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/webGui/javascript/vue.min.js">
|
||
<URL>https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js</URL>
|
||
</FILE>
|
||
|
||
<FILE Run="/bin/bash" Method="install Vue.js web component files">
|
||
<INLINE>
|
||
<![CDATA[
|
||
MANIFEST_JSON_PATH=/boot/config/plugins/Unraid.net/wc/manifest.json
|
||
BASE_URL=https://registration.unraid.net/wc/
|
||
BASE_PATH=/boot/config/plugins/Unraid.net/wc/
|
||
MANIFEST_JSON=manifest.json
|
||
MANIFEST_TXT=manifest.txt
|
||
|
||
# Check if we need to download files
|
||
# On first install we cache these
|
||
if [[ ! -f "$MANIFEST_JSON_PATH" ]]; then
|
||
echo "🕹️ Start downloading web components"
|
||
|
||
# Ensure we start off with a clean dir
|
||
rm -rf $BASE_PATH && mkdir -p $BASE_PATH && cd $BASE_PATH
|
||
|
||
# Fetch the manifest
|
||
echo "🎣 Fetching manifest"
|
||
curl --http2 -s ${BASE_URL}$MANIFEST_JSON | jq . > $MANIFEST_JSON
|
||
echo "📋 Received manifest"
|
||
|
||
# Take each key and add it to MANIFEST_TXT
|
||
KEYS=($((<${MANIFEST_JSON} jq -r 'keys | @sh') | tr -d \'\"))
|
||
for ix in ${!KEYS[*]}
|
||
do
|
||
echo "${BASE_URL}${KEYS[$ix]}" >> $MANIFEST_TXT
|
||
echo "✨ ${BASE_URL}${KEYS[$ix]}"
|
||
done
|
||
|
||
# Download files from the manifest file
|
||
echo "🚀 Parsed Manifest – starting downloads"
|
||
xargs -n 1 curl --http2 -s -O < $MANIFEST_TXT
|
||
|
||
# Done
|
||
echo "✅ Registration Wizard files downloaded"
|
||
if [ -f "$MANIFEST_TXT" ]; then
|
||
echo "☄️🚮 Deleted temp files"
|
||
rm $MANIFEST_TXT
|
||
fi
|
||
fi
|
||
|
||
# Copy files to webGui's public dir
|
||
mkdir -p /usr/local/emhttp/webGui/wc/
|
||
rsync -r --exclude="$MANIFEST_JSON" /boot/config/plugins/Unraid.net/wc/ /usr/local/emhttp/webGui/wc/
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice1">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<!-- RegWiz -->
|
||
<style>
|
||
#header {
|
||
z-index: 102 !important;
|
||
display: -webkit-box;
|
||
display: -ms-flexbox;
|
||
display: flex;
|
||
-webkit-box-pack: justify;
|
||
-ms-flex-pack: justify;
|
||
justify-content: space-between;
|
||
-webkit-box-align: center;
|
||
-ms-flex-align: center;
|
||
align-items: center;
|
||
}
|
||
vue-userprofile,
|
||
unraid-user-profile {
|
||
font-size: 16px;
|
||
margin-left: auto;
|
||
height: 100%;
|
||
}
|
||
|
||
unraid-launchpad {
|
||
position: relative;
|
||
z-index: 10001;
|
||
}
|
||
</style>
|
||
<!-- /RegWiz -->
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice2">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<!-- RegWiz -->
|
||
<script type="text/javascript">
|
||
const UPC_ENV = localStorage.getItem('UPC_ENV') ? localStorage.getItem('UPC_ENV') : 'production'; // @TODO - it would be great if this was auto-set when PLG was built
|
||
const VUE_CDN_PREFIX = 'https://cdn.jsdelivr.net/npm/vue@2.6.12';
|
||
const injectJS = (src) => {
|
||
if (UPC_ENV !== 'production') console.debug('[injectJS]', src);
|
||
const script = document.createElement('script');
|
||
script.type = 'text/javascript';
|
||
script.src = src;
|
||
document.body.appendChild(script);
|
||
};
|
||
// by default prod is loaded from hosted sources
|
||
let vueFile = `${VUE_CDN_PREFIX}/dist/vue.min.js`;
|
||
let wcEntryFile = 'https://registration.unraid.net/wc/unraid.min.js';
|
||
// determine what source we should use
|
||
if (UPC_ENV !== 'production') console.debug('[UPC_ENV]', UPC_ENV);
|
||
switch (UPC_ENV) {
|
||
case 'staging':
|
||
// min version of staging
|
||
vueFile = `${VUE_CDN_PREFIX}/dist/vue.min.js`;
|
||
wcEntryFile = 'https://registration-dev.unraid.net/wc/unraid.min.js';
|
||
break;
|
||
case 'staging-debug':
|
||
// non-min version of staging
|
||
vueFile = `${VUE_CDN_PREFIX}/dist/vue.js`;
|
||
wcEntryFile = 'https://registration-dev.unraid.net/wc/unraid.js';
|
||
break;
|
||
case 'local':
|
||
// forces load from webGUI filesystem
|
||
vueFile = '<?autov('/webGui/javascript/vue.js') ?>';
|
||
wcEntryFile = '<?autov('/webGui/wc/unraid.min.js') ?>'; // @TODO / WIP - non-min files aren't downloaded just yet.
|
||
break;
|
||
case 'development':
|
||
// local filesystem Vue.js + external "local" dev server for RegWiz development
|
||
vueFile = '<?autov('/webGui/javascript/vue.js') ?>';
|
||
wcEntryFile = 'https://launchpad.unraid.test:6969/wc/unraid.js';
|
||
break;
|
||
default: // prod
|
||
break;
|
||
}
|
||
// add the intended files to the DOM
|
||
injectJS(vueFile);
|
||
// make sure specified vue file is loaded before attempting to
|
||
// inject and load web component entry since it relies on Vue.js
|
||
let checkForVue = setInterval(() => {
|
||
if (!window.Vue) return;
|
||
clearInterval(checkForVue); // stop checking
|
||
injectJS(wcEntryFile); // and just inject it already!
|
||
}, 10);
|
||
// start a timer to determine if we need to inject webGUI file system
|
||
// fallback if we take too long injecting the above
|
||
setTimeout(() => {
|
||
if (!window.Vue) {
|
||
if (UPC_ENV !== 'production') console.debug('[UPC] fallback to filesystem JS');
|
||
clearInterval(checkForVue); // stop initial check
|
||
injectJS("<?autov('/webGui/javascript/vue.min.js') ?>");
|
||
checkForVue = setInterval(() => { // reset check
|
||
if (!window.Vue) return;
|
||
clearInterval(checkForVue); // stop checking
|
||
injectJS("<?autov('/webGui/wc/unraid.min.js') ?>"); // and just inject it already!
|
||
}, 10);
|
||
}
|
||
}, 2000);
|
||
</script>
|
||
<!-- /RegWiz -->
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice3">
|
||
<INLINE>
|
||
<![CDATA[
|
||
<!-- RegWiz -->
|
||
<?
|
||
$serverstate = [
|
||
"email" => ($remote['email']) ? $remote['email'] : '',
|
||
"flashproduct" => $var['flashProduct'],
|
||
"flashvendor" => $var['flashVendor'],
|
||
"guid" => $var['flashGUID'],
|
||
"internalip" => $_SERVER['SERVER_ADDR'],
|
||
"internalport" => $_SERVER['SERVER_PORT'],
|
||
"keyfile" => str_replace(['+','/','='], ['-','_',''], trim(base64_encode(@file_get_contents($var['regFILE'])))),
|
||
"protocol" => $_SERVER['REQUEST_SCHEME'],
|
||
"reggen" => (int)$var['regGen'],
|
||
"registered" => empty($remote['apikey']) || empty($var['regFILE']) ? 0 : 1,
|
||
"servername" => $var['NAME'],
|
||
"serverip" => $_SERVER['SERVER_ADDR'],
|
||
"site" => $_SERVER['REQUEST_SCHEME']."://".$_SERVER['HTTP_HOST'],
|
||
"state" => strtoupper(empty($var['regCheck']) ? $var['regTy'] : $var['regCheck']),
|
||
];
|
||
?>
|
||
<unraid-user-profile
|
||
apikey="<?=($remote['apikey']) ? $remote['apikey'] : ''?>"
|
||
banner="<?=($display['banner']) ? $display['banner'] : ''?>"
|
||
bgcolor="<?=($backgnd) ? '#'.$backgnd : ''?>"
|
||
displaydesc="<?=($display['headerdescription']!='no') ? 'true' : ''?>"
|
||
expiretime="<?=1000*($var['regTy']=='Trial'||strstr($var['regTy'],'expired')?$var['regTm2']:0)?>"
|
||
reg-wiz-time="<?=($remote['regWizTime']) ? $remote['regWizTime'] : ''?>"
|
||
serverdesc="<?=$var['COMMENT']?>"
|
||
servermodel="<?=$var['SYS_MODEL']?>"
|
||
serverstate="<?=rawurlencode(json_encode($serverstate, JSON_UNESCAPED_SLASHES))?>"
|
||
textcolor="<?=($header) ? '#'.$header : ''?>"
|
||
theme="<?=$display['theme']?>"
|
||
uptime="<?=1000*(time() - round(strtok(exec("cat /proc/uptime"),' ')))?>"
|
||
></unraid-user-profile>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
|
||
<FILE Name="/usr/local/emhttp/plugins/dynamix/DisplaySettings.page-splice1">
|
||
<INLINE>
|
||
<![CDATA[
|
||
_(Header show description)_:
|
||
: <select name="headerdescription">
|
||
<?=mk_option($display['headerdescription'], "yes",_('Yes'))?>
|
||
<?=mk_option($display['headerdescription'], "no",_('No'))?>
|
||
</select>
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
|
||
<!-- Preserve in case plugin is removed -->
|
||
<FILE Run="/bin/bash" Method="install">
|
||
<INLINE>
|
||
<![CDATA[
|
||
cp -f /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-
|
||
|
||
cat /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php- | sed -e 's/<\/head>/<? include "\/usr\/local\/emhttp\/plugins\/dynamix.unraid.net\/include\/RegWizard.php" ?>\n<\/head>/g' > /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||
|
||
sed -i $'/<style>/{e cat /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice1\n}' /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||
sed -i $'/<\/body>/{e cat /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice2\n}' /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||
sed -i $'/<div class="block">/{e cat /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice3\n}' /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||
sed -i '/<div class="block">/,/<\/div>/d' /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php
|
||
|
||
rm /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice1
|
||
rm /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice2
|
||
rm /usr/local/emhttp/plugins/dynamix/include/DefaultPageLayout.php-splice3
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
<FILE Run="/bin/bash" Method="install">
|
||
<INLINE>
|
||
<![CDATA[
|
||
cp -f /usr/local/emhttp/plugins/dynamix/DisplaySettings.page /usr/local/emhttp/plugins/dynamix/DisplaySettings.page-
|
||
|
||
echo -e "\n\n" >> /usr/local/emhttp/plugins/dynamix/DisplaySettings.page-splice1
|
||
|
||
sed -i $'/_(Header custom text color)_/{e cat /usr/local/emhttp/plugins/dynamix/DisplaySettings.page-splice1\n}' /usr/local/emhttp/plugins/dynamix/DisplaySettings.page
|
||
|
||
rm /usr/local/emhttp/plugins/dynamix/DisplaySettings.page-splice1
|
||
]]>
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
|
||
<!-- gitty up -->
|
||
<FILE Run="/bin/bash" Method="install">
|
||
<INLINE>
|
||
sed -i "s@graphql-api.sock@unraid-api.sock@g" /etc/nginx/conf.d/emhttp-servers.conf
|
||
nginx -s reload
|
||
echo "/etc/rc.d/rc.unraid-api install; /etc/rc.d/rc.flash_backup start" | at -M now + 1 min
|
||
</INLINE>
|
||
</FILE>
|
||
|
||
</PLUGIN>
|