diff --git a/frontend/app/chat/[id]/page.tsx b/frontend/app/chat/[id]/page.tsx index 8b59bb55..7eb01776 100644 --- a/frontend/app/chat/[id]/page.tsx +++ b/frontend/app/chat/[id]/page.tsx @@ -1,28 +1,20 @@ import { Separator } from "@/components/ui/separator"; -import { testData } from "../test-data"; import { Sidebar } from "../sidebar"; import MessageArea from "../message-area"; -import { getUserId } from "@/app/lib/session"; +import { getCurrentUserId } from "@/app/lib/session"; import { getUser, getUsers } from "@/app/lib/actions"; -//async function getUsers() { -// return testData.users; -//} - export default async function ChatPage({ params: { id }, }: { params: { id: string }; }) { - const [tmpUsers, currentUserId] = await Promise.all([ + const [allUsers, currentUserId] = await Promise.all([ getUsers(), - getUserId(), + getCurrentUserId(), ]); - if (!currentUserId) { - throw new Error("getUserId error"); - } - const currentUser = await getUser(parseInt(currentUserId)); - const users = tmpUsers.filter((user) => user.id !== parseInt(currentUserId)); + const currentUser = await getUser(currentUserId); + const users = allUsers.filter((user) => user.id !== currentUserId); if (users.length === 0) { console.error("No other users exist"); throw new Error("No other users exist"); diff --git a/frontend/app/chat/page.tsx b/frontend/app/chat/page.tsx index 138f9b0e..ac82e573 100644 --- a/frontend/app/chat/page.tsx +++ b/frontend/app/chat/page.tsx @@ -1,23 +1,14 @@ import { Separator } from "@/components/ui/separator"; -//import { testData } from "./test-data"; import { Sidebar } from "./sidebar"; -import MessageArea from "./message-area"; -import { getUserId } from "@/app/lib/session"; import { getUsers } from "@/app/lib/actions"; - -//async function getUsers() { -// return testData.users; -//} +import { getCurrentUserId } from "@/app/lib/session"; export default async function ChatPage() { - const [tmpUsers, currentUserId] = await Promise.all([ + const [allUsers, currentUserId] = await Promise.all([ getUsers(), - getUserId(), + getCurrentUserId(), ]); - if (!currentUserId) { - throw new Error("getUserId error"); - } - const users = tmpUsers.filter((user) => user.id !== parseInt(currentUserId)); + const users = allUsers.filter((user) => user.id !== currentUserId); return ( <>
diff --git a/frontend/app/client-auth-provider.tsx b/frontend/app/client-auth-provider.tsx new file mode 100644 index 00000000..ed491fba --- /dev/null +++ b/frontend/app/client-auth-provider.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { AuthContext } from "@/app/lib/client-auth"; + +type User = { + id: number; + name: string; + email: string; + avatarURL?: string; + createdAt: string; +}; + +export type AuthProviderProps = { + user?: User; + children: React.ReactNode; + isLoggedIn: boolean; +}; + +export default function AuthProvider({ + children, + user, + isLoggedIn, +}: AuthProviderProps) { + const auth = { currentUser: user, isLoggedIn }; + return {children}; +} diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index dc8ff107..9c1a1db5 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -2,6 +2,9 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import AuthProvider from "./client-auth-provider"; +import { getCurrentUser, isLoggedIn } from "@/app/lib/session"; + // components import { ThemeProvider } from "@/components/theme-provider"; import { Toaster } from "@/components/ui/toaster"; @@ -16,11 +19,14 @@ export const metadata: Metadata = { description: "Classic Pong game", }; -export default function RootLayout({ +export default async function RootLayout({ children, }: { children: React.ReactNode; }) { + const isAuthorized = await isLoggedIn(); // Not allow expired tokens + const user = await getCurrentUser(); // Allows expired tokens + return ( @@ -30,11 +36,13 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > -
-
- + +
+
+ +
diff --git a/frontend/app/lib/actions.ts b/frontend/app/lib/actions.ts index 70e530f0..920ed825 100644 --- a/frontend/app/lib/actions.ts +++ b/frontend/app/lib/actions.ts @@ -249,3 +249,10 @@ export async function deleteRoomUser(roomId: number, userId: number) { return "Success"; } } + +export async function signInAsTestUser() { + const email = "test@example.com"; + const password = "password-test"; + await signIn({ email, password }); + redirect("/"); +} diff --git a/frontend/app/lib/client-auth.tsx b/frontend/app/lib/client-auth.tsx new file mode 100644 index 00000000..309394de --- /dev/null +++ b/frontend/app/lib/client-auth.tsx @@ -0,0 +1,21 @@ +"use client"; +import { createContext, useContext } from "react"; + +type User = { + id: number; + name: string; + email: string; + avatarURL?: string; + createdAt: string; +}; + +type AuthContextType = { + currentUser?: User; + isLoggedIn: boolean; +}; + +export const AuthContext = createContext({ + isLoggedIn: false, +}); + +export const useAuthContext = () => useContext(AuthContext); diff --git a/frontend/app/lib/session.ts b/frontend/app/lib/session.ts index 6f5c709c..ff0fed5e 100644 --- a/frontend/app/lib/session.ts +++ b/frontend/app/lib/session.ts @@ -1,5 +1,6 @@ import { cookies } from "next/headers"; import * as jose from "jose"; +import { getUser } from "./actions"; // TODO: add types export type Session = {}; @@ -22,10 +23,30 @@ export async function isLoggedIn() { return true; } -export async function getUserId(): Promise { +export async function getCurrentUser(): Promise { + try { + const userId = await getCurrentUserId(); + const user = getUser(userId); + return user; + } catch (e) { + console.log("getCurrentUser: ", e); + return null; + } +} + +// Only use this function if you are sure that the user is logged in +export async function getCurrentUserId(): Promise { const payload = await getAccessTokenPayload({ ignoreExpiration: true }); - if (!payload) return null; - return payload.userId as string; + const userId = payload?.userId?.toString(); + // If userId is not set, then return null + if (!userId) { + throw new Error("userId is not set"); + } + // If invalid userId, then return null + if (!userId.match(/^[0-9]+$/)) { + throw new Error("invalid userId"); + } + return parseInt(userId); } async function getAccessTokenPayload(options: any) { diff --git a/frontend/app/room/[id]/page.tsx b/frontend/app/room/[id]/page.tsx index 9c23371e..79d7f6b5 100644 --- a/frontend/app/room/[id]/page.tsx +++ b/frontend/app/room/[id]/page.tsx @@ -25,7 +25,7 @@ // ); //} -import { getUserId } from "@/app/lib/session"; +import { getCurrentUserId } from "@/app/lib/session"; import { Separator } from "@/components/ui/separator"; import { Sidebar } from "../sidebar"; import { getRoom, getUsers, getUser } from "@/app/lib/actions"; @@ -44,11 +44,7 @@ export default async function Page({ }: { params: { id: number }; }) { - const currentUserId = await getUserId(); - if (!currentUserId) { - console.error("getUserId error"); - throw new Error("getUserId error"); - } + const currentUserId = await getCurrentUserId(); const roomInfo = await getRoom(id); if (!roomInfo) { notFound(); @@ -60,10 +56,9 @@ export default async function Page({ .sort((a: UserRole, b: UserRole) => a.id - b.id); const myInfo = roomInfo.users.find((user: UserOnRoom) => { - return user.userId === parseInt(currentUserId); + return user.userId === currentUserId; }); if (!myInfo) { - console.error("Not participating in room"); throw new Error("Not participating in room"); } const allUsers = await getUsers(); @@ -76,7 +71,7 @@ export default async function Page({ role: usersRole[i].role, }), ); - const currentUser = await getUser(parseInt(currentUserId)); + const currentUser = await getUser(currentUserId); return ( <>
diff --git a/frontend/app/ui/direct-message/dm-sidebar.tsx b/frontend/app/ui/direct-message/dm-sidebar.tsx index 584d9cb0..412580ee 100644 --- a/frontend/app/ui/direct-message/dm-sidebar.tsx +++ b/frontend/app/ui/direct-message/dm-sidebar.tsx @@ -2,7 +2,7 @@ import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Separator } from "@/components/ui/separator"; import { getUsers } from "@/app/lib/actions"; -import { getUserId } from "@/app/lib/session"; +import { getCurrentUserId } from "@/app/lib/session"; import { UserButton } from "@/app/ui/direct-message/user-button"; const DirectMessageSidebar = async () => { @@ -11,12 +11,8 @@ const DirectMessageSidebar = async () => { console.error("getUsers Error"); return null; } - const currentUserId = await getUserId(); - if (!currentUserId) { - console.error("getUserId Error"); - return null; - } - const users = tmpUsers.filter((user) => parseInt(currentUserId) !== user.id); + const currentUserId = await getCurrentUserId(); + const users = tmpUsers.filter((user) => currentUserId !== user.id); return (
diff --git a/frontend/app/ui/nav.tsx b/frontend/app/ui/nav.tsx index dd23f946..dddbb99b 100644 --- a/frontend/app/ui/nav.tsx +++ b/frontend/app/ui/nav.tsx @@ -1,9 +1,8 @@ import { ModeToggle } from "@/components/toggle-mode"; import { Button } from "@/components/ui/button"; import Link from "next/link"; -import { signIn, signOut } from "@/app/lib/actions"; +import { signInAsTestUser, signOut } from "@/app/lib/actions"; import { isLoggedIn } from "@/app/lib/session"; -import { redirect } from "next/navigation"; function AuthorizedMenu() { return ( @@ -20,14 +19,6 @@ function AuthorizedMenu() { ); } -async function signInAsTestUser() { - "use server"; - const email = "test@example.com"; - const password = "password-test"; - await signIn({ email, password }); - redirect("/"); -} - function UnauthorizedMenu() { return (
  • diff --git a/frontend/app/ui/profile/profile-form.tsx b/frontend/app/ui/profile/profile-form.tsx index 2f99c5ae..7aabd54e 100644 --- a/frontend/app/ui/profile/profile-form.tsx +++ b/frontend/app/ui/profile/profile-form.tsx @@ -1,9 +1,11 @@ +"use client"; import { Skeleton } from "@/components/ui/skeleton"; import { Separator } from "@/components/ui/separator"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Stack } from "@/app/ui/layout/stack"; import { Label } from "@/components/ui/label"; +import { useAuthContext } from "@/app/lib/client-auth"; function AvatarSkeleton() { return ; @@ -23,10 +25,11 @@ function ProfileItem({ type, title, value }: ProfileItemProps) { export type ProfileItemProps = { type: string; title: string; - value: string; + value?: string; }; export default function ProfileForm() { + const { currentUser } = useAuthContext(); // Menu: min 100px // Profile : the rest return ( @@ -37,8 +40,8 @@ export default function ProfileForm() { - - + +