Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci-cd 구축 테스트, ssh 포트 포워드, cors 구조 설정, 공통 응답 api 기초 설정 #26 #28

Closed
wants to merge 17 commits into from
Closed
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
28 changes: 25 additions & 3 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ jobs:
# 2. Spring Boot 애플리케이션 빌드

- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
uses: gradle/gradle-build-action@v2
with:
arguments: clean bootJar

# 3. Docker 이미지 빌드
- name: docker image build
run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/github-actions-demo.
run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/big-brother-demo .

# 4. DockerHub 로그인
- name: docker login
Expand All @@ -45,4 +45,26 @@ jobs:

# 5. Docker Hub 이미지 푸시
- name: docker Hub push
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/github-actions-demo
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/big-brother-demo

## 푸시한 임지를 ec2에서 풀받아서 실행시키는 과정
run-docker-image-on-ec2:
needs: build-docker-image
runs-on: self-hosted

steps:
# 1. 최신 이미지를 풀받는다.,
- name: docker pull
run: sudo docker pull ${{secrets.DOCERHUB_USERNAME}}/big-brother-demo

# 2. 기존의 컨테이너를 중지시킵니다.
- name: docker stop container
# 모든 컨테이너를 중지시킴.
run: sudo docker stop $(sudo docker ps -q) 2>/dev/null || true
# 3. 최신 이미지를 컨테이너화하여 실행시킨다.
- name: docker run new container
run: sudo docker run --name github-actions-demo --rm -d -p 8080:8080 --env-file ./env1.list ${{ secrets.DOCKERHUB_USERNAME }}/big-brother-demo

# 4. 미사용 이미지를 정리합니다.
- name: delete old docker image
run: sudo docker system prune -f
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Dockerfile

# jdk17 Image Start
FROM openjdk:17

# 인자 설정 - JAR_File
ARG JAR_FILE=build/libs/*.jar

# jar 파일 복제
COPY ${JAR_FILE} app.jar

# 인자 설정 부분과 jar 파일 복제 부분 합쳐서 진행해도 무방
#COPY build/libs/*.jar app.jar

# 실행 명령어
ENTRYPOINT ["java", "-jar", "app.jar"]
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ repositories {

dependencies {

// JSch ssh 터널링
implementation 'com.github.mwiede:jsch:0.2.16'

// DB
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
Expand Down Expand Up @@ -70,6 +73,9 @@ dependencies {
implementation 'com.amazonaws:aws-java-sdk-lambda:1.12.50'
implementation 'com.amazonaws:aws-java-sdk-core:1.12.50'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.3'


implementation 'org.springframework.boot:spring-boot-starter-validation'
}

tasks.named('test') {
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@


@RequestMapping("/api/big-brother/members")
@CrossOrigin(origins = "http://localhost:8080")
@Tag(name = "멤버", description = "회원가입,로그인 API")
public interface MemberController {

Expand All @@ -29,5 +28,5 @@ public interface MemberController {
content = {@Content(schema = @Schema(implementation = MemberResponse.class))}),
@ApiResponse(responseCode = "404", description = "?")
})
ResponseEntity<MemberResponse> signUp(@RequestBody @Valid SignUpDto signUpDto);
ResponseEntity<com.example.bigbrotherbe.global.response.ApiResponse<MemberResponse>> signUp(@RequestBody @Valid SignUpDto signUpDto);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.example.bigbrotherbe.domain.member.entity.dto.response.MemberResponse;
import com.example.bigbrotherbe.global.email.EmailVerificationResult;
import com.example.bigbrotherbe.global.jwt.JwtToken;
import com.example.bigbrotherbe.global.response.ApiResponse;
import com.example.bigbrotherbe.global.security.SecurityConfig;
import com.example.bigbrotherbe.domain.member.service.MemberService;
import java.util.Map;
Expand All @@ -26,8 +27,13 @@ public class MemberControllerImpl implements MemberController{

private final MemberService memberService;

public ResponseEntity<MemberResponse> signUp(SignUpDto signUpDto) {
return ResponseEntity.ok(memberService.userSignUp(signUpDto));

public ResponseEntity<ApiResponse<MemberResponse>> signUp(SignUpDto signUpDto) {
MemberResponse memberResponse = memberService.userSignUp(signUpDto);

// ApiResponse 생성
ApiResponse<MemberResponse> response = ApiResponse.ok(memberResponse);
return ResponseEntity.ok(response);
}


Expand All @@ -45,8 +51,8 @@ public JwtToken signIn(@RequestBody MemberRequest memberRequest) {

@PostMapping("/test")
public String test() {
// return "suess";
return SecurityConfig.getCurrentUserName();
memberService.makeAffiliation();
return "완성";
}

// member 상세 조회
Expand All @@ -71,8 +77,6 @@ public String adminTest() {
return SecurityConfig.getCurrentUserName();
}



// 이메일 중복 확인
@GetMapping("/sign-up/emails/verification")
public ResponseEntity<EmailVerificationResult> verificateEmail(@RequestParam(name = "member-email") String email) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Data;

@Getter
@Data
@AllArgsConstructor
@Builder
public class MemberResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;

@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Affiliation {
@Id
private long affiliation_id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public interface MemberService {
boolean checkExistAffiliationById(Long affiliationId);

MemberResponse changePasswrd(String memberId, MemberRequest memberRequest);

void makeAffiliation();
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ public MemberResponse changePasswrd(String memberId, MemberRequest memberRequest
return MemberResponse.form(member.getId(), member.getUsername(), member.getEmail(), member.getCreateAt(), null, member.getPassword());
}

@Override
@Transactional
public void makeAffiliation() {
affiliationRepository.save(Affiliation.builder().affiliation_id(1).affiliationName("총학").build());
}

private Member findByUserName(String username) {
return memberRepository.findByUsername(username)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import com.example.bigbrotherbe.domain.member.repository.MemberRepository;
import com.example.bigbrotherbe.domain.member.entity.role.Affiliation;
import com.example.bigbrotherbe.domain.member.entity.role.AffiliationMember;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@Slf4j
public class InitialDataLoader {

@Bean
Expand All @@ -22,9 +24,9 @@ public CommandLineRunner loadData(MemberRepository memberRepository, Affiliation
if (memberRepository.findByUsername("admin").isEmpty()) {
// Create initial admin user
Member admin = new Member();
admin.setUsername("admin");
admin.setEmail("admin");
admin.setPassword(passwordEncoder.encode("admin_password"));
admin.setEmail("[email protected]");
admin.setUsername("admin");
memberRepository.save(admin);
Affiliation affiliation = affiliationRepository.findByAffiliationName("총학")
.orElseThrow(() -> new IllegalArgumentException("잘못된 소속 이름입니다."));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.example.bigbrotherbe.global.filter;


import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomerCorsFilter implements Filter {

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;

response.setHeader("Access-Control-Allow-Origin", "http://api.mju-bigbrother.xyz:3000, http://localhost:3000");

// CORS 허용한 Origin
response.setHeader("Access-Control-Allow-Credentials", "true");
// 자격이 포함된 요청 받기
response.setHeader("Access-Control-Allow-Methods","*");
// http 메서드 전체 다 허용
response.setHeader("Access-Control-Max-Age", "3600");
// 프리플라이트(preflight) 요청의 캐시 시간을 정의합니다.
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization");
// 이 헤더는 클라이언트가 요청 시 사용할 수 있는 HTTP 헤더를 정의합니다.

if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
}else {
chain.doFilter(req, res);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.bigbrotherbe.global.response;


import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Builder;

@Builder
@JsonPropertyOrder
public record ApiResponse<T>(
String status,
T data,
String message
) {
private static final String SUCCESS = "success";
private static final String ERROR = "error";

public static <T> ApiResponse<T> ok(T data){
return ApiResponse.<T>builder()
.status(SUCCESS)
.data(data)
.build();
}

public static <T> ApiResponse<T> error(String errorMessage){
return ApiResponse.<T>builder()
.status(ERROR)
.message(errorMessage)
.build();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//package com.example.bigbrotherbe.global.ssh;
//
//import lombok.RequiredArgsConstructor;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
//import org.springframework.boot.jdbc.DataSourceBuilder;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//
//import javax.sql.DataSource;
//
//@Slf4j
//@Configuration
//@RequiredArgsConstructor
//public class SshDataSourceConfig {
//
// private final SshTunnelingInitializer initializer;
//
// @Bean("dataSource")
// @Primary
// public DataSource dataSource(DataSourceProperties properties) {
// Integer forwardedPort = initializer.buildSshConnection(); // ssh 연결 및 터널링 설정
// String url = properties.getUrl().replace("[forwardedPort]",String.valueOf(forwardedPort));
// log.info(url);
// return DataSourceBuilder.create()
// .url(url)
// .username(properties.getUsername())
// .password(properties.getPassword())
// .driverClassName(properties.getDriverClassName())
// .build();
// }
//}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//package com.example.bigbrotherbe.global.ssh;
//
//
//import com.jcraft.jsch.JSch;
//import com.jcraft.jsch.JSchException;
//import com.jcraft.jsch.Session;
//import jakarta.annotation.PreDestroy;
//import lombok.Setter;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.beans.factory.annotation.Value;
//import org.springframework.stereotype.Component;
//import org.springframework.validation.annotation.Validated;
//
//@Slf4j
//@Component
//@Validated @Setter
//public class SshTunnelingInitializer {
//
// @Value("${ssh.remote-host}")
// private String remoteJumpHost;
//
// @Value("${ssh.ssh-port}")
// private int sshPort;
//
// @Value("${ssh.user-name}")
// private String userName;
//
// @Value("${ssh.private-key-path}")
// private String privateKeyPath; // SSH Private Key 경로;
// private int databasePort = 3306;
//
// private Session session;
// @PreDestroy
// public void destroy() {
// if (session.isConnected())
// session.disconnect();
// }
//
// public Integer buildSshConnection() {
// Integer forwardPort = null;
//
// try {
// log.info("Connecting to SSH with {}@{}:{} using privateKey at {}", userName, remoteJumpHost, sshPort, privateKeyPath);
// JSch jsch = new JSch();
//
// // connection 1. application server to jump server 앱서버 -> ec2
// jsch.addIdentity(privateKeyPath); // pem 키 추가
// session = jsch.getSession(userName, remoteJumpHost, sshPort); // 세션 설정
// session.setConfig("StrictHostKeyChecking", "no");
//
// log.info("Starting SSH session connection...");
// session.connect(); // 연결
// log.info("SSH session connected");
//
// // connection 2. jump server to remote server
// forwardPort = session.setPortForwardingL(0, "localhost", databasePort);
// log.info("Port forwarding created on local port {} to remote port {}", forwardPort, databasePort);
//
// } catch (JSchException e) {
// log.error(e.getMessage());
// this.destroy();
// throw new RuntimeException(e);
// }
//
// return forwardPort;
// }
//}
Loading
Loading