@@ -9,9 +9,57 @@ import (
99
1010 "github.com/spf13/cobra"
1111
12+ "github.com/built-fast/vector-cli/internal/appctx"
1213 "github.com/built-fast/vector-cli/internal/output"
1314)
1415
16+ func uploadMultipart (cmd * cobra.Command , app * appctx.App , file * os.File , fileSize int64 , filename string , importID string , uploadParts []any ) ([]map [string ]any , error ) {
17+ w := cmd .ErrOrStderr ()
18+ partCount := int64 (len (uploadParts ))
19+ baseSize := fileSize / partCount
20+ lastSize := fileSize - baseSize * (partCount - 1 )
21+
22+ sizeMB := float64 (fileSize ) / (1024 * 1024 )
23+ _ , _ = fmt .Fprintf (w , "Uploading %s (%.1f MB) in %d parts...\n " , filename , sizeMB , partCount )
24+
25+ completedParts := make ([]map [string ]any , 0 , partCount )
26+
27+ for i , part := range uploadParts {
28+ partMap , ok := part .(map [string ]any )
29+ if ! ok {
30+ return nil , fmt .Errorf ("invalid upload part at index %d" , i )
31+ }
32+
33+ partNumber := int (getFloat (partMap , "part_number" ))
34+ partURL := getString (partMap , "url" )
35+ if partURL == "" {
36+ return nil , fmt .Errorf ("upload part %d missing URL" , partNumber )
37+ }
38+
39+ chunkSize := baseSize
40+ if i == len (uploadParts )- 1 {
41+ chunkSize = lastSize
42+ }
43+ offset := baseSize * int64 (i )
44+
45+ _ , _ = fmt .Fprintf (w , " Uploading part %d/%d...\n " , partNumber , partCount )
46+
47+ section := io .NewSectionReader (file , offset , chunkSize )
48+ etag , err := app .Client .PutFilePart (cmd .Context (), partURL , section , chunkSize )
49+ if err != nil {
50+ return nil , fmt .Errorf ("failed to upload part %d: %w" , partNumber , err )
51+ }
52+
53+ completedParts = append (completedParts , map [string ]any {
54+ "part_number" : partNumber ,
55+ "etag" : etag ,
56+ })
57+ }
58+
59+ _ , _ = fmt .Fprintln (w , "Upload complete." )
60+ return completedParts , nil
61+ }
62+
1563// NewArchiveCmd creates the archive command group.
1664func NewArchiveCmd () * cobra.Command {
1765 cmd := & cobra.Command {
@@ -116,40 +164,59 @@ func newArchiveImportCmd() *cobra.Command {
116164 }
117165
118166 importID := getString (item , "id" )
119- uploadURL := getString (item , "upload_url" )
120-
121- if importID == "" || uploadURL == "" {
122- return fmt .Errorf ("import session response missing upload URL or import ID" )
167+ if importID == "" {
168+ return fmt .Errorf ("import session response missing import ID" )
123169 }
124170
125- // Step 2: Upload file to presigned URL
126- sizeMB := float64 (fileSize ) / (1024 * 1024 )
127- _ , _ = fmt .Fprintf (w , "Uploading %s (%.1f MB)...\n " , filename , sizeMB )
171+ // Step 2: Upload file
172+ var runBody any
128173
129- uploadResp , err := app . Client . PutFile ( cmd . Context (), uploadURL , file )
130- if err != nil {
131- return fmt . Errorf ( "failed to upload file: %w" , err )
132- }
133- defer func () { _ = uploadResp . Body . Close () }()
174+ if getBool ( item , "is_multipart" ) {
175+ uploadParts := getSlice ( item , "upload_parts" )
176+ if len ( uploadParts ) == 0 {
177+ return fmt . Errorf ( "multipart import session response missing upload parts" )
178+ }
134179
135- _ , _ = fmt .Fprintln (w , "Upload complete." )
180+ completedParts , uploadErr := uploadMultipart (cmd , app , file , fileSize , filename , importID , uploadParts )
181+ if uploadErr != nil {
182+ return uploadErr
183+ }
184+
185+ runBody = map [string ]any {"parts" : completedParts }
186+ } else {
187+ uploadURL := getString (item , "upload_url" )
188+ if uploadURL == "" {
189+ return fmt .Errorf ("import session response missing upload URL" )
190+ }
191+
192+ sizeMB := float64 (fileSize ) / (1024 * 1024 )
193+ _ , _ = fmt .Fprintf (w , "Uploading %s (%.1f MB)...\n " , filename , sizeMB )
194+
195+ uploadResp , uploadErr := app .Client .PutFile (cmd .Context (), uploadURL , file )
196+ if uploadErr != nil {
197+ return fmt .Errorf ("failed to upload file: %w" , uploadErr )
198+ }
199+ defer func () { _ = uploadResp .Body .Close () }()
200+
201+ _ , _ = fmt .Fprintln (w , "Upload complete." )
202+ }
136203
137204 // Step 3: Trigger import
138205 _ , _ = fmt .Fprintln (w , "Starting import..." )
139206
140207 runEndpoint := fmt .Sprintf ("%s/%s/run" , importsPath (siteID ), importID )
141- runResp , err := app .Client .Post (cmd .Context (), runEndpoint , nil )
208+ runResp , err := app .Client .Post (cmd .Context (), runEndpoint , runBody )
142209 if err != nil {
143210 return fmt .Errorf ("failed to start import: %w" , err )
144211 }
145212 defer func () { _ = runResp .Body .Close () }()
146213
147- runBody , err := io .ReadAll (runResp .Body )
214+ runRespBody , err := io .ReadAll (runResp .Body )
148215 if err != nil {
149216 return fmt .Errorf ("failed to start import: %w" , err )
150217 }
151218
152- runData , err := parseResponseData (runBody )
219+ runData , err := parseResponseData (runRespBody )
153220 if err != nil {
154221 return fmt .Errorf ("failed to start import: %w" , err )
155222 }
0 commit comments