374 lines
24 KiB
HTML
374 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
|
|
|
|
<title>Cloudron Restore</title>
|
|
<meta name="description" content="Cloudron Restore">
|
|
|
|
<link id="favicon" href="/api/v1/cloudron/avatar" rel="icon" type="image/png">
|
|
|
|
<!-- contains all thing already using import statement -->
|
|
<script type="module" src="./src/modules.js"></script>
|
|
|
|
<!-- Theme CSS -->
|
|
<link type="text/css" rel="stylesheet" href="/theme.css">
|
|
|
|
<!-- jQuery-->
|
|
<script type="text/javascript" src="/js/jquery.min.js"></script>
|
|
|
|
<!-- async -->
|
|
<script type="text/javascript" src="/js/async-3.2.0.min.js"></script>
|
|
|
|
<!-- Angularjs scripts -->
|
|
<script type="text/javascript" src="/js/angular.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-loader.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-cookies.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-md5.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-ui-notification.js"></script>
|
|
<script type="text/javascript" src="/js/autofill-event.js"></script>
|
|
|
|
<!-- Angular directives for bootstrap https://angular-ui.github.io/bootstrap/ -->
|
|
<script type="text/javascript" src="/js/ui-bootstrap-tpls-1.3.3.min.js"></script>
|
|
|
|
<!-- Angular translate https://angular-translate.github.io/ -->
|
|
<script type="text/javascript" src="/js/angular-translate.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-translate-loader-static-files.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-translate-storage-cookie.min.js"></script>
|
|
<script type="text/javascript" src="/js/angular-translate-storage-local.min.js"></script>
|
|
|
|
<!-- Showdown (markdown converter) -->
|
|
<script type="text/javascript" src="/js/showdown-1.9.1.min.js"></script>
|
|
|
|
<!-- Setup Application -->
|
|
<script type="text/javascript" src="/js/restore.js"></script>
|
|
<script type="text/javascript" src="/js/client.js"></script>
|
|
<script type="text/javascript" src="/js/utils.js"></script>
|
|
|
|
</head>
|
|
|
|
<body class="setup" ng-app="Application" ng-controller="RestoreController">
|
|
|
|
<a class="offline-banner animateMe" ng-show="client.offline" ng-cloak href="https://docs.cloudron.io/troubleshooting/" target="_blank"><i class="fa fa-circle-notch fa-spin"></i> Cloudron is offline. Reconnecting...</a>
|
|
|
|
<div class="main-container ng-cloak text-center" ng-show="busy">
|
|
<div class="row">
|
|
<div class="col-md-6 col-md-offset-3">
|
|
<i class="fa fa-circle-notch fa-spin fa-5x"></i><br/>
|
|
<h3>{{ message }} ...</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="main-container ng-cloak" ng-show="initialized && !busy">
|
|
<div class="row">
|
|
<div class="col-md-6 col-md-offset-3">
|
|
<div class="card" style="max-width: none; padding: 20px;">
|
|
<form name="configureBackupForm" role="form" novalidate ng-submit="restore()" autocomplete="off">
|
|
<div class="row">
|
|
<div class="col-md-10 col-md-offset-1 text-center">
|
|
<h2>Cloudron Restore</h2>
|
|
<p>Provide the backup to restore from</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin-bottom: 20px">
|
|
<div class="col-md-8 col-md-offset-2 text-center">
|
|
<input type="file" id="backupConfigFileInput" style="display:none"/>
|
|
<button type="button" class="btn btn-default" onclick="getElementById('backupConfigFileInput').click();">Upload Backup Config</button>
|
|
</div>
|
|
<br/>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-8 col-md-offset-2">
|
|
<p class="has-error text-center" ng-show="error">{{ error.generic }}</p>
|
|
|
|
<div class="form-group">
|
|
<label class="control-label" for="storageProviderProvider">Storage provider <sup><a ng-href="https://docs.cloudron.io/backups/#storage-providers" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
<select class="form-control" id="storageProviderProvider" ng-model="provider" ng-options="a.value as a.name for a in storageProviders" ng-change=clearForm()></select>
|
|
</div>
|
|
|
|
<!-- mountpoint -->
|
|
<div class="form-group" ng-class="{ 'has-error': error.mountPoint }" ng-show="provider === 'mountpoint'">
|
|
<label class="control-label" for="inputConfigureMountPoint">Mountpoint</label>
|
|
<input type="text" class="form-control" ng-model="mountPoint" id="inputConfigureMountPoint" name="mountPoint" ng-disabled="busy" placeholder="Folder where filesystem is mounted" ng-required="provider === 'mountpoint'">
|
|
</div>
|
|
|
|
<!-- CIFS/NFS/SSHFS -->
|
|
<div class="form-group" ng-show="provider === 'cifs' || provider === 'nfs' || provider === 'sshfs'">
|
|
<label class="control-label" for="configureBackupHost">Server IP or Hostname</label>
|
|
<input type="text" class="form-control" ng-model="mountOptions.host" id="configureBackupHost" name="host" ng-disabled="busy" placeholder="Server IP or hostname" ng-required="provider === 'cifs' || provider === 'nfs'">
|
|
</div>
|
|
|
|
<!-- CIFS -->
|
|
<div class="checkbox" ng-show="provider === 'cifs'">
|
|
<label>
|
|
<input type="checkbox" ng-model="mountOptions.seal">Use seal encryption. Requires at least SMB v3</input>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- CIFS/NFS/SSHFS -->
|
|
<div class="form-group" ng-show="provider === 'cifs' || provider === 'nfs' || provider === 'sshfs'">
|
|
<label class="control-label" for="configureBackupRemoteDir">Remote Directory</label>
|
|
<input type="text" class="form-control" ng-model="mountOptions.remoteDir" id="configureBackupRemoteDir" name="remoteDir" ng-disabled="busy" placeholder="/share" ng-required="provider === 'cifs' || provider === 'nfs'">
|
|
</div>
|
|
|
|
<!-- CIFS -->
|
|
<div class="form-group" ng-show="provider === 'cifs'">
|
|
<label class="control-label" for="configureBackupUsername">Username ({{ provider }})</label>
|
|
<input type="text" class="form-control" ng-model="mountOptions.username" id="configureBackupUsername" name="cifsUsername" ng-disabled="busy">
|
|
</div>
|
|
|
|
<!-- CIFS -->
|
|
<div class="form-group" ng-show="provider === 'cifs'">
|
|
<label class="control-label" for="configureBackupPassword">Password ({{ provider }})</label>
|
|
<input type="password" class="form-control" ng-model="mountOptions.password" id="configureBackupPassword" name="cifsPassword" ng-disabled="busy" password-reveal>
|
|
</div>
|
|
|
|
<!-- EXT4/XFS -->
|
|
<div class="form-group" ng-class="{ 'has-error': error.diskPath }" ng-show="provider === 'ext4' || provider === 'xfs'">
|
|
<label class="control-label" for="inputConfigureDiskPath">Disk Path</label>
|
|
<input type="text" class="form-control" ng-model="mountOptions.diskPath" id="inputConfigureDiskPath" name="diskPath" ng-disabled="busy" placeholder="Directory for backups" ng-required="provider === 'ext4' || provider === 'xfs'">
|
|
</div>
|
|
|
|
<!-- Disk -->
|
|
<div class="form-group" ng-class="{ 'has-error': error.diskPath }" ng-show="provider === 'disk'">
|
|
<label class="control-label">Device</label>
|
|
<select class="form-control" ng-model="disk" ng-options="item as item.label for item in blockDevices track by item.path" ng-required="provider === 'disk'"></select>
|
|
</div>
|
|
|
|
<!-- SSHFS -->
|
|
<div class="form-group" ng-show="provider === 'sshfs'">
|
|
<label class="control-label" for="configureBackupPort">SSH Port</label>
|
|
<input type="number" class="form-control" ng-model="mountOptions.port" id="configureBackupPort" name="port" ng-disabled="busy">
|
|
</div>
|
|
|
|
<!-- SSHFS -->
|
|
<div class="form-group" ng-show="provider === 'sshfs'">
|
|
<label class="control-label" for="configureBackupUser">SSH User</label>
|
|
<input type="text" class="form-control" ng-model="mountOptions.user" id="configureBackupUser" name="user" ng-disabled="busy">
|
|
</div>
|
|
|
|
<!-- SSHFS -->
|
|
<div class="form-group" ng-show="provider === 'sshfs'">
|
|
<label class="control-label" for="configureBackupPrivateKey">SSH Private Key</label>
|
|
<textarea class="form-control" ng-model="mountOptions.privateKey" id="configureBackupPrivateKey" name="privateKey" ng-disabled="busy"></textarea>
|
|
</div>
|
|
|
|
<!-- Filesystem -->
|
|
<div class="form-group" ng-class="{ 'has-error': error.backupFolder }" ng-show="provider === 'filesystem'">
|
|
<label class="control-label" for="inputConfigureBackupFolder">Local backup directory</label>
|
|
<input type="text" class="form-control" ng-model="backupFolder" id="inputConfigureBackupFolder" name="backupFolder" ng-disabled="busy" placeholder="Directory for backups" ng-required="provider === 'filesystem'">
|
|
</div>
|
|
|
|
<!-- S3/Minio/SOS -->
|
|
<div class="form-group" ng-class="{ 'has-error': error.endpoint }" ng-show="provider === 'minio' || provider === 'upcloud-objectstorage' || provider === 'backblaze-b2' || provider === 'cloudflare-r2' || provider === 's3-v4-compat' || provider === 'idrive-e2'">
|
|
<label class="control-label" for="inputConfigureBackupEndpoint">Endpoint</label>
|
|
<input type="text" class="form-control" ng-model="endpoint" id="inputConfigureBackupEndpoint" name="endpoint" ng-disabled="busy" placeholder="URL" ng-required="provider === 'minio' || provider === 'upcloud-objectstorage' || provider === 'backblaze-b2' || provider === 'cloudflare-r2' || provider === 's3-v4-compat' || provider === 'idrive-e2'">
|
|
</div>
|
|
|
|
<div class="checkbox" ng-show="provider === 'minio' || provider === 's3-v4-compat'" >
|
|
<label>
|
|
<input type="checkbox" ng-model="acceptSelfSignedCerts" id="inputConfigureBackupSelfSigned">
|
|
Accept Self-signed certificate
|
|
</input>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.bucket }" ng-show="s3like(provider) || provider === 'gcs'">
|
|
<label class="control-label" for="inputConfigureBackupBucket">Bucket name</label>
|
|
<input type="text" class="form-control" ng-model="bucket" id="inputConfigureBackupBucket" name="bucket" ng-disabled="busy" ng-required="s3like(provider)">
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.prefix }" ng-show="provider !== 'filesystem' && provider !== 'noop'">
|
|
<label class="control-label" for="inputConfigureBackupPrefix">Prefix</label>
|
|
<input type="text" class="form-control" ng-model="prefix" id="inputConfigureBackupPrefix" name="prefix" ng-disabled="busy" placeholder="Prefix for backup file names">
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 's3'">
|
|
<label class="control-label" for="inputConfigureBackupS3Region">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupS3Region" ng-model="region" ng-options="a.value as a.name for a in s3Regions" ng-disabled="busy" ng-required="provider === 's3'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 's3-v4-compat'">
|
|
<label class="control-label" for="inputConfigureBackupS3V4CompatRegion">Region</label>
|
|
<input class="form-control" type="text" name="region" id="inputConfigureBackupS3V4CompatRegion" ng-model="region" ng-disabled="busy" placeholder="Leave empty to use us-east-1 as default"></input>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'digitalocean-spaces'">
|
|
<label class="control-label" for="inputConfigureBackupDORegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupDORegion" ng-model="endpoint" ng-options="a.value as a.name for a in doSpacesRegions" ng-disabled="busy" ng-required="provider === 'digitalocean-spaces'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'exoscale-sos'">
|
|
<label class="control-label" for="inputConfigureBackupExoscaleRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupExoscaleRegion" ng-model="endpoint" ng-options="a.value as a.name for a in exoscaleSosRegions" ng-disabled="busy" ng-required="provider === 'exoscale-sos'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'hetzner-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupHetznerRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupHetznerRegion" ng-model="endpoint" ng-options="a.value as a.name for a in hetznerRegions" ng-disabled="busy" ng-required="provider === 'hetzner-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'wasabi'">
|
|
<label class="control-label" for="inputConfigureBackupWasabiRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupWasabiRegion" ng-model="endpoint" ng-options="a.value as a.name for a in wasabiRegions" ng-disabled="busy" ng-required="provider === 'wasabi'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'scaleway-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupScalewayRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupScalewayRegion" ng-model="endpoint" ng-options="a.value as a.name for a in scalewayRegions" ng-disabled="busy" ng-required="provider === 'scaleway-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'linode-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupLinodeRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupLinodeRegion" ng-model="endpoint" ng-options="a.value as a.name for a in linodeRegions" ng-disabled="busy" ng-required="provider === 'linode-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'ovh-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupOvhRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupOvhRegion" ng-model="endpoint" ng-options="a.value as a.name for a in ovhRegions" ng-disabled="busy" ng-required="provider === 'ovh-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'ionos-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupIonosRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupIonosRegion" ng-model="endpoint" ng-options="a.value as a.name for a in ionosRegions" ng-disabled="busy" ng-required="provider === 'ionos-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'vultr-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupVultrRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupVultrRegion" ng-model="endpoint" ng-options="a.value as a.name for a in vultrRegions" ng-disabled="busy" ng-required="provider === 'vultr-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.region }" ng-show="provider === 'contabo-objectstorage'">
|
|
<label class="control-label" for="inputConfigureBackupContaboRegion">Region</label>
|
|
<select class="form-control" name="region" id="inputConfigureBackupContaboRegion" ng-model="endpoint" ng-options="a.value as a.name for a in contaboRegions" ng-disabled="busy" ng-required="provider === 'contabo-objectstorage'"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.accessKeyId }" ng-show="s3like(provider)">
|
|
<label class="control-label" for="inputConfigureBackupAccessKeyId">Access key id</label>
|
|
<input type="text" class="form-control" ng-model="accessKeyId" id="inputConfigureBackupAccessKeyId" name="accessKeyId" ng-disabled="busy" ng-required="s3like(provider)">
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.secretAccessKey }" ng-show="s3like(provider)">
|
|
<label class="control-label" for="inputConfigureBackupSecretAccessKey">Secret access key</label>
|
|
<input type="text" class="form-control" ng-model="secretAccessKey" id="inputConfigureBackupSecretAccessKey" name="secretAccessKey" ng-disabled="busy" ng-required="s3like(provider)">
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.gcsKeyInput }" ng-show="provider === 'gcs'">
|
|
<label class="control-label" for="gcsKeyInput">Service Account Key</label>
|
|
|
|
<div class="input-group">
|
|
<input type="file" id="gcsKeyFileInput" style="display:none"/>
|
|
<input type="text" class="form-control" placeholder="Service Account Key" ng-model="gcsKey.keyFileName" id="gcsKeyInput" name="cert" onclick="getElementById('gcsKeyFileInput').click();" style="cursor: pointer;" ng-disabled="busy" ng-required="provider === 'gcs'">
|
|
<span class="input-group-addon">
|
|
<i class="fa fa-upload" onclick="getElementById('gcsKeyFileInput').click();"></i>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="control-label" for="storageFormat">Storage Format</label>
|
|
<select class="form-control" id="storageFormat" ng-change="key = ''" ng-model="format" ng-options="a.value as a.name for a in formats"></select>
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.remotePath }">
|
|
<label class="control-label" for="inputConfigureRemotePath">Backup Path<sup><a ng-href="https://docs.cloudron.io/backups/#restore-cloudron" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
|
|
<input type="text" class="form-control" ng-model="remotePath" name="inputConfigureBackupId" placeholder="e.g. 2024-02-20-130007-637/box_v7.4.3.tar.gz" required ng-disabled="busy">
|
|
</div>
|
|
|
|
<div class="form-group" ng-class="{ 'has-error': error.key }">
|
|
<label class="control-label" for="inputConfigureBackupPassword">Encryption password <span ng-hide="encrypted">(optional)</span></label>
|
|
<input type="text" class="form-control" ng-model="password" id="inputConfigureBackupPassword" name="prefix" ng-disabled="busy" placeholder="Passphrase used to encrypt the backups" ng-required="encrypted">
|
|
</div>
|
|
|
|
<div class="checkbox" ng-show="format === 'rsync' && password.length !== 0">
|
|
<label>
|
|
<input type="checkbox" ng-model="encryptedFilenames">Decrypt Filenames</input>
|
|
</label>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<div class="checkbox">
|
|
<label>
|
|
<input type="checkbox" ng-model="skipDnsSetup"><b>Dry run</b></sup>
|
|
</label>
|
|
<br/>
|
|
<small>When enabled, apps are restored but the DNS records are not updated to point to this server. To access the dashboard, this browser's host must have an entry in <code>/etc/hosts</code> for the dashboard domain to this server's IP.
|
|
See the <a href="https://docs.cloudron.io/backups/#dry-run" target="_blank">docs</a> for more information.</small>
|
|
</div>
|
|
|
|
<input class="ng-hide" type="submit" ng-disabled="configureBackupForm.$invalid"/>
|
|
|
|
<div uib-collapse="!advancedVisible">
|
|
<!-- IPv4 provider -->
|
|
<div class="form-group">
|
|
<label class="control-label">IPv4 Configuration <sup><a ng-href="https://docs.cloudron.io/networking/#ip-configuration" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
<select class="form-control" ng-model="ipv4Config.provider" ng-options="a.value as a.name for a in ipProviders"></select>
|
|
</div>
|
|
|
|
<!-- IPv4 Fixed -->
|
|
<div class="form-group" ng-show="ipv4Config.provider === 'fixed'">
|
|
<label class="control-label">IPv4 Address</label>
|
|
<input type="text" class="form-control" ng-model="ipv4Config.ip" name="ipv4" ng-required="ipv4Config.provider === 'fixed'">
|
|
</div>
|
|
|
|
<!-- IPv4 Network Interface -->
|
|
<div class="form-group" ng-show="ipv4Config.provider === 'network-interface'">
|
|
<label class="control-label">IPv4 Interface Name</label>
|
|
<input type="text" class="form-control" ng-model="ipv4Config.ifname" name="ifname4" ng-required="ipv4Config.provider === 'network-interface'">
|
|
</div>
|
|
|
|
|
|
<!-- IPv6 provider -->
|
|
<div class="form-group">
|
|
<label class="control-label">IPv6 Configuration <sup><a ng-href="https://docs.cloudron.io/networking/#ip-configuration" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
<select class="form-control" ng-model="ipv6Config.provider" ng-options="a.value as a.name for a in ipProviders"></select>
|
|
</div>
|
|
|
|
<!-- IPv6 Fixed -->
|
|
<div class="form-group" ng-show="ipv6Config.provider === 'fixed'">
|
|
<label class="control-label">IPv6 Address</label>
|
|
<input type="text" class="form-control" ng-model="ipv6Config.ip" name="ipv6" ng-required="ipv6Config.provider === 'fixed'">
|
|
</div>
|
|
|
|
<!-- IPv6 Network Interface -->
|
|
<div class="form-group" ng-show="ipv6Config.provider === 'network-interface'">
|
|
<label class="control-label">IPv6 Interface Name</label>
|
|
<input type="text" class="form-control" ng-model="ipv6Config.ifname" name="ifname6" ng-required="ipv6Config.provider === 'network-interface'">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-center">
|
|
<a href="" ng-click="advancedVisible = true" ng-hide="advancedVisible">Advanced settings...</a>
|
|
<a href="" ng-click="advancedVisible = false" ng-show="advancedVisible">Hide Advanced settings</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 text-center">
|
|
<button type="submit" class="btn btn-primary" ng-disabled="configureBackupForm.$invalid"/><i class="fa fa-circle-notch fa-spin" ng-show="busy"></i> Restore</button>
|
|
</div>
|
|
</div>
|
|
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="text-center">
|
|
<span class="text-muted">©2022 <a href="https://cloudron.io" target="_blank">Cloudron</a></span>
|
|
<span class="text-muted"><a href="https://forum.cloudron.io" target="_blank">Forum <i class="fa fa-comments"></i></a></span>
|
|
</footer>
|
|
|
|
</body>
|
|
</html>
|