1 | import { Interfaces, LogType } from '@lxdhub/common';
|
2 | import { IDatabaseSettings } from '@lxdhub/db';
|
3 | import { INestApplication } from '@nestjs/common';
|
4 | import { NestFactory } from '@nestjs/core';
|
5 | import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
6 | import { Type } from 'class-transformer';
|
7 | import { IsInt, IsString } from 'class-validator';
|
8 |
|
9 | import { AppModule } from './app.module';
|
10 | import { HttpExceptionFilter } from './exception';
|
11 | import { LogService } from './log';
|
12 | import { RequestLoggerInterceptor } from './log/request-logger.interceptor';
|
13 | import { Application } from 'express';
|
14 | import * as Chalk from 'chalk';
|
15 | import * as express from 'express';
|
16 | import * as cors from 'cors';
|
17 | import { IoAdapter } from '@nestjs/websockets';
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | export class LXDHubAPISettings {
|
23 | @IsInt()
|
24 | @Type(() => Number)
|
25 | port?: number = 3000;
|
26 | @IsString()
|
27 | @Type(() => String)
|
28 | hostUrl?: string = '0.0.0.0';
|
29 | database: IDatabaseSettings;
|
30 | lxd?: Interfaces.ILXDRemoteAuthentication;
|
31 | logLevel?: LogType = 'silly';
|
32 | docUrl: string = '/api/v1/doc';
|
33 | }
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | export class LXDHubAPI implements Interfaces.ILXDHubHttpService {
|
40 | private app: INestApplication;
|
41 | private logger: LogService;
|
42 | private url: string;
|
43 |
|
44 | constructor(private settings: LXDHubAPISettings, private server?: Application) {
|
45 | this.logger = new LogService('LXDHubAPI', settings.logLevel);
|
46 | this.url = `http://${this.settings.hostUrl}:${this.settings.port}`;
|
47 | }
|
48 |
|
49 | |
50 |
|
51 |
|
52 | private setupSwagger() {
|
53 | const options = new DocumentBuilder()
|
54 | .setTitle('LXDHub API')
|
55 | .setDescription('Display, search and copy LXD images using a web interface.')
|
56 | .setVersion('1.0')
|
57 | .build();
|
58 |
|
59 | const document = SwaggerModule.createDocument(this.app, options);
|
60 | SwaggerModule.setup(this.settings.docUrl || '/api/v1/doc', this.app, document);
|
61 | }
|
62 |
|
63 | |
64 |
|
65 |
|
66 | private async createNestApp() {
|
67 | const nestSettings = { logger: this.logger };
|
68 |
|
69 | if (!this.server) {
|
70 | this.server = express();
|
71 | }
|
72 |
|
73 | this.app = await NestFactory.create(AppModule.forRoot(this.settings), this.server, nestSettings);
|
74 | this.app.useWebSocketAdapter(new IoAdapter(this.app.getHttpServer()));
|
75 | }
|
76 |
|
77 | |
78 |
|
79 |
|
80 | private setupMiddleware() {
|
81 |
|
82 |
|
83 |
|
84 | this.app.useGlobalFilters(new HttpExceptionFilter());
|
85 |
|
86 |
|
87 | this.app.useGlobalInterceptors(new RequestLoggerInterceptor());
|
88 |
|
89 |
|
90 | if (process.env.NODE_ENV !== 'production') {
|
91 | this.app.use(cors({
|
92 | origin: true,
|
93 | credentials: true
|
94 | }));
|
95 | }
|
96 |
|
97 | this.app.use((req, res, next) => {
|
98 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, DEVICE_ID, SSO_TOKEN');
|
99 |
|
100 | next();
|
101 | });
|
102 | }
|
103 |
|
104 | |
105 |
|
106 |
|
107 |
|
108 | async bootstrap(): Promise<Application> {
|
109 | await this.createNestApp();
|
110 | this.setupSwagger();
|
111 | this.setupMiddleware();
|
112 |
|
113 | return this.server;
|
114 | }
|
115 |
|
116 | |
117 |
|
118 |
|
119 | async run() {
|
120 | this.logger.log('Bootstraping application');
|
121 | try {
|
122 | await this.bootstrap();
|
123 | }
|
124 | catch (err) {
|
125 | err = err as Error;
|
126 | this.logger.error(`An error occured while bootstraping the application`);
|
127 | this.logger.error(err.message);
|
128 | }
|
129 |
|
130 |
|
131 | await this.app.listen(this.settings.port, this.settings.hostUrl);
|
132 | this.logger.log(`Open on ${Chalk.default.blue(this.url)}`);
|
133 | }
|
134 | }
|