Skip to content

Commit

Permalink
(FE) Add User registration / Login (#69)
Browse files Browse the repository at this point in the history
* Change router path

* Add favicon

* Add main header

* Add redirection to auth callback router

* Add Base API Url to env

* Add SocialButtons deps

* Add login Page

* Add get token logic from redirect url

* Add redux persist to store accessToken in localstorage

* Add auth reducer

* Add redux-persist

* Add ignoredActions - redux-persist
  • Loading branch information
devleejb authored Jan 19, 2024
1 parent 2944ade commit 01317fb
Show file tree
Hide file tree
Showing 18 changed files with 271 additions and 16 deletions.
12 changes: 9 additions & 3 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import { Controller, Get, Req, UseGuards } from "@nestjs/common";
import { Controller, Get, HttpRedirectResponse, Redirect, Req, UseGuards } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { LoginRequest } from "./types/login-request.type";
import { JwtService } from "@nestjs/jwt";
import { LoginResponse } from "./types/login-response.type";
import { UsersService } from "src/users/users.service";
import { Public } from "src/utils/decorators/auth.decorator";
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { ConfigService } from "@nestjs/config";

@ApiTags("Auth")
@Controller("auth")
export class AuthController {
constructor(
private configService: ConfigService,
private jwtService: JwtService,
private usersService: UsersService
) {}

@Public()
@Get("login/github")
@Get("callback/github")
@Redirect()
@UseGuards(AuthGuard("github"))
@ApiOperation({
summary: "SignUp/LogIn with GitHub",
description: "SignUp/Login with GitHub social login",
})
@ApiResponse({ type: LoginResponse })
async login(@Req() req: LoginRequest): Promise<LoginResponse> {
async login(@Req() req: LoginRequest): Promise<HttpRedirectResponse> {
const user = await this.usersService.findOrCreate(
req.user.socialProvider,
req.user.socialUid,
Expand All @@ -33,6 +36,9 @@ export class AuthController {

const accessToken = this.jwtService.sign({ sub: user.id, nickname: user.nickname });

return { accessToken };
return {
url: `${this.configService.get("FRONTEND_BASE_URL")}/auth/callback?token=${accessToken}`,
statusCode: 302,
};
}
}
5 changes: 3 additions & 2 deletions frontend/.env.development
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
VITE_YORKIE_API_ADDR='https://api.yorkie.dev'
VITE_YORKIE_API_KEY='cmftp10ksk14av0kc7gg'
VITE_API_ADDR="http://localhost:3000"
VITE_YORKIE_API_ADDR="https://api.yorkie.dev"
VITE_YORKIE_API_KEY="cmftp10ksk14av0kc7gg"
8 changes: 6 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="apple-touch-icon" href="/favicon-512x512.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<meta name="description" content="Yorkie Codepair" />
<title>CodePair</title>
<!-- Start Single Page Apps for GitHub Pages -->
<script type="text/javascript">
// Single Page Apps for GitHub Pages
Expand Down
18 changes: 18 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
"react-redux": "^9.0.4",
"react-resizable-layout": "^0.7.2",
"react-router-dom": "^6.21.1",
"react-social-login-buttons": "^3.9.1",
"redux-persist": "^6.0.0",
"yorkie-js-sdk": "^0.4.13-rc"
},
"devDependencies": {
Expand Down
Binary file added frontend/public/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/favicon-512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/favicon.ico
Binary file not shown.
21 changes: 19 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,35 @@ import EditorLayout from "./components/layouts/EditorLayout";
import EditorIndex from "./pages/editor/Index";
import { useMemo } from "react";
import { selectConfig } from "./store/configSlice";
import MainLayout from "./components/layouts/MainLayout";
import Index from "./pages/Index";
import CallbackIndex from "./pages/auth/callback/Index";

const router = createBrowserRouter([
{
path: "/",
path: "",
element: <MainLayout />,
children: [
{
path: "",
element: <Index />,
},
],
},
{
path: ":documentId",
element: <EditorLayout />,
children: [
{
path: ":documentId",
path: "",
element: <EditorIndex />,
},
],
},
{
path: "auth/callback",
element: <CallbackIndex />,
},
]);

function App() {
Expand Down
23 changes: 23 additions & 0 deletions frontend/src/components/headers/MainHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { AppBar, Stack, Toolbar } from "@mui/material";

import ThemeButton from "../common/ThemeButton";
import CodePairIcon from "../icons/CodePairIcon";
function MainHeader() {
return (
<AppBar position="static" sx={{ zIndex: 100 }}>
<Toolbar>
<Stack
width="100%"
direction="row"
justifyContent="space-between"
alignItems="center"
>
<CodePairIcon />
<ThemeButton />
</Stack>
</Toolbar>
</AppBar>
);
}

export default MainHeader;
33 changes: 33 additions & 0 deletions frontend/src/components/icons/CodePairIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { SvgIcon, SvgIconProps } from "@mui/material";

type CodePairIconProps = SvgIconProps;

function CodePairIcon(props: CodePairIconProps) {
return (
<SvgIcon {...props}>
<svg
width="40"
height="38"
viewBox="0 0 40 38"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ width: 30, height: 30 }}
>
<path
d="M11.8574 11.4048L18.8525 21.4507C19.2947 22.086 20.1683 22.2423 20.8036 21.8001C20.9398 21.7052 21.0581 21.5869 21.153 21.4507L28.148 11.4048C29.0327 10.1343 28.7198 8.3872 27.4495 7.5027C26.9794 7.17549 26.4205 7 25.8477 7H14.1577C12.6095 7 11.3545 8.25503 11.3545 9.80322C11.3547 10.3758 11.5302 10.9347 11.8574 11.4048Z"
fill="#514C49"
/>
<path
d="M22.8637 29.5446C23.3612 29.8283 23.9338 29.9528 24.5042 29.9014L37.2991 28.7469C38.3271 28.6542 39.0851 27.7457 38.9924 26.7178C38.9876 26.6636 38.9803 26.6096 38.9706 26.556C38.5862 24.4114 37.8296 22.3507 36.7352 20.4668C35.6407 18.5829 34.2255 16.9048 32.5532 15.5085C31.761 14.8471 30.5825 14.953 29.9211 15.7455C29.8862 15.7872 29.8532 15.8305 29.8219 15.8752L22.4807 26.418C22.1535 26.888 21.978 27.4469 21.978 28.0198V27.9849C21.978 28.3055 22.0604 28.6208 22.2176 28.9002C22.3826 29.1751 22.6155 29.4029 22.8942 29.5617"
fill="#FDC433"
/>
<path
d="M17.8492 28.7605C17.6844 29.097 17.4222 29.376 17.0969 29.5616L17.1365 29.539C16.6391 29.8227 16.0665 29.9472 15.4961 29.8959L2.70114 28.7414C2.64694 28.7365 2.59295 28.7293 2.53935 28.7196C1.52348 28.5375 0.847507 27.5663 1.02965 26.5505C1.41407 24.4057 2.17064 22.3451 3.26489 20.4611C4.35914 18.577 5.77455 16.8993 7.44706 15.5028C7.48877 15.4679 7.53208 15.4349 7.57681 15.4037C8.42384 14.8139 9.58841 15.0225 10.1784 15.8695L17.5196 26.4124C17.8468 26.8825 18.0223 27.4414 18.0223 28.0142V27.9685C18.0223 28.343 17.9096 28.7091 17.6991 29.019"
fill="#FDC433"
/>
</svg>
</SvgIcon>
);
}

export default CodePairIcon;
12 changes: 12 additions & 0 deletions frontend/src/components/layouts/MainLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Stack } from "@mui/material";
import { Outlet } from "react-router-dom";

function MainLayout() {
return (
<Stack sx={{ flexGrow: 1 }} gap={3}>
<Outlet />
</Stack>
);
}

export default MainLayout;
8 changes: 7 additions & 1 deletion frontend/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import "./index.css";
import App from "./App";
import { store } from "./store/store";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { persistStore } from "redux-persist";

const persistor = persistStore(store);

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Provider store={store}>
<App />
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>
);
60 changes: 60 additions & 0 deletions frontend/src/pages/Index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Box, Container, Divider, Grid, Paper, Stack, Typography } from "@mui/material";
import CodePairIcon from "../components/icons/CodePairIcon";
import { GithubLoginButton } from "react-social-login-buttons";

const socialLoginList = [
{
SocailLoginComponent: GithubLoginButton,
provider: "github",
},
];

function Index() {
const handleLogin = (provider: string) => {
window.location.href = `${import.meta.env.VITE_API_ADDR}/auth/login/${provider}`;
};

return (
<Container>
<Stack alignItems="center" justifyContent="center" sx={{ height: "100vh" }}>
<Paper sx={{ p: 5, width: "small", boxShadow: 2, maxWidth: "80%" }}>
<Stack gap={4}>
<Box>
<Stack direction="row" gap={1}>
<CodePairIcon />
<Typography variant="h6">Login</Typography>
</Stack>
<Typography variant="body2" color="text.secondary" maxWidth={320}>
Real-time markdown editor for interviews, meetings and more...
</Typography>
</Box>
<Stack gap={2}>
<Grid container spacing={1} alignItems="center">
<Grid item xs>
<Divider sx={{ width: 1 }} />
</Grid>
<Grid item xs="auto">
<Typography variant="body2" color="text.secondary">
Login with
</Typography>
</Grid>
<Grid item xs>
<Divider sx={{ width: 1 }} />
</Grid>
</Grid>
{socialLoginList.map(({ SocailLoginComponent, provider }) => (
<SocailLoginComponent
key={provider}
size="48px"
onClick={() => handleLogin(provider)}
/>
))}
</Stack>
</Stack>
</Paper>
</Stack>
</Container>
);
}

export default Index;
26 changes: 26 additions & 0 deletions frontend/src/pages/auth/callback/Index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Box } from "@mui/material";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { useNavigate, useSearchParams } from "react-router-dom";
import { setAccessToken } from "../../../store/authSlice";

function CallbackIndex() {
const dispatch = useDispatch();
const navigate = useNavigate();
const [searchParams] = useSearchParams();

useEffect(() => {
const token = searchParams.get("token");

if (!token) {
navigate("/");
return;
}

dispatch(setAccessToken(token));
}, [dispatch, navigate, searchParams]);

return <Box></Box>;
}

export default CallbackIndex;
27 changes: 27 additions & 0 deletions frontend/src/store/authSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createSlice } from "@reduxjs/toolkit";
import type { PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "./store";

export interface ConfigState {
accessToken: string | null;
}

const initialState: ConfigState = {
accessToken: null,
};

export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
setAccessToken: (state, action: PayloadAction<string | null>) => {
state.accessToken = action.payload;
},
},
});

export const { setAccessToken } = authSlice.actions;

export const selectConfig = (state: RootState) => state.config;

export default authSlice.reducer;
Loading

0 comments on commit 01317fb

Please sign in to comment.