Skip to content

Commit

Permalink
🐞 FIX: refresh token and middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
hyper-dot committed Oct 12, 2024
1 parent 6c3d437 commit 82d5d6e
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/app/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class AuthController {
refresh = asyncWrapper(async (req, res) => {
const { refreshToken } = req.body;
const accessToken = this.service.refreshAccessToken(refreshToken);

return res.json({
message: 'Token refreshed successfully',
data: { accessToken },
Expand Down
13 changes: 7 additions & 6 deletions src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs'; // For password hashing
import bcrypt from 'bcryptjs';
import {
BadRequestError,
ForbiddenError,
Expand Down Expand Up @@ -36,7 +36,7 @@ export class AuthService {

generateRefreshToken(user: User) {
return jwt.sign(
{ id: user.id, email: user.email },
{ id: user.id, email: user.email, role: user.role },
this.refreshTokenSecret,
{ expiresIn: this.refreshTokenExpiry },
);
Expand All @@ -60,13 +60,14 @@ export class AuthService {

refreshAccessToken(refreshToken: string) {
try {
const decoded: any = this.verifyRefreshToken(refreshToken);
const user: any = this.verifyRefreshToken(refreshToken);
return this.generateAccessToken({
id: decoded.id,
email: decoded.email,
role: decoded.role || 'user',
id: user.id,
email: user.email,
role: user.role || 'user',
});
} catch (err) {
console.log(err);
throw new BadRequestError('Could not refresh access token');
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import swaggerUi from 'swagger-ui-express';
import { swaggerConfig } from '../configs/swagger';
import { otpRoutes } from './otp/opt.route';
import { authRoutes } from './auth/auth.routes';
import productRoutes from './sales/product/product.routes';
import { isAuthencticated } from '../middleware';

export class App {
public app: Application;
Expand All @@ -35,22 +37,23 @@ export class App {
}

private setRoutes() {
this.app.use('/user', userRoutes);
this.app.use('/auth', authRoutes);
this.app.use('/otp', otpRoutes);

if (process.env.ENV === 'development') {
this.app.use('*', (req, res, next) => {
this.app.use('*', (req, _, next) => {
console.log('BODY', req.body);
console.log('QUERY', req.query);
next();
});
this.app.use('/user', userRoutes);
this.app.use('/auth', authRoutes);
this.app.use('/otp', otpRoutes);
this.app.use('/product', isAuthencticated, productRoutes);

this.app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerConfig));
}
}

private handleErrors() {
this.app.get('*', () => {
this.app.use('*', () => {
throw new NotFoundError();
});

Expand Down
15 changes: 15 additions & 0 deletions src/app/sales/product/product.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { asyncWrapper } from '../../../utils/wrapper';
import { ProductService } from './product.service';

export class ProductController {
private service: ProductService;

constructor() {
this.service = new ProductService();
}

addnewProduct = asyncWrapper(async (req, res) => {
await this.service.addNewProduct(req.body);
return res.json({ message: 'Product created successfully' });
});
}
Empty file.
44 changes: 44 additions & 0 deletions src/app/sales/product/product.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Schema, model } from 'mongoose';

// Define the schema for the Product
const productSchema = new Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User', // Reference to the User model
required: true,
},
batchNo: {
type: String,
required: true,
},
qty: {
type: Number,
min: 0,
default: 0,
},
reorderQty: {
type: Number,
min: 0,
default: 0,
},
salesPrice: {
type: Number,
required: true,
min: 0,
},
costPrice: {
type: Number,
required: true,
min: 0,
},
},
{
timestamps: true,
},
);

// Create the Product model
const ProductModel = model('Product', productSchema);

export default ProductModel;
21 changes: 21 additions & 0 deletions src/app/sales/product/product.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Router } from 'express';
import { ProductController } from './product.controller';

class ProductRouter {
public router: Router;

private controller: ProductController;

constructor() {
this.router = Router();
this.controller = new ProductController();
this.mountRoutes();
}

mountRoutes() {
this.router.post('/', this.controller.addnewProduct);
}
}

const productRoutes = new ProductRouter().router;
export default productRoutes;
11 changes: 11 additions & 0 deletions src/app/sales/product/product.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ProductModel from './product.model';
import { productSchema, TProductSchema } from '../../../schema/product.schema';
import { BadRequestError } from '../../../utils/exceptions';
export class ProductService {
async addNewProduct(payload: TProductSchema) {
const { data, success } = productSchema.safeParse(payload);
if (!success) throw new BadRequestError('Invalid payload format');
const newProduct = new ProductModel(data);
await newProduct.save();
}
}
47 changes: 47 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { ForbiddenError, UnauthorizedError } from './utils/exceptions';
import UserModel from './app/user/user.model';

export const isAuthencticated = (
req: Request,
res: Response,
next: NextFunction,
) => {
let token = req.headers.authorization;
if (!token) {
throw new UnauthorizedError('Token is required');
}
if (token.startsWith('Bearer ')) {
token = token.slice(7, token.length).trim();
}

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log('decoded', decoded);
req.params.userId = (decoded as any).userId;
next();
} catch (err) {
console.error(err);
res.status(401).json({ message: 'Invalid or expired token' });
}
};

// export const isAdmin = async (
// req: Request,
// res: Response,
// next: NextFunction,
// ) => {
// try {
// const userId = req.params.userId;
// const user = await UserModel.findById(userId);
// if (user?.isAdmin) {
// next();
// } else {
// throw new ForbiddenError('Only Admins are allowed');
// }
// } catch (err: any) {
// console.error(err);
// res.status(403).json({ message: err.message });
// }
// };
11 changes: 11 additions & 0 deletions src/schema/product.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from 'zod';

export const productSchema = z.object({
name: z.string().min(1),
stock: z.coerce.number().min(0),
reorderLevel: z.coerce.number().min(0),
salesPrice: z.coerce.number().min(0),
costPrice: z.coerce.number().min(0),
});

export type TProductSchema = z.infer<typeof productSchema>;
1 change: 0 additions & 1 deletion src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export function handleError(err: Error): {
if (err instanceof CustomError) {
return { statusCode: err.statusCode, error: err.message };
} else {
console.log(err);
return { statusCode: 500, error: 'Internal Server Error' };
}
}

0 comments on commit 82d5d6e

Please sign in to comment.