Skip to content

Commit 4c9ca2e

Browse files
authored
Merge pull request #3 from techcode-viewer/develop
Develop
2 parents d7df3f4 + f335f61 commit 4c9ca2e

File tree

17 files changed

+464
-17
lines changed

17 files changed

+464
-17
lines changed

.github/workflows/CICD.yml

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: CICD
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v3
17+
- name: Install JDK 17
18+
uses: actions/setup-java@v3
19+
with:
20+
java-version: '17'
21+
distribution: 'temurin'
22+
23+
- name: Create application-secret-deploy.yml
24+
run: |
25+
echo "Decoding SECRET_FILE secret and create application-secret-deploy.yml"
26+
echo "${{ secrets.SECRET_FILE }}" | base64 -d > ./src/main/resources/application-secret-deploy.yml
27+
28+
29+
- name: Build with Gradle
30+
run: |
31+
chmod 777 ./gradlew
32+
./gradlew clean assemble -x test
33+
34+
- name: Login to DockerHub
35+
if: github.event_name == 'push'
36+
uses: docker/login-action@v1
37+
with:
38+
username: ${{ secrets.DOCKERHUB_USERNAME }}
39+
password: ${{ secrets.DOCKERHUB_TOKEN }}
40+
41+
- name: Build Docker
42+
if: github.event_name == 'push'
43+
run: docker build --platform linux/amd64 -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO_NAME }} .
44+
45+
- name: Push Docker
46+
if: github.event_name == 'push'
47+
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO_NAME }}:latest
48+
49+
deploy:
50+
needs: build
51+
if: github.event_name == 'push'
52+
runs-on: ubuntu-latest
53+
steps:
54+
- name: Set target IP
55+
run: |
56+
STATUS=$(curl -o /dev/null -w "%{http_code}" "http://${{ secrets.LIVE_SERVER_IP }}/env")
57+
echo $STATUS
58+
if [ $STATUS = 200 ]; then
59+
CURRENT_UPSTREAM=$(curl -s "http://${{ secrets.LIVE_SERVER_IP }}/env")
60+
else
61+
CURRENT_UPSTREAM=green
62+
fi
63+
echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV
64+
if [ $CURRENT_UPSTREAM = blue ]; then
65+
echo "CURRENT_PORT=8080" >> $GITHUB_ENV
66+
echo "STOPPED_PORT=8081" >> $GITHUB_ENV
67+
echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV
68+
elif [ $CURRENT_UPSTREAM = green ]; then
69+
echo "CURRENT_PORT=8081" >> $GITHUB_ENV
70+
echo "STOPPED_PORT=8080" >> $GITHUB_ENV
71+
echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV
72+
else
73+
echo "error"
74+
exit 1
75+
fi
76+
77+
- name: Login Server
78+
uses: appleboy/ssh-action@master
79+
with:
80+
username: ${{ secrets.SERVER_USER_NAME }}
81+
host: ${{ secrets.LIVE_SERVER_IP }}
82+
key: ${{ secrets.SSH_KEY }}
83+
script_stop: true
84+
85+
- name: Docker compose
86+
uses: appleboy/ssh-action@master
87+
with:
88+
username: ${{ secrets.SERVER_USER_NAME }}
89+
host: ${{ secrets.LIVE_SERVER_IP }}
90+
key: ${{ secrets.SSH_KEY }}
91+
script_stop: true
92+
script: |
93+
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO_NAME }}:latest
94+
sudo docker-compose -f docker-compose-${{env.TARGET_UPSTREAM}}.yml up -d
95+
96+
- name: Check deploy server URL
97+
uses: jtalk/url-health-check-action@v3
98+
with:
99+
url: http://${{ secrets.LIVE_SERVER_IP }}:${{env.STOPPED_PORT}}/env
100+
max-attempts: 5
101+
retry-delay: 10s
102+
103+
- name: Change nginx upstream
104+
uses: appleboy/ssh-action@master
105+
with:
106+
username: ${{ secrets.SERVER_USER_NAME }}
107+
host: ${{ secrets.LIVE_SERVER_IP }}
108+
key: ${{ secrets.SSH_KEY }}
109+
script_stop: true
110+
script: |
111+
sudo docker exec -i nginxserver bash -c 'echo "set \$service_url ${{ env.TARGET_UPSTREAM }};" > /etc/nginx/conf.d/service-env.inc && nginx -s reload'
112+
113+
- name: Stop current server
114+
uses: appleboy/ssh-action@master
115+
with:
116+
username: ${{ secrets.SERVER_USER_NAME }}
117+
host: ${{ secrets.LIVE_SERVER_IP }}
118+
key: ${{ secrets.SSH_KEY }}
119+
script_stop: true
120+
script: |
121+
if [ $(sudo docker ps -a -q -f name=${{env.CURRENT_UPSTREAM}}) ]; then
122+
sudo docker stop ${{env.CURRENT_UPSTREAM}}
123+
sudo docker rm ${{env.CURRENT_UPSTREAM}}
124+
else
125+
echo "Container ${{env.CURRENT_UPSTREAM}} does not exist, skipping stop and remove steps."
126+
fi
127+
128+
- name: Delete old docker images
129+
uses: appleboy/ssh-action@master
130+
with:
131+
username: ${{ secrets.SERVER_USER_NAME }}
132+
host: ${{ secrets.LIVE_SERVER_IP }}
133+
key: ${{ secrets.SSH_KEY }}
134+
script: |
135+
dangling_images=$(sudo docker images -f "dangling=true" -q)
136+
if [ ! -z "$dangling_images" ]; then
137+
sudo docker rmi $dangling_images
138+
else
139+
echo "No dangling images found"
140+
fi

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ build/
44
!gradle/wrapper/gradle-wrapper.jar
55
!**/src/main/**/build/
66
!**/src/test/**/build/
7+
!**/src/main/resoureces/application.yml
8+
application-*.yml
79

810
### STS ###
911
.apt_generated
@@ -38,4 +40,5 @@ out/
3840

3941

4042
### SET ###
41-
!**/src/main/resources/application.properties
43+
application.properties
44+
application.yml

Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM amazoncorretto:17-alpine-jdk
2+
ARG JAR_FILE=build/libs/*.jar
3+
ARG PROFILES
4+
ARG ENV
5+
COPY ${JAR_FILE} app.jar
6+
ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILES}", "-Dserver.env=${ENV}", "-jar", "app.jar"]

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ repositories {
2525

2626
dependencies {
2727
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
28-
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
29-
implementation 'org.springframework.boot:spring-boot-starter-security'
28+
// implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
29+
// implementation 'org.springframework.boot:spring-boot-starter-security'
3030
implementation 'org.springframework.boot:spring-boot-starter-web'
3131
compileOnly 'org.projectlombok:lombok'
3232
runtimeOnly 'com.mysql:mysql-connector-j'

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
rootProject.name = 'main'
1+
rootProject.name = 'code-view'
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package codeview.main.controller;
2+
3+
import codeview.main.dto.ApiResponse;
4+
import codeview.main.dto.BoardRequest;
5+
import codeview.main.dto.BoardResponse;
6+
import codeview.main.entity.Board;
7+
import codeview.main.service.BoardService;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.web.bind.annotation.*;
12+
13+
import java.util.Optional;
14+
15+
16+
@RestController
17+
@RequiredArgsConstructor
18+
@RequestMapping("/board")
19+
public class BoardController {
20+
private final BoardService boardService;
21+
@GetMapping("/{id}")
22+
public ResponseEntity<BoardResponse> getBoardById(@PathVariable Long id) {
23+
Optional<Board> findBoardObj = boardService.findBoardById(id);
24+
if (findBoardObj.isPresent()) {
25+
BoardResponse response = new BoardResponse(findBoardObj.get());
26+
return ResponseEntity.ok(response);
27+
} else {
28+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
29+
}
30+
}
31+
32+
@PostMapping("/write")
33+
public ResponseEntity<ApiResponse<BoardResponse>> boardSave(@RequestBody Board board) {
34+
Board saveBoard = boardService.save(board);
35+
BoardResponse boardResponse = new BoardResponse(saveBoard);
36+
ApiResponse<BoardResponse> response = new ApiResponse<>(HttpStatus.CREATED, "Board Write Success", boardResponse);
37+
return ResponseEntity.status(HttpStatus.CREATED)
38+
.body(response);
39+
}
40+
@PutMapping("/{id}")
41+
public ResponseEntity<BoardResponse> updateBoard(
42+
@PathVariable Long id,
43+
@RequestBody BoardRequest boardRequest) {
44+
Board updatedBoard = new Board();
45+
updatedBoard.setTitle(boardRequest.getTitle());
46+
47+
try {
48+
Board savedBoard = boardService.updateBoard(id, updatedBoard);
49+
BoardResponse responseDto = new BoardResponse(savedBoard);
50+
return ResponseEntity.ok(responseDto);
51+
} catch (RuntimeException e) {
52+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
53+
}
54+
}
55+
56+
@DeleteMapping("/{id}")
57+
public ResponseEntity<BoardResponse> deleteBoard(@PathVariable Long id) {
58+
try {
59+
boardService.deleteBoard(id);
60+
return ResponseEntity.noContent().build();
61+
}catch (RuntimeException e){
62+
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
63+
}
64+
}
65+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package codeview.main.controller;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.ResponseBody;
7+
import org.springframework.web.bind.annotation.RestController;
8+
9+
import java.util.Map;
10+
import java.util.TreeMap;
11+
12+
@RestController
13+
public class ServerHealthCheckController {
14+
@Value("${server.env}")
15+
private String env;
16+
@Value("${server.port}")
17+
private String serverPort;
18+
@Value("${server.serverAddress}")
19+
private String serverAddress;
20+
@Value("${serverName}")
21+
private String serverName;
22+
23+
@GetMapping("/hc")
24+
public ResponseEntity<?> healthCheck() {
25+
Map<String, String> responseData = new TreeMap<>();
26+
responseData.put("serverName", serverName);
27+
responseData.put("serverAddress", serverAddress);
28+
responseData.put("serverPort", serverPort);
29+
responseData.put("env", env);
30+
return ResponseEntity.ok(responseData);
31+
}
32+
33+
@GetMapping("/env")
34+
@ResponseBody
35+
public ResponseEntity<?> getEnv() {
36+
return ResponseEntity.ok().body(env);
37+
}
38+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package codeview.main.dto;
2+
3+
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
import org.springframework.http.HttpStatus;
7+
8+
@Getter
9+
@Setter
10+
public class ApiResponse<T> {
11+
private HttpStatus httpStatus;
12+
private String message;
13+
private T data;
14+
15+
public ApiResponse(HttpStatus httpStatus, String message, T data) {
16+
this.httpStatus = httpStatus;
17+
this.message = message;
18+
this.data = data;
19+
}
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package codeview.main.dto;
2+
import lombok.Getter;
3+
import lombok.Setter;
4+
5+
@Getter
6+
@Setter
7+
public class BoardRequest {
8+
private String title;
9+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package codeview.main.dto;
2+
3+
import codeview.main.entity.Board;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
@Getter
8+
@Setter
9+
public class BoardResponse {
10+
private Long id;
11+
private String title;
12+
13+
public BoardResponse(Board board) {
14+
this.id = board.getId();
15+
this.title = board.getTitle();
16+
}
17+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package codeview.main.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.*;
5+
6+
import java.util.List;
7+
8+
@Entity
9+
@Getter @Setter
10+
@NoArgsConstructor
11+
public class Board {
12+
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
13+
@Column
14+
private String title;
15+
//
16+
// @Column @OneToOne
17+
// private Content content;
18+
//
19+
// @Column @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
20+
// private List<Comment> commentList;
21+
22+
public Board(String title) {
23+
this.title = title;
24+
}
25+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package codeview.main.entity;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
6+
@Entity
7+
public class Comment {
8+
@Id
9+
private Long id;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package codeview.main.entity;
2+
3+
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.Id;
6+
7+
@Entity
8+
public class Content {
9+
@Id
10+
private Long id;
11+
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package codeview.main.repository;
2+
3+
import codeview.main.entity.Board;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
@Repository
8+
public interface BoardRepository extends JpaRepository<Board, Long> {
9+
}

0 commit comments

Comments
 (0)