391 lines
16 KiB
Django/Jinja
391 lines
16 KiB
Django/Jinja
{% extends "layouts/base.jinja2" %}
|
|
|
|
{% block head %}
|
|
{{ super() }}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<main>
|
|
<div class="section no-pad-bot" id="index-banner">
|
|
<div class="container">
|
|
<br><br>
|
|
<h1 class="header center">Get Started</h1>
|
|
<div class="row center">
|
|
<h5 class="header col s12 light">To begin, this add-on will need access to your Google Drive™.
|
|
<span id="flavor_auto_folder">On your first backup it will create a new folder at the root of your <a
|
|
href="https://drive.google.com/drive/my-drive" target="_blank" rel="noreferrer">My Drive</a> where future backups will be stored. Once
|
|
its created, you can move the folder wherever you want.</span>
|
|
<span id="flavor_specific_folder">After connecting with Google Drive, you'll be prompted to select your
|
|
backup folder.</span>
|
|
</h5>
|
|
</div>
|
|
<div>
|
|
|
|
</div>
|
|
<div class="row center">
|
|
<div id="option1" class="col m12 l6 offset-l3 center center-align">
|
|
<div id="option1-step1">
|
|
<div class="row center">
|
|
<div class="col s12">
|
|
<h6 id="what_do_next_now_please" style="display: none;" class="header light">
|
|
<span id="what_do_next_now_please_text"></span>
|
|
Change your <a href="#!" onclick="loadSettings()"><i class="material-icons"
|
|
style="display: inline; margin-right: 2px; vertical-align: middle; font-size: 15px">settings</i>settings</a>
|
|
to alter the add-on's behavior if that isn't what you want.
|
|
</h6>
|
|
</div>
|
|
</div>
|
|
<a target="_blank" id="authenticate-button" style="margin: 10px"
|
|
class="btn-large btn-flat btn-high-vis">
|
|
Authenticate with Google Drive
|
|
</a>
|
|
<div class="row center">
|
|
<div class="col s12">
|
|
<ul class="browser-default" style="text-align: left;">
|
|
<li>
|
|
Clicking this link authenticates you with an external domain maintained by the developer of this add-on (<a
|
|
href="https://habackup.io">habackup.io</a>).
|
|
</li>
|
|
<li>You can <a href="https://github.com/sabeechen/hassio-google-drive-backup/blob/master/hassio-google-drive-backup/AUTHENTICATION.md" target="_blank" rel="noreferrer">read this</a> to understand what this means and why it's necessary.
|
|
</li>
|
|
<li>
|
|
By doing this you must agree to the <a href="pp">Privacy Policy</a> and <a href="tos">Terms of Service</a></h6>
|
|
</li>
|
|
<li>
|
|
The addon will only have access to the files and folders it creates. It can't see any of the other files you've added to Google Drive.</h6>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="row center">
|
|
<div class="col s12">
|
|
|
|
After you get the authorization code from the link above, paste the result here:
|
|
<br/>
|
|
<textarea id="google_credentials_box" style="height: 5rem;"></textarea>
|
|
<br>
|
|
<div class="left" style="display: none;" id="save_cred_message">
|
|
<div class="cls-spinner left" id="save_cred_working">
|
|
<div class="cls-signal"></div>
|
|
</div>
|
|
<div class="left center" style="display: flex;align-items: center;height: 52px;" id="save_cred_message"></div>
|
|
</div>
|
|
|
|
<a target="_blank" class="right btn-large btn-flat btn-high-vis disabled"
|
|
id="send_creds_button" onclick="send_creds($('#google_credentials_box').val())">Save</a>
|
|
</div>
|
|
</div>
|
|
<div class="row center">
|
|
To generate your own private credentials with Google instead that doesn't use a third-party domain, <a href="#" onclick="doOption2();">click here</a>. Fair warning: that way is much more complicated.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="option2" class="col s12 m6 offset-m3 default-hidden">
|
|
<div id="option2-step1">
|
|
Follow the <a target="_blank" rel="noreferrer"
|
|
href="https://github.com/sabeechen/hassio-google-drive-backup/blob/master/LOCAL_AUTH.md">instructions
|
|
here</a> to get your own client ID and client secret from Google's Cloud Console. This is a much more complicated and time
|
|
consuming approach but doesn't require you to authenticate yourself with an external domain.
|
|
<div class="row">
|
|
<form class="col s10" action="manualauth" method="get">
|
|
<div class="row">
|
|
<div class="input-field col s12">
|
|
<i class="material-icons prefix">vpn_key</i>
|
|
<input id="client_id" name="client_id" type="text" class="validate" />
|
|
<label for="client_id">Client ID</label>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="input-field col s12">
|
|
<i class="material-icons prefix">vpn_key</i>
|
|
<input id="client_secret" name="client_secret" type="text" class="validate" />
|
|
<label for="client_secret">Client Secret</label>
|
|
</div>
|
|
</div>
|
|
<div class=row>
|
|
<div class="input-field col s12">
|
|
<a target="_blank" class="right btn-large btn-flat btn-high-vis disabled right-align" style="position: absolute; right: 0px;"
|
|
id="secret_button" onclick="getAuthUrl()">Next</a>
|
|
<a target="_blank" class="right btn-large btn-flat btn-high-vis left-align" style="position: absolute; left: 30px;"
|
|
id="secret_button" onclick="backOption2Step1()"><i class="material-icons">arrow_back</i>back</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="row" style="display: none" id="option2-step2">
|
|
<div class="col s12">
|
|
<div class="row">
|
|
<div class="col s12">
|
|
Please navigate to <a id="custom-auth-link" target="_blank" rel="noreferrer"></a> and enter the code shown below to authorize the addon using your Google account. This page will automatically update once you do so.
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s12">
|
|
<span id="code-box" class="device-code-box"></span>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s12">
|
|
<div>
|
|
Expires: <span id="device-code-valid-time-left"></span>
|
|
</div>
|
|
<div id="device-code-loading" class="progress">
|
|
<div class="indeterminate"></div>
|
|
</div>
|
|
<div id="device-code-status-text">
|
|
Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col s12">
|
|
<a target="_blank" class="right btn-large btn-flat btn-high-vis left-align"
|
|
id="secret_button" onclick="backOption2Step2()">Back</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<br><br>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script type="text/javascript">
|
|
|
|
function doOption2() {
|
|
$('#option2').show();
|
|
$('#option1').hide();
|
|
}
|
|
|
|
function backOption2Step1() {
|
|
$('#option2').hide();
|
|
$('#option1').show();
|
|
}
|
|
function backOption2Step2() {
|
|
$("#option2-step1").show();
|
|
$("#option2-step2").hide();
|
|
}
|
|
|
|
function submitCode() {
|
|
var code = $("#code").val();
|
|
if (!code || code.length == 0) {
|
|
M.toast({ html: "Please enter the code you got form Google by clicking \"connect\" above" })
|
|
}
|
|
var url = "manualauth?code=" + code;
|
|
$.get(url,
|
|
function (data) {
|
|
M.toast({ html: "Submitting authorization code..." })
|
|
if (data.hasOwnProperty("auth_url")) {
|
|
console.log("Redirecting to " + data.auth_url)
|
|
window.location = data.auth_url
|
|
} else if (data.hasOwnProperty("error")) {
|
|
M.toast({ html: data.error })
|
|
}
|
|
}, "json")
|
|
}
|
|
|
|
function updateHref() {
|
|
var client_id = $("#client_id").val();
|
|
var client_secret = $("#client_secret").val();
|
|
if (client_id && client_id.length > 0 && client_secret && client_secret.length > 0) {
|
|
$("#secret_button").removeClass('disabled');
|
|
$("#help_text").fadeIn(400)
|
|
$("#code_form").fadeIn(400)
|
|
} else {
|
|
$("#secret_button").addClass('disabled');
|
|
}
|
|
}
|
|
|
|
function updateCredsButton() {
|
|
var text = $('#google_credentials_box').val()
|
|
if (text.length > 0){
|
|
$("#send_creds_button").removeClass('disabled');
|
|
} else {
|
|
$("#send_creds_button").addClass('disabled');
|
|
}
|
|
}
|
|
|
|
function send_creds(value) {
|
|
path = URI("{{save_drive_creds_path}}").query({
|
|
'creds': value.trim(),
|
|
'host': getOutterHomeUri()
|
|
});
|
|
postJson(path.toString(), {}, function(data){
|
|
window.location.assign(getInnerHomeUri());
|
|
},
|
|
function(data){
|
|
M.toast({ html: "Couldn't save these credentials, did you copy the whole string?" })
|
|
}, "Saving credentials")
|
|
}
|
|
|
|
last_cred_version = -1;
|
|
function reloadForNewCreds() {
|
|
var jqxhr = $.get("getstatus", function (data) {
|
|
last_data = data;
|
|
if (data.is_custom_creds) {
|
|
$(".hide-for-custom-creds").hide();
|
|
$(".hide-for-default-creds").show();
|
|
} else {
|
|
$(".hide-for-custom-creds").show();
|
|
$(".hide-for-default-creds").hide();
|
|
}
|
|
|
|
if (data.is_specify_folder) {
|
|
$("#flavor_auto_folder").hide();
|
|
$("#flavor_specific_folder").show();
|
|
} else {
|
|
$("#flavor_auto_folder").show();
|
|
$("#flavor_specific_folder").hide();
|
|
}
|
|
|
|
if (data.hasOwnProperty("cred_version")) {
|
|
if (last_cred_version == -1) {
|
|
last_cred_version = data.cred_version;
|
|
} else if (last_cred_version != data.cred_version) {
|
|
//reload
|
|
window.location.assign(getInnerHomeUri())
|
|
} else {
|
|
last_cred_version = data.cred_version;
|
|
}
|
|
}
|
|
token_url = URI(getInnerHomeUri() + "{{ save_drive_creds_path }}");
|
|
return_url = getOutterHomeUri()
|
|
auth_url = URI(data.authenticate_url).query({
|
|
'redirectbacktoken': encodeURIComponent(token_url.toString()),
|
|
'version': encodeURIComponent("{{ version }}"),
|
|
'return': encodeURIComponent(return_url),
|
|
'bg': themeStyleContainer.dataset.backgroundColor,
|
|
"ac": themeStyleContainer.dataset.accentColor,
|
|
});
|
|
document.getElementById("authenticate-button").href = auth_url.toString();
|
|
|
|
if (!data.firstSync) {
|
|
backups = data.sources.HomeAssistant.backups;
|
|
hasBackups = backups > 0;
|
|
maxConfigured = data.sources.HomeAssistant.max;
|
|
toDelete = Math.max(0, hasBackups - data.sources.HomeAssistant.max);
|
|
if (data.sources.HomeAssistant.max == 0) {
|
|
toDelete = 0;
|
|
}
|
|
willBackup = data.next_backup_text != "Disabled";
|
|
willUpload = data.sources.GoogleDrive.max > 0;
|
|
toUpload = Math.min(data.sources.GoogleDrive.max, backups - toDelete);
|
|
text = "";
|
|
if (!hasBackups && !willBackup) {
|
|
text = "You have no backups in Home Assistant and you've configured this add-on not to create any."
|
|
} else if (hasBackups && toDelete > 0) {
|
|
text = "You have <b>" + backups + " backup(s)</b> in Home Assistant already. Once you authenticate with Google Drive the <b>" + toDelete + " oldest backup(s) will be deleted</b>";
|
|
if (toUpload > 0) {
|
|
text += " and the <b>" + toUpload + " newest backup(s) will get uploaded</b>."
|
|
} else {
|
|
text += "."
|
|
}
|
|
} else if (hasBackups && toUpload > 0) {
|
|
text = "You have <b>" + backups + " backup(s)</b> in Home Assistant already. Once you authenticate with Google Drive the <b>" + toUpload + " newest backup(s) will get uploaded</b>."
|
|
} else if (!hasBackups) {
|
|
text = "You have <b>no backups in Home Assistant</b>";
|
|
if (willUpload) {
|
|
text += ", authenticate with Google Drive to start automatically creating and backing them up."
|
|
} else {
|
|
text += ", authenticate with Google Drive to start automatically creating them."
|
|
}
|
|
} else if (willBackup) {
|
|
text = "You have <b>" + backups + " backup(s)</b> in Home Assistant already, authenticate with Google Drive to start creating new ones on a schedule."
|
|
} else {
|
|
text = "You have <b>" + backups + " backup(s)</b> in Home Assistant already but you haven't configured this add-on to do anything with them or create new ones."
|
|
}
|
|
$("#what_do_next_now_please_text").html(text);
|
|
$("#what_do_next_now_please").show();
|
|
}
|
|
})
|
|
}
|
|
|
|
$(document).ready(function () {
|
|
// Needed to make modal dialogs work.
|
|
$('.modal').modal();
|
|
$("#client_id").change(updateHref);
|
|
$("#client_secret").change(updateHref);
|
|
$("#client_secret").keyup(updateHref);
|
|
$("#client_id").keyup(updateHref);
|
|
$("#client_secret").mouseup(updateHref);
|
|
$("#client_id").mouseup(updateHref);
|
|
$("#google_credentials_box").change(updateCredsButton)
|
|
$("#google_credentials_box").keyup(updateCredsButton);
|
|
|
|
window.setInterval(reloadForNewCreds, 5000);
|
|
reloadForNewCreds();
|
|
|
|
const params = new URLSearchParams(window.location.search);
|
|
if (params.get("private")) {
|
|
doOption2();
|
|
}
|
|
});
|
|
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
async function getAuthUrl() {
|
|
var client_id = $("#client_id").val();
|
|
var client_secret = $("#client_secret").val();
|
|
if (!(client_id && client_id.length > 0 && client_secret && client_secret.length > 0)) {
|
|
M.toast({ html: "Please enter a Client ID and Client Secret first" })
|
|
}
|
|
let toast = M.toast({ html: "Talking with Google..."})
|
|
url = URI("manualauth").query({
|
|
'client_id': client_id,
|
|
'client_secret': client_secret});
|
|
try {
|
|
const resp = await fetch(url.toString());
|
|
const data = await resp.json();
|
|
if (resp.ok) {
|
|
$("#custom-auth-link").attr("href", data.auth_url);
|
|
$("#custom-auth-link").html(data.auth_url);
|
|
$("#code-box").html(data.code);
|
|
$("#code-hidden").val(data.code);
|
|
$("#option2-step2").show();
|
|
$("#option2-step1").hide();
|
|
} else {
|
|
toast.dismiss();
|
|
M.toast({ html: data.message })
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
toast.dismiss();
|
|
M.toast({ html: "Encountered and unexpected error. Please make sure the add-on is still running."})
|
|
return;
|
|
}
|
|
toast.dismiss();
|
|
|
|
// Continually check for status
|
|
let status = $("#device-code-status-text");
|
|
let progress = $("#device-code-loading");
|
|
progress.show();
|
|
while(true) {
|
|
try {
|
|
const resp = await fetch(URI("checkManualAuth").toString());
|
|
const data = await resp.json();
|
|
status.html(data.message);
|
|
if(resp.ok) {
|
|
$("#device-code-valid-time-left").html(data.expires)
|
|
await sleep(1000);
|
|
} else {
|
|
progress.hide();
|
|
break;
|
|
}
|
|
} catch (e) {
|
|
progress.hode();
|
|
status.html("Unexpected error: " + e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
</script>
|
|
{% endblock %}
|