Switch to prefixed IDs and tell agents which Mac a message came from
This commit is contained in:
@@ -101,7 +101,7 @@ func errorText(_ error: any Error) -> String {
|
||||
return String(describing: e)
|
||||
}
|
||||
|
||||
private let clientID = UUID().uuidString
|
||||
private let clientID = newID("cli")
|
||||
let macName = Host.current().localizedName ?? ProcessInfo.processInfo.hostName
|
||||
|
||||
final class API: Sendable {
|
||||
|
||||
@@ -545,7 +545,7 @@ struct ComposerView: View {
|
||||
let imgs = images.take(draftKey)
|
||||
text = ""
|
||||
let sent = drafts.startSend(draftKey, value, imgs.count)
|
||||
let sendID = UUID().uuidString
|
||||
let sendID = newID("snd")
|
||||
let cancel = chat.team ? { true } : chat.builder.showPending(value, imgs, sendID)
|
||||
let sessionId = m.live?.id
|
||||
Task {
|
||||
|
||||
@@ -38,6 +38,45 @@ final class DraftSend {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func migrateRecordIDs() {
|
||||
var changed = false
|
||||
for v in fetchRecords(FetchDescriptor<VisitRecord>()) where migratedID("tsk", v.taskId) != v.taskId {
|
||||
v.taskId = migratedID("tsk", v.taskId)
|
||||
changed = true
|
||||
}
|
||||
let composerKey = { (key: String) in
|
||||
key.hasPrefix("message:") ? "message:" + migratedID("tsk", String(key.dropFirst("message:".count))) : migratedID("tsk", key)
|
||||
}
|
||||
let task = { (key: String) in migratedID("tsk", key) }
|
||||
let item = { (key: String) in migratedID("inb", key) }
|
||||
func rekey<V>(_ d: [String: V], _ f: (String) -> String) -> [String: V] {
|
||||
Dictionary(d.map { (f($0.key), $0.value) }) { a, _ in a }
|
||||
}
|
||||
for record in fetchRecords(FetchDescriptor<DraftRecord>()) {
|
||||
guard var saved = try? JSONDecoder().decode(Saved.self, from: record.json) else {
|
||||
continue
|
||||
}
|
||||
let comments = saved.comments ?? [:]
|
||||
let moved = saved.composer.keys.contains { composerKey($0) != $0 } || saved.checked.keys.contains { item($0) != $0 } || saved.texts.keys.contains { item($0) != $0 } || comments.keys.contains { task($0) != $0 }
|
||||
guard moved else {
|
||||
continue
|
||||
}
|
||||
saved.composer = rekey(saved.composer, composerKey)
|
||||
saved.checked = rekey(saved.checked, item)
|
||||
saved.texts = rekey(saved.texts, item)
|
||||
saved.comments = saved.comments.map { rekey($0, task) }
|
||||
guard let json = try? JSONEncoder().encode(saved) else {
|
||||
continue
|
||||
}
|
||||
record.json = json
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
saveRecords()
|
||||
}
|
||||
}
|
||||
|
||||
func draftOrigin(_ origin: URL) -> String {
|
||||
let c = URLComponents(url: origin, resolvingAgainstBaseURL: false)!
|
||||
let scheme = (c.scheme ?? "https").lowercased()
|
||||
|
||||
@@ -28,7 +28,7 @@ final class Forwards {
|
||||
}
|
||||
var c = URLComponents()
|
||||
c.scheme = "http"
|
||||
c.host = "\(slug(key.name).isEmpty ? "preview" : slug(key.name))-\(taskId.prefix(6)).localhost"
|
||||
c.host = "\(slug(key.name).isEmpty ? "preview" : slug(key.name))-\(taskId.lowercased()).localhost"
|
||||
c.port = local
|
||||
c.path = "/"
|
||||
return c.url
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
|
||||
private let idAlphabet = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||
|
||||
func newID(_ prefix: String) -> String {
|
||||
prefix + String((0..<11).map { _ in idAlphabet.randomElement()! })
|
||||
}
|
||||
|
||||
// IDs from before prefixed IDs were 12 lowercase hex characters; the server renamed each to this same derivation.
|
||||
func migratedID(_ prefix: String, _ old: String) -> String {
|
||||
guard old.count == 12, old.allSatisfy({ "0123456789abcdef".contains($0) }) else {
|
||||
return old
|
||||
}
|
||||
var n = SHA256.hash(data: Data(old.utf8)).prefix(8).reduce(UInt64(0)) { $0 << 8 | UInt64($1) }
|
||||
var out = ""
|
||||
for _ in 0..<11 {
|
||||
out.append(idAlphabet[Int(n % UInt64(idAlphabet.count))])
|
||||
n /= UInt64(idAlphabet.count)
|
||||
}
|
||||
return prefix + out
|
||||
}
|
||||
@@ -21,6 +21,7 @@ final class Servers {
|
||||
@ObservationIgnored var openWindow: (() -> Void)?
|
||||
|
||||
private init() {
|
||||
migrateRecordIDs()
|
||||
load()
|
||||
current = origins.first
|
||||
Task {
|
||||
@@ -30,6 +31,7 @@ final class Servers {
|
||||
}
|
||||
synced(event, end)
|
||||
if event.type == .import {
|
||||
migrateRecordIDs()
|
||||
for connection in connections.values {
|
||||
connection.store?.drafts?.reload()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user