mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-08 06:51:43 -04:00
182 lines
6.5 KiB
Vue
182 lines
6.5 KiB
Vue
<template>
|
|
<section>
|
|
<Alert type="danger" :message="error"/>
|
|
|
|
<div class="card shadow">
|
|
<div class="card-body">
|
|
<div v-if="token === null">
|
|
<p v-if="$te('user.login.help')">
|
|
<Icon v="info-circle"/>
|
|
<T>user.login.help</T>
|
|
</p>
|
|
<div class="row flex-row-reverse">
|
|
<div class="col-12 col-md-4">
|
|
<div class="btn-group-vertical w-100 mb-3">
|
|
<SocialLogin v-for="(providerOptions, provider) in socialProviders" :key="provider"
|
|
:provider="provider" :options="providerOptions"/>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-8">
|
|
<form @submit.prevent="login" :disabled="saving" class="mb-4 mb-md-0">
|
|
<input type="text" class="form-control mb-3" v-model="usernameOrEmail"
|
|
:placeholder="$t('user.login.placeholder')" autofocus required/>
|
|
<p class="small text-muted mb-1">
|
|
<Icon v="info-circle"/>
|
|
<T>captcha.reason</T>
|
|
</p>
|
|
<Captcha v-model="captchaToken"/>
|
|
<button class="btn btn-primary mt-3 d-none d-md-block" :disabled="!canInit">
|
|
<Icon v="sign-in"/>
|
|
<T>user.login.action</T>
|
|
</button>
|
|
<button class="btn btn-primary mt-3 d-block-force d-md-none w-100" :disabled="!canInit">
|
|
<Icon v="sign-in"/>
|
|
<T>user.login.action</T>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else-if="payload && !payload.code">
|
|
<div class="alert alert-success">
|
|
<p class="mb-0">
|
|
<Icon v="envelope-open-text"/>
|
|
<T :params="{email: addBrackets(getEmail(payload))}">user.login.emailSent</T>
|
|
</p>
|
|
</div>
|
|
|
|
<form @submit.prevent="validate" :disabled="saving">
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control text-center" v-model="code"
|
|
placeholder="000000" autofocus required minlength="0" maxlength="6"
|
|
inputmode="numeric" pattern="[0-9]{6}" autocomplete="one-time-code"
|
|
ref="code"
|
|
/>
|
|
<button class="btn btn-primary">
|
|
<Icon v="key"/>
|
|
<T>user.code.action</T>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer small">
|
|
<p>
|
|
<Icon v="info-circle"/>
|
|
<T>user.login.why</T>
|
|
</p>
|
|
<p>
|
|
<Icon v="gavel"/>
|
|
<T>terms.consent</T>
|
|
</p>
|
|
<p class="mb-0">
|
|
<Icon v="lock"/>
|
|
<T>user.login.passwordless</T>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script>
|
|
import jwt from 'jsonwebtoken';
|
|
import {socialProviders} from "../src/socialProviders";
|
|
import cookieSettings from "../src/cookieSettings";
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
token: null,
|
|
usernameOrEmail: '',
|
|
code: '',
|
|
|
|
error: '',
|
|
|
|
socialProviders,
|
|
|
|
saving: false,
|
|
|
|
captchaToken: null,
|
|
};
|
|
},
|
|
computed: {
|
|
payload() {
|
|
if (!this.token) {
|
|
return null;
|
|
}
|
|
|
|
this.$setToken(this.token);
|
|
|
|
return jwt.verify(this.token, process.env.PUBLIC_KEY, {
|
|
algorithm: 'RS256',
|
|
audience: this.$base,
|
|
issuer: this.$base,
|
|
});
|
|
},
|
|
canInit() {
|
|
return this.usernameOrEmail && this.captchaToken;
|
|
},
|
|
},
|
|
methods: {
|
|
async login() {
|
|
if (this.saving) {
|
|
return;
|
|
}
|
|
this.saving = true;
|
|
try {
|
|
await this.post(`/user/init`, {
|
|
usernameOrEmail: this.usernameOrEmail,
|
|
captchaToken: this.captchaToken,
|
|
});
|
|
} finally {
|
|
this.saving = false;
|
|
}
|
|
},
|
|
async validate() {
|
|
if (this.saving) {
|
|
return;
|
|
}
|
|
this.saving = true;
|
|
try {
|
|
await this.post(`/user/validate`, {
|
|
code: this.code
|
|
}, {
|
|
headers: {
|
|
authorization: 'Bearer ' + this.token,
|
|
},
|
|
});
|
|
} finally {
|
|
this.saving = false;
|
|
}
|
|
},
|
|
async post(url, data, options = {}) {
|
|
this.error = '';
|
|
|
|
const response = await this.$post(url, data, options);
|
|
|
|
this.usernameOrEmail = '';
|
|
this.code = '';
|
|
|
|
if (response.error) {
|
|
this.error = response.error;
|
|
return;
|
|
}
|
|
|
|
this.token = response.token;
|
|
|
|
this.$nextTick(_ => {
|
|
if (this.$refs.code) {
|
|
this.$refs.code.focus();
|
|
}
|
|
})
|
|
},
|
|
getEmail(payload) {
|
|
return payload.email || payload.emailObfuscated || '';
|
|
},
|
|
addBrackets(str) {
|
|
return str ? `(${str})` : '';
|
|
},
|
|
},
|
|
}
|
|
</script>
|