2023-03-13 16:46:18 +01:00
|
|
|
<!DOCTYPE html>
|
2023-03-11 17:22:27 +01:00
|
|
|
<html>
|
|
|
|
|
<head>
|
2023-03-13 16:46:18 +01:00
|
|
|
<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" />
|
|
|
|
|
|
2023-06-19 11:50:53 +02:00
|
|
|
<title>{{ login.loginTo }} <%= name %></title>
|
2023-03-13 16:46:18 +01:00
|
|
|
|
2023-05-11 12:15:10 +02:00
|
|
|
<link id="favicon" type="image/png" rel="icon" href="/api/v1/cloudron/avatar">
|
|
|
|
|
<link rel="apple-touch-icon" href="/api/v1/cloudron/avatar">
|
|
|
|
|
<link rel="icon" href="/api/v1/cloudron/avatar">
|
|
|
|
|
|
2023-03-13 16:46:18 +01:00
|
|
|
<!-- Theme CSS -->
|
|
|
|
|
<link type="text/css" rel="stylesheet" href="/theme.css">
|
|
|
|
|
|
|
|
|
|
<link type="text/css" rel="stylesheet" href="/3rdparty/fontawesome/css/all.css"/>
|
2023-05-12 18:47:48 +02:00
|
|
|
<script type="text/javascript" src="/3rdparty/js/password-reveal.js"></script>
|
|
|
|
|
|
2023-04-27 16:20:32 +02:00
|
|
|
<style>
|
|
|
|
|
|
|
|
|
|
.card {
|
|
|
|
|
padding: 20px;
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
max-width: 620px;
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar {
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media(min-width:620px) {
|
|
|
|
|
.card {
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
margin-top: 100px;
|
|
|
|
|
min-height: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.avatar {
|
|
|
|
|
margin-top: -84px
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-06 18:33:26 +02:00
|
|
|
.hide {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-27 16:20:32 +02:00
|
|
|
</style>
|
|
|
|
|
|
2023-03-11 17:22:27 +01:00
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
|
2023-03-13 16:46:18 +01:00
|
|
|
<div class="layout-root">
|
|
|
|
|
<div class="layout-content">
|
2023-04-27 16:20:32 +02:00
|
|
|
<div class="card">
|
2023-03-13 16:46:18 +01:00
|
|
|
<div class="row">
|
|
|
|
|
<div class="col-md-12" style="text-align: center;">
|
2023-05-12 13:32:43 +02:00
|
|
|
<img width="128" height="128" class="avatar" src="<%= iconUrl %>"/>
|
2023-03-13 16:46:18 +01:00
|
|
|
<br/>
|
2023-06-19 11:50:53 +02:00
|
|
|
<h1><small>{{ login.loginTo }}</small> <%= name %></h1>
|
2023-03-11 17:22:27 +01:00
|
|
|
</div>
|
2023-03-13 16:46:18 +01:00
|
|
|
</div>
|
|
|
|
|
<br/>
|
|
|
|
|
<div class="row">
|
|
|
|
|
<div class="col-md-12">
|
2023-08-17 13:33:31 +02:00
|
|
|
<%- note %>
|
2023-03-13 19:08:41 +01:00
|
|
|
<form id="loginForm">
|
2023-03-13 16:46:18 +01:00
|
|
|
<div class="form-group">
|
2023-06-19 11:50:53 +02:00
|
|
|
<label class="control-label" for="inputUsername">{{ login.username }}</label>
|
2023-03-13 16:46:18 +01:00
|
|
|
<input type="text" class="form-control" id="inputUsername" name="username" autofocus required>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
2023-06-19 11:50:53 +02:00
|
|
|
<label class="control-label" for="inputPassword">{{ login.password }}</label>
|
2023-03-13 16:46:18 +01:00
|
|
|
<input type="password" class="form-control" name="password" id="inputPassword" required password-reveal>
|
2023-07-06 18:33:26 +02:00
|
|
|
<p class="has-error hide" id="passwordError">{{ login.errorIncorrectCredentials }}</p>
|
|
|
|
|
<p class="has-error hide" id="internalError">{{ login.errorInternal }}</p>
|
2023-03-13 16:46:18 +01:00
|
|
|
</div>
|
|
|
|
|
<div class="form-group">
|
2023-06-19 11:50:53 +02:00
|
|
|
<label class="control-label" for="inputTotpToken">{{ login.2faToken }}</label>
|
2023-03-13 16:46:18 +01:00
|
|
|
<input type="text" class="form-control" name="totpToken" id="inputTotpToken" value="">
|
2023-07-06 18:33:26 +02:00
|
|
|
<p class="has-error hide" id="totpError">{{ login.errorIncorrect2FAToken }}</p>
|
2023-03-13 16:46:18 +01:00
|
|
|
</div>
|
2023-06-19 14:08:10 +02:00
|
|
|
<div class="card-form-bottom-bar">
|
2023-06-19 13:55:58 +02:00
|
|
|
<a href="/passwordreset.html">{{ login.resetPasswordAction }}</a>
|
2024-01-03 14:27:01 +01:00
|
|
|
<button class="btn btn-primary btn-outline" type="submit" id="loginSubmitButton"><i id="busyIndicator" class="hide fa fa-circle-notch fa-spin"></i> {{ login.signInAction }}</button>
|
2023-06-19 13:55:58 +02:00
|
|
|
</div>
|
2023-03-13 16:46:18 +01:00
|
|
|
</form>
|
2023-03-11 17:22:27 +01:00
|
|
|
</div>
|
2023-03-13 16:46:18 +01:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2023-08-11 13:36:46 +02:00
|
|
|
|
|
|
|
|
<footer class="text-center">
|
|
|
|
|
<span class="text-muted"><%- footer %></span>
|
|
|
|
|
</footer>
|
2023-03-13 16:46:18 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
2023-03-13 19:08:41 +01:00
|
|
|
document.getElementById('loginForm').addEventListener('submit', function (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
2023-07-06 18:33:26 +02:00
|
|
|
document.getElementById('passwordError').classList.add('hide');
|
|
|
|
|
document.getElementById('totpError').classList.add('hide');
|
|
|
|
|
document.getElementById('internalError').classList.add('hide');
|
2024-01-03 14:27:01 +01:00
|
|
|
document.getElementById('busyIndicator').classList.remove('hide');
|
2023-05-12 14:31:26 +02:00
|
|
|
|
2024-04-03 18:11:21 +02:00
|
|
|
const apiUrl = '<%= submitUrl %>';
|
2023-03-13 19:08:41 +01:00
|
|
|
|
2024-04-03 18:11:21 +02:00
|
|
|
const body = {
|
2023-03-13 19:08:41 +01:00
|
|
|
username: document.getElementById('inputUsername').value,
|
|
|
|
|
password: document.getElementById('inputPassword').value,
|
|
|
|
|
totpToken: document.getElementById('inputTotpToken').value
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-12 14:31:26 +02:00
|
|
|
let res;
|
2023-03-13 19:08:41 +01:00
|
|
|
fetch(apiUrl, {
|
2023-03-14 10:47:01 +01:00
|
|
|
method: 'POST',
|
2023-03-13 19:08:41 +01:00
|
|
|
body: JSON.stringify(body),
|
|
|
|
|
headers: { 'Content-type': 'application/json; charset=UTF-8' }
|
|
|
|
|
}).then(function (response) {
|
2023-05-12 14:31:26 +02:00
|
|
|
res = response;
|
|
|
|
|
return res.json(); // we always return objects
|
2023-03-13 19:08:41 +01:00
|
|
|
}).then(function (data) {
|
2023-05-12 14:31:26 +02:00
|
|
|
if (res.status === 401) {
|
2023-12-29 18:15:33 +01:00
|
|
|
if (data.message.indexOf('totpToken') !== -1) {
|
2023-07-06 18:33:26 +02:00
|
|
|
document.getElementById('inputTotpToken').value = '';
|
|
|
|
|
document.getElementById('inputTotpToken').focus();
|
|
|
|
|
document.getElementById('totpError').classList.remove('hide');
|
2023-05-12 14:31:26 +02:00
|
|
|
} else {
|
2023-12-29 18:15:33 +01:00
|
|
|
document.getElementById('inputPassword').value = '';
|
|
|
|
|
document.getElementById('inputPassword').focus();
|
|
|
|
|
document.getElementById('passwordError').classList.remove('hide');
|
2023-05-12 14:31:26 +02:00
|
|
|
}
|
2024-01-03 14:27:01 +01:00
|
|
|
|
|
|
|
|
document.getElementById('busyIndicator').classList.add('hide');
|
|
|
|
|
return;
|
2023-05-12 14:31:26 +02:00
|
|
|
} else if (res.status !== 200) {
|
2024-01-03 14:27:01 +01:00
|
|
|
document.getElementById('busyIndicator').classList.add('hide');
|
2023-05-12 14:31:26 +02:00
|
|
|
throw new Error('Something went wrong');
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-14 10:47:01 +01:00
|
|
|
if (data.redirectTo) window.location.href = data.redirectTo;
|
|
|
|
|
else console.log('login success but missing redirectTo in data:', data);
|
2023-03-13 19:08:41 +01:00
|
|
|
}).catch(function (error) {
|
2023-07-06 18:33:26 +02:00
|
|
|
document.getElementById('internalError').classList.remove('hide');
|
2024-01-03 14:27:01 +01:00
|
|
|
document.getElementById('busyIndicator').classList.add('hide');
|
2023-05-12 14:31:26 +02:00
|
|
|
console.warn(error, res);
|
2023-03-13 19:08:41 +01:00
|
|
|
});
|
|
|
|
|
});
|
2023-03-13 16:46:18 +01:00
|
|
|
|
2024-04-03 18:11:21 +02:00
|
|
|
const token = location.search.slice(1);
|
|
|
|
|
if (token) {
|
|
|
|
|
console.log('got token do auto login', token);
|
|
|
|
|
|
|
|
|
|
const apiUrl = '<%= submitUrl %>';
|
|
|
|
|
|
|
|
|
|
let res;
|
|
|
|
|
fetch(apiUrl, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify({ token: token }),
|
|
|
|
|
headers: { 'Content-type': 'application/json; charset=UTF-8' }
|
|
|
|
|
}).then(function (response) {
|
|
|
|
|
res = response;
|
|
|
|
|
return res.json(); // we always return objects
|
|
|
|
|
}).then(function (data) {
|
|
|
|
|
if (data.redirectTo) window.location.href = data.redirectTo;
|
|
|
|
|
else console.log('login success but missing redirectTo in data:', data);
|
|
|
|
|
}).catch(function (error) {
|
|
|
|
|
document.getElementById('internalError').classList.remove('hide');
|
|
|
|
|
document.getElementById('busyIndicator').classList.add('hide');
|
|
|
|
|
console.warn(error, res);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-13 16:46:18 +01:00
|
|
|
</script>
|
|
|
|
|
|
2023-03-11 17:22:27 +01:00
|
|
|
</body>
|
|
|
|
|
</html>
|