✨ Page verrouilée (#58)
This commit is contained in:
commit
3b7dec6a4c
@ -9,6 +9,7 @@ import Image from "next/image";
|
|||||||
|
|
||||||
export type IProps = IBaseFieldProps & {
|
export type IProps = IBaseFieldProps & {
|
||||||
canCopy?: boolean;
|
canCopy?: boolean;
|
||||||
|
password?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class TextField extends BaseField<IProps> {
|
export default class TextField extends BaseField<IProps> {
|
||||||
@ -32,6 +33,7 @@ export default class TextField extends BaseField<IProps> {
|
|||||||
onBlur={this.onBlur}
|
onBlur={this.onBlur}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
|
type={this.props.password ? "password" : "text"}
|
||||||
/>
|
/>
|
||||||
<div className={classes["fake-placeholder"]}>
|
<div className={classes["fake-placeholder"]}>
|
||||||
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
|
{this.props.placeholder} {!this.props.required && " (Facultatif)"}
|
||||||
|
29
src/front/Components/Layouts/Protect/classes.module.scss
Normal file
29
src/front/Components/Layouts/Protect/classes.module.scss
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
@import "@Themes/constants.scss";
|
||||||
|
|
||||||
|
.root {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 530px;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 32px 0;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
@media (max-width: $screen-s) {
|
||||||
|
font-family: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.forget-password {
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
57
src/front/Components/Layouts/Protect/index.tsx
Normal file
57
src/front/Components/Layouts/Protect/index.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import Module from "@Front/Config/Module";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import classes from "./classes.module.scss";
|
||||||
|
import LandingImage from "./landing-connect.jpeg";
|
||||||
|
import DefaultDoubleSidePage from "@Front/Components/LayoutTemplates/DefaultDoubleSidePage";
|
||||||
|
import Typography, { ITypo } from "@Front/Components/DesignSystem/Typography";
|
||||||
|
import CoffreIcon from "@Assets/Icons/coffre.svg";
|
||||||
|
import TextField from "@Front/Components/DesignSystem/Form/TextField";
|
||||||
|
import Button from "@Front/Components/DesignSystem/Button";
|
||||||
|
|
||||||
|
export default function Protect() {
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const router = useRouter();
|
||||||
|
const setPasswordFromInput = (event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
|
||||||
|
setPassword(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitAuth = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (password === "team-fullstack") {
|
||||||
|
console.log("ok");
|
||||||
|
setCookie("protect_staging", Date.now().toString());
|
||||||
|
router.push(Module.getInstance().get().modules.pages.Login.props.path);
|
||||||
|
} else {
|
||||||
|
console.log("pas ok");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setCookie = (name: string, value: string) => {
|
||||||
|
if (!name || !value) throw new Error("Cookie name or value is empty");
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
// Set it expire in 7 days
|
||||||
|
date.setTime(date.getTime() + 7 * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
// Set it
|
||||||
|
document.cookie = name + "=" + value + "; expires=" + date.toUTCString() + "; path=/";
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DefaultDoubleSidePage title={"Login"} image={LandingImage}>
|
||||||
|
<div className={classes["root"]}>
|
||||||
|
<Image alt="coffre" src={CoffreIcon} />
|
||||||
|
<Typography typo={ITypo.H1}>
|
||||||
|
<div className={classes["title"]}>Le site est verrouillé</div>
|
||||||
|
</Typography>
|
||||||
|
<form onSubmit={submitAuth} className={classes["form"]}>
|
||||||
|
<TextField placeholder="Password" onChange={setPasswordFromInput} password />
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</DefaultDoubleSidePage>
|
||||||
|
);
|
||||||
|
}
|
BIN
src/front/Components/Layouts/Protect/landing-connect.jpeg
Normal file
BIN
src/front/Components/Layouts/Protect/landing-connect.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 MiB |
@ -4,11 +4,27 @@ import { NextResponse } from "next/server";
|
|||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
|
|
||||||
export async function middleware(request: NextRequest) {
|
export async function middleware(request: NextRequest) {
|
||||||
|
const cookieStaging = request.cookies.get("protect_staging");
|
||||||
|
if (!cookieStaging) return NextResponse.redirect(new URL("/protect", request.url));
|
||||||
|
|
||||||
|
// Get the JWT from the cookies
|
||||||
const cookies = request.cookies.get("leCoffreAccessToken");
|
const cookies = request.cookies.get("leCoffreAccessToken");
|
||||||
if (!cookies) return NextResponse.redirect(new URL("/login", request.url));
|
if (!cookies) return NextResponse.redirect(new URL("/login", request.url));
|
||||||
|
|
||||||
|
// Decode it
|
||||||
const userDecodedToken = jwt_decode(cookies.value) as IUserJwtPayload;
|
const userDecodedToken = jwt_decode(cookies.value) as IUserJwtPayload;
|
||||||
const customerDecodedToken = jwt_decode(cookies.value) as ICustomerJwtPayload;
|
const customerDecodedToken = jwt_decode(cookies.value) as ICustomerJwtPayload;
|
||||||
|
|
||||||
|
// If no JWT provided, redirect to login page
|
||||||
if (!userDecodedToken && !customerDecodedToken) return NextResponse.redirect(new URL("/login", request.url));
|
if (!userDecodedToken && !customerDecodedToken) return NextResponse.redirect(new URL("/login", request.url));
|
||||||
|
|
||||||
|
// If JWT expired, redirect to login page
|
||||||
|
const token = userDecodedToken ?? customerDecodedToken;
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
if (token.exp < now) {
|
||||||
|
return NextResponse.redirect(new URL("/login", request.url));
|
||||||
|
}
|
||||||
|
|
||||||
const requestUrlPath = request.nextUrl.pathname;
|
const requestUrlPath = request.nextUrl.pathname;
|
||||||
if (
|
if (
|
||||||
requestUrlPath.startsWith("/collaborators") ||
|
requestUrlPath.startsWith("/collaborators") ||
|
||||||
@ -21,7 +37,8 @@ export async function middleware(request: NextRequest) {
|
|||||||
if (userDecodedToken.role !== "admin" && userDecodedToken.role !== "super-admin")
|
if (userDecodedToken.role !== "admin" && userDecodedToken.role !== "super-admin")
|
||||||
return NextResponse.redirect(new URL("/404", request.url));
|
return NextResponse.redirect(new URL("/404", request.url));
|
||||||
}
|
}
|
||||||
if ((requestUrlPath.startsWith("/my-account") || requestUrlPath.startsWith("/document-types")) && !userDecodedToken) return NextResponse.redirect(new URL("/404", request.url));
|
if ((requestUrlPath.startsWith("/my-account") || requestUrlPath.startsWith("/document-types")) && !userDecodedToken)
|
||||||
|
return NextResponse.redirect(new URL("/404", request.url));
|
||||||
if (requestUrlPath.startsWith("/client-dashboard") && !customerDecodedToken) return NextResponse.redirect(new URL("/404", request.url));
|
if (requestUrlPath.startsWith("/client-dashboard") && !customerDecodedToken) return NextResponse.redirect(new URL("/404", request.url));
|
||||||
|
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
@ -33,7 +50,7 @@ export const config = {
|
|||||||
"/collaborators/:path*",
|
"/collaborators/:path*",
|
||||||
"/customer/:path*",
|
"/customer/:path*",
|
||||||
"/document-types/:path*",
|
"/document-types/:path*",
|
||||||
"/deed-types/:path*",
|
"/deed-types/:path*",
|
||||||
"/folders/:path*",
|
"/folders/:path*",
|
||||||
"/my-account/:path*",
|
"/my-account/:path*",
|
||||||
"/offices/:path*",
|
"/offices/:path*",
|
||||||
|
5
src/pages/protect.tsx
Normal file
5
src/pages/protect.tsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import Protect from "@Front/Components/Layouts/Protect";
|
||||||
|
|
||||||
|
export default function Route() {
|
||||||
|
return <Protect />;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user