mirror of https://github.com/lissy93/dashy
348 lines
11 KiB
Vue
348 lines
11 KiB
Vue
<template>
|
|
<div class="login-page">
|
|
<!-- User is already logged in -->
|
|
<div v-if="isUserAlreadyLoggedIn" class="already-logged-in">
|
|
<h2>{{ $t('login.already-logged-in-title') }}</h2>
|
|
<p class="already-logged-in">
|
|
{{ $t('login.already-logged-in-text') }}
|
|
<span class="username">{{ existingUsername }}</span>
|
|
</p>
|
|
<Button class="login-button" :click="stayLoggedIn">
|
|
{{ $t('login.proceed-to-dashboard') }}
|
|
</Button>
|
|
<Button class="login-button" :click="getOut">{{ $t('login.log-out-button') }}</Button>
|
|
<span class="already-logged-in-note">
|
|
You need to log out, in order to proceed as a different user.
|
|
</span>
|
|
<transition name="bounce">
|
|
<p :class="`login-error-message ${status}`" v-show="message">{{ message }}</p>
|
|
</transition>
|
|
</div>
|
|
<!-- Main login form -->
|
|
<form class="login-form" v-if="(!isUserAlreadyLoggedIn) && isAuthenticationEnabled">
|
|
<h2 class="login-title">{{ $t('login.title') }}</h2>
|
|
<Input type="text"
|
|
v-model="username"
|
|
:onEnter="submitLogin"
|
|
:label="$t('login.username-label')"
|
|
class="login-field username"
|
|
/>
|
|
<Input type="password"
|
|
v-model="password"
|
|
:onEnter="submitLogin"
|
|
:label="$t('login.password-label')"
|
|
class="login-field password"
|
|
/>
|
|
<label>{{ $t('login.remember-me-label') }}</label>
|
|
<v-select
|
|
v-model="timeout"
|
|
:selectOnTab="true"
|
|
:options="dropDownMenu"
|
|
:map-keydown="(map) => ({ ...map, 13: () => this.submitLogin() })"
|
|
class="login-time-dropdown"
|
|
/>
|
|
<Button class="login-button" :click="submitLogin">
|
|
{{ $t('login.login-button') }}
|
|
</Button>
|
|
<transition name="bounce">
|
|
<p :class="`login-error-message ${status}`" v-show="message">{{ message }}</p>
|
|
</transition>
|
|
</form>
|
|
<!-- Guest login form -->
|
|
<form class="guest-form"
|
|
v-if="isGuestAccessEnabled && !isUserAlreadyLoggedIn && isAuthenticationEnabled">
|
|
<h2 class="login-title">Guest Access</h2>
|
|
<Button class="login-button" :click="guestLogin">
|
|
{{ $t('login.proceed-guest-button') }}
|
|
</Button>
|
|
<p class="guest-intro">
|
|
This instance has guest access enabled.<br>
|
|
Guests have view-only access to dashboards,
|
|
so cannot write any changes to disk.
|
|
</p>
|
|
</form>
|
|
<!-- Edge case - guest mode enabled, but no users configured -->
|
|
<div class="not-configured" v-if="!isAuthenticationEnabled">
|
|
<h2>Error</h2>
|
|
<p>Authentication is not enabled, or no users have been configured</p>
|
|
<Button class="login-button" :click="guestLogin">
|
|
Go Home
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import router from '@/router';
|
|
import Button from '@/components/FormElements/Button';
|
|
import Input from '@/components/FormElements/Input';
|
|
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
|
import { InfoHandler, WarningInfoHandler, InfoKeys } from '@/utils/ErrorHandler';
|
|
import {
|
|
checkCredentials,
|
|
login,
|
|
isLoggedIn,
|
|
logout,
|
|
isGuestAccessEnabled,
|
|
} from '@/utils/Auth';
|
|
|
|
export default {
|
|
name: 'login',
|
|
components: {
|
|
Button,
|
|
Input,
|
|
},
|
|
data() {
|
|
return {
|
|
username: '',
|
|
password: '',
|
|
message: '',
|
|
status: 'waiting', // wating, error, success
|
|
timeout: undefined,
|
|
};
|
|
},
|
|
computed: {
|
|
appConfig() {
|
|
return this.$store.getters.appConfig;
|
|
},
|
|
/* Data for timeout dropdown menu, translated label + value in ms */
|
|
dropDownMenu() {
|
|
return [
|
|
{ label: this.$t('login.remember-me-never'), time: 0 },
|
|
{ label: this.$t('login.remember-me-hour'), time: 14400 * 1000 },
|
|
{ label: this.$t('login.remember-me-day'), time: 86400 * 1000 },
|
|
{ label: this.$t('login.remember-me-week'), time: 604800 * 1000 },
|
|
{ label: this.$t('login.remember-me-long-time'), time: 604800 * 52 * 1000 },
|
|
];
|
|
},
|
|
/* Translations for login response messages */
|
|
responseMessages() {
|
|
return {
|
|
missingUsername: this.$t('login.error-missing-username'),
|
|
missingPassword: this.$t('login.error-missing-password'),
|
|
incorrectUsername: this.$t('login.error-incorrect-username'),
|
|
incorrectPassword: this.$t('login.error-incorrect-password'),
|
|
successMsg: this.$t('login.success-message'),
|
|
};
|
|
},
|
|
existingUsername() {
|
|
return localStorage[localStorageKeys.USERNAME];
|
|
},
|
|
users() {
|
|
const auth = this.appConfig.auth || {};
|
|
return Array.isArray(auth) ? auth : auth.users || [];
|
|
},
|
|
isUserAlreadyLoggedIn() {
|
|
const loggedIn = (!this.users || this.users.length === 0 || isLoggedIn());
|
|
return (loggedIn && this.existingUsername);
|
|
},
|
|
isGuestAccessEnabled() {
|
|
return isGuestAccessEnabled();
|
|
},
|
|
isAuthenticationEnabled() {
|
|
return (this.appConfig && this.appConfig.auth && this.users.length > 0);
|
|
},
|
|
},
|
|
methods: {
|
|
/* Checks form is filled in, then initiates the login, and redirects to /home */
|
|
submitLogin() {
|
|
// Use selected timeout, if available,else revedrt to zero
|
|
const timeout = this.timeout ? this.timeout.time : 0;
|
|
// Check users credentials
|
|
const response = checkCredentials(
|
|
this.username,
|
|
this.password,
|
|
this.users, // All users
|
|
this.responseMessages, // Translated response messages
|
|
);
|
|
this.message = response.msg; // Show error or success message to the user
|
|
this.status = response.correct ? 'success' : 'error';
|
|
if (response.correct) { // Yay, credentials were correct :)
|
|
login(this.username, this.password, timeout); // Login, to set the cookie
|
|
this.goHome();
|
|
InfoHandler(`Succesfully signed in as ${this.username}`, InfoKeys.AUTH);
|
|
} else {
|
|
WarningInfoHandler('Unable to Sign In', InfoKeys.AUTH, this.message);
|
|
}
|
|
},
|
|
/* Calls function to double-check guest access enabled, then log in as guest */
|
|
guestLogin() {
|
|
const isAllowed = this.isGuestAccessEnabled;
|
|
if (isAllowed) {
|
|
this.$toasted.show('Logged in as Guest, Redirecting...', { className: 'toast-success' });
|
|
InfoHandler('Logged in as Guest', InfoKeys.AUTH);
|
|
this.goHome();
|
|
} else {
|
|
this.$toasted.show('Guest Access Not Allowed', { className: 'toast-error' });
|
|
WarningInfoHandler('Guest Access Not Allowed', InfoKeys.AUTH);
|
|
}
|
|
},
|
|
/* Calls logout, shows status message, and refreshed page */
|
|
getOut() {
|
|
logout();
|
|
this.status = 'success';
|
|
this.message = 'Logging out...';
|
|
this.refreshPage();
|
|
},
|
|
/* Logged in user redirects to home page */
|
|
stayLoggedIn() {
|
|
this.status = 'success';
|
|
this.message = 'Redirecting...';
|
|
this.goHome();
|
|
},
|
|
/* Refreshes the page */
|
|
refreshPage() {
|
|
setTimeout(() => { location.reload(); }, 250); // eslint-disable-line no-restricted-globals
|
|
},
|
|
/* Redirects to the homepage */
|
|
goHome() {
|
|
setTimeout(() => { // Wait a short while, then redirect back home
|
|
router.push({ path: '/' });
|
|
}, 250);
|
|
},
|
|
/* Since Theme setter isn't loaded at this point, we must manually get and apply users theme */
|
|
setTheme() {
|
|
const theme = localStorage[localStorageKeys.THEME] || Defaults.theme;
|
|
document.getElementsByTagName('html')[0].setAttribute('data-theme', theme);
|
|
},
|
|
},
|
|
created() {
|
|
this.setTheme();
|
|
setTimeout(() => { this.timeout = this.dropDownMenu[0]; }, 1); //eslint-disable-line
|
|
},
|
|
};
|
|
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
|
|
/* Login page base styles */
|
|
.login-page {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
justify-content: space-evenly;
|
|
min-height: calc(100vh - var(--footer-height));
|
|
|
|
/* User is already logged in note */
|
|
div.already-logged-in {
|
|
margin: 0 auto 0.5rem;
|
|
p.already-logged-in {
|
|
margin: 0 auto 0.5rem;
|
|
text-align: center;
|
|
}
|
|
span.username {
|
|
font-weight: bold;
|
|
text-transform: capitalize;
|
|
}
|
|
span.already-logged-in-note {
|
|
font-size: 0.8rem;
|
|
opacity: var(--dimming-factor);
|
|
text-align: left;
|
|
}
|
|
}
|
|
|
|
/* Login form container */
|
|
form.login-form, form.guest-form, div.already-logged-in, div.not-configured {
|
|
background: var(--login-form-background);
|
|
color: var(--login-form-color);
|
|
border: 1px solid var(--login-form-color);
|
|
border-radius: var(--curve-factor);
|
|
font-size: 1.4rem;
|
|
padding: 2rem;
|
|
margin: 2rem;
|
|
max-width: 22rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
/* Login form title */
|
|
h2 {
|
|
font-size: 2rem;
|
|
margin: 0 0 1rem 0;
|
|
text-align: center;
|
|
cursor: default;
|
|
}
|
|
|
|
/* Set sizings for input fields and login button */
|
|
.login-field input, Button.login-button {
|
|
width: 20rem;
|
|
margin: 0.5rem auto;
|
|
font-size: 1.4rem;
|
|
padding: 0.5rem 1rem;
|
|
}
|
|
|
|
/* Custom colors for username/ password input fields */
|
|
.login-field input {
|
|
color: var(--login-form-color);
|
|
border-color: var(--login-form-color);
|
|
background: var(--login-form-background);
|
|
}
|
|
/* Custom colors for Login Button */
|
|
Button.login-button {
|
|
background: var(--login-form-color);
|
|
border-color: var(--login-form-background);
|
|
color: var(--login-form-background);
|
|
&:hover {
|
|
color: var(--login-form-color);
|
|
border-color: var(--login-form-color);
|
|
background: var(--login-form-background);
|
|
}
|
|
&:active, &:focus {
|
|
box-shadow: 1px 1px 6px var(--login-form-color);
|
|
}
|
|
}
|
|
/* Apply color to status message, depending on status */
|
|
p.login-error-message {
|
|
font-size: 1rem;
|
|
text-align: center;
|
|
&.waiting { color: var(--login-form-color); }
|
|
&.success { color: var(--success); }
|
|
&.error { color: var(--warning); }
|
|
}
|
|
p.guest-intro {
|
|
font-size: 0.8rem;
|
|
opacity: var(--dimming-factor);
|
|
text-align: left;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Enter animations for error/ success message */
|
|
.bounce-enter-active { animation: bounce-in 0.25s; }
|
|
.bounce-leave-active { animation: bounce-in 0.25s reverse; }
|
|
@keyframes bounce-in {
|
|
0% { transform: scale(0); }
|
|
50% { transform: scale(1.25); }
|
|
100% { transform: scale(1); }
|
|
}
|
|
|
|
/* Custom styles for dropdown component */
|
|
.v-select.login-time-dropdown {
|
|
margin: 0.5rem 0;
|
|
.vs__dropdown-toggle {
|
|
border-color: var(--login-form-color);
|
|
background: var(--login-form-background);
|
|
cursor: pointer;
|
|
span.vs__selected {
|
|
color: var(--login-form-color);
|
|
}
|
|
.vs__actions svg path { fill: var(--login-form-color); }
|
|
}
|
|
ul.vs__dropdown-menu {
|
|
background: var(--login-form-background);
|
|
border-color: var(--login-form-color);
|
|
li {
|
|
color: var(--login-form-color);
|
|
&:hover {
|
|
color: var(--login-form-background);
|
|
background: var(--login-form-color);
|
|
}
|
|
&.vs__dropdown-option--highlight {
|
|
color: var(--login-form-background) !important;
|
|
background: var(--login-form-color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|