diff --git a/ServerlessFunction/gradlew b/ServerlessFunction/gradlew index adff685a..fcb6fca1 100755 --- a/ServerlessFunction/gradlew +++ b/ServerlessFunction/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015 the original authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# SPDX-License-Identifier: Apache-2.0 -# ############################################################################## # @@ -57,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -85,8 +83,7 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,6 +111,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -146,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -154,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -171,6 +169,7 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -202,15 +201,16 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. diff --git a/ServerlessFunction/gradlew.bat b/ServerlessFunction/gradlew.bat index c4bdd3ab..93e3f59f 100644 --- a/ServerlessFunction/gradlew.bat +++ b/ServerlessFunction/gradlew.bat @@ -13,8 +13,6 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -45,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. goto fail @@ -59,21 +57,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. goto fail :execute @rem Setup the command line +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/websocket/SpeakingHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java similarity index 97% rename from ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/websocket/SpeakingHandler.java rename to ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java index c515f950..90e041a7 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/websocket/SpeakingHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/speaking/handler/SpeakingHandler.java @@ -1,4 +1,4 @@ -package com.mzc.secondproject.serverless.domain.speaking.handler.websocket; +package com.mzc.secondproject.serverless.domain.speaking.handler; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; @@ -9,6 +9,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mzc.secondproject.serverless.common.util.JwtUtil; +import com.mzc.secondproject.serverless.domain.speaking.dto.response.SpeakingResponse; import com.mzc.secondproject.serverless.domain.speaking.service.SpeakingService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -102,7 +103,7 @@ private APIGatewayProxyResponseEvent handleChat(String userId, String body) { String audio = request.has("audio") ? request.get("audio").getAsString() : null; String text = request.has("text") ? request.get("text").getAsString() : null; - SpeakingService.SpeakingResponse result; + SpeakingResponse result; if (audio != null && !audio.isEmpty()) { // 음성 입력 처리 @@ -134,7 +135,7 @@ private APIGatewayProxyResponseEvent handleReset(String userId, String body) { } JsonObject request = JsonParser.parseString(body).getAsJsonObject(); - String sessionId = request.has("sessionId") ? request.get("sessionId").getAsString() : null; + String sessionId = getStringOrNull(request, "sessionId"); if (sessionId == null || sessionId.isEmpty()) { return response(400, Map.of("error", "sessionId is required")); @@ -176,4 +177,6 @@ private APIGatewayProxyResponseEvent response(int statusCode, Map parseHistory(String historyJson) { return history; } + /** * 히스토리 JSON 변환 */ @@ -328,18 +330,9 @@ private String toJson(List history) { return array.toString(); } - // ==================== Inner Classes ==================== - - private record Message(String role, String content) {} - /** - * Speaking 응답 DTO + * 대화 메시지 (히스토리용) */ - public record SpeakingResponse( - String sessionId, // 세션 ID (다음 요청에 사용) - String userTranscript, // 사용자가 말한 내용 (STT 결과) - String aiText, // AI 응답 텍스트 - String aiAudioUrl, // AI 응답 음성 URL (Polly) - double confidence // STT 신뢰도comp - ) {} + private record Message(String role, String content) {} + } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/dto/response/ProfileResponse.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/dto/response/ProfileResponse.java index bdc1ced7..7f17bd4a 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/dto/response/ProfileResponse.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/dto/response/ProfileResponse.java @@ -29,7 +29,7 @@ public static ProfileResponse from(User user) { .email(user.getEmail()) .nickname(user.getNickname()) .level(user.getLevel()) - .profileUrl(user.getProfileUrl()) + .profileUrl(user.getProfileUrlForResponse()) .createdAt(user.getCreatedAt()) .updatedAt(user.getUpdatedAt()) .build(); diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/handler/UserHandler.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/handler/UserHandler.java index b4fc9aea..9ad8618f 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/handler/UserHandler.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/handler/UserHandler.java @@ -59,9 +59,20 @@ private APIGatewayProxyResponseEvent getMyProfile( APIGatewayProxyRequestEvent request, String userId // cognitoSub ) { - User user = userService.getProfile(userId, request); - ProfileResponse response = ProfileResponse.from(user); + + // profileUrl을 Presigned URL로 변환 + String presignedUrl = userService.getPresignedProfileUrl(user.getProfileUrl()); + + ProfileResponse response = ProfileResponse.builder() + .userId(user.getCognitoSub()) + .email(user.getEmail()) + .nickname(user.getNickname()) + .level(user.getLevel()) + .profileUrl(presignedUrl) // Presigned URL 사용 + .createdAt(user.getCreatedAt()) + .updatedAt(user.getUpdatedAt()) + .build(); return ResponseGenerator.ok(user.getNickname() + " 환영합니다!", response); } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/model/User.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/model/User.java index 344d77e4..60dc6be5 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/model/User.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/model/User.java @@ -27,11 +27,13 @@ public class User { private String nickname; private String level; private String profileUrl; + private String profileUrlForResponse; private String createdAt; private String updatedAt; private String lastLoginAt; private Long ttl; - + + /** * 신규 사용자 생성 * - Lazy Registration 적용: 최초 프로필 조회 시 DynamoDB에 저장 @@ -114,7 +116,12 @@ public void updateProfileUrl(String newProfileUrl) { this.profileUrl = newProfileUrl; this.updatedAt = Instant.now().toString(); } - + + @DynamoDbIgnore + public String getProfileUrlForResponse() { + return profileUrlForResponse != null ? profileUrlForResponse : profileUrl; + } + public void updateLastLoginAt() { this.lastLoginAt = Instant.now().toString(); } diff --git a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java index 2421f118..6783c42b 100644 --- a/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java +++ b/ServerlessFunction/src/main/java/com/mzc/secondproject/serverless/domain/user/service/UserService.java @@ -7,8 +7,10 @@ import com.mzc.secondproject.serverless.domain.user.repository.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; @@ -52,18 +54,53 @@ public UserService(UserRepository userRepository) { * @return User 객체 */ public User getProfile(String userId, APIGatewayProxyRequestEvent request) { - - return userRepository.findByCognitoSub(userId) - .map(user -> { - // 정상 DB에서 조회 완료 - user.updateLastLoginAt(); - userRepository.update(user); - return user; + + User user = userRepository.findByCognitoSub(userId) + .map(u -> { + u.updateLastLoginAt(); + userRepository.update(u); + return u; }) - .orElseGet(() -> { - // PostConfirmation 실패 대비 fallback - return createUserFromRequest(userId, request); - }); + .orElseGet(() -> createUserFromRequest(userId, request)); + + // 프로필 URL을 Presigned URL로 변환 + String presignedProfileUrl = getPresignedProfileUrl(user.getProfileUrl()); + user.setProfileUrlForResponse(presignedProfileUrl); // 응답용으로만 설정 + + return user; + } + + public String getPresignedProfileUrl(String s3Url) { + if (s3Url == null || s3Url.isEmpty()) { + return generateGetPresignedUrl("profile/default.png"); + } + String key = extractKeyFromS3Url(s3Url); + return generateGetPresignedUrl(key); + } + + private String generateGetPresignedUrl(String imageKey) { + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(BUCKET_NAME) + .key(imageKey) + .build(); + + GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() + .signatureDuration(Duration.ofHours(24)) + .getObjectRequest(getObjectRequest) + .build(); + + return s3Presigner.presignGetObject(presignRequest).url().toString(); + } + + + private String extractKeyFromS3Url(String s3Url) { + // https://group2-englishstudy.s3.amazonaws.com/profile/user123/img.png + // → profile/user123/img.png + String prefix = String.format("https://%s.s3.amazonaws.com/", BUCKET_NAME); + if (s3Url.startsWith(prefix)) { + return s3Url.substring(prefix.length()); + } + return s3Url; } /** diff --git a/ServerlessFunction/template.yaml b/ServerlessFunction/template.yaml index 97fe03e6..0f64417e 100644 --- a/ServerlessFunction/template.yaml +++ b/ServerlessFunction/template.yaml @@ -2,25 +2,6 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Group2 English Study - Unified API (Chatting + Vocabulary) -Parameters: - Environment: - Type: String - Default: dev - AllowedValues: - - dev - - test - - prod - Description: Deployment environment - - ExistingCognitoUserPoolId: - Type: String - Default: "" - Description: Existing Cognito User Pool ID (leave empty to create new) - - ExistingCognitoClientId: - Type: String - Description: Existing Cognito User Pool Client ID - Globals: Function: Timeout: 30 @@ -35,13 +16,11 @@ Globals: CHAT_TABLE_NAME: !Ref ChatTable VOCAB_TABLE_NAME: !Ref VocabTable OPIC_TABLE_NAME: !Ref OPIcTable - NEWS_TABLE_NAME: !Ref NewsTable - BUCKET_NAME: !Sub "${AWS::StackName}" - CHAT_BUCKET_NAME: !Sub "${AWS::StackName}" - VOCAB_BUCKET_NAME: !Sub "${AWS::StackName}" - PROFILE_BUCKET_NAME: !Sub "${AWS::StackName}" - OPIC_BUCKET_NAME: !Sub "${AWS::StackName}" - NEWS_BUCKET_NAME: !Sub "${AWS::StackName}" + BUCKET_NAME: group2-englishstudy + CHAT_BUCKET_NAME: group2-englishstudy + VOCAB_BUCKET_NAME: group2-englishstudy + PROFILE_BUCKET_NAME: group2-englishstudy + OPIC_BUCKET_NAME: group2-englishstudy AWS_REGION_NAME: !Ref AWS::Region ROOM_TOKEN_TTL_SECONDS: "300" TRANSCRIBE_PROXY_URL: "https://tfo1zm7vec.execute-api.ap-northeast-2.amazonaws.com/prod/transcribe" @@ -50,10 +29,65 @@ Globals: Resources: ############################################# - # Cognito - Using Existing User Pool - # (Cognito resources are managed in group2-englishstudy-chatting stack) + # Cognito User Pool ############################################# + CognitoUserPool: + Type: AWS::Cognito::UserPool + DeletionPolicy: Retain + # UpdateReplacePolicy: Retain + Properties: + UserPoolName: !Sub "${AWS::StackName}-userpool" + UsernameAttributes: + - email + AutoVerifiedAttributes: + - email + Policies: + PasswordPolicy: + MinimumLength: 8 + RequireLowercase: true + RequireNumbers: true + RequireSymbols: true + RequireUppercase: false + # Cognito에 저장할 사용자 정보 정의 ≈ 회원 테이블 컬럼 + Schema: + - Name: email + AttributeDataType: String + Required: true + Mutable: true + - Name: nickname + AttributeDataType: String + Required: false + Mutable: true + - Name: level + AttributeDataType: String + Required: false + Mutable: true + - Name: profileUrl + AttributeDataType: String + Required: false + Mutable: true + LambdaConfig: + PreSignUp: !GetAtt PreSignUpFunction.Arn + PostConfirmation: !GetAtt PostConfirmationFunction.Arn + + # Cognito에게 Lambda 호출 권한 부여 + PreSignUpPermission: + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !Ref PreSignUpFunction + Principal: cognito-idp.amazonaws.com + SourceArn: !Sub arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/* + + PostConfirmationPermission: + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !GetAtt PostConfirmationFunction.Arn + Principal: cognito-idp.amazonaws.com + SourceArn: !GetAtt CognitoUserPool.Arn + # 사용자 custom 속성들 기본값 설정 Lambda 함수 PreSignUpFunction: Type: AWS::Serverless::Function @@ -66,7 +100,7 @@ Resources: Timeout: 10 Environment: Variables: - DEFAULT_PROFILE_URL: !Sub "https://${AWS::StackName}.s3.amazonaws.com/profile/default.png" + DEFAULT_PROFILE_URL: https://group2-englishstudy.s3.amazonaws.com/profile/default.png # 회원가입 시점에 사용자 모든 정보가 DB에 저장 Lambda 함수 PostConfirmationFunction: @@ -84,6 +118,18 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref UserTable + CognitoUserPoolClient: + Type: AWS::Cognito::UserPoolClient + Properties: + ClientName: !Sub "${AWS::StackName}-client" + UserPoolId: !Ref CognitoUserPool + GenerateSecret: false + ExplicitAuthFlows: + - ALLOW_USER_SRP_AUTH + - ALLOW_REFRESH_TOKEN_AUTH + - ALLOW_USER_PASSWORD_AUTH + PreventUserExistenceErrors: ENABLED + ############################################# # API Gateway (Unified) ############################################# @@ -91,11 +137,11 @@ Resources: MainApi: Type: AWS::Serverless::Api Properties: - Name: !Sub "${AWS::StackName}-api" - StageName: !Ref Environment + Name: group2-englishstudy-api + StageName: dev Cors: - AllowMethods: "'GET,POST,PUT,PATCH,DELETE,OPTIONS'" - AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Requested-With,Accept'" + AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'" + AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key'" AllowOrigin: "'*'" AllowCredentials: false GatewayResponses: @@ -105,7 +151,7 @@ Resources: Headers: Access-Control-Allow-Origin: "'*'" Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key'" - Access-Control-Allow-Methods: "'GET,POST,PUT,PATCH,DELETE,OPTIONS'" + Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'" ResponseTemplates: application/json: '{"message": "Unauthorized", "statusCode": 401}' ACCESS_DENIED: @@ -114,7 +160,7 @@ Resources: Headers: Access-Control-Allow-Origin: "'*'" Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key'" - Access-Control-Allow-Methods: "'GET,POST,PUT,PATCH,DELETE,OPTIONS'" + Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'" ResponseTemplates: application/json: '{"message": "Access Denied", "statusCode": 403}' DEFAULT_4XX: @@ -122,28 +168,27 @@ Resources: Headers: Access-Control-Allow-Origin: "'*'" Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key'" - Access-Control-Allow-Methods: "'GET,POST,PUT,PATCH,DELETE,OPTIONS'" + Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'" DEFAULT_5XX: ResponseParameters: Headers: Access-Control-Allow-Origin: "'*'" Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key'" - Access-Control-Allow-Methods: "'GET,POST,PUT,PATCH,DELETE,OPTIONS'" + Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'" EXPIRED_TOKEN: StatusCode: 401 ResponseParameters: Headers: Access-Control-Allow-Origin: "'*'" Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key'" - Access-Control-Allow-Methods: "'GET,POST,PUT,PATCH,DELETE,OPTIONS'" + Access-Control-Allow-Methods: "'GET,POST,PUT,DELETE,OPTIONS'" ResponseTemplates: application/json: '{"message": "Token expired", "statusCode": 401}' Auth: DefaultAuthorizer: CognitoAuthorizer - AddDefaultAuthorizerToCorsPreflight: false Authorizers: CognitoAuthorizer: - UserPoolArn: !Sub "arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${ExistingCognitoUserPoolId}" + UserPoolArn: !GetAtt CognitoUserPool.Arn Identity: Header: Authorization @@ -154,7 +199,7 @@ Resources: WebSocketApi: Type: AWS::ApiGatewayV2::Api Properties: - Name: !Sub "${AWS::StackName}-websocket" + Name: group2-englishstudy-websocket ProtocolType: WEBSOCKET RouteSelectionExpression: "$request.body.action" @@ -162,7 +207,7 @@ Resources: Type: AWS::ApiGatewayV2::Stage Properties: ApiId: !Ref WebSocketApi - StageName: !Ref Environment + StageName: dev AutoDeploy: true # WebSocket Connect Route @@ -220,7 +265,7 @@ Resources: WebSocketConnectFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-ws-connect" + FunctionName: group2-englishstudy-ws-connect CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.websocket.WebSocketConnectHandler::handleRequest Description: Handle WebSocket $connect @@ -229,7 +274,7 @@ Resources: Environment: Variables: WEBSOCKET_CONNECTION_TTL_SECONDS: "600" - WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -250,7 +295,7 @@ Resources: WebSocketDisconnectFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-ws-disconnect" + FunctionName: group2-englishstudy-ws-disconnect CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.websocket.WebSocketDisconnectHandler::handleRequest Description: Handle WebSocket $disconnect @@ -258,7 +303,7 @@ Resources: ApplyOn: PublishedVersions Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -279,7 +324,7 @@ Resources: WebSocketMessageFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-ws-message" + FunctionName: group2-englishstudy-ws-message CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.websocket.WebSocketMessageHandler::handleRequest Description: Handle WebSocket sendMessage @@ -287,7 +332,7 @@ Resources: ApplyOn: PublishedVersions Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" GAME_AUTO_CLOSE_LAMBDA_ARN: !GetAtt GameAutoCloseFunction.Arn SCHEDULER_ROLE_ARN: !GetAtt GameSchedulerRole.Arn Policies: @@ -307,7 +352,7 @@ Resources: - scheduler:CreateSchedule - scheduler:DeleteSchedule - scheduler:GetSchedule - Resource: !Sub "arn:aws:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/${AWS::StackName}-game-auto-close/*" + Resource: !Sub "arn:aws:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/game-auto-close/*" - Statement: - Effect: Allow Action: @@ -339,7 +384,7 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref UserTable - S3CrudPolicy: - BucketName: !Sub "${AWS::StackName}" + BucketName: group2-englishstudy Events: GetMyProfile: Type: Api @@ -367,7 +412,7 @@ Resources: ChatRoomFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-chat-room-handler" + FunctionName: group2-englishstudy-chat-room-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.ChatRoomHandler::handleRequest Description: Handle chat room CRUD operations @@ -375,7 +420,7 @@ Resources: ApplyOn: PublishedVersions Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -439,7 +484,7 @@ Resources: GameFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-game-handler" + FunctionName: group2-englishstudy-game-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.GameHandler::handleRequest Description: Handle catch-mind game operations @@ -447,7 +492,7 @@ Resources: ApplyOn: PublishedVersions Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" GAME_AUTO_CLOSE_LAMBDA_ARN: !GetAtt GameAutoCloseFunction.Arn SCHEDULER_ROLE_ARN: !GetAtt GameSchedulerRole.Arn Policies: @@ -467,7 +512,7 @@ Resources: - scheduler:CreateSchedule - scheduler:DeleteSchedule - scheduler:GetSchedule - Resource: !Sub "arn:aws:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/${AWS::StackName}-game-auto-close/*" + Resource: !Sub "arn:aws:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/game-auto-close/*" - Statement: - Effect: Allow Action: @@ -511,7 +556,7 @@ Resources: GameAutoCloseFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-game-auto-close" + FunctionName: group2-englishstudy-game-auto-close CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.GameAutoCloseHandler::handleRequest Description: Auto-close game after 7 minutes @@ -521,7 +566,7 @@ Resources: ApplyOn: PublishedVersions Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -557,12 +602,12 @@ Resources: GameScheduleGroup: Type: AWS::Scheduler::ScheduleGroup Properties: - Name: !Sub "${AWS::StackName}-game-auto-close" + Name: game-auto-close ChatMessageFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-chat-message-handler" + FunctionName: group2-englishstudy-chat-message-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.ChatMessageHandler::handleRequest Description: Handle chat messages @@ -572,7 +617,7 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref ChatTable - S3CrudPolicy: - BucketName: !Sub "${AWS::StackName}" + BucketName: group2-englishstudy - Statement: - Effect: Allow Action: @@ -614,7 +659,7 @@ Resources: ChatVoiceFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-chat-voice-handler" + FunctionName: group2-englishstudy-chat-voice-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.chatting.handler.ChatVoiceHandler::handleRequest Description: Convert text to speech using Polly @@ -624,7 +669,7 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref ChatTable - S3CrudPolicy: - BucketName: !Sub "${AWS::StackName}" + BucketName: group2-englishstudy - Statement: - Effect: Allow Action: @@ -648,7 +693,7 @@ Resources: WordFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-word-handler" + FunctionName: group2-englishstudy-vocab-word-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.WordHandler::handleRequest Description: Handle word CRUD operations @@ -726,7 +771,7 @@ Resources: UserWordFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-userword-handler" + FunctionName: group2-englishstudy-vocab-userword-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.UserWordHandler::handleRequest Description: Handle user word learning status @@ -788,7 +833,7 @@ Resources: WordGroupFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-wordgroup-handler" + FunctionName: group2-englishstudy-vocab-wordgroup-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.WordGroupHandler::handleRequest Description: Handle user custom word groups @@ -858,7 +903,7 @@ Resources: DailyStudyFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-daily-handler" + FunctionName: group2-englishstudy-vocab-daily-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.DailyStudyHandler::handleRequest Description: Handle daily study word assignment @@ -888,7 +933,7 @@ Resources: TestFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-test-handler" + FunctionName: group2-englishstudy-vocab-test-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.TestHandler::handleRequest Description: Handle vocabulary tests @@ -947,7 +992,7 @@ Resources: StatsFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-stats-handler" + FunctionName: group2-englishstudy-vocab-stats-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.StatsHandler::handleRequest Description: Handle user learning statistics @@ -985,7 +1030,7 @@ Resources: VocabVoiceFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-vocab-voice-handler" + FunctionName: group2-englishstudy-vocab-voice-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.VoiceHandler::handleRequest Description: Convert word to speech using Polly @@ -995,7 +1040,7 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref VocabTable - S3CrudPolicy: - BucketName: !Sub "${AWS::StackName}" + BucketName: group2-englishstudy - Statement: - Effect: Allow Action: @@ -1020,7 +1065,7 @@ Resources: StatsStreamFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-stats-stream-handler" + FunctionName: group2-englishstudy-stats-stream-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.stats.handler.StatsStreamHandler::handleRequest Description: Process DynamoDB Streams for stats aggregation @@ -1045,7 +1090,7 @@ Resources: UserStatsFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-user-stats-handler" + FunctionName: group2-englishstudy-user-stats-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.stats.handler.UserStatsHandler::handleRequest Description: Handle user learning statistics API @@ -1055,14 +1100,6 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref VocabTable Events: - GetDashboardStats: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /stats/dashboard - Method: GET - Auth: - Authorizer: CognitoAuthorizer GetDailyStats: Type: Api Properties: @@ -1108,7 +1145,7 @@ Resources: BadgeFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-badge-handler" + FunctionName: group2-englishstudy-badge-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.badge.handler.BadgeHandler::handleRequest Description: Handle user badges and achievements @@ -1118,7 +1155,7 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref VocabTable - S3ReadPolicy: - BucketName: !Sub "${AWS::StackName}" + BucketName: group2-englishstudy Events: GetAllBadges: Type: Api @@ -1144,7 +1181,7 @@ Resources: GrammarFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-grammar-handler" + FunctionName: group2-englishstudy-grammar-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.grammar.handler.GrammarHandler::handleRequest Description: Handle grammar check using Bedrock AI @@ -1223,7 +1260,7 @@ Resources: GrammarWebSocketApi: Type: AWS::ApiGatewayV2::Api Properties: - Name: !Sub "${AWS::StackName}-grammar-websocket" + Name: group2-englishstudy-grammar-websocket ProtocolType: WEBSOCKET RouteSelectionExpression: "$request.body.action" @@ -1231,7 +1268,7 @@ Resources: Type: AWS::ApiGatewayV2::Stage Properties: ApiId: !Ref GrammarWebSocketApi - StageName: !Ref Environment + StageName: dev AutoDeploy: true # Grammar WebSocket Connect Route @@ -1286,13 +1323,13 @@ Resources: GrammarStreamingConnectFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-grammar-ws-connect" + FunctionName: group2-grammar-ws-connect CodeUri: . Handler: com.mzc.secondproject.serverless.domain.grammar.handler.websocket.GrammarStreamingConnectHandler::handleRequest Description: Handle Grammar WebSocket $connect with JWT auth Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -1313,13 +1350,13 @@ Resources: GrammarStreamingDisconnectFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-grammar-ws-disconnect" + FunctionName: group2-grammar-ws-disconnect CodeUri: . Handler: com.mzc.secondproject.serverless.domain.grammar.handler.websocket.GrammarStreamingDisconnectHandler::handleRequest Description: Handle Grammar WebSocket $disconnect Environment: Variables: - WEBSOCKET_ENDPOINT: !Sub "https://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + WEBSOCKET_ENDPOINT: !Sub "https://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -1340,7 +1377,7 @@ Resources: GrammarStreamingFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-grammar-ws-streaming" + FunctionName: group2-grammar-ws-streaming CodeUri: . Handler: com.mzc.secondproject.serverless.domain.grammar.handler.websocket.GrammarStreamingHandler::handleRequest Description: Handle Grammar streaming with Bedrock @@ -1348,7 +1385,7 @@ Resources: MemorySize: 1024 Environment: Variables: - GRAMMAR_WEBSOCKET_ENDPOINT: !Sub "https://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}" + GRAMMAR_WEBSOCKET_ENDPOINT: !Sub "https://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev" Policies: - DynamoDBCrudPolicy: TableName: !Ref ChatTable @@ -1376,7 +1413,7 @@ Resources: ScheduledStatsFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-scheduled-stats" + FunctionName: group2-englishstudy-scheduled-stats CodeUri: . Handler: com.mzc.secondproject.serverless.domain.stats.handler.ScheduledStatsHandler::handleRequest Description: Daily scheduled job for word learning stats aggregation @@ -1392,7 +1429,7 @@ Resources: Type: Schedule Properties: Schedule: cron(0 15 * * ? *) # UTC 15:00 = KST 00:00 (자정) - Name: !Sub "${AWS::StackName}-daily-stats-aggregation" + Name: daily-stats-aggregation Description: Daily word learning stats aggregation Enabled: true @@ -1403,7 +1440,7 @@ Resources: SpeakingFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-speaking-handler" + FunctionName: group2-englishstudy-speaking-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.speaking.handler.SpeakingHandler::handleRequest Description: Handle Speaking AI conversation (REST API) @@ -1413,13 +1450,12 @@ Resources: ApplyOn: PublishedVersions Environment: Variables: - SPEAKING_TABLE_NAME: !Ref SpeakingTable TRANSCRIBE_API_KEY: "/opic/transcribe-proxy-api-key" Policies: - DynamoDBCrudPolicy: - TableName: !Ref SpeakingTable + TableName: !Ref ChatTable - S3CrudPolicy: - BucketName: !Ref ContentBucket + BucketName: group2-englishstudy - Statement: - Effect: Allow Action: @@ -1460,7 +1496,7 @@ Resources: OPIcSessionFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-opic-session-handler" + FunctionName: group2-englishstudy-opic-session-handler CodeUri: . Handler: com.mzc.secondproject.serverless.domain.opic.handler.OPIcSessionHandler::handleRequest Description: Handle OPIc speaking practice sessions @@ -1475,7 +1511,7 @@ Resources: - DynamoDBCrudPolicy: TableName: !Ref OPIcTable - S3CrudPolicy: - BucketName: !Sub "${AWS::StackName}" + BucketName: group2-englishstudy - Statement: - Effect: Allow Action: @@ -1568,7 +1604,7 @@ Resources: DeletionPolicy: Retain # UpdateReplacePolicy: Retain Properties: - TableName: !Sub "${AWS::StackName}-user" + TableName: group2-englishstudy-user BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: PK @@ -1613,7 +1649,7 @@ Resources: ChatTable: Type: AWS::DynamoDB::Table Properties: - TableName: !Sub "${AWS::StackName}-chat" + TableName: group2-englishstudy-chat BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: PK @@ -1657,7 +1693,7 @@ Resources: VocabTable: Type: AWS::DynamoDB::Table Properties: - TableName: !Sub "${AWS::StackName}-vocab" + TableName: group2-englishstudy-vocab BillingMode: PAY_PER_REQUEST StreamSpecification: StreamViewType: NEW_IMAGE @@ -1715,255 +1751,7 @@ Resources: OPIcTable: Type: AWS::DynamoDB::Table Properties: - TableName: !Sub "${AWS::StackName}-opic" - BillingMode: PAY_PER_REQUEST - AttributeDefinitions: - - AttributeName: PK - AttributeType: S - - AttributeName: SK - AttributeType: S - - AttributeName: GSI1PK - AttributeType: S - - AttributeName: GSI1SK - AttributeType: S - KeySchema: - - AttributeName: PK - KeyType: HASH - - AttributeName: SK - KeyType: RANGE - GlobalSecondaryIndexes: - - IndexName: GSI1 - KeySchema: - - AttributeName: GSI1PK - KeyType: HASH - - AttributeName: GSI1SK - KeyType: RANGE - Projection: - ProjectionType: ALL - TimeToLiveSpecification: - AttributeName: ttl - Enabled: true - - SpeakingTable: - Type: AWS::DynamoDB::Table - Properties: - TableName: !Sub "${AWS::StackName}-speaking" - BillingMode: PAY_PER_REQUEST - AttributeDefinitions: - - AttributeName: PK - AttributeType: S - - AttributeName: SK - AttributeType: S - - AttributeName: GSI1PK - AttributeType: S - - AttributeName: GSI1SK - AttributeType: S - KeySchema: - - AttributeName: PK - KeyType: HASH - - AttributeName: SK - KeyType: RANGE - GlobalSecondaryIndexes: - - IndexName: GSI1 - KeySchema: - - AttributeName: GSI1PK - KeyType: HASH - - AttributeName: GSI1SK - KeyType: RANGE - Projection: - ProjectionType: ALL - TimeToLiveSpecification: - AttributeName: ttl - Enabled: true - - ############################################# - # News Collection Scheduled Lambda - ############################################# - - NewsCollectionFunction: - Type: AWS::Serverless::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-news-collection" - CodeUri: . - Handler: com.mzc.secondproject.serverless.domain.news.handler.NewsCollectionHandler::handleRequest - Description: 매일 18시에 영어 뉴스를 수집하는 Lambda - MemorySize: 512 - Timeout: 300 - Policies: - - DynamoDBCrudPolicy: - TableName: !Ref NewsTable - - Statement: - - Effect: Allow - Action: - - bedrock:InvokeModel - Resource: "*" - - Statement: - - Effect: Allow - Action: - - comprehend:DetectKeyPhrases - Resource: "*" - Events: - DailySchedule: - Type: Schedule - Properties: - Schedule: cron(0 9 * * ? *) - Name: !Sub "${AWS::StackName}-news-collection-daily-schedule" - Description: 매일 18시 KST (09:00 UTC)에 뉴스 수집 - Enabled: true - - NewsFunction: - Type: AWS::Serverless::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-news" - CodeUri: . - Handler: com.mzc.secondproject.serverless.domain.news.handler.NewsHandler::handleRequest - Description: 뉴스 학습 API - MemorySize: 256 - Timeout: 30 - Policies: - - DynamoDBCrudPolicy: - TableName: !Ref NewsTable - - DynamoDBCrudPolicy: - TableName: !Ref VocabTable - - S3CrudPolicy: - BucketName: !Sub "${AWS::StackName}" - - Statement: - - Effect: Allow - Action: - - polly:SynthesizeSpeech - Resource: "*" - Events: - GetNewsList: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news - Method: GET - GetTodayNews: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/today - Method: GET - GetRecommendedNews: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/recommended - Method: GET - GetNewsStats: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/stats - Method: GET - Auth: - Authorizer: CognitoAuthorizer - GetBookmarks: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/bookmarks - Method: GET - Auth: - Authorizer: CognitoAuthorizer - GetUserWords: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/words - Method: GET - Auth: - Authorizer: CognitoAuthorizer - SyncWordToVocab: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/words/{word}/sync - Method: POST - Auth: - Authorizer: CognitoAuthorizer - CollectWord: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/words - Method: POST - Auth: - Authorizer: CognitoAuthorizer - DeleteWord: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/words/{word} - Method: DELETE - Auth: - Authorizer: CognitoAuthorizer - GetWordDetail: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/words/{word} - Method: GET - Auth: - Authorizer: CognitoAuthorizer - GetQuizHistory: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/quiz/history - Method: GET - Auth: - Authorizer: CognitoAuthorizer - GetQuiz: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/quiz - Method: GET - SubmitQuiz: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/quiz - Method: POST - Auth: - Authorizer: CognitoAuthorizer - MarkAsRead: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/read - Method: POST - Auth: - Authorizer: CognitoAuthorizer - ToggleBookmark: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/bookmark - Method: POST - Auth: - Authorizer: CognitoAuthorizer - GetAudio: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId}/audio - Method: GET - Auth: - Authorizer: CognitoAuthorizer - GetNewsDetail: - Type: Api - Properties: - RestApiId: !Ref MainApi - Path: /news/{articleId} - Method: GET - - NewsTable: - Type: AWS::DynamoDB::Table - Properties: - TableName: !Sub "${AWS::StackName}-news" + TableName: group2-englishstudy-opic BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: PK @@ -1974,10 +1762,6 @@ Resources: AttributeType: S - AttributeName: GSI1SK AttributeType: S - - AttributeName: GSI2PK - AttributeType: S - - AttributeName: GSI2SK - AttributeType: S KeySchema: - AttributeName: PK KeyType: HASH @@ -1992,45 +1776,10 @@ Resources: KeyType: RANGE Projection: ProjectionType: ALL - - IndexName: GSI2 - KeySchema: - - AttributeName: GSI2PK - KeyType: HASH - - AttributeName: GSI2SK - KeyType: RANGE - Projection: - ProjectionType: ALL TimeToLiveSpecification: AttributeName: ttl Enabled: true - ############################################# - # S3 Bucket for Content Storage - ############################################# - - ContentBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: !Sub "${AWS::StackName}" - CorsConfiguration: - CorsRules: - - AllowedHeaders: - - "*" - AllowedMethods: - - GET - - PUT - - POST - - DELETE - - HEAD - AllowedOrigins: - - "*" - MaxAge: 3600 - PublicAccessBlockConfiguration: - BlockPublicAcls: false - BlockPublicPolicy: false - IgnorePublicAcls: false - RestrictPublicBuckets: false - ############################################# # SNS / SQS for Async Statistics Processing ############################################# @@ -2039,20 +1788,20 @@ Resources: TestResultTopic: Type: AWS::SNS::Topic Properties: - TopicName: !Sub "${AWS::StackName}-test-result-topic" + TopicName: group2-englishstudy-test-result-topic # SQS Dead Letter Queue - 실패한 메시지 보관 StatisticsDeadLetterQueue: Type: AWS::SQS::Queue Properties: - QueueName: !Sub "${AWS::StackName}-statistics-dlq" + QueueName: group2-englishstudy-statistics-dlq MessageRetentionPeriod: 1209600 # 14일 # SQS Queue - 통계 처리용 StatisticsQueue: Type: AWS::SQS::Queue Properties: - QueueName: !Sub "${AWS::StackName}-statistics-queue" + QueueName: group2-englishstudy-statistics-queue VisibilityTimeout: 60 RedrivePolicy: deadLetterTargetArn: !GetAtt StatisticsDeadLetterQueue.Arn @@ -2088,7 +1837,7 @@ Resources: StatisticsProcessorFunction: Type: AWS::Serverless::Function Properties: - FunctionName: !Sub "${AWS::StackName}-statistics-processor" + FunctionName: group2-englishstudy-statistics-processor CodeUri: . Handler: com.mzc.secondproject.serverless.domain.vocabulary.handler.StatisticsHandler::handleRequest Description: Process test results and update user word statistics @@ -2114,15 +1863,15 @@ Resources: Outputs: ApiUrl: Description: Unified API Gateway endpoint URL - Value: !Sub 'https://${MainApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}/' + Value: !Sub 'https://${MainApi}.execute-api.${AWS::Region}.amazonaws.com/dev/' WebSocketUrl: Description: WebSocket API Gateway endpoint URL - Value: !Sub 'wss://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}' + Value: !Sub 'wss://${WebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev' GrammarWebSocketUrl: Description: Grammar Streaming WebSocket API endpoint URL - Value: !Sub 'wss://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}' + Value: !Sub 'wss://${GrammarWebSocketApi}.execute-api.${AWS::Region}.amazonaws.com/dev' ChatTableName: Description: Chat DynamoDB Table Name @@ -2134,20 +1883,16 @@ Outputs: BucketName: Description: S3 Bucket Name - Value: !Ref ContentBucket + Value: group2-englishstudy CognitoUserPoolId: Description: Cognito User Pool ID - Value: !Ref ExistingCognitoUserPoolId + Value: !Ref CognitoUserPool CognitoUserPoolClientId: Description: Cognito User Pool Client ID - Value: !Ref ExistingCognitoClientId + Value: !Ref CognitoUserPoolClient OPIcTableName: Description: OPIc DynamoDB Table Name Value: !Ref OPIcTable - - SpeakingTableName: - Description: Speaking DynamoDB Table Name - Value: !Ref SpeakingTable