feat(backend-common): add common code for service shell
This commit is contained in:
@@ -27,12 +27,17 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"helmet": "^3.22.0",
|
||||
"morgan": "^1.10.0",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.7",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/http-errors": "^1.6.3",
|
||||
"@types/morgan": "^1.9.0",
|
||||
|
||||
@@ -17,3 +17,4 @@
|
||||
export * from './errors';
|
||||
export * from './logging';
|
||||
export * from './middleware';
|
||||
export * from './service';
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import compression from 'compression';
|
||||
import cors from 'cors';
|
||||
import express, { Router } from 'express';
|
||||
import helmet from 'helmet';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
import { getRootLogger } from '../logging';
|
||||
import {
|
||||
errorHandler,
|
||||
notFoundHandler,
|
||||
requestLoggingHandler,
|
||||
} from '../middleware';
|
||||
import { ServiceBuilder } from './types';
|
||||
|
||||
const DEFAULT_PORT = 7000;
|
||||
|
||||
export class ServiceBuilderImpl implements ServiceBuilder {
|
||||
private port: number | undefined;
|
||||
private logger: Logger | undefined;
|
||||
private corsOptions: cors.CorsOptions | undefined;
|
||||
private routers: [string, Router][];
|
||||
|
||||
constructor() {
|
||||
this.routers = [];
|
||||
}
|
||||
|
||||
setPort(port: number): ServiceBuilder {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
setLogger(logger: Logger): ServiceBuilder {
|
||||
this.logger = logger;
|
||||
return this;
|
||||
}
|
||||
|
||||
enableCors(options: cors.CorsOptions): ServiceBuilder {
|
||||
this.corsOptions = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
addRouter(root: string, router: Router): ServiceBuilder {
|
||||
this.routers.push([root, router]);
|
||||
return this;
|
||||
}
|
||||
|
||||
start(): Promise<Server> {
|
||||
const app = express();
|
||||
const { port, logger, corsOptions } = this.getOptions();
|
||||
|
||||
app.use(helmet());
|
||||
if (corsOptions) {
|
||||
app.use(cors(corsOptions));
|
||||
}
|
||||
app.use(compression());
|
||||
app.use(express.json());
|
||||
app.use(requestLoggingHandler());
|
||||
for (const [root, route] of this.routers) {
|
||||
app.use(root, route);
|
||||
}
|
||||
app.use(notFoundHandler());
|
||||
app.use(errorHandler());
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
app.on('error', e => {
|
||||
logger.error(`Failed to start up on port ${port}, ${e}`);
|
||||
reject(e);
|
||||
});
|
||||
const server = app.listen(port, () => {
|
||||
logger.info(`Listening on port ${port}`);
|
||||
});
|
||||
resolve(server);
|
||||
});
|
||||
}
|
||||
|
||||
private getOptions(): {
|
||||
port: number;
|
||||
logger: Logger;
|
||||
corsOptions?: cors.CorsOptions;
|
||||
} {
|
||||
let port: number;
|
||||
if (this.port !== undefined) {
|
||||
port = this.port;
|
||||
} else {
|
||||
port = parseInt(process.env.PORT ?? '', 10) || DEFAULT_PORT;
|
||||
}
|
||||
|
||||
let logger: Logger;
|
||||
if (this.logger) {
|
||||
logger = this.logger;
|
||||
} else {
|
||||
logger = getRootLogger();
|
||||
}
|
||||
|
||||
return {
|
||||
port,
|
||||
logger,
|
||||
corsOptions: this.corsOptions,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ServiceBuilderImpl } from './ServiceBuilderImpl';
|
||||
|
||||
/**
|
||||
* Creates a new service builder.
|
||||
*/
|
||||
export function createServiceBuilder() {
|
||||
return new ServiceBuilderImpl();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { createServiceBuilder } from './createServiceBuilder';
|
||||
export type { ServiceBuilder } from './types';
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import cors from 'cors';
|
||||
import { Router } from 'express';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
export type ServiceBuilder = {
|
||||
/**
|
||||
* Sets the port to listen on.
|
||||
*
|
||||
* If no port is specified, the service will first look for an environment
|
||||
* variable named PORT and use that if present, otherwise it picks a default
|
||||
* port (7000).
|
||||
*
|
||||
* @param port The port to listen on
|
||||
*/
|
||||
setPort(port: number): ServiceBuilder;
|
||||
|
||||
/**
|
||||
* Sets the logger to use for service-specific logging.
|
||||
*
|
||||
* If no logger is given, the default root logger is used.
|
||||
*
|
||||
* @param logger A winston logger
|
||||
*/
|
||||
setLogger(logger: Logger): ServiceBuilder;
|
||||
|
||||
/**
|
||||
* Enables CORS handling using the given settings.
|
||||
*
|
||||
* If this method is not called, the resulting service will not have any
|
||||
* built in CORS handling.
|
||||
*
|
||||
* @param options Standard CORS options
|
||||
*/
|
||||
enableCors(options: cors.CorsOptions): ServiceBuilder;
|
||||
|
||||
/**
|
||||
* Adds a router (similar to the express .use call) to the service.
|
||||
*
|
||||
* @param root The root URL to bind to (e.g. "/api/function1")
|
||||
* @param router An express router
|
||||
*/
|
||||
addRouter(root: string, router: Router): ServiceBuilder;
|
||||
|
||||
/**
|
||||
* Starts the server using the given settings.
|
||||
*/
|
||||
start(): Promise<Server>;
|
||||
};
|
||||
Reference in New Issue
Block a user