diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..a5bcda7
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,41 @@
+# IDE and build system files
+.idea/
+.vscode/
+*.iml
+*.log
+*.tmp
+*.swp
+*.swo
+*.bak
+*.orig
+*.rej
+
+# Maven and Eclipse files
+.mvn/
+.settings/
+.classpath
+.project
+.factorypath
+target/
+**/target/
+**/.classpath
+**/.project
+**/.settings
+
+# Git files
+.git/
+.gitignore
+.gitattributes
+
+# OS/Editor junk
+.DS_Store
+Thumbs.db
+
+# Test sources
+src/test/
+**/src/test/
+
+# Docs and markdown
+README.md
+CONTRIBUTING.md
+HELP.md
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index d0942bb..a1ccaac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,6 +50,12 @@
lombok
true
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
org.springframework.boot
spring-boot-starter-test
diff --git a/src/main/java/com/thughari/randomchat/RandomChatApplication.java b/src/main/java/com/thughari/randomchat/RandomChatApplication.java
index d9e1c54..7ebbf66 100644
--- a/src/main/java/com/thughari/randomchat/RandomChatApplication.java
+++ b/src/main/java/com/thughari/randomchat/RandomChatApplication.java
@@ -1,11 +1,7 @@
package com.thughari.randomchat;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@@ -16,8 +12,4 @@ public static void main(String[] args) {
SpringApplication.run(RandomChatApplication.class, args);
}
- @Bean("virtualThreadTaskExecutor")
- public ExecutorService virtualThreadTaskExecutor() {
- return Executors.newVirtualThreadPerTaskExecutor();
- }
}
\ No newline at end of file
diff --git a/src/main/java/com/thughari/randomchat/component/TwilioHttpClient.java b/src/main/java/com/thughari/randomchat/component/TwilioHttpClient.java
new file mode 100644
index 0000000..f2d38f0
--- /dev/null
+++ b/src/main/java/com/thughari/randomchat/component/TwilioHttpClient.java
@@ -0,0 +1,83 @@
+package com.thughari.randomchat.component;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import com.thughari.randomchat.exceptions.TwilioClientException;
+
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.HttpServerErrorException;
+import org.springframework.web.client.ResourceAccessException;
+
+import java.util.Base64;
+
+@Component
+public class TwilioHttpClient {
+
+ private static final Logger logger = LoggerFactory.getLogger(TwilioHttpClient.class);
+
+ @Value("${twilio.account.sid}")
+ private String twilioAccountSid;
+
+ @Value("${twilio.auth.token}")
+ private String twilioAuthToken;
+
+ private final RestTemplate restTemplate;
+
+ public TwilioHttpClient() {
+ this.restTemplate = new RestTemplate();
+ }
+
+ /**
+ * Makes an HTTP POST request to Twilio's Tokens API to fetch ICE servers.
+ *
+ * @return The raw JSON response body from Twilio.
+ * @throws TwilioClientException if the API call fails or returns a non-successful status.
+ */
+ public String fetchTwilioTokensApiResponse() throws TwilioClientException {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ String auth = twilioAccountSid + ":" + twilioAuthToken;
+ String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
+ headers.add("Authorization", "Basic " + encodedAuth);
+
+ HttpEntity requestEntity = new HttpEntity<>(headers);
+
+ String twilioApiUrl = String.format("https://api.twilio.com/2010-04-01/Accounts/%s/Tokens.json", twilioAccountSid);
+
+ try {
+ logger.debug("Making Twilio Tokens API call to: {}", twilioApiUrl);
+ ResponseEntity response = restTemplate.exchange(
+ twilioApiUrl,
+ HttpMethod.POST,
+ requestEntity,
+ String.class
+ );
+
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
+ logger.debug("Successfully received response from Twilio API.");
+ return response.getBody();
+ } else {
+ logger.error("Twilio API call failed with status: {} and body: {}", response.getStatusCode(), response.getBody());
+ throw new TwilioClientException("Twilio API returned non-2xx status: " + response.getStatusCode());
+ }
+ } catch (HttpClientErrorException | HttpServerErrorException e) {
+ logger.error("Twilio API client/server error: {} - {}", e.getStatusCode(), e.getResponseBodyAsString(), e);
+ throw new TwilioClientException("Twilio API returned an HTTP error: " + e.getStatusCode(), e);
+ } catch (ResourceAccessException e) {
+ logger.error("Network or connection error connecting to Twilio API: {}", e.getMessage(), e);
+ throw new TwilioClientException("Network error connecting to Twilio API", e);
+ } catch (Exception e) {
+ logger.error("An unexpected error occurred during Twilio API call: {}", e.getMessage(), e);
+ throw new TwilioClientException("Unexpected error during Twilio API call", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/thughari/randomchat/config/VirtualThreadConfig.java b/src/main/java/com/thughari/randomchat/config/VirtualThreadConfig.java
new file mode 100644
index 0000000..d1c4edf
--- /dev/null
+++ b/src/main/java/com/thughari/randomchat/config/VirtualThreadConfig.java
@@ -0,0 +1,16 @@
+package com.thughari.randomchat.config;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class VirtualThreadConfig {
+
+ @Bean("virtualThreadTaskExecutor")
+ public ExecutorService virtualThreadTaskExecutor() {
+ return Executors.newVirtualThreadPerTaskExecutor();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/thughari/randomchat/controller/TurnConfigController.java b/src/main/java/com/thughari/randomchat/controller/TurnConfigController.java
index 6ff94ac..8ba2223 100644
--- a/src/main/java/com/thughari/randomchat/controller/TurnConfigController.java
+++ b/src/main/java/com/thughari/randomchat/controller/TurnConfigController.java
@@ -1,29 +1,28 @@
package com.thughari.randomchat.controller;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-
-import org.springframework.beans.factory.annotation.Value;
+import com.thughari.randomchat.service.TwilioTurnService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
@RestController
public class TurnConfigController {
- @Value("${turn.server.username}")
- private String username;
+ private final TwilioTurnService twilioTurnService;
- @Value("${turn.server.credential}")
- private String credential;
+ // Constructor injection
+ public TurnConfigController(TwilioTurnService twilioTurnService) {
+ this.twilioTurnService = twilioTurnService;
+ }
- @GetMapping("/api/turn-config")
- @Async("virtualThreadTaskExecutor")
- public CompletableFuture