frontend: initial commit

This commit is contained in:
$(pass /github/name)
2024-09-07 13:36:15 +02:00
parent 4d6938de96
commit 147b8c0afd
22 changed files with 1804 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
/** @type {import('./$types').LayoutLoad} */
export async function load({ fetch, url, data }) {
const { user } = data;
return { fetch, url: url.pathname, user };
}

View File

@@ -0,0 +1,6 @@
/** @type {import('./$types').LayoutServerLoad} */
export async function load({ locals }) {
return {
user: locals.user,
};
}

View File

@@ -0,0 +1,17 @@
<script>
import Footer from "$lib/components/Footer.svelte";
import Header from "$lib/components/Header.svelte";
import Transition from "$lib/components/Transition.svelte";
import "$lib/css/styles.min.css";
/** @type {import('./$types').PageData} */
export let data;
</script>
<Transition key={data.url} duration={600}>
<Header />
<slot />
<Footer />
</Transition>

View File

@@ -0,0 +1,19 @@
<!-- <script>
import Developer from "$lib/img/hero-image.png";
</script> -->
<div class="hero-container">
<!-- <div class="hero-logo"><img src={Developer} alt="Alexander Stölting" /></div> -->
<h3 class="hero-subtitle subtitle">
This application is the demonstration of a series of tutorials on
session-based authentication using Go at the backend and JavaScript
(SvelteKit) on the front-end.
</h3>
<div class="hero-buttons-container">
<a
class="button-dark"
href="https://dev.to/sirneij/series/23239"
data-learn-more>Learn more</a
>
</div>
</div>

View File

@@ -0,0 +1,98 @@
import { BASE_API_URI } from "$lib/utils/constants";
import { formatError } from "$lib/utils/helpers";
import { fail, redirect } from "@sveltejs/kit";
/** @type {import('./$types').PageServerLoad} */
export async function load({ locals }) {
// redirect user if logged in
if (locals.user) {
throw redirect(302, "/");
}
}
/** @type {import('./$types').Actions} */
export const actions = {
/**
*
* @param request - The request object
* @param fetch - Fetch object from sveltekit
* @param cookies - SvelteKit's cookie object
* @returns Error data or redirects user to the home page or the previous page
*/
login: async ({ request, fetch, cookies }) => {
const data = await request.formData();
const email = String(data.get("email"));
const password = String(data.get("password"));
const next = String(data.get("next"));
/** @type {RequestInit} */
const requestInitOptions = {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: email,
password: password,
}),
};
const res = await fetch(`${BASE_API_URI}/users/login/`, requestInitOptions);
console.log("Login response status:", res.status);
console.log("Login response headers:", Object.fromEntries(res.headers));
if (!res.ok) {
let errorMessage = `HTTP error! status: ${res.status}`;
try {
const errorData = await res.json();
errorMessage = errorData.error || errorMessage;
} catch (parseError) {
console.error("Failed to parse error response:", parseError);
errorMessage = await res.text(); // Get the raw response text if JSON parsing fails
}
console.error("Login failed:", errorMessage);
return fail(res.status, {
errors: [{ error: errorMessage, id: Date.now() }],
});
}
const responseBody = await res.json();
console.log("Login response body:", responseBody);
// Check for the cookie in the response headers
const setCookieHeader = res.headers.get("set-cookie");
console.log("Set-Cookie header:", setCookieHeader);
if (setCookieHeader) {
// Parse the Set-Cookie header to get the JWT
const jwtCookie = setCookieHeader.split(";")[0];
const [cookieName, cookieValue] = jwtCookie.split("=");
if (cookieName.trim() === "jwt") {
console.log("JWT cookie found in response");
cookies.set("jwt", cookieValue.trim(), {
path: "/",
httpOnly: true,
sameSite: "strict",
secure: process.env.NODE_ENV === "production",
});
} else {
console.log("JWT cookie not found in response");
}
} else {
console.log("No Set-Cookie header in response");
}
console.log("Redirecting to:", next || "/");
throw redirect(303, next || "/");
},
// if (!res.ok) {
// const response = await res.json();
// const errors = formatError(response.error);
// return fail(400, { errors: errors });
// }
// throw redirect(303, next || "/");
// },
};

View File

@@ -0,0 +1,76 @@
<script>
import { applyAction, enhance } from "$app/forms";
import { page } from "$app/stores";
import { receive, send } from "$lib/utils/helpers";
/** @type {import('./$types').ActionData} */
export let form;
/** @type {import('./$types').SubmitFunction} */
const handleLogin = async () => {
return async ({ result }) => {
await applyAction(result);
};
};
let message = "";
if ($page.url.searchParams.get("message")) {
message = $page.url.search.split("=")[1].replaceAll("%20", " ");
}
</script>
<div class="container">
<form
class="content"
method="POST"
action="?/login"
use:enhance={handleLogin}
>
<h1 class="step-title">Login User</h1>
{#if form?.errors}
{#each form?.errors as error (error.id)}
<h4
class="step-subtitle warning"
in:receive={{ key: error.id }}
out:send={{ key: error.id }}
>
{error.error}
</h4>
{/each}
{/if}
{#if message}
<h4 class="step-subtitle">{message}</h4>
{/if}
<input
type="hidden"
name="next"
value={$page.url.searchParams.get("next")}
/>
<div class="input-box">
<span class="label">Email:</span>
<input
class="input"
type="email"
name="email"
placeholder="Email address"
/>
</div>
<div class="input-box">
<span class="label">Password:</span>
<input
class="input"
type="password"
name="password"
placeholder="Password"
/>
<a href="/auth/password/request-change" style="margin-left: 1rem;"
>Forgot password?</a
>
</div>
<div class="btn-container">
<button class="button-dark">Login</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,43 @@
import { BASE_API_URI } from "$lib/utils/constants";
import { fail, redirect } from "@sveltejs/kit";
/** @type {import('./$types').PageServerLoad} */
export async function load({ locals }) {
// redirect user if not logged in
if (!locals.user) {
throw redirect(302, `/auth/login?next=/auth/logout`);
}
}
/** @type {import('./$types').Actions} */
export const actions = {
default: async ({ fetch, cookies }) => {
/** @type {RequestInit} */
const requestInitOptions = {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
Cookie: `jwt=${cookies.get("jwt")}`,
},
};
const res = await fetch(
`${BASE_API_URI}/users/backend/logout/`,
requestInitOptions
);
if (!res.ok) {
const response = await res.json();
const errors = [];
errors.push({ error: response.error, id: 0 });
return fail(400, { errors: errors });
}
// eat the cookie
cookies.delete("jwt", { path: "/" });
// redirect the user
throw redirect(302, "/auth/login");
},
};