adding middleware

This commit is contained in:
qpismont 2024-07-01 21:58:06 +02:00
parent abad2cec96
commit 65cccb9620
15 changed files with 115 additions and 28 deletions

View file

@ -3,6 +3,7 @@ import { defineConfig } from "astro/config";
import react from "@astrojs/react"; import react from "@astrojs/react";
import node from "@astrojs/node"; import node from "@astrojs/node";
// https://astro.build/config
export default defineConfig({ export default defineConfig({
integrations: [react()], integrations: [react()],
output: "server", output: "server",

View file

@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"organizeImports": { "organizeImports": {
"enabled": true "enabled": true
}, },

BIN
bun.lockb

Binary file not shown.

View file

@ -4,24 +4,24 @@
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"dev": "bunx --bun astro dev", "dev": "bunx --bun astro dev",
"start": "bunx --bun astro dev",
"build": "rm -rf dist && bunx --bun astro check && bunx --bun astro build", "build": "rm -rf dist && bunx --bun astro check && bunx --bun astro build",
"preview": "bunx --bun astro preview", "preview": "bunx --bun astro preview",
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/check": "^0.7.0", "@astrojs/check": "^0.7.0",
"@astrojs/node": "^8.2.5", "@astrojs/node": "^8.3.2",
"@astrojs/react": "^3.4.0", "@astrojs/react": "^3.6.0",
"@types/react": "^18.3.2", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"astro": "^4.9.1", "astro": "^4.11.3",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"typescript": "^5.3.3" "react-icons": "^5.2.1",
"typescript": "^5.5.2"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.7.3" "@biomejs/biome": "1.8.3"
} }
} }

View file

@ -0,0 +1,3 @@
<div class="col-auto"><a href="/home">Movies</a></div>
<div class="col-auto"><a href="/home/settings">Settings</a></div>
<div class="col-auto"><a href="/logout">Logout</a></div>

View file

@ -2,7 +2,9 @@ import LoginForm, { type LoginFormValue } from "./LoginForm";
export default function LoginFormData() { export default function LoginFormData() {
function handleOnFormSubmit(data: LoginFormValue) { function handleOnFormSubmit(data: LoginFormValue) {
fetch(`${import.meta.env.PUBLIC_API_URL}/accounts/login`, { const searchParams = new URLSearchParams(window.location.search);
fetch("/api/accounts/login", {
method: "POST", method: "POST",
body: JSON.stringify(data), body: JSON.stringify(data),
credentials: "include", credentials: "include",
@ -13,7 +15,7 @@ export default function LoginFormData() {
}) })
.then((res) => res.json()) .then((res) => res.json())
.then((json) => { .then((json) => {
window.location.href = "/home"; window.location.href = searchParams.get("redirect") || "/home";
}) })
.catch((err) => console.log(err)); .catch((err) => console.log(err));
} }

View file

@ -1,4 +1,5 @@
--- ---
import Header from "../components/Header.astro";
import RootLayout from "./RootLayout.astro"; import RootLayout from "./RootLayout.astro";
interface Props { interface Props {
@ -8,13 +9,11 @@ interface Props {
const { title } = Astro.props; const { title } = Astro.props;
--- ---
<RootLayout title={title}> <RootLayout title={`Trepa - ${title}`}>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-auto">Films</div> <Header />
<div class="col-auto">Compte</div>
<div class="col-auto">Deconnexion</div>
</div> </div>
<hr />
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<slot /> <slot />
@ -22,4 +21,17 @@ const { title } = Astro.props;
</div> </div>
</RootLayout> </RootLayout>
<script></script> <script>
const logoutElt = document.getElementById("logout-btn");
logoutElt?.addEventListener("click", () => {
fetch(`${import.meta.env.PUBLIC_API_URL}/accounts/logout`, {
method: "POST",
credentials: "include",
})
.then(() => {
window.location.href = "/";
})
.catch((err) => console.error(err));
});
</script>

View file

@ -2,22 +2,25 @@ import { defineMiddleware, sequence } from "astro:middleware";
const protectedRoutes = defineMiddleware(async (ctx, next) => { const protectedRoutes = defineMiddleware(async (ctx, next) => {
const pathname = ctx.url.pathname; const pathname = ctx.url.pathname;
if (!["/", "/register"].includes(pathname)) { if (
const jwt = ctx.cookies.get("token"); !["/", "/register", "/logout"].includes(pathname) &&
!pathname.startsWith("/api")
) {
const jwt = ctx.cookies.get("jwt");
if (!jwt) { if (!jwt) {
return ctx.redirect("/"); return ctx.redirect(`/?redirect=${pathname}`);
} }
const res = await fetch( const jwtValue = jwt.value;
`${import.meta.env.PUBLIC_API_URL}/accounts/recovery`, const res = await fetch(`${import.meta.env.API_URL}/accounts/recovery`, {
{
credentials: "include", credentials: "include",
headers: ctx.request.headers, headers: {
Authorization: `Bearer ${jwtValue}`,
}, },
); });
if (res.status !== 201) { if (res.status !== 200) {
return ctx.redirect("/"); return ctx.redirect(`/?redirect=${pathname}`);
} }
ctx.locals.account = JSON.parse(await res.text()).account; ctx.locals.account = JSON.parse(await res.text()).account;

View file

@ -0,0 +1,28 @@
import type { APIRoute } from "astro";
export const POST: APIRoute = async ({ request, cookies }) => {
const reqBody = await request.text();
if (!reqBody) throw new Error("body not found");
const res = await fetch(`${import.meta.env.API_URL}/accounts/login`, {
method: "POST",
body: reqBody,
headers: {
"Content-Type": "application/json",
},
});
const resBody = await res.text();
const resBodyJson = JSON.parse(resBody);
if (res.status === 201 && resBodyJson?.jwt) {
cookies.set("jwt", resBodyJson.jwt, {
secure: true,
httpOnly: true,
path: "/",
sameSite: "strict",
});
}
return new Response(resBody, { status: res.status });
};

View file

@ -4,6 +4,6 @@ import HomeLayout from "../../layouts/HomeLayout.astro";
const account = Astro.locals.account; const account = Astro.locals.account;
--- ---
<HomeLayout title="Trepa"> <HomeLayout title="Movies">
Hello {account?.username} Hello {account?.username}
</HomeLayout> </HomeLayout>

View file

View file

View file

@ -0,0 +1,34 @@
---
import {
MdAccountBox,
MdAdminPanelSettings,
MdOutlineLocalMovies,
} from "react-icons/md";
import HomeLayout from "../../../layouts/HomeLayout.astro";
---
<HomeLayout title="Settings">
<div class="row justify-content-center">
<div class="col-4">
<div class="list-group">
<a
href="/home/settings/account"
class="list-group-item list-group-item-action"
>
<MdAccountBox /> My account
</a>
<a
href="/home/settings/accounts"
class="list-group-item list-group-item-action"
><MdAdminPanelSettings /> Accounts</a
>
<a
href="/home/settings/movies"
class="list-group-item list-group-item-action"
><MdOutlineLocalMovies /> Movies</a
>
</div>
</div>
</div>
</HomeLayout>

View file

4
src/pages/logout.astro Normal file
View file

@ -0,0 +1,4 @@
---
Astro.cookies.delete("jwt");
return Astro.redirect("/");
---