diff --git a/doc/api.oa3 b/doc/api.oa3 index b48b5d0c..37d9c259 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -591,52 +591,16 @@ paths: - account description: Add a new account. Basic auth will be used for the accounts username and password. Access granted to valid create account token. operationId: add-account - parameters: - - name: token - in: query - description: access token for creating accounts - required: false - schema: - type: string - - name: appName - in: query - description: name of connecting app - required: false - schema: - type: string - - name: appVersion - in: query - description: version of connecting app - required: false - schema: - type: string - - name: platform - in: query - description: device platform - required: false - schema: - type: string - - name: deviceToken - in: query - description: deviceToken for push notification - required: false - schema: - type: string - - name: notifications - in: query - description: push notifications to receive - required: false - schema: - type: array - items: - type: string - responses: + security: + - bearerAuth: [] + - basicCredentials: [] + responses: '201': description: successful operation content: application/json: schema: - $ref: '#/components/schemas/LoginAccess' + $ref: '#/components/schemas/Profile' '400': description: invalid handle or password '401': diff --git a/net/server/internal/api_addAccountApp.go b/net/server/internal/api_addAccountApp.go index 0384a253..d1240fe9 100644 --- a/net/server/internal/api_addAccountApp.go +++ b/net/server/internal/api_addAccountApp.go @@ -3,9 +3,11 @@ package databag import ( "databag/internal/store" "encoding/hex" + "encoding/json" "github.com/theckman/go-securerandom" "gorm.io/gorm" "net/http" + "errors" ) //AddAccountApp with access token, attach an app to an account generating agent token @@ -17,6 +19,19 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) { return } + // parse authentication token + appName := r.FormValue("appName") + appVersion := r.FormValue("appVersion") + platform := r.FormValue("platform") + deviceToken := r.FormValue("deviceToken") + var notifications []string + if r.FormValue("notifications") != "" { + if err := json.Unmarshal([]byte(r.FormValue("notifications")), ¬ifications); err != nil { + ErrResponse(w, http.StatusBadRequest, errors.New("invalid notification types")); + return; + } + } + // parse app data var appData AppData if err := ParseRequest(r, w, &appData); err != nil { @@ -32,21 +47,35 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) { } access := hex.EncodeToString(data) - // create app entry - app := store.App{ - AccountID: account.GUID, - Name: appData.Name, - Description: appData.Description, - Image: appData.Image, - URL: appData.URL, - Token: access, + login := LoginAccess{ + GUID: account.GUID, + AppToken: account.GUID + "." + access, } // save app and delete token err = store.DB.Transaction(func(tx *gorm.DB) error { - if res := store.DB.Create(&app).Error; res != nil { + + // create session + session := &store.Session{} + session.AccountID = account.GUID + session.Token = access + session.AppName = appName + session.AppVersion = appVersion + session.Platform = platform + session.PushToken = deviceToken + if res := tx.Save(session).Error; res != nil { return res } + login.Created = session.Created + + for _, notification := range notifications { + eventType := &store.EventType{} + eventType.SessionID = session.ID + eventType.Name = notification + if res := tx.Save(eventType).Error; res != nil { + return res + } + } return nil }) if err != nil { @@ -54,11 +83,5 @@ func AddAccountApp(w http.ResponseWriter, r *http.Request) { return } - login := LoginAccess{ - GUID: account.GUID, - AppToken: account.GUID + "." + access, - Created: app.Created, - } - WriteResponse(w, login) } diff --git a/net/server/internal/api_removeAgentToken.go b/net/server/internal/api_removeAgentToken.go new file mode 100644 index 00000000..4d190f3c --- /dev/null +++ b/net/server/internal/api_removeAgentToken.go @@ -0,0 +1,38 @@ +package databag + +import ( + "databag/internal/store" + "gorm.io/gorm" + "net/http" + "errors" +) + +//RemoveAgentToken +func RemoveAgentToken(w http.ResponseWriter, r *http.Request) { + + // parse authentication token + target, access, err := ParseToken(r.FormValue("agent")) + if err != nil { + ErrResponse(w, http.StatusBadRequest, err); + return + } + + // load session + var session store.Session + if err = store.DB.Where("account_id = ? AND token = ?", target, access).Find(&session).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusNotFound, err); + } else { + ErrResponse(w, http.StatusInternalServerError, err); + } + return; + } + + // delete session + if err = store.DB.Delete(&session).Error; err != nil { + ErrResponse(w, http.StatusInternalServerError, err); + return; + } + + WriteResponse(w, nil) +} diff --git a/net/server/internal/api_setAccountAccess.go b/net/server/internal/api_setAccountAccess.go index 75d93712..4112bfab 100644 --- a/net/server/internal/api_setAccountAccess.go +++ b/net/server/internal/api_setAccountAccess.go @@ -41,12 +41,8 @@ func SetAccountAccess(w http.ResponseWriter, r *http.Request) { access := hex.EncodeToString(data) // create app entry - app := store.App{ + app := store.Session{ AccountID: account.GUID, - Name: appData.Name, - Description: appData.Description, - Image: appData.Image, - URL: appData.URL, Token: access, } diff --git a/net/server/internal/api_status.go b/net/server/internal/api_status.go index 2760aeb0..78ffbd67 100644 --- a/net/server/internal/api_status.go +++ b/net/server/internal/api_status.go @@ -51,14 +51,14 @@ func Status(w http.ResponseWriter, r *http.Request) { } // retrieve reference account - var app store.App - if err := store.DB.Preload("Account").Where("account_id = ? AND token = ?", target, access).First(&app).Error; err != nil { + var session store.Session + if err := store.DB.Preload("Account").Where("account_id = ? AND token = ?", target, access).First(&session).Error; err != nil { ErrMsg(err) return } // send current version - rev := getRevision(&app.Account) + rev := getRevision(&session.Account) var msg []byte msg, err = json.Marshal(rev) if err != nil { @@ -75,8 +75,8 @@ func Status(w http.ResponseWriter, r *http.Request) { defer close(c) // register channel for revisions - addStatusListener(app.Account.ID, c) - defer removeStatusListener(app.Account.ID, c) + addStatusListener(session.Account.ID, c) + defer removeStatusListener(session.Account.ID, c) // start ping pong ticker ticker := time.NewTicker(60 * time.Second) diff --git a/net/server/internal/authUtil.go b/net/server/internal/authUtil.go index 0f18aa26..94e77ac9 100644 --- a/net/server/internal/authUtil.go +++ b/net/server/internal/authUtil.go @@ -104,28 +104,29 @@ func ParamAgentToken(r *http.Request, detail bool) (*store.Account, int, error) return nil, http.StatusBadRequest, err } - // find token record - var app store.App - if detail { - if err := store.DB.Preload("Account.AccountDetail").Where("account_id = ? AND token = ?", target, access).First(&app).Error; err != nil { + // find session record + var session store.Session; + if detail { + if err := store.DB.Preload("Account.AccountDetail").Where("account_id = ? AND token =?", target, access).Find(&session).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, http.StatusNotFound, err } return nil, http.StatusInternalServerError, err - } - } else { - if err := store.DB.Preload("Account").Where("account_id = ? AND token = ?", target, access).First(&app).Error; err != nil { + } + } else { + if err := store.DB.Preload("Account").Where("account_id = ? AND token =?", target, access).Find(&session).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, http.StatusNotFound, err } return nil, http.StatusInternalServerError, err - } - } - if app.Account.Disabled { + } + } + + if session.Account.Disabled { return nil, http.StatusGone, errors.New("account is inactive") } - return &app.Account, http.StatusOK, nil + return &session.Account, http.StatusOK, nil } //BearerAppToken retrieves account specified by authorization header diff --git a/net/server/internal/models.go b/net/server/internal/models.go index 3d3f274b..d5143e10 100644 --- a/net/server/internal/models.go +++ b/net/server/internal/models.go @@ -189,6 +189,13 @@ type ChannelDetail struct { Members []string `json:"members"` } +//ChannelMember contact member of channel +type ChannelMember struct { + Member string `json:"member"` + + PushEnabled bool `json:"pushEnabled"` +} + //ChannelSummary latest topic posted on channel type ChannelSummary struct { LastTopic *TopicDetail `json:"lastTopic,omitempty"` diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go index bfd6a1f6..1ed7d009 100644 --- a/net/server/internal/routers.go +++ b/net/server/internal/routers.go @@ -139,6 +139,13 @@ var endpoints = routes{ RemoveAccountApp, }, + route{ + "RemoveAgentToken", + strings.ToUpper("Delete"), + "/account/apps", + RemoveAgentToken, + }, + route{ "SetAccountAccess", strings.ToUpper("Put"), diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index 2b7ddcec..1ba4208f 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -6,6 +6,8 @@ func AutoMigrate(db *gorm.DB) { db.AutoMigrate(&Notification{}); db.AutoMigrate(&Config{}); db.AutoMigrate(&App{}); + db.AutoMigrate(&Session{}); + db.AutoMigrate(&EventType{}); db.AutoMigrate(&Account{}); db.AutoMigrate(&AccountToken{}); db.AutoMigrate(&GroupSlot{}); @@ -13,6 +15,7 @@ func AutoMigrate(db *gorm.DB) { db.AutoMigrate(&Group{}); db.AutoMigrate(&ChannelSlot{}); db.AutoMigrate(&Channel{}); + db.AutoMigrate(&Member{}); db.AutoMigrate(&CardSlot{}); db.AutoMigrate(&Card{}); db.AutoMigrate(&ArticleSlot{}); @@ -93,6 +96,27 @@ type AccountDetail struct { Image string } +type Session struct { + ID uint `gorm:"primaryKey;not null;unique;autoIncrement"` + AccountID string `gorm:"not null;index:sessguid,unique"` + AppName string + AppVersion string + Platform string + PushEnabled bool + PushToken string + Created int64 `gorm:"autoCreateTime"` + Account Account `gorm:"references:GUID"` + Token string `gorm:"not null;index:sessguid,unique"` + EventTypes []EventType +} + +type EventType struct { + ID uint `gorm:"primaryKey;not null;unique;autoIncrement"` + SessionID uint `gorm:"not null;index:sessiontype"` + Name string + Session *Session +} + type App struct { ID uint `gorm:"primaryKey;not null;unique;autoIncrement"` AccountID string `gorm:"not null;index:appguid,unique"` @@ -214,14 +238,24 @@ type Channel struct { DetailRevision int64 `gorm:"not null"` DataType string `gorm:"index"` Data string + ChannelPush bool + HostPush bool Created int64 `gorm:"autoCreateTime"` Updated int64 `gorm:"autoUpdateTime"` Groups []Group `gorm:"many2many:channel_groups;"` Cards []Card `gorm:"many2many:channel_cards;"` + Members []Member Topics []Topic ChannelSlot ChannelSlot } +type Member struct { + ID uint `gorm:"primaryKey;not null;unique;autoIncrement"` + ChannelID uint + CardID uint + PushEnabled bool +} + type TopicSlot struct { ID uint TopicSlotID string `gorm:"not null;index:topicaccount,unique;index:topicchannel,unique"`