Merge pull request '비밀번호 찾기 API 추가' (#4) from test into main

Reviewed-on: #4
This commit is contained in:
klaus 2023-08-19 07:05:17 +00:00
commit 8b98a2dd07
7 changed files with 117 additions and 0 deletions

View File

@ -0,0 +1,30 @@
package kr.co.vividnext.sodalive.configs
import com.amazonaws.auth.AWSStaticCredentialsProvider
import com.amazonaws.auth.BasicAWSCredentials
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class AmazonSESConfig(
@Value("\${cloud.aws.credentials.access-key}")
private val accessKey: String,
@Value("\${cloud.aws.credentials.secret-key}")
private val secretKey: String,
@Value("\${cloud.aws.region.static}")
private val region: String
) {
@Bean
fun amazonSimpleEmailService(): AmazonSimpleEmailService {
val basicAWSCredentials = BasicAWSCredentials(accessKey, secretKey)
val awsStaticCredentialsProvider = AWSStaticCredentialsProvider(basicAWSCredentials)
return AmazonSimpleEmailServiceClientBuilder.standard()
.withCredentials(awsStaticCredentialsProvider)
.withRegion(region)
.build()
}
}

View File

@ -0,0 +1,18 @@
package kr.co.vividnext.sodalive.email
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService
import org.springframework.stereotype.Service
@Service
class SendEmailService(private val amazonSimpleEmailService: AmazonSimpleEmailService) {
fun sendTemplatedEmail(template: String, templateData: String, receiver: String) {
val senderDto = TemplatedEmailSenderDto(
senderEmail = "yozmlive.noreply@gmail.com",
template = template,
templateData = templateData,
to = receiver
)
amazonSimpleEmailService.sendTemplatedEmail(senderDto.toSendRequest())
}
}

View File

@ -0,0 +1,21 @@
package kr.co.vividnext.sodalive.email
import com.amazonaws.services.simpleemail.model.Destination
import com.amazonaws.services.simpleemail.model.SendTemplatedEmailRequest
data class TemplatedEmailSenderDto(
private val senderEmail: String,
private val template: String,
private val templateData: String,
private val to: String
) {
fun toSendRequest(): SendTemplatedEmailRequest {
val destination = Destination().withToAddresses(to)
return SendTemplatedEmailRequest()
.withTemplate(template)
.withDestination(destination)
.withSource(senderEmail)
.withTemplateData(templateData)
}
}

View File

@ -0,0 +1,3 @@
package kr.co.vividnext.sodalive.member
data class ForgotPasswordRequest(val email: String)

View File

@ -193,4 +193,9 @@ class MemberController(private val service: MemberService) {
@RequestParam("image") multipartFile: MultipartFile,
@AuthenticationPrincipal user: User
) = ApiResponse.ok(service.profileImageUpdate(multipartFile, user))
@PostMapping("/forgot-password")
fun forgotPassword(
@RequestBody request: ForgotPasswordRequest
) = ApiResponse.ok(service.forgotPassword(request))
}

View File

@ -39,6 +39,7 @@ interface MemberQueryRepository {
fun getMessageRecipientPushToken(messageId: Long): GetMessageRecipientPushTokenResponse
fun getIndividualRecipientPushTokens(recipients: List<Long>, isAuth: Boolean): Map<String, List<List<String>>>
fun getChangeNicknamePrice(memberId: Long): GetChangeNicknamePriceResponse
fun getMemberByEmail(email: String): Member?
}
@Repository
@ -229,4 +230,11 @@ class MemberQueryRepositoryImpl(
}
)
}
override fun getMemberByEmail(email: String): Member? {
return queryFactory
.selectFrom(member)
.where(member.email.eq(email))
.fetchOne()
}
}

View File

@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.can.payment.CanPaymentService
import kr.co.vividnext.sodalive.can.use.CanUsage
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.email.SendEmailService
import kr.co.vividnext.sodalive.jwt.TokenProvider
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
import kr.co.vividnext.sodalive.member.block.BlockMember
@ -33,6 +34,7 @@ import kr.co.vividnext.sodalive.member.tag.MemberCreatorTag
import kr.co.vividnext.sodalive.member.tag.MemberTagRepository
import kr.co.vividnext.sodalive.member.token.MemberTokenRepository
import kr.co.vividnext.sodalive.utils.generateFileName
import kr.co.vividnext.sodalive.utils.generatePassword
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.repository.findByIdOrNull
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
@ -46,6 +48,9 @@ import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.multipart.MultipartFile
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write
@ -62,6 +67,7 @@ class MemberService(
private val nicknameChangeLogRepository: NicknameChangeLogRepository,
private val memberTagRepository: MemberTagRepository,
private val emailService: SendEmailService,
private val canPaymentService: CanPaymentService,
private val memberNotificationService: MemberNotificationService,
@ -546,6 +552,32 @@ class MemberService(
return "$cloudFrontHost/${member.profileImage!!}"
}
@Transactional
fun forgotPassword(request: ForgotPasswordRequest) {
val member = repository.getMemberByEmail(email = request.email)
?: throw SodaException("등록되지 않은 계정입니다.\n확인 후 다시 시도해 주세요.")
val password = generatePassword(12)
member.password = passwordEncoder.encode(password)
val date = LocalDateTime.now()
.atZone(ZoneId.of("UTC"))
.withZoneSameInstant(ZoneId.of("Asia/Seoul"))
.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일, HH:mm"))
val data = HashMap<String, String>()
data["password"] = password
data["date"] = date
val templateData = objectMapper.writeValueAsString(data)
emailService.sendTemplatedEmail(
template = "sodalive-find-password",
templateData = templateData,
receiver = request.email
)
}
private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock {
return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() }
}