/// /// Copyright © 2016-2023 The Thingsboard Authors /// /// 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 express from 'express'; import config from 'config'; import { _logger} from './config/logger'; import path from 'path'; import http, { ServerResponse } from 'http'; import httpProxy from 'http-proxy'; import compression from 'compression'; import historyApiFallback from 'connect-history-api-fallback'; import { Socket } from 'net'; const logger = _logger('main'); let server: http.Server | null; let connections: Socket[] = []; (async() => { try { logger.info('Starting ThingsBoard Web UI Microservice...'); const bindAddress: string = config.get('server.address'); const bindPort = Number(config.get('server.port')); const thingsboardEnableProxy: string = config.get('thingsboard.enableProxy'); const thingsboardHost: string = config.get('thingsboard.host'); const thingsboardPort = Number(config.get('thingsboard.port')); logger.info('Bind address: %s', bindAddress); logger.info('Bind port: %s', bindPort); logger.info('ThingsBoard Enable Proxy: %s', thingsboardEnableProxy); logger.info('ThingsBoard host: %s', thingsboardHost); logger.info('ThingsBoard port: %s', thingsboardPort); const useApiProxy = thingsboardEnableProxy === "true"; let webDir = path.join(__dirname, 'web'); if (typeof process.env.WEB_FOLDER !== 'undefined') { webDir = path.resolve(process.env.WEB_FOLDER); } logger.info('Web folder: %s', webDir); const app = express(); server = http.createServer(app); let apiProxy: httpProxy; if (useApiProxy) { apiProxy = httpProxy.createProxyServer({ target: { host: thingsboardHost, port: thingsboardPort } }); apiProxy.on('error', (err, req, res) => { logger.warn('API proxy error: %s', err.message); if (res instanceof ServerResponse) { res.writeHead(500); const error = err as any; if (error.code && error.code === 'ECONNREFUSED') { res.end('Unable to connect to ThingsBoard server.'); } else { res.end('ThingsBoard server connection error: ' + error.code ? error.code : ''); } } }); app.all('/api/*', (req, res) => { logger.debug(req.method + ' ' + req.originalUrl); apiProxy.web(req, res); }); app.all('/static/rulenode/*', (req, res) => { apiProxy.web(req, res); }); server.on('upgrade', (req, socket, head) => { apiProxy.ws(req, socket, head); }); } app.use(historyApiFallback()); app.use(compression()); const root = path.join(webDir, 'public'); app.use(express.static(root)); server.listen(bindPort, bindAddress, () => { logger.info('==> 🌎 Listening on port %s.', bindPort); logger.info('Started ThingsBoard Web UI Microservice.'); }).on('error', async (error) => { logger.error('Failed to start ThingsBoard Web UI Microservice: %s', error.message); logger.error(error.stack); await exit(-1); }); server.on('connection', connection => { connections.push(connection); connection.on('close', () => connections = connections.filter(curr => curr !== connection)); }); } catch (e: any) { logger.error('Failed to start ThingsBoard Web UI Microservice: %s', e.message); logger.error(e.stack); await exit(-1); } })(); [`SIGINT`, `SIGUSR1`, `SIGUSR2`, `uncaughtException`, `SIGTERM`].forEach((eventType) => { process.once(eventType, async () => { logger.info(`${eventType} signal received`); await exit(0); }); }) process.on('exit', async (code: number) => { logger.info(`ThingsBoard Web UI Microservice has been stopped. Exit code: ${code}.`); }); async function exit(status: number) { logger.info('Exiting with status: %d ...', status); if (server) { logger.info('Stopping HTTP Server...'); connections.forEach(curr => curr.end(() => curr.destroy())); const _server = server; server = null; const serverClosePromise = new Promise( (resolve, reject) => { _server.close((err) => { logger.info('HTTP Server stopped.'); resolve(); }); } ); await serverClosePromise; } process.exit(status); }