Add KeyToken authorization
This commit is contained in:
parent
16f6ab4861
commit
b1bd278f9b
@ -10,12 +10,17 @@ HASH=$(shell git rev-parse HEAD)
|
|||||||
build: swagger fmt
|
build: swagger fmt
|
||||||
mkdir -p _build
|
mkdir -p _build
|
||||||
rm -f ./_build/scn_backend
|
rm -f ./_build/scn_backend
|
||||||
|
go generate ./...
|
||||||
CGO_ENABLED=1 go build -v -o _build/scn_backend -tags "timetzdata sqlite_fts5 sqlite_foreign_keys" ./cmd/scnserver
|
CGO_ENABLED=1 go build -v -o _build/scn_backend -tags "timetzdata sqlite_fts5 sqlite_foreign_keys" ./cmd/scnserver
|
||||||
|
|
||||||
run: build
|
run: build
|
||||||
mkdir -p .run-data
|
mkdir -p .run-data
|
||||||
_build/scn_backend
|
_build/scn_backend
|
||||||
|
|
||||||
|
gow:
|
||||||
|
# go install github.com/mitranim/gow@latest
|
||||||
|
gow run blackforestbytes.com/portfoliomanager2/cmd/server
|
||||||
|
|
||||||
docker: build
|
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
|
||||||
|
@ -49,11 +49,27 @@
|
|||||||
|
|
||||||
- ios purchase verification
|
- ios purchase verification
|
||||||
|
|
||||||
- re-add ack labels as compat table for v1 api user
|
- [X] re-add ack labels as compat table for v1 api user
|
||||||
- return channel as "[..] asdf" in compat methods (mark clients as compat and send compat FB to them...)
|
- return channel as "[..] asdf" in compat methods (mark clients as compat and send compat FB to them...)
|
||||||
(then we can replace the old server without switching phone clients)
|
(then we can replace the old server without switching phone clients)
|
||||||
(still needs switching of the send-script)
|
(still needs switching of the send-script)
|
||||||
-
|
|
||||||
|
- do not use uuidgen in bash script (potetnially not installed) - use `head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 `
|
||||||
|
|
||||||
|
- move to KeyToken model
|
||||||
|
* [X] User can have multiple keys with different permissions
|
||||||
|
* [X] compat simply uses default-keys
|
||||||
|
* [X] CRUD routes for keys
|
||||||
|
* [X] KeyToken.messagecounter
|
||||||
|
* [ ] update old-data migration to create keys
|
||||||
|
* [ ] unit tests
|
||||||
|
|
||||||
|
- We no longer have a route to reshuffle all keys (previously in updateUser), add a /user/:uid/keys/reset ?
|
||||||
|
Would delete all existing keys and create 3 new ones?
|
||||||
|
|
||||||
|
- the explanation of user_id and key in ./website is now wrong (was already wrong and is even wronger now that there are multiple KeyToken's with permissions etc)
|
||||||
|
|
||||||
|
- swagger broken?
|
||||||
|
|
||||||
#### PERSONAL
|
#### PERSONAL
|
||||||
|
|
||||||
|
295
scnserver/_gen/enum-generate.go
Normal file
295
scnserver/_gen/enum-generate.go
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnumDefVal struct {
|
||||||
|
VarName string
|
||||||
|
Value string
|
||||||
|
Description *string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnumDef struct {
|
||||||
|
File string
|
||||||
|
EnumTypeName string
|
||||||
|
Type string
|
||||||
|
Values []EnumDefVal
|
||||||
|
}
|
||||||
|
|
||||||
|
var rexPackage = rext.W(regexp.MustCompile("^package\\s+(?P<name>[A-Za-z0-9_]+)\\s*$"))
|
||||||
|
|
||||||
|
var rexEnumDef = rext.W(regexp.MustCompile("^\\s*type\\s+(?P<name>[A-Za-z0-9_]+)\\s+(?P<type>[A-Za-z0-9_]+)\\s*//\\s*(@enum:type).*$"))
|
||||||
|
|
||||||
|
var rexValueDef = rext.W(regexp.MustCompile("^\\s*(?P<name>[A-Za-z0-9_]+)\\s+(?P<type>[A-Za-z0-9_]+)\\s*=\\s*(?P<value>(\"[A-Za-z0-9_]+\"|[0-9]+))\\s*(//(?P<descr>.*))?.*$"))
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dest := os.Args[2]
|
||||||
|
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
errpanic(err)
|
||||||
|
|
||||||
|
files, err := os.ReadDir(wd)
|
||||||
|
errpanic(err)
|
||||||
|
|
||||||
|
allEnums := make([]EnumDef, 0)
|
||||||
|
|
||||||
|
pkgname := ""
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if !strings.HasSuffix(f.Name(), ".go") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("========= %s =========\n\n", f.Name())
|
||||||
|
fileEnums, pn := processFile(f.Name())
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
allEnums = append(allEnums, fileEnums...)
|
||||||
|
|
||||||
|
if pn != "" {
|
||||||
|
pkgname = pn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgname == "" {
|
||||||
|
panic("no package name found in any file")
|
||||||
|
}
|
||||||
|
|
||||||
|
errpanic(os.WriteFile(dest, []byte(fmtOutput(allEnums, pkgname)), 0o755))
|
||||||
|
}
|
||||||
|
|
||||||
|
func errpanic(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processFile(fn string) ([]EnumDef, string) {
|
||||||
|
file, err := os.Open(fn)
|
||||||
|
errpanic(err)
|
||||||
|
defer func() { errpanic(file.Close()) }()
|
||||||
|
|
||||||
|
bin, err := io.ReadAll(file)
|
||||||
|
errpanic(err)
|
||||||
|
|
||||||
|
lines := strings.Split(string(bin), "\n")
|
||||||
|
|
||||||
|
enums := make([]EnumDef, 0)
|
||||||
|
|
||||||
|
pkgname := ""
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
if i == 0 && strings.HasPrefix(line, "// Code generated by") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, ok := rexPackage.MatchFirst(line); i == 0 && ok {
|
||||||
|
pkgname = match.GroupByName("name").Value()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, ok := rexEnumDef.MatchFirst(line); ok {
|
||||||
|
def := EnumDef{
|
||||||
|
File: fn,
|
||||||
|
EnumTypeName: match.GroupByName("name").Value(),
|
||||||
|
Type: match.GroupByName("type").Value(),
|
||||||
|
Values: make([]EnumDefVal, 0),
|
||||||
|
}
|
||||||
|
enums = append(enums, def)
|
||||||
|
fmt.Printf("Found enum definition { '%s' -> '%s' }\n", def.EnumTypeName, def.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, ok := rexValueDef.MatchFirst(line); ok {
|
||||||
|
typename := match.GroupByName("type").Value()
|
||||||
|
def := EnumDefVal{
|
||||||
|
VarName: match.GroupByName("name").Value(),
|
||||||
|
Value: match.GroupByName("value").Value(),
|
||||||
|
Description: match.GroupByNameOrEmpty("descr").ValueOrNil(),
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for i, v := range enums {
|
||||||
|
if v.EnumTypeName == typename {
|
||||||
|
enums[i].Values = append(enums[i].Values, def)
|
||||||
|
found = true
|
||||||
|
if def.Description != nil {
|
||||||
|
fmt.Printf("Found enum value [%s] for '%s' ('%s')\n", def.Value, def.VarName, *def.Description)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Found enum value [%s] for '%s'\n", def.Value, def.VarName)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
fmt.Printf("Found non-enum value [%s] for '%s' ( looks like enum value, but no matching @enum:type )\n", def.Value, def.VarName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return enums, pkgname
|
||||||
|
}
|
||||||
|
|
||||||
|
func fmtOutput(enums []EnumDef, pkgname string) string {
|
||||||
|
str := "// Code generated by permissions_gen.sh DO NOT EDIT.\n"
|
||||||
|
str += "\n"
|
||||||
|
str += "package " + pkgname + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
str += "import \"gogs.mikescher.com/BlackForestBytes/goext/langext\"" + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
str += "type Enum interface {" + "\n"
|
||||||
|
str += " Valid() bool" + "\n"
|
||||||
|
str += " ValuesAny() []any" + "\n"
|
||||||
|
str += " ValuesMeta() []EnumMetaValue" + "\n"
|
||||||
|
str += " VarName() string" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "type StringEnum interface {" + "\n"
|
||||||
|
str += " Enum" + "\n"
|
||||||
|
str += " String() string" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "type DescriptionEnum interface {" + "\n"
|
||||||
|
str += " Enum" + "\n"
|
||||||
|
str += " Description() string" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
str += "type EnumMetaValue struct {" + "\n"
|
||||||
|
str += " VarName string `json:\"varName\"`" + "\n"
|
||||||
|
str += " Value any `json:\"value\"`" + "\n"
|
||||||
|
str += " Description *string `json:\"description\"`" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
for _, enumdef := range enums {
|
||||||
|
|
||||||
|
hasDescr := langext.ArrAll(enumdef.Values, func(val EnumDefVal) bool { return val.Description != nil })
|
||||||
|
hasStr := enumdef.Type == "string"
|
||||||
|
|
||||||
|
str += "// ================================ " + enumdef.EnumTypeName + " ================================" + "\n"
|
||||||
|
str += "//" + "\n"
|
||||||
|
str += "// File: " + enumdef.File + "\n"
|
||||||
|
str += "// StringEnum: " + langext.Conditional(hasStr, "true", "false") + "\n"
|
||||||
|
str += "// DescrEnum: " + langext.Conditional(hasDescr, "true", "false") + "\n"
|
||||||
|
str += "//" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "var __" + enumdef.EnumTypeName + "Values = []" + enumdef.EnumTypeName + "{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
str += " " + v.VarName + "," + "\n"
|
||||||
|
}
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
if hasDescr {
|
||||||
|
str += "var __" + enumdef.EnumTypeName + "Descriptions = map[" + enumdef.EnumTypeName + "]string{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
str += " " + v.VarName + ": \"" + strings.TrimSpace(*v.Description) + "\"," + "\n"
|
||||||
|
}
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
str += "var __" + enumdef.EnumTypeName + "Varnames = map[" + enumdef.EnumTypeName + "]string{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
str += " " + v.VarName + ": \"" + v.VarName + "\"," + "\n"
|
||||||
|
}
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") Valid() bool {" + "\n"
|
||||||
|
str += " return langext.InArray(e, __" + enumdef.EnumTypeName + "Values)" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") Values() []" + enumdef.EnumTypeName + " {" + "\n"
|
||||||
|
str += " return __" + enumdef.EnumTypeName + "Values" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") ValuesAny() []any {" + "\n"
|
||||||
|
str += " return langext.ArrCastToAny(__" + enumdef.EnumTypeName + "Values)" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") ValuesMeta() []EnumMetaValue {" + "\n"
|
||||||
|
str += " return []EnumMetaValue{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
if hasDescr {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: langext.Ptr(\"%s\")},", v.VarName, v.VarName, strings.TrimSpace(*v.Description)) + "\n"
|
||||||
|
} else {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: nil},", v.VarName, v.VarName) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
if hasStr {
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") String() string {" + "\n"
|
||||||
|
str += " return string(e)" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDescr {
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") Description() string {" + "\n"
|
||||||
|
str += " if d, ok := __" + enumdef.EnumTypeName + "Descriptions[e]; ok {" + "\n"
|
||||||
|
str += " return d" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " return \"\"" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") VarName() string {" + "\n"
|
||||||
|
str += " if d, ok := __" + enumdef.EnumTypeName + "Varnames[e]; ok {" + "\n"
|
||||||
|
str += " return d" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " return \"\"" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func Parse" + enumdef.EnumTypeName + "(vv string) (" + enumdef.EnumTypeName + ", bool) {" + "\n"
|
||||||
|
str += " for _, ev := range __" + enumdef.EnumTypeName + "Values {" + "\n"
|
||||||
|
str += " if string(ev) == vv {" + "\n"
|
||||||
|
str += " return ev, true" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " return \"\", false" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func " + enumdef.EnumTypeName + "Values() []" + enumdef.EnumTypeName + " {" + "\n"
|
||||||
|
str += " return __" + enumdef.EnumTypeName + "Values" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func " + enumdef.EnumTypeName + "ValuesMeta() []EnumMetaValue {" + "\n"
|
||||||
|
str += " return []EnumMetaValue{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
if hasDescr {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: langext.Ptr(\"%s\")},", v.VarName, v.VarName, strings.TrimSpace(*v.Description)) + "\n"
|
||||||
|
} else {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: nil},", v.VarName, v.VarName) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package apierr
|
package apierr
|
||||||
|
|
||||||
type APIError int
|
type APIError int //@enum:type
|
||||||
|
|
||||||
//goland:noinspection GoSnakeCaseUsage
|
//goland:noinspection GoSnakeCaseUsage
|
||||||
const (
|
const (
|
||||||
@ -37,11 +37,13 @@ const (
|
|||||||
SUBSCRIPTION_NOT_FOUND APIError = 1304
|
SUBSCRIPTION_NOT_FOUND APIError = 1304
|
||||||
MESSAGE_NOT_FOUND APIError = 1305
|
MESSAGE_NOT_FOUND APIError = 1305
|
||||||
SUBSCRIPTION_USER_MISMATCH APIError = 1306
|
SUBSCRIPTION_USER_MISMATCH APIError = 1306
|
||||||
|
KEY_NOT_FOUND APIError = 1307
|
||||||
USER_AUTH_FAILED APIError = 1311
|
USER_AUTH_FAILED APIError = 1311
|
||||||
|
|
||||||
NO_DEVICE_LINKED APIError = 1401
|
NO_DEVICE_LINKED APIError = 1401
|
||||||
|
|
||||||
CHANNEL_ALREADY_EXISTS APIError = 1501
|
CHANNEL_ALREADY_EXISTS APIError = 1501
|
||||||
|
CANNOT_SELFDELETE_KEY APIError = 1511
|
||||||
|
|
||||||
QUOTA_REACHED APIError = 2101
|
QUOTA_REACHED APIError = 2101
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package apihighlight
|
package apihighlight
|
||||||
|
|
||||||
type ErrHighlight int
|
type ErrHighlight int //@enum:type
|
||||||
|
|
||||||
//goland:noinspection GoSnakeCaseUsage
|
//goland:noinspection GoSnakeCaseUsage
|
||||||
const (
|
const (
|
||||||
|
@ -108,6 +108,11 @@ func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp HTTPResponse,
|
|||||||
|
|
||||||
permObj, hasPerm := g.Get("perm")
|
permObj, hasPerm := g.Get("perm")
|
||||||
|
|
||||||
|
hasTok := false
|
||||||
|
if hasPerm {
|
||||||
|
hasTok = permObj.(models.PermissionSet).Token != nil
|
||||||
|
}
|
||||||
|
|
||||||
return models.RequestLog{
|
return models.RequestLog{
|
||||||
Method: g.Request.Method,
|
Method: g.Request.Method,
|
||||||
URI: g.Request.URL.String(),
|
URI: g.Request.URL.String(),
|
||||||
@ -117,8 +122,9 @@ func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp HTTPResponse,
|
|||||||
RequestBodySize: int64(len(reqbody)),
|
RequestBodySize: int64(len(reqbody)),
|
||||||
RequestContentType: ct,
|
RequestContentType: ct,
|
||||||
RemoteIP: g.RemoteIP(),
|
RemoteIP: g.RemoteIP(),
|
||||||
UserID: langext.ConditionalFn10(hasPerm, func() *models.UserID { return permObj.(models.PermissionSet).UserID }, nil),
|
TokenID: langext.ConditionalFn10(hasTok, func() *models.KeyTokenID { return langext.Ptr(permObj.(models.PermissionSet).Token.KeyTokenID) }, nil),
|
||||||
Permissions: langext.ConditionalFn10(hasPerm, func() *string { return langext.Ptr(string(permObj.(models.PermissionSet).KeyType)) }, nil),
|
UserID: langext.ConditionalFn10(hasTok, func() *models.UserID { return langext.Ptr(permObj.(models.PermissionSet).Token.OwnerUserID) }, nil),
|
||||||
|
Permissions: langext.ConditionalFn10(hasTok, func() *string { return langext.Ptr(permObj.(models.PermissionSet).Token.Permissions.String()) }, nil),
|
||||||
ResponseStatuscode: langext.ConditionalFn10(resp != nil, func() *int64 { return langext.Ptr(int64(resp.Statuscode())) }, nil),
|
ResponseStatuscode: langext.ConditionalFn10(resp != nil, func() *int64 { return langext.Ptr(int64(resp.Statuscode())) }, nil),
|
||||||
ResponseBodySize: langext.ConditionalFn10(strrespbody != nil, func() *int64 { return langext.Ptr(int64(len(*respbody))) }, nil),
|
ResponseBodySize: langext.ConditionalFn10(strrespbody != nil, func() *int64 { return langext.Ptr(int64(len(*respbody))) }, nil),
|
||||||
ResponseBody: strrespbody,
|
ResponseBody: strrespbody,
|
||||||
|
@ -36,7 +36,7 @@ func NewAPIHandler(app *logic.Application) APIHandler {
|
|||||||
//
|
//
|
||||||
// @Param post_body body handler.CreateUser.body false " "
|
// @Param post_body body handler.CreateUser.body false " "
|
||||||
//
|
//
|
||||||
// @Success 200 {object} models.UserJSONWithClients
|
// @Success 200 {object} models.UserJSONWithClientsAndKeys
|
||||||
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
// @Failure 500 {object} ginresp.apiError "internal server error"
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
//
|
//
|
||||||
@ -111,13 +111,28 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
username = langext.Ptr(h.app.NormalizeUsername(*username))
|
username = langext.Ptr(h.app.NormalizeUsername(*username))
|
||||||
}
|
}
|
||||||
|
|
||||||
userobj, err := h.database.CreateUser(ctx, readKey, sendKey, adminKey, b.ProToken, username)
|
userobj, err := h.database.CreateUser(ctx, b.ProToken, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create user in db", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create user in db", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = h.database.CreateKeyToken(ctx, "AdminKey (default)", userobj.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermAdmin}, adminKey)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = h.database.CreateKeyToken(ctx, "SendKey (default)", userobj.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermChannelSend}, sendKey)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create send-key in db", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = h.database.CreateKeyToken(ctx, "ReadKey (default)", userobj.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermUserRead, models.PermChannelRead}, readKey)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create read-key in db", err)
|
||||||
|
}
|
||||||
|
|
||||||
if b.NoClient {
|
if b.NoClient {
|
||||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, userobj.JSONWithClients(make([]models.Client, 0))))
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, userobj.JSONWithClients(make([]models.Client, 0), adminKey, sendKey, readKey)))
|
||||||
} else {
|
} else {
|
||||||
err := h.database.DeleteClientsByFCM(ctx, b.FCMToken)
|
err := h.database.DeleteClientsByFCM(ctx, b.FCMToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -129,7 +144,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, userobj.JSONWithClients([]models.Client{client})))
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, userobj.JSONWithClients([]models.Client{client}, adminKey, sendKey, readKey)))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -205,9 +220,6 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
type body struct {
|
type body struct {
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
ProToken *string `json:"pro_token"`
|
ProToken *string `json:"pro_token"`
|
||||||
RefreshReadKey *bool `json:"read_key"`
|
|
||||||
RefreshSendKey *bool `json:"send_key"`
|
|
||||||
RefreshAdminKey *bool `json:"admin_key"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
@ -262,33 +274,6 @@ func (h APIHandler) UpdateUser(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if langext.Coalesce(b.RefreshSendKey, false) {
|
|
||||||
newkey := h.app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
err := h.database.UpdateUserSendKey(ctx, u.UserID, newkey)
|
|
||||||
if err != nil {
|
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if langext.Coalesce(b.RefreshReadKey, false) {
|
|
||||||
newkey := h.app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
err := h.database.UpdateUserReadKey(ctx, u.UserID, newkey)
|
|
||||||
if err != nil {
|
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if langext.Coalesce(b.RefreshAdminKey, false) {
|
|
||||||
newkey := h.app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
err := h.database.UpdateUserAdminKey(ctx, u.UserID, newkey)
|
|
||||||
if err != nil {
|
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update user", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := h.database.GetUser(ctx, u.UserID)
|
user, err := h.database.GetUser(ctx, u.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) user", err)
|
||||||
@ -705,9 +690,8 @@ func (h APIHandler) CreateChannel(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
subscribeKey := h.app.GenerateRandomAuthKey()
|
subscribeKey := h.app.GenerateRandomAuthKey()
|
||||||
sendKey := h.app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
channel, err := h.database.CreateChannel(ctx, u.UserID, channelDisplayName, channelInternalName, subscribeKey, sendKey)
|
channel, err := h.database.CreateChannel(ctx, u.UserID, channelDisplayName, channelInternalName, subscribeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create channel", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create channel", err)
|
||||||
}
|
}
|
||||||
@ -756,7 +740,6 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
type body struct {
|
type body struct {
|
||||||
RefreshSubscribeKey *bool `json:"subscribe_key"`
|
RefreshSubscribeKey *bool `json:"subscribe_key"`
|
||||||
RefreshSendKey *bool `json:"send_key"`
|
|
||||||
DisplayName *string `json:"display_name"`
|
DisplayName *string `json:"display_name"`
|
||||||
DescriptionName *string `json:"description_name"`
|
DescriptionName *string `json:"description_name"`
|
||||||
}
|
}
|
||||||
@ -789,15 +772,6 @@ func (h APIHandler) UpdateChannel(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query user", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if langext.Coalesce(b.RefreshSendKey, false) {
|
|
||||||
newkey := h.app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
err := h.database.UpdateChannelSendKey(ctx, u.ChannelID, newkey)
|
|
||||||
if err != nil {
|
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channel", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if langext.Coalesce(b.RefreshSubscribeKey, false) {
|
if langext.Coalesce(b.RefreshSubscribeKey, false) {
|
||||||
newkey := h.app.GenerateRandomAuthKey()
|
newkey := h.app.GenerateRandomAuthKey()
|
||||||
|
|
||||||
@ -905,10 +879,6 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
pageSize := mathext.Clamp(langext.Coalesce(q.PageSize, 64), 1, maxPageSize)
|
pageSize := mathext.Clamp(langext.Coalesce(q.PageSize, 64), 1, maxPageSize)
|
||||||
|
|
||||||
if permResp := ctx.CheckPermissionRead(); permResp != nil {
|
|
||||||
return *permResp
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, err := h.database.GetChannel(ctx, u.ChannelUserID, u.ChannelID)
|
channel, err := h.database.GetChannel(ctx, u.ChannelUserID, u.ChannelID)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
return ginresp.APIError(g, 404, apierr.CHANNEL_NOT_FOUND, "Channel not found", err)
|
||||||
@ -917,17 +887,8 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query channel", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
userid := *ctx.GetPermissionUserID()
|
if permResp := ctx.CheckPermissionChanMessagesRead(channel.Channel); permResp != nil {
|
||||||
|
return *permResp
|
||||||
sub, err := h.database.GetSubscriptionBySubscriber(ctx, userid, channel.ChannelID)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query subscription", err)
|
|
||||||
}
|
|
||||||
if !sub.Confirmed {
|
|
||||||
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tok, err := ct.Decode(langext.Coalesce(q.NextPageToken, ""))
|
tok, err := ct.Decode(langext.Coalesce(q.NextPageToken, ""))
|
||||||
@ -1413,7 +1374,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
pageSize := mathext.Clamp(langext.Coalesce(q.PageSize, 64), 1, maxPageSize)
|
pageSize := mathext.Clamp(langext.Coalesce(q.PageSize, 64), 1, maxPageSize)
|
||||||
|
|
||||||
if permResp := ctx.CheckPermissionRead(); permResp != nil {
|
if permResp := ctx.CheckPermissionSelfAllMessagesRead(); permResp != nil {
|
||||||
return *permResp
|
return *permResp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1494,12 +1455,14 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query message", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query message", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.CheckPermissionMessageReadDirect(msg) {
|
|
||||||
|
|
||||||
// either we have direct read permissions (it is our message + read/admin key)
|
// either we have direct read permissions (it is our message + read/admin key)
|
||||||
// or we subscribe (+confirmed) to the channel and have read/admin key
|
// or we subscribe (+confirmed) to the channel and have read/admin key
|
||||||
|
|
||||||
if uid := ctx.GetPermissionUserID(); uid != nil && ctx.IsPermissionUserRead() {
|
if ctx.CheckPermissionMessageRead(msg) {
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if uid := ctx.GetPermissionUserID(); uid != nil && ctx.CheckPermissionUserRead(*uid) == nil {
|
||||||
sub, err := h.database.GetSubscriptionBySubscriber(ctx, *uid, msg.ChannelID)
|
sub, err := h.database.GetSubscriptionBySubscriber(ctx, *uid, msg.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query subscription", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query subscription", err)
|
||||||
@ -1512,18 +1475,14 @@ func (h APIHandler) GetMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
// sub not confirmed
|
// sub not confirmed
|
||||||
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// => perm okay
|
// => perm okay
|
||||||
|
|
||||||
} else {
|
|
||||||
// auth-key is not set or not a user:x variant
|
|
||||||
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON()))
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteMessage swaggerdoc
|
// DeleteMessage swaggerdoc
|
||||||
//
|
//
|
||||||
// @Summary Delete a single message
|
// @Summary Delete a single message
|
||||||
@ -1564,7 +1523,7 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query message", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query message", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ctx.CheckPermissionMessageReadDirect(msg) {
|
if !ctx.CheckPermissionMessageRead(msg) {
|
||||||
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
return ginresp.APIError(g, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1580,3 +1539,284 @@ func (h APIHandler) DeleteMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON()))
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, msg.FullJSON()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListUserKeys swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary List keys of the user
|
||||||
|
// @Description The request must be done with an ADMIN key, the returned keys are without their token.
|
||||||
|
// @ID api-tokenkeys-list
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param uid path int true "UserID"
|
||||||
|
//
|
||||||
|
// @Success 200 {object} handler.ListUserKeys.response
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/users/:uid/keys [GET]
|
||||||
|
func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
type response struct {
|
||||||
|
Tokens []models.KeyTokenJSON `json:"tokens"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionUserAdmin(u.UserID); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
clients, err := h.database.ListKeyTokens(ctx, u.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query keys", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := langext.ArrMap(clients, func(v models.KeyToken) models.KeyTokenJSON { return v.JSON() })
|
||||||
|
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Tokens: res}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserKey swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary Get a single key
|
||||||
|
// @Description The request must be done with an ADMIN key, the returned key does not include its token.
|
||||||
|
// @ID api-tokenkeys-get
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param uid path int true "UserID"
|
||||||
|
// @Param kid path int true "TokenKeyID"
|
||||||
|
//
|
||||||
|
// @Success 200 {object} models.KeyTokenJSON
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/users/:uid/keys/:kid [GET]
|
||||||
|
func (h APIHandler) GetUserKey(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
KeyID models.KeyTokenID `uri:"kid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionUserAdmin(u.UserID); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := h.database.GetKeyToken(ctx, u.UserID, u.KeyID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserKey swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary Update a key
|
||||||
|
// @ID api-tokenkeys-update
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param uid path int true "UserID"
|
||||||
|
// @Param kid path int true "TokenKeyID"
|
||||||
|
//
|
||||||
|
// @Success 200 {object} models.KeyTokenJSON
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/users/:uid/keys/:kid [PATCH]
|
||||||
|
func (h APIHandler) UpdateUserKey(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
KeyID models.KeyTokenID `uri:"kid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
type body struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
AllChannels *bool `json:"all_channels"`
|
||||||
|
Channels *[]models.ChannelID `json:"channels"`
|
||||||
|
Permissions *string `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
var b body
|
||||||
|
ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionUserAdmin(u.UserID); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := h.database.GetKeyToken(ctx, u.UserID, u.KeyID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Name != nil {
|
||||||
|
err := h.database.UpdateKeyTokenName(ctx, u.KeyID, *b.Name)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update name", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Permissions != nil {
|
||||||
|
err := h.database.UpdateKeyTokenPermissions(ctx, u.KeyID, models.ParseTokenPermissionList(*b.Permissions))
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update permissions", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.AllChannels != nil {
|
||||||
|
err := h.database.UpdateKeyTokenAllChannels(ctx, u.KeyID, *b.AllChannels)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update all_channels", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Channels != nil {
|
||||||
|
err := h.database.UpdateKeyTokenChannels(ctx, u.KeyID, *b.Channels)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update channels", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserKey swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary Create a new key
|
||||||
|
// @ID api-tokenkeys-create
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param uid path int true "UserID"
|
||||||
|
//
|
||||||
|
// @Param post_body body handler.CreateUserKey.body false " "
|
||||||
|
//
|
||||||
|
// @Success 200 {object} models.KeyTokenJSON
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/users/:uid/keys [POST]
|
||||||
|
func (h APIHandler) CreateUserKey(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
type body struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
AllChannels *bool `json:"all_channels" binding:"required"`
|
||||||
|
Channels *[]models.ChannelID `json:"channels" binding:"required"`
|
||||||
|
Permissions *string `json:"permissions" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
var b body
|
||||||
|
ctx, errResp := h.app.StartRequest(g, &u, nil, &b, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
for _, c := range *b.Channels {
|
||||||
|
if err := c.Valid(); err != nil {
|
||||||
|
return ginresp.APIError(g, 400, apierr.INVALID_BODY_PARAM, "Invalid ChannelID", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionUserAdmin(u.UserID); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
token := h.app.GenerateRandomAuthKey()
|
||||||
|
|
||||||
|
perms := models.ParseTokenPermissionList(*b.Permissions)
|
||||||
|
|
||||||
|
keytok, err := h.database.CreateKeyToken(ctx, b.Name, *ctx.GetPermissionUserID(), *b.AllChannels, *b.Channels, perms, token)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create keytoken in db", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, keytok.JSON().WithToken(token)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserKey swaggerdoc
|
||||||
|
//
|
||||||
|
// @Summary Delete a key
|
||||||
|
// @Description Cannot be used to delete the key used in the request itself
|
||||||
|
// @ID api-tokenkeys-delete
|
||||||
|
// @Tags API-v2
|
||||||
|
//
|
||||||
|
// @Param uid path int true "UserID"
|
||||||
|
// @Param kid path int true "TokenKeyID"
|
||||||
|
//
|
||||||
|
// @Success 200 {object} models.KeyTokenJSON
|
||||||
|
// @Failure 400 {object} ginresp.apiError "supplied values/parameters cannot be parsed / are invalid"
|
||||||
|
// @Failure 401 {object} ginresp.apiError "user is not authorized / has missing permissions"
|
||||||
|
// @Failure 404 {object} ginresp.apiError "message not found"
|
||||||
|
// @Failure 500 {object} ginresp.apiError "internal server error"
|
||||||
|
//
|
||||||
|
// @Router /api/v2/users/:uid/keys/:kid [DELETE]
|
||||||
|
func (h APIHandler) DeleteUserKey(g *gin.Context) ginresp.HTTPResponse {
|
||||||
|
type uri struct {
|
||||||
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
|
KeyID models.KeyTokenID `uri:"kid" binding:"entityid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var u uri
|
||||||
|
ctx, errResp := h.app.StartRequest(g, &u, nil, nil, nil)
|
||||||
|
if errResp != nil {
|
||||||
|
return *errResp
|
||||||
|
}
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
if permResp := ctx.CheckPermissionUserAdmin(u.UserID); permResp != nil {
|
||||||
|
return *permResp
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := h.database.GetKeyToken(ctx, u.UserID, u.KeyID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.APIError(g, 404, apierr.KEY_NOT_FOUND, "Key not found", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query client", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.KeyID == *ctx.GetPermissionKeyTokenID() {
|
||||||
|
return ginresp.APIError(g, 404, apierr.CANNOT_SELFDELETE_KEY, "Cannot delete the currently used key", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.database.DeleteKeyToken(ctx, u.KeyID)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete client", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, client.JSON()))
|
||||||
|
}
|
||||||
|
@ -86,7 +86,7 @@ func (h MessageHandler) SendMessageCompat(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
|
return ginresp.SendAPIError(g, 400, apierr.USER_NOT_FOUND, hl.USER_ID, "User not found (compat)", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
okResp, errResp := h.sendMessageInternal(g, ctx, langext.Ptr(models.UserID(*newid)), data.UserKey, nil, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil)
|
okResp, errResp := h.sendMessageInternal(g, ctx, langext.Ptr(models.UserID(*newid)), data.UserKey, nil, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, nil)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
} else {
|
} else {
|
||||||
@ -195,8 +195,6 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readKey := h.app.GenerateRandomAuthKey()
|
|
||||||
sendKey := h.app.GenerateRandomAuthKey()
|
|
||||||
adminKey := h.app.GenerateRandomAuthKey()
|
adminKey := h.app.GenerateRandomAuthKey()
|
||||||
|
|
||||||
err := h.database.ClearFCMTokens(ctx, *data.FCMToken)
|
err := h.database.ClearFCMTokens(ctx, *data.FCMToken)
|
||||||
@ -211,11 +209,16 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := h.database.CreateUser(ctx, readKey, sendKey, adminKey, data.ProToken, nil)
|
user, err := h.database.CreateUser(ctx, data.ProToken, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to create user in db")
|
return ginresp.CompatAPIError(0, "Failed to create user in db")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err = h.database.CreateKeyToken(ctx, "CompatKey", user.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermAdmin}, adminKey)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat")
|
_, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to create client in db")
|
return ginresp.CompatAPIError(0, "Failed to create client in db")
|
||||||
@ -230,7 +233,7 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
Success: true,
|
Success: true,
|
||||||
Message: "New user registered",
|
Message: "New user registered",
|
||||||
UserID: oldid,
|
UserID: oldid,
|
||||||
UserKey: user.AdminKey,
|
UserKey: adminKey,
|
||||||
QuotaUsed: user.QuotaUsedToday(),
|
QuotaUsed: user.QuotaUsedToday(),
|
||||||
QuotaMax: user.QuotaPerDay(),
|
QuotaMax: user.QuotaPerDay(),
|
||||||
IsPro: user.IsPro,
|
IsPro: user.IsPro,
|
||||||
@ -305,7 +308,14 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.AdminKey != *data.UserKey {
|
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
|
}
|
||||||
|
if !keytok.IsAdmin(user.UserID) {
|
||||||
return ginresp.CompatAPIError(204, "Authentification failed")
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +330,7 @@ func (h CompatHandler) Info(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
Success: true,
|
Success: true,
|
||||||
Message: "ok",
|
Message: "ok",
|
||||||
UserID: *data.UserID,
|
UserID: *data.UserID,
|
||||||
UserKey: user.AdminKey,
|
UserKey: keytok.Token,
|
||||||
QuotaUsed: user.QuotaUsedToday(),
|
QuotaUsed: user.QuotaUsedToday(),
|
||||||
QuotaMax: user.QuotaPerDay(),
|
QuotaMax: user.QuotaPerDay(),
|
||||||
IsPro: langext.Conditional(user.IsPro, 1, 0),
|
IsPro: langext.Conditional(user.IsPro, 1, 0),
|
||||||
@ -398,7 +408,14 @@ func (h CompatHandler) Ack(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.AdminKey != *data.UserKey {
|
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
|
}
|
||||||
|
if !keytok.IsAdmin(user.UserID) {
|
||||||
return ginresp.CompatAPIError(204, "Authentification failed")
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,7 +510,14 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.AdminKey != *data.UserKey {
|
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
|
}
|
||||||
|
if !keytok.IsAdmin(user.UserID) {
|
||||||
return ginresp.CompatAPIError(204, "Authentification failed")
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +627,14 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.AdminKey != *data.UserKey {
|
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
|
}
|
||||||
|
if !keytok.IsAdmin(user.UserID) {
|
||||||
return ginresp.CompatAPIError(204, "Authentification failed")
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,10 +644,13 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newAdminKey := h.app.GenerateRandomAuthKey()
|
newAdminKey := h.app.GenerateRandomAuthKey()
|
||||||
newReadKey := h.app.GenerateRandomAuthKey()
|
|
||||||
newSendKey := h.app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
err = h.database.UpdateUserKeys(ctx, user.UserID, newSendKey, newReadKey, newAdminKey)
|
_, err = h.database.CreateKeyToken(ctx, "CompatKey", user.UserID, true, make([]models.ChannelID, 0), models.TokenPermissionList{models.PermAdmin}, newAdminKey)
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.database.DeleteKeyToken(ctx, keytok.KeyTokenID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to update keys")
|
return ginresp.CompatAPIError(0, "Failed to update keys")
|
||||||
}
|
}
|
||||||
@ -648,7 +682,7 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
Success: true,
|
Success: true,
|
||||||
Message: "user updated",
|
Message: "user updated",
|
||||||
UserID: *data.UserID,
|
UserID: *data.UserID,
|
||||||
UserKey: user.AdminKey,
|
UserKey: newAdminKey,
|
||||||
QuotaUsed: user.QuotaUsedToday(),
|
QuotaUsed: user.QuotaUsedToday(),
|
||||||
QuotaMax: user.QuotaPerDay(),
|
QuotaMax: user.QuotaPerDay(),
|
||||||
IsPro: langext.Conditional(user.IsPro, 1, 0),
|
IsPro: langext.Conditional(user.IsPro, 1, 0),
|
||||||
@ -723,7 +757,14 @@ func (h CompatHandler) Expand(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.AdminKey != *data.UserKey {
|
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
|
}
|
||||||
|
if !keytok.IsAdmin(user.UserID) {
|
||||||
return ginresp.CompatAPIError(204, "Authentification failed")
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -835,7 +876,14 @@ func (h CompatHandler) Upgrade(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.AdminKey != *data.UserKey {
|
keytok, err := h.database.GetKeyTokenByToken(ctx, *data.UserKey)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return ginresp.CompatAPIError(0, "Failed to query token")
|
||||||
|
}
|
||||||
|
if !keytok.IsAdmin(user.UserID) {
|
||||||
return ginresp.CompatAPIError(204, "Authentification failed")
|
return ginresp.CompatAPIError(204, "Authentification failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +59,8 @@ func NewMessageHandler(app *logic.Application) MessageHandler {
|
|||||||
func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
||||||
type combined struct {
|
type combined struct {
|
||||||
UserID *models.UserID `json:"user_id" form:"user_id" example:"7725" `
|
UserID *models.UserID `json:"user_id" form:"user_id" example:"7725" `
|
||||||
UserKey *string `json:"user_key" form:"user_key" example:"P3TNH8mvv14fm" `
|
KeyToken *string `json:"key" form:"key" example:"P3TNH8mvv14fm" `
|
||||||
Channel *string `json:"channel" form:"channel" example:"test" `
|
Channel *string `json:"channel" form:"channel" example:"test" `
|
||||||
ChanKey *string `json:"chan_key" form:"chan_key" example:"qhnUbKcLgp6tg" `
|
|
||||||
Title *string `json:"title" form:"title" example:"Hello World" `
|
Title *string `json:"title" form:"title" example:"Hello World" `
|
||||||
Content *string `json:"content" form:"content" example:"This is a message" `
|
Content *string `json:"content" form:"content" example:"This is a message" `
|
||||||
Priority *int `json:"priority" form:"priority" example:"1" enums:"0,1,2" `
|
Priority *int `json:"priority" form:"priority" example:"1" enums:"0,1,2" `
|
||||||
@ -95,7 +94,7 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
// query has highest prio, then form, then json
|
// query has highest prio, then form, then json
|
||||||
data := dataext.ObjectMerge(dataext.ObjectMerge(b, f), q)
|
data := dataext.ObjectMerge(dataext.ObjectMerge(b, f), q)
|
||||||
|
|
||||||
okResp, errResp := h.sendMessageInternal(g, ctx, data.UserID, data.UserKey, data.Channel, data.ChanKey, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, data.SenderName)
|
okResp, errResp := h.sendMessageInternal(g, ctx, data.UserID, data.KeyToken, data.Channel, data.Title, data.Content, data.Priority, data.UserMessageID, data.SendTimestamp, data.SenderName)
|
||||||
if errResp != nil {
|
if errResp != nil {
|
||||||
return *errResp
|
return *errResp
|
||||||
} else {
|
} else {
|
||||||
@ -129,7 +128,7 @@ func (h MessageHandler) SendMessage(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContext, UserID *models.UserID, UserKey *string, Channel *string, ChanKey *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) (*SendMessageResponse, *ginresp.HTTPResponse) {
|
func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContext, UserID *models.UserID, Key *string, Channel *string, Title *string, Content *string, Priority *int, UserMessageID *string, SendTimestamp *float64, SenderName *string) (*SendMessageResponse, *ginresp.HTTPResponse) {
|
||||||
if Title != nil {
|
if Title != nil {
|
||||||
Title = langext.Ptr(strings.TrimSpace(*Title))
|
Title = langext.Ptr(strings.TrimSpace(*Title))
|
||||||
}
|
}
|
||||||
@ -140,8 +139,8 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
|||||||
if UserID == nil {
|
if UserID == nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_UID, hl.USER_ID, "Missing parameter [[user_id]]", nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_UID, hl.USER_ID, "Missing parameter [[user_id]]", nil))
|
||||||
}
|
}
|
||||||
if UserKey == nil {
|
if Key == nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_TOK, hl.USER_KEY, "Missing parameter [[user_token]]", nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_TOK, hl.USER_KEY, "Missing parameter [[key]]", nil))
|
||||||
}
|
}
|
||||||
if Title == nil {
|
if Title == nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_TITLE, hl.TITLE, "Missing parameter [[title]]", nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.MISSING_TITLE, hl.TITLE, "Missing parameter [[title]]", nil))
|
||||||
@ -224,32 +223,13 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
|||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 403, apierr.QUOTA_REACHED, hl.NONE, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()), nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 403, apierr.QUOTA_REACHED, hl.NONE, fmt.Sprintf("Daily quota reached (%d)", user.QuotaPerDay()), nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
var channel models.Channel
|
channel, err := h.app.GetOrCreateChannel(ctx, *UserID, channelDisplayName, channelInternalName)
|
||||||
if ChanKey != nil {
|
|
||||||
// foreign channel (+ channel send-key)
|
|
||||||
|
|
||||||
foreignChan, err := h.database.GetChannelByNameAndSendKey(ctx, channelInternalName, *ChanKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query (foreign) channel", err))
|
|
||||||
}
|
|
||||||
if foreignChan == nil {
|
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 400, apierr.CHANNEL_NOT_FOUND, hl.CHANNEL, "(Foreign) Channel not found", err))
|
|
||||||
}
|
|
||||||
channel = *foreignChan
|
|
||||||
} else {
|
|
||||||
// own channel
|
|
||||||
|
|
||||||
channel, err = h.app.GetOrCreateChannel(ctx, *UserID, channelDisplayName, channelInternalName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create (owned) channel", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to query/create (owned) channel", err))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
selfChanAdmin := *UserID == channel.OwnerUserID && *UserKey == user.AdminKey
|
keytok, permResp := ctx.CheckPermissionSend(channel, *Key)
|
||||||
selfChanSend := *UserID == channel.OwnerUserID && *UserKey == user.SendKey
|
if permResp != nil {
|
||||||
forgChanSend := *UserID != channel.OwnerUserID && ChanKey != nil && *ChanKey == channel.SendKey
|
|
||||||
|
|
||||||
if !selfChanAdmin && !selfChanSend && !forgChanSend {
|
|
||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 401, apierr.USER_AUTH_FAILED, hl.USER_KEY, "You are not authorized for this action", nil))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 401, apierr.USER_AUTH_FAILED, hl.USER_KEY, "You are not authorized for this action", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +267,11 @@ func (h MessageHandler) sendMessageInternal(g *gin.Context, ctx *logic.AppContex
|
|||||||
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to inc channel msg-counter", err))
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to inc channel msg-counter", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = h.database.IncKeyTokenMessageCounter(ctx, keytok.KeyTokenID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, langext.Ptr(ginresp.SendAPIError(g, 500, apierr.DATABASE_ERROR, hl.NONE, "Failed to inc token msg-counter", err))
|
||||||
|
}
|
||||||
|
|
||||||
for _, sub := range subscriptions {
|
for _, sub := range subscriptions {
|
||||||
clients, err := h.database.ListClients(ctx, sub.SubscriberUserID)
|
clients, err := h.database.ListClients(ctx, sub.SubscriberUserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -127,6 +127,12 @@ func (r *Router) Init(e *gin.Engine) error {
|
|||||||
apiv2.GET("/users/:uid", r.Wrap(r.apiHandler.GetUser))
|
apiv2.GET("/users/:uid", r.Wrap(r.apiHandler.GetUser))
|
||||||
apiv2.PATCH("/users/:uid", r.Wrap(r.apiHandler.UpdateUser))
|
apiv2.PATCH("/users/:uid", r.Wrap(r.apiHandler.UpdateUser))
|
||||||
|
|
||||||
|
apiv2.GET("/users/:uid/keys", r.Wrap(r.apiHandler.ListUserKeys))
|
||||||
|
apiv2.POST("/users/:uid/keys", r.Wrap(r.apiHandler.CreateUserKey))
|
||||||
|
apiv2.GET("/users/:uid/keys/:kid", r.Wrap(r.apiHandler.GetUserKey))
|
||||||
|
apiv2.PATCH("/users/:uid/keys/:kid", r.Wrap(r.apiHandler.UpdateUserKey))
|
||||||
|
apiv2.DELETE("/users/:uid/keys/:kid", r.Wrap(r.apiHandler.DeleteUserKey))
|
||||||
|
|
||||||
apiv2.GET("/users/:uid/clients", r.Wrap(r.apiHandler.ListClients))
|
apiv2.GET("/users/:uid/clients", r.Wrap(r.apiHandler.ListClients))
|
||||||
apiv2.GET("/users/:uid/clients/:cid", r.Wrap(r.apiHandler.GetClient))
|
apiv2.GET("/users/:uid/clients/:cid", r.Wrap(r.apiHandler.GetClient))
|
||||||
apiv2.POST("/users/:uid/clients", r.Wrap(r.apiHandler.AddClient))
|
apiv2.POST("/users/:uid/clients", r.Wrap(r.apiHandler.AddClient))
|
||||||
|
@ -12,36 +12,36 @@ import (
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
BaseURL string `env:"SCN_URL"`
|
BaseURL string `env:"URL"`
|
||||||
GinDebug bool `env:"SCN_GINDEBUG"`
|
GinDebug bool `env:"GINDEBUG"`
|
||||||
LogLevel zerolog.Level `env:"SCN_LOGLEVEL"`
|
LogLevel zerolog.Level `env:"LOGLEVEL"`
|
||||||
ServerIP string `env:"SCN_IP"`
|
ServerIP string `env:"IP"`
|
||||||
ServerPort string `env:"SCN_PORT"`
|
ServerPort string `env:"PORT"`
|
||||||
DBMain DBConfig `env:"SCN_DB_MAIN"`
|
DBMain DBConfig `env:"DB_MAIN"`
|
||||||
DBRequests DBConfig `env:"SCN_DB_REQUESTS"`
|
DBRequests DBConfig `env:"DB_REQUESTS"`
|
||||||
DBLogs DBConfig `env:"SCN_DB_LOGS"`
|
DBLogs DBConfig `env:"DB_LOGS"`
|
||||||
RequestTimeout time.Duration `env:"SCN_REQUEST_TIMEOUT"`
|
RequestTimeout time.Duration `env:"REQUEST_TIMEOUT"`
|
||||||
RequestMaxRetry int `env:"SCN_REQUEST_MAXRETRY"`
|
RequestMaxRetry int `env:"REQUEST_MAXRETRY"`
|
||||||
RequestRetrySleep time.Duration `env:"SCN_REQUEST_RETRYSLEEP"`
|
RequestRetrySleep time.Duration `env:"REQUEST_RETRYSLEEP"`
|
||||||
Cors bool `env:"SCN_CORS"`
|
Cors bool `env:"CORS"`
|
||||||
ReturnRawErrors bool `env:"SCN_ERROR_RETURN"`
|
ReturnRawErrors bool `env:"ERROR_RETURN"`
|
||||||
DummyFirebase bool `env:"SCN_DUMMY_FB"`
|
DummyFirebase bool `env:"DUMMY_FB"`
|
||||||
DummyGoogleAPI bool `env:"SCN_DUMMY_GOOG"`
|
DummyGoogleAPI bool `env:"DUMMY_GOOG"`
|
||||||
FirebaseTokenURI string `env:"SCN_FB_TOKENURI"`
|
FirebaseTokenURI string `env:"FB_TOKENURI"`
|
||||||
FirebaseProjectID string `env:"SCN_FB_PROJECTID"`
|
FirebaseProjectID string `env:"FB_PROJECTID"`
|
||||||
FirebasePrivKeyID string `env:"SCN_FB_PRIVATEKEYID"`
|
FirebasePrivKeyID string `env:"FB_PRIVATEKEYID"`
|
||||||
FirebaseClientMail string `env:"SCN_FB_CLIENTEMAIL"`
|
FirebaseClientMail string `env:"FB_CLIENTEMAIL"`
|
||||||
FirebasePrivateKey string `env:"SCN_FB_PRIVATEKEY"`
|
FirebasePrivateKey string `env:"FB_PRIVATEKEY"`
|
||||||
GoogleAPITokenURI string `env:"SCN_GOOG_TOKENURI"`
|
GoogleAPITokenURI string `env:"GOOG_TOKENURI"`
|
||||||
GoogleAPIPrivKeyID string `env:"SCN_GOOG_PRIVATEKEYID"`
|
GoogleAPIPrivKeyID string `env:"GOOG_PRIVATEKEYID"`
|
||||||
GoogleAPIClientMail string `env:"SCN_GOOG_CLIENTEMAIL"`
|
GoogleAPIClientMail string `env:"GOOG_CLIENTEMAIL"`
|
||||||
GoogleAPIPrivateKey string `env:"SCN_GOOG_PRIVATEKEY"`
|
GoogleAPIPrivateKey string `env:"GOOG_PRIVATEKEY"`
|
||||||
GooglePackageName string `env:"SCN_GOOG_PACKAGENAME"`
|
GooglePackageName string `env:"GOOG_PACKAGENAME"`
|
||||||
GoogleProProductID string `env:"SCN_GOOG_PROPRODUCTID"`
|
GoogleProProductID string `env:"GOOG_PROPRODUCTID"`
|
||||||
ReqLogEnabled bool `env:"SCN_REQUESTLOG_ENABLED"`
|
ReqLogEnabled bool `env:"REQUESTLOG_ENABLED"`
|
||||||
ReqLogMaxBodySize int `env:"SCN_REQUESTLOG_MAXBODYSIZE"`
|
ReqLogMaxBodySize int `env:"REQUESTLOG_MAXBODYSIZE"`
|
||||||
ReqLogHistoryMaxCount int `env:"SCN_REQUESTLOG_HISTORY_MAXCOUNT"`
|
ReqLogHistoryMaxCount int `env:"REQUESTLOG_HISTORY_MAXCOUNT"`
|
||||||
ReqLogHistoryMaxDuration time.Duration `env:"SCN_REQUESTLOG_HISTORY_MAXDURATION"`
|
ReqLogHistoryMaxDuration time.Duration `env:"REQUESTLOG_HISTORY_MAXDURATION"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DBConfig struct {
|
type DBConfig struct {
|
||||||
@ -430,7 +430,7 @@ func GetConfig(ns string) (Config, bool) {
|
|||||||
}
|
}
|
||||||
if cfn, ok := allConfig[ns]; ok {
|
if cfn, ok := allConfig[ns]; ok {
|
||||||
c := cfn()
|
c := cfn()
|
||||||
err := confext.ApplyEnvOverrides(&c, "_")
|
err := confext.ApplyEnvOverrides("SCN_", &c, "_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mode string
|
type Mode string //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CTMStart = "START"
|
CTMStart = "START"
|
||||||
|
@ -32,31 +32,6 @@ func (db *Database) GetChannelByName(ctx TxContext, userid models.UserID, chanNa
|
|||||||
return &channel, nil
|
return &channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) GetChannelByNameAndSendKey(ctx TxContext, chanName string, sendKey string) (*models.Channel, error) {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := tx.Query(ctx, "SELECT * FROM channels WHERE internal_name = :chan_name OR send_key = :send_key LIMIT 1", sq.PP{
|
|
||||||
"chan_name": chanName,
|
|
||||||
"send_key": sendKey,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, err := models.DecodeChannel(rows)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &channel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetChannelByID(ctx TxContext, chanid models.ChannelID) (*models.Channel, error) {
|
func (db *Database) GetChannelByID(ctx TxContext, chanid models.ChannelID) (*models.Channel, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -81,7 +56,7 @@ func (db *Database) GetChannelByID(ctx TxContext, chanid models.ChannelID) (*mod
|
|||||||
return &channel, nil
|
return &channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName string, intName string, subscribeKey string, sendKey string) (models.Channel, error) {
|
func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName string, intName string, subscribeKey string) (models.Channel, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Channel{}, err
|
return models.Channel{}, err
|
||||||
@ -91,14 +66,13 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName
|
|||||||
|
|
||||||
channelid := models.NewChannelID()
|
channelid := models.NewChannelID()
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "INSERT INTO channels (channel_id, owner_user_id, display_name, internal_name, description_name, subscribe_key, send_key, timestamp_created) VALUES (:cid, :ouid, :dnam, :inam, :hnam, :subkey, :sendkey, :ts)", sq.PP{
|
_, err = tx.Exec(ctx, "INSERT INTO channels (channel_id, owner_user_id, display_name, internal_name, description_name, subscribe_key, timestamp_created) VALUES (:cid, :ouid, :dnam, :inam, :hnam, :subkey, :ts)", sq.PP{
|
||||||
"cid": channelid,
|
"cid": channelid,
|
||||||
"ouid": userid,
|
"ouid": userid,
|
||||||
"dnam": dispName,
|
"dnam": dispName,
|
||||||
"inam": intName,
|
"inam": intName,
|
||||||
"hnam": nil,
|
"hnam": nil,
|
||||||
"subkey": subscribeKey,
|
"subkey": subscribeKey,
|
||||||
"sendkey": sendKey,
|
|
||||||
"ts": time2DB(now),
|
"ts": time2DB(now),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -111,7 +85,6 @@ func (db *Database) CreateChannel(ctx TxContext, userid models.UserID, dispName
|
|||||||
DisplayName: dispName,
|
DisplayName: dispName,
|
||||||
InternalName: intName,
|
InternalName: intName,
|
||||||
SubscribeKey: subscribeKey,
|
SubscribeKey: subscribeKey,
|
||||||
SendKey: sendKey,
|
|
||||||
TimestampCreated: now,
|
TimestampCreated: now,
|
||||||
TimestampLastSent: nil,
|
TimestampLastSent: nil,
|
||||||
MessagesSent: 0,
|
MessagesSent: 0,
|
||||||
@ -244,23 +217,6 @@ func (db *Database) IncChannelMessageCounter(ctx TxContext, channel models.Chann
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) UpdateChannelSendKey(ctx TxContext, channelid models.ChannelID, newkey string) error {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "UPDATE channels SET send_key = :key WHERE channel_id = :cid", sq.PP{
|
|
||||||
"key": newkey,
|
|
||||||
"cid": channelid,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.ChannelID, newkey string) error {
|
func (db *Database) UpdateChannelSubscribeKey(ctx TxContext, channelid models.ChannelID, newkey string) error {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
227
scnserver/db/impl/primary/keytokens.go
Normal file
227
scnserver/db/impl/primary/keytokens.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package primary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
|
"database/sql"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (db *Database) CreateKeyToken(ctx TxContext, name string, owner models.UserID, allChannels bool, channels []models.ChannelID, permissions models.TokenPermissionList, token string) (models.KeyToken, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return models.KeyToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
|
||||||
|
keyTokenid := models.NewKeyTokenID()
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "INSERT INTO keytokens (keytoken_id, name, timestamp_created, owner_user_id, all_channels, channels, token, permissions) VALUES (:tid, :nam, :tsc, :owr, :all, :cha, :tok, :prm)", sq.PP{
|
||||||
|
"tid": keyTokenid,
|
||||||
|
"nam": name,
|
||||||
|
"tsc": time2DB(now),
|
||||||
|
"owr": owner.String(),
|
||||||
|
"all": bool2DB(allChannels),
|
||||||
|
"cha": strings.Join(langext.ArrMap(channels, func(v models.ChannelID) string { return v.String() }), ";"),
|
||||||
|
"tok": token,
|
||||||
|
"prm": permissions.String(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return models.KeyToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.KeyToken{
|
||||||
|
KeyTokenID: keyTokenid,
|
||||||
|
Name: name,
|
||||||
|
TimestampCreated: now,
|
||||||
|
TimestampLastUsed: nil,
|
||||||
|
OwnerUserID: owner,
|
||||||
|
AllChannels: allChannels,
|
||||||
|
Channels: channels,
|
||||||
|
Token: token,
|
||||||
|
Permissions: permissions,
|
||||||
|
MessagesSent: 0,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) ListKeyTokens(ctx TxContext, ownerID models.UserID) ([]models.KeyToken, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := tx.Query(ctx, "SELECT * FROM keytokens WHERE owner_user_id = :uid ORDER BY keytokens.timestamp_created DESC, keytokens.keytoken_id ASC", sq.PP{"uid": ownerID})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := models.DecodeKeyTokens(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetKeyToken(ctx TxContext, userid models.UserID, keyTokenid models.KeyTokenID) (models.KeyToken, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return models.KeyToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := tx.Query(ctx, "SELECT * FROM keytokens WHERE owner_user_id = :uid AND keytoken_id = :cid LIMIT 1", sq.PP{
|
||||||
|
"uid": userid,
|
||||||
|
"cid": keyTokenid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return models.KeyToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyToken, err := models.DecodeKeyToken(rows)
|
||||||
|
if err != nil {
|
||||||
|
return models.KeyToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) GetKeyTokenByToken(ctx TxContext, key string) (*models.KeyToken, error) {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := tx.Query(ctx, "SELECT * FROM keytokens WHERE token = :key LIMIT 1", sq.PP{"key": key})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := models.DecodeKeyToken(rows)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) DeleteKeyToken(ctx TxContext, keyTokenid models.KeyTokenID) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "DELETE FROM keytokens WHERE keytoken_id = :tid", sq.PP{"tid": keyTokenid})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) UpdateKeyTokenName(ctx TxContext, keyTokenid models.KeyTokenID, name string) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "UPDATE keytokens SET name = :nam WHERE keytoken_id = :tid", sq.PP{
|
||||||
|
"nam": name,
|
||||||
|
"tid": keyTokenid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) UpdateKeyTokenPermissions(ctx TxContext, keyTokenid models.KeyTokenID, perm models.TokenPermissionList) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "UPDATE keytokens SET permissions = :prm WHERE keytoken_id = :tid", sq.PP{
|
||||||
|
"tid": keyTokenid,
|
||||||
|
"prm": perm.String(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) UpdateKeyTokenAllChannels(ctx TxContext, keyTokenid models.KeyTokenID, allChannels bool) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "UPDATE keytokens SET all_channels = :all WHERE keytoken_id = :tid", sq.PP{
|
||||||
|
"tid": keyTokenid,
|
||||||
|
"all": bool2DB(allChannels),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) UpdateKeyTokenChannels(ctx TxContext, keyTokenid models.KeyTokenID, channels []models.ChannelID) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "UPDATE keytokens SET channels = :cha WHERE keytoken_id = :tid", sq.PP{
|
||||||
|
"tid": keyTokenid,
|
||||||
|
"cha": strings.Join(langext.ArrMap(channels, func(v models.ChannelID) string { return v.String() }), ";"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) IncKeyTokenMessageCounter(ctx TxContext, keyTokenid models.KeyTokenID) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "UPDATE keytokens SET messages_sent = messages_sent + 1, timestamp_lastused = :ts WHERE keytoken_id = :tid", sq.PP{
|
||||||
|
"ts": time2DB(time.Now()),
|
||||||
|
"tid": keyTokenid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Database) UpdateKeyTokenLastUsed(ctx TxContext, keyTokenid models.KeyTokenID) error {
|
||||||
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Exec(ctx, "UPDATE keytokens SET timestamp_lastused = :ts WHERE keytoken_id = :tid", sq.PP{
|
||||||
|
"ts": time2DB(time.Now()),
|
||||||
|
"tid": keyTokenid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -4,10 +4,6 @@ CREATE TABLE users
|
|||||||
|
|
||||||
username TEXT NULL DEFAULT NULL,
|
username TEXT NULL DEFAULT NULL,
|
||||||
|
|
||||||
send_key TEXT NOT NULL,
|
|
||||||
read_key TEXT NOT NULL,
|
|
||||||
admin_key TEXT NOT NULL,
|
|
||||||
|
|
||||||
timestamp_created INTEGER NOT NULL,
|
timestamp_created INTEGER NOT NULL,
|
||||||
timestamp_lastread INTEGER NULL DEFAULT NULL,
|
timestamp_lastread INTEGER NULL DEFAULT NULL,
|
||||||
timestamp_lastsent INTEGER NULL DEFAULT NULL,
|
timestamp_lastsent INTEGER NULL DEFAULT NULL,
|
||||||
@ -25,6 +21,29 @@ CREATE TABLE users
|
|||||||
CREATE UNIQUE INDEX "idx_users_protoken" ON users (pro_token) WHERE pro_token IS NOT NULL;
|
CREATE UNIQUE INDEX "idx_users_protoken" ON users (pro_token) WHERE pro_token IS NOT NULL;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE keytokens
|
||||||
|
(
|
||||||
|
keytoken_id TEXT NOT NULL,
|
||||||
|
|
||||||
|
timestamp_created INTEGER NOT NULL,
|
||||||
|
timestamp_lastused INTEGER NULL DEFAULT NULL,
|
||||||
|
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
|
||||||
|
owner_user_id TEXT NOT NULL,
|
||||||
|
|
||||||
|
all_channels INTEGER CHECK(all_channels IN (0, 1)) NOT NULL,
|
||||||
|
channels TEXT NOT NULL,
|
||||||
|
token TEXT NOT NULL,
|
||||||
|
permissions TEXT NOT NULL,
|
||||||
|
|
||||||
|
messages_sent INTEGER NOT NULL DEFAULT '0',
|
||||||
|
|
||||||
|
PRIMARY KEY (keytoken_id)
|
||||||
|
) STRICT;
|
||||||
|
CREATE UNIQUE INDEX "idx_keytokens_token" ON keytokens (token);
|
||||||
|
|
||||||
|
|
||||||
CREATE TABLE clients
|
CREATE TABLE clients
|
||||||
(
|
(
|
||||||
client_id TEXT NOT NULL,
|
client_id TEXT NOT NULL,
|
||||||
@ -55,7 +74,6 @@ CREATE TABLE channels
|
|||||||
description_name TEXT NULL,
|
description_name TEXT NULL,
|
||||||
|
|
||||||
subscribe_key TEXT NOT NULL,
|
subscribe_key TEXT NOT NULL,
|
||||||
send_key TEXT NOT NULL,
|
|
||||||
|
|
||||||
timestamp_created INTEGER NOT NULL,
|
timestamp_created INTEGER NOT NULL,
|
||||||
timestamp_lastsent INTEGER NULL DEFAULT NULL,
|
timestamp_lastsent INTEGER NULL DEFAULT NULL,
|
||||||
|
@ -3,12 +3,11 @@ package primary
|
|||||||
import (
|
import (
|
||||||
scn "blackforestbytes.com/simplecloudnotifier"
|
scn "blackforestbytes.com/simplecloudnotifier"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"database/sql"
|
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (db *Database) CreateUser(ctx TxContext, readKey string, sendKey string, adminKey string, protoken *string, username *string) (models.User, error) {
|
func (db *Database) CreateUser(ctx TxContext, protoken *string, username *string) (models.User, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.User{}, err
|
return models.User{}, err
|
||||||
@ -18,12 +17,9 @@ func (db *Database) CreateUser(ctx TxContext, readKey string, sendKey string, ad
|
|||||||
|
|
||||||
userid := models.NewUserID()
|
userid := models.NewUserID()
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "INSERT INTO users (user_id, username, read_key, send_key, admin_key, is_pro, pro_token, timestamp_created) VALUES (:uid, :un, :rk, :sk, :ak, :pro, :tok, :ts)", sq.PP{
|
_, err = tx.Exec(ctx, "INSERT INTO users (user_id, username, is_pro, pro_token, timestamp_created) VALUES (:uid, :un, :pro, :tok, :ts)", sq.PP{
|
||||||
"uid": userid,
|
"uid": userid,
|
||||||
"un": username,
|
"un": username,
|
||||||
"rk": readKey,
|
|
||||||
"sk": sendKey,
|
|
||||||
"ak": adminKey,
|
|
||||||
"pro": bool2DB(protoken != nil),
|
"pro": bool2DB(protoken != nil),
|
||||||
"tok": protoken,
|
"tok": protoken,
|
||||||
"ts": time2DB(now),
|
"ts": time2DB(now),
|
||||||
@ -35,9 +31,6 @@ func (db *Database) CreateUser(ctx TxContext, readKey string, sendKey string, ad
|
|||||||
return models.User{
|
return models.User{
|
||||||
UserID: userid,
|
UserID: userid,
|
||||||
Username: username,
|
Username: username,
|
||||||
ReadKey: readKey,
|
|
||||||
SendKey: sendKey,
|
|
||||||
AdminKey: adminKey,
|
|
||||||
TimestampCreated: now,
|
TimestampCreated: now,
|
||||||
TimestampLastRead: nil,
|
TimestampLastRead: nil,
|
||||||
TimestampLastSent: nil,
|
TimestampLastSent: nil,
|
||||||
@ -63,28 +56,6 @@ func (db *Database) ClearProTokens(ctx TxContext, protoken string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) GetUserByKey(ctx TxContext, key string) (*models.User, error) {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := tx.Query(ctx, "SELECT * FROM users WHERE admin_key = :key OR send_key = :key OR read_key = :key LIMIT 1", sq.PP{"key": key})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := models.DecodeUser(rows)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetUser(ctx TxContext, userid models.UserID) (models.User, error) {
|
func (db *Database) GetUser(ctx TxContext, userid models.UserID) (models.User, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -177,73 +148,3 @@ func (db *Database) UpdateUserLastRead(ctx TxContext, userid models.UserID) erro
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) UpdateUserKeys(ctx TxContext, userid models.UserID, sendKey string, readKey string, adminKey string) error {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "UPDATE users SET send_key = :sk, read_key = :rk, admin_key = :ak WHERE user_id = :uid", sq.PP{
|
|
||||||
"sk": sendKey,
|
|
||||||
"rk": readKey,
|
|
||||||
"ak": adminKey,
|
|
||||||
"uid": userid,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) UpdateUserSendKey(ctx TxContext, userid models.UserID, newkey string) error {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "UPDATE users SET send_key = :sk WHERE user_id = :uid", sq.PP{
|
|
||||||
"sk": newkey,
|
|
||||||
"uid": userid,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) UpdateUserReadKey(ctx TxContext, userid models.UserID, newkey string) error {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "UPDATE users SET read_key = :rk WHERE user_id = :uid", sq.PP{
|
|
||||||
"rk": newkey,
|
|
||||||
"uid": userid,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) UpdateUserAdminKey(ctx TxContext, userid models.UserID, newkey string) error {
|
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = tx.Exec(ctx, "UPDATE users SET admin_key = :ak WHERE user_id = :uid", sq.PP{
|
|
||||||
"ak": newkey,
|
|
||||||
"uid": userid,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -9,7 +9,7 @@ require (
|
|||||||
github.com/jmoiron/sqlx v1.3.5
|
github.com/jmoiron/sqlx v1.3.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/rs/zerolog v1.28.0
|
github.com/rs/zerolog v1.28.0
|
||||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.59
|
gogs.mikescher.com/BlackForestBytes/goext v0.0.103
|
||||||
gopkg.in/loremipsum.v1 v1.1.0
|
gopkg.in/loremipsum.v1 v1.1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,6 +81,8 @@ gogs.mikescher.com/BlackForestBytes/goext v0.0.58 h1:W53yfHhpFQS13zgtzCjfJQ42WG0
|
|||||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.58/go.mod h1:ZEXyKUr8t0EKdPN1FYdk0klY7N8OwXxipGE9lWgpVE8=
|
gogs.mikescher.com/BlackForestBytes/goext v0.0.58/go.mod h1:ZEXyKUr8t0EKdPN1FYdk0klY7N8OwXxipGE9lWgpVE8=
|
||||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.59 h1:3bHSjqgty9yp0EIyqwGAb06ZS7bLvm806zRj6j+WOEE=
|
gogs.mikescher.com/BlackForestBytes/goext v0.0.59 h1:3bHSjqgty9yp0EIyqwGAb06ZS7bLvm806zRj6j+WOEE=
|
||||||
gogs.mikescher.com/BlackForestBytes/goext v0.0.59/go.mod h1:ZEXyKUr8t0EKdPN1FYdk0klY7N8OwXxipGE9lWgpVE8=
|
gogs.mikescher.com/BlackForestBytes/goext v0.0.59/go.mod h1:ZEXyKUr8t0EKdPN1FYdk0klY7N8OwXxipGE9lWgpVE8=
|
||||||
|
gogs.mikescher.com/BlackForestBytes/goext v0.0.103 h1:CkRVpRrTlq9k3mdTNGQAr4cxaXHsKdUJNjHt5Maas4k=
|
||||||
|
gogs.mikescher.com/BlackForestBytes/goext v0.0.103/go.mod h1:w8JlyUHpoOJmW5GxsiheZkFh3vn8Mp80ynSVOFLszL0=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
||||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
||||||
|
@ -35,7 +35,7 @@ func NewAndroidPublisherAPI(conf scn.Config) (AndroidPublisherClient, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseType int
|
type PurchaseType int //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PurchaseTypeTest PurchaseType = 0 // i.e. purchased from a license testing account
|
PurchaseTypeTest PurchaseType = 0 // i.e. purchased from a license testing account
|
||||||
@ -43,14 +43,14 @@ const (
|
|||||||
PurchaseTypeRewarded PurchaseType = 2 // i.e. from watching a video ad instead of paying
|
PurchaseTypeRewarded PurchaseType = 2 // i.e. from watching a video ad instead of paying
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConsumptionState int
|
type ConsumptionState int //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConsumptionStateYetToBeConsumed ConsumptionState = 0
|
ConsumptionStateYetToBeConsumed ConsumptionState = 0
|
||||||
ConsumptionStateConsumed ConsumptionState = 1
|
ConsumptionStateConsumed ConsumptionState = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
type PurchaseState int
|
type PurchaseState int //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PurchaseStatePurchased PurchaseState = 0
|
PurchaseStatePurchased PurchaseState = 0
|
||||||
@ -58,7 +58,7 @@ const (
|
|||||||
PurchaseStatePending PurchaseState = 2
|
PurchaseStatePending PurchaseState = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
type AcknowledgementState int
|
type AcknowledgementState int //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AcknowledgementStateYetToBeAcknowledged AcknowledgementState = 0
|
AcknowledgementStateYetToBeAcknowledged AcknowledgementState = 0
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AppContext struct {
|
type AppContext struct {
|
||||||
|
app *Application
|
||||||
inner context.Context
|
inner context.Context
|
||||||
cancelFunc context.CancelFunc
|
cancelFunc context.CancelFunc
|
||||||
cancelled bool
|
cancelled bool
|
||||||
@ -22,8 +23,9 @@ type AppContext struct {
|
|||||||
ginContext *gin.Context
|
ginContext *gin.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAppContext(g *gin.Context, innerCtx context.Context, cancelFn context.CancelFunc) *AppContext {
|
func CreateAppContext(app *Application, g *gin.Context, innerCtx context.Context, cancelFn context.CancelFunc) *AppContext {
|
||||||
return &AppContext{
|
return &AppContext{
|
||||||
|
app: app,
|
||||||
inner: innerCtx,
|
inner: innerCtx,
|
||||||
cancelFunc: cancelFn,
|
cancelFunc: cancelFn,
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
|
@ -248,7 +248,7 @@ func (app *Application) StartRequest(g *gin.Context, uri any, query any, body an
|
|||||||
}
|
}
|
||||||
|
|
||||||
ictx, cancel := context.WithTimeout(context.Background(), app.Config.RequestTimeout)
|
ictx, cancel := context.WithTimeout(context.Background(), app.Config.RequestTimeout)
|
||||||
actx := CreateAppContext(g, ictx, cancel)
|
actx := CreateAppContext(app, g, ictx, cancel)
|
||||||
|
|
||||||
authheader := g.GetHeader("Authorization")
|
authheader := g.GetHeader("Authorization")
|
||||||
|
|
||||||
@ -280,19 +280,19 @@ func (app *Application) getPermissions(ctx *AppContext, hdr string) (models.Perm
|
|||||||
|
|
||||||
key := strings.TrimSpace(hdr[4:])
|
key := strings.TrimSpace(hdr[4:])
|
||||||
|
|
||||||
user, err := app.Database.Primary.GetUserByKey(ctx, key)
|
tok, err := app.Database.Primary.GetKeyTokenByToken(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.PermissionSet{}, err
|
return models.PermissionSet{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user != nil && user.SendKey == key {
|
if tok != nil {
|
||||||
return models.PermissionSet{UserID: langext.Ptr(user.UserID), KeyType: models.PermKeyTypeUserSend}, nil
|
|
||||||
|
err = app.Database.Primary.UpdateKeyTokenLastUsed(ctx, tok.KeyTokenID)
|
||||||
|
if err != nil {
|
||||||
|
return models.PermissionSet{}, err
|
||||||
}
|
}
|
||||||
if user != nil && user.ReadKey == key {
|
|
||||||
return models.PermissionSet{UserID: langext.Ptr(user.UserID), KeyType: models.PermKeyTypeUserRead}, nil
|
return models.PermissionSet{Token: tok}, nil
|
||||||
}
|
|
||||||
if user != nil && user.AdminKey == key {
|
|
||||||
return models.PermissionSet{UserID: langext.Ptr(user.UserID), KeyType: models.PermKeyTypeUserAdmin}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.NewEmptyPermissions(), nil
|
return models.NewEmptyPermissions(), nil
|
||||||
@ -309,9 +309,8 @@ func (app *Application) GetOrCreateChannel(ctx *AppContext, userid models.UserID
|
|||||||
}
|
}
|
||||||
|
|
||||||
subscribeKey := app.GenerateRandomAuthKey()
|
subscribeKey := app.GenerateRandomAuthKey()
|
||||||
sendKey := app.GenerateRandomAuthKey()
|
|
||||||
|
|
||||||
newChan, err := app.Database.Primary.CreateChannel(ctx, userid, displayChanName, intChanName, subscribeKey, sendKey)
|
newChan, err := app.Database.Primary.CreateChannel(ctx, userid, displayChanName, intChanName, subscribeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Channel{}, err
|
return models.Channel{}, err
|
||||||
}
|
}
|
||||||
|
@ -4,94 +4,116 @@ import (
|
|||||||
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
"blackforestbytes.com/simplecloudnotifier/api/apierr"
|
||||||
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
"blackforestbytes.com/simplecloudnotifier/api/ginresp"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
|
"database/sql"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ac *AppContext) CheckPermissionUserRead(userid models.UserID) *ginresp.HTTPResponse {
|
func (ac *AppContext) CheckPermissionUserRead(userid models.UserID) *ginresp.HTTPResponse {
|
||||||
p := ac.permissions
|
p := ac.permissions
|
||||||
if p.UserID != nil && *p.UserID == userid && p.KeyType == models.PermKeyTypeUserRead {
|
if p.Token != nil && p.Token.IsUserRead(userid) {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if p.UserID != nil && *p.UserID == userid && p.KeyType == models.PermKeyTypeUserAdmin {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppContext) CheckPermissionRead() *ginresp.HTTPResponse {
|
func (ac *AppContext) CheckPermissionSelfAllMessagesRead() *ginresp.HTTPResponse {
|
||||||
p := ac.permissions
|
p := ac.permissions
|
||||||
if p.UserID != nil && p.KeyType == models.PermKeyTypeUserRead {
|
if p.Token != nil && p.Token.IsAllMessagesRead(p.Token.OwnerUserID) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if p.UserID != nil && p.KeyType == models.PermKeyTypeUserAdmin {
|
|
||||||
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *AppContext) CheckPermissionAllMessagesRead(userid models.UserID) *ginresp.HTTPResponse {
|
||||||
|
p := ac.permissions
|
||||||
|
if p.Token != nil && p.Token.IsAllMessagesRead(userid) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ac *AppContext) CheckPermissionChanMessagesRead(channel models.Channel) *ginresp.HTTPResponse {
|
||||||
|
p := ac.permissions
|
||||||
|
if p.Token != nil && p.Token.IsChannelMessagesRead(channel.ChannelID) {
|
||||||
|
|
||||||
|
if channel.OwnerUserID == p.Token.OwnerUserID {
|
||||||
|
return nil // owned channel
|
||||||
|
} else {
|
||||||
|
sub, err := ac.app.Database.Primary.GetSubscriptionBySubscriber(ac, p.Token.OwnerUserID, channel.ChannelID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 500, apierr.DATABASE_ERROR, "Failed to query subscription", err))
|
||||||
|
}
|
||||||
|
if !sub.Confirmed {
|
||||||
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
|
||||||
func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginresp.HTTPResponse {
|
func (ac *AppContext) CheckPermissionUserAdmin(userid models.UserID) *ginresp.HTTPResponse {
|
||||||
p := ac.permissions
|
p := ac.permissions
|
||||||
if p.UserID != nil && *p.UserID == userid && p.KeyType == models.PermKeyTypeUserAdmin {
|
if p.Token != nil && p.Token.IsAdmin(userid) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppContext) CheckPermissionSend() *ginresp.HTTPResponse {
|
func (ac *AppContext) CheckPermissionSend(channel models.Channel, key string) (*models.KeyToken, *ginresp.HTTPResponse) {
|
||||||
|
|
||||||
|
keytok, err := ac.app.Database.Primary.GetKeyTokenByToken(ac, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, langext.Ptr(ginresp.APIError(ac.ginContext, 500, apierr.DATABASE_ERROR, "Failed to query token", err))
|
||||||
|
}
|
||||||
|
if keytok == nil {
|
||||||
|
return nil, langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
if keytok.IsChannelMessagesSend(channel) {
|
||||||
|
return keytok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *AppContext) CheckPermissionMessageRead(msg models.Message) bool {
|
||||||
p := ac.permissions
|
p := ac.permissions
|
||||||
if p.UserID != nil && p.KeyType == models.PermKeyTypeUserSend {
|
if p.Token != nil && p.Token.IsChannelMessagesRead(msg.ChannelID) {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if p.UserID != nil && p.KeyType == models.PermKeyTypeUserAdmin {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AppContext) CheckPermissionAny() *ginresp.HTTPResponse {
|
|
||||||
p := ac.permissions
|
|
||||||
if p.KeyType == models.PermKeyTypeNone {
|
|
||||||
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AppContext) CheckPermissionMessageReadDirect(msg models.Message) bool {
|
|
||||||
p := ac.permissions
|
|
||||||
if p.UserID != nil && msg.OwnerUserID == *p.UserID && p.KeyType == models.PermKeyTypeUserRead {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if p.UserID != nil && msg.OwnerUserID == *p.UserID && p.KeyType == models.PermKeyTypeUserAdmin {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ac *AppContext) CheckPermissionAny() *ginresp.HTTPResponse {
|
||||||
|
p := ac.permissions
|
||||||
|
if p.Token == nil {
|
||||||
|
return langext.Ptr(ginresp.APIError(ac.ginContext, 401, apierr.USER_AUTH_FAILED, "You are not authorized for this action", nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ac *AppContext) GetPermissionUserID() *models.UserID {
|
func (ac *AppContext) GetPermissionUserID() *models.UserID {
|
||||||
if ac.permissions.UserID == nil {
|
if ac.permissions.Token == nil {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return langext.Ptr(*ac.permissions.UserID)
|
return langext.Ptr(ac.permissions.Token.OwnerUserID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppContext) IsPermissionUserRead() bool {
|
func (ac *AppContext) GetPermissionKeyTokenID() *models.KeyTokenID {
|
||||||
p := ac.permissions
|
if ac.permissions.Token == nil {
|
||||||
return p.KeyType == models.PermKeyTypeUserRead || p.KeyType == models.PermKeyTypeUserAdmin
|
return nil
|
||||||
|
} else {
|
||||||
|
return langext.Ptr(ac.permissions.Token.KeyTokenID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppContext) IsPermissionUserSend() bool {
|
|
||||||
p := ac.permissions
|
|
||||||
return p.KeyType == models.PermKeyTypeUserSend || p.KeyType == models.PermKeyTypeUserAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ac *AppContext) IsPermissionUserAdmin() bool {
|
|
||||||
p := ac.permissions
|
|
||||||
return p.KeyType == models.PermKeyTypeUserAdmin
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ type Channel struct {
|
|||||||
DisplayName string
|
DisplayName string
|
||||||
DescriptionName *string
|
DescriptionName *string
|
||||||
SubscribeKey string
|
SubscribeKey string
|
||||||
SendKey string
|
|
||||||
TimestampCreated time.Time
|
TimestampCreated time.Time
|
||||||
TimestampLastSent *time.Time
|
TimestampLastSent *time.Time
|
||||||
MessagesSent int
|
MessagesSent int
|
||||||
@ -28,7 +27,6 @@ func (c Channel) JSON(includeKey bool) ChannelJSON {
|
|||||||
DisplayName: c.DisplayName,
|
DisplayName: c.DisplayName,
|
||||||
DescriptionName: c.DescriptionName,
|
DescriptionName: c.DescriptionName,
|
||||||
SubscribeKey: langext.Conditional(includeKey, langext.Ptr(c.SubscribeKey), nil),
|
SubscribeKey: langext.Conditional(includeKey, langext.Ptr(c.SubscribeKey), nil),
|
||||||
SendKey: langext.Conditional(includeKey, langext.Ptr(c.SendKey), nil),
|
|
||||||
TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano),
|
TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano),
|
||||||
TimestampLastSent: timeOptFmt(c.TimestampLastSent, time.RFC3339Nano),
|
TimestampLastSent: timeOptFmt(c.TimestampLastSent, time.RFC3339Nano),
|
||||||
MessagesSent: c.MessagesSent,
|
MessagesSent: c.MessagesSent,
|
||||||
@ -65,7 +63,6 @@ type ChannelJSON struct {
|
|||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
DescriptionName *string `json:"description_name"`
|
DescriptionName *string `json:"description_name"`
|
||||||
SubscribeKey *string `json:"subscribe_key"` // can be nil, depending on endpoint
|
SubscribeKey *string `json:"subscribe_key"` // can be nil, depending on endpoint
|
||||||
SendKey *string `json:"send_key"` // can be nil, depending on endpoint
|
|
||||||
TimestampCreated string `json:"timestamp_created"`
|
TimestampCreated string `json:"timestamp_created"`
|
||||||
TimestampLastSent *string `json:"timestamp_lastsent"`
|
TimestampLastSent *string `json:"timestamp_lastsent"`
|
||||||
MessagesSent int `json:"messages_sent"`
|
MessagesSent int `json:"messages_sent"`
|
||||||
@ -98,8 +95,7 @@ func (c ChannelDB) Model() Channel {
|
|||||||
DisplayName: c.DisplayName,
|
DisplayName: c.DisplayName,
|
||||||
DescriptionName: c.DescriptionName,
|
DescriptionName: c.DescriptionName,
|
||||||
SubscribeKey: c.SubscribeKey,
|
SubscribeKey: c.SubscribeKey,
|
||||||
SendKey: c.SendKey,
|
TimestampCreated: timeFromMilli(c.TimestampCreated),
|
||||||
TimestampCreated: time.UnixMilli(c.TimestampCreated),
|
|
||||||
TimestampLastSent: timeOptFromMilli(c.TimestampLastSent),
|
TimestampLastSent: timeOptFromMilli(c.TimestampLastSent),
|
||||||
MessagesSent: c.MessagesSent,
|
MessagesSent: c.MessagesSent,
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientType string
|
type ClientType string //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ClientTypeAndroid ClientType = "ANDROID"
|
ClientTypeAndroid ClientType = "ANDROID"
|
||||||
@ -62,7 +62,7 @@ func (c ClientDB) Model() Client {
|
|||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Type: c.Type,
|
Type: c.Type,
|
||||||
FCMToken: c.FCMToken,
|
FCMToken: c.FCMToken,
|
||||||
TimestampCreated: time.UnixMilli(c.TimestampCreated),
|
TimestampCreated: timeFromMilli(c.TimestampCreated),
|
||||||
AgentModel: c.AgentModel,
|
AgentModel: c.AgentModel,
|
||||||
AgentVersion: c.AgentVersion,
|
AgentVersion: c.AgentVersion,
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeliveryStatus string
|
type DeliveryStatus string //@enum:type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DeliveryStatusRetry DeliveryStatus = "RETRY"
|
DeliveryStatusRetry DeliveryStatus = "RETRY"
|
||||||
@ -79,7 +79,7 @@ func (d DeliveryDB) Model() Delivery {
|
|||||||
MessageID: d.MessageID,
|
MessageID: d.MessageID,
|
||||||
ReceiverUserID: d.ReceiverUserID,
|
ReceiverUserID: d.ReceiverUserID,
|
||||||
ReceiverClientID: d.ReceiverClientID,
|
ReceiverClientID: d.ReceiverClientID,
|
||||||
TimestampCreated: time.UnixMilli(d.TimestampCreated),
|
TimestampCreated: timeFromMilli(d.TimestampCreated),
|
||||||
TimestampFinalized: timeOptFromMilli(d.TimestampFinalized),
|
TimestampFinalized: timeOptFromMilli(d.TimestampFinalized),
|
||||||
Status: d.Status,
|
Status: d.Status,
|
||||||
RetryCount: d.RetryCount,
|
RetryCount: d.RetryCount,
|
||||||
|
242
scnserver/models/enums_gen.go
Normal file
242
scnserver/models/enums_gen.go
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
// Code generated by permissions_gen.sh DO NOT EDIT.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
|
||||||
|
type Enum interface {
|
||||||
|
Valid() bool
|
||||||
|
ValuesAny() []any
|
||||||
|
ValuesMeta() []EnumMetaValue
|
||||||
|
VarName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringEnum interface {
|
||||||
|
Enum
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DescriptionEnum interface {
|
||||||
|
Enum
|
||||||
|
Description() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnumMetaValue struct {
|
||||||
|
VarName string `json:"varName"`
|
||||||
|
Value any `json:"value"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ ClientType ================================
|
||||||
|
//
|
||||||
|
// File: client.go
|
||||||
|
// StringEnum: true
|
||||||
|
// DescrEnum: false
|
||||||
|
//
|
||||||
|
|
||||||
|
var __ClientTypeValues = []ClientType{
|
||||||
|
ClientTypeAndroid,
|
||||||
|
ClientTypeIOS,
|
||||||
|
}
|
||||||
|
|
||||||
|
var __ClientTypeVarnames = map[ClientType]string{
|
||||||
|
ClientTypeAndroid: "ClientTypeAndroid",
|
||||||
|
ClientTypeIOS: "ClientTypeIOS",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ClientType) Valid() bool {
|
||||||
|
return langext.InArray(e, __ClientTypeValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ClientType) Values() []ClientType {
|
||||||
|
return __ClientTypeValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ClientType) ValuesAny() []any {
|
||||||
|
return langext.ArrCastToAny(__ClientTypeValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ClientType) ValuesMeta() []EnumMetaValue {
|
||||||
|
return []EnumMetaValue{
|
||||||
|
EnumMetaValue{VarName: "ClientTypeAndroid", Value: ClientTypeAndroid, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "ClientTypeIOS", Value: ClientTypeIOS, Description: nil},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ClientType) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ClientType) VarName() string {
|
||||||
|
if d, ok := __ClientTypeVarnames[e]; ok {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseClientType(vv string) (ClientType, bool) {
|
||||||
|
for _, ev := range __ClientTypeValues {
|
||||||
|
if string(ev) == vv {
|
||||||
|
return ev, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientTypeValues() []ClientType {
|
||||||
|
return __ClientTypeValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClientTypeValuesMeta() []EnumMetaValue {
|
||||||
|
return []EnumMetaValue{
|
||||||
|
EnumMetaValue{VarName: "ClientTypeAndroid", Value: ClientTypeAndroid, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "ClientTypeIOS", Value: ClientTypeIOS, Description: nil},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ DeliveryStatus ================================
|
||||||
|
//
|
||||||
|
// File: delivery.go
|
||||||
|
// StringEnum: true
|
||||||
|
// DescrEnum: false
|
||||||
|
//
|
||||||
|
|
||||||
|
var __DeliveryStatusValues = []DeliveryStatus{
|
||||||
|
DeliveryStatusRetry,
|
||||||
|
DeliveryStatusSuccess,
|
||||||
|
DeliveryStatusFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
var __DeliveryStatusVarnames = map[DeliveryStatus]string{
|
||||||
|
DeliveryStatusRetry: "DeliveryStatusRetry",
|
||||||
|
DeliveryStatusSuccess: "DeliveryStatusSuccess",
|
||||||
|
DeliveryStatusFailed: "DeliveryStatusFailed",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DeliveryStatus) Valid() bool {
|
||||||
|
return langext.InArray(e, __DeliveryStatusValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DeliveryStatus) Values() []DeliveryStatus {
|
||||||
|
return __DeliveryStatusValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DeliveryStatus) ValuesAny() []any {
|
||||||
|
return langext.ArrCastToAny(__DeliveryStatusValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DeliveryStatus) ValuesMeta() []EnumMetaValue {
|
||||||
|
return []EnumMetaValue{
|
||||||
|
EnumMetaValue{VarName: "DeliveryStatusRetry", Value: DeliveryStatusRetry, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "DeliveryStatusSuccess", Value: DeliveryStatusSuccess, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "DeliveryStatusFailed", Value: DeliveryStatusFailed, Description: nil},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DeliveryStatus) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e DeliveryStatus) VarName() string {
|
||||||
|
if d, ok := __DeliveryStatusVarnames[e]; ok {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDeliveryStatus(vv string) (DeliveryStatus, bool) {
|
||||||
|
for _, ev := range __DeliveryStatusValues {
|
||||||
|
if string(ev) == vv {
|
||||||
|
return ev, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeliveryStatusValues() []DeliveryStatus {
|
||||||
|
return __DeliveryStatusValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeliveryStatusValuesMeta() []EnumMetaValue {
|
||||||
|
return []EnumMetaValue{
|
||||||
|
EnumMetaValue{VarName: "DeliveryStatusRetry", Value: DeliveryStatusRetry, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "DeliveryStatusSuccess", Value: DeliveryStatusSuccess, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "DeliveryStatusFailed", Value: DeliveryStatusFailed, Description: nil},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ TokenPerm ================================
|
||||||
|
//
|
||||||
|
// File: keytoken.go
|
||||||
|
// StringEnum: true
|
||||||
|
// DescrEnum: false
|
||||||
|
//
|
||||||
|
|
||||||
|
var __TokenPermValues = []TokenPerm{
|
||||||
|
PermAdmin,
|
||||||
|
PermChannelRead,
|
||||||
|
PermChannelSend,
|
||||||
|
PermUserRead,
|
||||||
|
}
|
||||||
|
|
||||||
|
var __TokenPermVarnames = map[TokenPerm]string{
|
||||||
|
PermAdmin: "PermAdmin",
|
||||||
|
PermChannelRead: "PermChannelRead",
|
||||||
|
PermChannelSend: "PermChannelSend",
|
||||||
|
PermUserRead: "PermUserRead",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPerm) Valid() bool {
|
||||||
|
return langext.InArray(e, __TokenPermValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPerm) Values() []TokenPerm {
|
||||||
|
return __TokenPermValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPerm) ValuesAny() []any {
|
||||||
|
return langext.ArrCastToAny(__TokenPermValues)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPerm) ValuesMeta() []EnumMetaValue {
|
||||||
|
return []EnumMetaValue{
|
||||||
|
EnumMetaValue{VarName: "PermAdmin", Value: PermAdmin, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "PermChannelRead", Value: PermChannelRead, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "PermChannelSend", Value: PermChannelSend, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "PermUserRead", Value: PermUserRead, Description: nil},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPerm) String() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPerm) VarName() string {
|
||||||
|
if d, ok := __TokenPermVarnames[e]; ok {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTokenPerm(vv string) (TokenPerm, bool) {
|
||||||
|
for _, ev := range __TokenPermValues {
|
||||||
|
if string(ev) == vv {
|
||||||
|
return ev, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenPermValues() []TokenPerm {
|
||||||
|
return __TokenPermValues
|
||||||
|
}
|
||||||
|
|
||||||
|
func TokenPermValuesMeta() []EnumMetaValue {
|
||||||
|
return []EnumMetaValue{
|
||||||
|
EnumMetaValue{VarName: "PermAdmin", Value: PermAdmin, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "PermChannelRead", Value: PermChannelRead, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "PermChannelSend", Value: PermChannelSend, Description: nil},
|
||||||
|
EnumMetaValue{VarName: "PermUserRead", Value: PermUserRead, Description: nil},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -40,6 +40,7 @@ const (
|
|||||||
prefixSubscriptionID = "SUB"
|
prefixSubscriptionID = "SUB"
|
||||||
prefixClientID = "CLN"
|
prefixClientID = "CLN"
|
||||||
prefixRequestID = "REQ"
|
prefixRequestID = "REQ"
|
||||||
|
prefixKeyTokenID = "TOK"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -50,6 +51,7 @@ var (
|
|||||||
regexSubscriptionID = generateRegex(prefixSubscriptionID)
|
regexSubscriptionID = generateRegex(prefixSubscriptionID)
|
||||||
regexClientID = generateRegex(prefixClientID)
|
regexClientID = generateRegex(prefixClientID)
|
||||||
regexRequestID = generateRegex(prefixRequestID)
|
regexRequestID = generateRegex(prefixRequestID)
|
||||||
|
regexKeyTokenID = generateRegex(prefixKeyTokenID)
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateRegex(prefix string) rext.Regex {
|
func generateRegex(prefix string) rext.Regex {
|
||||||
@ -375,3 +377,35 @@ func (id RequestID) CheckString() string {
|
|||||||
func (id RequestID) Regex() rext.Regex {
|
func (id RequestID) Regex() rext.Regex {
|
||||||
return regexRequestID
|
return regexRequestID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
type KeyTokenID string
|
||||||
|
|
||||||
|
func NewKeyTokenID() KeyTokenID {
|
||||||
|
return KeyTokenID(generateID(prefixKeyTokenID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id KeyTokenID) Valid() error {
|
||||||
|
return validateID(prefixKeyTokenID, string(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id KeyTokenID) String() string {
|
||||||
|
return string(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id KeyTokenID) Prefix() string {
|
||||||
|
return prefixKeyTokenID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id KeyTokenID) Raw() string {
|
||||||
|
return getRawData(prefixKeyTokenID, string(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id KeyTokenID) CheckString() string {
|
||||||
|
return getCheckString(prefixKeyTokenID, string(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (id KeyTokenID) Regex() rext.Regex {
|
||||||
|
return regexKeyTokenID
|
||||||
|
}
|
||||||
|
160
scnserver/models/keytoken.go
Normal file
160
scnserver/models/keytoken.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenPerm string //@enum:type
|
||||||
|
|
||||||
|
const (
|
||||||
|
PermAdmin TokenPerm = "A"
|
||||||
|
PermChannelRead TokenPerm = "CR"
|
||||||
|
PermChannelSend TokenPerm = "CS"
|
||||||
|
PermUserRead TokenPerm = "UR"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenPermissionList []TokenPerm
|
||||||
|
|
||||||
|
func (e TokenPermissionList) Any(p ...TokenPerm) bool {
|
||||||
|
for _, v1 := range e {
|
||||||
|
for _, v2 := range p {
|
||||||
|
if v1 == v2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e TokenPermissionList) String() string {
|
||||||
|
return strings.Join(langext.ArrMap(e, func(v TokenPerm) string { return string(v) }), ";")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTokenPermissionList(input string) TokenPermissionList {
|
||||||
|
r := make([]TokenPerm, 0, len(input))
|
||||||
|
for _, v := range strings.Split(input, ";") {
|
||||||
|
if vv, ok := ParseTokenPerm(v); ok {
|
||||||
|
r = append(r, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyToken struct {
|
||||||
|
KeyTokenID KeyTokenID
|
||||||
|
Name string
|
||||||
|
TimestampCreated time.Time
|
||||||
|
TimestampLastUsed *time.Time
|
||||||
|
OwnerUserID UserID
|
||||||
|
AllChannels bool
|
||||||
|
Channels []ChannelID // can also be owned by other user (needs active subscription)
|
||||||
|
Token string
|
||||||
|
Permissions TokenPermissionList
|
||||||
|
MessagesSent int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyToken) IsUserRead(uid UserID) bool {
|
||||||
|
return k.OwnerUserID == uid && k.Permissions.Any(PermAdmin, PermUserRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyToken) IsAllMessagesRead(uid UserID) bool {
|
||||||
|
return k.OwnerUserID == uid && k.AllChannels == true && k.Permissions.Any(PermAdmin, PermChannelRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyToken) IsChannelMessagesRead(cid ChannelID) bool {
|
||||||
|
return (k.AllChannels == true || langext.InArray(cid, k.Channels)) && k.Permissions.Any(PermAdmin, PermChannelRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyToken) IsAdmin(uid UserID) bool {
|
||||||
|
return k.OwnerUserID == uid && k.Permissions.Any(PermAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyToken) IsChannelMessagesSend(c Channel) bool {
|
||||||
|
return (k.AllChannels == true || langext.InArray(c.ChannelID, k.Channels)) && k.OwnerUserID == c.OwnerUserID && k.Permissions.Any(PermAdmin, PermChannelSend)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyToken) JSON() KeyTokenJSON {
|
||||||
|
return KeyTokenJSON{
|
||||||
|
KeyTokenID: k.KeyTokenID,
|
||||||
|
Name: k.Name,
|
||||||
|
TimestampCreated: k.TimestampCreated,
|
||||||
|
TimestampLastUsed: k.TimestampLastUsed,
|
||||||
|
OwnerUserID: k.OwnerUserID,
|
||||||
|
AllChannels: k.AllChannels,
|
||||||
|
Channels: k.Channels,
|
||||||
|
Permissions: k.Permissions.String(),
|
||||||
|
MessagesSent: k.MessagesSent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyTokenJSON struct {
|
||||||
|
KeyTokenID KeyTokenID `json:"keytoken_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TimestampCreated time.Time `json:"timestamp_created"`
|
||||||
|
TimestampLastUsed *time.Time `json:"timestamp_lastused"`
|
||||||
|
OwnerUserID UserID `json:"owner_user_id"`
|
||||||
|
AllChannels bool `json:"all_channels"`
|
||||||
|
Channels []ChannelID `json:"channels"`
|
||||||
|
Permissions string `json:"permissions"`
|
||||||
|
MessagesSent int `json:"messages_sent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyTokenWithTokenJSON struct {
|
||||||
|
KeyTokenJSON
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j KeyTokenJSON) WithToken(tok string) any {
|
||||||
|
return KeyTokenWithTokenJSON{
|
||||||
|
KeyTokenJSON: j,
|
||||||
|
Token: tok,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyTokenDB struct {
|
||||||
|
KeyTokenID KeyTokenID `db:"keytoken_id"`
|
||||||
|
Name string `db:"name"`
|
||||||
|
TimestampCreated int64 `db:"timestamp_created"`
|
||||||
|
TimestampLastUsed *int64 `db:"timestamp_lastused"`
|
||||||
|
OwnerUserID UserID `db:"owner_user_id"`
|
||||||
|
AllChannels bool `db:"all_channels"`
|
||||||
|
Channels string `db:"channels"`
|
||||||
|
Token string `db:"token"`
|
||||||
|
Permissions string `db:"permissions"`
|
||||||
|
MessagesSent int `db:"messages_sent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k KeyTokenDB) Model() KeyToken {
|
||||||
|
return KeyToken{
|
||||||
|
KeyTokenID: k.KeyTokenID,
|
||||||
|
Name: k.Name,
|
||||||
|
TimestampCreated: timeFromMilli(k.TimestampCreated),
|
||||||
|
TimestampLastUsed: timeOptFromMilli(k.TimestampLastUsed),
|
||||||
|
OwnerUserID: k.OwnerUserID,
|
||||||
|
AllChannels: k.AllChannels,
|
||||||
|
Channels: langext.ArrMap(strings.Split(k.Channels, ";"), func(v string) ChannelID { return ChannelID(v) }),
|
||||||
|
Token: k.Token,
|
||||||
|
Permissions: ParseTokenPermissionList(k.Permissions),
|
||||||
|
MessagesSent: k.MessagesSent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeKeyToken(r *sqlx.Rows) (KeyToken, error) {
|
||||||
|
data, err := sq.ScanSingle[KeyTokenDB](r, sq.SModeFast, sq.Safe, true)
|
||||||
|
if err != nil {
|
||||||
|
return KeyToken{}, err
|
||||||
|
}
|
||||||
|
return data.Model(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeKeyTokens(r *sqlx.Rows) ([]KeyToken, error) {
|
||||||
|
data, err := sq.ScanAll[KeyTokenDB](r, sq.SModeFast, sq.Safe, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return langext.ArrMap(data, func(v KeyTokenDB) KeyToken { return v.Model() }), nil
|
||||||
|
}
|
@ -135,7 +135,7 @@ func (m MessageDB) Model() Message {
|
|||||||
ChannelID: m.ChannelID,
|
ChannelID: m.ChannelID,
|
||||||
SenderName: m.SenderName,
|
SenderName: m.SenderName,
|
||||||
SenderIP: m.SenderIP,
|
SenderIP: m.SenderIP,
|
||||||
TimestampReal: time.UnixMilli(m.TimestampReal),
|
TimestampReal: timeFromMilli(m.TimestampReal),
|
||||||
TimestampClient: timeOptFromMilli(m.TimestampClient),
|
TimestampClient: timeOptFromMilli(m.TimestampClient),
|
||||||
Title: m.Title,
|
Title: m.Title,
|
||||||
Content: m.Content,
|
Content: m.Content,
|
||||||
|
@ -1,22 +1,11 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type PermKeyType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
PermKeyTypeNone PermKeyType = "NONE" // (nothing)
|
|
||||||
PermKeyTypeUserSend PermKeyType = "USER_SEND" // send-messages
|
|
||||||
PermKeyTypeUserRead PermKeyType = "USER_READ" // send-messages, list-messages, read-user
|
|
||||||
PermKeyTypeUserAdmin PermKeyType = "USER_ADMIN" // send-messages, list-messages, read-user, delete-messages, update-user
|
|
||||||
)
|
|
||||||
|
|
||||||
type PermissionSet struct {
|
type PermissionSet struct {
|
||||||
UserID *UserID
|
Token *KeyToken // KeyToken.Permissions
|
||||||
KeyType PermKeyType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmptyPermissions() PermissionSet {
|
func NewEmptyPermissions() PermissionSet {
|
||||||
return PermissionSet{
|
return PermissionSet{
|
||||||
UserID: nil,
|
Token: nil,
|
||||||
KeyType: PermKeyTypeNone,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ type RequestLog struct {
|
|||||||
RequestBodySize int64
|
RequestBodySize int64
|
||||||
RequestContentType string
|
RequestContentType string
|
||||||
RemoteIP string
|
RemoteIP string
|
||||||
|
TokenID *KeyTokenID
|
||||||
UserID *UserID
|
UserID *UserID
|
||||||
Permissions *string
|
Permissions *string
|
||||||
ResponseStatuscode *int64
|
ResponseStatuscode *int64
|
||||||
@ -44,6 +45,7 @@ func (c RequestLog) JSON() RequestLogJSON {
|
|||||||
RequestBodySize: c.RequestBodySize,
|
RequestBodySize: c.RequestBodySize,
|
||||||
RequestContentType: c.RequestContentType,
|
RequestContentType: c.RequestContentType,
|
||||||
RemoteIP: c.RemoteIP,
|
RemoteIP: c.RemoteIP,
|
||||||
|
TokenID: c.TokenID,
|
||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Permissions: c.Permissions,
|
Permissions: c.Permissions,
|
||||||
ResponseStatuscode: c.ResponseStatuscode,
|
ResponseStatuscode: c.ResponseStatuscode,
|
||||||
@ -71,6 +73,7 @@ func (c RequestLog) DB() RequestLogDB {
|
|||||||
RequestBodySize: c.RequestBodySize,
|
RequestBodySize: c.RequestBodySize,
|
||||||
RequestContentType: c.RequestContentType,
|
RequestContentType: c.RequestContentType,
|
||||||
RemoteIP: c.RemoteIP,
|
RemoteIP: c.RemoteIP,
|
||||||
|
TokenID: c.TokenID,
|
||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Permissions: c.Permissions,
|
Permissions: c.Permissions,
|
||||||
ResponseStatuscode: c.ResponseStatuscode,
|
ResponseStatuscode: c.ResponseStatuscode,
|
||||||
@ -97,6 +100,7 @@ type RequestLogJSON struct {
|
|||||||
RequestBodySize int64 `json:"request_body_size"`
|
RequestBodySize int64 `json:"request_body_size"`
|
||||||
RequestContentType string `json:"request_content_type"`
|
RequestContentType string `json:"request_content_type"`
|
||||||
RemoteIP string `json:"remote_ip"`
|
RemoteIP string `json:"remote_ip"`
|
||||||
|
TokenID *KeyTokenID `json:"token_id"`
|
||||||
UserID *UserID `json:"userid"`
|
UserID *UserID `json:"userid"`
|
||||||
Permissions *string `json:"permissions"`
|
Permissions *string `json:"permissions"`
|
||||||
ResponseStatuscode *int64 `json:"response_statuscode"`
|
ResponseStatuscode *int64 `json:"response_statuscode"`
|
||||||
@ -122,6 +126,7 @@ type RequestLogDB struct {
|
|||||||
RequestBodySize int64 `db:"request_body_size"`
|
RequestBodySize int64 `db:"request_body_size"`
|
||||||
RequestContentType string `db:"request_content_type"`
|
RequestContentType string `db:"request_content_type"`
|
||||||
RemoteIP string `db:"remote_ip"`
|
RemoteIP string `db:"remote_ip"`
|
||||||
|
TokenID *KeyTokenID `db:"token_id"`
|
||||||
UserID *UserID `db:"userid"`
|
UserID *UserID `db:"userid"`
|
||||||
Permissions *string `db:"permissions"`
|
Permissions *string `db:"permissions"`
|
||||||
ResponseStatuscode *int64 `db:"response_statuscode"`
|
ResponseStatuscode *int64 `db:"response_statuscode"`
|
||||||
@ -158,9 +163,9 @@ func (c RequestLogDB) Model() RequestLog {
|
|||||||
Panicked: c.Panicked != 0,
|
Panicked: c.Panicked != 0,
|
||||||
PanicStr: c.PanicStr,
|
PanicStr: c.PanicStr,
|
||||||
ProcessingTime: timeext.FromMilliseconds(c.ProcessingTime),
|
ProcessingTime: timeext.FromMilliseconds(c.ProcessingTime),
|
||||||
TimestampCreated: time.UnixMilli(c.TimestampCreated),
|
TimestampCreated: timeFromMilli(c.TimestampCreated),
|
||||||
TimestampStart: time.UnixMilli(c.TimestampStart),
|
TimestampStart: timeFromMilli(c.TimestampStart),
|
||||||
TimestampFinish: time.UnixMilli(c.TimestampFinish),
|
TimestampFinish: timeFromMilli(c.TimestampFinish),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// [!] subscriptions are read-access to channels,
|
||||||
|
//
|
||||||
|
// The set of subscriptions specifies which messages the ListMessages() API call returns
|
||||||
|
// also single messages/channels that are subscribed can be queries
|
||||||
|
//
|
||||||
|
// (use keytokens for write-access)
|
||||||
|
|
||||||
type Subscription struct {
|
type Subscription struct {
|
||||||
SubscriptionID SubscriptionID
|
SubscriptionID SubscriptionID
|
||||||
SubscriberUserID UserID
|
SubscriberUserID UserID
|
||||||
@ -56,7 +63,7 @@ func (s SubscriptionDB) Model() Subscription {
|
|||||||
ChannelOwnerUserID: s.ChannelOwnerUserID,
|
ChannelOwnerUserID: s.ChannelOwnerUserID,
|
||||||
ChannelID: s.ChannelID,
|
ChannelID: s.ChannelID,
|
||||||
ChannelInternalName: s.ChannelInternalName,
|
ChannelInternalName: s.ChannelInternalName,
|
||||||
TimestampCreated: time.UnixMilli(s.TimestampCreated),
|
TimestampCreated: timeFromMilli(s.TimestampCreated),
|
||||||
Confirmed: s.Confirmed != 0,
|
Confirmed: s.Confirmed != 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,6 @@ import (
|
|||||||
type User struct {
|
type User struct {
|
||||||
UserID UserID
|
UserID UserID
|
||||||
Username *string
|
Username *string
|
||||||
SendKey string
|
|
||||||
ReadKey string
|
|
||||||
AdminKey string
|
|
||||||
TimestampCreated time.Time
|
TimestampCreated time.Time
|
||||||
TimestampLastRead *time.Time
|
TimestampLastRead *time.Time
|
||||||
TimestampLastSent *time.Time
|
TimestampLastSent *time.Time
|
||||||
@ -28,9 +25,6 @@ func (u User) JSON() UserJSON {
|
|||||||
return UserJSON{
|
return UserJSON{
|
||||||
UserID: u.UserID,
|
UserID: u.UserID,
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
ReadKey: u.ReadKey,
|
|
||||||
SendKey: u.SendKey,
|
|
||||||
AdminKey: u.AdminKey,
|
|
||||||
TimestampCreated: u.TimestampCreated.Format(time.RFC3339Nano),
|
TimestampCreated: u.TimestampCreated.Format(time.RFC3339Nano),
|
||||||
TimestampLastRead: timeOptFmt(u.TimestampLastRead, time.RFC3339Nano),
|
TimestampLastRead: timeOptFmt(u.TimestampLastRead, time.RFC3339Nano),
|
||||||
TimestampLastSent: timeOptFmt(u.TimestampLastSent, time.RFC3339Nano),
|
TimestampLastSent: timeOptFmt(u.TimestampLastSent, time.RFC3339Nano),
|
||||||
@ -43,10 +37,13 @@ func (u User) JSON() UserJSON {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) JSONWithClients(clients []Client) UserJSONWithClients {
|
func (u User) JSONWithClients(clients []Client, ak string, sk string, rk string) UserJSONWithClientsAndKeys {
|
||||||
return UserJSONWithClients{
|
return UserJSONWithClientsAndKeys{
|
||||||
UserJSON: u.JSON(),
|
UserJSON: u.JSON(),
|
||||||
Clients: langext.ArrMap(clients, func(v Client) ClientJSON { return v.JSON() }),
|
Clients: langext.ArrMap(clients, func(v Client) ClientJSON { return v.JSON() }),
|
||||||
|
SendKey: sk,
|
||||||
|
ReadKey: rk,
|
||||||
|
AdminKey: ak,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +111,6 @@ func (u User) MaxTimestampDiffHours() int {
|
|||||||
type UserJSON struct {
|
type UserJSON struct {
|
||||||
UserID UserID `json:"user_id"`
|
UserID UserID `json:"user_id"`
|
||||||
Username *string `json:"username"`
|
Username *string `json:"username"`
|
||||||
ReadKey string `json:"read_key"`
|
|
||||||
SendKey string `json:"send_key"`
|
|
||||||
AdminKey string `json:"admin_key"`
|
|
||||||
TimestampCreated string `json:"timestamp_created"`
|
TimestampCreated string `json:"timestamp_created"`
|
||||||
TimestampLastRead *string `json:"timestamp_lastread"`
|
TimestampLastRead *string `json:"timestamp_lastread"`
|
||||||
TimestampLastSent *string `json:"timestamp_lastsent"`
|
TimestampLastSent *string `json:"timestamp_lastsent"`
|
||||||
@ -128,17 +122,17 @@ type UserJSON struct {
|
|||||||
DefaultChannel string `json:"default_channel"`
|
DefaultChannel string `json:"default_channel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserJSONWithClients struct {
|
type UserJSONWithClientsAndKeys struct {
|
||||||
UserJSON
|
UserJSON
|
||||||
Clients []ClientJSON `json:"clients"`
|
Clients []ClientJSON `json:"clients"`
|
||||||
|
SendKey string `json:"send_key"`
|
||||||
|
ReadKey string `json:"read_key"`
|
||||||
|
AdminKey string `json:"admin_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserDB struct {
|
type UserDB struct {
|
||||||
UserID UserID `db:"user_id"`
|
UserID UserID `db:"user_id"`
|
||||||
Username *string `db:"username"`
|
Username *string `db:"username"`
|
||||||
SendKey string `db:"send_key"`
|
|
||||||
ReadKey string `db:"read_key"`
|
|
||||||
AdminKey string `db:"admin_key"`
|
|
||||||
TimestampCreated int64 `db:"timestamp_created"`
|
TimestampCreated int64 `db:"timestamp_created"`
|
||||||
TimestampLastRead *int64 `db:"timestamp_lastread"`
|
TimestampLastRead *int64 `db:"timestamp_lastread"`
|
||||||
TimestampLastSent *int64 `db:"timestamp_lastsent"`
|
TimestampLastSent *int64 `db:"timestamp_lastsent"`
|
||||||
@ -153,10 +147,7 @@ func (u UserDB) Model() User {
|
|||||||
return User{
|
return User{
|
||||||
UserID: u.UserID,
|
UserID: u.UserID,
|
||||||
Username: u.Username,
|
Username: u.Username,
|
||||||
SendKey: u.SendKey,
|
TimestampCreated: timeFromMilli(u.TimestampCreated),
|
||||||
ReadKey: u.ReadKey,
|
|
||||||
AdminKey: u.AdminKey,
|
|
||||||
TimestampCreated: time.UnixMilli(u.TimestampCreated),
|
|
||||||
TimestampLastRead: timeOptFromMilli(u.TimestampLastRead),
|
TimestampLastRead: timeOptFromMilli(u.TimestampLastRead),
|
||||||
TimestampLastSent: timeOptFromMilli(u.TimestampLastSent),
|
TimestampLastSent: timeOptFromMilli(u.TimestampLastSent),
|
||||||
MessagesSent: u.MessagesSent,
|
MessagesSent: u.MessagesSent,
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate go run ../_gen/enum-generate.go -- enums_gen.go
|
||||||
|
|
||||||
func timeOptFmt(t *time.Time, fmt string) *string {
|
func timeOptFmt(t *time.Time, fmt string) *string {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -19,3 +21,7 @@ func timeOptFromMilli(millis *int64) *time.Time {
|
|||||||
}
|
}
|
||||||
return langext.Ptr(time.UnixMilli(*millis))
|
return langext.Ptr(time.UnixMilli(*millis))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func timeFromMilli(millis int64) time.Time {
|
||||||
|
return time.UnixMilli(millis)
|
||||||
|
}
|
||||||
|
@ -17,12 +17,6 @@
|
|||||||
],
|
],
|
||||||
"summary": "Send a new message",
|
"summary": "Send a new message",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "qhnUbKcLgp6tg",
|
|
||||||
"name": "chan_key",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "test",
|
"example": "test",
|
||||||
@ -35,6 +29,12 @@
|
|||||||
"name": "content",
|
"name": "content",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"example": "P3TNH8mvv14fm",
|
||||||
|
"name": "key",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "db8b0e6a-a08c-4646",
|
"example": "db8b0e6a-a08c-4646",
|
||||||
@ -76,12 +76,6 @@
|
|||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "P3TNH8mvv14fm",
|
|
||||||
"name": "user_key",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": " ",
|
"description": " ",
|
||||||
"name": "post_body",
|
"name": "post_body",
|
||||||
@ -90,12 +84,6 @@
|
|||||||
"$ref": "#/definitions/handler.SendMessage.combined"
|
"$ref": "#/definitions/handler.SendMessage.combined"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "qhnUbKcLgp6tg",
|
|
||||||
"name": "chan_key",
|
|
||||||
"in": "formData"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "test",
|
"example": "test",
|
||||||
@ -108,6 +96,12 @@
|
|||||||
"name": "content",
|
"name": "content",
|
||||||
"in": "formData"
|
"in": "formData"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"example": "P3TNH8mvv14fm",
|
||||||
|
"name": "key",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "db8b0e6a-a08c-4646",
|
"example": "db8b0e6a-a08c-4646",
|
||||||
@ -148,12 +142,6 @@
|
|||||||
"example": "7725",
|
"example": "7725",
|
||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
"in": "formData"
|
"in": "formData"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "P3TNH8mvv14fm",
|
|
||||||
"name": "user_key",
|
|
||||||
"in": "formData"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -1034,7 +1022,7 @@
|
|||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/definitions/models.UserJSONWithClients"
|
"$ref": "#/definitions/models.UserJSONWithClientsAndKeys"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
@ -1052,6 +1040,282 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v2/users/:uid/keys": {
|
||||||
|
"get": {
|
||||||
|
"description": "The request must be done with an ADMIN key, the returned keys are without their token.",
|
||||||
|
"tags": [
|
||||||
|
"API-v2"
|
||||||
|
],
|
||||||
|
"summary": "List keys of the user",
|
||||||
|
"operationId": "api-tokenkeys-list",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "UserID",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/handler.ListUserKeys.response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "supplied values/parameters cannot be parsed / are invalid",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "user is not authorized / has missing permissions",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "message not found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "internal server error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"API-v2"
|
||||||
|
],
|
||||||
|
"summary": "Create a new key",
|
||||||
|
"operationId": "api-tokenkeys-create",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "UserID",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": " ",
|
||||||
|
"name": "post_body",
|
||||||
|
"in": "body",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/handler.CreateUserKey.body"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.KeyTokenJSON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "supplied values/parameters cannot be parsed / are invalid",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "user is not authorized / has missing permissions",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "message not found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "internal server error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v2/users/:uid/keys/:kid": {
|
||||||
|
"get": {
|
||||||
|
"description": "The request must be done with an ADMIN key, the returned key does not include its token.",
|
||||||
|
"tags": [
|
||||||
|
"API-v2"
|
||||||
|
],
|
||||||
|
"summary": "Get a single key",
|
||||||
|
"operationId": "api-tokenkeys-get",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "UserID",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "TokenKeyID",
|
||||||
|
"name": "kid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.KeyTokenJSON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "supplied values/parameters cannot be parsed / are invalid",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "user is not authorized / has missing permissions",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "message not found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "internal server error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"description": "Cannot be used to delete the key used in the request itself",
|
||||||
|
"tags": [
|
||||||
|
"API-v2"
|
||||||
|
],
|
||||||
|
"summary": "Delete a key",
|
||||||
|
"operationId": "api-tokenkeys-delete",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "UserID",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "TokenKeyID",
|
||||||
|
"name": "kid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.KeyTokenJSON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "supplied values/parameters cannot be parsed / are invalid",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "user is not authorized / has missing permissions",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "message not found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "internal server error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"tags": [
|
||||||
|
"API-v2"
|
||||||
|
],
|
||||||
|
"summary": "Update a key",
|
||||||
|
"operationId": "api-tokenkeys-update",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "UserID",
|
||||||
|
"name": "uid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "TokenKeyID",
|
||||||
|
"name": "kid",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.KeyTokenJSON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "supplied values/parameters cannot be parsed / are invalid",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "user is not authorized / has missing permissions",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "message not found",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "internal server error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/ginresp.apiError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v2/users/{uid}": {
|
"/api/v2/users/{uid}": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -2081,12 +2345,6 @@
|
|||||||
],
|
],
|
||||||
"summary": "Send a new message",
|
"summary": "Send a new message",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "qhnUbKcLgp6tg",
|
|
||||||
"name": "chan_key",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "test",
|
"example": "test",
|
||||||
@ -2099,6 +2357,12 @@
|
|||||||
"name": "content",
|
"name": "content",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"example": "P3TNH8mvv14fm",
|
||||||
|
"name": "key",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "db8b0e6a-a08c-4646",
|
"example": "db8b0e6a-a08c-4646",
|
||||||
@ -2140,12 +2404,6 @@
|
|||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "P3TNH8mvv14fm",
|
|
||||||
"name": "user_key",
|
|
||||||
"in": "query"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": " ",
|
"description": " ",
|
||||||
"name": "post_body",
|
"name": "post_body",
|
||||||
@ -2154,12 +2412,6 @@
|
|||||||
"$ref": "#/definitions/handler.SendMessage.combined"
|
"$ref": "#/definitions/handler.SendMessage.combined"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "qhnUbKcLgp6tg",
|
|
||||||
"name": "chan_key",
|
|
||||||
"in": "formData"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "test",
|
"example": "test",
|
||||||
@ -2172,6 +2424,12 @@
|
|||||||
"name": "content",
|
"name": "content",
|
||||||
"in": "formData"
|
"in": "formData"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"example": "P3TNH8mvv14fm",
|
||||||
|
"name": "key",
|
||||||
|
"in": "formData"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "db8b0e6a-a08c-4646",
|
"example": "db8b0e6a-a08c-4646",
|
||||||
@ -2212,12 +2470,6 @@
|
|||||||
"example": "7725",
|
"example": "7725",
|
||||||
"name": "user_id",
|
"name": "user_id",
|
||||||
"in": "formData"
|
"in": "formData"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "string",
|
|
||||||
"example": "P3TNH8mvv14fm",
|
|
||||||
"name": "user_key",
|
|
||||||
"in": "formData"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -2370,6 +2622,97 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
|
"apierr.APIError": {
|
||||||
|
"type": "integer",
|
||||||
|
"enum": [
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
1101,
|
||||||
|
1102,
|
||||||
|
1103,
|
||||||
|
1104,
|
||||||
|
1105,
|
||||||
|
1106,
|
||||||
|
1121,
|
||||||
|
1151,
|
||||||
|
1152,
|
||||||
|
1153,
|
||||||
|
1161,
|
||||||
|
1171,
|
||||||
|
1201,
|
||||||
|
1202,
|
||||||
|
1203,
|
||||||
|
1204,
|
||||||
|
1205,
|
||||||
|
1206,
|
||||||
|
1207,
|
||||||
|
1208,
|
||||||
|
1251,
|
||||||
|
1301,
|
||||||
|
1302,
|
||||||
|
1303,
|
||||||
|
1304,
|
||||||
|
1305,
|
||||||
|
1306,
|
||||||
|
1307,
|
||||||
|
1311,
|
||||||
|
1401,
|
||||||
|
1501,
|
||||||
|
1511,
|
||||||
|
2101,
|
||||||
|
3001,
|
||||||
|
3002,
|
||||||
|
9901,
|
||||||
|
9902,
|
||||||
|
9903,
|
||||||
|
9904,
|
||||||
|
9905
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"UNDEFINED",
|
||||||
|
"NO_ERROR",
|
||||||
|
"MISSING_UID",
|
||||||
|
"MISSING_TOK",
|
||||||
|
"MISSING_TITLE",
|
||||||
|
"INVALID_PRIO",
|
||||||
|
"REQ_METHOD",
|
||||||
|
"INVALID_CLIENTTYPE",
|
||||||
|
"PAGETOKEN_ERROR",
|
||||||
|
"BINDFAIL_QUERY_PARAM",
|
||||||
|
"BINDFAIL_BODY_PARAM",
|
||||||
|
"BINDFAIL_URI_PARAM",
|
||||||
|
"INVALID_BODY_PARAM",
|
||||||
|
"INVALID_ENUM_VALUE",
|
||||||
|
"NO_TITLE",
|
||||||
|
"TITLE_TOO_LONG",
|
||||||
|
"CONTENT_TOO_LONG",
|
||||||
|
"USR_MSG_ID_TOO_LONG",
|
||||||
|
"TIMESTAMP_OUT_OF_RANGE",
|
||||||
|
"SENDERNAME_TOO_LONG",
|
||||||
|
"CHANNEL_TOO_LONG",
|
||||||
|
"CHANNEL_DESCRIPTION_TOO_LONG",
|
||||||
|
"CHANNEL_NAME_WOULD_CHANGE",
|
||||||
|
"USER_NOT_FOUND",
|
||||||
|
"CLIENT_NOT_FOUND",
|
||||||
|
"CHANNEL_NOT_FOUND",
|
||||||
|
"SUBSCRIPTION_NOT_FOUND",
|
||||||
|
"MESSAGE_NOT_FOUND",
|
||||||
|
"SUBSCRIPTION_USER_MISMATCH",
|
||||||
|
"KEY_NOT_FOUND",
|
||||||
|
"USER_AUTH_FAILED",
|
||||||
|
"NO_DEVICE_LINKED",
|
||||||
|
"CHANNEL_ALREADY_EXISTS",
|
||||||
|
"CANNOT_SELFDELETE_KEY",
|
||||||
|
"QUOTA_REACHED",
|
||||||
|
"FAILED_VERIFY_PRO_TOKEN",
|
||||||
|
"INVALID_PRO_TOKEN",
|
||||||
|
"FIREBASE_COM_FAILED",
|
||||||
|
"FIREBASE_COM_ERRORED",
|
||||||
|
"INTERNAL_EXCEPTION",
|
||||||
|
"PANIC",
|
||||||
|
"NOT_IMPLEMENTED"
|
||||||
|
]
|
||||||
|
},
|
||||||
"ginresp.apiError": {
|
"ginresp.apiError": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -2492,6 +2835,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handler.CreateUserKey.body": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"all_channels",
|
||||||
|
"channels",
|
||||||
|
"name",
|
||||||
|
"permissions"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"all_channels": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handler.DatabaseTest.response": {
|
"handler.DatabaseTest.response": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -2630,6 +2999,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"handler.ListUserKeys.response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"tokens": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/models.KeyTokenJSON"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"handler.ListUserSubscriptions.response": {
|
"handler.ListUserSubscriptions.response": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -2690,10 +3070,6 @@
|
|||||||
"handler.SendMessage.combined": {
|
"handler.SendMessage.combined": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"chan_key": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "qhnUbKcLgp6tg"
|
|
||||||
},
|
|
||||||
"channel": {
|
"channel": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "test"
|
"example": "test"
|
||||||
@ -2702,6 +3078,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "This is a message"
|
"example": "This is a message"
|
||||||
},
|
},
|
||||||
|
"key": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "P3TNH8mvv14fm"
|
||||||
|
},
|
||||||
"msg_id": {
|
"msg_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "db8b0e6a-a08c-4646"
|
"example": "db8b0e6a-a08c-4646"
|
||||||
@ -2730,10 +3110,6 @@
|
|||||||
"user_id": {
|
"user_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"example": "7725"
|
"example": "7725"
|
||||||
},
|
|
||||||
"user_key": {
|
|
||||||
"type": "string",
|
|
||||||
"example": "P3TNH8mvv14fm"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2744,7 +3120,7 @@
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"type": "integer"
|
"$ref": "#/definitions/apierr.APIError"
|
||||||
},
|
},
|
||||||
"is_pro": {
|
"is_pro": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@ -2779,7 +3155,7 @@
|
|||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"type": "integer"
|
"$ref": "#/definitions/apierr.APIError"
|
||||||
},
|
},
|
||||||
"is_pro": {
|
"is_pro": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
@ -2936,10 +3312,6 @@
|
|||||||
"owner_user_id": {
|
"owner_user_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"send_key": {
|
|
||||||
"description": "can be nil, depending on endpoint",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"subscribe_key": {
|
"subscribe_key": {
|
||||||
"description": "can be nil, depending on endpoint",
|
"description": "can be nil, depending on endpoint",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -2974,13 +3346,24 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string"
|
"$ref": "#/definitions/models.ClientType"
|
||||||
},
|
},
|
||||||
"user_id": {
|
"user_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.ClientType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ANDROID",
|
||||||
|
"IOS"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"ClientTypeAndroid",
|
||||||
|
"ClientTypeIOS"
|
||||||
|
]
|
||||||
|
},
|
||||||
"models.CompatMessage": {
|
"models.CompatMessage": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -3007,6 +3390,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"models.KeyTokenJSON": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"all_channels": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"channels": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keytoken_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"messages_sent": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"owner_user_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timestamp_created": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"timestamp_lastused": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"models.MessageJSON": {
|
"models.MessageJSON": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -3080,9 +3498,6 @@
|
|||||||
"models.UserJSON": {
|
"models.UserJSON": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"admin_key": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"default_channel": {
|
"default_channel": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -3101,12 +3516,6 @@
|
|||||||
"quota_used": {
|
"quota_used": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
"read_key": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"send_key": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"timestamp_created": {
|
"timestamp_created": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -3124,7 +3533,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"models.UserJSONWithClients": {
|
"models.UserJSONWithClientsAndKeys": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"admin_key": {
|
"admin_key": {
|
||||||
|
@ -1,5 +1,93 @@
|
|||||||
basePath: /
|
basePath: /
|
||||||
definitions:
|
definitions:
|
||||||
|
apierr.APIError:
|
||||||
|
enum:
|
||||||
|
- -1
|
||||||
|
- 0
|
||||||
|
- 1101
|
||||||
|
- 1102
|
||||||
|
- 1103
|
||||||
|
- 1104
|
||||||
|
- 1105
|
||||||
|
- 1106
|
||||||
|
- 1121
|
||||||
|
- 1151
|
||||||
|
- 1152
|
||||||
|
- 1153
|
||||||
|
- 1161
|
||||||
|
- 1171
|
||||||
|
- 1201
|
||||||
|
- 1202
|
||||||
|
- 1203
|
||||||
|
- 1204
|
||||||
|
- 1205
|
||||||
|
- 1206
|
||||||
|
- 1207
|
||||||
|
- 1208
|
||||||
|
- 1251
|
||||||
|
- 1301
|
||||||
|
- 1302
|
||||||
|
- 1303
|
||||||
|
- 1304
|
||||||
|
- 1305
|
||||||
|
- 1306
|
||||||
|
- 1307
|
||||||
|
- 1311
|
||||||
|
- 1401
|
||||||
|
- 1501
|
||||||
|
- 1511
|
||||||
|
- 2101
|
||||||
|
- 3001
|
||||||
|
- 3002
|
||||||
|
- 9901
|
||||||
|
- 9902
|
||||||
|
- 9903
|
||||||
|
- 9904
|
||||||
|
- 9905
|
||||||
|
type: integer
|
||||||
|
x-enum-varnames:
|
||||||
|
- UNDEFINED
|
||||||
|
- NO_ERROR
|
||||||
|
- MISSING_UID
|
||||||
|
- MISSING_TOK
|
||||||
|
- MISSING_TITLE
|
||||||
|
- INVALID_PRIO
|
||||||
|
- REQ_METHOD
|
||||||
|
- INVALID_CLIENTTYPE
|
||||||
|
- PAGETOKEN_ERROR
|
||||||
|
- BINDFAIL_QUERY_PARAM
|
||||||
|
- BINDFAIL_BODY_PARAM
|
||||||
|
- BINDFAIL_URI_PARAM
|
||||||
|
- INVALID_BODY_PARAM
|
||||||
|
- INVALID_ENUM_VALUE
|
||||||
|
- NO_TITLE
|
||||||
|
- TITLE_TOO_LONG
|
||||||
|
- CONTENT_TOO_LONG
|
||||||
|
- USR_MSG_ID_TOO_LONG
|
||||||
|
- TIMESTAMP_OUT_OF_RANGE
|
||||||
|
- SENDERNAME_TOO_LONG
|
||||||
|
- CHANNEL_TOO_LONG
|
||||||
|
- CHANNEL_DESCRIPTION_TOO_LONG
|
||||||
|
- CHANNEL_NAME_WOULD_CHANGE
|
||||||
|
- USER_NOT_FOUND
|
||||||
|
- CLIENT_NOT_FOUND
|
||||||
|
- CHANNEL_NOT_FOUND
|
||||||
|
- SUBSCRIPTION_NOT_FOUND
|
||||||
|
- MESSAGE_NOT_FOUND
|
||||||
|
- SUBSCRIPTION_USER_MISMATCH
|
||||||
|
- KEY_NOT_FOUND
|
||||||
|
- USER_AUTH_FAILED
|
||||||
|
- NO_DEVICE_LINKED
|
||||||
|
- CHANNEL_ALREADY_EXISTS
|
||||||
|
- CANNOT_SELFDELETE_KEY
|
||||||
|
- QUOTA_REACHED
|
||||||
|
- FAILED_VERIFY_PRO_TOKEN
|
||||||
|
- INVALID_PRO_TOKEN
|
||||||
|
- FIREBASE_COM_FAILED
|
||||||
|
- FIREBASE_COM_ERRORED
|
||||||
|
- INTERNAL_EXCEPTION
|
||||||
|
- PANIC
|
||||||
|
- NOT_IMPLEMENTED
|
||||||
ginresp.apiError:
|
ginresp.apiError:
|
||||||
properties:
|
properties:
|
||||||
errhighlight:
|
errhighlight:
|
||||||
@ -80,6 +168,24 @@ definitions:
|
|||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
handler.CreateUserKey.body:
|
||||||
|
properties:
|
||||||
|
all_channels:
|
||||||
|
type: boolean
|
||||||
|
channels:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
permissions:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- all_channels
|
||||||
|
- channels
|
||||||
|
- name
|
||||||
|
- permissions
|
||||||
|
type: object
|
||||||
handler.DatabaseTest.response:
|
handler.DatabaseTest.response:
|
||||||
properties:
|
properties:
|
||||||
libVersion:
|
libVersion:
|
||||||
@ -169,6 +275,13 @@ definitions:
|
|||||||
page_size:
|
page_size:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
handler.ListUserKeys.response:
|
||||||
|
properties:
|
||||||
|
tokens:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/models.KeyTokenJSON'
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
handler.ListUserSubscriptions.response:
|
handler.ListUserSubscriptions.response:
|
||||||
properties:
|
properties:
|
||||||
subscriptions:
|
subscriptions:
|
||||||
@ -208,15 +321,15 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
handler.SendMessage.combined:
|
handler.SendMessage.combined:
|
||||||
properties:
|
properties:
|
||||||
chan_key:
|
|
||||||
example: qhnUbKcLgp6tg
|
|
||||||
type: string
|
|
||||||
channel:
|
channel:
|
||||||
example: test
|
example: test
|
||||||
type: string
|
type: string
|
||||||
content:
|
content:
|
||||||
example: This is a message
|
example: This is a message
|
||||||
type: string
|
type: string
|
||||||
|
key:
|
||||||
|
example: P3TNH8mvv14fm
|
||||||
|
type: string
|
||||||
msg_id:
|
msg_id:
|
||||||
example: db8b0e6a-a08c-4646
|
example: db8b0e6a-a08c-4646
|
||||||
type: string
|
type: string
|
||||||
@ -239,16 +352,13 @@ definitions:
|
|||||||
user_id:
|
user_id:
|
||||||
example: "7725"
|
example: "7725"
|
||||||
type: string
|
type: string
|
||||||
user_key:
|
|
||||||
example: P3TNH8mvv14fm
|
|
||||||
type: string
|
|
||||||
type: object
|
type: object
|
||||||
handler.SendMessage.response:
|
handler.SendMessage.response:
|
||||||
properties:
|
properties:
|
||||||
errhighlight:
|
errhighlight:
|
||||||
type: integer
|
type: integer
|
||||||
error:
|
error:
|
||||||
type: integer
|
$ref: '#/definitions/apierr.APIError'
|
||||||
is_pro:
|
is_pro:
|
||||||
type: boolean
|
type: boolean
|
||||||
message:
|
message:
|
||||||
@ -271,7 +381,7 @@ definitions:
|
|||||||
errhighlight:
|
errhighlight:
|
||||||
type: integer
|
type: integer
|
||||||
error:
|
error:
|
||||||
type: integer
|
$ref: '#/definitions/apierr.APIError'
|
||||||
is_pro:
|
is_pro:
|
||||||
type: boolean
|
type: boolean
|
||||||
message:
|
message:
|
||||||
@ -373,9 +483,6 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
owner_user_id:
|
owner_user_id:
|
||||||
type: string
|
type: string
|
||||||
send_key:
|
|
||||||
description: can be nil, depending on endpoint
|
|
||||||
type: string
|
|
||||||
subscribe_key:
|
subscribe_key:
|
||||||
description: can be nil, depending on endpoint
|
description: can be nil, depending on endpoint
|
||||||
type: string
|
type: string
|
||||||
@ -399,10 +506,18 @@ definitions:
|
|||||||
timestamp_created:
|
timestamp_created:
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
type: string
|
$ref: '#/definitions/models.ClientType'
|
||||||
user_id:
|
user_id:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
models.ClientType:
|
||||||
|
enum:
|
||||||
|
- ANDROID
|
||||||
|
- IOS
|
||||||
|
type: string
|
||||||
|
x-enum-varnames:
|
||||||
|
- ClientTypeAndroid
|
||||||
|
- ClientTypeIOS
|
||||||
models.CompatMessage:
|
models.CompatMessage:
|
||||||
properties:
|
properties:
|
||||||
body:
|
body:
|
||||||
@ -420,6 +535,29 @@ definitions:
|
|||||||
usr_msg_id:
|
usr_msg_id:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
models.KeyTokenJSON:
|
||||||
|
properties:
|
||||||
|
all_channels:
|
||||||
|
type: boolean
|
||||||
|
channels:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
keytoken_id:
|
||||||
|
type: string
|
||||||
|
messages_sent:
|
||||||
|
type: integer
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
owner_user_id:
|
||||||
|
type: string
|
||||||
|
permissions:
|
||||||
|
type: string
|
||||||
|
timestamp_created:
|
||||||
|
type: string
|
||||||
|
timestamp_lastused:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
models.MessageJSON:
|
models.MessageJSON:
|
||||||
properties:
|
properties:
|
||||||
channel_id:
|
channel_id:
|
||||||
@ -468,8 +606,6 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
models.UserJSON:
|
models.UserJSON:
|
||||||
properties:
|
properties:
|
||||||
admin_key:
|
|
||||||
type: string
|
|
||||||
default_channel:
|
default_channel:
|
||||||
type: string
|
type: string
|
||||||
is_pro:
|
is_pro:
|
||||||
@ -482,10 +618,6 @@ definitions:
|
|||||||
type: integer
|
type: integer
|
||||||
quota_used:
|
quota_used:
|
||||||
type: integer
|
type: integer
|
||||||
read_key:
|
|
||||||
type: string
|
|
||||||
send_key:
|
|
||||||
type: string
|
|
||||||
timestamp_created:
|
timestamp_created:
|
||||||
type: string
|
type: string
|
||||||
timestamp_lastread:
|
timestamp_lastread:
|
||||||
@ -497,7 +629,7 @@ definitions:
|
|||||||
username:
|
username:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
models.UserJSONWithClients:
|
models.UserJSONWithClientsAndKeys:
|
||||||
properties:
|
properties:
|
||||||
admin_key:
|
admin_key:
|
||||||
type: string
|
type: string
|
||||||
@ -544,10 +676,6 @@ paths:
|
|||||||
description: All parameter can be set via query-parameter or the json body.
|
description: All parameter can be set via query-parameter or the json body.
|
||||||
Only UserID, UserKey and Title are required
|
Only UserID, UserKey and Title are required
|
||||||
parameters:
|
parameters:
|
||||||
- example: qhnUbKcLgp6tg
|
|
||||||
in: query
|
|
||||||
name: chan_key
|
|
||||||
type: string
|
|
||||||
- example: test
|
- example: test
|
||||||
in: query
|
in: query
|
||||||
name: channel
|
name: channel
|
||||||
@ -556,6 +684,10 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: content
|
name: content
|
||||||
type: string
|
type: string
|
||||||
|
- example: P3TNH8mvv14fm
|
||||||
|
in: query
|
||||||
|
name: key
|
||||||
|
type: string
|
||||||
- example: db8b0e6a-a08c-4646
|
- example: db8b0e6a-a08c-4646
|
||||||
in: query
|
in: query
|
||||||
name: msg_id
|
name: msg_id
|
||||||
@ -584,19 +716,11 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: user_id
|
name: user_id
|
||||||
type: string
|
type: string
|
||||||
- example: P3TNH8mvv14fm
|
|
||||||
in: query
|
|
||||||
name: user_key
|
|
||||||
type: string
|
|
||||||
- description: ' '
|
- description: ' '
|
||||||
in: body
|
in: body
|
||||||
name: post_body
|
name: post_body
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handler.SendMessage.combined'
|
$ref: '#/definitions/handler.SendMessage.combined'
|
||||||
- example: qhnUbKcLgp6tg
|
|
||||||
in: formData
|
|
||||||
name: chan_key
|
|
||||||
type: string
|
|
||||||
- example: test
|
- example: test
|
||||||
in: formData
|
in: formData
|
||||||
name: channel
|
name: channel
|
||||||
@ -605,6 +729,10 @@ paths:
|
|||||||
in: formData
|
in: formData
|
||||||
name: content
|
name: content
|
||||||
type: string
|
type: string
|
||||||
|
- example: P3TNH8mvv14fm
|
||||||
|
in: formData
|
||||||
|
name: key
|
||||||
|
type: string
|
||||||
- example: db8b0e6a-a08c-4646
|
- example: db8b0e6a-a08c-4646
|
||||||
in: formData
|
in: formData
|
||||||
name: msg_id
|
name: msg_id
|
||||||
@ -633,10 +761,6 @@ paths:
|
|||||||
in: formData
|
in: formData
|
||||||
name: user_id
|
name: user_id
|
||||||
type: string
|
type: string
|
||||||
- example: P3TNH8mvv14fm
|
|
||||||
in: formData
|
|
||||||
name: user_key
|
|
||||||
type: string
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
@ -1240,7 +1364,7 @@ paths:
|
|||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.UserJSONWithClients'
|
$ref: '#/definitions/models.UserJSONWithClientsAndKeys'
|
||||||
"400":
|
"400":
|
||||||
description: supplied values/parameters cannot be parsed / are invalid
|
description: supplied values/parameters cannot be parsed / are invalid
|
||||||
schema:
|
schema:
|
||||||
@ -1252,6 +1376,193 @@ paths:
|
|||||||
summary: Create a new user
|
summary: Create a new user
|
||||||
tags:
|
tags:
|
||||||
- API-v2
|
- API-v2
|
||||||
|
/api/v2/users/:uid/keys:
|
||||||
|
get:
|
||||||
|
description: The request must be done with an ADMIN key, the returned keys are
|
||||||
|
without their token.
|
||||||
|
operationId: api-tokenkeys-list
|
||||||
|
parameters:
|
||||||
|
- description: UserID
|
||||||
|
in: path
|
||||||
|
name: uid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handler.ListUserKeys.response'
|
||||||
|
"400":
|
||||||
|
description: supplied values/parameters cannot be parsed / are invalid
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: user is not authorized / has missing permissions
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: message not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: List keys of the user
|
||||||
|
tags:
|
||||||
|
- API-v2
|
||||||
|
post:
|
||||||
|
operationId: api-tokenkeys-create
|
||||||
|
parameters:
|
||||||
|
- description: UserID
|
||||||
|
in: path
|
||||||
|
name: uid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: ' '
|
||||||
|
in: body
|
||||||
|
name: post_body
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/handler.CreateUserKey.body'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.KeyTokenJSON'
|
||||||
|
"400":
|
||||||
|
description: supplied values/parameters cannot be parsed / are invalid
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: user is not authorized / has missing permissions
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: message not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: Create a new key
|
||||||
|
tags:
|
||||||
|
- API-v2
|
||||||
|
/api/v2/users/:uid/keys/:kid:
|
||||||
|
delete:
|
||||||
|
description: Cannot be used to delete the key used in the request itself
|
||||||
|
operationId: api-tokenkeys-delete
|
||||||
|
parameters:
|
||||||
|
- description: UserID
|
||||||
|
in: path
|
||||||
|
name: uid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: TokenKeyID
|
||||||
|
in: path
|
||||||
|
name: kid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.KeyTokenJSON'
|
||||||
|
"400":
|
||||||
|
description: supplied values/parameters cannot be parsed / are invalid
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: user is not authorized / has missing permissions
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: message not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: Delete a key
|
||||||
|
tags:
|
||||||
|
- API-v2
|
||||||
|
get:
|
||||||
|
description: The request must be done with an ADMIN key, the returned key does
|
||||||
|
not include its token.
|
||||||
|
operationId: api-tokenkeys-get
|
||||||
|
parameters:
|
||||||
|
- description: UserID
|
||||||
|
in: path
|
||||||
|
name: uid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: TokenKeyID
|
||||||
|
in: path
|
||||||
|
name: kid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.KeyTokenJSON'
|
||||||
|
"400":
|
||||||
|
description: supplied values/parameters cannot be parsed / are invalid
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: user is not authorized / has missing permissions
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: message not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: Get a single key
|
||||||
|
tags:
|
||||||
|
- API-v2
|
||||||
|
patch:
|
||||||
|
operationId: api-tokenkeys-update
|
||||||
|
parameters:
|
||||||
|
- description: UserID
|
||||||
|
in: path
|
||||||
|
name: uid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
- description: TokenKeyID
|
||||||
|
in: path
|
||||||
|
name: kid
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.KeyTokenJSON'
|
||||||
|
"400":
|
||||||
|
description: supplied values/parameters cannot be parsed / are invalid
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"401":
|
||||||
|
description: user is not authorized / has missing permissions
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"404":
|
||||||
|
description: message not found
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
"500":
|
||||||
|
description: internal server error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ginresp.apiError'
|
||||||
|
summary: Update a key
|
||||||
|
tags:
|
||||||
|
- API-v2
|
||||||
/api/v2/users/{uid}:
|
/api/v2/users/{uid}:
|
||||||
get:
|
get:
|
||||||
operationId: api-user-get
|
operationId: api-user-get
|
||||||
@ -1956,10 +2267,6 @@ paths:
|
|||||||
description: All parameter can be set via query-parameter or the json body.
|
description: All parameter can be set via query-parameter or the json body.
|
||||||
Only UserID, UserKey and Title are required
|
Only UserID, UserKey and Title are required
|
||||||
parameters:
|
parameters:
|
||||||
- example: qhnUbKcLgp6tg
|
|
||||||
in: query
|
|
||||||
name: chan_key
|
|
||||||
type: string
|
|
||||||
- example: test
|
- example: test
|
||||||
in: query
|
in: query
|
||||||
name: channel
|
name: channel
|
||||||
@ -1968,6 +2275,10 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: content
|
name: content
|
||||||
type: string
|
type: string
|
||||||
|
- example: P3TNH8mvv14fm
|
||||||
|
in: query
|
||||||
|
name: key
|
||||||
|
type: string
|
||||||
- example: db8b0e6a-a08c-4646
|
- example: db8b0e6a-a08c-4646
|
||||||
in: query
|
in: query
|
||||||
name: msg_id
|
name: msg_id
|
||||||
@ -1996,19 +2307,11 @@ paths:
|
|||||||
in: query
|
in: query
|
||||||
name: user_id
|
name: user_id
|
||||||
type: string
|
type: string
|
||||||
- example: P3TNH8mvv14fm
|
|
||||||
in: query
|
|
||||||
name: user_key
|
|
||||||
type: string
|
|
||||||
- description: ' '
|
- description: ' '
|
||||||
in: body
|
in: body
|
||||||
name: post_body
|
name: post_body
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/handler.SendMessage.combined'
|
$ref: '#/definitions/handler.SendMessage.combined'
|
||||||
- example: qhnUbKcLgp6tg
|
|
||||||
in: formData
|
|
||||||
name: chan_key
|
|
||||||
type: string
|
|
||||||
- example: test
|
- example: test
|
||||||
in: formData
|
in: formData
|
||||||
name: channel
|
name: channel
|
||||||
@ -2017,6 +2320,10 @@ paths:
|
|||||||
in: formData
|
in: formData
|
||||||
name: content
|
name: content
|
||||||
type: string
|
type: string
|
||||||
|
- example: P3TNH8mvv14fm
|
||||||
|
in: formData
|
||||||
|
name: key
|
||||||
|
type: string
|
||||||
- example: db8b0e6a-a08c-4646
|
- example: db8b0e6a-a08c-4646
|
||||||
in: formData
|
in: formData
|
||||||
name: msg_id
|
name: msg_id
|
||||||
@ -2045,10 +2352,6 @@ paths:
|
|||||||
in: formData
|
in: formData
|
||||||
name: user_id
|
name: user_id
|
||||||
type: string
|
type: string
|
||||||
- example: P3TNH8mvv14fm
|
|
||||||
in: formData
|
|
||||||
name: user_key
|
|
||||||
type: string
|
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -439,18 +439,6 @@ func TestChannelUpdate(t *testing.T) {
|
|||||||
tt.AssertEqual(t, "channels.send_key", chan0["send_key"], chan1["send_key"])
|
tt.AssertEqual(t, "channels.send_key", chan0["send_key"], chan1["send_key"])
|
||||||
}
|
}
|
||||||
|
|
||||||
// [4] renew send_key
|
|
||||||
|
|
||||||
tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", uid, chanid), gin.H{
|
|
||||||
"send_key": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
{
|
|
||||||
chan1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", uid, chanid))
|
|
||||||
tt.AssertNotEqual(t, "channels.subscribe_key", chan0["subscribe_key"], chan1["subscribe_key"])
|
|
||||||
tt.AssertNotEqual(t, "channels.send_key", chan0["send_key"], chan1["send_key"])
|
|
||||||
}
|
|
||||||
|
|
||||||
// [5] update description_name
|
// [5] update description_name
|
||||||
|
|
||||||
tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", uid, chanid), gin.H{
|
tt.RequestAuthPatch[tt.Void](t, admintok, baseUrl, fmt.Sprintf("/api/v2/users/%s/channels/%s", uid, chanid), gin.H{
|
||||||
|
@ -32,7 +32,6 @@ func TestGetClient(t *testing.T) {
|
|||||||
r1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
r1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
||||||
|
|
||||||
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r1["admin_key"])
|
|
||||||
tt.AssertEqual(t, "username", nil, r1["username"])
|
tt.AssertEqual(t, "username", nil, r1["username"])
|
||||||
|
|
||||||
type rt2 struct {
|
type rt2 struct {
|
||||||
|
@ -672,7 +672,7 @@ func TestCompatRequery(t *testing.T) {
|
|||||||
|
|
||||||
r6 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
r6 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_id": useridnew,
|
"user_id": useridnew,
|
||||||
"user_key": userkey,
|
"key": userkey,
|
||||||
"title": "HelloWorld_001",
|
"title": "HelloWorld_001",
|
||||||
"msg_id": "r6",
|
"msg_id": "r6",
|
||||||
})
|
})
|
||||||
|
@ -54,7 +54,7 @@ func TestDeleteMessage(t *testing.T) {
|
|||||||
admintok := r0["admin_key"].(string)
|
admintok := r0["admin_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Message_1",
|
"title": "Message_1",
|
||||||
})
|
})
|
||||||
@ -82,7 +82,7 @@ func TestDeleteMessageAndResendUsrMsgId(t *testing.T) {
|
|||||||
admintok := r0["admin_key"].(string)
|
admintok := r0["admin_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Message_1",
|
"title": "Message_1",
|
||||||
"msg_id": "bef8dd3d-078e-4f89-abf4-5258ad22a2e4",
|
"msg_id": "bef8dd3d-078e-4f89-abf4-5258ad22a2e4",
|
||||||
@ -93,7 +93,7 @@ func TestDeleteMessageAndResendUsrMsgId(t *testing.T) {
|
|||||||
tt.RequestAuthGet[tt.Void](t, admintok, baseUrl, "/api/v2/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
tt.RequestAuthGet[tt.Void](t, admintok, baseUrl, "/api/v2/messages/"+fmt.Sprintf("%v", msg1["scn_msg_id"]))
|
||||||
|
|
||||||
msg2 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg2 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Message_1",
|
"title": "Message_1",
|
||||||
"msg_id": "bef8dd3d-078e-4f89-abf4-5258ad22a2e4",
|
"msg_id": "bef8dd3d-078e-4f89-abf4-5258ad22a2e4",
|
||||||
@ -106,7 +106,7 @@ func TestDeleteMessageAndResendUsrMsgId(t *testing.T) {
|
|||||||
// even though message is deleted, we still get a `suppress_send` on send_message
|
// even though message is deleted, we still get a `suppress_send` on send_message
|
||||||
|
|
||||||
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Message_1",
|
"title": "Message_1",
|
||||||
"msg_id": "bef8dd3d-078e-4f89-abf4-5258ad22a2e4",
|
"msg_id": "bef8dd3d-078e-4f89-abf4-5258ad22a2e4",
|
||||||
@ -123,7 +123,7 @@ func TestGetMessageSimple(t *testing.T) {
|
|||||||
data := tt.InitDefaultData(t, ws)
|
data := tt.InitDefaultData(t, ws)
|
||||||
|
|
||||||
msgOut := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msgOut := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": data.User[0].SendKey,
|
"key": data.User[0].SendKey,
|
||||||
"user_id": data.User[0].UID,
|
"user_id": data.User[0].UID,
|
||||||
"title": "Message_1",
|
"title": "Message_1",
|
||||||
})
|
})
|
||||||
@ -163,7 +163,7 @@ func TestGetMessageFull(t *testing.T) {
|
|||||||
content := tt.ShortLipsum0(2)
|
content := tt.ShortLipsum0(2)
|
||||||
|
|
||||||
msgOut := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msgOut := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": data.User[0].SendKey,
|
"key": data.User[0].SendKey,
|
||||||
"user_id": data.User[0].UID,
|
"user_id": data.User[0].UID,
|
||||||
"title": "Message_1",
|
"title": "Message_1",
|
||||||
"content": content,
|
"content": content,
|
||||||
|
@ -31,19 +31,19 @@ func TestSendSimpleMessageJSON(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_001",
|
"title": "HelloWorld_001",
|
||||||
})
|
})
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": readtok,
|
"key": readtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_001",
|
"title": "HelloWorld_001",
|
||||||
}, 401, apierr.USER_AUTH_FAILED)
|
}, 401, apierr.USER_AUTH_FAILED)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": "asdf",
|
"key": "asdf",
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_001",
|
"title": "HelloWorld_001",
|
||||||
}, 401, apierr.USER_AUTH_FAILED)
|
}, 401, apierr.USER_AUTH_FAILED)
|
||||||
@ -82,7 +82,7 @@ func TestSendSimpleMessageQuery(t *testing.T) {
|
|||||||
admintok := r0["admin_key"].(string)
|
admintok := r0["admin_key"].(string)
|
||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("Hello World 2134")), nil)
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&key=%s&title=%s", uid, sendtok, url.QueryEscape("Hello World 2134")), nil)
|
||||||
|
|
||||||
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
|
tt.AssertEqual(t, "messageCount", 1, len(pusher.Data))
|
||||||
tt.AssertStrRepEqual(t, "msg.title", "Hello World 2134", pusher.Last().Message.Title)
|
tt.AssertStrRepEqual(t, "msg.title", "Hello World 2134", pusher.Last().Message.Title)
|
||||||
@ -119,7 +119,7 @@ func TestSendSimpleMessageForm(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", tt.FormData{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Hello World 9999 [$$$]",
|
"title": "Hello World 9999 [$$$]",
|
||||||
})
|
})
|
||||||
@ -157,8 +157,8 @@ func TestSendSimpleMessageFormAndQuery(t *testing.T) {
|
|||||||
uid := r0["user_id"].(string)
|
uid := r0["user_id"].(string)
|
||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), tt.FormData{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), tt.FormData{
|
||||||
"user_key": "ERR",
|
"key": "ERR",
|
||||||
"user_id": "999999",
|
"user_id": "999999",
|
||||||
"title": "2222222",
|
"title": "2222222",
|
||||||
})
|
})
|
||||||
@ -185,8 +185,8 @@ func TestSendSimpleMessageJSONAndQuery(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
// query overwrite body
|
// query overwrite body
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&user_key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&key=%s&title=%s", uid, sendtok, url.QueryEscape("1111111")), gin.H{
|
||||||
"user_key": "ERR",
|
"key": "ERR",
|
||||||
"user_id": models.NewUserID(),
|
"user_id": models.NewUserID(),
|
||||||
"title": "2222222",
|
"title": "2222222",
|
||||||
})
|
})
|
||||||
@ -215,13 +215,13 @@ func TestSendSimpleMessageAlt1(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/send", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/send", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_001",
|
"title": "HelloWorld_001",
|
||||||
})
|
})
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/send", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/send", gin.H{
|
||||||
"user_key": readtok,
|
"key": readtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_001",
|
"title": "HelloWorld_001",
|
||||||
}, 401, apierr.USER_AUTH_FAILED)
|
}, 401, apierr.USER_AUTH_FAILED)
|
||||||
@ -261,7 +261,7 @@ func TestSendContentMessage(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": "I am Content\nasdf",
|
"content": "I am Content\nasdf",
|
||||||
@ -306,7 +306,7 @@ func TestSendWithSendername(t *testing.T) {
|
|||||||
admintok := r0["admin_key"].(string)
|
admintok := r0["admin_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_xyz",
|
"title": "HelloWorld_xyz",
|
||||||
"content": "Unicode: 日本 - yäy\000\n\t\x00...",
|
"content": "Unicode: 日本 - yäy\000\n\t\x00...",
|
||||||
@ -360,7 +360,7 @@ func TestSendLongContent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -416,7 +416,7 @@ func TestSendTooLongContent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -445,7 +445,7 @@ func TestSendLongContentPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -459,7 +459,7 @@ func TestSendLongContentPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -474,7 +474,7 @@ func TestSendLongContentPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -488,7 +488,7 @@ func TestSendLongContentPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -502,7 +502,7 @@ func TestSendLongContentPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "HelloWorld_042",
|
"title": "HelloWorld_042",
|
||||||
"content": longContent,
|
"content": longContent,
|
||||||
@ -525,7 +525,7 @@ func TestSendTooLongTitle(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
"title": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
||||||
}, 400, apierr.TITLE_TOO_LONG)
|
}, 400, apierr.TITLE_TOO_LONG)
|
||||||
@ -549,7 +549,7 @@ func TestSendIdempotent(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Hello SCN",
|
"title": "Hello SCN",
|
||||||
"content": "mamma mia",
|
"content": "mamma mia",
|
||||||
@ -571,7 +571,7 @@ func TestSendIdempotent(t *testing.T) {
|
|||||||
tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages))
|
tt.AssertEqual(t, "len(messages)", 1, len(msgList1.Messages))
|
||||||
|
|
||||||
msg2 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg2 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Hello again",
|
"title": "Hello again",
|
||||||
"content": "mother mia",
|
"content": "mother mia",
|
||||||
@ -590,7 +590,7 @@ func TestSendIdempotent(t *testing.T) {
|
|||||||
tt.AssertEqual(t, "len(messages)", 1, len(msgList2.Messages))
|
tt.AssertEqual(t, "len(messages)", 1, len(msgList2.Messages))
|
||||||
|
|
||||||
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "Hello third",
|
"title": "Hello third",
|
||||||
"content": "let me go",
|
"content": "let me go",
|
||||||
@ -628,7 +628,7 @@ func TestSendWithPriority(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M_001",
|
"title": "M_001",
|
||||||
"content": "TestSendWithPriority#001",
|
"content": "TestSendWithPriority#001",
|
||||||
@ -646,7 +646,7 @@ func TestSendWithPriority(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
msg2 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg2 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M_002",
|
"title": "M_002",
|
||||||
"content": "TestSendWithPriority#002",
|
"content": "TestSendWithPriority#002",
|
||||||
@ -665,7 +665,7 @@ func TestSendWithPriority(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg3 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M_003",
|
"title": "M_003",
|
||||||
"content": "TestSendWithPriority#003",
|
"content": "TestSendWithPriority#003",
|
||||||
@ -684,7 +684,7 @@ func TestSendWithPriority(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
msg4 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg4 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M_004",
|
"title": "M_004",
|
||||||
"content": "TestSendWithPriority#004",
|
"content": "TestSendWithPriority#004",
|
||||||
@ -720,7 +720,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
admintok := r0["admin_key"].(string)
|
admintok := r0["admin_key"].(string)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -728,7 +728,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -736,7 +736,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -744,7 +744,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": admintok,
|
"key": admintok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -752,7 +752,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": admintok,
|
"key": admintok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -760,7 +760,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": admintok,
|
"key": admintok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -768,7 +768,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -776,7 +776,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -784,7 +784,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -792,7 +792,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": admintok,
|
"key": admintok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -800,7 +800,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": admintok,
|
"key": admintok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -808,7 +808,7 @@ func TestSendInvalidPriority(t *testing.T) {
|
|||||||
}, 400, apierr.INVALID_PRIO)
|
}, 400, apierr.INVALID_PRIO)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": admintok,
|
"key": admintok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "(title)",
|
"title": "(title)",
|
||||||
"content": "(content)",
|
"content": "(content)",
|
||||||
@ -838,7 +838,7 @@ func TestSendWithTimestamp(t *testing.T) {
|
|||||||
ts := time.Now().Unix() - int64(time.Hour.Seconds())
|
ts := time.Now().Unix() - int64(time.Hour.Seconds())
|
||||||
|
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", tt.FormData{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": fmt.Sprintf("%s", uid),
|
"user_id": fmt.Sprintf("%s", uid),
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": fmt.Sprintf("%d", ts),
|
"timestamp": fmt.Sprintf("%d", ts),
|
||||||
@ -892,83 +892,83 @@ func TestSendInvalidTimestamp(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": fmt.Sprintf("%s", uid),
|
"user_id": fmt.Sprintf("%s", uid),
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": "-10000",
|
"timestamp": "-10000",
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": fmt.Sprintf("%s", uid),
|
"user_id": fmt.Sprintf("%s", uid),
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": "0",
|
"timestamp": "0",
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": fmt.Sprintf("%s", uid),
|
"user_id": fmt.Sprintf("%s", uid),
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": fmt.Sprintf("%d", time.Now().Unix()-int64(25*time.Hour.Seconds())),
|
"timestamp": fmt.Sprintf("%d", time.Now().Unix()-int64(25*time.Hour.Seconds())),
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
tt.RequestPostShouldFail(t, baseUrl, "/", tt.FormData{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": fmt.Sprintf("%s", uid),
|
"user_id": fmt.Sprintf("%s", uid),
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": fmt.Sprintf("%d", time.Now().Unix()+int64(25*time.Hour.Seconds())),
|
"timestamp": fmt.Sprintf("%d", time.Now().Unix()+int64(25*time.Hour.Seconds())),
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": -10000,
|
"timestamp": -10000,
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": 0,
|
"timestamp": 0,
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": time.Now().Unix() - int64(25*time.Hour.Seconds()),
|
"timestamp": time.Now().Unix() - int64(25*time.Hour.Seconds()),
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "TTT",
|
"title": "TTT",
|
||||||
"timestamp": time.Now().Unix() + int64(25*time.Hour.Seconds()),
|
"timestamp": time.Now().Unix() + int64(25*time.Hour.Seconds()),
|
||||||
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
}, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
|
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?key=%s&user_id=%s&title=%s×tamp=%d",
|
||||||
sendtok,
|
sendtok,
|
||||||
uid,
|
uid,
|
||||||
"TTT",
|
"TTT",
|
||||||
-10000,
|
-10000,
|
||||||
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
|
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?key=%s&user_id=%s&title=%s×tamp=%d",
|
||||||
sendtok,
|
sendtok,
|
||||||
uid,
|
uid,
|
||||||
"TTT",
|
"TTT",
|
||||||
0,
|
0,
|
||||||
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
|
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?key=%s&user_id=%s&title=%s×tamp=%d",
|
||||||
sendtok,
|
sendtok,
|
||||||
uid,
|
uid,
|
||||||
"TTT",
|
"TTT",
|
||||||
time.Now().Unix()-int64(25*time.Hour.Seconds()),
|
time.Now().Unix()-int64(25*time.Hour.Seconds()),
|
||||||
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
), nil, 400, apierr.TIMESTAMP_OUT_OF_RANGE)
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?user_key=%s&user_id=%s&title=%s×tamp=%d",
|
tt.RequestPostShouldFail(t, baseUrl, fmt.Sprintf("/?key=%s&user_id=%s&title=%s×tamp=%d",
|
||||||
sendtok,
|
sendtok,
|
||||||
uid,
|
uid,
|
||||||
"TTT",
|
"TTT",
|
||||||
@ -1003,7 +1003,7 @@ func TestSendToNewChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M0",
|
"title": "M0",
|
||||||
})
|
})
|
||||||
@ -1015,7 +1015,7 @@ func TestSendToNewChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M1",
|
"title": "M1",
|
||||||
"content": tt.ShortLipsum0(4),
|
"content": tt.ShortLipsum0(4),
|
||||||
@ -1029,7 +1029,7 @@ func TestSendToNewChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M2",
|
"title": "M2",
|
||||||
"content": tt.ShortLipsum0(4),
|
"content": tt.ShortLipsum0(4),
|
||||||
@ -1043,7 +1043,7 @@ func TestSendToNewChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M3",
|
"title": "M3",
|
||||||
"channel": "test",
|
"channel": "test",
|
||||||
@ -1082,7 +1082,7 @@ func TestSendToManualChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M0",
|
"title": "M0",
|
||||||
})
|
})
|
||||||
@ -1094,7 +1094,7 @@ func TestSendToManualChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M1",
|
"title": "M1",
|
||||||
"content": tt.ShortLipsum0(4),
|
"content": tt.ShortLipsum0(4),
|
||||||
@ -1119,7 +1119,7 @@ func TestSendToManualChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M2",
|
"title": "M2",
|
||||||
"content": tt.ShortLipsum0(4),
|
"content": tt.ShortLipsum0(4),
|
||||||
@ -1133,7 +1133,7 @@ func TestSendToManualChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M3",
|
"title": "M3",
|
||||||
"channel": "test",
|
"channel": "test",
|
||||||
@ -1161,21 +1161,21 @@ func TestSendToTooLongChannel(t *testing.T) {
|
|||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M3",
|
"title": "M3",
|
||||||
"channel": "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
"channel": "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
||||||
})
|
})
|
||||||
|
|
||||||
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
tt.RequestPost[tt.Void](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M3",
|
"title": "M3",
|
||||||
"channel": "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
"channel": "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
||||||
})
|
})
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": "M3",
|
"title": "M3",
|
||||||
"channel": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901",
|
"channel": "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901",
|
||||||
@ -1203,7 +1203,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
@ -1222,7 +1222,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
|
|||||||
for i := 0; i < 48; i++ {
|
for i := 0; i < 48; i++ {
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
@ -1237,7 +1237,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg50 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg50 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
@ -1253,7 +1253,7 @@ func TestQuotaExceededNoPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
}, 403, apierr.QUOTA_REACHED)
|
}, 403, apierr.QUOTA_REACHED)
|
||||||
@ -1281,7 +1281,7 @@ func TestQuotaExceededPro(t *testing.T) {
|
|||||||
|
|
||||||
{
|
{
|
||||||
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg1 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
@ -1300,7 +1300,7 @@ func TestQuotaExceededPro(t *testing.T) {
|
|||||||
for i := 0; i < 998; i++ {
|
for i := 0; i < 998; i++ {
|
||||||
|
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
@ -1315,7 +1315,7 @@ func TestQuotaExceededPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg50 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
msg50 := tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
@ -1331,7 +1331,7 @@ func TestQuotaExceededPro(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
tt.RequestPostShouldFail(t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
}, 403, apierr.QUOTA_REACHED)
|
}, 403, apierr.QUOTA_REACHED)
|
||||||
@ -1361,7 +1361,7 @@ func TestSendParallel(t *testing.T) {
|
|||||||
sem <- tt.Void{}
|
sem <- tt.Void{}
|
||||||
}()
|
}()
|
||||||
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
tt.RequestPost[gin.H](t, baseUrl, "/", gin.H{
|
||||||
"user_key": sendtok,
|
"key": sendtok,
|
||||||
"user_id": uid,
|
"user_id": uid,
|
||||||
"title": tt.ShortLipsum0(2),
|
"title": tt.ShortLipsum0(2),
|
||||||
})
|
})
|
||||||
|
@ -19,7 +19,6 @@ func TestCreateUserNoClient(t *testing.T) {
|
|||||||
tt.AssertEqual(t, "len(clients)", 0, len(r0["clients"].([]any)))
|
tt.AssertEqual(t, "len(clients)", 0, len(r0["clients"].([]any)))
|
||||||
|
|
||||||
uid := fmt.Sprintf("%v", r0["user_id"])
|
uid := fmt.Sprintf("%v", r0["user_id"])
|
||||||
admintok := r0["admin_key"].(string)
|
|
||||||
readtok := r0["read_key"].(string)
|
readtok := r0["read_key"].(string)
|
||||||
sendtok := r0["send_key"].(string)
|
sendtok := r0["send_key"].(string)
|
||||||
|
|
||||||
@ -29,7 +28,6 @@ func TestCreateUserNoClient(t *testing.T) {
|
|||||||
r1 := tt.RequestAuthGet[gin.H](t, readtok, baseUrl, "/api/v2/users/"+uid)
|
r1 := tt.RequestAuthGet[gin.H](t, readtok, baseUrl, "/api/v2/users/"+uid)
|
||||||
|
|
||||||
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r1["admin_key"])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateUserDummyClient(t *testing.T) {
|
func TestCreateUserDummyClient(t *testing.T) {
|
||||||
@ -52,7 +50,6 @@ func TestCreateUserDummyClient(t *testing.T) {
|
|||||||
r1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
r1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
||||||
|
|
||||||
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r1["admin_key"])
|
|
||||||
tt.AssertEqual(t, "username", nil, r1["username"])
|
tt.AssertEqual(t, "username", nil, r1["username"])
|
||||||
|
|
||||||
type rt2 struct {
|
type rt2 struct {
|
||||||
@ -92,7 +89,6 @@ func TestCreateUserWithUsername(t *testing.T) {
|
|||||||
r1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
r1 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
||||||
|
|
||||||
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
tt.AssertEqual(t, "uid", uid, fmt.Sprintf("%v", r1["user_id"]))
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r1["admin_key"])
|
|
||||||
tt.AssertEqual(t, "username", "my_user", r1["username"])
|
tt.AssertEqual(t, "username", "my_user", r1["username"])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,65 +184,6 @@ func TestFailedUgradeUserToPro(t *testing.T) {
|
|||||||
tt.RequestAuthPatchShouldFail(t, admintok0, baseUrl, "/api/v2/users/"+uid0, gin.H{"pro_token": "@INVALID"}, 400, apierr.INVALID_PRO_TOKEN)
|
tt.RequestAuthPatchShouldFail(t, admintok0, baseUrl, "/api/v2/users/"+uid0, gin.H{"pro_token": "@INVALID"}, 400, apierr.INVALID_PRO_TOKEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecreateKeys(t *testing.T) {
|
|
||||||
_, baseUrl, stop := tt.StartSimpleWebserver(t)
|
|
||||||
defer stop()
|
|
||||||
|
|
||||||
r0 := tt.RequestPost[gin.H](t, baseUrl, "/api/v2/users", gin.H{
|
|
||||||
"agent_model": "DUMMY_PHONE",
|
|
||||||
"agent_version": "4X",
|
|
||||||
"client_type": "ANDROID",
|
|
||||||
"fcm_token": "DUMMY_FCM",
|
|
||||||
})
|
|
||||||
tt.AssertEqual(t, "username", nil, r0["username"])
|
|
||||||
|
|
||||||
uid := fmt.Sprintf("%v", r0["user_id"])
|
|
||||||
|
|
||||||
admintok := r0["admin_key"].(string)
|
|
||||||
readtok := r0["read_key"].(string)
|
|
||||||
sendtok := r0["send_key"].(string)
|
|
||||||
|
|
||||||
tt.RequestAuthPatchShouldFail(t, readtok, baseUrl, "/api/v2/users/"+uid, gin.H{"read_key": true}, 401, apierr.USER_AUTH_FAILED)
|
|
||||||
|
|
||||||
tt.RequestAuthPatchShouldFail(t, sendtok, baseUrl, "/api/v2/users/"+uid, gin.H{"read_key": true}, 401, apierr.USER_AUTH_FAILED)
|
|
||||||
|
|
||||||
r1 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid, gin.H{})
|
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r1["admin_key"])
|
|
||||||
tt.AssertEqual(t, "read_key", readtok, r1["read_key"])
|
|
||||||
tt.AssertEqual(t, "send_key", sendtok, r1["send_key"])
|
|
||||||
|
|
||||||
r2 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid, gin.H{"read_key": true})
|
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r2["admin_key"])
|
|
||||||
tt.AssertNotEqual(t, "read_key", readtok, r2["read_key"])
|
|
||||||
tt.AssertEqual(t, "send_key", sendtok, r2["send_key"])
|
|
||||||
readtok = r2["read_key"].(string)
|
|
||||||
|
|
||||||
r3 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid, gin.H{"read_key": true, "send_key": true})
|
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r3["admin_key"])
|
|
||||||
tt.AssertNotEqual(t, "read_key", readtok, r3["read_key"])
|
|
||||||
tt.AssertNotEqual(t, "send_key", sendtok, r3["send_key"])
|
|
||||||
readtok = r3["read_key"].(string)
|
|
||||||
sendtok = r3["send_key"].(string)
|
|
||||||
|
|
||||||
r4 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid)
|
|
||||||
tt.AssertEqual(t, "admin_key", admintok, r4["admin_key"])
|
|
||||||
tt.AssertEqual(t, "read_key", readtok, r4["read_key"])
|
|
||||||
tt.AssertEqual(t, "send_key", sendtok, r4["send_key"])
|
|
||||||
|
|
||||||
r5 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid, gin.H{"admin_key": true})
|
|
||||||
tt.AssertNotEqual(t, "admin_key", admintok, r5["admin_key"])
|
|
||||||
tt.AssertEqual(t, "read_key", readtok, r5["read_key"])
|
|
||||||
tt.AssertEqual(t, "send_key", sendtok, r5["send_key"])
|
|
||||||
admintokNew := r5["admin_key"].(string)
|
|
||||||
|
|
||||||
tt.RequestAuthGetShouldFail(t, admintok, baseUrl, "/api/v2/users/"+uid, 401, apierr.USER_AUTH_FAILED)
|
|
||||||
|
|
||||||
r6 := tt.RequestAuthGet[gin.H](t, admintokNew, baseUrl, "/api/v2/users/"+uid)
|
|
||||||
tt.AssertEqual(t, "admin_key", admintokNew, r6["admin_key"])
|
|
||||||
tt.AssertEqual(t, "read_key", readtok, r6["read_key"])
|
|
||||||
tt.AssertEqual(t, "send_key", sendtok, r6["send_key"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteUser(t *testing.T) {
|
func TestDeleteUser(t *testing.T) {
|
||||||
t.SkipNow() // TODO DeleteUser Not implemented
|
t.SkipNow() // TODO DeleteUser Not implemented
|
||||||
|
|
||||||
|
@ -352,9 +352,9 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
|
|||||||
body["user_id"] = users[mex.User].UID
|
body["user_id"] = users[mex.User].UID
|
||||||
switch mex.Key {
|
switch mex.Key {
|
||||||
case AKEY:
|
case AKEY:
|
||||||
body["user_key"] = users[mex.User].AdminKey
|
body["key"] = users[mex.User].AdminKey
|
||||||
case SKEY:
|
case SKEY:
|
||||||
body["user_key"] = users[mex.User].SendKey
|
body["key"] = users[mex.User].SendKey
|
||||||
}
|
}
|
||||||
if mex.Content != "" {
|
if mex.Content != "" {
|
||||||
body["content"] = mex.Content
|
body["content"] = mex.Content
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<pre>
|
<pre>
|
||||||
curl \
|
curl \
|
||||||
--data "user_id=${userid}" \
|
--data "user_id=${userid}" \
|
||||||
--data "user_key=${userkey}" \
|
--data "key=${key}" \
|
||||||
--data "title=${message_title}" \
|
--data "title=${message_title}" \
|
||||||
--data "content=${message_body}" \
|
--data "content=${message_body}" \
|
||||||
--data "priority=${0|1|2}" \
|
--data "priority=${0|1|2}" \
|
||||||
@ -36,7 +36,7 @@ curl \
|
|||||||
<pre>
|
<pre>
|
||||||
curl \
|
curl \
|
||||||
--data "user_id={userid}" \
|
--data "user_id={userid}" \
|
||||||
--data "user_key={userkey}" \
|
--data "key={key}" \
|
||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
{{config|baseURL}}/</pre>
|
{{config|baseURL}}/</pre>
|
||||||
|
|
||||||
|
@ -26,11 +26,11 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
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.
|
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>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>
|
||||||
<p>
|
<p>
|
||||||
You can at any time generate a new <code>user_key</code> in the app and invalidate the old one.
|
You can at any time generate a new <code>key</code> in the app and invalidate the old one.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
There is also a <a href="/">web interface</a> for this API to manually send notifications to your phone or to test your setup.
|
There is also a <a href="/">web interface</a> for this API to manually send notifications to your phone or to test your setup.
|
||||||
@ -52,7 +52,7 @@
|
|||||||
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).
|
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, 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, key]</code> pair and a <code>title</code> for your message, all other parameter are optional.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -90,7 +90,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td data-label="Statuscode">401 (Unauthorized)</td>
|
<td data-label="Statuscode">401 (Unauthorized)</td>
|
||||||
<td data-label="Explanation">The user_id was not found or the user_key is wrong</td>
|
<td data-label="Explanation">The user_id was not found or the key is wrong</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td data-label="Statuscode">403 (Forbidden)</td>
|
<td data-label="Statuscode">403 (Forbidden)</td>
|
||||||
@ -126,7 +126,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<pre>curl \
|
<pre>curl \
|
||||||
--data "user_id={userid}" \
|
--data "user_id={userid}" \
|
||||||
--data "user_key={userkey}" \
|
--data "key={key}" \
|
||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
--data "content={message_content}" \
|
--data "content={message_content}" \
|
||||||
{{config|baseURL}}/</pre>
|
{{config|baseURL}}/</pre>
|
||||||
@ -144,7 +144,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<pre>curl \
|
<pre>curl \
|
||||||
--data "user_id={userid}" \
|
--data "user_id={userid}" \
|
||||||
--data "user_key={userkey}" \
|
--data "key={key}" \
|
||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
--data "priority={0|1|2}" \
|
--data "priority={0|1|2}" \
|
||||||
{{config|baseURL}}/</pre>
|
{{config|baseURL}}/</pre>
|
||||||
@ -159,7 +159,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<pre>curl \
|
<pre>curl \
|
||||||
--data "user_id={userid}" \
|
--data "user_id={userid}" \
|
||||||
--data "user_key={userkey}" \
|
--data "key={key}" \
|
||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
--data "channel={my_channel}" \
|
--data "channel={my_channel}" \
|
||||||
{{config|baseURL}}/</pre>
|
{{config|baseURL}}/</pre>
|
||||||
@ -179,7 +179,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<pre>curl \
|
<pre>curl \
|
||||||
--data "user_id={userid}" \
|
--data "user_id={userid}" \
|
||||||
--data "user_key={userkey}" \
|
--data "key={key}" \
|
||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
--data "msg_id={message_id}" \
|
--data "msg_id={message_id}" \
|
||||||
{{config|baseURL}}/</pre>
|
{{config|baseURL}}/</pre>
|
||||||
@ -198,7 +198,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<pre>curl \
|
<pre>curl \
|
||||||
--data "user_id={userid}" \
|
--data "user_id={userid}" \
|
||||||
--data "user_key={userkey}" \
|
--data "key={key}" \
|
||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
--data "timestamp={unix_timestamp}" \
|
--data "timestamp={unix_timestamp}" \
|
||||||
{{config|baseURL}}/</pre>
|
{{config|baseURL}}/</pre>
|
||||||
|
@ -23,7 +23,7 @@ function send()
|
|||||||
|
|
||||||
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('key', key.value);
|
||||||
if (tit.value !== '') data.append('title', tit.value);
|
if (tit.value !== '') data.append('title', tit.value);
|
||||||
if (cnt.value !== '') data.append('content', cnt.value);
|
if (cnt.value !== '') data.append('content', cnt.value);
|
||||||
if (pio.value !== '') data.append('priority', pio.value);
|
if (pio.value !== '') data.append('priority', pio.value);
|
||||||
|
@ -89,7 +89,7 @@ usage<span style="color: #f92672">()</span> <span style="color: #f92672">{</span
|
|||||||
--output /dev/null <span style="color: #ae81ff">\</span>
|
--output /dev/null <span style="color: #ae81ff">\</span>
|
||||||
--write-out <span style="color: #e6db74">"%{http_code}"</span> <span style="color: #ae81ff">\</span>
|
--write-out <span style="color: #e6db74">"%{http_code}"</span> <span style="color: #ae81ff">\</span>
|
||||||
--data <span style="color: #e6db74">"user_id=$user_id"</span> <span style="color: #ae81ff">\</span>
|
--data <span style="color: #e6db74">"user_id=$user_id"</span> <span style="color: #ae81ff">\</span>
|
||||||
--data <span style="color: #e6db74">"user_key=$user_key"</span> <span style="color: #ae81ff">\</span>
|
--data <span style="color: #e6db74">"key=$key"</span> <span style="color: #ae81ff">\</span>
|
||||||
--data <span style="color: #e6db74">"title=$title"</span> <span style="color: #ae81ff">\</span>
|
--data <span style="color: #e6db74">"title=$title"</span> <span style="color: #ae81ff">\</span>
|
||||||
--data <span style="color: #e6db74">"timestamp=$sendtime"</span> <span style="color: #ae81ff">\</span>
|
--data <span style="color: #e6db74">"timestamp=$sendtime"</span> <span style="color: #ae81ff">\</span>
|
||||||
--data <span style="color: #e6db74">"content=$content"</span> <span style="color: #ae81ff">\</span>
|
--data <span style="color: #e6db74">"content=$content"</span> <span style="color: #ae81ff">\</span>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<span style="color: #008800; font-style: italic"># INSERT YOUR DATA HERE #</span>
|
<span style="color: #008800; font-style: italic"># INSERT YOUR DATA HERE #</span>
|
||||||
<span style="color: #008800; font-style: italic">################################################################################</span>
|
<span style="color: #008800; font-style: italic">################################################################################</span>
|
||||||
user_id=<span style="color: #0000FF">"999"</span>
|
user_id=<span style="color: #0000FF">"999"</span>
|
||||||
user_key=<span style="color: #0000FF">"??"</span>
|
key=<span style="color: #0000FF">"??"</span>
|
||||||
<span style="color: #008800; font-style: italic">################################################################################</span>
|
<span style="color: #008800; font-style: italic">################################################################################</span>
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
@ -89,7 +89,7 @@ content=<span style="color: #0000FF">""</span>
|
|||||||
--output /dev/null <span style="color: #0000FF">\</span>
|
--output /dev/null <span style="color: #0000FF">\</span>
|
||||||
--write-out <span style="color: #0000FF">"%{http_code}"</span> <span style="color: #0000FF">\</span>
|
--write-out <span style="color: #0000FF">"%{http_code}"</span> <span style="color: #0000FF">\</span>
|
||||||
--data <span style="color: #0000FF">"user_id=$user_id"</span> <span style="color: #0000FF">\</span>
|
--data <span style="color: #0000FF">"user_id=$user_id"</span> <span style="color: #0000FF">\</span>
|
||||||
--data <span style="color: #0000FF">"user_key=$user_key"</span> <span style="color: #0000FF">\</span>
|
--data <span style="color: #0000FF">"key=$key"</span> <span style="color: #0000FF">\</span>
|
||||||
--data <span style="color: #0000FF">"title=$title"</span> <span style="color: #0000FF">\</span>
|
--data <span style="color: #0000FF">"title=$title"</span> <span style="color: #0000FF">\</span>
|
||||||
--data <span style="color: #0000FF">"timestamp=$sendtime"</span> <span style="color: #0000FF">\</span>
|
--data <span style="color: #0000FF">"timestamp=$sendtime"</span> <span style="color: #0000FF">\</span>
|
||||||
--data <span style="color: #0000FF">"content=$content"</span> <span style="color: #0000FF">\</span>
|
--data <span style="color: #0000FF">"content=$content"</span> <span style="color: #0000FF">\</span>
|
||||||
|
Loading…
Reference in New Issue
Block a user