Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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