Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion DevLog/Data/Repository/AuthenticationRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository {
}
}

try await authService.deleteFirestoreUserData()
try await authService.deleteCurrentUser()
try await authService.clearCurrentSession()
}
Expand Down
4 changes: 2 additions & 2 deletions DevLog/Infra/Common/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class Logger {
) {
var fullMessage = message
if let error = error {
fullMessage += " | Error: \(error.localizedDescription)"
fullMessage += " | Error: \(error)"
}
log(fullMessage, type: .error, file: file, function: function, line: line)
}
Expand All @@ -69,7 +69,7 @@ final class Logger {
) {
var fullMessage = message
if let error = error {
fullMessage += " | Error: \(error.localizedDescription)"
fullMessage += " | Error: \(error)"
}
log(fullMessage, type: .fault, file: file, function: function, line: line)
}
Expand Down
24 changes: 13 additions & 11 deletions DevLog/Infra/Service/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@

import FirebaseAuth
import FirebaseFirestore
import FirebaseFunctions
import FirebaseMessaging

final class AuthService {
private let store = Firestore.firestore()
private let functions = Functions.functions(region: "asia-northeast3")
private let messaging = Messaging.messaging()
private let logger = Logger(category: "AuthService")

Expand Down Expand Up @@ -47,13 +45,6 @@ final class AuthService {
}
}

func deleteFirestoreUserData() async throws {
logger.info("Deleting Firestore user data")

let deleteFunction = functions.httpsCallable("deleteUserFirestoreData")
_ = try await deleteFunction.call()
}

func deleteCurrentUser() async throws {
logger.info("Deleting FirebaseAuth current user")

Expand All @@ -62,7 +53,12 @@ final class AuthService {
throw AuthError.notAuthenticated
}

try await currentUser.delete()
do {
try await currentUser.delete()
} catch {
logger.error("Failed to delete FirebaseAuth current user", error: error)
throw error
}
}

func clearCurrentSession() async throws {
Expand All @@ -73,6 +69,12 @@ final class AuthService {
} catch {
logger.error("Failed to delete FCM token while clearing session", error: error)
}
try Auth.auth().signOut()

do {
try Auth.auth().signOut()
} catch {
logger.error("Failed to sign out while clearing session", error: error)
throw error
}
}
}
123 changes: 73 additions & 50 deletions DevLog/Infra/Service/PushNotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,30 @@ final class PushNotificationService {

/// 푸시 알림 시간 설정
func fetchPushNotificationTime() async throws -> DateComponents {
logger.info("Fetching push notification time")

guard let uid = Auth.auth().currentUser?.uid else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}

let settingsRef = store.document("users/\(uid)/userData/settings")
let doc = try await settingsRef.getDocument()
do {
let settingsRef = store.document("users/\(uid)/userData/settings")
let doc = try await settingsRef.getDocument()

guard let hour = doc.data()?["pushNotificationHour"] as? Int else {
throw FirestoreError.dataNotFound("pushNotificationHour")
}
guard let hour = doc.data()?["pushNotificationHour"] as? Int else {
throw FirestoreError.dataNotFound("pushNotificationHour")
}

guard let minute = doc.data()?["pushNotificationMinute"] as? Int else {
throw FirestoreError.dataNotFound("pushNotificationMinute")
}
guard let minute = doc.data()?["pushNotificationMinute"] as? Int else {
throw FirestoreError.dataNotFound("pushNotificationMinute")
}

return DateComponents(hour: hour, minute: minute)
return DateComponents(hour: hour, minute: minute)
} catch {
logger.error("Failed to fetch push notification time", error: error)
throw error
}
}

/// 푸시 알림 설정 업데이트
Expand Down Expand Up @@ -94,35 +102,40 @@ final class PushNotificationService {
_ notificationQuery: PushNotificationQuery,
cursor: PushNotificationCursorDTO?
) async throws -> PushNotificationPageResponse {
guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated }
do {
guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated }

var firestoreQuery = makeQuery(uid: uid, query: notificationQuery)
var firestoreQuery = makeQuery(uid: uid, query: notificationQuery)

if let cursor {
firestoreQuery = firestoreQuery.start(after: [
Timestamp(date: cursor.receivedAt),
cursor.documentID
])
}
if let cursor {
firestoreQuery = firestoreQuery.start(after: [
Timestamp(date: cursor.receivedAt),
cursor.documentID
])
}

let snapshot = try await firestoreQuery
.limit(to: notificationQuery.pageSize)
.getDocuments()
let snapshot = try await firestoreQuery
.limit(to: notificationQuery.pageSize)
.getDocuments()

let items = snapshot.documents.compactMap { makeResponse(from: $0) }
let items = snapshot.documents.compactMap { makeResponse(from: $0) }

let nextCursor: PushNotificationCursorDTO? = snapshot.documents.last.map { document in
guard let receivedAt = document.data()[Key.receivedAt.rawValue] as? Timestamp else {
return nil
}
let nextCursor: PushNotificationCursorDTO? = snapshot.documents.last.map { document in
guard let receivedAt = document.data()[Key.receivedAt.rawValue] as? Timestamp else {
return nil
}

return PushNotificationCursorDTO(
receivedAt: receivedAt.dateValue(),
documentID: document.documentID
)
} ?? nil
return PushNotificationCursorDTO(
receivedAt: receivedAt.dateValue(),
documentID: document.documentID
)
} ?? nil

return PushNotificationPageResponse(items: items, nextCursor: nextCursor)
return PushNotificationPageResponse(items: items, nextCursor: nextCursor)
} catch {
logger.error("Failed to request notifications", error: error)
throw error
}
}

func observeNotifications(
Expand Down Expand Up @@ -160,37 +173,47 @@ final class PushNotificationService {

/// 푸시 알림 기록 삭제
func deleteNotification(_ notificationID: String) async throws {
guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated }
do {
guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated }

let docRef = store.collection("users/\(uid)/notifications").document(notificationID)
let docRef = store.collection("users/\(uid)/notifications").document(notificationID)

try await docRef.delete()
try await docRef.delete()
} catch {
logger.error("Failed to delete notification", error: error)
throw error
}
}

/// 푸시 알림 읽음/안읽음 토글
func toggleNotificationRead(_ todoId: String) async throws {
logger.info("Toggling notification read for todoId: \(todoId)")

guard let uid = Auth.auth().currentUser?.uid else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}
do {
guard let uid = Auth.auth().currentUser?.uid else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}

let collection = store.collection("users/\(uid)/notifications")
let snapshot = try await collection.whereField("todoId", isEqualTo: todoId).getDocuments()
let collection = store.collection("users/\(uid)/notifications")
let snapshot = try await collection.whereField("todoId", isEqualTo: todoId).getDocuments()

guard let document = snapshot.documents.first else {
logger.error("Notification not found for todoId: \(todoId)")
throw FirestoreError.dataNotFound("notification")
}
guard let document = snapshot.documents.first else {
logger.error("Notification not found for todoId: \(todoId)")
throw FirestoreError.dataNotFound("notification")
}

guard let currentValue = document.data()["isRead"] as? Bool else {
logger.error("isRead not found for notification: \(document.documentID)")
throw FirestoreError.dataNotFound("isRead")
}
guard let currentValue = document.data()["isRead"] as? Bool else {
logger.error("isRead not found for notification: \(document.documentID)")
throw FirestoreError.dataNotFound("isRead")
}

try await document.reference.updateData(["isRead": !currentValue])
logger.info("Successfully toggled notification read")
try await document.reference.updateData(["isRead": !currentValue])
logger.info("Successfully toggled notification read")
} catch {
logger.error("Failed to toggle notification read", error: error)
throw error
}
}
}

Expand Down
73 changes: 44 additions & 29 deletions DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,51 +89,66 @@ final class AppleAuthenticationService: AuthenticationService {
}

func signOut(_ uid: String) async throws {
let infoRef = store.document("users/\(uid)/userData/tokens")
let doc = try await infoRef.getDocument()
do {
let infoRef = store.document("users/\(uid)/userData/tokens")
let doc = try await infoRef.getDocument()

if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
}
if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
}

try await messaging.deleteToken()
try await messaging.deleteToken()

try Auth.auth().signOut()
try Auth.auth().signOut()
} catch {
logger.error("Failed to sign out with Apple", error: error)
throw error
}
}

func deleteAuth(_ uid: String) async throws {
let token = try await refreshAppleAccessToken()
do {
let token = try await refreshAppleAccessToken()

try await revokeAppleAccessToken(token: token)
try await revokeAppleAccessToken(token: token)
} catch {
logger.error("Failed to delete Apple auth", error: error)
throw error
}
}

func link(uid: String, email: String) async throws {
let response = try await authenticateWithAppleAsync()
do {
let response = try await authenticateWithAppleAsync()

let nonce = response.nonce
let credential = response.credential
let authorizationCode = response.authorizationCode
let idTokenString = response.idTokenString
let nonce = response.nonce
let credential = response.credential
let authorizationCode = response.authorizationCode
let idTokenString = response.idTokenString

let refreshToken = try await requestAppleRefreshToken(uid: uid, authorizationCode: authorizationCode)
let refreshToken = try await requestAppleRefreshToken(uid: uid, authorizationCode: authorizationCode)

guard let appleEmail = credential.email else {
try await revokeAppleAccessToken(token: refreshToken)
throw EmailFetchError.emailNotFound
}
guard let appleEmail = credential.email else {
try await revokeAppleAccessToken(token: refreshToken)
throw EmailFetchError.emailNotFound
}

if appleEmail != email {
try await revokeAppleAccessToken(token: refreshToken)
throw EmailFetchError.emailMismatch
}
if appleEmail != email {
try await revokeAppleAccessToken(token: refreshToken)
throw EmailFetchError.emailMismatch
}

let appleCredential = OAuthProvider.credential(
providerID: providerID,
idToken: idTokenString,
rawNonce: nonce
)
let appleCredential = OAuthProvider.credential(
providerID: providerID,
idToken: idTokenString,
rawNonce: nonce
)

try await user?.link(with: appleCredential)
try await user?.link(with: appleCredential)
} catch {
logger.error("Failed to link Apple account", error: error)
throw error
}
}

func unlink(_ uid: String) async throws {
Expand Down
26 changes: 18 additions & 8 deletions DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,30 @@ final class GithubAuthenticationService: NSObject, AuthenticationService {
}

func signOut(_ uid: String) async throws {
let infoRef = store.document("users/\(uid)/userData/tokens")
let doc = try await infoRef.getDocument()
do {
let infoRef = store.document("users/\(uid)/userData/tokens")
let doc = try await infoRef.getDocument()

if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
}
if doc.exists {
try await infoRef.updateData(["fcmToken": FieldValue.delete()])
}

try await messaging.deleteToken()
try await messaging.deleteToken()

try Auth.auth().signOut()
try Auth.auth().signOut()
} catch {
logger.error("Failed to sign out with GitHub", error: error)
throw error
}
}

func deleteAuth(_ uid: String) async throws {
try await revokeAccessToken()
do {
try await revokeAccessToken()
} catch {
logger.error("Failed to delete GitHub auth", error: error)
throw error
}
}

func link(uid: String, email: String) async throws {
Expand Down
Loading
Loading