diff --git a/server/README.md b/server/README.md index 8e48ea2..a017fb9 100644 --- a/server/README.md +++ b/server/README.md @@ -2,7 +2,8 @@ //TODO - - remove fcm/goog_api keys from repo (and invalidate them !!) + - Return client on register + - Register with noclient=true - migration script for existing data @@ -14,6 +15,8 @@ - dark mode toggle for html - app-store link in HTML + - tests + - deploy ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/server/api/handler/api.go b/server/api/handler/api.go index 50291e8..5179907 100644 --- a/server/api/handler/api.go +++ b/server/api/handler/api.go @@ -34,14 +34,15 @@ func NewAPIHandler(app *logic.Application) APIHandler { // // @Summary Create a new user // @ID api-user-create +// @Tags API-v2 // // @Param post_body body handler.CreateUser.body false " " // -// @Success 200 {object} models.UserJSON +// @Success 200 {object} handler.sendMessageInternal.response // @Failure 400 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/ [POST] +// @Router /api/users/ [POST] func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { type body struct { FCMToken string `json:"fcm_token" binding:"required"` @@ -117,6 +118,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { // // @Summary Get a user // @ID api-user-get +// @Tags API-v2 // // @Param uid path int true "UserID" // @@ -126,7 +128,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid} [GET] +// @Router /api/users/{uid} [GET] func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -159,6 +161,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse { // @Summary (Partially) update a user // @Description The body-values are optional, only send the ones you want to update // @ID api-user-update +// @Tags API-v2 // // @Param uid path int true "UserID" // @@ -174,7 +177,7 @@ func (h APIHandler) GetUser(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid} [PATCH] +// @Router /api/users/{uid} [PATCH] func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -271,6 +274,7 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse { // // @Summary List all clients // @ID api-clients-list +// @Tags API-v2 // // @Param uid path int true "UserID" // @@ -280,7 +284,7 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/clients [GET] +// @Router /api/users/{uid}/clients [GET] func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -314,6 +318,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { // // @Summary Get a single clients // @ID api-clients-get +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param cid path int true "ClientID" @@ -324,7 +329,7 @@ func (h APIHandler) ListClients(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/clients/{cid} [GET] +// @Router /api/users/{uid}/clients/{cid} [GET] func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -357,6 +362,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse { // // @Summary Add a new clients // @ID api-clients-create +// @Tags API-v2 // // @Param uid path int true "UserID" // @@ -368,7 +374,7 @@ func (h APIHandler) GetClient(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/clients [POST] +// @Router /api/users/{uid}/clients [POST] func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -413,6 +419,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { // // @Summary Delete a client // @ID api-clients-delete +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param cid path int true "ClientID" @@ -423,7 +430,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/clients [POST] +// @Router /api/users/{uid}/clients [POST] func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -467,6 +474,7 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse { // @Description - "subscribed_any" Return all channels that the user is subscribing to (even unconfirmed) // @Description - "all_any" Return channels that the user owns or is subscribing (even unconfirmed) // @ID api-channels-list +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param selector query string true "Filter channels (default: owned)" Enums(owned, subscribed, all, subscribed_any, all_any) @@ -477,7 +485,7 @@ func (h APIHandler) DeleteClient(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/channels [GET] +// @Router /api/users/{uid}/channels [GET] func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -546,6 +554,7 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { // // @Summary List all channels of a user // @ID api-channels-get +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param cid path int true "ChannelID" @@ -556,7 +565,7 @@ func (h APIHandler) ListChannels(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/channels/{cid} [GET] +// @Router /api/users/{uid}/channels/{cid} [GET] func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -589,6 +598,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse { // // @Summary (Partially) update a channel // @ID api-channels-update +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param cid path int true "ChannelID" @@ -602,7 +612,7 @@ func (h APIHandler) GetChannel(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/channels/{cid} [PATCH] +// @Router /api/users/{uid}/channels/{cid} [PATCH] func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -667,6 +677,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { // @Description If there are no more entries the token "@end" will be returned // @Description By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size) // @ID api-channel-messages +// @Tags API-v2 // // @Param query_data query handler.ListChannelMessages.query false " " // @@ -676,7 +687,7 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/channels/{cid}/messages [GET] +// @Router /api/users/{uid}/channels/{cid}/messages [GET] func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse { type uri struct { ChannelUserID models.UserID `uri:"uid"` @@ -757,6 +768,7 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse { // // @Summary List all channels of a user // @ID api-user-subscriptions-list +// @Tags API-v2 // // @Param uid path int true "UserID" // @@ -766,7 +778,7 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/subscriptions [GET] +// @Router /api/users/{uid}/subscriptions [GET] func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -800,6 +812,7 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { // // @Summary List all subscriptions of a channel // @ID api-chan-subscriptions-list +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param cid path int true "ChannelID" @@ -810,7 +823,7 @@ func (h APIHandler) ListUserSubscriptions(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/channels/{cid}/subscriptions [GET] +// @Router /api/users/{uid}/channels/{cid}/subscriptions [GET] func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -853,6 +866,7 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons // // @Summary Get a single subscription // @ID api-subscriptions-get +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param sid path int true "SubscriptionID" @@ -863,7 +877,7 @@ func (h APIHandler) ListChannelSubscriptions(g *gin.Context) ginresp.HTTPRespons // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/subscriptions/{sid} [GET] +// @Router /api/users/{uid}/subscriptions/{sid} [GET] func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -900,6 +914,7 @@ func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse { // // @Summary Cancel (delete) subscription // @ID api-subscriptions-delete +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param sid path int true "SubscriptionID" @@ -910,7 +925,7 @@ func (h APIHandler) GetSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/subscriptions/{sid} [DELETE] +// @Router /api/users/{uid}/subscriptions/{sid} [DELETE] func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -952,6 +967,7 @@ func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse { // // @Summary Creare/Request a subscription // @ID api-subscriptions-create +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param query_data query handler.CreateSubscription.query false " " @@ -963,7 +979,7 @@ func (h APIHandler) CancelSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/subscriptions [POST] +// @Router /api/users/{uid}/subscriptions [POST] func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -1013,6 +1029,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { // // @Summary Update a subscription (e.g. confirm) // @ID api-subscriptions-update +// @Tags API-v2 // // @Param uid path int true "UserID" // @Param sid path int true "SubscriptionID" @@ -1023,7 +1040,7 @@ func (h APIHandler) CreateSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/users/{uid}/subscriptions/{sid} [PATCH] +// @Router /api/users/{uid}/subscriptions/{sid} [PATCH] func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse { type uri struct { UserID models.UserID `uri:"uid"` @@ -1080,6 +1097,7 @@ func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse { // @Description If there are no more entries the token "@end" will be returned // @Description By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size) // @ID api-messages-list +// @Tags API-v2 // // @Param query_data query handler.ListMessages.query false " " // @@ -1089,7 +1107,7 @@ func (h APIHandler) UpdateSubscription(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/messages [GET] +// @Router /api/messages [GET] func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse { type query struct { PageSize *int `form:"page_size"` @@ -1154,6 +1172,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse { // @Description Or the user must subscribe to the corresponding channel (and be confirmed) and request the resource with the READ or ADMIN Key // @Description The returned message is never trimmed // @ID api-messages-get +// @Tags API-v2 // // @Param mid path int true "SCNMessageID" // @@ -1163,7 +1182,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/messages/{mid} [PATCH] +// @Router /api/messages/{mid} [PATCH] func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { type uri struct { MessageID models.SCNMessageID `uri:"mid"` @@ -1223,6 +1242,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { // @Summary Delete a single message // @Description The user must own the message and request the resource with the ADMIN Key // @ID api-messages-delete +// @Tags API-v2 // // @Param mid path int true "SCNMessageID" // @@ -1232,7 +1252,7 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/messages/{mid} [PATCH] +// @Router /api/messages/{mid} [PATCH] func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse { type uri struct { MessageID models.SCNMessageID `uri:"mid"` @@ -1280,6 +1300,7 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse { // @Description This is similar to the main route `POST -> https://scn.blackfrestbytes.com/` // @Description But this route can change in the future, for long-living scripts etc. it's better to use the normal POST route // @ID api-messages-create +// @Tags API-v2 // // @Param post_data query handler.CreateMessage.body false " " // @@ -1289,7 +1310,7 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse { // @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError // -// @Router /api-v2/messages [POST] +// @Router /api/messages [POST] func (h APIHandler) CreateMessage(g *gin.Context) ginresp.HTTPResponse { type body struct { Channel *string `json:"channel"` diff --git a/server/api/handler/common.go b/server/api/handler/common.go index 7e68130..521d4f1 100644 --- a/server/api/handler/common.go +++ b/server/api/handler/common.go @@ -4,6 +4,7 @@ import ( "blackforestbytes.com/simplecloudnotifier/common/ginresp" "blackforestbytes.com/simplecloudnotifier/logic" "bytes" + "errors" "github.com/gin-gonic/gin" sqlite3 "github.com/mattn/go-sqlite3" "net/http" @@ -33,13 +34,18 @@ type pingResponseInfo struct { // Ping swaggerdoc // +// @Summary Simple endpoint to test connection (any http method) +// @ID api-common-ping +// @Tags Common +// // @Success 200 {object} pingResponse // @Failure 500 {object} ginresp.apiError -// @Router /ping [get] -// @Router /ping [post] -// @Router /ping [put] -// @Router /ping [delete] -// @Router /ping [patch] +// +// @Router /api/ping [get] +// @Router /api/ping [post] +// @Router /api/ping [put] +// @Router /api/ping [delete] +// @Router /api/ping [patch] func (h CommonHandler) Ping(g *gin.Context) ginresp.HTTPResponse { buf := new(bytes.Buffer) _, _ = buf.ReadFrom(g.Request.Body) @@ -59,9 +65,14 @@ func (h CommonHandler) Ping(g *gin.Context) ginresp.HTTPResponse { // DatabaseTest swaggerdoc // +// @Summary Check for a wroking database connection +// @ID api-common-dbtest +// @Tags Common +// // @Success 200 {object} handler.DatabaseTest.response // @Failure 500 {object} ginresp.apiError -// @Router /db-test [get] +// +// @Router /api/db-test [get] func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse { type response struct { Success bool `json:"success"` @@ -87,23 +98,42 @@ func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse { // Health swaggerdoc // +// @Summary Server Health-checks +// @ID api-common-health +// @Tags Common +// // @Success 200 {object} handler.Health.response // @Failure 500 {object} ginresp.apiError -// @Router /health [get] +// +// @Router /api/health [get] func (h CommonHandler) Health(*gin.Context) ginresp.HTTPResponse { type response struct { Status string `json:"status"` } + + _, libVersionNumber, _ := sqlite3.Version() + + if libVersionNumber < 3039000 { + ginresp.InternalError(errors.New("sqlite version too low")) + } + + err := h.app.Database.Ping() + if err != nil { + return ginresp.InternalError(err) + } + return ginresp.JSON(http.StatusOK, response{Status: "ok"}) } func (h CommonHandler) NoRoute(g *gin.Context) ginresp.HTTPResponse { return ginresp.JSON(http.StatusNotFound, gin.H{ + "": "================ ROUTE NOT FOUND ================", "FullPath": g.FullPath(), "Method": g.Request.Method, "URL": g.Request.URL.String(), "RequestURI": g.Request.RequestURI, "Proto": g.Request.Proto, "Header": g.Request.Header, + "~": "================ ROUTE NOT FOUND ================", }) } diff --git a/server/api/handler/compat.go b/server/api/handler/compat.go index 495927d..3796305 100644 --- a/server/api/handler/compat.go +++ b/server/api/handler/compat.go @@ -28,6 +28,8 @@ func NewCompatHandler(app *logic.Application) CompatHandler { // // @Summary Register a new account // @ID compat-register +// @Tags API-v1 +// // @Deprecated // // @Param fcm_token query string true "the (android) fcm token" @@ -134,6 +136,8 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse { // // @Summary Get information about the current user // @ID compat-info +// @Tags API-v1 +// // @Deprecated // // @Param user_id query string true "the user_id" @@ -216,6 +220,8 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse { // // @Summary Acknowledge that a message was received // @ID compat-ack +// @Tags API-v1 +// // @Deprecated // // @Param user_id query string true "the user_id" @@ -287,6 +293,8 @@ func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse { // // @Summary Return all not-acknowledged messages // @ID compat-requery +// @Tags API-v1 +// // @Deprecated // // @Param user_id query string true "the user_id" @@ -352,6 +360,8 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse { // // @Summary Set the fcm-token (android) // @ID compat-update +// @Tags API-v1 +// // @Deprecated // // @Param user_id query string true "the user_id" @@ -463,6 +473,8 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse { // // @Summary Get a whole (potentially truncated) message // @ID compat-expand +// @Tags API-v1 +// // @Deprecated // // @Param user_id query string true "The user_id" @@ -548,6 +560,8 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse { // // @Summary Upgrade a free account to a paid account // @ID compat-upgrade +// @Tags API-v1 +// // @Deprecated // // @Param user_id query string true "the user_id" diff --git a/server/api/handler/message.go b/server/api/handler/message.go index a620de6..8598325 100644 --- a/server/api/handler/message.go +++ b/server/api/handler/message.go @@ -36,6 +36,7 @@ func NewMessageHandler(app *logic.Application) MessageHandler { // // @Summary Send a new message (compatibility) // @Description All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required +// @Tags External // // @Param query_data query handler.SendMessageCompat.query false " " // @Param form_data formData handler.SendMessageCompat.form false " " @@ -85,6 +86,7 @@ func (h MessageHandler) SendMessageCompat(g *gin.Context) ginresp.HTTPResponse { // // @Summary Send a new message // @Description All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required +// @Tags External // // @Param query_data query handler.SendMessage.query false " " // @Param post_body body handler.SendMessage.body false " " diff --git a/server/api/router.go b/server/api/router.go index e77a5fe..a50d7c5 100644 --- a/server/api/router.go +++ b/server/api/router.go @@ -32,49 +32,65 @@ func NewRouter(app *logic.Application) *Router { } // Init swaggerdocs +// // @title SimpleCloudNotifier API // @version 2.0 // @description API for SCN // @host scn.blackforestbytes.com +// +// @tag.name Common +// @tag.name External +// @tag.name API-v1 +// @tag.name API-v2 +// // @BasePath / func (r *Router) Init(e *gin.Engine) { // ================ General ================ - e.Any("/api/common/ping", ginresp.Wrap(r.commonHandler.Ping)) - e.POST("/api/common/db-test", ginresp.Wrap(r.commonHandler.DatabaseTest)) - e.GET("/api/common/health", ginresp.Wrap(r.commonHandler.Health)) + commonAPI := e.Group("/api") + { + commonAPI.Any("/ping", ginresp.Wrap(r.commonHandler.Ping)) + commonAPI.POST("/db-test", ginresp.Wrap(r.commonHandler.DatabaseTest)) + commonAPI.GET("/health", ginresp.Wrap(r.commonHandler.Health)) + } // ================ Swagger ================ - e.GET("/documentation/swagger", ginext.RedirectTemporary("/documentation/swagger/")) - e.GET("/documentation/swagger/", ginresp.Wrap(swagger.Handle)) - e.GET("/documentation/swagger/:fn", ginresp.Wrap(swagger.Handle)) + docs := e.Group("/documentation") + { + docs.GET("/swagger", ginext.RedirectTemporary("/documentation/swagger/")) + docs.GET("/swagger/", ginresp.Wrap(swagger.Handle)) + docs.GET("/swagger/:fn", ginresp.Wrap(swagger.Handle)) + } // ================ Website ================ - e.GET("/", ginresp.Wrap(r.websiteHandler.Index)) - e.GET("/index.php", ginresp.Wrap(r.websiteHandler.Index)) - e.GET("/index.html", ginresp.Wrap(r.websiteHandler.Index)) - e.GET("/index", ginresp.Wrap(r.websiteHandler.Index)) + frontend := e.Group("") + { + frontend.GET("/", ginresp.Wrap(r.websiteHandler.Index)) + frontend.GET("/index.php", ginresp.Wrap(r.websiteHandler.Index)) + frontend.GET("/index.html", ginresp.Wrap(r.websiteHandler.Index)) + frontend.GET("/index", ginresp.Wrap(r.websiteHandler.Index)) - e.GET("/api", ginresp.Wrap(r.websiteHandler.APIDocs)) - e.GET("/api.php", ginresp.Wrap(r.websiteHandler.APIDocs)) - e.GET("/api.html", ginresp.Wrap(r.websiteHandler.APIDocs)) + frontend.GET("/api", ginresp.Wrap(r.websiteHandler.APIDocs)) + frontend.GET("/api.php", ginresp.Wrap(r.websiteHandler.APIDocs)) + frontend.GET("/api.html", ginresp.Wrap(r.websiteHandler.APIDocs)) - e.GET("/api_more", ginresp.Wrap(r.websiteHandler.APIDocsMore)) - e.GET("/api_more.php", ginresp.Wrap(r.websiteHandler.APIDocsMore)) - e.GET("/api_more.html", ginresp.Wrap(r.websiteHandler.APIDocsMore)) + frontend.GET("/api_more", ginresp.Wrap(r.websiteHandler.APIDocsMore)) + frontend.GET("/api_more.php", ginresp.Wrap(r.websiteHandler.APIDocsMore)) + frontend.GET("/api_more.html", ginresp.Wrap(r.websiteHandler.APIDocsMore)) - e.GET("/message_sent", ginresp.Wrap(r.websiteHandler.MessageSent)) - e.GET("/message_sent.php", ginresp.Wrap(r.websiteHandler.MessageSent)) - e.GET("/message_sent.html", ginresp.Wrap(r.websiteHandler.MessageSent)) + frontend.GET("/message_sent", ginresp.Wrap(r.websiteHandler.MessageSent)) + frontend.GET("/message_sent.php", ginresp.Wrap(r.websiteHandler.MessageSent)) + frontend.GET("/message_sent.html", ginresp.Wrap(r.websiteHandler.MessageSent)) - e.GET("/favicon.ico", ginresp.Wrap(r.websiteHandler.FaviconIco)) - e.GET("/favicon.png", ginresp.Wrap(r.websiteHandler.FaviconPNG)) + frontend.GET("/favicon.ico", ginresp.Wrap(r.websiteHandler.FaviconIco)) + frontend.GET("/favicon.png", ginresp.Wrap(r.websiteHandler.FaviconPNG)) - e.GET("/js/:fn", ginresp.Wrap(r.websiteHandler.Javascript)) - e.GET("/css/:fn", ginresp.Wrap(r.websiteHandler.CSS)) + frontend.GET("/js/:fn", ginresp.Wrap(r.websiteHandler.Javascript)) + frontend.GET("/css/:fn", ginresp.Wrap(r.websiteHandler.CSS)) + } // ================ Compat (v1) ================ @@ -91,7 +107,7 @@ func (r *Router) Init(e *gin.Engine) { // ================ Manage API ================ - apiv2 := e.Group("/api-v2/") + apiv2 := e.Group("/api/") { apiv2.POST("/users", ginresp.Wrap(r.apiHandler.CreateUser)) diff --git a/server/cmd/scnserver/main.go b/server/cmd/scnserver/main.go index b219ee1..732e6bd 100644 --- a/server/cmd/scnserver/main.go +++ b/server/cmd/scnserver/main.go @@ -6,9 +6,9 @@ import ( "blackforestbytes.com/simplecloudnotifier/common" "blackforestbytes.com/simplecloudnotifier/common/ginext" "blackforestbytes.com/simplecloudnotifier/db" - "blackforestbytes.com/simplecloudnotifier/firebase" "blackforestbytes.com/simplecloudnotifier/jobs" "blackforestbytes.com/simplecloudnotifier/logic" + "blackforestbytes.com/simplecloudnotifier/push" "fmt" "github.com/rs/zerolog/log" ) @@ -36,15 +36,20 @@ func main() { router := api.NewRouter(app) - fb, err := firebase.NewFirebase(conf) - if err != nil { - log.Fatal().Err(err).Msg("failed to init firebase") - return + var nc push.NotificationClient + if conf.DummyFirebase { + nc, err = push.NewDummy() + } else { + nc, err = push.NewFirebaseConn(conf) + if err != nil { + log.Fatal().Err(err).Msg("failed to init firebase") + return + } } jobRetry := jobs.NewDeliveryRetryJob(app) - app.Init(conf, ginengine, fb, []logic.Job{jobRetry}) + app.Init(conf, ginengine, nc, []logic.Job{jobRetry}) router.Init(ginengine) diff --git a/server/config.go b/server/config.go index e8bf45d..9424749 100644 --- a/server/config.go +++ b/server/config.go @@ -15,6 +15,7 @@ type Config struct { DBFile string RequestTimeout time.Duration ReturnRawErrors bool + DummyFirebase bool FirebaseTokenURI string FirebaseProjectID string FirebasePrivKeyID string @@ -24,97 +25,110 @@ type Config struct { var Conf Config -var configLocHost = Config{ - Namespace: "local-host", - GinDebug: true, - ServerIP: "0.0.0.0", - ServerPort: "8080", - DBFile: ".run-data/db.sqlite3", - RequestTimeout: 16 * time.Second, - ReturnRawErrors: true, - FirebaseTokenURI: "https://oauth2.googleapis.com/token", - FirebaseProjectID: "", - FirebasePrivKeyID: "", - FirebaseClientMail: "", - FirebasePrivateKey: "", +var configLocHost = func() Config { + return Config{ + Namespace: "local-host", + GinDebug: true, + ServerIP: "0.0.0.0", + ServerPort: "8080", + DBFile: ".run-data/db.sqlite3", + RequestTimeout: 16 * time.Second, + ReturnRawErrors: true, + DummyFirebase: true, + FirebaseTokenURI: "", + FirebaseProjectID: "", + FirebasePrivKeyID: "", + FirebaseClientMail: "", + FirebasePrivateKey: "", + } } -var configLocDocker = Config{ - Namespace: "local-docker", - GinDebug: true, - ServerIP: "0.0.0.0", - ServerPort: "80", - DBFile: "/data/scn_docker.sqlite3", - RequestTimeout: 16 * time.Second, - ReturnRawErrors: true, - FirebaseTokenURI: "https://oauth2.googleapis.com/token", - FirebaseProjectID: "", - FirebasePrivKeyID: "", - FirebaseClientMail: "", - FirebasePrivateKey: "", +var configLocDocker = func() Config { + return Config{ + Namespace: "local-docker", + GinDebug: true, + ServerIP: "0.0.0.0", + ServerPort: "80", + DBFile: "/data/scn_docker.sqlite3", + RequestTimeout: 16 * time.Second, + ReturnRawErrors: true, + DummyFirebase: true, + FirebaseTokenURI: "", + FirebaseProjectID: "", + FirebasePrivKeyID: "", + FirebaseClientMail: "", + FirebasePrivateKey: "", + } } -var configDev = Config{ - Namespace: "develop", - GinDebug: true, - ServerIP: "0.0.0.0", - ServerPort: "80", - DBFile: "/data/scn.sqlite3", - RequestTimeout: 16 * time.Second, - ReturnRawErrors: true, - FirebaseTokenURI: "https://oauth2.googleapis.com/token", - FirebaseProjectID: confEnv("FB_PROJECTID"), - FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"), - FirebaseClientMail: confEnv("FB_CLIENTEMAIL"), - FirebasePrivateKey: confEnv("FB_PRIVATEKEY"), +var configDev = func() Config { + return Config{ + Namespace: "develop", + GinDebug: true, + ServerIP: "0.0.0.0", + ServerPort: "80", + DBFile: "/data/scn.sqlite3", + RequestTimeout: 16 * time.Second, + ReturnRawErrors: true, + DummyFirebase: false, + FirebaseTokenURI: "https://oauth2.googleapis.com/token", + FirebaseProjectID: confEnv("FB_PROJECTID"), + FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"), + FirebaseClientMail: confEnv("FB_CLIENTEMAIL"), + FirebasePrivateKey: confEnv("FB_PRIVATEKEY"), + } } -var configStag = Config{ - Namespace: "staging", - GinDebug: true, - ServerIP: "0.0.0.0", - ServerPort: "80", - DBFile: "/data/scn.sqlite3", - RequestTimeout: 16 * time.Second, - ReturnRawErrors: true, - FirebaseTokenURI: "https://oauth2.googleapis.com/token", - FirebaseProjectID: confEnv("FB_PROJECTID"), - FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"), - FirebaseClientMail: confEnv("FB_CLIENTEMAIL"), - FirebasePrivateKey: confEnv("FB_PRIVATEKEY"), +var configStag = func() Config { + return Config{ + Namespace: "staging", + GinDebug: true, + ServerIP: "0.0.0.0", + ServerPort: "80", + DBFile: "/data/scn.sqlite3", + RequestTimeout: 16 * time.Second, + ReturnRawErrors: true, + DummyFirebase: false, + FirebaseTokenURI: "https://oauth2.googleapis.com/token", + FirebaseProjectID: confEnv("FB_PROJECTID"), + FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"), + FirebaseClientMail: confEnv("FB_CLIENTEMAIL"), + FirebasePrivateKey: confEnv("FB_PRIVATEKEY"), + } } -var configProd = Config{ - Namespace: "production", - GinDebug: false, - ServerIP: "0.0.0.0", - ServerPort: "80", - DBFile: "/data/scn.sqlite3", - RequestTimeout: 16 * time.Second, - ReturnRawErrors: false, - FirebaseTokenURI: "https://oauth2.googleapis.com/token", - FirebaseProjectID: confEnv("FB_PROJECTID"), - FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"), - FirebaseClientMail: confEnv("FB_CLIENTEMAIL"), - FirebasePrivateKey: confEnv("FB_PRIVATEKEY"), +var configProd = func() Config { + return Config{ + Namespace: "production", + GinDebug: false, + ServerIP: "0.0.0.0", + ServerPort: "80", + DBFile: "/data/scn.sqlite3", + RequestTimeout: 16 * time.Second, + ReturnRawErrors: false, + DummyFirebase: false, + FirebaseTokenURI: "https://oauth2.googleapis.com/token", + FirebaseProjectID: confEnv("FB_PROJECTID"), + FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"), + FirebaseClientMail: confEnv("FB_CLIENTEMAIL"), + FirebasePrivateKey: confEnv("FB_PRIVATEKEY"), + } } -var allConfig = []Config{ - configLocHost, - configLocDocker, - configDev, - configStag, - configProd, +var allConfig = map[string]func() Config{ + "local-host": configLocHost, + "local-docker": configLocDocker, + "develop": configDev, + "staging": configStag, + "production": configProd, } func getConfig(ns string) (Config, bool) { if ns == "" { - return configLocHost, true + return configLocHost(), true } - for _, c := range allConfig { - if c.Namespace == ns { - return c, true - } + if c, ok := allConfig[ns]; ok { + return c(), true } return Config{}, false } diff --git a/server/firebase/scnserviceaccountkey.json b/server/firebase/scnserviceaccountkey.json deleted file mode 100644 index ecff666..0000000 --- a/server/firebase/scnserviceaccountkey.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "service_account", - "project_id": "simplecloudnotifier-ea7ef", - "private_key_id": "5bfab19fca25034e87c5b3bd1a4334499d2d1f85", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQD2NWOQDcalRdkp\nHtQHABLlu3GMBQBJrGiCxzOZhi/lLwrw2MJEmg1VFz6TVkX2z3SCzXCPOgGriM70\nuWCNLyZQvUng7u6/WH9hlpCg0vJpkw6BvOBt1zYu3gbb5M0SKEOR+lDVccEjAnT4\nexebXdJHJcbaYAcPnBQ9tgP+cozQBnr2EfxYL0bGMgiH9fErJSGMBDFI996uUW9a\nbtfkZ/XpZqYAvyGQMEjknGnQ8t8PHAnsS9dc1PXSWfBvz07ba3fkypWcpTsIYUiZ\nSpwTLV8awihKHJuphoTWb4x6p/ijop05qr1p3fe8gZd9qOGgALe+JT4IBLgNYKrP\nLMSKH3TdAgMBAAECggEAdFcWDOP1kfNHgl7G4efvBg9kwD08vZNybxmiEFGQIEPy\nb4x9f90rn6G0N/r0ZIPzEjvxjDxkvaGP6aQPM6er+0r2tgsxVcmDp6F2Bgin86tB\nl5ygkEa5m7vekdmz7XiJNVmLCNEP6nMmwqOnrArRaj03kcj+jSm7hs2TZZDLaSA5\nf+2q7h0jaU7Nm0ZwCNJqfPJEGdu1J3fR29Ej0rI8N0w/BuYRet1VYDO09lquqOPS\n0WirOOWV6eyqijqRT+RCt0vVzAppS6guhN7J7RS0V9GLJ/13sdvHuJy/WTjBb7gQ\na6QTo8D3yYF+cn3+0BmgP55uW7N6tsYwXIRZcTI3IQKBgQD+tDKMx0puZu+8zTX9\nC2oHSb4Frl2xq17ZpbkfFmOBPWfQbAHNiQTUoQlzCOQM6QejykXFvfsddP7EY2tL\npgLUrBh81wSCAOOo19vYwQB3YKa5ZZucKxh2VxFSefL/+BYHijFb0mWBj5HmqWS6\n7l6IYT3L04aRK9kxj0Cg6L/z6wKBgQD3dh/kQlPemfdxRpZUJ6WEE5x3Bv7WjLop\nnWgE02Pk8+DB+s50GD3nOR276ADCYS6OkBsgfMkwhhKWZigiEoK9DMul5n587jc9\no5AalZN3IbBGAoXk+u3g1GC9bOY3454K6IJyhehDTImEFyfm00qfUL8fMNcdEx8O\nnwxtyRawVwKBgGqsnd9IOGw0wIOajtoERcv3npZSiPs4guk092uFvPcL+MbZ9YdX\ns6Y6K/L57klZ79ExjjdbcijML0ehO/ba+KSJz1e51jF8ndzBS1pkuwVEfY94dsvZ\nYM1vednJKXT7On696h5C6DBzKPAqUf3Yh88mqvMLDHkQnE6daLv7vykxAoGAOPmA\ndDx1NO48E1+OIwgRyqv9PUZmDB3Qit5L4biN6lvgJqlJOV+PeRokZ2wOKLLZVkeF\nh2BTrhFgXDJfESEz6rT0eljsTHVIUK/E8On5Ttd5z1SrYUII3NfpAhP9mWaVr6tC\nxX1hMYWAr+Ho9PM23iFoL5U+IdqSLvqdkPVYfPcCgYB1ANKNYPIJNx/wLxYWNS0r\nI98HwKfv2TxxE/l+2459NMMHY5wlpFl7MNoeK2SdY+ghWPlxC6u5Nxpnk+bZ8TJe\np7U2nY0SQDLCmPgGWs3KBb/zR49X2b7JS3CXXqQSrLxBe2phZg6kE5nB6NPUDc/i\n6WG8tG20rCfgwlXeXl0+Ow==\n-----END PRIVATE KEY-----\n", - "client_email": "firebase-adminsdk-42grv@simplecloudnotifier-ea7ef.iam.gserviceaccount.com", - "client_id": "109837766844812362714", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-42grv%40simplecloudnotifier-ea7ef.iam.gserviceaccount.com" -} diff --git a/server/logic/application.go b/server/logic/application.go index 5f5c746..0b10886 100644 --- a/server/logic/application.go +++ b/server/logic/application.go @@ -5,8 +5,8 @@ import ( "blackforestbytes.com/simplecloudnotifier/api/apierr" "blackforestbytes.com/simplecloudnotifier/common/ginresp" "blackforestbytes.com/simplecloudnotifier/db" - "blackforestbytes.com/simplecloudnotifier/firebase" "blackforestbytes.com/simplecloudnotifier/models" + "blackforestbytes.com/simplecloudnotifier/push" "context" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" @@ -27,7 +27,7 @@ type Application struct { Config scn.Config Gin *gin.Engine Database *db.Database - Firebase *firebase.FBConnector + Firebase push.NotificationClient Jobs []Job } @@ -37,7 +37,7 @@ func NewApp(db *db.Database) *Application { } } -func (app *Application) Init(cfg scn.Config, g *gin.Engine, fb *firebase.FBConnector, jobs []Job) { +func (app *Application) Init(cfg scn.Config, g *gin.Engine, fb push.NotificationClient, jobs []Job) { app.Config = cfg app.Gin = g app.Firebase = fb diff --git a/server/push/dummy.go b/server/push/dummy.go new file mode 100644 index 0000000..71afbe7 --- /dev/null +++ b/server/push/dummy.go @@ -0,0 +1,17 @@ +package push + +import ( + "blackforestbytes.com/simplecloudnotifier/models" + "context" + _ "embed" +) + +type DummyConnector struct{} + +func NewDummy() (NotificationClient, error) { + return &DummyConnector{}, nil +} + +func (d DummyConnector) SendNotification(ctx context.Context, client models.Client, msg models.Message) (string, error) { + return "%DUMMY%", nil +} diff --git a/server/firebase/firebase.go b/server/push/firebase.go similarity index 90% rename from server/firebase/firebase.go rename to server/push/firebase.go index 54ea4ad..16d532a 100644 --- a/server/firebase/firebase.go +++ b/server/push/firebase.go @@ -1,4 +1,4 @@ -package firebase +package push import ( scn "blackforestbytes.com/simplecloudnotifier" @@ -21,20 +21,20 @@ import ( // https://firebase.google.com/docs/cloud-messaging/send-message#rest // https://firebase.google.com/docs/cloud-messaging/auth-server -type FBConnector struct { +type FirebaseConnector struct { fbProject string client http.Client - auth *FBOAuth2 + auth *FirebaseOAuth2 } -func NewFirebase(conf scn.Config) (*FBConnector, error) { +func NewFirebaseConn(conf scn.Config) (NotificationClient, error) { fbauth, err := NewAuth(conf.FirebaseTokenURI, conf.FirebaseProjectID, conf.FirebaseClientMail, conf.FirebasePrivateKey) if err != nil { return nil, err } - return &FBConnector{ + return &FirebaseConnector{ fbProject: conf.FirebaseProjectID, client: http.Client{Timeout: 5 * time.Second}, auth: fbauth, @@ -50,7 +50,7 @@ type Notification struct { Priority int } -func (fb FBConnector) SendNotification(ctx context.Context, client models.Client, msg models.Message) (string, error) { +func (fb FirebaseConnector) SendNotification(ctx context.Context, client models.Client, msg models.Message) (string, error) { uri := "https://fcm.googleapis.com/v1/projects/" + fb.fbProject + "/messages:send" diff --git a/server/push/notificationClient.go b/server/push/notificationClient.go new file mode 100644 index 0000000..57617e4 --- /dev/null +++ b/server/push/notificationClient.go @@ -0,0 +1,10 @@ +package push + +import ( + "blackforestbytes.com/simplecloudnotifier/models" + "context" +) + +type NotificationClient interface { + SendNotification(ctx context.Context, client models.Client, msg models.Message) (string, error) +} diff --git a/server/firebase/FBOAuth.go b/server/push/oauth2.go similarity index 92% rename from server/firebase/FBOAuth.go rename to server/push/oauth2.go index cfb365c..2172983 100644 --- a/server/firebase/FBOAuth.go +++ b/server/push/oauth2.go @@ -1,4 +1,4 @@ -package firebase +package push import ( "context" @@ -22,7 +22,7 @@ import ( "time" ) -type FBOAuth2 struct { +type FirebaseOAuth2 struct { client *http.Client scopes []string @@ -35,14 +35,14 @@ type FBOAuth2 struct { privateKey *rsa.PrivateKey } -func NewAuth(tokenURL string, privKeyID string, cmail string, pemstr string) (*FBOAuth2, error) { +func NewAuth(tokenURL string, privKeyID string, cmail string, pemstr string) (*FirebaseOAuth2, error) { pkey, err := decodePemKey(pemstr) if err != nil { return nil, err } - return &FBOAuth2{ + return &FirebaseOAuth2{ client: &http.Client{Timeout: 3 * time.Second}, tokenURL: tokenURL, privateKey: pkey, @@ -87,7 +87,7 @@ func decodePemKey(pemstr string) (*rsa.PrivateKey, error) { return nil, errors.New(fmt.Sprintf("failed to parse private-key: [ %v | %v ]", err1, err2)) } -func (a *FBOAuth2) Token(ctx context.Context) (string, error) { +func (a *FirebaseOAuth2) Token(ctx context.Context) (string, error) { if a.currToken == nil || a.tokenExpiry == nil || a.tokenExpiry.Before(time.Now()) { err := a.Refresh(ctx) if err != nil { @@ -98,7 +98,7 @@ func (a *FBOAuth2) Token(ctx context.Context) (string, error) { return *a.currToken, nil } -func (a *FBOAuth2) Refresh(ctx context.Context) error { +func (a *FirebaseOAuth2) Refresh(ctx context.Context) error { assertion, err := a.encodeAssertion(a.privateKey) if err != nil { @@ -153,7 +153,7 @@ func (a *FBOAuth2) Refresh(ctx context.Context) error { return nil } -func (a *FBOAuth2) encodeAssertion(key *rsa.PrivateKey) (string, error) { +func (a *FirebaseOAuth2) encodeAssertion(key *rsa.PrivateKey) (string, error) { headBin, err := json.Marshal(gin.H{"alg": "RS256", "typ": "JWT", "kid": a.privateKeyID}) if err != nil { return "", err diff --git a/server/swagger/swagger.json b/server/swagger/swagger.json index a47740c..3ac49d4 100644 --- a/server/swagger/swagger.json +++ b/server/swagger/swagger.json @@ -12,6 +12,9 @@ "/": { "post": { "description": "All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required", + "tags": [ + "External" + ], "summary": "Send a new message", "parameters": [ { @@ -147,9 +150,230 @@ } } }, - "/api-v2/messages": { + "/api/ack.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Acknowledge that a message was received", + "operationId": "compat-ack", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the message id", + "name": "scn_msg_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the message id", + "name": "scn_msg_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/db-test": { + "get": { + "tags": [ + "Common" + ], + "summary": "Check for a wroking database connection", + "operationId": "api-common-dbtest", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.DatabaseTest.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + } + }, + "/api/expand.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Get a whole (potentially truncated) message", + "operationId": "compat-expand", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "The user_id", + "name": "user_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "The user_key", + "name": "user_key", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "The message-id", + "name": "scn_msg_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "The user_id", + "name": "user_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "The user_key", + "name": "user_key", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "The message-id", + "name": "scn_msg_id", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/health": { + "get": { + "tags": [ + "Common" + ], + "summary": "Server Health-checks", + "operationId": "api-common-health", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.Health.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + } + }, + "/api/info.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Get information about the current user", + "operationId": "compat-info", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/messages": { "get": { "description": "The next_page_token is an opaque token, the special value \"@start\" (or empty-string) is the beginning and \"@end\" is the end\nSimply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query\nIf there are no more entries the token \"@end\" will be returned\nBy default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size)", + "tags": [ + "API-v2" + ], "summary": "List all (subscribed) messages", "operationId": "api-messages-list", "parameters": [ @@ -209,6 +433,9 @@ }, "post": { "description": "This is similar to the main route `POST -\u003e https://scn.blackfrestbytes.com/`\nBut this route can change in the future, for long-living scripts etc. it's better to use the normal POST route", + "tags": [ + "API-v2" + ], "summary": "Create a new message", "operationId": "api-messages-create", "parameters": [ @@ -282,9 +509,12 @@ } } }, - "/api-v2/messages/{mid}": { + "/api/messages/{mid}": { "patch": { "description": "The user must own the message and request the resource with the ADMIN Key", + "tags": [ + "API-v2" + ], "summary": "Delete a single message", "operationId": "api-messages-delete", "parameters": [ @@ -330,8 +560,382 @@ } } }, - "/api-v2/users/": { + "/api/ping": { + "get": { + "tags": [ + "Common" + ], + "summary": "Simple endpoint to test connection (any http method)", + "operationId": "api-common-ping", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.pingResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + }, + "put": { + "tags": [ + "Common" + ], + "summary": "Simple endpoint to test connection (any http method)", + "operationId": "api-common-ping", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.pingResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + }, "post": { + "tags": [ + "Common" + ], + "summary": "Simple endpoint to test connection (any http method)", + "operationId": "api-common-ping", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.pingResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + }, + "delete": { + "tags": [ + "Common" + ], + "summary": "Simple endpoint to test connection (any http method)", + "operationId": "api-common-ping", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.pingResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + }, + "patch": { + "tags": [ + "Common" + ], + "summary": "Simple endpoint to test connection (any http method)", + "operationId": "api-common-ping", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.pingResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + } + }, + "/api/register.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Register a new account", + "operationId": "compat-register", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "the (android) fcm token", + "name": "fcm_token", + "in": "query", + "required": true + }, + { + "enum": [ + "true", + "false" + ], + "type": "string", + "description": "if the user is a paid account", + "name": "pro", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the (android) IAP token", + "name": "pro_token", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the (android) fcm token", + "name": "fcm_token", + "in": "formData", + "required": true + }, + { + "enum": [ + "true", + "false" + ], + "type": "string", + "description": "if the user is a paid account", + "name": "pro", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the (android) IAP token", + "name": "pro_token", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/requery.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Return all not-acknowledged messages", + "operationId": "compat-requery", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/update.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Set the fcm-token (android)", + "operationId": "compat-update", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the (android) fcm token", + "name": "fcm_token", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the (android) fcm token", + "name": "fcm_token", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/upgrade.php": { + "get": { + "tags": [ + "API-v1" + ], + "summary": "Upgrade a free account to a paid account", + "operationId": "compat-upgrade", + "deprecated": true, + "parameters": [ + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "query", + "required": true + }, + { + "enum": [ + "true", + "false" + ], + "type": "string", + "description": "if the user is a paid account", + "name": "pro", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the (android) IAP token", + "name": "pro_token", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "the user_id", + "name": "user_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the user_key", + "name": "user_key", + "in": "formData", + "required": true + }, + { + "enum": [ + "true", + "false" + ], + "type": "string", + "description": "if the user is a paid account", + "name": "pro", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "the (android) IAP token", + "name": "pro_token", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/ginresp.compatAPIError" + } + } + } + } + }, + "/api/users/": { + "post": { + "tags": [ + "API-v2" + ], "summary": "Create a new user", "operationId": "api-user-create", "parameters": [ @@ -348,7 +952,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.UserJSON" + "$ref": "#/definitions/handler.sendMessageInternal.response" } }, "400": { @@ -366,8 +970,11 @@ } } }, - "/api-v2/users/{uid}": { + "/api/users/{uid}": { "get": { + "tags": [ + "API-v2" + ], "summary": "Get a user", "operationId": "api-user-get", "parameters": [ @@ -414,6 +1021,9 @@ }, "patch": { "description": "The body-values are optional, only send the ones you want to update", + "tags": [ + "API-v2" + ], "summary": "(Partially) update a user", "operationId": "api-user-update", "parameters": [ @@ -499,9 +1109,12 @@ } } }, - "/api-v2/users/{uid}/channels": { + "/api/users/{uid}/channels": { "get": { "description": "The possible values for 'selector' are:\n- \"owned\" Return all channels of the user\n- \"subscribed\" Return all channels that the user is subscribing to\n- \"all\" Return channels that the user owns or is subscribing\n- \"subscribed_any\" Return all channels that the user is subscribing to (even unconfirmed)\n- \"all_any\" Return channels that the user owns or is subscribing (even unconfirmed)", + "tags": [ + "API-v2" + ], "summary": "List channels of a user (subscribed/owned)", "operationId": "api-channels-list", "parameters": [ @@ -561,8 +1174,11 @@ } } }, - "/api-v2/users/{uid}/channels/{cid}": { + "/api/users/{uid}/channels/{cid}": { "get": { + "tags": [ + "API-v2" + ], "summary": "List all channels of a user", "operationId": "api-channels-get", "parameters": [ @@ -615,6 +1231,9 @@ } }, "patch": { + "tags": [ + "API-v2" + ], "summary": "(Partially) update a channel", "operationId": "api-channels-update", "parameters": [ @@ -683,9 +1302,12 @@ } } }, - "/api-v2/users/{uid}/channels/{cid}/messages": { + "/api/users/{uid}/channels/{cid}/messages": { "get": { "description": "The next_page_token is an opaque token, the special value \"@start\" (or empty-string) is the beginning and \"@end\" is the end\nSimply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query\nIf there are no more entries the token \"@end\" will be returned\nBy default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size)", + "tags": [ + "API-v2" + ], "summary": "List messages of a channel", "operationId": "api-channel-messages", "parameters": [ @@ -744,8 +1366,11 @@ } } }, - "/api-v2/users/{uid}/channels/{cid}/subscriptions": { + "/api/users/{uid}/channels/{cid}/subscriptions": { "get": { + "tags": [ + "API-v2" + ], "summary": "List all subscriptions of a channel", "operationId": "api-chan-subscriptions-list", "parameters": [ @@ -798,8 +1423,11 @@ } } }, - "/api-v2/users/{uid}/clients": { + "/api/users/{uid}/clients": { "get": { + "tags": [ + "API-v2" + ], "summary": "List all clients", "operationId": "api-clients-list", "parameters": [ @@ -845,6 +1473,9 @@ } }, "post": { + "tags": [ + "API-v2" + ], "summary": "Delete a client", "operationId": "api-clients-delete", "parameters": [ @@ -897,8 +1528,11 @@ } } }, - "/api-v2/users/{uid}/clients/{cid}": { + "/api/users/{uid}/clients/{cid}": { "get": { + "tags": [ + "API-v2" + ], "summary": "Get a single clients", "operationId": "api-clients-get", "parameters": [ @@ -951,8 +1585,11 @@ } } }, - "/api-v2/users/{uid}/subscriptions": { + "/api/users/{uid}/subscriptions": { "get": { + "tags": [ + "API-v2" + ], "summary": "List all channels of a user", "operationId": "api-user-subscriptions-list", "parameters": [ @@ -998,6 +1635,9 @@ } }, "post": { + "tags": [ + "API-v2" + ], "summary": "Creare/Request a subscription", "operationId": "api-subscriptions-create", "parameters": [ @@ -1056,8 +1696,11 @@ } } }, - "/api-v2/users/{uid}/subscriptions/{sid}": { + "/api/users/{uid}/subscriptions/{sid}": { "get": { + "tags": [ + "API-v2" + ], "summary": "Get a single subscription", "operationId": "api-subscriptions-get", "parameters": [ @@ -1110,6 +1753,9 @@ } }, "delete": { + "tags": [ + "API-v2" + ], "summary": "Cancel (delete) subscription", "operationId": "api-subscriptions-delete", "parameters": [ @@ -1162,6 +1808,9 @@ } }, "patch": { + "tags": [ + "API-v2" + ], "summary": "Update a subscription (e.g. confirm)", "operationId": "api-subscriptions-update", "parameters": [ @@ -1214,542 +1863,12 @@ } } }, - "/api/ack.php": { - "get": { - "summary": "Acknowledge that a message was received", - "operationId": "compat-ack", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the message id", - "name": "scn_msg_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the message id", - "name": "scn_msg_id", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/api/expand.php": { - "get": { - "summary": "Get a whole (potentially truncated) message", - "operationId": "compat-expand", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "The user_id", - "name": "user_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "The user_key", - "name": "user_key", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "The message-id", - "name": "scn_msg_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "The user_id", - "name": "user_id", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "The user_key", - "name": "user_key", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "The message-id", - "name": "scn_msg_id", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/api/info.php": { - "get": { - "summary": "Get information about the current user", - "operationId": "compat-info", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/api/register.php": { - "get": { - "summary": "Register a new account", - "operationId": "compat-register", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "the (android) fcm token", - "name": "fcm_token", - "in": "query", - "required": true - }, - { - "enum": [ - "true", - "false" - ], - "type": "string", - "description": "if the user is a paid account", - "name": "pro", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the (android) IAP token", - "name": "pro_token", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the (android) fcm token", - "name": "fcm_token", - "in": "formData", - "required": true - }, - { - "enum": [ - "true", - "false" - ], - "type": "string", - "description": "if the user is a paid account", - "name": "pro", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the (android) IAP token", - "name": "pro_token", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/api/requery.php": { - "get": { - "summary": "Return all not-acknowledged messages", - "operationId": "compat-requery", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/api/update.php": { - "get": { - "summary": "Set the fcm-token (android)", - "operationId": "compat-update", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the (android) fcm token", - "name": "fcm_token", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the (android) fcm token", - "name": "fcm_token", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/api/upgrade.php": { - "get": { - "summary": "Upgrade a free account to a paid account", - "operationId": "compat-upgrade", - "deprecated": true, - "parameters": [ - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "query", - "required": true - }, - { - "enum": [ - "true", - "false" - ], - "type": "string", - "description": "if the user is a paid account", - "name": "pro", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the (android) IAP token", - "name": "pro_token", - "in": "query", - "required": true - }, - { - "type": "string", - "description": "the user_id", - "name": "user_id", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the user_key", - "name": "user_key", - "in": "formData", - "required": true - }, - { - "enum": [ - "true", - "false" - ], - "type": "string", - "description": "if the user is a paid account", - "name": "pro", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "the (android) IAP token", - "name": "pro_token", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/ginresp.compatAPIError" - } - } - } - } - }, - "/db-test": { - "get": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.DatabaseTest.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - } - }, - "/health": { - "get": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.Health.response" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - } - }, - "/ping": { - "get": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.pingResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - }, - "put": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.pingResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - }, - "post": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.pingResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - }, - "delete": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.pingResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - }, - "patch": { - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/handler.pingResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/ginresp.apiError" - } - } - } - } - }, "/send": { "post": { "description": "All parameter can be set via query-parameter or the json body. Only UserID, UserKey and Title are required", + "tags": [ + "External" + ], "summary": "Send a new message", "parameters": [ { @@ -1888,6 +2007,9 @@ "/send.php": { "post": { "description": "All parameter can be set via query-parameter or form-data body. Only UserID, UserKey and Title are required", + "tags": [ + "External" + ], "summary": "Send a new message (compatibility)", "deprecated": true, "parameters": [ @@ -2658,5 +2780,19 @@ } } } - } + }, + "tags": [ + { + "name": "Common" + }, + { + "name": "External" + }, + { + "name": "API-v1" + }, + { + "name": "API-v2" + } + ] } \ No newline at end of file diff --git a/server/swagger/swagger.yaml b/server/swagger/swagger.yaml index 8997793..94222d7 100644 --- a/server/swagger/swagger.yaml +++ b/server/swagger/swagger.yaml @@ -527,7 +527,158 @@ paths: schema: $ref: '#/definitions/ginresp.apiError' summary: Send a new message - /api-v2/messages: + tags: + - External + /api/ack.php: + get: + deprecated: true + operationId: compat-ack + parameters: + - description: the user_id + in: query + name: user_id + required: true + type: string + - description: the user_key + in: query + name: user_key + required: true + type: string + - description: the message id + in: query + name: scn_msg_id + required: true + type: string + - description: the user_id + in: formData + name: user_id + required: true + type: string + - description: the user_key + in: formData + name: user_key + required: true + type: string + - description: the message id + in: formData + name: scn_msg_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/ginresp.compatAPIError' + summary: Acknowledge that a message was received + tags: + - API-v1 + /api/db-test: + get: + operationId: api-common-dbtest + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.DatabaseTest.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ginresp.apiError' + summary: Check for a wroking database connection + tags: + - Common + /api/expand.php: + get: + deprecated: true + operationId: compat-expand + parameters: + - description: The user_id + in: query + name: user_id + required: true + type: string + - description: The user_key + in: query + name: user_key + required: true + type: string + - description: The message-id + in: query + name: scn_msg_id + required: true + type: string + - description: The user_id + in: formData + name: user_id + required: true + type: string + - description: The user_key + in: formData + name: user_key + required: true + type: string + - description: The message-id + in: formData + name: scn_msg_id + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/ginresp.compatAPIError' + summary: Get a whole (potentially truncated) message + tags: + - API-v1 + /api/health: + get: + operationId: api-common-health + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.Health.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ginresp.apiError' + summary: Server Health-checks + tags: + - Common + /api/info.php: + get: + deprecated: true + operationId: compat-info + parameters: + - description: the user_id + in: query + name: user_id + required: true + type: string + - description: the user_key + in: query + name: user_key + required: true + type: string + - description: the user_id + in: formData + name: user_id + required: true + type: string + - description: the user_key + in: formData + name: user_key + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/ginresp.compatAPIError' + summary: Get information about the current user + tags: + - API-v1 + /api/messages: get: description: |- The next_page_token is an opaque token, the special value "@start" (or empty-string) is the beginning and "@end" is the end @@ -570,6 +721,8 @@ paths: schema: $ref: '#/definitions/ginresp.apiError' summary: List all (subscribed) messages + tags: + - API-v2 post: description: |- This is similar to the main route `POST -> https://scn.blackfrestbytes.com/` @@ -619,7 +772,9 @@ paths: schema: $ref: '#/definitions/ginresp.apiError' summary: Create a new message - /api-v2/messages/{mid}: + tags: + - API-v2 + /api/messages/{mid}: patch: description: The user must own the message and request the resource with the ADMIN Key @@ -652,715 +807,79 @@ paths: schema: $ref: '#/definitions/ginresp.apiError' summary: Delete a single message - /api-v2/users/: - post: - operationId: api-user-create - parameters: - - description: ' ' - in: body - name: post_body - schema: - $ref: '#/definitions/handler.CreateUser.body' - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.UserJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' - summary: Create a new user - /api-v2/users/{uid}: - get: - operationId: api-user-get - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.UserJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: Get a user - patch: - description: The body-values are optional, only send the ones you want to update - operationId: api-user-update - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: Change the username (send an empty string to clear it) - in: body - name: username - schema: - type: string - - description: Send a verification of permium purchase - in: body - name: pro_token - schema: - type: string - - description: Send `true` to create a new read_key - in: body - name: read_key - schema: - type: string - - description: Send `true` to create a new send_key - in: body - name: send_key - schema: - type: string - - description: Send `true` to create a new admin_key - in: body - name: admin_key - schema: - type: string - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.UserJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: (Partially) update a user - /api-v2/users/{uid}/channels: - get: - description: |- - The possible values for 'selector' are: - - "owned" Return all channels of the user - - "subscribed" Return all channels that the user is subscribing to - - "all" Return channels that the user owns or is subscribing - - "subscribed_any" Return all channels that the user is subscribing to (even unconfirmed) - - "all_any" Return channels that the user owns or is subscribing (even unconfirmed) - operationId: api-channels-list - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: 'Filter channels (default: owned)' - enum: - - owned - - subscribed - - all - - subscribed_any - - all_any - in: query - name: selector - required: true - type: string - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.ListChannels.response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: List channels of a user (subscribed/owned) - /api-v2/users/{uid}/channels/{cid}: - get: - operationId: api-channels-get - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: ChannelID - in: path - name: cid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.ChannelJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: List all channels of a user - patch: - operationId: api-channels-update - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: ChannelID - in: path - name: cid - required: true - type: integer - - description: Send `true` to create a new subscribe_key - in: body - name: subscribe_key - schema: - type: string - - description: Send `true` to create a new send_key - in: body - name: send_key - schema: - type: string - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.ChannelJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: (Partially) update a channel - /api-v2/users/{uid}/channels/{cid}/messages: - get: - description: |- - The next_page_token is an opaque token, the special value "@start" (or empty-string) is the beginning and "@end" is the end - Simply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query - If there are no more entries the token "@end" will be returned - By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size) - operationId: api-channel-messages - parameters: - - in: query - name: filter - type: string - - in: query - name: nextPageToken - type: string - - in: query - name: pageSize - type: integer - - in: query - name: trimmed - type: boolean - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.ListChannelMessages.response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: List messages of a channel - /api-v2/users/{uid}/channels/{cid}/subscriptions: - get: - operationId: api-chan-subscriptions-list - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: ChannelID - in: path - name: cid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.ListChannelSubscriptions.response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: List all subscriptions of a channel - /api-v2/users/{uid}/clients: - get: - operationId: api-clients-list - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.ListClients.response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: List all clients - post: - operationId: api-clients-delete - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: ClientID - in: path - name: cid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.ClientJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: Delete a client - /api-v2/users/{uid}/clients/{cid}: - get: - operationId: api-clients-get - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: ClientID - in: path - name: cid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.ClientJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: Get a single clients - /api-v2/users/{uid}/subscriptions: - get: - operationId: api-user-subscriptions-list - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.ListUserSubscriptions.response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: List all channels of a user - post: - operationId: api-subscriptions-create - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - in: query - name: chanSubscribeKey - type: string - - description: ' ' - in: body - name: post_data - schema: - $ref: '#/definitions/handler.CreateSubscription.body' - responses: - "200": - description: OK - schema: - $ref: '#/definitions/models.SubscriptionJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - 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: Creare/Request a subscription - /api-v2/users/{uid}/subscriptions/{sid}: + tags: + - API-v2 + /api/ping: delete: - operationId: api-subscriptions-delete - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: SubscriptionID - in: path - name: sid - required: true - type: integer + operationId: api-common-ping responses: "200": description: OK schema: - $ref: '#/definitions/models.SubscriptionJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/ginresp.apiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/handler.pingResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/ginresp.apiError' - summary: Cancel (delete) subscription + summary: Simple endpoint to test connection (any http method) + tags: + - Common get: - operationId: api-subscriptions-get - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: SubscriptionID - in: path - name: sid - required: true - type: integer + operationId: api-common-ping responses: "200": description: OK schema: - $ref: '#/definitions/models.SubscriptionJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/ginresp.apiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/handler.pingResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/ginresp.apiError' - summary: Get a single subscription + summary: Simple endpoint to test connection (any http method) + tags: + - Common patch: - operationId: api-subscriptions-update - parameters: - - description: UserID - in: path - name: uid - required: true - type: integer - - description: SubscriptionID - in: path - name: sid - required: true - type: integer + operationId: api-common-ping responses: "200": description: OK schema: - $ref: '#/definitions/models.SubscriptionJSON' - "400": - description: Bad Request - schema: - $ref: '#/definitions/ginresp.apiError' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/ginresp.apiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/ginresp.apiError' + $ref: '#/definitions/handler.pingResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/ginresp.apiError' - summary: Update a subscription (e.g. confirm) - /api/ack.php: - get: - deprecated: true - operationId: compat-ack - parameters: - - description: the user_id - in: query - name: user_id - required: true - type: string - - description: the user_key - in: query - name: user_key - required: true - type: string - - description: the message id - in: query - name: scn_msg_id - required: true - type: string - - description: the user_id - in: formData - name: user_id - required: true - type: string - - description: the user_key - in: formData - name: user_key - required: true - type: string - - description: the message id - in: formData - name: scn_msg_id - required: true - type: string + summary: Simple endpoint to test connection (any http method) + tags: + - Common + post: + operationId: api-common-ping responses: "200": description: OK schema: - $ref: '#/definitions/ginresp.compatAPIError' - summary: Acknowledge that a message was received - /api/expand.php: - get: - deprecated: true - operationId: compat-expand - parameters: - - description: The user_id - in: query - name: user_id - required: true - type: string - - description: The user_key - in: query - name: user_key - required: true - type: string - - description: The message-id - in: query - name: scn_msg_id - required: true - type: string - - description: The user_id - in: formData - name: user_id - required: true - type: string - - description: The user_key - in: formData - name: user_key - required: true - type: string - - description: The message-id - in: formData - name: scn_msg_id - required: true - type: string + $ref: '#/definitions/handler.pingResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/ginresp.apiError' + summary: Simple endpoint to test connection (any http method) + tags: + - Common + put: + operationId: api-common-ping responses: "200": description: OK schema: - $ref: '#/definitions/ginresp.compatAPIError' - summary: Get a whole (potentially truncated) message - /api/info.php: - get: - deprecated: true - operationId: compat-info - parameters: - - description: the user_id - in: query - name: user_id - required: true - type: string - - description: the user_key - in: query - name: user_key - required: true - type: string - - description: the user_id - in: formData - name: user_id - required: true - type: string - - description: the user_key - in: formData - name: user_key - required: true - type: string - responses: - "200": - description: OK + $ref: '#/definitions/handler.pingResponse' + "500": + description: Internal Server Error schema: - $ref: '#/definitions/ginresp.compatAPIError' - summary: Get information about the current user + $ref: '#/definitions/ginresp.apiError' + summary: Simple endpoint to test connection (any http method) + tags: + - Common /api/register.php: get: deprecated: true @@ -1408,6 +927,8 @@ paths: schema: $ref: '#/definitions/ginresp.compatAPIError' summary: Register a new account + tags: + - API-v1 /api/requery.php: get: deprecated: true @@ -1439,6 +960,8 @@ paths: schema: $ref: '#/definitions/ginresp.compatAPIError' summary: Return all not-acknowledged messages + tags: + - API-v1 /api/update.php: get: deprecated: true @@ -1480,6 +1003,8 @@ paths: schema: $ref: '#/definitions/ginresp.compatAPIError' summary: Set the fcm-token (android) + tags: + - API-v1 /api/upgrade.php: get: deprecated: true @@ -1537,79 +1062,636 @@ paths: schema: $ref: '#/definitions/ginresp.compatAPIError' summary: Upgrade a free account to a paid account - /db-test: - get: - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.DatabaseTest.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' - /health: - get: - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.Health.response' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' - /ping: - delete: - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.pingResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' - get: - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.pingResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' - patch: - responses: - "200": - description: OK - schema: - $ref: '#/definitions/handler.pingResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/ginresp.apiError' + tags: + - API-v1 + /api/users/: post: + operationId: api-user-create + parameters: + - description: ' ' + in: body + name: post_body + schema: + $ref: '#/definitions/handler.CreateUser.body' responses: "200": description: OK schema: - $ref: '#/definitions/handler.pingResponse' + $ref: '#/definitions/handler.sendMessageInternal.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' "500": description: Internal Server Error schema: $ref: '#/definitions/ginresp.apiError' - put: + summary: Create a new user + tags: + - API-v2 + /api/users/{uid}: + get: + operationId: api-user-get + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer responses: "200": description: OK schema: - $ref: '#/definitions/handler.pingResponse' + $ref: '#/definitions/models.UserJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Get a user + tags: + - API-v2 + patch: + description: The body-values are optional, only send the ones you want to update + operationId: api-user-update + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: Change the username (send an empty string to clear it) + in: body + name: username + schema: + type: string + - description: Send a verification of permium purchase + in: body + name: pro_token + schema: + type: string + - description: Send `true` to create a new read_key + in: body + name: read_key + schema: + type: string + - description: Send `true` to create a new send_key + in: body + name: send_key + schema: + type: string + - description: Send `true` to create a new admin_key + in: body + name: admin_key + schema: + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.UserJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: (Partially) update a user + tags: + - API-v2 + /api/users/{uid}/channels: + get: + description: |- + The possible values for 'selector' are: + - "owned" Return all channels of the user + - "subscribed" Return all channels that the user is subscribing to + - "all" Return channels that the user owns or is subscribing + - "subscribed_any" Return all channels that the user is subscribing to (even unconfirmed) + - "all_any" Return channels that the user owns or is subscribing (even unconfirmed) + operationId: api-channels-list + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: 'Filter channels (default: owned)' + enum: + - owned + - subscribed + - all + - subscribed_any + - all_any + in: query + name: selector + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.ListChannels.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: List channels of a user (subscribed/owned) + tags: + - API-v2 + /api/users/{uid}/channels/{cid}: + get: + operationId: api-channels-get + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: ChannelID + in: path + name: cid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ChannelJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: List all channels of a user + tags: + - API-v2 + patch: + operationId: api-channels-update + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: ChannelID + in: path + name: cid + required: true + type: integer + - description: Send `true` to create a new subscribe_key + in: body + name: subscribe_key + schema: + type: string + - description: Send `true` to create a new send_key + in: body + name: send_key + schema: + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ChannelJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: (Partially) update a channel + tags: + - API-v2 + /api/users/{uid}/channels/{cid}/messages: + get: + description: |- + The next_page_token is an opaque token, the special value "@start" (or empty-string) is the beginning and "@end" is the end + Simply start the pagination without a next_page_token and get the next page by calling this endpoint with the returned next_page_token of the last query + If there are no more entries the token "@end" will be returned + By default we return long messages with a trimmed body, if trimmed=false is supplied we return full messages (this reduces the max page_size) + operationId: api-channel-messages + parameters: + - in: query + name: filter + type: string + - in: query + name: nextPageToken + type: string + - in: query + name: pageSize + type: integer + - in: query + name: trimmed + type: boolean + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.ListChannelMessages.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: List messages of a channel + tags: + - API-v2 + /api/users/{uid}/channels/{cid}/subscriptions: + get: + operationId: api-chan-subscriptions-list + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: ChannelID + in: path + name: cid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.ListChannelSubscriptions.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: List all subscriptions of a channel + tags: + - API-v2 + /api/users/{uid}/clients: + get: + operationId: api-clients-list + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.ListClients.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: List all clients + tags: + - API-v2 + post: + operationId: api-clients-delete + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: ClientID + in: path + name: cid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ClientJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Delete a client + tags: + - API-v2 + /api/users/{uid}/clients/{cid}: + get: + operationId: api-clients-get + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: ClientID + in: path + name: cid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.ClientJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Get a single clients + tags: + - API-v2 + /api/users/{uid}/subscriptions: + get: + operationId: api-user-subscriptions-list + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.ListUserSubscriptions.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: List all channels of a user + tags: + - API-v2 + post: + operationId: api-subscriptions-create + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - in: query + name: chanSubscribeKey + type: string + - description: ' ' + in: body + name: post_data + schema: + $ref: '#/definitions/handler.CreateSubscription.body' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.SubscriptionJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Creare/Request a subscription + tags: + - API-v2 + /api/users/{uid}/subscriptions/{sid}: + delete: + operationId: api-subscriptions-delete + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: SubscriptionID + in: path + name: sid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.SubscriptionJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Cancel (delete) subscription + tags: + - API-v2 + get: + operationId: api-subscriptions-get + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: SubscriptionID + in: path + name: sid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.SubscriptionJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Get a single subscription + tags: + - API-v2 + patch: + operationId: api-subscriptions-update + parameters: + - description: UserID + in: path + name: uid + required: true + type: integer + - description: SubscriptionID + in: path + name: sid + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.SubscriptionJSON' + "400": + description: Bad Request + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: Unauthorized + 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: Update a subscription (e.g. confirm) + tags: + - API-v2 /send: post: description: All parameter can be set via query-parameter or the json body. @@ -1697,6 +1779,8 @@ paths: schema: $ref: '#/definitions/ginresp.apiError' summary: Send a new message + tags: + - External /send.php: post: deprecated: true @@ -1767,4 +1851,11 @@ paths: schema: $ref: '#/definitions/ginresp.apiError' summary: Send a new message (compatibility) + tags: + - External swagger: "2.0" +tags: +- name: Common +- name: External +- name: API-v1 +- name: API-v2