Compare commits

..

10 commits

11 changed files with 266 additions and 107 deletions

View file

@ -1,7 +1,16 @@
<template>
<div>
<Loading v-if="loading" />
<NuxtPage />
</div>
</template>
<script lang="ts" setup>
const loading = useState("loading");
</script>
<style>
html,
body {

View file

@ -1,6 +1,6 @@
<template>
<div>
Loading...
<div id="loading-container">
<Icon name="lucide:loader-circle" id="loading-circle" />
</div>
</template>
@ -8,6 +8,31 @@
</script>
<style>
<style scoped>
#loading-container {
position: fixed;
left: 50dvw;
top: 50dvh;
transform: translate(-50%, -50%);
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#loading-circle {
animation-name: spin;
animation-duration: 1s;
animation-timing-function: linear;
animation-iteration-count: infinite;
font-size: 2rem;
}
</style>

View file

@ -30,11 +30,11 @@ export const useAuth = () => {
{
username, password: hashedPass, device_name: "Linux Laptop"
}
}) as { access_token: string, refresh_token: string }; fetch
}) as { access_token: string, refresh_token: string };
console.log("hi");
accessToken.value = res.access_token;
console.log("access token:", accessToken.value);
await fetchUser();
//await fetchUser();
}
async function logout(password: string) {
@ -60,19 +60,17 @@ export const useAuth = () => {
async function refresh() {
console.log("refreshing");
try {
const res = await fetchWithApi("/auth/refresh", {
method: "POST"
}) as { access_token: string };
accessToken.value = res.access_token;
}) as any;
console.log("finished refreshing:", res);
accessToken.value = res?.access_token;
console.log("set new access token");
} catch (error) {
console.error("refresh error:", error);
}
}
async function fetchUser() {
if (!accessToken.value) return;
console.log("fetchuser access token:", accessToken.value);
const res = await fetchWithApi("/users/me") as UserResponse;
user.value = res;
return user.value;

29
error.vue Normal file
View file

@ -0,0 +1,29 @@
<template>
<div id="error-container">
<h2>{{ error?.statusCode }}</h2>
<p>{{ error?.message }}</p>
</div>
</template>
<script lang="ts" setup>
import type { NuxtError } from '#app';
const props = defineProps({
error: Object as () => NuxtError
});
if (props.error?.statusCode == 401) {
console.log("HELLO THERE");
clearError({ redirect: `/login?redirect_to=${useRoute().fullPath}` });
}
</script>
<style scoped>
#error-container {
text-align: center;
margin: auto;
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<div id="root-container" style="margin-top: 5dvh;">
<Loading v-if="!mounted" />
<div v-else id="main-container">
<div v-else id="root-container" style="margin-top: 5dvh;">
<div id="main-container">
<div v-if="!instanceUrl">
<div v-if="instanceError" style="color: red;">
{{ instanceError }}

17
middleware/auth.global.ts Normal file
View file

@ -0,0 +1,17 @@
export default defineNuxtRouteMiddleware(async (to, from) => {
console.log("to.path:", to.path);
const accessToken = useCookie("access_token").value;
if (["/login", "/register"].includes(to.path)) {
if (accessToken) {
return await navigateTo("/");
}
return;
};
if (!accessToken) {
const { refresh } = useAuth();
console.log("hi");
await refresh();
return await navigateTo("/login");
}
})

17
pages/index.vue Normal file
View file

@ -0,0 +1,17 @@
<template>
<NuxtLayout>
</NuxtLayout>
</template>
<script lang="ts" setup>
definePageMeta({
layout: "client"
});
</script>
<style>
</style>

View file

@ -16,7 +16,7 @@
</div>
</form>
<div>
Don't have an account? <NuxtLink href="/register">Register</NuxtLink> one!
Don't have an account? <NuxtLink :href="registerUrl">Register</NuxtLink> one!
</div>
</NuxtLayout>
</template>
@ -34,12 +34,26 @@ const form = reactive({
//const authStore = useAuthStore();
const query = useRoute().query as Record<string, string>;
const searchParams = new URLSearchParams(query);
const registerUrl = `/register?${searchParams}`
const { login } = useAuth();
async function formLogin(e: Event) {
e.preventDefault();
console.log("Sending login data");
try {
await login(form.username, form.password, "Linux Laptop");
console.log("logged in");
if (query.redirect_to) {
console.log("redirecting to:", query.redirect_to);
return await navigateTo(query.redirect_to);
}
return await navigateTo("/");
} catch (error) {
console.error("Error logging in:", error);
}
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
}

View file

@ -33,7 +33,7 @@
</div>
</form>
<div>
Already have an account? <NuxtLink href="/login">Log in</NuxtLink>!
Already have an account? <NuxtLink :href="loginUrl">Log in</NuxtLink>!
</div>
</NuxtLayout>
</template>
@ -74,7 +74,9 @@ const errorMessages = reactive({
//const authStore = useAuthStore();
const auth = useAuth();
const redirectTo = useRoute().query.redirect_to;
const query = useRoute().query as Record<string, string>;
const searchParams = new URLSearchParams(query);
const loginUrl = `/login?${searchParams}`
onMounted(() => {
if (auth.accessToken.value) {
@ -120,7 +122,12 @@ const apiVersion = useRuntimeConfig().public.apiVersion;
async function register(e: Event) {
e.preventDefault();
console.log("Sending registration data");
try {
await auth.register(form.username, form.email, form.password);
return await navigateTo(query.redirect_to);
} catch (error) {
console.error("Error registering:", error);
}
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
}

32
pages/verify-email.vue Normal file
View file

@ -0,0 +1,32 @@
<template>
<div id="container">
<div v-if="errorMessage">
{{ errorMessage }}
</div>
</div>
</template>
<script lang="ts" setup>
const errorMessage = ref();
const token = useRoute().query.token;
try {
const res = await fetchWithApi("/auth/verify-email", { query: { token } });
console.log("hi");
} catch (error) {
console.error("Error verifying email:", error);
errorMessage.value = error;
}
</script>
<style scoped>
#container {
text-align: center;
margin: auto;
}
</style>

View file

@ -9,7 +9,6 @@ export default async <T>(path: string, options: NitroFetchOptions<string> = {})
path = path.slice(0, path.lastIndexOf("/"));
}
console.log("formatted path:", path);
try {
const accessToken = useCookie("access_token");
console.log("access token:", accessToken.value);
const apiBase = useCookie("api_base").value;
@ -49,23 +48,35 @@ export default async <T>(path: string, options: NitroFetchOptions<string> = {})
return res;
} catch (error: any) {
console.error("Error fetching resource");
if (error?.response?.status === 401) {
console.log("Error status is 401");
if (!path.startsWith("/auth/refresh")) {
console.log("Path is not refresh endpoint");
try {
console.log("Trying to refresh");
await refresh();
console.log("Successfully refreshed token");
} catch (error: any) {
console.log("Failed to refresh token");
if (error?.response?.status === 401) {
console.log("Refresh returned 401");
reauthFailed = true;
console.log("Revoking");
await revoke();
console.log("Redirecting to login");
await navigateTo("/login");
console.log("redirected");
return;
}
}
}
}
} else {
console.log("Path is refresh endpoint, throwing error");
throw error;
}
}
} catch (error) {
console.error("error:", error);
console.log("throwing error");
throw error;
}
}
}