Erweiterungen
This commit is contained in:
22
pom.xml
22
pom.xml
@@ -103,6 +103,28 @@
|
|||||||
<artifactId>openpdf</artifactId>
|
<artifactId>openpdf</artifactId>
|
||||||
<version>1.3.30</version>
|
<version>1.3.30</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- iText 7 Core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.itextpdf</groupId>
|
||||||
|
<artifactId>itext7-core</artifactId>
|
||||||
|
<version>7.2.5</version>
|
||||||
|
<type>pom</type>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- iText 7 Kernel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.itextpdf</groupId>
|
||||||
|
<artifactId>kernel</artifactId>
|
||||||
|
<version>7.2.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- iText 7 Layout -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.itextpdf</groupId>
|
||||||
|
<artifactId>layout</artifactId>
|
||||||
|
<version>7.2.5</version>
|
||||||
|
</dependency>
|
||||||
<!-- Spring Boot Mail Starter -->
|
<!-- Spring Boot Mail Starter -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient;
|
|||||||
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
|
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
|
||||||
import de.assecutor.votianlt.config.MqttProperties;
|
import de.assecutor.votianlt.config.MqttProperties;
|
||||||
import de.assecutor.votianlt.controller.MessageController;
|
import de.assecutor.votianlt.controller.MessageController;
|
||||||
|
import de.assecutor.votianlt.model.PendingMqttMessage;
|
||||||
|
import de.assecutor.votianlt.repository.PendingMqttMessageRepository;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.SmartLifecycle;
|
import org.springframework.context.SmartLifecycle;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -16,6 +18,7 @@ import org.springframework.context.annotation.Lazy;
|
|||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -29,14 +32,16 @@ public class MqttV5ClientManager implements SmartLifecycle {
|
|||||||
|
|
||||||
private final MqttProperties props;
|
private final MqttProperties props;
|
||||||
private final MessageController messageController;
|
private final MessageController messageController;
|
||||||
|
private final PendingMqttMessageRepository pendingMessageRepository;
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
private Mqtt5AsyncClient client;
|
private Mqtt5AsyncClient client;
|
||||||
|
|
||||||
public MqttV5ClientManager(MqttProperties props, @Lazy MessageController messageController) {
|
public MqttV5ClientManager(MqttProperties props, @Lazy MessageController messageController, PendingMqttMessageRepository pendingMessageRepository) {
|
||||||
this.props = props;
|
this.props = props;
|
||||||
this.messageController = messageController;
|
this.messageController = messageController;
|
||||||
|
this.pendingMessageRepository = pendingMessageRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -89,6 +94,9 @@ public class MqttV5ClientManager implements SmartLifecycle {
|
|||||||
}
|
}
|
||||||
running = true;
|
running = true;
|
||||||
log.info("[MQTT] Subscribed to {} topics (QoS={}), awaiting messages ...", topics.length, qos);
|
log.info("[MQTT] Subscribed to {} topics (QoS={}), awaiting messages ...", topics.length, qos);
|
||||||
|
|
||||||
|
// Process pending messages after successful connection
|
||||||
|
processPendingMessages();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to start HiveMQ MQTT client: {}", e.getMessage(), e);
|
log.error("Failed to start HiveMQ MQTT client: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -192,18 +200,75 @@ public class MqttV5ClientManager implements SmartLifecycle {
|
|||||||
|
|
||||||
public void publish(String topic, byte[] payload, int qos, boolean retained) {
|
public void publish(String topic, byte[] payload, int qos, boolean retained) {
|
||||||
try {
|
try {
|
||||||
if (client == null) {
|
if (client == null || !running) {
|
||||||
log.warn("[MQTT] Not connected, dropping publish topic={}", topic);
|
log.warn("[MQTT] Not connected, saving message for later: topic={}", topic);
|
||||||
|
savePendingMessage(topic, payload, qos, retained);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
client.publishWith().topic(topic).payload(payload).qos(mapQos(qos)).retain(retained).send()
|
client.publishWith().topic(topic).payload(payload).qos(mapQos(qos)).retain(retained).send()
|
||||||
.whenComplete((ack, ex) -> {
|
.whenComplete((ack, ex) -> {
|
||||||
if (ex != null) {
|
if (ex != null) {
|
||||||
log.error("Failed to publish to {}: {}", topic, ex.getMessage(), ex);
|
log.error("Failed to publish to {}: {}, saving for retry", topic, ex.getMessage(), ex);
|
||||||
|
savePendingMessage(topic, payload, qos, retained);
|
||||||
|
} else {
|
||||||
|
log.debug("Successfully published to topic: {}", topic);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to publish to {}: {}", topic, e.getMessage(), e);
|
log.error("Failed to publish to {}: {}, saving for retry", topic, e.getMessage(), e);
|
||||||
|
savePendingMessage(topic, payload, qos, retained);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePendingMessage(String topic, byte[] payload, int qos, boolean retained) {
|
||||||
|
try {
|
||||||
|
PendingMqttMessage pendingMessage = new PendingMqttMessage(topic, payload, qos, retained);
|
||||||
|
pendingMessageRepository.save(pendingMessage);
|
||||||
|
log.info("[MQTT] Saved pending message for topic: {}", topic);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to save pending MQTT message: {}", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processPendingMessages() {
|
||||||
|
try {
|
||||||
|
List<PendingMqttMessage> pendingMessages = pendingMessageRepository.findAllByOrderByCreatedAtAsc();
|
||||||
|
if (pendingMessages.isEmpty()) {
|
||||||
|
log.debug("[MQTT] No pending messages to process");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("[MQTT] Processing {} pending messages", pendingMessages.size());
|
||||||
|
for (PendingMqttMessage pendingMessage : pendingMessages) {
|
||||||
|
try {
|
||||||
|
// Attempt to send the pending message
|
||||||
|
client.publishWith()
|
||||||
|
.topic(pendingMessage.getTopic())
|
||||||
|
.payload(pendingMessage.getPayload())
|
||||||
|
.qos(mapQos(pendingMessage.getQos()))
|
||||||
|
.retain(pendingMessage.isRetained())
|
||||||
|
.send()
|
||||||
|
.whenComplete((ack, ex) -> {
|
||||||
|
if (ex != null) {
|
||||||
|
log.warn("Failed to resend pending message to {}: {}", pendingMessage.getTopic(), ex.getMessage());
|
||||||
|
// Update retry count
|
||||||
|
pendingMessage.incrementRetryCount();
|
||||||
|
pendingMessageRepository.save(pendingMessage);
|
||||||
|
} else {
|
||||||
|
// Successfully sent, remove from pending
|
||||||
|
log.info("Successfully resent pending message to topic: {}", pendingMessage.getTopic());
|
||||||
|
pendingMessageRepository.delete(pendingMessage);
|
||||||
|
}
|
||||||
|
}).join(); // Wait for completion to process sequentially
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Error processing pending message for {}: {}", pendingMessage.getTopic(), e.getMessage());
|
||||||
|
pendingMessage.incrementRetryCount();
|
||||||
|
pendingMessageRepository.save(pendingMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error processing pending MQTT messages: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import static com.vaadin.flow.theme.lumo.LumoUtility.*;
|
|||||||
|
|
||||||
@AnonymousAllowed
|
@AnonymousAllowed
|
||||||
|
|
||||||
@Layout
|
@Layout("main")
|
||||||
public final class MainLayout extends AppLayout {
|
public final class MainLayout extends AppLayout {
|
||||||
|
|
||||||
private final SecurityService securityService;
|
private final SecurityService securityService;
|
||||||
|
|||||||
@@ -12,16 +12,24 @@ import com.vaadin.flow.router.Route;
|
|||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
|
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import com.vaadin.flow.component.UI;
|
||||||
|
|
||||||
@Route(value = "dashboard", layout = MainLayout.class)
|
@Route(value = "dashboard", layout = MainLayout.class)
|
||||||
@PageTitle("VotianLT - Dashboard")
|
@PageTitle("VotianLT - Dashboard")
|
||||||
@RolesAllowed("USER")
|
@RolesAllowed({"USER"})
|
||||||
public class AuthenticatedStartView extends VerticalLayout {
|
public class AuthenticatedStartView extends VerticalLayout {
|
||||||
|
|
||||||
private final SecurityService securityService;
|
private final SecurityService securityService;
|
||||||
|
|
||||||
public AuthenticatedStartView(SecurityService securityService) {
|
public AuthenticatedStartView(SecurityService securityService) {
|
||||||
this.securityService = securityService;
|
this.securityService = securityService;
|
||||||
|
|
||||||
|
// Redirect admin users to admin dashboard
|
||||||
|
if (securityService.hasRole("ADMIN")) {
|
||||||
|
getUI().ifPresent(ui -> ui.navigate("admin-dashboard"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
setPadding(false);
|
setPadding(false);
|
||||||
setSpacing(false);
|
setSpacing(false);
|
||||||
|
|||||||
@@ -115,7 +115,13 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
|
|||||||
wrappedSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
|
wrappedSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
|
||||||
SecurityContextHolder.getContext());
|
SecurityContextHolder.getContext());
|
||||||
}
|
}
|
||||||
UI.getCurrent().getPage().setLocation("/dashboard");
|
// Check if user is admin and redirect accordingly
|
||||||
|
if (auth.getAuthorities().stream()
|
||||||
|
.anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"))) {
|
||||||
|
UI.getCurrent().getPage().setLocation("/admin-dashboard");
|
||||||
|
} else {
|
||||||
|
UI.getCurrent().getPage().setLocation("/dashboard");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
loginForm.setError(true);
|
loginForm.setError(true);
|
||||||
@@ -152,7 +158,13 @@ public class LoginView extends VerticalLayout implements BeforeEnterObserver, Af
|
|||||||
}
|
}
|
||||||
this.pendingAuth = null;
|
this.pendingAuth = null;
|
||||||
// Full reload, damit der neue SecurityContext im UI sicher greift
|
// Full reload, damit der neue SecurityContext im UI sicher greift
|
||||||
UI.getCurrent().getPage().setLocation("/dashboard");
|
// Check if user is admin and redirect accordingly
|
||||||
|
if (SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream()
|
||||||
|
.anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN"))) {
|
||||||
|
UI.getCurrent().getPage().setLocation("/admin-dashboard");
|
||||||
|
} else {
|
||||||
|
UI.getCurrent().getPage().setLocation("/dashboard");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ import java.util.List;
|
|||||||
public interface TaskRepository extends MongoRepository<BaseTask, ObjectId> {
|
public interface TaskRepository extends MongoRepository<BaseTask, ObjectId> {
|
||||||
List<BaseTask> findByJobIdOrderByTaskOrderAsc(ObjectId jobId);
|
List<BaseTask> findByJobIdOrderByTaskOrderAsc(ObjectId jobId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count tasks by completion status
|
||||||
|
*/
|
||||||
|
long countByCompleted(boolean completed);
|
||||||
|
|
||||||
// Deprecated - use findByJobIdOrderByTaskOrderAsc instead
|
// Deprecated - use findByJobIdOrderByTaskOrderAsc instead
|
||||||
@Deprecated
|
@Deprecated
|
||||||
default List<BaseTask> findByJobId(ObjectId jobId) {
|
default List<BaseTask> findByJobId(ObjectId jobId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user