Merge pull request #9457 from thingsboard/feature/connectivity-ui
Device connectivity settings move in UI
This commit is contained in:
commit
5652166b98
@ -265,6 +265,7 @@ public class ThingsboardInstallService {
|
|||||||
case "3.6.0":
|
case "3.6.0":
|
||||||
log.info("Upgrading ThingsBoard from version 3.6.0 to 3.6.1 ...");
|
log.info("Upgrading ThingsBoard from version 3.6.0 to 3.6.1 ...");
|
||||||
databaseEntitiesUpgradeService.upgradeDatabase("3.6.0");
|
databaseEntitiesUpgradeService.upgradeDatabase("3.6.0");
|
||||||
|
dataUpdateService.updateData("3.6.0");
|
||||||
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
|
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -264,6 +264,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
ObjectNode node = JacksonUtil.newObjectNode();
|
ObjectNode node = JacksonUtil.newObjectNode();
|
||||||
node.put("baseUrl", "http://localhost:8080");
|
node.put("baseUrl", "http://localhost:8080");
|
||||||
node.put("prohibitDifferentUrl", false);
|
node.put("prohibitDifferentUrl", false);
|
||||||
|
node.set("connectivity", createDeviceConnectivityConfiguration());
|
||||||
generalSettings.setJsonValue(node);
|
generalSettings.setJsonValue(node);
|
||||||
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
|
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
|
||||||
|
|
||||||
@ -284,6 +285,53 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
node.put("showChangePassword", false);
|
node.put("showChangePassword", false);
|
||||||
mailSettings.setJsonValue(node);
|
mailSettings.setJsonValue(node);
|
||||||
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
|
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
|
||||||
|
|
||||||
|
AdminSettings connectivitySettings = new AdminSettings();
|
||||||
|
connectivitySettings.setTenantId(TenantId.SYS_TENANT_ID);
|
||||||
|
connectivitySettings.setKey("connectivity");
|
||||||
|
connectivitySettings.setJsonValue(createDeviceConnectivityConfiguration());
|
||||||
|
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, connectivitySettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectNode createDeviceConnectivityConfiguration() {
|
||||||
|
ObjectNode config = JacksonUtil.newObjectNode();
|
||||||
|
|
||||||
|
ObjectNode http = JacksonUtil.newObjectNode();
|
||||||
|
http.put("enabled", true);
|
||||||
|
http.put("host", "");
|
||||||
|
http.put("port", 8080);
|
||||||
|
config.set("http", http);
|
||||||
|
|
||||||
|
ObjectNode https = JacksonUtil.newObjectNode();
|
||||||
|
https.put("enabled", false);
|
||||||
|
https.put("host", "");
|
||||||
|
https.put("port", 443);
|
||||||
|
config.set("https", https);
|
||||||
|
|
||||||
|
ObjectNode mqtt = JacksonUtil.newObjectNode();
|
||||||
|
mqtt.put("enabled", true);
|
||||||
|
mqtt.put("host", "");
|
||||||
|
mqtt.put("port", 1883);
|
||||||
|
config.set("mqtt", mqtt);
|
||||||
|
|
||||||
|
ObjectNode mqtts = JacksonUtil.newObjectNode();
|
||||||
|
mqtts.put("enabled", false);
|
||||||
|
mqtts.put("host", "");
|
||||||
|
mqtts.put("port", 8883);
|
||||||
|
config.set("mqtts", mqtts);
|
||||||
|
|
||||||
|
ObjectNode coap = JacksonUtil.newObjectNode();
|
||||||
|
coap.put("enabled", true);
|
||||||
|
coap.put("host", "");
|
||||||
|
coap.put("port", 5683);
|
||||||
|
config.set("coap", coap);
|
||||||
|
|
||||||
|
ObjectNode coaps = JacksonUtil.newObjectNode();
|
||||||
|
coaps.put("enabled", false);
|
||||||
|
coaps.put("host", "");
|
||||||
|
coaps.put("port", 5684);
|
||||||
|
config.set("coaps", coaps);
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
|
|||||||
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
|
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
|
||||||
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
|
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
|
||||||
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
|
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.DataConstants;
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
import org.thingsboard.server.common.data.EntityView;
|
import org.thingsboard.server.common.data.EntityView;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
@ -70,6 +71,7 @@ import org.thingsboard.server.common.data.util.TbPair;
|
|||||||
import org.thingsboard.server.dao.DaoUtil;
|
import org.thingsboard.server.dao.DaoUtil;
|
||||||
import org.thingsboard.server.dao.alarm.AlarmDao;
|
import org.thingsboard.server.dao.alarm.AlarmDao;
|
||||||
import org.thingsboard.server.dao.audit.AuditLogDao;
|
import org.thingsboard.server.dao.audit.AuditLogDao;
|
||||||
|
import org.thingsboard.server.dao.device.DeviceConnectivityConfiguration;
|
||||||
import org.thingsboard.server.dao.edge.EdgeEventDao;
|
import org.thingsboard.server.dao.edge.EdgeEventDao;
|
||||||
import org.thingsboard.server.dao.entity.EntityService;
|
import org.thingsboard.server.dao.entity.EntityService;
|
||||||
import org.thingsboard.server.dao.entityview.EntityViewService;
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||||
@ -78,6 +80,7 @@ import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
|
|||||||
import org.thingsboard.server.dao.queue.QueueService;
|
import org.thingsboard.server.dao.queue.QueueService;
|
||||||
import org.thingsboard.server.dao.relation.RelationService;
|
import org.thingsboard.server.dao.relation.RelationService;
|
||||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||||
|
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||||
import org.thingsboard.server.dao.sql.JpaExecutorService;
|
import org.thingsboard.server.dao.sql.JpaExecutorService;
|
||||||
import org.thingsboard.server.dao.sql.device.DeviceProfileRepository;
|
import org.thingsboard.server.dao.sql.device.DeviceProfileRepository;
|
||||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
||||||
@ -161,6 +164,12 @@ public class DefaultDataUpdateService implements DataUpdateService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
JpaExecutorService jpaExecutorService;
|
JpaExecutorService jpaExecutorService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
AdminSettingsService adminSettingsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
DeviceConnectivityConfiguration connectivityConfiguration;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateData(String fromVersion) throws Exception {
|
public void updateData(String fromVersion) throws Exception {
|
||||||
switch (fromVersion) {
|
switch (fromVersion) {
|
||||||
@ -214,6 +223,11 @@ public class DefaultDataUpdateService implements DataUpdateService {
|
|||||||
case "3.5.1":
|
case "3.5.1":
|
||||||
log.info("Updating data from version 3.5.1 to 3.6.0 ...");
|
log.info("Updating data from version 3.5.1 to 3.6.0 ...");
|
||||||
migrateEdgeEvents("Starting edge events migration - adding seq_id column. ");
|
migrateEdgeEvents("Starting edge events migration - adding seq_id column. ");
|
||||||
|
migrateDeviceConnectivity();
|
||||||
|
break;
|
||||||
|
case "3.6.0":
|
||||||
|
log.info("Updating data from version 3.6.0 to 3.6.1 ...");
|
||||||
|
migrateDeviceConnectivity();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
|
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
|
||||||
@ -230,6 +244,14 @@ public class DefaultDataUpdateService implements DataUpdateService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void migrateDeviceConnectivity() {
|
||||||
|
AdminSettings connectivitySettings = new AdminSettings();
|
||||||
|
connectivitySettings.setTenantId(TenantId.SYS_TENANT_ID);
|
||||||
|
connectivitySettings.setKey("connectivity");
|
||||||
|
connectivitySettings.setJsonValue(JacksonUtil.valueToTree(connectivityConfiguration.getConnectivity()));
|
||||||
|
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, connectivitySettings);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void upgradeRuleNodes() {
|
public void upgradeRuleNodes() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -17,12 +17,14 @@ package org.thingsboard.server.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.mockito.AdditionalAnswers;
|
import org.mockito.AdditionalAnswers;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -32,6 +34,7 @@ import org.springframework.test.context.ContextConfiguration;
|
|||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||||
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.DeviceProfileType;
|
import org.thingsboard.server.common.data.DeviceProfileType;
|
||||||
@ -44,6 +47,7 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileCon
|
|||||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||||
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
|
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
|
||||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||||
@ -65,12 +69,7 @@ import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS;
|
|||||||
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM;
|
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM;
|
||||||
|
|
||||||
@TestPropertySource(properties = {
|
@TestPropertySource(properties = {
|
||||||
"device.connectivity.https.enabled=true",
|
"device.connectivity.mqtts.pem_cert_file=/tmp/" + CA_ROOT_CERT_PEM
|
||||||
"device.connectivity.http.port=8080",
|
|
||||||
"device.connectivity.https.port=444",
|
|
||||||
"device.connectivity.mqtts.enabled=true",
|
|
||||||
"device.connectivity.mqtts.pem_cert_file=/tmp/" + CA_ROOT_CERT_PEM,
|
|
||||||
"device.connectivity.coaps.enabled=true",
|
|
||||||
})
|
})
|
||||||
@ContextConfiguration(classes = {DeviceConnectivityControllerTest.Config.class})
|
@ContextConfiguration(classes = {DeviceConnectivityControllerTest.Config.class})
|
||||||
@DaoSqlTest
|
@DaoSqlTest
|
||||||
@ -122,6 +121,19 @@ public class DeviceConnectivityControllerTest extends AbstractControllerTest {
|
|||||||
|
|
||||||
loginSysAdmin();
|
loginSysAdmin();
|
||||||
|
|
||||||
|
AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class);
|
||||||
|
JsonNode connectivity = adminSettings.getJsonValue();
|
||||||
|
|
||||||
|
((ObjectNode)connectivity.get("http")).put("port", 8080);
|
||||||
|
((ObjectNode)connectivity.get("http")).put("enabled", true);
|
||||||
|
((ObjectNode)connectivity.get("https")).put("enabled", true);
|
||||||
|
((ObjectNode)connectivity.get("https")).put("port", 444);
|
||||||
|
((ObjectNode)connectivity.get("mqtt")).put("enabled", true);
|
||||||
|
((ObjectNode)connectivity.get("mqtts")).put("enabled", true);
|
||||||
|
((ObjectNode)connectivity.get("coap")).put("enabled", true);
|
||||||
|
((ObjectNode)connectivity.get("coaps")).put("enabled", true);
|
||||||
|
doPost("/api/admin/settings", adminSettings);
|
||||||
|
|
||||||
Tenant tenant = new Tenant();
|
Tenant tenant = new Tenant();
|
||||||
tenant.setTitle("My tenant");
|
tenant.setTitle("My tenant");
|
||||||
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
|
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
|
||||||
|
|||||||
@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -28,8 +29,8 @@ import org.mockito.Mockito;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
|
||||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||||
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
@ -43,14 +44,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP;
|
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP;
|
||||||
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS;
|
import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS;
|
||||||
|
|
||||||
@TestPropertySource(properties = {
|
|
||||||
"device.connectivity.https.enabled=true",
|
|
||||||
"device.connectivity.http.port=80",
|
|
||||||
"device.connectivity.mqtt.enabled=false",
|
|
||||||
"device.connectivity.mqtts.enabled=false",
|
|
||||||
"device.connectivity.coap.enabled=false",
|
|
||||||
"device.connectivity.coaps.enabled=false",
|
|
||||||
})
|
|
||||||
@ContextConfiguration(classes = {DeviceConnectivityControllerWithDefaultPortTest.Config.class})
|
@ContextConfiguration(classes = {DeviceConnectivityControllerWithDefaultPortTest.Config.class})
|
||||||
@DaoSqlTest
|
@DaoSqlTest
|
||||||
public class DeviceConnectivityControllerWithDefaultPortTest extends AbstractControllerTest {
|
public class DeviceConnectivityControllerWithDefaultPortTest extends AbstractControllerTest {
|
||||||
@ -73,6 +66,17 @@ public class DeviceConnectivityControllerWithDefaultPortTest extends AbstractCon
|
|||||||
|
|
||||||
loginSysAdmin();
|
loginSysAdmin();
|
||||||
|
|
||||||
|
AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class);
|
||||||
|
JsonNode connectivity = adminSettings.getJsonValue();
|
||||||
|
|
||||||
|
((ObjectNode) connectivity.get("http")).put("port", 80);
|
||||||
|
((ObjectNode) connectivity.get("https")).put("enabled", true);
|
||||||
|
((ObjectNode) connectivity.get("mqtt")).put("enabled", false);
|
||||||
|
((ObjectNode) connectivity.get("mqtts")).put("enabled", false);
|
||||||
|
((ObjectNode) connectivity.get("coaps")).put("enabled", false);
|
||||||
|
((ObjectNode) connectivity.get("coap")).put("enabled", false);
|
||||||
|
doPost("/api/admin/settings", adminSettings);
|
||||||
|
|
||||||
Tenant tenant = new Tenant();
|
Tenant tenant = new Tenant();
|
||||||
tenant.setTitle("My tenant");
|
tenant.setTitle("My tenant");
|
||||||
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
|
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import java.util.Map;
|
|||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "device")
|
@ConfigurationProperties(prefix = "device")
|
||||||
@Data
|
@Data
|
||||||
|
@Deprecated(since = "3.6.1")
|
||||||
public class DeviceConnectivityConfiguration {
|
public class DeviceConnectivityConfiguration {
|
||||||
private Map<String, DeviceConnectivityInfo> connectivity = new HashMap<>();
|
private Map<String, DeviceConnectivityInfo> connectivity = new HashMap<>();
|
||||||
|
|
||||||
|
|||||||
@ -22,5 +22,4 @@ public class DeviceConnectivityInfo {
|
|||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private String host;
|
private String host;
|
||||||
private String port;
|
private String port;
|
||||||
private String pemCertFile;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,14 +18,16 @@ package org.thingsboard.server.dao.device;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bouncycastle.cert.X509CertificateHolder;
|
import org.bouncycastle.cert.X509CertificateHolder;
|
||||||
import org.bouncycastle.openssl.PEMParser;
|
import org.bouncycastle.openssl.PEMParser;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.server.common.data.AdminSettings;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||||
@ -33,11 +35,12 @@ import org.thingsboard.server.common.data.ResourceUtils;
|
|||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
|
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||||
|
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||||
import org.thingsboard.server.dao.util.DeviceConnectivityUtil;
|
import org.thingsboard.server.dao.util.DeviceConnectivityUtil;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -64,6 +67,7 @@ import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.WINDOWS;
|
|||||||
|
|
||||||
@Service("DeviceConnectivityDaoService")
|
@Service("DeviceConnectivityDaoService")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class DeviceConnectivityServiceImpl implements DeviceConnectivityService {
|
public class DeviceConnectivityServiceImpl implements DeviceConnectivityService {
|
||||||
|
|
||||||
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||||
@ -74,26 +78,12 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
|
|
||||||
private final Map<String, Resource> certs = new ConcurrentHashMap<>();
|
private final Map<String, Resource> certs = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Autowired
|
private final DeviceCredentialsService deviceCredentialsService;
|
||||||
private DeviceCredentialsService deviceCredentialsService;
|
private final DeviceProfileService deviceProfileService;
|
||||||
|
private final AdminSettingsService adminSettingsService;
|
||||||
|
|
||||||
@Autowired
|
@Value("${device.connectivity.mqtts.pem_cert_file:}")
|
||||||
private DeviceProfileService deviceProfileService;
|
private String mqttsPemCertFile;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DeviceConnectivityConfiguration deviceConnectivityConfiguration;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
private void init() {
|
|
||||||
DeviceConnectivityInfo mqtts = deviceConnectivityConfiguration.getConnectivity(MQTTS);
|
|
||||||
if (mqtts != null && mqtts.isEnabled()) {
|
|
||||||
String certFilePath = mqtts.getPemCertFile();
|
|
||||||
if (StringUtils.isBlank(certFilePath) || !ResourceUtils.resourceExists(this, certFilePath)) {
|
|
||||||
String error = StringUtils.isBlank(certFilePath) ? "path is empty" : "file is not exists";
|
|
||||||
log.error("MQTTS is enabled but cert {}!", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException {
|
public JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException {
|
||||||
@ -149,11 +139,11 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId);
|
DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId);
|
||||||
|
|
||||||
ObjectNode commands = JacksonUtil.newObjectNode();
|
ObjectNode commands = JacksonUtil.newObjectNode();
|
||||||
if (deviceConnectivityConfiguration.isEnabled(MQTT)) {
|
if (isEnabled(MQTT)) {
|
||||||
Optional.ofNullable(getGatewayDockerCommands(baseUrl, creds, MQTT))
|
Optional.ofNullable(getGatewayDockerCommands(baseUrl, creds, MQTT))
|
||||||
.ifPresent(v -> commands.set(MQTT, v));
|
.ifPresent(v -> commands.set(MQTT, v));
|
||||||
}
|
}
|
||||||
if (deviceConnectivityConfiguration.isEnabled(MQTTS)) {
|
if (isEnabled(MQTTS)) {
|
||||||
Optional.ofNullable(getGatewayDockerCommands(baseUrl, creds, MQTTS))
|
Optional.ofNullable(getGatewayDockerCommands(baseUrl, creds, MQTTS))
|
||||||
.ifPresent(v -> commands.set(MQTTS, v));
|
.ifPresent(v -> commands.set(MQTTS, v));
|
||||||
}
|
}
|
||||||
@ -163,15 +153,15 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
@Override
|
@Override
|
||||||
public Resource getPemCertFile(String protocol) {
|
public Resource getPemCertFile(String protocol) {
|
||||||
return certs.computeIfAbsent(protocol, key -> {
|
return certs.computeIfAbsent(protocol, key -> {
|
||||||
DeviceConnectivityInfo connectivity = deviceConnectivityConfiguration.getConnectivity(protocol);
|
DeviceConnectivityInfo connectivity = getConnectivity(protocol);
|
||||||
if (connectivity == null) {
|
if (!MQTTS.equals(protocol) || connectivity == null) {
|
||||||
log.warn("Unknown connectivity protocol: {}", protocol);
|
log.warn("Unknown connectivity protocol: {}", protocol);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String certFilePath = connectivity.getPemCertFile();
|
|
||||||
if (StringUtils.isNotBlank(certFilePath) && ResourceUtils.resourceExists(this, certFilePath)) {
|
if (StringUtils.isNotBlank(mqttsPemCertFile) && ResourceUtils.resourceExists(this, mqttsPemCertFile)) {
|
||||||
try {
|
try {
|
||||||
return getCert(certFilePath);
|
return getCert(mqttsPemCertFile);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String msg = String.format("Failed to read %s server certificate!", protocol);
|
String msg = String.format("Failed to read %s server certificate!", protocol);
|
||||||
log.warn(msg);
|
log.warn(msg);
|
||||||
@ -183,6 +173,20 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DeviceConnectivityInfo getConnectivity(String protocol) {
|
||||||
|
AdminSettings connectivitySettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "connectivity");
|
||||||
|
JsonNode connectivity;
|
||||||
|
if (connectivitySettings != null && (connectivity = connectivitySettings.getJsonValue()) != null) {
|
||||||
|
return JacksonUtil.convertValue(connectivity.get(protocol), DeviceConnectivityInfo.class);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled(String protocol) {
|
||||||
|
var info = getConnectivity(protocol);
|
||||||
|
return info != null && info.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
private Resource getCert(String path) throws Exception {
|
private Resource getCert(String path) throws Exception {
|
||||||
StringBuilder pemContentBuilder = new StringBuilder();
|
StringBuilder pemContentBuilder = new StringBuilder();
|
||||||
|
|
||||||
@ -221,7 +225,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getHttpPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
private String getHttpPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity(protocol);
|
DeviceConnectivityInfo properties = getConnectivity(protocol);
|
||||||
if (properties == null || !properties.isEnabled() ||
|
if (properties == null || !properties.isEnabled() ||
|
||||||
deviceCredentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
|
deviceCredentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) {
|
||||||
return null;
|
return null;
|
||||||
@ -247,7 +251,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
|
|
||||||
ObjectNode dockerMqttCommands = JacksonUtil.newObjectNode();
|
ObjectNode dockerMqttCommands = JacksonUtil.newObjectNode();
|
||||||
|
|
||||||
if (deviceConnectivityConfiguration.isEnabled(MQTT)) {
|
if (isEnabled(MQTT)) {
|
||||||
Optional.ofNullable(getMqttPublishCommand(baseUrl, topic, deviceCredentials)).
|
Optional.ofNullable(getMqttPublishCommand(baseUrl, topic, deviceCredentials)).
|
||||||
ifPresent(v -> mqttCommands.put(MQTT, v));
|
ifPresent(v -> mqttCommands.put(MQTT, v));
|
||||||
|
|
||||||
@ -255,7 +259,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
.ifPresent(v -> dockerMqttCommands.put(MQTT, v));
|
.ifPresent(v -> dockerMqttCommands.put(MQTT, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceConnectivityConfiguration.isEnabled(MQTTS)) {
|
if (isEnabled(MQTTS)) {
|
||||||
List<String> mqttsPublishCommand = getMqttsPublishCommand(baseUrl, topic, deviceCredentials);
|
List<String> mqttsPublishCommand = getMqttsPublishCommand(baseUrl, topic, deviceCredentials);
|
||||||
if (mqttsPublishCommand != null) {
|
if (mqttsPublishCommand != null) {
|
||||||
ArrayNode arrayNode = mqttCommands.putArray(MQTTS);
|
ArrayNode arrayNode = mqttCommands.putArray(MQTTS);
|
||||||
@ -273,14 +277,14 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getMqttPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
private String getMqttPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity(MQTT);
|
DeviceConnectivityInfo properties = getConnectivity(MQTT);
|
||||||
String mqttHost = getHost(baseUrl, properties);
|
String mqttHost = getHost(baseUrl, properties);
|
||||||
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
||||||
return DeviceConnectivityUtil.getMqttPublishCommand(MQTT, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials);
|
return DeviceConnectivityUtil.getMqttPublishCommand(MQTT, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getMqttsPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
private List<String> getMqttsPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity(MQTTS);
|
DeviceConnectivityInfo properties = getConnectivity(MQTTS);
|
||||||
String mqttHost = getHost(baseUrl, properties);
|
String mqttHost = getHost(baseUrl, properties);
|
||||||
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
||||||
String pubCommand = DeviceConnectivityUtil.getMqttPublishCommand(MQTTS, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials);
|
String pubCommand = DeviceConnectivityUtil.getMqttPublishCommand(MQTTS, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials);
|
||||||
@ -296,7 +300,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
|
|
||||||
private JsonNode getGatewayDockerCommands(String baseUrl, DeviceCredentials deviceCredentials, String mqttType) throws URISyntaxException {
|
private JsonNode getGatewayDockerCommands(String baseUrl, DeviceCredentials deviceCredentials, String mqttType) throws URISyntaxException {
|
||||||
ObjectNode dockerLaunchCommands = JacksonUtil.newObjectNode();
|
ObjectNode dockerLaunchCommands = JacksonUtil.newObjectNode();
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity().get(mqttType);
|
DeviceConnectivityInfo properties = getConnectivity(mqttType);
|
||||||
String mqttHost = getHost(baseUrl, properties);
|
String mqttHost = getHost(baseUrl, properties);
|
||||||
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
||||||
Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(LINUX, mqttHost, mqttPort, deviceCredentials))
|
Optional.ofNullable(DeviceConnectivityUtil.getGatewayLaunchCommand(LINUX, mqttHost, mqttPort, deviceCredentials))
|
||||||
@ -307,7 +311,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getDockerMqttPublishCommand(String protocol, String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
private String getDockerMqttPublishCommand(String protocol, String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity(protocol);
|
DeviceConnectivityInfo properties = getConnectivity(protocol);
|
||||||
String mqttHost = getHost(baseUrl, properties);
|
String mqttHost = getHost(baseUrl, properties);
|
||||||
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
String mqttPort = properties.getPort().isEmpty() ? null : properties.getPort();
|
||||||
return DeviceConnectivityUtil.getDockerMqttPublishCommand(protocol, baseUrl, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials);
|
return DeviceConnectivityUtil.getDockerMqttPublishCommand(protocol, baseUrl, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials);
|
||||||
@ -323,7 +327,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
|
|
||||||
ObjectNode dockerCoapCommands = JacksonUtil.newObjectNode();
|
ObjectNode dockerCoapCommands = JacksonUtil.newObjectNode();
|
||||||
|
|
||||||
if (deviceConnectivityConfiguration.isEnabled(COAP)) {
|
if (isEnabled(COAP)) {
|
||||||
Optional.ofNullable(getCoapPublishCommand(COAP, baseUrl, deviceCredentials))
|
Optional.ofNullable(getCoapPublishCommand(COAP, baseUrl, deviceCredentials))
|
||||||
.ifPresent(v -> coapCommands.put(COAP, v));
|
.ifPresent(v -> coapCommands.put(COAP, v));
|
||||||
|
|
||||||
@ -331,7 +335,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
.ifPresent(v -> dockerCoapCommands.put(COAP, v));
|
.ifPresent(v -> dockerCoapCommands.put(COAP, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceConnectivityConfiguration.isEnabled(COAPS)) {
|
if (isEnabled(COAPS)) {
|
||||||
Optional.ofNullable(getCoapPublishCommand(COAPS, baseUrl, deviceCredentials))
|
Optional.ofNullable(getCoapPublishCommand(COAPS, baseUrl, deviceCredentials))
|
||||||
.ifPresent(v -> coapCommands.put(COAPS, v));
|
.ifPresent(v -> coapCommands.put(COAPS, v));
|
||||||
|
|
||||||
@ -347,14 +351,14 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
private String getCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity(protocol);
|
DeviceConnectivityInfo properties = getConnectivity(protocol);
|
||||||
String hostName = getHost(baseUrl, properties);
|
String hostName = getHost(baseUrl, properties);
|
||||||
String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort();
|
String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort();
|
||||||
return DeviceConnectivityUtil.getCoapPublishCommand(protocol, hostName, port, deviceCredentials);
|
return DeviceConnectivityUtil.getCoapPublishCommand(protocol, hostName, port, deviceCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getDockerCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
private String getDockerCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException {
|
||||||
DeviceConnectivityInfo properties = deviceConnectivityConfiguration.getConnectivity(protocol);
|
DeviceConnectivityInfo properties = getConnectivity(protocol);
|
||||||
String host = getHost(baseUrl, properties);
|
String host = getHost(baseUrl, properties);
|
||||||
String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort();
|
String port = properties.getPort().isEmpty() ? "" : ":" + properties.getPort();
|
||||||
return DeviceConnectivityUtil.getDockerCoapPublishCommand(protocol, host, port, deviceCredentials);
|
return DeviceConnectivityUtil.getDockerCoapPublishCommand(protocol, host, port, deviceCredentials);
|
||||||
|
|||||||
@ -43,6 +43,16 @@ VALUES ( '6eaaefa6-4612-11e7-a919-92ebcb67fe33', 1592576748000, '13814000-1dd2-1
|
|||||||
"password": ""
|
"password": ""
|
||||||
}' );
|
}' );
|
||||||
|
|
||||||
|
INSERT INTO admin_settings ( id, created_time, tenant_id, key, json_value )
|
||||||
|
VALUES ( '23199d80-6e7e-11ee-8829-ef9fd52a6141', 1697719852888, '13814000-1dd2-11b2-8080-808080808080', 'connectivity', '{
|
||||||
|
"http":{"enabled":true,"host":"","port":"8080"},
|
||||||
|
"https":{"enabled":false,"host":"","port":"443"},
|
||||||
|
"mqtt":{"enabled":true,"host":"","port":"1883"},
|
||||||
|
"mqtts":{"enabled":false,"host":"","port":"8883"},
|
||||||
|
"coap":{"enabled":true,"host":"","port":"5683"},
|
||||||
|
"coaps":{"enabled":false,"host":"","port":"5684"}
|
||||||
|
}' );
|
||||||
|
|
||||||
INSERT INTO queue ( id, created_time, tenant_id, name, topic, poll_interval, partitions, consumer_per_partition, pack_processing_timeout, submit_strategy, processing_strategy )
|
INSERT INTO queue ( id, created_time, tenant_id, name, topic, poll_interval, partitions, consumer_per_partition, pack_processing_timeout, submit_strategy, processing_strategy )
|
||||||
VALUES ( '6eaaefa6-4612-11e7-a919-92ebcb67fe33', 1592576748000 ,'13814000-1dd2-11b2-8080-808080808080', 'Main' ,'tb_rule_engine.main', 25, 10, true, 2000,
|
VALUES ( '6eaaefa6-4612-11e7-a919-92ebcb67fe33', 1592576748000 ,'13814000-1dd2-11b2-8080-808080808080', 'Main' ,'tb_rule_engine.main', 25, 10, true, 2000,
|
||||||
'{"type": "BURST", "batchSize": 1000}',
|
'{"type": "BURST", "batchSize": 1000}',
|
||||||
|
|||||||
@ -15,8 +15,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<div>
|
<mat-card appearance="outlined" class="settings-card">
|
||||||
<mat-card appearance="outlined" class="settings-card">
|
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<mat-card-title>
|
<mat-card-title>
|
||||||
<div fxLayout="row">
|
<div fxLayout="row">
|
||||||
@ -28,7 +27,7 @@
|
|||||||
</mat-progress-bar>
|
</mat-progress-bar>
|
||||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<form [formGroup]="generalSettings" (ngSubmit)="save()">
|
<form [formGroup]="generalSettings" (ngSubmit)="save()" class="tb-form-panel no-border no-padding">
|
||||||
<fieldset [disabled]="isLoading$ | async">
|
<fieldset [disabled]="isLoading$ | async">
|
||||||
<mat-form-field class="mat-block">
|
<mat-form-field class="mat-block">
|
||||||
<mat-label translate>admin.base-url</mat-label>
|
<mat-label translate>admin.base-url</mat-label>
|
||||||
@ -37,17 +36,94 @@
|
|||||||
{{ 'admin.base-url-required' | translate }}
|
{{ 'admin.base-url-required' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<tb-checkbox formControlName="prohibitDifferentUrl" style="display: block;">
|
<div class="tb-form-row" tb-hint-tooltip-icon="{{ 'admin.prohibit-different-url-hint' | translate }}">
|
||||||
|
<mat-slide-toggle class="mat-slide margin" formControlName="prohibitDifferentUrl">
|
||||||
{{ 'admin.prohibit-different-url' | translate }}
|
{{ 'admin.prohibit-different-url' | translate }}
|
||||||
</tb-checkbox>
|
</mat-slide-toggle>
|
||||||
<div class="tb-hint" style="padding-left: 10px;" translate>admin.prohibit-different-url-hint</div>
|
</div>
|
||||||
<div fxLayout="row" fxLayoutAlign="end center" style="width: 100%;" class="layout-wrap">
|
</fieldset>
|
||||||
|
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px" class="layout-wrap">
|
||||||
|
<button mat-button color="primary"
|
||||||
|
[disabled]="generalSettings.pristine"
|
||||||
|
(click)="discardGeneralSettings()"
|
||||||
|
type="button">{{'action.undo' | translate}}
|
||||||
|
</button>
|
||||||
<button mat-button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || generalSettings.invalid || !generalSettings.dirty"
|
<button mat-button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || generalSettings.invalid || !generalSettings.dirty"
|
||||||
type="submit">{{'action.save' | translate}}
|
type="submit">{{'action.save' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
</form>
|
</form>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
<mat-card appearance="outlined" class="settings-card">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>
|
||||||
|
<div class="mat-headline-5" translate>admin.device-connectivity.device-connectivity</div>
|
||||||
|
</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
|
</mat-progress-bar>
|
||||||
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||||
|
<mat-card-content>
|
||||||
|
<section class="tb-form-panel no-border no-padding">
|
||||||
|
<div class="tb-form-panel no-border no-padding toggle-group">
|
||||||
|
<tb-toggle-select appearance="fill" [(ngModel)]="protocol">
|
||||||
|
<tb-toggle-option value="http">{{ "admin.device-connectivity.http-s" | translate }}</tb-toggle-option>
|
||||||
|
<tb-toggle-option value="mqtt">{{ 'admin.device-connectivity.mqtt-s' | translate }}</tb-toggle-option>
|
||||||
|
<tb-toggle-option value="coap">{{ 'admin.device-connectivity.coap-s' | translate }}</tb-toggle-option>
|
||||||
|
</tb-toggle-select>
|
||||||
|
<div class="tb-form-hint tb-primary-fill">{{ 'admin.device-connectivity.hint' | translate }}</div>
|
||||||
|
</div>
|
||||||
|
<form [formGroup]="deviceConnectivitySettingsForm" (ngSubmit)="saveDeviceConnectivitySettings()"
|
||||||
|
class="tb-form-panel no-border no-padding">
|
||||||
|
<ng-container *ngIf="protocol === 'http'">
|
||||||
|
<ng-container *ngTemplateOutlet="connectivitySettings; context:{protocol: protocol}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="connectivitySettings; context:{protocol: protocol + 's'}"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="protocol === 'mqtt'">
|
||||||
|
<ng-container *ngTemplateOutlet="connectivitySettings; context:{protocol: protocol}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="connectivitySettings; context:{protocol: protocol + 's'}"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="protocol === 'coap'">
|
||||||
|
<ng-container *ngTemplateOutlet="connectivitySettings; context:{protocol: protocol}"></ng-container>
|
||||||
|
<ng-container *ngTemplateOutlet="connectivitySettings; context:{protocol: protocol + 's'}"></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #connectivitySettings let-protocol="protocol">
|
||||||
|
<div class="tb-form-panel stroked no-padding-bottom" [formGroupName]="protocol">
|
||||||
|
<mat-slide-toggle class="mat-slide" formControlName="enabled">
|
||||||
|
{{ 'admin.device-connectivity.' + protocol | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
<div class="tb-form-row column-xs no-border no-padding tb-standard-fields">
|
||||||
|
<mat-form-field fxFlex>
|
||||||
|
<mat-label translate>admin.device-connectivity.host</mat-label>
|
||||||
|
<input matInput formControlName="host"/>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field fxFlex>
|
||||||
|
<mat-label translate>admin.device-connectivity.port</mat-label>
|
||||||
|
<input matInput type="number" min="0" max="65535" formControlName="port"/>
|
||||||
|
<mat-error *ngIf="deviceConnectivitySettingsForm.get(protocol + '.port').hasError('pattern')">
|
||||||
|
{{ 'admin.device-connectivity.port-pattern' | translate }}
|
||||||
|
</mat-error>
|
||||||
|
<mat-error *ngIf="deviceConnectivitySettingsForm.get(protocol + '.port').hasError('min') ||
|
||||||
|
deviceConnectivitySettingsForm.get(protocol + '.port').hasError('max')">
|
||||||
|
{{ 'admin.device-connectivity.port-range' | translate }}
|
||||||
|
</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px" class="layout-wrap">
|
||||||
|
<button mat-button color="primary"
|
||||||
|
[disabled]="deviceConnectivitySettingsForm.pristine"
|
||||||
|
(click)="discardDeviceConnectivitySettings()"
|
||||||
|
type="button">{{'action.undo' | translate}}
|
||||||
|
</button>
|
||||||
|
<button mat-raised-button color="primary"
|
||||||
|
[disabled]="(isLoading$ | async) || deviceConnectivitySettingsForm.invalid || !deviceConnectivitySettingsForm.dirty"
|
||||||
|
type="submit">{{'action.save' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
|||||||
@ -13,11 +13,24 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
:host {
|
@import "../../../../../scss/constants";
|
||||||
}
|
|
||||||
|
|
||||||
:host ::ng-deep {
|
:host {
|
||||||
.mat-checkbox-layout {
|
.settings-card {
|
||||||
white-space: normal;
|
.toggle-group {
|
||||||
|
max-width: 670px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
.tb-form-hint {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media #{$mat-xs} {
|
||||||
|
.tb-form-row {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,62 +14,130 @@
|
|||||||
/// limitations under the License.
|
/// limitations under the License.
|
||||||
///
|
///
|
||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy } from '@angular/core';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
import { PageComponent } from '@shared/components/page.component';
|
||||||
import { Router } from '@angular/router';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import {
|
||||||
import { AdminSettings, GeneralSettings } from '@shared/models/settings.models';
|
AdminSettings,
|
||||||
|
DeviceConnectivityProtocol,
|
||||||
|
DeviceConnectivitySettings,
|
||||||
|
GeneralSettings
|
||||||
|
} from '@shared/models/settings.models';
|
||||||
import { AdminService } from '@core/http/admin.service';
|
import { AdminService } from '@core/http/admin.service';
|
||||||
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
|
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-general-settings',
|
selector: 'tb-general-settings',
|
||||||
templateUrl: './general-settings.component.html',
|
templateUrl: './general-settings.component.html',
|
||||||
styleUrls: ['./general-settings.component.scss', './settings-card.scss']
|
styleUrls: ['./general-settings.component.scss', './settings-card.scss']
|
||||||
})
|
})
|
||||||
export class GeneralSettingsComponent extends PageComponent implements OnInit, HasConfirmForm {
|
export class GeneralSettingsComponent extends PageComponent implements HasConfirmForm, OnDestroy {
|
||||||
|
|
||||||
generalSettings: UntypedFormGroup;
|
generalSettings: FormGroup;
|
||||||
adminSettings: AdminSettings<GeneralSettings>;
|
deviceConnectivitySettingsForm: FormGroup;
|
||||||
|
|
||||||
|
protocol: DeviceConnectivityProtocol = 'http';
|
||||||
|
|
||||||
|
private adminSettings: AdminSettings<GeneralSettings>;
|
||||||
|
private deviceConnectivitySettings: AdminSettings<DeviceConnectivitySettings>;
|
||||||
|
|
||||||
|
private readonly destroy$ = new Subject<void>();
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
private router: Router,
|
|
||||||
private adminService: AdminService,
|
private adminService: AdminService,
|
||||||
public fb: UntypedFormBuilder) {
|
public fb: FormBuilder) {
|
||||||
super(store);
|
super(store);
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.buildGeneralServerSettingsForm();
|
this.buildGeneralServerSettingsForm();
|
||||||
this.adminService.getAdminSettings<GeneralSettings>('general').subscribe(
|
this.adminService.getAdminSettings<GeneralSettings>('general')
|
||||||
(adminSettings) => {
|
.subscribe(adminSettings => this.processGeneralSettings(adminSettings));
|
||||||
this.adminSettings = adminSettings;
|
this.buildDeviceConnectivitySettingsForm();
|
||||||
this.generalSettings.reset(this.adminSettings.jsonValue);
|
this.adminService.getAdminSettings<DeviceConnectivitySettings>('connectivity')
|
||||||
}
|
.subscribe(deviceConnectivitySettings => this.processDeviceConnectivitySettings(deviceConnectivitySettings));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildGeneralServerSettingsForm() {
|
ngOnDestroy() {
|
||||||
|
super.ngOnDestroy();
|
||||||
|
this.destroy$.next();
|
||||||
|
this.destroy$.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildGeneralServerSettingsForm() {
|
||||||
this.generalSettings = this.fb.group({
|
this.generalSettings = this.fb.group({
|
||||||
baseUrl: ['', [Validators.required]],
|
baseUrl: ['', [Validators.required]],
|
||||||
prohibitDifferentUrl: ['',[]]
|
prohibitDifferentUrl: ['',[]]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
save(): void {
|
private buildDeviceConnectivitySettingsForm() {
|
||||||
this.adminSettings.jsonValue = {...this.adminSettings.jsonValue, ...this.generalSettings.value};
|
this.deviceConnectivitySettingsForm = this.fb.group({
|
||||||
this.adminService.saveAdminSettings(this.adminSettings).subscribe(
|
http: this.buildDeviceConnectivityInfoForm(),
|
||||||
(adminSettings) => {
|
https: this.buildDeviceConnectivityInfoForm(),
|
||||||
this.adminSettings = adminSettings;
|
mqtt: this.buildDeviceConnectivityInfoForm(),
|
||||||
this.generalSettings.reset(this.adminSettings.jsonValue);
|
mqtts: this.buildDeviceConnectivityInfoForm(),
|
||||||
}
|
coap: this.buildDeviceConnectivityInfoForm(),
|
||||||
);
|
coaps: this.buildDeviceConnectivityInfoForm()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmForm(): UntypedFormGroup {
|
private buildDeviceConnectivityInfoForm(): FormGroup {
|
||||||
return this.generalSettings;
|
const formGroup = this.fb.group({
|
||||||
|
enabled: [false, []],
|
||||||
|
host: [{value: '', disabled: true}],
|
||||||
|
port: [{value: null, disabled: true}, [Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]]
|
||||||
|
});
|
||||||
|
formGroup.get('enabled').valueChanges.pipe(
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
).subscribe(value => {
|
||||||
|
if (value) {
|
||||||
|
formGroup.get('host').enable({emitEvent: false});
|
||||||
|
formGroup.get('port').enable({emitEvent: false});
|
||||||
|
} else {
|
||||||
|
formGroup.get('host').disable({emitEvent: false});
|
||||||
|
formGroup.get('port').disable({emitEvent: false});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return formGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(): void {
|
||||||
|
this.adminSettings.jsonValue = {...this.adminSettings.jsonValue, ...this.generalSettings.value};
|
||||||
|
this.adminService.saveAdminSettings(this.adminSettings)
|
||||||
|
.subscribe(adminSettings => this.processGeneralSettings(adminSettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
saveDeviceConnectivitySettings(): void {
|
||||||
|
this.deviceConnectivitySettings.jsonValue = {
|
||||||
|
...this.deviceConnectivitySettings.jsonValue,
|
||||||
|
...this.deviceConnectivitySettingsForm.value
|
||||||
|
};
|
||||||
|
this.adminService.saveAdminSettings<DeviceConnectivitySettings>(this.deviceConnectivitySettings)
|
||||||
|
.subscribe(deviceConnectivitySettings => this.processDeviceConnectivitySettings(deviceConnectivitySettings));
|
||||||
|
}
|
||||||
|
|
||||||
|
discardGeneralSettings(): void {
|
||||||
|
this.generalSettings.reset(this.adminSettings.jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
discardDeviceConnectivitySettings(): void {
|
||||||
|
this.deviceConnectivitySettingsForm.reset(this.deviceConnectivitySettings.jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private processGeneralSettings(generalSettings: AdminSettings<GeneralSettings>): void {
|
||||||
|
this.adminSettings = generalSettings;
|
||||||
|
this.generalSettings.reset(this.adminSettings.jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private processDeviceConnectivitySettings(deviceConnectivitySettings: AdminSettings<DeviceConnectivitySettings>): void {
|
||||||
|
this.deviceConnectivitySettings = deviceConnectivitySettings;
|
||||||
|
this.deviceConnectivitySettingsForm.reset(this.deviceConnectivitySettings.jsonValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmForm(): FormGroup {
|
||||||
|
return this.generalSettings.dirty ? this.generalSettings : this.deviceConnectivitySettingsForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,16 @@ export interface GeneralSettings {
|
|||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DeviceConnectivityProtocol = 'http' | 'https' | 'mqtt' | 'mqtts' | 'coap' | 'coaps';
|
||||||
|
|
||||||
|
export interface DeviceConnectivityInfo {
|
||||||
|
enabled: boolean;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DeviceConnectivitySettings = Record<DeviceConnectivityProtocol, DeviceConnectivityInfo>;
|
||||||
|
|
||||||
export interface UserPasswordPolicy {
|
export interface UserPasswordPolicy {
|
||||||
minimumLength: number;
|
minimumLength: number;
|
||||||
minimumUppercaseLetters: number;
|
minimumUppercaseLetters: number;
|
||||||
|
|||||||
@ -105,6 +105,23 @@
|
|||||||
"base-url-required": "Base URL is required.",
|
"base-url-required": "Base URL is required.",
|
||||||
"prohibit-different-url": "Prohibit to use hostname from the client request headers",
|
"prohibit-different-url": "Prohibit to use hostname from the client request headers",
|
||||||
"prohibit-different-url-hint": "This setting should be enabled for production environments. May cause security issues when disabled",
|
"prohibit-different-url-hint": "This setting should be enabled for production environments. May cause security issues when disabled",
|
||||||
|
"device-connectivity": {
|
||||||
|
"device-connectivity": "Device connectivity",
|
||||||
|
"http-s": "HTTP(s)",
|
||||||
|
"mqtt-s": "MQTT(s)",
|
||||||
|
"coap-s": "COAP(s)",
|
||||||
|
"http": "HTTP",
|
||||||
|
"https": "HTTPs",
|
||||||
|
"mqtt": "MQTT",
|
||||||
|
"mqtts": "MQTTs",
|
||||||
|
"coap": "COAP",
|
||||||
|
"coaps": "COAPs",
|
||||||
|
"hint": "If host or port fields are empty, default protocol value will be used.",
|
||||||
|
"host": "Host",
|
||||||
|
"port": "Port",
|
||||||
|
"port-pattern": "Port must be a positive integer.",
|
||||||
|
"port-range": "Port should be in a range from 1 to 65535."
|
||||||
|
},
|
||||||
"mail-from": "Mail From",
|
"mail-from": "Mail From",
|
||||||
"mail-from-required": "Mail From is required.",
|
"mail-from-required": "Mail From is required.",
|
||||||
"smtp-protocol": "SMTP protocol",
|
"smtp-protocol": "SMTP protocol",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user