@longtq2501/next-spring-skills 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -63,6 +63,9 @@ See [ui-state.md](./nextjs/ui-state.md) for Zustand store patterns and URL param
|
|
|
63
63
|
### Three.js & 3D
|
|
64
64
|
See [threejs.md](./nextjs/threejs.md) for high-performance 3D development and R3F.
|
|
65
65
|
|
|
66
|
+
### WebRTC & P2P
|
|
67
|
+
See [webrtc.md](./nextjs/webrtc.md) for real-time media and signaling.
|
|
68
|
+
|
|
66
69
|
### Accessibility (a11y)
|
|
67
70
|
See [accessibility.md](./nextjs/accessibility.md) for semantic HTML and ARIA.
|
|
68
71
|
|
|
@@ -111,6 +114,10 @@ High-performance, stateless REST API patterns using Spring Boot, JPA, and JWT.
|
|
|
111
114
|
- [Query Optimization](./spring/query_optimization.md)
|
|
112
115
|
- [Performance Optimization](./spring/performance_optimization.md)
|
|
113
116
|
|
|
117
|
+
### Real-time
|
|
118
|
+
- [WebSocket & STOMP](./spring/websocket.md)
|
|
119
|
+
- [Server-Sent Events (SSE)](./spring/sse.md)
|
|
120
|
+
|
|
114
121
|
---
|
|
115
122
|
|
|
116
123
|
**Templates:** See the [spring/templates/](./spring/templates/) directory for production-ready boilerplates.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Skill: WebRTC - Peer-to-Peer Communication
|
|
2
|
+
|
|
3
|
+
Guidelines for implementing real-time video, audio, and data streaming between browsers.
|
|
4
|
+
|
|
5
|
+
## TL;DR - Quick Reference
|
|
6
|
+
|
|
7
|
+
### Critical Rules
|
|
8
|
+
1. **Signaling Server Required**: WebRTC needs a side-channel (WebSocket/SSE) to exchange SDP and ICE candidates.
|
|
9
|
+
2. **ICE Protocol**: Use STUN/TURN servers to bypass NAT and Firewalls. (Google's STUN is for dev, use Twilio/Metered for Prod).
|
|
10
|
+
3. **Media Constraints**: Use `navigator.mediaDevices.getUserMedia` with specific resolutions to optimize performance.
|
|
11
|
+
4. **DataChannels**: Use `RTCDataChannel` for low-latency P2P data (e.g., file sharing, gaming).
|
|
12
|
+
5. **Security**: Always use HTTPS. WebRTC will not work on non-secure origins.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Core Lifecycle (Signaling)
|
|
17
|
+
|
|
18
|
+
WebRTC communication happens in steps:
|
|
19
|
+
1. **Offer**: Peer A creates an offer (SDP).
|
|
20
|
+
2. **Answer**: Peer B receives the offer and sends back an answer.
|
|
21
|
+
3. **ICE Candidates**: Both peers exchange network pathway information.
|
|
22
|
+
|
|
23
|
+
// Bad: Trying to connect without a signaling server
|
|
24
|
+
// Good: Using WebSocket for signaling
|
|
25
|
+
socket.on('offer', async (offer) => {
|
|
26
|
+
await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
|
|
27
|
+
const answer = await peerConnection.createAnswer();
|
|
28
|
+
await peerConnection.setLocalDescription(answer);
|
|
29
|
+
socket.emit('answer', answer);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Media Handling
|
|
35
|
+
|
|
36
|
+
### Capturing Video/Audio
|
|
37
|
+
// Good: Requesting specific constraints
|
|
38
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
39
|
+
video: { width: 1280, height: 720 },
|
|
40
|
+
audio: true
|
|
41
|
+
});
|
|
42
|
+
videoRef.current.srcObject = stream;
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 3. NAT Traversal (STUN/TURN)
|
|
47
|
+
Without these, peers cannot find each other over the public internet.
|
|
48
|
+
|
|
49
|
+
// Good: Configuration with ICE servers
|
|
50
|
+
const pcConfig = {
|
|
51
|
+
iceServers: [
|
|
52
|
+
{ urls: 'stun:stun.l.google.com:19302' }, // STUN finds public IP
|
|
53
|
+
{
|
|
54
|
+
urls: 'turn:my-turn-server.com', // TURN relays data if P2P fails
|
|
55
|
+
username: 'user',
|
|
56
|
+
credential: 'password'
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
const peerConnection = new RTCPeerConnection(pcConfig);
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 4. Performance & Troubleshooting
|
|
65
|
+
|
|
66
|
+
### Optimization
|
|
67
|
+
- **Simulcast**: Send multiple resolution streams to cater to different bandwidths.
|
|
68
|
+
- **Bitrate Capping**: Manually limit bandwidth to prevent congestion.
|
|
69
|
+
|
|
70
|
+
### Debugging
|
|
71
|
+
- **chrome://webrtc-internals**: Essential tool for inspecting connections and bitrates.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Related Skills
|
|
76
|
+
- **WebSocket & STOMP**: `skills/spring/websocket.md`
|
|
77
|
+
- **Interactivity & Animation**: `skills/nextjs/interactivity.md`
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Skill: Server-Sent Events (SSE)
|
|
2
|
+
|
|
3
|
+
Guidelines for implementing lightweight unidirectional server-to-client streaming.
|
|
4
|
+
|
|
5
|
+
## TL;DR - Quick Reference
|
|
6
|
+
|
|
7
|
+
### Critical Rules
|
|
8
|
+
1. **Unidirectional**: Use SSE for server-to-client push only (e.g., notifications, stock tickers). Use WebSocket for bi-directional.
|
|
9
|
+
2. **Standard Protocol**: SSE uses standard HTTP and doesn't require a special protocol upgrade (unlike WebSocket).
|
|
10
|
+
3. **Reconnection**: Browsers automatically reconnect to SSE streams if the connection drops.
|
|
11
|
+
4. **Content-Type**: Always set response to `text/event-stream`.
|
|
12
|
+
5. **Resource Cleaning**: Always complete or timeout `SseEmitter` to avoid thread/memory leaks.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Spring Boot Implementation
|
|
17
|
+
|
|
18
|
+
### SseEmitter (Imperative)
|
|
19
|
+
Ideal for basic notifications within a standard Spring Web project.
|
|
20
|
+
|
|
21
|
+
// Good: Basic SSE Controller
|
|
22
|
+
@GetMapping(path = "/notifications", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
|
23
|
+
public SseEmitter streamNotifications() {
|
|
24
|
+
SseEmitter emitter = new SseEmitter(30_000L); // 30s timeout
|
|
25
|
+
|
|
26
|
+
// In a real app, store emitter in a Map keyed by UserID
|
|
27
|
+
executor.execute(() -> {
|
|
28
|
+
try {
|
|
29
|
+
emitter.send(SseEmitter.event()
|
|
30
|
+
.name("message")
|
|
31
|
+
.data("Hello at " + LocalTime.now()));
|
|
32
|
+
emitter.complete();
|
|
33
|
+
} catch (Exception e) {
|
|
34
|
+
emitter.completeWithError(e);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return emitter;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
### WebFlux (Reactive)
|
|
41
|
+
Best for high-concurrency streaming.
|
|
42
|
+
|
|
43
|
+
// Good: Reactive Streaming
|
|
44
|
+
@GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
|
45
|
+
public Flux<String> streamFlux() {
|
|
46
|
+
return Flux.interval(Duration.ofSeconds(1))
|
|
47
|
+
.map(i -> "Data chunk " + i);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 2. Next.js Client Integration
|
|
53
|
+
|
|
54
|
+
### EventSource API
|
|
55
|
+
SSE is natively supported by the browser's `EventSource`.
|
|
56
|
+
|
|
57
|
+
// Good: Consuming SSE in React
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const eventSource = new EventSource('/api/notifications');
|
|
60
|
+
|
|
61
|
+
eventSource.onmessage = (event) => {
|
|
62
|
+
const newData = JSON.parse(event.data);
|
|
63
|
+
setNotifications(prev => [...prev, newData]);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
eventSource.onerror = (err) => {
|
|
67
|
+
console.error("SSE failed:", err);
|
|
68
|
+
eventSource.close();
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return () => eventSource.close(); // Important: Cleanup
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 3. Advanced Patterns
|
|
77
|
+
|
|
78
|
+
### Last-Event-ID
|
|
79
|
+
Used for data consistency during reconnections.
|
|
80
|
+
- The server sends an `id` with each event.
|
|
81
|
+
- If the client reconnects, it sends the `Last-Event-ID` header.
|
|
82
|
+
|
|
83
|
+
### Scaling SSE
|
|
84
|
+
- **Load Balancers**: Ensure your load balancer supports long-lived connections and `text/event-stream` (disable buffering).
|
|
85
|
+
- **Redis Pub/Sub**: Use Redis to broadcast events across multiple server instances.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Related Skills
|
|
90
|
+
- **WebSocket & STOMP**: `skills/spring/websocket.md`
|
|
91
|
+
- **Performance Optimization**: `skills/spring/performance_optimization.md`
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package com.example.config;
|
|
2
|
+
|
|
3
|
+
import org.springframework.context.annotation.Configuration;
|
|
4
|
+
import org.springframework.messaging.simp.config.ChannelRegistration;
|
|
5
|
+
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
|
6
|
+
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
|
7
|
+
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
|
8
|
+
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Standard WebSocket Configuration using STOMP.
|
|
12
|
+
*/
|
|
13
|
+
@Configuration
|
|
14
|
+
@EnableWebSocketMessageBroker
|
|
15
|
+
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
|
16
|
+
|
|
17
|
+
@Override
|
|
18
|
+
public void configureMessageBroker(MessageBrokerRegistry config) {
|
|
19
|
+
// /topic for Broadcast (1-to-many)
|
|
20
|
+
// /queue for Private (1-to-1)
|
|
21
|
+
config.enableSimpleBroker("/topic", "/queue");
|
|
22
|
+
|
|
23
|
+
// Prefix for messages originating from the client
|
|
24
|
+
config.setApplicationDestinationPrefixes("/app");
|
|
25
|
+
|
|
26
|
+
// Prefix for private messages (SendToUser)
|
|
27
|
+
config.setUserDestinationPrefix("/user");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Override
|
|
31
|
+
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
|
32
|
+
registry.addEndpoint("/ws")
|
|
33
|
+
.setAllowedOrigins("*") // In production, limit this to your frontend domain
|
|
34
|
+
.withSockJS(); // Enables fallback options for older browsers
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Override
|
|
38
|
+
public void configureClientInboundChannel(ChannelRegistration registration) {
|
|
39
|
+
// Add interceptors for security (e.g., JWT validation on CONNECT frames)
|
|
40
|
+
// registration.interceptors(myAuthInterceptor);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Skill: WebSocket & Real-time Communication
|
|
2
|
+
|
|
3
|
+
Guidelines for implementing robust, secure, and scalable real-time messaging using Spring Boot (STOMP) and Next.js.
|
|
4
|
+
|
|
5
|
+
## TL;DR - Quick Reference
|
|
6
|
+
|
|
7
|
+
### Critical Rules
|
|
8
|
+
1. **Use STOMP over WebSocket**: Standard sub-protocol for messaging with built-in routing (`@MessageMapping`).
|
|
9
|
+
2. **Security first**: Authenticate the initial WebSocket handshake using JWT in headers or query params.
|
|
10
|
+
3. **Heartbeats**: Enable heartbeats to detect and close dead connections promptly.
|
|
11
|
+
4. **Error Handling**: Use `@MessageExceptionHandler` to gracefully handle errors in message processing.
|
|
12
|
+
5. **Payload Size**: Keep WebSocket messages small; for large data, send a notification and fetch via REST.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Spring Boot Configuration
|
|
17
|
+
|
|
18
|
+
### STOMP Setup
|
|
19
|
+
Always separate the message broker into a "Simple Broker" (for dev/local) and a "Full Broker" (like RabbitMQ) for production scaling.
|
|
20
|
+
|
|
21
|
+
// Good: Basic STOMP Configuration
|
|
22
|
+
@Configuration
|
|
23
|
+
@EnableWebSocketMessageBroker
|
|
24
|
+
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
|
25
|
+
@Override
|
|
26
|
+
public void configureMessageBroker(MessageBrokerRegistry config) {
|
|
27
|
+
config.enableSimpleBroker("/topic", "/queue"); // Simple in-memory broker
|
|
28
|
+
config.setApplicationDestinationPrefixes("/app");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Override
|
|
32
|
+
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
|
33
|
+
registry.addEndpoint("/ws")
|
|
34
|
+
.setAllowedOrigins("*")
|
|
35
|
+
.withSockJS(); // Fallback for browsers without WS support
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Message Handling
|
|
42
|
+
|
|
43
|
+
### Controller Pattern
|
|
44
|
+
Use `@MessageMapping` to handle incoming messages and `@SendTo` to broadcast output.
|
|
45
|
+
|
|
46
|
+
// Good: Real-time Chat Example
|
|
47
|
+
@Controller
|
|
48
|
+
public class ChatController {
|
|
49
|
+
|
|
50
|
+
@MessageMapping("/chat.send")
|
|
51
|
+
@SendTo("/topic/public")
|
|
52
|
+
public ChatMessage sendMessage(@Payload ChatMessage message) {
|
|
53
|
+
return message;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@MessageExceptionHandler
|
|
57
|
+
@SendToUser("/queue/errors")
|
|
58
|
+
public String handleException(Exception exception) {
|
|
59
|
+
return "Error: " + exception.getMessage();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 3. Security (JWT & Interceptors)
|
|
66
|
+
Since WebSockets are long-lived, the initial handshake is the critical entry point, but STOMP frames also need individual validation.
|
|
67
|
+
|
|
68
|
+
### Handshake vs. Interceptors
|
|
69
|
+
- **Handshake Interceptor**: Used for the initial HTTP upgrade request.
|
|
70
|
+
- **Channel Interceptor**: Required for validating tokens on every `CONNECT` or `SUBSCRIBE` frame.
|
|
71
|
+
|
|
72
|
+
// Good: Registering a STOMP Interceptor
|
|
73
|
+
@Override
|
|
74
|
+
public void configureClientInboundChannel(ChannelRegistration registration) {
|
|
75
|
+
registration.interceptors(new ChannelInterceptor() {
|
|
76
|
+
@Override
|
|
77
|
+
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
|
78
|
+
StompHeaderAccessor accessor = MessageHeaderUtils.getAccessor(message, StompHeaderAccessor.class);
|
|
79
|
+
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
|
|
80
|
+
String token = accessor.getFirstNativeHeader("Authorization");
|
|
81
|
+
// Validate token and set SecurityContext
|
|
82
|
+
}
|
|
83
|
+
return message;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. Frontend Integration (Next.js)
|
|
91
|
+
Use libraries like `@stomp/stompjs` for robust STOMP client management.
|
|
92
|
+
|
|
93
|
+
// Good: Client-side Connection
|
|
94
|
+
const client = new Client({
|
|
95
|
+
brokerURL: 'ws://localhost:8080/ws',
|
|
96
|
+
onConnect: () => {
|
|
97
|
+
client.subscribe('/topic/public', (message) => {
|
|
98
|
+
console.log('Received:', JSON.parse(message.body));
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
client.activate();
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Related Skills
|
|
107
|
+
- **Security Config**: `skills/spring/security_config.md`
|
|
108
|
+
- **Performance Optimization**: `skills/spring/performance_optimization.md`
|