import {ClerkExpressWithAuth, StrictAuthProp} from '@clerk/clerk-sdk-node';
import {Db, Long, MongoClient, MongoClientOptions} from 'mongodb';
import {Counter, Histogram, register} from 'prom-client';
import express, {NextFunction, Request, Response} from 'express';
import StatusCode from '@instamenta/http-status-codes';
import Vlogger from "@instamenta/vlogger";
import 'dotenv/config';
const http_request_count = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status'],
});
const response_time_histogram = new Histogram({
name: 'http_response_time_seconds',
help: 'Histogram of response times',
labelNames: ['method', 'route', 'status'],
buckets: [0.1, 0.5, 1, 2, 5],
});
function useHttpServer(): express.Express {
const _server = express();
//* Extensions
_server.use(require('cors')());
_server.use(require('helmet')());
_server.use(require('compression')());
_server.use(require('cookie-parser')());
_server.use(require('morgan')('dev'));
_server.use(express.json());
//* Prometheus
_server.use(_metrics_middleware);
_server.get('/metrics', _metrics_endpoint);
//* Clerk
_server.use(ClerkExpressWithAuth({jwtKey: process.env?.CLERK_JWT_PUBLIC_KEY}));
return _server;
}
function startHttpServer(_server: express.Express) {
const vlogger = Vlogger.getInstance();
_server.listen(process.env?.PORT, () => {
console.log('======================================================================');
vlogger.getVlogger(process.env?.SERVICE_NAME || 'SERVICE').info({
f: 'startHttpServer',
m: `[ [ ${process.env?.SERVICE_NAME || 'SERVICE'} ] Running on port: [ ${process.env?.PORT} ]`,
});
console.log('======================================================================');
});
_server.on('error', (e: Error | unknown) => {
console.log('======================================================================');
vlogger.getVlogger(process.env?.SERVICE_NAME || 'SERVICE').error({
f: 'startHttpServer',
m: `[ ${process.env?.SERVICE_NAME || 'SERVICE'} ] ran into Error:`,
e,
});
console.log('======================================================================');
});
}
function useMongoDB(config?: MongoClientOptions | null): { database: Db, db_client: MongoClient } {
Vlogger.getInstance().getVlogger(process.env?.SERVICE_NAME || 'SERVICE')
.info({f: 'initialize_database', m: '[ Connecting to Mongo Client ]'})
const db_client = new MongoClient(process.env?.DB_URI || '', config || {appName: process.env?.SERVICE_NAME,});
Vlogger.getInstance().getVlogger(process.env?.SERVICE_NAME || 'SERVICE')
.info({f: 'initialize_database', m: `[ Connecting to Database "${process.env?.DB_NAME || 'main'} ]`})
return {
database: db_client.db(process.env?.DB_NAME || 'main'),
db_client: db_client,
}
}
class GracefulShutdown {
public static process_on(_cases_: string[] = ['unhandledRejection', 'uncaughtException']): void {
_cases_.forEach((_type_: string) => {
process.on(_type_, (error: Error) => {
try {
Vlogger.getInstance().getVlogger('NODE_PROCESS')
.error({
e: error,
f: 'process_on',
m: `[${process.env?.SERVICE_NAME || 'SERVICE'}] ~ process.on: [${_type_}] `
})
} catch {
process.exit(1);
}
});
});
}
public static process_once(_cases_: string[] = ['SIGTERM', 'SIGINT', 'SIGUSR2']): void {
_cases_.forEach((_type_: string) => {
process.once(_type_, (error: Error) => {
try {
Vlogger.getInstance().getVlogger('NODE_PROCESS')
.error({
e: error,
f: 'process_once',
m: `[${process.env?.SERVICE_NAME || 'SERVICE'}] - process.on: [${_type_}] `
})
process.exit(0);
} finally {
process.kill(process.pid, _type_);
}
});
});
}
}
class ErrorMiddleware {
public static _errorHandler(e: Error, r: Request, w: Response, n: NextFunction) {
Vlogger.getInstance().getVlogger('ErrorMiddleware').error({e, f: '_errorHandler'})
w.status(StatusCode.INTERNAL_SERVER_ERROR)
.json({error: 'Internal Server Error!'}).end();
}
public static _404Handler(r: Request, w: Response, n: NextFunction) {
Vlogger.getInstance().getVlogger('ErrorMiddleware').error({e: 'Not Found 404 ', f: '_404Handler', m: r.url})
w.status(StatusCode.NOT_FOUND)
.json({error: 'Not Found'}).end();
}
}
const Exports = {
'GracefulShutdown': GracefulShutdown,
'ErrorMiddleware': ErrorMiddleware,
'useHttpServer': useHttpServer,
'useMongoDB': useMongoDB,
'StatusCode': StatusCode,
'Vlogger': Vlogger,
}
export default Exports;
function _get_elapsed_time(startTime: [number, number]): number {
const elapsedNanoseconds = process.hrtime(startTime);
return elapsedNanoseconds[0] + elapsedNanoseconds[1] * 1e-9;
}
async function _metrics_endpoint(req: Request, res: Response) {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
}
function _metrics_middleware(req: Request, res: Response, next: NextFunction) {
res.on('finish', () => {
http_request_count.inc({
method: req.method,
route: req.route ? req.route.path : 'unknown',
status: res.statusCode,
});
response_time_histogram.observe({
method: req.method,
route: req.route ? req.route.path : 'unknown',
status: res.statusCode,
}, _get_elapsed_time(process.hrtime()));
});
next();
}
declare global { namespace Express { interface Request extends StrictAuthProp { } } }
// @ts-ignore
BigInt.prototype.toJSON = function () { return this.toString(); };
// @ts-ignore
Long.prototype.toJSON = function () { return this.toString(); };
@instamenta/server-utilities
1.3.19 • Public • PublishedPackage Sidebar
Install
npm i @instamenta/server-utilities
Weekly Downloads
1
Version
1.3.19
License
MIT
Unpacked Size
34.2 kB
Total Files
9