gitlab.com/SiaPrime/SiaPrime@v1.4.1/node/api/client/renter.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io" 6 "net/url" 7 "strconv" 8 "strings" 9 "time" 10 11 "gitlab.com/NebulousLabs/errors" 12 13 "gitlab.com/SiaPrime/SiaPrime/modules" 14 "gitlab.com/SiaPrime/SiaPrime/node/api" 15 "gitlab.com/SiaPrime/SiaPrime/types" 16 ) 17 18 type ( 19 // AllowanceRequestPost is a helper type to be able to build an allowance 20 // request. 21 AllowanceRequestPost struct { 22 c *Client 23 sent bool 24 values url.Values 25 } 26 ) 27 28 // RenterPostPartialAllowance starts an allowance request which can be extended 29 // using its methods. 30 func (c *Client) RenterPostPartialAllowance() *AllowanceRequestPost { 31 return &AllowanceRequestPost{c: c, values: make(url.Values)} 32 } 33 34 // WithFunds adds the funds field to the request. 35 func (a *AllowanceRequestPost) WithFunds(funds types.Currency) *AllowanceRequestPost { 36 a.values.Set("funds", funds.String()) 37 return a 38 } 39 40 // WithHosts adds the hosts field to the request. 41 func (a *AllowanceRequestPost) WithHosts(hosts uint64) *AllowanceRequestPost { 42 a.values.Set("hosts", fmt.Sprint(hosts)) 43 return a 44 } 45 46 // WithPeriod adds the period field to the request. 47 func (a *AllowanceRequestPost) WithPeriod(period types.BlockHeight) *AllowanceRequestPost { 48 a.values.Set("period", fmt.Sprint(period)) 49 return a 50 } 51 52 // WithRenewWindow adds the renewwindow field to the request. 53 func (a *AllowanceRequestPost) WithRenewWindow(renewWindow types.BlockHeight) *AllowanceRequestPost { 54 a.values.Set("renewwindow", fmt.Sprint(renewWindow)) 55 return a 56 } 57 58 // WithExpectedStorage adds the expected storage field to the request. 59 func (a *AllowanceRequestPost) WithExpectedStorage(expectedStorage uint64) *AllowanceRequestPost { 60 a.values.Set("expectedstorage", fmt.Sprint(expectedStorage)) 61 return a 62 } 63 64 // WithExpectedUpload adds the expected upload field to the request. 65 func (a *AllowanceRequestPost) WithExpectedUpload(expectedUpload uint64) *AllowanceRequestPost { 66 a.values.Set("expectedupload", fmt.Sprint(expectedUpload)) 67 return a 68 } 69 70 // WithExpectedDownload adds the expected download field to the request. 71 func (a *AllowanceRequestPost) WithExpectedDownload(expectedDownload uint64) *AllowanceRequestPost { 72 a.values.Set("expecteddownload", fmt.Sprint(expectedDownload)) 73 return a 74 } 75 76 // WithExpectedRedundancy adds the expected redundancy field to the request. 77 func (a *AllowanceRequestPost) WithExpectedRedundancy(expectedRedundancy float64) *AllowanceRequestPost { 78 a.values.Set("expectedredundancy", fmt.Sprint(expectedRedundancy)) 79 return a 80 } 81 82 // FilterHostsSubnet is not in the Allowance but RenterSettings 83 // WithFilterHostsSubnet adds the filterhostssubnet field to the request. 84 //func (a *AllowanceRequestPost) WithFilterHostsSubnet(filterHostsSubnet bool) *AllowanceRequestPost { 85 // a.values.Set("filterhostssubnet", fmt.Sprint(filterHostsSubnet)) 86 // return a 87 //} 88 89 // Send finalizes and sends the request. 90 func (a *AllowanceRequestPost) Send() (err error) { 91 if a.sent { 92 return errors.New("Error, request already sent") 93 } 94 a.sent = true 95 err = a.c.post("/renter", a.values.Encode(), nil) 96 return 97 } 98 99 // escapeSiaPath escapes the siapath to make it safe to use within a URL. This 100 // should only be used on SiaPaths which are used as part of the URL path. 101 // Paths within the query have to be escaped with url.PathEscape. 102 func escapeSiaPath(siaPath modules.SiaPath) string { 103 sp := siaPath.String() 104 pathSegments := strings.Split(sp, "/") 105 escapedSegments := make([]string, 0, len(pathSegments)) 106 for _, segment := range pathSegments { 107 escapedSegments = append(escapedSegments, url.PathEscape(segment)) 108 } 109 return strings.Join(escapedSegments, "/") 110 } 111 112 // RenterContractCancelPost uses the /renter/contract/cancel endpoint to cancel 113 // a contract 114 func (c *Client) RenterContractCancelPost(id types.FileContractID) (err error) { 115 values := url.Values{} 116 values.Set("id", id.String()) 117 err = c.post("/renter/contract/cancel", values.Encode(), nil) 118 return 119 } 120 121 // RenterAllContractsGet requests the /renter/contracts resource with all 122 // options set to true 123 func (c *Client) RenterAllContractsGet() (rc api.RenterContracts, err error) { 124 values := url.Values{} 125 values.Set("disabled", fmt.Sprint(true)) 126 values.Set("expired", fmt.Sprint(true)) 127 values.Set("recoverable", fmt.Sprint(true)) 128 err = c.get("/renter/contracts?"+values.Encode(), &rc) 129 return 130 } 131 132 // RenterContractsGet requests the /renter/contracts resource and returns 133 // Contracts and ActiveContracts 134 func (c *Client) RenterContractsGet() (rc api.RenterContracts, err error) { 135 err = c.get("/renter/contracts", &rc) 136 return 137 } 138 139 // RenterDisabledContractsGet requests the /renter/contracts resource with the 140 // disabled flag set to true 141 func (c *Client) RenterDisabledContractsGet() (rc api.RenterContracts, err error) { 142 values := url.Values{} 143 values.Set("disabled", fmt.Sprint(true)) 144 err = c.get("/renter/contracts?"+values.Encode(), &rc) 145 return 146 } 147 148 // RenterInactiveContractsGet requests the /renter/contracts resource with the 149 // inactive flag set to true 150 func (c *Client) RenterInactiveContractsGet() (rc api.RenterContracts, err error) { 151 values := url.Values{} 152 values.Set("inactive", fmt.Sprint(true)) 153 err = c.get("/renter/contracts?"+values.Encode(), &rc) 154 return 155 } 156 157 // RenterInitContractRecoveryScanPost initializes a contract recovery scan 158 // using the /renter/recoveryscan endpoint. 159 func (c *Client) RenterInitContractRecoveryScanPost() (err error) { 160 err = c.post("/renter/recoveryscan", "", nil) 161 return 162 } 163 164 // RenterContractRecoveryProgressGet returns information about potentially 165 // ongoing contract recovery scans. 166 func (c *Client) RenterContractRecoveryProgressGet() (rrs api.RenterRecoveryStatusGET, err error) { 167 err = c.get("/renter/recoveryscan", &rrs) 168 return 169 } 170 171 // RenterExpiredContractsGet requests the /renter/contracts resource with the 172 // expired flag set to true 173 func (c *Client) RenterExpiredContractsGet() (rc api.RenterContracts, err error) { 174 values := url.Values{} 175 values.Set("expired", fmt.Sprint(true)) 176 err = c.get("/renter/contracts?"+values.Encode(), &rc) 177 return 178 } 179 180 // RenterRecoverableContractsGet requests the /renter/contracts resource with the 181 // recoverable flag set to true 182 func (c *Client) RenterRecoverableContractsGet() (rc api.RenterContracts, err error) { 183 values := url.Values{} 184 values.Set("recoverable", fmt.Sprint(true)) 185 err = c.get("/renter/contracts?"+values.Encode(), &rc) 186 return 187 } 188 189 // RenterCancelDownloadPost requests the /renter/download/cancel endpoint to 190 // cancel an ongoing doing. 191 func (c *Client) RenterCancelDownloadPost(id string) (err error) { 192 values := url.Values{} 193 values.Set("id", id) 194 err = c.post("/renter/download/cancel", values.Encode(), nil) 195 return 196 } 197 198 // RenterDeletePost uses the /renter/delete endpoint to delete a file. 199 func (c *Client) RenterDeletePost(siaPath modules.SiaPath) (err error) { 200 sp := escapeSiaPath(siaPath) 201 err = c.post(fmt.Sprintf("/renter/delete/%s", sp), "", nil) 202 return 203 } 204 205 // RenterDownloadGet uses the /renter/download endpoint to download a file to a 206 // destination on disk. 207 func (c *Client) RenterDownloadGet(siaPath modules.SiaPath, destination string, offset, length uint64, async bool) (string, error) { 208 sp := escapeSiaPath(siaPath) 209 values := url.Values{} 210 values.Set("destination", destination) 211 values.Set("offset", fmt.Sprint(offset)) 212 values.Set("length", fmt.Sprint(length)) 213 values.Set("async", fmt.Sprint(async)) 214 h, _, err := c.getRawResponse(fmt.Sprintf("/renter/download/%s?%s", sp, values.Encode())) 215 return h.Get("ID"), err 216 } 217 218 // RenterBackups lists the backups the renter has uploaded to hosts. 219 func (c *Client) RenterBackups() (ubs api.RenterBackupsGET, err error) { 220 err = c.get("/renter/backups", &ubs) 221 return 222 } 223 224 // RenterCreateBackupPost creates a backup of the SiaFiles of the renter and 225 // uploads it to hosts. 226 func (c *Client) RenterCreateBackupPost(name string) (err error) { 227 values := url.Values{} 228 values.Set("name", name) 229 err = c.post("/renter/backups/create", values.Encode(), nil) 230 return 231 } 232 233 // RenterRecoverBackupPost downloads and restores the specified backup. 234 func (c *Client) RenterRecoverBackupPost(name string) (err error) { 235 values := url.Values{} 236 values.Set("name", name) 237 err = c.post("/renter/backups/restore", values.Encode(), nil) 238 return 239 } 240 241 // RenterCreateLocalBackupPost creates a local backup of the SiaFiles of the 242 // renter. 243 // 244 // Deprecated: Use RenterCreateBackupPost instead. 245 func (c *Client) RenterCreateLocalBackupPost(dst string) (err error) { 246 values := url.Values{} 247 values.Set("destination", dst) 248 err = c.post("/renter/backup", values.Encode(), nil) 249 return 250 } 251 252 // RenterRecoverLocalBackupPost restores the specified backup. 253 // 254 // Deprecated: Use RenterCreateBackupPost instead. 255 func (c *Client) RenterRecoverLocalBackupPost(src string) (err error) { 256 values := url.Values{} 257 values.Set("source", src) 258 err = c.post("/renter/recoverbackup", values.Encode(), nil) 259 return 260 } 261 262 // RenterDownloadFullGet uses the /renter/download endpoint to download a full 263 // file. 264 func (c *Client) RenterDownloadFullGet(siaPath modules.SiaPath, destination string, async bool) (string, error) { 265 sp := escapeSiaPath(siaPath) 266 values := url.Values{} 267 values.Set("destination", destination) 268 values.Set("httpresp", fmt.Sprint(false)) 269 values.Set("async", fmt.Sprint(async)) 270 h, _, err := c.getRawResponse(fmt.Sprintf("/renter/download/%s?%s", sp, values.Encode())) 271 return h.Get("ID"), err 272 } 273 274 // RenterClearAllDownloadsPost requests the /renter/downloads/clear resource 275 // with no parameters 276 func (c *Client) RenterClearAllDownloadsPost() (err error) { 277 err = c.post("/renter/downloads/clear", "", nil) 278 return 279 } 280 281 // RenterClearDownloadsAfterPost requests the /renter/downloads/clear resource 282 // with only the after timestamp provided 283 func (c *Client) RenterClearDownloadsAfterPost(after time.Time) (err error) { 284 values := url.Values{} 285 values.Set("after", strconv.FormatInt(after.UnixNano(), 10)) 286 err = c.post("/renter/downloads/clear", values.Encode(), nil) 287 return 288 } 289 290 // RenterClearDownloadsBeforePost requests the /renter/downloads/clear resource 291 // with only the before timestamp provided 292 func (c *Client) RenterClearDownloadsBeforePost(before time.Time) (err error) { 293 values := url.Values{} 294 values.Set("before", strconv.FormatInt(before.UnixNano(), 10)) 295 err = c.post("/renter/downloads/clear", values.Encode(), nil) 296 return 297 } 298 299 // RenterClearDownloadsRangePost requests the /renter/downloads/clear resource 300 // with both before and after timestamps provided 301 func (c *Client) RenterClearDownloadsRangePost(after, before time.Time) (err error) { 302 values := url.Values{} 303 values.Set("before", strconv.FormatInt(before.UnixNano(), 10)) 304 values.Set("after", strconv.FormatInt(after.UnixNano(), 10)) 305 err = c.post("/renter/downloads/clear", values.Encode(), nil) 306 return 307 } 308 309 // RenterDownloadsGet requests the /renter/downloads resource 310 func (c *Client) RenterDownloadsGet() (rdq api.RenterDownloadQueue, err error) { 311 err = c.get("/renter/downloads", &rdq) 312 return 313 } 314 315 // RenterDownloadHTTPResponseGet uses the /renter/download endpoint to download 316 // a file and return its data. 317 func (c *Client) RenterDownloadHTTPResponseGet(siaPath modules.SiaPath, offset, length uint64) (resp []byte, err error) { 318 sp := escapeSiaPath(siaPath) 319 values := url.Values{} 320 values.Set("offset", fmt.Sprint(offset)) 321 values.Set("length", fmt.Sprint(length)) 322 values.Set("httpresp", fmt.Sprint(true)) 323 _, resp, err = c.getRawResponse(fmt.Sprintf("/renter/download/%s?%s", sp, values.Encode())) 324 return 325 } 326 327 // RenterFileGet uses the /renter/file/:siapath endpoint to query a file. 328 func (c *Client) RenterFileGet(siaPath modules.SiaPath) (rf api.RenterFile, err error) { 329 sp := escapeSiaPath(siaPath) 330 err = c.get("/renter/file/"+sp, &rf) 331 return 332 } 333 334 // RenterFilesGet requests the /renter/files resource. 335 func (c *Client) RenterFilesGet(cached bool) (rf api.RenterFiles, err error) { 336 err = c.get("/renter/files?cached="+fmt.Sprint(cached), &rf) 337 return 338 } 339 340 // RenterGet requests the /renter resource. 341 func (c *Client) RenterGet() (rg api.RenterGET, err error) { 342 err = c.get("/renter", &rg) 343 return 344 } 345 346 // RenterPostAllowance uses the /renter endpoint to change the renter's allowance 347 func (c *Client) RenterPostAllowance(allowance modules.Allowance) error { 348 a := c.RenterPostPartialAllowance() 349 a = a.WithFunds(allowance.Funds) 350 a = a.WithHosts(allowance.Hosts) 351 a = a.WithPeriod(allowance.Period) 352 a = a.WithRenewWindow(allowance.RenewWindow) 353 // a = a.WithFilterHostsSubnet(allowance.FilterHostsSubnet) 354 a = a.WithExpectedStorage(allowance.ExpectedStorage) 355 a = a.WithExpectedUpload(allowance.ExpectedUpload) 356 a = a.WithExpectedDownload(allowance.ExpectedDownload) 357 a = a.WithExpectedRedundancy(allowance.ExpectedRedundancy) 358 return a.Send() 359 } 360 361 // RenterCancelAllowance uses the /renter endpoint to cancel the allowance. 362 func (c *Client) RenterCancelAllowance() (err error) { 363 err = c.RenterPostAllowance(modules.Allowance{}) 364 return 365 } 366 367 // RenterPricesGet requests the /renter/prices endpoint's resources. 368 func (c *Client) RenterPricesGet(allowance modules.Allowance) (rpg api.RenterPricesGET, err error) { 369 query := fmt.Sprintf("?funds=%v&hosts=%v&period=%v&renewwindow=%v", 370 allowance.Funds, allowance.Hosts, allowance.Period, allowance.RenewWindow) 371 err = c.get("/renter/prices"+query, &rpg) 372 return 373 } 374 375 // RenterPostRateLimit uses the /renter endpoint to change the renter's bandwidth rate 376 // limit. 377 func (c *Client) RenterPostRateLimit(readBPS, writeBPS int64) (err error) { 378 values := url.Values{} 379 values.Set("maxdownloadspeed", strconv.FormatInt(readBPS, 10)) 380 values.Set("maxuploadspeed", strconv.FormatInt(writeBPS, 10)) 381 err = c.post("/renter", values.Encode(), nil) 382 return 383 } 384 385 // RenterRenamePost uses the /renter/rename/:siapath endpoint to rename a file. 386 func (c *Client) RenterRenamePost(siaPathOld, siaPathNew modules.SiaPath) (err error) { 387 spo := escapeSiaPath(siaPathOld) 388 values := url.Values{} 389 values.Set("newsiapath", fmt.Sprintf("/%s", siaPathNew.String())) 390 err = c.post(fmt.Sprintf("/renter/rename/%s", spo), values.Encode(), nil) 391 return 392 } 393 394 // RenterSetStreamCacheSizePost uses the /renter endpoint to change the renter's 395 // streamCacheSize for streaming 396 func (c *Client) RenterSetStreamCacheSizePost(cacheSize uint64) (err error) { 397 values := url.Values{} 398 values.Set("streamcachesize", fmt.Sprint(cacheSize)) 399 err = c.post("/renter", values.Encode(), nil) 400 return 401 } 402 403 // RenterSetCheckIPViolationPost uses the /renter endpoint to enable/disable the IP 404 // violation check in the renter. 405 func (c *Client) RenterSetCheckIPViolationPost(enabled bool) (err error) { 406 values := url.Values{} 407 values.Set("checkforipviolation", fmt.Sprint(enabled)) 408 err = c.post("/renter", values.Encode(), nil) 409 return 410 } 411 412 // RenterStreamGet uses the /renter/stream endpoint to download data as a 413 // stream. 414 func (c *Client) RenterStreamGet(siaPath modules.SiaPath) (resp []byte, err error) { 415 sp := escapeSiaPath(siaPath) 416 _, resp, err = c.getRawResponse(fmt.Sprintf("/renter/stream/%s", sp)) 417 return 418 } 419 420 // RenterStreamPartialGet uses the /renter/stream endpoint to download a part 421 // of data as a stream. 422 func (c *Client) RenterStreamPartialGet(siaPath modules.SiaPath, start, end uint64) (resp []byte, err error) { 423 sp := escapeSiaPath(siaPath) 424 resp, err = c.getRawPartialResponse(fmt.Sprintf("/renter/stream/%s", sp), start, end) 425 return 426 } 427 428 // RenterSetRepairPathPost uses the /renter/tracking endpoint to set the repair 429 // path of a file to a new location. The file at newPath must exists. 430 func (c *Client) RenterSetRepairPathPost(siaPath modules.SiaPath, newPath string) (err error) { 431 sp := escapeSiaPath(siaPath) 432 values := url.Values{} 433 values.Set("trackingpath", newPath) 434 err = c.post(fmt.Sprintf("/renter/file/%v", sp), values.Encode(), nil) 435 return 436 } 437 438 // RenterSetFileStuckPost sets the 'stuck' field of the siafile at siaPath to 439 // stuck. 440 func (c *Client) RenterSetFileStuckPost(siaPath modules.SiaPath, stuck bool) (err error) { 441 sp := escapeSiaPath(siaPath) 442 values := url.Values{} 443 values.Set("stuck", fmt.Sprint(stuck)) 444 err = c.post(fmt.Sprintf("/renter/file/%v", sp), values.Encode(), nil) 445 return 446 } 447 448 // RenterUploadPost uses the /renter/upload endpoint to upload a file 449 func (c *Client) RenterUploadPost(path string, siaPath modules.SiaPath, dataPieces, parityPieces uint64) (err error) { 450 return c.RenterUploadForcePost(path, siaPath, dataPieces, parityPieces, false) 451 } 452 453 // RenterUploadForcePost uses the /renter/upload endpoint to upload a file 454 // and to overwrite if the file already exists 455 func (c *Client) RenterUploadForcePost(path string, siaPath modules.SiaPath, dataPieces, parityPieces uint64, force bool) (err error) { 456 sp := escapeSiaPath(siaPath) 457 values := url.Values{} 458 values.Set("source", path) 459 values.Set("datapieces", strconv.FormatUint(dataPieces, 10)) 460 values.Set("paritypieces", strconv.FormatUint(parityPieces, 10)) 461 values.Set("force", strconv.FormatBool(force)) 462 err = c.post(fmt.Sprintf("/renter/upload/%s", sp), values.Encode(), nil) 463 return 464 } 465 466 // RenterUploadDefaultPost uses the /renter/upload endpoint with default 467 // redundancy settings to upload a file. 468 func (c *Client) RenterUploadDefaultPost(path string, siaPath modules.SiaPath) (err error) { 469 sp := escapeSiaPath(siaPath) 470 values := url.Values{} 471 values.Set("source", path) 472 err = c.post(fmt.Sprintf("/renter/upload/%s", sp), values.Encode(), nil) 473 return 474 } 475 476 // RenterUploadStreamPost uploads data using a stream. 477 func (c *Client) RenterUploadStreamPost(r io.Reader, siaPath modules.SiaPath, dataPieces, parityPieces uint64, force bool) error { 478 sp := escapeSiaPath(siaPath) 479 values := url.Values{} 480 values.Set("datapieces", strconv.FormatUint(dataPieces, 10)) 481 values.Set("paritypieces", strconv.FormatUint(parityPieces, 10)) 482 values.Set("force", strconv.FormatBool(force)) 483 values.Set("stream", strconv.FormatBool(true)) 484 _, err := c.postRawResponse(fmt.Sprintf("/renter/uploadstream/%s?%s", sp, values.Encode()), r) 485 return err 486 } 487 488 // RenterUploadStreamRepairPost a siafile using a stream. If the data provided 489 // by r is not the same as the previously uploaded data, the data will be 490 // corrupted. 491 func (c *Client) RenterUploadStreamRepairPost(r io.Reader, siaPath modules.SiaPath) error { 492 sp := escapeSiaPath(siaPath) 493 values := url.Values{} 494 values.Set("repair", strconv.FormatBool(true)) 495 values.Set("stream", strconv.FormatBool(true)) 496 _, err := c.postRawResponse(fmt.Sprintf("/renter/uploadstream/%s?%s", sp, values.Encode()), r) 497 return err 498 } 499 500 // RenterDirCreatePost uses the /renter/dir/ endpoint to create a directory for the 501 // renter 502 func (c *Client) RenterDirCreatePost(siaPath modules.SiaPath) (err error) { 503 sp := escapeSiaPath(siaPath) 504 err = c.post(fmt.Sprintf("/renter/dir/%s", sp), "action=create", nil) 505 return 506 } 507 508 // RenterDirDeletePost uses the /renter/dir/ endpoint to delete a directory for the 509 // renter 510 func (c *Client) RenterDirDeletePost(siaPath modules.SiaPath) (err error) { 511 sp := escapeSiaPath(siaPath) 512 err = c.post(fmt.Sprintf("/renter/dir/%s", sp), "action=delete", nil) 513 return 514 } 515 516 // RenterDirRenamePost uses the /renter/dir/ endpoint to rename a directory for the 517 // renter 518 func (c *Client) RenterDirRenamePost(siaPath, newSiaPath modules.SiaPath) (err error) { 519 sp := escapeSiaPath(siaPath) 520 nsp := escapeSiaPath(newSiaPath) 521 err = c.post(fmt.Sprintf("/renter/dir/%s?newsiapath=%s", sp, nsp), "action=rename", nil) 522 return 523 } 524 525 // RenterGetDir uses the /renter/dir/ endpoint to query a directory 526 func (c *Client) RenterGetDir(siaPath modules.SiaPath) (rd api.RenterDirectory, err error) { 527 sp := escapeSiaPath(siaPath) 528 err = c.get(fmt.Sprintf("/renter/dir/%s", sp), &rd) 529 return 530 } 531 532 // RenterValidateSiaPathPost uses the /renter/validatesiapath endpoint to 533 // validate a potential siapath 534 // 535 // NOTE: This function specifically takes a string as an argument not a type 536 // SiaPath 537 func (c *Client) RenterValidateSiaPathPost(siaPathStr string) (err error) { 538 err = c.post(fmt.Sprintf("/renter/validatesiapath/%s", siaPathStr), "", nil) 539 return 540 }