adding middleware
This commit is contained in:
parent
abad2cec96
commit
65cccb9620
15 changed files with 115 additions and 28 deletions
|
@ -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",
|
||||||
|
|
|
@ -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
BIN
bun.lockb
Binary file not shown.
14
package.json
14
package.json
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
src/components/Header.astro
Normal file
3
src/components/Header.astro
Normal 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>
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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: {
|
||||||
headers: ctx.request.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;
|
||||||
|
|
28
src/pages/api/accounts/login.ts
Normal file
28
src/pages/api/accounts/login.ts
Normal 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 });
|
||||||
|
};
|
|
@ -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>
|
||||||
|
|
0
src/pages/home/settings/account.astro
Normal file
0
src/pages/home/settings/account.astro
Normal file
0
src/pages/home/settings/accounts.astro
Normal file
0
src/pages/home/settings/accounts.astro
Normal file
34
src/pages/home/settings/index.astro
Normal file
34
src/pages/home/settings/index.astro
Normal 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>
|
0
src/pages/home/settings/movies.astro
Normal file
0
src/pages/home/settings/movies.astro
Normal file
4
src/pages/logout.astro
Normal file
4
src/pages/logout.astro
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
Astro.cookies.delete("jwt");
|
||||||
|
return Astro.redirect("/");
|
||||||
|
---
|
Loading…
Reference in a new issue