diff --git a/doc/api.oa3 b/doc/api.oa3 index 6e709d66..c531218b 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -43,12 +43,12 @@ paths: '200': description: Awaiting announce - /admin/available: + /admin/status: get: tags: - admin description: Check if portal params have been set - operationId: get-node-available + operationId: get-node-status responses: '200': description: success @@ -58,13 +58,11 @@ paths: type: boolean '500': description: internal server error - - /admin/claim: - post: + put: tags: - admin description: Set admin password and node domain - operationId: set-node-claim + operationId: set-node-status security: - basicCredentials: [] parameters: @@ -269,12 +267,12 @@ paths: type: string format: binary - /account/public/available: + /account/public/status: get: tags: - account description: Check if a public account can be created. - operationId: get-public-available + operationId: get-public-status responses: '200': description: success @@ -728,6 +726,10 @@ paths: responses: '200': description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Profile' '401': description: permission denied '410': @@ -774,6 +776,10 @@ paths: responses: '200': description: success + content: + application/json: + schema: + $ref: '#/components/schemas/Profile' '401': description: permission denied '405': @@ -784,10 +790,9 @@ paths: description: internal server error requestBody: content: - application/octet-stream: + application/json: schema: type: string - format: binary /profile/message: get: @@ -983,7 +988,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Card' + $ref: '#/components/schemas/DataMessage' '400': description: invalid data message '401': @@ -992,42 +997,8 @@ paths: description: account disabled '500': description: internal server error - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DataMessage' - + /contact/cards/{cardId}: - get: - tags: - - contact - description: get card entry. Access granted to app tokens of account holder. - operationId: get-card - security: - - bearerAuth: [] - parameters: - - name: cardId - in: path - description: specified card id - required: true - schema: - type: string - responses: - '200': - description: success - content: - application/json: - schema: - $ref: '#/components/schemas/Card' - '401': - description: permission denied - '404': - description: card not found - '410': - description: account disabled - '500': - description: internal server error delete: tags: - contact @@ -1075,6 +1046,30 @@ paths: required: false # required for connected schema: type: string + - name: profileRevision + in: query + description: profile revision of contact + required: false # required for connected + schema: + type: string + - name: articleRevision + in: query + description: article revision of contact + required: false # required for connected + schema: + type: string + - name: channelRevision + in: query + description: channel revision of contact + required: false # required for connected + schema: + type: string + - name: viewRevision + in: query + description: view revision of contact + required: false # required for connected + schema: + type: string responses: '200': description: success @@ -1193,18 +1188,6 @@ paths: responses: '200': description: successful operation - content: - application/json: - schema: - type: object - required: - - status - properties: - token: - type: string - status: - type: string - enum: [ pending, confirmed, requested, connecting, connected ] '400': description: invalid data message '410': @@ -1508,12 +1491,36 @@ paths: type: integer format: int64 - /contact/content/revision: + /contact/article/revision: put: tags: - contact - description: Set content revision for contact. This is intend to be invoked automatically anytime a contact updates their content or sharing. Access granted to contact tokens. - operationId: set-content-revision + description: Set artcile revision for contact. This is intend to be invoked automatically anytime a contact updates their content or sharing. Access granted to contact tokens. + operationId: set-article-revision + security: + - bearerAuth: [] + responses: + '200': + description: revision set + '401': + description: not authorized + '410': + description: account disabled + '500': + description: internal server error + requestBody: + content: + application/json: + schema: + type: integer + format: int64 + + /contact/channel/revision: + put: + tags: + - contact + description: Set channel revision for contact. This is intend to be invoked automatically anytime a contact updates their content or sharing. Access granted to contact tokens. + operationId: set-channel-revision security: - bearerAuth: [] responses: @@ -1726,15 +1733,7 @@ paths: content: application/json: schema: - type: object - required: - - type - - data - properties: - type: - type: string - data: - type: string + $ref: '#/components/schemas/Subject' /attribute/articles/{articleId}/groups/{groupId}: put: @@ -2375,15 +2374,7 @@ paths: content: application/json: schema: - type: object - required: - - type - - data - properties: - type: - type: string - data: - type: string + $ref: '#/components/schemas/Subject' /content/channels/{channelId}/topics/{topicId}/assets: get: @@ -2838,7 +2829,7 @@ components: type: object required: - profile - - attribute + - article - group - channel - card @@ -2989,7 +2980,7 @@ components: id: type: string revision: - type: number + type: integer format: int64 data: $ref: '#/components/schemas/CardData' @@ -3000,7 +2991,7 @@ components: - detailRevision - profileRevision - notifiedProfile - - notifiedContent + - notifiedArticle - notifiedChannel - notifiedView properties: @@ -3013,7 +3004,7 @@ components: notifiedProfile: type: integer format: int64 - notifiedContent: + notifiedArticle: type: integer format: int64 notifiedChannel: @@ -3072,7 +3063,7 @@ components: required: - id - revision - - ata + - data properties: id: type: string @@ -3196,7 +3187,8 @@ components: id: type: string revision: - type: string + type: integer + format: int64 data: $ref: '#/components/schemas/TopicData' @@ -3266,7 +3258,8 @@ components: id: type: string revision: - type: string + type: integer + format: int64 data: $ref: '#/components/schemas/TagData' @@ -3302,7 +3295,8 @@ components: id: type: string revision: - type: string + type: integer + format: int64 data: $ref: '#/components/schemas/ArticleData' @@ -3412,7 +3406,7 @@ components: viewRevision: type: integer format: int64 - contentRevision: + articleRevision: type: integer format: int64 profileRevision: @@ -3469,6 +3463,18 @@ components: properties: token: type: string + profileRevision: + type: integer + format: int64 + articleRevision: + type: integer + format: int64 + channelRevision: + type: integer + format: int64 + viewRevision: + type: integer + format: int64 status: type: string enum: [ pending, confirmed, requested, connecting, connected ] diff --git a/net/server/internal/api_getProfile.go b/net/server/internal/api_getProfile.go index c2ecaed6..1320844d 100644 --- a/net/server/internal/api_getProfile.go +++ b/net/server/internal/api_getProfile.go @@ -11,19 +11,7 @@ func GetProfile(w http.ResponseWriter, r *http.Request) { ErrResponse(w, code, err) return } - detail := account.AccountDetail - profile := &Profile{ - Guid: account.Guid, - Handle: account.Username, - Description: detail.Description, - Location: detail.Location, - Image: detail.Image, - Revision: account.ProfileRevision, - Version: APP_VERSION, - Node: "https://" + getStrConfigValue(CONFIG_DOMAIN, "") + "/", - } - - WriteResponse(w, profile) + WriteResponse(w, getProfileModel(account)) } diff --git a/net/server/internal/api_profile.go b/net/server/internal/api_profile.go index 8e04ef7e..1278a65b 100644 --- a/net/server/internal/api_profile.go +++ b/net/server/internal/api_profile.go @@ -18,8 +18,4 @@ func GetProfileImage(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func SetProfileImage(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} diff --git a/net/server/internal/api_setProfile.go b/net/server/internal/api_setProfile.go index 46f80f49..3315ea9e 100644 --- a/net/server/internal/api_setProfile.go +++ b/net/server/internal/api_setProfile.go @@ -13,7 +13,6 @@ func SetProfile(w http.ResponseWriter, r *http.Request) { ErrResponse(w, code, err) return } - detail := account.AccountDetail // extract profile data from body var profileData ProfileData; @@ -24,12 +23,12 @@ func SetProfile(w http.ResponseWriter, r *http.Request) { } // update record - detail.Name = profileData.Name - detail.Location = profileData.Location - detail.Description = profileData.Description + account.AccountDetail.Name = profileData.Name + account.AccountDetail.Location = profileData.Location + account.AccountDetail.Description = profileData.Description err = store.DB.Transaction(func(tx *gorm.DB) error { - if res := store.DB.Save(&detail).Error; res != nil { + if res := store.DB.Save(&account.AccountDetail).Error; res != nil { return res } if res := store.DB.Model(&account).Update("profile_revision", account.ProfileRevision + 1).Error; res != nil { @@ -44,6 +43,6 @@ func SetProfile(w http.ResponseWriter, r *http.Request) { SetProfileNotification(account) SetStatus(account) - WriteResponse(w, nil) + WriteResponse(w, getProfileModel(account)) } diff --git a/net/server/internal/api_setProfileImage.go b/net/server/internal/api_setProfileImage.go new file mode 100644 index 00000000..8d5aebf7 --- /dev/null +++ b/net/server/internal/api_setProfileImage.go @@ -0,0 +1,43 @@ +package databag + +import ( + "net/http" + "gorm.io/gorm" + "databag/internal/store" +) + +func SetProfileImage(w http.ResponseWriter, r *http.Request) { + + account, code, err := BearerAppToken(r, true); + if err != nil { + ErrResponse(w, code, err) + return + } + + var image string + if err := ParseRequest(r, w, &image); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + + account.AccountDetail.Image = image + + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := store.DB.Save(&account.AccountDetail).Error; res != nil { + return res + } + if res := store.DB.Model(&account).Update("profile_revision", account.ProfileRevision + 1).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + SetProfileNotification(account) + SetStatus(account) + WriteResponse(w, getProfileModel(account)) +} + diff --git a/net/server/internal/modelUtil.go b/net/server/internal/modelUtil.go index 8cbb28b8..a70e1924 100644 --- a/net/server/internal/modelUtil.go +++ b/net/server/internal/modelUtil.go @@ -4,6 +4,20 @@ import ( "databag/internal/store" ) +func getProfileModel(account *store.Account) *Profile { + + return &Profile{ + Guid: account.Guid, + Handle: account.Username, + Description: account.AccountDetail.Description, + Location: account.AccountDetail.Location, + Image: account.AccountDetail.Image, + Revision: account.ProfileRevision, + Version: APP_VERSION, + Node: "https://" + getStrConfigValue(CONFIG_DOMAIN, "") + "/", + } +} + func getCardModel(slot *store.CardSlot) *Card { if slot.Card == nil { diff --git a/net/server/internal/ucUpdateProfile_test.go b/net/server/internal/ucUpdateProfile_test.go index 0553dafb..c01a7124 100644 --- a/net/server/internal/ucUpdateProfile_test.go +++ b/net/server/internal/ucUpdateProfile_test.go @@ -13,6 +13,7 @@ func TestUpdateProfile(t *testing.T) { var bCardRev int64 var cProfileRev int64 var cCardRev int64 + var profile Profile // setup testing group set, err := AddTestGroup("updateprofile") @@ -44,7 +45,8 @@ func TestUpdateProfile(t *testing.T) { }; assert.NoError(t, SendEndpointTest(SetProfile, "PUT", "/profile/data", nil, profileData, - APP_TOKENAPP, set.A.Token, nil, nil)) + APP_TOKENAPP, set.A.Token, &profile, nil)) + assert.Equal(t, "databaggerr", profile.Description) // recv websocket event assert.NotEqual(t, bCardRev, GetTestRevision(set.B.Revisions).Card) @@ -88,9 +90,12 @@ func TestUpdateProfile(t *testing.T) { assert.Equal(t, card.Data.ProfileRevision, card.Data.NotifiedProfile) assert.Equal(t, card.Data.CardProfile.Name, "Namer") - // TODO set image - // "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" + // set profile image + image := "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" + assert.NoError(t, SendEndpointTest(SetProfileImage, "PUT", "/profile/image", + nil, image, + APP_TOKENAPP, set.A.Token, &profile, nil)) - // TODO retrieve image + // TODO retrieve and validate profile image }