feat: separate password reset page into two, one for sending the email and one to change the password
Some checks failed
ci/woodpecker/push/build-and-publish Pipeline was successful
ci/woodpecker/pr/build-and-publish Pipeline failed

This commit is contained in:
SauceyRed 2025-07-16 11:36:12 +02:00
parent 17791fc017
commit db2a99736a
Signed by: sauceyred
GPG key ID: 270B096EF6E9A462
4 changed files with 108 additions and 74 deletions

View file

@ -18,7 +18,7 @@
</div>
<div v-else id="auth-form-container">
<slot />
<div v-if="useRoute().path != '/reset-password'">Forgot password? Recover <NuxtLink :href="resetPasswordUrl">here</NuxtLink>!</div>
<div v-if="!['/recover', '/reset-password'].includes(route.path)">Forgot password? Recover <NuxtLink href="/recover">here</NuxtLink>!</div>
</div>
<div v-if="instanceUrl">
Instance URL is set to <span style="color: var(--primary-color);">{{ instanceUrl }}</span>
@ -37,14 +37,12 @@ const apiVersion = useRuntimeConfig().public.apiVersion;
const apiBase = useCookie("api_base");
const registrationEnabled = useState("registrationEnabled", () => true);
const query = useRoute().query as Record<string, string>;
const route = useRoute();
const query = route.query as Record<string, string>;
const searchParams = new URLSearchParams(query);
searchParams.delete("token");
const resetPasswordUrl = `/reset-password?${searchParams}`;
const auth = useAuth();
onMounted(async () => {
instanceUrl.value = useCookie("instance_url").value;
console.log("set instance url to:", instanceUrl.value);

View file

@ -16,7 +16,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
}
}
if (["/login", "/register", "/reset-password"].includes(to.path) && !Object.keys(to.query).includes("special")) {
if (["/login", "/register", "/recover", "/reset-password"].includes(to.path) && !Object.keys(to.query).includes("special")) {
console.log("path is login or register");
const apiBase = useCookie("api_base");
console.log("apiBase gotten:", apiBase.value);

89
pages/recover.vue Normal file
View file

@ -0,0 +1,89 @@
<template>
<NuxtLayout name="auth">
<div v-if="errorValue">{{ errorValue }}</div>
<form v-if="!emailFormSent" @submit.prevent="sendEmail">
<div>
<label for="identifier">Email or username</label>
<br>
<input type="text" name="identifier" id="identifier" v-model="emailForm.identifier">
</div>
<div>
<Button type="submit" text="Send email" variant="normal" />
</div>
</form>
<div v-else>
If an account with that username/email exists, an email will be sent to it shortly.
</div>
<div v-if="registrationEnabled">
Don't have an account? <NuxtLink :href="registerUrl">Register</NuxtLink> one!
</div>
<div>
Already have an account? <NuxtLink :href="loginUrl">Log in</NuxtLink>!
</div>
</NuxtLayout>
</template>
<script lang="ts" setup>
import Button from '~/components/UserInterface/Button.vue';
const emailForm = reactive({
identifier: ""
});
const emailFormSent = ref(false);
const passwordForm = reactive({
password: ""
});
const errorValue = ref<string>();
const registrationEnabled = ref<boolean>(true);
const apiBase = useCookie("api_base");
const query = useRoute().query as Record<string, string>;
const searchParams = new URLSearchParams(query);
const token = ref(searchParams.get("token"))
searchParams.delete("token");
const { resetPassword } = useApi();
const registerUrl = `/register?${searchParams}`;
const loginUrl = `/login?${searchParams}`;
if (apiBase.value) {
console.log("apiBase:", apiBase.value);
const stats = await useApi().fetchInstanceStats(apiBase.value);
if (stats) {
registrationEnabled.value = stats.registration_enabled;
}
}
const { sendPasswordResetEmail } = useApi();
async function sendEmail() {
try {
await sendPasswordResetEmail(emailForm.identifier);
emailFormSent.value = true;
} catch (error) {
errorValue.value = (error as any).toString();
}
}
async function sendPassword() {
try {
console.log("pass:", passwordForm.password);
const hashedPass = await hashPassword(passwordForm.password)
console.log("hashed pass:", hashedPass);
await resetPassword(hashedPass, token.value!);
return await navigateTo(`/login?${searchParams}`);
} catch (error) {
errorValue.value = (error as any).toString();
}
}
</script>
<style>
</style>

View file

@ -1,22 +1,6 @@
<template>
<NuxtLayout name="auth">
<div v-if="errorValue">{{ errorValue }}</div>
<div v-if="!token">
<form v-if="!emailFormSent" @submit.prevent="sendEmail">
<div>
<label for="identifier">Email or username</label>
<br>
<input type="text" name="identifier" id="identifier" v-model="emailForm.identifier">
</div>
<div>
<Button type="submit" text="Send email" variant="normal" />
</div>
</form>
<div v-else>
If an account with that username/email exists, an email will be sent to it shortly.
</div>
</div>
<div v-else>
<form @submit.prevent="sendPassword">
<div>
<label for="password">Password</label>
@ -27,24 +11,17 @@
<Button type="submit" text="Submit" variant="normal" />
</div>
</form>
</div>
<div v-if="registrationEnabled">
Don't have an account? <NuxtLink :href="registerUrl">Register</NuxtLink> one!
</div>
<div>
Already have an account? <NuxtLink :href="loginUrl">Log in</NuxtLink>!
</div>
</NuxtLayout>
</template>
<script lang="ts" setup>
import Button from '~/components/UserInterface/Button.vue';
const emailForm = reactive({
identifier: ""
});
const query = useRoute().query as Record<string, string>;
const searchParams = new URLSearchParams(query);
const token = ref(searchParams.get("token"))
const emailFormSent = ref(false);
if (!token.value) await navigateTo("/login");
const passwordForm = reactive({
password: ""
@ -52,45 +29,15 @@ const passwordForm = reactive({
const errorValue = ref<string>();
const registrationEnabled = ref<boolean>(true);
const apiBase = useCookie("api_base");
const query = useRoute().query as Record<string, string>;
const searchParams = new URLSearchParams(query);
const token = ref(searchParams.get("token"))
searchParams.delete("token");
const { resetPassword } = useApi();
const registerUrl = `/register?${searchParams}`;
const loginUrl = `/login?${searchParams}`;
if (apiBase.value) {
console.log("apiBase:", apiBase.value);
const stats = await useApi().fetchInstanceStats(apiBase.value);
if (stats) {
registrationEnabled.value = stats.registration_enabled;
}
}
const { sendPasswordResetEmail } = useApi();
async function sendEmail() {
try {
await sendPasswordResetEmail(emailForm.identifier);
emailFormSent.value = true;
} catch (error) {
errorValue.value = (error as any).toString();
}
}
async function sendPassword() {
try {
console.log("pass:", passwordForm.password);
const hashedPass = await hashPassword(passwordForm.password)
console.log("hashed pass:", hashedPass);
await resetPassword(hashedPass, token.value!);
return await navigateTo(`/login?${searchParams}`);
return await navigateTo("/login?");
} catch (error) {
errorValue.value = (error as any).toString();
}