Flutter-App mit WebView, STOMP-Client und Konfigurationssystem
- Flutter-App (app/) für iOS, Android, macOS, Windows und Linux erstellt - WebView-Startseite mit flutter_inappwebview (iOS/Android/macOS/Windows), Linux-Fallback mit url_launcher - STOMP-over-WebSocket: Topic-basierte Echtzeit-Kommunikation zwischen Flutter-App und Spring Boot Core - Core: STOMP-Broker (/ws/stomp), CallEventBroadcaster auf /topic/calls, StompMessageController für /app/ping und /app/broadcast - SecurityConfig: /ws/** permitAll + CSRF-Ausnahme - Asset-basierte Konfigurationsdatei (app_config.json) für Server-URL, STOMP-Reconnect, Topics und WebView-URL - launch.json um Flutter-Debug/Profile/Release-Konfigurationen erweitert - macOS: FLTEnableMergedPlatformUIThread deaktiviert (WKWebView-Kompatibilität), network.client Entitlement gesetzt - iOS: NSAllowsLocalNetworking für lokale Entwicklung - Android: INTERNET-Permission und usesCleartextTraffic Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,11 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
|
||||
@@ -45,8 +45,11 @@ public class SecurityConfig extends VaadinWebSecurity {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers(new AntPathRequestMatcher("/api/**")).permitAll()
|
||||
.requestMatchers(new AntPathRequestMatcher("/ws/**")).permitAll()
|
||||
.requestMatchers(new AntPathRequestMatcher("/actuator/health")).permitAll());
|
||||
http.csrf(csrf -> csrf.ignoringRequestMatchers(new AntPathRequestMatcher("/api/**")));
|
||||
http.csrf(csrf -> csrf
|
||||
.ignoringRequestMatchers(new AntPathRequestMatcher("/api/**"))
|
||||
.ignoringRequestMatchers(new AntPathRequestMatcher("/ws/**")));
|
||||
super.configure(http);
|
||||
setLoginView(http, LoginView.class);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package de.assecutor.swyx.ws;
|
||||
|
||||
import de.assecutor.swyx.webhook.CallEventStore;
|
||||
import de.assecutor.swyx.webhook.StoredCallEvent;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CallEventBroadcaster {
|
||||
|
||||
public static final String TOPIC_ALL = "/topic/calls";
|
||||
public static final String TOPIC_PER_TYPE_PREFIX = "/topic/calls/";
|
||||
|
||||
private final CallEventStore store;
|
||||
private final SimpMessagingTemplate messaging;
|
||||
private CallEventStore.Registration registration;
|
||||
|
||||
public CallEventBroadcaster(CallEventStore store, SimpMessagingTemplate messaging) {
|
||||
this.store = store;
|
||||
this.messaging = messaging;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void subscribe() {
|
||||
registration = store.register(this::publish);
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void unsubscribe() {
|
||||
if (registration != null) {
|
||||
registration.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private void publish(StoredCallEvent event) {
|
||||
messaging.convertAndSend(TOPIC_ALL, event);
|
||||
if (event.eventType() != null) {
|
||||
messaging.convertAndSend(TOPIC_PER_TYPE_PREFIX + event.eventType().name().toLowerCase(), event);
|
||||
}
|
||||
log.debug("Broadcast STOMP call event: callId={} type={}", event.callId(), event.eventType());
|
||||
}
|
||||
}
|
||||
24
core/src/main/java/de/assecutor/swyx/ws/StompConfig.java
Normal file
24
core/src/main/java/de/assecutor/swyx/ws/StompConfig.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package de.assecutor.swyx.ws;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class StompConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/ws/stomp").setAllowedOriginPatterns("*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
registry.enableSimpleBroker("/topic", "/queue");
|
||||
registry.setApplicationDestinationPrefixes("/app");
|
||||
registry.setUserDestinationPrefix("/user");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.assecutor.swyx.ws;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.Payload;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Controller
|
||||
public class StompMessageController {
|
||||
|
||||
@MessageMapping("/ping")
|
||||
@SendTo("/topic/pong")
|
||||
public Map<String, Object> ping(@Payload Map<String, Object> payload) {
|
||||
log.info("STOMP /app/ping: {}", payload);
|
||||
Map<String, Object> response = new HashMap<>(payload);
|
||||
response.put("pongAt", OffsetDateTime.now().toString());
|
||||
return response;
|
||||
}
|
||||
|
||||
@MessageMapping("/broadcast")
|
||||
@SendTo("/topic/messages")
|
||||
public Map<String, Object> broadcast(@Payload Map<String, Object> payload) {
|
||||
log.info("STOMP /app/broadcast: {}", payload);
|
||||
Map<String, Object> response = new HashMap<>(payload);
|
||||
response.put("broadcastAt", OffsetDateTime.now().toString());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user