From 9842a723924e799c3d7fd4b01c9959f4ccaadd25 Mon Sep 17 00:00:00 2001 From: Sejeong Kim <64718002+clean2001@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:12:38 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20Table=20=EC=96=B4=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=EC=9C=BC=EB=A1=9C=20=EB=B3=B5=ED=95=A9=20?= =?UTF-8?q?=EC=9C=A0=EB=8B=88=ED=81=AC=20=EC=A0=9C=EC=95=BD=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=20=EC=B6=94=EA=B0=80=20(#262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/layer/domain/member/entity/Member.java | 1 + 1 file changed, 1 insertion(+) diff --git a/layer-domain/src/main/java/org/layer/domain/member/entity/Member.java b/layer-domain/src/main/java/org/layer/domain/member/entity/Member.java index 45c6c225..5fe784da 100644 --- a/layer-domain/src/main/java/org/layer/domain/member/entity/Member.java +++ b/layer-domain/src/main/java/org/layer/domain/member/entity/Member.java @@ -13,6 +13,7 @@ @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "socialType", "socialId" }) }) public class Member extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) From d7f1ec364031f9f8e22948fe5e4f328e37716b4b Mon Sep 17 00:00:00 2001 From: Sejeong Kim <64718002+clean2001@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:21:59 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20layer-batch=20>=20application.yml?= =?UTF-8?q?=EC=97=90=20=EB=94=94=EC=8A=A4=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B6=94=EA=B0=80=20(#264)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- layer-batch/src/main/resources/application.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/layer-batch/src/main/resources/application.yml b/layer-batch/src/main/resources/application.yml index 75718ee9..07de1fe2 100644 --- a/layer-batch/src/main/resources/application.yml +++ b/layer-batch/src/main/resources/application.yml @@ -63,4 +63,10 @@ openai: systemContent: ${OPENAI_SYSTEM_CONTENT} apiKey: ${OPENAI_API_KEY} model: ${OPENAI_MODEL} - maxTokens: ${OPENAI_MAX_TOKENS} \ No newline at end of file + maxTokens: ${OPENAI_MAX_TOKENS} + +discord: + webhook: + retrospect-url: ${DISCORD_RETROSPECT_URL} + space-url: ${DISCORD_SPACE_URL} + member-url: ${DISCORD_MEMBER_URL} \ No newline at end of file From e912fb019e9e00e78cf78689b1574eb1459d4ce0 Mon Sep 17 00:00:00 2001 From: Mingyu Song <100754581+mikekks@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:55:58 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=08feat:=20admin=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EA=B0=9C=EB=B0=9C=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20(#266)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: 어드민 모듈 추가 * add: 어드민 모듈 추가 * add: 어드민 모듈 docker 파일 추가 * feat: 어드민 관련 레포지토리 메서드 도메인 내에 구현 * feat: 회원 활동 목록 조회 기능 구현 * add: admin 스프링 서버 환경 세팅 * add: 모든 요청 허용하는 시큐리티 설정 추가 * add: dev yml 추가 * chore: admin 모듈 ci-cd 추가 * chore: admin 관련 컨테이너 및 프록시 추가 * style: 공백 제거 * chore: page 1부터 시작하도록 수정 * chore: 페이지네이션 default value 설정 --- .github/workflows/aws-cicd-dev.yml | 20 +++++++ .github/workflows/aws-cicd-prod.yml | 19 +++++++ .gitignore | 3 +- build.gradle | 22 +++++++- config/tokens/StoredCredential.txt | Bin 0 -> 1130 bytes layer-admin/Dockerfile-admin | 10 ++++ .../main/java/org/layer/AdminApplication.java | 14 +++++ .../java/org/layer/config/SecurityConfig.java | 23 ++++++++ .../member/controller/AdminMemberApi.java | 26 +++++++++ .../controller/AdminMemberController.java | 29 ++++++++++ .../dto/GetMemberActivityResponse.java | 29 ++++++++++ .../dto/GetMembersActivitiesResponse.java | 15 +++++ .../member/service/AdminMemberService.java | 52 ++++++++++++++++++ .../src/main/resources/application-dev.yml | 23 ++++++++ .../src/main/resources/application-prod.yml | 23 ++++++++ .../infra/development/docker-compose.yaml | 18 ++++++ layer-api/infra/development/nginx.conf | 12 ++++ .../repository/AdminAnswerRepository.java | 8 +++ .../repository/AdminMemberRepository.java | 10 ++++ .../AdminMemberSpaceRelationRepository.java | 9 +++ settings.gradle | 1 + 21 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 config/tokens/StoredCredential.txt create mode 100644 layer-admin/Dockerfile-admin create mode 100644 layer-admin/src/main/java/org/layer/AdminApplication.java create mode 100644 layer-admin/src/main/java/org/layer/config/SecurityConfig.java create mode 100644 layer-admin/src/main/java/org/layer/member/controller/AdminMemberApi.java create mode 100644 layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java create mode 100644 layer-admin/src/main/java/org/layer/member/controller/dto/GetMemberActivityResponse.java create mode 100644 layer-admin/src/main/java/org/layer/member/controller/dto/GetMembersActivitiesResponse.java create mode 100644 layer-admin/src/main/java/org/layer/member/service/AdminMemberService.java create mode 100644 layer-admin/src/main/resources/application-dev.yml create mode 100644 layer-admin/src/main/resources/application-prod.yml create mode 100644 layer-domain/src/main/java/org/layer/domain/answer/repository/AdminAnswerRepository.java create mode 100644 layer-domain/src/main/java/org/layer/domain/member/repository/AdminMemberRepository.java create mode 100644 layer-domain/src/main/java/org/layer/domain/space/repository/AdminMemberSpaceRelationRepository.java diff --git a/.github/workflows/aws-cicd-dev.yml b/.github/workflows/aws-cicd-dev.yml index 83033cf9..9fdcb789 100644 --- a/.github/workflows/aws-cicd-dev.yml +++ b/.github/workflows/aws-cicd-dev.yml @@ -4,6 +4,7 @@ on: push: branches: - develop + - feat/LA-20 env: REGISTRY: "docker.io" @@ -56,6 +57,7 @@ jobs: run: | echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-api/src/main/resources/application-secret.properties echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-batch/src/main/resources/application-secret.properties + echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-admin/src/main/resources/application-secret.properties - name: Build layer-api module run: ./gradlew :layer-api:build @@ -69,6 +71,12 @@ jobs: - name: Test layer-batch module run: ./gradlew :layer-batch:test + - name: Build layer-admin module + run: ./gradlew :layer-admin:build + + - name: Test layer-admin module + run: ./gradlew :layer-admin:test + - name: Docker Hub Login uses: docker/login-action@v1 with: @@ -82,6 +90,7 @@ jobs: images: | ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-api ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-batch + ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-admin - name: Push layer-api Docker Image uses: docker/build-push-action@v4 @@ -104,6 +113,16 @@ jobs: tags: | ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-batch:latest + - name: Push layer-admin Docker Image + uses: docker/build-push-action@v4 + with: + context: ./layer-admin + file: ./layer-admin/Dockerfile-admin # Dockerfile 이름 지정 + platforms: linux/amd64 + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-admin:latest + deploy: name: Deploy needs: [ build, setup ] @@ -119,6 +138,7 @@ jobs: run: | echo "${{ secrets.APPLICATION_SECRET_PROPERTIES }}" > ./layer-api/infra/${{ env.DEPLOY_TARGET }}/application-secret.properties echo "${{ secrets.APPLICATION_SECRET_PROPERTIES }}" > ./layer-batch/src/main/resources/application-secret.properties + echo "${{ secrets.APPLICATION_SECRET_PROPERTIES }}" > ./layer-admin/src/main/resources/application-secret.properties - name: Archive Files run: | diff --git a/.github/workflows/aws-cicd-prod.yml b/.github/workflows/aws-cicd-prod.yml index 6b3c8eb9..120105e4 100644 --- a/.github/workflows/aws-cicd-prod.yml +++ b/.github/workflows/aws-cicd-prod.yml @@ -57,6 +57,7 @@ jobs: run: | echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-api/src/main/resources/application-secret.properties echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-batch/src/main/resources/application-secret.properties + echo "${APPLICATION_SECRET_PROPERTIES}" > ./layer-admin/src/main/resources/application-secret.properties - name: Build layer-api module run: ./gradlew :layer-api:build @@ -70,6 +71,12 @@ jobs: - name: Test layer-batch module run: ./gradlew :layer-batch:test + - name: Build layer-admin module + run: ./gradlew :layer-admin:build + + - name: Test layer-admin module + run: ./gradlew :layer-admin:test + - name: Docker Hub Login uses: docker/login-action@v1 with: @@ -83,6 +90,7 @@ jobs: images: | ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-api ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-batch + ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-admin - name: Push layer-api Docker Image uses: docker/build-push-action@v4 @@ -106,6 +114,16 @@ jobs: ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-batch:latest no-cache: true + - name: Push layer-admin Docker Image + uses: docker/build-push-action@v4 + with: + context: ./layer-admin + file: ./layer-admin/Dockerfile-admin # Dockerfile 이름 지정 + platforms: linux/amd64 + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.IMAGE_NAME }}_layer-admin:latest + deploy: name: Deploy needs: [ build, setup ] @@ -121,6 +139,7 @@ jobs: run: | echo "${{ secrets.APPLICATION_SECRET_PROPERTIES }}" > ./layer-api/infra/${{ env.DEPLOY_TARGET }}/application-secret.properties echo "${{ secrets.APPLICATION_SECRET_PROPERTIES }}" > ./layer-batch/src/main/resources/application-secret.properties + echo "${{ secrets.APPLICATION_SECRET_PROPERTIES }}" > ./layer-admin/src/main/resources/application-secret.properties - name: Archive Files run: | diff --git a/.gitignore b/.gitignore index e0630052..4e76a4c9 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,5 @@ credentials.json layer-api/src/main/resources/tokens/StoredCredential -layer-batch/src/main/resources/application-secret.properties \ No newline at end of file +layer-batch/src/main/resources/application-secret.properties +layer-admin/src/main/resources/application-secret.properties \ No newline at end of file diff --git a/build.gradle b/build.gradle index a9a41f8e..ac8dbd9e 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,6 @@ project(":layer-api") { implementation project(path: ':layer-domain') implementation project(path: ':layer-external') - implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-validation' @@ -82,7 +81,6 @@ project(":layer-api") { implementation 'org.springframework.boot:spring-boot-starter-data-redis' testImplementation 'org.springframework.boot:spring-boot-starter-test' - // openfeign implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.1.2") @@ -195,4 +193,24 @@ project(":layer-batch") { runtimeOnly 'com.mysql:mysql-connector-j' } +} + +project(":layer-admin") { + jar.enabled = false + bootJar.enabled = true + + dependencies { + implementation project(path: ':layer-domain') + + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'com.mysql:mysql-connector-j' + + // swagger + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") + + // Security + implementation 'org.springframework.boot:spring-boot-starter-security' + } + } \ No newline at end of file diff --git a/config/tokens/StoredCredential.txt b/config/tokens/StoredCredential.txt new file mode 100644 index 0000000000000000000000000000000000000000..63b1c6c17b9026d421421e9725d098cde63b28b2 GIT binary patch literal 1130 zcma)5L66%+6rN;vp>!*>Z~zGjq^dn|sGZo`>=N}*yh)vSv7Jqvgv}xW6MO88V|$YE zOq>M7v8vQVi&XvqCqRq1!U-WR9Jp}hKr4ZSs)`dA)KkaVN?R_~jx_J`-uJ%uzWL_( zAFz>9@Ja}0Q05FraucQ_3r;p({Qak&U;FsCHBi*S`G`PIhb|-3057o-Md^q{-h6WX zU67v(**Oun#4Rwe&QnA=1;r2cj{klAzZcGR9~1$YPXJhZe0)Nux+InZLV^g%aDrtw z!YE-fk)Cs3|u_Pv z>kAb5xdJQtDfkQ&<`V|~SU|NZ!?NaDJBpe)i!>ct4ZXv7Fx20Vc-Qu~r@D?QuTFc? z_O9L1=uy9}br!XyPI^map+vFTt#wB?+P#6*$e5<}m!ThHR38t!3ki=?hZ}<~VG6F= z^_^B6WKPEolPq+$tyz6GHJW&24LVev#P&hiqmtq_ss?4?{1wlIVx zw1#%ou`^vVs|KwdE%u%CqoAY1UNQ}(YCl}KjrQ13! auth + .anyRequest().permitAll() // 모든 요청 허용 + ) + .httpBasic(AbstractHttpConfigurer::disable); // HTTP Basic 인증 비활성화 + + return http.build(); + } +} diff --git a/layer-admin/src/main/java/org/layer/member/controller/AdminMemberApi.java b/layer-admin/src/main/java/org/layer/member/controller/AdminMemberApi.java new file mode 100644 index 00000000..747b24d8 --- /dev/null +++ b/layer-admin/src/main/java/org/layer/member/controller/AdminMemberApi.java @@ -0,0 +1,26 @@ +package org.layer.member.controller; + +import org.layer.member.controller.dto.GetMembersActivitiesResponse; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestParam; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +@Tag(name = "[ADMIN] 회원 서비스", description = "회원 관련 api") +public interface AdminMemberApi { + + @Operation(summary = "회원 활동 목록 조회") + @Parameters({ + @Parameter(name = "password", description = "패스워드", example = "abcdef", required = true), + @Parameter(name = "page", description = "페이지 수, 최솟값 1", example = "1", required = true), + @Parameter(name = "take", description = "가져올 데이터 수", example = "20", required = true) + }) + ResponseEntity getMemberActivities( + @RequestParam String password, + @RequestParam int page, + @RequestParam int take); + +} diff --git a/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java b/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java new file mode 100644 index 00000000..1d85167b --- /dev/null +++ b/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java @@ -0,0 +1,29 @@ +package org.layer.member.controller; + +import org.layer.member.controller.dto.GetMembersActivitiesResponse; +import org.layer.member.service.AdminMemberService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import lombok.RequiredArgsConstructor; + +@RequestMapping("/admin/members") +@RequiredArgsConstructor +@RestController +public class AdminMemberController implements AdminMemberApi { + private final AdminMemberService adminMemberService; + + @Override + @GetMapping + public ResponseEntity getMemberActivities( + @RequestParam String password, + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "20") int take) { + + return ResponseEntity.ok().body(adminMemberService.getMemberActivities(password, page, take)); + } + +} diff --git a/layer-admin/src/main/java/org/layer/member/controller/dto/GetMemberActivityResponse.java b/layer-admin/src/main/java/org/layer/member/controller/dto/GetMemberActivityResponse.java new file mode 100644 index 00000000..3a957e66 --- /dev/null +++ b/layer-admin/src/main/java/org/layer/member/controller/dto/GetMemberActivityResponse.java @@ -0,0 +1,29 @@ +package org.layer.member.controller.dto; + +import java.time.LocalDateTime; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +@Schema(name = "GetMemberActivityResponse", description = "회원 활동 Dto") +public record GetMemberActivityResponse( + @NotNull + @Schema(description = "회원 이름", example = "홍길동") + String name, + @NotNull + @Schema(description = "최근 활동 날짜", example = "2024-11-30T16:21:47.031Z") + LocalDateTime recentActivityDate, + @NotNull + @Schema(description = "소속된 스페이스 수", example = "7") + long spaceCount, + @NotNull + @Schema(description = "작성한 회고 수", example = "15") + long retrospectAnswerCount, + @NotNull + @Schema(description = "회원가입 날짜", example = "2024-10-30T16:21:47.031Z") + LocalDateTime signUpDate, + @NotNull + @Schema(description = "회원가입 플랫폼", example = "KAKAO") + String socialType +) { +} diff --git a/layer-admin/src/main/java/org/layer/member/controller/dto/GetMembersActivitiesResponse.java b/layer-admin/src/main/java/org/layer/member/controller/dto/GetMembersActivitiesResponse.java new file mode 100644 index 00000000..3db49acb --- /dev/null +++ b/layer-admin/src/main/java/org/layer/member/controller/dto/GetMembersActivitiesResponse.java @@ -0,0 +1,15 @@ +package org.layer.member.controller.dto; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; + +@Schema(name = "GetMembersActivitiesResponse", description = "회원 활동 목록 Dto") +public record GetMembersActivitiesResponse( + @NotNull + @Schema(description = "회원 활동 목록", example = "") + List responses + +) { +} diff --git a/layer-admin/src/main/java/org/layer/member/service/AdminMemberService.java b/layer-admin/src/main/java/org/layer/member/service/AdminMemberService.java new file mode 100644 index 00000000..d761a368 --- /dev/null +++ b/layer-admin/src/main/java/org/layer/member/service/AdminMemberService.java @@ -0,0 +1,52 @@ +package org.layer.member.service; + +import java.util.List; + +import org.layer.domain.answer.repository.AdminAnswerRepository; +import org.layer.domain.member.entity.Member; +import org.layer.domain.member.repository.AdminMemberRepository; +import org.layer.domain.space.repository.AdminMemberSpaceRelationRepository; +import org.layer.member.controller.dto.GetMemberActivityResponse; +import org.layer.member.controller.dto.GetMembersActivitiesResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AdminMemberService { + private final AdminMemberRepository adminMemberRepository; + private final AdminMemberSpaceRelationRepository adminMemberSpaceRelationRepository; + private final AdminAnswerRepository adminAnswerRepository; + + @Value("${admin.password}") + private String password; + + public GetMembersActivitiesResponse getMemberActivities(String password, int page, int take) { + + // TODO: 검증 로직 필터단으로 옮기기 + if (!password.equals(this.password)) { + throw new IllegalArgumentException("비밀번호가 올바르지 않습니다."); + } + + PageRequest pageRequest = PageRequest.of(page - 1, take); + Page members = adminMemberRepository.findAll(pageRequest); + + List responses = members.getContent().stream() + .map(member -> { + + Long spaceCount = adminMemberSpaceRelationRepository.countAllByMemberId(member.getId()); + Long retrospectAnswerCount = adminAnswerRepository.countAllByMemberId(member.getId()); + + return new GetMemberActivityResponse(member.getName(), null, spaceCount, retrospectAnswerCount, + member.getCreatedAt(), member.getSocialType().name()); + }).toList(); + + return new GetMembersActivitiesResponse(responses); + } +} diff --git a/layer-admin/src/main/resources/application-dev.yml b/layer-admin/src/main/resources/application-dev.yml new file mode 100644 index 00000000..941c6352 --- /dev/null +++ b/layer-admin/src/main/resources/application-dev.yml @@ -0,0 +1,23 @@ +server: + port: 3000 + +spring: + config: + import: application-secret.properties + datasource: + url: ${AWS_DEV_DB_URL} + username: ${AWS_PROD_DB_NAME} + password: ${AWS_PROD_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + format_sql: true + show_sql: true + open-in-view: false + database: mysql + +admin: + password: ${ADMIN_PASSWORD} \ No newline at end of file diff --git a/layer-admin/src/main/resources/application-prod.yml b/layer-admin/src/main/resources/application-prod.yml new file mode 100644 index 00000000..d053cad6 --- /dev/null +++ b/layer-admin/src/main/resources/application-prod.yml @@ -0,0 +1,23 @@ +server: + port: 3000 + +spring: + config: + import: application-secret.properties + datasource: + url: ${AWS_PROD_DB_URL} + username: ${AWS_PROD_DB_NAME} + password: ${AWS_PROD_DB_PASSWORD} + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + hibernate: + ddl-auto: validate + properties: + hibernate: + format_sql: true + show_sql: true + open-in-view: false + database: mysql + +admin: + password: ${ADMIN_PASSWORD} \ No newline at end of file diff --git a/layer-api/infra/development/docker-compose.yaml b/layer-api/infra/development/docker-compose.yaml index edd2b8c9..b0ea5bbe 100644 --- a/layer-api/infra/development/docker-compose.yaml +++ b/layer-api/infra/development/docker-compose.yaml @@ -29,6 +29,24 @@ services: - java-app restart: always + admin-app: + image: docker.io/clean01/layer-server_layer-admin:latest # + container_name: layer-admin + ports: + - "3000:3000" + environment: + - TZ=Asia/Seoul + - SPRING_PROFILES_ACTIVE=dev + volumes: + - ./application-secret.properties:/config/application-secret.properties + - ./log:/log + - ./tokens:/config/tokens + networks: + - app-network + depends_on: + - java-app + restart: always + nginx: image: nginx:latest container_name: nginx diff --git a/layer-api/infra/development/nginx.conf b/layer-api/infra/development/nginx.conf index 4bcbe484..639490fb 100644 --- a/layer-api/infra/development/nginx.conf +++ b/layer-api/infra/development/nginx.conf @@ -5,6 +5,10 @@ http { server layer-api:8080; } + upstream layer-admin { + server layer-admin:3000; + } + server { listen 80; @@ -15,5 +19,13 @@ http { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } + + location /admin/ { + proxy_pass http://layer-admin; # Proxy to layer-admin + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } } } \ No newline at end of file diff --git a/layer-domain/src/main/java/org/layer/domain/answer/repository/AdminAnswerRepository.java b/layer-domain/src/main/java/org/layer/domain/answer/repository/AdminAnswerRepository.java new file mode 100644 index 00000000..b0b044c6 --- /dev/null +++ b/layer-domain/src/main/java/org/layer/domain/answer/repository/AdminAnswerRepository.java @@ -0,0 +1,8 @@ +package org.layer.domain.answer.repository; + +import org.layer.domain.answer.entity.Answer; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AdminAnswerRepository extends JpaRepository { + Long countAllByMemberId(Long memberId); +} diff --git a/layer-domain/src/main/java/org/layer/domain/member/repository/AdminMemberRepository.java b/layer-domain/src/main/java/org/layer/domain/member/repository/AdminMemberRepository.java new file mode 100644 index 00000000..2c8aa81e --- /dev/null +++ b/layer-domain/src/main/java/org/layer/domain/member/repository/AdminMemberRepository.java @@ -0,0 +1,10 @@ +package org.layer.domain.member.repository; + +import org.layer.domain.member.entity.Member; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AdminMemberRepository extends JpaRepository { + Page findAll(Pageable pageable); +} diff --git a/layer-domain/src/main/java/org/layer/domain/space/repository/AdminMemberSpaceRelationRepository.java b/layer-domain/src/main/java/org/layer/domain/space/repository/AdminMemberSpaceRelationRepository.java new file mode 100644 index 00000000..7d3307e1 --- /dev/null +++ b/layer-domain/src/main/java/org/layer/domain/space/repository/AdminMemberSpaceRelationRepository.java @@ -0,0 +1,9 @@ +package org.layer.domain.space.repository; + +import org.layer.domain.space.entity.MemberSpaceRelation; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AdminMemberSpaceRelationRepository extends JpaRepository { + + Long countAllByMemberId(Long memberId); +} diff --git a/settings.gradle b/settings.gradle index b5e01a2c..b8ffbacd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,4 +4,5 @@ include 'layer-common' include 'layer-domain' include 'layer-external' include 'layer-batch' +include 'layer-admin' From b17d50f85f0827ca3c893620d11a2755f5a2d8d1 Mon Sep 17 00:00:00 2001 From: Mingyu Song <100754581+mikekks@users.noreply.github.com> Date: Tue, 3 Dec 2024 21:36:16 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EB=A6=AC=EB=B2=84=EC=8A=A4=20=ED=94=84?= =?UTF-8?q?=EB=A1=9D=EC=8B=B1=20v2=20(#270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: nginx 리버스 프록시 설정 * chore: admin 모듈 컨테이너에 추가 * chore: 커넥션 풀 수 조정 * chore: nginx 수정 * chore: nginx 수정 * chore: blue 상태 핸들링 * chore: 엔드포인트 변경 * chore: dev nginx conf 수정 * chore: dev 브랜치 테스트 --- .github/workflows/aws-cicd-dev.yml | 2 +- .github/workflows/aws-cicd-prod.yml | 1 - .../controller/AdminMemberController.java | 2 +- .../src/main/resources/application-dev.yml | 3 +++ .../src/main/resources/application-prod.yml | 3 +++ layer-api/infra/development/nginx.conf | 12 ++++++++-- .../infra/production/docker-compose-blue.yaml | 18 +++++++++++++++ .../production/docker-compose-green.yaml | 18 +++++++++++++++ layer-api/infra/production/nginx.blue.conf | 22 ++++++++++++++++++- layer-api/infra/production/nginx.conf | 19 ---------------- layer-api/infra/production/nginx.green.conf | 22 ++++++++++++++++++- .../src/main/resources/application.yml | 3 +++ 12 files changed, 99 insertions(+), 26 deletions(-) delete mode 100644 layer-api/infra/production/nginx.conf diff --git a/.github/workflows/aws-cicd-dev.yml b/.github/workflows/aws-cicd-dev.yml index 9fdcb789..37627514 100644 --- a/.github/workflows/aws-cicd-dev.yml +++ b/.github/workflows/aws-cicd-dev.yml @@ -4,7 +4,7 @@ on: push: branches: - develop - - feat/LA-20 + - feat/LA-20_3 env: REGISTRY: "docker.io" diff --git a/.github/workflows/aws-cicd-prod.yml b/.github/workflows/aws-cicd-prod.yml index 120105e4..33aa5669 100644 --- a/.github/workflows/aws-cicd-prod.yml +++ b/.github/workflows/aws-cicd-prod.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - chore/migration env: REGISTRY: "docker.io" diff --git a/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java b/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java index 1d85167b..795efed0 100644 --- a/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java +++ b/layer-admin/src/main/java/org/layer/member/controller/AdminMemberController.java @@ -10,7 +10,7 @@ import lombok.RequiredArgsConstructor; -@RequestMapping("/admin/members") +@RequestMapping("/members") @RequiredArgsConstructor @RestController public class AdminMemberController implements AdminMemberApi { diff --git a/layer-admin/src/main/resources/application-dev.yml b/layer-admin/src/main/resources/application-dev.yml index 941c6352..b68b603a 100644 --- a/layer-admin/src/main/resources/application-dev.yml +++ b/layer-admin/src/main/resources/application-dev.yml @@ -9,6 +9,9 @@ spring: username: ${AWS_PROD_DB_NAME} password: ${AWS_PROD_DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 5 # 최대 pool 크기 + minimum-idle: 5 # 최소 pool 크기 jpa: hibernate: ddl-auto: validate diff --git a/layer-admin/src/main/resources/application-prod.yml b/layer-admin/src/main/resources/application-prod.yml index d053cad6..54120ad1 100644 --- a/layer-admin/src/main/resources/application-prod.yml +++ b/layer-admin/src/main/resources/application-prod.yml @@ -9,6 +9,9 @@ spring: username: ${AWS_PROD_DB_NAME} password: ${AWS_PROD_DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 5 # 최대 pool 크기 + minimum-idle: 5 # 최소 pool 크기 jpa: hibernate: ddl-auto: validate diff --git a/layer-api/infra/development/nginx.conf b/layer-api/infra/development/nginx.conf index 639490fb..fabe06a6 100644 --- a/layer-api/infra/development/nginx.conf +++ b/layer-api/infra/development/nginx.conf @@ -9,8 +9,10 @@ http { server layer-admin:3000; } + # api.layerapp.io에 대한 서버 블록 server { listen 80; + server_name api.layerapp.io; location / { proxy_pass http://layer-api; @@ -19,9 +21,15 @@ http { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } + } - location /admin/ { - proxy_pass http://layer-admin; # Proxy to layer-admin + # admin.api.layerapp.io에 대한 서버 블록 + server { + listen 80; + server_name admin-dev.layerapp.io; + + location / { + proxy_pass http://layer-admin; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/layer-api/infra/production/docker-compose-blue.yaml b/layer-api/infra/production/docker-compose-blue.yaml index 82d57859..1f7311d0 100644 --- a/layer-api/infra/production/docker-compose-blue.yaml +++ b/layer-api/infra/production/docker-compose-blue.yaml @@ -29,5 +29,23 @@ services: - layer-api-blue restart: always + admin-app-blue: + image: docker.io/clean01/layer-server_layer-admin:latest # + container_name: layer-admin-blue + ports: + - "3001:3000" + environment: + - TZ=Asia/Seoul + - SPRING_PROFILES_ACTIVE=prod + volumes: + - ./application-secret.properties:/config/application-secret.properties + - ./log:/log + - ./tokens:/config/tokens + networks: + - app-network + depends_on: + - layer-api-blue + restart: always + networks: app-network: \ No newline at end of file diff --git a/layer-api/infra/production/docker-compose-green.yaml b/layer-api/infra/production/docker-compose-green.yaml index bf36a0b6..9df97019 100644 --- a/layer-api/infra/production/docker-compose-green.yaml +++ b/layer-api/infra/production/docker-compose-green.yaml @@ -29,5 +29,23 @@ services: - layer-api-green restart: always + admin-app-green: + image: docker.io/clean01/layer-server_layer-admin:latest # + container_name: layer-admin-green + ports: + - "3000:3000" + environment: + - TZ=Asia/Seoul + - SPRING_PROFILES_ACTIVE=prod + volumes: + - ./application-secret.properties:/config/application-secret.properties + - ./log:/log + - ./tokens:/config/tokens + networks: + - app-network + depends_on: + - layer-api-green + restart: always + networks: app-network: \ No newline at end of file diff --git a/layer-api/infra/production/nginx.blue.conf b/layer-api/infra/production/nginx.blue.conf index 326ea474..d8486355 100644 --- a/layer-api/infra/production/nginx.blue.conf +++ b/layer-api/infra/production/nginx.blue.conf @@ -5,8 +5,14 @@ http { server localhost:8081; } + upstream layer-admin { + server localhost:3001; + } + + # api.layerapp.io에 대한 서버 블록 server { listen 80; + server_name api.layerapp.io; location / { proxy_pass http://layer-api; @@ -16,4 +22,18 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } } -} \ No newline at end of file + + # admin.api.layerapp.io에 대한 서버 블록 + server { + listen 80; + server_name admin-prod.layerapp.io; + + location / { + proxy_pass http://layer-admin; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/layer-api/infra/production/nginx.conf b/layer-api/infra/production/nginx.conf deleted file mode 100644 index 4bcbe484..00000000 --- a/layer-api/infra/production/nginx.conf +++ /dev/null @@ -1,19 +0,0 @@ -events { } - -http { - upstream layer-api { - server layer-api:8080; - } - - server { - listen 80; - - location / { - proxy_pass http://layer-api; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - } -} \ No newline at end of file diff --git a/layer-api/infra/production/nginx.green.conf b/layer-api/infra/production/nginx.green.conf index a7f4110b..f03454f1 100644 --- a/layer-api/infra/production/nginx.green.conf +++ b/layer-api/infra/production/nginx.green.conf @@ -5,8 +5,14 @@ http { server localhost:8080; } + upstream layer-admin { + server localhost:3000; + } + + # api.layerapp.io에 대한 서버 블록 server { listen 80; + server_name api.layerapp.io; location / { proxy_pass http://layer-api; @@ -16,4 +22,18 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } } -} \ No newline at end of file + + # admin.api.layerapp.io에 대한 서버 블록 + server { + listen 80; + server_name admin-prod.layerapp.io; + + location / { + proxy_pass http://layer-admin; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} diff --git a/layer-batch/src/main/resources/application.yml b/layer-batch/src/main/resources/application.yml index 07de1fe2..d009e947 100644 --- a/layer-batch/src/main/resources/application.yml +++ b/layer-batch/src/main/resources/application.yml @@ -9,6 +9,9 @@ spring: username: ${AWS_PROD_DB_NAME} password: ${AWS_PROD_DB_PASSWORD} driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 5 # 최대 pool 크기 + minimum-idle: 5 # 최소 pool 크기 jpa: hibernate: ddl-auto: validate