Replace manual backend config with auto JWT auth
Some checks failed
CI / build-and-deploy (push) Failing after 6s

Device auto-registers on first launch using hardcoded backend URL
and anon key (MarksAuth.swift). 90-day JWT stored in UserDefaults,
refreshed transparently. No settings fields needed — ClaudeService
is always active and non-optional throughout the app.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krishna Kumar
2026-05-22 14:14:54 -05:00
parent 9f0a10d228
commit 525e6557c8
11 changed files with 143 additions and 137 deletions

View File

@@ -16,23 +16,18 @@ final class BookmarksViewModel {
var unreadFilter = false
private let api: LinkdingAPI
private(set) var claude: ClaudeService?
let claude = ClaudeService()
private var enrichTask: Task<Void, Never>?
private let cacheFileUrl: URL
init(api: LinkdingAPI, claude: ClaudeService?, cacheKey: String = "") {
init(api: LinkdingAPI, cacheKey: String = "") {
self.api = api
self.claude = claude
let dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
let safe = cacheKey.replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: ":", with: "_")
let name = safe.isEmpty ? "bookmarks_cache" : "bookmarks_\(safe)"
self.cacheFileUrl = dir.appendingPathComponent("\(name).json")
}
func updateClaude(_ service: ClaudeService?) {
claude = service
}
func load(useCache: Bool = true) async {
if useCache { loadFromCache() }
isLoading = true
@@ -83,7 +78,7 @@ final class BookmarksViewModel {
}
func semanticSearch() async {
guard let claude, !searchQuery.isEmpty else { return }
guard !searchQuery.isEmpty else { return }
isLoading = true
do {
let allResponse = try await api.fetchBookmarks(limit: 200, unread: unreadFilter)
@@ -102,8 +97,7 @@ final class BookmarksViewModel {
let create = BookmarkCreate(url: url, title: title, tagNames: tags, isArchived: false, unread: false, shared: false)
let bookmark = try await api.createBookmark(create)
bookmarks.insert(bookmark, at: 0)
print("[AI] addBookmark: saved id=\(bookmark.id) claude=\(claude != nil ? "present" : "nil")")
guard let claude else { return }
print("[AI] addBookmark: saved id=\(bookmark.id)")
Task {
print("[AI] addBookmark: starting enrich for id=\(bookmark.id) url=\(bookmark.url)")
do {
@@ -159,7 +153,7 @@ final class BookmarksViewModel {
}
func generateSmartCollections() async {
guard let claude, !bookmarks.isEmpty else { return }
guard !bookmarks.isEmpty else { return }
isGeneratingCollections = true
do {
let allResponse = try await api.fetchBookmarks(limit: 200)
@@ -171,7 +165,6 @@ final class BookmarksViewModel {
}
func enrichAll() async {
guard let claude else { return }
let toEnrich = bookmarks.indices.filter { bookmarks[$0].aiSummary == nil }
guard !toEnrich.isEmpty else { return }
enrichmentProgress = 0
@@ -191,10 +184,6 @@ final class BookmarksViewModel {
// Silently enrich new bookmarks that lack summaries (background, non-blocking)
private func startEnrichment() {
guard claude != nil else {
print("[AI] startEnrichment: skipped (no claude)")
return
}
enrichTask?.cancel()
enrichTask = Task { [weak self] in
guard let self else { return }
@@ -202,7 +191,7 @@ final class BookmarksViewModel {
print("[AI] startEnrichment: \(indices.count) bookmarks to enrich")
for i in indices {
guard !Task.isCancelled else { return }
guard let claude = await MainActor.run(body: { self.claude }) else { return }
let claude = await MainActor.run(body: { self.claude })
do {
let bm = await MainActor.run { bookmarks[i] }
print("[AI] startEnrichment: enriching id=\(bm.id) \(bm.url)")