update HTML with new methods

This commit is contained in:
Mike Schwörer 2022-11-21 22:52:44 +01:00
parent d5aea1a828
commit 03c35d6446
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
17 changed files with 374 additions and 234 deletions

View File

@ -5,7 +5,7 @@ PORT=9090
NAMESPACE=$(shell git rev-parse --abbrev-ref HEAD) NAMESPACE=$(shell git rev-parse --abbrev-ref HEAD)
HASH=$(shell git rev-parse HEAD) HASH=$(shell git rev-parse HEAD)
build: build: swagger fmt
rm -f ./_build/scn_backend rm -f ./_build/scn_backend
CGO_ENABLED=1 go build -v -o _build/scn_backend -tags timetzdata ./cmd/scnserver CGO_ENABLED=1 go build -v -o _build/scn_backend -tags timetzdata ./cmd/scnserver
@ -13,7 +13,7 @@ run: build
mkdir -p .run-data mkdir -p .run-data
_build/scn_backend _build/scn_backend
docker: docker: build
[ ! -f "DOCKER_GIT_INFO" ] || rm DOCKER_GIT_INFO [ ! -f "DOCKER_GIT_INFO" ] || rm DOCKER_GIT_INFO
git rev-parse --abbrev-ref HEAD >> DOCKER_GIT_INFO git rev-parse --abbrev-ref HEAD >> DOCKER_GIT_INFO
git rev-parse HEAD >> DOCKER_GIT_INFO git rev-parse HEAD >> DOCKER_GIT_INFO
@ -33,7 +33,7 @@ swagger:
which swag || go install github.com/swaggo/swag/cmd/swag@latest which swag || go install github.com/swaggo/swag/cmd/swag@latest
swag init -generalInfo api/router.go --output ./swagger/ --outputTypes "json,yaml" swag init -generalInfo api/router.go --output ./swagger/ --outputTypes "json,yaml"
run-docker-local: run-docker-local: docker
mkdir -p .run-data mkdir -p .run-data
docker run --rm \ docker run --rm \
--init \ --init \
@ -42,7 +42,7 @@ run-docker-local:
--publish "8080:80" \ --publish "8080:80" \
$(DOCKER_NAME):latest $(DOCKER_NAME):latest
inspect-docker: inspect-docker: docker
mkdir -p .run-data mkdir -p .run-data
docker run -ti \ docker run -ti \
--rm \ --rm \
@ -50,7 +50,7 @@ inspect-docker:
$(DOCKER_NAME):latest \ $(DOCKER_NAME):latest \
bash bash
push-docker: push-docker: docker
docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):$(HASH)" docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):$(HASH)"
docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):$(NAMESPACE)-latest" docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):$(NAMESPACE)-latest"
docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):latest" docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):latest"

View File

@ -2,6 +2,8 @@
//TODO //TODO
- remove fcm/goog_api keys from repo (and invalidate them !!)
- migration script for existing data - migration script for existing data
- ack/read deliveries && return ack-count (? or not, how to query?) - ack/read deliveries && return ack-count (? or not, how to query?)
@ -9,8 +11,8 @@
- verify pro_token - verify pro_token
- full-text-search: https://www.sqlite.org/fts5.html#contentless_tables - full-text-search: https://www.sqlite.org/fts5.html#contentless_tables
- update html (--channel, api doku, better send route, etc)
- dark mode toggle for html - dark mode toggle for html
- app-store link in HTML
- deploy - deploy

View File

@ -1356,6 +1356,9 @@ func (h APIHandler) CreateMessage(g *gin.Context) ginresp.HTTPResponse {
if b.Content != nil && len(*b.Content) > user.MaxContentLength() { if b.Content != nil && len(*b.Content) > user.MaxContentLength() {
return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, 104, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(*b.Content), user.MaxContentLength()), nil) return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, 104, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(*b.Content), user.MaxContentLength()), nil)
} }
if len(channelName) > user.MaxChannelNameLength() {
return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, 106, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil)
}
if b.UserMessageID != nil { if b.UserMessageID != nil {
msg, err := h.database.GetMessageByUserMessageID(ctx, *b.UserMessageID) msg, err := h.database.GetMessageByUserMessageID(ctx, *b.UserMessageID)

View File

@ -44,7 +44,6 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
// @Failure 400 {object} ginresp.apiError // @Failure 400 {object} ginresp.apiError
// @Failure 401 {object} ginresp.apiError // @Failure 401 {object} ginresp.apiError
// @Failure 403 {object} ginresp.apiError // @Failure 403 {object} ginresp.apiError
// @Failure 404 {object} ginresp.apiError
// @Failure 500 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError
// //
// @Router /send.php [POST] // @Router /send.php [POST]
@ -93,10 +92,9 @@ func (h MessageHandler) SendMessageCompat(g *gin.Context) ginresp.HTTPResponse {
// //
// @Success 200 {object} handler.sendMessageInternal.response // @Success 200 {object} handler.sendMessageInternal.response
// @Failure 400 {object} ginresp.apiError // @Failure 400 {object} ginresp.apiError
// @Failure 401 {object} ginresp.apiError // @Failure 401 {object} ginresp.apiError "The user_id was not found or the user_key is wrong"
// @Failure 403 {object} ginresp.apiError // @Failure 403 {object} ginresp.apiError "The user has exceeded its daily quota - wait 24 hours or upgrade your account"
// @Failure 404 {object} ginresp.apiError // @Failure 500 {object} ginresp.apiError "An internal server error occurred - try again later"
// @Failure 500 {object} ginresp.apiError
// //
// @Router / [POST] // @Router / [POST]
// @Router /send [POST] // @Router /send [POST]
@ -212,6 +210,9 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
if Content != nil && len(*Content) > user.MaxContentLength() { if Content != nil && len(*Content) > user.MaxContentLength() {
return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, 104, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(*Content), user.MaxContentLength()), nil) return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, 104, fmt.Sprintf("Content too long (%d characters; max := %d characters)", len(*Content), user.MaxContentLength()), nil)
} }
if len(channelName) > user.MaxChannelNameLength() {
return ginresp.SendAPIError(g, 400, apierr.CONTENT_TOO_LONG, 106, fmt.Sprintf("Channel too long (max %d characters)", user.MaxChannelNameLength()), nil)
}
if UserMessageID != nil { if UserMessageID != nil {
msg, err := h.database.GetMessageByUserMessageID(ctx, *UserMessageID) msg, err := h.database.GetMessageByUserMessageID(ctx, *UserMessageID)

View File

@ -4,43 +4,47 @@ import (
"blackforestbytes.com/simplecloudnotifier/common/ginresp" "blackforestbytes.com/simplecloudnotifier/common/ginresp"
"blackforestbytes.com/simplecloudnotifier/logic" "blackforestbytes.com/simplecloudnotifier/logic"
"blackforestbytes.com/simplecloudnotifier/website" "blackforestbytes.com/simplecloudnotifier/website"
"errors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"regexp"
"strings" "strings"
) )
type WebsiteHandler struct { type WebsiteHandler struct {
app *logic.Application app *logic.Application
rexTemplate *regexp.Regexp
} }
func NewWebsiteHandler(app *logic.Application) WebsiteHandler { func NewWebsiteHandler(app *logic.Application) WebsiteHandler {
return WebsiteHandler{ return WebsiteHandler{
app: app, app: app,
rexTemplate: regexp.MustCompile("{{template\\|[A-Za-z0-9_\\-.]+}}"),
} }
} }
func (h WebsiteHandler) Index(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) Index(g *gin.Context) ginresp.HTTPResponse {
return h.serveAsset(g, "index.html") return h.serveAsset(g, "index.html", true)
} }
func (h WebsiteHandler) APIDocs(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) APIDocs(g *gin.Context) ginresp.HTTPResponse {
return h.serveAsset(g, "api.html") return h.serveAsset(g, "api.html", true)
} }
func (h WebsiteHandler) APIDocsMore(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) APIDocsMore(g *gin.Context) ginresp.HTTPResponse {
return h.serveAsset(g, "api_more.html") return h.serveAsset(g, "api_more.html", true)
} }
func (h WebsiteHandler) MessageSent(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) MessageSent(g *gin.Context) ginresp.HTTPResponse {
return h.serveAsset(g, "message_sent.html") return h.serveAsset(g, "message_sent.html", true)
} }
func (h WebsiteHandler) FaviconIco(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) FaviconIco(g *gin.Context) ginresp.HTTPResponse {
return h.serveAsset(g, "favicon.ico") return h.serveAsset(g, "favicon.ico", false)
} }
func (h WebsiteHandler) FaviconPNG(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) FaviconPNG(g *gin.Context) ginresp.HTTPResponse {
return h.serveAsset(g, "favicon.png") return h.serveAsset(g, "favicon.png", false)
} }
func (h WebsiteHandler) Javascript(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) Javascript(g *gin.Context) ginresp.HTTPResponse {
@ -53,7 +57,7 @@ func (h WebsiteHandler) Javascript(g *gin.Context) ginresp.HTTPResponse {
return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} }
return h.serveAsset(g, "js/"+u.Filename) return h.serveAsset(g, "js/"+u.Filename, false)
} }
func (h WebsiteHandler) CSS(g *gin.Context) ginresp.HTTPResponse { func (h WebsiteHandler) CSS(g *gin.Context) ginresp.HTTPResponse {
@ -66,15 +70,32 @@ func (h WebsiteHandler) CSS(g *gin.Context) ginresp.HTTPResponse {
return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} }
return h.serveAsset(g, "css/"+u.Filename) return h.serveAsset(g, "css/"+u.Filename, false)
} }
func (h WebsiteHandler) serveAsset(g *gin.Context, fn string) ginresp.HTTPResponse { func (h WebsiteHandler) serveAsset(g *gin.Context, fn string, repl bool) ginresp.HTTPResponse {
data, err := website.Assets.ReadFile(fn) data, err := website.Assets.ReadFile(fn)
if err != nil { if err != nil {
return ginresp.Status(http.StatusNotFound) return ginresp.Status(http.StatusNotFound)
} }
if repl {
failed := false
data = h.rexTemplate.ReplaceAllFunc(data, func(match []byte) []byte {
prefix := len("{{template|")
suffix := len("}}")
fnSub := match[prefix : len(match)-suffix]
subdata, err := website.Assets.ReadFile(string(fnSub))
if err != nil {
failed = true
}
return subdata
})
if failed {
return ginresp.InternalError(errors.New("template replacement failed"))
}
}
mime := "text/plain" mime := "text/plain"
lowerFN := strings.ToLower(fn) lowerFN := strings.ToLower(fn)

View File

@ -1,6 +1,7 @@
package server package server
import ( import (
"fmt"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"os" "os"
"time" "time"
@ -14,8 +15,8 @@ type Config struct {
DBFile string DBFile string
RequestTimeout time.Duration RequestTimeout time.Duration
ReturnRawErrors bool ReturnRawErrors bool
FirebaseProjectID string
FirebaseTokenURI string FirebaseTokenURI string
FirebaseProjectID string
FirebasePrivKeyID string FirebasePrivKeyID string
FirebaseClientMail string FirebaseClientMail string
FirebasePrivateKey string FirebasePrivateKey string
@ -31,11 +32,11 @@ var configLocHost = Config{
DBFile: ".run-data/db.sqlite3", DBFile: ".run-data/db.sqlite3",
RequestTimeout: 16 * time.Second, RequestTimeout: 16 * time.Second,
ReturnRawErrors: true, ReturnRawErrors: true,
FirebaseProjectID: "simplecloudnotifier-ea7ef",
FirebaseTokenURI: "https://oauth2.googleapis.com/token", FirebaseTokenURI: "https://oauth2.googleapis.com/token",
FirebasePrivKeyID: "5bfab19fca25034e87c5b3bd1a4334499d2d1f85", FirebaseProjectID: "",
FirebaseClientMail: "firebase-adminsdk-42grv@simplecloudnotifier-ea7ef.iam.gserviceaccount.com", FirebasePrivKeyID: "",
FirebasePrivateKey: "-----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", FirebaseClientMail: "",
FirebasePrivateKey: "",
} }
var configLocDocker = Config{ var configLocDocker = Config{
@ -46,11 +47,11 @@ var configLocDocker = Config{
DBFile: "/data/scn_docker.sqlite3", DBFile: "/data/scn_docker.sqlite3",
RequestTimeout: 16 * time.Second, RequestTimeout: 16 * time.Second,
ReturnRawErrors: true, ReturnRawErrors: true,
FirebaseProjectID: "simplecloudnotifier-ea7ef",
FirebaseTokenURI: "https://oauth2.googleapis.com/token", FirebaseTokenURI: "https://oauth2.googleapis.com/token",
FirebasePrivKeyID: "5bfab19fca25034e87c5b3bd1a4334499d2d1f85", FirebaseProjectID: "",
FirebaseClientMail: "firebase-adminsdk-42grv@simplecloudnotifier-ea7ef.iam.gserviceaccount.com", FirebasePrivKeyID: "",
FirebasePrivateKey: "TODO", FirebaseClientMail: "",
FirebasePrivateKey: "",
} }
var configDev = Config{ var configDev = Config{
@ -61,11 +62,11 @@ var configDev = Config{
DBFile: "/data/scn.sqlite3", DBFile: "/data/scn.sqlite3",
RequestTimeout: 16 * time.Second, RequestTimeout: 16 * time.Second,
ReturnRawErrors: true, ReturnRawErrors: true,
FirebaseProjectID: "simplecloudnotifier-ea7ef",
FirebaseTokenURI: "https://oauth2.googleapis.com/token", FirebaseTokenURI: "https://oauth2.googleapis.com/token",
FirebasePrivKeyID: "5bfab19fca25034e87c5b3bd1a4334499d2d1f85", FirebaseProjectID: confEnv("FB_PROJECTID"),
FirebaseClientMail: "firebase-adminsdk-42grv@simplecloudnotifier-ea7ef.iam.gserviceaccount.com", FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"),
FirebasePrivateKey: "TODO", FirebaseClientMail: confEnv("FB_CLIENTEMAIL"),
FirebasePrivateKey: confEnv("FB_PRIVATEKEY"),
} }
var configStag = Config{ var configStag = Config{
@ -76,11 +77,11 @@ var configStag = Config{
DBFile: "/data/scn.sqlite3", DBFile: "/data/scn.sqlite3",
RequestTimeout: 16 * time.Second, RequestTimeout: 16 * time.Second,
ReturnRawErrors: true, ReturnRawErrors: true,
FirebaseProjectID: "simplecloudnotifier-ea7ef",
FirebaseTokenURI: "https://oauth2.googleapis.com/token", FirebaseTokenURI: "https://oauth2.googleapis.com/token",
FirebasePrivKeyID: "5bfab19fca25034e87c5b3bd1a4334499d2d1f85", FirebaseProjectID: confEnv("FB_PROJECTID"),
FirebaseClientMail: "firebase-adminsdk-42grv@simplecloudnotifier-ea7ef.iam.gserviceaccount.com", FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"),
FirebasePrivateKey: "TODO", FirebaseClientMail: confEnv("FB_CLIENTEMAIL"),
FirebasePrivateKey: confEnv("FB_PRIVATEKEY"),
} }
var configProd = Config{ var configProd = Config{
@ -91,11 +92,11 @@ var configProd = Config{
DBFile: "/data/scn.sqlite3", DBFile: "/data/scn.sqlite3",
RequestTimeout: 16 * time.Second, RequestTimeout: 16 * time.Second,
ReturnRawErrors: false, ReturnRawErrors: false,
FirebaseProjectID: "simplecloudnotifier-ea7ef",
FirebaseTokenURI: "https://oauth2.googleapis.com/token", FirebaseTokenURI: "https://oauth2.googleapis.com/token",
FirebasePrivKeyID: "5bfab19fca25034e87c5b3bd1a4334499d2d1f85", FirebaseProjectID: confEnv("FB_PROJECTID"),
FirebaseClientMail: "firebase-adminsdk-42grv@simplecloudnotifier-ea7ef.iam.gserviceaccount.com", FirebasePrivKeyID: confEnv("FB_PRIVATEKEYID"),
FirebasePrivateKey: "TODO", FirebaseClientMail: confEnv("FB_CLIENTEMAIL"),
FirebasePrivateKey: confEnv("FB_PRIVATEKEY"),
} }
var allConfig = []Config{ var allConfig = []Config{
@ -118,6 +119,14 @@ func getConfig(ns string) (Config, bool) {
return Config{}, false return Config{}, false
} }
func confEnv(key string) string {
if v, ok := os.LookupEnv(key); ok {
return v
} else {
panic(fmt.Sprintf("Missing required environment variable '%s'", key))
}
}
func init() { func init() {
ns := os.Getenv("CONF_NS") ns := os.Getenv("CONF_NS")

View File

@ -79,6 +79,10 @@ func (u User) DefaultChannel() string {
return "main" return "main"
} }
func (u User) MaxChannelNameLength() int {
return 120
}
type UserJSON struct { type UserJSON struct {
UserID UserID `json:"user_id"` UserID UserID `json:"user_id"`
Username *string `json:"username"` Username *string `json:"username"`

View File

@ -127,25 +127,19 @@
} }
}, },
"401": { "401": {
"description": "Unauthorized", "description": "The user_id was not found or the user_key is wrong",
"schema": { "schema": {
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
}, },
"403": { "403": {
"description": "Forbidden", "description": "The user has exceeded its daily quota - wait 24 hours or upgrade your account",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
},
"404": {
"description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "An internal server error occurred - try again later",
"schema": { "schema": {
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
@ -1871,25 +1865,19 @@
} }
}, },
"401": { "401": {
"description": "Unauthorized", "description": "The user_id was not found or the user_key is wrong",
"schema": { "schema": {
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
}, },
"403": { "403": {
"description": "Forbidden", "description": "The user has exceeded its daily quota - wait 24 hours or upgrade your account",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
},
"404": {
"description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "An internal server error occurred - try again later",
"schema": { "schema": {
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
@ -1999,12 +1987,6 @@
"$ref": "#/definitions/ginresp.apiError" "$ref": "#/definitions/ginresp.apiError"
} }
}, },
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/ginresp.apiError"
}
},
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {

View File

@ -514,19 +514,16 @@ paths:
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"401": "401":
description: Unauthorized description: The user_id was not found or the user_key is wrong
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"403": "403":
description: Forbidden description: The user has exceeded its daily quota - wait 24 hours or upgrade
schema: your account
$ref: '#/definitions/ginresp.apiError'
"404":
description: Not Found
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"500": "500":
description: Internal Server Error description: An internal server error occurred - try again later
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
summary: Send a new message summary: Send a new message
@ -1687,19 +1684,16 @@ paths:
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"401": "401":
description: Unauthorized description: The user_id was not found or the user_key is wrong
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"403": "403":
description: Forbidden description: The user has exceeded its daily quota - wait 24 hours or upgrade
schema: your account
$ref: '#/definitions/ginresp.apiError'
"404":
description: Not Found
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"500": "500":
description: Internal Server Error description: An internal server error occurred - try again later
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
summary: Send a new message summary: Send a new message
@ -1768,10 +1762,6 @@ paths:
description: Forbidden description: Forbidden
schema: schema:
$ref: '#/definitions/ginresp.apiError' $ref: '#/definitions/ginresp.apiError'
"404":
description: Not Found
schema:
$ref: '#/definitions/ginresp.apiError'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:

View File

@ -19,22 +19,27 @@
</div> </div>
<div id="mainpnl"> <div id="mainpnl">
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a> <a tabindex="-1" href="/documentation/swagger" class="button bordered" id="tl_linkDocs"><span class="icn-openapi"></span><span class="tl_btntxt">API Documentation</span></a>
<a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a> <a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a>
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a> <a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
<p>Get your user-id and user-key from the app and send notifications to your phone by performing a POST request against <code>https://simplecloudnotifier.blackforestbytes.com/</code></p> <p>Get your user-id and user-key from the app and send notifications to your phone by performing a POST request against <code>https://simplecloudnotifier.blackforestbytes.com/</code></p>
<pre>curl \ <pre>
--data "user_id={userid}" \ curl \
--data "user_key={userkey}" \ --data "user_id=${userid}" \
--data "title={message_title}" \ --data "user_key=${userkey}" \
--data "content={message_body}" \ --data "title=${message_title}" \
--data "priority={0|1|2}" \ --data "content=${message_body}" \
--data "msg_id={unique_message_id}" \ --data "priority=${0|1|2}" \
--data "msg_id=$(uuidgen)" \
--data "timestamp=$(date +%s)" \
--data "channel={channel_name}" \
https://scn.blackforestbytes.com/</pre> https://scn.blackforestbytes.com/</pre>
<p>The <code>content</code>, <code>priority</code> and <code>msg_id</code> parameters are optional, you can also send message with only a title and the default priority</p> <p>Most parameters are optional, you can send a message with only a title (default priority and channel will be used)</p>
<pre>curl \ <pre>
curl \
--data "user_id={userid}" \ --data "user_id={userid}" \
--data "user_key={userkey}" \ --data "user_key={userkey}" \
--data "title={message_title}" \ --data "title={message_title}" \

View File

@ -19,7 +19,8 @@
</div> </div>
<div id="mainpnl"> <div id="mainpnl">
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a> <a tabindex="-1" href="/documentation/swagger" class="button bordered" id="tl_linkDocs"><span class="icn-openapi"></span><span class="tl_btntxt">API Documentation</span></a>
<a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a> <a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a>
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a> <a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
@ -30,7 +31,7 @@
With this API you can send push notifications to your phone. With this API you can send push notifications to your phone.
</p> </p>
<p> <p>
To recieve them you will need to install the <a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier">SimpleCloudNotifier</a> app from the play store. To receive them you will need to install the <a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier">SimpleCloudNotifier</a> app from the play store.
When you open the app you can click on the account tab to see you unique <code>user_id</code> and <code>user_key</code>. When you open the app you can click on the account tab to see you unique <code>user_id</code> and <code>user_key</code>.
These two values are used to identify and authenticate your device so that send messages can be routed to your phone. These two values are used to identify and authenticate your device so that send messages can be routed to your phone.
</p> </p>
@ -45,7 +46,7 @@
<h2>Quota</h2> <h2>Quota</h2>
<div class="section"> <div class="section">
<p> <p>
By default you can send up to 100 messages per day per device. By default you can send up to 50 messages per day per device.
If you need more you can upgrade your account in the app to get 1000 messages per day, this has the additional benefit of removing ads and supporting the development of the app (and making sure I can pay the server costs). If you need more you can upgrade your account in the app to get 1000 messages per day, this has the additional benefit of removing ads and supporting the development of the app (and making sure I can pay the server costs).
</p> </p>
</div> </div>
@ -54,10 +55,10 @@
<div class="section"> <div class="section">
<p> <p>
To send a new notification you send a <code>POST</code> request to the URL <code>https://scn.blackforestbytes.com/</code>. To send a new notification you send a <code>POST</code> request to the URL <code>https://scn.blackforestbytes.com/</code>.
All Parameters can either directly be submitted as URL parameters or they can be put into the POST body. All Parameters can either directly be submitted as URL parameters or they can be put into the POST body (either multipart/form-data or JSON).
</p> </p>
<p> <p>
You <i>need</i> to supply a valid <code>user_id</code> - <code>user_key</code> pair and a <code>title</code> for your message, all other parameter are optional. You <i>need</i> to supply a valid <code>[user_id, user_key]</code> pair and a <code>title</code> for your message, all other parameter are optional.
</p> </p>
</div> </div>
@ -81,7 +82,7 @@
"quota_max":100 "quota_max":100
}</pre> }</pre>
<p> <p>
If the operation is <b>not</b> successful the API will respond with an 4xx HTTP statuscode. If the operation is <b>not</b> successful the API will respond with a 4xx or 500 HTTP statuscode.
</p> </p>
<table class="scode_table"> <table class="scode_table">
<thead> <thead>
@ -107,10 +108,6 @@
<td data-label="Statuscode">403 (Forbidden)</td> <td data-label="Statuscode">403 (Forbidden)</td>
<td data-label="Explanation">The user has exceeded its daily quota - wait 24 hours or upgrade your account</td> <td data-label="Explanation">The user has exceeded its daily quota - wait 24 hours or upgrade your account</td>
</tr> </tr>
<tr>
<td data-label="Statuscode">412 (Precondition Failed)</td>
<td data-label="Explanation">There is no device connected with this account - open the app and press the refresh button in the account tab</td>
</tr>
<tr> <tr>
<td data-label="Statuscode">500 (Internal Server Error)</td> <td data-label="Statuscode">500 (Internal Server Error)</td>
<td data-label="Explanation">There was an internal error while sending your data - try again later</td> <td data-label="Explanation">There was an internal error while sending your data - try again later</td>
@ -122,10 +119,10 @@
The <code>success</code> field is always there and in the error state you the <code>message</code> field to get a descritpion of the problem. The <code>success</code> field is always there and in the error state you the <code>message</code> field to get a descritpion of the problem.
</p> </p>
<pre class="red-code">{ <pre class="red-code">{
"success":false, "success": false,
"error":2101, "error": 2101,
"errhighlight":-1, "errhighlight": -1,
"message":"Daily quota reached (100)" "message": "Daily quota reached (100)"
}</pre> }</pre>
</div> </div>
@ -133,7 +130,7 @@
<div class="section"> <div class="section">
<p> <p>
Every message must have a title set. Every message must have a title set.
But you also (optionally) add more content, while the title has a max length of 120 characters, the conntent can be up to 10.000 characters. But you also (optionally) add more content, while the title has a max length of 120 characters, the content can be up to 10.000 characters.
You can see the whole message with title and content in the app or when clicking on the notification. You can see the whole message with title and content in the app or when clicking on the notification.
</p> </p>
<p> <p>
@ -165,6 +162,21 @@
https://scn.blackforestbytes.com/s</pre> https://scn.blackforestbytes.com/s</pre>
</div> </div>
<h2>Channels</h2>
<div class="section">
<p>
By default all messages are sent to the user default channel (typically <code>main</code>)
You can specify a different channel with the <code>channel</code> parameter, if the channel does not already exist it will be created.
Channel names are case-insensitive and can only contain letters, numbers, underscores and minuses ( <code>/[[:alnum:]\-_]+/</code> )
</p>
<pre>curl \
--data "user_id={userid}" \
--data "user_key={userkey}" \
--data "title={message_title}" \
--data "channel={my_channel}" \
https://scn.blackforestbytes.com/s</pre>
</div>
<h2>Message Uniqueness</h2> <h2>Message Uniqueness</h2>
<div class="section"> <div class="section">
<p> <p>
@ -172,7 +184,7 @@
</p> </p>
<p> <p>
To ensure that a message is only send once you can generate a unique id for your message (I would recommend a simple <code>uuidgen</code>). To ensure that a message is only send once you can generate a unique id for your message (I would recommend a simple <code>uuidgen</code>).
If you send a message with an UUID that was already used in the near past the API still returns OK, but no new message is sent. If you send a message with a UUID that was already used in the near past the API still returns OK, but no new message is sent.
</p> </p>
<p> <p>
The message_id is optional - but if you want to use it you need to supply it via the <code>msg_id</code> parameter. The message_id is optional - but if you want to use it you need to supply it via the <code>msg_id</code> parameter.
@ -208,90 +220,9 @@
<div class="section"> <div class="section">
<p> <p>
Depending on your use case it can be useful to create a bash script that handles things like resending messages if you have connection problems or waiting if there is no quota left.<br/> Depending on your use case it can be useful to create a bash script that handles things like resending messages if you have connection problems or waiting if there is no quota left.<br/>
Here is an example how such a scrippt could look like, you can put it into <code>/usr/local/sbin</code> and call it with <code>scn_send "title" "content"</code> Here is an example how such a scrippt could look like, you can put it into <code>/usr/local/sbin</code> and call it with <code>scn_send "title" "content"</code> (or with more parameters, see the script itself)
</p>
<pre style="color:#000000;" class="yellow-code"><span style="color:#3f7f59; font-weight:bold;">#!/usr/bin/env bash</span>
<span style="color:#3f7f59; ">#</span>
<span style="color:#3f7f59; "># Call with `scn_send title`</span>
<span style="color:#3f7f59; "># or `scn_send title content`</span>
<span style="color:#3f7f59; "># or `scn_send title content priority`</span>
<span style="color:#3f7f59; ">#</span>
<span style="color:#3f7f59; ">#</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -lt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"no title supplied via parameter"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#3f7f59; ">################################################################################</span>
<span style="color:#3f7f59; "># INSERT YOUR DATA HERE #</span>
<span style="color:#3f7f59; ">################################################################################</span>
user_id=999
user_key=<span style="color:#2a00ff; ">"????????????????????????????????????????????????????????????????"</span>
<span style="color:#3f7f59; ">################################################################################</span>
title=$1
content=<span style="color:#2a00ff; ">""</span>
sendtime=$(date +%s)
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
content=$2
<span style="color:#7f0055; font-weight:bold; ">fi</span>
priority=1
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 2 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
priority=$3
<span style="color:#7f0055; font-weight:bold; ">fi</span>
usr_msg_id=$(uuidgen)
<span style="color:#7f0055; font-weight:bold; ">while</span> true ; <span style="color:#7f0055; font-weight:bold; ">do</span>
curlresp=$(curl -s -o <span style="color:#3f3fbf; ">/dev/null</span> -w <span style="color:#2a00ff; ">"%{http_code}"</span> <span style="color:#2a00ff; ">\</span>
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_id</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_key</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_key</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">title</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$title</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">timestamp</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$sendtime</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">content</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$content</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">priority</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$priority</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">msg_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$usr_msg_id</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
https:<span style="color:#3f3fbf; ">/</span><span style="color:#3f3fbf; ">/scn.blackforestbytes.com/</span>)
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 200 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Successfully send"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 0
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 400 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Bad request - something went wrong"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 401 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Unauthorized - wrong </span><span style="color:#3f3fbf; ">userid/userkey</span><span style="color:#2a00ff; ">"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 403 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Quota exceeded - wait one hour before re-try"</span>
sleep 3600
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 412 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Precondition Failed - No device linked"</span>
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 500 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Internal server error - waiting for better times"</span>
sleep 60
<span style="color:#7f0055; font-weight:bold; ">fi</span>
<span style="color:#3f7f59; "># if none of the above matched we probably hav no network ...</span>
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Send failed (response code </span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">) ... try again in 5s"</span>
sleep 5
<span style="color:#7f0055; font-weight:bold; ">done</span>
</pre>
<p>
Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again.
</p> </p>
<pre style="color:#000000; margin: 0; line-height: 125%" class="yellow-code">{{template|scn_send.html}}</pre>
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,9 @@
<form id="mainpnl"> <form id="mainpnl">
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a> <a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link1"><span class="icn-google-play"></span></a>
<a tabindex="-1" href="#" class="button bordered" id="tl_link2"><span class="icn-app-store"></span></a>
<a tabindex="-1" href="/api" class="button bordered" id="tr_link">API</a> <a tabindex="-1" href="/api" class="button bordered" id="tr_link">API</a>
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a> <a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
@ -36,6 +38,11 @@
<div class="col-sm-12 col-md"><input placeholder="Key" id="ukey" class="doc" type="text" maxlength="64"></div> <div class="col-sm-12 col-md"><input placeholder="Key" id="ukey" class="doc" type="text" maxlength="64"></div>
</div> </div>
<div class="row responsive-label">
<div class="col-sm-12 col-md-3"><label for="chan" class="doc">Channel</label></div>
<div class="col-sm-12 col-md"><input placeholder="Channel Name" id="chan" class="doc" type="text" maxlength="80"></div>
</div>
<div class="row responsive-label"> <div class="row responsive-label">
<div class="col-sm-12 col-md-3"><label for="prio" class="doc">Priority</label></div> <div class="col-sm-12 col-md-3"><label for="prio" class="doc">Priority</label></div>
<div class="col-sm-12 col-md"> <div class="col-sm-12 col-md">
@ -48,13 +55,13 @@
</div> </div>
<div class="row responsive-label"> <div class="row responsive-label">
<div class="col-sm-12 col-md-3"><label for="msg" class="doc">Message Title</label></div> <div class="col-sm-12 col-md-3"><label for="tit" class="doc">Message Title</label></div>
<div class="col-sm-12 col-md"><input placeholder="Message" id="msg" class="doc" type="text" maxlength="80"></div> <div class="col-sm-12 col-md"><input placeholder="Message" id="tit" class="doc" type="text" maxlength="80"></div>
</div> </div>
<div class="row responsive-label"> <div class="row responsive-label">
<div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div> <div class="col-sm-12 col-md-3"><label for="cnt" class="doc">Message Content</label></div>
<div class="col-sm-12 col-md"><textarea id="txt" class="doc" rows="8" maxlength="2048"></textarea></div> <div class="col-sm-12 col-md"><textarea id="cnt" class="doc" rows="8" maxlength="2048"></textarea></div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -10,22 +10,24 @@ function send()
let uid = document.getElementById("uid"); let uid = document.getElementById("uid");
let key = document.getElementById("ukey"); let key = document.getElementById("ukey");
let msg = document.getElementById("msg"); let tit = document.getElementById("tit");
let txt = document.getElementById("txt"); let cnt = document.getElementById("cnt");
let pio = document.getElementById("prio"); let pio = document.getElementById("prio");
let cha = document.getElementById("chan");
uid.classList.remove('input-invalid'); uid.classList.remove('input-invalid');
key.classList.remove('input-invalid'); key.classList.remove('input-invalid');
msg.classList.remove('input-invalid'); msg.classList.remove('input-invalid');
txt.classList.remove('input-invalid'); cnt.classList.remove('input-invalid');
pio.classList.remove('input-invalid'); pio.classList.remove('input-invalid');
let data = new FormData(); let data = new FormData();
data.append('user_id', uid.value); data.append('user_id', uid.value);
data.append('user_key', key.value); data.append('user_key', key.value);
data.append('title', msg.value); if (tit.value !== '') data.append('title', tit.value);
data.append('content', txt.value); if (cnt.value !== '') data.append('content', cnt.value);
data.append('priority', pio.value); if (pio.value !== '') data.append('priority', pio.value);
if (cha.value !== '') data.append('channel', cha.value);
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.open('POST', '/send.php', true); xhr.open('POST', '/send.php', true);
@ -41,9 +43,10 @@ function send()
{ {
if (resp.errhighlight === 101) uid.classList.add('input-invalid'); if (resp.errhighlight === 101) uid.classList.add('input-invalid');
if (resp.errhighlight === 102) key.classList.add('input-invalid'); if (resp.errhighlight === 102) key.classList.add('input-invalid');
if (resp.errhighlight === 103) msg.classList.add('input-invalid'); if (resp.errhighlight === 103) tit.classList.add('input-invalid');
if (resp.errhighlight === 104) txt.classList.add('input-invalid'); if (resp.errhighlight === 104) cnt.classList.add('input-invalid');
if (resp.errhighlight === 105) pio.classList.add('input-invalid'); if (resp.errhighlight === 105) pio.classList.add('input-invalid');
if (resp.errhighlight === 106) cha.classList.add('input-invalid');
Toastify({ Toastify({
text: resp.message, text: resp.message,
@ -62,7 +65,8 @@ function send()
'&quota_remain=' + (resp.quota_max-resp.quota) + '&quota_remain=' + (resp.quota_max-resp.quota) +
'&quota_max=' + resp.quota_max + '&quota_max=' + resp.quota_max +
'&preset_user_id=' + uid.value + '&preset_user_id=' + uid.value +
'&preset_user_key=' + key.value; '&preset_user_key=' + key.value +
'&preset_channel=' + cha.value;
} }
} }
else else
@ -85,23 +89,21 @@ window.addEventListener("load", function ()
{ {
const qp = new URLSearchParams(window.location.search); const qp = new URLSearchParams(window.location.search);
const btnSend = document.getElementById("btnSend"); let btn = document.getElementById("btnSend");
const selPrio = document.getElementById("prio"); let uid = document.getElementById("uid");
const txtKey = document.getElementById("ukey"); let key = document.getElementById("ukey");
const txtUID = document.getElementById("uid"); let tit = document.getElementById("tit");
const txtTitl = document.getElementById("msg"); let cnt = document.getElementById("cnt");
const txtCont = document.getElementById("txt"); let pio = document.getElementById("prio");
let cha = document.getElementById("chan");
btnSend.onclick = function () { send(); return false; }; btn.onclick = function () { send(); return false; };
if (qp.has('preset_priority')) selPrio.selectedIndex = parseInt(qp.get("preset_priority")); if (qp.has('preset_priority')) pio.selectedIndex = parseInt(qp.get("preset_priority"));
if (qp.has('preset_user_key')) key.value = qp.get("preset_user_key");
if (qp.has('preset_user_key')) txtKey.value = qp.get("preset_user_key"); if (qp.has('preset_user_id')) uid.value = qp.get("preset_user_id");
if (qp.has('preset_title')) tit.value = qp.get("preset_title");
if (qp.has('preset_user_id')) txtUID.value = qp.get("preset_user_id"); if (qp.has('preset_content')) cnt.value = qp.get("preset_content");
if (qp.has('preset_channel')) cha.value = qp.get("preset_channel");
if (qp.has('preset_title')) txtTitl.value = qp.get("preset_title");
if (qp.has('preset_content')) txtCont.value = qp.get("preset_content");
}, false); }, false);

View File

@ -14,7 +14,10 @@ window.addEventListener("load", function ()
const preset_user_id = qp.get('preset_user_id') ?? 'ERR'; const preset_user_id = qp.get('preset_user_id') ?? 'ERR';
const preset_user_key = qp.get('preset_user_key') ?? 'ERR'; const preset_user_key = qp.get('preset_user_key') ?? 'ERR';
linkSucc.setAttribute("href", "/?preset_user_id="+preset_user_id+"&preset_user_key="+preset_user_key); let hrefBack = "/?preset_user_id="+preset_user_id+"&preset_user_key="+preset_user_key
if (qp.has('preset_channel') && qp.get('preset_channel') !== '') hrefBack += "&preset_channel="+qp.get('preset_channel')
linkSucc.setAttribute("href", hrefBack);
if (qp.get("ok") === "1") { if (qp.get("ok") === "1") {

View File

@ -20,9 +20,16 @@
<div id="mainpnl"> <div id="mainpnl">
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link1"><span class="icn-google-play"></span></a>
<a tabindex="-1" href="#" class="button bordered" id="tl_link2"><span class="icn-app-store"></span></a>
<a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a>
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
<div class="fullcenterflex"> <div class="fullcenterflex">
<a id="succ_link" class="display_none card success" href="/?preset_user_id=<?php echo isset($_GET['preset_user_id'])?$_GET['preset_user_id']:'ERR';?>&preset_user_key=<?php echo isset($_GET['preset_user_key'])?$_GET['preset_user_key']:'ERR';?>"> <a id="succ_link" class="display_none card success" href="/">
<div class="section"> <div class="section">
<h3 class="doc">Message sent</h3> <h3 class="doc">Message sent</h3>
<p class="doc">Message succesfully sent<br> <p class="doc">Message succesfully sent<br>
@ -39,10 +46,6 @@
</div> </div>
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
<a tabindex="-1" href="/" class="button bordered" id="tr_link">Send</a>
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
</div> </div>

View File

@ -0,0 +1,131 @@
<!-- https://hilite.me/ | bash | borland -->
<!-- HTML generated using hilite.me --><span style="color: #008800; font-style: italic">#!/usr/bin/env bash</span>
<span style="color: #008800; font-style: italic">#</span>
<span style="color: #008800; font-style: italic"># Wrapper around SCN ( https://scn.blackforestbytes.com/ )</span>
<span style="color: #008800; font-style: italic"># ========================================================</span>
<span style="color: #008800; font-style: italic">#</span>
<span style="color: #008800; font-style: italic"># ./scn_send [@channel] title [content] [priority]</span>
<span style="color: #008800; font-style: italic">#</span>
<span style="color: #008800; font-style: italic">#</span>
<span style="color: #008800; font-style: italic"># Call with scn_send &quot;${title}&quot;</span>
<span style="color: #008800; font-style: italic"># or scn_send &quot;${title}&quot; ${content}&quot;</span>
<span style="color: #008800; font-style: italic"># or scn_send &quot;${title}&quot; ${content}&quot; &quot;${priority:0|1|2}&quot;</span>
<span style="color: #008800; font-style: italic"># or scn_send &quot;@${channel} &quot;${title}&quot;</span>
<span style="color: #008800; font-style: italic"># or scn_send &quot;@${channel} &quot;${title}&quot; ${content}&quot;</span>
<span style="color: #008800; font-style: italic"># or scn_send &quot;@${channel} &quot;${title}&quot; ${content}&quot; &quot;${priority:0|1|2}&quot;</span>
<span style="color: #008800; font-style: italic">#</span>
<span style="color: #008800; font-style: italic">#</span>
<span style="color: #008800; font-style: italic">################################################################################</span>
<span style="color: #008800; font-style: italic"># INSERT YOUR DATA HERE #</span>
<span style="color: #008800; font-style: italic">################################################################################</span>
user_id=<span style="color: #0000FF">&quot;999&quot;</span>
user_key=<span style="color: #0000FF">&quot;??&quot;</span>
<span style="color: #008800; font-style: italic">################################################################################</span>
usage() {
echo <span style="color: #0000FF">&quot;Usage: &quot;</span>
echo <span style="color: #0000FF">&quot; scn_send [@channel] title [content] [priority]&quot;</span>
echo <span style="color: #0000FF">&quot;&quot;</span>
}
args=( <span style="color: #0000FF">&quot;$@&quot;</span> )
title=$1
content=<span style="color: #0000FF">&quot;&quot;</span>
channel=<span style="color: #0000FF">&quot;&quot;</span>
priority=1
usr_msg_id=<span style="color: #000080; font-weight: bold">$(</span>uuidgen<span style="color: #000080; font-weight: bold">)</span>
sendtime=<span style="color: #000080; font-weight: bold">$(</span>date +%s<span style="color: #000080; font-weight: bold">)</span>
<span style="color: #000080; font-weight: bold">if</span> [ <span style="color: #000080; font-weight: bold">${#</span>args[@]<span style="color: #000080; font-weight: bold">}</span> -lt 1 ]; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;[ERROR]: no title supplied via parameter&quot;</span> 1&gt;&amp;2
usage
exit 1
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold">if</span> [[ <span style="color: #0000FF">&quot;${args[0]}&quot;</span> =~ ^@.* ]]; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>channel=<span style="color: #0000FF">&quot;${args[0]}&quot;</span>
unset <span style="color: #0000FF">&quot;args[0]&quot;</span>
channel=<span style="color: #0000FF">&quot;${channel:1}&quot;</span>
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold">if</span> [ <span style="color: #000080; font-weight: bold">${#</span>args[@]<span style="color: #000080; font-weight: bold">}</span> -lt 1 ]; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;[ERROR]: no title supplied via parameter&quot;</span> 1&gt;&amp;2
usage
exit 1
<span style="color: #000080; font-weight: bold">fi</span>
title=<span style="color: #0000FF">&quot;${args[0]}&quot;</span>
content=<span style="color: #0000FF">&quot;&quot;</span>
sendtime=<span style="color: #000080; font-weight: bold">$(</span>date +%s<span style="color: #000080; font-weight: bold">)</span>
<span style="color: #000080; font-weight: bold">if</span> [ <span style="color: #000080; font-weight: bold">${#</span>args[@]<span style="color: #000080; font-weight: bold">}</span> -gt 1 ]; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>content=<span style="color: #0000FF">&quot;${args[0]}&quot;</span>
unset <span style="color: #0000FF">&quot;args[0]&quot;</span>
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold">if</span> [ <span style="color: #000080; font-weight: bold">${#</span>args[@]<span style="color: #000080; font-weight: bold">}</span> -gt 1 ]; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>priority=<span style="color: #0000FF">&quot;${args[0]}&quot;</span>
unset <span style="color: #0000FF">&quot;args[0]&quot;</span>
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold">if</span> [ <span style="color: #000080; font-weight: bold">${#</span>args[@]<span style="color: #000080; font-weight: bold">}</span> -gt 1 ]; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Too many arguments to scn_send&quot;</span> 1&gt;&amp;2
usage
exit 1
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold">while </span>true ; <span style="color: #000080; font-weight: bold">do</span>
<span style="color: #000080; font-weight: bold"> </span>curlresp=<span style="color: #000080; font-weight: bold">$(</span>curl --silent <span style="color: #0000FF">\</span>
--output /dev/null <span style="color: #0000FF">\</span>
--write-out <span style="color: #0000FF">&quot;%{http_code}&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;user_id=$user_id&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;user_key=$user_key&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;title=$title&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;timestamp=$sendtime&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;content=$content&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;priority=$priority&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;msg_id=$usr_msg_id&quot;</span> <span style="color: #0000FF">\</span>
--data <span style="color: #0000FF">&quot;channel=$channel&quot;</span> <span style="color: #0000FF">\</span>
<span style="color: #0000FF">&quot;https://scn.blackforestbytes.com/&quot;</span> <span style="color: #000080; font-weight: bold">)</span>
<span style="color: #000080; font-weight: bold">if</span> [ <span style="color: #0000FF">&quot;$curlresp&quot;</span> == 200 ] ; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Successfully send&quot;</span>
exit 0
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold"> if</span> [ <span style="color: #0000FF">&quot;$curlresp&quot;</span> == 400 ] ; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Bad request - something went wrong&quot;</span> 1&gt;&amp;2
exit 1
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold"> if</span> [ <span style="color: #0000FF">&quot;$curlresp&quot;</span> == 401 ] ; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Unauthorized - wrong userid/userkey&quot;</span> 1&gt;&amp;2
exit 1
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold"> if</span> [ <span style="color: #0000FF">&quot;$curlresp&quot;</span> == 403 ] ; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Quota exceeded - wait one hour before re-try&quot;</span> 1&gt;&amp;2
sleep 3600
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold"> if</span> [ <span style="color: #0000FF">&quot;$curlresp&quot;</span> == 412 ] ; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Precondition Failed - No device linked&quot;</span> 1&gt;&amp;2
exit 1
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #000080; font-weight: bold"> if</span> [ <span style="color: #0000FF">&quot;$curlresp&quot;</span> == 500 ] ; <span style="color: #000080; font-weight: bold">then</span>
<span style="color: #000080; font-weight: bold"> </span>echo <span style="color: #0000FF">&quot;Internal server error - waiting for better times&quot;</span> 1&gt;&amp;2
sleep 60
<span style="color: #000080; font-weight: bold">fi</span>
<span style="color: #008800; font-style: italic"># if none of the above matched we probably hav no network ...</span>
echo <span style="color: #0000FF">&quot;Send failed (response code $curlresp) ... try again in 5s&quot;</span> 1&gt;&amp;2
sleep 5
<span style="color: #000080; font-weight: bold">done</span>