Skip to content

Commit de37de5

Browse files
Merge branch 'main' of github.com-java:vimal-java-dev/vimaltech-contact-api
2 parents e21fb6c + b391063 commit de37de5

9 files changed

Lines changed: 145 additions & 44 deletions

File tree

.github/workflows/ci.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Java CI + Docker Publish Pipeline
1+
name: Java CI + Docker Publish Pipeline + Deploy to VPS
22

33
on:
44
push:
@@ -35,7 +35,8 @@ jobs:
3535
runs-on: ubuntu-latest
3636

3737
steps:
38-
- uses: actions/checkout@v4
38+
- name: Checkout source code
39+
uses: actions/checkout@v4
3940

4041
- name: Log in to GHCR
4142
uses: docker/login-action@v3
@@ -50,4 +51,20 @@ jobs:
5051
5152
- name: Push Docker image
5253
run: |
53-
docker push ghcr.io/vimal-java-dev/vimaltech-contact-api:latest
54+
docker push ghcr.io/vimal-java-dev/vimaltech-contact-api:latest
55+
56+
deploy:
57+
# ✅ CRITICAL CONDITION (owner repo only)
58+
if: github.event_name == 'push' && github.repository == 'vimal-java-dev/vimaltech-contact-api'
59+
needs: docker
60+
runs-on: ubuntu-latest
61+
62+
steps:
63+
- name: Deploy to VPS via SSH
64+
uses: appleboy/ssh-action@v1.0.3
65+
with:
66+
host: ${{ secrets.VPS_HOST }}
67+
username: ${{ secrets.VPS_USER }}
68+
key: ${{ secrets.VPS_SSH_KEY }}
69+
script: |
70+
/home/deploy/deploy.sh

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ pom.xml.next
99
release.properties
1010
dependency-reduced-pom.xml
1111

12+
#####################
13+
target/
14+
temp-repo/
15+
.m2/
16+
1217
############################################
1318
# Logs
1419
############################################
@@ -18,7 +23,7 @@ logs/
1823
############################################
1924
# Environment / Secrets
2025
############################################
21-
.env
26+
.env.dev
2227
.env.*
2328
*.env
2429
*.key

docker-compose.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: "3.9"
2-
31
services:
42

53
postgres:
@@ -15,10 +13,12 @@ services:
1513
volumes:
1614
- vimaltech_pgdata:/var/lib/postgresql/data
1715
healthcheck:
18-
test: [ "CMD-SHELL", "pg_isready -U contactuser -d contactdb" ]
16+
test: ["CMD-SHELL", "pg_isready -U contactuser -d contactdb"]
1917
interval: 10s
2018
timeout: 5s
2119
retries: 5
20+
networks:
21+
- monitoring-network
2222

2323
contact-api:
2424
build: .
@@ -28,9 +28,17 @@ services:
2828
condition: service_healthy
2929
ports:
3030
- "8080:8080"
31+
env_file:
32+
- .env.prod
3133
environment:
32-
SPRING_PROFILES_ACTIVE: dev
34+
SPRING_PROFILES_ACTIVE: prod
3335
restart: unless-stopped
36+
networks:
37+
- monitoring-network
3438

3539
volumes:
36-
vimaltech_pgdata:
40+
vimaltech_pgdata:
41+
42+
networks:
43+
monitoring-network:
44+
name: monitoring-network

monitoring/docker-compose.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
services:
2+
3+
prometheus:
4+
image: prom/prometheus:latest
5+
container_name: vimaltech-prometheus
6+
restart: unless-stopped
7+
volumes:
8+
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
9+
ports:
10+
- "9090:9090"
11+
networks:
12+
- monitoring-network
13+
14+
grafana:
15+
image: grafana/grafana:latest
16+
container_name: vimaltech-grafana
17+
restart: unless-stopped
18+
ports:
19+
- "3000:3000"
20+
networks:
21+
- monitoring-network
22+
volumes:
23+
- grafana-data:/var/lib/grafana
24+
25+
volumes:
26+
grafana-data:
27+
28+
networks:
29+
monitoring-network:
30+
external: true
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
global:
2+
scrape_interval: 15s
3+
4+
scrape_configs:
5+
- job_name: 'contact-api'
6+
metrics_path: /actuator/prometheus
7+
static_configs:
8+
- targets: ['contact-api:8080']

pom.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,26 +87,39 @@
8787
<groupId>org.flywaydb</groupId>
8888
<artifactId>flyway-core</artifactId>
8989
</dependency>
90+
9091
<dependency>
9192
<groupId>org.flywaydb</groupId>
9293
<artifactId>flyway-database-postgresql</artifactId>
9394
</dependency>
95+
9496
<dependency>
9597
<groupId>com.bucket4j</groupId>
9698
<artifactId>bucket4j-core</artifactId>
97-
<version>8.6.0</version>
99+
<version>8.9.0</version>
98100
</dependency>
99101

100102
<dependency>
101103
<groupId>com.bucket4j</groupId>
102104
<artifactId>bucket4j-redis</artifactId>
103-
<version>8.6.0</version>
105+
<version>8.9.0</version>
104106
</dependency>
105107

108+
<!-- Spring Redis -->
106109
<dependency>
107110
<groupId>org.springframework.boot</groupId>
108111
<artifactId>spring-boot-starter-data-redis</artifactId>
109112
</dependency>
113+
114+
<dependency>
115+
<groupId>org.springframework.boot</groupId>
116+
<artifactId>spring-boot-starter-actuator</artifactId>
117+
</dependency>
118+
119+
<dependency>
120+
<groupId>io.micrometer</groupId>
121+
<artifactId>micrometer-registry-prometheus</artifactId>
122+
</dependency>
110123
</dependencies>
111124

112125
<build>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.vimaltech.contactapi.config;
2+
3+
import com.vimaltech.contactapi.security.IpRateLimitFilter;
4+
import org.springframework.boot.web.servlet.FilterRegistrationBean;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.context.annotation.Profile;
8+
9+
@Configuration
10+
@Profile("prod")
11+
public class RateLimitConfig {
12+
13+
@Bean
14+
public IpRateLimitFilter ipRateLimitFilter() {
15+
return new IpRateLimitFilter();
16+
}
17+
18+
@Bean
19+
public FilterRegistrationBean<IpRateLimitFilter> rateLimitFilterRegistration() {
20+
21+
FilterRegistrationBean<IpRateLimitFilter> registration =
22+
new FilterRegistrationBean<>();
23+
24+
registration.setFilter(ipRateLimitFilter());
25+
registration.addUrlPatterns("/*");
26+
registration.setOrder(1);
27+
28+
return registration;
29+
}
30+
}

src/main/java/com/vimaltech/contactapi/security/RedisRateLimitFilter.java renamed to src/main/java/com/vimaltech/contactapi/security/IpRateLimitFilter.java

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,28 @@
22

33
import io.github.bucket4j.Bandwidth;
44
import io.github.bucket4j.Bucket;
5-
import io.github.bucket4j.BucketConfiguration;
6-
import io.github.bucket4j.distributed.proxy.ProxyManager;
7-
import io.github.bucket4j.redis.lettuce.cas.LettuceBasedProxyManager;
8-
9-
import io.lettuce.core.RedisClient;
10-
import io.lettuce.core.RedisURI;
115

126
import jakarta.servlet.FilterChain;
137
import jakarta.servlet.ServletException;
148
import jakarta.servlet.http.HttpServletRequest;
159
import jakarta.servlet.http.HttpServletResponse;
1610

17-
import org.springframework.beans.factory.annotation.Value;
1811
import org.springframework.context.annotation.Profile;
1912
import org.springframework.stereotype.Component;
2013
import org.springframework.web.filter.OncePerRequestFilter;
2114

2215
import java.io.IOException;
2316
import java.time.Duration;
17+
import java.util.Map;
2418
import java.util.Optional;
19+
import java.util.concurrent.ConcurrentHashMap;
2520

26-
@Component
27-
@Profile("prod")
28-
public class RedisRateLimitFilter extends OncePerRequestFilter {
29-
30-
private final ProxyManager<byte[]> proxyManager;
31-
32-
public RedisRateLimitFilter(
33-
@Value("${spring.data.redis.host}") String host,
34-
@Value("${spring.data.redis.port}") int port
35-
) {
36-
37-
RedisURI redisURI = RedisURI.Builder.redis(host)
38-
.withPort(port)
39-
.build();
40-
41-
RedisClient redisClient = RedisClient.create(redisURI);
21+
public class IpRateLimitFilter extends OncePerRequestFilter {
4222

43-
this.proxyManager = LettuceBasedProxyManager
44-
.builderFor(redisClient)
45-
.build();
46-
}
23+
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
4724

48-
private BucketConfiguration configuration() {
49-
return BucketConfiguration.builder()
25+
private Bucket createBucket() {
26+
return Bucket.builder()
5027
.addLimit(Bandwidth.simple(10, Duration.ofMinutes(1)))
5128
.build();
5229
}
@@ -57,7 +34,7 @@ protected void doFilterInternal(HttpServletRequest request,
5734
FilterChain filterChain)
5835
throws ServletException, IOException {
5936

60-
if (!request.getRequestURI().equals("/api/v1/contact")) {
37+
if (!request.getServletPath().startsWith("/api/v1/contact")) {
6138
filterChain.doFilter(request, response);
6239
return;
6340
}
@@ -66,8 +43,7 @@ protected void doFilterInternal(HttpServletRequest request,
6643
.map(s -> s.split(",")[0].trim())
6744
.orElse(request.getRemoteAddr());
6845

69-
Bucket bucket = proxyManager.builder()
70-
.build(("ip:" + ip).getBytes(), this::configuration);
46+
Bucket bucket = buckets.computeIfAbsent(ip, k -> createBucket());
7147

7248
if (bucket.tryConsume(1)) {
7349
filterChain.doFilter(request, response);

src/main/resources/application-prod.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,18 @@ logging:
3535
root: WARN
3636
com.vimaltech.contactapi: INFO
3737
org.springframework: WARN
38-
org.hibernate: WARN
38+
org.hibernate: WARN
39+
40+
management:
41+
endpoints:
42+
web:
43+
exposure:
44+
include: health,info,prometheus
45+
46+
endpoint:
47+
health:
48+
show-details: never
49+
50+
metrics:
51+
tags:
52+
application: contact-api

0 commit comments

Comments
 (0)