mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 21:33:10 +01:00
Login and register functionality
This commit is contained in:
@@ -1,25 +1,33 @@
|
|||||||
import { BrowserRouter, Switch, Route } from 'react-router-dom';
|
import { Fragment, useContext, useEffect } from 'react';
|
||||||
|
import { Switch, Route } from 'react-router-dom';
|
||||||
import { Navbar } from './components/Navigation/Navbar';
|
import { Navbar } from './components/Navigation/Navbar';
|
||||||
import { Editor, Home, Snippet, Snippets, Auth } from './containers';
|
import { Editor, Home, Snippet, Snippets, Auth } from './containers';
|
||||||
import { AuthContextProvider, SnippetsContextProvider } from './store';
|
|
||||||
import { ProtectedRoute } from './utils';
|
import { ProtectedRoute } from './utils';
|
||||||
|
import { AuthContext } from './store';
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
|
const { autoLogin } = useContext(AuthContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// autoLogin();
|
||||||
|
// const checker = setInterval(() => {
|
||||||
|
// autoLogin();
|
||||||
|
// console.log('cake');
|
||||||
|
// }, 1000);
|
||||||
|
// return () => window.clearInterval(checker);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<Fragment>
|
||||||
<AuthContextProvider>
|
<Navbar />
|
||||||
<SnippetsContextProvider>
|
<Switch>
|
||||||
<Navbar />
|
<Route exact path='/' component={Home} />
|
||||||
<Switch>
|
<Route path='/snippets' component={Snippets} />
|
||||||
<Route exact path='/' component={Home} />
|
<Route path='/snippet/:id' component={Snippet} />
|
||||||
<Route path='/snippets' component={Snippets} />
|
<Route path='/snippet/:id' component={Snippet} />
|
||||||
<Route path='/snippet/:id' component={Snippet} />
|
<Route path='/auth' component={Auth} />
|
||||||
<Route path='/snippet/:id' component={Snippet} />
|
<ProtectedRoute path='/editor/:id?' component={Editor} />
|
||||||
<Route path='/auth' component={Auth} />
|
</Switch>
|
||||||
<ProtectedRoute path='/editor/:id?' component={Editor} />
|
</Fragment>
|
||||||
</Switch>
|
|
||||||
</SnippetsContextProvider>
|
|
||||||
</AuthContextProvider>
|
|
||||||
</BrowserRouter>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Button } from '../UI';
|
|||||||
export const AuthForm = (): JSX.Element => {
|
export const AuthForm = (): JSX.Element => {
|
||||||
const [isInLogin, setIsInLogin] = useState(true);
|
const [isInLogin, setIsInLogin] = useState(true);
|
||||||
const [formData, setFormData] = useState({ email: '', password: '' });
|
const [formData, setFormData] = useState({ email: '', password: '' });
|
||||||
const { login } = useContext(AuthContext);
|
const { login, register } = useContext(AuthContext);
|
||||||
|
|
||||||
const inputHandler = (e: ChangeEvent<HTMLInputElement>) => {
|
const inputHandler = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
@@ -21,7 +21,7 @@ export const AuthForm = (): JSX.Element => {
|
|||||||
if (isInLogin) {
|
if (isInLogin) {
|
||||||
login(formData);
|
login(formData);
|
||||||
} else {
|
} else {
|
||||||
// register
|
register(formData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Card, Layout } from '../components/UI';
|
|||||||
export const Auth = (): JSX.Element => {
|
export const Auth = (): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<div className='col-12 col-md-6 mx-auto'>
|
<div className='col-12 col-md-8 mx-auto'>
|
||||||
<Card>
|
<Card>
|
||||||
<AuthForm />
|
<AuthForm />
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './styles/style.scss';
|
import './styles/style.scss';
|
||||||
|
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import { AuthContextProvider, SnippetsContextProvider } from './store';
|
||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<BrowserRouter>
|
||||||
|
<AuthContextProvider>
|
||||||
|
<SnippetsContextProvider>
|
||||||
|
<App />
|
||||||
|
</SnippetsContextProvider>
|
||||||
|
</AuthContextProvider>
|
||||||
|
</BrowserRouter>
|
||||||
</React.StrictMode>,
|
</React.StrictMode>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import axios from 'axios';
|
|
||||||
import { createContext, ReactNode } from 'react';
|
|
||||||
|
|
||||||
import { AuthContext as Context, Response } from '../typescript/interfaces';
|
|
||||||
import { errorHandler } from '../utils';
|
|
||||||
|
|
||||||
export const AuthContext = createContext<Context>({
|
|
||||||
isAuthenticated: false,
|
|
||||||
login: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
children: ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const AuthContextProvider = (props: Props): JSX.Element => {
|
|
||||||
const login = async (formData: { email: string; password: string }) => {
|
|
||||||
try {
|
|
||||||
const res = await axios.post<Response<{ token: string }>>(
|
|
||||||
'/api/auth/login',
|
|
||||||
formData
|
|
||||||
);
|
|
||||||
|
|
||||||
localStorage.setItem('token', res.data.data.token);
|
|
||||||
|
|
||||||
// get profile
|
|
||||||
// redirect to snippets? / home?
|
|
||||||
} catch (err) {
|
|
||||||
errorHandler(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const context: Context = {
|
|
||||||
isAuthenticated: false,
|
|
||||||
login
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AuthContext.Provider value={context}>
|
|
||||||
{props.children}
|
|
||||||
</AuthContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
2
client/src/store/auth/actions/index.ts
Normal file
2
client/src/store/auth/actions/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './registerUser';
|
||||||
|
export * from './loginUser';
|
||||||
39
client/src/store/auth/actions/loginUser.ts
Normal file
39
client/src/store/auth/actions/loginUser.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
|
||||||
|
import { errorHandler } from '../../../utils';
|
||||||
|
import axios from 'axios';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
formData: {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
setIsAuthenticated: (v: React.SetStateAction<boolean>) => void;
|
||||||
|
setUser: (v: React.SetStateAction<UserWithRole | null>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loginUser = async (params: Params) => {
|
||||||
|
const { formData, setIsAuthenticated, setUser } = params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post<Response<{ token: string; user: User }>>(
|
||||||
|
'/api/auth/login',
|
||||||
|
formData
|
||||||
|
);
|
||||||
|
|
||||||
|
const { token: resToken, user: resUser } = res.data.data;
|
||||||
|
|
||||||
|
setUser({
|
||||||
|
...resUser,
|
||||||
|
isAdmin: resUser.role === 'admin'
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem('token', resToken);
|
||||||
|
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
|
||||||
|
// redirect to snippets? / home?
|
||||||
|
} catch (err) {
|
||||||
|
errorHandler(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
37
client/src/store/auth/actions/registerUser.ts
Normal file
37
client/src/store/auth/actions/registerUser.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
|
||||||
|
import { errorHandler } from '../../../utils';
|
||||||
|
import axios from 'axios';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
formData: {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
setIsAuthenticated: (v: React.SetStateAction<boolean>) => void;
|
||||||
|
setUser: (v: React.SetStateAction<UserWithRole | null>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const registerUser = async (params: Params) => {
|
||||||
|
const { formData, setIsAuthenticated, setUser } = params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await axios.post<Response<{ token: string; user: User }>>(
|
||||||
|
'/api/auth/register',
|
||||||
|
formData
|
||||||
|
);
|
||||||
|
|
||||||
|
const { token: resToken, user: resUser } = res.data.data;
|
||||||
|
|
||||||
|
setUser({
|
||||||
|
...resUser,
|
||||||
|
isAdmin: resUser.role === 'admin'
|
||||||
|
});
|
||||||
|
|
||||||
|
localStorage.setItem('token', resToken);
|
||||||
|
|
||||||
|
setIsAuthenticated(true);
|
||||||
|
} catch (err) {
|
||||||
|
errorHandler(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
71
client/src/store/auth/index.tsx
Normal file
71
client/src/store/auth/index.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { createContext, ReactNode, useState } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import { loginUser, registerUser } from './actions';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AuthContext as Context,
|
||||||
|
Response,
|
||||||
|
User,
|
||||||
|
UserWithRole
|
||||||
|
} from '../../typescript/interfaces';
|
||||||
|
import { errorHandler } from '../../utils';
|
||||||
|
|
||||||
|
export const AuthContext = createContext<Context>({
|
||||||
|
isAuthenticated: false,
|
||||||
|
user: null,
|
||||||
|
autoLogin: () => {},
|
||||||
|
login: () => {},
|
||||||
|
register: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AuthContextProvider = (props: Props): JSX.Element => {
|
||||||
|
const [user, setUser] = useState<UserWithRole | null>(null);
|
||||||
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
|
|
||||||
|
const autoLogin = async () => {
|
||||||
|
if (localStorage.token) {
|
||||||
|
await getProfile(localStorage.token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = async (formData: { email: string; password: string }) => {
|
||||||
|
await loginUser({ formData, setIsAuthenticated, setUser });
|
||||||
|
};
|
||||||
|
|
||||||
|
const register = async (formData: { email: string; password: string }) => {
|
||||||
|
await registerUser({ formData, setIsAuthenticated, setUser });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getProfile = async (token: string) => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get<Response<User>>('/api/auth/me', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(res.data.data);
|
||||||
|
} catch (err) {
|
||||||
|
errorHandler(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const context: Context = {
|
||||||
|
isAuthenticated,
|
||||||
|
user,
|
||||||
|
autoLogin,
|
||||||
|
login,
|
||||||
|
register
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider value={context}>
|
||||||
|
{props.children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './SnippetsContext';
|
export * from './SnippetsContext';
|
||||||
export * from './AuthContext';
|
export * from './auth';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TagCount, NewSnippet, Snippet, SearchQuery } from '.';
|
import { TagCount, NewSnippet, Snippet, SearchQuery, UserWithRole } from '.';
|
||||||
|
|
||||||
export interface SnippetsContext {
|
export interface SnippetsContext {
|
||||||
snippets: Snippet[];
|
snippets: Snippet[];
|
||||||
@@ -18,5 +18,8 @@ export interface SnippetsContext {
|
|||||||
|
|
||||||
export interface AuthContext {
|
export interface AuthContext {
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
|
user: UserWithRole | null;
|
||||||
|
autoLogin: () => void;
|
||||||
login: (formData: { email: string; password: string }) => void;
|
login: (formData: { email: string; password: string }) => void;
|
||||||
|
register: (formData: { email: string; password: string }) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,9 @@ import { Model } from '.';
|
|||||||
|
|
||||||
export interface User extends Model {
|
export interface User extends Model {
|
||||||
email: string;
|
email: string;
|
||||||
|
role: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserWithRole extends Model {
|
||||||
|
isAdmin: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/controllers/auth/createUser.ts
Normal file
51
src/controllers/auth/createUser.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { UserInstance, UserModel } from '../../models';
|
||||||
|
import { hashPassword, signToken } from '../../utils';
|
||||||
|
|
||||||
|
interface RequestBody {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponseBody {
|
||||||
|
data: {
|
||||||
|
user: Omit<UserInstance, 'password'>;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Create new user
|
||||||
|
* @route /api/auth
|
||||||
|
* @request POST
|
||||||
|
* @access Public
|
||||||
|
*/
|
||||||
|
export const createUser = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: Request<{}, {}, RequestBody>,
|
||||||
|
res: Response<ResponseBody>,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const password = await hashPassword(req.body.password);
|
||||||
|
|
||||||
|
await UserModel.create({
|
||||||
|
...req.body,
|
||||||
|
password
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = (await UserModel.findOne({
|
||||||
|
where: { email: req.body.email },
|
||||||
|
attributes: { exclude: ['password'] }
|
||||||
|
})) as UserInstance;
|
||||||
|
|
||||||
|
const token = signToken({ email: req.body.email });
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
data: {
|
||||||
|
user,
|
||||||
|
token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
3
src/controllers/auth/index.ts
Normal file
3
src/controllers/auth/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './createUser';
|
||||||
|
export * from './loginUser';
|
||||||
|
export * from './getProfile';
|
||||||
60
src/controllers/auth/loginUser.ts
Normal file
60
src/controllers/auth/loginUser.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import { asyncWrapper } from '../../middleware';
|
||||||
|
import { UserInstance, UserModel } from '../../models';
|
||||||
|
import { ErrorResponse, signToken } from '../../utils';
|
||||||
|
import { compare } from 'bcrypt';
|
||||||
|
|
||||||
|
interface RequestBody {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponseBody {
|
||||||
|
data: {
|
||||||
|
user: Omit<UserInstance, 'password'>;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description User login
|
||||||
|
* @route /api/auth/login
|
||||||
|
* @request POST
|
||||||
|
* @access Public
|
||||||
|
*/
|
||||||
|
export const loginUser = asyncWrapper(
|
||||||
|
async (
|
||||||
|
req: Request<{}, {}, RequestBody>,
|
||||||
|
res: Response<ResponseBody>,
|
||||||
|
next: NextFunction
|
||||||
|
): Promise<void> => {
|
||||||
|
const { email, password } = req.body;
|
||||||
|
|
||||||
|
// Find user
|
||||||
|
let user = await UserModel.findOne({ where: { email }, raw: true });
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
// Check password
|
||||||
|
const passwordMatch = await compare(password, user.password);
|
||||||
|
|
||||||
|
if (passwordMatch) {
|
||||||
|
// Sign token
|
||||||
|
const token = await signToken({ email });
|
||||||
|
|
||||||
|
user = (await UserModel.findOne({
|
||||||
|
where: { email },
|
||||||
|
attributes: { exclude: ['password'] },
|
||||||
|
raw: true
|
||||||
|
})) as UserInstance;
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
data: { token, user }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return next(new ErrorResponse(400, 'Invalid credentials'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return next(new ErrorResponse(400, 'Invalid credentials'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
type Foo = (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
type Foo = (req: any, res: Response, next: NextFunction) => Promise<void>;
|
||||||
|
|
||||||
export const asyncWrapper =
|
export const asyncWrapper =
|
||||||
(foo: Foo) => (req: Request, res: Response, next: NextFunction) => {
|
(foo: Foo) => (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
|||||||
46
src/middleware/authenticate.ts
Normal file
46
src/middleware/authenticate.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
import { asyncWrapper } from '.';
|
||||||
|
import { ErrorResponse } from '../utils';
|
||||||
|
import { verify } from 'jsonwebtoken';
|
||||||
|
import { Token, UserInfoRequest } from '../typescript/interfaces';
|
||||||
|
import { UserModel } from '../models';
|
||||||
|
|
||||||
|
export const authenticate = asyncWrapper(
|
||||||
|
async (req: UserInfoRequest, res: Response, next: NextFunction) => {
|
||||||
|
let token: string | null = null;
|
||||||
|
|
||||||
|
// Check if token was provided
|
||||||
|
if (req.headers.authorization) {
|
||||||
|
if (req.headers.authorization.startsWith('Bearer ')) {
|
||||||
|
token = req.headers.authorization.split(' ')[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
const secret = process.env.JWT_SECRET || 'secret';
|
||||||
|
|
||||||
|
// Decode token and extract data
|
||||||
|
const decoded = verify(token, secret) as Token;
|
||||||
|
|
||||||
|
// Find user
|
||||||
|
const user = await UserModel.findOne({
|
||||||
|
where: { email: decoded.email },
|
||||||
|
attributes: { exclude: ['password'] },
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
req.user = {
|
||||||
|
...user,
|
||||||
|
isAdmin: user.role == 'admin'
|
||||||
|
};
|
||||||
|
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
return next(new ErrorResponse(401, 'Not authorized'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return next(new ErrorResponse(401, 'Not authorized'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './asyncWrapper';
|
export * from './asyncWrapper';
|
||||||
export * from './requireBody';
|
export * from './requireBody';
|
||||||
export * from './errorHandler';
|
export * from './errorHandler';
|
||||||
|
export * from './authenticate';
|
||||||
|
|||||||
13
src/routes/auth.ts
Normal file
13
src/routes/auth.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import { createUser, getProfile, loginUser } from '../controllers/auth';
|
||||||
|
import { authenticate, requireBody } from '../middleware';
|
||||||
|
|
||||||
|
export const authRouter = Router();
|
||||||
|
|
||||||
|
authRouter
|
||||||
|
.route('/register')
|
||||||
|
.post(requireBody('email', 'password'), createUser);
|
||||||
|
|
||||||
|
authRouter.route('/login').post(requireBody('email', 'password'), loginUser);
|
||||||
|
|
||||||
|
authRouter.route('/me').get(authenticate, getProfile);
|
||||||
2
src/routes/index.ts
Normal file
2
src/routes/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './snippets';
|
||||||
|
export * from './auth';
|
||||||
@@ -6,7 +6,7 @@ import { connectDB } from './db';
|
|||||||
import { errorHandler } from './middleware';
|
import { errorHandler } from './middleware';
|
||||||
|
|
||||||
// Routers
|
// Routers
|
||||||
import { snippetRouter } from './routes/snippets';
|
import { snippetRouter, authRouter } from './routes';
|
||||||
import { associateModels } from './db/associateModels';
|
import { associateModels } from './db/associateModels';
|
||||||
|
|
||||||
// Env config
|
// Env config
|
||||||
@@ -27,6 +27,7 @@ app.get(/^\/(?!api)/, (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.use('/api/snippets', snippetRouter);
|
app.use('/api/snippets', snippetRouter);
|
||||||
|
app.use('/api/auth', authRouter);
|
||||||
|
|
||||||
// Error handler
|
// Error handler
|
||||||
app.use(errorHandler);
|
app.use(errorHandler);
|
||||||
|
|||||||
10
src/typescript/interfaces/Request.ts
Normal file
10
src/typescript/interfaces/Request.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
|
export interface UserInfoRequest<body = {}> extends Request<{}, {}, body> {
|
||||||
|
user: {
|
||||||
|
id: number;
|
||||||
|
email: string;
|
||||||
|
role: string;
|
||||||
|
isAdmin: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
3
src/typescript/interfaces/Token.ts
Normal file
3
src/typescript/interfaces/Token.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export interface Token {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
@@ -5,3 +5,5 @@ export * from './Snippet_Tag';
|
|||||||
export * from './Body';
|
export * from './Body';
|
||||||
export * from './SearchQuery';
|
export * from './SearchQuery';
|
||||||
export * from './User';
|
export * from './User';
|
||||||
|
export * from './Token';
|
||||||
|
export * from './Request';
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { sign } from 'jsonwebtoken';
|
import { sign } from 'jsonwebtoken';
|
||||||
|
import { Token } from '../typescript/interfaces';
|
||||||
|
|
||||||
type Data = string | object | Buffer;
|
export const signToken = (data: Token): string => {
|
||||||
|
const secret = process.env.JWT_SECRET || 'secret';
|
||||||
export const signToken = (data: Data): string => {
|
|
||||||
const secret =
|
|
||||||
process.env.JWT_SECRET || 'x7-joXEF89Q5hUx9Od5mibNVQb9vUuLr1091TMZSM-w';
|
|
||||||
|
|
||||||
const token = sign(data, secret, {
|
const token = sign(data, secret, {
|
||||||
expiresIn: '30d'
|
expiresIn: '30d'
|
||||||
|
|||||||
Reference in New Issue
Block a user