Skip to content

Commit

Permalink
feat: kakao oauth 추가 및 api 명세 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
wjdtkdgns committed May 26, 2024
1 parent 3f24739 commit 534b261
Show file tree
Hide file tree
Showing 90 changed files with 1,985 additions and 206 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/API-CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ on:
push:
branches:
# - main
- develop
pull_request:
branches:
- develop
types: [ opened, synchronize, reopened ]
# - develop
# pull_request:
# branches:
# - develop
# types: [ opened, synchronize, reopened ]

jobs:
build:
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ out/
### Kotlin ###
.kotlin

mysqldata/
mysqldata/

.env
.env.dev

docker-compose.prod.yml
128 changes: 67 additions & 61 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
val kotlinVersion = "1.9.23"

id("org.springframework.boot") version "3.2.5"
id("io.spring.dependency-management") version "1.1.4"
kotlin("jvm") version kotlinVersion
kotlin("plugin.spring") version kotlinVersion
kotlin("plugin.jpa") version kotlinVersion
kotlin("plugin.allopen") version kotlinVersion
kotlin("kapt") version kotlinVersion
idea

/** ktlint **/
id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
val kotlinVersion = "1.9.23"

id("org.springframework.boot") version "3.2.5"
id("io.spring.dependency-management") version "1.1.4"
kotlin("jvm") version kotlinVersion
kotlin("plugin.spring") version kotlinVersion
kotlin("plugin.jpa") version kotlinVersion
kotlin("plugin.allopen") version kotlinVersion
kotlin("kapt") version kotlinVersion
idea

/** ktlint **/
id("org.jlleitschuh.gradle.ktlint") version "11.6.1"
}

group = "com.balancemania"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
mavenCentral()
mavenCentral()
}

idea {
module {
val kaptMain = file("build/generated/source/kapt/main")
sourceDirs.add(kaptMain)
generatedSourceDirs.add(kaptMain)
}
module {
val kaptMain = file("build/generated/source/kapt/main")
sourceDirs.add(kaptMain)
generatedSourceDirs.add(kaptMain)
}
}

springBoot.buildInfo { properties { } }
Expand All @@ -41,77 +41,83 @@ springBoot.buildInfo { properties { } }
* jpa meta-annotations not automatically opened through the default settings of the plugin.spring
*/
allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
kapt("org.springframework.boot:spring-boot-configuration-processor")

implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-slf4j")
runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

implementation("io.arrow-kt:arrow-fx-coroutines:1.2.1")
implementation("io.arrow-kt:arrow-fx-stm:1.2.1")

implementation(
platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0")
)
implementation("io.awspring.cloud:spring-cloud-aws-starter-parameter-store")

implementation("io.github.oshai:kotlin-logging-jvm:6.0.3")
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("org.springframework.boot:spring-boot-starter-webflux")
kapt("org.springframework.boot:spring-boot-configuration-processor")

implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-slf4j")
runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

implementation("io.arrow-kt:arrow-fx-coroutines:1.2.1")
implementation("io.arrow-kt:arrow-fx-stm:1.2.1")

implementation(
platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.1.0")
)
implementation("io.awspring.cloud:spring-cloud-aws-starter-parameter-store")

implementation("io.github.oshai:kotlin-logging-jvm:6.0.3")
implementation("net.logstash.logback:logstash-logback-encoder:7.4")

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0")
runtimeOnly("com.github.therapi:therapi-runtime-javadoc-scribe:0.15.0")
kapt("com.github.therapi:therapi-runtime-javadoc-scribe:0.15.0")

developmentOnly("org.springframework.boot:spring-boot-devtools")
developmentOnly("org.springframework.boot:spring-boot-devtools")

implementation("com.auth0:java-jwt:4.4.0")

runtimeOnly("com.mysql:mysql-connector-j")
runtimeOnly("com.mysql:mysql-connector-j")
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

defaultTasks("bootRun")

configurations.all {
resolutionStrategy.cacheChangingModulesFor(0, "seconds")
resolutionStrategy.cacheChangingModulesFor(0, "seconds")
}

tasks.getByName<Jar>("jar") {
enabled = false
enabled = false
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "17"
}
}

tasks.withType<Wrapper> {
gradleVersion = "8.5"
gradleVersion = "8.5"
}

tasks.withType<Test> {
useJUnitPlatform()
useJUnitPlatform()
}

/** build시 ktlint 미적용 */
gradle.taskGraph.whenReady {
if (hasTask(":build")) {
allTasks.forEach { task ->
if (task.name.contains("ktlint") || task.name.contains("Ktlint")) {
task.enabled = false
}
}
}
if (hasTask(":build")) {
allTasks.forEach { task ->
if (task.name.contains("ktlint") || task.name.contains("Ktlint")) {
task.enabled = false
}
}
}
}
16 changes: 11 additions & 5 deletions script/DDL.sql
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
-- scheme
CREATE DATABASE balancemania CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE
DATABASE balancemania CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 유저 정보
CREATE TABLE `user`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'user id',
`name` varchar(256) NOT NULL COMMENT 'user 이름',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'user id',
`oauth_provider` int NOT NULL COMMENT 'oauth 제공자, KAKAO: 0',
`oauth_id` varchar(256) NOT NULL COMMENT 'oauth id',
`name` varchar(256) NOT NULL COMMENT 'user 이름',
`gender` int DEFAULT NULL COMMENT 'user 성별, 남성: 0, 여성: 1',
`birth` date DEFAULT NULL COMMENT 'user 출생년도',
`status_type` int NOT NULL COMMENT '상태 정보 타입 정보 / 활동 : 1, 탈퇴 : 2, 일시 정지 7일 : 3, 영구 정지 : 4',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='유저 정보';
3 changes: 1 addition & 2 deletions src/main/kotlin/com/balancemania/api/ApiApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ApiApplication(
private val buildProperties: BuildProperties,
private val environment: Environment,
) : ApplicationListener<ApplicationReadyEvent> {
val logger = KotlinLogging.logger { }
val logger = KotlinLogging.logger { }

override fun onApplicationEvent(event: ApplicationReadyEvent) {
val application = buildProperties.name
Expand All @@ -35,4 +35,3 @@ fun init() {
/** Setting the Default TimeZone */
TimeZone.setDefault(TimeZone.getTimeZone(Zone.KST))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.balancemania.api.auth.application

import arrow.fx.coroutines.parZip
import com.balancemania.api.auth.application.domain.RefreshToken
import com.balancemania.api.auth.model.AuthUser
import com.balancemania.api.auth.model.AuthUserImpl
import com.balancemania.api.auth.model.AuthUserToken
import com.balancemania.api.auth.model.TokenDto
import com.balancemania.api.auth.model.response.TokenRefreshRequest
import com.balancemania.api.config.database.TransactionTemplates
import com.balancemania.api.exception.ErrorCode
import com.balancemania.api.exception.InvalidTokenException
import com.balancemania.api.exception.NoAuthorityException
import com.balancemania.api.extension.coExecuteOrNull
import com.balancemania.api.user.application.UserService
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class AuthFacade(
private val userService: UserService,
private val jwtTokenService: JwtTokenService,
private val refreshTokenService: RefreshTokenService,
private val oAuthService: OAuthService,
private val eventPublisher: ApplicationEventPublisher,
private val txTemplates: TransactionTemplates,
) {
fun resolveAuthUser(token: AuthUserToken): Any {
return jwtTokenService.verifyToken(token)
.let { payload ->
if (payload.type != "accessToken") {
throw InvalidTokenException(ErrorCode.INVALID_ACCESS_TOKEN)
}

val user = userService.findByIdOrThrowSync(payload.id)

AuthUserImpl(uid = user.id)
}
}

@Transactional
suspend fun logout(user: AuthUser) {
refreshTokenService.deleteByKey(user.uid.toString())
}

@Transactional
suspend fun refreshToken(request: TokenRefreshRequest): TokenDto {
val accessPayload = jwtTokenService.verifyTokenWithExtendedExpiredAt(request.accessToken)
val refreshPayload = jwtTokenService.verifyRefreshToken(request.refreshToken)

if (accessPayload.id != refreshPayload.id) {
throw NoAuthorityException(ErrorCode.INVALID_TOKEN)
}

return parZip(
{ refreshTokenService.deleteByKey(refreshPayload.id.toString()) },
{ jwtTokenService.generateAccessAndRefreshToken(refreshPayload.id) }
) { _, tokenDto ->
RefreshToken(
uid = refreshPayload.id,
refreshToken = tokenDto.refreshToken
).run { refreshTokenService.save(this) }

tokenDto
}
}

@Transactional
suspend fun withdraw(authUser: AuthUser) {
val user = userService.findByIdOrThrow(authUser.uid)

coroutineScope {
val txDeferred = async {
txTemplates.writer.coExecuteOrNull {
user.apply {
this.oauthInfo = oauthInfo.withdrawOAuthInfo()
}.run { userService.saveSync(this) }
}
}

val oAuthDeferred = async {
oAuthService.withdraw(user.oauthInfo)
}

awaitAll(txDeferred, oAuthDeferred)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.balancemania.api.auth.application

import com.balancemania.api.auth.application.oauth.KakaoOAuthService
import com.balancemania.api.auth.model.response.OAuthLoginLinkResponse
import com.balancemania.api.auth.model.response.OAuthTokenResponse
import com.balancemania.api.user.domain.vo.OAuthProvider
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Service

@Service
class DevOAuthService(
private val kakaoOAuthService: KakaoOAuthService,
) {
private val logger = KotlinLogging.logger { }

/** oauth login link 가져오기 */
suspend fun getOAuthLoginLinkDev(provider: OAuthProvider): OAuthLoginLinkResponse {
return when (provider) {
OAuthProvider.KAKAO -> kakaoOAuthService.getOAuthLoginLinkDev()
}
}

/** oauth token 가져오기 */
suspend fun getOAuthTokenDev(provider: OAuthProvider, code: String): OAuthTokenResponse {
return when (provider) {
OAuthProvider.KAKAO -> kakaoOAuthService.getOAuthTokenDev(code)
}
}
}
Loading

0 comments on commit 534b261

Please sign in to comment.