ListChannels(), GetChannel(), ListUserSubscriptions(), ListChannelSubscriptions(), GetSubscription(), CancelSubscription()
This commit is contained in:
parent
85bfe79115
commit
5c2877bdb8
@ -22,9 +22,11 @@ const (
|
||||
USR_MSG_ID_TOO_LONG APIError = 1204
|
||||
TIMESTAMP_OUT_OF_RANGE APIError = 1205
|
||||
|
||||
USER_NOT_FOUND APIError = 1301
|
||||
CLIENT_NOT_FOUND APIError = 1302
|
||||
USER_AUTH_FAILED APIError = 1311
|
||||
USER_NOT_FOUND APIError = 1301
|
||||
CLIENT_NOT_FOUND APIError = 1302
|
||||
CHANNEL_NOT_FOUND APIError = 1303
|
||||
SUBSCRIPTION_NOT_FOUND APIError = 1304
|
||||
USER_AUTH_FAILED APIError = 1311
|
||||
|
||||
NO_DEVICE_LINKED APIError = 1401
|
||||
|
||||
|
@ -36,7 +36,7 @@ func NewAPIHandler(app *logic.Application) APIHandler {
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/ [POST]
|
||||
// @Router /api-v2/users/ [POST]
|
||||
func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
|
||||
type body struct {
|
||||
FCMToken string `json:"fcm_token"`
|
||||
@ -116,7 +116,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/{uid} [GET]
|
||||
// @Router /api-v2/users/{uid} [GET]
|
||||
func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
@ -158,7 +158,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse {
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/{uid} [PATCH]
|
||||
// @Router /api-v2/users/{uid} [PATCH]
|
||||
func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
@ -228,18 +228,18 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
//
|
||||
// @Success 200 {object} handler.ListClients.result
|
||||
// @Success 200 {object} handler.ListClients.response
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/{uid}/clients [GET]
|
||||
// @Router /api-v2/users/{uid}/clients [GET]
|
||||
func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
}
|
||||
type result struct {
|
||||
type response struct {
|
||||
Clients []models.ClientJSON `json:"clients"`
|
||||
}
|
||||
|
||||
@ -261,7 +261,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse {
|
||||
|
||||
res := langext.ArrMap(clients, func(v models.Client) models.ClientJSON { return v.JSON() })
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, result{Clients: res}))
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Clients: res}))
|
||||
}
|
||||
|
||||
// GetClient swaggerdoc
|
||||
@ -278,7 +278,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse {
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/{uid}/clients/{cid} [GET]
|
||||
// @Router /api-v2/users/{uid}/clients/{cid} [GET]
|
||||
func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
@ -322,7 +322,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse {
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/{uid}/clients [POST]
|
||||
// @Router /api-v2/users/{uid}/clients [POST]
|
||||
func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
@ -377,7 +377,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/user/{uid}/clients [POST]
|
||||
// @Router /api-v2/users/{uid}/clients [POST]
|
||||
func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
@ -411,38 +411,299 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON()))
|
||||
}
|
||||
|
||||
// ListChannels swaggerdoc
|
||||
//
|
||||
// @Summary List all channels of a user
|
||||
// @ID api-channels-list
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
//
|
||||
// @Success 200 {object} handler.ListChannels.response
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/users/{uid}/channels [GET]
|
||||
func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
}
|
||||
type response struct {
|
||||
Channels []models.ChannelJSON `json:"channels"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
if permResp := ctx.CheckPermissionUserRead(u.UserID); permResp != nil {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
clients, err := h.database.ListChannels(ctx, u.UserID)
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channels", err)
|
||||
}
|
||||
|
||||
res := langext.ArrMap(clients, func(v models.Channel) models.ChannelJSON { return v.JSON() })
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Channels: res}))
|
||||
}
|
||||
|
||||
// GetChannel swaggerdoc
|
||||
//
|
||||
// @Summary List all channels of a user
|
||||
// @ID api-channels-get
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
// @Param cid path int true "ChannelID"
|
||||
//
|
||||
// @Success 200 {object} models.ChannelJSON
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/users/{uid}/channels/{cid} [GET]
|
||||
func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
ChannelID int64 `uri:"cid"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
if permResp := ctx.CheckPermissionUserRead(u.UserID); permResp != nil {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
channel, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
||||
if err == sql.ErrNoRows {
|
||||
return ginresp.InternAPIError(404, apierr.CLIENT_NOT_FOUND, "Channel not found", err)
|
||||
}
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||
}
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, channel.JSON()))
|
||||
}
|
||||
|
||||
func (h APIHandler) GetChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
}
|
||||
|
||||
// ListUserSubscriptions swaggerdoc
|
||||
//
|
||||
// @Summary List all channels of a user
|
||||
// @ID api-user-subscriptions-list
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
//
|
||||
// @Success 200 {object} handler.ListUserSubscriptions.response
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/users/{uid}/subscriptions [GET]
|
||||
func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
}
|
||||
type response struct {
|
||||
Subscriptions []models.SubscriptionJSON `json:"subscriptions"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
if permResp := ctx.CheckPermissionUserRead(u.UserID); permResp != nil {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
clients, err := h.database.ListSubscriptionsByOwner(ctx, u.UserID)
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channels", err)
|
||||
}
|
||||
|
||||
res := langext.ArrMap(clients, func(v models.Subscription) models.SubscriptionJSON { return v.JSON() })
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Subscriptions: res}))
|
||||
}
|
||||
|
||||
// ListChannelSubscriptions swaggerdoc
|
||||
//
|
||||
// @Summary List all subscriptions of a channel
|
||||
// @ID api-chan-subscriptions-list
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
// @Param cid path int true "ChannelID"
|
||||
//
|
||||
// @Success 200 {object} handler.ListChannelSubscriptions.response
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/users/{uid}/channels/{cid}/subscriptions [GET]
|
||||
func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
ChannelID int64 `uri:"cid"`
|
||||
}
|
||||
type response struct {
|
||||
Subscriptions []models.SubscriptionJSON `json:"subscriptions"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
if permResp := ctx.CheckPermissionUserRead(u.UserID); permResp != nil {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
_, err := h.database.GetChannel(ctx, u.UserID, u.ChannelID)
|
||||
if err == sql.ErrNoRows {
|
||||
return ginresp.InternAPIError(404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||
}
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channels", err)
|
||||
}
|
||||
|
||||
clients, err := h.database.ListSubscriptionsByChannel(ctx, u.ChannelID)
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channels", err)
|
||||
}
|
||||
|
||||
res := langext.ArrMap(clients, func(v models.Subscription) models.SubscriptionJSON { return v.JSON() })
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Subscriptions: res}))
|
||||
}
|
||||
|
||||
// GetSubscription swaggerdoc
|
||||
//
|
||||
// @Summary Get a single subscription
|
||||
// @ID api-subscriptions-get
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
// @Param sid path int true "SubscriptionID"
|
||||
//
|
||||
// @Success 200 {object} models.SubscriptionJSON
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/users/{uid}/subscriptions/{sid} [GET]
|
||||
func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
SubscriptionID int64 `uri:"sid"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
if permResp := ctx.CheckPermissionUserRead(u.UserID); permResp != nil {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
subscription, err := h.database.GetSubscription(ctx, u.SubscriptionID)
|
||||
if err == sql.ErrNoRows {
|
||||
return ginresp.InternAPIError(404, apierr.SUBSCRIPTION_NOT_FOUND, "Subscription not found", err)
|
||||
}
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||
}
|
||||
|
||||
if subscription.SubscriberUserID != u.UserID {
|
||||
return ginresp.InternAPIError(401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||
}
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, subscription.JSON()))
|
||||
}
|
||||
|
||||
// CancelSubscription swaggerdoc
|
||||
//
|
||||
// @Summary Cancel (delete) subscription
|
||||
// @ID api-subscriptions-delete
|
||||
//
|
||||
// @Param uid path int true "UserID"
|
||||
// @Param sid path int true "SubscriptionID"
|
||||
//
|
||||
// @Success 200 {object} models.SubscriptionJSON
|
||||
// @Failure 400 {object} ginresp.apiError
|
||||
// @Failure 401 {object} ginresp.apiError
|
||||
// @Failure 404 {object} ginresp.apiError
|
||||
// @Failure 500 {object} ginresp.apiError
|
||||
//
|
||||
// @Router /api-v2/users/{uid}/subscriptions/{sid} [DELETE]
|
||||
func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
type uri struct {
|
||||
UserID int64 `uri:"uid"`
|
||||
SubscriptionID int64 `uri:"sid"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
ctx, errResp := h.app.StartRequest(g, &u, nil, nil)
|
||||
if errResp != nil {
|
||||
return *errResp
|
||||
}
|
||||
defer ctx.Cancel()
|
||||
|
||||
if permResp := ctx.CheckPermissionUserRead(u.UserID); permResp != nil {
|
||||
return *permResp
|
||||
}
|
||||
|
||||
subscription, err := h.database.GetSubscription(ctx, u.SubscriptionID)
|
||||
if err == sql.ErrNoRows {
|
||||
return ginresp.InternAPIError(404, apierr.SUBSCRIPTION_NOT_FOUND, "Subscription not found", err)
|
||||
}
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||
}
|
||||
|
||||
if subscription.SubscriberUserID != u.UserID {
|
||||
return ginresp.InternAPIError(401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||
}
|
||||
|
||||
err = h.database.DeleteSubscription(ctx, u.SubscriptionID)
|
||||
if err != nil {
|
||||
return ginresp.InternAPIError(500, apierr.DATABASE_ERROR, "Failed to delete subscription", err)
|
||||
}
|
||||
|
||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, subscription.JSON()))
|
||||
}
|
||||
|
||||
func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
}
|
||||
|
||||
func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
}
|
||||
|
||||
func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.NotImplemented()
|
||||
}
|
||||
|
@ -76,7 +76,6 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
||||
ErrorHighlight int `json:"errhighlight"`
|
||||
Message string `json:"message"`
|
||||
SuppressSend bool `json:"suppress_send"`
|
||||
Response string `json:"response"`
|
||||
MessageCount int `json:"messagecount"`
|
||||
Quota int `json:"quota"`
|
||||
IsPro bool `json:"is_pro"`
|
||||
@ -145,10 +144,9 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.JSON(http.StatusOK, response{
|
||||
Success: true,
|
||||
ErrorID: apierr.NO_ERROR,
|
||||
ErrorHighlight: 0,
|
||||
ErrorHighlight: -1,
|
||||
Message: "Message already sent",
|
||||
SuppressSend: true,
|
||||
Response: "",
|
||||
MessageCount: user.MessagesSent,
|
||||
Quota: user.QuotaUsedToday(),
|
||||
IsPro: user.IsPro,
|
||||
@ -187,7 +185,7 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to create message in db")
|
||||
}
|
||||
|
||||
subscriptions, err := h.database.ListChannelSubscriptions(ctx, channel.ChannelID)
|
||||
subscriptions, err := h.database.ListSubscriptionsByChannel(ctx, channel.ChannelID)
|
||||
if err != nil {
|
||||
return ginresp.SendAPIError(500, apierr.DATABASE_ERROR, -1, "Failed to query subscriptions")
|
||||
}
|
||||
@ -216,7 +214,18 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
||||
}
|
||||
}
|
||||
|
||||
return ginresp.NotImplemented()
|
||||
return ginresp.JSON(http.StatusOK, response{
|
||||
Success: true,
|
||||
ErrorID: apierr.NO_ERROR,
|
||||
ErrorHighlight: -1,
|
||||
Message: "Message sent",
|
||||
SuppressSend: false,
|
||||
MessageCount: user.MessagesSent,
|
||||
Quota: user.QuotaUsedToday(),
|
||||
IsPro: user.IsPro,
|
||||
QuotaMax: user.QuotaPerDay(),
|
||||
SCNMessageID: msg.SCNMessageID,
|
||||
})
|
||||
}
|
||||
|
||||
func (h MessageHandler) deliverMessage(ctx *logic.AppContext, client models.Client, msg models.Message) (*string, error) {
|
||||
|
@ -94,24 +94,25 @@ func (r *Router) Init(e *gin.Engine) {
|
||||
apiv2 := e.Group("/api-v2/")
|
||||
{
|
||||
|
||||
apiv2.POST("/user/", ginresp.Wrap(r.apiHandler.CreateUser))
|
||||
apiv2.GET("/user/:uid", ginresp.Wrap(r.apiHandler.GetUser))
|
||||
apiv2.PATCH("/user/:uid", ginresp.Wrap(r.apiHandler.UpdateUser))
|
||||
apiv2.POST("/users/", ginresp.Wrap(r.apiHandler.CreateUser))
|
||||
apiv2.GET("/users/:uid", ginresp.Wrap(r.apiHandler.GetUser))
|
||||
apiv2.PATCH("/users/:uid", ginresp.Wrap(r.apiHandler.UpdateUser))
|
||||
|
||||
apiv2.GET("/user/:uid/clients", ginresp.Wrap(r.apiHandler.ListClients))
|
||||
apiv2.GET("/user/:uid/clients/:cid", ginresp.Wrap(r.apiHandler.GetClient))
|
||||
apiv2.POST("/user/:uid/clients", ginresp.Wrap(r.apiHandler.AddClient))
|
||||
apiv2.DELETE("/user/:uid/clients", ginresp.Wrap(r.apiHandler.DeleteClient))
|
||||
apiv2.GET("/users/:uid/clients", ginresp.Wrap(r.apiHandler.ListClients))
|
||||
apiv2.GET("/users/:uid/clients/:cid", ginresp.Wrap(r.apiHandler.GetClient))
|
||||
apiv2.POST("/users/:uid/clients", ginresp.Wrap(r.apiHandler.AddClient))
|
||||
apiv2.DELETE("/users/:uid/clients", ginresp.Wrap(r.apiHandler.DeleteClient))
|
||||
|
||||
apiv2.GET("/user/:uid/channels", ginresp.Wrap(r.apiHandler.ListChannels))
|
||||
apiv2.GET("/user/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.GetChannel))
|
||||
apiv2.GET("/user/:uid/channels/:cid/messages", ginresp.Wrap(r.apiHandler.GetChannelMessages))
|
||||
apiv2.GET("/user/:uid/channels/:cid/subscriptions", ginresp.Wrap(r.apiHandler.ListChannelSubscriptions))
|
||||
apiv2.GET("/users/:uid/channels", ginresp.Wrap(r.apiHandler.ListChannels))
|
||||
apiv2.GET("/users/:uid/channels/:cid", ginresp.Wrap(r.apiHandler.GetChannel))
|
||||
apiv2.GET("/users/:uid/channels/:cid/messages", ginresp.Wrap(r.apiHandler.GetChannelMessages))
|
||||
apiv2.GET("/users/:uid/channels/:cid/subscriptions", ginresp.Wrap(r.apiHandler.ListChannelSubscriptions))
|
||||
|
||||
apiv2.GET("/user/:uid/subscriptions", ginresp.Wrap(r.apiHandler.ListUserSubscriptions))
|
||||
apiv2.GET("/user/:uid/subscriptions/:sid", ginresp.Wrap(r.apiHandler.GetSubscription))
|
||||
apiv2.DELETE("/user/:uid/subscriptions/:sid", ginresp.Wrap(r.apiHandler.CancelSubscription))
|
||||
apiv2.POST("/user/:uid/subscriptions", ginresp.Wrap(r.apiHandler.CreateSubscription))
|
||||
apiv2.GET("/users/:uid/subscriptions", ginresp.Wrap(r.apiHandler.ListUserSubscriptions))
|
||||
apiv2.GET("/users/:uid/subscriptions/:sid", ginresp.Wrap(r.apiHandler.GetSubscription))
|
||||
apiv2.DELETE("/users/:uid/subscriptions/:sid", ginresp.Wrap(r.apiHandler.CancelSubscription))
|
||||
apiv2.POST("/users/:uid/subscriptions", ginresp.Wrap(r.apiHandler.CreateSubscription))
|
||||
apiv2.PATCH("/users/:uid/subscriptions", ginresp.Wrap(r.apiHandler.UpdateSubscription))
|
||||
|
||||
apiv2.GET("/messages", ginresp.Wrap(r.apiHandler.ListMessages))
|
||||
apiv2.GET("/messages/:mid", ginresp.Wrap(r.apiHandler.GetMessage))
|
||||
|
@ -1,6 +1,7 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||
"blackforestbytes.com/simplecloudnotifier/models"
|
||||
"database/sql"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
@ -413,7 +414,7 @@ func (db *Database) CreateMessage(ctx TxContext, senderUserID int64, channel mod
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *Database) ListChannelSubscriptions(ctx TxContext, channelID int64) ([]models.Subscription, error) {
|
||||
func (db *Database) ListSubscriptionsByChannel(ctx TxContext, channelID int64) ([]models.Subscription, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -432,6 +433,25 @@ func (db *Database) ListChannelSubscriptions(ctx TxContext, channelID int64) ([]
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (db *Database) ListSubscriptionsByOwner(ctx TxContext, ownerUserID int64) ([]models.Subscription, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := tx.QueryContext(ctx, "SELECT * FROM subscriptions WHERE channel_owner_user_id = ?", ownerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := models.DecodeSubscriptions(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (db *Database) CreateRetryDelivery(ctx TxContext, client models.Client, msg models.Message) (models.Delivery, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
@ -512,3 +532,74 @@ func (db *Database) CreateSuccessDelivery(ctx TxContext, client models.Client, m
|
||||
FCMMessageID: langext.Ptr(fcmDelivID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (db *Database) ListChannels(ctx *logic.AppContext, userid int64) ([]models.Channel, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := tx.QueryContext(ctx, "SELECT * FROM channels WHERE owner_user_id = ?", userid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := models.DecodeChannels(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (db *Database) GetChannel(ctx TxContext, userid int64, channelid int64) (models.Channel, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return models.Channel{}, err
|
||||
}
|
||||
|
||||
rows, err := tx.QueryContext(ctx, "SELECT * FROM channels WHERE owner_user_id = ? AND channel_id = ? LIMIT 1", userid, channelid)
|
||||
if err != nil {
|
||||
return models.Channel{}, err
|
||||
}
|
||||
|
||||
client, err := models.DecodeChannel(rows)
|
||||
if err != nil {
|
||||
return models.Channel{}, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (db *Database) GetSubscription(ctx *logic.AppContext, subid int64) (models.Subscription, error) {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return models.Subscription{}, err
|
||||
}
|
||||
|
||||
rows, err := tx.QueryContext(ctx, "SELECT * FROM subscriptions WHERE subscription_id = ? LIMIT 1", subid)
|
||||
if err != nil {
|
||||
return models.Subscription{}, err
|
||||
}
|
||||
|
||||
sub, err := models.DecodeSubscription(rows)
|
||||
if err != nil {
|
||||
return models.Subscription{}, err
|
||||
}
|
||||
|
||||
return sub, nil
|
||||
}
|
||||
|
||||
func (db *Database) DeleteSubscription(ctx *logic.AppContext, subid int64) error {
|
||||
tx, err := ctx.GetOrCreateTransaction(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, "DELETE FROM subscriptions WHERE subscription_id = ?", subid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -47,11 +47,20 @@ type Notification struct {
|
||||
}
|
||||
|
||||
func (fb App) SendNotification(ctx context.Context, client models.Client, msg models.Message) (string, error) {
|
||||
|
||||
n := messaging.Message{
|
||||
Data: map[string]string{"scn_msg_id": strconv.FormatInt(msg.SCNMessageID, 10)},
|
||||
Data: map[string]string{
|
||||
"scn_msg_id": strconv.FormatInt(msg.SCNMessageID, 10),
|
||||
"usr_msg_id": langext.Coalesce(msg.UserMessageID, ""),
|
||||
"timestamp": strconv.FormatInt(msg.Timestamp().Unix(), 10),
|
||||
"priority": strconv.Itoa(msg.Priority),
|
||||
"trimmed": langext.Conditional(msg.NeedsTrim(), "true", "false"),
|
||||
"title": msg.Title,
|
||||
"body": msg.TrimmedBody(),
|
||||
},
|
||||
Notification: &messaging.Notification{
|
||||
Title: msg.Title,
|
||||
Body: langext.Coalesce(msg.Content, ""),
|
||||
Body: msg.ShortBody(),
|
||||
},
|
||||
Android: nil,
|
||||
APNS: nil,
|
||||
@ -61,12 +70,12 @@ func (fb App) SendNotification(ctx context.Context, client models.Client, msg mo
|
||||
Topic: "",
|
||||
Condition: "",
|
||||
}
|
||||
|
||||
if client.Type == models.ClientTypeIOS {
|
||||
n.APNS = nil
|
||||
}
|
||||
|
||||
if client.Type == models.ClientTypeAndroid {
|
||||
n.Android = nil
|
||||
} else if client.Type == models.ClientTypeAndroid {
|
||||
n.Notification = nil
|
||||
n.Android = &messaging.AndroidConfig{Priority: "high"}
|
||||
}
|
||||
|
||||
res, err := fb.messaging.Send(ctx, &n)
|
||||
|
@ -9,7 +9,7 @@ require (
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/swaggo/swag v1.8.7
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.20
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.21
|
||||
google.golang.org/api v0.103.0
|
||||
)
|
||||
|
||||
|
@ -168,6 +168,8 @@ gogs.mikescher.com/BlackForestBytes/goext v0.0.19 h1:IvCHlIHDviHQXntZFTNdV7qNq5y
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.19/go.mod h1:TMBOjo3FRFh/GiTT0z3nwLmgcFJB87oSF2VMs4XUCTQ=
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.20 h1:HxJ0iZ838TQnp/a+/DNajdZjZkV43OsK4VbHarOiHTs=
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.20/go.mod h1:TMBOjo3FRFh/GiTT0z3nwLmgcFJB87oSF2VMs4XUCTQ=
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.21 h1:OibsssmorZsTdFYRiQFlkXtjUYweQg9SBkWO40ONe0Y=
|
||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.21/go.mod h1:TMBOjo3FRFh/GiTT0z3nwLmgcFJB87oSF2VMs4XUCTQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
|
@ -28,15 +28,39 @@ func (m Message) JSON() MessageJSON {
|
||||
OwnerUserID: m.OwnerUserID,
|
||||
ChannelName: m.ChannelName,
|
||||
ChannelID: m.ChannelID,
|
||||
Timestamp: langext.Coalesce(m.TimestampClient, m.TimestampReal).Format(time.RFC3339Nano),
|
||||
Timestamp: m.Timestamp().Format(time.RFC3339Nano),
|
||||
Title: m.Title,
|
||||
Content: m.Content,
|
||||
Priority: m.Priority,
|
||||
UserMessageID: m.UserMessageID,
|
||||
Trimmed: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (m Message) Timestamp() time.Time {
|
||||
return langext.Coalesce(m.TimestampClient, m.TimestampReal)
|
||||
}
|
||||
|
||||
func (m Message) NeedsTrim() bool {
|
||||
return m.Content != nil && len(*m.Content) > 1900
|
||||
}
|
||||
|
||||
func (m Message) TrimmedBody() string {
|
||||
if !m.NeedsTrim() {
|
||||
return langext.Coalesce(m.Content, "")
|
||||
}
|
||||
return langext.Coalesce(m.Content, "")[0:1900-3] + "..."
|
||||
}
|
||||
|
||||
func (m Message) ShortBody() string {
|
||||
if m.Content == nil {
|
||||
return ""
|
||||
}
|
||||
if len(*m.Content) < 200 {
|
||||
return *m.Content
|
||||
}
|
||||
return (*m.Content)[0:200-3] + "..."
|
||||
}
|
||||
|
||||
type MessageJSON struct {
|
||||
SCNMessageID int64 `json:"scn_message_id"`
|
||||
SenderUserID int64 `json:"sender_user_id"`
|
||||
@ -48,7 +72,6 @@ type MessageJSON struct {
|
||||
Content *string `json:"body"`
|
||||
Priority int `json:"priority"`
|
||||
UserMessageID *string `json:"usr_message_id"`
|
||||
Trimmed bool `json:"trimmed"`
|
||||
}
|
||||
|
||||
type MessageDB struct {
|
||||
|
@ -91,7 +91,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api-v2/user/": {
|
||||
"/api-v2/users/": {
|
||||
"post": {
|
||||
"summary": "Create a new user",
|
||||
"operationId": "api-user-create",
|
||||
@ -127,7 +127,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api-v2/user/{uid}": {
|
||||
"/api-v2/users/{uid}": {
|
||||
"get": {
|
||||
"summary": "Get a user",
|
||||
"operationId": "api-user-get",
|
||||
@ -221,7 +221,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api-v2/user/{uid}/clients": {
|
||||
"/api-v2/users/{uid}/clients": {
|
||||
"get": {
|
||||
"summary": "List all clients",
|
||||
"operationId": "api-clients-list",
|
||||
@ -320,7 +320,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api-v2/user/{uid}/clients/{cid}": {
|
||||
"/api-v2/users/{uid}/clients/{cid}": {
|
||||
"get": {
|
||||
"summary": "Get a single clients",
|
||||
"operationId": "api-clients-get",
|
||||
|
@ -340,7 +340,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.apiError'
|
||||
summary: Send a new message
|
||||
/api-v2/user/:
|
||||
/api-v2/users/:
|
||||
post:
|
||||
operationId: api-user-create
|
||||
parameters:
|
||||
@ -363,7 +363,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.apiError'
|
||||
summary: Create a new user
|
||||
/api-v2/user/{uid}:
|
||||
/api-v2/users/{uid}:
|
||||
get:
|
||||
operationId: api-user-get
|
||||
parameters:
|
||||
@ -425,7 +425,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.apiError'
|
||||
summary: (Partially) update a user
|
||||
/api-v2/user/{uid}/clients:
|
||||
/api-v2/users/{uid}/clients:
|
||||
get:
|
||||
operationId: api-clients-list
|
||||
parameters:
|
||||
@ -491,7 +491,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.apiError'
|
||||
summary: Delete a client
|
||||
/api-v2/user/{uid}/clients/{cid}:
|
||||
/api-v2/users/{uid}/clients/{cid}:
|
||||
get:
|
||||
operationId: api-clients-get
|
||||
parameters:
|
||||
|
Loading…
Reference in New Issue
Block a user