Browse Source

ThingsBoard Web UI Microservice.

pull/1150/head
Igor Kulikov 8 years ago
parent
commit
4bf57ebf20
  1. 13
      msa/docker/docker-compose.yml
  2. 9
      msa/docker/tb-web-ui.env
  3. 1
      msa/pom.xml
  4. 31
      msa/web-ui/.gitignore
  5. 125
      msa/web-ui/build.gradle
  6. 30
      msa/web-ui/config/custom-environment-variables.yml
  7. 30
      msa/web-ui/config/default.yml
  8. 59
      msa/web-ui/config/logger.js
  9. 20
      msa/web-ui/config/tb-web-ui.conf
  10. 26
      msa/web-ui/docker/Dockerfile
  11. 29
      msa/web-ui/docker/start-web-ui.sh
  12. 42
      msa/web-ui/install.js
  13. 38
      msa/web-ui/package.json
  14. 372
      msa/web-ui/pom.xml
  15. 129
      msa/web-ui/server.js
  16. 75
      msa/web-ui/src/main/assembly/windows.xml
  17. 1
      msa/web-ui/src/main/filters/unix.properties
  18. 2
      msa/web-ui/src/main/filters/windows.properties
  19. 6
      msa/web-ui/src/main/scripts/control/deb/postinst
  20. 3
      msa/web-ui/src/main/scripts/control/deb/postrm
  21. 18
      msa/web-ui/src/main/scripts/control/deb/preinst
  22. 5
      msa/web-ui/src/main/scripts/control/deb/prerm
  23. 9
      msa/web-ui/src/main/scripts/control/rpm/postinst
  24. 6
      msa/web-ui/src/main/scripts/control/rpm/postrm
  25. 6
      msa/web-ui/src/main/scripts/control/rpm/preinst
  26. 6
      msa/web-ui/src/main/scripts/control/rpm/prerm
  27. 11
      msa/web-ui/src/main/scripts/control/tb-web-ui.service
  28. 233
      msa/web-ui/src/main/scripts/init/tb-web-ui
  29. 31
      msa/web-ui/src/main/scripts/windows/install.bat
  30. 30
      msa/web-ui/src/main/scripts/windows/service.xml
  31. 25
      msa/web-ui/src/main/scripts/windows/uninstall.bat
  32. 2
      ui/package.json

13
msa/docker/docker-compose.yml

@ -29,7 +29,7 @@ services:
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_LISTENERS: INSIDE://:9093,OUTSIDE://:9092
KAFKA_ADVERTISED_LISTENERS: INSIDE://:9093,OUTSIDE://${KAFKA_HOSTNAME}:9092
KAFKA_ADVERTISED_LISTENERS: INSIDE://:9093,OUTSIDE://${EXTERNAL_HOSTNAME}:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
KAFKA_CREATE_TOPICS: "${KAFKA_TOPICS}"
@ -44,3 +44,14 @@ services:
- tb-js-executor.env
depends_on:
- kafka
tb-web-ui:
image: "local-maven-build/tb-web-ui:latest"
ports:
- "8090:8090"
environment:
HTTP_BIND_ADDRESS: 0.0.0.0
HTTP_BIND_PORT: 8090
TB_HOST: ${EXTERNAL_HOSTNAME}
TB_PORT: 8080
env_file:
- tb-web-ui.env

9
msa/docker/tb-web-ui.env

@ -0,0 +1,9 @@
HTTP_BIND_ADDRESS=0.0.0.0
HTTP_BIND_PORT=8090
TB_HOST=localhost
TB_PORT=8080
LOGGER_LEVEL=debug
LOG_FOLDER=logs
LOGGER_FILENAME=tb-web-ui-%DATE%.log
DOCKER_MODE=true

1
msa/pom.xml

@ -36,6 +36,7 @@
<modules>
<module>js-executor</module>
<module>web-ui</module>
</modules>
</project>

31
msa/web-ui/.gitignore

@ -0,0 +1,31 @@
*.toDelete
output/**
*.class
*~
*.iml
*/.idea/**
.idea/**
.idea
*.log
*.log.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
*/.classpath
.classpath
*/.project
.project
.cache/**
target/
logs/
build/
.settings/
/bin
bin/
**/dependency-reduced-pom.xml
pom.xml.versionsBackup
.DS_Store
**/.gradle
**/local.properties
**/build
**/target
**/.env
node_modules
package-lock.json

125
msa/web-ui/build.gradle

@ -0,0 +1,125 @@
/**
* Copyright © 2016-2018 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 org.apache.tools.ant.filters.ReplaceTokens
buildscript {
ext {
osPackageVersion = "3.8.0"
}
repositories {
jcenter()
}
dependencies {
classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}")
}
}
apply plugin: "nebula.ospackage"
buildDir = projectBuildDir
version = projectVersion
distsDirName = "./"
// OS Package plugin configuration
ospackage {
packageName = pkgName
version = "${project.version}"
release = 1
os = LINUX
type = BINARY
into pkgInstallFolder
user pkgUser
permissionGroup pkgUser
// Copy the executable file
from("target/package/linux/bin/${pkgName}") {
fileMode 0500
into "bin"
}
// Copy the init file
from("target/package/linux/init/${pkgName}") {
fileMode 0500
into "init"
}
// Copy the config files
from("target/package/linux/conf") {
fileType CONFIG | NOREPLACE
fileMode 0754
into "conf"
}
// Copy web files
from("target/package/linux/web") {
into "web"
}
}
// Configure our RPM build task
buildRpm {
arch = X86_64
version = projectVersion.replace('-', '')
archiveName = "${pkgName}.rpm"
preInstall file("${buildDir}/control/rpm/preinst")
postInstall file("${buildDir}/control/rpm/postinst")
preUninstall file("${buildDir}/control/rpm/prerm")
postUninstall file("${buildDir}/control/rpm/postrm")
user pkgUser
permissionGroup pkgUser
// Copy the system unit files
from("${buildDir}/control/${pkgName}.service") {
addParentDirs = false
fileMode 0644
into "/usr/lib/systemd/system"
}
directory(pkgLogFolder, 0755)
link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
}
// Same as the buildRpm task
buildDeb {
arch = "amd64"
archiveName = "${pkgName}.deb"
configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
configurationFile("${pkgInstallFolder}/conf/custom-environment-variables.yml")
configurationFile("${pkgInstallFolder}/conf/default.yml")
configurationFile("${pkgInstallFolder}/conf/logger.js")
preInstall file("${buildDir}/control/deb/preinst")
postInstall file("${buildDir}/control/deb/postinst")
preUninstall file("${buildDir}/control/deb/prerm")
postUninstall file("${buildDir}/control/deb/postrm")
user pkgUser
permissionGroup pkgUser
directory(pkgLogFolder, 0755)
link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/init/${pkgName}")
link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
}

30
msa/web-ui/config/custom-environment-variables.yml

@ -0,0 +1,30 @@
#
# Copyright © 2016-2018 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.
#
server:
# Server bind address
address: "HTTP_BIND_ADDRESS"
# Server bind port
port: "HTTP_BIND_PORT"
thingsboard:
# ThingsBoard node host
host: "TB_HOST"
# ThingsBoard node port
port: "TB_PORT"
logger:
level: "LOGGER_LEVEL"
path: "LOG_FOLDER"
filename: "LOGGER_FILENAME"

30
msa/web-ui/config/default.yml

@ -0,0 +1,30 @@
#
# Copyright © 2016-2018 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.
#
server:
# Server bind address
address: "0.0.0.0"
# Server bind port
port: "8090"
thingsboard:
# ThingsBoard node host
host: "localhost"
# ThingsBoard node port
port: "8080"
logger:
level: "info"
path: "logs"
filename: "tb-web-ui-%DATE%.log"

59
msa/web-ui/config/logger.js

@ -0,0 +1,59 @@
/*
* Copyright © 2016-2018 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.
*/
var config = require('config'),
path = require('path'),
DailyRotateFile = require('winston-daily-rotate-file');
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf, splat } = format;
var loggerTransports = [];
if (process.env.NODE_ENV !== 'production' || process.env.DOCKER_MODE === 'true') {
loggerTransports.push(new transports.Console({
handleExceptions: true
}));
} else {
var filename = path.join(config.get('logger.path'), config.get('logger.filename'));
var transport = new (DailyRotateFile)({
filename: filename,
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
handleExceptions: true
});
loggerTransports.push(transport);
}
const tbFormat = printf(info => {
return `${info.timestamp} [${info.label}] ${info.level.toUpperCase()}: ${info.message}`;
});
function _logger(moduleLabel) {
return createLogger({
level: config.get('logger.level'),
format:combine(
splat(),
label({ label: moduleLabel }),
timestamp({format: 'YYYY-MM-DD HH:mm:ss,SSS'}),
tbFormat
),
transports: loggerTransports
});
}
module.exports = _logger;

20
msa/web-ui/config/tb-web-ui.conf

@ -0,0 +1,20 @@
#
# Copyright © 2016-2018 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.
#
export NODE_CONFIG_DIR=${pkg.installFolder}/conf
export LOG_FOLDER=${pkg.logFolder}
export NODE_ENV=production
export WEB_FOLDER=${pkg.installFolder}/web

26
msa/web-ui/docker/Dockerfile

@ -0,0 +1,26 @@
#
# Copyright © 2016-2018 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.
#
FROM debian:stretch
COPY start-web-ui.sh ${pkg.name}.deb /tmp/
RUN chmod a+x /tmp/*.sh \
&& mv /tmp/start-web-ui.sh /usr/bin
RUN dpkg -i /tmp/${pkg.name}.deb
CMD ["start-web-ui.sh"]

29
msa/web-ui/docker/start-web-ui.sh

@ -0,0 +1,29 @@
#!/bin/bash
#
# Copyright © 2016-2018 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.
#
echo "Starting '${project.name}' ..."
CONF_FOLDER="${pkg.installFolder}/conf"
mainfile=${pkg.installFolder}/bin/${pkg.name}
configfile=${pkg.name}.conf
identity=${pkg.name}
source "${CONF_FOLDER}/${configfile}"
su -s /bin/sh -c "$mainfile"

42
msa/web-ui/install.js

@ -0,0 +1,42 @@
/*
* Copyright © 2016-2018 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.
*/
const fs = require('fs');
const fse = require('fs-extra');
const path = require('path');
let _projectRoot = null;
(async() => {
await fse.move(path.join(projectRoot(), 'target', 'thingsboard-web-ui-linux'),
path.join(targetPackageDir('linux'), 'bin', 'tb-web-ui'),
{overwrite: true});
await fse.move(path.join(projectRoot(), 'target', 'thingsboard-web-ui-win.exe'),
path.join(targetPackageDir('windows'), 'bin', 'tb-web-ui.exe'),
{overwrite: true});
})();
function projectRoot() {
if (!_projectRoot) {
_projectRoot = __dirname;
}
return _projectRoot;
}
function targetPackageDir(platform) {
return path.join(projectRoot(), 'target', 'package', platform);
}

38
msa/web-ui/package.json

@ -0,0 +1,38 @@
{
"name": "thingsboard-web-ui",
"private": true,
"version": "2.2.0",
"description": "ThingsBoard Web UI Microservice",
"main": "server.js",
"bin": "server.js",
"scripts": {
"install": "pkg -t node8-linux-x64,node8-win-x64 --out-path ./target . && node install.js",
"test": "echo \"Error: no test specified\" && exit 1",
"start": "WEB_FOLDER=./target/web nodemon server.js",
"start-prod": "NODE_ENV=production nodemon server.js"
},
"dependencies": {
"config": "^1.30.0",
"connect-history-api-fallback": "^1.5.0",
"express": "^4.16.3",
"http": "0.0.0",
"http-proxy": "^1.17.0",
"js-yaml": "^3.12.0",
"winston": "^3.0.0",
"winston-daily-rotate-file": "^3.2.1"
},
"engine": "node >= 5.9.0",
"nyc": {
"exclude": [
"test",
"__tests__",
"node_modules",
"target"
]
},
"devDependencies": {
"fs-extra": "^6.0.1",
"nodemon": "^1.17.5",
"pkg": "^4.3.3"
}
}

372
msa/web-ui/pom.xml

@ -0,0 +1,372 @@
<!--
Copyright © 2016-2018 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.2.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>
<artifactId>web-ui</artifactId>
<packaging>pom</packaging>
<name>ThingsBoard Web UI Microservice</name>
<url>https://thingsboard.io</url>
<description>Service for hosting ThingsBoard Web UI</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
<pkg.name>tb-web-ui</pkg.name>
<pkg.user>thingsboard</pkg.user>
<pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
<pkg.linux.dist>${project.build.directory}/package/linux</pkg.linux.dist>
<pkg.win.dist>${project.build.directory}/package/windows</pkg.win.dist>
<dockerfile.skip>true</dockerfile.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard</groupId>
<artifactId>ui</artifactId>
<version>${project.version}</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sun.winsw</groupId>
<artifactId>winsw</artifactId>
<classifier>bin</classifier>
<type>exe</type>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<installDirectory>target</installDirectory>
<workingDirectory>${basedir}</workingDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v8.11.3</nodeVersion>
<npmVersion>5.6.0</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>extract-web-ui</id>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.thingsboard</groupId>
<artifactId>ui</artifactId>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/web</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>copy-winsw-service</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.sun.winsw</groupId>
<artifactId>winsw</artifactId>
<classifier>bin</classifier>
<type>exe</type>
<destFileName>service.exe</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${pkg.win.dist}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-linux-conf</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${pkg.linux.dist}/conf</outputDirectory>
<resources>
<resource>
<directory>config</directory>
<filtering>true</filtering>
</resource>
</resources>
<filters>
<filter>src/main/filters/unix.properties</filter>
</filters>
</configuration>
</execution>
<execution>
<id>copy-linux-init</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${pkg.linux.dist}/init</outputDirectory>
<resources>
<resource>
<directory>src/main/scripts/init</directory>
<filtering>true</filtering>
</resource>
</resources>
<filters>
<filter>src/main/filters/unix.properties</filter>
</filters>
</configuration>
</execution>
<execution>
<id>copy-win-conf</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${pkg.win.dist}/conf</outputDirectory>
<resources>
<resource>
<directory>config</directory>
<excludes>
<exclude>tb-web-ui.conf</exclude>
</excludes>
<filtering>true</filtering>
</resource>
</resources>
<filters>
<filter>src/main/filters/windows.properties</filter>
</filters>
</configuration>
</execution>
<execution>
<id>copy-control</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/control</outputDirectory>
<resources>
<resource>
<directory>src/main/scripts/control</directory>
<filtering>true</filtering>
</resource>
</resources>
<filters>
<filter>src/main/filters/unix.properties</filter>
</filters>
</configuration>
</execution>
<execution>
<id>copy-windows-control</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${pkg.win.dist}</outputDirectory>
<resources>
<resource>
<directory>src/main/scripts/windows</directory>
<filtering>true</filtering>
</resource>
</resources>
<filters>
<filter>src/main/filters/windows.properties</filter>
</filters>
</configuration>
</execution>
<execution>
<id>copy-docker-config</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>docker</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.fortasoft</groupId>
<artifactId>gradle-maven-plugin</artifactId>
<configuration>
<tasks>
<task>build</task>
<task>buildDeb</task>
<task>buildRpm</task>
</tasks>
<args>
<arg>-PprojectBuildDir=${project.build.directory}</arg>
<arg>-PprojectVersion=${project.version}</arg>
<arg>-PpkgName=${pkg.name}</arg>
<arg>-PpkgUser=${pkg.user}</arg>
<arg>-PpkgInstallFolder=${pkg.installFolder}</arg>
<arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>
</args>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>invoke</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<finalName>${pkg.name}</finalName>
<descriptors>
<descriptor>src/main/assembly/windows.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<id>build-docker-image</id>
<phase>pre-integration-test</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<skip>${dockerfile.skip}</skip>
<repository>local-maven-build/${pkg.name}</repository>
<verbose>true</verbose>
<googleContainerRegistryEnabled>false</googleContainerRegistryEnabled>
<contextDirectory>${project.build.directory}</contextDirectory>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>npm-start</id>
<activation>
<property>
<name>npm-start</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<installDirectory>target</installDirectory>
<workingDirectory>${basedir}</workingDirectory>
</configuration>
<executions>
<execution>
<id>npm start</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>start</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>jenkins</id>
<name>Jenkins Repository</name>
<url>http://repo.jenkins-ci.org/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

129
msa/web-ui/server.js

@ -0,0 +1,129 @@
/*
* Copyright © 2016-2018 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.
*/
const config = require('config'),
logger = require('./config/logger')('main'),
express = require('express'),
http = require('http'),
httpProxy = require('http-proxy'),
path = require('path'),
historyApiFallback = require("connect-history-api-fallback");
var server;
(async() => {
try {
logger.info('Starting ThingsBoard Web UI Microservice...');
const bindAddress = config.get('server.address');
const bindPort = config.get('server.port');
const thingsboardHost = config.get('thingsboard.host');
const thingsboardPort = config.get('thingsboard.port');
logger.info('Bind address: %s', bindAddress);
logger.info('Bind port: %s', bindPort);
logger.info('ThingsBoard host: %s', thingsboardHost);
logger.info('ThingsBoard port: %s', thingsboardPort);
var webDir = path.join(__dirname, 'web');
if (typeof process.env.WEB_FOLDER === 'string') {
webDir = path.resolve(process.env.WEB_FOLDER);
}
logger.info('Web folder: %s', webDir);
const app = express();
server = http.createServer(app);
const apiProxy = httpProxy.createProxyServer({
target: {
host: thingsboardHost,
port: thingsboardPort
}
});
apiProxy.on('error', function (err, req, res) {
logger.warn('API proxy error: %s', err.message);
res.writeHead(500);
if (err.code && err.code === 'ECONNREFUSED') {
res.end('Unable to connect to ThingsBoard server.');
} else {
res.end('Thingsboard server connection error: ' + err.code ? err.code : '');
}
});
const root = path.join(webDir, 'public');
const staticDir = path.join(root, 'static');
app.all('/api/*', (req, res) => {
logger.info(req.method + ' ' + req.originalUrl);
apiProxy.web(req, res);
});
app.all('/static/rulenode/*', (req, res) => {
apiProxy.web(req, res);
});
app.use(historyApiFallback());
app.use('/static', express.static(staticDir));
app.get('*', (req, res) => {
apiProxy.web(req, res);
});
server.on('upgrade', (req, socket, head) => {
apiProxy.ws(req, socket, head);
});
server.listen(bindPort, bindAddress, (error) => {
if (error) {
logger.error('Failed to start ThingsBoard Web UI Microservice: %s', e.message);
logger.error(error.stack);
exit(-1);
} else {
logger.info('==> 🌎 Listening on port %s.', bindPort);
logger.info('Started ThingsBoard Web UI Microservice.');
}
});
} catch (e) {
logger.error('Failed to start ThingsBoard Web UI Microservice: %s', e.message);
logger.error(e.stack);
exit(-1);
}
})();
process.on('exit', function () {
exit(0);
});
function exit(status) {
logger.info('Exiting with status: %d ...', status);
if (server) {
logger.info('Stopping HTTP Server...');
var _server = server;
server = null;
_server.close(() => {
logger.info('HTTP Server stopped.');
process.exit(status);
});
} else {
process.exit(status);
}
}

75
msa/web-ui/src/main/assembly/windows.xml

@ -0,0 +1,75 @@
<!--
Copyright © 2016-2018 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.
-->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>windows</id>
<formats>
<format>zip</format>
</formats>
<!-- Workaround to create logs directory -->
<fileSets>
<fileSet>
<directory>${pkg.win.dist}</directory>
<outputDirectory>logs</outputDirectory>
<excludes>
<exclude>*/**</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${pkg.win.dist}/conf</directory>
<outputDirectory>conf</outputDirectory>
<lineEnding>windows</lineEnding>
</fileSet>
<fileSet>
<directory>${project.build.directory}/web</directory>
<outputDirectory>web</outputDirectory>
</fileSet>
</fileSets>
<files>
<file>
<source>${pkg.win.dist}/bin/${pkg.name}.exe</source>
<outputDirectory>bin</outputDirectory>
<destName>${pkg.name}.exe</destName>
</file>
<file>
<source>${pkg.win.dist}/service.exe</source>
<outputDirectory/>
<destName>${pkg.name}.exe</destName>
</file>
<file>
<source>${pkg.win.dist}/service.xml</source>
<outputDirectory/>
<destName>${pkg.name}.xml</destName>
<lineEnding>windows</lineEnding>
</file>
<file>
<source>${pkg.win.dist}/install.bat</source>
<outputDirectory/>
<lineEnding>windows</lineEnding>
</file>
<file>
<source>${pkg.win.dist}/uninstall.bat</source>
<outputDirectory/>
<lineEnding>windows</lineEnding>
</file>
</files>
</assembly>

1
msa/web-ui/src/main/filters/unix.properties

@ -0,0 +1 @@
pkg.logFolder=${pkg.unixLogFolder}

2
msa/web-ui/src/main/filters/windows.properties

@ -0,0 +1,2 @@
pkg.logFolder=${BASE}\\logs
pkg.winWrapperLogFolder=%BASE%\\logs

6
msa/web-ui/src/main/scripts/control/deb/postinst

@ -0,0 +1,6 @@
#!/bin/sh
chown -R ${pkg.user}: ${pkg.logFolder}
chown -R ${pkg.user}: ${pkg.installFolder}
# update-rc.d ${pkg.name} defaults

3
msa/web-ui/src/main/scripts/control/deb/postrm

@ -0,0 +1,3 @@
#!/bin/sh
update-rc.d -f ${pkg.name} remove

18
msa/web-ui/src/main/scripts/control/deb/preinst

@ -0,0 +1,18 @@
#!/bin/sh
if ! getent group ${pkg.user} >/dev/null; then
addgroup --system ${pkg.user}
fi
if ! getent passwd ${pkg.user} >/dev/null; then
adduser --quiet \
--system \
--ingroup ${pkg.user} \
--quiet \
--disabled-login \
--disabled-password \
--home ${pkg.installFolder} \
--no-create-home \
-gecos "Thingsboard application" \
${pkg.user}
fi

5
msa/web-ui/src/main/scripts/control/deb/prerm

@ -0,0 +1,5 @@
#!/bin/sh
if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then
service ${pkg.name} stop
fi

9
msa/web-ui/src/main/scripts/control/rpm/postinst

@ -0,0 +1,9 @@
#!/bin/sh
chown -R ${pkg.user}: ${pkg.logFolder}
chown -R ${pkg.user}: ${pkg.installFolder}
if [ $1 -eq 1 ] ; then
# Initial installation
systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || :
fi

6
msa/web-ui/src/main/scripts/control/rpm/postrm

@ -0,0 +1,6 @@
#!/bin/sh
if [ $1 -ge 1 ] ; then
# Package upgrade, not uninstall
systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || :
fi

6
msa/web-ui/src/main/scripts/control/rpm/preinst

@ -0,0 +1,6 @@
#!/bin/sh
getent group ${pkg.user} >/dev/null || groupadd -r ${pkg.user}
getent passwd ${pkg.user} >/dev/null || \
useradd -d ${pkg.installFolder} -g ${pkg.user} -M -r ${pkg.user} -s /sbin/nologin \
-c "Thingsboard application"

6
msa/web-ui/src/main/scripts/control/rpm/prerm

@ -0,0 +1,6 @@
#!/bin/sh
if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
fi

11
msa/web-ui/src/main/scripts/control/tb-web-ui.service

@ -0,0 +1,11 @@
[Unit]
Description=${pkg.name}
After=syslog.target
[Service]
User=${pkg.user}
ExecStart=${pkg.installFolder}/init/${pkg.name}
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target

233
msa/web-ui/src/main/scripts/init/tb-web-ui

@ -0,0 +1,233 @@
#!/bin/bash
#
### BEGIN INIT INFO
# Provides: tb-web-ui
# Required-Start: $remote_fs $syslog $network
# Required-Stop: $remote_fs $syslog $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: ${project.name}
# Description: ${project.description}
# chkconfig: 2345 99 01
### END INIT INFO
[[ -n "$DEBUG" ]] && set -x
# Initialize variables that cannot be provided by a .conf file
WORKING_DIR="$(pwd)"
# shellcheck disable=SC2153
mainfile=${pkg.installFolder}/bin/${pkg.name}
configfile=${pkg.name}.conf
# Follow symlinks to find the real script and detect init.d script
cd "$(dirname "$0")" || exit 1
[[ -z "$initfile" ]] && initfile=$(pwd)/$(basename "$0")
while [[ -L "$initfile" ]]; do
[[ "$initfile" =~ init\.d ]] && init_script=$(basename "$initfile")
initfile=$(readlink "$initfile")
cd "$(dirname "$initfile")" || exit 1
initfile=$(pwd)/$(basename "$initfile")
done
initfolder="$( (cd "$(dirname "initfile")" && pwd -P) )"
cd "$WORKING_DIR" || exit 1
# Initialize CONF_FOLDER location
[[ -z "$CONF_FOLDER" ]] && CONF_FOLDER="${pkg.installFolder}/conf"
# shellcheck source=/dev/null
[[ -r "${CONF_FOLDER}/${configfile}" ]] && source "${CONF_FOLDER}/${configfile}"
# Initialize PID/LOG locations if they weren't provided by the config file
[[ -z "$PID_FOLDER" ]] && PID_FOLDER="/var/run"
[[ -z "$LOG_FOLDER" ]] && LOG_FOLDER="${pkg.unixLogFolder}"
! [[ "$PID_FOLDER" == /* ]] && PID_FOLDER="$(dirname "$mainfile")"/"$PID_FOLDER"
! [[ "$LOG_FOLDER" == /* ]] && LOG_FOLDER="$(dirname "$mainfile")"/"$LOG_FOLDER"
! [[ -x "$PID_FOLDER" ]] && PID_FOLDER="/tmp"
! [[ -x "$LOG_FOLDER" ]] && LOG_FOLDER="/tmp"
# Set up defaults
[[ -z "$MODE" ]] && MODE="auto" # modes are "auto", "service" or "run"
[[ -z "$USE_START_STOP_DAEMON" ]] && USE_START_STOP_DAEMON="true"
# Create an identity for log/pid files
if [[ -z "$identity" ]]; then
if [[ -n "$init_script" ]]; then
identity="${init_script}"
else
identity=$(basename "${initfile%.*}")_${initfolder//\//}
fi
fi
# Initialize log file name if not provided by the config file
[[ -z "$LOG_FILENAME" ]] && LOG_FILENAME="${identity}.log"
# ANSI Colors
echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; }
echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; }
echoYellow() { echo $'\e[0;33m'"$1"$'\e[0m'; }
# Utility functions
checkPermissions() {
touch "$pid_file" &> /dev/null || { echoRed "Operation not permitted (cannot access pid file)"; return 4; }
touch "$log_file" &> /dev/null || { echoRed "Operation not permitted (cannot access log file)"; return 4; }
}
isRunning() {
ps -p "$1" &> /dev/null
}
await_file() {
end=$(date +%s)
let "end+=10"
while [[ ! -s "$1" ]]
do
now=$(date +%s)
if [[ $now -ge $end ]]; then
break
fi
sleep 1
done
}
# Determine the script mode
action="run"
if [[ "$MODE" == "auto" && -n "$init_script" ]] || [[ "$MODE" == "service" ]]; then
action="$1"
shift
fi
# Build the pid and log filenames
if [[ "$identity" == "$init_script" ]] || [[ "$identity" == "$APP_NAME" ]]; then
PID_FOLDER="$PID_FOLDER/${identity}"
pid_subfolder=$PID_FOLDER
fi
pid_file="$PID_FOLDER/${identity}.pid"
log_file="$LOG_FOLDER/$LOG_FILENAME"
# Determine the user to run as if we are root
# shellcheck disable=SC2012
[[ $(id -u) == "0" ]] && run_user=$(ls -ld "$mainfile" | awk '{print $3}')
arguments=($RUN_ARGS "$@")
# Action functions
start() {
if [[ -f "$pid_file" ]]; then
pid=$(cat "$pid_file")
isRunning "$pid" && { echoYellow "Already running [$pid]"; return 0; }
fi
do_start "$@"
}
do_start() {
working_dir=$(dirname "$mainfile")
pushd "$working_dir" > /dev/null
mkdir -p "$PID_FOLDER" &> /dev/null
if [[ -n "$run_user" ]]; then
checkPermissions || return $?
if [[ -z "$pid_subfolder" ]]; then
chown "$run_user" "$pid_subfolder"
fi
chown "$run_user" "$pid_file"
chown "$run_user" "$log_file"
if [ $USE_START_STOP_DAEMON = true ] && type start-stop-daemon > /dev/null 2>&1; then
start-stop-daemon --start --quiet \
--chuid "$run_user" \
--name "$identity" \
--make-pidfile --pidfile "$pid_file" \
--background --no-close \
--startas "$mainfile" \
--chdir "$working_dir" \
-- "${arguments[@]}" \
>> "$log_file" 2>&1
await_file "$pid_file"
else
su -s /bin/sh -c "$mainfile $(printf "\"%s\" " "${arguments[@]}") >> \"$log_file\" 2>&1 & echo \$!" "$run_user" > "$pid_file"
fi
pid=$(cat "$pid_file")
else
checkPermissions || return $?
"$mainfile" "${arguments[@]}" >> "$log_file" 2>&1 &
pid=$!
disown $pid
echo "$pid" > "$pid_file"
fi
[[ -z $pid ]] && { echoRed "Failed to start"; return 1; }
echoGreen "Started [$pid]"
}
stop() {
working_dir=$(dirname "$mainfile")
pushd "$working_dir" > /dev/null
[[ -f $pid_file ]] || { echoYellow "Not running (pidfile not found)"; return 0; }
pid=$(cat "$pid_file")
isRunning "$pid" || { echoYellow "Not running (process ${pid}). Removing stale pid file."; rm -f "$pid_file"; return 0; }
do_stop "$pid" "$pid_file"
}
do_stop() {
kill -2 "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; }
for i in $(seq 1 60); do
isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; }
[[ $i -eq 30 ]] && kill -9 "$1" &> /dev/null
sleep 1
done
echoRed "Unable to kill process $1";
return 1;
}
restart() {
stop && start
}
orce_reload() {
working_dir=$(dirname "$mainfile")
pushd "$working_dir" > /dev/null
[[ -f $pid_file ]] || { echoRed "Not running (pidfile not found)"; return 7; }
pid=$(cat "$pid_file")
rm -f "$pid_file"
isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 7; }
do_stop "$pid" "$pid_file"
do_start
}
status() {
working_dir=$(dirname "$mainfile")
pushd "$working_dir" > /dev/null
[[ -f "$pid_file" ]] || { echoRed "Not running"; return 3; }
pid=$(cat "$pid_file")
isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 1; }
echoGreen "Running [$pid]"
return 0
}
run() {
pushd "$(dirname "$mainfile")" > /dev/null
"$mainfile" "${arguments[@]}"
result=$?
popd > /dev/null
return "$result"
}
# Call the appropriate action function
case "$action" in
start)
start "$@"; exit $?;;
stop)
stop "$@"; exit $?;;
restart)
restart "$@"; exit $?;;
force-reload)
force_reload "$@"; exit $?;;
status)
status "$@"; exit $?;;
run)
run "$@"; exit $?;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status|run}"; exit 1;
esac
exit 0

31
msa/web-ui/src/main/scripts/windows/install.bat

@ -0,0 +1,31 @@
@REM
@REM Copyright © 2016-2018 The Thingsboard Authors
@REM
@REM Licensed under the Apache License, Version 2.0 (the "License");
@REM you may not use this file except in compliance with the License.
@REM You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing, software
@REM distributed under the License is distributed on an "AS IS" BASIS,
@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@REM See the License for the specific language governing permissions and
@REM limitations under the License.
@REM
@ECHO OFF
setlocal ENABLEEXTENSIONS
@ECHO Installing ${pkg.name} ...
SET BASE=%~dp0
%BASE%${pkg.name}.exe install
@ECHO ${pkg.name} installed successfully!
GOTO END
:END

30
msa/web-ui/src/main/scripts/windows/service.xml

@ -0,0 +1,30 @@
<!--
Copyright © 2016-2018 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.
-->
<service>
<id>${pkg.name}</id>
<name>${project.name}</name>
<description>${project.description}</description>
<workingdirectory>%BASE%\bin</workingdirectory>
<logpath>${pkg.winWrapperLogFolder}</logpath>
<logmode>rotate</logmode>
<env name="NODE_CONFIG_DIR" value="%BASE%\conf" />
<env name="LOG_FOLDER" value="${pkg.winWrapperLogFolder}" />
<env name="NODE_ENV" value="production" />
<env name="WEB_FOLDER" value="%BASE%\web" />
<executable>%BASE%\bin\${pkg.name}.exe</executable>
</service>

25
msa/web-ui/src/main/scripts/windows/uninstall.bat

@ -0,0 +1,25 @@
@REM
@REM Copyright © 2016-2018 The Thingsboard Authors
@REM
@REM Licensed under the Apache License, Version 2.0 (the "License");
@REM you may not use this file except in compliance with the License.
@REM You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing, software
@REM distributed under the License is distributed on an "AS IS" BASIS,
@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@REM See the License for the specific language governing permissions and
@REM limitations under the License.
@REM
@ECHO OFF
@ECHO Stopping ${pkg.name} ...
net stop ${pkg.name}
@ECHO Uninstalling ${pkg.name} ...
%~dp0${pkg.name}.exe uninstall
@ECHO DONE.

2
ui/package.json

@ -2,7 +2,7 @@
"name": "thingsboard",
"private": true,
"version": "2.2.0",
"description": "Thingsboard UI",
"description": "ThingsBoard UI",
"licenses": [
{
"type": "Apache-2.0",

Loading…
Cancel
Save