Skip to content

Commit

Permalink
feat: add api for login&join (#292)
Browse files Browse the repository at this point in the history
* feat:add api(users/verify-email, users/temporary-join)

* feat:add api(auth)

* feat:add api(user/join) except profileImg

* feat:add api(auth/mobile) & tokenManager

* feat:add api(users/me)
  • Loading branch information
HeewonP825 authored Feb 22, 2024
1 parent f071c65 commit 6566882
Show file tree
Hide file tree
Showing 28 changed files with 672 additions and 45 deletions.
1 change: 1 addition & 0 deletions src/mobile/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.android.gms:play-services-basement:18.3.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
Expand Down
1 change: 1 addition & 0 deletions src/mobile/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.smilegate.Easel.data

import android.content.Context
import android.content.SharedPreferences

object TokenManager {
private const val PREF_NAME = "TokenPrefs"
private const val KEY_ACCESS_TOKEN = "accessToken"
private const val KEY_REFRESH_TOKEN = "refreshToken"

private var sharedPreferences: SharedPreferences? = null

// SharedPreferences 인스턴스 가져오기
private fun getSharedPreferences(context: Context): SharedPreferences {
if (sharedPreferences == null) {
sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
}
return sharedPreferences!!
}

// AccessToken 저장
fun saveAccessToken(context: Context, accessToken: String) {
val editor = getSharedPreferences(context).edit()
editor.putString(KEY_ACCESS_TOKEN, accessToken)
editor.apply()
}

// RefreshToken 저장
fun saveRefreshToken(context: Context, refreshToken: String) {
val editor = getSharedPreferences(context).edit()
editor.putString(KEY_REFRESH_TOKEN, refreshToken)
editor.apply()
}

// AccessToken 가져오기
fun getAccessToken(context: Context): String? {
return getSharedPreferences(context).getString(KEY_ACCESS_TOKEN, null)
}

// RefreshToken 가져오기
fun getRefreshToken(context: Context): String? {
return getSharedPreferences(context).getString(KEY_REFRESH_TOKEN, null)
}

// 토큰 삭제
fun clearTokens(context: Context) {
val editor = getSharedPreferences(context).edit()
editor.remove(KEY_ACCESS_TOKEN)
editor.remove(KEY_REFRESH_TOKEN)
editor.apply()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.smilegate.Easel.domain.api

import com.smilegate.Easel.domain.model.auth.EmailAuth
import com.smilegate.Easel.domain.model.auth.EmailRequest
import com.smilegate.Easel.domain.model.auth.EmailResponse
import com.smilegate.Easel.domain.model.join.JoinRequest
import com.smilegate.Easel.domain.model.join.TemporaryJoinRequest
import com.smilegate.Easel.domain.model.join.VerifyUsernameRequest
import com.smilegate.Easel.domain.model.join.VerifyUsernameResponse
import com.smilegate.Easel.domain.model.login.LoginRequest
import com.smilegate.Easel.domain.model.login.LoginResponse
import com.smilegate.Easel.domain.model.user.UserProfileResponse
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST

interface ApiService {
@POST("api/auth")
suspend fun sendCode(@Body request: EmailAuth): Response<ResponseBody>

@POST("api/users/verify-email")
suspend fun verifyEmail(@Body request: EmailRequest): Response<EmailResponse>

@POST("api/users/temporary-join")
suspend fun temporaryJoin(@Body request: TemporaryJoinRequest): Response<Unit>

@POST("api/users/verify-username")
suspend fun verifyUsername(@Body request: VerifyUsernameRequest): Response<VerifyUsernameResponse>

@POST("api/users/join")
suspend fun joinUser(@Body joinRequest: JoinRequest): Response<Unit>

@POST("api/auth/mobile")
suspend fun login(@Body loginRequest: LoginRequest): Response<LoginResponse>

@GET("api/users/me")
suspend fun getUserProfile(@Header("Authorization") token: String): Response<UserProfileResponse>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.smilegate.Easel.domain.model.auth

data class EmailAuth(
val email: String,
val payload: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.smilegate.Easel.domain.model.auth

data class EmailRequest(
val email: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.smilegate.Easel.domain.model.auth

data class EmailResponse(
val isDuplicated: Boolean
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.smilegate.Easel.domain.model.join

data class JoinRequest(
val email: String,
val password: String,
val username: String,
val nickname: String,
val introduce: String? = null,
val profileImagePath: String? = null,
val websitePath: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.smilegate.Easel.domain.model.join

data class TemporaryJoinRequest(
val email: String,
val nickname: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.smilegate.Easel.domain.model.join

data class VerifyUsernameRequest(
val username: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.smilegate.Easel.domain.model.join

data class VerifyUsernameResponse(
val isDuplicated: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.smilegate.Easel.domain.model.login

data class LoginRequest(
val email: String,
val password: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.smilegate.Easel.domain.model.login

data class LoginResponse(
val accessToken: String,
val refreshToken: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.smilegate.Easel.domain.model.user

data class UserProfileResponse(
val backgroundImagePath: String,
val profileImagePath: String,
val nickname: String,
val username: String,
val introduce: String,
val websitePath: String,
val joinedAt: String,
val followingCount: Long,
val followerCount: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.smilegate.Easel.domain.repository

import android.util.Log
import com.smilegate.Easel.domain.api.ApiService
import com.smilegate.Easel.domain.model.join.JoinRequest

class JoinRepository(private val apiService: ApiService) {

suspend fun joinUser(joinRequest: JoinRequest) {
try {
val response = apiService.joinUser(joinRequest)

if (response.isSuccessful) {
// 성공적으로 응답을 받았을 때의 처리
// 여기에서는 특별히 할 일이 없다면 그냥 지나가도 됩니다.
} else {
// 서버로부터 오류 응답을 받았을 때의 처리
// 예를 들어, 오류 메시지를 출력하거나 특정 동작을 수행할 수 있습니다.
val errorBody = response.errorBody()?.string()
Log.e("UserRepository", "Error: $errorBody")
Log.e("UserRepository","Failed to join user. Response code: ${response.code()}")
}
} catch (e: Exception) {
// 네트워크 오류 등 예외 발생 시의 처리
Log.e("UserRepository", "Error: ${e.message}", e)
Log.e("UserRepository","Failed to join user. Error: ${e.message}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.smilegate.Easel.domain.repository

import android.util.Log
import androidx.lifecycle.MutableLiveData
import com.smilegate.Easel.domain.api.ApiService
import com.smilegate.Easel.domain.model.auth.EmailAuth
import java.io.IOException

class SendCodeRepository(private val apiService: ApiService) {
private val _codeLiveData = MutableLiveData<String?>()
private val codeLiveData: MutableLiveData<String?> = _codeLiveData

suspend fun sendCode(email: String, payload: String) {
val request = EmailAuth(email, payload)
try {
val response = apiService.sendCode(request)
if (response.isSuccessful) {
// 성공적인 응답 처리
val responseBody = response.body()?.string()
if (responseBody != null) {
codeLiveData.postValue(responseBody)
} else {
// 서버가 빈 응답을 반환한 경우 처리
}
} else {
// 실패한 응답 처리
val errorMessage = response.errorBody()?.string()
if (!errorMessage.isNullOrEmpty()) {
Log.e("SendCodeRepository", "Error: $errorMessage")
} else {
Log.e("SendCodeRepository", "Failed to send code. Response code: ${response.code()}")
}
}
} catch (e: IOException) {
Log.e("SendCodeRepository", "Network error: ${e.message}")
} catch (e: Exception) {
Log.e("SendCodeRepository", "Unexpected error: ${e.message}")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.smilegate.Easel.domain.repository

import com.smilegate.Easel.domain.api.ApiService
import com.smilegate.Easel.domain.model.auth.EmailRequest

class UserRepository(private val apiService: ApiService) {
suspend fun verifyEmail(email: String): Boolean {
val request = EmailRequest(email)
val response = apiService.verifyEmail(request)
return response.isSuccessful && response.body()?.isDuplicated == false
}
}
Loading

0 comments on commit 6566882

Please sign in to comment.