added template for new golang backend
This commit is contained in:
parent
bd11d7973c
commit
0e58a5c5f0
176
server/.gitignore
vendored
Normal file
176
server/.gitignore
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
|
||||
|
||||
_build
|
||||
.run-data
|
||||
|
||||
DOCKER_GIT_INFO
|
||||
|
||||
|
||||
##############
|
||||
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/goland,macos,linux
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=goland,macos,linux
|
||||
|
||||
### GoLand ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### GoLand Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/goland,macos,linux
|
8
server/.idea/.gitignore
generated
vendored
Normal file
8
server/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
11
server/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
11
server/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,11 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
8
server/.idea/modules.xml
generated
Normal file
8
server/.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/server.iml" filepath="$PROJECT_DIR$/.idea/server.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
9
server/.idea/server.iml
generated
Normal file
9
server/.idea/server.iml
generated
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
server/.idea/vcs.xml
generated
Normal file
6
server/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
65
server/Makefile
Normal file
65
server/Makefile
Normal file
@ -0,0 +1,65 @@
|
||||
DOCKER_REPO=registry.blackforestbytes.com
|
||||
DOCKER_NAME=scn_server
|
||||
PORT=9090
|
||||
|
||||
NAMESPACE=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
HASH=$(shell git rev-parse HEAD)
|
||||
|
||||
build:
|
||||
rm -f ./_build/scn_backend
|
||||
go build -v -o _build/scn_backend ./cmd/scnserver
|
||||
|
||||
run: build
|
||||
mkdir -p .run-data
|
||||
_build/scn_backend
|
||||
|
||||
build-docker:
|
||||
[ ! -f "DOCKER_GIT_INFO" ] || rm DOCKER_GIT_INFO
|
||||
git rev-parse --abbrev-ref HEAD >> DOCKER_GIT_INFO
|
||||
git rev-parse HEAD >> DOCKER_GIT_INFO
|
||||
git log -1 --format=%cd --date=iso >> DOCKER_GIT_INFO
|
||||
git config --get remote.origin.url >> DOCKER_GIT_INFO
|
||||
docker build \
|
||||
-t "$(DOCKER_NAME):$(HASH)" \
|
||||
-t "$(DOCKER_NAME):$(NAMESPACE)-latest" \
|
||||
-t "$(DOCKER_NAME):latest" \
|
||||
-t "$(DOCKER_REPO)/$(DOCKER_NAME):$(HASH)" \
|
||||
-t "$(DOCKER_REPO)/$(DOCKER_NAME):$(NAMESPACE)-latest" \
|
||||
-t "$(DOCKER_REPO)/$(DOCKER_NAME):latest" \
|
||||
.
|
||||
|
||||
build-swagger:
|
||||
which swag || go install github.com/swaggo/swag/cmd/swag@latest
|
||||
swag init -generalInfo api/router.go --output ./swagger/ --outputTypes "json,yaml"
|
||||
|
||||
run-docker-local:
|
||||
mkdir -p .run-data
|
||||
docker run --rm \
|
||||
--init \
|
||||
--env "CONF_NS=local" \
|
||||
--volume "$(shell pwd)/.run-data/docker-local:/data" \
|
||||
--network host \
|
||||
$(DOCKER_NAME):latest
|
||||
|
||||
inspect-docker:
|
||||
mkdir -p .run-data
|
||||
docker run -ti \
|
||||
--rm \
|
||||
--volume "$(shell pwd)/.run-data/docker-inspect:/data" \
|
||||
$(DOCKER_NAME):latest \
|
||||
bash
|
||||
|
||||
push-docker:
|
||||
docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):$(HASH)"
|
||||
docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):$(NAMESPACE)-latest"
|
||||
docker image push "$(DOCKER_REPO)/$(DOCKER_NAME):latest"
|
||||
|
||||
clean:
|
||||
rm -rf _build/*
|
||||
rm -rf .run-data/*
|
||||
git clean -fdx
|
||||
go clean
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
swag fmt
|
30
server/api/apierr/enums.go
Normal file
30
server/api/apierr/enums.go
Normal file
@ -0,0 +1,30 @@
|
||||
package apierr
|
||||
|
||||
type APIError int
|
||||
|
||||
const (
|
||||
NO_ERROR APIError = 0000
|
||||
|
||||
MISSING_UID APIError = 1101
|
||||
MISSING_TOK APIError = 1102
|
||||
MISSING_TITLE APIError = 1103
|
||||
INVALID_PRIO APIError = 1104
|
||||
REQ_METHOD APIError = 1105
|
||||
|
||||
NO_TITLE APIError = 1201
|
||||
TITLE_TOO_LONG APIError = 1202
|
||||
CONTENT_TOO_LONG APIError = 1203
|
||||
USR_MSG_ID_TOO_LONG APIError = 1204
|
||||
TIMESTAMP_OUT_OF_RANGE APIError = 1205
|
||||
|
||||
USER_NOT_FOUND APIError = 1301
|
||||
USER_AUTH_FAILED APIError = 1302
|
||||
|
||||
NO_DEVICE_LINKED APIError = 1401
|
||||
|
||||
QUOTA_REACHED APIError = 2101
|
||||
|
||||
FIREBASE_COM_FAILED APIError = 9901
|
||||
FIREBASE_COM_ERRORED APIError = 9902
|
||||
INTERNAL_EXCEPTION APIError = 9903
|
||||
)
|
98
server/api/handler/common.go
Normal file
98
server/api/handler/common.go
Normal file
@ -0,0 +1,98 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"blackforestbytes.com/simplecloudnotifier/common/ginresp"
|
||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||
"bytes"
|
||||
"github.com/gin-gonic/gin"
|
||||
sqlite3 "github.com/mattn/go-sqlite3"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CommonHandler struct {
|
||||
app *logic.Application
|
||||
}
|
||||
|
||||
func NewCommonHandler(app *logic.Application) CommonHandler {
|
||||
return CommonHandler{
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
type pingResponse struct {
|
||||
Message string `json:"message"`
|
||||
Info pingResponseInfo `json:"info"`
|
||||
}
|
||||
type pingResponseInfo struct {
|
||||
Method string `json:"method"`
|
||||
Request string `json:"request"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
URI string `json:"uri"`
|
||||
Address string `json:"addr"`
|
||||
}
|
||||
|
||||
// Ping swaggerdoc
|
||||
//
|
||||
// @Success 200 {object} pingResponse
|
||||
// @Failure 500 {object} ginresp.errBody
|
||||
// @Router /ping [get]
|
||||
// @Router /ping [post]
|
||||
// @Router /ping [put]
|
||||
// @Router /ping [delete]
|
||||
// @Router /ping [patch]
|
||||
func (h CommonHandler) Ping(g *gin.Context) ginresp.HTTPResponse {
|
||||
buf := new(bytes.Buffer)
|
||||
_, _ = buf.ReadFrom(g.Request.Body)
|
||||
resuestBody := buf.String()
|
||||
|
||||
return ginresp.JSON(http.StatusOK, pingResponse{
|
||||
Message: "Pong",
|
||||
Info: pingResponseInfo{
|
||||
Method: g.Request.Method,
|
||||
Request: resuestBody,
|
||||
Headers: g.Request.Header,
|
||||
URI: g.Request.RequestURI,
|
||||
Address: g.Request.RemoteAddr,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DatabaseTest swaggerdoc
|
||||
//
|
||||
// @Success 200 {object} handler.DatabaseTest.response
|
||||
// @Failure 500 {object} ginresp.errBody
|
||||
// @Router /db-test [get]
|
||||
func (h CommonHandler) DatabaseTest(g *gin.Context) ginresp.HTTPResponse {
|
||||
type response struct {
|
||||
Success bool `json:"success"`
|
||||
LibVersion string `json:"libVersion"`
|
||||
LibVersionNumber int `json:"libVersionNumber"`
|
||||
SourceID string `json:"sourceID"`
|
||||
}
|
||||
|
||||
libVersion, libVersionNumber, sourceID := sqlite3.Version()
|
||||
|
||||
err := h.app.Database.Ping()
|
||||
if err != nil {
|
||||
return ginresp.InternalError(err)
|
||||
}
|
||||
|
||||
return ginresp.JSON(http.StatusOK, response{
|
||||
Success: true,
|
||||
LibVersion: libVersion,
|
||||
LibVersionNumber: libVersionNumber,
|
||||
SourceID: sourceID,
|
||||
})
|
||||
}
|
||||
|
||||
// Health swaggerdoc
|
||||
//
|
||||
// @Success 200 {object} handler.Health.response
|
||||
// @Failure 500 {object} ginresp.errBody
|
||||
// @Router /health [get]
|
||||
func (h CommonHandler) Health(*gin.Context) ginresp.HTTPResponse {
|
||||
type response struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
return ginresp.JSON(http.StatusOK, response{Status: "ok"})
|
||||
}
|
248
server/api/handler/compat.go
Normal file
248
server/api/handler/compat.go
Normal file
@ -0,0 +1,248 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||
"blackforestbytes.com/simplecloudnotifier/api/models"
|
||||
"blackforestbytes.com/simplecloudnotifier/common/ginresp"
|
||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CompatHandler struct {
|
||||
app *logic.Application
|
||||
}
|
||||
|
||||
func NewCompatHandler(app *logic.Application) CompatHandler {
|
||||
return CompatHandler{
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
// Register swaggerdoc
|
||||
//
|
||||
// @Summary Register a new account
|
||||
// @Param fcm_token query string true "the (android) fcm token"
|
||||
// @Param pro query string true "if the user is a paid account" Enums(true, false)
|
||||
// @Param pro_token query string true "the (android) IAP token"
|
||||
// @Success 200 {object} handler.Register.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /register.php [get]
|
||||
func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
FCMToken string `form:"fcm_token"`
|
||||
Pro string `form:"pro"`
|
||||
ProToken string `form:"pro_token"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
UserID string `json:"user_id"`
|
||||
UserKey string `json:"user_key"`
|
||||
QuotaUsed string `json:"quota"`
|
||||
QuotaMax string `json:"quota_max"`
|
||||
IsPro string `json:"is_pro"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Info swaggerdoc
|
||||
//
|
||||
// @Summary Get information about the current user
|
||||
// @Param user_id query string true "the user_id"
|
||||
// @Param user_key query string true "the user_key"
|
||||
// @Success 200 {object} handler.Info.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /info.php [get]
|
||||
func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
UserID string `form:"user_id"`
|
||||
UserKey string `form:"user_key"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
UserID string `json:"user_id"`
|
||||
UserKey string `json:"user_key"`
|
||||
QuotaUsed string `json:"quota"`
|
||||
QuotaMax string `json:"quota_max"`
|
||||
IsPro string `json:"is_pro"`
|
||||
FCMSet bool `json:"fcm_token_set"`
|
||||
UnackCount int `json:"unack_count"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Ack swaggerdoc
|
||||
//
|
||||
// @Summary Acknowledge that a message was received
|
||||
// @Param user_id query string true "the user_id"
|
||||
// @Param user_key query string true "the user_key"
|
||||
// @Param scn_msg_id query string true "the message id"
|
||||
// @Success 200 {object} handler.Ack.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /ack.php [get]
|
||||
func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
UserID string `form:"user_id"`
|
||||
UserKey string `form:"user_key"`
|
||||
MessageID string `form:"scn_msg_id"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
PrevAckValue int `json:"prev_ack"`
|
||||
NewAckValue int `json:"new_ack"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Requery swaggerdoc
|
||||
//
|
||||
// @Summary Return all not-acknowledged messages
|
||||
// @Param user_id query string true "the user_id"
|
||||
// @Param user_key query string true "the user_key"
|
||||
// @Success 200 {object} handler.Requery.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /requery.php [get]
|
||||
func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
UserID string `form:"user_id"`
|
||||
UserKey string `form:"user_key"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Count int `json:"count"`
|
||||
Data []models.CompatMessage `json:"data"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Update swaggerdoc
|
||||
//
|
||||
// @Summary Set the fcm-token (android)
|
||||
// @Param user_id query string true "the user_id"
|
||||
// @Param user_key query string true "the user_key"
|
||||
// @Param fcm_token query string true "the (android) fcm token"
|
||||
// @Success 200 {object} handler.Update.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /update.php [get]
|
||||
func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
UserID string `form:"user_id"`
|
||||
UserKey string `form:"user_key"`
|
||||
FCMToken string `form:"fcm_token"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
UserID string `json:"user_id"`
|
||||
UserKey string `json:"user_key"`
|
||||
QuotaUsed string `json:"quota"`
|
||||
QuotaMax string `json:"quota_max"`
|
||||
IsPro string `json:"is_pro"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Expand swaggerdoc
|
||||
//
|
||||
// @Summary Get a whole (potentially truncated) message
|
||||
// @Success 200 {object} handler.Expand.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /expand.php [get]
|
||||
func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
UserID string `form:"user_id"`
|
||||
UserKey string `form:"user_key"`
|
||||
MessageID string `form:"scn_msg_id"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data models.ShortCompatMessage `json:"data"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Upgrade swaggerdoc
|
||||
//
|
||||
// @Summary Upgrade a free account to a paid account
|
||||
// @Param user_id query string true "the user_id"
|
||||
// @Param user_key query string true "the user_key"
|
||||
// @Param pro query string true "if the user is a paid account" Enums(true, false)
|
||||
// @Param pro_token query string true "the (android) IAP token"
|
||||
// @Success 200 {object} handler.Upgrade.response
|
||||
// @Failure 500 {object} ginresp.internAPIError
|
||||
// @Router /upgrade.php [get]
|
||||
func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
UserID string `form:"user_id"`
|
||||
UserKey string `form:"user_key"`
|
||||
Pro string `form:"pro"`
|
||||
ProToken string `form:"pro_token"`
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data models.ShortCompatMessage `json:"data"`
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.NotImplemented(0)
|
||||
}
|
||||
|
||||
// Send swaggerdoc
|
||||
//
|
||||
// @Summary Send a message
|
||||
// @Description all aeguments can either be supplied in the query or in the json body
|
||||
// @Param user_id query string true "the user_id"
|
||||
// @Param user_key query string true "the user_key"
|
||||
// @Param title query string true "The message title"
|
||||
// @Param content query string false "The message content"
|
||||
// @Param priority query string false "The message priority" Enum(0, 1, 2)
|
||||
// @Param msg_id query string false "The message idempotency id"
|
||||
// @Param timestamp query string false "The message timestamp"
|
||||
// @Param user_id body string true "the user_id"
|
||||
// @Param user_key body string true "the user_key"
|
||||
// @Param title body string true "The message title"
|
||||
// @Param content body string false "The message content"
|
||||
// @Param priority body string false "The message priority" Enum(0, 1, 2)
|
||||
// @Param msg_id body string false "The message idempotency id"
|
||||
// @Param timestamp body string false "The message timestamp"
|
||||
// @Success 200 {object} handler.Send.response
|
||||
// @Failure 500 {object} ginresp.sendAPIError
|
||||
// @Router /send.php [post]
|
||||
func (h CompatHandler) Send(g *gin.Context) ginresp.HTTPResponse {
|
||||
type query struct {
|
||||
//TODO
|
||||
}
|
||||
type response struct {
|
||||
Success string `json:"success"`
|
||||
Message string `json:"message"`
|
||||
//TODO
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
return ginresp.SendAPIError(apierr.INTERNAL_EXCEPTION, -1, "NotImplemented")
|
||||
}
|
20
server/api/models/compat.go
Normal file
20
server/api/models/compat.go
Normal file
@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
type CompatMessage struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Priority int `json:"priority"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UserMessageID string `json:"usr_msg_id"`
|
||||
SCNMessageID string `json:"scn_msg_id"`
|
||||
}
|
||||
|
||||
type ShortCompatMessage struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
Trimmed bool `json:"trimmed"`
|
||||
Priority int `json:"priority"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UserMessageID string `json:"usr_msg_id"`
|
||||
SCNMessageID string `json:"scn_msg_id"`
|
||||
}
|
52
server/api/router.go
Normal file
52
server/api/router.go
Normal file
@ -0,0 +1,52 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"blackforestbytes.com/simplecloudnotifier/api/handler"
|
||||
"blackforestbytes.com/simplecloudnotifier/common/ginext"
|
||||
"blackforestbytes.com/simplecloudnotifier/common/ginresp"
|
||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||
"blackforestbytes.com/simplecloudnotifier/swagger"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Router struct {
|
||||
app *logic.Application
|
||||
|
||||
commonHandler handler.CommonHandler
|
||||
compatHandler handler.CompatHandler
|
||||
}
|
||||
|
||||
func NewRouter(app *logic.Application) *Router {
|
||||
return &Router{
|
||||
app: app,
|
||||
|
||||
commonHandler: handler.NewCommonHandler(app),
|
||||
compatHandler: handler.NewCompatHandler(app),
|
||||
}
|
||||
}
|
||||
|
||||
// Init swaggerdocs
|
||||
// @title SimpleCloudNotifier API
|
||||
// @version 2.0
|
||||
// @description API for SCN
|
||||
// @host scn.blackforestbytes.com
|
||||
// @BasePath /api/
|
||||
func (r *Router) Init(e *gin.Engine) {
|
||||
|
||||
e.Any("/ping", ginresp.Wrap(r.commonHandler.Ping))
|
||||
e.POST("/db-test", ginresp.Wrap(r.commonHandler.DatabaseTest))
|
||||
e.GET("/health", ginresp.Wrap(r.commonHandler.Health))
|
||||
|
||||
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))
|
||||
|
||||
e.POST("/send.php", ginresp.Wrap(r.compatHandler.Send))
|
||||
e.GET("/register.php", ginresp.Wrap(r.compatHandler.Register))
|
||||
e.GET("/info.php", ginresp.Wrap(r.compatHandler.Info))
|
||||
e.GET("/ack.php", ginresp.Wrap(r.compatHandler.Ack))
|
||||
e.GET("/requery.php", ginresp.Wrap(r.compatHandler.Requery))
|
||||
e.GET("/update.php", ginresp.Wrap(r.compatHandler.Update))
|
||||
e.GET("/expand.php", ginresp.Wrap(r.compatHandler.Expand))
|
||||
e.GET("/upgrade.php", ginresp.Wrap(r.compatHandler.Upgrade))
|
||||
}
|
37
server/cmd/scnserver/main.go
Normal file
37
server/cmd/scnserver/main.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
scn "blackforestbytes.com/simplecloudnotifier"
|
||||
"blackforestbytes.com/simplecloudnotifier/api"
|
||||
"blackforestbytes.com/simplecloudnotifier/common"
|
||||
"blackforestbytes.com/simplecloudnotifier/common/ginext"
|
||||
"blackforestbytes.com/simplecloudnotifier/db"
|
||||
"blackforestbytes.com/simplecloudnotifier/logic"
|
||||
"fmt"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var conf = scn.Conf
|
||||
|
||||
func main() {
|
||||
common.Init(conf)
|
||||
|
||||
log.Info().Msg(fmt.Sprintf("Starting with config-namespace <%s>", conf.Namespace))
|
||||
|
||||
sqlite, err := db.NewDatabase(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
app := logic.NewApp(sqlite)
|
||||
|
||||
ginengine := ginext.NewEngine(conf)
|
||||
|
||||
router := api.NewRouter(app)
|
||||
|
||||
app.Init(conf, ginengine)
|
||||
|
||||
router.Init(ginengine)
|
||||
|
||||
app.Run()
|
||||
}
|
21
server/common/ginext/cors.go
Normal file
21
server/common/ginext/cors.go
Normal file
@ -0,0 +1,21 @@
|
||||
package ginext
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func CorsMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusOK)
|
||||
} else {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
}
|
21
server/common/ginext/gin.go
Normal file
21
server/common/ginext/gin.go
Normal file
@ -0,0 +1,21 @@
|
||||
package ginext
|
||||
|
||||
import (
|
||||
scn "blackforestbytes.com/simplecloudnotifier"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewEngine(cfg scn.Config) *gin.Engine {
|
||||
engine := gin.New()
|
||||
|
||||
engine.RedirectFixedPath = false
|
||||
engine.RedirectTrailingSlash = false
|
||||
|
||||
engine.Use(CorsMiddleware())
|
||||
|
||||
if cfg.GinDebug {
|
||||
engine.Use(gin.Logger())
|
||||
}
|
||||
|
||||
return engine
|
||||
}
|
24
server/common/ginext/handler.go
Normal file
24
server/common/ginext/handler.go
Normal file
@ -0,0 +1,24 @@
|
||||
package ginext
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func RedirectFound(newuri string) gin.HandlerFunc {
|
||||
return func(g *gin.Context) {
|
||||
g.Redirect(http.StatusFound, newuri)
|
||||
}
|
||||
}
|
||||
|
||||
func RedirectTemporary(newuri string) gin.HandlerFunc {
|
||||
return func(g *gin.Context) {
|
||||
g.Redirect(http.StatusTemporaryRedirect, newuri)
|
||||
}
|
||||
}
|
||||
|
||||
func RedirectPermanent(newuri string) gin.HandlerFunc {
|
||||
return func(g *gin.Context) {
|
||||
g.Redirect(http.StatusPermanentRedirect, newuri)
|
||||
}
|
||||
}
|
14
server/common/ginresp/apiError.go
Normal file
14
server/common/ginresp/apiError.go
Normal file
@ -0,0 +1,14 @@
|
||||
package ginresp
|
||||
|
||||
type sendAPIError struct {
|
||||
Success bool `json:"success"`
|
||||
Error int `json:"error"`
|
||||
ErrorHighlight int `json:"errhighlight"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type internAPIError struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorID int `json:"errid,omitempty"`
|
||||
Message string `json:"message"`
|
||||
}
|
6
server/common/ginresp/error.go
Normal file
6
server/common/ginresp/error.go
Normal file
@ -0,0 +1,6 @@
|
||||
package ginresp
|
||||
|
||||
type errBody struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
85
server/common/ginresp/resp.go
Normal file
85
server/common/ginresp/resp.go
Normal file
@ -0,0 +1,85 @@
|
||||
package ginresp
|
||||
|
||||
import (
|
||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HTTPResponse interface {
|
||||
Write(context *gin.Context)
|
||||
}
|
||||
|
||||
type jsonHTTPResponse struct {
|
||||
statusCode int
|
||||
data any
|
||||
}
|
||||
|
||||
func (j jsonHTTPResponse) Write(g *gin.Context) {
|
||||
g.JSON(j.statusCode, j.data)
|
||||
}
|
||||
|
||||
type emptyHTTPResponse struct {
|
||||
statusCode int
|
||||
data any
|
||||
}
|
||||
|
||||
func (j emptyHTTPResponse) Write(g *gin.Context) {
|
||||
g.Status(j.statusCode)
|
||||
}
|
||||
|
||||
type textHTTPResponse struct {
|
||||
statusCode int
|
||||
data string
|
||||
}
|
||||
|
||||
func (j textHTTPResponse) Write(g *gin.Context) {
|
||||
g.String(j.statusCode, "%s", j.data)
|
||||
}
|
||||
|
||||
type dataHTTPResponse struct {
|
||||
statusCode int
|
||||
data []byte
|
||||
contentType string
|
||||
}
|
||||
|
||||
func (j dataHTTPResponse) Write(g *gin.Context) {
|
||||
g.Data(j.statusCode, j.contentType, j.data)
|
||||
}
|
||||
|
||||
type errHTTPResponse struct {
|
||||
statusCode int
|
||||
data any
|
||||
}
|
||||
|
||||
func (j errHTTPResponse) Write(g *gin.Context) {
|
||||
g.JSON(j.statusCode, j.data)
|
||||
}
|
||||
|
||||
func Status(sc int) HTTPResponse {
|
||||
return &emptyHTTPResponse{statusCode: sc}
|
||||
}
|
||||
|
||||
func JSON(sc int, data any) HTTPResponse {
|
||||
return &jsonHTTPResponse{statusCode: sc, data: data}
|
||||
}
|
||||
|
||||
func Data(sc int, contentType string, data []byte) HTTPResponse {
|
||||
return &dataHTTPResponse{statusCode: sc, contentType: contentType, data: data}
|
||||
}
|
||||
|
||||
func Text(sc int, data string) HTTPResponse {
|
||||
return &textHTTPResponse{statusCode: sc, data: data}
|
||||
}
|
||||
|
||||
func InternalError(e error) HTTPResponse {
|
||||
return &errHTTPResponse{statusCode: http.StatusInternalServerError, data: errBody{Success: false, Message: e.Error()}}
|
||||
}
|
||||
|
||||
func NotImplemented(errid int) HTTPResponse {
|
||||
return &errHTTPResponse{statusCode: http.StatusInternalServerError, data: internAPIError{Success: false, ErrorID: errid, Message: "NotImplemented"}}
|
||||
}
|
||||
|
||||
func SendAPIError(errorid apierr.APIError, highlight int, msg string) HTTPResponse {
|
||||
return &errHTTPResponse{statusCode: http.StatusInternalServerError, data: sendAPIError{Success: false, Error: int(errorid), ErrorHighlight: highlight, Message: msg}}
|
||||
}
|
21
server/common/ginresp/wrapper.go
Normal file
21
server/common/ginresp/wrapper.go
Normal file
@ -0,0 +1,21 @@
|
||||
package ginresp
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type WHandlerFunc func(*gin.Context) HTTPResponse
|
||||
|
||||
func Wrap(fn WHandlerFunc) gin.HandlerFunc {
|
||||
|
||||
return func(context *gin.Context) {
|
||||
|
||||
wrap := fn(context)
|
||||
|
||||
if context.Writer.Written() {
|
||||
panic("Writing in WrapperFunc is not supported")
|
||||
}
|
||||
|
||||
wrap.Write(context)
|
||||
|
||||
}
|
||||
|
||||
}
|
35
server/common/init.go
Normal file
35
server/common/init.go
Normal file
@ -0,0 +1,35 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
scn "blackforestbytes.com/simplecloudnotifier"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Init(cfg scn.Config) {
|
||||
cw := zerolog.ConsoleWriter{
|
||||
Out: os.Stdout,
|
||||
TimeFormat: "2006-01-02 15:04:05 Z07:00",
|
||||
}
|
||||
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
multi := zerolog.MultiLevelWriter(cw)
|
||||
logger := zerolog.New(multi).With().
|
||||
Timestamp().
|
||||
Caller().
|
||||
Logger()
|
||||
|
||||
log.Logger = logger
|
||||
|
||||
if cfg.GinDebug {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
} else {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
}
|
||||
|
||||
log.Debug().Msg("Initialized")
|
||||
}
|
78
server/config.go
Normal file
78
server/config.go
Normal file
@ -0,0 +1,78 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Namespace string
|
||||
GinDebug bool
|
||||
ServerIP string
|
||||
ServerPort string
|
||||
DBFile string
|
||||
}
|
||||
|
||||
var Conf Config
|
||||
|
||||
var configLoc = Config{
|
||||
Namespace: "local",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: "8080",
|
||||
DBFile: ".run-data/db.sqlite3",
|
||||
}
|
||||
|
||||
var configDev = Config{
|
||||
Namespace: "develop",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: "80",
|
||||
DBFile: "/data/scn.sqlite3",
|
||||
}
|
||||
|
||||
var configStag = Config{
|
||||
Namespace: "staging",
|
||||
GinDebug: true,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: "80",
|
||||
DBFile: "/data/scn.sqlite3",
|
||||
}
|
||||
|
||||
var configProd = Config{
|
||||
Namespace: "production",
|
||||
GinDebug: false,
|
||||
ServerIP: "0.0.0.0",
|
||||
ServerPort: "80",
|
||||
DBFile: "/data/scn.sqlite3",
|
||||
}
|
||||
|
||||
var allConfig = []Config{
|
||||
configLoc,
|
||||
configDev,
|
||||
configStag,
|
||||
configProd,
|
||||
}
|
||||
|
||||
func getConfig(ns string) (Config, bool) {
|
||||
if ns == "" {
|
||||
return configLoc, true
|
||||
}
|
||||
for _, c := range allConfig {
|
||||
if c.Namespace == ns {
|
||||
return c, true
|
||||
}
|
||||
}
|
||||
return Config{}, false
|
||||
}
|
||||
|
||||
func init() {
|
||||
ns := os.Getenv("CONF_NS")
|
||||
|
||||
cfg, ok := getConfig(ns)
|
||||
if !ok {
|
||||
log.Fatal().Str("ns", ns).Msg("Unknown config-namespace")
|
||||
}
|
||||
|
||||
Conf = cfg
|
||||
}
|
11
server/db/database.go
Normal file
11
server/db/database.go
Normal file
@ -0,0 +1,11 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
scn "blackforestbytes.com/simplecloudnotifier"
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func NewDatabase(conf scn.Config) (*sql.DB, error) {
|
||||
return sql.Open("sqlite3", conf.DBFile)
|
||||
}
|
50
server/docs/docs.go
Normal file
50
server/docs/docs.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Package docs GENERATED BY SWAG; DO NOT EDIT
|
||||
// This file was generated by swaggo/swag
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/ping": {
|
||||
"get": {
|
||||
"description": "asdf",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "Test request",
|
||||
"operationId": "ping",
|
||||
"responses": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "",
|
||||
Host: "",
|
||||
BasePath: "",
|
||||
Schemes: []string{},
|
||||
Title: "",
|
||||
Description: "",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
22
server/docs/swagger.json
Normal file
22
server/docs/swagger.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"contact": {}
|
||||
},
|
||||
"paths": {
|
||||
"/ping": {
|
||||
"get": {
|
||||
"description": "asdf",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "Test request",
|
||||
"operationId": "ping",
|
||||
"responses": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
server/docs/swagger.yaml
Normal file
14
server/docs/swagger.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
info:
|
||||
contact: {}
|
||||
paths:
|
||||
/ping:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: asdf
|
||||
operationId: ping
|
||||
produces:
|
||||
- application/json
|
||||
responses: {}
|
||||
summary: Test request
|
||||
swagger: "2.0"
|
42
server/go.mod
Normal file
42
server/go.mod
Normal file
@ -0,0 +1,42 @@
|
||||
module blackforestbytes.com/simplecloudnotifier
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/swaggo/swag v1.8.7
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
133
server/go.sum
Normal file
133
server/go.sum
Normal file
@ -0,0 +1,133 @@
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
|
||||
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
|
||||
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
63
server/logic/application.go
Normal file
63
server/logic/application.go
Normal file
@ -0,0 +1,63 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
scn "blackforestbytes.com/simplecloudnotifier"
|
||||
"context"
|
||||
"database/sql"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/rs/zerolog/log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
Config scn.Config
|
||||
Gin *gin.Engine
|
||||
Database *sql.DB
|
||||
}
|
||||
|
||||
func NewApp(db *sql.DB) *Application {
|
||||
return &Application{Database: db}
|
||||
}
|
||||
|
||||
func (app *Application) Init(cfg scn.Config, g *gin.Engine) {
|
||||
app.Config = cfg
|
||||
app.Gin = g
|
||||
}
|
||||
|
||||
func (app *Application) Run() {
|
||||
httpserver := &http.Server{
|
||||
Addr: net.JoinHostPort(app.Config.ServerIP, app.Config.ServerPort),
|
||||
Handler: app.Gin,
|
||||
}
|
||||
|
||||
errChan := make(chan error)
|
||||
|
||||
go func() {
|
||||
log.Info().Str("address", httpserver.Addr).Msg("HTTP-Server started")
|
||||
errChan <- httpserver.ListenAndServe()
|
||||
}()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||
select {
|
||||
case <-stop:
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
log.Info().Msg("Stopping HTTP-Server")
|
||||
err := httpserver.Shutdown(ctx)
|
||||
if err != nil {
|
||||
log.Info().Err(err).Msg("Error while stopping the http-server")
|
||||
return
|
||||
}
|
||||
log.Info().Msg("Stopped HTTP-Server")
|
||||
|
||||
case err := <-errChan:
|
||||
log.Error().Err(err).Msg("HTTP-Server failed")
|
||||
}
|
||||
|
||||
}
|
27
server/swagger/index.html
Normal file
27
server/swagger/index.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>API Documentation</title>
|
||||
<link rel="stylesheet" href="swagger-ui.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="swagger-ui-bundle.js" crossorigin></script>
|
||||
<script src="swagger-ui-standalone-preset.js" crossorigin></script>
|
||||
<script>
|
||||
window.onload = () => {
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: './swagger.json',
|
||||
dom_id: '#swagger-ui',
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
3
server/swagger/swagger-ui-bundle.js
Normal file
3
server/swagger/swagger-ui-bundle.js
Normal file
File diff suppressed because one or more lines are too long
3
server/swagger/swagger-ui-standalone-preset.js
Normal file
3
server/swagger/swagger-ui-standalone-preset.js
Normal file
File diff suppressed because one or more lines are too long
4
server/swagger/swagger-ui.css
Normal file
4
server/swagger/swagger-ui.css
Normal file
File diff suppressed because one or more lines are too long
68
server/swagger/swagger.go
Normal file
68
server/swagger/swagger.go
Normal file
@ -0,0 +1,68 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"blackforestbytes.com/simplecloudnotifier/common/ginresp"
|
||||
"embed"
|
||||
_ "embed"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed index.html
|
||||
//go:embed swagger.json
|
||||
//go:embed swagger.yaml
|
||||
//go:embed swagger-ui-bundle.js
|
||||
//go:embed swagger-ui.css
|
||||
//go:embed swagger-ui-standalone-preset.js
|
||||
var assets embed.FS
|
||||
|
||||
func getAsset(fn string) ([]byte, string, bool) {
|
||||
data, err := assets.ReadFile(fn)
|
||||
if err != nil {
|
||||
return nil, "", false
|
||||
}
|
||||
|
||||
mime := "text/plain"
|
||||
|
||||
lowerFN := strings.ToLower(fn)
|
||||
if strings.HasSuffix(lowerFN, ".html") || strings.HasSuffix(lowerFN, ".htm") {
|
||||
mime = "text/html"
|
||||
} else if strings.HasSuffix(lowerFN, ".css") {
|
||||
mime = "text/css"
|
||||
} else if strings.HasSuffix(lowerFN, ".js") {
|
||||
mime = "text/javascript"
|
||||
} else if strings.HasSuffix(lowerFN, ".json") {
|
||||
mime = "application/json"
|
||||
} else if strings.HasSuffix(lowerFN, ".jpeg") || strings.HasSuffix(lowerFN, ".jpg") {
|
||||
mime = "image/jpeg"
|
||||
} else if strings.HasSuffix(lowerFN, ".png") {
|
||||
mime = "image/png"
|
||||
} else if strings.HasSuffix(lowerFN, ".svg") {
|
||||
mime = "image/svg+xml"
|
||||
}
|
||||
|
||||
return data, mime, true
|
||||
}
|
||||
|
||||
func Handle(g *gin.Context) ginresp.HTTPResponse {
|
||||
type uri struct {
|
||||
Filename string `uri:"fn"`
|
||||
}
|
||||
|
||||
var u uri
|
||||
if err := g.ShouldBindUri(&u); err != nil {
|
||||
return ginresp.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
if u.Filename == "" {
|
||||
index, _, _ := getAsset("index.html")
|
||||
return ginresp.Data(http.StatusOK, "text/html", index)
|
||||
}
|
||||
|
||||
if data, mime, ok := getAsset(u.Filename); ok {
|
||||
return ginresp.Data(http.StatusOK, mime, data)
|
||||
}
|
||||
|
||||
return ginresp.JSON(http.StatusNotFound, gin.H{"error": "AssetNotFound", "filename": u.Filename})
|
||||
}
|
844
server/swagger/swagger.json
Normal file
844
server/swagger/swagger.json
Normal file
@ -0,0 +1,844 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "API for SCN",
|
||||
"title": "SimpleCloudNotifier API",
|
||||
"contact": {},
|
||||
"version": "2.0"
|
||||
},
|
||||
"host": "scn.blackforestbytes.com",
|
||||
"basePath": "/api/",
|
||||
"paths": {
|
||||
"/ack.php": {
|
||||
"get": {
|
||||
"summary": "Acknowledge that a message was received",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Ack.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/db-test": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.DatabaseTest.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/expand.php": {
|
||||
"get": {
|
||||
"summary": "Get a whole (potentially truncated) message",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Expand.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/health": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Health.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/info.php": {
|
||||
"get": {
|
||||
"summary": "Get information about the current user",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Info.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ping": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.pingResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.pingResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.pingResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.pingResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.pingResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.errBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/register.php": {
|
||||
"get": {
|
||||
"summary": "Register a new account",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Register.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/requery.php": {
|
||||
"get": {
|
||||
"summary": "Return all not-acknowledged messages",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Requery.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/send.php": {
|
||||
"post": {
|
||||
"description": "all aeguments can either be supplied in the query or in the json body",
|
||||
"summary": "Send a message",
|
||||
"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 title",
|
||||
"name": "title",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The message content",
|
||||
"name": "content",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The message priority",
|
||||
"name": "priority",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The message idempotency id",
|
||||
"name": "msg_id",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "The message timestamp",
|
||||
"name": "timestamp",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"description": "the user_id",
|
||||
"name": "user_id",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "the user_key",
|
||||
"name": "user_key",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The message title",
|
||||
"name": "title",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The message content",
|
||||
"name": "content",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The message priority",
|
||||
"name": "priority",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The message idempotency id",
|
||||
"name": "msg_id",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "The message timestamp",
|
||||
"name": "timestamp",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Send.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.sendAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/update.php": {
|
||||
"get": {
|
||||
"summary": "Set the fcm-token (android)",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Update.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/upgrade.php": {
|
||||
"get": {
|
||||
"summary": "Upgrade a free account to a paid account",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.Upgrade.response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ginresp.internAPIError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"ginresp.errBody": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ginresp.internAPIError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"errid": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ginresp.sendAPIError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"errhighlight": {
|
||||
"type": "integer"
|
||||
},
|
||||
"error": {
|
||||
"type": "integer"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Ack.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"new_ack": {
|
||||
"type": "integer"
|
||||
},
|
||||
"prev_ack": {
|
||||
"type": "integer"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.DatabaseTest.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"libVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"libVersionNumber": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sourceID": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Expand.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.ShortCompatMessage"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Health.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Info.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fcm_token_set": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"is_pro": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota_max": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
},
|
||||
"unack_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Register.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"is_pro": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota_max": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Requery.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"data": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/models.CompatMessage"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Send.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Update.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"is_pro": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota": {
|
||||
"type": "string"
|
||||
},
|
||||
"quota_max": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.Upgrade.response": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/models.ShortCompatMessage"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.pingResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"$ref": "#/definitions/handler.pingResponseInfo"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handler.pingResponseInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"method": {
|
||||
"type": "string"
|
||||
},
|
||||
"request": {
|
||||
"type": "string"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.CompatMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer"
|
||||
},
|
||||
"scn_msg_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"usr_msg_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"models.ShortCompatMessage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"body": {
|
||||
"type": "string"
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer"
|
||||
},
|
||||
"scn_msg_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"trimmed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"usr_msg_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
551
server/swagger/swagger.yaml
Normal file
551
server/swagger/swagger.yaml
Normal file
@ -0,0 +1,551 @@
|
||||
basePath: /api/
|
||||
definitions:
|
||||
ginresp.errBody:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
type: object
|
||||
ginresp.internAPIError:
|
||||
properties:
|
||||
errid:
|
||||
type: integer
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
type: object
|
||||
ginresp.sendAPIError:
|
||||
properties:
|
||||
errhighlight:
|
||||
type: integer
|
||||
error:
|
||||
type: integer
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
type: object
|
||||
handler.Ack.response:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
new_ack:
|
||||
type: integer
|
||||
prev_ack:
|
||||
type: integer
|
||||
success:
|
||||
type: string
|
||||
type: object
|
||||
handler.DatabaseTest.response:
|
||||
properties:
|
||||
libVersion:
|
||||
type: string
|
||||
libVersionNumber:
|
||||
type: integer
|
||||
sourceID:
|
||||
type: string
|
||||
success:
|
||||
type: boolean
|
||||
type: object
|
||||
handler.Expand.response:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/definitions/models.ShortCompatMessage'
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
type: object
|
||||
handler.Health.response:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
type: object
|
||||
handler.Info.response:
|
||||
properties:
|
||||
fcm_token_set:
|
||||
type: boolean
|
||||
is_pro:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
quota:
|
||||
type: string
|
||||
quota_max:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
unack_count:
|
||||
type: integer
|
||||
user_id:
|
||||
type: string
|
||||
user_key:
|
||||
type: string
|
||||
type: object
|
||||
handler.Register.response:
|
||||
properties:
|
||||
is_pro:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
quota:
|
||||
type: string
|
||||
quota_max:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
user_key:
|
||||
type: string
|
||||
type: object
|
||||
handler.Requery.response:
|
||||
properties:
|
||||
count:
|
||||
type: integer
|
||||
data:
|
||||
items:
|
||||
$ref: '#/definitions/models.CompatMessage'
|
||||
type: array
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
type: object
|
||||
handler.Send.response:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
type: object
|
||||
handler.Update.response:
|
||||
properties:
|
||||
is_pro:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
quota:
|
||||
type: string
|
||||
quota_max:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
user_key:
|
||||
type: string
|
||||
type: object
|
||||
handler.Upgrade.response:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/definitions/models.ShortCompatMessage'
|
||||
message:
|
||||
type: string
|
||||
success:
|
||||
type: string
|
||||
type: object
|
||||
handler.pingResponse:
|
||||
properties:
|
||||
info:
|
||||
$ref: '#/definitions/handler.pingResponseInfo'
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
handler.pingResponseInfo:
|
||||
properties:
|
||||
addr:
|
||||
type: string
|
||||
headers:
|
||||
additionalProperties:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
method:
|
||||
type: string
|
||||
request:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
type: object
|
||||
models.CompatMessage:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
priority:
|
||||
type: integer
|
||||
scn_msg_id:
|
||||
type: string
|
||||
timestamp:
|
||||
type: integer
|
||||
title:
|
||||
type: string
|
||||
usr_msg_id:
|
||||
type: string
|
||||
type: object
|
||||
models.ShortCompatMessage:
|
||||
properties:
|
||||
body:
|
||||
type: string
|
||||
priority:
|
||||
type: integer
|
||||
scn_msg_id:
|
||||
type: string
|
||||
timestamp:
|
||||
type: integer
|
||||
title:
|
||||
type: string
|
||||
trimmed:
|
||||
type: boolean
|
||||
usr_msg_id:
|
||||
type: string
|
||||
type: object
|
||||
host: scn.blackforestbytes.com
|
||||
info:
|
||||
contact: {}
|
||||
description: API for SCN
|
||||
title: SimpleCloudNotifier API
|
||||
version: "2.0"
|
||||
paths:
|
||||
/ack.php:
|
||||
get:
|
||||
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
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Ack.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Acknowledge that a message was received
|
||||
/db-test:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.DatabaseTest.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
/expand.php:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Expand.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Get a whole (potentially truncated) message
|
||||
/health:
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Health.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
/info.php:
|
||||
get:
|
||||
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
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Info.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Get information about the current user
|
||||
/ping:
|
||||
delete:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.pingResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
get:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.pingResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
patch:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.pingResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
post:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.pingResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
put:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.pingResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.errBody'
|
||||
/register.php:
|
||||
get:
|
||||
parameters:
|
||||
- description: the (android) fcm token
|
||||
in: query
|
||||
name: fcm_token
|
||||
required: true
|
||||
type: string
|
||||
- description: if the user is a paid account
|
||||
enum:
|
||||
- "true"
|
||||
- "false"
|
||||
in: query
|
||||
name: pro
|
||||
required: true
|
||||
type: string
|
||||
- description: the (android) IAP token
|
||||
in: query
|
||||
name: pro_token
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Register.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Register a new account
|
||||
/requery.php:
|
||||
get:
|
||||
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
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Requery.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Return all not-acknowledged messages
|
||||
/send.php:
|
||||
post:
|
||||
description: all aeguments can either be supplied in the query or in the json
|
||||
body
|
||||
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 title
|
||||
in: query
|
||||
name: title
|
||||
required: true
|
||||
type: string
|
||||
- description: The message content
|
||||
in: query
|
||||
name: content
|
||||
type: string
|
||||
- description: The message priority
|
||||
in: query
|
||||
name: priority
|
||||
type: string
|
||||
- description: The message idempotency id
|
||||
in: query
|
||||
name: msg_id
|
||||
type: string
|
||||
- description: The message timestamp
|
||||
in: query
|
||||
name: timestamp
|
||||
type: string
|
||||
- description: the user_id
|
||||
in: body
|
||||
name: user_id
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: the user_key
|
||||
in: body
|
||||
name: user_key
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: The message title
|
||||
in: body
|
||||
name: title
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: The message content
|
||||
in: body
|
||||
name: content
|
||||
schema:
|
||||
type: string
|
||||
- description: The message priority
|
||||
in: body
|
||||
name: priority
|
||||
schema:
|
||||
type: string
|
||||
- description: The message idempotency id
|
||||
in: body
|
||||
name: msg_id
|
||||
schema:
|
||||
type: string
|
||||
- description: The message timestamp
|
||||
in: body
|
||||
name: timestamp
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Send.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.sendAPIError'
|
||||
summary: Send a message
|
||||
/update.php:
|
||||
get:
|
||||
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 (android) fcm token
|
||||
in: query
|
||||
name: fcm_token
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Update.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Set the fcm-token (android)
|
||||
/upgrade.php:
|
||||
get:
|
||||
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: if the user is a paid account
|
||||
enum:
|
||||
- "true"
|
||||
- "false"
|
||||
in: query
|
||||
name: pro
|
||||
required: true
|
||||
type: string
|
||||
- description: the (android) IAP token
|
||||
in: query
|
||||
name: pro_token
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.Upgrade.response'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/ginresp.internAPIError'
|
||||
summary: Upgrade a free account to a paid account
|
||||
swagger: "2.0"
|
Loading…
Reference in New Issue
Block a user