Use a shared public view layout component

This commit is contained in:
Johannes Zellner
2025-03-31 11:18:09 +02:00
parent cbba373d7d
commit 15269713cc
11 changed files with 176 additions and 238 deletions
+128
View File
@@ -0,0 +1,128 @@
<script setup>
import { ref, onMounted } from 'vue';
import { Button, PasswordInput, TextInput, fetcher, FormGroup } from 'pankow';
import { API_ORIGIN } from '../constants.js';
import PublicPageLayout from '../components/PublicPageLayout.vue';
const ready = ref(false);
const busy = ref(false);
const passwordError = ref(null);
const totpError = ref(null);
const internalError = ref(null);
const username = ref('');
const password = ref('');
const totpToken = ref('');
const totpTokenRequired = ref(false);
// coming from login.html template
const name = window.cloudron.name;
const note = window.cloudron.note;
const iconUrl = window.cloudron.iconUrl;
const footer = window.cloudron.footer;
const submitUrl = window.cloudron.submitUrl;
async function onSubmit() {
busy.value = true;
passwordError.value = false;
totpError.value = false;
internalError.value = false;
const body = {
username: username.value,
password: password.value,
totpToken: totpToken.value
};
try {
const res = await fetcher.post(submitUrl, body);
if (res.status === 410) {
// the oidc login session is old
window.location.reload();
} else if (res.status === 401) {
if (res.body.message.indexOf('totpToken') !== -1) {
totpError.value = totpTokenRequired.value; // only set on second try coming from login
totpTokenRequired.value = true;
totpToken.value = '';
setTimeout(() => document.getElementById('inputTotpToken').focus(), 0);
} else {
password.value = '';
passwordError.value = true;
document.getElementById('inputPassword').focus();
}
} else if (res.status === 200 ) {
if (res.body.redirectTo) return window.location.href = res.body.redirectTo;
console.error('login success but missing redirectTo in data:', res.body);
internalError.value = true;
} else {
internalError.value = true;
}
} catch (error) {
internalError.value = true;
console.error(error);
}
busy.value = false;
}
onMounted(async () => {
// placed optionally in local storage by setupaccount.js
const autoLoginToken = localStorage.cloudronFirstTimeToken;
if (autoLoginToken) {
try {
const res = await fetch.post(submitUrl, { autoLoginToken });
localStorage.removeItem('cloudronFirstTimeToken');
if (res.body.redirectTo) window.location.href = res.body.redirectTo;
else console.log('login success but missing redirectTo in data:', res.body);
} catch (error) {
console.error('Failed to use autologin token', error);
}
}
ready.value = true;
});
</script>
<template>
<PublicPageLayout :footerHtml="footer">
<div v-if="ready" style="max-width: 300px;">
<small>{{ $t('login.loginTo') }}</small>
<h1>{{ name }}</h1>
<br/>
<div :html="note"></div>
<div class="has-error" v-if="passwordError">{{ $t('login.errorIncorrectCredentials') }}</div>
<div class="has-error" v-if="internalError">{{ $t('login.errorInternal') }}</div>
<form @submit.prevent="onSubmit" v-if="!totpTokenRequired">
<input type="submit" style="display: none;"/>
<FormGroup>
<label for="inputUsername">{{ $t('login.username') }}</label>
<TextInput id="inputUsername" v-model="username" autofocus required/>
</FormGroup>
<FormGroup :has-error="passwordError">
<label for="inputPassword">{{ $t('login.password') }}</label>
<PasswordInput id="inputPassword" v-model="password" required/>
</FormGroup>
<Button id="loginSubmitButton" style="margin-top: 12px" @click.prevent="onSubmit" :loading="busy">{{ $t('login.signInAction') }}</Button>
<a href="/passwordreset.html" style="margin-left: 10px;">{{ $t('login.resetPasswordAction') }}</a>
</form>
<form @submit.prevent="onSubmit" v-if="totpTokenRequired" autocomplete="off">
<input type="submit" style="display: none;"/>
<FormGroup :has-error="totpError">
<label for="inputTotpToken">{{ $t('login.2faToken') }}</label>
<TextInput id="inputTotpToken" v-model="totpToken" required/>
</FormGroup>
<Button id="totpTokenSubmitButton" style="margin-top: 12px" type="submit" @click.prevent="onSubmit" :loading="busy">{{ $t('login.signInAction') }}</Button>
</form>
</div>
</PublicPageLayout>
</template>