Erweiterungen

This commit is contained in:
2025-08-29 09:55:21 +02:00
parent 21b0bb946f
commit 4d2ed39275
7 changed files with 186 additions and 2 deletions

View File

@@ -1,8 +1,14 @@
package de.assecutor.votianlt.controller;
import de.assecutor.votianlt.dto.AppLoginRequest;
import de.assecutor.votianlt.dto.AppLoginResponse;
import de.assecutor.votianlt.model.AppUser;
import de.assecutor.votianlt.pages.service.AppUserService;
import de.assecutor.votianlt.repository.AppUserRepository;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.stereotype.Controller;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,6 +26,12 @@ public class MessageController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Autowired
private AppUserRepository appUserRepository;
@Autowired
private AppUserService appUserService;
/**
* Handles messages sent to /app/message and broadcasts them to all subscribers of /topic/messages
*/
@@ -82,4 +94,30 @@ public class MessageController {
messagingTemplate.convertAndSend("/topic/broadcasts", broadcast);
}
/**
* Authentication endpoint for mobile app users via STOMP.
* Client sends to /app/auth/login with payload { email, password }.
* The response is sent back to the requesting user on /user/queue/auth
*/
@MessageMapping("/auth/login")
@SendToUser("/queue/auth")
public AppLoginResponse handleAppLogin(AppLoginRequest request) {
if (request == null || request.getEmail() == null || request.getPassword() == null
|| request.getEmail().isBlank() || request.getPassword().isBlank()) {
return new AppLoginResponse(false, "E-Mail und Passwort sind erforderlich", null);
}
AppUser user = appUserRepository.findByEmail(request.getEmail());
if (user == null) {
return new AppLoginResponse(false, "Benutzer nicht gefunden", null);
}
boolean ok = appUserService.verifyPassword(request.getPassword(), user.getPassword());
if (!ok) {
return new AppLoginResponse(false, "Ungültige Anmeldedaten", null);
}
return new AppLoginResponse(true, "Anmeldung erfolgreich", user.getId() != null ? user.getId().toHexString() : null);
}
}

View File

@@ -0,0 +1,9 @@
package de.assecutor.votianlt.dto;
import lombok.Data;
@Data
public class AppLoginRequest {
private String email;
private String password;
}

View File

@@ -0,0 +1,12 @@
package de.assecutor.votianlt.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class AppLoginResponse {
private boolean success;
private String message;
private String appUserId; // MongoDB ObjectId as hex string
}

View File

@@ -13,8 +13,9 @@ public interface AppUserRepository extends MongoRepository<AppUser, ObjectId> {
// Find all AppUsers created by a specific user
List<AppUser> findByErstelltVon(ObjectId erstelltVon);
// Find AppUser by email for login
AppUser findByEmail(String email);
// Custom query methods can be added here if needed
// For example:
// List<AppUser> findByEmail(String email);
// List<AppUser> findByBezeichnung(String bezeichnung);
}

View File

@@ -0,0 +1,86 @@
package de.assecutor.votianlt.zeroconf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.net.InetAddress;
/**
* Publishes the STOMP WebSocket endpoint via Zeroconf (mDNS/Bonjour) using reflection.
* If JmDNS is present on the classpath, it will register the service _stomp._tcp.local.
*/
@Component
public class ZeroconfPublisher {
private static final Logger logger = LoggerFactory.getLogger(ZeroconfPublisher.class);
@Value("${server.port:8080}")
private int serverPort;
// Expose stomp endpoints paths via TXT records
@Value("${app.stomp.wsPath:/ws}")
private String wsPath;
@Value("${app.stomp.websocketPath:/websocket}")
private String websocketPath;
@Value("${app.zeroconf.enabled:true}")
private boolean enabled;
@Value("${app.zeroconf.serviceName:votianlt-stomp}")
private String serviceName;
// Controls whether to log a notice if JmDNS is not available
@Value("${app.zeroconf.warnWhenMissing:false}")
private boolean warnWhenMissing;
private Object jmdns; // javax.jmdns.JmDNS instance if available
@EventListener(org.springframework.boot.context.event.ApplicationReadyEvent.class)
public void onAppReady() {
if (!enabled) return;
try {
Class<?> jmDNSClass = Class.forName("javax.jmdns.JmDNS");
Class<?> serviceInfoClass = Class.forName("javax.jmdns.ServiceInfo");
InetAddress addr = InetAddress.getLocalHost();
Method createMethod = jmDNSClass.getMethod("create", InetAddress.class);
jmdns = createMethod.invoke(null, addr);
String type = "_stomp._tcp.local.";
String text = "path=" + wsPath + ",websocket=" + websocketPath + ",protocol=stomp";
Method createServiceInfo = serviceInfoClass.getMethod("create", String.class, String.class, int.class, String.class);
Object serviceInfo = createServiceInfo.invoke(null, type, serviceName, serverPort, text);
Method registerService = jmDNSClass.getMethod("registerService", serviceInfoClass);
registerService.invoke(jmdns, serviceInfo);
logger.info("STOMP-Service veröffentlicht: {} name={} port={}", type, serviceName, serverPort);
} catch (ClassNotFoundException e) {
if (warnWhenMissing) {
logger.warn("Hinweis: JmDNS ist nicht vorhanden Zeroconf ist deaktiviert.");
}
} catch (Exception e) {
logger.error("Registrierung fehlgeschlagen: {}", e.getMessage(), e);
}
}
@EventListener(ContextClosedEvent.class)
public void onShutdown() {
if (jmdns != null) {
try {
Method unregisterAll = jmdns.getClass().getMethod("unregisterAllServices");
unregisterAll.invoke(jmdns);
Method close = jmdns.getClass().getMethod("close");
close.invoke(jmdns);
} catch (Exception ignored) {
}
}
}
}