mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-04 03:27:05 -04:00
191 lines
7.4 KiB
Vue
191 lines
7.4 KiB
Vue
<template>
|
|
<section>
|
|
<Alert type="danger" :message="error"/>
|
|
|
|
<div class="card">
|
|
<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">
|
|
<a :href="!providerOptions.instanceRequired ? `${homeUrl}/api/user/social-redirect/${provider}/${config.locale}` : null"
|
|
:class="providerOptions.instanceRequired ? 'btn border-primary text-primary' : 'btn btn-outline-primary'"
|
|
v-for="(providerOptions, provider) in socialProviders">
|
|
<Icon :v="providerOptions.icon || provider" set="b"/>
|
|
{{ providerOptions.name }}
|
|
<form :action="`${homeUrl}/api/user/social-redirect/${provider}/${config.locale}`"
|
|
v-if="providerOptions.instanceRequired" class="input-group">
|
|
<input type="text" name="instance" class="form-control"
|
|
:placeholder="$t('user.login.instancePlaceholder')">
|
|
<button type="submit" class="btn btn-outline-primary">
|
|
<Icon v="arrow-right"/>
|
|
</button>
|
|
</form>
|
|
</a>
|
|
</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 class="h-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 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>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,
|
|
|
|
homeUrl: process.env.HOME_URL,
|
|
};
|
|
},
|
|
computed: {
|
|
payload() {
|
|
if (!this.token) {
|
|
return null;
|
|
}
|
|
|
|
this.$store.commit('setToken', this.token);
|
|
this.$cookies.set('token', this.$store.state.token, cookieSettings);
|
|
|
|
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();
|
|
}
|
|
})
|
|
},
|
|
},
|
|
}
|
|
</script>
|