Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/main/java/io/paradaux/api/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.paradaux.api.config;

import io.paradaux.api.interceptors.ProtectedRouteInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private ProtectedRouteInterceptor protectedRouteInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(protectedRouteInterceptor);
}
}
12 changes: 6 additions & 6 deletions src/main/java/io/paradaux/api/controllers/IfumController.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package io.paradaux.api.controllers;

import io.paradaux.api.models.IfumVisit;
import io.paradaux.api.models.annotations.ProtectedRoute;
import io.paradaux.api.services.IfumService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/ifum")
Expand All @@ -16,9 +15,10 @@ public IfumController(IfumService ifumService) {
this.ifumService = ifumService;
}

@ProtectedRoute
@PostMapping("/visit")
public void insertVisit() {
ifumService.insertVisit();
public void insertVisit(@RequestBody IfumVisit visit) {
ifumService.insertVisit(visit);
}

@GetMapping("/visits")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.paradaux.api.interceptors;

import io.paradaux.api.models.annotations.ProtectedRoute;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class ProtectedRouteInterceptor implements HandlerInterceptor {

@Value("${api.secret}")
private String secret;

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;

boolean methodProtected = method.hasMethodAnnotation(ProtectedRoute.class);
boolean classProtected = method.getBeanType().isAnnotationPresent(ProtectedRoute.class);

if (methodProtected || classProtected) {
String secretHeader = request.getHeader("X-SECRET");
if (secretHeader == null || !secretHeader.equals(secret)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Unauthorized: invalid or missing secret");
return false;
}
}
}
return true;
}
}
4 changes: 2 additions & 2 deletions src/main/java/io/paradaux/api/mappers/IfumMapper.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.paradaux.api.mappers;

import io.paradaux.api.models.IfumVisit;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface IfumMapper {

void insertVisit();
void insertVisit(IfumVisit visit);
Integer getVisitCount();
}
2 changes: 2 additions & 0 deletions src/main/java/io/paradaux/api/models/IfumVisit.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

@Data
public class IfumVisit {
private String ipAddress;
private String userAgent;
private int id;
private Timestamp createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.paradaux.api.models.annotations;

import java.lang.annotation.*;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ProtectedRoute {
}
4 changes: 3 additions & 1 deletion src/main/java/io/paradaux/api/services/IfumService.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package io.paradaux.api.services;

import io.paradaux.api.models.IfumVisit;

public interface IfumService {
/**
* Inserts a new IfumVisit record into the database.
*/
void insertVisit();
void insertVisit(IfumVisit visit);

/**
* Retrieves the total count of IfumVisit records.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.paradaux.api.services.impl;

import io.paradaux.api.mappers.IfumMapper;
import io.paradaux.api.models.IfumVisit;
import io.paradaux.api.services.IfumService;
import org.springframework.stereotype.Service;

Expand All @@ -14,8 +15,8 @@ public IfumServiceImpl(IfumMapper ifumMapper) {
}

@Override
public void insertVisit() {
ifumMapper.insertVisit();
public void insertVisit(IfumVisit visit) {
ifumMapper.insertVisit(visit);
}

@Override
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
password: ${SPRING_DATASOURCE_PASSWORD}

api:
secret: ${API_SECRET}
3 changes: 3 additions & 0 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ spring:
username: postgres
password: example
driver-class-name: org.postgresql.Driver

api:
secret: test
5 changes: 4 additions & 1 deletion src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
password: ${SPRING_DATASOURCE_PASSWORD}

api:
secret: ${API_SECRET}
4 changes: 3 additions & 1 deletion src/main/resources/db/1.sql
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ CREATE TABLE IF NOT EXISTS api.contact_form_responses
CREATE SCHEMA IF NOT EXISTS ifum;
CREATE TABLE IF NOT EXISTS ifum.visits (
id SERIAL PRIMARY KEY,
ip_address VARCHAR(45) UNIQUE NOT NULL,
user_agent TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
);
8 changes: 6 additions & 2 deletions src/main/resources/mappers/IfumMapper.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
<result property="createdAt" column="created_at"/>
</resultMap>

<insert id="insertVisit">
INSERT INTO ifum.visits DEFAULT VALUES
<insert id="insertVisit" parameterType="IfumVisit">
INSERT INTO ifum.visits (ip_address, user_agent)
SELECT #{ipAddress}, #{userAgent}
WHERE NOT EXISTS (
SELECT 1 FROM ifum.visits WHERE ip_address = #{ipAddress}
)
</insert>

<select id="getVisitCount" parameterType="int" resultType="Integer">
Expand Down