Skip to content

Commit

Permalink
refactor: do not use hof
Browse files Browse the repository at this point in the history
  • Loading branch information
ooooorobo committed Aug 29, 2024
1 parent d167aa9 commit e5aa5bd
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 127 deletions.
12 changes: 8 additions & 4 deletions src/app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { MetaFunction } from '@remix-run/node';
import type { LoaderFunction, MetaFunction } from '@remix-run/node';
import { json } from '@remix-run/node';
import { withAuthenticated } from 'src/app/server/withAuthenticated';
import { authenticate } from 'src/app/server/authenticate';
import { getAllInfo, info } from 'src/types';
import { InfoListPage } from 'src/pages/main/info_list/InfoListPage';
import { useLoaderData } from '@remix-run/react';
Expand All @@ -10,19 +10,23 @@ export const meta: MetaFunction = () => {
return [{ title: '구구' }, { name: 'description', content: '내 사랑을 구해줄래? 구해줄게!' }];
};

export const loader = withAuthenticated(async (_, accessToken) => {
export const loader: LoaderFunction = async ({ request }) => {
const accessToken = await authenticate(request);

const { data: profileList } = await getAllInfo({
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

const { data: userInfo } = await info({
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

return json({ userInfo, profileList });
});
};

export default function Index() {
const { profileList, userInfo } = useLoaderData<typeof loader>();
Expand Down
48 changes: 9 additions & 39 deletions src/app/routes/api.$.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,11 @@
import { ActionFunction, LoaderFunction } from '@remix-run/node';
import { withAuthenticated } from 'src/app/server/withAuthenticated';
//
// export const loader = withAuthenticated(async ({ request }: LoaderFunctionArgs, accessToken) => {
// const url = new URL(request.url);
// const newHeaders = new AxiosHeaders();
// request.headers.forEach((v, k) => newHeaders.set(k, v));
// request.headers.set('Authorization', `Bearer ${accessToken}`);
// try {
// return customInstance({
// method: request.method,
// url: `${url.pathname}${url.search}`,
// headers: {
// Authorization: `Bearer ${accessToken}`,
// },
// });
// } catch (e) {
// console.error(e);
// return null;
// }
// });
//
// export const action = withAuthenticated(async ({ request }: ActionFunctionArgs, accessToken) => {
// const url = new URL(request.url);
// const newHeaders = new AxiosHeaders();
// request.headers.forEach((v, k) => newHeaders.set(k, v));
// request.headers.set('Authorization', `Bearer ${accessToken}`);
//
// return customInstance({
// method: request.method,
// url: `${url.pathname}${url.search}`,
// headers: {
// Authorization: `Bearer ${accessToken}`,
// },
// });
// });
import { authenticate } from 'src/app/server/authenticate';

const apiURL = new URL(process.env.API_BASE_URL ?? '');

export const loader: LoaderFunction = withAuthenticated((args, accessToken) => {
export const loader: LoaderFunction = async (args) => {
const accessToken = await authenticate(args.request);

const url = new URL(args.request.url);
url.protocol = apiURL.protocol;
url.host = apiURL.host;
Expand All @@ -52,9 +20,11 @@ export const loader: LoaderFunction = withAuthenticated((args, accessToken) => {
},
}),
);
});
};

export const action: ActionFunction = async (args) => {
const accessToken = await authenticate(args.request);

export const action: ActionFunction = withAuthenticated((args, accessToken) => {
const url = new URL(args.request.url);
url.protocol = apiURL.protocol;
url.host = apiURL.host;
Expand All @@ -70,4 +40,4 @@ export const action: ActionFunction = withAuthenticated((args, accessToken) => {
},
}),
);
});
};
38 changes: 22 additions & 16 deletions src/app/routes/auth.kakao.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { LoaderFunction, redirect } from '@remix-run/node';
import { loginKakao } from 'src/types';
import { commitSession, getAuthSession } from 'src/app/server/sessions';
import { getRefreshTokenFromHeader } from 'src/app/server/getRefreshToken';
import dayjs from 'dayjs';

export const loader: LoaderFunction = async ({ request }) => {
if (import.meta.env.DEV) {
const session = await getAuthSession(request);
session.set('accessToken', process.env.VITE_DEV_JWT_TOKEN ?? '');
session.set('refreshToken', process.env.VITE_DEV_JWT_TOKEN ?? '');
session.set('expiredAt', dayjs().add(1, 'day').valueOf().toString());

return redirect('/', {
headers: {
'Set-Cookie': await commitSession(session),
},
});
}

const searchParams = new URL(request.url).searchParams;
const code = searchParams.get('code');

Expand All @@ -15,25 +28,18 @@ export const loader: LoaderFunction = async ({ request }) => {
}

try {
const { data, headers } = await loginKakao({ code });

const refreshToken = getRefreshTokenFromHeader(headers);
if (!refreshToken) return redirect('/login');
const { data } = await loginKakao({ code });

const session = await getAuthSession(request);
session.set('accessToken', data.accessToken);
session.set('refreshToken', refreshToken);

const token = await getAuthSession(request);
if (token) {
return redirect('/', {
headers: {
'Set-Cookie': await commitSession(session),
},
});
}
session.set('refreshToken', data.refreshToken);
session.set('expiredAt', dayjs().add(1, 'day').valueOf().toString());

return redirect('/login');
return redirect('/', {
headers: {
'Set-Cookie': await commitSession(session),
},
});
} catch (e) {
console.error(e);
return redirect('/login');
Expand Down
9 changes: 6 additions & 3 deletions src/app/routes/mypage.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { MyPage as _MyPage } from 'src/pages/mypage/MyPage';
import { withAuthenticated } from 'src/app/server/withAuthenticated';
import { authenticate } from 'src/app/server/authenticate';
import { info } from 'src/types';
import { useLoaderData } from '@remix-run/react';
import { LoaderFunction } from '@remix-run/node';

export const loader: LoaderFunction = async ({ request }) => {
const accessToken = await authenticate(request);

export const loader = withAuthenticated(async (_, accessToken) => {
const { data: userInfo } = await info({
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

return { userInfo };
});
};

export default function MyPage() {
const { userInfo } = useLoaderData<typeof loader>();
Expand Down
9 changes: 6 additions & 3 deletions src/app/routes/profile.$key.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { ProfilePage } from 'src/pages/profile/ProfilePage';
import { getInfo } from 'src/types';
import { withAuthenticated } from 'src/app/server/withAuthenticated';
import { authenticate } from 'src/app/server/authenticate';
import { useLoaderData } from '@remix-run/react';
import { MyProfileProvider } from 'src/entities/profile/model/myProfileStore';
import { IdealPartnerProvider } from 'src/entities/ideal_partner/model/idealPartnerStore';
import { useMemo } from 'react';
import { convertDtoToProfile } from 'src/entities/profile/model/convertProfileToDto';
import { convertDtoToIdealPartner } from 'src/entities/ideal_partner/model/convertIdealPartnerToDto';
import { LoaderFunction } from '@remix-run/node';

export const loader: LoaderFunction = async ({ request, params }) => {
const accessToken = await authenticate(request);

export const loader = withAuthenticated(async ({ params }, accessToken) => {
const { key } = params;

if (!key) {
Expand All @@ -25,7 +28,7 @@ export const loader = withAuthenticated(async ({ params }, accessToken) => {
});

return { profile: data };
});
};

export default function Page() {
const { profile } = useLoaderData<typeof loader>();
Expand Down
55 changes: 55 additions & 0 deletions src/app/server/authenticate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { redirect } from '@remix-run/node';
import { commitSession, getAuthSession } from 'src/app/server/sessions';
import dayjs from 'dayjs';
import { AuthorizationError } from 'src/shared/lib/custom_instance';
import { AxiosError } from 'axios';
import { refreshToken } from 'src/types';

class UnauthorizedError extends Error {
message = 'unauthorized';
}

export const authenticate = async (request: Request) => {
const session = await getAuthSession(request);
const expiredAt = session.get('expiredAt');
const accessToken = session.get('accessToken');

try {
if (!accessToken) {
throw redirect('/login');
}

if (!expiredAt || dayjs(expiredAt).diff(dayjs(), 'day') > 1) {
throw new UnauthorizedError();
}

return accessToken;
} catch (e) {
if (
e instanceof AuthorizationError ||
(((e: unknown): e is AxiosError => e instanceof AxiosError)(e) && e.status === 401)
) {
await requestRefreshToken(request);
}
}
};

export const requestRefreshToken = async (request: Request) => {
const session = await getAuthSession(request);
const { data } = await refreshToken({
accessToken: session.get('accessToken')!,
refreshToken: session.get('refreshToken')!,
});

session.set('accessToken', data.accessToken);
session.set('refreshToken', data.refreshToken);

if (request.method === 'GET')
throw redirect(request.url, {
headers: {
'Set-Cookie': await commitSession(session),
},
});

return data.accessToken;
};
9 changes: 3 additions & 6 deletions src/app/server/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createCookieSessionStorage, Session } from '@remix-run/node';
type AuthSessionData = {
accessToken: string;
refreshToken: string;
expiredAt: string;
};

type SessionFlashData = {
Expand All @@ -15,7 +16,7 @@ const { getSession, commitSession, destroySession } = createCookieSessionStorage
cookie: {
name: '__session',
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 10,
maxAge: 60 * 60 * 24,
path: '/',
sameSite: 'lax',
secrets: ['s3cret1'],
Expand All @@ -24,11 +25,7 @@ const { getSession, commitSession, destroySession } = createCookieSessionStorage
});

const getAuthSession = async (request: Request) => {
const foundSession = await getSession(request.headers.get('Cookie'));
if (process.env.NODE_ENV === 'development') {
foundSession.set('accessToken', process.env.VITE_DEV_JWT_TOKEN ?? '');
}
return foundSession;
return await getSession(request.headers.get('Cookie'));
};

export { getAuthSession, commitSession, destroySession };
49 changes: 0 additions & 49 deletions src/app/server/withAuthenticated.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/pages/main/login/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const LoginPage = () => {
<h1>내 사랑 구해줄래? 구해줄게!</h1>
</div>
<div className={styles.ButtonWrapper}>
<Link to={import.meta.env.VITE_KAUTH_URL}>
<Link to={import.meta.env.DEV ? '/auth/kakao' : import.meta.env.VITE_KAUTH_URL}>
<button className={`${styles.Button} ${styles.Kakao}`}>
<span className={styles.Icon}>
<img src="/images/kakao.png" alt="카카오 로그인" width={29} height={29} />
Expand Down
13 changes: 7 additions & 6 deletions src/shared/lib/custom_instance.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import { requestRefreshToken } from 'src/app/server/authenticate';

export class AuthorizationError extends Error {}

Expand All @@ -11,13 +12,13 @@ instance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
console.error(error);
const { config } = error;
async (e) => {
if (e.status !== 401 || e.request.url.includes('refresh') || e.request.config.sent || !e.config) return;

if (!config.url.includes('refresh-token') && error.response.status === 401) {
throw new AuthorizationError();
}
const accessToken = await requestRefreshToken(e.request);
e.config.headers.Authorization = `Bearer ${accessToken}`;
e.config.sent = true;
return instance(e.config);
},
);

Expand Down

0 comments on commit e5aa5bd

Please sign in to comment.