103 lines
3.5 KiB
Swift
103 lines
3.5 KiB
Swift
// Auth/AuthManager.swift
|
|
|
|
import Foundation
|
|
import SwiftUI
|
|
import Combine
|
|
|
|
struct EmptyDecodable: Decodable, Sendable {
|
|
nonisolated init(from decoder: any Decoder) throws {}
|
|
}
|
|
|
|
@MainActor
|
|
class AuthManager: ObservableObject {
|
|
|
|
@Published var isAuthenticated = false
|
|
@Published var isLoading = false
|
|
@Published var errorMessage: String?
|
|
|
|
@Published var currentUserId: Int = 0
|
|
@Published var currentUsername: String = ""
|
|
@Published var currentUserRole: String = ""
|
|
@Published var currentDisplayName: String = ""
|
|
|
|
static let shared = AuthManager()
|
|
private init() {}
|
|
|
|
func restoreSession() async {
|
|
guard KeychainHelper.get(Constants.Keychain.accessToken) != nil else {
|
|
isAuthenticated = false
|
|
return
|
|
}
|
|
isLoading = true
|
|
defer { isLoading = false }
|
|
|
|
do {
|
|
let response: MeResponseData = try await APIClient.shared.request("/api/v1/auth/me")
|
|
applyUser(response.user)
|
|
isAuthenticated = true
|
|
} catch APIError.notAuthenticated {
|
|
KeychainHelper.clearAll()
|
|
isAuthenticated = false
|
|
} catch {
|
|
restoreUserFromKeychain()
|
|
isAuthenticated = true
|
|
}
|
|
}
|
|
|
|
func login(username: String, password: String) async {
|
|
isLoading = true
|
|
errorMessage = nil
|
|
defer { isLoading = false }
|
|
|
|
do {
|
|
let response: LoginResponseData = try await APIClient.shared.post(
|
|
"/api/v1/auth/login",
|
|
body: ["username": username, "password": password]
|
|
)
|
|
KeychainHelper.set(response.accessToken, forKey: Constants.Keychain.accessToken)
|
|
KeychainHelper.set(response.refreshToken, forKey: Constants.Keychain.refreshToken)
|
|
applyUser(response.user)
|
|
isAuthenticated = true
|
|
} catch APIError.serverError(let msg) {
|
|
errorMessage = msg
|
|
} catch APIError.networkError {
|
|
errorMessage = "Cannot reach the server. Please check your connection."
|
|
} catch {
|
|
errorMessage = error.localizedDescription
|
|
}
|
|
}
|
|
|
|
func logout() async {
|
|
if let refreshToken = KeychainHelper.get(Constants.Keychain.refreshToken) {
|
|
_ = try? await APIClient.shared.post(
|
|
"/api/v1/auth/logout",
|
|
body: ["refresh_token": refreshToken]
|
|
) as EmptyDecodable
|
|
}
|
|
KeychainHelper.clearAll()
|
|
isAuthenticated = false
|
|
currentUserId = 0
|
|
currentUsername = ""
|
|
currentUserRole = ""
|
|
currentDisplayName = ""
|
|
}
|
|
|
|
private func applyUser(_ user: APIUser) {
|
|
currentUserId = user.id
|
|
currentUsername = user.username
|
|
currentUserRole = user.role
|
|
currentDisplayName = user.displayName
|
|
KeychainHelper.set(String(user.id), forKey: Constants.Keychain.userId)
|
|
KeychainHelper.set(user.role, forKey: Constants.Keychain.userRole)
|
|
KeychainHelper.set(user.username, forKey: Constants.Keychain.username)
|
|
KeychainHelper.set(user.displayName, forKey: Constants.Keychain.displayName)
|
|
}
|
|
|
|
private func restoreUserFromKeychain() {
|
|
currentUserId = Int(KeychainHelper.get(Constants.Keychain.userId) ?? "0") ?? 0
|
|
currentUserRole = KeychainHelper.get(Constants.Keychain.userRole) ?? ""
|
|
currentUsername = KeychainHelper.get(Constants.Keychain.username) ?? ""
|
|
currentDisplayName = KeychainHelper.get(Constants.Keychain.displayName) ?? ""
|
|
}
|
|
}
|