Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(FE) Add User registration / Login #69

Merged
merged 12 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading