Skip to content

Commit

Permalink
add: api metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
genzyy committed Nov 11, 2023
1 parent 921b59c commit c48fd5f
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 7 deletions.
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,21 @@ services:
- '5432:5432'
volumes:
- ./pgdata:/var/lib/postgresql/data

redis:
image: redis:7.2.3
ports:
- '6379:6379'
volumes:
- redis:/data

redis-queue:
image: redis:7.2.3
ports:
- '6380:6380'
volumes:
- redis:/queue-data

volumes:
redis:
redis-queue:
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "express-prisma-boilerplate",
"version": "1.0.0",
"main": "index.ts",
"type": "module",
"license": "MIT",
"author": "Rishit",
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import JwtStrategy from './core/passport';
import { NotFound } from './utils/ApiError';
import { StartAllJobs } from './jobs';
import { errorConverter, errorHandler } from './core/middlewares/error';
import { ValidateApiStatus } from './core/middlewares/apiStatus';

const app = express();

Expand All @@ -35,6 +36,7 @@ app.use((req, res, next) => {

app.use(errorConverter);
app.use(errorHandler);
app.use(ValidateApiStatus);

StartAllJobs();

Expand Down
Empty file.
4 changes: 4 additions & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const envVarsSchema = Joi.object()
QUEUE_CONCURRENCY: Joi.number().default(1),
AWS_REGION: Joi.string().default(''),
AWS_SES_API_VERSION: Joi.string().default(''),
API_STATUS: Joi.string().default(''),
})
.unknown();

Expand All @@ -38,6 +39,9 @@ export default {
environment: envVars.ENVIRONMENT,
port: envVars.PORT,
redisUrl: envVars.REDIS_URL,
api: {
status: envVars.API_STATUS,
},
logging: {
showSqlQueries: envVars.SHOW_SQL_QUERIES,
},
Expand Down
14 changes: 14 additions & 0 deletions src/core/middlewares/apiStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import config from '../config';
import { ApiStatus } from '../../types/apiMetadata';
import { ApiUnavailable } from '../../utils/ApiError';

export const ValidateApiStatus = async (req: Request, res: Response, next: NextFunction) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(async () => {
if (config.api.status === ApiStatus.Maintenance) throw new ApiUnavailable();
})()
.then(() => next())
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch((error) => next(new ApiUnavailable()));
};
14 changes: 11 additions & 3 deletions src/jobs/apiMaintenanace.job.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import * as cron from 'node-cron';
import apiMetadataRepository from '../repositories/apiMetadata';
import logger from '../core/logger';
import config from '../core/config';
import { ApiStatus } from '../types/apiMetadata';

export const ApiMaintenanceJob = cron.schedule(
'10 * * * * *',
() => {
logger.info('API maintenance job started...');
'*/10 * * * * *',
async () => {
let apiStatus = await apiMetadataRepository.getApiMetadata('status');
if (!apiStatus) {
logger.warn('Api status is null, adding status immediately.');
apiStatus = await apiMetadataRepository.createApiMetadata('status', ApiStatus.Maintenance);
}
config.api.status = apiStatus.value;
},
{
scheduled: false,
Expand Down
2 changes: 2 additions & 0 deletions src/jobs/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logger from '../core/logger';
import { ApiMaintenanceJob } from './apiMaintenanace.job';

export const StartAllJobs = () => {
ApiMaintenanceJob.start();
logger.info('API maintenance job started...');
};

export const StopAllJobs = () => {
Expand Down
5 changes: 5 additions & 0 deletions src/types/apiMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ export const ApiMetadatReturn = include<ApiMetadataModel, Key>([
'created',
'updated',
]);

export enum ApiStatus {
Live = 'live',
Maintenance = 'Maintenance',
}
12 changes: 10 additions & 2 deletions src/utils/ApiError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST } from 'http-status';
import { NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST, SERVICE_UNAVAILABLE } from 'http-status';

class ApiError extends Error {
statusCode: number;
Expand Down Expand Up @@ -56,6 +56,14 @@ class BadRequest extends ApiError {
}
}

class ApiUnavailable extends ApiError {
message: string;
constructor(message: string = 'Api is on maintenance mode.') {
super(SERVICE_UNAVAILABLE, message);
this.message = message;
}
}

export default ApiError;

export { Unauthorized, Forbidden, BadRequest, NotFound };
export { Unauthorized, Forbidden, BadRequest, NotFound, ApiUnavailable };
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "CommonJS", /* Specify what module code is generated. */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
Expand Down

0 comments on commit c48fd5f

Please sign in to comment.