streaming, markdown, model selecting, and lots more
This commit is contained in:
+65
-53
@@ -1,47 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
import { authClient } from '~~/lib/auth-client';
|
||||
import { deriveKey } from '~/utils/crypto';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'auth',
|
||||
})
|
||||
});
|
||||
|
||||
const { signIn, session, fetchSession, authClient } = useAuth();
|
||||
|
||||
if (session.value !== null) {
|
||||
navigateTo("/");
|
||||
}
|
||||
const to = useRoute().query.to as string | undefined;
|
||||
|
||||
const form = reactive({
|
||||
email: "",
|
||||
password: "",
|
||||
email: '',
|
||||
password: '',
|
||||
});
|
||||
const loading = ref(false);
|
||||
|
||||
let emailInputEl = ref<HTMLInputElement | null>(null);
|
||||
let passwordInputEl = ref<HTMLInputElement | null>(null);
|
||||
const emailInputEl = ref<HTMLInputElement | null>(null);
|
||||
const passwordInputEl = ref<HTMLInputElement | null>(null);
|
||||
|
||||
let tempForm = {
|
||||
email: "",
|
||||
password: "",
|
||||
}
|
||||
const tempForm = {
|
||||
email: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
// prevent text fields from clearing on hydration
|
||||
onBeforeMount(() => {
|
||||
tempForm.email = (document.getElementById("email") as HTMLInputElement)?.value ?? "";
|
||||
tempForm.password = (document.getElementById("password") as HTMLInputElement)?.value ?? "";
|
||||
})
|
||||
tempForm.email = (document.getElementById('email') as HTMLInputElement)?.value ?? '';
|
||||
tempForm.password = (document.getElementById('password') as HTMLInputElement)?.value ?? '';
|
||||
});
|
||||
|
||||
let hydrated = ref(false);
|
||||
const hydrated = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
form.email = tempForm.email;
|
||||
form.password = tempForm.password;
|
||||
hydrated.value = true;
|
||||
|
||||
emailInputEl.value!.addEventListener("input", () => {
|
||||
emailInputEl.value!.setCustomValidity("");
|
||||
emailInputEl.value!.addEventListener('input', () => {
|
||||
emailInputEl.value!.setCustomValidity('');
|
||||
});
|
||||
|
||||
passwordInputEl.value!.addEventListener("input", () => {
|
||||
passwordInputEl.value!.setCustomValidity("");
|
||||
passwordInputEl.value!.addEventListener('input', () => {
|
||||
passwordInputEl.value!.setCustomValidity('');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,40 +56,52 @@ const submit = async () => {
|
||||
|
||||
loading.value = true;
|
||||
|
||||
await signIn.email({
|
||||
const { data, error } = await authClient.signIn.email({
|
||||
email: form.email,
|
||||
password: form.password,
|
||||
}, {
|
||||
onSuccess: async () => {
|
||||
await fetchSession();
|
||||
navigateTo("/")
|
||||
},
|
||||
onError: (ctx) => {
|
||||
const error = ctx.error.code as keyof typeof authClient.$ERROR_CODES;
|
||||
|
||||
// TODO: i18n
|
||||
// ref https://www.better-auth.com/docs/concepts/client#error-codes
|
||||
switch (error) {
|
||||
case "INVALID_PASSWORD":
|
||||
passwordInputEl.value!.setCustomValidity(ctx.error.message);
|
||||
passwordInputEl.value!.reportValidity();
|
||||
break;
|
||||
case "ACCOUNT_NOT_FOUND":
|
||||
case "USER_NOT_FOUND":
|
||||
case "USER_EMAIL_NOT_FOUND":
|
||||
emailInputEl.value!.setCustomValidity(ctx.error.message);
|
||||
emailInputEl.value!.reportValidity();
|
||||
break;
|
||||
default:
|
||||
console.log(ctx.error);
|
||||
alert(`Something went wrong. ${ctx.error.message}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
const errorCode = error.code! as keyof typeof authClient.$ERROR_CODES;
|
||||
|
||||
// TODO: i18n
|
||||
// ref https://www.better-auth.com/docs/concepts/client#error-codes
|
||||
switch (errorCode) {
|
||||
case 'INVALID_PASSWORD':
|
||||
passwordInputEl.value!.setCustomValidity(error.message!);
|
||||
passwordInputEl.value!.reportValidity();
|
||||
break;
|
||||
case 'ACCOUNT_NOT_FOUND':
|
||||
case 'USER_NOT_FOUND':
|
||||
case 'USER_EMAIL_NOT_FOUND':
|
||||
emailInputEl.value!.setCustomValidity(error.message!);
|
||||
emailInputEl.value!.reportValidity();
|
||||
break;
|
||||
default:
|
||||
console.log(error);
|
||||
alert(`Something went wrong. ${error.message}`);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const key = await deriveKey(form.password, data.user.id);
|
||||
localStorage.setItem('encryptionKey', JSON.stringify(key));
|
||||
|
||||
// force a session refetch
|
||||
clearNuxtData();
|
||||
|
||||
// success
|
||||
const triplit = useTriplitClient();
|
||||
if ('startSession' in triplit) {
|
||||
await triplit.startSession(data.token);
|
||||
}
|
||||
|
||||
return navigateTo(to ?? '/');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -103,9 +114,10 @@ const submit = async () => {
|
||||
<input required minlength="8" maxlength="128" ref="passwordInputEl" type="password"
|
||||
autocomplete="current-password" id="password" v-model="form.password" />
|
||||
<button :disabled="!hydrated" class="accent" type="submit">
|
||||
<Icon v-if="loading" width="24" name="svg-spinners:90-ring-with-bg" />
|
||||
<Icon v-if="loading" class="text-6" name="svg-spinners:90-ring-with-bg" />
|
||||
<span v-else>Login</span>
|
||||
</button>
|
||||
</form>
|
||||
<p class="text-center">Dont have an account? <a href="/auth/register">Register</a></p>
|
||||
<p class="text-center">Dont have an account? <a
|
||||
:href="to ? `/auth/register?to=${to}` : '/auth/register'">Register</a></p>
|
||||
</template>
|
||||
+83
-68
@@ -1,47 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { authClient } from '~~/lib/auth-client';
|
||||
import { deriveKey } from '~/utils/crypto';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'auth',
|
||||
})
|
||||
});
|
||||
|
||||
const { signUp, session, fetchSession, authClient } = useAuth();
|
||||
const { session } = await useAuth();
|
||||
const to = useRoute().query.to as string | undefined;
|
||||
|
||||
if (session.value !== null) {
|
||||
navigateTo("/");
|
||||
await navigateTo(to ?? '/');
|
||||
}
|
||||
|
||||
if (import.meta.server) {
|
||||
if (process.env.DISABLE_SIGNUP?.toLowerCase() === "true" || process.env.DISABLE_SIGNUP === "1") {
|
||||
navigateTo("/auth/login")
|
||||
if (process.env.DISABLE_SIGNUP?.toLowerCase() === 'true' || process.env.DISABLE_SIGNUP === '1') {
|
||||
await navigateTo(to ? `/auth/login?to=${to}` : '/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
const form = reactive({
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
});
|
||||
const loading = ref(false);
|
||||
|
||||
let nameInputEl = ref<HTMLInputElement | null>(null);
|
||||
let emailInputEl = ref<HTMLInputElement | null>(null);
|
||||
let passwordInputEl = ref<HTMLInputElement | null>(null);
|
||||
let confirmPasswordInputEl = ref<HTMLInputElement | null>(null);
|
||||
const nameInputEl = ref<HTMLInputElement | null>(null);
|
||||
const emailInputEl = ref<HTMLInputElement | null>(null);
|
||||
const passwordInputEl = ref<HTMLInputElement | null>(null);
|
||||
const confirmPasswordInputEl = ref<HTMLInputElement | null>(null);
|
||||
|
||||
let tempForm = {
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
}
|
||||
const tempForm = {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
};
|
||||
|
||||
// prevent text fields from clearing on hydration
|
||||
onBeforeMount(() => {
|
||||
tempForm.name = (document.getElementById("name") as HTMLInputElement)?.value ?? "";
|
||||
tempForm.email = (document.getElementById("email") as HTMLInputElement)?.value ?? "";
|
||||
tempForm.password = (document.getElementById("password") as HTMLInputElement)?.value ?? "";
|
||||
tempForm.confirmPassword = (document.getElementById("confirmPassword") as HTMLInputElement)?.value ?? "";
|
||||
})
|
||||
tempForm.name = (document.getElementById('name') as HTMLInputElement)?.value ?? '';
|
||||
tempForm.email = (document.getElementById('email') as HTMLInputElement)?.value ?? '';
|
||||
tempForm.password = (document.getElementById('password') as HTMLInputElement)?.value ?? '';
|
||||
tempForm.confirmPassword = (document.getElementById('confirmPassword') as HTMLInputElement)?.value ?? '';
|
||||
});
|
||||
|
||||
const hydrated = ref(false);
|
||||
|
||||
@@ -52,25 +56,24 @@ onMounted(() => {
|
||||
form.confirmPassword = tempForm.confirmPassword;
|
||||
hydrated.value = true;
|
||||
|
||||
nameInputEl.value!.addEventListener("input", () => {
|
||||
nameInputEl.value!.setCustomValidity("");
|
||||
nameInputEl.value!.addEventListener('input', () => {
|
||||
nameInputEl.value!.setCustomValidity('');
|
||||
});
|
||||
|
||||
emailInputEl.value!.addEventListener("input", () => {
|
||||
emailInputEl.value!.setCustomValidity("");
|
||||
emailInputEl.value!.addEventListener('input', () => {
|
||||
emailInputEl.value!.setCustomValidity('');
|
||||
});
|
||||
|
||||
passwordInputEl.value!.addEventListener("input", () => {
|
||||
passwordInputEl.value!.setCustomValidity("");
|
||||
passwordInputEl.value!.addEventListener('input', () => {
|
||||
passwordInputEl.value!.setCustomValidity('');
|
||||
});
|
||||
|
||||
|
||||
confirmPasswordInputEl.value!.addEventListener("input", () => {
|
||||
confirmPasswordInputEl.value!.addEventListener('input', () => {
|
||||
if (passwordInputEl.value!.value !== confirmPasswordInputEl.value!.value) {
|
||||
confirmPasswordInputEl.value!.setCustomValidity("Passwords do not match");
|
||||
confirmPasswordInputEl.value!.setCustomValidity('Passwords do not match');
|
||||
confirmPasswordInputEl.value!.reportValidity();
|
||||
} else {
|
||||
confirmPasswordInputEl.value!.setCustomValidity("");
|
||||
confirmPasswordInputEl.value!.setCustomValidity('');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -86,50 +89,62 @@ const submit = async () => {
|
||||
}
|
||||
|
||||
if (form.password !== form.confirmPassword) {
|
||||
alert("Passwords do not match")
|
||||
alert('Passwords do not match');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
await signUp.email({
|
||||
const { data, error } = await authClient.signUp.email({
|
||||
name: form.name,
|
||||
email: form.email,
|
||||
password: form.password,
|
||||
}, {
|
||||
onSuccess: async () => {
|
||||
await fetchSession();
|
||||
navigateTo("/")
|
||||
},
|
||||
onError: (ctx) => {
|
||||
const error = ctx.error.code as keyof typeof authClient.$ERROR_CODES;
|
||||
|
||||
// TODO: i18n
|
||||
// ref https://www.better-auth.com/docs/concepts/client#error-codes
|
||||
switch (error) {
|
||||
case "USER_ALREADY_EXISTS":
|
||||
case "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL":
|
||||
emailInputEl.value!.setCustomValidity("Account with this email already exists");
|
||||
emailInputEl.value!.reportValidity();
|
||||
break;
|
||||
case "INVALID_EMAIL":
|
||||
emailInputEl.value!.setCustomValidity(ctx.error.message);
|
||||
emailInputEl.value!.reportValidity();
|
||||
break;
|
||||
case "INVALID_PASSWORD":
|
||||
passwordInputEl.value!.setCustomValidity(ctx.error.message);
|
||||
passwordInputEl.value!.reportValidity();
|
||||
break;
|
||||
default:
|
||||
console.log(ctx.error);
|
||||
alert("Something went wrong")
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
const errorCode = error.code! as keyof typeof authClient.$ERROR_CODES;
|
||||
|
||||
// TODO: i18n
|
||||
// ref https://www.better-auth.com/docs/concepts/client#error-codes
|
||||
switch (errorCode) {
|
||||
case 'USER_ALREADY_EXISTS':
|
||||
case 'USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL':
|
||||
emailInputEl.value!.setCustomValidity('Account with this email already exists');
|
||||
emailInputEl.value!.reportValidity();
|
||||
break;
|
||||
case 'INVALID_EMAIL':
|
||||
emailInputEl.value!.setCustomValidity(error.message!);
|
||||
emailInputEl.value!.reportValidity();
|
||||
break;
|
||||
case 'INVALID_PASSWORD':
|
||||
passwordInputEl.value!.setCustomValidity(error.message!);
|
||||
passwordInputEl.value!.reportValidity();
|
||||
break;
|
||||
default:
|
||||
console.log(error);
|
||||
alert('Something went wrong: ' + error.message);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const key = await deriveKey(form.password, data.user.id);
|
||||
localStorage.setItem('encryptionKey', JSON.stringify(key));
|
||||
|
||||
// force a session refetch
|
||||
clearNuxtData();
|
||||
|
||||
// success
|
||||
const triplit = useTriplitClient();
|
||||
if ('startSession' in triplit) {
|
||||
await triplit.startSession(data.token!);
|
||||
}
|
||||
|
||||
return navigateTo(to ?? '/');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -151,5 +166,5 @@ const submit = async () => {
|
||||
<span v-else>Register</span>
|
||||
</button>
|
||||
</form>
|
||||
<p class="text-center">Already have an account? <a href="/auth/login">Login</a></p>
|
||||
<p class="text-center">Already have an account? <a :href="to ? `/auth/login?to=${to}` : '/auth/login'">Login</a></p>
|
||||
</template>
|
||||
Reference in New Issue
Block a user