Skip to content

Commit

Permalink
Merge pull request #136 from Team-Capple/develop
Browse files Browse the repository at this point in the history
[RELEASE] ๊ฒŒ์‹œํŒ, ๊ฒŒ์‹œํŒ ๋Œ“๊ธ€ ,์• ํ”Œ ํ‘ธ์‹œ ์•Œ๋ฆผ ๊ตฌํ˜„
  • Loading branch information
tnals2384 authored Aug 25, 2024
2 parents f4b448f + f610335 commit c7ae22b
Show file tree
Hide file tree
Showing 43 changed files with 1,849 additions and 61 deletions.
45 changes: 0 additions & 45 deletions .github/workflows/cicd-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,48 +83,3 @@ jobs:
sudo docker image rm ${{ env.SPRING_IMAGE }}
sudo docker-compose --env-file=config/env/release.env -f docker-compose.release.yml -p backend up -d
sudo docker image prune -af
releaseExport:
name: Export Release Jar
runs-on: ubuntu-latest
needs: releaseDeploy
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.GITHUB_ACTOR }}
password: ${{ secrets.TOKEN_GITHUB }}

- name: lowercase the image tag & repository
run: |
echo "REPOSITORY=$(echo $REPOSITORY | tr '[:upper:]' '[:lower:]')" >> ${GITHUB_ENV}
- name: Get Version
run: |
echo "VERSION=$( echo ${{ github.ref_name }} | cut -c 9- )" >> ${GITHUB_ENV}
- name: Set Spring Image Environment Variable
run: |
echo "SPRING_IMAGE=${{ env.REGISTRY }}/${{ env.REPOSITORY }}-release:${{ env.VERSION }}" >> ${GITHUB_ENV}
- name: Run Container
run: docker run -d --name container-capple ${{ env.SPRING_IMAGE }}

- name: Extract .jar File From Container
run: docker cp container-capple:/app/app.jar .

- name: Stop Container
run: docker rm -f container-capple

- name: Rename Jar
run: mv app.jar capple-${{ env.VERSION }}.jar

- name: Extract .jar File
uses: actions/upload-artifact@v4
with:
name: capple-${{ env.VERSION }}
path: ./*.jar
compression-level: 9
retention-days: 1
overwrite: true
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ dependencies {

// https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305
implementation 'com.google.code.findbugs:jsr305:3.0.2'
// webflux
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

dependencyManagement {
Expand Down
2 changes: 1 addition & 1 deletion config
2 changes: 2 additions & 0 deletions src/main/java/com/server/capple/CappleApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;
Expand All @@ -16,6 +17,7 @@
@EnableFeignClients
@EnableConfigurationProperties
@EnableScheduling
@EnableCaching
public class CappleApplication {

public static void main(String[] args) {
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/server/capple/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package com.server.capple.config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
public class RedisConfig {

Expand All @@ -18,13 +27,25 @@ public class RedisConfig {
private int port;
@Value("${spring.data.redis.database}")
private int database;
@Value("${redis-cloud.host}")
private String redisCloudHost;
@Value("${redis-cloud.port}")
private int redisCloudPort;
@Value("${redis-cloud.database}")
private int redisCloudDatabase;
@Value("${redis-cloud.username}")
private String redisCloudUsername;
@Value("${redis-cloud.password}")
private String redisCloudPassword;

@Bean
@Primary
public RedisConnectionFactory redisConnectionFactory() {
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(host, port);
connectionFactory.setDatabase(database);
return connectionFactory;
}

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
Expand All @@ -35,4 +56,25 @@ public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisC

return redisTemplate;
}

@Bean
public RedisConnectionFactory redisCloudConnectionFactory() {
RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(redisCloudHost, redisCloudPort);
redisConfiguration.setUsername(redisCloudUsername);
redisConfiguration.setPassword(redisCloudPassword);
LettuceConnectionFactory apnsRedisConnectionFactory = new LettuceConnectionFactory(redisConfiguration);
apnsRedisConnectionFactory.setDatabase(redisCloudDatabase);
apnsRedisConnectionFactory.start();
return apnsRedisConnectionFactory;
}

@Bean
@Qualifier("redisCloudConnectionFactory")
public CacheManager apnsJwtCacheManager(RedisConnectionFactory redisCloudConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofMinutes(30));
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisCloudConnectionFactory).cacheDefaults(redisCacheConfiguration).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.server.capple.config.apns.config;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.WriteTimeoutHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.netty.http.HttpProtocol;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

import javax.net.ssl.SSLException;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

@Configuration
public class ApnsClientConfig {
@Bean("apnsSslContext")
public SslContext getApnsSslContext() throws SSLException {
return SslContextBuilder.forClient().protocols("TLSv1.2").build(); // SSL ์„ค์ •
}

@Bean("apnsConnectionProvider")
public ConnectionProvider getApnsConnectionProvider() {
return ConnectionProvider.builder("apns")
.maxConnections(10) // ์ตœ๋Œ€ ์ปค๋‚ต์…˜ ์ˆ˜
.pendingAcquireMaxCount(-1) // ์žฌ์‹œ๋„ ํšŸ์ˆ˜ (-1 : ๋ฌดํ•œ๋Œ€)
.pendingAcquireTimeout(java.time.Duration.ofSeconds(10)) // ์ปค๋„ฅ์…˜ ํ’€์— ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปค๋„ฅ์…˜ ์—†์„ ๋•Œ์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„
.maxIdleTime(java.time.Duration.ofSeconds(5)) // ์ตœ๋Œ€ ์œ ํœด ์‹œ๊ฐ„
.maxLifeTime(java.time.Duration.ofSeconds(300)) // ์ตœ๋Œ€ ์ƒ๋ช… ์‹œ๊ฐ„
.lifo() // ํ›„์ž…์„ ์ถœ
.build();
}

@Bean("apnsH2HttpClient")
public HttpClient getApnsH2HttpClient(ConnectionProvider apnsConnectionProvider, SslContext apnsSslContext) {
return HttpClient.create(apnsConnectionProvider) // reactor HttpClient ์ƒ์„ฑ
.keepAlive(true) // keep-alive ํ™œ์„ฑํ™”
.protocol(HttpProtocol.H2) // HTTP/2 ํ™œ์„ฑํ™”
.doOnConnected(connection -> connection.addHandlerLast(new WriteTimeoutHandler(10, TimeUnit.SECONDS))) // ์“ฐ๊ธฐ ํƒ€์ž„ ์•„์›ƒ
.responseTimeout(Duration.ofSeconds(10)) // ์‘๋‹ต ํƒ€์ž„ ์•„์›ƒ
.secure(sslSpec -> sslSpec.sslContext(apnsSslContext)); // SSL ํ™œ์„ฑํ™”
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.server.capple.config.apns.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;

public class ApnsClientRequest {

@Getter
@NoArgsConstructor
@ToString
public static class SimplePushBody {
private Aps aps;

public SimplePushBody(String title, String subTitle, String body, Integer badge, String threadId, String targetContentId) {
this.aps = new Aps(new Aps.Alert(title, subTitle, body), badge, threadId, targetContentId);
}

@ToString
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class Aps {
private Alert alert;
private Integer badge;
@JsonProperty("thread-id")
private String threadId;
@JsonProperty("target-content-id")
private String targetContentId; // ํ”„๋ก ํŠธ ์ธก ์ž‘์—… ํ•„์š”ํ•จ

@ToString
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class Alert {
private String title;
private String subtitle;
private String body;
}
}
}

@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public static class FullAlertBody {
private Aps aps;

@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public static class Aps {
private Alert alert; // alert ์ •๋ณด
private Integer badge; // ์•ฑ ์•„์ด์ฝ˜์— ํ‘œ์‹œํ•  ๋ฑƒ์ง€ ์ˆซ์ž
@Schema(defaultValue = "default")
private String sound; // Library/Sounds ํด๋” ๋‚ด์˜ ํŒŒ์ผ ์ด๋ฆ„
@Schema(defaultValue = "thread-id")
private String threadId; // ์•Œ๋ฆผ ๊ทธ๋ฃนํ™”๋ฅผ ์œ„ํ•œ thread id (UNNotificationContent ๊ฐ์ฒด์˜ threadIdentifier์™€ ์ผ์น˜ํ•ด์•ผ ํ•จ)
private String category; // ์•Œ๋ฆผ ๊ทธ๋ฃนํ™”๋ฅผ ์œ„ํ•œ category, (UNNotificationCategory ์‹๋ณ„์ž์™€ ์ผ์น˜ํ•ด์•ผ ํ•จ)
@Schema(defaultValue = "0")
@JsonProperty("content-available")
private Integer contentAvailable; // ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์•Œ๋ฆผ ์—ฌ๋ถ€, 1์ด๋ฉด ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์•Œ๋ฆผ, 0์ด๋ฉด ํฌ๊ทธ๋ผ์šด๋“œ ์•Œ๋ฆผ (๋ฐฑ๊ทธ๋ผ์šด๋“œ์ผ ๊ฒฝ์šฐ alert, badge, sound๋Š” ๋„ฃ์œผ๋ฉด ์•ˆ๋จ)
@Schema(defaultValue = "0")
@JsonProperty("mutable-content")
private Integer mutableContent; // ์•Œ๋ฆผ ์„œ๋น„์Šค ํ™•์žฅ ํ”Œ๋ž˜๊ทธ
@Schema(defaultValue = "")
@JsonProperty("target-content-id")
private String targetContentId; // ์•Œ๋ฆผ์ด ํด๋ฆญ๋˜์—ˆ์„ ๋•Œ ๊ฐ€์ ธ์˜ฌ ์ฐฝ์˜ ์‹๋ณ„์ž, UNNotificationContent ๊ฐ์ฒด์— ์ฑ„์›Œ์ง

@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public static class Alert {
@Schema(defaultValue = "title")
private String title;
@Schema(defaultValue = "subTitle")
private String subtitle;
@Schema(defaultValue = "body")
private String body;
@Schema(defaultValue = "")
@JsonProperty("launch-image")
private String launchImage; // ์‹คํ–‰์‹œ ๋ณด์—ฌ์ค„ ์ด๋ฏธ์ง€ ํŒŒ์ผ, ๊ธฐ๋ณธ ์‹คํ–‰ ์ด๋ฏธ์ง€ ๋Œ€์‹  ์ž…๋ ฅํ•œ ์ด๋ฏธ์ง€ ๋˜๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ๊ฐ€ ์ผœ์ง
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.server.capple.config.apns.service;

import java.util.List;

public interface ApnsService {
<T> Boolean sendApns(T request, String ... deviceTokens);
<T> Boolean sendApns(T request, List<String> deviceTokenList);
<T> Boolean sendApnsToMembers(T request, Long ... memberIds);
<T> Boolean sendApnsToMembers(T request, List<Long> memberIdList);
}
Loading

0 comments on commit c7ae22b

Please sign in to comment.