summaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-07-16 01:06:46 -0500
committerChristian Cleberg <[email protected]>2026-07-16 01:06:46 -0500
commit871b04159aa47c0b0e62e2520c2f30e81f0f024b (patch)
tree5dcc31ad97b094f52a59a539ae3950ae0dc2ab4c /Hutch/Views/Repositories/RepositoryDetailViewModel.swift
parentd32ad837ac9eb153598c10c3cf47fbdb4e51e3ba (diff)
downloadhutch-871b04159aa47c0b0e62e2520c2f30e81f0f024b.tar.gz
hutch-871b04159aa47c0b0e62e2520c2f30e81f0f024b.tar.bz2
hutch-871b04159aa47c0b0e62e2520c2f30e81f0f024b.zip
fix: download artifacts through the API instead of handing them to Safari
Tapping download opened Artifact.url in the browser, which answered with "Authorization header is required". That URL is not a web page: git.sr.ht resolves it to <api origin>/query/artifact/<checksum>/<filename>, which demands a bearer token. Safari has none and no way to get one, so the download could never have worked — this predates the upload work. Fetch it with the client that already holds the token and hand the user the file through a share sheet. fetchData mirrors fetchText, including its host guard, so an authenticated request still cannot be aimed anywhere but *.sr.ht over https. Also guards zero-byte uploads. sr.ht streams into S3, which rejects a zero-part multipart completion with "MalformedXML: UnknownError" — an error that says nothing about the cause and cost a round of testing to identify. Empty files are now refused by name before the request is made.
Diffstat (limited to 'Hutch/Views/Repositories/RepositoryDetailViewModel.swift')
-rw-r--r--Hutch/Views/Repositories/RepositoryDetailViewModel.swift31
1 files changed, 31 insertions, 0 deletions
diff --git a/Hutch/Views/Repositories/RepositoryDetailViewModel.swift b/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
index dce39c1..9b6b942 100644
--- a/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
+++ b/Hutch/Views/Repositories/RepositoryDetailViewModel.swift
@@ -542,6 +542,14 @@ final class RepositoryDetailViewModel {
return false
}
+ // sr.ht streams the upload into S3, which rejects a zero-part multipart
+ // completion with "MalformedXML" — an error that says nothing about the
+ // actual problem. Catch it here where we can name it.
+ guard !fileData.isEmpty else {
+ self.error = "\(fileURL.lastPathComponent) is empty. SourceHut rejects zero-byte artifacts."
+ return false
+ }
+
do {
_ = try await client.executeMultipart(
service: service,
@@ -567,6 +575,29 @@ final class RepositoryDetailViewModel {
}
}
+ /// Downloads an artifact and returns a local file URL to share.
+ ///
+ /// `Artifact.url` points at the API origin, not the web one, and returns an
+ /// auth error to anything without a bearer token — so it cannot be opened in
+ /// a browser. Fetch it here and hand the user the file instead.
+ func downloadArtifact(_ artifact: ArtifactInfo) async -> URL? {
+ guard !isMutatingArtifact else { return nil }
+ isMutatingArtifact = true
+ error = nil
+ defer { isMutatingArtifact = false }
+
+ do {
+ let data = try await client.fetchData(url: artifact.url)
+ let destination = FileManager.default.temporaryDirectory
+ .appendingPathComponent(artifact.filename)
+ try data.write(to: destination, options: .atomic)
+ return destination
+ } catch {
+ self.error = "Couldn't download \(artifact.filename). \(error.userFacingMessage)"
+ return nil
+ }
+ }
+
@discardableResult
func deleteArtifact(id: Int) async -> Bool {
guard !isMutatingArtifact else { return false }