github.com/0chain/gosdk@v1.17.11/zboxapi/sdk.go (about) 1 package zboxapi 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "net/http" 10 "strconv" 11 "time" 12 13 "github.com/0chain/gosdk/zcncore" 14 15 thrown "github.com/0chain/errors" 16 "github.com/0chain/gosdk/core/encryption" 17 "github.com/0chain/gosdk/core/logger" 18 "github.com/0chain/gosdk/core/resty" 19 ) 20 21 var log logger.Logger 22 23 func GetLogger() *logger.Logger { 24 return &log 25 } 26 27 type Client struct { 28 baseUrl string 29 appType string 30 clientID string 31 clientPublicKey string 32 clientPrivateKey string 33 } 34 35 // NewClient create a zbox api client with wallet info 36 func NewClient() *Client { 37 return &Client{} 38 } 39 40 // SetRequest set base url and app type of zbox api request 41 func (c *Client) SetRequest(baseUrl, appType string) { 42 c.baseUrl = baseUrl 43 c.appType = appType 44 } 45 46 func (c *Client) SetWallet(clientID, clientPrivateKey, clientPublicKey string) { 47 c.clientID = clientID 48 c.clientPrivateKey = clientPrivateKey 49 c.clientPublicKey = clientPublicKey 50 } 51 52 func (c *Client) parseResponse(resp *http.Response, respBody []byte, result interface{}) error { 53 54 log.Info("zboxapi: ", resp.StatusCode, " ", string(respBody)) 55 if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated { 56 if err := json.Unmarshal(respBody, result); err != nil { 57 return thrown.Throw(ErrInvalidJsonResponse, string(respBody)) 58 } 59 return nil 60 } 61 62 if len(respBody) == 0 { 63 return errors.New(resp.Status) 64 } 65 66 errResp := &ErrorResponse{} 67 if err := json.Unmarshal(respBody, errResp); err != nil { 68 return thrown.Throw(ErrInvalidJsonResponse, string(respBody)) 69 } 70 71 return errors.New(string(errResp.Error)) 72 } 73 74 // GetCsrfToken obtain a fresh csrf token from 0box api server 75 func (c *Client) GetCsrfToken(ctx context.Context) (string, error) { 76 r, err := c.createResty(ctx, "", "", nil) 77 if err != nil { 78 return "", err 79 } 80 result := &CsrfTokenResponse{} 81 r.DoGet(ctx, c.baseUrl+"/v2/csrftoken").Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 82 if err != nil { 83 return err 84 } 85 return c.parseResponse(resp, respBody, result) 86 }) 87 88 if err := r.Wait(); len(err) > 0 { 89 return "", err[0] 90 } 91 92 return result.Token, nil 93 } 94 95 func (c *Client) createResty(ctx context.Context, csrfToken, userID string, headers map[string]string) (*resty.Resty, error) { 96 h := make(map[string]string) 97 h["X-App-Client-ID"] = c.clientID 98 h["X-App-Client-Key"] = c.clientPublicKey 99 h["X-App-User-ID"] = userID 100 101 if c.clientPrivateKey != "" { 102 data := fmt.Sprintf("%v:%v:%v", c.clientID, userID, c.clientPublicKey) 103 hash := encryption.Hash(data) 104 105 sign, err := zcncore.SignFn(hash) 106 if err != nil { 107 return nil, err 108 } 109 h["X-App-Client-Signature"] = sign 110 } 111 112 h["X-CSRF-TOKEN"] = csrfToken 113 h["X-App-Timestamp"] = strconv.FormatInt(time.Now().Unix(), 10) 114 115 if _, ok := h["X-App-ID-Token"]; !ok { 116 h["X-App-ID-Token"] = "*" //ignore firebase token in jwt requests 117 } 118 119 h["X-App-Type"] = c.appType 120 121 for k, v := range headers { 122 h[k] = v 123 } 124 125 return resty.New(resty.WithHeader(h)), nil 126 } 127 128 // CreateJwtToken create a jwt token with jwt session id and otp 129 func (c *Client) CreateJwtToken(ctx context.Context, userID, accessToken string) (string, error) { 130 csrfToken, err := c.GetCsrfToken(ctx) 131 if err != nil { 132 return "", err 133 } 134 headers := map[string]string{ 135 "X-App-ID-Token": accessToken, 136 } 137 138 r, err := c.createResty(ctx, csrfToken, userID, headers) 139 140 if err != nil { 141 return "", err 142 } 143 144 result := &JwtTokenResponse{} 145 r.DoPost(ctx, nil, c.baseUrl+"/v2/jwt/token"). 146 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 147 if err != nil { 148 return err 149 } 150 151 return c.parseResponse(resp, respBody, result) 152 }) 153 154 if errs := r.Wait(); len(errs) > 0 { 155 return "", errs[0] 156 } 157 158 return result.Token, nil 159 } 160 161 // RefreshJwtToken refresh jwt token 162 func (c *Client) RefreshJwtToken(ctx context.Context, userID string, token string) (string, error) { 163 csrfToken, err := c.GetCsrfToken(ctx) 164 if err != nil { 165 return "", err 166 } 167 headers := map[string]string{ 168 "X-JWT-Token": token, 169 } 170 171 r, err := c.createResty(ctx, csrfToken, userID, headers) 172 173 if err != nil { 174 return "", err 175 } 176 177 result := &JwtTokenResponse{} 178 r.DoPut(ctx, nil, c.baseUrl+"/v2/jwt/token"). 179 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 180 if err != nil { 181 return err 182 } 183 184 return c.parseResponse(resp, respBody, result) 185 }) 186 187 if errs := r.Wait(); len(errs) > 0 { 188 return "", errs[0] 189 } 190 191 return result.Token, nil 192 } 193 194 func (c *Client) GetFreeStorage(ctx context.Context, phoneNumber, token string) (*FreeMarker, error) { 195 csrfToken, err := c.GetCsrfToken(ctx) 196 if err != nil { 197 return nil, err 198 } 199 headers := map[string]string{ 200 "X-App-ID-Token": token, 201 } 202 203 r, err := c.createResty(ctx, csrfToken, phoneNumber, headers) 204 205 if err != nil { 206 return nil, err 207 } 208 209 result := &FreeStorageResponse{} 210 r.DoGet(ctx, c.baseUrl+"/v2/freestorage"). 211 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 212 if err != nil { 213 return err 214 } 215 216 return c.parseResponse(resp, respBody, result) 217 }) 218 219 if errs := r.Wait(); len(errs) > 0 { 220 return nil, errs[0] 221 } 222 223 return result.ToMarker() 224 225 } 226 227 func (c *Client) CreateSharedInfo(ctx context.Context, phoneNumber, token string, s SharedInfo) error { 228 csrfToken, err := c.GetCsrfToken(ctx) 229 if err != nil { 230 return err 231 } 232 headers := map[string]string{ 233 "X-App-ID-Token": token, 234 "Content-Type": "application/json", 235 } 236 237 r, err := c.createResty(ctx, csrfToken, phoneNumber, headers) 238 239 if err != nil { 240 return err 241 } 242 243 buf, err := json.Marshal(s) 244 if err != nil { 245 return err 246 } 247 248 result := &JsonResult[string]{} 249 r.DoPost(ctx, bytes.NewReader(buf), c.baseUrl+"/v2/shareinfo"). 250 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 251 if err != nil { 252 return err 253 } 254 255 return c.parseResponse(resp, respBody, &result) 256 }) 257 258 if errs := r.Wait(); len(errs) > 0 { 259 return errs[0] 260 } 261 262 return nil 263 } 264 265 func (c *Client) DeleteSharedInfo(ctx context.Context, phoneNumber, token, authTicket string, lookupHash string) error { 266 csrfToken, err := c.GetCsrfToken(ctx) 267 if err != nil { 268 return err 269 } 270 headers := map[string]string{ 271 "X-App-ID-Token": token, 272 } 273 274 r, err := c.createResty(ctx, csrfToken, phoneNumber, headers) 275 276 if err != nil { 277 return err 278 } 279 280 result := &JsonResult[string]{} 281 r.DoDelete(ctx, c.baseUrl+"/v2/shareinfo?auth_ticket="+authTicket+"&lookup_hash="+lookupHash). 282 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 283 if err != nil { 284 return err 285 } 286 287 return c.parseResponse(resp, respBody, &result) 288 }) 289 290 if errs := r.Wait(); len(errs) > 0 { 291 return errs[0] 292 } 293 294 return nil 295 } 296 297 func (c *Client) GetSharedByPublic(ctx context.Context, phoneNumber, token string) ([]SharedInfoSent, error) { 298 return c.getShared(ctx, phoneNumber, token, false) 299 } 300 301 func (c *Client) GetSharedByMe(ctx context.Context, phoneNumber, token string) ([]SharedInfoSent, error) { 302 return c.getShared(ctx, phoneNumber, token, true) 303 } 304 305 func (c *Client) getShared(ctx context.Context, phoneNumber, token string, isPrivate bool) ([]SharedInfoSent, error) { 306 csrfToken, err := c.GetCsrfToken(ctx) 307 if err != nil { 308 return nil, err 309 } 310 headers := map[string]string{ 311 "X-App-ID-Token": token, 312 } 313 314 r, err := c.createResty(ctx, csrfToken, phoneNumber, headers) 315 316 if err != nil { 317 return nil, err 318 } 319 320 shareInfoType := "public" 321 if isPrivate { 322 shareInfoType = "private" 323 } 324 325 result := &JsonResult[SharedInfoSent]{} 326 r.DoGet(ctx, c.baseUrl+"/v2/shareinfo/shared?share_info_type="+shareInfoType). 327 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 328 if err != nil { 329 return err 330 } 331 332 return c.parseResponse(resp, respBody, &result) 333 }) 334 335 if errs := r.Wait(); len(errs) > 0 { 336 return nil, errs[0] 337 } 338 339 return result.Data, nil 340 341 } 342 343 func (c *Client) GetSharedToMe(ctx context.Context, phoneNumber, token string) ([]SharedInfoReceived, error) { 344 csrfToken, err := c.GetCsrfToken(ctx) 345 if err != nil { 346 return nil, err 347 } 348 headers := map[string]string{ 349 "X-App-ID-Token": token, 350 } 351 352 r, err := c.createResty(ctx, csrfToken, phoneNumber, headers) 353 354 if err != nil { 355 return nil, err 356 } 357 358 result := &JsonResult[SharedInfoReceived]{} 359 r.DoGet(ctx, c.baseUrl+"/v2/shareinfo/received?share_info_type=private"). 360 Then(func(req *http.Request, resp *http.Response, respBody []byte, cf context.CancelFunc, err error) error { 361 if err != nil { 362 return err 363 } 364 365 return c.parseResponse(resp, respBody, &result) 366 }) 367 368 if errs := r.Wait(); len(errs) > 0 { 369 return nil, errs[0] 370 } 371 372 return result.Data, nil 373 374 }