diff --git a/backend/api/auth.go b/backend/api/auth.go index 4383054..69b2b60 100644 --- a/backend/api/auth.go +++ b/backend/api/auth.go @@ -135,8 +135,10 @@ func AuthJWT(next http.Handler) http.Handler { return } + ctx := context.WithValue(r.Context(), "token", token) + // Call the next handler if the JWT is valid - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/backend/api/get_me.go b/backend/api/get_me.go new file mode 100644 index 0000000..6ad406e --- /dev/null +++ b/backend/api/get_me.go @@ -0,0 +1,33 @@ +package api + +import ( + "net/http" + + "fr.latosa-escrima/api/core" + "github.com/golang-jwt/jwt/v5" +) + +func HandleGetMe(w http.ResponseWriter, r *http.Request) { + token, ok := r.Context().Value("token").(*jwt.Token) + if !ok { + core.JSONError{ + Status: core.Error, + Message: "Couldn't retrieve your JWT.", + }.Respond(w, http.StatusInternalServerError) + return + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + core.JSONError{ + Status: core.Error, + Message: "Invalid token claims.", + }.Respond(w, http.StatusInternalServerError) + return + } + + uuid := claims["user_id"].(string) + + r.SetPathValue("user_uuid", uuid) + HandleGetUser(w, r) +} diff --git a/backend/main.go b/backend/main.go index e799caf..aa115ae 100644 --- a/backend/main.go +++ b/backend/main.go @@ -17,6 +17,21 @@ import ( func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "

Hello, World!

") } +func Cors(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Allow all origins (can restrict to specific origins) + w.Header().Set("Access-Control-Allow-Origin", "*") + // Allow certain HTTP methods (you can customize these as needed) + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH") + // Allow certain headers (you can add more as needed) + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + // Handle OPTIONS pre-flight request + if r.Method == http.MethodOptions { + return + } + next.ServeHTTP(w, r) + }) +} func main() { err := godotenv.Load() @@ -52,10 +67,13 @@ func main() { core.HandleRoutes(mux, map[string]core.Handler{ "/": { Handler: handler, - Middlewares: []core.Middleware{api.Methods("post")}}, + Middlewares: []core.Middleware{api.Methods("get")}}, "/users/login": { Handler: api.HandleLogin, Middlewares: []core.Middleware{api.Methods("POST")}}, + "/users/me": { + Handler: api.HandleGetMe, + Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}}, "/users": { Handler: api.HandleGetUsers, Middlewares: []core.Middleware{api.Methods("GET"), api.AuthJWT}}, @@ -80,7 +98,7 @@ func main() { }) fmt.Printf("Serving on port %s\n", port) - err = http.ListenAndServe(fmt.Sprintf(":%s", port), mux) + err = http.ListenAndServe(fmt.Sprintf(":%s", port), Cors(mux)) if err != nil { fmt.Printf("Error starting server: %s\n", err) } diff --git a/frontend/app/(main)/login/page.tsx b/frontend/app/(main)/login/page.tsx index 78f09d5..1ccb800 100644 --- a/frontend/app/(main)/login/page.tsx +++ b/frontend/app/(main)/login/page.tsx @@ -1,8 +1,15 @@ -import { GalleryVerticalEnd } from "lucide-react"; +import Image from "next/image"; import { LoginForm } from "@/components/login-form"; +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; + +export default async function LoginPage() { + const cookiesObj = await cookies(); + const token = cookiesObj.get("auth_token")?.value; + + if (token) redirect("/dashboard"); -export default function LoginPage() { return (
@@ -13,7 +20,9 @@ export default function LoginPage() {
- Image) { return ( - // fetch(url).then((res) => res.json()), - // revalidateOnFocus: false, - // }} - //> - - - {children} - - - // + + + {children} + + ); } diff --git a/frontend/components/app-sidebar.tsx b/frontend/components/app-sidebar.tsx index 5f094b6..715a576 100644 --- a/frontend/components/app-sidebar.tsx +++ b/frontend/components/app-sidebar.tsx @@ -10,6 +10,7 @@ import { GalleryVerticalEnd, Settings2, Calendar, + Loader2, } from "lucide-react"; import { NavMain } from "@/components/nav-main"; @@ -23,6 +24,8 @@ import { SidebarHeader, SidebarRail, } from "@/components/ui/sidebar"; +import useMe from "@/hooks/use-me"; +import { useEffect } from "react"; // This is sample data. const data = { @@ -93,6 +96,8 @@ const data = { }; export function AppSidebar({ ...props }: React.ComponentProps) { + const { user, isLoading, success, error } = useMe(); + return ( @@ -102,7 +107,11 @@ export function AppSidebar({ ...props }: React.ComponentProps) { - + {isLoading ? ( + + ) : ( + + )} diff --git a/frontend/components/layouts/swr-layout.tsx b/frontend/components/layouts/swr-layout.tsx new file mode 100644 index 0000000..44d81bb --- /dev/null +++ b/frontend/components/layouts/swr-layout.tsx @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Hammed Abass. <3 All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +"use client"; + +import { SWRConfig } from "swr"; + +interface SWRLayoutProps { + children: React.ReactNode; +} + +const SWRLayout: React.FC = ({ children }) => ( + + fetch(url, { credentials: "include" }).then((res) => + res.json(), + ), + revalidateOnFocus: false, + }} + > + {children} + +); + +export default SWRLayout; diff --git a/frontend/components/login-form.tsx b/frontend/components/login-form.tsx index 78da3d8..427d62a 100644 --- a/frontend/components/login-form.tsx +++ b/frontend/components/login-form.tsx @@ -1,14 +1,39 @@ +"use client"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { useState } from "react"; +import { useRouter } from "next/navigation"; +import useLogin from "@/hooks/use-login"; +import { Loader2 } from "lucide-react"; export function LoginForm({ className, ...props }: React.ComponentPropsWithoutRef<"form">) { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const { login, loading, isSuccess } = useLogin(); + const router = useRouter(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + try { + const res = await login({ email, password }); + if (res.status === "Success") router.push("/dashboard"); + console.log(res); + } catch (err: any) { + console.log(err.message); + } + }; + return ( -
+

Connectez-vous à votre compte. @@ -23,6 +48,9 @@ export function LoginForm({ setEmail(e.currentTarget.value)} placeholder="m@example.com" required /> @@ -37,9 +65,21 @@ export function LoginForm({ Forgot your password?

- + setPassword(e.currentTarget.value)} + disabled={loading} + id="password" + type="password" + required + />
-
diff --git a/frontend/components/nav-bar.tsx b/frontend/components/nav-bar.tsx index 99a0400..d87b900 100644 --- a/frontend/components/nav-bar.tsx +++ b/frontend/components/nav-bar.tsx @@ -68,7 +68,7 @@ const subMenuItemsTwo = [ const Navbar = () => { return ( -
+