|
@@ -13,12 +13,15 @@ import { InvalidMiddlewareException } from '../base/exceptions';
|
|
import { InvalidRouteException } from '../base/exceptions';
|
|
import { InvalidRouteException } from '../base/exceptions';
|
|
import { ServerNotInitializedException } from '../base/exceptions';
|
|
import { ServerNotInitializedException } from '../base/exceptions';
|
|
import { WebSocketHandler } from '../base/websocket';
|
|
import { WebSocketHandler } from '../base/websocket';
|
|
|
|
+import swaggerJsDoc from 'swagger-jsdoc';
|
|
|
|
+import swaggerUi from 'swagger-ui-express';
|
|
|
|
|
|
/** @sealed */
|
|
/** @sealed */
|
|
class Server {
|
|
class Server {
|
|
private instance: Express;
|
|
private instance: Express;
|
|
private httpServer: http.Server;
|
|
private httpServer: http.Server;
|
|
private readonly port: number;
|
|
private readonly port: number;
|
|
|
|
+ private readonly host: string;
|
|
private readonly logger: Logger;
|
|
private readonly logger: Logger;
|
|
private i18n: i18nLoader;
|
|
private i18n: i18nLoader;
|
|
public static readonly i18nDefaultPath = '../resources/i18n.json';
|
|
public static readonly i18nDefaultPath = '../resources/i18n.json';
|
|
@@ -36,10 +39,17 @@ class Server {
|
|
private readonly viewsPath?: string;
|
|
private readonly viewsPath?: string;
|
|
private readonly options?: {[key: string]: any};
|
|
private readonly options?: {[key: string]: any};
|
|
|
|
|
|
|
|
+ private readonly swaggerDocsPath? : string;
|
|
|
|
+ private readonly swaggerTitle?: string;
|
|
|
|
+ private readonly swaggerDescription?: string;
|
|
|
|
+ private readonly swaggerApiVersion?: string;
|
|
|
|
+ private readonly swaggerRoute?: string;
|
|
|
|
+
|
|
public constructor(properties: ServerProperties) {
|
|
public constructor(properties: ServerProperties) {
|
|
this.instance = express();
|
|
this.instance = express();
|
|
this.httpServer = http.createServer(this.instance);
|
|
this.httpServer = http.createServer(this.instance);
|
|
this.port = properties.port;
|
|
this.port = properties.port;
|
|
|
|
+ this.host = properties.host || 'http://localhost:3000';
|
|
this.i18n = i18nLoader.getInstance();
|
|
this.i18n = i18nLoader.getInstance();
|
|
if(properties.locale)
|
|
if(properties.locale)
|
|
this.i18n.setLocale(properties.locale);
|
|
this.i18n.setLocale(properties.locale);
|
|
@@ -53,6 +63,13 @@ class Server {
|
|
this.viewEngine = properties.viewEngine;
|
|
this.viewEngine = properties.viewEngine;
|
|
this.viewsPath = properties.viewsPath;
|
|
this.viewsPath = properties.viewsPath;
|
|
this.options = properties.options;
|
|
this.options = properties.options;
|
|
|
|
+ if(properties.swagger) {
|
|
|
|
+ this.swaggerDocsPath = properties.swagger?.docsPath;
|
|
|
|
+ this.swaggerTitle = properties.swagger?.title || 'API Documentation';
|
|
|
|
+ this.swaggerDescription = properties.swagger?.description || 'API Documentation';
|
|
|
|
+ this.swaggerApiVersion = properties.swagger?.version ||'1.0.0';
|
|
|
|
+ this.swaggerRoute = properties.swagger?.route || '/api-docs';
|
|
|
|
+ }
|
|
this.initialized = false;
|
|
this.initialized = false;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -76,6 +93,8 @@ class Server {
|
|
await this.registerMiddlewares(this.middlewaresPath);
|
|
await this.registerMiddlewares(this.middlewaresPath);
|
|
if(this.routesPath)
|
|
if(this.routesPath)
|
|
await this.registerRoutes(this.routesPath);
|
|
await this.registerRoutes(this.routesPath);
|
|
|
|
+ if(this.swaggerDocsPath)
|
|
|
|
+ fs.writeFileSync(this.swaggerDocsPath, '');
|
|
if(Object.keys(this.wsHandlers).length > 0) {
|
|
if(Object.keys(this.wsHandlers).length > 0) {
|
|
this.registerWsServers();
|
|
this.registerWsServers();
|
|
this.applyWsHandlers();
|
|
this.applyWsHandlers();
|
|
@@ -117,6 +136,28 @@ class Server {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private registerRoutesDocumentation(): Server {
|
|
|
|
+ for(const routeName in this.httpHandlers) {
|
|
|
|
+ const route = this.httpHandlers[routeName];
|
|
|
|
+ if(!(route instanceof Route))
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if(route.getRoute() == null)
|
|
|
|
+ throw new RouteNotSetException();
|
|
|
|
+
|
|
|
|
+ if(![HttpMethod.GET, HttpMethod.POST].includes(route.getMethod()))
|
|
|
|
+ throw new IncorrectMethodException();
|
|
|
|
+
|
|
|
|
+ const docs = route.getDocumentation();
|
|
|
|
+
|
|
|
|
+ if(this.swaggerDocsPath && docs.length > 0) {
|
|
|
|
+ fs.appendFileSync(this.swaggerDocsPath, `${docs}\n`);
|
|
|
|
+ this.logInfo(`Swagger documentation for route '${route.getRoute()}' generated!`);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return this;
|
|
|
|
+ }
|
|
|
|
+
|
|
public registerWsServers(): Server {
|
|
public registerWsServers(): Server {
|
|
for(const url of Object.keys(this.wsHandlers)) {
|
|
for(const url of Object.keys(this.wsHandlers)) {
|
|
const wsServer = this.wsServers[url] = new WebSocketServer({ noServer: true });
|
|
const wsServer = this.wsServers[url] = new WebSocketServer({ noServer: true });
|
|
@@ -184,6 +225,31 @@ class Server {
|
|
return this;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private registerSwaggerMiddleware(): Server {
|
|
|
|
+ if(!this.swaggerDocsPath)
|
|
|
|
+ return this;
|
|
|
|
+
|
|
|
|
+ const swaggerOptions = {
|
|
|
|
+ swaggerDefinition: {
|
|
|
|
+ openapi: '3.0.0',
|
|
|
|
+ info: {
|
|
|
|
+ title: this.swaggerTitle!,
|
|
|
|
+ version: this.swaggerApiVersion!,
|
|
|
|
+ description: this.swaggerDescription!,
|
|
|
|
+ },
|
|
|
|
+ servers: [
|
|
|
|
+ { url: this.host }
|
|
|
|
+ ],
|
|
|
|
+ },
|
|
|
|
+ apis: [this.swaggerDocsPath],
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ const swaggerDocs = swaggerJsDoc(swaggerOptions);
|
|
|
|
+
|
|
|
|
+ this.instance.use(this.swaggerRoute!, swaggerUi.serve, swaggerUi.setup(swaggerDocs));
|
|
|
|
+ return this;
|
|
|
|
+ }
|
|
|
|
+
|
|
public async registerRoutes(dir: string): Promise<Server> {
|
|
public async registerRoutes(dir: string): Promise<Server> {
|
|
const files = fs.readdirSync(dir);
|
|
const files = fs.readdirSync(dir);
|
|
|
|
|
|
@@ -228,6 +294,8 @@ class Server {
|
|
public start(callback?: () => any): void {
|
|
public start(callback?: () => any): void {
|
|
if(!this.initialized)
|
|
if(!this.initialized)
|
|
throw new ServerNotInitializedException();
|
|
throw new ServerNotInitializedException();
|
|
|
|
+ this.registerRoutesDocumentation();
|
|
|
|
+ this.registerSwaggerMiddleware();
|
|
this.processHttpHandlers();
|
|
this.processHttpHandlers();
|
|
const cb = (): void => {
|
|
const cb = (): void => {
|
|
this.logInfo($$('org.crazydoctor.expressts.start', { 'port': this.port }));
|
|
this.logInfo($$('org.crazydoctor.expressts.start', { 'port': this.port }));
|