diff options
-rw-r--r-- | src/main/java/org/traccar/config/Keys.java | 38 | ||||
-rw-r--r-- | src/main/java/org/traccar/schedule/ScheduleManager.java | 3 | ||||
-rw-r--r-- | src/main/java/org/traccar/schedule/TaskExpirations.java | 130 | ||||
-rw-r--r-- | templates/full/deviceExpiration.vm | 7 | ||||
-rw-r--r-- | templates/full/deviceExpirationReminder.vm | 7 | ||||
-rw-r--r-- | templates/full/userExpiration.vm | 7 | ||||
-rw-r--r-- | templates/full/userExpirationReminder.vm | 7 |
7 files changed, 189 insertions, 10 deletions
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 3059c4f4b..3528dafa0 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -490,14 +490,6 @@ public final class Keys { List.of(KeyType.CONFIG)); /** - * By default, server syncs with the database if it encounters and unknown device. This flag allows to disable that - * behavior to improve performance in some cases. - */ - public static final ConfigKey<Boolean> DATABASE_IGNORE_UNKNOWN = new BooleanConfigKey( - "database.ignoreUnknown", - List.of(KeyType.CONFIG)); - - /** * Automatically register unknown devices in the database. */ public static final ConfigKey<Boolean> DATABASE_REGISTER_UNKNOWN = new BooleanConfigKey( @@ -664,7 +656,7 @@ public final class Keys { /** * OpenID Connect Authorization URL. * This can usually be found in the documentation of your identity provider or by using the well-known - * configuration endpoint, eg. https://auth.example.com//.well-known/openid-configuration + * configuration endpoint, e.g. https://auth.example.com//.well-known/openid-configuration * Required to enable SSO if openid.issuerUrl is not set. */ public static final ConfigKey<String> OPENID_AUTH_URL = new StringConfigKey( @@ -1226,6 +1218,34 @@ public final class Keys { List.of(KeyType.CONFIG)); /** + * Enable user expiration email notification. + */ + public static final ConfigKey<Boolean> NOTIFICATION_EXPIRATION_USER = new BooleanConfigKey( + "notification.expiration.user", + List.of(KeyType.CONFIG)); + + /** + * User expiration reminder. Value in milliseconds. + */ + public static final ConfigKey<Long> NOTIFICATION_EXPIRATION_USER_REMINDER = new LongConfigKey( + "notification.expiration.user.reminder", + List.of(KeyType.CONFIG)); + + /** + * Enable device expiration email notification. + */ + public static final ConfigKey<Boolean> NOTIFICATION_EXPIRATION_DEVICE = new BooleanConfigKey( + "notification.expiration.device", + List.of(KeyType.CONFIG)); + + /** + * Device expiration reminder. Value in milliseconds. + */ + public static final ConfigKey<Long> NOTIFICATION_EXPIRATION_DEVICE_REMINDER = new LongConfigKey( + "notification.expiration.device.reminder", + List.of(KeyType.CONFIG)); + + /** * Maximum time period for reports in seconds. Can be useful to prevent users to request unreasonably long reports. * By default there is no limit. */ diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java index 38e8f281c..3756d955b 100644 --- a/src/main/java/org/traccar/schedule/ScheduleManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ public class ScheduleManager implements LifecycleObject { public void start() { executor = Executors.newSingleThreadScheduledExecutor(); var tasks = List.of( + TaskExpirations.class, TaskDeleteTemporary.class, TaskReports.class, TaskDeviceInactivityCheck.class, diff --git a/src/main/java/org/traccar/schedule/TaskExpirations.java b/src/main/java/org/traccar/schedule/TaskExpirations.java new file mode 100644 index 000000000..94f855c5f --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskExpirations.java @@ -0,0 +1,130 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.schedule; + +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.mail.MailManager; +import org.traccar.model.Device; +import org.traccar.model.Disableable; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.notification.TextTemplateFormatter; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskExpirations implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskExpirations.class); + + private static final long CHECK_PERIOD_HOURS = 1; + + private final Config config; + private final Storage storage; + private final TextTemplateFormatter textTemplateFormatter; + private final MailManager mailManager; + + @Inject + public TaskExpirations( + Config config, Storage storage, TextTemplateFormatter textTemplateFormatter, MailManager mailManager) { + this.config = config; + this.storage = storage; + this.textTemplateFormatter = textTemplateFormatter; + this.mailManager = mailManager; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS); + } + + private boolean checkTimeTrigger(Disableable disableable, long currentTime, long offsetTime) { + if (disableable.getExpirationTime() != null) { + long previousTime = currentTime - TimeUnit.HOURS.toMillis(CHECK_PERIOD_HOURS); + long expirationTime = disableable.getExpirationTime().getTime() + offsetTime; + return previousTime < expirationTime && currentTime >= expirationTime; + } + return false; + } + + private void sendUserExpiration( + Server server, User user, String template) throws MessagingException { + var velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("expiration", user.getExpirationTime()); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); + } + + private void sendDeviceExpiration( + Server server, Device device, String template) throws MessagingException, StorageException { + var users = storage.getObjects(User.class, new Request( + new Columns.All(), new Condition.Permission(User.class, Device.class, device.getId()))); + for (User user : users) { + var velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("expiration", device.getExpirationTime()); + velocityContext.put("device", device); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); + } + } + + @Override + public void run() { + try { + + long currentTime = System.currentTimeMillis(); + Server server = storage.getObject(Server.class, new Request(new Columns.All())); + + if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_USER)) { + long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER); + var users = storage.getObjects(User.class, new Request(new Columns.All())); + for (User user : users) { + if (checkTimeTrigger(user, currentTime, 0)) { + sendUserExpiration(server, user, "userExpiration"); + } else if (reminder > 0 && checkTimeTrigger(user, currentTime, -reminder)) { + sendUserExpiration(server, user, "userExpirationReminder"); + } + } + } + + if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_DEVICE)) { + long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER); + var devices = storage.getObjects(Device.class, new Request(new Columns.All())); + for (Device device : devices) { + if (checkTimeTrigger(device, currentTime, 0)) { + sendDeviceExpiration(server, device, "deviceExpiration"); + } else if (reminder > 0 && checkTimeTrigger(device, currentTime, -reminder)) { + sendDeviceExpiration(server, device, "deviceExpirationReminder"); + } + } + } + + } catch (StorageException | MessagingException e) { + LOGGER.warn("Failed to check expirations", e); + } + } + +} diff --git a/templates/full/deviceExpiration.vm b/templates/full/deviceExpiration.vm new file mode 100644 index 000000000..879b31778 --- /dev/null +++ b/templates/full/deviceExpiration.vm @@ -0,0 +1,7 @@ +#set($subject = "Device expiration") +<!DOCTYPE html> +<html> +<body> +Your device $device.name has expired. +</body> +</html> diff --git a/templates/full/deviceExpirationReminder.vm b/templates/full/deviceExpirationReminder.vm new file mode 100644 index 000000000..aa47ac0ed --- /dev/null +++ b/templates/full/deviceExpirationReminder.vm @@ -0,0 +1,7 @@ +#set($subject = "Device expiration reminder") +<!DOCTYPE html> +<html> +<body> +Your device $device.name will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone). +</body> +</html> diff --git a/templates/full/userExpiration.vm b/templates/full/userExpiration.vm new file mode 100644 index 000000000..43fe2563e --- /dev/null +++ b/templates/full/userExpiration.vm @@ -0,0 +1,7 @@ +#set($subject = "Account expiration") +<!DOCTYPE html> +<html> +<body> +Your user account has expired. +</body> +</html> diff --git a/templates/full/userExpirationReminder.vm b/templates/full/userExpirationReminder.vm new file mode 100644 index 000000000..ecdee0588 --- /dev/null +++ b/templates/full/userExpirationReminder.vm @@ -0,0 +1,7 @@ +#set($subject = "Account expiration reminder") +<!DOCTYPE html> +<html> +<body> +Your user account will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone). +</body> +</html> |