Das Backend bool-konvertiert nur is_premium explizit; alle anderen 0/1-Spalten gehen unverändert durch FastAPI. Decode-Fehler vorher still verschluckt → jetzt auch geloggt, damit das nicht nochmal passiert.
69 lines
2.1 KiB
Swift
69 lines
2.1 KiB
Swift
import Foundation
|
|
import Observation
|
|
|
|
@Observable
|
|
@MainActor
|
|
final class AuthSession {
|
|
var token: String?
|
|
var userName: String?
|
|
var isPremium: Bool = false
|
|
var profile: UserProfile?
|
|
var isLoggingIn: Bool = false
|
|
var errorMessage: String?
|
|
|
|
private let tokenKey = "by_token"
|
|
|
|
init() {
|
|
if let savedToken = KeychainStore.read(tokenKey) {
|
|
token = savedToken
|
|
APIClient.shared.token = savedToken
|
|
}
|
|
}
|
|
|
|
var isLoggedIn: Bool { token != nil }
|
|
|
|
func login(email: String, password: String) async {
|
|
isLoggingIn = true
|
|
errorMessage = nil
|
|
defer { isLoggingIn = false }
|
|
do {
|
|
let response: LoginResponse = try await APIClient.shared.post(
|
|
"/api/auth/login",
|
|
body: LoginRequest(email: email, password: password)
|
|
)
|
|
KeychainStore.save(response.token, for: tokenKey)
|
|
APIClient.shared.token = response.token
|
|
self.token = response.token
|
|
self.userName = response.name
|
|
self.isPremium = response.isPremium
|
|
} catch {
|
|
self.errorMessage = error.localizedDescription
|
|
}
|
|
}
|
|
|
|
func logout() {
|
|
KeychainStore.delete(tokenKey)
|
|
APIClient.shared.token = nil
|
|
token = nil
|
|
userName = nil
|
|
isPremium = false
|
|
profile = nil
|
|
}
|
|
|
|
/// Fetches the full user profile from /api/auth/me. Called after login and
|
|
/// when MainTabView appears, so admin/founder/role info shows up.
|
|
func loadProfile() async {
|
|
guard token != nil else { return }
|
|
do {
|
|
let me: UserProfile = try await APIClient.shared.get("/api/auth/me")
|
|
self.profile = me
|
|
if let premium = me.isPremium {
|
|
self.isPremium = premium
|
|
}
|
|
} catch {
|
|
// Profil-Refresh ist non-critical; Settings zeigt sonst nur die Basics.
|
|
// Loggen ist trotzdem nützlich, damit Decode-Fehler nicht stillschweigend untergehen.
|
|
print("loadProfile failed: \(error)")
|
|
}
|
|
}
|
|
}
|