package handler import ( "blackforestbytes.com/simplecloudnotifier/api/apierr" hl "blackforestbytes.com/simplecloudnotifier/api/apihighlight" "blackforestbytes.com/simplecloudnotifier/api/ginresp" ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken" primarydb "blackforestbytes.com/simplecloudnotifier/db/impl/primary" "blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/models" "database/sql" "errors" "fmt" "gogs.mikescher.com/BlackForestBytes/goext/dataext" "gogs.mikescher.com/BlackForestBytes/goext/ginext" "gogs.mikescher.com/BlackForestBytes/goext/langext" "net/http" ) type CompatHandler struct { app *logic.Application database *primarydb.Database } func NewCompatHandler(app *logic.Application) CompatHandler { return CompatHandler{ app: app, database: app.Database.Primary, } } // SendMessage swaggerdoc // // @Deprecated // // @Summary Send a new message (compatibility) // @Description All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required // @Tags External // // @Param query_data query handler.SendMessage.combined false " " // @Param form_data formData handler.SendMessage.combined false " " // // @Success 200 {object} handler.SendMessage.response // @Failure 400 {object} ginresp.apiError // @Failure 401 {object} ginresp.apiError // @Failure 403 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // // @Router /send.php [POST] func (h CompatHandler) SendMessage(pctx ginext.PreContext) ginext.HTTPResponse { type combined struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` Title *string `json:"title" form:"title"` Content *string `json:"content" form:"content"` Priority *int `json:"priority" form:"priority"` UserMessageID *string `json:"msg_id" form:"msg_id"` SendTimestamp *float64 `json:"timestamp" form:"timestamp"` } type response struct { Success bool `json:"success"` ErrorID apierr.APIError `json:"error"` ErrorHighlight int `json:"errhighlight"` Message string `json:"message"` SuppressSend bool `json:"suppress_send"` MessageCount int `json:"messagecount"` Quota int `json:"quota"` IsPro bool `json:"is_pro"` QuotaMax int `json:"quota_max"` SCNMessageID int64 `json:"scn_msg_id"` } var f combined var q combined ctx, g, errResp := pctx.Query(&q).Form(&f).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { data := dataext.ObjectMerge(f, q) newid, err := h.database.ConvertCompatID(ctx, langext.Coalesce(data.UserID, -1), "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if newid == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil) } okResp, errResp := h.app.SendMessage(g, ctx, langext.Ptr(models.UserID(*newid)), data.UserKey, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil) if errResp != nil { return *errResp } else { return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, ErrorID: apierr.NO_ERROR, ErrorHighlight: -1, Message: langext.Conditional(okResp.MessageIsOld, "Message already sent", "Message sent"), SuppressSend: okResp.MessageIsOld, MessageCount: okResp.User.MessagesSent, Quota: okResp.User.QuotaUsedToday(), IsPro: okResp.User.IsPro, QuotaMax: okResp.User.QuotaPerDay(), SCNMessageID: okResp.CompatMessageID, })) } }) } // Register swaggerdoc // // @Summary Register a new account // @ID compat-register // @Tags API-v1 // // @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" // // @Param fcm_token formData string true "the (android) fcm token" // @Param pro formData string true "if the user is a paid account" Enums(true, false) // @Param pro_token formData string true "the (android) IAP token" // // @Success 200 {object} handler.Register.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/register.php [get] func (h CompatHandler) Register(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { FCMToken *string `json:"fcm_token" form:"fcm_token"` Pro *string `json:"pro" form:"pro"` ProToken *string `json:"pro_token" form:"pro_token"` } type response struct { Success bool `json:"success"` Message string `json:"message"` UserID int64 `json:"user_id"` UserKey string `json:"user_key"` QuotaUsed int `json:"quota"` QuotaMax int `json:"quota_max"` IsPro bool `json:"is_pro"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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.ProToken != nil { data.ProToken = langext.Ptr("ANDROID|v1|" + *data.ProToken) } if *data.Pro != "true" { data.ProToken = nil } if data.ProToken != nil { ptok, err := h.app.VerifyProToken(ctx, *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") } } 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 pro tokens") } } user, err := h.database.CreateUser(ctx, data.ProToken, nil) if err != nil { return ginresp.CompatAPIError(0, "Failed to create user in db") } _, err = h.database.CreateKeyToken(ctx, "CompatKey", user.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermAdmin}, adminKey) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err) } _, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat", nil) if err != nil { return ginresp.CompatAPIError(0, "Failed to create client in db") } oldid, err := h.database.CreateCompatID(ctx, "userid", user.UserID.String()) if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to create userid", err) } return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "New user registered", UserID: oldid, UserKey: adminKey, QuotaUsed: user.QuotaUsedToday(), QuotaMax: user.QuotaPerDay(), IsPro: user.IsPro, })) }) } // Info swaggerdoc // // @Summary Get information about the current user // @ID compat-info // @Tags API-v1 // // @Deprecated // // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // // @Param user_id formData string true "the user_id" // @Param user_key formData string true "the user_key" // // @Success 200 {object} handler.Info.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/info.php [get] func (h CompatHandler) Info(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` } type response struct { Success bool `json:"success"` Message string `json:"message"` UserID int64 `json:"user_id"` UserKey string `json:"user_key"` QuotaUsed int `json:"quota"` QuotaMax int `json:"quota_max"` IsPro int `json:"is_pro"` FCMSet bool `json:"fcm_token_set"` UnackCount int64 `json:"unack_count"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockRead, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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]]") } useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if useridCompNew == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil) } user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew)) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(201, "User not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } if keytok == nil { return ginresp.CompatAPIError(204, "Authentification failed") } if !keytok.IsAdmin(user.UserID) { 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") } filter := models.MessageFilter{ Sender: langext.Ptr([]models.UserID{user.UserID}), CompatAcknowledged: langext.Ptr(false), } unackCount, err := h.database.CountMessages(ctx, filter) if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", UserID: *data.UserID, UserKey: keytok.Token, QuotaUsed: user.QuotaUsedToday(), QuotaMax: user.QuotaPerDay(), IsPro: langext.Conditional(user.IsPro, 1, 0), FCMSet: len(clients) > 0, UnackCount: unackCount, })) }) } // Ack swaggerdoc // // @Summary Acknowledge that a message was received // @ID compat-ack // @Tags API-v1 // // @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" // // @Param user_id formData string true "the user_id" // @Param user_key formData string true "the user_key" // @Param scn_msg_id formData string true "the message id" // // @Success 200 {object} handler.Ack.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/ack.php [get] func (h CompatHandler) Ack(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` MessageID *int64 `json:"scn_msg_id" form:"scn_msg_id"` } type response struct { Success bool `json:"success"` Message string `json:"message"` PrevAckValue int `json:"prev_ack"` NewAckValue int `json:"new_ack"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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]]") } useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if useridCompNew == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, fmt.Sprintf("User %d not found (compat)", *data.UserID), nil) } user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew)) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(201, "User not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } if keytok == nil { return ginresp.CompatAPIError(204, "Authentification failed") } if !keytok.IsAdmin(user.UserID) { return ginresp.CompatAPIError(204, "Authentification failed") } messageIdComp, err := h.database.ConvertCompatID(ctx, *data.MessageID, "messageid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query messageid", err) } if messageIdComp == nil { return ginresp.SendAPIError(g, 400, apierr.MESSAGE_NOT_FOUND, hl.NONE, fmt.Sprintf("Message %d not found (compat)", *data.MessageID), nil) } ackBefore, err := h.database.GetAck(ctx, models.MessageID(*messageIdComp)) if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query ack", err) } if !ackBefore { err = h.database.SetAck(ctx, user.UserID, models.MessageID(*messageIdComp)) if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to set ack", err) } } return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", PrevAckValue: langext.Conditional(ackBefore, 1, 0), NewAckValue: 1, })) }) } // Requery swaggerdoc // // @Summary Return all not-acknowledged messages // @ID compat-requery // @Tags API-v1 // // @Deprecated // // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // // @Param user_id formData string true "the user_id" // @Param user_key formData string true "the user_key" // // @Success 200 {object} handler.Requery.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/requery.php [get] func (h CompatHandler) Requery(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` } type response struct { Success bool `json:"success"` Message string `json:"message"` Count int `json:"count"` Data []models.CompatMessage `json:"data"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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]]") } useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if useridCompNew == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil) } user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew)) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(201, "User not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } if keytok == nil { return ginresp.CompatAPIError(204, "Authentification failed") } if !keytok.IsAdmin(user.UserID) { return ginresp.CompatAPIError(204, "Authentification failed") } filter := models.MessageFilter{ Sender: langext.Ptr([]models.UserID{user.UserID}), CompatAcknowledged: langext.Ptr(false), } msgs, _, err := h.database.ListMessages(ctx, filter, langext.Ptr(16), ct.Start()) if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } compMsgs := make([]models.CompatMessage, 0, len(msgs)) for _, v := range msgs { messageIdComp, err := h.database.ConvertToCompatIDOrCreate(ctx, "messageid", v.MessageID.String()) if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create messageid", err) } compMsgs = append(compMsgs, models.CompatMessage{ Title: v.Title, Body: v.Content, Priority: v.Priority, Timestamp: v.Timestamp().Unix(), UserMessageID: v.UserMessageID, SCNMessageID: messageIdComp, Trimmed: nil, }) } return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", Count: len(compMsgs), Data: compMsgs, })) }) } // Update swaggerdoc // // @Summary Set the fcm-token (android) // @ID compat-update // @Tags API-v1 // // @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" // // @Param user_id formData string true "the user_id" // @Param user_key formData string true "the user_key" // @Param fcm_token formData string true "the (android) fcm token" // // @Success 200 {object} handler.Update.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/update.php [get] func (h CompatHandler) Update(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` FCMToken *string `json:"fcm_token" form:"fcm_token"` } type response struct { Success bool `json:"success"` Message string `json:"message"` UserID int64 `json:"user_id"` UserKey string `json:"user_key"` QuotaUsed int `json:"quota"` QuotaMax int `json:"quota_max"` IsPro int `json:"is_pro"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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]]") } useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if useridCompNew == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil) } user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew)) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(201, "User not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } if keytok == nil { return ginresp.CompatAPIError(204, "Authentification failed") } if !keytok.IsAdmin(user.UserID) { 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() _, err = h.database.CreateKeyToken(ctx, "CompatKey", user.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermAdmin}, newAdminKey) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err) } err = h.database.DeleteKeyToken(ctx, keytok.KeyTokenID) 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", nil) if err != nil { return ginresp.CompatAPIError(0, "Failed to create client") } } user, err = h.database.GetUser(ctx, user.UserID) if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "user updated", UserID: *data.UserID, UserKey: newAdminKey, 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 // @Tags API-v1 // // @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" // // @Param user_id formData string true "The user_id" // @Param user_key formData string true "The user_key" // @Param scn_msg_id formData string true "The message-id" // // @Success 200 {object} handler.Expand.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/expand.php [get] func (h CompatHandler) Expand(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` MessageID *int64 `json:"scn_msg_id" form:"scn_msg_id"` } type response struct { Success bool `json:"success"` Message string `json:"message"` Data models.CompatMessage `json:"data"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockRead, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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]]") } useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if useridCompNew == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil) } user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew)) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(201, "User not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } if keytok == nil { return ginresp.CompatAPIError(204, "Authentification failed") } if !keytok.IsAdmin(user.UserID) { return ginresp.CompatAPIError(204, "Authentification failed") } messageCompNew, err := h.database.ConvertCompatID(ctx, *data.MessageID, "messageid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query messagid", err) } if messageCompNew == nil { return ginresp.CompatAPIError(301, "Message not found") } msg, err := h.database.GetMessage(ctx, models.MessageID(*messageCompNew), false) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(301, "Message not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query message") } return finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "ok", Data: models.CompatMessage{ Title: msg.Title, Body: msg.Content, Trimmed: langext.Ptr(false), Priority: msg.Priority, Timestamp: msg.Timestamp().Unix(), UserMessageID: msg.UserMessageID, SCNMessageID: *data.MessageID, }, })) }) } // Upgrade swaggerdoc // // @Summary Upgrade a free account to a paid account // @ID compat-upgrade // @Tags API-v1 // // @Deprecated // // @Param user_id query string true "the user_id" // @Param user_key query string true "the user_key" // @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" // // @Param user_id formData string true "the user_id" // @Param user_key formData string true "the user_key" // @Param pro formData string true "if the user is a paid account" Enums(true, false) // @Param pro_token formData string true "the (android) IAP token" // // @Success 200 {object} handler.Upgrade.response // @Failure default {object} ginresp.compatAPIError // // @Router /api/upgrade.php [get] func (h CompatHandler) Upgrade(pctx ginext.PreContext) ginext.HTTPResponse { type query struct { UserID *int64 `json:"user_id" form:"user_id"` UserKey *string `json:"user_key" form:"user_key"` Pro *string `json:"pro" form:"pro"` ProToken *string `json:"pro_token" form:"pro_token"` } type response struct { 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"` } var datq query var datb query ctx, g, errResp := pctx.Query(&datq).Body(&datb).IgnoreWrongContentType().Start() if errResp != nil { return *errResp } defer ctx.Cancel() return h.app.DoRequest(ctx, g, models.TLockReadWrite, func(ctx *logic.AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse { 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]]") } useridCompNew, err := h.database.ConvertCompatID(ctx, *data.UserID, "userid") if err != nil { return ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query userid", err) } if useridCompNew == nil { return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil) } user, err := h.database.GetUser(ctx, models.UserID(*useridCompNew)) if errors.Is(err, sql.ErrNoRows) { return ginresp.CompatAPIError(201, "User not found") } if err != nil { return ginresp.CompatAPIError(0, "Failed to query user") } keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey) if err != nil { return ginresp.CompatAPIError(0, "Failed to query token") } if keytok == nil { return ginresp.CompatAPIError(204, "Authentification failed") } if !keytok.IsAdmin(user.UserID) { return ginresp.CompatAPIError(204, "Authentification failed") } if data.ProToken != nil { data.ProToken = langext.Ptr("ANDROID|v1|" + *data.ProToken) } if *data.Pro != "true" { data.ProToken = nil } if data.ProToken != nil { ptok, err := h.app.VerifyProToken(ctx, *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.ClearProTokens(ctx, *data.ProToken) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to clear existing fcm tokens", err) } err = h.database.UpdateUserProToken(ctx, user.UserID, langext.Ptr(*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 finishSuccess(ginext.JSON(http.StatusOK, response{ Success: true, Message: "user updated", UserID: *data.UserID, QuotaUsed: user.QuotaUsedToday(), QuotaMax: user.QuotaPerDay(), IsPro: user.IsPro, })) }) }