import Foundation struct MicroblogClient: PostingService { let account: Account private let token: String private static let micropubURL = "https://micro.blog/micropub" private static let mediaURL = "https://micro.blog/micropub/media" init(account: Account, token: String) { self.account = account self.token = token } func uploadMedia(imageData: Data, filename: String, altText: String?) async throws -> String { guard let url = URL(string: Self.mediaURL) else { throw PostingError(message: "Invalid Micro.blog media URL.") } var request = URLRequest(url: url) request.httpMethod = "POST" request.timeoutInterval = 30 request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") var form = MultipartFormData() form.addFile(name: "file", filename: filename, mimeType: mimeTypeFor(filename), data: imageData) request.setValue(form.contentType, forHTTPHeaderField: "Content-Type") request.httpBody = form.finalized let (_, httpResponse) = try await NetworkSupport.data( for: request, context: "Micro.blog media upload" ) let statusCode = httpResponse.statusCode guard (200...202).contains(statusCode) else { throw PostingError(message: "Micro.blog media upload failed (\(statusCode))") } guard let location = httpResponse.value(forHTTPHeaderField: "Location") else { throw PostingError(message: "No Location header in Micro.blog media response") } return location } func createPost(text: String, mediaIDs: [String]) async throws -> URL { // mediaIDs are image URLs for Micro.blog guard let url = URL(string: Self.micropubURL) else { throw PostingError(message: "Invalid Micro.blog post URL.") } var request = URLRequest(url: url) request.httpMethod = "POST" request.timeoutInterval = 30 request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") if mediaIDs.isEmpty { // Simple form-encoded post request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") var components = URLComponents() components.queryItems = [ URLQueryItem(name: "h", value: "entry"), URLQueryItem(name: "content", value: text), ] let params = components.percentEncodedQuery ?? "h=entry&content=" request.httpBody = params.data(using: .utf8) } else { // JSON with HTML content including images request.setValue("application/json", forHTTPHeaderField: "Content-Type") var htmlContent = "
\(text)
" for imageURL in mediaIDs { htmlContent += "