Skip to content

Commit e01307a

Browse files
committed
test
test
1 parent a773afe commit e01307a

20 files changed

Lines changed: 584 additions & 8 deletions

File tree

.github/workflows/main.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Build Docker
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v2
15+
16+
- name: Set up Docker Buildx
17+
uses: docker/setup-buildx-action@v1
18+
19+
- name: Log in to Docker Hub
20+
uses: docker/login-action@v1
21+
with:
22+
username: ${{ secrets.username }}
23+
password: ${{ secrets.password }}
24+
25+
- name: Build and push backend image
26+
uses: docker/build-push-action@v2
27+
with:
28+
context: ./task-tracker-backend
29+
tags: metarash/task-tracker-backend:latest
30+
31+
- name: Build and push scheduler image
32+
uses: docker/build-push-action@v2
33+
with:
34+
context: ./task-tracker-scheduler
35+
tags: metarash/task-tracker-scheduler:latest
36+
37+
- name: Build and push email-sender image
38+
uses: docker/build-push-action@v2
39+
with:
40+
context: ./task-tracker-email-sender
41+
tags: metarash/task-tracker-email-sender:latest

task-tracker-backend/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
FROM openjdk:22-jdk-slim
22

3+
34
WORKDIR /app
45

56
COPY target/task-tracker-backend-0.0.1-SNAPSHOT.jar app.jar

task-tracker-backend/src/main/java/com/metarash/backend/config/WebSocketConfig.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.metarash.backend.config;
22

3+
import com.metarash.backend.interceptor.WebSocketAuthChannelInterceptor;
34
import lombok.RequiredArgsConstructor;
45
import org.springframework.beans.factory.annotation.Value;
56
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.messaging.simp.config.ChannelRegistration;
68
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
79
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
810
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@@ -15,13 +17,20 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
1517
@Value("${cors.allowed-origins}")
1618
private String allowedOrigins;
1719

20+
private final WebSocketAuthChannelInterceptor webSocketAuthChannelInterceptor;
21+
1822
@Override
1923
public void configureMessageBroker(MessageBrokerRegistry registry) {
2024
registry.enableSimpleBroker("/topic");
2125
registry.setApplicationDestinationPrefixes("/app");
2226
registry.setUserDestinationPrefix("/user");
2327
}
2428

29+
@Override
30+
public void configureClientInboundChannel(ChannelRegistration registration) {
31+
registration.interceptors(webSocketAuthChannelInterceptor);
32+
}
33+
2534
@Override
2635
public void registerStompEndpoints(StompEndpointRegistry registry) {
2736
registry.addEndpoint("/ws")
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.metarash.backend.interceptor;
2+
3+
import com.metarash.backend.security.jwt.JwtService;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.messaging.Message;
7+
import org.springframework.messaging.MessageChannel;
8+
import org.springframework.messaging.simp.stomp.StompCommand;
9+
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
10+
import org.springframework.messaging.support.ChannelInterceptor;
11+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
12+
import org.springframework.security.core.Authentication;
13+
import org.springframework.stereotype.Component;
14+
15+
@Slf4j
16+
@Component
17+
@RequiredArgsConstructor
18+
public class WebSocketAuthChannelInterceptor implements ChannelInterceptor {
19+
20+
private final JwtService jwtService;
21+
22+
@Override
23+
public Message<?> preSend(Message<?> message, MessageChannel channel) {
24+
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
25+
log.info("[WS-INTERCEPTOR] Incoming command: {}", accessor.getCommand());
26+
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
27+
String authHeader = accessor.getFirstNativeHeader("Authorization");
28+
log.info("[WS-INTERCEPTOR] Authorization header: {}", authHeader);
29+
if (authHeader != null && authHeader.startsWith("Bearer ")) {
30+
String token = authHeader.substring(7);
31+
log.info("[WS-INTERCEPTOR] Extracted token: {}", token);
32+
String username = jwtService.getEmailFromToken(token);
33+
log.info("[WS-INTERCEPTOR] Parsed username/email from token: {}", username);
34+
boolean valid = jwtService.validateJwtToken(token);
35+
log.info("[WS-INTERCEPTOR] Token valid: {}", valid);
36+
if (username != null && valid) {
37+
Authentication auth = new UsernamePasswordAuthenticationToken(username, null, /* authorities */ null);
38+
accessor.setUser(auth);
39+
log.info("[WS-INTERCEPTOR] Principal set: {}", username);
40+
} else {
41+
log.info("[WS-INTERCEPTOR] JWT invalid or username not found");
42+
}
43+
} else {
44+
log.info("[WS-INTERCEPTOR] No valid Authorization header found");
45+
}
46+
}
47+
return message;
48+
}
49+
}

task-tracker-backend/src/main/java/com/metarash/backend/kafka/consumer/OverdueNotificationListener.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ public class OverdueNotificationListener {
2121
)
2222
public void onOverdueNotification(NotificationMessage msg) {
2323
log.info("getting overdue notification: {}", msg);
24+
String targetPrincipal = msg.getUserEmail();
25+
log.info("Target principal: {}", targetPrincipal);
2426
try {
2527
log.info("Sending overdue tasks notification to user: {}", msg.getUserEmail());
26-
simpMessagingTemplate.convertAndSendToUser(
27-
msg.getUserEmail(),
28-
"/topic/notifications",
29-
msg
30-
);
28+
simpMessagingTemplate.convertAndSend("/topic/notifications." + targetPrincipal, msg);
3129
} catch (Exception e) {
3230
log.error(e.getMessage());
3331
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.metarash.backend.log;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.context.event.EventListener;
5+
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.socket.messaging.SessionConnectEvent;
8+
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
9+
10+
@Slf4j
11+
@Component
12+
public class WebSocketEventLogger {
13+
14+
@EventListener
15+
public void handleSessionConnected(SessionConnectEvent event) {
16+
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
17+
String sessionId = accessor.getSessionId();
18+
Object user = accessor.getUser();
19+
log.info("[WS-EVENT] CONNECTED: sessionId={}, principal={}", sessionId, user);
20+
}
21+
22+
@EventListener
23+
public void handleSessionDisconnected(SessionDisconnectEvent event) {
24+
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
25+
String sessionId = accessor.getSessionId();
26+
Object user = accessor.getUser();
27+
log.info("[WS-EVENT] DISCONNECTED: sessionId={}, principal={}", sessionId, user);
28+
log.info("[WS-EVENT] DISCONNECT REASON: {}", event.getCloseStatus());
29+
}
30+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.metarash.backend.log;
2+
3+
public class WebSocketEventLogger {
4+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
spring:
2+
application:
3+
name: backend
4+
datasource:
5+
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
6+
username: sa
7+
password: sa
8+
driver-class-name: org.h2.Driver
9+
hikari:
10+
idle-timeout: 60000
11+
connection-timeout: 5000
12+
max-lifetime: 900000
13+
minimum-idle: 2
14+
maximum-pool-size: 10
15+
data:
16+
redis:
17+
host: localhost
18+
port: 6379
19+
cloud:
20+
vault:
21+
uri: http://stub-vault
22+
token: dummy
23+
authentication: token
24+
kv:
25+
enabled: false
26+
jpa:
27+
show-sql: false
28+
hibernate:
29+
ddl-auto: none
30+
properties:
31+
hibernate:
32+
dialect: org.hibernate.dialect.H2Dialect
33+
flyway:
34+
enabled: false
35+
config:
36+
import: ""
37+
management:
38+
endpoints:
39+
web:
40+
exposure:
41+
include: health
42+
kafka:
43+
bootstrap-servers: localhost:9092
44+
jwt:
45+
access-token-expiration: 15m
46+
refresh-token-expiration: 7d
47+
cors:
48+
allowed-origins: "http://localhost"
49+
server:
50+
error:
51+
include-stacktrace: always
52+
cache:
53+
ttl-minutes: 10
54+
logging:
55+
level:
56+
org:
57+
springframework:
58+
web:
59+
cors: INFO
60+
cloud:
61+
vault: INFO
62+
vault: INFO
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
spring:
2+
application:
3+
name: email-sender
4+
mail:
5+
host: localhost
6+
port: 1025
7+
username: dummy
8+
password: dummy
9+
properties:
10+
mail.smtp.auth: false
11+
mail.smtp.starttls.enable: false
12+
mail.debug: false
13+
kafka:
14+
consumer:
15+
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
16+
properties:
17+
spring.json.trusted.packages: '*'
18+
bootstrap-servers: localhost:9092
19+
cloud:
20+
vault:
21+
uri: http://stub-vault
22+
token: dummy
23+
authentication: token
24+
kv:
25+
enabled: false
26+
config:
27+
import: ""
28+
kafka:
29+
topics:
30+
email-sending:
31+
name: email-sending-tasks
32+
partitions: 1
33+
replicas: 1
34+
all-tasks:
35+
name: all-tasks-topic
36+
partitions: 1
37+
replicas: 1
38+
unfinished-tasks:
39+
name: unfinished-tasks-topic
40+
partitions: 1
41+
replicas: 1
42+
finished-tasks:
43+
name: finished-tasks-topic
44+
partitions: 1
45+
replicas: 1

task-tracker-frontend/src/features/websocket/notifications.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ export const connectToNotifications = (
1111

1212
stompClient = new Client({
1313
webSocketFactory: () => socket,
14+
connectHeaders: {
15+
Authorization: "Bearer " + localStorage.getItem("accessToken")
16+
},
1417
reconnectDelay: 5000,
1518
heartbeatIncoming: 4000,
1619
heartbeatOutgoing: 4000,
1720
onConnect: (frame: IFrame) => {
1821
console.log('[WebSocket] Connected! Username:', username);
1922
stompClient?.subscribe(
20-
`/user/topic/notifications`,
23+
`/topic/notifications.${username}`,
2124
(message) => {
2225
try {
2326
const parsed = JSON.parse(message.body);

0 commit comments

Comments
 (0)