replace PHP in html with js & bugfixes
This commit is contained in:
parent
728b12107f
commit
0d3526221d
@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
|
|
||||||
- background job for re-delivery
|
- background job for re-delivery
|
||||||
- accept/decline subscriptions (PATCH subs)
|
- POST::/messages
|
||||||
- (message.go) api routes
|
|
||||||
- (compat.go) api routes
|
|
||||||
- https://firebase.google.com/docs/cloud-messaging/send-message#rest
|
- https://firebase.google.com/docs/cloud-messaging/send-message#rest
|
||||||
- List subscriptions on owned channels /RESTful?)
|
- List subscriptions on all owned channels (RESTful?)
|
||||||
- deploy
|
- deploy
|
||||||
- Dockerfile
|
- Dockerfile
|
||||||
- php in html
|
- php in html
|
||||||
- full-text-search: https://www.sqlite.org/fts5.html#contentless_tables
|
- full-text-search: https://www.sqlite.org/fts5.html#contentless_tables
|
||||||
- route to re-create keys
|
- route to re-create keys
|
||||||
|
- ack/read deliveries && return ack-count
|
||||||
|
- compat methods should bind body as form-data
|
@ -40,16 +40,16 @@ func NewAPIHandler(app *logic.Application) APIHandler {
|
|||||||
// @Router /api-v2/users/ [POST]
|
// @Router /api-v2/users/ [POST]
|
||||||
func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
|
func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
|
||||||
type body struct {
|
type body struct {
|
||||||
FCMToken string `json:"fcm_token"`
|
FCMToken string `json:"fcm_token" binding:"required"`
|
||||||
ProToken *string `json:"pro_token"`
|
ProToken *string `json:"pro_token"`
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
AgentModel string `json:"agent_model"`
|
AgentModel string `json:"agent_model" binding:"required"`
|
||||||
AgentVersion string `json:"agent_version"`
|
AgentVersion string `json:"agent_version" binding:"required"`
|
||||||
ClientType string `json:"client_type"`
|
ClientType string `json:"client_type" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var b body
|
var b body
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, nil, &b)
|
ctx, errResp := h.app.StartRequest(g, nil, nil, &b, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
var b body
|
var b body
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, &b)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -250,7 +250,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -292,7 +292,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -334,15 +334,15 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
UserID int64 `uri:"uid"`
|
UserID int64 `uri:"uid"`
|
||||||
}
|
}
|
||||||
type body struct {
|
type body struct {
|
||||||
FCMToken string `json:"fcm_token"`
|
FCMToken string `json:"fcm_token" binding:"required"`
|
||||||
AgentModel string `json:"agent_model"`
|
AgentModel string `json:"agent_model" binding:"required"`
|
||||||
AgentVersion string `json:"agent_version"`
|
AgentVersion string `json:"agent_version" binding:"required"`
|
||||||
ClientType string `json:"client_type"`
|
ClientType string `json:"client_type" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
var b body
|
var b body
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, &b)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -391,7 +391,7 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -440,7 +440,7 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -482,7 +482,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -540,7 +540,7 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
var q query
|
var q query
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, &q, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, &q, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -620,7 +620,7 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -665,7 +665,7 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -715,7 +715,7 @@ func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -762,7 +762,7 @@ func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -813,8 +813,8 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
UserID int64 `uri:"uid"`
|
UserID int64 `uri:"uid"`
|
||||||
}
|
}
|
||||||
type body struct {
|
type body struct {
|
||||||
ChannelOwnerUserID int64 `form:"channel_owner_user_id"`
|
ChannelOwnerUserID int64 `form:"channel_owner_user_id" binding:"required"`
|
||||||
Channel string `form:"channel_name"`
|
Channel string `form:"channel_name" binding:"required"`
|
||||||
}
|
}
|
||||||
type query struct {
|
type query struct {
|
||||||
ChanSubscribeKey *string `form:"chan_subscribe_key"`
|
ChanSubscribeKey *string `form:"chan_subscribe_key"`
|
||||||
@ -823,7 +823,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
var u uri
|
var u uri
|
||||||
var q query
|
var q query
|
||||||
var b body
|
var b body
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, &q, &b)
|
ctx, errResp := h.app.StartRequest(g, &u, &q, &b, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -841,6 +841,10 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.InternAPIError(400, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
return ginresp.InternAPIError(400, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if channel.OwnerUserID != u.UserID && (q.ChanSubscribeKey == nil || *q.ChanSubscribeKey != channel.SubscribeKey) {
|
||||||
|
ginresp.InternAPIError(401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||||
|
}
|
||||||
|
|
||||||
sub, err := h.database.CreateSubscription(ctx, u.UserID, *channel, channel.OwnerUserID == u.UserID)
|
sub, err := h.database.CreateSubscription(ctx, u.UserID, *channel, channel.OwnerUserID == u.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to create subscription", err)
|
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to create subscription", err)
|
||||||
@ -875,7 +879,7 @@ func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
var b body
|
var b body
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, &b)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -944,7 +948,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var q query
|
var q query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &q, nil)
|
ctx, errResp := h.app.StartRequest(g, nil, &q, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -1010,7 +1014,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -1079,7 +1083,7 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -152,7 +152,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -225,7 +225,7 @@ func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -287,7 +287,7 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -351,7 +351,7 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -448,7 +448,7 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
@ -531,7 +531,7 @@ func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
var datq query
|
var datq query
|
||||||
var datb query
|
var datb query
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb)
|
ctx, errResp := h.app.StartRequest(g, nil, &datq, &datb, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,62 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMessageCompat 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
|
||||||
|
//
|
||||||
|
// @Param query_data query handler.SendMessageCompat.query false " "
|
||||||
|
// @Param form_data formData handler.SendMessageCompat.form false " "
|
||||||
|
//
|
||||||
|
// @Success 200 {object} handler.sendMessageInternal.response
|
||||||
|
// @Failure 400 {object} ginresp.apiError
|
||||||
|
// @Failure 401 {object} ginresp.apiError
|
||||||
|
// @Failure 403 {object} ginresp.apiError
|
||||||
|
// @Failure 404 {object} ginresp.apiError
|
||||||
|
// @Failure 500 {object} ginresp.apiError
|
||||||
|
//
|
||||||
|
// @Router /send.php [POST]
|
||||||
|
func (h MessageHandler) SendMessageCompat(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type query struct {
|
||||||
|
UserID *int64 `form:"user_id"`
|
||||||
|
UserKey *string `form:"user_key"`
|
||||||
|
Channel *string `form:"channel"`
|
||||||
|
ChanKey *string `form:"chan_key"`
|
||||||
|
Title *string `form:"title"`
|
||||||
|
Content *string `form:"content"`
|
||||||
|
Priority *int `form:"priority"`
|
||||||
|
UserMessageID *string `form:"msg_id"`
|
||||||
|
SendTimestamp *float64 `form:"timestamp"`
|
||||||
|
}
|
||||||
|
type form struct {
|
||||||
|
UserID *int64 `form:"user_id"`
|
||||||
|
UserKey *string `form:"user_key"`
|
||||||
|
Channel *string `form:"channel"`
|
||||||
|
ChanKey *string `form:"chan_key"`
|
||||||
|
Title *string `form:"title"`
|
||||||
|
Content *string `form:"content"`
|
||||||
|
Priority *int `form:"priority"`
|
||||||
|
UserMessageID *string `form:"msg_id"`
|
||||||
|
SendTimestamp *float64 `form:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var f form
|
||||||
|
var q query
|
||||||
|
ctx, errResp := h.app.StartRequest(g, nil, &q, nil, &f)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
data := dataext.ObjectMerge(f, q)
|
||||||
|
|
||||||
|
return h.sendMessageInternal(ctx, data.UserID, data.UserKey, data.Channel, data.ChanKey, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// SendMessage swaggerdoc
|
// SendMessage swaggerdoc
|
||||||
//
|
//
|
||||||
// @Summary Send a new message
|
// @Summary Send a new message
|
||||||
@ -38,7 +94,7 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
|
|||||||
// @Param query_data query handler.SendMessage.query false " "
|
// @Param query_data query handler.SendMessage.query false " "
|
||||||
// @Param post_body body handler.SendMessage.body false " "
|
// @Param post_body body handler.SendMessage.body false " "
|
||||||
//
|
//
|
||||||
// @Success 200 {object} handler.SendMessage.response
|
// @Success 200 {object} handler.sendMessageInternal.response
|
||||||
// @Failure 400 {object} ginresp.apiError
|
// @Failure 400 {object} ginresp.apiError
|
||||||
// @Failure 401 {object} ginresp.apiError
|
// @Failure 401 {object} ginresp.apiError
|
||||||
// @Failure 403 {object} ginresp.apiError
|
// @Failure 403 {object} ginresp.apiError
|
||||||
@ -53,8 +109,8 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
UserKey *string `form:"user_key"`
|
UserKey *string `form:"user_key"`
|
||||||
Channel *string `form:"channel"`
|
Channel *string `form:"channel"`
|
||||||
ChanKey *string `form:"chan_key"`
|
ChanKey *string `form:"chan_key"`
|
||||||
Title *string `form:"message_title"`
|
Title *string `form:"title"`
|
||||||
Content *string `form:"message_content"`
|
Content *string `form:"content"`
|
||||||
Priority *int `form:"priority"`
|
Priority *int `form:"priority"`
|
||||||
UserMessageID *string `form:"msg_id"`
|
UserMessageID *string `form:"msg_id"`
|
||||||
SendTimestamp *float64 `form:"timestamp"`
|
SendTimestamp *float64 `form:"timestamp"`
|
||||||
@ -64,12 +120,28 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
UserKey *string `json:"user_key"`
|
UserKey *string `json:"user_key"`
|
||||||
Channel *string `json:"channel"`
|
Channel *string `json:"channel"`
|
||||||
ChanKey *string `form:"chan_key"`
|
ChanKey *string `form:"chan_key"`
|
||||||
Title *string `json:"message_title"`
|
Title *string `json:"title"`
|
||||||
Content *string `json:"message_content"`
|
Content *string `json:"content"`
|
||||||
Priority *int `json:"priority"`
|
Priority *int `json:"priority"`
|
||||||
UserMessageID *string `json:"msg_id"`
|
UserMessageID *string `json:"msg_id"`
|
||||||
SendTimestamp *float64 `json:"timestamp"`
|
SendTimestamp *float64 `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var b body
|
||||||
|
var q query
|
||||||
|
ctx, errResp := h.app.StartRequest(g, nil, &q, &b, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
data := dataext.ObjectMerge(b, q)
|
||||||
|
|
||||||
|
return h.sendMessageInternal(ctx, data.UserID, data.UserKey, data.Channel, data.ChanKey, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h MessageHandler) sendMessageInternal(ctx *logic.AppContext, UserID *int64, UserKey *string, Channel *string, ChanKey *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64) ginresp.HTTPResponse {
|
||||||
type response struct {
|
type response struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
ErrorID apierr.APIError `json:"error"`
|
ErrorID apierr.APIError `json:"error"`
|
||||||
@ -83,65 +155,55 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
SCNMessageID int64 `json:"scn_msg_id"`
|
SCNMessageID int64 `json:"scn_msg_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var b body
|
if UserID == nil {
|
||||||
var q query
|
return ginresp.SendAPIError(400, apierr.MISSING_UID, 101, "Missing parameter [[user_id]]", nil)
|
||||||
ctx, errResp := h.app.StartRequest(g, nil, &q, &b)
|
|
||||||
if errResp != nil {
|
|
||||||
return *errResp
|
|
||||||
}
|
}
|
||||||
defer ctx.Cancel()
|
if UserKey == nil {
|
||||||
|
return ginresp.SendAPIError(400, apierr.MISSING_UID, 102, "Missing parameter [[user_token]]", nil)
|
||||||
data := dataext.ObjectMerge(b, q)
|
|
||||||
|
|
||||||
if data.UserID == nil {
|
|
||||||
return ginresp.SendAPIError(400, apierr.MISSING_UID, 101, "Missing parameter [[user_id]]")
|
|
||||||
}
|
}
|
||||||
if data.UserKey == nil {
|
if Title == nil {
|
||||||
return ginresp.SendAPIError(400, apierr.MISSING_UID, 102, "Missing parameter [[user_token]]")
|
return ginresp.SendAPIError(400, apierr.MISSING_UID, 103, "Missing parameter [[title]]", nil)
|
||||||
}
|
}
|
||||||
if data.Title == nil {
|
if SendTimestamp != nil && mathext.Abs(*SendTimestamp-float64(time.Now().Unix())) > (24*time.Hour).Seconds() {
|
||||||
return ginresp.SendAPIError(400, apierr.MISSING_UID, 103, "Missing parameter [[title]]")
|
return ginresp.SendAPIError(400, apierr.TIMESTAMP_OUT_OF_RANGE, -1, "The timestamp mus be within 24 hours of now()", nil)
|
||||||
}
|
}
|
||||||
if data.SendTimestamp != nil && mathext.Abs(*data.SendTimestamp-float64(time.Now().Unix())) > (24*time.Hour).Seconds() {
|
if Priority != nil && (*Priority != 0 && *Priority != 1 && *Priority != 2) {
|
||||||
return ginresp.SendAPIError(400, apierr.TIMESTAMP_OUT_OF_RANGE, -1, "The timestamp mus be within 24 hours of now()")
|
return ginresp.SendAPIError(400, apierr.INVALID_PRIO, 105, "Invalid priority", nil)
|
||||||
}
|
}
|
||||||
if data.Priority != nil && (*data.Priority != 0 && *data.Priority != 1 && *data.Priority != 2) {
|
if len(strings.TrimSpace(*Title)) == 0 {
|
||||||
return ginresp.SendAPIError(400, apierr.INVALID_PRIO, 105, "Invalid priority")
|
return ginresp.SendAPIError(400, apierr.NO_TITLE, 103, "No title specified", nil)
|
||||||
}
|
}
|
||||||
if len(strings.TrimSpace(*data.Title)) == 0 {
|
if UserMessageID != nil && len(strings.TrimSpace(*UserMessageID)) > 64 {
|
||||||
return ginresp.SendAPIError(400, apierr.NO_TITLE, 103, "No title specified")
|
return ginresp.SendAPIError(400, apierr.USR_MSG_ID_TOO_LONG, -1, "MessageID too long (64 characters)", nil)
|
||||||
}
|
|
||||||
if data.UserMessageID != nil && len(strings.TrimSpace(*data.UserMessageID)) > 64 {
|
|
||||||
return ginresp.SendAPIError(400, apierr.USR_MSG_ID_TOO_LONG, -1, "MessageID too long (64 characters)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
channelName := "main"
|
channelName := "main"
|
||||||
if data.Channel != nil {
|
if Channel != nil {
|
||||||
channelName = strings.ToLower(strings.TrimSpace(*data.Channel))
|
channelName = strings.ToLower(strings.TrimSpace(*Channel))
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := h.database.GetUser(ctx, *data.UserID)
|
user, err := h.database.GetUser(ctx, *UserID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return ginresp.SendAPIError(400, apierr.USER_NOT_FOUND, -1, "User not found")
|
return ginresp.SendAPIError(400, apierr.USER_NOT_FOUND, -1, "User not found", nil)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query user")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query user", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(strings.TrimSpace(*data.Title)) > 120 {
|
if len(strings.TrimSpace(*Title)) > 120 {
|
||||||
return ginresp.SendAPIError(400, apierr.TITLE_TOO_LONG, 103, "Title too long (120 characters)")
|
return ginresp.SendAPIError(400, apierr.TITLE_TOO_LONG, 103, "Title too long (120 characters)", nil)
|
||||||
}
|
}
|
||||||
if data.Content != nil && len(strings.TrimSpace(*data.Content)) > user.MaxContentLength() {
|
if Content != nil && len(strings.TrimSpace(*Content)) > user.MaxContentLength() {
|
||||||
return ginresp.SendAPIError(400, apierr.CONTENT_TOO_LONG, 104, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(strings.TrimSpace(*data.Content)), user.MaxContentLength()))
|
return ginresp.SendAPIError(400, apierr.CONTENT_TOO_LONG, 104, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(strings.TrimSpace(*Content)), user.MaxContentLength()), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.UserMessageID != nil {
|
if UserMessageID != nil {
|
||||||
msg, err := h.database.GetMessageByUserMessageID(ctx, *data.UserMessageID)
|
msg, err := h.database.GetMessageByUserMessageID(ctx, *UserMessageID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query existing message")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query existing message", err)
|
||||||
}
|
}
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
return ginresp.JSON(http.StatusOK, response{
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{
|
||||||
Success: true,
|
Success: true,
|
||||||
ErrorID: apierr.NO_ERROR,
|
ErrorID: apierr.NO_ERROR,
|
||||||
ErrorHighlight: -1,
|
ErrorHighlight: -1,
|
||||||
@ -152,58 +214,58 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
IsPro: user.IsPro,
|
IsPro: user.IsPro,
|
||||||
QuotaMax: user.QuotaPerDay(),
|
QuotaMax: user.QuotaPerDay(),
|
||||||
SCNMessageID: msg.SCNMessageID,
|
SCNMessageID: msg.SCNMessageID,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.QuotaRemainingToday() <= 0 {
|
if user.QuotaRemainingToday() <= 0 {
|
||||||
return ginresp.SendAPIError(403, apierr.QUOTA_REACHED, -1, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()))
|
return ginresp.SendAPIError(403, apierr.QUOTA_REACHED, -1, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
channel, err := h.app.GetOrCreateChannel(ctx, *data.UserID, channelName)
|
channel, err := h.app.GetOrCreateChannel(ctx, *UserID, channelName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query/create channel")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query/create channel", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
selfChanAdmin := *data.UserID == channel.OwnerUserID && *data.UserKey == user.AdminKey
|
selfChanAdmin := *UserID == channel.OwnerUserID && *UserKey == user.AdminKey
|
||||||
selfChanSend := *data.UserID == channel.OwnerUserID && *data.UserKey == user.SendKey
|
selfChanSend := *UserID == channel.OwnerUserID && *UserKey == user.SendKey
|
||||||
forgChanSend := *data.UserID != channel.OwnerUserID && data.ChanKey != nil && *data.ChanKey == channel.SendKey
|
forgChanSend := *UserID != channel.OwnerUserID && ChanKey != nil && *ChanKey == channel.SendKey
|
||||||
|
|
||||||
if !selfChanAdmin && !selfChanSend && !forgChanSend {
|
if !selfChanAdmin && !selfChanSend && !forgChanSend {
|
||||||
return ginresp.SendAPIError(401, apierr.USER_AUTH_FAILED, 102, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()))
|
return ginresp.SendAPIError(401, apierr.USER_AUTH_FAILED, 102, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendTimestamp *time.Time = nil
|
var sendTimestamp *time.Time = nil
|
||||||
if data.SendTimestamp != nil {
|
if SendTimestamp != nil {
|
||||||
sendTimestamp = langext.Ptr(timeext.UnixFloatSeconds(*data.SendTimestamp))
|
sendTimestamp = langext.Ptr(timeext.UnixFloatSeconds(*SendTimestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
priority := langext.Coalesce(data.Priority, 1)
|
priority := langext.Coalesce(Priority, 1)
|
||||||
|
|
||||||
msg, err := h.database.CreateMessage(ctx, *data.UserID, channel, sendTimestamp, *data.Title, data.Content, priority, data.UserMessageID)
|
msg, err := h.database.CreateMessage(ctx, *UserID, channel, sendTimestamp, *Title, Content, priority, UserMessageID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create message in db")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create message in db", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subscriptions, err := h.database.ListSubscriptionsByChannel(ctx, channel.ChannelID)
|
subscriptions, err := h.database.ListSubscriptionsByChannel(ctx, channel.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query subscriptions")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query subscriptions", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.database.IncUserMessageCounter(ctx, user)
|
err = h.database.IncUserMessageCounter(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to inc user msg-counter")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to inc user msg-counter", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.database.IncChannelMessageCounter(ctx, channel)
|
err = h.database.IncChannelMessageCounter(ctx, channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to channel msg-counter")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to inc channel msg-counter", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, sub := range subscriptions {
|
for _, sub := range subscriptions {
|
||||||
clients, err := h.database.ListClients(ctx, sub.SubscriberUserID)
|
clients, err := h.database.ListClients(ctx, sub.SubscriberUserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query clients")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query clients", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sub.Confirmed {
|
if !sub.Confirmed {
|
||||||
@ -216,19 +278,19 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
_, err = h.database.CreateRetryDelivery(ctx, client, msg)
|
_, err = h.database.CreateRetryDelivery(ctx, client, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create delivery")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create delivery", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = h.database.CreateSuccessDelivery(ctx, client, msg, *fcmDelivID)
|
_, err = h.database.CreateSuccessDelivery(ctx, client, msg, *fcmDelivID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create delivery")
|
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create delivery", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ginresp.JSON(http.StatusOK, response{
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{
|
||||||
Success: true,
|
Success: true,
|
||||||
ErrorID: apierr.NO_ERROR,
|
ErrorID: apierr.NO_ERROR,
|
||||||
ErrorHighlight: -1,
|
ErrorHighlight: -1,
|
||||||
@ -239,7 +301,7 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
IsPro: user.IsPro,
|
IsPro: user.IsPro,
|
||||||
QuotaMax: user.QuotaPerDay(),
|
QuotaMax: user.QuotaPerDay(),
|
||||||
SCNMessageID: msg.SCNMessageID,
|
SCNMessageID: msg.SCNMessageID,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h MessageHandler) deliverMessage(ctx *logic.AppContext, client models.Client, msg models.Message) (*string, error) {
|
func (h MessageHandler) deliverMessage(ctx *logic.AppContext, client models.Client, msg models.Message) (*string, error) {
|
||||||
|
@ -94,7 +94,7 @@ func (r *Router) Init(e *gin.Engine) {
|
|||||||
apiv2 := e.Group("/api-v2/")
|
apiv2 := e.Group("/api-v2/")
|
||||||
{
|
{
|
||||||
|
|
||||||
apiv2.POST("/users/", ginresp.Wrap(r.apiHandler.CreateUser))
|
apiv2.POST("/users", ginresp.Wrap(r.apiHandler.CreateUser))
|
||||||
apiv2.GET("/users/:uid", ginresp.Wrap(r.apiHandler.GetUser))
|
apiv2.GET("/users/:uid", ginresp.Wrap(r.apiHandler.GetUser))
|
||||||
apiv2.PATCH("/users/:uid", ginresp.Wrap(r.apiHandler.UpdateUser))
|
apiv2.PATCH("/users/:uid", ginresp.Wrap(r.apiHandler.UpdateUser))
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ func (r *Router) Init(e *gin.Engine) {
|
|||||||
{
|
{
|
||||||
sendAPI.POST("/", ginresp.Wrap(r.messageHandler.SendMessage))
|
sendAPI.POST("/", ginresp.Wrap(r.messageHandler.SendMessage))
|
||||||
sendAPI.POST("/send", ginresp.Wrap(r.messageHandler.SendMessage))
|
sendAPI.POST("/send", ginresp.Wrap(r.messageHandler.SendMessage))
|
||||||
sendAPI.POST("/send.php", ginresp.Wrap(r.messageHandler.SendMessage))
|
sendAPI.POST("/send.php", ginresp.Wrap(r.messageHandler.SendMessageCompat))
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.app.Config.ReturnRawErrors {
|
if r.app.Config.ReturnRawErrors {
|
||||||
|
@ -5,7 +5,8 @@ type apiError struct {
|
|||||||
Error int `json:"error"`
|
Error int `json:"error"`
|
||||||
ErrorHighlight int `json:"errhighlight"`
|
ErrorHighlight int `json:"errhighlight"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
RawError error `json:"errorObject,omitempty"`
|
RawError string `json:"errorObj,omitempty"`
|
||||||
|
Trace string `json:"traceObj,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type compatAPIError struct {
|
type compatAPIError struct {
|
||||||
|
@ -3,8 +3,11 @@ package ginresp
|
|||||||
import (
|
import (
|
||||||
scn "blackforestbytes.com/simplecloudnotifier"
|
scn "blackforestbytes.com/simplecloudnotifier"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPResponse interface {
|
type HTTPResponse interface {
|
||||||
@ -64,25 +67,39 @@ func Text(sc int, data string) HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InternalError(e error) HTTPResponse {
|
func InternalError(e error) HTTPResponse {
|
||||||
|
log.Error().Err(e).Msg("[InternalError] " + e.Error())
|
||||||
|
|
||||||
return &jsonHTTPResponse{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 {
|
func InternAPIError(status int, errorid apierr.APIError, msg string, e error) HTTPResponse {
|
||||||
|
log.Error().Int("errorid", int(errorid)).Err(e).Msg("[InternAPIError] " + msg)
|
||||||
|
|
||||||
if scn.Conf.ReturnRawErrors {
|
if scn.Conf.ReturnRawErrors {
|
||||||
return &jsonHTTPResponse{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: fmt.Sprintf("%+v", e), Trace: string(debug.Stack())}}
|
||||||
} else {
|
} else {
|
||||||
return &jsonHTTPResponse{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 {
|
func CompatAPIError(errid int, msg string) HTTPResponse {
|
||||||
|
log.Error().Int("errid", errid).Msg("[CompatAPIError] " + msg)
|
||||||
|
|
||||||
return &jsonHTTPResponse{statusCode: 200, data: compatAPIError{Success: false, ErrorID: errid, Message: msg}}
|
return &jsonHTTPResponse{statusCode: 200, data: compatAPIError{Success: false, ErrorID: errid, Message: msg}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendAPIError(status int, errorid apierr.APIError, highlight int, msg string) HTTPResponse {
|
func SendAPIError(status int, errorid apierr.APIError, highlight int, msg string, e error) HTTPResponse {
|
||||||
return &jsonHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), ErrorHighlight: highlight, Message: msg}}
|
log.Error().Int("errorid", int(errorid)).Int("highlight", highlight).Err(e).Msg("[SendAPIError] " + msg)
|
||||||
|
|
||||||
|
if scn.Conf.ReturnRawErrors {
|
||||||
|
return &jsonHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), ErrorHighlight: highlight, Message: msg, RawError: fmt.Sprintf("%+v", e), Trace: string(debug.Stack())}}
|
||||||
|
} else {
|
||||||
|
return &jsonHTTPResponse{statusCode: status, data: apiError{Success: false, Error: int(errorid), ErrorHighlight: highlight, Message: msg}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotImplemented() HTTPResponse {
|
func NotImplemented() HTTPResponse {
|
||||||
|
log.Error().Msg("[NotImplemented]")
|
||||||
|
|
||||||
return &jsonHTTPResponse{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"}}
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ func (db *Database) IncChannelMessageCounter(ctx TxContext, channel models.Chann
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.ExecContext(ctx, "UPDATE channels SET messages_sent = ? AND timestamp_lastsent = ? WHERE channel_id = ?",
|
_, err = tx.ExecContext(ctx, "UPDATE channels SET messages_sent = ?, timestamp_lastsent = ? WHERE channel_id = ?",
|
||||||
channel.MessagesSent+1,
|
channel.MessagesSent+1,
|
||||||
time2DB(time.Now()),
|
time2DB(time.Now()),
|
||||||
channel.ChannelID)
|
channel.ChannelID)
|
||||||
|
@ -69,13 +69,13 @@ func (db *Database) ReadSchema(ctx context.Context) (int, error) {
|
|||||||
return 0, errors.New("no schema entry in meta table")
|
return 0, errors.New("no schema entry in meta table")
|
||||||
}
|
}
|
||||||
|
|
||||||
var schema int
|
var dbschema int
|
||||||
err = r2.Scan(&schema)
|
err = r2.Scan(&dbschema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return schema, nil
|
return dbschema, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) Ping() error {
|
func (db *Database) Ping() error {
|
||||||
@ -83,5 +83,5 @@ func (db *Database) Ping() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) BeginTx(ctx context.Context) (*sql.Tx, error) {
|
func (db *Database) BeginTx(ctx context.Context) (*sql.Tx, error) {
|
||||||
return db.db.BeginTx(ctx, nil)
|
return db.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelDefault})
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg
|
|||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
next := now.Add(5 * time.Second)
|
next := now.Add(5 * time.Second)
|
||||||
|
|
||||||
res, err := tx.ExecContext(ctx, "INSERT INTO deliveries (scn_message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
res, err := tx.ExecContext(ctx, "INSERT INTO deliveries (scn_message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
msg.SCNMessageID,
|
msg.SCNMessageID,
|
||||||
client.UserID,
|
client.UserID,
|
||||||
client.ClientID,
|
client.ClientID,
|
||||||
@ -55,7 +55,7 @@ func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, m
|
|||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
|
|
||||||
res, err := tx.ExecContext(ctx, "INSERT INTO deliveries (scn_message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
res, err := tx.ExecContext(ctx, "INSERT INTO deliveries (scn_message_id, receiver_user_id, receiver_client_id, timestamp_created, timestamp_finalized, status, fcm_message_id, next_delivery) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
msg.SCNMessageID,
|
msg.SCNMessageID,
|
||||||
client.UserID,
|
client.UserID,
|
||||||
client.ClientID,
|
client.ClientID,
|
||||||
|
@ -57,7 +57,7 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID int64, channel mod
|
|||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
|
|
||||||
res, err := tx.ExecContext(ctx, "INSERT INTO messages (sender_user_id, owner_user_id, channel_name, channel_id, timestamp_real, timestamp_client, title, content, priority, usr_message_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
res, err := tx.ExecContext(ctx, "INSERT INTO messages (sender_user_id, owner_user_id, channel_name, channel_id, timestamp_real, timestamp_client, title, content, priority, usr_message_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
senderUserID,
|
senderUserID,
|
||||||
channel.OwnerUserID,
|
channel.OwnerUserID,
|
||||||
channel.Name,
|
channel.Name,
|
||||||
|
@ -103,7 +103,7 @@ CREATE TABLE deliveries
|
|||||||
receiver_client_id INTEGER NOT NULL,
|
receiver_client_id INTEGER NOT NULL,
|
||||||
|
|
||||||
timestamp_created INTEGER NOT NULL,
|
timestamp_created INTEGER NOT NULL,
|
||||||
timestamp_finalized INTEGER NOT NULL,
|
timestamp_finalized INTEGER NULL,
|
||||||
|
|
||||||
|
|
||||||
status TEXT CHECK(status IN ('RETRY','SUCCESS','FAILED')) NOT NULL,
|
status TEXT CHECK(status IN ('RETRY','SUCCESS','FAILED')) NOT NULL,
|
||||||
|
@ -126,7 +126,7 @@ func (db *Database) UpdateUserProToken(ctx TxContext, userid int64, protoken *st
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.ExecContext(ctx, "UPDATE users SET pro_token = ? AND is_pro = ? WHERE user_id = ?",
|
_, err = tx.ExecContext(ctx, "UPDATE users SET pro_token = ?, is_pro = ? WHERE user_id = ?",
|
||||||
protoken,
|
protoken,
|
||||||
bool2DB(protoken != nil),
|
bool2DB(protoken != nil),
|
||||||
userid)
|
userid)
|
||||||
@ -145,7 +145,7 @@ func (db *Database) IncUserMessageCounter(ctx TxContext, user models.User) error
|
|||||||
|
|
||||||
quota := user.QuotaUsedToday() + 1
|
quota := user.QuotaUsedToday() + 1
|
||||||
|
|
||||||
_, err = tx.ExecContext(ctx, "UPDATE users SET timestamp_lastsent = ? AND messages_sent = ? AND quota_used = ? AND quota_used_day = ? WHERE user_id = ?",
|
_, err = tx.ExecContext(ctx, "UPDATE users SET timestamp_lastsent = ?, messages_sent = ?, quota_used = ?, quota_used_day = ? WHERE user_id = ?",
|
||||||
time2DB(time.Now()),
|
time2DB(time.Now()),
|
||||||
user.MessagesSent+1,
|
user.MessagesSent+1,
|
||||||
quota,
|
quota,
|
||||||
@ -180,7 +180,7 @@ func (db *Database) UpdateUserKeys(ctx TxContext, userid int64, sendKey string,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tx.ExecContext(ctx, "UPDATE users SET send_key = ? AND read_key = ? AND admin_key = ? WHERE user_id = ?",
|
_, err = tx.ExecContext(ctx, "UPDATE users SET send_key = ?, read_key = ?, admin_key = ? WHERE user_id = ?",
|
||||||
sendKey,
|
sendKey,
|
||||||
readKey,
|
readKey,
|
||||||
adminKey,
|
adminKey,
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"context"
|
"context"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -100,23 +101,29 @@ func (app *Application) Migrate() error {
|
|||||||
return app.Database.Migrate(ctx)
|
return app.Database.Migrate(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) StartRequest(g *gin.Context, uri any, query any, body any) (*AppContext, *ginresp.HTTPResponse) {
|
func (app *Application) StartRequest(g *gin.Context, uri any, query any, body any, form any) (*AppContext, *ginresp.HTTPResponse) {
|
||||||
|
|
||||||
if body != nil {
|
if uri != nil {
|
||||||
if err := g.ShouldBindJSON(&body); err != nil {
|
if err := g.ShouldBindUri(uri); err != nil {
|
||||||
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_BODY_PARAM, "Failed to read body", err))
|
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_URI_PARAM, "Failed to read uri", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if query != nil {
|
if query != nil {
|
||||||
if err := g.ShouldBindQuery(&query); err != nil {
|
if err := g.ShouldBindQuery(query); err != nil {
|
||||||
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_QUERY_PARAM, "Failed to read query", err))
|
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_QUERY_PARAM, "Failed to read query", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if uri != nil {
|
if body != nil {
|
||||||
if err := g.ShouldBindUri(&uri); err != nil {
|
if err := g.ShouldBindJSON(body); err != nil {
|
||||||
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_URI_PARAM, "Failed to read uri", err))
|
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_BODY_PARAM, "Failed to read body", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if form != nil {
|
||||||
|
if err := g.ShouldBindWith(form, binding.Form); err != nil {
|
||||||
|
return nil, langext.Ptr(ginresp.InternAPIError(400, apierr.BINDFAIL_BODY_PARAM, "Failed to read multipart-form", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ func (ac *AppContext) Value(key any) any {
|
|||||||
func (ac *AppContext) Cancel() {
|
func (ac *AppContext) Cancel() {
|
||||||
ac.cancelled = true
|
ac.cancelled = true
|
||||||
if ac.transaction != nil {
|
if ac.transaction != nil {
|
||||||
|
log.Error().Msg("Rollback transaction")
|
||||||
err := ac.transaction.Rollback()
|
err := ac.transaction.Rollback()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to rollback transaction: " + err.Error())
|
panic("failed to rollback transaction: " + err.Error())
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handler.SendMessage.response"
|
"$ref": "#/definitions/handler.sendMessageInternal.response"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@ -1404,7 +1404,144 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/handler.SendMessage.response"
|
"$ref": "#/definitions/handler.sendMessageInternal.response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "Forbidden",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Not Found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/send.php": {
|
||||||
|
"post": {
|
||||||
|
"description": "All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required",
|
||||||
|
"summary": "Send a new message (compatibility)",
|
||||||
|
"deprecated": true,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "chanKey",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "channel",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "content",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"name": "priority",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"name": "sendTimestamp",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "title",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"name": "userID",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "userKey",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "userMessageID",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "chanKey",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "channel",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "content",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"name": "priority",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"name": "sendTimestamp",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "title",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"name": "userID",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "userKey",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"name": "userMessageID",
|
||||||
|
"in": "formData"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/handler.sendMessageInternal.response"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@ -1451,12 +1588,17 @@
|
|||||||
"error": {
|
"error": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"errorObject": {},
|
"errorObj": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"traceObj": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1493,6 +1635,12 @@
|
|||||||
},
|
},
|
||||||
"handler.AddClient.body": {
|
"handler.AddClient.body": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"agent_model",
|
||||||
|
"agent_version",
|
||||||
|
"client_type",
|
||||||
|
"fcm_token"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"agent_model": {
|
"agent_model": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -1510,6 +1658,10 @@
|
|||||||
},
|
},
|
||||||
"handler.CreateSubscription.body": {
|
"handler.CreateSubscription.body": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"channel",
|
||||||
|
"channelOwnerUserID"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"channel": {
|
"channel": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -1521,6 +1673,12 @@
|
|||||||
},
|
},
|
||||||
"handler.CreateUser.body": {
|
"handler.CreateUser.body": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"agent_model",
|
||||||
|
"agent_version",
|
||||||
|
"client_type",
|
||||||
|
"fcm_token"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"agent_model": {
|
"agent_model": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -1746,10 +1904,7 @@
|
|||||||
"channel": {
|
"channel": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"message_content": {
|
"content": {
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"message_title": {
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"msg_id": {
|
"msg_id": {
|
||||||
@ -1761,6 +1916,9 @@
|
|||||||
"timestamp": {
|
"timestamp": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@ -1769,41 +1927,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handler.SendMessage.response": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"errhighlight": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"error": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"is_pro": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"message": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"messagecount": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"quota": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"quota_max": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"scn_msg_id": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"suppress_send": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handler.Update.response": {
|
"handler.Update.response": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -1901,6 +2024,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handler.sendMessageInternal.response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"errhighlight": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"is_pro": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"messagecount": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"quota": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"quota_max": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"scn_msg_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"suppress_send": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.ChannelJSON": {
|
"models.ChannelJSON": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -6,11 +6,14 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
error:
|
error:
|
||||||
type: integer
|
type: integer
|
||||||
errorObject: {}
|
errorObj:
|
||||||
|
type: string
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
success:
|
success:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
traceObj:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
ginresp.compatAPIError:
|
ginresp.compatAPIError:
|
||||||
properties:
|
properties:
|
||||||
@ -42,6 +45,11 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
fcm_token:
|
fcm_token:
|
||||||
type: string
|
type: string
|
||||||
|
required:
|
||||||
|
- agent_model
|
||||||
|
- agent_version
|
||||||
|
- client_type
|
||||||
|
- fcm_token
|
||||||
type: object
|
type: object
|
||||||
handler.CreateSubscription.body:
|
handler.CreateSubscription.body:
|
||||||
properties:
|
properties:
|
||||||
@ -49,6 +57,9 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
channelOwnerUserID:
|
channelOwnerUserID:
|
||||||
type: integer
|
type: integer
|
||||||
|
required:
|
||||||
|
- channel
|
||||||
|
- channelOwnerUserID
|
||||||
type: object
|
type: object
|
||||||
handler.CreateUser.body:
|
handler.CreateUser.body:
|
||||||
properties:
|
properties:
|
||||||
@ -64,6 +75,11 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
|
required:
|
||||||
|
- agent_model
|
||||||
|
- agent_version
|
||||||
|
- client_type
|
||||||
|
- fcm_token
|
||||||
type: object
|
type: object
|
||||||
handler.DatabaseTest.response:
|
handler.DatabaseTest.response:
|
||||||
properties:
|
properties:
|
||||||
@ -197,9 +213,7 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
channel:
|
channel:
|
||||||
type: string
|
type: string
|
||||||
message_content:
|
content:
|
||||||
type: string
|
|
||||||
message_title:
|
|
||||||
type: string
|
type: string
|
||||||
msg_id:
|
msg_id:
|
||||||
type: string
|
type: string
|
||||||
@ -207,34 +221,13 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
timestamp:
|
timestamp:
|
||||||
type: number
|
type: number
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
user_id:
|
user_id:
|
||||||
type: integer
|
type: integer
|
||||||
user_key:
|
user_key:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
handler.SendMessage.response:
|
|
||||||
properties:
|
|
||||||
errhighlight:
|
|
||||||
type: integer
|
|
||||||
error:
|
|
||||||
type: integer
|
|
||||||
is_pro:
|
|
||||||
type: boolean
|
|
||||||
message:
|
|
||||||
type: string
|
|
||||||
messagecount:
|
|
||||||
type: integer
|
|
||||||
quota:
|
|
||||||
type: integer
|
|
||||||
quota_max:
|
|
||||||
type: integer
|
|
||||||
scn_msg_id:
|
|
||||||
type: integer
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
suppress_send:
|
|
||||||
type: boolean
|
|
||||||
type: object
|
|
||||||
handler.Update.response:
|
handler.Update.response:
|
||||||
properties:
|
properties:
|
||||||
is_pro:
|
is_pro:
|
||||||
@ -298,6 +291,29 @@ definitions:
|
|||||||
uri:
|
uri:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
handler.sendMessageInternal.response:
|
||||||
|
properties:
|
||||||
|
errhighlight:
|
||||||
|
type: integer
|
||||||
|
error:
|
||||||
|
type: integer
|
||||||
|
is_pro:
|
||||||
|
type: boolean
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
messagecount:
|
||||||
|
type: integer
|
||||||
|
quota:
|
||||||
|
type: integer
|
||||||
|
quota_max:
|
||||||
|
type: integer
|
||||||
|
scn_msg_id:
|
||||||
|
type: integer
|
||||||
|
success:
|
||||||
|
type: boolean
|
||||||
|
suppress_send:
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
models.ChannelJSON:
|
models.ChannelJSON:
|
||||||
properties:
|
properties:
|
||||||
channel_id:
|
channel_id:
|
||||||
@ -468,7 +484,7 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handler.SendMessage.response'
|
$ref: '#/definitions/handler.sendMessageInternal.response'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@ -1355,7 +1371,7 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handler.SendMessage.response'
|
$ref: '#/definitions/handler.sendMessageInternal.response'
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
@ -1377,4 +1393,90 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/ginresp.apiError'
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
summary: Send a new message
|
summary: Send a new message
|
||||||
|
/send.php:
|
||||||
|
post:
|
||||||
|
deprecated: true
|
||||||
|
description: All parameter can be set via query-parameter or form-data body.
|
||||||
|
Only UserID, UserKey and Title are required
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: chanKey
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: channel
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: content
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: priority
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: sendTimestamp
|
||||||
|
type: number
|
||||||
|
- in: query
|
||||||
|
name: title
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: userID
|
||||||
|
type: integer
|
||||||
|
- in: query
|
||||||
|
name: userKey
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: userMessageID
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: chanKey
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: channel
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: content
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: priority
|
||||||
|
type: integer
|
||||||
|
- in: formData
|
||||||
|
name: sendTimestamp
|
||||||
|
type: number
|
||||||
|
- in: formData
|
||||||
|
name: title
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: userID
|
||||||
|
type: integer
|
||||||
|
- in: formData
|
||||||
|
name: userKey
|
||||||
|
type: string
|
||||||
|
- in: formData
|
||||||
|
name: userMessageID
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handler.sendMessageInternal.response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"403":
|
||||||
|
description: Forbidden
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: Not Found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: Send a new message (compatibility)
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
@ -248,3 +248,7 @@ pre, pre span
|
|||||||
font-family: Menlo, Consolas, monospace;
|
font-family: Menlo, Consolas, monospace;
|
||||||
background: #F9F9F9;;
|
background: #F9F9F9;;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.display_none {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -28,33 +28,33 @@
|
|||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="uid" class="doc">UserID</label></div>
|
<div class="col-sm-12 col-md-3"><label for="uid" class="doc">UserID</label></div>
|
||||||
<div class="col-sm-12 col-md"><input placeholder="UserID" id="uid" class="doc" <?php echo (isset($_GET['preset_user_id']) ? (' value="'.$_GET['preset_user_id'].'" '):(''));?> type="number"></div>
|
<div class="col-sm-12 col-md"><input placeholder="UserID" id="uid" class="doc" type="number"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="ukey" class="doc">Authentification Key</label></div>
|
<div class="col-sm-12 col-md-3"><label for="ukey" class="doc">Authentification Key</label></div>
|
||||||
<div class="col-sm-12 col-md"><input placeholder="Key" id="ukey" class="doc" <?php echo (isset($_GET['preset_user_key']) ? (' value="'.$_GET['preset_user_key'].'" '):(''));?> type="text" maxlength="64"></div>
|
<div class="col-sm-12 col-md"><input placeholder="Key" id="ukey" class="doc" type="text" maxlength="64"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="prio" class="doc">Priority</label></div>
|
<div class="col-sm-12 col-md-3"><label for="prio" class="doc">Priority</label></div>
|
||||||
<div class="col-sm-12 col-md">
|
<div class="col-sm-12 col-md">
|
||||||
<select id="prio" class="doc" type="text" style="width:100%;">
|
<select id="prio" class="doc" type="text" style="width:100%;">
|
||||||
<option value="0" <?php echo (( isset($_GET['preset_priority'])&&$_GET['preset_priority']==='0') ? 'selected':'');?>>Low</option>
|
<option value="0" >Low</option>
|
||||||
<option value="1" <?php echo ((!isset($_GET['preset_priority'])||$_GET['preset_priority']==='1') ? 'selected':'');?>>Normal</option>
|
<option value="1" selected>Normal</option>
|
||||||
<option value="2" <?php echo (( isset($_GET['preset_priority'])&&$_GET['preset_priority']==='2') ? 'selected':'');?>>High</option>
|
<option value="2" >High</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="msg" class="doc">Message Title</label></div>
|
<div class="col-sm-12 col-md-3"><label for="msg" class="doc">Message Title</label></div>
|
||||||
<div class="col-sm-12 col-md"><input placeholder="Message" id="msg" class="doc" <?php echo (isset($_GET['preset_title']) ? (' value="'.$_GET['preset_title'].'" '):(''));?> type="text" maxlength="80"></div>
|
<div class="col-sm-12 col-md"><input placeholder="Message" id="msg" class="doc" type="text" maxlength="80"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div>
|
<div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div>
|
||||||
<div class="col-sm-12 col-md"><textarea id="txt" class="doc" <?php echo (isset($_GET['preset_content']) ? (' value="'.$_GET['preset_content'].'" '):(''));?> rows="8" maxlength="2048"></textarea></div>
|
<div class="col-sm-12 col-md"><textarea id="txt" class="doc" rows="8" maxlength="2048"></textarea></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -28,7 +28,7 @@ function send()
|
|||||||
data.append('priority', pio.value);
|
data.append('priority', pio.value);
|
||||||
|
|
||||||
let xhr = new XMLHttpRequest();
|
let xhr = new XMLHttpRequest();
|
||||||
xhr.open('POST', '/', true);
|
xhr.open('POST', '/send.php', true);
|
||||||
xhr.onreadystatechange = function ()
|
xhr.onreadystatechange = function ()
|
||||||
{
|
{
|
||||||
if (xhr.readyState !== 4) return;
|
if (xhr.readyState !== 4) return;
|
||||||
@ -81,10 +81,27 @@ function send()
|
|||||||
xhr.send(data);
|
xhr.send(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("load",function ()
|
window.addEventListener("load", function ()
|
||||||
{
|
{
|
||||||
let btnSend = document.getElementById("btnSend");
|
const qp = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
if (btnSend !== undefined) btnSend.onclick = function () { send(); return false; };
|
const btnSend = document.getElementById("btnSend");
|
||||||
|
const selPrio = document.getElementById("prio");
|
||||||
|
const txtKey = document.getElementById("ukey");
|
||||||
|
const txtUID = document.getElementById("uid");
|
||||||
|
const txtTitl = document.getElementById("msg");
|
||||||
|
const txtCont = document.getElementById("txt");
|
||||||
|
|
||||||
},false);
|
btnSend.onclick = function () { send(); return false; };
|
||||||
|
|
||||||
|
if (qp.has('preset_priority')) selPrio.selectedIndex = parseInt(qp.get("preset_priority"));
|
||||||
|
|
||||||
|
if (qp.has('preset_user_key')) txtKey.value = qp.get("preset_user_key");
|
||||||
|
|
||||||
|
if (qp.has('preset_user_id')) txtUID.value = qp.get("preset_user_id");
|
||||||
|
|
||||||
|
if (qp.has('preset_title')) txtTitl.value = qp.get("preset_title");
|
||||||
|
|
||||||
|
if (qp.has('preset_content')) txtCont.value = qp.get("preset_content");
|
||||||
|
|
||||||
|
}, false);
|
31
server/website/js/message_sent.js
Normal file
31
server/website/js/message_sent.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
window.addEventListener("load", function ()
|
||||||
|
{
|
||||||
|
const qp = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
const spanQuota1 = document.getElementById("insQuota1");
|
||||||
|
const spanQuota2 = document.getElementById("insQuota2");
|
||||||
|
const linkSucc = document.getElementById("succ_link");
|
||||||
|
const linkErr = document.getElementById("err_link");
|
||||||
|
|
||||||
|
spanQuota1.innerText = qp.get('quota_remain') ?? 'ERR';
|
||||||
|
spanQuota2.innerText = qp.get('quota_max') ?? 'ERR';
|
||||||
|
|
||||||
|
const preset_user_id = qp.get('preset_user_id') ?? 'ERR';
|
||||||
|
const preset_user_key = qp.get('preset_user_key') ?? 'ERR';
|
||||||
|
|
||||||
|
linkSucc.setAttribute("href", "/?preset_user_id="+preset_user_id+"&preset_user_key="+preset_user_key);
|
||||||
|
|
||||||
|
if (qp.get("ok") === "1") {
|
||||||
|
|
||||||
|
linkSucc.classList.remove('display_none');
|
||||||
|
linkErr.classList.add('display_none');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
linkSucc.classList.add('display_none');
|
||||||
|
linkErr.classList.remove('display_none');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, false);
|
@ -22,35 +22,31 @@
|
|||||||
|
|
||||||
<div class="fullcenterflex">
|
<div class="fullcenterflex">
|
||||||
|
|
||||||
<?php if (isset($_GET['ok']) && $_GET['ok'] === "1" ): ?>
|
<a id="succ_link" class="display_none card success" href="/?preset_user_id=<?php echo isset($_GET['preset_user_id'])?$_GET['preset_user_id']:'ERR';?>&preset_user_key=<?php echo isset($_GET['preset_user_key'])?$_GET['preset_user_key']:'ERR';?>">
|
||||||
|
<div class="section">
|
||||||
|
<h3 class="doc">Message sent</h3>
|
||||||
|
<p class="doc">Message succesfully sent<br>
|
||||||
|
<span id="insQuota1">ERR</span>/<span id="insQuota2">ERR</span> remaining</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a class="card success" href="/index.php?preset_user_id=<?php echo isset($_GET['preset_user_id'])?$_GET['preset_user_id']:'ERR';?>&preset_user_key=<?php echo isset($_GET['preset_user_key'])?$_GET['preset_user_key']:'ERR';?>">
|
<a id="err_link" class="card error" href="/">
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h3 class="doc">Message sent</h3>
|
<h3 class="doc">Failure</h3>
|
||||||
<p class="doc">Message succesfully sent<br>
|
<p class="doc">Unknown error</p>
|
||||||
<?php echo isset($_GET['quota_remain'])?$_GET['quota_remain']:'ERR';?>/<?php echo isset($_GET['quota_max'])?$_GET['quota_max']:'ERR';?> remaining</p>
|
</div>
|
||||||
</div>
|
</a>
|
||||||
</a>
|
|
||||||
|
|
||||||
<?php else: ?>
|
|
||||||
|
|
||||||
<a class="card error" href="/index.php">
|
|
||||||
<div class="section">
|
|
||||||
<h3 class="doc">Failure</h3>
|
|
||||||
<p class="doc">Unknown error</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
||||||
<a tabindex="-1" href="/index.php" class="button bordered" id="tr_link">Send</a>
|
<a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a>
|
||||||
|
|
||||||
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/message_sent.js" type="text/javascript" ></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue
Block a user