Commit cfdb0bad authored by Nguyễn Hải Sơn's avatar Nguyễn Hải Sơn

Update base

parent 91c68eaa
...@@ -4,4 +4,4 @@ ...@@ -4,4 +4,4 @@
# Prisma supports the native connection string format for PostgreSQL, MySQL and SQLite. # Prisma supports the native connection string format for PostgreSQL, MySQL and SQLite.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings # See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="mysql://qldh:qldh@2021!wGqvF9DV4W@localhost:3308/qldh" DATABASE_URL="mysql://user:password@host:port/dbname"
\ No newline at end of file \ No newline at end of file
node_modules/ node_modules/
dist/ dist/
dist
*.log *.log
.env
import { AppConfig } from './configurations/app-config';
import express from 'express';
import meetingRoom from './routes/meeting-rooms';
import device from './routes/device';
const app = express();
app.use('/devices', device);
app.use('/meeting-rooms', meetingRoom);
app.listen(AppConfig.PORT, () => console.log(`Server is running on port ${AppConfig.PORT}`));
import { Request } from 'express';
import log4js from 'log4js'; import log4js from 'log4js';
const isProduction = process.env.NODE_ENV === 'production'; const isProduction = process.env.NODE_ENV === 'production';
console.log(isProduction); console.log(isProduction);
...@@ -18,4 +19,22 @@ log4js.configure({ ...@@ -18,4 +19,22 @@ log4js.configure({
}, },
}, },
}); });
export const logError = (
req: Request,
logMsg: string,
errorMsg: string,
errorStack?: string,
isFatal: boolean = false,
) => {
logger[isFatal ? 'fatal' : 'error'](`[${req.requestId}][${logMsg}]: `, errorStack);
logger.info(`[${req.requestId}][${logMsg}]: `, errorMsg);
logger.info(`[${req.requestId}][${logMsg}][BODY REQ]: `, {
body: req.body,
params: req.params,
query: req.query,
headers: req.headers,
});
};
export const logger = log4js.getLogger('log'); export const logger = log4js.getLogger('log');
import { AppConfig } from './../configurations/app-config';
import { createPool } from 'mysql';
export const DB_POOL = createPool({
connectionLimit: AppConfig.DB_CONNECTION_AMOUNT,
host: AppConfig.DB_HOST,
user: AppConfig.DB_USER,
password: AppConfig.DB_PASSWORD,
database: AppConfig.DB_DATABASE,
port: AppConfig.DB_PORT,
});
export enum DB_Tables {
device = 'device',
meetingRoom = 'room',
}
export enum queryErrorCode { export const queryErrorCode: Record<string, string> = {
P2000 = '', P2000: '',
P2001 = '', P2001: '',
P2002 = '', P2002: '',
P2003 = '', P2003: '',
P2004 = '', P2004: '',
P2005 = '', P2005: '',
P2006 = '', P2006: '',
P2007 = '', P2007: '',
P2008 = '', P2008: '',
P2009 = '', P2009: '',
P2010 = '', P2010: '',
P2011 = '', P2011: '',
P2012 = '', P2012: '',
P2013 = '', P2013: '',
P2014 = '', P2014: '',
P2015 = '', P2015: '',
P2016 = '', P2016: '',
P2017 = '', P2017: '',
P2018 = '', P2018: '',
P2019 = '', P2019: '',
P2020 = '', P2020: '',
P2021 = '', P2021: '',
P2022 = '', P2022: '',
P2023 = '', P2023: '',
P2024 = '', P2024: '',
P2025 = '', P2025: '',
} P3000: '',
P3001: '',
P3002: '',
P3003: '',
P3004: '',
P3005: '',
P3006: '',
P3007: '',
P3008: '',
P3009: '',
P3010: '',
P3011: '',
P3012: '',
P3013: '',
P3014: '',
P3015: '',
P1000: '',
P1001: '',
P1002: '',
P1003: '',
P1004: '',
P1005: '',
P1006: '',
P1007: '',
P1008: '',
P1009: '',
P1010: '',
P1011: '',
P1012: '',
P1013: '',
P1014: '',
P1015: '',
P4000: '',
P4001: '',
P4002: '',
};
export enum ResponseMessages { export enum ResponseMessages {
UNAUTHORIZED = 'Vui lòng đăng nhập', UNAUTHORIZED = 'Vui lòng đăng nhập',
FORBIDDEN = 'Không được phép truy cập', FORBIDDEN = 'Không được phép truy cập',
DB_FATAL_ERROR = 'Database fatal error. Contact system administrator asap.',
QUERY_ENGINE_START_ERROR = 'Cannot initialize query engine.',
QUERY_UNKNOWN_ERROR = 'Unknown query error',
QUERY_VALIDATION_ERROR = 'Query validation error',
} }
import { DB_POOL } from './../../database/qldh-mysql';
export const getDemoData = () => {
return new Promise<Record<string, unknown>>((resolve, reject) => {
DB_POOL.query('SELECT 1 + 1 AS solution', (error, results, fields) => {
if (error) {
reject({ error });
} else {
resolve({ results });
}
});
});
};
import { DB_POOL } from '../../database/qldh-mysql';
import { Device } from '../../models/Device';
export const getListDevice = (pageIndex: number = 1, pageSize: number = 10, keyword: string) => {
console.log(pageIndex, pageSize);
return new Promise<Array<Device>>((resolve, reject) => {
let query = 'select * from device';
if (keyword) {
query += ` where name like ${DB_POOL.escape(`%${keyword}%`)}`;
}
query += ` limit ${DB_POOL.escape((pageIndex - 1) * pageSize)}, ${DB_POOL.escape(pageSize)}`;
console.log(query);
DB_POOL.query(query, (error, results: Array<Device>) => {
if (error) {
reject(error);
} else {
resolve(results);
}
});
});
};
...@@ -3,7 +3,7 @@ import { AppConfig } from './../../configurations/app-config'; ...@@ -3,7 +3,7 @@ import { AppConfig } from './../../configurations/app-config';
import { sign, verify } from 'jsonwebtoken'; import { sign, verify } from 'jsonwebtoken';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { errorBody } from '../../response/error-body'; import { errorBody } from '../../response/error-body';
import { ResponseMessages } from '../../response/response-messages'; import { ResponseMessages } from '../../enums/response-messages';
import { account_cms } from '@prisma/client'; import { account_cms } from '@prisma/client';
export const generateAccessToken = (user: account_cms) => { export const generateAccessToken = (user: account_cms) => {
......
import { DB_POOL, DB_Tables } from '../../database/qldh-mysql';
import { FilterMeetingRoomOptions, MeetingRoom } from '../../models/MeetingRoom';
export const getMeetingRooms = (pageIndex: number, pageSize: number, Options?: FilterMeetingRoomOptions) => {
let query = `select * from ${DB_Tables.meetingRoom}`;
query += ` limit ${DB_POOL.escape((pageIndex - 1) * pageSize)}, ${DB_POOL.escape(pageSize)}`;
console.log(query);
return new Promise<Array<MeetingRoom>>((resolve, reject) => {
DB_POOL.query(query, (error, results: Array<MeetingRoom>, fields) => {
if (error) {
reject(error);
} else {
resolve(results);
}
});
});
};
import { Prisma } from '@prisma/client';
import { Request, Response, Router } from 'express';
import { validationResult } from 'express-validator';
import { ReasonPhrases } from 'http-status-codes';
import { logError } from '../../configurations/log4js.config';
import {
databaseFatalResponse,
internalServerErrorResponse,
queryErrorResponse,
queryStartErrorResponse,
queryUnknownErrorResponse,
queryValidationErrorResponse,
validationErrorResponse,
} from '../../response/error-body';
export const processRequest = async (
req: Request,
res: Response,
processorLogic: () => Promise<void | Response<any, Record<string, any>>>,
) => {
try {
const validationErrors = validationResult(req);
if (validationErrors.isEmpty()) {
const processed = await processorLogic();
return processed;
} else {
return validationErrorResponse(req, res, validationErrors);
}
} catch (error) {
return processRequestError(req, res, error);
}
};
export const processRequestError = (req: Request, res: Response, error: any) => {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
return queryErrorResponse(req, res, error);
} else if (error instanceof Prisma.PrismaClientRustPanicError) {
return databaseFatalResponse(req, res, error);
} else if (error instanceof Prisma.PrismaClientInitializationError) {
return queryStartErrorResponse(req, res, error);
} else if (error instanceof Prisma.PrismaClientUnknownRequestError) {
return queryUnknownErrorResponse(req, res, error);
} else if (error instanceof Prisma.PrismaClientValidationError) {
return queryValidationErrorResponse(req, res, error);
} else {
logError(req, ReasonPhrases.INTERNAL_SERVER_ERROR, ReasonPhrases.INTERNAL_SERVER_ERROR, error.stack, true);
return internalServerErrorResponse(req, res, ReasonPhrases.INTERNAL_SERVER_ERROR);
}
};
export type Device = {
id: number;
code?: string;
name: string;
model?: string;
image?: string;
status?: number;
create_time?: Date;
create_by?: string;
update_time?: Date;
update_by?: string;
};
import { Device } from './Device';
export type FilterMeetingRoomOptions = {
status?: number;
name?: string;
capacity?: number;
devices?: Array<Device>;
};
export type MeetingRoom = {
id: number;
code?: string;
name: string;
description?: string;
capacity: number;
floor: number;
building: number;
status?: number;
};
...@@ -2,8 +2,9 @@ import { Prisma } from '@prisma/client'; ...@@ -2,8 +2,9 @@ import { Prisma } from '@prisma/client';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { Result, ValidationError } from 'express-validator'; import { Result, ValidationError } from 'express-validator';
import { ReasonPhrases, StatusCodes } from 'http-status-codes'; import { ReasonPhrases, StatusCodes } from 'http-status-codes';
import { logger } from '../../configurations/log4js.config'; import { logError, logger } from '../../configurations/log4js.config';
import { ValidatorMessage } from '../../validators/validator-config'; import { queryErrorCode } from '../../enums/common';
import { ResponseMessages } from '../../enums/response-messages';
export const errorBody = (data: unknown, code: number, requestId: string, message?: string) => { export const errorBody = (data: unknown, code: number, requestId: string, message?: string) => {
return { return {
...@@ -16,17 +17,48 @@ export const errorBody = (data: unknown, code: number, requestId: string, messag ...@@ -16,17 +17,48 @@ export const errorBody = (data: unknown, code: number, requestId: string, messag
}; };
}; };
export const internalServerErrorResponse = (req: Request, res: Response, data: unknown) => {
return res
.status(StatusCodes.INTERNAL_SERVER_ERROR)
.send(errorBody(data, StatusCodes.INTERNAL_SERVER_ERROR, req.requestId, ReasonPhrases.INTERNAL_SERVER_ERROR));
};
export const queryErrorResponse = (req: Request, res: Response, error: Prisma.PrismaClientKnownRequestError) => { export const queryErrorResponse = (req: Request, res: Response, error: Prisma.PrismaClientKnownRequestError) => {
logger.error(`[${req.requestId}][QUERY ERROR ${error.code}]: `, error.stack); logError(req, `QUERY ERROR ${error.code}`, error.message, error.stack);
logger.info(`[${req.requestId}][QUERY ERROR ${error.code}]: `, error.message); return internalServerErrorResponse(req, res, queryErrorCode[error.code]);
res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(
errorBody(error.message, StatusCodes.INTERNAL_SERVER_ERROR, req.requestId, ReasonPhrases.INTERNAL_SERVER_ERROR),
);
}; };
export const validationErrorResponse = (req: Request, res: Response, errors: Result<ValidationError>) => { export const validationErrorResponse = (req: Request, res: Response, errors: Result<ValidationError>) => {
const errorArray = errors.array(); const errorArray = errors.array();
res.status(StatusCodes.UNPROCESSABLE_ENTITY).send( return res
errorBody(errorArray, StatusCodes.UNPROCESSABLE_ENTITY, errorArray[0].msg), .status(StatusCodes.UNPROCESSABLE_ENTITY)
); .send(errorBody(errorArray, StatusCodes.UNPROCESSABLE_ENTITY, errorArray[0].msg));
};
export const databaseFatalResponse = (req: Request, res: Response, error: Prisma.PrismaClientRustPanicError) => {
logError(req, 'DATABASE FATAL ERROR', error.message, error.stack, true);
return internalServerErrorResponse(req, res, ResponseMessages.DB_FATAL_ERROR);
};
export const queryStartErrorResponse = (req: Request, res: Response, error: Prisma.PrismaClientInitializationError) => {
logError(req, 'QUERY ENGINE START ERROR', error.message, error.stack);
return internalServerErrorResponse(req, res, ResponseMessages.QUERY_ENGINE_START_ERROR);
};
export const queryUnknownErrorResponse = (
req: Request,
res: Response,
error: Prisma.PrismaClientUnknownRequestError,
) => {
logError(req, 'QUERY UNKNOWN ERROR', error.message, error.stack);
return internalServerErrorResponse(req, res, ResponseMessages.QUERY_UNKNOWN_ERROR);
};
export const queryValidationErrorResponse = (
req: Request,
res: Response,
error: Prisma.PrismaClientValidationError,
) => {
logError(req, 'QUERY VALIDATION ERROR', error.message, error.stack);
return internalServerErrorResponse(req, res, ResponseMessages.QUERY_VALIDATION_ERROR);
}; };
import { Router } from 'express';
import { StatusCodes, ReasonPhrases } from 'http-status-codes';
import { getDemoData } from '../functions/demo-functions/demo-api';
const router = Router();
router.get('/', async (req, res) => {
try {
const data = await getDemoData();
res.status(StatusCodes.OK).send(data);
} catch (error) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ error });
}
});
export default router;
import { Request, Response, Router } from 'express';
import { validationResult } from 'express-validator';
import { StatusCodes, ReasonPhrases } from 'http-status-codes';
import { getListDevice } from '../../functions/devices';
import { deviceListValidator } from '../../validators/devices';
const route = Router();
route.get('/', deviceListValidator, async (req: Request, res: Response) => {
try {
const errors = validationResult(req);
if (errors.isEmpty()) {
const devices = await getListDevice(
Number(req.query.pageIndex),
Number(req.query.pageSize),
req.query.keyword as string,
);
res.status(StatusCodes.OK).send(devices);
} else {
res.status(StatusCodes.UNPROCESSABLE_ENTITY).send({
errors: errors.array(),
message: ReasonPhrases.UNPROCESSABLE_ENTITY,
});
}
} catch (error) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).send(error);
}
});
export default route;
import { account_cms, Prisma } from '@prisma/client'; import { account_cms } from '@prisma/client';
import { MD5 } from 'crypto-js'; import { MD5 } from 'crypto-js';
import { Request, Response, Router } from 'express'; import { Request, Response, Router } from 'express';
import { validationResult } from 'express-validator';
import { StatusCodes } from 'http-status-codes'; import { StatusCodes } from 'http-status-codes';
import { prisma } from '../..'; import { prisma } from '../..';
import { logger } from '../../configurations/log4js.config'; import { logger } from '../../configurations/log4js.config';
import { generateAccessToken } from '../../functions/jwt-authentication'; import { generateAccessToken } from '../../functions/jwt-authentication';
import { errorBody, queryErrorResponse, validationErrorResponse } from '../../response/error-body'; import { processRequest } from '../../functions/process-request';
import { errorBody } from '../../response/error-body';
import { successResponse } from '../../response/success-body'; import { successResponse } from '../../response/success-body';
import { loginValidator } from '../../validators/login'; import { loginValidator } from '../../validators/login';
import { ValidatorMessage } from '../../validators/validator-config'; import { ValidatorMessage } from '../../validators/validator-config';
...@@ -14,10 +14,9 @@ import { ValidatorMessage } from '../../validators/validator-config'; ...@@ -14,10 +14,9 @@ import { ValidatorMessage } from '../../validators/validator-config';
const loginRouter = Router(); const loginRouter = Router();
loginRouter.post('/', loginValidator, async (req: Request, res: Response) => { loginRouter.post('/', loginValidator, async (req: Request, res: Response) => {
try { const processorLogic = async () => {
const validationErrors = validationResult(req);
if (validationErrors.isEmpty()) {
const reqBody = req.body as account_cms; const reqBody = req.body as account_cms;
const user = await prisma.account_cms.findFirst({ const user = await prisma.account_cms.findFirst({
where: { where: {
user_name: { user_name: {
...@@ -39,7 +38,7 @@ loginRouter.post('/', loginValidator, async (req: Request, res: Response) => { ...@@ -39,7 +38,7 @@ loginRouter.post('/', loginValidator, async (req: Request, res: Response) => {
}; };
return successResponse(req, res, data); return successResponse(req, res, data);
} else { } else {
logger.warn(`[${req.requestId}]: `, req.body); logger.warn(`[${req.requestId}][LOGIN FAILED]: `, req.body);
return res return res
.status(StatusCodes.UNPROCESSABLE_ENTITY) .status(StatusCodes.UNPROCESSABLE_ENTITY)
.send( .send(
...@@ -51,14 +50,9 @@ loginRouter.post('/', loginValidator, async (req: Request, res: Response) => { ...@@ -51,14 +50,9 @@ loginRouter.post('/', loginValidator, async (req: Request, res: Response) => {
), ),
); );
} }
} else { };
return validationErrorResponse(req, res, validationErrors);
} return processRequest(req, res, processorLogic);
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
return queryErrorResponse(req, res, error);
}
}
}); });
export { loginRouter }; export { loginRouter };
import { Router } from 'express';
import { ReasonPhrases, StatusCodes } from 'http-status-codes';
import { getMeetingRooms } from '../../functions/meeting-rooms';
const router = Router();
router.get('/', async (req, res) => {
try {
const results = await getMeetingRooms(1, 10);
res.status(StatusCodes.OK).send(results);
} catch (error) {
res.status(StatusCodes.INTERNAL_SERVER_ERROR).send({
error,
message: ReasonPhrases.INTERNAL_SERVER_ERROR,
});
}
});
export default router;
import { check } from 'express-validator';
export const deviceListValidator = [
check('pageIndex', 'Please provide pageIndex').isNumeric(),
check('pageSize', 'Please provide pageSize').isNumeric(),
check('keyword', 'Please provide keyword').optional().isString(),
];
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment