diff --git a/net/server/internal/api_setProfile.go b/net/server/internal/api_setProfile.go index 700ced11..a535f5fe 100644 --- a/net/server/internal/api_setProfile.go +++ b/net/server/internal/api_setProfile.go @@ -2,6 +2,7 @@ package databag import ( "net/http" + "gorm.io/gorm" "databag/internal/store" ) @@ -30,11 +31,24 @@ func SetProfile(w http.ResponseWriter, r *http.Request) { detail.Name = profileData.Name detail.Location = profileData.Location detail.Description = profileData.Description - if store.DB.Save(&detail).Error != nil { + + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := store.DB.Save(&detail).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 { + PrintMsg(err) + LogMsg("failed to store profile") w.WriteHeader(http.StatusInternalServerError) return } + SetStatus(account) w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(http.StatusOK) } diff --git a/net/server/internal/api_status.go b/net/server/internal/api_status.go index 4cafb4cd..d0832880 100644 --- a/net/server/internal/api_status.go +++ b/net/server/internal/api_status.go @@ -35,20 +35,27 @@ func Status(w http.ResponseWriter, r *http.Request) { } defer conn.Close() - log.Println("CONNECTED") - // receive announce message - var act uint = 0 + // receive announce + t, m, err := conn.ReadMessage() + if t != websocket.TextMessage || err != nil { + LogMsg("failed to receive announce") + return + } + var announce Announce + if json.Unmarshal(m, &announce) != nil { + LogMsg("invalid announce") + return + } - // get revisions for the account - var account accountRevision - err = store.DB.Model(&store.Account{}).Where("ID = ?", act).First(&account).Error - if err != nil { - log.Println("Status - failed to get account revision") -// return + // retrieve reference account + var app store.App + if store.DB.Preload("Account").Where("token = ?", announce.AppToken).First(&app).Error != nil { + LogMsg("invalid app token") + return } - rev := getRevision(account) // send current version + rev := getRevision(app.Account) var msg []byte msg, err = json.Marshal(rev) if err != nil { @@ -66,8 +73,8 @@ func Status(w http.ResponseWriter, r *http.Request) { defer close(c) // register channel for revisions - AddStatusListener(0, c) - defer RemoveStatusListener(act, c) + AddStatusListener(app.Account.ID, c) + defer RemoveStatusListener(app.Account.ID, c) // send revision until channel is closed for { @@ -86,15 +93,15 @@ func Status(w http.ResponseWriter, r *http.Request) { } } -func getRevision(rev accountRevision) Revision { +func getRevision(account store.Account) Revision { var r Revision - r.Profile = rev.ProfileRevision - r.Content = rev.ContentRevision - r.Label = rev.LabelRevision - r.Group = rev.GroupRevision - r.Card = rev.CardRevision - r.Dialogue = rev.DialogueRevision - r.Insight = rev.InsightRevision + r.Profile = account.ProfileRevision + r.Content = account.ContentRevision + r.Label = account.LabelRevision + r.Group = account.GroupRevision + r.Card = account.CardRevision + r.Dialogue = account.DialogueRevision + r.Insight = account.InsightRevision return r } @@ -102,18 +109,11 @@ func ExitStatus() { wsExit <- true } -func SetStatus(act uint) { +func SetStatus(account store.Account) { // get revisions for the account - var ar accountRevision - err := store.DB.Model(&Revision{}).Where("ID = ?", act).First(&ar).Error - if err != nil { - log.Println("SetStatus - failed to retrieve account revisions") - return - } - rev := getRevision(ar) - var msg []byte - msg, err = json.Marshal(rev) + rev := getRevision(account); + msg, err := json.Marshal(rev) if err != nil { log.Println("SetStatus - failed to marshal revision") return @@ -124,7 +124,7 @@ func SetStatus(act uint) { defer wsSync.Unlock() // notify all listeners - chs, ok := statusListener[act] + chs, ok := statusListener[account.ID] if ok { for _, ch := range chs { ch <- msg diff --git a/net/server/internal/main_test.go b/net/server/internal/main_test.go index 049c90b5..16634306 100644 --- a/net/server/internal/main_test.go +++ b/net/server/internal/main_test.go @@ -53,24 +53,6 @@ func TestMain(m *testing.M) { panic("failed to set account storage"); } - // get account token - r, w, _ = NewRequest("POST", "/admin/accounts", nil) - SetBasicAuth(r, "admin:pass") - AddNodeAccount(w, r) - var token string - if ReadResponse(w, &token) != nil { - panic("failed to create token") - } - - // set account profile - r, w, _ = NewRequest("GET", "/account/profile", nil) - SetBearerAuth(r, token); - SetCredentials(r, "test:pass") - AddAccount(w, r) - if ReadResponse(w, nil) != nil { - panic("failed to create account") - } - m.Run() } diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index 068e7c9c..ef1c8adf 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -58,7 +58,7 @@ type Account struct { LabelRevision int64 `gorm:"not null;default:1"` CardRevision int64 `gorm:"not null;default:1"` DialogueRevision int64 `gorm:"not null;default:1"` - InsightRevision uint64 `gorm:"not null;default:1"` + InsightRevision int64 `gorm:"not null;default:1"` Created int64 `gorm:"autoCreateTime"` Disabled bool `gorm:"not null;default:false"` AccountDetail AccountDetail diff --git a/net/server/internal/ucAddAccount_test.go b/net/server/internal/ucAddAccount_test.go index 151a87a9..b2c2192a 100644 --- a/net/server/internal/ucAddAccount_test.go +++ b/net/server/internal/ucAddAccount_test.go @@ -22,7 +22,7 @@ func TestAddAccount(t *testing.T) { assert.NoError(t, ReadResponse(w, &tokenType)) // check if username is available - r, w, _ = NewRequest("GET", "/account/claimable?username=user", nil) + r, w, _ = NewRequest("GET", "/account/claimable?username=addaccount", nil) SetBearerAuth(r, token) GetAccountUsername(w, r) var available bool @@ -31,7 +31,7 @@ func TestAddAccount(t *testing.T) { // create account r, w, _ = NewRequest("GET", "/account/profile", nil) - SetCredentials(r, "user:pass") + SetCredentials(r, "addaccount:pass") SetBearerAuth(r, token) AddAccount(w, r) var profile Profile @@ -44,7 +44,7 @@ func TestAddAccount(t *testing.T) { assert.NoError(t, ReadResponse(w, &token)) // check if dup is available - r, w, _ = NewRequest("GET", "/account/claimable?username=user", nil) + r, w, _ = NewRequest("GET", "/account/claimable?username=addaccount", nil) SetBearerAuth(r, token) GetAccountUsername(w, r) assert.NoError(t, ReadResponse(w, &available)) @@ -52,7 +52,7 @@ func TestAddAccount(t *testing.T) { // create dup account r, w, _ = NewRequest("GET", "/account/profile", nil) - SetCredentials(r, "user:pass") + SetCredentials(r, "addaccount:pass") SetBearerAuth(r, token); AddAccount(w, r) assert.Error(t, ReadResponse(w, &profile)) diff --git a/net/server/internal/ucAttachApp_test.go b/net/server/internal/ucAttachApp_test.go index 5465b6d0..a2116117 100644 --- a/net/server/internal/ucAttachApp_test.go +++ b/net/server/internal/ucAttachApp_test.go @@ -9,14 +9,48 @@ import ( "crypto/rsa" "crypto" "time" + "net/url" + "net/http" + "net/http/httptest" "github.com/stretchr/testify/assert" + "github.com/gorilla/websocket" ) +type StatusHandler struct {} + +func (h *StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + Status(w, r) +} + func TestAttachAccount(t *testing.T) { + // setup websocket server + h := StatusHandler{} + s := httptest.NewServer(&h) + wsUrl, _ := url.Parse(s.URL) + wsUrl.Scheme = "ws" + + // get account token + r, w, _ := NewRequest("POST", "/admin/accounts", nil) + SetBasicAuth(r, "admin:pass") + AddNodeAccount(w, r) + var account string + if ReadResponse(w, &account) != nil { + panic("failed to create token") + } + + // set account profile + r, w, _ = NewRequest("GET", "/account/profile", nil) + SetBearerAuth(r, account); + SetCredentials(r, "attachapp:pass") + AddAccount(w, r) + if ReadResponse(w, nil) != nil { + panic("failed to create account") + } + // acquire new token for attaching app - r, w, _ := NewRequest("POST", "/account/apps", nil) - SetBasicAuth(r, "user:pass"); + r, w, _ = NewRequest("POST", "/account/apps", nil) + SetBasicAuth(r, "attachapp:pass"); AddAccountApp(w, r); var token string assert.NoError(t, ReadResponse(w, &token)) @@ -35,7 +69,7 @@ func TestAttachAccount(t *testing.T) { // autorize app r, w, _ = NewRequest("PUT", "/authorize", "aabbccdd") - SetBearerAuth(r, access); + SetBearerAuth(r, access) Authorize(w, r); var message DataMessage assert.NoError(t, ReadResponse(w, &message)) @@ -61,6 +95,16 @@ func TestAttachAccount(t *testing.T) { assert.GreaterOrEqual(t, cur, auth.Timestamp) assert.Less(t, cur - 60, auth.Timestamp) + // app connects websocket + ws, _, _ := websocket.DefaultDialer.Dial(wsUrl.String(), nil) + announce := Announce{ AppToken: access } + msg, _ := json.Marshal(&announce) + ws.WriteMessage(websocket.TextMessage, msg) + _, msg, _ = ws.ReadMessage() + var revision Revision + assert.NoError(t, json.Unmarshal(msg, &revision)) + profileRevision := revision.Profile + // set profile profileData := ProfileData{ Name: "Namer", @@ -79,7 +123,12 @@ func TestAttachAccount(t *testing.T) { var profile Profile assert.NoError(t, ReadResponse(w, &profile)) assert.Equal(t, guid, profile.Guid) - assert.Equal(t, "user", profile.Handle) + assert.Equal(t, "attachapp", profile.Handle) assert.Equal(t, "Namer", profile.Name) + + // profile revision incremented + _, msg, _ = ws.ReadMessage() + assert.NoError(t, json.Unmarshal(msg, &revision)) + assert.NotEqual(t, profileRevision, revision.Profile) }