964 lines
30 KiB
Go
964 lines
30 KiB
Go
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<old>", 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.DeleteClientsByFCM(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<old>", 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<old>", 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<old>", 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<old>", 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<old>", 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<old>", 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<old>", 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<old>", 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<old>", 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<old>", 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,
|
|
}))
|
|
|
|
})
|
|
}
|