diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f83573bb36..4e877fbbc8 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,7 +4,6 @@ on: push: branches: - develop-back - - main permissions: contents: read diff --git a/.gitignore b/.gitignore index 9bd38c3eb0..d99e0363da 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ build/ !**/src/main/**/build/ !**/src/test/**/build/ +### for android build ### +front/capstone_front/android/settings.gradle +front/capstone_front/android/app/release/ + ### STS ### .apt_generated .classpath @@ -35,4 +39,4 @@ out/ ### VS Code ### .vscode/ -.env \ No newline at end of file +.env diff --git a/README.md b/README.md index 5ee082ebcf..23522f5e16 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ -## 0. 중간발표 자료 +# 👋 외국인 유학생들을 위한 앱, 외국민 +![최종 발표자료](https://github.com/kookmin-sw/capstone-2024-30/assets/52407470/20fc41c1-8a22-4c90-a1f1-8539dea92ed1) -[https://drive.google.com/drive/folders/1qLw6-LrNG9_9Of6zh4YmYm2VoOt31NlA?usp=drive_link](https://drive.google.com/drive/folders/1qLw6-LrNG9_9Of6zh4YmYm2VoOt31NlA?usp=drive_link) +[중간발표 자료 및 보고서](https://drive.google.com/drive/folders/1qLw6-LrNG9_9Of6zh4YmYm2VoOt31NlA?usp=drive_link) + +[최종발표 자료 및 보고서](https://drive.google.com/drive/folders/1gVtsjX9nk8KhyeNu-hTzpeZjZnjj-pR8?usp=sharing)
## 1. 프로젝트 소개 -이 프로젝트는 국민대학교에서 공부하는 외국인 유학생들을 위한 종합적인 앱 서비스를 개발하는 것입니다. 앱은 학생들이 캠퍼스 생활에 빠르게 적응할 수 있도록 다양한 정보와 서비스를 제공합니다. +이 프로젝트는 국민대학교 유학생들이 겪는 언어적, 문화적 불편함을 해결하기 위한 프로젝트입니다. 이 프로젝트에서 제공하는 앱에서 유학생들이 캠퍼스 생활에 빠르게 적응할 수 있도록 다양한 정보와 서비스를 제공합니다.
@@ -18,9 +21,35 @@ This project aims to develop a comprehensive app service for international stude ## 3. 프로젝트 기능 -### 기능 1 +#### 1️⃣ 번역된 공지사항 / 학식 / 학교정보 제공 + +국민대학교에서는 공지사항, 학식, 학교정보의 번역을 잘 지원하지 않습니다. 이에따라 외국인 유학생들은 매번 번역기를 사용하여 학교에 대한 정보를 얻기 때문에 정보의 접근성이 낮습니다. + +따라서, 외국민 서비스는 설정한 언어에 맞춰서 공지사항/학식/학교정보 번역본을 제공합니다. + +#### 2️⃣ 챗봇 기능 + +국민대학교에서는 ON국민 챗봇 "쿠민이"를 서비스하고있으나, 성능이 매우 형편없습니다. 간단한 질문에도 동문서답을 하거나, 영어로 질문했는데 한글로 답변하는 등 전혀 챗봇으로서의 기능을 수행하지 못하고 있습니다. + +따라서, 외국민은 RAG와 LLM을 사용하여 국민대학교에 특화된 답변을 제공하고 다국어를 지원하는 "KuKu" 챗봇을 제공합니다. + +#### 3️⃣ 발음 교정 기능 + +많은 외국인들은 한국에 와서 언어 문제로 힘들어합니다. 특히 학교 생활을 하다보면 발표를 하거나 일상생활에서 의사소통을 해야할 때, 본인의 발음이 정확한지 확인할 방법이 없어서 힘들어합니다. + +따라서, 외국민은 자신의 발표 스크립트를 입력으로 넣어서 발음 평가를 받을 수 있을 뿐만 아니라, 한국의 일상생활에서 많이 쓰이는 여러 표현들을 연습할 수 있도록하여 한국 유학생활을 돕고자 합니다. + +#### 4️⃣ 헬퍼 매칭 기능 + +많은 외국인들이 낯선 땅에 왔을 때 도움을 받을 사람이 없어서 매우 힘들어합니다. -### 기능 2 +따라서, 외국민은 외국인들을 도울 수 있도록 헬퍼 매칭 기능을 제공합니다. 한국인 or 오랜 유학생활을 하여 한국 생활에 익숙해진 외국인 헬퍼를 구할 수 있도록 커뮤니티를 제공합니다. + +#### 5️⃣ Q&A와 FAQ 기능 + +유학생들이 한국생활에서 궁금한 것을 물어볼만한 곳이 마땅치 않고, ON국민에 있는 FAQ의 존재를 알기 쉽지 않습니다. 하지만, 이 FAQ 또한 번역을 제공하지 않고 있습니다. + +따라서, 외국민은 Q&A 게시판과 다국어로 번역된 FAQ를 제공합니다.
@@ -82,7 +111,7 @@ This project aims to develop a comprehensive app service for international stude ## 6. 기술스택 -### Frontend +### 🛠 Frontend |역할|종류| |-|-| @@ -98,13 +127,13 @@ This project aims to develop a comprehensive app service for international stude |-|-| |Framework|RED RED RED RED| |Database|RED RED| -|Programming Language|RED RED| -|API|![REST](https://img.shields.io/badge/Rest-4B3263?style=for-the-badge&logo=rest&logoColor=white) -|Test|RED RED RED| +|Programming Language|RED RED| +|Test|RED RED | |Deploy|RED RED RED RED RED RED RED| |CI/CD|RED| |ETC|RED RED RED| -
+
+ ### 📻 AI @@ -133,7 +162,12 @@ This project aims to develop a comprehensive app service for international stude ### 💻 서비스 아키택처
- + + +### 🤖 챗봇 아키텍처 + +
+ ### 📂 디렉토리 구조 @@ -155,18 +189,6 @@ This project aims to develop a comprehensive app service for international stude ## 8. 사용법 -### Backend - -`.env.example`을 바탕으로 `.env`를 작성합니다. 그 다음 - -``` -docker-compose up -d -``` - -를 통해 docker compose를 통하여 실행하시면 됩니다. 이미지는 모두 Dockerhub에 업로드 되어 있습니다. - -
- ### Frontend #### 1. 플러터 설치 @@ -195,6 +217,18 @@ flutter run
+### Backend + +`.env.example`을 바탕으로 `.env`를 작성합니다. 그 다음 + +``` +docker-compose up -d +``` + +를 통해 docker compose를 통하여 실행하시면 됩니다. 이미지는 모두 Dockerhub에 업로드 되어 있습니다. + +
+ ### AI ### Chat bot `KUKU` 소개 diff --git a/back-gateway/src/main/java/com/gateway/backgateway/filter/AuthorizationHeaderFilter.java b/back-gateway/src/main/java/com/gateway/backgateway/filter/AuthorizationHeaderFilter.java index f1e02bd99a..e7c344ac8b 100644 --- a/back-gateway/src/main/java/com/gateway/backgateway/filter/AuthorizationHeaderFilter.java +++ b/back-gateway/src/main/java/com/gateway/backgateway/filter/AuthorizationHeaderFilter.java @@ -38,7 +38,6 @@ public GatewayFilter apply(Config config) { GatewayFilter filter = (exchange, chain) -> { String requiredRole = config.getRequiredRole(); ServerHttpRequest request = exchange.getRequest(); - log.info("요청한 uri : " + request.getURI()); if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) throw JwtTokenInvalidException.INSTANCE; @@ -46,6 +45,8 @@ public GatewayFilter apply(Config config) { String token = request.getHeaders() .getFirst(HttpHeaders.AUTHORIZATION).replace("Bearer ", ""); + log.info("Authorization Token : {}", token); + if (!validateToken(token)) { throw JwtTokenInvalidException.INSTANCE; } diff --git a/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java b/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java new file mode 100644 index 0000000000..9151a76eb5 --- /dev/null +++ b/back-gateway/src/main/java/com/gateway/backgateway/filter/GlobalLoggingFilter.java @@ -0,0 +1,62 @@ +package com.gateway.backgateway.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +@Slf4j +@Configuration +public class GlobalLoggingFilter { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Bean + @Order(-1) + public GlobalFilter preLoggingFilter() { + return (exchange, chain) -> { + ServerHttpRequest request = exchange.getRequest(); + return DataBufferUtils.join(request.getBody()) + .flatMap(dataBuffer -> { + byte[] bodyBytes = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(bodyBytes); + DataBufferUtils.release(dataBuffer); + String bodyString = new String(bodyBytes, StandardCharsets.UTF_8); + String jsonBody; + try { + Object json = objectMapper.readValue(bodyString, Object.class); + jsonBody = objectMapper.writeValueAsString(json); + } catch (Exception e) { + jsonBody = bodyString; + } + + log.info("Global Filter Start: request id -> {}", request.getId()); + log.info("Request: {} {}", request.getMethod(), request.getURI()); + log.info("Request Body: {}", jsonBody); + + return chain.filter(exchange); + }); + }; + } + + @Bean + @Order(Ordered.LOWEST_PRECEDENCE) + public GlobalFilter postLoggingFilter() { + return (exchange, chain) -> { + return chain.filter(exchange).then(Mono.fromRunnable(() -> { + log.info("Response: " + exchange.getResponse().getStatusCode()); + })); + }; + } +} \ No newline at end of file diff --git a/back-gateway/src/main/resources/application.yml b/back-gateway/src/main/resources/application.yml index 1574e51d12..0e9284314a 100644 --- a/back-gateway/src/main/resources/application.yml +++ b/back-gateway/src/main/resources/application.yml @@ -8,11 +8,19 @@ spring: data: 0 + jwt: secret: key: ${JWT_SECRET} + server: port: 8081 chatbot-url: ${CHATBOT_URL} +logging: + level: + root: INFO + com: + gateway: + backgateway: DEBUG \ No newline at end of file diff --git a/back/.env.example b/back/.env.example index fe7c82491a..8bd0728ee5 100644 --- a/back/.env.example +++ b/back/.env.example @@ -1,6 +1,41 @@ # JWT Secret Key JWT_SECRET= +JWT_ACCESS_EXPIRATION_TIME= +JWT_REFRESH_EXPIRATION_TIME= # HMAC HMAC_SECRET= -HMAC_ALGORITHM= \ No newline at end of file +HMAC_ALGORITHM= + +#DeepL +DeepL_API_KEY= + +#MYSQL +DB_ENDPOINT= +DB_PORT= +DB_NAME= +MYSQL_USERNAME= +MYSQL_PASSWORD= + +#TEST +TEST_KEY= + +#Azure +Azure_API_KEY= + +##REDIS +REDIS_HOST= +REDIS_PORT= + +## S3 +S3_ACCESS_KEY= +S3_SECRET_KEY= + +## Ruby On Rails Production Key +SECRET_KEY_BASE= + +## Nginx ENV +SERVER_NAME= + +## CHAT BOT ENV +CHATBOT_URL= \ No newline at end of file diff --git a/back/README.md b/back/README.md new file mode 100644 index 0000000000..f6cc50d382 --- /dev/null +++ b/back/README.md @@ -0,0 +1,153 @@ +![image](https://github.com/kookmin-sw/capstone-2024-30/assets/55117706/be6e2f51-ade6-4453-9ba1-07ae10ec4bfa)### 기능적 고려 사항 + +### 성능적 고려 사항 + +#### 캐싱 + +유저들이 자주 접근하는 데이터는 캐싱하는 것이 좋다. +예를 들어서, 공지사항 목록이나 Q&A 목록인 경우, 자주 바뀌지도 않을텐데 유저들이 해당 게시판에 접속할 때마다 매번 RDB에서 목록을 읽어오는 것은 비효율적이다. +따라서, Redis같은 곳에 캐싱해두고, 똑같은 요청이 들어오면 빠르게 Redis에서 뽑아가도록 구성했다. +캐싱 전략은 다음과 같다. + +캐싱 전략은 Look Aside와 Write Around 전략의 조합을 택하였다. +Read 전략은 Look Aside 전략을 활용했다. 우선적으로 Redis에서 읽어오고, cache miss가 발생했을 때만, RDS에서 읽어오고 다시 redis에 저장하는 방식이다. +Write 전략은 Write Around 전략을 활용했다. 데이터를 쓸 때, Redis에 저장하지 않고, 반드시 RDB에 저장하고 기존 캐시는 무효화하는 전략이다. + +실제 캐싱을 적용하기 전과 후의 시간을 측정해보았다. + +캐싱 전에는 메서드를 실행하는데 414ms가 걸렸다. + + + +하지만 캐싱 후에는 12ms로 단축되었다. + + + +그런데 캐싱된 데이터를 받으면, class 정보에 대한 field가 json에 추가되는 현상이 있어서 추가 보류를 하였다. + +
+ +#### 비동기 처리 + +모든 요청을 동기적으로 처리하는 것은 매우 비효율적이다. 특히, 이미지를 업로드하거나, 번역 API 같은 다른 API에 요청을 보내거나, 크롤링 등은 매우 오래걸리는 작업이다. 이 모든 작업을 동기적으로 처리하면, 성능에 매우 치명적일 것이다. 그래서, 해당 기능들을 사용하는 경우에는 Async를 통한 비동기 처리를 실시하였다. + +이 비동기처리를 위해서 Spring에서 ThreadExecutor를 이용한다. 그런데, 기본 Async Executor SimpleAsyncTaskExecutor인데, 이는 비동기 작업마다 새로운 스레드를 생성한다. 이로 인해 리소스 낭비, 성능 저하, 스케일링 문제 등이 발생할 수 있다. 왜냐하면 Thread Pool 방식 Executor가 아니라서 스레드 재사용을 하지 않기 때문이다. 그래서 실행시간이 짧은 많은 량의 Task를 처리할 때 불리하다. 리소스 측면에서는 각 비동기 작업마다 새로운 스레드를 생성하므로, 동시에 많은 비동기 작업이 요청되면 매번 많은 스레드가 생성된다. 따라서 CPU와 메모리 리소스의 사용량이 과도하게 증가하는 문제가 발생할 수 있다. 성능 저하 면에서는 스레드를 생성하고 소멸시키는 데는 많은 시간과 리소스가 소요된다. 각 작업마다 스레드를 생성하면 이런 오버헤드가 계속 발생하게 되고, 전체적인 시스템 성능에 영향을 끼칠 수 있다. 또한, SimpleAsyncTaskExecutor는 스레드 수에 대한 제한이 없다. 따라서 동시에 많은 요청이 들어올 경우 스레드 수가 무한정으로 제어할 수 없는 수준으로 증가할 수 있으며, 이는 곧 OutOfMemoryError 등의 문제를 일으킬 수 있다. + + + +그래서, 위 사진과 같이 Async의 Executor를 ThreadPool방식의 Executor로 설정했다. 이는, 사용할 스레드 풀에 속한 기본 스레드 수인 corepoolsize, corepoolsize가 가득 찬 상태에서 더이상 추가 처리가 불가능 할때, 대기하는 장소 크기인 queuecapacity, 스레드 풀이 확장될 수 있는 스레드의 상한선, 즉 스레드 수의 상한선인 maxpoolsize를 조정하여 ThreadPool방식의 Async Executor를 구성했다. + +#### Race Condition 해결 + +#### N+1 문제 + +
+ +### 안정성 고려 사항 + +
+ +#### 사용자 트래픽 제한 + + + + + +실제로 위에 사진처럼 + + +#### 사용자 업로드 파일 용량 제한 + +
+ +### 보안적 고려 사항 + +
+ +#### 사용자 비밀번호 암호화 + +#### JWT를 통한 Authentication + +#### Token 탈취 상황 예방 + +#### AccessToken Ban + +#### HMAC + +#### 환경변수 적용 + +
+ +### 테스트 고려 사항 + +#### Test Container + +#### Jacoco + +#### Apache Jmeter + +
+ +### 현실적 제한 및 개선 필요 사항 + +
+ +#### 검색 기능 개선 + +현재 게시물 및 공지사항 검색은 제목으로만 검색된다. 하지만, 이보다 제목 + 내용으로 검색이 가능한 것이 더 좋을 것이다. 이를 위해서 SQL Like 연산자로 내용까지 검색이 가능하긴 하나, Like 연산자는 선형 연산자이기 때문에 성능에 매우 치명적이다. 따라서, ElasticSearch를 이용해서 빠르게 검색하는 것이 더 좋을 것이다. ElasticSearch는 분산 검색 및 분석 엔진으로, 대규모 데이터에서 빠른 전체 텍스트 검색을 지원한다. 추후, ElasticSearch를 통해 우리 RDB에 저장된 글들을 역색인하여 거의 실시간에 가깝게 검색하도록 개선할 것이다. + +
+ +#### 인스턴스 성능의 한계 + + + +현재 우리서버는 Free Tier를 사용하여 견딜 수 있는 트래픽의 한계가 있는 상황이다. 이를 해결하기 위해서 수직적 확장 또는 수평적 확장을 고려할 수 있다. 현재 Free Tier 성능이 아닌 더 좋은 EC2 인스턴스를 사용하거나, 여러개의 EC2를 만들어 Load Balancing을 적용하여 Scale Out적인 확장을 할 수도 있다. 하지만, 현재는 비용적인 문제로 불가능한 상황이다. + +
+ +#### 무중단 배포 + +현재 우리 서비스는 Git Actions를 이용한 CI/CD를 적용하고 있다. 하지만, 현재 방식은 새 버전으로 서버가 배포될 때 반드시 1~2분정도 서버가 중단될 수 밖에 없다. 이는, 실제 서비스할 때 매우 치명적이다. 따라서, 무중단 배포가 반드시 필요하다. + +
+ + + +
+ +무중단 배포 방식으로는 Rolling, Canary, Blue-Green 배포 방식 등 여러가지 방법이 있지만, 만약 우리가 무중단 배포를 구축한다면 Blue-Green 배포 방식을 적용할 것이다. Blue-Green이란 두 개의 동일한 환경인 "블루"와 "그린"을 사용하여 새로운 버전의 소프트웨어를 배포하고 롤백하는 방식이다. 블루 그린 배포 방식은 가장 많이 쓰이는 무중단 배포 방식이며, 카나리에 비해서 구현도 간단하고, Rolling 방식보다 훨씬 안정적이다. 따라서, 추후 Blue-Green 무중단 배포 환경을 구축할 계획이다. + +
+ +#### 로드 밸런싱 + +서버를 단일로 구성하면, 많은 트래픽이 몰렸을 때 감당하기 힘들 것이다. 따라서, 서버를 여러개 만들고, Load Balacning하는게 좋은 선택지일 것이다. 하지만, 로드 밸런싱을 하기엔 비용적인 문제 때문에 환경 구축이 어렵다. 왜냐하면, 여러개의 EC2를 띄워야되기 때문이다. 그래서, 어쩔 수 없이 단일 서버로 운영하게 되었지만, 추후 사용자가 늘어나면 로드 밸런싱을 적용할 계획이다. + +
+ +#### 캐싱 + +이미지를 받아오는 S3도 캐싱을 무조건 해야한다. 아니면 S3 비용이 굉장히 많아질 것이고 응답시간이 길어진다. 일반적으로 AWS CloudFront같은 CDN 서버를 두는 경우가 많은데, 이 부분은 시간이 부족해서 하지 못했다. + +
+ +#### 모니터링 및 로깅 강화 + + + +우리 서비스의 전반적인 로깅을 하는 것도 중요하다. 현재 로깅은 Nginx 단에서 파일 저장으로 한번, API Gateway단에서 요청 url, body, Authorization으로 한번, 각 MSA 서버 단에서 한번 이루어진다. 하지만, 애플리케이션 단에서의 로그는 파일로 저장하고 있지 않아 우리가 직접 컨테이너에 접근해서 하나하나 찾아봐야한다. 또한, MSA 구조다 보니깐, 각각 컨테이너에 직접 접근해서 봐야한다는 문제점이 있다. 하지만, 이런식으로 진행하면 애플리케이션 로그를 검색하고 파악하는데 시간을 더 써서 서비스에 매우 치명적이다. 따라서, Kafka + ELK Stack을 통하여 비동기적으로 로그 데이터를 ELK Stack으로 전송하고, 이를 분석한 후, 대시보드 형태의 시각적인 데이터로 바꿔주는 시스템이 구축되어야 할 것이다. + + + +또한, 서비스를 운영할 때 모니터링 및 성능 관리는 매우 중요하다. 우리가 24시간 365일 컴퓨터 앞에 상주하여 서버 모니터링을 할 수 없기 때문이다. 따라서, Prometheus + Grafana나 Datadog 등을 이용하여 우리 서비스를 모니터링 하는 환경은 필수적이다. 이런 환경을 구축해두면 서버의 CPU나 메모리 사용량이 급증 했을 때, 우리 팀 Slack으로 결과를 전송하고 대시보드로 현황을 쉽게 현황을 파악할 수 있을 것이다. + +
+ +#### 컨테이너 오케스트라제이션 + + + +현재 우리 서비스는 MSA 구조에 가까운 형태여서 수많은 Container들이 돌아가고 있다. 돌아가는 Container만 세보더라도, Redis, Spring, Spring Cloud Gateway, Ruby On Rails, Nginx, Certbot, FastAPI 벌써 7가지가 있다. 그런데 여기에 앞서 언급한 Kafka, Grafana, Prometheus, DB Replication, ELK Stack 등 까지 적용하면 수많은 컨테이너가 돌아갈 것이다. 거기에 로드 밸린성까지 적용한다면 우리가 이 컨테이너들을 한번에 관리하는 것은 무리일 것이다. 그래서, EKS, Docker Swarm, Kubernetes같은 자동으로 컨테이너 장애 복구를 도와주는 컨테이너 오케스트라제이션이 필요하다. + + diff --git a/back/docker-compose.yml b/back/docker-compose.yml index 62aee99004..69fc6e0cc6 100644 --- a/back/docker-compose.yml +++ b/back/docker-compose.yml @@ -1,5 +1,4 @@ ## Test Version - version: '3' networks: backend_network: diff --git a/back/env.sh b/back/env.sh deleted file mode 100644 index 41cba56fb7..0000000000 --- a/back/env.sh +++ /dev/null @@ -1,6 +0,0 @@ -ENV_FILE=".env" - -if test -f "$ENV_FILE"; then - export "$(grep -v '^#' .env | xargs -d '\n')" -fi -echo "DONE" \ No newline at end of file diff --git a/back/nginx/tool/encrypt.sh b/back/nginx/tool/encrypt.sh new file mode 100644 index 0000000000..c748e38e79 --- /dev/null +++ b/back/nginx/tool/encrypt.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +if ! [ -x "$(command -v docker-compose)" ]; then + echo 'Error: docker-compose is not installed.' >&2 + exit 1 +fi + +domains={SERVER_NAME} +rsa_key_size=4096 +data_path="./data/certbot" +email="" # Adding a valid address is strongly recommended +staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits + +if [ -d "$data_path" ]; then + read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision + if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then + exit + fi +fi + + +if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then + echo "### Downloading recommended TLS parameters ..." + mkdir -p "$data_path/conf" + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf" + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem" + echo +fi + +echo "### Creating dummy certificate for $domains ..." +path="/etc/letsencrypt/live/$domains" +mkdir -p "$data_path/conf/live/$domains" +sudo docker compose run --rm --entrypoint "\ + openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\ + -keyout '$path/privkey.pem' \ + -out '$path/fullchain.pem' \ + -subj '/CN=localhost'" certbot +echo + + +echo "### Starting nginx ..." +sudo docker compose up --force-recreate -d nginx +echo + +echo "### Deleting dummy certificate for $domains ..." +sudo docker compose run --rm --entrypoint "\ + rm -Rf /etc/letsencrypt/live/$domains && \ + rm -Rf /etc/letsencrypt/archive/$domains && \ + rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot +echo + + +echo "### Requesting Let's Encrypt certificate for $domains ..." +#Join $domains to -d args +domain_args="" +for domain in "${domains[@]}"; do + domain_args="$domain_args -d $domain" +done + +# Select appropriate email arg +case "$email" in + "") email_arg="--register-unsafely-without-email" ;; + *) email_arg="--email $email" ;; +esac + +# Enable staging mode if needed +if [ $staging != "0" ]; then staging_arg="--staging"; fi + +sudo docker compose run --rm --entrypoint "\ + certbot certonly --webroot -w /var/www/certbot \ + $staging_arg \ + $email_arg \ + $domain_args \ + --rsa-key-size $rsa_key_size \ + --agree-tos \ + --force-renewal" certbot +echo + +echo "### Reloading nginx ..." +sudo docker compose exec nginx nginx -s reload \ No newline at end of file diff --git a/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCallerService.java b/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCallerService.java index 7b597f1989..ba98207686 100644 --- a/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCallerService.java +++ b/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCallerService.java @@ -15,7 +15,7 @@ public class AnnouncementCallerService { private final AnnouncementCrawlService announcementCrawlService; - @Scheduled(cron = "0 0 0 * * *") + @Scheduled(cron = "0 0 12,18 * * *") public void crawlingAnnouncement() { announcementCrawlService.crawlKookminAnnouncement(KOOKMIN_OFFICIAL); announcementCrawlService.crawlInternationlAnnouncement(INTERNATIONAL_ACADEMIC); diff --git a/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCrawlService.java b/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCrawlService.java index 7abed22946..23279ae631 100644 --- a/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCrawlService.java +++ b/back/src/main/java/com/example/capstone/domain/announcement/service/AnnouncementCrawlService.java @@ -55,7 +55,7 @@ public void crawlKookminAnnouncement(AnnouncementUrl url) { String dateStr = announcement.select(".board_etc > span").first().text(); LocalDate noticeDate = LocalDate.parse(dateStr, formatter); - if (noticeDate.equals(currentDate) || noticeDate.isEqual(currentDate.minusDays(1))) { + if (noticeDate.equals(currentDate)) { String href = announcement.attr("href"); links.add(href); } @@ -251,7 +251,7 @@ public void crawlSoftwareAnnouncement(AnnouncementUrl url) { String dateStr = announcement.select(".date").text(); LocalDate noticeDate = LocalDate.parse(dateStr, formatter); - if (noticeDate.equals(currentDate) || noticeDate.isAfter(currentDate.minusDays(4))) { + if (noticeDate.equals(currentDate)) { String href = announcement.select(".subject > a").attr("href"); links.add(href.substring(1, href.length())); } diff --git a/back/src/main/java/com/example/capstone/global/config/AsyncConfig.java b/back/src/main/java/com/example/capstone/global/config/AsyncConfig.java index 7a6fa2e329..289601e0ac 100644 --- a/back/src/main/java/com/example/capstone/global/config/AsyncConfig.java +++ b/back/src/main/java/com/example/capstone/global/config/AsyncConfig.java @@ -1,6 +1,5 @@ package com.example.capstone.global.config; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; diff --git a/front/capstone_front/android/app/src/main/res/drawable-hdpi/splash.png b/front/capstone_front/android/app/src/main/res/drawable-hdpi/splash.png new file mode 100644 index 0000000000..6322b5c784 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable-hdpi/splash.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable-mdpi/splash.png b/front/capstone_front/android/app/src/main/res/drawable-mdpi/splash.png new file mode 100644 index 0000000000..88d947eafe Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable-mdpi/splash.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable-v21/background.png b/front/capstone_front/android/app/src/main/res/drawable-v21/background.png new file mode 100644 index 0000000000..3107d37fa5 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable-v21/background.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable-v21/launch_background.xml b/front/capstone_front/android/app/src/main/res/drawable-v21/launch_background.xml index f74085f3f6..3cc4948a14 100644 --- a/front/capstone_front/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/front/capstone_front/android/app/src/main/res/drawable-v21/launch_background.xml @@ -1,12 +1,9 @@ - - - - - + + + + + + diff --git a/front/capstone_front/android/app/src/main/res/drawable-xhdpi/splash.png b/front/capstone_front/android/app/src/main/res/drawable-xhdpi/splash.png new file mode 100644 index 0000000000..eab34b2e24 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable-xhdpi/splash.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable-xxhdpi/splash.png b/front/capstone_front/android/app/src/main/res/drawable-xxhdpi/splash.png new file mode 100644 index 0000000000..53de0d97f3 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable-xxhdpi/splash.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable-xxxhdpi/splash.png b/front/capstone_front/android/app/src/main/res/drawable-xxxhdpi/splash.png new file mode 100644 index 0000000000..8989ec4e9f Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable-xxxhdpi/splash.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable/background.png b/front/capstone_front/android/app/src/main/res/drawable/background.png new file mode 100644 index 0000000000..3107d37fa5 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/drawable/background.png differ diff --git a/front/capstone_front/android/app/src/main/res/drawable/launch_background.xml b/front/capstone_front/android/app/src/main/res/drawable/launch_background.xml index 304732f884..3cc4948a14 100644 --- a/front/capstone_front/android/app/src/main/res/drawable/launch_background.xml +++ b/front/capstone_front/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,9 @@ - - - - - + + + + + + diff --git a/front/capstone_front/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/front/capstone_front/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..90f958096f --- /dev/null +++ b/front/capstone_front/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cc6e33219d..3368e979fd 100644 Binary files a/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000..a7b38501a0 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000..6d942e39c7 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 6765be7ad2..5b0153923b 100644 Binary files a/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000..904f271dd2 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000..58365d1888 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 51d8332f76..ad19733f03 100644 Binary files a/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000..95a6b1f90c Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000..373ab8ff54 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index a379f5916c..61ea632a44 100644 Binary files a/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000..2b4543f696 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000..118c0eab17 Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 77e05b900b..1bd868a479 100644 Binary files a/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000..b28ec2131d Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png differ diff --git a/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000..77bab08b0f Binary files /dev/null and b/front/capstone_front/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png differ diff --git a/front/capstone_front/android/app/src/main/res/values-night-v31/styles.xml b/front/capstone_front/android/app/src/main/res/values-night-v31/styles.xml new file mode 100644 index 0000000000..ca56ad1527 --- /dev/null +++ b/front/capstone_front/android/app/src/main/res/values-night-v31/styles.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/front/capstone_front/android/app/src/main/res/values-night/styles.xml b/front/capstone_front/android/app/src/main/res/values-night/styles.xml index 06952be745..dbc9ea9f1b 100644 --- a/front/capstone_front/android/app/src/main/res/values-night/styles.xml +++ b/front/capstone_front/android/app/src/main/res/values-night/styles.xml @@ -5,6 +5,10 @@ @drawable/launch_background + false + false + false + shortEdges + + + + diff --git a/front/capstone_front/android/app/src/main/res/values/styles.xml b/front/capstone_front/android/app/src/main/res/values/styles.xml index cb1ef88056..0d1fa8fce8 100644 --- a/front/capstone_front/android/app/src/main/res/values/styles.xml +++ b/front/capstone_front/android/app/src/main/res/values/styles.xml @@ -5,6 +5,10 @@ @drawable/launch_background + false + false + false + shortEdges - + capstone_front @@ -37,9 +35,109 @@ const serviceWorkerVersion = null; - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + \ No newline at end of file diff --git a/front/capstone_front/web/splash/img/dark-1x.png b/front/capstone_front/web/splash/img/dark-1x.png new file mode 100644 index 0000000000..88d947eafe Binary files /dev/null and b/front/capstone_front/web/splash/img/dark-1x.png differ diff --git a/front/capstone_front/web/splash/img/dark-2x.png b/front/capstone_front/web/splash/img/dark-2x.png new file mode 100644 index 0000000000..eab34b2e24 Binary files /dev/null and b/front/capstone_front/web/splash/img/dark-2x.png differ diff --git a/front/capstone_front/web/splash/img/dark-3x.png b/front/capstone_front/web/splash/img/dark-3x.png new file mode 100644 index 0000000000..53de0d97f3 Binary files /dev/null and b/front/capstone_front/web/splash/img/dark-3x.png differ diff --git a/front/capstone_front/web/splash/img/dark-4x.png b/front/capstone_front/web/splash/img/dark-4x.png new file mode 100644 index 0000000000..8989ec4e9f Binary files /dev/null and b/front/capstone_front/web/splash/img/dark-4x.png differ diff --git a/front/capstone_front/web/splash/img/light-1x.png b/front/capstone_front/web/splash/img/light-1x.png new file mode 100644 index 0000000000..88d947eafe Binary files /dev/null and b/front/capstone_front/web/splash/img/light-1x.png differ diff --git a/front/capstone_front/web/splash/img/light-2x.png b/front/capstone_front/web/splash/img/light-2x.png new file mode 100644 index 0000000000..eab34b2e24 Binary files /dev/null and b/front/capstone_front/web/splash/img/light-2x.png differ diff --git a/front/capstone_front/web/splash/img/light-3x.png b/front/capstone_front/web/splash/img/light-3x.png new file mode 100644 index 0000000000..53de0d97f3 Binary files /dev/null and b/front/capstone_front/web/splash/img/light-3x.png differ diff --git a/front/capstone_front/web/splash/img/light-4x.png b/front/capstone_front/web/splash/img/light-4x.png new file mode 100644 index 0000000000..8989ec4e9f Binary files /dev/null and b/front/capstone_front/web/splash/img/light-4x.png differ