Connect macOS app to backend device bootstrap

This commit is contained in:
diyaa
2026-05-25 16:07:04 +02:00
parent 6d426f2a54
commit 1674174a4e
7 changed files with 724 additions and 44 deletions
@@ -49,6 +49,48 @@ public struct DeviceRegistrationPayload: Codable, Hashable, Sendable {
}
}
public struct DeviceRegistrationResponse: Codable, Hashable, Sendable {
public var deviceId: String
public var bootstrapToken: String
public var serverTime: String
public init(
deviceId: String,
bootstrapToken: String,
serverTime: String
) {
self.deviceId = deviceId
self.bootstrapToken = bootstrapToken
self.serverTime = serverTime
}
}
public struct DeviceHeartbeatPayload: Codable, Hashable, Sendable {
public var deviceId: String
public var appVersion: String
public init(
deviceId: String,
appVersion: String
) {
self.deviceId = deviceId
self.appVersion = appVersion
}
}
public struct DeviceHeartbeatResponse: Codable, Hashable, Sendable {
public var ok: Bool
public var serverTime: String
public init(
ok: Bool,
serverTime: String
) {
self.ok = ok
self.serverTime = serverTime
}
}
public struct SyncCursor: Codable, Hashable, Sendable {
public var value: String
@@ -57,12 +99,109 @@ public struct SyncCursor: Codable, Hashable, Sendable {
}
}
public struct SyncEvent: Codable, Hashable, Sendable {
public var entityType: String
public var entityId: String
public var action: String
public var eventId: String
public init(
entityType: String,
entityId: String,
action: String,
eventId: String
) {
self.entityType = entityType
self.entityId = entityId
self.action = action
self.eventId = eventId
}
}
public struct SyncBootstrapResponse: Codable, Hashable, Sendable {
public var nextCursor: SyncCursor
public var tracks: [LibraryTrack]
public var events: [SyncEvent]
public var deletedTrackIds: [String]
public var serverTime: String
public init(
nextCursor: SyncCursor,
tracks: [LibraryTrack],
events: [SyncEvent],
deletedTrackIds: [String],
serverTime: String
) {
self.nextCursor = nextCursor
self.tracks = tracks
self.events = events
self.deletedTrackIds = deletedTrackIds
self.serverTime = serverTime
}
private enum CodingKeys: String, CodingKey {
case nextCursor
case tracks
case events
case deletedTrackIds
case serverTime
}
private struct WireTrack: Codable {
var id: String?
var title: String?
var artist: String?
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let wireTracks = try container.decode([WireTrack].self, forKey: .tracks)
nextCursor = SyncCursor(
value: try container.decode(String.self, forKey: .nextCursor)
)
tracks = wireTracks.map { track in
LibraryTrack(
id: track.id ?? UUID().uuidString,
title: track.title ?? "Unknown Title",
artist: track.artist ?? "Unknown Artist",
album: nil,
durationSeconds: nil,
localFilePath: "",
sha256: nil
)
}
events = try container.decode([SyncEvent].self, forKey: .events)
deletedTrackIds = try container.decode([String].self, forKey: .deletedTrackIds)
serverTime = try container.decode(String.self, forKey: .serverTime)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
let wireTracks = tracks.map { track in
WireTrack(
id: track.id,
title: track.title,
artist: track.artist
)
}
try container.encode(nextCursor.value, forKey: .nextCursor)
try container.encode(wireTracks, forKey: .tracks)
try container.encode(events, forKey: .events)
try container.encode(deletedTrackIds, forKey: .deletedTrackIds)
try container.encode(serverTime, forKey: .serverTime)
}
}
public struct ServerEnvironment: Codable, Hashable, Sendable {
public static let defaultLocalBaseURL = URL(string: "http://localhost:3000")!
public var baseURL: URL
public var appVersion: String
public init(
baseURL: URL,
baseURL: URL = ServerEnvironment.defaultLocalBaseURL,
appVersion: String
) {
self.baseURL = baseURL