diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 199e80dc64..115f3d66dc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -16,6 +16,8 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiParam; +import lombok.Builder; +import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -49,6 +51,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.util.ThrowingSupplier; import org.thingsboard.server.dao.resource.ImageCacheKey; import org.thingsboard.server.dao.resource.ImageService; @@ -281,6 +284,19 @@ public class ImageController extends BaseController { return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result); } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping("/api/image/specs") + public ImageSpecs getImageUploadSpecs() throws ThingsboardException { + SecurityUser user = getCurrentUser(); + if (user.isSystemAdmin()) { + return ImageSpecs.DEFAULT; + } + DefaultTenantProfileConfiguration tenantProfileConfig = tenantProfileCache.get(user.getTenantId()).getDefaultProfileConfiguration(); + return ImageSpecs.builder() + .maximumSize(tenantProfileConfig.getMaxResourceSize()) + .build(); + } + private ResponseEntity downloadIfChanged(String type, String key, String etag, boolean preview) throws Exception { ImageCacheKey cacheKey = ImageCacheKey.forImage(getTenantId(type), key, preview); return downloadIfChanged(cacheKey, etag, () -> checkImageInfo(type, key, Operation.READ)); @@ -343,4 +359,14 @@ public class ImageController extends BaseController { return tenantId; } + @Data + @Builder + public static class ImageSpecs { + private final long maximumSize; + + public static final ImageSpecs DEFAULT = ImageSpecs.builder() + .maximumSize(0) + .build(); + } + } \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java index e3ac9e530c..8e7496e6fd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.controller.ImageController.ImageSpecs; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.sql.resource.TbResourceRepository; @@ -240,6 +241,28 @@ public class ImageControllerTest extends AbstractControllerTest { doGet("/api/images/public/" + publicKey).andExpect(status().isNotFound()); } + @Test + public void testGetImageUploadSpecs() throws Exception { + ImageSpecs specs = doGet("/api/image/specs", ImageSpecs.class); + assertThat(specs.getMaximumSize()).isZero(); + + loginSysAdmin(); + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMaxResourceSize(100); + }); + loginTenantAdmin(); + specs = doGet("/api/image/specs", ImageSpecs.class); + assertThat(specs.getMaximumSize()).isEqualTo(100); + + loginSysAdmin(); + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMaxResourceSize(0); + }); + loginTenantAdmin(); + specs = doGet("/api/image/specs", ImageSpecs.class); + assertThat(specs.getMaximumSize()).isEqualTo(0); + } + private TbResourceInfo updateImagePublicStatus(String filename, boolean isPublic) throws Exception { return doPut("/api/images/tenant/" + filename + "/public/" + isPublic, "", TbResourceInfo.class); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index 7e5910556d..6151ce99b6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -138,8 +138,8 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Override @SneakyThrows public TbResourceInfo saveImage(TbResource image) { - if (image.getId() == null && StringUtils.isEmpty(image.getResourceKey())) { - image.setResourceKey(getUniqueKey(image.getTenantId(), image.getFileName())); + if (image.getId() == null) { + image.setResourceKey(getUniqueKey(image.getTenantId(), StringUtils.defaultIfEmpty(image.getResourceKey(), image.getFileName()))); } resourceValidator.validate(image, TbResourceInfo::getTenantId); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index e43aea4e9d..4221e46990 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4056,15 +4056,15 @@ "maximum-rule-chains": "Rule chains maximum number", "maximum-rule-chains-required": "Rule chains maximum number is required.", "maximum-rule-chains-range": "Rule chains maximum number can't be negative", - "maximum-resources-sum-data-size": "Resource files sum size", - "maximum-resources-sum-data-size-required": "Resource files sum size is required.", - "maximum-resources-sum-data-size-range": "Resource files sum size can't be negative", - "maximum-resource-size": "Resource maximum size", - "maximum-resource-size-required": "Resource maximum size is required", - "maximum-resource-size-range": "Resource maximum size can't be negative", - "maximum-ota-packages-sum-data-size": "OTA package files sum size", - "maximum-ota-package-sum-data-size-required": "OTA package files sum size is required.", - "maximum-ota-package-sum-data-size-range": "OTA package files sum size can't be negative", + "maximum-resources-sum-data-size": "Maximum total size of resources files (bytes)", + "maximum-resources-sum-data-size-required": "Maximum total size of resources files is required.", + "maximum-resources-sum-data-size-range": "Maximum total size of resources files can't be negative", + "maximum-resource-size": "Maximum resource file size (bytes)", + "maximum-resource-size-required": "Maximum resource file size is required", + "maximum-resource-size-range": "Maximum resource file size can't be negative", + "maximum-ota-packages-sum-data-size": "Maximum total size of OTA package files (bytes)", + "maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.", + "maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative", "rest-requests-for-tenant": "REST requests for tenant", "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages", "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points",