diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..f99697f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,22 @@
+# --- Stage 1: Build the application using Maven ---
+FROM maven:3.9.6-eclipse-temurin-17 AS build
+
+WORKDIR /app
+
+COPY . .
+
+# Build the application while caching Maven dependencies to speed up future builds
+RUN --mount=type=cache,target=/root/.m2 \
+ mvn clean package -DENV_VAR=docker -DskipTests -Dgit.skip=true
+
+# --- Stage 2: Run the application with a minimal JRE image ---
+FROM eclipse-temurin:17-jre
+
+WORKDIR /app
+
+# Copy the built WAR file from the build stage
+COPY --from=build /app/target/*.war app.war
+
+EXPOSE 8080
+
+ENTRYPOINT ["java", "-jar", "app.war"]
diff --git a/pom.xml b/pom.xml
index ac63bb8..050c2aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -392,6 +392,18 @@
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 3.2.2
+
+
+
+ repackage
+
+
+
+
diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties
index f0ed942..24558d3 100644
--- a/src/main/environment/common_ci.properties
+++ b/src/main/environment/common_ci.properties
@@ -15,7 +15,7 @@ callcentre-server-ip=@env.CALLCENTRE_SERVER_IP@
common-url=@env.COMMON_API@
### Redis IP
-spring.redis.host=localhost
+spring.redis.host=@env.REDIS_HOST@
jwt.secret=@env.JWT_SECRET_KEY@
#ELK logging file name
@@ -25,4 +25,5 @@ logging.file.name=@env.SCHEDULER_API_LOGGING_FILE_NAME@
springdoc.api-docs.enabled=@env.SWAGGER_DOC_ENABLED@
springdoc.swagger-ui.enabled=@env.SWAGGER_DOC_ENABLED@
+cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@
diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties
new file mode 100644
index 0000000..2a14eb1
--- /dev/null
+++ b/src/main/environment/common_docker.properties
@@ -0,0 +1,24 @@
+# local env
+# DB Connections
+spring.datasource.url=${DATABASE_URL}
+spring.datasource.username=${DATABASE_USERNAME}
+spring.datasource.password=${DATABASE_PASSWORD}
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+spring.jpa.show-sql=true
+
+spring.profiles.active=test
+
+callcentre-server-ip=${CALLCENTRE_SERVER_IP}
+
+common-url=${COMMON_API}
+
+### Redis IP
+spring.redis.host=${REDIS_HOST}
+jwt.secret=${JWT_SECRET_KEY}
+
+#ELK logging file name
+logging.file.name=${SCHEDULER_API_LOGGING_FILE_NAME}
+
+springdoc.api-docs.enabled=${SWAGGER_DOC_ENABLED}
+springdoc.swagger-ui.enabled=${SWAGGER_DOC_ENABLED}
diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties
index d6b4463..d074aa7 100644
--- a/src/main/environment/common_example.properties
+++ b/src/main/environment/common_example.properties
@@ -22,3 +22,5 @@ jwt.secret=my-32-character-ultra-secure-and-ultra-long-secret
logging.path=logs/
logging.file.name=logs/scheduler-api.log
+cors.allowed-origins=http://localhost:*
+
diff --git a/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java b/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java
index 3cb6812..1af4dc1 100644
--- a/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java
+++ b/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java
@@ -26,7 +26,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.CrossOrigin;
+
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -43,7 +43,6 @@
import io.swagger.v3.oas.annotations.Operation;
-
@RestController
@RequestMapping(value = "/schedule", headers = "Authorization")
public class SchedulingController {
@@ -52,7 +51,6 @@ public class SchedulingController {
@Autowired
private SchedulingService schedulingService;
- @CrossOrigin()
@Operation(summary = "Mark availability of specialist")
@RequestMapping(value = "markavailability", method = RequestMethod.POST)
public String markavailability(@RequestBody String specialistInput1) {
@@ -72,7 +70,6 @@ public String markavailability(@RequestBody String specialistInput1) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Mark unavailability of specialist")
@RequestMapping(value = "unmarkavailability", method = RequestMethod.POST)
public String unmarkavailability(@RequestBody String specialistInput1) {
@@ -91,7 +88,6 @@ public String unmarkavailability(@RequestBody String specialistInput1) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Get available slots of specialist for a particular day")
@RequestMapping(value = "getavailableSlot", method = RequestMethod.POST)
public String getavailableSlot(@RequestBody String specialistInput1) {
@@ -109,7 +105,6 @@ public String getavailableSlot(@RequestBody String specialistInput1) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Get available slots of specialist for a particular month")
@RequestMapping(value = { "/monthview/{year}", "/monthview/{year}/{month}",
"/monthview/{year}/{month}/{day}" }, method = RequestMethod.POST)
@@ -131,7 +126,6 @@ public String view(@RequestBody String specialistInput1, @PathVariable("year") I
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Book available slots of specialist of a particular day")
@RequestMapping(value = "bookSlot", method = RequestMethod.POST)
public String bookSlot(@RequestBody String specialistInput1) {
@@ -149,7 +143,6 @@ public String bookSlot(@RequestBody String specialistInput1) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Cancel booked slots of specialist of a particular day")
@RequestMapping(value = "cancelBookedSlot", method = RequestMethod.POST)
public String cancelBookedSlot(@RequestBody String specialistInput1) {
@@ -167,7 +160,6 @@ public String cancelBookedSlot(@RequestBody String specialistInput1) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Get day view of particular specialization")
@RequestMapping(value = "getdayview", method = RequestMethod.POST)
public String getdayview(@RequestBody String specialistInput1) {
diff --git a/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java b/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java
index c42e124..f890e1b 100644
--- a/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java
+++ b/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java
@@ -26,7 +26,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.CrossOrigin;
+
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -41,7 +41,6 @@
import io.swagger.v3.oas.annotations.Operation;
-
@RestController
@RequestMapping(value = "/specialist", headers = "Authorization")
public class SpecialistController {
@@ -50,7 +49,6 @@ public class SpecialistController {
@Autowired
private SpecialistService specialistService;
- @CrossOrigin()
@Operation(summary = "Fetch master specialization")
@RequestMapping(value = "masterspecialization", method = RequestMethod.POST)
public String markavailability() {
@@ -66,7 +64,6 @@ public String markavailability() {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Fetch list of specialists")
@RequestMapping(value = "getSpecialist", method = RequestMethod.POST)
public String getSpecialist(@RequestBody Specialist specialist) {
@@ -83,7 +80,6 @@ public String getSpecialist(@RequestBody Specialist specialist) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Fetch user specialist")
@RequestMapping(value = "info/{userID}", method = RequestMethod.GET)
public String info(@PathVariable("userID") Long userID) {
@@ -101,7 +97,6 @@ public String info(@PathVariable("userID") Long userID) {
return response.toString();
}
- @CrossOrigin()
@Operation(summary = "Fetch all specialists")
@RequestMapping(value = "getSpecialistAll", method = RequestMethod.POST)
public String getSpecialistAll(@RequestBody Specialist specialist) {
diff --git a/src/main/java/com/iemr/tm/controller/van/VanController.java b/src/main/java/com/iemr/tm/controller/van/VanController.java
index 876f593..26fa53b 100644
--- a/src/main/java/com/iemr/tm/controller/van/VanController.java
+++ b/src/main/java/com/iemr/tm/controller/van/VanController.java
@@ -24,7 +24,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.CrossOrigin;
+
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -36,8 +36,6 @@
import io.swagger.v3.oas.annotations.Operation;
-
-
@RestController
@RequestMapping(value = "/van", headers = "Authorization")
public class VanController {
@@ -46,7 +44,6 @@ public class VanController {
@Autowired
private VanService vanService;
- @CrossOrigin()
@Operation(summary = "Fetch specialization by van id")
@RequestMapping(value = "getvan/{vanid}", method = RequestMethod.GET)
public String markavailability(@PathVariable("vanid") Integer vanid) {
diff --git a/src/main/java/com/iemr/tm/utils/FilterConfig.java b/src/main/java/com/iemr/tm/utils/FilterConfig.java
index 48aee27..b11a7ee 100644
--- a/src/main/java/com/iemr/tm/utils/FilterConfig.java
+++ b/src/main/java/com/iemr/tm/utils/FilterConfig.java
@@ -3,17 +3,26 @@
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.beans.factory.annotation.Value;
@Configuration
public class FilterConfig {
+ @Value("${cors.allowed-origins}")
+ private String allowedOrigins;
+
@Bean
public FilterRegistrationBean jwtUserIdValidationFilter(
JwtAuthenticationUtil jwtAuthenticationUtil) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
- registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil));
+
+ // Pass allowedOrigins explicitly to the filter constructor
+ JwtUserIdValidationFilter filter = new JwtUserIdValidationFilter(jwtAuthenticationUtil, allowedOrigins);
+
+ registrationBean.setFilter(filter);
+ registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
registrationBean.addUrlPatterns("/*"); // Apply filter to all API endpoints
return registrationBean;
}
-
}
diff --git a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
index 174ed73..3a2ef5e 100644
--- a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
+++ b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
@@ -1,6 +1,7 @@
package com.iemr.tm.utils;
import java.io.IOException;
+import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,14 +18,16 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-@Component
public class JwtUserIdValidationFilter implements Filter {
private final JwtAuthenticationUtil jwtAuthenticationUtil;
private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
+ private final String allowedOrigins;
- public JwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
+ public JwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil,
+ String allowedOrigins) {
this.jwtAuthenticationUtil = jwtAuthenticationUtil;
+ this.allowedOrigins = allowedOrigins;
}
@Override
@@ -33,6 +36,27 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
+ String origin = request.getHeader("Origin");
+
+ logger.debug("Incoming Origin: {}", origin);
+ logger.debug("Allowed Origins Configured: {}", allowedOrigins);
+
+ if (origin != null && isOriginAllowed(origin)) {
+ response.setHeader("Access-Control-Allow-Origin", origin);
+ response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Jwttoken");
+ response.setHeader("Vary", "Origin");
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+ } else {
+ logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin);
+ }
+
+ if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
+ logger.info("OPTIONS request - skipping JWT validation");
+ response.setStatus(HttpServletResponse.SC_OK);
+ return;
+ }
+
String path = request.getRequestURI();
String contextPath = request.getContextPath();
logger.info("JwtUserIdValidationFilter invoked for path: " + path);
@@ -110,12 +134,33 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization error: " + e.getMessage());
}
}
+
+ private boolean isOriginAllowed(String origin) {
+ if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
+ logger.warn("No allowed origins configured or origin is null");
+ return false;
+ }
+
+ return Arrays.stream(allowedOrigins.split(","))
+ .map(String::trim)
+ .anyMatch(pattern -> {
+ String regex = pattern
+ .replace(".", "\\.")
+ .replace("*", ".*")
+ .replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port
+
+ boolean matched = origin.matches(regex);
+ return matched;
+ });
+ }
+
private boolean isMobileClient(String userAgent) {
if (userAgent == null)
return false;
userAgent = userAgent.toLowerCase();
return userAgent.contains("okhttp");
}
+
private String getJwtTokenFromCookies(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {