Commit 1c79480b332c20151c9d99acef82f3059522617a
Committed by
Andrew Shvayka
1 parent
449cb594
created sendApiFeatureStateEmail
Showing
8 changed files
with
467 additions
and
10 deletions
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
@@ -17,21 +17,21 @@ package org.thingsboard.server.service.apiusage; | @@ -17,21 +17,21 @@ package org.thingsboard.server.service.apiusage; | ||
17 | 17 | ||
18 | import com.google.common.util.concurrent.FutureCallback; | 18 | import com.google.common.util.concurrent.FutureCallback; |
19 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.apache.commons.lang3.StringUtils; | ||
20 | import org.checkerframework.checker.nullness.qual.Nullable; | 21 | import org.checkerframework.checker.nullness.qual.Nullable; |
21 | import org.springframework.beans.factory.annotation.Autowired; | 22 | import org.springframework.beans.factory.annotation.Autowired; |
22 | import org.springframework.beans.factory.annotation.Value; | 23 | import org.springframework.beans.factory.annotation.Value; |
23 | -import org.springframework.boot.context.event.ApplicationReadyEvent; | ||
24 | import org.springframework.context.annotation.Lazy; | 24 | import org.springframework.context.annotation.Lazy; |
25 | -import org.springframework.context.event.EventListener; | ||
26 | -import org.springframework.core.annotation.Order; | ||
27 | -import org.springframework.data.util.Pair; | ||
28 | import org.springframework.stereotype.Service; | 25 | import org.springframework.stereotype.Service; |
26 | +import org.thingsboard.rule.engine.api.MailService; | ||
29 | import org.thingsboard.server.common.data.ApiFeature; | 27 | import org.thingsboard.server.common.data.ApiFeature; |
30 | import org.thingsboard.server.common.data.ApiUsageRecordKey; | 28 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
31 | import org.thingsboard.server.common.data.ApiUsageState; | 29 | import org.thingsboard.server.common.data.ApiUsageState; |
30 | +import org.thingsboard.server.common.data.ApiUsageStateMailMessage; | ||
32 | import org.thingsboard.server.common.data.ApiUsageStateValue; | 31 | import org.thingsboard.server.common.data.ApiUsageStateValue; |
33 | import org.thingsboard.server.common.data.Tenant; | 32 | import org.thingsboard.server.common.data.Tenant; |
34 | import org.thingsboard.server.common.data.TenantProfile; | 33 | import org.thingsboard.server.common.data.TenantProfile; |
34 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
35 | import org.thingsboard.server.common.data.id.ApiUsageStateId; | 35 | import org.thingsboard.server.common.data.id.ApiUsageStateId; |
36 | import org.thingsboard.server.common.data.id.TenantId; | 36 | import org.thingsboard.server.common.data.id.TenantId; |
37 | import org.thingsboard.server.common.data.id.TenantProfileId; | 37 | import org.thingsboard.server.common.data.id.TenantProfileId; |
@@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | ||
45 | import org.thingsboard.server.common.msg.queue.ServiceType; | 45 | import org.thingsboard.server.common.msg.queue.ServiceType; |
46 | import org.thingsboard.server.common.msg.queue.TbCallback; | 46 | import org.thingsboard.server.common.msg.queue.TbCallback; |
47 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; | 47 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; |
48 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | ||
48 | import org.thingsboard.server.dao.tenant.TenantService; | 49 | import org.thingsboard.server.dao.tenant.TenantService; |
49 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 50 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
50 | import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; | 51 | import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
@@ -54,14 +55,11 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; | @@ -54,14 +55,11 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; | ||
54 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; | 55 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
55 | import org.thingsboard.server.queue.discovery.PartitionService; | 56 | import org.thingsboard.server.queue.discovery.PartitionService; |
56 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; | 57 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
57 | -import org.thingsboard.server.queue.util.TbCoreComponent; | ||
58 | -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | ||
59 | import org.thingsboard.server.service.queue.TbClusterService; | 58 | import org.thingsboard.server.service.queue.TbClusterService; |
60 | import org.thingsboard.server.service.telemetry.InternalTelemetryService; | 59 | import org.thingsboard.server.service.telemetry.InternalTelemetryService; |
61 | 60 | ||
62 | import javax.annotation.PostConstruct; | 61 | import javax.annotation.PostConstruct; |
63 | import java.util.ArrayList; | 62 | import java.util.ArrayList; |
64 | -import java.util.HashMap; | ||
65 | import java.util.HashSet; | 63 | import java.util.HashSet; |
66 | import java.util.List; | 64 | import java.util.List; |
67 | import java.util.Map; | 65 | import java.util.Map; |
@@ -94,6 +92,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -94,6 +92,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
94 | private final ApiUsageStateService apiUsageStateService; | 92 | private final ApiUsageStateService apiUsageStateService; |
95 | private final SchedulerComponent scheduler; | 93 | private final SchedulerComponent scheduler; |
96 | private final TbTenantProfileCache tenantProfileCache; | 94 | private final TbTenantProfileCache tenantProfileCache; |
95 | + private final MailService mailService; | ||
97 | 96 | ||
98 | @Lazy | 97 | @Lazy |
99 | @Autowired | 98 | @Autowired |
@@ -118,7 +117,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -118,7 +117,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
118 | TimeseriesService tsService, | 117 | TimeseriesService tsService, |
119 | ApiUsageStateService apiUsageStateService, | 118 | ApiUsageStateService apiUsageStateService, |
120 | SchedulerComponent scheduler, | 119 | SchedulerComponent scheduler, |
121 | - TbTenantProfileCache tenantProfileCache) { | 120 | + TbTenantProfileCache tenantProfileCache, MailService mailService) { |
122 | this.clusterService = clusterService; | 121 | this.clusterService = clusterService; |
123 | this.partitionService = partitionService; | 122 | this.partitionService = partitionService; |
124 | this.tenantService = tenantService; | 123 | this.tenantService = tenantService; |
@@ -126,6 +125,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -126,6 +125,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
126 | this.apiUsageStateService = apiUsageStateService; | 125 | this.apiUsageStateService = apiUsageStateService; |
127 | this.scheduler = scheduler; | 126 | this.scheduler = scheduler; |
128 | this.tenantProfileCache = tenantProfileCache; | 127 | this.tenantProfileCache = tenantProfileCache; |
128 | + this.mailService = mailService; | ||
129 | } | 129 | } |
130 | 130 | ||
131 | @PostConstruct | 131 | @PostConstruct |
@@ -286,7 +286,26 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | @@ -286,7 +286,26 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { | ||
286 | List<TsKvEntry> stateTelemetry = new ArrayList<>(); | 286 | List<TsKvEntry> stateTelemetry = new ArrayList<>(); |
287 | result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); | 287 | result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); |
288 | tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); | 288 | tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); |
289 | - //TODO: notify tenant admin via email! | 289 | + |
290 | + String email = tenantService.findTenantById(state.getTenantId()).getEmail(); | ||
291 | + | ||
292 | + if (StringUtils.isNotEmpty(email)) { | ||
293 | + result.forEach((apiFeature, stateValue) -> { | ||
294 | + ApiUsageRecordKey[] keys = ApiUsageRecordKey.getKeys(apiFeature); | ||
295 | + ApiUsageStateMailMessage[] msgs = new ApiUsageStateMailMessage[keys.length]; | ||
296 | + for (int i = 0; i < keys.length; i++) { | ||
297 | + ApiUsageRecordKey key = keys[i]; | ||
298 | + msgs[i] = new ApiUsageStateMailMessage(key, state.getProfileThreshold(key), state.get(key)); | ||
299 | + } | ||
300 | + try { | ||
301 | + mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, msgs); | ||
302 | + } catch (ThingsboardException e) { | ||
303 | + log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e); | ||
304 | + } | ||
305 | + }); | ||
306 | + } else { | ||
307 | + log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId()); | ||
308 | + } | ||
290 | } | 309 | } |
291 | 310 | ||
292 | private void checkStartOfNextCycle() { | 311 | private void checkStartOfNextCycle() { |
@@ -29,6 +29,9 @@ import org.springframework.stereotype.Service; | @@ -29,6 +29,9 @@ import org.springframework.stereotype.Service; | ||
29 | import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; | 29 | import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; |
30 | import org.thingsboard.rule.engine.api.MailService; | 30 | import org.thingsboard.rule.engine.api.MailService; |
31 | import org.thingsboard.server.common.data.AdminSettings; | 31 | import org.thingsboard.server.common.data.AdminSettings; |
32 | +import org.thingsboard.server.common.data.ApiFeature; | ||
33 | +import org.thingsboard.server.common.data.ApiUsageStateMailMessage; | ||
34 | +import org.thingsboard.server.common.data.ApiUsageStateValue; | ||
32 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | 35 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
33 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 36 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
34 | import org.thingsboard.server.common.data.id.EntityId; | 37 | import org.thingsboard.server.common.data.id.EntityId; |
@@ -246,6 +249,32 @@ public class DefaultMailService implements MailService { | @@ -246,6 +249,32 @@ public class DefaultMailService implements MailService { | ||
246 | sendMail(mailSender, mailFrom, email, subject, message); | 249 | sendMail(mailSender, mailFrom, email, subject, message); |
247 | } | 250 | } |
248 | 251 | ||
252 | + @Override | ||
253 | + public void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage[] stateMailMessages) throws ThingsboardException { | ||
254 | + String subject = messages.getMessage("api.usage.state", null, Locale.US); | ||
255 | + | ||
256 | + Map<String, Object> model = new HashMap<>(); | ||
257 | + model.put("apiFeature", apiFeature.getApiStateKey()); | ||
258 | + model.put("apiUsageStateMailMessages", stateMailMessages); | ||
259 | + | ||
260 | + model.put(TARGET_EMAIL, email); | ||
261 | + | ||
262 | + String message = null; | ||
263 | + | ||
264 | + switch (stateValue) { | ||
265 | + case ENABLED: | ||
266 | + message = mergeTemplateIntoString("state.enabled.ftl", model); | ||
267 | + break; | ||
268 | + case WARNING: | ||
269 | + message = mergeTemplateIntoString("state.warning.ftl", model); | ||
270 | + break; | ||
271 | + case DISABLED: | ||
272 | + message = mergeTemplateIntoString("state.disabled.ftl", model); | ||
273 | + break; | ||
274 | + } | ||
275 | + sendMail(mailSender, mailFrom, email, subject, message); | ||
276 | + } | ||
277 | + | ||
249 | private void sendMail(JavaMailSenderImpl mailSender, | 278 | private void sendMail(JavaMailSenderImpl mailSender, |
250 | String mailFrom, String email, | 279 | String mailFrom, String email, |
251 | String subject, String message) throws ThingsboardException { | 280 | String subject, String message) throws ThingsboardException { |
@@ -3,4 +3,5 @@ activation.subject=Your account activation on Thingsboard | @@ -3,4 +3,5 @@ activation.subject=Your account activation on Thingsboard | ||
3 | account.activated.subject=Thingsboard - your account has been activated | 3 | account.activated.subject=Thingsboard - your account has been activated |
4 | reset.password.subject=Thingsboard - Password reset has been requested | 4 | reset.password.subject=Thingsboard - Password reset has been requested |
5 | password.was.reset.subject=Thingsboard - your account password has been reset | 5 | password.was.reset.subject=Thingsboard - your account password has been reset |
6 | -account.lockout.subject=Thingsboard - User account has been lockout | ||
6 | +account.lockout.subject=Thingsboard - User account has been lockout | ||
7 | +api.usage.state=Thingsboard - Api Usage State for tenant has been updated |
1 | +<#-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
19 | +<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
20 | +<head> | ||
21 | +<meta name="viewport" content="width=device-width" /> | ||
22 | +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||
23 | +<title>Thingsboard - Api Usage State</title> | ||
24 | + | ||
25 | + | ||
26 | +<style type="text/css"> | ||
27 | +img { | ||
28 | +max-width: 100%; | ||
29 | +} | ||
30 | +body { | ||
31 | +-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; | ||
32 | +} | ||
33 | +body { | ||
34 | +background-color: #f6f6f6; | ||
35 | +} | ||
36 | +@media only screen and (max-width: 640px) { | ||
37 | + body { | ||
38 | + padding: 0 !important; | ||
39 | + } | ||
40 | + h1 { | ||
41 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
42 | + } | ||
43 | + h2 { | ||
44 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
45 | + } | ||
46 | + h3 { | ||
47 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
48 | + } | ||
49 | + h4 { | ||
50 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
51 | + } | ||
52 | + h1 { | ||
53 | + font-size: 22px !important; | ||
54 | + } | ||
55 | + h2 { | ||
56 | + font-size: 18px !important; | ||
57 | + } | ||
58 | + h3 { | ||
59 | + font-size: 16px !important; | ||
60 | + } | ||
61 | + .container { | ||
62 | + padding: 0 !important; width: 100% !important; | ||
63 | + } | ||
64 | + .content { | ||
65 | + padding: 0 !important; | ||
66 | + } | ||
67 | + .content-wrap { | ||
68 | + padding: 10px !important; | ||
69 | + } | ||
70 | + .invoice { | ||
71 | + width: 100% !important; | ||
72 | + } | ||
73 | +} | ||
74 | +</style> | ||
75 | +</head> | ||
76 | + | ||
77 | +<body itemscope itemtype="http://schema.org/EmailMessage" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"> | ||
78 | + | ||
79 | +<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td> | ||
80 | + <td class="container" width="600" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" valign="top"> | ||
81 | + <div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"> | ||
82 | + <table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> | ||
83 | + <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
84 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
85 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
86 | + <h2>Thingsboard Api Usage State for tenant has been updated</h2> | ||
87 | + </td> | ||
88 | + </tr> | ||
89 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
90 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
91 | + Thingsboard Usage state ${apiFeature} was updated to status DISABLED. | ||
92 | + </td> | ||
93 | + </tr> | ||
94 | + <#list apiUsageStateMailMessages as msg> | ||
95 | + <tr> | ||
96 | + <td> | ||
97 | + ${msg.key.apiLimitKey} = ${msg.threshold} | ||
98 | + </td> | ||
99 | + </tr> | ||
100 | + <tr> | ||
101 | + <td> | ||
102 | + ${msg.key.apiCountKey} = ${msg.value} | ||
103 | + </td> | ||
104 | + </tr> | ||
105 | + </#list> | ||
106 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
107 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
108 | + — The Thingsboard | ||
109 | + </td> | ||
110 | + </tr></table></td> | ||
111 | + </tr> | ||
112 | + </table> | ||
113 | + <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> | ||
114 | + <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
115 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
116 | + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td> | ||
117 | + </tr> | ||
118 | + </table> | ||
119 | + </div> | ||
120 | + </div> | ||
121 | + </td> | ||
122 | + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td> | ||
123 | + </tr> | ||
124 | +</table> | ||
125 | +</body> | ||
126 | +</html> |
1 | +<#-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
19 | +<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
20 | +<head> | ||
21 | +<meta name="viewport" content="width=device-width" /> | ||
22 | +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||
23 | +<title>Thingsboard - Api Usage State</title> | ||
24 | + | ||
25 | + | ||
26 | +<style type="text/css"> | ||
27 | +img { | ||
28 | +max-width: 100%; | ||
29 | +} | ||
30 | +body { | ||
31 | +-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; | ||
32 | +} | ||
33 | +body { | ||
34 | +background-color: #f6f6f6; | ||
35 | +} | ||
36 | +@media only screen and (max-width: 640px) { | ||
37 | + body { | ||
38 | + padding: 0 !important; | ||
39 | + } | ||
40 | + h1 { | ||
41 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
42 | + } | ||
43 | + h2 { | ||
44 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
45 | + } | ||
46 | + h3 { | ||
47 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
48 | + } | ||
49 | + h4 { | ||
50 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
51 | + } | ||
52 | + h1 { | ||
53 | + font-size: 22px !important; | ||
54 | + } | ||
55 | + h2 { | ||
56 | + font-size: 18px !important; | ||
57 | + } | ||
58 | + h3 { | ||
59 | + font-size: 16px !important; | ||
60 | + } | ||
61 | + .container { | ||
62 | + padding: 0 !important; width: 100% !important; | ||
63 | + } | ||
64 | + .content { | ||
65 | + padding: 0 !important; | ||
66 | + } | ||
67 | + .content-wrap { | ||
68 | + padding: 10px !important; | ||
69 | + } | ||
70 | + .invoice { | ||
71 | + width: 100% !important; | ||
72 | + } | ||
73 | +} | ||
74 | +</style> | ||
75 | +</head> | ||
76 | + | ||
77 | +<body itemscope itemtype="http://schema.org/EmailMessage" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"> | ||
78 | + | ||
79 | +<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td> | ||
80 | + <td class="container" width="600" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" valign="top"> | ||
81 | + <div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"> | ||
82 | + <table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> | ||
83 | + <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
84 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
85 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
86 | + <h2>Thingsboard Api Usage State for tenant has been updated</h2> | ||
87 | + </td> | ||
88 | + </tr> | ||
89 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0 ;"> | ||
90 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
91 | + Thingsboard Usage state ${apiFeature} was updated to status ENABLED. | ||
92 | + </td> | ||
93 | + </tr> | ||
94 | + <#list apiUsageStateMailMessages as msg> | ||
95 | + <tr> | ||
96 | + <td> | ||
97 | + ${msg.key.apiLimitKey} = ${msg.threshold} | ||
98 | + </td> | ||
99 | + </tr> | ||
100 | + <tr> | ||
101 | + <td> | ||
102 | + ${msg.key.apiCountKey} = ${msg.value} | ||
103 | + </td> | ||
104 | + </tr> | ||
105 | + </#list> | ||
106 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
107 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
108 | + — The Thingsboard | ||
109 | + </td> | ||
110 | + </tr></table></td> | ||
111 | + </tr> | ||
112 | + </table> | ||
113 | + <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> | ||
114 | + <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
115 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
116 | + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td> | ||
117 | + </tr> | ||
118 | + </table> | ||
119 | + </div> | ||
120 | + </div> | ||
121 | + </td> | ||
122 | + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td> | ||
123 | + </tr> | ||
124 | +</table> | ||
125 | +</body> | ||
126 | +</html> |
1 | +<#-- | ||
2 | + | ||
3 | + Copyright © 2016-2020 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | ||
19 | +<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
20 | +<head> | ||
21 | +<meta name="viewport" content="width=device-width" /> | ||
22 | +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | ||
23 | +<title>Thingsboard - Api Usage State</title> | ||
24 | + | ||
25 | + | ||
26 | +<style type="text/css"> | ||
27 | +img { | ||
28 | +max-width: 100%; | ||
29 | +} | ||
30 | +body { | ||
31 | +-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; | ||
32 | +} | ||
33 | +body { | ||
34 | +background-color: #f6f6f6; | ||
35 | +} | ||
36 | +@media only screen and (max-width: 640px) { | ||
37 | + body { | ||
38 | + padding: 0 !important; | ||
39 | + } | ||
40 | + h1 { | ||
41 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
42 | + } | ||
43 | + h2 { | ||
44 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
45 | + } | ||
46 | + h3 { | ||
47 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
48 | + } | ||
49 | + h4 { | ||
50 | + font-weight: 800 !important; margin: 20px 0 5px !important; | ||
51 | + } | ||
52 | + h1 { | ||
53 | + font-size: 22px !important; | ||
54 | + } | ||
55 | + h2 { | ||
56 | + font-size: 18px !important; | ||
57 | + } | ||
58 | + h3 { | ||
59 | + font-size: 16px !important; | ||
60 | + } | ||
61 | + .container { | ||
62 | + padding: 0 !important; width: 100% !important; | ||
63 | + } | ||
64 | + .content { | ||
65 | + padding: 0 !important; | ||
66 | + } | ||
67 | + .content-wrap { | ||
68 | + padding: 10px !important; | ||
69 | + } | ||
70 | + .invoice { | ||
71 | + width: 100% !important; | ||
72 | + } | ||
73 | +} | ||
74 | +</style> | ||
75 | +</head> | ||
76 | + | ||
77 | +<body itemscope itemtype="http://schema.org/EmailMessage" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"> | ||
78 | + | ||
79 | +<table class="body-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;" bgcolor="#f6f6f6"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td> | ||
80 | + <td class="container" width="600" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;" valign="top"> | ||
81 | + <div class="content" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;"> | ||
82 | + <table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;" bgcolor="#fff"><tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"><td class="content-wrap" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> | ||
83 | + <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
84 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
85 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
86 | + <h2>Thingsboard Api Usage State for tenant has been updated</h2> | ||
87 | + </td> | ||
88 | + </tr> | ||
89 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
90 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
91 | + Thingsboard Usage state ${apiFeature} was updated to status WARNING. | ||
92 | + </td> | ||
93 | + </tr> | ||
94 | + <#list apiUsageStateMailMessages as msg> | ||
95 | + <tr> | ||
96 | + <td> | ||
97 | + ${msg.key.apiLimitKey} = ${msg.threshold} | ||
98 | + </td> | ||
99 | + </tr> | ||
100 | + <tr> | ||
101 | + <td> | ||
102 | + ${msg.key.apiCountKey} = ${msg.value} | ||
103 | + </td> | ||
104 | + </tr> | ||
105 | + </#list> | ||
106 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
107 | + <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> | ||
108 | + — The Thingsboard | ||
109 | + </td> | ||
110 | + </tr></table></td> | ||
111 | + </tr> | ||
112 | + </table> | ||
113 | + <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> | ||
114 | + <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
115 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | ||
116 | + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td> | ||
117 | + </tr> | ||
118 | + </table> | ||
119 | + </div> | ||
120 | + </div> | ||
121 | + </td> | ||
122 | + <td style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;" valign="top"></td> | ||
123 | + </tr> | ||
124 | +</table> | ||
125 | +</body> | ||
126 | +</html> |
common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateMailMessage.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class ApiUsageStateMailMessage { | ||
22 | + private final ApiUsageRecordKey key; | ||
23 | + private final long threshold; | ||
24 | + private final long value; | ||
25 | +} |
@@ -16,6 +16,9 @@ | @@ -16,6 +16,9 @@ | ||
16 | package org.thingsboard.rule.engine.api; | 16 | package org.thingsboard.rule.engine.api; |
17 | 17 | ||
18 | import com.fasterxml.jackson.databind.JsonNode; | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | +import org.thingsboard.server.common.data.ApiFeature; | ||
20 | +import org.thingsboard.server.common.data.ApiUsageStateMailMessage; | ||
21 | +import org.thingsboard.server.common.data.ApiUsageStateValue; | ||
19 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 22 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
20 | 23 | ||
21 | import javax.mail.MessagingException; | 24 | import javax.mail.MessagingException; |
@@ -39,4 +42,6 @@ public interface MailService { | @@ -39,4 +42,6 @@ public interface MailService { | ||
39 | void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException; | 42 | void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException; |
40 | 43 | ||
41 | void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException; | 44 | void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException; |
45 | + | ||
46 | + void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage[] stateMailMessages) throws ThingsboardException; | ||
42 | } | 47 | } |