Browse Source

Merge pull request #67 from thingsboard/feature/TB-43

TB-43: Updates service
pull/72/head
Andrew Shvayka 10 years ago
committed by GitHub
parent
commit
8f2217d9ac
  1. 1
      .gitignore
  2. 20
      application/build.gradle
  3. 2
      application/src/main/conf/thingsboard.conf
  4. 17
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  5. 128
      application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
  6. 25
      application/src/main/java/org/thingsboard/server/service/update/UpdateService.java
  7. 26
      application/src/main/java/org/thingsboard/server/service/update/model/UpdateMessage.java
  8. 4
      application/src/main/resources/thingsboard.yml
  9. 1
      application/src/main/scripts/windows/service.xml
  10. 1
      ui/package.json
  11. 14
      ui/src/app/api/admin.service.js
  12. 10
      ui/src/app/api/user.service.js
  13. 25
      ui/src/app/services/info-toast.tpl.html
  14. 7
      ui/src/app/services/toast.scss
  15. 14
      ui/src/app/services/toast.service.js

1
.gitignore

@ -30,3 +30,4 @@ pom.xml.versionsBackup
**/target
**/Californium.properties
**/.env
.instance_id

20
application/build.gradle

@ -13,6 +13,9 @@
* 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"
@ -56,6 +59,7 @@ ospackage {
// Copy the config files
from("target/conf") {
exclude "${pkgName}.conf"
fileType CONFIG | NOREPLACE
fileMode 0754
into "conf"
@ -84,6 +88,14 @@ buildRpm {
requires("java-1.8.0")
from("target/conf") {
include "${pkgName}.conf"
filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
fileType CONFIG | NOREPLACE
fileMode 0754
into "${pkgInstallFolder}/conf"
}
preInstall file("${buildDir}/control/rpm/preinst")
postInstall file("${buildDir}/control/rpm/postinst")
preUninstall file("${buildDir}/control/rpm/prerm")
@ -113,6 +125,14 @@ buildDeb {
requires("openjdk-8-jre").or("java8-runtime").or("oracle-java8-installer").or("openjdk-8-jre-headless")
from("target/conf") {
include "${pkgName}.conf"
filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
fileType CONFIG | NOREPLACE
fileMode 0754
into "${pkgInstallFolder}/conf"
}
configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf")
configurationFile("${pkgInstallFolder}/conf/${pkgName}.yml")
configurationFile("${pkgInstallFolder}/conf/logback.xml")

2
application/src/main/conf/thingsboard.conf

@ -14,6 +14,6 @@
# limitations under the License.
#
export JAVA_OPTS="$JAVA_OPTS"
export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@"
export LOG_FILENAME=${pkg.name}.out
export LOADER_PATH=${pkg.installFolder}/conf,${pkg.installFolder}/extensions

17
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -22,6 +22,8 @@ import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.mail.MailService;
import org.thingsboard.server.service.update.UpdateService;
import org.thingsboard.server.service.update.model.UpdateMessage;
@RestController
@RequestMapping("/api/admin")
@ -33,6 +35,9 @@ public class AdminController extends BaseController {
@Autowired
private AdminSettingsService adminSettingsService;
@Autowired
private UpdateService updateService;
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/settings/{key}", method = RequestMethod.GET)
@ResponseBody
@ -72,4 +77,16 @@ public class AdminController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/updates", method = RequestMethod.GET)
@ResponseBody
public UpdateMessage checkUpdates() throws ThingsboardException {
try {
return updateService.checkUpdates();
} catch (Exception e) {
throw handleException(e);
}
}
}

128
application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java

@ -0,0 +1,128 @@
/**
* Copyright © 2016-2017 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.
*/
package org.thingsboard.server.service.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.service.update.model.UpdateMessage;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class DefaultUpdateService implements UpdateService {
private static final String INSTANCE_ID_FILE = ".instance_id";
private static final String UPDATE_SERVER_BASE_URL = "https://updates.thingsboard.io";
private static final String PLATFORM_PARAM = "platform";
private static final String VERSION_PARAM = "version";
private static final String INSTANCE_ID_PARAM = "instanceId";
@Value("${updates.enabled}")
private boolean updatesEnabled;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture checkUpdatesFuture = null;
private RestTemplate restClient = new RestTemplate();
private UpdateMessage updateMessage;
private String platform;
private String version;
private UUID instanceId = null;
@PostConstruct
private void init() {
updateMessage = new UpdateMessage("", false);
if (updatesEnabled) {
try {
platform = System.getProperty("platform", "unknown");
version = getClass().getPackage().getImplementationVersion();
if (version == null) {
version = "unknown";
}
Path instanceIdPath = Paths.get(INSTANCE_ID_FILE);
if (Files.exists(instanceIdPath)) {
byte[] data = Files.readAllBytes(instanceIdPath);
if (data != null && data.length > 0) {
try {
instanceId = UUID.fromString(new String(data));
} catch (IllegalArgumentException e) {
}
}
}
if (instanceId == null) {
instanceId = UUID.randomUUID();
Files.write(instanceIdPath, instanceId.toString().getBytes());
}
checkUpdatesFuture = scheduler.scheduleAtFixedRate(checkUpdatesRunnable, 0, 1, TimeUnit.HOURS);
} catch (Exception e) {}
}
}
@PreDestroy
private void destroy() {
try {
if (checkUpdatesFuture != null) {
checkUpdatesFuture.cancel(true);
}
scheduler.shutdownNow();
} catch (Exception e) {}
}
Runnable checkUpdatesRunnable = new Runnable() {
@Override
public void run() {
try {
log.trace("Executing check update method for instanceId [{}], platform [{}] and version [{}]", instanceId, platform, version);
ObjectNode request = new ObjectMapper().createObjectNode();
request.put(PLATFORM_PARAM, platform);
request.put(VERSION_PARAM, version);
request.put(INSTANCE_ID_PARAM, instanceId.toString());
JsonNode response = restClient.postForObject(UPDATE_SERVER_BASE_URL+"/api/thingsboard/updates", request, JsonNode.class);
updateMessage = new UpdateMessage(
response.get("message").asText(),
response.get("updateAvailable").asBoolean()
);
} catch (Exception e) {
log.trace(e.getMessage());
}
}
};
@Override
public UpdateMessage checkUpdates() {
return updateMessage;
}
}

25
application/src/main/java/org/thingsboard/server/service/update/UpdateService.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2017 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.
*/
package org.thingsboard.server.service.update;
import org.thingsboard.server.service.update.model.UpdateMessage;
public interface UpdateService {
UpdateMessage checkUpdates();
}

26
application/src/main/java/org/thingsboard/server/service/update/model/UpdateMessage.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2017 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.
*/
package org.thingsboard.server.service.update.model;
import lombok.Data;
@Data
public class UpdateMessage {
private final String message;
private final boolean isUpdateAvailable;
}

4
application/src/main/resources/thingsboard.yml

@ -184,3 +184,7 @@ cache:
policy: "${CACHE_DEVICE_CREDENTIAL_MAX_SIZE_POLICY:PER_NODE}"
size: "${CACHE_DEVICE_CREDENTIAL_MAX_SIZE_SIZE:1000000}"
# Check new version updates parameters
updates:
# Enable/disable updates checking.
enabled: "${UPDATES_ENABLED:true}"

1
application/src/main/scripts/windows/service.xml

@ -7,6 +7,7 @@
<logmode>rotate</logmode>
<env name="LOADER_PATH" value="%BASE%\conf,%BASE%\extensions" />
<executable>java</executable>
<startargument>-Dplatform=windows</startargument>
<startargument>-jar</startargument>
<startargument>%BASE%\lib\${pkg.name}.jar</startargument>
</service>

1
ui/package.json

@ -64,6 +64,7 @@
"ngreact": "^0.3.0",
"objectpath": "^1.2.1",
"oclazyload": "^1.0.9",
"raphael": "^2.2.7",
"rc-select": "^6.6.1",
"react": "^15.4.1",
"react-ace": "^4.1.0",

14
ui/src/app/api/admin.service.js

@ -23,7 +23,8 @@ function AdminService($http, $q) {
var service = {
getAdminSettings: getAdminSettings,
saveAdminSettings: saveAdminSettings,
sendTestMail: sendTestMail
sendTestMail: sendTestMail,
checkUpdates: checkUpdates
}
return service;
@ -60,4 +61,15 @@ function AdminService($http, $q) {
});
return deferred.promise;
}
function checkUpdates() {
var deferred = $q.defer();
var url = '/api/admin/updates';
$http.get(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

10
ui/src/app/api/user.service.js

@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
.name;
/*@ngInject*/
function UserService($http, $q, $rootScope, store, jwtHelper, $translate, $state) {
function UserService($http, $q, $rootScope, adminService, toast, store, jwtHelper, $translate, $state) {
var currentUser = null,
currentUserDetails = null,
userLoaded = false;
@ -382,6 +382,14 @@ function UserService($http, $q, $rootScope, store, jwtHelper, $translate, $state
place = 'home.dashboards.dashboard';
params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId};
}
} else if (currentUser.authority === 'SYS_ADMIN') {
adminService.checkUpdates().then(
function (updateMessage) {
if (updateMessage && updateMessage.updateAvailable) {
toast.showInfo(updateMessage.message, 0, null, 'bottom right');
}
}
);
}
$state.go(place, params);
} else {

25
ui/src/app/services/info-toast.tpl.html

@ -0,0 +1,25 @@
<!--
Copyright © 2016-2017 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.
-->
<md-toast class="tb-info-toast">
<div class="md-toast-content">
<div class="md-toast-text" ng-bind-html="vm.message"></div>
<md-button class="md-action md-highlight md-accent" ng-click="vm.closeToast()">
{{ 'action.close' | translate }}
</md-button>
</div>
</md-toast>

7
ui/src/app/services/toast.scss

@ -13,6 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
md-toast.tb-info-toast .md-toast-content {
font-size: 18px;
padding: 18px;
height: 100%;
}
md-toast.tb-success-toast .md-toast-content {
font-size: 18px !important;
background-color: green;

14
ui/src/app/services/toast.service.js

@ -15,6 +15,7 @@
*/
/* eslint-disable import/no-unresolved, import/default */
import infoToast from './info-toast.tpl.html';
import successToast from './success-toast.tpl.html';
import errorToast from './error-toast.tpl.html';
@ -26,6 +27,7 @@ export default function Toast($mdToast, $document) {
var showing = false;
var service = {
showInfo: showInfo,
showSuccess: showSuccess,
showError: showError,
hide: hide
@ -33,7 +35,15 @@ export default function Toast($mdToast, $document) {
return service;
function showInfo(infoMessage, delay, toastParent, position) {
showMessage(infoToast, infoMessage, delay, toastParent, position);
}
function showSuccess(successMessage, delay, toastParent, position) {
showMessage(successToast, successMessage, delay, toastParent, position);
}
function showMessage(templateUrl, message, delay, toastParent, position) {
if (!toastParent) {
toastParent = angular.element($document[0].getElementById('toast-parent'));
}
@ -45,8 +55,8 @@ export default function Toast($mdToast, $document) {
position: position,
controller: 'ToastController',
controllerAs: 'vm',
templateUrl: successToast,
locals: {message: successMessage},
templateUrl: templateUrl,
locals: {message: message},
parent: toastParent
});
}

Loading…
Cancel
Save