Skip to content

Commit 8f3603b

Browse files
ndbroadbentclaude
andcommitted
Fix API endpoints to match SaaS app
Use same endpoints as web app instead of non-existent /api/desktop/* paths: - /api/upload/presign - Get pre-signed URL and job_id - /api/upload/complete - Complete upload and start processing Updated request/response handling to match SaaS API format: - Parse wrapped { success, data, error } response format - Removed unused file_key, source, chat_count, message_count params - complete_upload() now just takes job_id 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent d53616f commit 8f3603b

File tree

2 files changed

+78
-43
lines changed

2 files changed

+78
-43
lines changed

src-tauri/src/main.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use chat_to_map_desktop::{
88
export::{export_chats, ExportProgress},
99
list_chats as lib_list_chats,
1010
screenshot::{capture_window, ScreenshotConfig},
11-
upload::{create_job, get_presigned_url, get_results_url, upload_file},
11+
upload::{complete_upload, get_presigned_url, get_results_url, upload_file},
1212
ChatInfo,
1313
};
1414
use clap::Parser;
@@ -138,16 +138,12 @@ async fn export_and_upload(
138138
.await
139139
.map_err(|e| format!("Upload failed: {e}"))?;
140140

141-
// Stage 4: Create job (90-95%)
142-
emit("Processing", 90, "Creating processing job...");
141+
// Stage 4: Complete upload and start processing (90-95%)
142+
emit("Processing", 90, "Starting processing...");
143143

144-
let job_response = create_job(
145-
&presign_response.file_key,
146-
export_result.chat_count,
147-
export_result.total_messages,
148-
)
149-
.await
150-
.map_err(|e| format!("Failed to create job: {e}"))?;
144+
let job_response = complete_upload(&presign_response.job_id)
145+
.await
146+
.map_err(|e| format!("Failed to start processing: {e}"))?;
151147

152148
// Stage 5: Complete (95-100%)
153149
let results_url = get_results_url(&job_response.job_id);

src-tauri/src/upload.rs

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,43 @@ use serde::{Deserialize, Serialize};
1313
// Types
1414
// =============================================================================
1515

16-
/// Response from the pre-sign endpoint
16+
/// Generic API response wrapper
1717
#[derive(Debug, Deserialize)]
18+
struct ApiResponse<T> {
19+
success: bool,
20+
data: Option<T>,
21+
error: Option<String>,
22+
}
23+
24+
/// Data from the pre-sign endpoint
25+
#[derive(Debug, Deserialize)]
26+
struct PresignData {
27+
upload_url: String,
28+
job_id: String,
29+
}
30+
31+
/// Public presign response (flattened for caller convenience)
32+
#[derive(Debug)]
1833
pub struct PresignResponse {
19-
/// Pre-signed upload URL
2034
pub upload_url: String,
21-
/// Job ID for tracking
2235
pub job_id: String,
23-
/// File key in storage
24-
pub file_key: String,
2536
}
2637

27-
/// Request to create a job
38+
/// Request to complete upload
2839
#[derive(Debug, Serialize)]
29-
struct CreateJobRequest {
30-
file_key: String,
31-
source: String,
32-
chat_count: usize,
33-
message_count: usize,
40+
struct CompleteUploadRequest {
41+
job_id: String,
3442
}
3543

36-
/// Response from the create job endpoint
44+
/// Data from the complete endpoint
3745
#[derive(Debug, Deserialize)]
46+
struct CompleteData {
47+
job_id: String,
48+
status: String,
49+
}
50+
51+
/// Public complete response
52+
#[derive(Debug)]
3853
pub struct CreateJobResponse {
3954
pub job_id: String,
4055
pub status: String,
@@ -61,11 +76,12 @@ pub const SERVER_BASE_URL: &str = "https://chattomap.com";
6176
/// Request a pre-signed upload URL from the server
6277
pub async fn get_presigned_url() -> Result<PresignResponse, String> {
6378
let client = Client::new();
64-
let url = format!("{}/api/desktop/presign", SERVER_BASE_URL);
79+
let url = format!("{}/api/upload/presign", SERVER_BASE_URL);
6580

6681
let response = client
6782
.post(&url)
6883
.header("Content-Type", "application/json")
84+
.body("{}")
6985
.send()
7086
.await
7187
.map_err(|e| format!("Failed to request presigned URL: {e}"))?;
@@ -80,10 +96,25 @@ pub async fn get_presigned_url() -> Result<PresignResponse, String> {
8096
));
8197
}
8298

83-
response
84-
.json::<PresignResponse>()
99+
let api_response: ApiResponse<PresignData> = response
100+
.json()
85101
.await
86-
.map_err(|e| format!("Failed to parse presign response: {e}"))
102+
.map_err(|e| format!("Failed to parse presign response: {e}"))?;
103+
104+
if !api_response.success {
105+
return Err(api_response
106+
.error
107+
.unwrap_or_else(|| "Unknown error".to_string()));
108+
}
109+
110+
let data = api_response
111+
.data
112+
.ok_or("Missing data in presign response")?;
113+
114+
Ok(PresignResponse {
115+
upload_url: data.upload_url,
116+
job_id: data.job_id,
117+
})
87118
}
88119

89120
/// Upload a file to the pre-signed URL
@@ -135,43 +166,51 @@ pub async fn upload_file(
135166
Ok(())
136167
}
137168

138-
/// Notify server that upload is complete and create processing job
139-
pub async fn create_job(
140-
file_key: &str,
141-
chat_count: usize,
142-
message_count: usize,
143-
) -> Result<CreateJobResponse, String> {
169+
/// Notify server that upload is complete and start processing
170+
pub async fn complete_upload(job_id: &str) -> Result<CreateJobResponse, String> {
144171
let client = Client::new();
145-
let url = format!("{}/api/desktop/job", SERVER_BASE_URL);
172+
let url = format!("{}/api/upload/complete", SERVER_BASE_URL);
146173

147-
let request = CreateJobRequest {
148-
file_key: file_key.to_string(),
149-
source: "imessage".to_string(),
150-
chat_count,
151-
message_count,
174+
let request = CompleteUploadRequest {
175+
job_id: job_id.to_string(),
152176
};
153177

154178
let response = client
155179
.post(&url)
156180
.json(&request)
157181
.send()
158182
.await
159-
.map_err(|e| format!("Failed to create job: {e}"))?;
183+
.map_err(|e| format!("Failed to complete upload: {e}"))?;
160184

161185
if !response.status().is_success() {
162186
let status = response.status();
163187
let body = response.text().await.unwrap_or_default();
164188
return Err(format!(
165-
"Create job failed {}: {}",
189+
"Complete upload failed {}: {}",
166190
status,
167191
sanitize_error_body(&body)
168192
));
169193
}
170194

171-
response
172-
.json::<CreateJobResponse>()
195+
let api_response: ApiResponse<CompleteData> = response
196+
.json()
173197
.await
174-
.map_err(|e| format!("Failed to parse job response: {e}"))
198+
.map_err(|e| format!("Failed to parse complete response: {e}"))?;
199+
200+
if !api_response.success {
201+
return Err(api_response
202+
.error
203+
.unwrap_or_else(|| "Unknown error".to_string()));
204+
}
205+
206+
let data = api_response
207+
.data
208+
.ok_or("Missing data in complete response")?;
209+
210+
Ok(CreateJobResponse {
211+
job_id: data.job_id,
212+
status: data.status,
213+
})
175214
}
176215

177216
/// Get the results page URL for a job

0 commit comments

Comments
 (0)