diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 25f4ae8..5092176 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -67,7 +67,7 @@ jobs:
# Lint the server code
- name: Lint Server Code
- run: npm run eslint
+ run: npm run lint
working-directory: ./server
# Step: Formatting Frontend job
diff --git a/server/.env.example b/server/.env.example
index 341035b..70d70b1 100644
--- a/server/.env.example
+++ b/server/.env.example
@@ -20,16 +20,23 @@ DATE_FULL_FORMAT='yyyy-MM-dd HH:mm:ss.SSS'
# Database
# YOUR_MONGO_URI
MONGO_URI=mongodb://localhost:27017/
+
# YOUR_MONGO_DB_NAME
MONGO_DB_DATABASE_NAME=tasks-db-v1
+#YOUR_MONGO_DB_USER_NAME
+MONGO_DB_USERNAME=taskApplicationUser
+
+#YOUR_MONGO_DB_USER_PWD
+MONGO_DB_PWD=123456789
+
# Maintain up to x socket connections
-DB_MIN_POOL_SIZE=2
-DB_MAX_POOL_SIZE=5
+MONGO_DB_MIN_POOL_SIZE=2
+MONGO_DB_MAX_POOL_SIZE=5
# Give up initial connection after 10 seconds
-DB_CONNECT_TIMEOUT_MS=60000
+MONGO_DB_CONNECT_TIMEOUT_MS=60000
# Close sockets after 45 seconds of inactivity
-DB_SOCKET_TIMEOUT_MS=45000
+MONGO_DB_SOCKET_TIMEOUT_MS=45000
#localhost or IP of the server
# If using the docker installation then use 'mongo' for host name else localhost or ip or db server
@@ -37,12 +44,6 @@ DB_SOCKET_TIMEOUT_MS=45000
MONGO_DB_HOST=127.0.0.1
MONGO_DB_PORT=27017
-#YOUR_MONGO_DB_USER_NAME
-MONGO_DB_USERNAME=taskApplicationUser
-
-#YOUR_MONGO_DB_USER_PWD
-MONGO_DB_PWD=123456789
-
# Limiter
# The time window for which login attempts are counted
# 2 minutes = 120000
@@ -74,6 +75,9 @@ PASSWORD_RESET_TOKEN_VALIDITY_SEC=3600000
TOKEN_ISSUER=api.dev.saini.com
TOKEN_AUDIENCE=dev.saini.com
+# make the value production for prod env
+MAILTRAP_EMAIL_ENV=testing
+
# Mailtrap(Email service) Info
MAILTRAP_USERNAME=
MAILTRAP_PASSWORD=
diff --git a/server/dockerfile b/server/dockerfile
index 97d396b..4b8660b 100644
--- a/server/dockerfile
+++ b/server/dockerfile
@@ -27,6 +27,7 @@ RUN rm -rf \
.eslintignore \
.prettierignore \
tsconfig.json \
+ tsconfig.prod.json \
.prettierrc \
.eslintrc.json
diff --git a/server/package.json b/server/package.json
index e681351..787874c 100644
--- a/server/package.json
+++ b/server/package.json
@@ -10,9 +10,12 @@
"watch": "npx concurrently -k -p \"[{name}]\" -n \"TypeScript,Node\" -c \"yellow.bold,cyan.bold,green.bold,red.bold\" \"npm run watch-ts\" \"npm run watch-node\"",
"watch-node": "nodemon -r dotenv/config build/server.js",
"clean": "npx rimraf ./build",
- "build-ts": "npx tsc",
+ "build-ts": "npx tsc --project tsconfig.prod.json",
"watch-ts": "npx tsc -w",
- "eslint": "npx eslint . --ext .ts",
+ "lint": "npx eslint . --ext ts --report-unused-disable-directives --max-warnings 0",
+ "lint:fix": "npm run lint --fix",
+ "prettier:write": "npx prettier . --write",
+ "prettier": "npx prettier . --check",
"test": "npx jest --forceExit --detectOpenHandles --coverage --verbose",
"install:packages": "npm i",
"upgrade:packages": "npm update --save-dev && npm update --save",
diff --git a/server/src/app.ts b/server/src/app.ts
index ef477d0..dd230fc 100644
--- a/server/src/app.ts
+++ b/server/src/app.ts
@@ -1,6 +1,6 @@
import express, { NextFunction, Request, Response } from 'express';
import Logger from './middleware/Logger';
-import { API_VERSION, CORS_URL, ENVIRONMENT } from './config';
+import { API_VERSION, CORS_URL, ENVIRONMENT, LIMITER } from './config';
import cors from 'cors';
import helmet from 'helmet';
import './config/DatabaseConfig'; // initialize database
@@ -12,6 +12,7 @@ import {
NotFoundError,
} from './middleware/ApiError';
import routes from './routes/v1';
+import LimiterHelper from './helpers/LimiterHelper';
process.on('uncaughtException', (e) => {
Logger.error(e);
@@ -19,6 +20,15 @@ process.on('uncaughtException', (e) => {
const app = express();
+// Apply rate limiting to all requests
+app.use(
+ LimiterHelper.createRateLimiter({
+ windowMs: LIMITER.ipWS, // 15 minutes
+ max: LIMITER.ipMaxAttempt, // limit each IP to 100 requests per windowMs
+ message: 'Too many requests, please try again later.',
+ })
+);
+
// This middleware is responsible to enable cookie parsing
// commonly used to parse cookies from the incoming HTTP request headers.
app.use(cookieParser());
diff --git a/server/src/config/index.ts b/server/src/config/index.ts
index 2fe7742..1339b3f 100644
--- a/server/src/config/index.ts
+++ b/server/src/config/index.ts
@@ -3,6 +3,7 @@ export const PORT = process.env.PORT;
export const CORS_URL = process.env.CORS_URL?.split(',') || [];
export const API_VERSION = process.env.API_VERSION;
export const FRONTEND_RESET_URL = process.env.FRONTEND_RESET_URL;
+export const MAILTRAP_EMAIL_ENV = process.env.MAILTRAP_EMAIL_ENV || 'testing';
export const DATE_FORMAT = process.env.DATE_FORMAT || 'yyyy-MM-dd';
export const DATE_FULL_FORMAT =
@@ -15,14 +16,22 @@ export const MAILTRAP_EMAIL = {
host: process.env.MAILTRAP_TESTING_HOST || '',
port: parseInt(process.env.MAILTRAP_TESTING_PORT || ''),
},
+ prod: {
+ username: process.env.MAILTRAP_USERNAME || '',
+ password: process.env.MAILTRAP_PASSWORD || '',
+ host: process.env.MAILTRAP_HOST || '',
+ port: parseInt(process.env.MAILTRAP_PORT || ''),
+ },
};
export const LIMITER = {
loginWS: parseInt(process.env.LIMITER_LOGIN_WS || '120000'),
+ ipWS: parseInt(process.env.LIMITER_IP_WS || '900000'),
forgotPasswordWS: parseInt(
process.env.LIMITER_FORGOT_PASSWORD_WS || '120000'
),
loginMaxAttempt: parseInt(process.env.LIMITER_LOGIN_ATTEMPT || '5'),
+ ipMaxAttempt: parseInt(process.env.LIMITER_IP_ATTEMPT || '100'),
forgotPasswordMaxAttempt: parseInt(
process.env.LIMITER_FORGOT_PASSWORD_ATTEMPT || '2'
),
@@ -40,10 +49,12 @@ export const DB = {
username: process.env.MONGO_DB_USERNAME || '',
pwd: process.env.MONGO_DB_PWD || '',
port: process.env.MONGO_DB_PORT || '',
- minPoolSize: parseInt(process.env.DB_MIN_POOL_SIZE || '5'),
- maxPoolSize: parseInt(process.env.DB_MAX_POOL_SIZE || '10'),
- connectTimeoutMS: parseInt(process.env.DB_CONNECT_TIMEOUT_MS || '60000'),
- socketTimeoutMS: parseInt(process.env.DB_SOCKET_TIMEOUT_MS || '45000'),
+ minPoolSize: parseInt(process.env.MONGO_DB_MIN_POOL_SIZE || '5'),
+ maxPoolSize: parseInt(process.env.MONGO_DB_MAX_POOL_SIZE || '10'),
+ connectTimeoutMS: parseInt(
+ process.env.MONGO_DB_CONNECT_TIMEOUT_MS || '60000'
+ ),
+ socketTimeoutMS: parseInt(process.env.MONGO_DB_SOCKET_TIMEOUT_MS || '45000'),
};
export const TOKEN_INFO = {
diff --git a/server/src/controllers/AuthController.ts b/server/src/controllers/AuthController.ts
index 5cb8af0..d642ab6 100644
--- a/server/src/controllers/AuthController.ts
+++ b/server/src/controllers/AuthController.ts
@@ -1,7 +1,6 @@
import asyncHandler from 'express-async-handler';
import Logger from '../middleware/Logger';
import {
- ManyRequestResponse,
SuccessMsgResponse,
SuccessResponse,
TokenRefreshResponse,
@@ -16,23 +15,17 @@ import bcrypt from 'bcrypt';
import AuthHelper from '../helpers/AuthHelper';
import { COOKIE, LIMITER, TOKEN_INFO } from '../config';
import jwt, { JwtPayload } from 'jsonwebtoken';
-import rateLimit from 'express-rate-limit';
import { ProtectedRequest } from 'app-request';
import { UserModel } from '../models/UserModel';
import { RoleNameEnum, RoleStatusEnum } from '../models/RoleModel';
import RoleHelper from '../helpers/RoleHelper';
+import LimiterHelper from '../helpers/LimiterHelper';
class AuthController {
- forgotPasswordLimiter = rateLimit({
+ forgotPasswordLimiter = LimiterHelper.createRateLimiter({
windowMs: LIMITER.forgotPasswordWS,
max: LIMITER.forgotPasswordMaxAttempt,
- message: 'Too many reset passwords attempts, please try again later.',
- handler: (req, res, _, options) => {
- Logger.info(`${options.message}, Method: ${req.method}, Url: ${req.url}`);
- new ManyRequestResponse(options.message).send(res);
- },
- standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
- legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+ message: 'Too many reset password attempts, please try again later.',
});
forgotPassword = asyncHandler(async (req: ProtectedRequest, res, next) => {
@@ -73,14 +66,25 @@ class AuthController {
resetPassword = asyncHandler(async (req: ProtectedRequest, res, next) => {
const { password, email } = req.body;
+ const { token } = req.params ?? '';
- const filter = {
- passwordResetTokenRaw: req.params.token,
- passwordResetToken: AuthHelper.generateHashTokenKey(req.params.token),
+ let filter = {
+ passwordResetTokenRaw: token,
+ passwordResetToken: token,
email: email,
passwordResetTokenExpires: { $gt: Date.now() },
};
+ if (!token) {
+ Logger.info(`Attempted password reset, ${JSON.stringify(filter)}`);
+ throw new BadRequestError('Token is invalid or has been expired.');
+ }
+
+ filter = {
+ ...filter,
+ passwordResetToken: AuthHelper.generateHashTokenKey(token),
+ };
+
const user = await UserModel.findOne(filter);
if (!user) {
@@ -104,20 +108,14 @@ class AuthController {
next();
});
- loginLimiter = rateLimit({
+ loginLimiter = LimiterHelper.createRateLimiter({
windowMs: LIMITER.loginWS,
max: LIMITER.loginMaxAttempt,
message: 'Too many login attempts, please try again later.',
- handler: (req, res, _, options) => {
- Logger.info(`${options.message}, Method: ${req.method}, Url: ${req.url}`);
- new ManyRequestResponse(options.message).send(res);
- },
- standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
- legacyHeaders: false, // Disable the `X-RateLimit-*` headers
});
isAuthorized = asyncHandler(async (req: ProtectedRequest, _, next) => {
- const token = AuthHelper.getAccessToken(req.headers.authorization);
+ const token = AuthHelper.getAccessToken(req.headers.authorization) || '';
const accessTokenPayload = jwt.verify(
token,
TOKEN_INFO.accessTokenSecret
@@ -177,10 +175,8 @@ class AuthController {
},
]);
- if (!user || !user.password) {
- throw new BadRequestError(
- 'Your email address or your password is incorrect'
- );
+ if (!user?.password) {
+ throw new BadRequestError('Your email address or password is incorrect');
}
const isMatched = await bcrypt.compare(req.body.password, user.password);
diff --git a/server/src/controllers/EmailController.ts b/server/src/controllers/EmailController.ts
index 9feb510..684b671 100644
--- a/server/src/controllers/EmailController.ts
+++ b/server/src/controllers/EmailController.ts
@@ -23,13 +23,13 @@ class EmailController {
'Your password has been successfully updated. If you did not initiate this change, please contact your administrator for assistance.';
const email: Email = {
- to: user.email,
+ to: user.email || '',
subject: 'Password update successfully',
content: EmailHelper.emailFormatter(message, user.firstname),
};
try {
- await EmailHelper.testingEmailTransporter({
+ await EmailHelper.emailTransporter({
to: email.to,
subject: email.subject,
html: email.content,
@@ -72,14 +72,14 @@ class EmailController {
If you didn't initiate this request or have any concerns, please ignore this message. Your account remains secure.`;
const email: Email = {
- to: user.email,
+ to: user.email || '',
subject: 'Password change request received',
content: EmailHelper.emailFormatter(message, user.firstname),
url: resetUrl,
};
try {
- await EmailHelper.testingEmailTransporter({
+ await EmailHelper.emailTransporter({
to: email.to,
subject: email.subject,
html: email.content,
diff --git a/server/src/controllers/HealthCheckController.ts b/server/src/controllers/HealthCheckController.ts
index 8398997..2717c6e 100644
--- a/server/src/controllers/HealthCheckController.ts
+++ b/server/src/controllers/HealthCheckController.ts
@@ -2,12 +2,12 @@ import asyncHandler from 'express-async-handler';
import { SuccessResponse } from '../middleware/ApiResponse';
class HealthCheckController {
-
checkHealth = asyncHandler(async (req, res) => {
- new SuccessResponse('The API is up and running. Health check is passed.', {}).send(res);
+ new SuccessResponse(
+ 'The API is up and running. Health check is passed.',
+ {}
+ ).send(res);
});
-
}
-
-export default new HealthCheckController();
\ No newline at end of file
+export default new HealthCheckController();
diff --git a/server/src/controllers/UserController.ts b/server/src/controllers/UserController.ts
index ed8e6e5..a5bdbd0 100644
--- a/server/src/controllers/UserController.ts
+++ b/server/src/controllers/UserController.ts
@@ -49,7 +49,8 @@ class UserController {
});
getUser = asyncHandler(async (req, res) => {
- const user = await UserHelper.findById(req.params.id);
+ const { id } = req.params;
+ const user = (!!id && (await UserHelper.findById(id))) || null;
if (!user) throw new NotFoundError('User not found');
new SuccessResponse('User fetched successfully', {
@@ -59,24 +60,17 @@ class UserController {
updateUser = asyncHandler(async (req, res) => {
const { email, firstname, lastname } = req.body;
- const updateFields: any = {};
+ const updateFields: { [key: string]: any; } = { email, firstname, lastname };
- if (email) {
- updateFields.email = email;
- }
- if (firstname) {
- updateFields.firstname = firstname;
- }
- if (lastname) {
- updateFields.lastname = lastname;
- }
-
- const updatedUser = await UserModel.findOneAndUpdate(
- { _id: req.params.id },
- { $set: updateFields },
- { new: true }
+ // Remove undefined fields
+ Object.keys(updateFields).forEach(
+ (key) => updateFields[key] === undefined && delete updateFields[key]
);
+ const { id } = req.params;
+
+ const updatedUser = await UserHelper.findByIdAndUpdate(id, updateFields);
+
if (!updatedUser) {
throw new NotFoundError('User not found');
}
@@ -87,13 +81,16 @@ class UserController {
});
deleteUser = asyncHandler(async (req, res) => {
+ const { id } = req.params;
+
const result: DeleteResult = await UserModel.deleteOne({
- _id: req.params.id,
+ _id: id,
});
- if (!result.deletedCount) throw new NotFoundError('User not found');
+
+ if (result.deletedCount === 0) throw new NotFoundError('User not found');
new SuccessResponse('User deleted successfully', {
- userId: req.params.id,
+ userId: id,
}).send(res);
});
}
diff --git a/server/src/helpers/EmailHelper.ts b/server/src/helpers/EmailHelper.ts
index d1b5508..115b5ad 100644
--- a/server/src/helpers/EmailHelper.ts
+++ b/server/src/helpers/EmailHelper.ts
@@ -1,5 +1,5 @@
import nodemailer from 'nodemailer';
-import { MAILTRAP_EMAIL } from '../config';
+import { MAILTRAP_EMAIL, MAILTRAP_EMAIL_ENV } from '../config';
import SMTPTransport from 'nodemailer/lib/smtp-transport';
import Mail from 'nodemailer/lib/mailer';
@@ -14,26 +14,33 @@ const emailFormatter = (content: string, firstname = '') => {
${content}
Thank you for using our service!
Best regards,
- Node.js Auth API
+ Task API
`;
};
/**
- * used to send test emails during development and testing phases.
+ * used to send emails.
*
* for sending test emails using the Nodemailer library and a service like
- * Mailtrap for testing email delivery
+ * Mailtrap for email delivery
* @param options
+ * @returns
*/
-const testingEmailTransporter = async (
+const emailTransporter = async (
options: Mail.Options
): Promise => {
+ const isProdEnv = MAILTRAP_EMAIL_ENV === 'production';
+
const transporter = nodemailer.createTransport({
- host: MAILTRAP_EMAIL.testing.host,
- port: MAILTRAP_EMAIL.testing.port,
+ host: isProdEnv ? MAILTRAP_EMAIL.prod.host : MAILTRAP_EMAIL.testing.host,
+ port: isProdEnv ? MAILTRAP_EMAIL.prod.port : MAILTRAP_EMAIL.testing.port,
auth: {
- user: MAILTRAP_EMAIL.testing.username,
- pass: MAILTRAP_EMAIL.testing.password,
+ user: isProdEnv
+ ? MAILTRAP_EMAIL.prod.username
+ : MAILTRAP_EMAIL.testing.username,
+ pass: isProdEnv
+ ? MAILTRAP_EMAIL.prod.password
+ : MAILTRAP_EMAIL.testing.password,
},
});
@@ -46,6 +53,6 @@ const testingEmailTransporter = async (
};
export default {
- testingEmailTransporter,
emailFormatter,
+ emailTransporter,
};
diff --git a/server/src/helpers/LimiterHelper.ts b/server/src/helpers/LimiterHelper.ts
new file mode 100644
index 0000000..9614cd0
--- /dev/null
+++ b/server/src/helpers/LimiterHelper.ts
@@ -0,0 +1,28 @@
+import rateLimit from 'express-rate-limit';
+import Logger from '../middleware/Logger';
+import { ManyRequestResponse } from '../middleware/ApiResponse';
+
+const createRateLimiter = ({
+ windowMs,
+ max,
+ message,
+}: {
+ windowMs: number;
+ max: number;
+ message: string;
+}) =>
+ rateLimit({
+ windowMs,
+ max,
+ message,
+ handler: (req, res, _, options) => {
+ Logger.info(`${options.message}, Method: ${req.method}, Url: ${req.url}`);
+ new ManyRequestResponse(options.message).send(res);
+ },
+ standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
+ legacyHeaders: false, // Disable the `X-RateLimit-*` headers
+ });
+
+export default {
+ createRateLimiter,
+};
diff --git a/server/src/helpers/UserHelper.ts b/server/src/helpers/UserHelper.ts
index 25722b3..4c357ad 100644
--- a/server/src/helpers/UserHelper.ts
+++ b/server/src/helpers/UserHelper.ts
@@ -1,8 +1,15 @@
import User, { UserModel } from '../models/UserModel';
import { PopulateOptions, Types } from 'mongoose';
-import Role from '../models/RoleModel';
+import Role, { RoleStatusEnum } from '../models/RoleModel';
import RoleHelper from './RoleHelper';
+const defaultPopulates = [
+ {
+ path: 'roles',
+ match: { status: RoleStatusEnum.ACTIVE },
+ },
+];
+
const fullName = (firstname?: string, lastname?: string) => {
return `${firstname || ''}${lastname ? ' ' + lastname : ''}`;
};
@@ -23,7 +30,7 @@ const sanitizedUser = (user: User, roles: Role[] = []): User => {
const findById = async (
id: string,
selectFields = '',
- populates: PopulateOptions[] = []
+ populates: PopulateOptions[] = defaultPopulates
): Promise => {
return UserModel.findOne({ _id: new Types.ObjectId(id) })
.select(selectFields)
@@ -34,7 +41,7 @@ const findById = async (
const findByEmail = async (
email: string,
selectFields = '',
- populates: PopulateOptions[] = []
+ populates: PopulateOptions[] = defaultPopulates
): Promise => {
return UserModel.findOne({ email: email })
.select(selectFields)
@@ -45,9 +52,34 @@ const findByEmail = async (
const findAll = async (
filter: object = {},
selectFields = '',
- populates: PopulateOptions[] = []
+ populates: PopulateOptions[] = defaultPopulates
): Promise => {
return UserModel.find(filter).select(selectFields).populate(populates).exec();
};
-export default { findByEmail, fullName, findById, sanitizedUser, findAll };
+const findByIdAndUpdate = async (
+ id = '',
+ updateFields: {
+ [key: string]: any;
+ },
+ populates: PopulateOptions[] = defaultPopulates
+): Promise => {
+ return UserModel.findByIdAndUpdate(
+ {
+ _id: id,
+ },
+ { $set: updateFields },
+ { new: true }
+ )
+ .populate(populates)
+ .exec();
+};
+
+export default {
+ findByEmail,
+ fullName,
+ findById,
+ sanitizedUser,
+ findAll,
+ findByIdAndUpdate,
+};
diff --git a/server/src/models/EmailModel.ts b/server/src/models/EmailModel.ts
index d7dac42..6bfe765 100644
--- a/server/src/models/EmailModel.ts
+++ b/server/src/models/EmailModel.ts
@@ -11,10 +11,10 @@ export enum EmailStatusEnum {
export default interface Email {
_id?: Types.ObjectId;
- subject?: string;
- to?: string;
+ subject: string;
+ to: string;
url?: string;
- content?: string;
+ content: string;
status?: EmailStatusEnum;
error?: string;
createdAt?: string;
diff --git a/server/src/models/RoleModel.ts b/server/src/models/RoleModel.ts
index cd613fb..7d2cc2e 100644
--- a/server/src/models/RoleModel.ts
+++ b/server/src/models/RoleModel.ts
@@ -26,11 +26,11 @@ export enum RolePermissionEnum {
}
export default interface Role {
- _id?: Types.ObjectId;
- name?: RoleNameEnum;
+ _id: Types.ObjectId;
+ name: RoleNameEnum;
description?: string;
status?: RoleStatusEnum;
- permissions?: RolePermissionEnum[];
+ permissions: RolePermissionEnum[];
createdAt?: string;
updatedAt?: string;
}
@@ -59,14 +59,6 @@ const RoleSchema = new Schema(
enum: Object.values(RolePermissionEnum),
default: [RolePermissionEnum.VIEW],
},
- createdAt: {
- type: Schema.Types.Date,
- select: false,
- },
- updatedAt: {
- type: Schema.Types.Date,
- select: false,
- },
},
{
versionKey: false,
@@ -76,8 +68,6 @@ const RoleSchema = new Schema(
RoleSchema.index({ name: 1 });
RoleSchema.index({ status: 1 });
-RoleSchema.index({ createdAt: 1 });
-RoleSchema.index({ updatedAt: 1 });
export const RoleModel = model(
DOCUMENT_NAME,
diff --git a/server/src/models/UserModel.ts b/server/src/models/UserModel.ts
index c743999..a2fcd40 100644
--- a/server/src/models/UserModel.ts
+++ b/server/src/models/UserModel.ts
@@ -56,7 +56,7 @@ const UserSchema = new Schema(
terms: {
type: Schema.Types.Boolean,
select: false,
- default: true,
+ default: false,
},
passwordUpdatedAt: {
type: Schema.Types.Date,
@@ -82,7 +82,7 @@ const UserSchema = new Schema(
},
],
required: true,
- select: false,
+ select: true,
},
},
{
diff --git a/server/src/routes/v1/HealthRoutes.ts b/server/src/routes/v1/HealthRoutes.ts
index a698231..b7d10ee 100644
--- a/server/src/routes/v1/HealthRoutes.ts
+++ b/server/src/routes/v1/HealthRoutes.ts
@@ -3,10 +3,6 @@ import HealthCheckController from '../../controllers/HealthCheckController';
const router = express.Router();
-router
- .route('/')
- .get(
- HealthCheckController.checkHealth
- );
+router.route('/').get(HealthCheckController.checkHealth);
-export default router;
\ No newline at end of file
+export default router;
diff --git a/server/tsconfig.json b/server/tsconfig.json
index f9cbbfb..cd6845c 100644
--- a/server/tsconfig.json
+++ b/server/tsconfig.json
@@ -1,29 +1,30 @@
{
"compilerOptions": {
"module": "CommonJS",
- // Import non-ES modules as default imports.
- "esModuleInterop": true,
- // Target latest version of ECMAScript.
- "target": "ES2019",
- // Process & infer types from .js files.
+ "esModuleInterop": true, // Import non-ES modules as default imports.
+ "target": "ES2021", // or higher if needed
"allowJs": false,
- // Enable strictNullChecks & noImplicitAny.
"strictNullChecks": true,
"noImplicitAny": true,
"strict": true,
- "strictFunctionTypes": false,
- "noImplicitThis": false,
- // Search under node_modules for non-relative imports.
+ "strictFunctionTypes": false, // Optional: Enforces stricter checks for function types
+ "noImplicitThis": true, // Optional: Enforces stricter checks on 'this'
"moduleResolution": "node",
- // Import .json files
"resolveJsonModule": true,
"sourceMap": true,
"outDir": "build",
"baseUrl": ".",
"paths": {
"*": ["node_modules/*", "src/types/*"]
- }
+ },
+ "types": ["node"], // Include Node.js types
+ "skipLibCheck": true, // Skips type checking of declaration files for faster builds
+ "forceConsistentCasingInFileNames": true, // Ensures file name casing consistency
+ "noFallthroughCasesInSwitch": true, // Prevents fall-through cases in switch statements
+ "noUncheckedIndexedAccess": true, // Ensures array access is checked for undefined
+ "declaration": true, // Generates .d.ts files alongside .js files
+ "incremental": true // Enables incremental builds for faster compilation
},
"include": ["src/**/*"],
- "exclude": [".templates"]
+ "exclude": [".templates", "node_modules"]
}
diff --git a/server/tsconfig.prod.json b/server/tsconfig.prod.json
new file mode 100644
index 0000000..e145ccc
--- /dev/null
+++ b/server/tsconfig.prod.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "sourceMap": false, // Disable source maps for production
+ "removeComments": true, // Remove comments to reduce bundle size
+ "declaration": false, // Disable declaration files if not needed in production
+ "noImplicitReturns": true // Ensure all code paths return a value
+ }
+}