TB-43: Implement updates checking service
This commit is contained in:
parent
c745c1ce02
commit
05404d8727
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ pom.xml.versionsBackup
|
||||
**/target
|
||||
**/Californium.properties
|
||||
**/.env
|
||||
.instance_id
|
||||
|
||||
@ -88,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")
|
||||
@ -103,14 +111,6 @@ buildRpm {
|
||||
into "/usr/lib/systemd/system"
|
||||
}
|
||||
|
||||
from("target/conf") {
|
||||
include "${pkgName}.conf"
|
||||
filter(ReplaceTokens, tokens: ['pkg.platform': 'rpm'])
|
||||
fileType CONFIG | NOREPLACE
|
||||
fileMode 0754
|
||||
into "conf"
|
||||
}
|
||||
|
||||
directory(pkgLogFolder, 0755)
|
||||
link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
|
||||
link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")
|
||||
@ -125,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")
|
||||
@ -138,14 +146,6 @@ buildDeb {
|
||||
user pkgName
|
||||
permissionGroup pkgName
|
||||
|
||||
from("target/conf") {
|
||||
include "${pkgName}.conf"
|
||||
filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])
|
||||
fileType CONFIG | NOREPLACE
|
||||
fileMode 0754
|
||||
into "conf"
|
||||
}
|
||||
|
||||
directory(pkgLogFolder, 0755)
|
||||
link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")
|
||||
link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")
|
||||
|
||||
@ -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
|
||||
@ -76,10 +81,9 @@ public class AdminController extends BaseController {
|
||||
@PreAuthorize("hasAuthority('SYS_ADMIN')")
|
||||
@RequestMapping(value = "/updates", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public String checkUpdates() throws ThingsboardException {
|
||||
public UpdateMessage checkUpdates() throws ThingsboardException {
|
||||
try {
|
||||
return "<div>New update Thingsboard version 1.2 is available.<br/>" +
|
||||
"<a href='https://github.com/thingsboard/thingsboard/releases/download/v1.1/thingsboard-1.1.deb'>Download package</a></div>";
|
||||
return updateService.checkUpdates();
|
||||
} catch (Exception e) {
|
||||
throw handleException(e);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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}"
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
Normal file
25
ui/src/app/services/info-toast.tpl.html
Normal file
@ -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>
|
||||
@ -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;
|
||||
|
||||
@ -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…
x
Reference in New Issue
Block a user