From 728b12107f8be2481268b64c50c0f0ff6dca96e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Sun, 20 Nov 2022 01:28:32 +0100 Subject: [PATCH] compat methods --- server/README.md | 3 +- server/api/apierr/enums.go | 2 +- server/api/handler/compat.go | 511 ++++++++++++++++++++++++++---- server/common/ginresp/apiError.go | 6 + server/common/ginresp/resp.go | 24 +- server/db/users.go | 27 +- server/firebase/firebase.go | 1 + server/models/compat.go | 23 +- server/swagger/swagger.json | 153 ++++----- server/swagger/swagger.yaml | 121 +++---- 10 files changed, 609 insertions(+), 262 deletions(-) diff --git a/server/README.md b/server/README.md index 01f0c79..018d371 100644 --- a/server/README.md +++ b/server/README.md @@ -12,4 +12,5 @@ - deploy - Dockerfile - php in html - - full-text-search: https://www.sqlite.org/fts5.html#contentless_tables \ No newline at end of file + - full-text-search: https://www.sqlite.org/fts5.html#contentless_tables + - route to re-create keys \ No newline at end of file diff --git a/server/api/apierr/enums.go b/server/api/apierr/enums.go index f78d905..58c72fa 100644 --- a/server/api/apierr/enums.go +++ b/server/api/apierr/enums.go @@ -12,7 +12,7 @@ const ( INVALID_PRIO APIError = 1104 REQ_METHOD APIError = 1105 INVALID_CLIENTTYPE APIError = 1106 - PAGETOKEN_ERROR APIError = 1120 + PAGETOKEN_ERROR APIError = 1121 BINDFAIL_QUERY_PARAM APIError = 1151 BINDFAIL_BODY_PARAM APIError = 1152 BINDFAIL_URI_PARAM APIError = 1153 diff --git a/server/api/handler/compat.go b/server/api/handler/compat.go index c176a93..3b60dc5 100644 --- a/server/api/handler/compat.go +++ b/server/api/handler/compat.go @@ -2,18 +2,25 @@ package handler import ( "blackforestbytes.com/simplecloudnotifier/common/ginresp" + "blackforestbytes.com/simplecloudnotifier/db" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" + "database/sql" "github.com/gin-gonic/gin" + "gogs.mikescher.com/BlackForestBytes/goext/dataext" + "gogs.mikescher.com/BlackForestBytes/goext/langext" + "net/http" ) type CompatHandler struct { - app *logic.Application + app *logic.Application + database *db.Database } func NewCompatHandler(app *logic.Application) CompatHandler { return CompatHandler{ - app: app, + app: app, + database: app.Database, } } @@ -21,171 +28,477 @@ func NewCompatHandler(app *logic.Application) CompatHandler { // // @Summary Register a new account // @ID compat-register +// @Deprecated // @Param fcm_token query string true "the (android) fcm token" // @Param pro query string true "if the user is a paid account" Enums(true, false) // @Param pro_token query string true "the (android) IAP token" // @Success 200 {object} handler.Register.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/register.php [get] func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse { type query struct { - FCMToken *string `form:"fcm_token"` - Pro *string `form:"pro"` - ProToken *string `form:"pro_token"` + FCMToken *string `form:"fcm_token" json:"fcm_token"` + Pro *string `form:"pro" json:"pro"` + ProToken *string `form:"pro_token" json:"pro_token"` } type response struct { Success bool `json:"success"` Message string `json:"message"` - UserID string `json:"user_id"` + UserID int64 `json:"user_id"` UserKey string `json:"user_key"` QuotaUsed int `json:"quota"` QuotaMax int `json:"quota_max"` IsPro int `json:"is_pro"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.FCMToken == nil { + return ginresp.CompatAPIError(0, "Missing parameter [[fcm_token]]") + } + if data.Pro == nil { + return ginresp.CompatAPIError(0, "Missing parameter [[pro]]") + } + if data.ProToken == nil { + return ginresp.CompatAPIError(0, "Missing parameter [[pro_token]]") + } + + if *data.Pro != "true" { + data.ProToken = nil + } + + if data.ProToken != nil { + ptok, err := h.app.VerifyProToken(*data.ProToken) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query purchase status") + } + + if !ptok { + return ginresp.CompatAPIError(0, "Purchase token could not be verified") + } + } + + readKey := h.app.GenerateRandomAuthKey() + sendKey := h.app.GenerateRandomAuthKey() + adminKey := h.app.GenerateRandomAuthKey() + + err := h.database.ClearFCMTokens(ctx, *data.FCMToken) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to clear existing fcm tokens") + } + + if data.ProToken != nil { + err := h.database.ClearProTokens(ctx, *data.ProToken) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to clear existing fcm tokens") + } + } + + user, err := h.database.CreateUser(ctx, readKey, sendKey, adminKey, data.ProToken, nil) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to create user in db") + } + + _, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat") + if err != nil { + return ginresp.CompatAPIError(0, "Failed to create user in db") + } + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "New user registered", + UserID: user.UserID, + UserKey: user.AdminKey, + QuotaUsed: user.QuotaUsedToday(), + QuotaMax: user.QuotaPerDay(), + IsPro: langext.Conditional(user.IsPro, 1, 0), + })) } // Info swaggerdoc // // @Summary Get information about the current user // @ID compat-info +// @Deprecated // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // @Success 200 {object} handler.Info.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/info.php [get] func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse { type query struct { - UserID string `form:"user_id"` - UserKey string `form:"user_key"` + UserID *int64 `form:"user_id" json:"user_id"` + UserKey *string `form:"user_key" json:"user_key"` } type response struct { - Success string `json:"success"` + Success bool `json:"success"` Message string `json:"message"` - UserID string `json:"user_id"` + UserID int64 `json:"user_id"` UserKey string `json:"user_key"` - QuotaUsed string `json:"quota"` - QuotaMax string `json:"quota_max"` - IsPro string `json:"is_pro"` + QuotaUsed int `json:"quota"` + QuotaMax int `json:"quota_max"` + IsPro int `json:"is_pro"` FCMSet bool `json:"fcm_token_set"` UnackCount int `json:"unack_count"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.UserID == nil { + return ginresp.CompatAPIError(101, "Missing parameter [[user_id]]") + } + if data.UserKey == nil { + return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]") + } + + user, err := h.database.GetUser(ctx, *data.UserID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(201, "User not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + if user.AdminKey != *data.UserKey { + return ginresp.CompatAPIError(204, "Authentification failed") + } + + clients, err := h.database.ListClients(ctx, user.UserID) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query clients") + } + + fcmSet := langext.ArrAny(clients, func(i int) bool { return clients[i].FCMToken != nil }) + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "ok", + UserID: user.UserID, + UserKey: user.AdminKey, + QuotaUsed: user.QuotaUsedToday(), + QuotaMax: user.QuotaPerDay(), + IsPro: langext.Conditional(user.IsPro, 1, 0), + FCMSet: fcmSet, + UnackCount: 0, + })) } // Ack swaggerdoc // // @Summary Acknowledge that a message was received // @ID compat-ack +// @Deprecated // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // @Param scn_msg_id query string true "the message id" // @Success 200 {object} handler.Ack.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/ack.php [get] func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse { type query struct { - UserID string `form:"user_id"` - UserKey string `form:"user_key"` - MessageID string `form:"scn_msg_id"` + UserID *int64 `form:"user_id"` + UserKey *string `form:"user_key"` + MessageID *int64 `form:"scn_msg_id"` } type response struct { - Success string `json:"success"` + Success bool `json:"success"` Message string `json:"message"` PrevAckValue int `json:"prev_ack"` NewAckValue int `json:"new_ack"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.UserID == nil { + return ginresp.CompatAPIError(101, "Missing parameter [[user_id]]") + } + if data.UserKey == nil { + return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]") + } + if data.MessageID == nil { + return ginresp.CompatAPIError(103, "Missing parameter [[scn_msg_id]]") + } + + user, err := h.database.GetUser(ctx, *data.UserID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(201, "User not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + if user.AdminKey != *data.UserKey { + return ginresp.CompatAPIError(204, "Authentification failed") + } + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "ok", + PrevAckValue: 0, + NewAckValue: 1, + })) } // Requery swaggerdoc // // @Summary Return all not-acknowledged messages // @ID compat-requery +// @Deprecated // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // @Success 200 {object} handler.Requery.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/requery.php [get] func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse { type query struct { - UserID string `form:"user_id"` - UserKey string `form:"user_key"` + UserID *int64 `form:"user_id"` + UserKey *string `form:"user_key"` } type response struct { - Success string `json:"success"` + Success bool `json:"success"` Message string `json:"message"` Count int `json:"count"` Data []models.CompatMessage `json:"data"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.UserID == nil { + return ginresp.CompatAPIError(101, "Missing parameter [[user_id]]") + } + if data.UserKey == nil { + return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]") + } + + user, err := h.database.GetUser(ctx, *data.UserID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(201, "User not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + if user.AdminKey != *data.UserKey { + return ginresp.CompatAPIError(204, "Authentification failed") + } + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "ok", + Count: 0, + Data: make([]models.CompatMessage, 0), + })) } // Update swaggerdoc // // @Summary Set the fcm-token (android) // @ID compat-update +// @Deprecated // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // @Param fcm_token query string true "the (android) fcm token" // @Success 200 {object} handler.Update.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/update.php [get] func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse { type query struct { - UserID string `form:"user_id"` - UserKey string `form:"user_key"` - FCMToken string `form:"fcm_token"` + UserID *int64 `form:"user_id"` + UserKey *string `form:"user_key"` + FCMToken *string `form:"fcm_token"` } type response struct { - Success string `json:"success"` + Success bool `json:"success"` Message string `json:"message"` - UserID string `json:"user_id"` + UserID int64 `json:"user_id"` UserKey string `json:"user_key"` - QuotaUsed string `json:"quota"` - QuotaMax string `json:"quota_max"` - IsPro string `json:"is_pro"` + QuotaUsed int `json:"quota"` + QuotaMax int `json:"quota_max"` + IsPro int `json:"is_pro"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.UserID == nil { + return ginresp.CompatAPIError(101, "Missing parameter [[user_id]]") + } + if data.UserKey == nil { + return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]") + } + + user, err := h.database.GetUser(ctx, *data.UserID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(201, "User not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + if user.AdminKey != *data.UserKey { + return ginresp.CompatAPIError(204, "Authentification failed") + } + + clients, err := h.database.ListClients(ctx, user.UserID) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to list clients") + } + + newAdminKey := h.app.GenerateRandomAuthKey() + newReadKey := h.app.GenerateRandomAuthKey() + newSendKey := h.app.GenerateRandomAuthKey() + + err = h.database.UpdateUserKeys(ctx, user.UserID, newSendKey, newReadKey, newAdminKey) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to update keys") + } + + if data.FCMToken != nil { + + for _, client := range clients { + + err = h.database.DeleteClient(ctx, client.ClientID) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to delete client") + } + + } + + _, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat") + if err != nil { + return ginresp.CompatAPIError(0, "Failed to delete client") + } + + } + + user, err = h.database.GetUser(ctx, user.UserID) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "user updated", + UserID: user.UserID, + UserKey: user.AdminKey, + QuotaUsed: user.QuotaUsedToday(), + QuotaMax: user.QuotaPerDay(), + IsPro: langext.Conditional(user.IsPro, 1, 0), + })) } // Expand swaggerdoc // // @Summary Get a whole (potentially truncated) message // @ID compat-expand +// @Deprecated // @Success 200 {object} handler.Expand.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/expand.php [get] func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse { type query struct { - UserID string `form:"user_id"` - UserKey string `form:"user_key"` - MessageID string `form:"scn_msg_id"` + UserID *int64 `form:"user_id"` + UserKey *string `form:"user_key"` + MessageID *int64 `form:"scn_msg_id"` } type response struct { - Success string `json:"success"` - Message string `json:"message"` - Data models.ShortCompatMessage `json:"data"` + Success bool `json:"success"` + Message string `json:"message"` + Data models.CompatMessage `json:"data"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.UserID == nil { + return ginresp.CompatAPIError(101, "Missing parameter [[user_id]]") + } + if data.UserKey == nil { + return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]") + } + if data.MessageID == nil { + return ginresp.CompatAPIError(103, "Missing parameter [[scn_msg_id]]") + } + + user, err := h.database.GetUser(ctx, *data.UserID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(201, "User not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + if user.AdminKey != *data.UserKey { + return ginresp.CompatAPIError(204, "Authentification failed") + } + + msg, err := h.database.GetMessage(ctx, *data.MessageID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(301, "Message not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query message") + } + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "ok", + Data: models.CompatMessage{ + Title: msg.Title, + Body: langext.Coalesce(msg.Content, ""), + Trimmed: langext.Ptr(false), + Priority: msg.Priority, + Timestamp: msg.Timestamp().Unix(), + UserMessageID: msg.UserMessageID, + SCNMessageID: msg.SCNMessageID, + }, + })) } // Upgrade swaggerdoc @@ -197,22 +510,96 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse { // @Param pro query string true "if the user is a paid account" Enums(true, false) // @Param pro_token query string true "the (android) IAP token" // @Success 200 {object} handler.Upgrade.response -// @Failure 500 {object} ginresp.apiError +// @Failure 200 {object} ginresp.compatAPIError // @Router /api/upgrade.php [get] +// @Deprecated func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse { type query struct { - UserID string `form:"user_id"` - UserKey string `form:"user_key"` - Pro string `form:"pro"` - ProToken string `form:"pro_token"` + UserID *int64 `form:"user_id"` + UserKey *string `form:"user_key"` + Pro *string `form:"pro"` + ProToken *string `form:"pro_token"` } type response struct { - Success string `json:"success"` - Message string `json:"message"` - Data models.ShortCompatMessage `json:"data"` + Success bool `json:"success"` + Message string `json:"message"` + UserID int64 `json:"user_id"` + QuotaUsed int `json:"quota"` + QuotaMax int `json:"quota_max"` + IsPro bool `json:"is_pro"` } - //TODO + var datq query + var datb query + ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb) + if errResp != nil { + return *errResp + } + defer ctx.Cancel() - return ginresp.NotImplemented() //TODO + data := dataext.ObjectMerge(datb, datq) + + if data.UserID == nil { + return ginresp.CompatAPIError(101, "Missing parameter [[user_id]]") + } + if data.UserKey == nil { + return ginresp.CompatAPIError(102, "Missing parameter [[user_key]]") + } + if data.Pro == nil { + return ginresp.CompatAPIError(103, "Missing parameter [[pro]]") + } + if data.ProToken == nil { + return ginresp.CompatAPIError(104, "Missing parameter [[pro_token]]") + } + + user, err := h.database.GetUser(ctx, *data.UserID) + if err == sql.ErrNoRows { + return ginresp.CompatAPIError(201, "User not found") + } + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + if user.AdminKey != *data.UserKey { + return ginresp.CompatAPIError(204, "Authentification failed") + } + + if *data.Pro != "true" { + data.ProToken = nil + } + + if data.ProToken != nil { + ptok, err := h.app.VerifyProToken(*data.ProToken) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query purchase status") + } + + if !ptok { + return ginresp.CompatAPIError(0, "Purchase token could not be verified") + } + + err = h.database.UpdateUserProToken(ctx, user.UserID, data.ProToken) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to update user") + } + } else { + err = h.database.UpdateUserProToken(ctx, user.UserID, nil) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to update user") + } + } + + user, err = h.database.GetUser(ctx, user.UserID) + if err != nil { + return ginresp.CompatAPIError(0, "Failed to query user") + } + + return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{ + Success: true, + Message: "user updated", + UserID: user.UserID, + QuotaUsed: user.QuotaUsedToday(), + QuotaMax: user.QuotaPerDay(), + IsPro: user.IsPro, + })) } diff --git a/server/common/ginresp/apiError.go b/server/common/ginresp/apiError.go index 358240d..51f6356 100644 --- a/server/common/ginresp/apiError.go +++ b/server/common/ginresp/apiError.go @@ -7,3 +7,9 @@ type apiError struct { Message string `json:"message"` RawError error `json:"errorObject,omitempty"` } + +type compatAPIError struct { + Success bool `json:"success"` + ErrorID int `json:"errid,omitempty"` + Message string `json:"message"` +} diff --git a/server/common/ginresp/resp.go b/server/common/ginresp/resp.go index a8ca0fd..f54e929 100644 --- a/server/common/ginresp/resp.go +++ b/server/common/ginresp/resp.go @@ -22,7 +22,6 @@ func (j jsonHTTPResponse) Write(g *gin.Context) { type emptyHTTPResponse struct { statusCode int - data any } func (j emptyHTTPResponse) Write(g *gin.Context) { @@ -48,15 +47,6 @@ func (j dataHTTPResponse) Write(g *gin.Context) { g.Data(j.statusCode, j.contentType, j.data) } -type errHTTPResponse struct { - statusCode int - data any -} - -func (j errHTTPResponse) Write(g *gin.Context) { - g.JSON(j.statusCode, j.data) -} - func Status(sc int) HTTPResponse { return &emptyHTTPResponse{statusCode: sc} } @@ -74,21 +64,25 @@ func Text(sc int, data string) HTTPResponse { } func InternalError(e error) HTTPResponse { - return &errHTTPResponse{statusCode: http.StatusInternalServerError, data: apiError{Success: false, Error: int(apierr.INTERNAL_EXCEPTION), Message: e.Error()}} + return &jsonHTTPResponse{statusCode: http.StatusInternalServerError, data: apiError{Success: false, Error: int(apierr.INTERNAL_EXCEPTION), Message: e.Error()}} } func InternAPIError(status int, errorid apierr.APIError, msg string, e error) HTTPResponse { if scn.Conf.ReturnRawErrors { - return &errHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), Message: msg, RawError: e}} + return &jsonHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), Message: msg, RawError: e}} } else { - return &errHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), Message: msg}} + return &jsonHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), Message: msg}} } } +func CompatAPIError(errid int, msg string) HTTPResponse { + return &jsonHTTPResponse{statusCode: 200, data: compatAPIError{Success: false, ErrorID: errid, Message: msg}} +} + func SendAPIError(status int, errorid apierr.APIError, highlight int, msg string) HTTPResponse { - return &errHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), ErrorHighlight: highlight, Message: msg}} + return &jsonHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), ErrorHighlight: highlight, Message: msg}} } func NotImplemented() HTTPResponse { - return &errHTTPResponse{statusCode: http.StatusInternalServerError, data: apiError{Success: false, Error: -1, ErrorHighlight: 0, Message: "Not Implemented"}} + return &jsonHTTPResponse{statusCode: http.StatusInternalServerError, data: apiError{Success: false, Error: -1, ErrorHighlight: 0, Message: "Not Implemented"}} } diff --git a/server/db/users.go b/server/db/users.go index b2d0ee7..157c601 100644 --- a/server/db/users.go +++ b/server/db/users.go @@ -110,7 +110,9 @@ func (db *Database) UpdateUserUsername(ctx TxContext, userid int64, username *st return err } - _, err = tx.ExecContext(ctx, "UPDATE users SET username = ? WHERE user_id = ?", username, userid) + _, err = tx.ExecContext(ctx, "UPDATE users SET username = ? WHERE user_id = ?", + username, + userid) if err != nil { return err } @@ -124,7 +126,10 @@ func (db *Database) UpdateUserProToken(ctx TxContext, userid int64, protoken *st return err } - _, err = tx.ExecContext(ctx, "UPDATE users SET pro_token = ? AND is_pro = ? WHERE user_id = ?", protoken, bool2DB(protoken != nil), userid) + _, err = tx.ExecContext(ctx, "UPDATE users SET pro_token = ? AND is_pro = ? WHERE user_id = ?", + protoken, + bool2DB(protoken != nil), + userid) if err != nil { return err } @@ -168,3 +173,21 @@ func (db *Database) UpdateUserLastRead(ctx TxContext, userid int64) error { return nil } + +func (db *Database) UpdateUserKeys(ctx TxContext, userid int64, sendKey string, readKey string, adminKey string) error { + tx, err := ctx.GetOrCreateTransaction(db) + if err != nil { + return err + } + + _, err = tx.ExecContext(ctx, "UPDATE users SET send_key = ? AND read_key = ? AND admin_key = ? WHERE user_id = ?", + sendKey, + readKey, + adminKey, + userid) + if err != nil { + return err + } + + return nil +} diff --git a/server/firebase/firebase.go b/server/firebase/firebase.go index 2964e66..b8a5362 100644 --- a/server/firebase/firebase.go +++ b/server/firebase/firebase.go @@ -52,6 +52,7 @@ func (fb App) SendNotification(ctx context.Context, client models.Client, msg mo Data: map[string]string{ "scn_msg_id": strconv.FormatInt(msg.SCNMessageID, 10), "usr_msg_id": langext.Coalesce(msg.UserMessageID, ""), + "client_id": strconv.FormatInt(client.ClientID, 10), "timestamp": strconv.FormatInt(msg.Timestamp().Unix(), 10), "priority": strconv.Itoa(msg.Priority), "trimmed": langext.Conditional(msg.NeedsTrim(), "true", "false"), diff --git a/server/models/compat.go b/server/models/compat.go index f0789ad..d4b0e7c 100644 --- a/server/models/compat.go +++ b/server/models/compat.go @@ -1,20 +1,11 @@ package models type CompatMessage struct { - Title string `json:"title"` - Body string `json:"body"` - Priority int `json:"priority"` - Timestamp int64 `json:"timestamp"` - UserMessageID string `json:"usr_msg_id"` - SCNMessageID string `json:"scn_msg_id"` -} - -type ShortCompatMessage struct { - Title string `json:"title"` - Body string `json:"body"` - Trimmed bool `json:"trimmed"` - Priority int `json:"priority"` - Timestamp int64 `json:"timestamp"` - UserMessageID string `json:"usr_msg_id"` - SCNMessageID string `json:"scn_msg_id"` + Title string `json:"title"` + Body string `json:"body"` + Priority int `json:"priority"` + Timestamp int64 `json:"timestamp"` + UserMessageID *string `json:"usr_msg_id"` + SCNMessageID int64 `json:"scn_msg_id"` + Trimmed *bool `json:"trimmed"` } diff --git a/server/swagger/swagger.json b/server/swagger/swagger.json index 9ec41e2..1ab6fea 100644 --- a/server/swagger/swagger.json +++ b/server/swagger/swagger.json @@ -983,6 +983,7 @@ "get": { "summary": "Acknowledge that a message was received", "operationId": "compat-ack", + "deprecated": true, "parameters": [ { "type": "string", @@ -1010,13 +1011,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Ack.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1026,17 +1021,12 @@ "get": { "summary": "Get a whole (potentially truncated) message", "operationId": "compat-expand", + "deprecated": true, "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Expand.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1046,6 +1036,7 @@ "get": { "summary": "Get information about the current user", "operationId": "compat-info", + "deprecated": true, "parameters": [ { "type": "string", @@ -1066,13 +1057,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Info.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1082,6 +1067,7 @@ "get": { "summary": "Register a new account", "operationId": "compat-register", + "deprecated": true, "parameters": [ { "type": "string", @@ -1113,13 +1099,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Register.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1129,6 +1109,7 @@ "get": { "summary": "Return all not-acknowledged messages", "operationId": "compat-requery", + "deprecated": true, "parameters": [ { "type": "string", @@ -1149,13 +1130,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Requery.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1165,6 +1140,7 @@ "get": { "summary": "Set the fcm-token (android)", "operationId": "compat-update", + "deprecated": true, "parameters": [ { "type": "string", @@ -1192,13 +1168,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Update.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1208,6 +1178,7 @@ "get": { "summary": "Upgrade a free account to a paid account", "operationId": "compat-upgrade", + "deprecated": true, "parameters": [ { "type": "string", @@ -1246,13 +1217,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/handler.Upgrade.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" + "$ref": "#/definitions/ginresp.compatAPIError" } } } @@ -1495,6 +1460,20 @@ } } }, + "ginresp.compatAPIError": { + "type": "object", + "properties": { + "errid": { + "type": "integer" + }, + "message": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, "handler.Ack.response": { "type": "object", "properties": { @@ -1508,7 +1487,7 @@ "type": "integer" }, "success": { - "type": "string" + "type": "boolean" } } }, @@ -1584,13 +1563,13 @@ "type": "object", "properties": { "data": { - "$ref": "#/definitions/models.ShortCompatMessage" + "$ref": "#/definitions/models.CompatMessage" }, "message": { "type": "string" }, "success": { - "type": "string" + "type": "boolean" } } }, @@ -1609,25 +1588,25 @@ "type": "boolean" }, "is_pro": { - "type": "string" + "type": "integer" }, "message": { "type": "string" }, "quota": { - "type": "string" + "type": "integer" }, "quota_max": { - "type": "string" + "type": "integer" }, "success": { - "type": "string" + "type": "boolean" }, "unack_count": { "type": "integer" }, "user_id": { - "type": "string" + "type": "integer" }, "user_key": { "type": "string" @@ -1731,7 +1710,7 @@ "type": "boolean" }, "user_id": { - "type": "string" + "type": "integer" }, "user_key": { "type": "string" @@ -1754,7 +1733,7 @@ "type": "string" }, "success": { - "type": "string" + "type": "boolean" } } }, @@ -1829,22 +1808,22 @@ "type": "object", "properties": { "is_pro": { - "type": "string" + "type": "integer" }, "message": { "type": "string" }, "quota": { - "type": "string" + "type": "integer" }, "quota_max": { - "type": "string" + "type": "integer" }, "success": { - "type": "string" + "type": "boolean" }, "user_id": { - "type": "string" + "type": "integer" }, "user_key": { "type": "string" @@ -1865,14 +1844,23 @@ "handler.Upgrade.response": { "type": "object", "properties": { - "data": { - "$ref": "#/definitions/models.ShortCompatMessage" + "is_pro": { + "type": "boolean" }, "message": { "type": "string" }, + "quota": { + "type": "integer" + }, + "quota_max": { + "type": "integer" + }, "success": { - "type": "string" + "type": "boolean" + }, + "user_id": { + "type": "integer" } } }, @@ -1978,7 +1966,7 @@ "type": "integer" }, "scn_msg_id": { - "type": "string" + "type": "integer" }, "timestamp": { "type": "integer" @@ -1986,6 +1974,9 @@ "title": { "type": "string" }, + "trimmed": { + "type": "boolean" + }, "usr_msg_id": { "type": "string" } @@ -2029,32 +2020,6 @@ } } }, - "models.ShortCompatMessage": { - "type": "object", - "properties": { - "body": { - "type": "string" - }, - "priority": { - "type": "integer" - }, - "scn_msg_id": { - "type": "string" - }, - "timestamp": { - "type": "integer" - }, - "title": { - "type": "string" - }, - "trimmed": { - "type": "boolean" - }, - "usr_msg_id": { - "type": "string" - } - } - }, "models.SubscriptionJSON": { "type": "object", "properties": { diff --git a/server/swagger/swagger.yaml b/server/swagger/swagger.yaml index 5cd3454..c2b7954 100644 --- a/server/swagger/swagger.yaml +++ b/server/swagger/swagger.yaml @@ -12,6 +12,15 @@ definitions: success: type: boolean type: object + ginresp.compatAPIError: + properties: + errid: + type: integer + message: + type: string + success: + type: boolean + type: object handler.Ack.response: properties: message: @@ -21,7 +30,7 @@ definitions: prev_ack: type: integer success: - type: string + type: boolean type: object handler.AddClient.body: properties: @@ -70,11 +79,11 @@ definitions: handler.Expand.response: properties: data: - $ref: '#/definitions/models.ShortCompatMessage' + $ref: '#/definitions/models.CompatMessage' message: type: string success: - type: string + type: boolean type: object handler.Health.response: properties: @@ -86,19 +95,19 @@ definitions: fcm_token_set: type: boolean is_pro: - type: string + type: integer message: type: string quota: - type: string + type: integer quota_max: - type: string + type: integer success: - type: string + type: boolean unack_count: type: integer user_id: - type: string + type: integer user_key: type: string type: object @@ -165,7 +174,7 @@ definitions: success: type: boolean user_id: - type: string + type: integer user_key: type: string type: object @@ -180,7 +189,7 @@ definitions: message: type: string success: - type: string + type: boolean type: object handler.SendMessage.body: properties: @@ -229,17 +238,17 @@ definitions: handler.Update.response: properties: is_pro: - type: string + type: integer message: type: string quota: - type: string + type: integer quota_max: - type: string + type: integer success: - type: string + type: boolean user_id: - type: string + type: integer user_key: type: string type: object @@ -252,12 +261,18 @@ definitions: type: object handler.Upgrade.response: properties: - data: - $ref: '#/definitions/models.ShortCompatMessage' + is_pro: + type: boolean message: type: string + quota: + type: integer + quota_max: + type: integer success: - type: string + type: boolean + user_id: + type: integer type: object handler.pingResponse: properties: @@ -326,11 +341,13 @@ definitions: priority: type: integer scn_msg_id: - type: string + type: integer timestamp: type: integer title: type: string + trimmed: + type: boolean usr_msg_id: type: string type: object @@ -359,23 +376,6 @@ definitions: usr_message_id: type: string type: object - models.ShortCompatMessage: - properties: - body: - type: string - priority: - type: integer - scn_msg_id: - type: string - timestamp: - type: integer - title: - type: string - trimmed: - type: boolean - usr_msg_id: - type: string - type: object models.SubscriptionJSON: properties: channel_id: @@ -1076,6 +1076,7 @@ paths: summary: Update a subscription (e.g. confirm) /api/ack.php: get: + deprecated: true operationId: compat-ack parameters: - description: the user_id @@ -1097,27 +1098,21 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Ack.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Acknowledge that a message was received /api/expand.php: get: + deprecated: true operationId: compat-expand responses: "200": description: OK schema: - $ref: '#/definitions/handler.Expand.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Get a whole (potentially truncated) message /api/info.php: get: + deprecated: true operationId: compat-info parameters: - description: the user_id @@ -1134,14 +1129,11 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Info.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Get information about the current user /api/register.php: get: + deprecated: true operationId: compat-register parameters: - description: the (android) fcm token @@ -1166,14 +1158,11 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Register.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Register a new account /api/requery.php: get: + deprecated: true operationId: compat-requery parameters: - description: the user_id @@ -1190,14 +1179,11 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Requery.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Return all not-acknowledged messages /api/update.php: get: + deprecated: true operationId: compat-update parameters: - description: the user_id @@ -1219,14 +1205,11 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Update.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Set the fcm-token (android) /api/upgrade.php: get: + deprecated: true operationId: compat-upgrade parameters: - description: the user_id @@ -1256,11 +1239,7 @@ paths: "200": description: OK schema: - $ref: '#/definitions/handler.Upgrade.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/ginresp.compatAPIError' summary: Upgrade a free account to a paid account /db-test: get: