From 182d86eef2379861216de5bf1b6f8b9118f61544 Mon Sep 17 00:00:00 2001 From: Jeongmin Lee Date: Mon, 7 Oct 2024 06:01:33 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=EB=B6=81=EB=A7=88?= =?UTF-8?q?=ED=81=AC=20=EA=B4=80=EB=A0=A8=20api,=20type,=20keys=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84=20#52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/user/bookmarks/bookmarks.ts | 24 +++++++++++++++++++++ src/apis/user/bookmarks/bookmarksKeys.ts | 7 ++++++ src/apis/user/bookmarks/bookmarksType.ts | 27 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/apis/user/bookmarks/bookmarks.ts create mode 100644 src/apis/user/bookmarks/bookmarksKeys.ts create mode 100644 src/apis/user/bookmarks/bookmarksType.ts diff --git a/src/apis/user/bookmarks/bookmarks.ts b/src/apis/user/bookmarks/bookmarks.ts new file mode 100644 index 0000000..d51e7ed --- /dev/null +++ b/src/apis/user/bookmarks/bookmarks.ts @@ -0,0 +1,24 @@ +import instance from "@/apis/instance"; +import { FIESTA_ENDPOINTS } from "@/config"; +import { generateUrlWithParams } from "@/utils"; + +import { BookmarkFestivalParameter, BookmarksResponse } from "./bookmarksType"; + +const defaultParameter: BookmarkFestivalParameter = { + page: 0, + size: 6, +}; + +export const getBookmarkedFestival = async ( + params: BookmarkFestivalParameter = defaultParameter, +) => { + const endpoint = FIESTA_ENDPOINTS.users.bookmarks; + const { data } = await instance.get( + generateUrlWithParams(endpoint, params), + { + cache: "no-store", + }, + ); + + return data; +}; diff --git a/src/apis/user/bookmarks/bookmarksKeys.ts b/src/apis/user/bookmarks/bookmarksKeys.ts new file mode 100644 index 0000000..cb4fbdb --- /dev/null +++ b/src/apis/user/bookmarks/bookmarksKeys.ts @@ -0,0 +1,7 @@ +import { BookmarkFestivalParameter } from "./bookmarksType"; + +export const bookmarksKeys = { + all: ["bookmarks"] as const, + list: (params: BookmarkFestivalParameter) => + [...bookmarksKeys.all, params] as const, +}; diff --git a/src/apis/user/bookmarks/bookmarksType.ts b/src/apis/user/bookmarks/bookmarksType.ts new file mode 100644 index 0000000..8994b18 --- /dev/null +++ b/src/apis/user/bookmarks/bookmarksType.ts @@ -0,0 +1,27 @@ +import { SortOption } from "@/apis/review/reviews/reviewsType"; + +export type BookmarksResponse = { + content: Array; + offset: number; + pageNumber: number; + pageSize: number; + totalElements: number; + totalPages: number; +}; + +export type BookmarkFestivalParameter = { + page?: number; + size?: number; + sort?: SortOption; +}; + +export type BookmarkedFestival = { + festivalId: number; + name: string; + thumbnailImage: string; + sido: string; + sigungu: string; + startDate: string; + endDate: string; + isBookmarked: boolean; +}; From 40dc2c2d888e585e00b3a49befb5796ee182412a Mon Sep 17 00:00:00 2001 From: Jeongmin Lee Date: Mon, 7 Oct 2024 06:18:38 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20feat:=20mypage=20=EB=B6=81?= =?UTF-8?q?=EB=A7=88=ED=81=AC=20API=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20use?= =?UTF-8?q?OptimisticMutation=20=EA=B5=AC=ED=98=84=20#52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/review/reviews/reviewsType.ts | 3 +- .../TotalReviews/TotalReviewListItem.tsx | 2 +- .../_components/Bookmark/MyPageScrab.tsx | 106 ++++++++++++++++++ .../MypageBookmarkFallback.tsx} | 6 +- .../Bookmark/MypageBookmarkSkeleton.tsx | 19 ++++ src/app/mypage/_components/MypageTab.tsx | 10 +- src/hooks/useOptimisticMutation.ts | 64 +++++++++++ 7 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 src/app/mypage/_components/Bookmark/MyPageScrab.tsx rename src/app/mypage/_components/{MypageFallback.tsx => Bookmark/MypageBookmarkFallback.tsx} (57%) create mode 100644 src/app/mypage/_components/Bookmark/MypageBookmarkSkeleton.tsx create mode 100644 src/hooks/useOptimisticMutation.ts diff --git a/src/apis/review/reviews/reviewsType.ts b/src/apis/review/reviews/reviewsType.ts index 1ba3260..ab0a3ad 100644 --- a/src/apis/review/reviews/reviewsType.ts +++ b/src/apis/review/reviews/reviewsType.ts @@ -1,6 +1,6 @@ export type FestivalReviewsParameters = { festivalId?: number | string; - sort?: "createdAt" | "likeCount"; + sort?: SortOption; page?: number; size?: number; }; @@ -47,6 +47,7 @@ export interface Keyword { export enum SortOption { createdAt = "createdAt", likeCount = "likeCount", + desc = "desc", } export type PostReviewResponse = { diff --git a/src/app/(home)/festivals/[festivalId]/_components/DetailFestivalTab/Review/_components/TotalReviews/TotalReviewListItem.tsx b/src/app/(home)/festivals/[festivalId]/_components/DetailFestivalTab/Review/_components/TotalReviews/TotalReviewListItem.tsx index 599e14d..51266ba 100644 --- a/src/app/(home)/festivals/[festivalId]/_components/DetailFestivalTab/Review/_components/TotalReviews/TotalReviewListItem.tsx +++ b/src/app/(home)/festivals/[festivalId]/_components/DetailFestivalTab/Review/_components/TotalReviews/TotalReviewListItem.tsx @@ -128,7 +128,7 @@ const TotalReviewListItem: FC = ({ review }) => {

- {review.keywords.splice(0, 2).map((keyword) => ( + {review.keywords.map((keyword) => ( ))}
diff --git a/src/app/mypage/_components/Bookmark/MyPageScrab.tsx b/src/app/mypage/_components/Bookmark/MyPageScrab.tsx new file mode 100644 index 0000000..5d20c90 --- /dev/null +++ b/src/app/mypage/_components/Bookmark/MyPageScrab.tsx @@ -0,0 +1,106 @@ +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { FC, useRef, useState } from "react"; + +import { patchBookmarkFestival } from "@/apis/festivals/bookmarkFestival/bookmarkFestival"; +import { SortOption } from "@/apis/review/reviews/reviewsType"; +import { getBookmarkedFestival } from "@/apis/user/bookmarks/bookmarks"; +import { bookmarksKeys } from "@/apis/user/bookmarks/bookmarksKeys"; +import { + BookmarkFestivalParameter, + BookmarksResponse, +} from "@/apis/user/bookmarks/bookmarksType"; +import { TrendFestivalCard } from "@/components/core/Card"; +import ClientPagination from "@/components/Pagination/ClientPagination"; +import { useOptimisticMutation } from "@/hooks/useOptimisticMutation"; + +import MypageBookmarkFallback from "./MypageBookmarkFallback"; +import MypageBookmarkSkeleton from "./MypageBookmarkSkeleton"; + +interface Props {} + +const MypageBookmark: FC = ({}) => { + const containerRef = useRef(null); + const queryClient = useQueryClient(); + + const [params, setParams] = useState({ + sort: SortOption.createdAt, + page: 0, + size: 6, + }); + + const { data, isLoading } = useQuery({ + queryKey: bookmarksKeys.list(params), + queryFn: () => getBookmarkedFestival(params), + }); + + const { mutate: toggleBookmark } = useOptimisticMutation({ + mutationFn: patchBookmarkFestival, + queryKey: bookmarksKeys.list(params), + onMutate: async (festivalId) => { + await queryClient.cancelQueries({ + queryKey: bookmarksKeys.list(params), + }); + + const previousBookmarkData = queryClient.getQueryData( + bookmarksKeys.list(params), + ); + + queryClient.setQueryData( + bookmarksKeys.list(params), + (oldQueryData: BookmarksResponse) => ({ + ...oldQueryData, + content: oldQueryData.content.filter( + (c: { festivalId: number }) => c.festivalId !== festivalId, + ), + }), + ); + return previousBookmarkData; + }, + }); + + const handlePage = (page: number) => { + setParams((prev) => ({ ...prev, page })); + + if (containerRef.current) { + containerRef.current.scrollIntoView({ behavior: "smooth" }); + } + }; + + if (isLoading) { + return ; + } + + if (data?.content.length === 0 || !data) { + return ; + } + + return ( +
+ + {data.totalElements}개의 페스티벌 + + +
+ {data.content?.map((festival, index) => ( + toggleBookmark(festival.festivalId)} + /> + ))} +
+ + +
+ ); +}; + +export default MypageBookmark; diff --git a/src/app/mypage/_components/MypageFallback.tsx b/src/app/mypage/_components/Bookmark/MypageBookmarkFallback.tsx similarity index 57% rename from src/app/mypage/_components/MypageFallback.tsx rename to src/app/mypage/_components/Bookmark/MypageBookmarkFallback.tsx index f349be2..4c48b43 100644 --- a/src/app/mypage/_components/MypageFallback.tsx +++ b/src/app/mypage/_components/Bookmark/MypageBookmarkFallback.tsx @@ -1,8 +1,8 @@ import Image from "next/image"; -const MypageFallback = () => { +const MypageBookmarkFallback = () => { return ( -
+
service { ); }; -export default MypageFallback; +export default MypageBookmarkFallback; diff --git a/src/app/mypage/_components/Bookmark/MypageBookmarkSkeleton.tsx b/src/app/mypage/_components/Bookmark/MypageBookmarkSkeleton.tsx new file mode 100644 index 0000000..449531f --- /dev/null +++ b/src/app/mypage/_components/Bookmark/MypageBookmarkSkeleton.tsx @@ -0,0 +1,19 @@ +const MypageBookmarkSkeleton = () => { + return ( +
+
+
+ {Array.from({ length: 6 }).map((_, idx) => ( +
+ ))} +
+ + Loading... +
+ ); +}; + +export default MypageBookmarkSkeleton; diff --git a/src/app/mypage/_components/MypageTab.tsx b/src/app/mypage/_components/MypageTab.tsx index 913e129..f537904 100644 --- a/src/app/mypage/_components/MypageTab.tsx +++ b/src/app/mypage/_components/MypageTab.tsx @@ -3,13 +3,15 @@ import * as Tabs from "@radix-ui/react-tabs"; import { FC } from "react"; +import MypageBookmark from "./Bookmark/MyPageScrab"; + interface Props {} const MypageTab: FC = ({}) => { const TabList = [ { name: "스크랩", - contentComponent:

스크랩

, + contentComponent: , }, { name: "활동뱃지", @@ -26,11 +28,7 @@ const MypageTab: FC = ({}) => { return ( - + {TabList.map(({ name }, index) => ( { + mutationFn: (variables: TVariables) => Promise; + queryKey: QueryKey; + onMutate?: (variables: TVariables) => Promise; + setQueryData?: (data: TData) => void; + onError?: (error: DefaultError, variables: TVariables) => void; + onSettled?: ( + data: TData | undefined, + error: DefaultError | null, + variables: TVariables, + context: { context: unknown } | undefined, + ) => void; +} + +export const useOptimisticMutation = ({ + mutationFn, + queryKey, + onMutate, + setQueryData, + onError, + onSettled, +}: UseOptimisticMutationProps) => { + const queryClient = useQueryClient(); + + const { mutate } = useMutation({ + mutationFn, + onMutate: async (variables) => { + await queryClient.cancelQueries({ queryKey }); + const context = queryClient.getQueryData(queryKey); + + if (onMutate) { + const newData = await onMutate(variables); + queryClient.setQueryData(queryKey, newData); + } else { + queryClient.setQueryData(queryKey, setQueryData); + } + + return { context }; + }, + onError: (error, variables, context) => { + if (onError) { + onError(error, variables); + } + queryClient.setQueryData(queryKey, context?.context); + }, + onSettled: (data, error, variables, context) => { + if (onSettled) { + onSettled(data, error, variables, context); + } + queryClient.invalidateQueries({ queryKey }); + }, + }); + + return { + mutate, + }; +}; From 69c0eb6f810723cefc810a5114cf2c2fe49c0e1c Mon Sep 17 00:00:00 2001 From: Jeongmin Lee Date: Mon, 7 Oct 2024 06:19:47 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=84=B8=EC=85=98=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?#52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auth.ts | 2 + src/apis/instance.ts | 8 +- src/app/layout.tsx | 7 +- .../core/Button/BookingButton/ScrabButton.tsx | 51 ++++------- .../TrendFestivalCard/TrendFestivalCard.tsx | 90 ++++++++++++------- .../session/useInitializeUserProfile.tsx | 19 ++-- src/hooks/session/useUpdateUserSession.tsx | 11 +-- src/lib/Providers/SessionProvider.tsx | 8 +- src/lib/session.ts | 6 ++ src/middleware.ts | 30 ++++++- 10 files changed, 132 insertions(+), 100 deletions(-) create mode 100644 src/lib/session.ts diff --git a/auth.ts b/auth.ts index 8cea58d..df3c19b 100644 --- a/auth.ts +++ b/auth.ts @@ -64,6 +64,8 @@ export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({ if (params.trigger === "update") { params.token = { ...params.token, + userTypeId: params.session.user.userTypeId, + isProfileRegistered: params.session.user.isProfileRegistered, }; } diff --git a/src/apis/instance.ts b/src/apis/instance.ts index 8e55e66..6b1d7cd 100644 --- a/src/apis/instance.ts +++ b/src/apis/instance.ts @@ -1,5 +1,6 @@ import { Session } from "next-auth"; -import { getSession } from "next-auth/react"; + +import { getClientSideSession } from "@/lib/session"; import { env } from "../env"; import { getServerSideSession } from "./auth/auth"; @@ -22,11 +23,6 @@ export interface FiestaResponse { data: T; } -export async function getClientSideSession() { - const session = await getSession(); - return session; -} - export type FiestaFetchOptions = Omit; export class CreateFiestaFetch { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5483d5e..5f1fc3f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,6 +5,7 @@ import type { Metadata } from "next"; import Script from "next/script"; import { ReactNode } from "react"; +import { auth } from "@/auth"; import { env } from "@/env"; import MobileLayout from "@/layout/Mobile/MobileLayout"; import { @@ -21,15 +22,17 @@ export const metadata: Metadata = { description: "Generated by create next app", }; -export default function RootLayout({ +export default async function RootLayout({ children, }: Readonly<{ children: ReactNode; }>) { + const session = await auth(); + return ( - + diff --git a/src/components/core/Button/BookingButton/ScrabButton.tsx b/src/components/core/Button/BookingButton/ScrabButton.tsx index 38d3cbf..754bb26 100644 --- a/src/components/core/Button/BookingButton/ScrabButton.tsx +++ b/src/components/core/Button/BookingButton/ScrabButton.tsx @@ -1,6 +1,6 @@ "use client"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; import { Session } from "next-auth"; import { FC, useState } from "react"; @@ -8,8 +8,10 @@ import { getFestivalBookmark } from "@/apis/festivals/bookmarkCountFestival/book import { BookmarkCountFestivalKeys } from "@/apis/festivals/bookmarkCountFestival/bookmarkCountFestivalKeys"; import { patchBookmarkFestival } from "@/apis/festivals/bookmarkFestival/bookmarkFestival"; import { DetailFestivalResponse } from "@/apis/festivals/detailFestival/detailFestivalType"; +import { bookmarksKeys } from "@/apis/user/bookmarks/bookmarksKeys"; import LoginRequiredDialog from "@/components/Dialog/LoginRequiredDialog/LoginRequiredDialog"; import { ScrabIcon } from "@/components/icons"; +import { useOptimisticMutation } from "@/hooks/useOptimisticMutation"; interface Props { festival: DetailFestivalResponse; @@ -24,44 +26,23 @@ const ScrabButton: FC = ({ festival, session }) => { const queryClient = useQueryClient(); - const { mutate: toggleScrab } = useMutation({ + const { mutate: toggleScrab } = useOptimisticMutation({ mutationFn: patchBookmarkFestival, - onMutate: async (festivalId) => { - await queryClient.cancelQueries({ - queryKey: BookmarkCountFestivalKeys.byId(festival.festivalId), - }); - - const previousBookmarkData = queryClient.getQueryData( - BookmarkCountFestivalKeys.byId(festival.festivalId), - ); - - queryClient.setQueryData( - BookmarkCountFestivalKeys.byId(festival.festivalId), - ( - oldQueryData: Pick< - DetailFestivalResponse, - "bookmarkCount" | "isBookmarked" - >, - ) => { - return { - isBookmarked: !oldQueryData.isBookmarked, - bookmarkCount: !oldQueryData.isBookmarked - ? oldQueryData.bookmarkCount + 1 - : oldQueryData.bookmarkCount - 1, - }; - }, - ); - return previousBookmarkData; - }, - onError: (_error, _toggle, context) => - queryClient.setQueryData( - BookmarkCountFestivalKeys.byId(festival.festivalId), - context, - ), - onSettled: () => + queryKey: BookmarkCountFestivalKeys.byId(festival.festivalId), + setQueryData: (data) => ({ + isBookmarked: !data.isBookmarked, + bookmarkCount: !data.isBookmarked + ? data.bookmarkCount + 1 + : data.bookmarkCount - 1, + }), + onSettled: () => { queryClient.invalidateQueries({ queryKey: BookmarkCountFestivalKeys.byId(festival.festivalId), }), + queryClient.invalidateQueries({ + queryKey: bookmarksKeys.all, + }); + }, }); const [isOpen, setIsOpen] = useState(false); diff --git a/src/components/core/Card/TrendFestivalCard/TrendFestivalCard.tsx b/src/components/core/Card/TrendFestivalCard/TrendFestivalCard.tsx index f153083..092a02b 100644 --- a/src/components/core/Card/TrendFestivalCard/TrendFestivalCard.tsx +++ b/src/components/core/Card/TrendFestivalCard/TrendFestivalCard.tsx @@ -1,51 +1,79 @@ import Image from "next/image"; import Link, { LinkProps } from "next/link"; -import { FC } from "react"; +import { FC, MouseEvent } from "react"; import { FestivalListModel } from "@/apis/festivals/hotFestival/hotFestivalType"; import { DateTag } from "@/components/core/Tag"; +import { ScrabIcon } from "@/components/icons"; import { formatToKoreanDate, getDday } from "@/lib/dayjs"; +import { cn } from "@/utils"; + +import { IconButton } from "../../Button"; export interface TrendFestivalCardProps extends LinkProps { festival: FestivalListModel; + isBookmarkAvailable?: boolean; + isBookmarked?: boolean; + onToggle?: () => void; } const TrendFestivalCard: FC = ({ festival, + isBookmarkAvailable, + isBookmarked, + onToggle, ...props }) => { + const handleIconButtonClick = (event: MouseEvent) => { + event.stopPropagation(); + onToggle && onToggle(); + }; return ( - -
- trendy - -
+
+ +
+ trendy + +
-
- - {festival.sido + festival.sigungu} - - - {festival.name} - - - {`${formatToKoreanDate(festival.startDate)} - ${formatToKoreanDate(festival.endDate)}`} - -
- +
+ + {festival.sido + festival.sigungu} + + + {festival.name} + + + {`${formatToKoreanDate(festival.startDate)} - ${formatToKoreanDate(festival.endDate)}`} + +
+ + {isBookmarkAvailable && ( + + + + )} +
); }; diff --git a/src/hooks/session/useInitializeUserProfile.tsx b/src/hooks/session/useInitializeUserProfile.tsx index 86fbabe..4e2c992 100644 --- a/src/hooks/session/useInitializeUserProfile.tsx +++ b/src/hooks/session/useInitializeUserProfile.tsx @@ -1,24 +1,17 @@ +import { Session } from "next-auth"; import { useEffect } from "react"; -import { getClientSideSession } from "@/apis/instance"; import { useUserStore } from "@/store/user"; -import { log } from "@/utils"; -const useInitializeUserProfile = () => { +const useInitializeUserProfile = (session: Session | null) => { const setUser = useUserStore((state) => state.updateUser); const initializeUserProfile = async () => { - try { - const session = await getClientSideSession(); - - if (session) { - const { user } = session; - return setUser(user); - } - setUser(null); - } catch (error) { - log(error); + if (session) { + const { user } = session; + return setUser(user); } + setUser(null); }; useEffect(() => { diff --git a/src/hooks/session/useUpdateUserSession.tsx b/src/hooks/session/useUpdateUserSession.tsx index 8540341..c80c9e0 100644 --- a/src/hooks/session/useUpdateUserSession.tsx +++ b/src/hooks/session/useUpdateUserSession.tsx @@ -1,26 +1,23 @@ "use client"; import { useSearchParams } from "next/navigation"; +import { useSession } from "next-auth/react"; import { useEffect } from "react"; -import { getClientSideSession } from "@/apis/instance"; -import { updateAuthSession } from "@/lib/updateAuthSession"; import { useUserStore } from "@/store/user"; const useUpdateUserSession = () => { const searchParams = useSearchParams(); const setUser = useUserStore((state) => state.updateUser); + const { data: session, update } = useSession(); const handleSessionUpdate = async () => { try { - const session = await getClientSideSession(); - if (session) { session.user.userTypeId = Number(searchParams.get("userTypeId")); session.user.isProfileRegistered = true; - const newSession = await updateAuthSession(session); - - setUser(newSession.user); + await update(session); + setUser(session.user ?? null); } } catch (error) { console.error("Failed to update session:", error); diff --git a/src/lib/Providers/SessionProvider.tsx b/src/lib/Providers/SessionProvider.tsx index 1fc8479..7fb1099 100644 --- a/src/lib/Providers/SessionProvider.tsx +++ b/src/lib/Providers/SessionProvider.tsx @@ -1,5 +1,6 @@ "use client"; +import { Session } from "next-auth"; import { SessionProvider as AuthProvider } from "next-auth/react"; import { FC } from "react"; @@ -7,11 +8,12 @@ import useInitializeUserProfile from "@/hooks/session/useInitializeUserProfile"; interface Props { children: React.ReactNode; + session: Session | null; } -const SessionProvider: FC = ({ children }) => { - useInitializeUserProfile(); - return {children}; +const SessionProvider: FC = ({ children, session }) => { + useInitializeUserProfile(session); + return {children}; }; export default SessionProvider; diff --git a/src/lib/session.ts b/src/lib/session.ts new file mode 100644 index 0000000..80a6c92 --- /dev/null +++ b/src/lib/session.ts @@ -0,0 +1,6 @@ +import { getSession } from "next-auth/react"; + +export async function getClientSideSession() { + const session = await getSession(); + return session; +} diff --git a/src/middleware.ts b/src/middleware.ts index 1df27ab..00a3644 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -2,7 +2,10 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { match } from "path-to-regexp"; -import { getServerSideSession } from "./apis/auth/auth"; +import { getRefreshToken, getServerSideSession } from "./apis/auth/auth"; +import { decodeToken } from "./lib/jwt"; +import { updateAuthSession } from "./lib/updateAuthSession"; +import { log } from "./utils"; const serviceReadyRoute = ["/chat", "/map", "/calander"]; const matchersForAuth = ["/mypage"]; @@ -14,10 +17,31 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(new URL("/", request.url)); } + const session = await getServerSideSession(); + + if (session) { + if ( + session.refreshToken && + session.exp && + session.exp * 1000 < Date.now() + ) { + log("******** token Expired ...."); + const { accessToken, refreshToken } = await getRefreshToken( + session.refreshToken, + ); + + const decodedJWT = decodeToken(accessToken); + + session.accessToken = accessToken; + session.refreshToken = refreshToken; + session.exp = decodedJWT?.exp; + await updateAuthSession(session); + log("******** token updated ...."); + } + } + // * 인증이 필요한 페이지 접근 제어! if (isMatch(request.nextUrl.pathname, matchersForAuth)) { - const session = await getServerSideSession(); - if (!!session && !session.user.isProfileRegistered) { return NextResponse.redirect(new URL("/onboarding", request.url)); } From bd678fce02b1b0d8765e91ba3e30d07ca05e3243 Mon Sep 17 00:00:00 2001 From: Jeongmin Lee Date: Mon, 7 Oct 2024 06:24:20 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=EB=B9=8C=EB=93=9C?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EC=88=98=EC=A0=95=20#52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../festivals/new/_components/CreateFestivalSecondStep.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/(home)/festivals/new/_components/CreateFestivalSecondStep.tsx b/src/app/(home)/festivals/new/_components/CreateFestivalSecondStep.tsx index 9a82723..10780e4 100644 --- a/src/app/(home)/festivals/new/_components/CreateFestivalSecondStep.tsx +++ b/src/app/(home)/festivals/new/_components/CreateFestivalSecondStep.tsx @@ -120,8 +120,9 @@ const CreateFestivalSecondStep: FC = ({ moods, categories }) => { name="categoryIds" render={({ field: { onChange, value } }) => ( )} From c828740fb8e8ca5c6b38e11ac26a91aed76aee87 Mon Sep 17 00:00:00 2001 From: Jeongmin Lee Date: Tue, 8 Oct 2024 02:28:17 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=94=A7=20chore:=20refreshToken=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/middleware.ts | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/middleware.ts b/src/middleware.ts index 00a3644..b52b96f 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -2,10 +2,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { match } from "path-to-regexp"; -import { getRefreshToken, getServerSideSession } from "./apis/auth/auth"; -import { decodeToken } from "./lib/jwt"; -import { updateAuthSession } from "./lib/updateAuthSession"; -import { log } from "./utils"; +import { getServerSideSession } from "./apis/auth/auth"; const serviceReadyRoute = ["/chat", "/map", "/calander"]; const matchersForAuth = ["/mypage"]; @@ -19,26 +16,24 @@ export async function middleware(request: NextRequest) { const session = await getServerSideSession(); - if (session) { - if ( - session.refreshToken && - session.exp && - session.exp * 1000 < Date.now() - ) { - log("******** token Expired ...."); - const { accessToken, refreshToken } = await getRefreshToken( - session.refreshToken, - ); + // if (session) { + // if ( + // session.refreshToken && + // session.exp && + // session.exp * 1000 < Date.now() + // ) { + // const { accessToken, refreshToken } = await getRefreshToken( + // session.refreshToken, + // ); - const decodedJWT = decodeToken(accessToken); + // const decodedJWT = decodeToken(accessToken); - session.accessToken = accessToken; - session.refreshToken = refreshToken; - session.exp = decodedJWT?.exp; - await updateAuthSession(session); - log("******** token updated ...."); - } - } + // session.accessToken = accessToken; + // session.refreshToken = refreshToken; + // session.exp = decodedJWT?.exp; + // await updateAuthSession(session); + // } + // } // * 인증이 필요한 페이지 접근 제어! if (isMatch(request.nextUrl.pathname, matchersForAuth)) { From 04f0bf7d925dfbd87414c77a64468ee0d4b06e59 Mon Sep 17 00:00:00 2001 From: Jeongmin Lee Date: Tue, 8 Oct 2024 02:41:45 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20error=20route=20o?= =?UTF-8?q?n=20auth.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- auth.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/auth.ts b/auth.ts index df3c19b..1bf5db0 100644 --- a/auth.ts +++ b/auth.ts @@ -20,6 +20,7 @@ export const { handlers, auth, signIn, signOut, unstable_update } = NextAuth({ pages: { signIn: "/auth/sign-in", signOut: "/auth/sign-in", + error: "/auth/sign-in", }, callbacks: { async signIn() {