summaryrefslogtreecommitdiff
path: root/makima/ios/Sources/Makima/Net/ServerProfile.swift
blob: 75a6ab1eee175e7fde66334a64ca40f4bca0c00b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import Foundation

/// A configured Makima server.
/// Stored as a list in UserDefaults; the API key lives in Keychain keyed by
/// `keychainID`. Allows future multi-profile support without migration.
struct ServerProfile: Codable, Identifiable, Hashable {
    let id: UUID
    var label: String           // "makima.jp" or user-named
    var baseURLString: String   // e.g. "https://makima.jp"
    var lastConnectedAt: Date?

    init(id: UUID = UUID(), label: String, baseURLString: String, lastConnectedAt: Date? = nil) {
        self.id = id
        self.label = label
        self.baseURLString = baseURLString
        self.lastConnectedAt = lastConnectedAt
    }

    /// Validated URL, or nil if malformed. Strips trailing slash.
    var baseURL: URL? {
        let trimmed = baseURLString.trimmingCharacters(in: .whitespacesAndNewlines)
            .trimmingCharacters(in: CharacterSet(charactersIn: "/"))
        guard let url = URL(string: trimmed) else { return nil }
        guard let scheme = url.scheme?.lowercased(),
              scheme == "https" || scheme == "http" else { return nil }
        return url
    }

    /// API root: `<base>/api/v1`
    var apiBaseURL: URL? {
        baseURL?.appendingPathComponent("api/v1")
    }

    /// WebSocket scheme version of `apiBaseURL`.
    var apiWebSocketBaseURL: URL? {
        guard let url = apiBaseURL,
              var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        else { return nil }
        switch components.scheme {
        case "http":  components.scheme = "ws"
        case "https": components.scheme = "wss"
        default: return nil
        }
        return components.url
    }

    /// Stable Keychain identifier — never changes after the profile is minted.
    var keychainID: String { "profile.\(id.uuidString)" }

    static let defaultLabel = "makima.jp"
    static let defaultBaseURL = "https://makima.jp"
}