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"
}
|