Menu="ManagementAccess:100"
Title="Unraid API Settings"
Icon="icon-u-globe"
Tag="globe"
---
<?php
/* Copyright 2005-2023, Lime Technology
 *
 * 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.
 */
require_once "$docroot/plugins/dynamix.my.servers/include/state.php";
require_once "$docroot/plugins/dynamix.my.servers/include/api-config.php";
require_once "$docroot/webGui/include/Wrappers.php";
$serverState = new ServerState();

$keyfile = $serverState->keyfileBase64;

$myServersFlashCfg = $serverState->myServersFlashCfg;

$showT2Fa = (file_exists('/boot/config/plugins/dynamix.my.servers/showT2Fa'));

$hasMyUnraidNetCert = preg_match('/.*\.myunraid\.net$/', $serverState->nginxCfg['NGINX_CERTNAME']);

$isRegistered = $serverState->registered;
$isMiniGraphConnected = $serverState->myServersMiniGraphConnected;

$isConnectPluginInstalled = ApiConfig::isConnectPluginEnabled();

$flashbackup_status = $serverState->flashbackupStatus;

$passwd_result = exec('/usr/bin/passwd --status root');
$boolWebUIAuth = $isRegistered && (($passwd_result !== false) && (substr($passwd_result, 0, 6) == 'root P'));

// Helper to determine the current value for the remote access input
$dynamicRemoteAccessType = $myServersFlashCfg['remote']['dynamicRemoteAccessType'];
$upnpEnabled = $myServersFlashCfg['remote']['upnpEnabled'] === 'yes';
$wanaccessEnabled = $myServersFlashCfg['remote']['wanaccess'] === 'yes';

$currentRemoteAccessValue = 'OFF';
if ($dynamicRemoteAccessType === 'STATIC') {
  $currentRemoteAccessValue = 'DYNAMIC_MANUAL';
} elseif ($dynamicRemoteAccessType === 'UPNP') {
  $currentRemoteAccessValue = 'DYNAMIC_UPNP';
} elseif ($dynamicRemoteAccessType === 'DISABLED' && $wanaccessEnabled && $upnpEnabled) {
  $currentRemoteAccessValue = 'ALWAYS_UPNP';
} elseif ($dynamicRemoteAccessType === 'DISABLED' && $wanaccessEnabled && !$upnpEnabled) {
  $currentRemoteAccessValue = 'ALWAYS_MANUAL';
}

/**
 * mimicking PHP vars below in javascript to ensure people can't trick the js postobj
 * by disabling html attrs and vars before the post sends to set the field
 */
$enableRemoteT2fa = $showT2Fa && $currentRemoteAccessValue !== 'OFF' && $hasMyUnraidNetCert;
$enableLocalT2fa = $showT2Fa && $var['USE_SSL'] === 'auto' && $hasMyUnraidNetCert;
$shade="shade-".($display['theme']??'unk');
?>
<style>
div.shade-white{background-color:#ededed;margin-top:10px;padding:8px 0 3px 0}
div.shade-black{background-color:#212121;margin-top:10px;padding:8px 0 3px 0}
div.shade-azure{background-color:#edeaef;margin-top:10px;padding:8px 0 3px 0}
div.shade-gray{background-color:#121510;margin-top:10px;padding:8px 0 3px 0}
</style>
<script>
const hasMyUnraidNetCert = <?=($hasMyUnraidNetCert ? 'true' : 'false')?>;
const wanAccessOrg = "<?=$myServersFlashCfg['remote']['wanaccess']?>";

function registerServer(button) {

    const $remoteAccessInput = $('#remoteAccess');
    const $remoteAccessManualPort = $('#wanport');

    let computedRemoteAccessConfig = null;
    switch ($remoteAccessInput.val()) {
      case 'ALWAYS_MANUAL':
        computedRemoteAccessConfig = {
          remote_wanaccess: 'yes',   // always on
          remote_wanport: $remoteAccessManualPort.val(), // value provided by user or from cfg file
          remote_upnpEnabled: 'no',  // always off
          remote_dynamicRemoteAccessType: 'DISABLED',
        };
        break;
      case 'ALWAYS_UPNP':
        computedRemoteAccessConfig = {
          remote_wanaccess: 'yes',   // always on
          remote_wanport: '',        // existing value ignored, will be set by unraid-api after starting UPnP
          remote_upnpEnabled: 'yes', // always on
          remote_dynamicRemoteAccessType: 'DISABLED',
        };
        break;
      case 'DYNAMIC_UPNP':
        computedRemoteAccessConfig = {
          remote_wanaccess: 'no',    // will be set to "yes" by Connect when requested by user
          remote_wanport: '',        // existing value ignored, will be set by unraid-api after starting UPnP
          remote_upnpEnabled: 'no',  // will be set to "yes" by Connect when requested by user
          remote_dynamicRemoteAccessType: 'UPNP',
        };
        break;
      case 'DYNAMIC_MANUAL':
        computedRemoteAccessConfig = {
          remote_wanaccess: 'no',    // will be set to "yes" by Connect when requested by user
          remote_wanport: $remoteAccessManualPort.val(), // value provided by user or from cfg file
          remote_upnpEnabled: 'no',  // always off
          remote_dynamicRemoteAccessType: 'STATIC',
        };
        break;
      default:
        computedRemoteAccessConfig = {
          remote_wanaccess: 'no',
          remote_wanport: '',
          remote_upnpEnabled: 'no',
          remote_dynamicRemoteAccessType: 'DISABLED',
        };
        break;
    }

        const enableLocalT2fa = <?=($enableLocalT2fa ? 'true' : 'false')?>;
        const enableRemoteT2fa = $remoteAccessInput.val() !== 'OFF' && hasMyUnraidNetCert;

        var postobj = {
            "#cfg": "/boot/config/plugins/dynamix.my.servers/myservers.cfg",
            ...(computedRemoteAccessConfig ? computedRemoteAccessConfig : {}),
             // only allow 'yes' value when fields are enabled
            "local_2Fa": enableLocalT2fa ? $('#local2fa').val() : 'no',
            "remote_2Fa": enableRemoteT2fa ? $('#remote2fa').val()  : 'no',
        };

        $(button).prop("disabled", true).html("_(Applying)_ <i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i>");
        $.post('/webGui/include/Dispatcher.php', postobj, function(data2) {
<?if(!$isRegistered):?>
            swal({
              title: "",
              text: "_(Your server has been registered)_",
              type: "success",
              html: true,
              allowEscapeKey: false,
              confirmButtonText: "_(Ok)_"
            }, function() {
              button.form.submit();
            });
<?else:?>
      // give the unraid-api time to call rc.nginx before refreshing the page
      const delay = 4000;
      setTimeout(function() {
        button.form.submit();
      }, delay);
<?endif?>
        });

}

function dnsCheckServer(button) {
    // can't check DYNAMIC_MANUAL unless enabled on the Connect side too
    // tell user to switch to ALWAYS_MANUAL to check their port forward
    if ($('#remoteAccess').val() == "DYNAMIC_MANUAL") {
      swal({
            title: "Oops",
            text: "_(To test your manual port forward, temporarily configure 'Allow Remote Access' as 'Always On - Manual Port Forward')_",
            type: "error",
            html: true,
            confirmButtonText: "_(Ok)_"
          });
      return;
    }

    var oldlabel = $.trim($(button).text());
    var failure = function(data) {
        var status = data.status;
        var obj = data.responseJSON;
        var msg = "_(Sorry, an error occurred)_<br>_(The error is)_: " + obj.error + ".";
        $(button).prop("disabled", false).html(oldlabel);
        swal({
          title: "Oops",
          text: msg,
          type: "error",
          html: true,
          confirmButtonText: "_(Ok)_"
        });
    };

    var success = function(data) {
        $(button).prop("disabled", false).html(oldlabel);
        if (data.status) {
            swal({
              title: "",
              text: "_(Your Unraid Server is reachable from the internet)_",
              type: "success",
              html: true,
              confirmButtonText: "_(Ok)_"
            });
        } else {
            swal({
              title: "Oops",
              text: "<?=sprintf(_("The Unraid server is unreachable from outside your network. Be sure you have configured your router to forward port") . " <strong style='font-weight: bold'>%u/TCP</strong> " . _("to the Unraid server at") . " <strong style='font-weight: bold'>%s</strong> " . _("port") . " <strong style='font-weight: bold'>%u</strong>", $myServersFlashCfg['remote']['wanport'], htmlspecialchars($eth0['IPADDR:0']??''), $var['PORTSSL']??443)?>",
              type: "error",
              html: true,
              confirmButtonText: "_(Ok)_"
            });
        }
    };

    $(button).prop("disabled", true).html("_(Checking)_ <i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i>");
    $.post('/plugins/dynamix.my.servers/include/unraid-api.php',{command:'wanip'}, function(data) {
      wanip = (data && data.result) ? data.result : "";
      $.post("https://keys.lime-technology.com/account/server/checkdns",{wanip:wanip,externalport:$('#wanport').val(),keyfile:"<?=$keyfile?>"},success).fail(failure);
    });
}

function changeRemoteAccess(dropdown) {
    let $wanPanel = $("#wanpanel");
    let $remote2faSelect = $('#remote2fa');
    let $remote2faRemark = $('#remote2fa_remark');
    const dropdownValue = $(dropdown).val();

    if (dropdownValue == 'DYNAMIC_MANUAL' || dropdownValue == 'ALWAYS_UPNP' || dropdownValue == 'ALWAYS_MANUAL') {
        $wanPanel.slideDown('fast');
        if (dropdownValue == 'ALWAYS_UPNP') {
          // don't let the user edit the UPnP port
          $("#wanport").hide();
          // show the UPnP port read-only if neither port nor dropdown has changed since page loaded
          if (didRemoteAccessFormChange()) {
            $("#wanportdisplay").hide();
          } else {
            $("#wanportdisplay").show();
          }
        } else {
          // let the user edit the port
          $("#wanport").show();
          $("#wanportdisplay").hide();
        }
        if (dropdownValue == 'DYNAMIC_MANUAL' || dropdownValue == 'ALWAYS_MANUAL') {
          // show message about manually setting a port forward
          $("#wanportmsg").show();
        } else if (dropdownValue == 'ALWAYS_UPNP') {
          $("#wanportmsg").hide();
        }
        $remote2faSelect.prop('disabled', false);
        $remote2faRemark.fadeOut();
        enableDisableCheckButton();
    } else {
        // dropdown value is 'DYNAMIC_UPNP' or 'OFF'
        $wanPanel.slideUp('fast');
        $remote2faRemark.fadeIn();
        $remote2faSelect.prop('disabled', true);
        // perhaps we don't want this to auto set to no when remote access is toggled to no. Rather the submission of the form sets the value to no.
        // this way if the user toggles remote access to no then decided to put back to yes, Remote T2FA wouldn't accidentally be turned off.
        // $remote2faSelect.val('no'); // set value to 'no' since it's not allowed to be yes when remote access is NOT enabled
    }

    // display helpful messages based on the dropdownValue
    let $remoteAccessMsgTxt = '';
    let $useConnectMsgTxt = "Visit the <a href='https://connect.myunraid.net/' target='_blank'>Connect Dashboard</a> to use Remote Access.";
    switch (dropdownValue) {
      case 'OFF':
        $useConnectMsgTxt = '';
        break;
      case 'DYNAMIC_MANUAL':
        $remoteAccessMsgTxt = "<a href='https://docs.unraid.net/go/connect-remote-access/' target='_blank'>Enable Remote Access</a> on the <a href='https://connect.myunraid.net/' target='_blank'>Connect Dashboard</a>.";
        break;
      case 'DYNAMIC_UPNP':
        $remoteAccessMsgTxt = "<a href='https://docs.unraid.net/go/connect-remote-access/' target='_blank'>Enable Remote Access</a> on the <a href='https://connect.myunraid.net/' target='_blank'>Connect Dashboard</a>, a random WAN port will be assigned by UPnP.";
        break;
      case 'ALWAYS_MANUAL':
        $remoteAccessMsgTxt = "Remote Access is always on.";
        break;
      case 'ALWAYS_UPNP':
        $remoteAccessMsgTxt = "Remote Access is always on, a random WAN port will be assigned by UPnP.";
        break;
    }
    $('#remoteAccessMsg').html($remoteAccessMsgTxt);
    $('#useConnectMsg').html($useConnectMsgTxt);

    enableDisableApplyButton();
}

function didRemoteAccessFormChange() {
    $changed = !($("#remoteAccess").val() == $("#remoteAccess").data('orig') && $("#wanport").val() == $("#wanport").data('orig'));
    return $changed;
}

function enableDisableApplyButton() {
    $('body').find('.applyBtn').prop("disabled", !didRemoteAccessFormChange());
}

function enableDisableCheckButton() {
    $("#wancheck").prop("disabled", didRemoteAccessFormChange());
    enableDisableApplyButton();
}

function checkFlashBackup() {
    $.post("https://keys.lime-technology.com/backup/flash/check",{keyfile:"<?=$keyfile?>"}, function(data) {
      if (data && data.size) {
        $("#deletecloudbackup").prop("disabled", false);
      }
    });
}

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 occurred)_<br>_(The error is)_: " + obj.error + ".";
        $(button).prop("disabled", false).html(oldlabel);
        swal({
          title: "",
          text: msg,
          type: "error",
          allowEscapeKey: false,
          html: true,
          confirmButtonText: "_(Ok)_"
        },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("_(Activating)_ <i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i>");
        $.post("/plugins/dynamix.my.servers/include/UpdateFlashBackup.php",{command:"activate"},success).fail(failure);
    }
    if (oldlabel == "_(Deactivate)_") {
        swal({
            title:_("Deactivate"),
            showCancelButton:true,
            confirmButtonText:_('Deactivate'),
            cancelButtonText:_('Cancel'),
            text:_("Are you sure you want to delete your local flash backup?")+"<br><br><label style='font-weight:bold;color:red;'><input type='checkbox' id='remove_remote_backups'> "+_("Also delete cloud backup. Restore from backup will be unavailable.")+"</label>",
            html:true,
        },function(confirm){
            if (!confirm) return;
            $(button).prop("disabled", true).html("_(Deactivating)_ <i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i>");
            $.post("/plugins/dynamix.my.servers/include/UpdateFlashBackup.php",{command:"deactivate"},success).fail(failure);
            if ($('#remove_remote_backups').is(':checked')) {
                // post to key-server to remove remote backup
                $.post("https://keys.lime-technology.com/backup/flash/delete",{keyfile:"<?=$keyfile?>"}).fail(failure);
            }
        });
    }
    if (oldlabel == "_(Delete Cloud Backup)_") {
        swal({
            title:_("Delete Cloud Backup"),
            showCancelButton:true,
            confirmButtonText:_('Delete'),
            cancelButtonText:_('Cancel'),
            text:_("Are you sure you want to delete your cloud backup? Restore from backup will be unavailable."),
            html:true,
        },function(confirm){
            if (!confirm) return;
            // post to key-server to remove remote backup
            $.post("https://keys.lime-technology.com/backup/flash/delete",{keyfile:"<?=$keyfile?>"},success).fail(failure);
        });
    }
    if (oldlabel == "_(Update)_") {
        $(button).prop("disabled", true).html("_(Updating)_ <i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i>");
        $.post("/plugins/dynamix.my.servers/include/UpdateFlashBackup.php",{command:"flush"},success).fail(failure);
    }
    if (oldlabel == "_(Changes)_") {
        openBox("/webGui/include/gitstatus.php", "_(Changes)_", 600,600, false);
    }
}

function buttonStateReset(newstate) {
    $('#inactivespanel,#activepanel,#changespanel,#uptodatepanel,#errorpanel,#remoteerrorpanel').hide();

    if (newstate['loading'] && newstate['loading']!='') {
      if (newstate['activated'] == 'true' || newstate['activated'] == 'yes') {
        $('#flashbackuptext').html('<span class="green p0">_(Activated)_:</span> <span class="blue p0">' + newstate['loading'] + ' <i class="fa fa-spinner fa-spin" aria-hidden="true"></i></span>');
      } else {
        $('#flashbackuptext').html('<span class="orange p0">_(Not activated)_:</span> <span class="blue p0">' + newstate['loading'] + ' <i class="fa fa-spinner fa-spin" aria-hidden="true"></i></span>');
      }
      $('#uptodatepanel').show();
      return;
    }

    if (newstate['error'] && newstate['error']!='') {
      $('#flashbackuperror').html('<i class="fa fa-warning icon warning" aria-hidden="true"></i> ' + newstate['error']);
      $('#errorpanel').show();
    }

    if (newstate['remoteerror'] && newstate['remoteerror']!='') {
      $('#flashbackupremoteerror').html('<i class="fa fa-warning icon warning" aria-hidden="true"></i> ' + newstate['remoteerror']);
      $('#remoteerrorpanel').show();

      // if rate limited, disable the Update button
      if (newstate['remoteerror'].includes('Rate limited')) {
        $('#changespanel button').first().prop("disabled",true);
      }
    }

    if (newstate['activated'] == 'true' || newstate['activated'] == 'yes') {
        $('#activepanel').show();

        if (newstate['uptodate'] == 'true' || newstate['uptodate'] == 'yes') {
            $('#flashbackuptext').html('<span class="green p0">_(Activated)_:</span> <span class="green p0">_(up-to-date)_</span>');
            $('#uptodatepanel').show();
        } else {
            $('#flashbackuptext').html('<span class="green p0">_(Activated)_:</span> <span class="orange p0">_(Not up-to-date)_</span>');
            $('#changespanel').show();
        }
    } else {
        $('#flashbackuptext').html('<span class="orange p0">_(Not activated)_</span>');
        $('#inactivespanel').show();
        checkFlashBackup();
    }
}
$.post('/plugins/dynamix.my.servers/include/UpdateFlashBackup.php',{command:"status"});

function applyExtraOrigins(button) {
  const postobj = {
      "#cfg": "/boot/config/plugins/dynamix.my.servers/myservers.cfg",
      "api_extraOrigins": validateExtraOrigins(),
  };

  $(button).prop("disabled", true).html("_(Applying)_ <i class=\"fa fa-spinner fa-spin\" aria-hidden=\"true\"></i>");
  $.post('/webGui/include/Dispatcher.php', postobj, function(_extraOriginsData) {
    window.location.reload();
  });
}

let isExtraOriginsValid = false;
const validateExtraOrigins = () => {
  const val = $('.js-extraOrigins').val();
  const $label = $('.js-extraOriginsLabel'); // setting here in this scope b/c it didn't work setting outside of this function
  if (!val) {
    $label.removeClass('red-text');
    isExtraOriginsValid = true;
    return '';
  }
  let trimmed = val.replace(/\s+/g, '');
  if (trimmed.slice(-1) === ',') trimmed = trimmed.slice(0, -1); // if last char is in string is a comma we need to remove it other wise `isCommaSeparatedURLs` will fail
  const isValid = isCommaSeparatedURLs(trimmed);
  if (!isValid) { // tell the user to fix it
    $label.addClass('red-text');
    isExtraOriginsValid = false;
    return '';
  }
  // remove any negative feedback
  $label.removeClass('red-text');
  isExtraOriginsValid = true;
  return trimmed;
};
const isCommaSeparatedURLs = input =>
  input
    .split(",")
    .every(value => /^(http|https):\/\/[^ "]+$/.test(value));

// toggle extra origins apply button
function extraOriginsValidateAndToggleDisabled() {
  validateExtraOrigins();
  if (!isExtraOriginsValid) {
    return $('.js-extraOriginsApply').prop("disabled",true);
  }i
  return $('.js-extraOriginsApply').removeAttr('disabled');
}

$('body').on('change keyup', '.js-extraOrigins', function(data) {
  console.debug('change keyup', data);
  extraOriginsValidateAndToggleDisabled();
});

// Add the current URL to the Extra Origins setting on click then focus the input and automatically add the current window's origin
$('body').on('click', '.js-setCurrentHostExtraOrigins', function(e) {
  e.preventDefault();
  $extraOriginsInput = $('.js-extraOrigins');
  // if the curent window origin is already in the input, run the validation and enable the apply button
  if ($extraOriginsInput.val().includes(window.location.origin)) {
    return extraOriginsValidateAndToggleDisabled();
  }

  $extraOriginsInput.focus();
  if ($extraOriginsInput.val().length > 0) {
    // don't overwrite the value of the input, only add to it
    $extraOriginsInput.val($extraOriginsInput.val() + ', ' + window.location.origin);
  } else {
    // ensure the current window.location.origin is not already in the input before adding it
    $extraOriginsInput.val(window.location.origin);
  }
  // run the validation on the field and enable the apply button
  extraOriginsValidateAndToggleDisabled();
});
</script>

<?if($isConnectPluginInstalled):?>
<div markdown="1" class="<?=$shade?>"><!-- begin Flash Backup section -->
<?if(!$isRegistered):?>
_(Flash backup)_:
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until you have signed in)_</span>
<?elseif(!$isMiniGraphConnected):?>
 _(Flash backup)_:
: <span><i class="fa fa-warning icon warning"></i> _(Disabled until connected to Unraid Connect Cloud - try reloading the page)_</span>
<?else: // begin show flash backup form ?>
_(Flash backup)_:
: <span id='flashbackuptext'><span class='blue p0'>_(Loading)_ <i class="fa fa-spinner fa-spin" aria-hidden="true"></i></span></span>

<form markdown="1" name="FlashBackup" method="POST" action="/update.htm" target="progressFrame">

<div markdown="1" id="errorpanel" style="display:none">
&nbsp;
: <span id="flashbackuperror"></span>

</div>
<div markdown="1" id="remoteerrorpanel" style="display:none">
&nbsp;
: <span id="flashbackupremoteerror"></span>

</div>
<div markdown="1" id="inactivespanel" style="display:none">
<?if(disk_free_space('/boot') > 1024*1000*1000):?>
&nbsp;
: <span class="buttons-spaced">
    <button type="button" onclick="enableFlashBackup(this)">_(Activate)_</button>
    <span>_(Please note that the flash backup is not encrypted at this time.)_ <a href="https://docs.unraid.net/go/connect-flash-backup/" target="_blank">_(More information.)_</a></span>
  </span>
<?else:?>
&nbsp;
: <span class="buttons-spaced">
    <button type="button" disabled>_(Activate)_</button>
    <span><i class="fa fa-warning icon warning"></i> _(In order to activate Flash Backup there must be at least 1GB of free space on your flash drive.)_</span>
  </span>
<?endif?>

&nbsp;
: <span class="buttons-spaced">
    <button type="button" onclick="enableFlashBackup(this)" id="deletecloudbackup" disabled>_(Delete Cloud Backup)_</button>
  </span>

:unraidnet_inactivespanel_help:

</div>
<div markdown="1" id="changespanel" style="display:none">
&nbsp;
: <span class="buttons-spaced">
    <button type="button" onclick="enableFlashBackup(this)">_(Update)_</button>
    <button type="button" onclick="enableFlashBackup(this)">_(Changes)_</button>
  </span>

:unraidnet_changespanel_help:

</div>
<div markdown="1" id="uptodatepanel" style="display:none">
&nbsp;
: <span class="buttons-spaced">
    <button type="button" disabled>_(Update)_</button>
  </span>

:unraidnet_uptodatepanel_help:

</div>
<div markdown="1" id="activepanel" style="display:none">
&nbsp;
: <span class="buttons-spaced">
    <button type="button" onclick="enableFlashBackup(this)">_(Deactivate)_</button>
    <span>_(Please note that the flash backup is not encrypted at this time.)_ <a href="https://docs.unraid.net/go/connect-flash-backup/" target="_blank">_(More information.)_</a></span>
  </span>

<?if (in_array($_COOKIE['UPC_ENV']??'', ['development','staging']) && file_exists("/var/log/gitflash") && filesize("/var/log/gitflash")):?>
&nbsp;
: <span class="buttons-spaced">
    <button type="button" onclick="openBox('/plugins/dynamix.my.servers/scripts/gitflash_log', 'Flash Backup Error Log', 800, 800);">_(View Flash Backup Error Log)_</button>
    <span><em>_(Transient errors in this log can be ignored unless you are having issues)_</em></span>
  </span>
<?endif?>

</div>
</form>
<script>
changeRemoteAccess($('#remoteAccess'));
var flashbackupsub = new NchanSubscriber('/sub/flashbackup');
var initialFlashBackupState = <?=
    json_encode(
        $flashbackup_status ?: [],
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
    )
?> || {};

if (Object.keys(initialFlashBackupState).length === 0) {
  initialFlashBackupState = {
    activated: 'no',
    uptodate: 'no',
    loading: '',
    error: '',
    remoteerror: ''
  };
}

flashbackupsub.on('message', function(data) {
  var ini = parseINI(data);

  buttonStateReset(ini['flashbackup']);
});
$(function() {
  buttonStateReset(initialFlashBackupState);
  flashbackupsub.start();
  if ( typeof caPluginUpdateCheck === "function" ) {
    caPluginUpdateCheck("dynamix.unraid.net.plg",{name:"Unraid Connect"});
  }
});
</script>
<?endif // end show flash backup form ?>
</div><!-- end Flash Backup section -->
<?endif // end connect plugin check ?>

<!-- legacy search compatibility -->
<div style="display:none;">
_(Unraid API extra origins)_:
_(Connect Remote Access)_:
_(GraphQL API Developer Sandbox)_:
_(OIDC Configuration)_:

</div>

<!-- start unraid-api section -->
<unraid-connect-settings></unraid-connect-settings>
