···2727}
28282929type CreateRecordResp struct {
3030- URI string `json:"uri"`
3030+ URI string `json:"uri"`
3131+ ErrStr string `json:"error"`
3232+ Message string `json:"message"`
3133}
32343335func (s *Server) CreateNewStatus(ctx context.Context, oauthsession oauth.Session, status string, createdAt time.Time) (string, error) {
3434- privateJwk, err := oauthsession.CreatePrivateKey()
3535- if err != nil {
3636- return "", fmt.Errorf("create private jwk: %w", err)
3737- }
3838-3936 bodyReq := map[string]any{
4037 "repo": oauthsession.Did,
4138 "collection": "xyz.statusphere.status",
···5047 return "", fmt.Errorf("marshal update message request body: %w", err)
5148 }
52495353- // TODO: redo this loop business
5454- for range 2 {
5555- r := bytes.NewReader(bodyB)
5656- url := fmt.Sprintf("%s/xrpc/com.atproto.repo.createRecord", oauthsession.PdsUrl)
5757- request, err := http.NewRequestWithContext(ctx, "POST", url, r)
5858- if err != nil {
5959- return "", fmt.Errorf("create http request: %w", err)
6060- }
5050+ r := bytes.NewReader(bodyB)
5151+ url := fmt.Sprintf("%s/xrpc/com.atproto.repo.createRecord", oauthsession.PdsUrl)
5252+ request, err := http.NewRequestWithContext(ctx, "POST", url, r)
5353+ if err != nil {
5454+ return "", fmt.Errorf("create http request: %w", err)
5555+ }
5656+5757+ request.Header.Add("Content-Type", "application/json")
5858+ request.Header.Add("Accept", "application/json")
5959+ request.Header.Set("Authorization", "DPoP "+oauthsession.AccessToken)
61606262- request.Header.Add("Content-Type", "application/json")
6363- request.Header.Add("Accept", "application/json")
6161+ privateKey, err := oauthsession.CreatePrivateKey()
6262+ if err != nil {
6363+ return "", fmt.Errorf("create private key: %w", err)
6464+ }
64656565- dpopJwt, err := pdsDpopJwt("POST", url, oauthsession.AuthserverIss, oauthsession.AccessToken, oauthsession.DpopPdsNonce, privateJwk)
6666+ // try a maximum of 2 times to make the request. If the first attempt fails because the server returns an unauthorized due to a new use_dpop_nonce being issued,
6767+ // then try again. Otherwise just try once.
6868+ for range 2 {
6969+ dpopJwt, err := s.oauthService.PdsDpopJwt("POST", url, oauthsession, privateKey)
6670 if err != nil {
6771 return "", err
6872 }
69737074 request.Header.Set("DPoP", dpopJwt)
7171- request.Header.Set("Authorization", "DPoP "+oauthsession.AccessToken)
72757376 resp, err := s.httpClient.Do(request)
7477 if err != nil {
7578 return "", fmt.Errorf("do http request: %w", err)
7679 }
7780 defer resp.Body.Close()
8181+8282+ if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusBadRequest && resp.StatusCode != http.StatusUnauthorized {
8383+ return "", fmt.Errorf("unexpected status code returned: %d", resp.StatusCode)
8484+ }
8585+8686+ var result CreateRecordResp
8787+ err = decodeResp(resp.Body, &result)
8888+ if err != nil {
8989+ // just log the error.
9090+ // if a HTTP 200 is received then the record has been created and we only use the response URI to make an optimistic write to our DB, so nothing will go wrong here.
9191+ // if a HTTP 400 then we can at least log return that it was a bad request.
9292+ // if a HTTP 401 we only do something if the error string is use_dpop_nonce
9393+ slog.Error("decode response body", "error", err)
9494+ }
9595+9696+ slog.Info("resp", "status", resp.StatusCode)
78977998 if resp.StatusCode == http.StatusOK {
8080- var result CreateRecordResp
8181- err = decodeResp(resp.Body, &result)
8282- if err != nil {
8383- // just log error because we got a 200 indicating that the record was created. If this were to be tried again due to an error
8484- // returned here, there would be duplicate data
8585- slog.Error("decode success response", "error", err)
8686- }
8799 return result.URI, nil
88100 }
891019090- var errorResp XRPCError
9191- err = decodeResp(resp.Body, &errorResp)
9292- if err != nil {
9393- return "", fmt.Errorf("decode error resp: %w", err)
102102+ if resp.StatusCode == http.StatusBadRequest {
103103+ return "", fmt.Errorf("bad request: %s - %s", result.Message, result.ErrStr)
94104 }
951059696- if resp.StatusCode == 400 || resp.StatusCode == 401 && errorResp.ErrStr == "use_dpop_nonce" {
106106+ if resp.StatusCode == http.StatusUnauthorized && result.ErrStr == "use_dpop_nonce" {
97107 newNonce := resp.Header.Get("DPoP-Nonce")
98108 oauthsession.DpopPdsNonce = newNonce
99109 err := s.oauthService.UpdateOAuthSessionDPopPDSNonce(oauthsession.Did, newNonce)
100110 if err != nil {
111111+ // just log the error because we can still proceed without storing it.
101112 slog.Error("updating oauth session in store with new DPoP PDS nonce", "error", err)
102113 }
103114 continue
104115 }
105116106106- slog.Error("got error", "status code", resp.StatusCode, "message", errorResp.Message, "error", errorResp.ErrStr)
117117+ return "", fmt.Errorf("received an unauthorized status code and message: %s - %s", result.ErrStr, result.Message)
107118 }
108119109120 return "", fmt.Errorf("failed to create status record")