mirror of
https://github.com/pushbits/server.git
synced 2025-04-29 10:16:50 +02:00
Compare commits
45 commits
Author | SHA1 | Date | |
---|---|---|---|
|
b2e0275917 | ||
|
c13ebca593 | ||
|
83a9034be7 | ||
|
b0699da1e9 | ||
|
e657884326 | ||
|
737701d116 | ||
|
c8705b146e | ||
|
6903bdb62e | ||
|
c871854a13 | ||
|
15ea2935be | ||
|
35f957332b | ||
|
9852977baa | ||
|
175228bbe4 | ||
|
e836956019 | ||
|
e426b46f28 | ||
|
78a99281a7 | ||
|
61d5e04ecf | ||
|
833e666c37 | ||
|
77603a5c96 | ||
|
9f50df63fa | ||
|
f69833aeaa | ||
|
e2fbfbb28a | ||
|
551a706358 | ||
|
b058c03c30 | ||
|
291b32856c | ||
|
2c20e42a21 | ||
|
5e640800fe | ||
|
50a92b6d22 | ||
|
bc65c4661e | ||
|
f251b12fc8 | ||
|
e078a30fe2 | ||
|
c9fbdfda80 | ||
|
64563989b5 | ||
|
ead4f364f7 | ||
|
3bc0f26b81 | ||
|
2ddc0fbd74 | ||
|
21a4d156e1 | ||
|
f8bed7470c | ||
|
f739672e01 | ||
|
e355152330 | ||
|
fca461aaf7 | ||
|
7087cc6df9 | ||
|
77765e77a9 | ||
|
38b615a05d | ||
|
802eee5262 |
60 changed files with 971 additions and 1015 deletions
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
|||
layout_poetry
|
11
.github/workflows/documentation.yml
vendored
11
.github/workflows/documentation.yml
vendored
|
@ -6,6 +6,7 @@ on:
|
|||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24.0'
|
||||
PB_BUILD_VERSION: unknown # Needed for using Make targets.
|
||||
SSH_AUTH_SOCK: /tmp/ssh_agent.sock
|
||||
|
||||
|
@ -13,17 +14,15 @@ jobs:
|
|||
build_documentation:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Export GOBIN
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install Poetry
|
||||
run: curl -sSL https://install.python-poetry.org | python3 -
|
||||
go-version: '${{env.GO_VERSION}}'
|
||||
|
||||
- name: Install dependencies
|
||||
run: make setup
|
||||
|
@ -35,7 +34,7 @@ jobs:
|
|||
run: make swag
|
||||
|
||||
- name: Build static HTML
|
||||
run: npx redoc-cli bundle docs/swagger.yaml --output index.html
|
||||
run: npx redoc-cli build docs/swagger.yaml --output index.html
|
||||
|
||||
- name: Setup SSH keys and known_hosts
|
||||
run: |
|
||||
|
|
25
.github/workflows/publish.yml
vendored
25
.github/workflows/publish.yml
vendored
|
@ -5,10 +5,14 @@ on:
|
|||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24.0'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
@ -16,25 +20,21 @@ jobs:
|
|||
fetch-depth: 0 # Needed to describe git ref during build.
|
||||
|
||||
- name: Export GOBIN
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install Poetry
|
||||
run: curl -sSL https://install.python-poetry.org | python3 -
|
||||
go-version: '${{env.GO_VERSION}}'
|
||||
|
||||
- name: Install dependencies
|
||||
run: make setup
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
source $(poetry env info --path)/bin/activate
|
||||
make test
|
||||
run: make test
|
||||
|
||||
publish_docker_image:
|
||||
name: Publish Docker image
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
packages: write
|
||||
env:
|
||||
|
@ -79,6 +79,7 @@ jobs:
|
|||
name: Publish GitHub Release
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
|
@ -86,15 +87,15 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
|
||||
- name: Export GOBIN
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: '${{env.GO_VERSION}}'
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
20
.github/workflows/test.yml
vendored
20
.github/workflows/test.yml
vendored
|
@ -3,38 +3,34 @@ name: Test
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- '**'
|
||||
- '!v[0-9]+.[0-9]+.[0-9]+'
|
||||
- 'main'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24.0'
|
||||
PB_BUILD_VERSION: pipeline-${{ github.sha }}
|
||||
|
||||
jobs:
|
||||
test_build:
|
||||
name: Test and build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Export GOBIN
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install Poetry
|
||||
run: curl -sSL https://install.python-poetry.org | python3 -
|
||||
go-version: '${{env.GO_VERSION}}'
|
||||
|
||||
- name: Install dependencies
|
||||
run: make setup
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
source $(poetry env info --path)/bin/activate
|
||||
make test
|
||||
run: make test
|
||||
|
||||
- name: Build
|
||||
run: make build
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "tests/semgrep-rules"]
|
||||
path = tests/semgrep-rules
|
||||
url = https://github.com/returntocorp/semgrep-rules
|
14
Dockerfile
14
Dockerfile
|
@ -1,17 +1,22 @@
|
|||
FROM docker.io/library/golang:alpine as builder
|
||||
|
||||
ARG PB_BUILD_VERSION
|
||||
ARG CLI_VERSION=0.0.6
|
||||
ARG CLI_PLATFORM=linux_amd64
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache build-base \
|
||||
&& apk add --no-cache build-base ca-certificates curl \
|
||||
&& go mod download \
|
||||
&& go mod verify \
|
||||
&& PB_BUILD_VERSION="$PB_BUILD_VERSION" make build \
|
||||
&& chmod +x /build/out/pushbits
|
||||
&& make build \
|
||||
&& chmod +x /build/out/pushbits \
|
||||
&& curl -q -s -S -L -o /tmp/pbcli_${CLI_VERSION}.tar.gz https://github.com/pushbits/cli/releases/download/v${CLI_VERSION}/pbcli_${CLI_VERSION}_${CLI_PLATFORM}.tar.gz \
|
||||
&& tar -C /usr/local/bin -xvf /tmp/pbcli_${CLI_VERSION}.tar.gz pbcli \
|
||||
&& chown root:root /usr/local/bin/pbcli \
|
||||
&& chmod +x /usr/local/bin/pbcli
|
||||
|
||||
FROM docker.io/library/alpine
|
||||
|
||||
|
@ -25,6 +30,7 @@ WORKDIR /app
|
|||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=builder /build/out/pushbits ./run
|
||||
COPY --from=builder /usr/local/bin/pbcli /usr/local/bin/pbcli
|
||||
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache ca-certificates curl \
|
||||
|
|
54
Makefile
54
Makefile
|
@ -1,57 +1,69 @@
|
|||
# References:
|
||||
# [1] Needed so the Go files of semgrep-rules do not interfere with static analysis
|
||||
|
||||
DOCS_DIR := ./docs
|
||||
OUT_DIR := ./out
|
||||
TESTS_DIR := ./tests
|
||||
|
||||
GO_FILES := $(shell find . -type f \( -iname '*.go' ! -path "./tests/semgrep-rules/*" \))
|
||||
|
||||
PB_BUILD_VERSION ?= $(shell git describe --tags)
|
||||
ifeq ($(PB_BUILD_VERSION),)
|
||||
_ := $(error Cannot determine build version)
|
||||
endif
|
||||
|
||||
SEMGREP_MODFILE := $(TESTS_DIR)/semgrep-rules/go.mod
|
||||
GO_FILES := $(shell find . -type f \( -iname '*.go' \))
|
||||
GO_MODULE := github.com/pushbits/server
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
mkdir -p $(OUT_DIR)
|
||||
go build -ldflags="-w -s -X main.version=$(PB_BUILD_VERSION)" -o $(OUT_DIR)/pushbits ./cmd/pushbits
|
||||
go build -ldflags="-w -s" -o $(OUT_DIR)/pushbits ./cmd/pushbits
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(DOCS_DIR)
|
||||
rm -rf $(OUT_DIR)
|
||||
rm -rf $(SEMGREP_MODFILE)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
touch $(SEMGREP_MODFILE) # See [1].
|
||||
stdout=$$(gofumpt -l $(GO_FILES) 2>&1); if [ "$$stdout" ]; then exit 1; fi
|
||||
if [ -n "$$(gofumpt -l $(GO_FILES))" ]; then echo "Code is not properly formatted"; exit 1; fi
|
||||
if [ -n "$$(goimports -l -local $(GO_MODULE) $(GO_FILES))" ]; then echo "Imports are not properly formatted"; exit 1; fi
|
||||
go vet ./...
|
||||
misspell -error $(GO_FILES)
|
||||
gocyclo -over 10 $(GO_FILES)
|
||||
staticcheck ./...
|
||||
errcheck -ignoregenerated -exclude errcheck_excludes.txt ./...
|
||||
gocritic check -disable='#experimental,#opinionated' -@ifElseChain.minThreshold 3 ./...
|
||||
revive -set_exit_status -exclude ./docs ./...
|
||||
nilaway ./...
|
||||
go test -v -cover ./...
|
||||
gosec -exclude-dir=tests ./...
|
||||
semgrep --lang=go --config=tests/semgrep-rules/go --metrics=off
|
||||
rm -rf $(SEMGREP_MODFILE) # See [1].
|
||||
gosec -exclude-generated -exclude-dir=tests ./...
|
||||
govulncheck ./...
|
||||
@printf '\n%s\n' "> Test successful"
|
||||
|
||||
.PHONY: setup
|
||||
setup:
|
||||
git submodule update --init --recursive
|
||||
go install github.com/client9/misspell/cmd/misspell@latest
|
||||
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
|
||||
go install github.com/go-critic/go-critic/cmd/gocritic@latest
|
||||
go install github.com/kisielk/errcheck@latest
|
||||
go install github.com/mgechev/revive@latest
|
||||
go install github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
go install github.com/swaggo/swag/cmd/swag@latest
|
||||
go install go.uber.org/nilaway/cmd/nilaway@latest
|
||||
go install golang.org/x/tools/cmd/goimports@latest
|
||||
go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
go install mvdan.cc/gofumpt@latest
|
||||
poetry install
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
gofumpt -l -w $(GO_FILES)
|
||||
|
||||
.PHONY: swag
|
||||
swag:
|
||||
swag: build
|
||||
swag init --parseDependency=true --exclude $(TESTS_DIR) -g cmd/pushbits/main.go
|
||||
|
||||
.PHONY: docker_build_dev
|
||||
docker_build_dev:
|
||||
podman build \
|
||||
-t local/pushbits .
|
||||
|
||||
.PHONY: run_postgres_debug
|
||||
podman run \
|
||||
--rm \
|
||||
--name=postgres \
|
||||
--network=host \
|
||||
--env-file \
|
||||
postgres-debug.env docker.io/library/postgres:15
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/pushbits/server/actions"><img alt="Build status" src="https://img.shields.io/github/workflow/status/pushbits/server/Main"/></a>
|
||||
<a href="https://github.com/pushbits/server/actions"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/pushbits/server/test.yml?branch=main"/></a>
|
||||
<a href="https://www.pushbits.io/docs/"><img alt="Documentation" src="https://img.shields.io/badge/docs-online-success"/></a>
|
||||
<a href="https://www.pushbits.io/api/"><img alt="API Documentation" src="https://img.shields.io/badge/api docs-online-success"/></a>
|
||||
<a href="https://matrix.to/#/#pushbits:matrix.org"><img alt="Matrix" src="https://img.shields.io/matrix/pushbits:matrix.org"/></a>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// Package main provides the main function as a starting point of this tool.
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime/debug"
|
||||
"syscall"
|
||||
|
||||
"github.com/pushbits/server/internal/authentication/credentials"
|
||||
|
@ -14,8 +16,6 @@ import (
|
|||
"github.com/pushbits/server/internal/runner"
|
||||
)
|
||||
|
||||
var version string
|
||||
|
||||
func setupCleanup(db *database.Database, dp *dispatcher.Dispatcher) {
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
|
@ -28,8 +28,18 @@ func setupCleanup(db *database.Database, dp *dispatcher.Dispatcher) {
|
|||
}()
|
||||
}
|
||||
|
||||
func printStarupMessage() {
|
||||
buildInfo, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
log.L.Fatalln("Build info not available")
|
||||
return
|
||||
}
|
||||
|
||||
log.L.Printf("Starting PushBits %s", buildInfo.Main.Version)
|
||||
}
|
||||
|
||||
// @title PushBits Server API Documentation
|
||||
// @version 0.7.2
|
||||
// @version 0.10.5
|
||||
// @description Documentation for the PushBits server API.
|
||||
|
||||
// @contact.name The PushBits Developers
|
||||
|
@ -44,11 +54,7 @@ func setupCleanup(db *database.Database, dp *dispatcher.Dispatcher) {
|
|||
|
||||
// @securityDefinitions.basic BasicAuth
|
||||
func main() {
|
||||
if len(version) == 0 {
|
||||
log.L.Panic("Version not set")
|
||||
} else {
|
||||
log.L.Printf("Starting PushBits %s", version)
|
||||
}
|
||||
printStarupMessage()
|
||||
|
||||
c := configuration.Get()
|
||||
|
||||
|
@ -62,6 +68,11 @@ func main() {
|
|||
db, err := database.Create(cm, c.Database.Dialect, c.Database.Connection)
|
||||
if err != nil {
|
||||
log.L.Fatal(err)
|
||||
return
|
||||
}
|
||||
if db == nil {
|
||||
log.L.Fatal("db is nil but error was nil")
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
|
@ -72,23 +83,31 @@ func main() {
|
|||
dp, err := dispatcher.Create(c.Matrix.Homeserver, c.Matrix.Username, c.Matrix.Password, c.Formatting)
|
||||
if err != nil {
|
||||
log.L.Fatal(err)
|
||||
return
|
||||
}
|
||||
if dp == nil {
|
||||
log.L.Fatal("dp is nil but error was nil")
|
||||
return
|
||||
}
|
||||
defer dp.Close()
|
||||
|
||||
setupCleanup(db, dp)
|
||||
|
||||
err = db.RepairChannels(dp)
|
||||
err = db.RepairChannels(dp, &c.RepairBehavior)
|
||||
if err != nil {
|
||||
log.L.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
engine, err := router.Create(c.Debug, c.HTTP.TrustedProxies, cm, db, dp, &c.Alertmanager)
|
||||
if err != nil {
|
||||
log.L.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = runner.Run(engine, c.HTTP.ListenAddress, c.HTTP.Port)
|
||||
err = runner.Run(engine, c)
|
||||
if err != nil {
|
||||
log.L.Fatal(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,20 @@ http:
|
|||
# What proxies to trust.
|
||||
trustedproxies: []
|
||||
|
||||
# Filename of the TLS certificate.
|
||||
certfile: ''
|
||||
|
||||
# Filename of the TLS private key.
|
||||
keyfile: ''
|
||||
|
||||
database:
|
||||
# Currently sqlite3 and mysql are supported.
|
||||
# Currently sqlite3, mysql, and postgres are supported.
|
||||
dialect: 'sqlite3'
|
||||
|
||||
# For sqlite3, specifies the database file. For mysql, specifies the connection string. Check out
|
||||
# https://github.com/go-sql-driver/mysql#dsn-data-source-name for details.
|
||||
# - For sqlite3, specify the database file.
|
||||
# - For mysql specify the connection string. See details at https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
||||
# - For postgres, see https://github.com/jackc/pgx.
|
||||
# Also consider the canonical docs at https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING.
|
||||
connection: 'pushbits.db'
|
||||
|
||||
admin:
|
||||
|
@ -70,3 +78,9 @@ alertmanager:
|
|||
annotationtitle: title
|
||||
# The name of the entry in the alerts annotations or labels that should be used for the message
|
||||
annotationmessage: message
|
||||
|
||||
repairbehavior:
|
||||
# Reset the room's name to what was initially set by PushBits.
|
||||
resetroomname: true
|
||||
# Reset the room's topic to what was initially set by PushBits.
|
||||
resetroomtopic: true
|
||||
|
|
1
errcheck_excludes.txt
Normal file
1
errcheck_excludes.txt
Normal file
|
@ -0,0 +1 @@
|
|||
(*github.com/gin-gonic/gin.Context).AbortWithError
|
90
go.mod
90
go.mod
|
@ -1,44 +1,70 @@
|
|||
module github.com/pushbits/server
|
||||
|
||||
go 1.18
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b
|
||||
github.com/gin-contrib/location v0.0.2
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd
|
||||
github.com/jinzhu/configor v1.2.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/alexedwards/argon2id v1.0.0
|
||||
github.com/gin-contrib/location v1.0.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/gomarkdown/markdown v0.0.0-20250207164621-7a1f277a159e
|
||||
github.com/jinzhu/configor v1.2.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gorm.io/driver/mysql v1.0.4
|
||||
gorm.io/driver/sqlite v1.2.6
|
||||
gorm.io/gorm v1.22.3
|
||||
maunium.net/go/mautrix v0.10.10
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/driver/sqlite v1.5.7
|
||||
gorm.io/gorm v1.25.12
|
||||
maunium.net/go/mautrix v0.23.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/btcsuite/btcutil v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/bytedance/sonic v1.12.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
||||
github.com/golang/protobuf v1.3.3 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.2 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.2 // indirect
|
||||
github.com/json-iterator/go v1.1.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.10 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.mau.fi/util v0.8.4 // indirect
|
||||
golang.org/x/arch v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
298
go.sum
298
go.sum
|
@ -1,141 +1,207 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b h1:jEg+fE+POnmUy40B+aSKEPqZDmsdl55hZU0YKXEzz1k=
|
||||
github.com/alexedwards/argon2id v0.0.0-20201228115903-cf543ebc1f7b/go.mod h1:Kmn5t2Rb93Q4NTprN4+CCgARGvigKMJyxP0WckpTUp0=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
|
||||
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHcQQP0w=
|
||||
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
|
||||
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
|
||||
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gin-contrib/location v0.0.2 h1:QZKh1+K/LLR4KG/61eIO3b7MLuKi8tytQhV6texLgP4=
|
||||
github.com/gin-contrib/location v0.0.2/go.mod h1:NGoidiRlf0BlA/VKSVp+g3cuSMeTmip/63PhEjRhUAc=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd h1:0b8AqsWQb6A0jjx80UXLG/uMTXQkGD0IGuXWqsrNz1M=
|
||||
github.com/gomarkdown/markdown v0.0.0-20210408062403-ad838ccf8cdd/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gin-contrib/location v1.0.2 h1:FQ3bYuxtXjv/E8unfwfqdUXPms9JR64qBEul8TOw6IA=
|
||||
github.com/gin-contrib/location v1.0.2/go.mod h1:g+5CKBkpOHL+PkrH2j6wK1u46MTOKZvBM27Vg4/IFuc=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
|
||||
github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gomarkdown/markdown v0.0.0-20250207164621-7a1f277a159e h1:ESHlT0RVZphh4JGBz49I5R6nTdC8Qyc08vU25GQHzzQ=
|
||||
github.com/gomarkdown/markdown v0.0.0-20250207164621-7a1f277a159e/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
|
||||
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
|
||||
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/configor v1.2.2 h1:sLgh6KMzpCmaQB4e+9Fu/29VErtBUqsS2t8C9BNIVsA=
|
||||
github.com/jinzhu/configor v1.2.2/go.mod h1:iFFSfOBKP3kC2Dku0ZGB3t3aulfQgTGJknodhFavsU8=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.3/go.mod h1:5WdjKx3AQMvCJ4RG6/2UYT7dLrGvJUV1x4jdTAyGvZs=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ=
|
||||
go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY=
|
||||
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
|
||||
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
|
||||
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.0.4 h1:TATTzt+kR+IV0+h3iUB3dHUe8omCvQ0rOkmfCsUBohk=
|
||||
gorm.io/driver/mysql v1.0.4/go.mod h1:MEgp8tk2n60cSBCq5iTcPDw3ns8Gs+zOva9EUhkknTs=
|
||||
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
|
||||
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
|
||||
gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.22.3 h1:/JS6z+GStEQvJNW3t1FTwJwG/gZ+A7crFdRqtvG5ehA=
|
||||
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||
maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
|
||||
maunium.net/go/mautrix v0.10.10 h1:aaEuVopM3rkgOxL8Ldn2E8vcIIfKDE+tBfX/uPCRFWs=
|
||||
maunium.net/go/mautrix v0.10.10/go.mod h1:4XljZZGZiIlpfbQ+Tt2ykjapskJ8a7Z2i9y/+YaceF8=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
maunium.net/go/mautrix v0.23.0 h1:HNlR19eew5lvrNSL2muhExaGhYdaGk5FfEiA82QqUP4=
|
||||
maunium.net/go/mautrix v0.23.0/go.mod h1:AGnnaz3ylGikUo1I1MJVn9QLsl2No1/ZNnGDyO0QD5s=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package alertmanager provides definitions and functionality related to Alertmanager notifications.
|
||||
package alertmanager
|
||||
|
||||
import (
|
||||
|
@ -5,18 +6,21 @@ import (
|
|||
"net/url"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/pushbits/server/internal/api"
|
||||
"github.com/pushbits/server/internal/authentication"
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
)
|
||||
|
||||
type AlertmanagerHandler struct {
|
||||
// Handler holds information for processing alerts received via Alertmanager.
|
||||
type Handler struct {
|
||||
DP api.NotificationDispatcher
|
||||
Settings AlertmanagerHandlerSettings
|
||||
Settings HandlerSettings
|
||||
}
|
||||
|
||||
type AlertmanagerHandlerSettings struct {
|
||||
// HandlerSettings represents the settings for processing alerts received via Alertmanager.
|
||||
type HandlerSettings struct {
|
||||
TitleAnnotation string
|
||||
MessageAnnotation string
|
||||
}
|
||||
|
@ -33,8 +37,12 @@ type AlertmanagerHandlerSettings struct {
|
|||
// @Success 200 {object} []model.Notification
|
||||
// @Failure 500,404,403 ""
|
||||
// @Router /alert [post]
|
||||
func (h *AlertmanagerHandler) CreateAlert(ctx *gin.Context) {
|
||||
func (h *Handler) CreateAlert(ctx *gin.Context) {
|
||||
application := authentication.GetApplication(ctx)
|
||||
if application == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.L.Printf("Sending alert notification for application %s.", application.Name)
|
||||
|
||||
var hook model.AlertmanagerWebhook
|
||||
|
@ -52,7 +60,7 @@ func (h *AlertmanagerHandler) CreateAlert(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
notification.ID = messageID
|
||||
notification.UrlEncodedID = url.QueryEscape(messageID)
|
||||
notification.URLEncodedID = url.QueryEscape(messageID)
|
||||
notifications[i] = notification
|
||||
}
|
||||
ctx.JSON(http.StatusOK, ¬ifications)
|
||||
|
|
101
internal/api/api_test.go
Normal file
101
internal/api/api_test.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/pushbits/server/internal/authentication/credentials"
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/database"
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests/mockups"
|
||||
)
|
||||
|
||||
// TestContext holds all test-related objects
|
||||
type TestContext struct {
|
||||
ApplicationHandler *ApplicationHandler
|
||||
Users []*model.User
|
||||
Database *database.Database
|
||||
NotificationHandler *NotificationHandler
|
||||
UserHandler *UserHandler
|
||||
Config *configuration.Configuration
|
||||
}
|
||||
|
||||
var GlobalTestContext *TestContext
|
||||
|
||||
func cleanup() {
|
||||
err := os.Remove("pushbits-test.db")
|
||||
if err != nil {
|
||||
log.L.Warnln("Cannot delete test database: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
cleanup()
|
||||
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
GlobalTestContext = CreateTestContext(nil)
|
||||
|
||||
m.Run()
|
||||
|
||||
cleanup()
|
||||
}
|
||||
|
||||
// GetTestContext initializes and verifies all required test components
|
||||
func GetTestContext(_ *testing.T) *TestContext {
|
||||
if GlobalTestContext == nil {
|
||||
GlobalTestContext = CreateTestContext(nil)
|
||||
}
|
||||
|
||||
return GlobalTestContext
|
||||
}
|
||||
|
||||
// CreateTestContext initializes and verifies all required test components
|
||||
func CreateTestContext(_ *testing.T) *TestContext {
|
||||
ctx := &TestContext{}
|
||||
|
||||
config := configuration.Configuration{}
|
||||
config.Database.Connection = "pushbits-test.db"
|
||||
config.Database.Dialect = "sqlite3"
|
||||
config.Crypto.Argon2.Iterations = 4
|
||||
config.Crypto.Argon2.Parallelism = 4
|
||||
config.Crypto.Argon2.Memory = 131072
|
||||
config.Crypto.Argon2.SaltLength = 16
|
||||
config.Crypto.Argon2.KeyLength = 32
|
||||
config.Admin.Name = "user"
|
||||
config.Admin.Password = "pushbits"
|
||||
ctx.Config = &config
|
||||
|
||||
db, err := mockups.GetEmptyDatabase(ctx.Config.Crypto)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
panic(fmt.Errorf("cannot set up database: %w", err))
|
||||
}
|
||||
ctx.Database = db
|
||||
|
||||
ctx.ApplicationHandler = &ApplicationHandler{
|
||||
DB: ctx.Database,
|
||||
DP: &mockups.MockDispatcher{},
|
||||
}
|
||||
|
||||
ctx.Users = mockups.GetUsers(ctx.Config)
|
||||
|
||||
ctx.NotificationHandler = &NotificationHandler{
|
||||
DB: ctx.Database,
|
||||
DP: &mockups.MockDispatcher{},
|
||||
}
|
||||
|
||||
ctx.UserHandler = &UserHandler{
|
||||
AH: ctx.ApplicationHandler,
|
||||
CM: credentials.CreateManager(false, ctx.Config.Crypto),
|
||||
DB: ctx.Database,
|
||||
DP: &mockups.MockDispatcher{},
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/pushbits/server/internal/authentication"
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
|
||||
|
@ -27,9 +28,13 @@ func (h *ApplicationHandler) generateToken(compat bool) string {
|
|||
}
|
||||
|
||||
func (h *ApplicationHandler) registerApplication(ctx *gin.Context, a *model.Application, u *model.User) error {
|
||||
if a == nil || u == nil {
|
||||
return errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
log.L.Printf("Registering application %s.", a.Name)
|
||||
|
||||
channelID, err := h.DP.RegisterApplication(a.ID, a.Name, a.Token, u.MatrixID)
|
||||
channelID, err := h.DP.RegisterApplication(a.ID, a.Name, u.MatrixID)
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
return err
|
||||
}
|
||||
|
@ -45,6 +50,10 @@ func (h *ApplicationHandler) registerApplication(ctx *gin.Context, a *model.Appl
|
|||
}
|
||||
|
||||
func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User, name string, compat bool) (*model.Application, error) {
|
||||
if u == nil {
|
||||
return nil, errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
log.L.Printf("Creating application %s.", name)
|
||||
|
||||
application := model.Application{}
|
||||
|
@ -70,6 +79,10 @@ func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User,
|
|||
}
|
||||
|
||||
func (h *ApplicationHandler) deleteApplication(ctx *gin.Context, a *model.Application, u *model.User) error {
|
||||
if a == nil || u == nil {
|
||||
return errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
log.L.Printf("Deleting application %s (ID %d).", a.Name, a.ID)
|
||||
|
||||
err := h.DP.DeregisterApplication(a, u)
|
||||
|
@ -86,6 +99,10 @@ func (h *ApplicationHandler) deleteApplication(ctx *gin.Context, a *model.Applic
|
|||
}
|
||||
|
||||
func (h *ApplicationHandler) updateApplication(ctx *gin.Context, a *model.Application, updateApplication *model.UpdateApplication) error {
|
||||
if a == nil || updateApplication == nil {
|
||||
return errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
log.L.Printf("Updating application %s (ID %d).", a.Name, a.ID)
|
||||
|
||||
if updateApplication.Name != nil {
|
||||
|
@ -104,7 +121,7 @@ func (h *ApplicationHandler) updateApplication(ctx *gin.Context, a *model.Applic
|
|||
return err
|
||||
}
|
||||
|
||||
err = h.DP.UpdateApplication(a)
|
||||
err = h.DP.UpdateApplication(a, &configuration.RepairBehavior{ResetRoomName: true, ResetRoomTopic: true})
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
return err
|
||||
}
|
||||
|
@ -120,7 +137,7 @@ func (h *ApplicationHandler) updateApplication(ctx *gin.Context, a *model.Applic
|
|||
// @Accept json,mpfd
|
||||
// @Produce json
|
||||
// @Param name query string true "Name of the application"
|
||||
// @Param strict_compatability query boolean false "Use strict compatability mode"
|
||||
// @Param strict_compatibility query boolean false "Use strict compatibility mode"
|
||||
// @Success 200 {object} model.Application
|
||||
// @Failure 400 ""
|
||||
// @Security BasicAuth
|
||||
|
@ -185,7 +202,7 @@ func (h *ApplicationHandler) GetApplications(ctx *gin.Context) {
|
|||
// @Router /application/{id} [get]
|
||||
func (h *ApplicationHandler) GetApplication(ctx *gin.Context) {
|
||||
application, err := getApplication(ctx, h.DB)
|
||||
if err != nil {
|
||||
if err != nil || application == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -217,7 +234,7 @@ func (h *ApplicationHandler) GetApplication(ctx *gin.Context) {
|
|||
// @Router /application/{id} [delete]
|
||||
func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) {
|
||||
application, err := getApplication(ctx, h.DB)
|
||||
if err != nil {
|
||||
if err != nil || application == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -242,14 +259,14 @@ func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) {
|
|||
// @Param id path int true "ID of the application"
|
||||
// @Param name query string false "New name for the application"
|
||||
// @Param refresh_token query bool false "Generate new refresh token for the application"
|
||||
// @Param strict_compatability query bool false "Whether to use strict compataibility mode"
|
||||
// @Param strict_compatibility query bool false "Whether to use strict compataibility mode"
|
||||
// @Success 200 ""
|
||||
// @Failure 500,404,403 ""
|
||||
// @Security BasicAuth
|
||||
// @Router /application/{id} [put]
|
||||
func (h *ApplicationHandler) UpdateApplication(ctx *gin.Context) {
|
||||
application, err := getApplication(ctx, h.DB)
|
||||
if err != nil {
|
||||
if err != nil || application == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -3,132 +3,64 @@ package api
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pushbits/server/internal/authentication/credentials"
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/database"
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
"github.com/pushbits/server/tests/mockups"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
TestApplicationHandler *ApplicationHandler
|
||||
TestUsers []*model.User
|
||||
TestDatabase *database.Database
|
||||
TestNotificationHandler *NotificationHandler
|
||||
TestUserHandler *UserHandler
|
||||
TestConfig *configuration.Configuration
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
)
|
||||
|
||||
// Collect all created applications to check & delete them later
|
||||
var SuccessAplications map[uint][]model.Application
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
cleanUp()
|
||||
// Get main config and adapt
|
||||
config := &configuration.Configuration{}
|
||||
|
||||
config.Database.Connection = "pushbits-test.db"
|
||||
config.Database.Dialect = "sqlite3"
|
||||
config.Crypto.Argon2.Iterations = 4
|
||||
config.Crypto.Argon2.Parallelism = 4
|
||||
config.Crypto.Argon2.Memory = 131072
|
||||
config.Crypto.Argon2.SaltLength = 16
|
||||
config.Crypto.Argon2.KeyLength = 32
|
||||
config.Admin.Name = "user"
|
||||
config.Admin.Password = "pushbits"
|
||||
|
||||
TestConfig = config
|
||||
|
||||
// Set up test environment
|
||||
db, err := mockups.GetEmptyDatabase(config.Crypto)
|
||||
if err != nil {
|
||||
cleanUp()
|
||||
log.L.Println("Can not set up database: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
TestDatabase = db
|
||||
|
||||
appHandler, err := getApplicationHandler(config)
|
||||
if err != nil {
|
||||
cleanUp()
|
||||
log.L.Println("Can not set up application handler: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
TestApplicationHandler = appHandler
|
||||
TestUsers = mockups.GetUsers(config)
|
||||
SuccessAplications = make(map[uint][]model.Application)
|
||||
|
||||
TestNotificationHandler = &NotificationHandler{
|
||||
DB: TestDatabase,
|
||||
DP: &mockups.MockDispatcher{},
|
||||
}
|
||||
|
||||
TestUserHandler = &UserHandler{
|
||||
AH: TestApplicationHandler,
|
||||
CM: credentials.CreateManager(false, config.Crypto),
|
||||
DB: TestDatabase,
|
||||
DP: &mockups.MockDispatcher{},
|
||||
}
|
||||
|
||||
// Run
|
||||
m.Run()
|
||||
log.L.Println("Clean up after Test")
|
||||
cleanUp()
|
||||
}
|
||||
var SuccessApplications = make(map[uint][]model.Application)
|
||||
|
||||
func TestApi_RegisterApplicationWithoutUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
reqWoUser := tests.Request{Name: "Invalid JSON Data", Method: "POST", Endpoint: "/application", Data: `{"name": "test1", "strict_compatibility": true}`, Headers: map[string]string{"Content-Type": "application/json"}}
|
||||
_, c, err := reqWoUser.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
assert.Panicsf(func() { TestApplicationHandler.CreateApplication(c) }, "CreateApplication did not panic altough user is not in context")
|
||||
assert.Panicsf(func() { ctx.ApplicationHandler.CreateApplication(c) }, "CreateApplication did not panic although user is not in context")
|
||||
}
|
||||
|
||||
func TestApi_RegisterApplication(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
testCases := make([]tests.Request, 0)
|
||||
testCases = append(testCases, tests.Request{Name: "Invalid Form Data", Method: "POST", Endpoint: "/application", Data: "k=1&v=abc", ShouldStatus: 400})
|
||||
testCases = append(testCases, tests.Request{Name: "Invalid JSON Data", Method: "POST", Endpoint: "/application", Data: `{"name": "test1", "strict_compatibility": "oh yes"}`, Headers: map[string]string{"Content-Type": "application/json"}, ShouldStatus: 400})
|
||||
testCases = append(testCases, tests.Request{Name: "Valid JSON Data", Method: "POST", Endpoint: "/application", Data: `{"name": "test2", "strict_compatibility": true}`, Headers: map[string]string{"Content-Type": "application/json"}, ShouldStatus: 200})
|
||||
|
||||
for _, user := range TestUsers {
|
||||
SuccessAplications[user.ID] = make([]model.Application, 0)
|
||||
for _, user := range ctx.Users {
|
||||
SuccessApplications[user.ID] = make([]model.Application, 0)
|
||||
for _, req := range testCases {
|
||||
var application model.Application
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("user", user)
|
||||
TestApplicationHandler.CreateApplication(c)
|
||||
ctx.ApplicationHandler.CreateApplication(c)
|
||||
|
||||
// Parse body only for successful requests
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
body, err := ioutil.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Can not read request body")
|
||||
body, err := io.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Cannot read request body")
|
||||
err = json.Unmarshal(body, &application)
|
||||
require.NoErrorf(err, "Can not unmarshal request body")
|
||||
require.NoErrorf(err, "Cannot unmarshal request body")
|
||||
|
||||
SuccessAplications[user.ID] = append(SuccessAplications[user.ID], application)
|
||||
SuccessApplications[user.ID] = append(SuccessApplications[user.ID], application)
|
||||
}
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "CreateApplication (Test case: \"%s\") Expected status code %v but received %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
|
@ -137,37 +69,38 @@ func TestApi_RegisterApplication(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_GetApplications(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
var applications []model.Application
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
testCases := make([]tests.Request, 0)
|
||||
testCases = append(testCases, tests.Request{Name: "Valid Request", Method: "GET", Endpoint: "/application", ShouldStatus: 200})
|
||||
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
for _, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("user", user)
|
||||
TestApplicationHandler.GetApplications(c)
|
||||
ctx.ApplicationHandler.GetApplications(c)
|
||||
|
||||
// Parse body only for successful requests
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
body, err := ioutil.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Can not read request body")
|
||||
body, err := io.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Cannot read request body")
|
||||
err = json.Unmarshal(body, &applications)
|
||||
require.NoErrorf(err, "Can not unmarshal request body")
|
||||
require.NoErrorf(err, "Cannot unmarshal request body")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Truef(validateAllApplications(user, applications), "Did not find application created previously")
|
||||
assert.Equalf(len(applications), len(SuccessAplications[user.ID]), "Created %d application(s) but got %d back", len(SuccessAplications[user.ID]), len(applications))
|
||||
assert.Equalf(len(applications), len(SuccessApplications[user.ID]), "Created %d application(s) but got %d back", len(SuccessApplications[user.ID]), len(applications))
|
||||
}
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "GetApplications (Test case: \"%s\") Expected status code %v but received %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
|
@ -176,22 +109,24 @@ func TestApi_GetApplications(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_GetApplicationsWithoutUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
testCase := tests.Request{Name: "Valid Request", Method: "GET", Endpoint: "/application"}
|
||||
|
||||
_, c, err := testCase.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
assert.Panicsf(func() { TestApplicationHandler.GetApplications(c) }, "GetApplications did not panic altough user is not in context")
|
||||
assert.Panicsf(func() { ctx.ApplicationHandler.GetApplications(c) }, "GetApplications did not panic although user is not in context")
|
||||
}
|
||||
|
||||
func TestApi_GetApplicationErrors(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Arbitrary test cases
|
||||
testCases := make(map[uint]tests.Request)
|
||||
|
@ -199,16 +134,16 @@ func TestApi_GetApplicationErrors(t *testing.T) {
|
|||
testCases[5555] = tests.Request{Name: "Requesting unknown application 5555", Method: "GET", Endpoint: "/application/5555", ShouldStatus: 404}
|
||||
testCases[99999999999999999] = tests.Request{Name: "Requesting unknown application 99999999999999999", Method: "GET", Endpoint: "/application/99999999999999999", ShouldStatus: 404}
|
||||
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("user", user)
|
||||
c.Set("id", id)
|
||||
TestApplicationHandler.GetApplication(c)
|
||||
ctx.ApplicationHandler.GetApplication(c)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "GetApplication (Test case: \"%s\") Expected status code %v but have %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
}
|
||||
|
@ -216,32 +151,33 @@ func TestApi_GetApplicationErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_GetApplication(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
var application model.Application
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Previously generated applications
|
||||
for _, user := range TestUsers {
|
||||
for _, app := range SuccessAplications[user.ID] {
|
||||
for _, user := range ctx.Users {
|
||||
for _, app := range SuccessApplications[user.ID] {
|
||||
req := tests.Request{Name: fmt.Sprintf("Requesting application %s (%d)", app.Name, app.ID), Method: "GET", Endpoint: fmt.Sprintf("/application/%d", app.ID), ShouldStatus: 200}
|
||||
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("user", user)
|
||||
c.Set("id", app.ID)
|
||||
TestApplicationHandler.GetApplication(c)
|
||||
ctx.ApplicationHandler.GetApplication(c)
|
||||
|
||||
// Parse body only for successful requests
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
body, err := ioutil.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Can not read request body")
|
||||
body, err := io.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Cannot read request body")
|
||||
err = json.Unmarshal(body, &application)
|
||||
require.NoErrorf(err, "Can not unmarshal request body: %v", err)
|
||||
require.NoErrorf(err, "Cannot unmarshal request body: %v", err)
|
||||
|
||||
assert.Equalf(application.ID, app.ID, "Application ID should be %d but is %d", app.ID, application.ID)
|
||||
assert.Equalf(application.Name, app.Name, "Application Name should be %s but is %s", app.Name, application.Name)
|
||||
|
@ -255,14 +191,15 @@ func TestApi_GetApplication(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_UpdateApplication(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
testCases := make(map[uint]tests.Request)
|
||||
// Previously generated applications
|
||||
for _, app := range SuccessAplications[user.ID] {
|
||||
for _, app := range SuccessApplications[user.ID] {
|
||||
newName := app.Name + "-new_name"
|
||||
updateApp := model.UpdateApplication{
|
||||
Name: &newName,
|
||||
|
@ -282,12 +219,12 @@ func TestApi_UpdateApplication(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("user", user)
|
||||
c.Set("id", id)
|
||||
TestApplicationHandler.UpdateApplication(c)
|
||||
ctx.ApplicationHandler.UpdateApplication(c)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "UpdateApplication (Test case: \"%s\") Expected status code %v but have %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
}
|
||||
|
@ -295,13 +232,14 @@ func TestApi_UpdateApplication(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_DeleteApplication(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
for _, user := range TestUsers {
|
||||
assert := assert.New(t)
|
||||
|
||||
for _, user := range ctx.Users {
|
||||
testCases := make(map[uint]tests.Request)
|
||||
// Previously generated applications
|
||||
for _, app := range SuccessAplications[user.ID] {
|
||||
for _, app := range SuccessApplications[user.ID] {
|
||||
testCases[app.ID] = tests.Request{Name: fmt.Sprintf("Delete application %s (%d)", app.Name, app.ID), Method: "DELETE", Endpoint: fmt.Sprintf("/application/%d", app.ID), ShouldStatus: 200}
|
||||
}
|
||||
// Arbitrary test cases
|
||||
|
@ -310,35 +248,25 @@ func TestApi_DeleteApplication(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("user", user)
|
||||
c.Set("id", id)
|
||||
TestApplicationHandler.DeleteApplication(c)
|
||||
ctx.ApplicationHandler.DeleteApplication(c)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "DeleteApplication (Test case: \"%s\") Expected status code %v but have %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetApplicationHandler creates and returns an application handler
|
||||
func getApplicationHandler(c *configuration.Configuration) (*ApplicationHandler, error) {
|
||||
dispatcher := &mockups.MockDispatcher{}
|
||||
|
||||
return &ApplicationHandler{
|
||||
DB: TestDatabase,
|
||||
DP: dispatcher,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// True if all created applications are in list
|
||||
func validateAllApplications(user *model.User, apps []model.Application) bool {
|
||||
if _, ok := SuccessAplications[user.ID]; !ok {
|
||||
if _, ok := SuccessApplications[user.ID]; !ok {
|
||||
return len(apps) == 0
|
||||
}
|
||||
|
||||
for _, successApp := range SuccessAplications[user.ID] {
|
||||
for _, successApp := range SuccessApplications[user.ID] {
|
||||
foundApp := false
|
||||
for _, app := range apps {
|
||||
if app.ID == successApp.ID {
|
||||
|
@ -354,7 +282,3 @@ func validateAllApplications(user *model.User, apps []model.Application) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func cleanUp() {
|
||||
os.Remove("pushbits-test.db")
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
func getID(ctx *gin.Context) (uint, error) {
|
||||
id, ok := ctx.MustGet("id").(uint)
|
||||
if !ok {
|
||||
err := errors.New("an error occured while retrieving ID from context")
|
||||
err := errors.New("an error occurred while retrieving ID from context")
|
||||
ctx.AbortWithError(http.StatusInternalServerError, err)
|
||||
return 0, err
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func getID(ctx *gin.Context) (uint, error) {
|
|||
func getMessageID(ctx *gin.Context) (string, error) {
|
||||
id, ok := ctx.MustGet("messageid").(string)
|
||||
if !ok {
|
||||
err := errors.New("an error occured while retrieving messageID from context")
|
||||
err := errors.New("an error occurred while retrieving messageID from context")
|
||||
ctx.AbortWithError(http.StatusInternalServerError, err)
|
||||
return "", err
|
||||
}
|
||||
|
@ -41,6 +41,11 @@ func getApplication(ctx *gin.Context, db Database) (*model.Application, error) {
|
|||
if success := SuccessOrAbort(ctx, http.StatusNotFound, err); !success {
|
||||
return nil, err
|
||||
}
|
||||
if application == nil {
|
||||
err := errors.New("application not found")
|
||||
ctx.AbortWithError(http.StatusNotFound, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return application, nil
|
||||
}
|
||||
|
|
|
@ -3,18 +3,20 @@ package api
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
"github.com/pushbits/server/tests/mockups"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestApi_getID(t *testing.T) {
|
||||
GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
testValue := uint(1337)
|
||||
|
||||
testCases := make(map[interface{}]tests.Request)
|
||||
|
@ -31,21 +33,21 @@ func TestApi_getID(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("id", id)
|
||||
idReturned, err := getID(c)
|
||||
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
require.NoErrorf(err, "getId with id %v (%t) returned an error altough it should not: %v", id, id, err)
|
||||
require.NoErrorf(err, "getId with id %v (%t) returned an error although it should not: %v", id, id, err)
|
||||
|
||||
idUint, ok := id.(uint)
|
||||
if ok {
|
||||
assert.Equalf(idReturned, idUint, "getApi id was set to %d but result is %d", idUint, idReturned)
|
||||
}
|
||||
} else {
|
||||
assert.Errorf(err, "getId with id %v (%t) returned no error altough it should", id, id)
|
||||
assert.Errorf(err, "getId with id %v (%t) returned no error although it should", id, id)
|
||||
}
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "getApi id was set to %v (%T) and should result in status code %d but code is %d", id, id, req.ShouldStatus, w.Code)
|
||||
|
@ -53,12 +55,17 @@ func TestApi_getID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_getApplication(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
applications := mockups.GetAllApplications()
|
||||
mockups.AddApplicationsToDb(TestDatabase, applications)
|
||||
|
||||
err := mockups.AddApplicationsToDb(ctx.Database, applications)
|
||||
if err != nil {
|
||||
log.L.Fatalln("Cannot add mock applications to database: ", err)
|
||||
}
|
||||
|
||||
// No testing of invalid ids as that is tested in TestApi_getID already
|
||||
testCases := make(map[uint]tests.Request)
|
||||
|
@ -69,30 +76,32 @@ func TestApi_getApplication(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("id", id)
|
||||
app, err := getApplication(c, TestDatabase)
|
||||
app, err := getApplication(c, ctx.Database)
|
||||
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
require.NoErrorf(err, "getApplication with id %v (%t) returned an error altough it should not: %v", id, id, err)
|
||||
assert.Equalf(app.ID, id, "getApplication id was set to %d but resulting app id is %d", id, app.ID)
|
||||
require.NoErrorf(err, "getApplication with id %v returned an unexpected error: %v", id, err)
|
||||
require.NotNilf(app, "Expected a valid app for id %v, but got nil", id)
|
||||
assert.Equalf(app.ID, id, "Expected app ID %d, but got %d", id, app.ID)
|
||||
} else {
|
||||
assert.Errorf(err, "getApplication with id %v (%t) returned no error altough it should", id, id)
|
||||
require.Errorf(err, "Expected an error for id %v, but got none", id)
|
||||
assert.Nilf(app, "Expected app to be nil for id %v, but got %+v", id, app)
|
||||
}
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "getApplication id was set to %v (%T) and should result in status code %d but code is %d", id, id, req.ShouldStatus, w.Code)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "Expected status code %d for id %v, but got %d", req.ShouldStatus, id, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApi_getUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
_, err := mockups.AddUsersToDb(TestDatabase, TestUsers)
|
||||
_, err := mockups.AddUsersToDb(ctx.Database, ctx.Users)
|
||||
assert.NoErrorf(err, "Adding users to database failed: %v", err)
|
||||
|
||||
// No testing of invalid ids as that is tested in TestApi_getID already
|
||||
|
@ -104,20 +113,22 @@ func TestApi_getUser(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("id", id)
|
||||
user, err := getUser(c, TestDatabase)
|
||||
user, err := getUser(c, ctx.Database)
|
||||
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
require.NoErrorf(err, "getUser with id %v (%t) returned an error altough it should not: %v", id, id, err)
|
||||
assert.Equalf(user.ID, id, "getUser id was set to %d but resulting app id is %d", id, user.ID)
|
||||
|
||||
require.NoErrorf(err, "getUser with id %v returned an unexpected error: %v", id, err)
|
||||
require.NotNilf(user, "Expected a valid user for id %v, but got nil", id)
|
||||
assert.Equalf(user.ID, id, "Expected user ID %d, but got %d", id, user.ID)
|
||||
} else {
|
||||
assert.Errorf(err, "getUser with id %v (%t) returned no error altough it should", id, id)
|
||||
require.Errorf(err, "Expected an error for id %v, but got none", id)
|
||||
assert.Nilf(user, "Expected user to be nil for id %v, but got %+v", id, user)
|
||||
}
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "getUser id was set to %v (%T) and should result in status code %d but code is %d", id, id, req.ShouldStatus, w.Code)
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "Expected status code %d for id %v, but got %d", req.ShouldStatus, id, w.Code)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,17 @@ package api
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pushbits/server/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pushbits/server/tests"
|
||||
)
|
||||
|
||||
func TestApi_Health(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
handler := HealthHandler{
|
||||
DB: TestDatabase,
|
||||
DB: ctx.Database,
|
||||
}
|
||||
|
||||
testCases := make([]tests.Request, 0)
|
||||
|
@ -19,7 +22,7 @@ func TestApi_Health(t *testing.T) {
|
|||
for _, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
handler.Health(c)
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Package api provides definitions and functionality related to the API.
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
)
|
||||
|
||||
|
@ -26,9 +28,9 @@ type Database interface {
|
|||
|
||||
// The Dispatcher interface for relaying notifications.
|
||||
type Dispatcher interface {
|
||||
RegisterApplication(id uint, name, token, user string) (string, error)
|
||||
RegisterApplication(id uint, name, user string) (string, error)
|
||||
DeregisterApplication(a *model.Application, u *model.User) error
|
||||
UpdateApplication(a *model.Application) error
|
||||
UpdateApplication(a *model.Application, behavior *configuration.RepairBehavior) error
|
||||
}
|
||||
|
||||
// The CredentialsManager interface for updating credentials.
|
||||
|
|
|
@ -4,18 +4,20 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type idInURI struct {
|
||||
// IDInURI is used to retrieve an ID from a context.
|
||||
type IDInURI struct {
|
||||
ID uint `uri:"id" binding:"required"`
|
||||
}
|
||||
|
||||
type messageIdInURI struct {
|
||||
// messageIDInURI is used to retrieve an message ID from a context.
|
||||
type messageIDInURI struct {
|
||||
MessageID string `uri:"messageid" binding:"required"`
|
||||
}
|
||||
|
||||
// RequireIDInURI returns a Gin middleware which requires an ID to be supplied in the URI of the request.
|
||||
func RequireIDInURI() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
var requestModel idInURI
|
||||
var requestModel IDInURI
|
||||
|
||||
if err := ctx.BindUri(&requestModel); err != nil {
|
||||
return
|
||||
|
@ -28,7 +30,7 @@ func RequireIDInURI() gin.HandlerFunc {
|
|||
// RequireMessageIDInURI returns a Gin middleware which requires an messageID to be supplied in the URI of the request.
|
||||
func RequireMessageIDInURI() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
var requestModel messageIdInURI
|
||||
var requestModel messageIDInURI
|
||||
|
||||
if err := ctx.BindUri(&requestModel); err != nil {
|
||||
return
|
||||
|
|
|
@ -44,6 +44,10 @@ type NotificationHandler struct {
|
|||
// @Router /message [post]
|
||||
func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
|
||||
application := authentication.GetApplication(ctx)
|
||||
if application == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.L.Printf("Sending notification for application %s.", application.Name)
|
||||
|
||||
var notification model.Notification
|
||||
|
@ -59,7 +63,7 @@ func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
notification.ID = messageID
|
||||
notification.UrlEncodedID = url.QueryEscape(messageID)
|
||||
notification.URLEncodedID = url.QueryEscape(messageID)
|
||||
|
||||
ctx.JSON(http.StatusOK, ¬ification)
|
||||
}
|
||||
|
@ -78,6 +82,10 @@ func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
|
|||
// @Router /message/{message_id} [DELETE]
|
||||
func (h *NotificationHandler) DeleteNotification(ctx *gin.Context) {
|
||||
application := authentication.GetApplication(ctx)
|
||||
if application == nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.L.Printf("Deleting notification for application %s.", application.Name)
|
||||
|
||||
id, err := getMessageID(ctx)
|
||||
|
|
|
@ -2,20 +2,21 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
)
|
||||
|
||||
func TestApi_CreateNotification(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
testApplication := model.Application{
|
||||
ID: 1,
|
||||
|
@ -36,15 +37,15 @@ func TestApi_CreateNotification(t *testing.T) {
|
|||
var notification model.Notification
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("app", &testApplication)
|
||||
TestNotificationHandler.CreateNotification(c)
|
||||
ctx.NotificationHandler.CreateNotification(c)
|
||||
|
||||
// Parse body only for successful requests
|
||||
if req.ShouldStatus >= 200 && req.ShouldStatus < 300 {
|
||||
body, err := ioutil.ReadAll(w.Body)
|
||||
body, err := io.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Can not read request body")
|
||||
err = json.Unmarshal(body, ¬ification)
|
||||
require.NoErrorf(err, "Can not unmarshal request body")
|
||||
|
@ -64,8 +65,9 @@ func TestApi_CreateNotification(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_DeleteNotification(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
testApplication := model.Application{
|
||||
ID: 1,
|
||||
|
@ -83,12 +85,12 @@ func TestApi_DeleteNotification(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("app", &testApplication)
|
||||
c.Set("messageid", id)
|
||||
TestNotificationHandler.DeleteNotification(c)
|
||||
ctx.NotificationHandler.DeleteNotification(c)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "(Test case: \"%s\") Expected status code %v but have %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,10 @@ func (h *UserHandler) requireMultipleAdmins(ctx *gin.Context) error {
|
|||
}
|
||||
|
||||
func (h *UserHandler) deleteApplications(ctx *gin.Context, u *model.User) error {
|
||||
if ctx == nil || u == nil {
|
||||
return errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
applications, err := h.DB.GetApplications(u)
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
return err
|
||||
|
@ -55,6 +59,10 @@ func (h *UserHandler) deleteApplications(ctx *gin.Context, u *model.User) error
|
|||
}
|
||||
|
||||
func (h *UserHandler) updateChannels(ctx *gin.Context, u *model.User, matrixID string) error {
|
||||
if ctx == nil || u == nil {
|
||||
return errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
applications, err := h.DB.GetApplications(u)
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
return err
|
||||
|
@ -83,15 +91,7 @@ func (h *UserHandler) updateChannels(ctx *gin.Context, u *model.User, matrixID s
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) updateUser(ctx *gin.Context, u *model.User, updateUser model.UpdateUser) error {
|
||||
if updateUser.MatrixID != nil && u.MatrixID != *updateUser.MatrixID {
|
||||
if err := h.updateChannels(ctx, u, *updateUser.MatrixID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.L.Printf("Updating user %s.", u.Name)
|
||||
|
||||
func (h *UserHandler) updateUserFields(ctx *gin.Context, u *model.User, updateUser model.UpdateUser) error {
|
||||
if updateUser.Name != nil {
|
||||
u.Name = *updateUser.Name
|
||||
}
|
||||
|
@ -100,7 +100,6 @@ func (h *UserHandler) updateUser(ctx *gin.Context, u *model.User, updateUser mod
|
|||
if success := SuccessOrAbort(ctx, http.StatusBadRequest, err); !success {
|
||||
return err
|
||||
}
|
||||
|
||||
u.PasswordHash = hash
|
||||
}
|
||||
if updateUser.MatrixID != nil {
|
||||
|
@ -109,6 +108,25 @@ func (h *UserHandler) updateUser(ctx *gin.Context, u *model.User, updateUser mod
|
|||
if updateUser.IsAdmin != nil {
|
||||
u.IsAdmin = *updateUser.IsAdmin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) updateUser(ctx *gin.Context, u *model.User, updateUser model.UpdateUser) error {
|
||||
if u == nil {
|
||||
return errors.New("nil parameters provided")
|
||||
}
|
||||
|
||||
if updateUser.MatrixID != nil && u.MatrixID != *updateUser.MatrixID {
|
||||
if err := h.updateChannels(ctx, u, *updateUser.MatrixID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.L.Printf("Updating user %s.", u.Name)
|
||||
|
||||
if err := h.updateUserFields(ctx, u, updateUser); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := h.DB.UpdateUser(u)
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
|
@ -149,10 +167,12 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) {
|
|||
log.L.Printf("Creating user %s.", createUser.Name)
|
||||
|
||||
user, err := h.DB.CreateUser(createUser)
|
||||
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, user.IntoExternalUser())
|
||||
}
|
||||
|
@ -170,6 +190,10 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) {
|
|||
// @Security BasicAuth
|
||||
// @Router /user [get]
|
||||
func (h *UserHandler) GetUsers(ctx *gin.Context) {
|
||||
if ctx == nil {
|
||||
return
|
||||
}
|
||||
|
||||
users, err := h.DB.GetUsers()
|
||||
if success := SuccessOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
||||
return
|
||||
|
@ -199,7 +223,7 @@ func (h *UserHandler) GetUsers(ctx *gin.Context) {
|
|||
// @Router /user/{id} [get]
|
||||
func (h *UserHandler) GetUser(ctx *gin.Context) {
|
||||
user, err := getUser(ctx, h.DB)
|
||||
if err != nil {
|
||||
if err != nil || user == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -221,7 +245,7 @@ func (h *UserHandler) GetUser(ctx *gin.Context) {
|
|||
// @Router /user/{id} [delete]
|
||||
func (h *UserHandler) DeleteUser(ctx *gin.Context) {
|
||||
user, err := getUser(ctx, h.DB)
|
||||
if err != nil {
|
||||
if err != nil || user == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -265,7 +289,7 @@ func (h *UserHandler) DeleteUser(ctx *gin.Context) {
|
|||
// @Router /user/{id} [put]
|
||||
func (h *UserHandler) UpdateUser(ctx *gin.Context) {
|
||||
user, err := getUser(ctx, h.DB)
|
||||
if err != nil {
|
||||
if err != nil || user == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -275,6 +299,9 @@ func (h *UserHandler) UpdateUser(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
requestingUser := authentication.GetUser(ctx)
|
||||
if requestingUser == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Last privileged user must not be taken privileges. Assumes that the current user has privileges.
|
||||
if user.ID == requestingUser.ID && updateUser.IsAdmin != nil && !(*updateUser.IsAdmin) {
|
||||
|
|
|
@ -2,28 +2,31 @@ package api
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pushbits/server/internal/model"
|
||||
"github.com/pushbits/server/tests"
|
||||
)
|
||||
|
||||
func TestApi_CreateUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
testCases := make([]tests.Request, 0)
|
||||
|
||||
// Add all test users
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
createUser := &model.CreateUser{}
|
||||
createUser.ExternalUser.Name = user.Name
|
||||
createUser.ExternalUser.MatrixID = "@" + user.Name + ":matrix.org"
|
||||
createUser.ExternalUser.IsAdmin = user.IsAdmin
|
||||
createUser.UserCredentials.Password = TestConfig.Admin.Password
|
||||
createUser.UserCredentials.Password = ctx.Config.Admin.Password
|
||||
|
||||
testCase := tests.Request{
|
||||
Name: "Already existing user " + user.Name,
|
||||
|
@ -45,16 +48,18 @@ func TestApi_CreateUser(t *testing.T) {
|
|||
for _, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
TestUserHandler.CreateUser(c)
|
||||
ctx.UserHandler.CreateUser(c)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "(Test case: \"%s\") Expected status code %v but have %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApi_GetUsers(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
|
@ -65,21 +70,21 @@ func TestApi_GetUsers(t *testing.T) {
|
|||
|
||||
w, c, err := request.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf((err.Error()))
|
||||
t.Fatalf("error getting request: %v", err)
|
||||
}
|
||||
|
||||
TestUserHandler.GetUsers(c)
|
||||
ctx.UserHandler.GetUsers(c)
|
||||
assert.Equalf(w.Code, 200, "Response code should be 200 but is %d", w.Code)
|
||||
|
||||
// Get users from body
|
||||
users := make([]model.ExternalUser, 0)
|
||||
usersRaw, err := ioutil.ReadAll(w.Body)
|
||||
usersRaw, err := io.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "Failed to parse response body")
|
||||
err = json.Unmarshal(usersRaw, &users)
|
||||
require.NoErrorf(err, "Can not unmarshal users")
|
||||
|
||||
// Check existence of all known users
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
found := false
|
||||
for _, userExt := range users {
|
||||
if user.ID == userExt.ID && user.Name == userExt.Name {
|
||||
|
@ -92,13 +97,15 @@ func TestApi_GetUsers(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_UpdateUser(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
admin := getAdmin()
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
admin := getAdmin(ctx)
|
||||
testCases := make(map[uint]tests.Request)
|
||||
|
||||
// Add all test users
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
updateUser := &model.UpdateUser{}
|
||||
user.Name += "+1"
|
||||
user.IsAdmin = !user.IsAdmin
|
||||
|
@ -120,12 +127,12 @@ func TestApi_UpdateUser(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
w, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("id", id)
|
||||
c.Set("user", admin)
|
||||
TestUserHandler.UpdateUser(c)
|
||||
ctx.UserHandler.UpdateUser(c)
|
||||
|
||||
assert.Equalf(w.Code, req.ShouldStatus, "(Test case: \"%s\") Expected status code %v but have %v.", req.Name, req.ShouldStatus, w.Code)
|
||||
}
|
||||
|
@ -134,15 +141,17 @@ func TestApi_UpdateUser(t *testing.T) {
|
|||
for id, req := range testCases {
|
||||
_, c, err := req.GetRequest()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
c.Set("id", id)
|
||||
assert.Panicsf(func() { TestUserHandler.UpdateUser(c) }, "User not set should panic but did not")
|
||||
assert.Panicsf(func() { ctx.UserHandler.UpdateUser(c) }, "User not set should panic but did not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestApi_GetUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
|
@ -151,7 +160,7 @@ func TestApi_GetUser(t *testing.T) {
|
|||
testCases[uint(9999999)] = tests.Request{Name: "Unknown id", Method: "GET", Endpoint: "/user/99999999", ShouldStatus: 404}
|
||||
|
||||
// Check if we can get all existing users
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
testCases[user.ID] = tests.Request{
|
||||
Name: "Valid user " + user.Name,
|
||||
Method: "GET",
|
||||
|
@ -166,14 +175,14 @@ func TestApi_GetUser(t *testing.T) {
|
|||
require.NoErrorf(err, "(Test case %s) Could not make request", testCase.Name)
|
||||
|
||||
c.Set("id", id)
|
||||
TestUserHandler.GetUser(c)
|
||||
ctx.UserHandler.GetUser(c)
|
||||
|
||||
assert.Equalf(testCase.ShouldStatus, w.Code, "(Test case %s) Expected status code %d but have %d", testCase.Name, testCase.ShouldStatus, w.Code)
|
||||
|
||||
// Check content for successful requests
|
||||
if testCase.ShouldReturn == 200 {
|
||||
user := &model.ExternalUser{}
|
||||
userBytes, err := ioutil.ReadAll(w.Body)
|
||||
userBytes, err := io.ReadAll(w.Body)
|
||||
require.NoErrorf(err, "(Test case %s) Can not read body", testCase.Name)
|
||||
err = json.Unmarshal(userBytes, user)
|
||||
require.NoErrorf(err, "(Test case %s) Can not unmarshal body", testCase.Name)
|
||||
|
@ -191,13 +200,16 @@ func TestApi_GetUser(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_DeleteUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
testCases := make(map[interface{}]tests.Request)
|
||||
testCases["abcde"] = tests.Request{Name: "Invalid user - string", Method: "DELETE", Endpoint: "/user/abcde", ShouldStatus: 500}
|
||||
testCases[uint(999999)] = tests.Request{Name: "Unknown user", Method: "DELETE", Endpoint: "/user/999999", ShouldStatus: 404}
|
||||
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
shouldStatus := 200
|
||||
testCases[user.ID] = tests.Request{
|
||||
Name: "Valid user " + user.Name,
|
||||
|
@ -212,14 +224,14 @@ func TestApi_DeleteUser(t *testing.T) {
|
|||
require.NoErrorf(err, "(Test case %s) Could not make request", testCase.Name)
|
||||
|
||||
c.Set("id", id)
|
||||
TestUserHandler.DeleteUser(c)
|
||||
ctx.UserHandler.DeleteUser(c)
|
||||
|
||||
assert.Equalf(testCase.ShouldStatus, w.Code, "(Test case %s) Expected status code %d but have %d", testCase.Name, testCase.ShouldStatus, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func getAdmin() *model.User {
|
||||
for _, user := range TestUsers {
|
||||
func getAdmin(ctx *TestContext) *model.User {
|
||||
for _, user := range ctx.Users {
|
||||
if user.IsAdmin {
|
||||
return user
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SuccessOrAbort is a convenience function to write a HTTP status code based on a given error.
|
||||
func SuccessOrAbort(ctx *gin.Context, code int, err error) bool {
|
||||
if err != nil {
|
||||
// If we know the error force error code
|
||||
switch err {
|
||||
case pberrors.ErrorMessageNotFound:
|
||||
case pberrors.ErrMessageNotFound:
|
||||
ctx.AbortWithError(http.StatusNotFound, err)
|
||||
default:
|
||||
ctx.AbortWithError(code, err)
|
||||
|
@ -24,13 +25,13 @@ func SuccessOrAbort(ctx *gin.Context, code int, err error) bool {
|
|||
return err == nil
|
||||
}
|
||||
|
||||
func isCurrentUser(ctx *gin.Context, ID uint) bool {
|
||||
func isCurrentUser(ctx *gin.Context, id uint) bool {
|
||||
user := authentication.GetUser(ctx)
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if user.ID != ID {
|
||||
if user.ID != id {
|
||||
ctx.AbortWithError(http.StatusForbidden, errors.New("only owner can delete application"))
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -5,12 +5,15 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pushbits/server/tests"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pushbits/server/tests"
|
||||
)
|
||||
|
||||
func TestApi_SuccessOrAbort(t *testing.T) {
|
||||
GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
|
@ -37,10 +40,12 @@ func TestApi_SuccessOrAbort(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestApi_IsCurrentUser(t *testing.T) {
|
||||
ctx := GetTestContext(t)
|
||||
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
for _, user := range TestUsers {
|
||||
for _, user := range ctx.Users {
|
||||
testCases := make(map[uint]tests.Request)
|
||||
|
||||
testCases[user.ID] = tests.Request{Name: fmt.Sprintf("User %s - success", user.Name), Endpoint: "/", ShouldStatus: 200}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package assert provides convenience function to make assertions at runtime.
|
||||
package assert
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package authentication provides definitions and functionality related to user authentication.
|
||||
package authentication
|
||||
|
||||
import (
|
||||
|
@ -33,9 +34,9 @@ func (a *Authenticator) userFromBasicAuth(ctx *gin.Context) (*model.User, error)
|
|||
return nil, err
|
||||
} else if user != nil && credentials.ComparePassword(user.PasswordHash, []byte(password)) {
|
||||
return user, nil
|
||||
} else {
|
||||
return nil, errors.New("credentials were invalid")
|
||||
}
|
||||
|
||||
return nil, errors.New("credentials were invalid")
|
||||
}
|
||||
|
||||
return nil, errors.New("no credentials were supplied")
|
||||
|
@ -60,7 +61,7 @@ func (a *Authenticator) requireUserProperty(has hasUserProperty) gin.HandlerFunc
|
|||
|
||||
// RequireUser returns a Gin middleware which requires valid user credentials to be supplied with the request.
|
||||
func (a *Authenticator) RequireUser() gin.HandlerFunc {
|
||||
return a.requireUserProperty(func(user *model.User) bool {
|
||||
return a.requireUserProperty(func(_ *model.User) bool {
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
func GetApplication(ctx *gin.Context) *model.Application {
|
||||
app, ok := ctx.MustGet("app").(*model.Application)
|
||||
if app == nil || !ok {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, errors.New("an error occured while retrieving application from context"))
|
||||
ctx.AbortWithError(http.StatusInternalServerError, errors.New("an error occurred while retrieving application from context"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ func GetApplication(ctx *gin.Context) *model.Application {
|
|||
func GetUser(ctx *gin.Context) *model.User {
|
||||
user, ok := ctx.MustGet("user").(*model.User)
|
||||
if user == nil || !ok {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, errors.New("an error occured while retrieving user from context"))
|
||||
ctx.AbortWithError(http.StatusInternalServerError, errors.New("an error occurred while retrieving user from context"))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package credentials provides definitions and functionality related to credential management.
|
||||
package credentials
|
||||
|
||||
import (
|
||||
|
|
|
@ -3,7 +3,7 @@ package credentials
|
|||
import (
|
||||
"crypto/sha1" //#nosec G505 -- False positive, see the use below.
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -22,7 +22,6 @@ func IsPasswordPwned(password string) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// nosemgrep: tests.semgrep-rules.go.lang.security.audit.crypto.insecure-module-used, tests.semgrep-rules.go.lang.security.audit.crypto.use-of-sha1
|
||||
hash := sha1.Sum([]byte(password)) //#nosec G401 -- False positive, only the first 5 bytes are transmitted.
|
||||
hashStr := fmt.Sprintf("%X", hash)
|
||||
lookup := hashStr[0:5]
|
||||
|
@ -34,17 +33,24 @@ func IsPasswordPwned(password string) (bool, error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if resp == nil {
|
||||
return false, fmt.Errorf("received nil response from http request")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.L.Fatalf("Request failed with HTTP %s.", resp.Status)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
bodyText, err := ioutil.ReadAll(resp.Body)
|
||||
bodyText, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.L.Fatal(err)
|
||||
}
|
||||
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
log.L.Warnf("Failed to close file: %s.", err)
|
||||
}
|
||||
|
||||
bodyStr := string(bodyText)
|
||||
lines := strings.Split(bodyStr, "\n")
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ var (
|
|||
)
|
||||
|
||||
func randIntn(n int) int {
|
||||
max := big.NewInt(int64(n))
|
||||
maxValue := big.NewInt(int64(n))
|
||||
|
||||
res, err := rand.Int(rand.Reader, max)
|
||||
res, err := rand.Int(rand.Reader, maxValue)
|
||||
if err != nil {
|
||||
panic("random source is not available")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ const (
|
|||
minRandomChars = 14
|
||||
)
|
||||
|
||||
func isGoodToken(assert *assert.Assertions, require *require.Assertions, token string, compat bool) {
|
||||
func isGoodToken(assert *assert.Assertions, _ *require.Assertions, token string, compat bool) {
|
||||
tokenLength := len(token)
|
||||
|
||||
if compat {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
// Package configuration provides definitions and functionality related to the configuration.
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/configor"
|
||||
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/pberrors"
|
||||
)
|
||||
|
||||
// testMode indicates if the package is run in test mode
|
||||
|
@ -39,6 +43,12 @@ type Alertmanager struct {
|
|||
AnnotationMessage string `default:"message"`
|
||||
}
|
||||
|
||||
// RepairBehavior holds information on how repair applications.
|
||||
type RepairBehavior struct {
|
||||
ResetRoomName bool `default:"true"`
|
||||
ResetRoomTopic bool `default:"true"`
|
||||
}
|
||||
|
||||
// Configuration holds values that can be configured by the user.
|
||||
type Configuration struct {
|
||||
Debug bool `default:"false"`
|
||||
|
@ -46,6 +56,8 @@ type Configuration struct {
|
|||
ListenAddress string `default:""`
|
||||
Port int `default:"8080"`
|
||||
TrustedProxies []string `default:"[]"`
|
||||
CertFile string `default:""`
|
||||
KeyFile string `default:""`
|
||||
}
|
||||
Database struct {
|
||||
Dialect string `default:"sqlite3"`
|
||||
|
@ -60,9 +72,10 @@ type Configuration struct {
|
|||
Security struct {
|
||||
CheckHIBP bool `default:"false"`
|
||||
}
|
||||
Crypto CryptoConfig
|
||||
Formatting Formatting
|
||||
Alertmanager Alertmanager
|
||||
Crypto CryptoConfig
|
||||
Formatting Formatting
|
||||
Alertmanager Alertmanager
|
||||
RepairBehavior RepairBehavior
|
||||
}
|
||||
|
||||
func configFiles() []string {
|
||||
|
@ -72,6 +85,21 @@ func configFiles() []string {
|
|||
return []string{"config.yml"}
|
||||
}
|
||||
|
||||
func validateHTTPConfiguration(c *Configuration) error {
|
||||
certAndKeyEmpty := (c.HTTP.CertFile == "" && c.HTTP.KeyFile == "")
|
||||
certAndKeyPopulated := (c.HTTP.CertFile != "" && c.HTTP.KeyFile != "")
|
||||
|
||||
if !certAndKeyEmpty && !certAndKeyPopulated {
|
||||
return pberrors.ErrConfigTLSFilesInconsistent
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateConfiguration(c *Configuration) error {
|
||||
return validateHTTPConfiguration(c)
|
||||
}
|
||||
|
||||
// Get returns the configuration extracted from env variables or config file.
|
||||
func Get() *Configuration {
|
||||
config := &Configuration{}
|
||||
|
@ -85,5 +113,9 @@ func Get() *Configuration {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
if err := validateConfiguration(config); err != nil {
|
||||
log.L.Fatal(err)
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package configuration
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -10,6 +9,9 @@ import (
|
|||
"github.com/jinzhu/configor"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/pberrors"
|
||||
)
|
||||
|
||||
type Pair struct {
|
||||
|
@ -27,8 +29,7 @@ func TestMain(m *testing.M) {
|
|||
func TestConfiguration_GetMinimal(t *testing.T) {
|
||||
err := writeMinimalConfig()
|
||||
if err != nil {
|
||||
fmt.Println("Could not write minimal config: ", err)
|
||||
os.Exit(1)
|
||||
log.L.Fatalln("Cannot write minimal config file: ", err)
|
||||
}
|
||||
|
||||
validateConfig(t)
|
||||
|
@ -39,8 +40,7 @@ func TestConfiguration_GetValid(t *testing.T) {
|
|||
|
||||
err := writeValidConfig()
|
||||
if err != nil {
|
||||
fmt.Println("Could not write valid config: ", err)
|
||||
os.Exit(1)
|
||||
log.L.Fatalln("Cannot write valid config file: ", err)
|
||||
}
|
||||
|
||||
validateConfig(t)
|
||||
|
@ -64,17 +64,17 @@ func TestConfiguration_GetEmpty(t *testing.T) {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
assert.Panicsf(t, func() { Get() }, "Get() did not panic altough config is empty")
|
||||
assert.Panicsf(t, func() { Get() }, "Get() did not panic although config is empty")
|
||||
}
|
||||
|
||||
func TestConfiguration_GetInvalid(t *testing.T) {
|
||||
err := writeInvalidConfig()
|
||||
if err != nil {
|
||||
fmt.Println("Could not write empty config: ", err)
|
||||
fmt.Println("Could not write invalid config: ", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
assert.Panicsf(t, func() { Get() }, "Get() did not panic altough config is empty")
|
||||
assert.Panicsf(t, func() { Get() }, "Get() did not panic although config is empty")
|
||||
}
|
||||
|
||||
func TestConfiguaration_ConfigFiles(t *testing.T) {
|
||||
|
@ -136,6 +136,7 @@ type InvalidConfiguration struct {
|
|||
// Writes a minimal config to config.yml
|
||||
func writeMinimalConfig() error {
|
||||
cleanUp()
|
||||
|
||||
config := MinimalConfiguration{}
|
||||
config.Admin.MatrixID = "000000"
|
||||
config.Matrix.Username = "default-username"
|
||||
|
@ -146,17 +147,26 @@ func writeMinimalConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile("config_unittest.yml", configString, 0o644)
|
||||
err = os.WriteFile("config_unittest.yml", configString, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Writes a config with default values to config.yml
|
||||
func writeValidConfig() error {
|
||||
cleanUp()
|
||||
|
||||
err := writeMinimalConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load minimal config to get default values
|
||||
writeMinimalConfig()
|
||||
config := &Configuration{}
|
||||
err := configor.New(&configor.Config{
|
||||
err = configor.New(&configor.Config{
|
||||
Environment: "production",
|
||||
ENVPrefix: "PUSHBITS",
|
||||
ErrorOnUnmatchedKeys: true,
|
||||
|
@ -174,18 +184,30 @@ func writeValidConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile("config_unittest.yml", configString, 0o644)
|
||||
err = os.WriteFile("config_unittest.yml", configString, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Writes a config that is empty
|
||||
func writeEmptyConfig() error {
|
||||
cleanUp()
|
||||
return ioutil.WriteFile("config_unittest.yml", []byte(""), 0o644)
|
||||
|
||||
err := os.WriteFile("config_unittest.yml", []byte(""), 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Writes a config with invalid entries
|
||||
func writeInvalidConfig() error {
|
||||
cleanUp()
|
||||
|
||||
config := InvalidConfiguration{}
|
||||
config.Debug = 1337
|
||||
config.HTTP.ListenAddress = true
|
||||
|
@ -197,9 +219,32 @@ func writeInvalidConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile("config_unittest.yml", configString, 0o644)
|
||||
err = os.WriteFile("config_unittest.yml", configString, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanUp() error {
|
||||
return os.Remove("config_unittest.yml")
|
||||
func cleanUp() {
|
||||
err := os.Remove("config_unittest.yml")
|
||||
if err != nil {
|
||||
log.L.Warnln("Cannot remove config file: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigurationValidation_ConfigTLSFilesInconsistent(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
c := Configuration{}
|
||||
c.Admin.MatrixID = "000000"
|
||||
c.Matrix.Username = "default-username"
|
||||
c.Matrix.Password = "default-password"
|
||||
c.HTTP.CertFile = "populated"
|
||||
c.HTTP.KeyFile = ""
|
||||
|
||||
is := validateConfiguration(&c)
|
||||
should := pberrors.ErrConfigTLSFilesInconsistent
|
||||
assert.Equal(is, should, "validateConfiguration() should return ConfigTLSFilesInconsistent")
|
||||
}
|
||||
|
|
|
@ -25,16 +25,16 @@ func (d *Database) UpdateApplication(application *model.Application) error {
|
|||
}
|
||||
|
||||
// GetApplicationByID returns the application with the given ID or nil.
|
||||
func (d *Database) GetApplicationByID(ID uint) (*model.Application, error) {
|
||||
func (d *Database) GetApplicationByID(id uint) (*model.Application, error) {
|
||||
var application model.Application
|
||||
|
||||
err := d.gormdb.First(&application, ID).Error
|
||||
err := d.gormdb.First(&application, id).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assert.Assert(application.ID == ID)
|
||||
assert.Assert(application.ID == id)
|
||||
|
||||
return &application, err
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package database provides definitions and functionality related to the database.
|
||||
package database
|
||||
|
||||
import (
|
||||
|
@ -8,10 +9,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pushbits/server/internal/authentication/credentials"
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
@ -27,7 +30,6 @@ func createFileDir(file string) {
|
|||
dir := filepath.Dir(file)
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
// nosemgrep: tests.semgrep-rules.go.lang.correctness.permissions.incorrect-default-permission
|
||||
if err := os.MkdirAll(dir, 0o750); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -50,6 +52,8 @@ func Create(cm *credentials.Manager, dialect, connection string) (*Database, err
|
|||
db, err = gorm.Open(sqlite.Open(connection), &gorm.Config{})
|
||||
case "mysql":
|
||||
db, err = gorm.Open(mysql.Open(connection), &gorm.Config{})
|
||||
case "postgres":
|
||||
db, err = gorm.Open(postgres.Open(connection), &gorm.Config{})
|
||||
default:
|
||||
message := "Database dialect is not supported"
|
||||
return nil, errors.New(message)
|
||||
|
@ -111,7 +115,7 @@ func (d *Database) Populate(name, password, matrixID string) error {
|
|||
}
|
||||
|
||||
// RepairChannels resets channels that have been modified by a user.
|
||||
func (d *Database) RepairChannels(dp Dispatcher) error {
|
||||
func (d *Database) RepairChannels(dp Dispatcher, behavior *configuration.RepairBehavior) error {
|
||||
log.L.Print("Repairing application channels.")
|
||||
|
||||
users, err := d.GetUsers()
|
||||
|
@ -130,7 +134,7 @@ func (d *Database) RepairChannels(dp Dispatcher) error {
|
|||
for _, application := range applications {
|
||||
application := application // See https://stackoverflow.com/a/68247837
|
||||
|
||||
if err := dp.UpdateApplication(&application); err != nil {
|
||||
if err := dp.UpdateApplication(&application, behavior); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
)
|
||||
|
||||
// The Dispatcher interface for constructing and destructing channels.
|
||||
type Dispatcher interface {
|
||||
DeregisterApplication(a *model.Application, u *model.User) error
|
||||
UpdateApplication(a *model.Application) error
|
||||
UpdateApplication(a *model.Application, behavior *configuration.RepairBehavior) error
|
||||
IsOrphan(a *model.Application, u *model.User) (bool, error)
|
||||
RepairApplication(a *model.Application, u *model.User) error
|
||||
}
|
||||
|
|
|
@ -34,16 +34,16 @@ func (d *Database) UpdateUser(user *model.User) error {
|
|||
}
|
||||
|
||||
// GetUserByID returns the user with the given ID or nil.
|
||||
func (d *Database) GetUserByID(ID uint) (*model.User, error) {
|
||||
func (d *Database) GetUserByID(id uint) (*model.User, error) {
|
||||
var user model.User
|
||||
|
||||
err := d.gormdb.First(&user, ID).Error
|
||||
err := d.gormdb.First(&user, id).Error
|
||||
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assert.Assert(user.ID == ID)
|
||||
assert.Assert(user.ID == id)
|
||||
|
||||
return &user, err
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package dispatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/log"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
|
||||
|
@ -16,10 +18,10 @@ func buildRoomTopic(id uint) string {
|
|||
}
|
||||
|
||||
// RegisterApplication creates a channel for an application.
|
||||
func (d *Dispatcher) RegisterApplication(id uint, name, token, user string) (string, error) {
|
||||
func (d *Dispatcher) RegisterApplication(id uint, name, user string) (string, error) {
|
||||
log.L.Printf("Registering application %s, notifications will be relayed to user %s.\n", name, user)
|
||||
|
||||
resp, err := d.mautrixClient.CreateRoom(&mautrix.ReqCreateRoom{
|
||||
resp, err := d.mautrixClient.CreateRoom(context.Background(), &mautrix.ReqCreateRoom{
|
||||
Visibility: "private",
|
||||
Invite: []mId.UserID{mId.UserID(user)},
|
||||
IsDirect: true,
|
||||
|
@ -43,7 +45,7 @@ func (d *Dispatcher) DeregisterApplication(a *model.Application, u *model.User)
|
|||
|
||||
// The user might have left the channel, but we can still try to remove them.
|
||||
|
||||
if _, err := d.mautrixClient.KickUser(mId.RoomID(a.MatrixID), &mautrix.ReqKickUser{
|
||||
if _, err := d.mautrixClient.KickUser(context.Background(), mId.RoomID(a.MatrixID), &mautrix.ReqKickUser{
|
||||
Reason: "This application was deleted",
|
||||
UserID: mId.UserID(u.MatrixID),
|
||||
}); err != nil {
|
||||
|
@ -51,12 +53,12 @@ func (d *Dispatcher) DeregisterApplication(a *model.Application, u *model.User)
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := d.mautrixClient.LeaveRoom(mId.RoomID(a.MatrixID)); err != nil {
|
||||
if _, err := d.mautrixClient.LeaveRoom(context.Background(), mId.RoomID(a.MatrixID)); err != nil {
|
||||
log.L.Print(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := d.mautrixClient.ForgetRoom(mId.RoomID(a.MatrixID)); err != nil {
|
||||
if _, err := d.mautrixClient.ForgetRoom(context.Background(), mId.RoomID(a.MatrixID)); err != nil {
|
||||
log.L.Print(err)
|
||||
return err
|
||||
}
|
||||
|
@ -65,7 +67,7 @@ func (d *Dispatcher) DeregisterApplication(a *model.Application, u *model.User)
|
|||
}
|
||||
|
||||
func (d *Dispatcher) sendRoomEvent(roomID, eventType string, content interface{}) error {
|
||||
if _, err := d.mautrixClient.SendStateEvent(mId.RoomID(roomID), event.NewEventType(eventType), "", content); err != nil {
|
||||
if _, err := d.mautrixClient.SendStateEvent(context.Background(), mId.RoomID(roomID), event.NewEventType(eventType), "", content); err != nil {
|
||||
log.L.Print(err)
|
||||
return err
|
||||
}
|
||||
|
@ -74,23 +76,31 @@ func (d *Dispatcher) sendRoomEvent(roomID, eventType string, content interface{}
|
|||
}
|
||||
|
||||
// UpdateApplication updates a channel for an application.
|
||||
func (d *Dispatcher) UpdateApplication(a *model.Application) error {
|
||||
func (d *Dispatcher) UpdateApplication(a *model.Application, behavior *configuration.RepairBehavior) error {
|
||||
log.L.Printf("Updating application %s (ID %d) with Matrix ID %s.\n", a.Name, a.ID, a.MatrixID)
|
||||
|
||||
content := map[string]interface{}{
|
||||
"name": a.Name,
|
||||
if behavior.ResetRoomName {
|
||||
content := map[string]interface{}{
|
||||
"name": a.Name,
|
||||
}
|
||||
|
||||
if err := d.sendRoomEvent(a.MatrixID, "m.room.name", content); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.L.Debugf("Not reseting room name as per configuration.\n")
|
||||
}
|
||||
|
||||
if err := d.sendRoomEvent(a.MatrixID, "m.room.name", content); err != nil {
|
||||
return err
|
||||
}
|
||||
if behavior.ResetRoomTopic {
|
||||
content := map[string]interface{}{
|
||||
"topic": buildRoomTopic(a.ID),
|
||||
}
|
||||
|
||||
content = map[string]interface{}{
|
||||
"topic": buildRoomTopic(a.ID),
|
||||
}
|
||||
|
||||
if err := d.sendRoomEvent(a.MatrixID, "m.room.topic", content); err != nil {
|
||||
return err
|
||||
if err := d.sendRoomEvent(a.MatrixID, "m.room.topic", content); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.L.Debugf("Not reseting room topic as per configuration.\n")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -98,7 +108,7 @@ func (d *Dispatcher) UpdateApplication(a *model.Application) error {
|
|||
|
||||
// IsOrphan checks if the user is still connected to the channel.
|
||||
func (d *Dispatcher) IsOrphan(a *model.Application, u *model.User) (bool, error) {
|
||||
resp, err := d.mautrixClient.JoinedMembers(mId.RoomID(a.MatrixID))
|
||||
resp, err := d.mautrixClient.JoinedMembers(context.Background(), mId.RoomID(a.MatrixID))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -114,7 +124,7 @@ func (d *Dispatcher) IsOrphan(a *model.Application, u *model.User) (bool, error)
|
|||
|
||||
// RepairApplication re-invites the user to the channel.
|
||||
func (d *Dispatcher) RepairApplication(a *model.Application, u *model.User) error {
|
||||
_, err := d.mautrixClient.InviteUser(mId.RoomID(a.MatrixID), &mautrix.ReqInviteUser{
|
||||
_, err := d.mautrixClient.InviteUser(context.Background(), mId.RoomID(a.MatrixID), &mautrix.ReqInviteUser{
|
||||
UserID: mId.UserID(u.MatrixID),
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// Package dispatcher provides definitions and functionality related to executing Matrix requests.
|
||||
package dispatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
|
@ -23,7 +26,7 @@ func Create(homeserver, username, password string, formatting configuration.Form
|
|||
return nil, err
|
||||
}
|
||||
|
||||
_, err = matrixClient.Login(&mautrix.ReqLogin{
|
||||
_, err = matrixClient.Login(context.Background(), &mautrix.ReqLogin{
|
||||
Type: mautrix.AuthTypePassword,
|
||||
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: username},
|
||||
Password: password,
|
||||
|
@ -41,7 +44,7 @@ func Create(homeserver, username, password string, formatting configuration.Form
|
|||
func (d *Dispatcher) Close() {
|
||||
log.L.Printf("Logging out.")
|
||||
|
||||
_, err := d.mautrixClient.Logout()
|
||||
_, err := d.mautrixClient.Logout(context.Background())
|
||||
if err != nil {
|
||||
log.L.Printf("Error while logging out: %s", err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dispatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html"
|
||||
"strings"
|
||||
|
@ -52,8 +53,8 @@ type NewContent struct {
|
|||
Format MessageFormat `json:"format"`
|
||||
}
|
||||
|
||||
// SendNotification sends a notification to the specified user.
|
||||
func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notification) (eventId string, err error) {
|
||||
// SendNotification sends a notification to a given user.
|
||||
func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notification) (eventID string, err error) {
|
||||
log.L.Printf("Sending notification to room %s.", a.MatrixID)
|
||||
|
||||
plainMessage := strings.TrimSpace(n.Message)
|
||||
|
@ -71,7 +72,7 @@ func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notificatio
|
|||
Format: MessageFormatHTML,
|
||||
}
|
||||
|
||||
evt, err := d.mautrixClient.SendMessageEvent(mId.RoomID(a.MatrixID), event.EventMessage, &messageEvent)
|
||||
evt, err := d.mautrixClient.SendMessageEvent(context.Background(), mId.RoomID(a.MatrixID), event.EventMessage, &messageEvent)
|
||||
if err != nil {
|
||||
log.L.Errorln(err)
|
||||
return "", err
|
||||
|
@ -80,7 +81,7 @@ func (d *Dispatcher) SendNotification(a *model.Application, n *model.Notificatio
|
|||
return evt.EventID.String(), nil
|
||||
}
|
||||
|
||||
// DeleteNotification sends a notification to the specified user that another notificaion is deleted
|
||||
// DeleteNotification sends a notification to a given user that another notification is deleted
|
||||
func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNotification) error {
|
||||
log.L.Printf("Sending delete notification to room %s", a.MatrixID)
|
||||
var oldFormattedBody string
|
||||
|
@ -90,7 +91,7 @@ func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNot
|
|||
deleteMessage, err := d.getMessage(a, n.ID)
|
||||
if err != nil {
|
||||
log.L.Println(err)
|
||||
return pberrors.ErrorMessageNotFound
|
||||
return pberrors.ErrMessageNotFound
|
||||
}
|
||||
|
||||
oldBody, oldFormattedBody, err = bodiesFromMessage(deleteMessage)
|
||||
|
@ -103,7 +104,6 @@ func (d *Dispatcher) DeleteNotification(a *model.Application, n *model.DeleteNot
|
|||
newFormattedBody := fmt.Sprintf("<del>%s</del><br>- deleted", oldFormattedBody)
|
||||
|
||||
_, err = d.replaceMessage(a, newBody, newFormattedBody, deleteMessage.ID.String(), oldBody, oldFormattedBody)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func (d *Dispatcher) getFormattedTitle(n *model.Notification) string {
|
|||
// Converts different syntaxes to a HTML-formatted message
|
||||
func (d *Dispatcher) getFormattedMessage(n *model.Notification) string {
|
||||
trimmedMessage := strings.TrimSpace(n.Message)
|
||||
message := strings.Replace(html.EscapeString(trimmedMessage), "\n", "<br />", -1) // default to text/plain
|
||||
message := strings.ReplaceAll(html.EscapeString(trimmedMessage), "\n", "<br />") // default to text/plain
|
||||
|
||||
if optionsDisplayRaw, ok := n.Extras["client::display"]; ok {
|
||||
optionsDisplay, ok := optionsDisplayRaw.(map[string]interface{})
|
||||
|
@ -140,7 +140,7 @@ func (d *Dispatcher) getFormattedMessage(n *model.Notification) string {
|
|||
|
||||
switch contentType {
|
||||
case "html", "text/html":
|
||||
message = strings.Replace(trimmedMessage, "\n", "<br />", -1)
|
||||
message = strings.ReplaceAll(trimmedMessage, "\n", "<br />")
|
||||
case "markdown", "md", "text/md", "text/markdown":
|
||||
// Allow HTML in Markdown
|
||||
message = string(markdown.ToHTML([]byte(trimmedMessage), nil, nil))
|
||||
|
@ -186,7 +186,7 @@ func (d *Dispatcher) getMessage(a *model.Application, id string) (*event.Event,
|
|||
maxPages := 10 // Maximum pages to request (10 messages per page)
|
||||
|
||||
for i := 0; i < maxPages; i++ {
|
||||
messages, err := d.mautrixClient.Messages(mId.RoomID(a.MatrixID), start, end, 'b', nil, 10)
|
||||
messages, err := d.mautrixClient.Messages(context.Background(), mId.RoomID(a.MatrixID), start, end, 'b', nil, 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ func (d *Dispatcher) getMessage(a *model.Application, id string) (*event.Event,
|
|||
start = messages.End
|
||||
}
|
||||
|
||||
return nil, pberrors.ErrorMessageNotFound
|
||||
return nil, pberrors.ErrMessageNotFound
|
||||
}
|
||||
|
||||
// Replaces the content of a matrix message
|
||||
|
@ -225,7 +225,7 @@ func (d *Dispatcher) replaceMessage(a *model.Application, newBody, newFormattedB
|
|||
Format: MessageFormatHTML,
|
||||
}
|
||||
|
||||
sendEvent, err := d.mautrixClient.SendMessageEvent(mId.RoomID(a.MatrixID), event.EventMessage, &replaceEvent)
|
||||
sendEvent, err := d.mautrixClient.SendMessageEvent(context.Background(), mId.RoomID(a.MatrixID), event.EventMessage, &replaceEvent)
|
||||
if err != nil {
|
||||
log.L.Errorln(err)
|
||||
return nil, err
|
||||
|
@ -260,7 +260,7 @@ func (d *Dispatcher) respondToMessage(a *model.Application, body, formattedBody
|
|||
}
|
||||
notificationEvent.RelatesTo = ¬ificationRelation
|
||||
|
||||
sendEvent, err := d.mautrixClient.SendMessageEvent(mId.RoomID(a.MatrixID), event.EventMessage, ¬ificationEvent)
|
||||
sendEvent, err := d.mautrixClient.SendMessageEvent(context.Background(), mId.RoomID(a.MatrixID), event.EventMessage, ¬ificationEvent)
|
||||
if err != nil {
|
||||
log.L.Errorln(err)
|
||||
return nil, err
|
||||
|
@ -273,7 +273,7 @@ func (d *Dispatcher) respondToMessage(a *model.Application, body, formattedBody
|
|||
func bodiesFromMessage(message *event.Event) (body, formattedBody string, err error) {
|
||||
msgContent := message.Content.AsMessage()
|
||||
if msgContent == nil {
|
||||
return "", "", pberrors.ErrorMessageNotFound
|
||||
return "", "", pberrors.ErrMessageNotFound
|
||||
}
|
||||
|
||||
formattedBody = msgContent.Body
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Source: https://github.com/toorop/gin-logrus
|
||||
|
||||
// Package log provides a connector between gin and logrus.
|
||||
package log
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package log provides functionality to configure the logger.
|
||||
package log
|
||||
|
||||
import (
|
||||
|
@ -6,6 +7,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// L is the global logger instance for PushBits.
|
||||
var L *log.Logger
|
||||
|
||||
func init() {
|
||||
|
@ -17,6 +19,7 @@ func init() {
|
|||
})
|
||||
}
|
||||
|
||||
// SetDebug sets the logger to output debug information.
|
||||
func SetDebug() {
|
||||
L.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package model
|
|||
|
||||
import "strings"
|
||||
|
||||
// AlertmanagerWebhook is used to pass notifications over webhook pushes.
|
||||
type AlertmanagerWebhook struct {
|
||||
Version string `json:"version"`
|
||||
GroupKey string `json:"groupKey"`
|
||||
|
@ -13,6 +14,7 @@ type AlertmanagerWebhook struct {
|
|||
Alerts []AlertmanagerAlert `json:"alerts"`
|
||||
}
|
||||
|
||||
// AlertmanagerAlert holds information related to a single alert in a notification.
|
||||
type AlertmanagerAlert struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
|
@ -21,6 +23,7 @@ type AlertmanagerAlert struct {
|
|||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// ToNotification converts an Alertmanager alert into a Notification
|
||||
func (alert *AlertmanagerAlert) ToNotification(titleAnnotation, messageAnnotation string) Notification {
|
||||
title := strings.Builder{}
|
||||
message := strings.Builder{}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package model contains structs used in the PushBits API and across the application.
|
||||
package model
|
||||
|
||||
import (
|
||||
|
@ -8,7 +9,7 @@ import (
|
|||
// Notification holds information like the message, the title, and the priority of a notification.
|
||||
type Notification struct {
|
||||
ID string `json:"id"`
|
||||
UrlEncodedID string `json:"id_url_encoded"`
|
||||
URLEncodedID string `json:"id_url_encoded"`
|
||||
ApplicationID uint `json:"appid"`
|
||||
Message string `json:"message" form:"message" query:"message" binding:"required"`
|
||||
Title string `json:"title" form:"title" query:"title"`
|
||||
|
@ -20,7 +21,7 @@ type Notification struct {
|
|||
// Sanitize sets explicit defaults for a notification.
|
||||
func (n *Notification) Sanitize(application *Application) {
|
||||
n.ID = ""
|
||||
n.UrlEncodedID = ""
|
||||
n.URLEncodedID = ""
|
||||
n.ApplicationID = application.ID
|
||||
if strings.TrimSpace(n.Title) == "" {
|
||||
n.Title = application.Name
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Package pberrors defines errors specific to PushBits
|
||||
package pberrors
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrorMessageNotFound indicates that a message does not exist
|
||||
var ErrorMessageNotFound = errors.New("message not found")
|
||||
// ErrMessageNotFound indicates that a message does not exist
|
||||
var ErrMessageNotFound = errors.New("message not found")
|
||||
|
||||
// ErrConfigTLSFilesInconsistent indicates that either just a certfile or a keyfile was provided
|
||||
var ErrConfigTLSFilesInconsistent = errors.New("TLS certfile and keyfile must either both be provided or omitted")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package router provides functions to configure the web server.
|
||||
package router
|
||||
|
||||
import (
|
||||
|
@ -28,7 +29,7 @@ func Create(debug bool, trustedProxies []string, cm *credentials.Manager, db *da
|
|||
healthHandler := api.HealthHandler{DB: db}
|
||||
notificationHandler := api.NotificationHandler{DB: db, DP: dp}
|
||||
userHandler := api.UserHandler{AH: &applicationHandler, CM: cm, DB: db, DP: dp}
|
||||
alertmanagerHandler := alertmanager.AlertmanagerHandler{DP: dp, Settings: alertmanager.AlertmanagerHandlerSettings{
|
||||
alertmanagerHandler := alertmanager.Handler{DP: dp, Settings: alertmanager.HandlerSettings{
|
||||
TitleAnnotation: alertmanagerConfig.AnnotationTitle,
|
||||
MessageAnnotation: alertmanagerConfig.AnnotationMessage,
|
||||
}}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
// Package runner provides functions to run the web server.
|
||||
package runner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
)
|
||||
|
||||
// Run starts the Gin engine.
|
||||
func Run(engine *gin.Engine, address string, port int) error {
|
||||
err := engine.Run(fmt.Sprintf("%s:%d", address, port))
|
||||
if err != nil {
|
||||
return err
|
||||
func Run(engine *gin.Engine, c *configuration.Configuration) error {
|
||||
var err error
|
||||
address := fmt.Sprintf("%s:%d", c.HTTP.ListenAddress, c.HTTP.Port)
|
||||
|
||||
if c.HTTP.CertFile != "" && c.HTTP.KeyFile != "" {
|
||||
err = engine.RunTLS(address, c.HTTP.CertFile, c.HTTP.KeyFile)
|
||||
} else {
|
||||
err = engine.Run(address)
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
|
439
poetry.lock
generated
439
poetry.lock
generated
|
@ -1,439 +0,0 @@
|
|||
[[package]]
|
||||
name = "attrs"
|
||||
version = "21.4.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
|
||||
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
|
||||
|
||||
[[package]]
|
||||
name = "bracex"
|
||||
version = "2.2.1"
|
||||
description = "Bash style brace expander."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2021.10.8"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "2.0.12"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5.0"
|
||||
|
||||
[package.extras]
|
||||
unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.0.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[[package]]
|
||||
name = "click-option-group"
|
||||
version = "0.5.3"
|
||||
description = "Option groups missing in Click"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4"
|
||||
|
||||
[package.dependencies]
|
||||
Click = ">=7.0,<9"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=2.3,<3)", "pallets-sphinx-themes", "m2r"]
|
||||
tests = ["coverage (<6)", "pytest", "pytest-cov", "coveralls"]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.3"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "4.11.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
perf = ["ipython"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-resources"
|
||||
version = "5.4.0"
|
||||
description = "Read resources from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.4.0"
|
||||
description = "An implementation of JSON Schema validation for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=17.4.0"
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""}
|
||||
pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
|
||||
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
|
||||
format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "peewee"
|
||||
version = "3.14.8"
|
||||
description = "a little orm"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.7"
|
||||
description = "Python parsing module"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
diagrams = ["jinja2", "railroad-diagrams"]
|
||||
|
||||
[[package]]
|
||||
name = "pyrsistent"
|
||||
version = "0.18.1"
|
||||
description = "Persistent/Functional/Immutable data structures"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.27.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
|
||||
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruamel.yaml"
|
||||
version = "0.17.21"
|
||||
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3"
|
||||
|
||||
[package.dependencies]
|
||||
"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["ryd"]
|
||||
jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruamel.yaml.clib"
|
||||
version = "0.2.6"
|
||||
description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "semgrep"
|
||||
version = "0.82.0"
|
||||
description = "Lightweight static analysis for many languages. Find bug variants with patterns that look like source code."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=19.3.0"
|
||||
click = ">=8.0.1"
|
||||
click-option-group = ">=0.5.3"
|
||||
colorama = ">=0.4.3"
|
||||
jsonschema = ">=3.2.0,<5"
|
||||
packaging = ">=20.4"
|
||||
peewee = ">=3.14.4,<3.15.0"
|
||||
requests = ">=2.22.0"
|
||||
"ruamel.yaml" = ">=0.16.0,<0.18"
|
||||
tqdm = ">=4.46.1"
|
||||
wcmatch = "8.3"
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.62.3"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["py-make (>=0.1.0)", "twine", "wheel"]
|
||||
notebook = ["ipywidgets (>=6)"]
|
||||
telegram = ["requests"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.1.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.6+"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.8"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "wcmatch"
|
||||
version = "8.3"
|
||||
description = "Wildcard/glob file name matcher."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
bracex = ">=2.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.7.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "792ec3aa3a1641a70a6cd82c2399211c85c8a5cad5522f260c86ef407f8482ce"
|
||||
|
||||
[metadata.files]
|
||||
attrs = [
|
||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
]
|
||||
bracex = [
|
||||
{file = "bracex-2.2.1-py3-none-any.whl", hash = "sha256:096c4b788bf492f7af4e90ef8b5bcbfb99759ae3415ea1b83c9d29a5ed8f9a94"},
|
||||
{file = "bracex-2.2.1.tar.gz", hash = "sha256:1c8d1296e00ad9a91030ccb4c291f9e4dc7c054f12c707ba3c5ff3e9a81bcd21"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
|
||||
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
|
||||
]
|
||||
charset-normalizer = [
|
||||
{file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
|
||||
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
|
||||
{file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
|
||||
]
|
||||
click-option-group = [
|
||||
{file = "click-option-group-0.5.3.tar.gz", hash = "sha256:a6e924f3c46b657feb5b72679f7e930f8e5b224b766ab35c91ae4019b4e0615e"},
|
||||
{file = "click_option_group-0.5.3-py3-none-any.whl", hash = "sha256:9653a2297357335d7325a1827e71ac1245d91c97d959346a7decabd4a52d5354"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
|
||||
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.11.0-py3-none-any.whl", hash = "sha256:6affcdb3aec542dd98df8211e730bba6c5f2bec8288d47bacacde898f548c9ad"},
|
||||
{file = "importlib_metadata-4.11.0.tar.gz", hash = "sha256:9e5e553bbba1843cb4a00823014b907616be46ee503d2b9ba001d214a8da218f"},
|
||||
]
|
||||
importlib-resources = [
|
||||
{file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"},
|
||||
{file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"},
|
||||
]
|
||||
jsonschema = [
|
||||
{file = "jsonschema-4.4.0-py3-none-any.whl", hash = "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"},
|
||||
{file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
||||
]
|
||||
peewee = [
|
||||
{file = "peewee-3.14.8.tar.gz", hash = "sha256:01bd7f734defb08d7a3346a0c0ca7011bc8d0d685934ec0e001b3371d522ec53"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
|
||||
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
|
||||
]
|
||||
pyrsistent = [
|
||||
{file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"},
|
||||
{file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"},
|
||||
{file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"},
|
||||
{file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"},
|
||||
{file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"},
|
||||
{file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"},
|
||||
{file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"},
|
||||
{file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"},
|
||||
{file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"},
|
||||
{file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"},
|
||||
{file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"},
|
||||
{file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"},
|
||||
{file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"},
|
||||
{file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"},
|
||||
{file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"},
|
||||
{file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"},
|
||||
{file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"},
|
||||
{file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"},
|
||||
{file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"},
|
||||
{file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"},
|
||||
{file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
||||
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
||||
]
|
||||
"ruamel.yaml" = [
|
||||
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
|
||||
{file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"},
|
||||
]
|
||||
"ruamel.yaml.clib" = [
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"},
|
||||
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"},
|
||||
{file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"},
|
||||
]
|
||||
semgrep = [
|
||||
{file = "semgrep-0.82.0-cp37.cp38.cp39.py37.py38.py39-none-any.whl", hash = "sha256:1750f2b755ffc7db4690e6c5990e03e03729e6de4d4c4edf3f39435020244989"},
|
||||
{file = "semgrep-0.82.0-cp37.cp38.cp39.py37.py38.py39-none-macosx_10_14_x86_64.whl", hash = "sha256:037124db822a0c9235ea6cfbc637f06771ca2882d181dfdbd460c430bf51d7ec"},
|
||||
{file = "semgrep-0.82.0.tar.gz", hash = "sha256:aa6c57d08602ffde3bf36159c0325d6d72f70dc634cd237f30ff46f7d6b78be3"},
|
||||
]
|
||||
tqdm = [
|
||||
{file = "tqdm-4.62.3-py2.py3-none-any.whl", hash = "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c"},
|
||||
{file = "tqdm-4.62.3.tar.gz", hash = "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.1.0-py3-none-any.whl", hash = "sha256:c13180fbaa7cd97065a4915ceba012bdb31dc34743e63ddee16360161d358414"},
|
||||
{file = "typing_extensions-4.1.0.tar.gz", hash = "sha256:ba97c5143e5bb067b57793c726dd857b1671d4b02ced273ca0538e71ff009095"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
|
||||
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
|
||||
]
|
||||
wcmatch = [
|
||||
{file = "wcmatch-8.3-py3-none-any.whl", hash = "sha256:7141d2c85314253f16b38cb3d6cc0fb612918d407e1df3ccc2be7c86cc259c22"},
|
||||
{file = "wcmatch-8.3.tar.gz", hash = "sha256:371072912398af61d1e4e78609e18801c6faecd3cb36c54c82556a60abc965db"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"},
|
||||
{file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"},
|
||||
]
|
3
postgres-debug.env
Normal file
3
postgres-debug.env
Normal file
|
@ -0,0 +1,3 @@
|
|||
POSTGRES_PASSWORD=pushbits
|
||||
POSTGRES_USER=pushbits
|
||||
POSTGRES_DB=pushbits
|
|
@ -1,11 +0,0 @@
|
|||
[tool.poetry]
|
||||
name = "pushbits"
|
||||
version = "0.0.0"
|
||||
description = ""
|
||||
authors = []
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
semgrep = "^0.82.0"
|
|
@ -1,3 +1,4 @@
|
|||
// Package mockups contains mockup objects and functions for tests.
|
||||
package mockups
|
||||
|
||||
import "github.com/pushbits/server/internal/model"
|
||||
|
|
|
@ -2,7 +2,6 @@ package mockups
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
|
@ -22,12 +21,12 @@ func ReadConfig(filename string, removeFile bool) (config *configuration.Configu
|
|||
return nil, errors.New("empty filename")
|
||||
}
|
||||
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
file, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile("config.yml", file, 0o644)
|
||||
err = os.WriteFile("config.yml", file, 0o644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -35,7 +34,10 @@ func ReadConfig(filename string, removeFile bool) (config *configuration.Configu
|
|||
config = configuration.Get()
|
||||
|
||||
if removeFile {
|
||||
os.Remove("config.yml")
|
||||
err = os.Remove("config.yml")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
|
|
|
@ -3,28 +3,34 @@ package mockups
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pushbits/server/internal/configuration"
|
||||
"github.com/pushbits/server/internal/model"
|
||||
)
|
||||
|
||||
// MockDispatcher is a dispatcher used for testing - it does not need any storage interface
|
||||
type MockDispatcher struct{}
|
||||
|
||||
func (d *MockDispatcher) RegisterApplication(id uint, name, token, user string) (string, error) {
|
||||
// RegisterApplication mocks a functions to create a channel for an application.
|
||||
func (*MockDispatcher) RegisterApplication(id uint, name, _ string) (string, error) {
|
||||
return fmt.Sprintf("%d-%s", id, name), nil
|
||||
}
|
||||
|
||||
func (d *MockDispatcher) DeregisterApplication(a *model.Application, u *model.User) error {
|
||||
// DeregisterApplication mocks a function to delete a channel for an application.
|
||||
func (*MockDispatcher) DeregisterApplication(_ *model.Application, _ *model.User) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *MockDispatcher) UpdateApplication(a *model.Application) error {
|
||||
// UpdateApplication mocks a function to update a channel for an application.
|
||||
func (*MockDispatcher) UpdateApplication(_ *model.Application, _ *configuration.RepairBehavior) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *MockDispatcher) SendNotification(a *model.Application, n *model.Notification) (id string, err error) {
|
||||
// SendNotification mocks a function to send a notification to a given user.
|
||||
func (*MockDispatcher) SendNotification(_ *model.Application, _ *model.Notification) (id string, err error) {
|
||||
return randStr(15), nil
|
||||
}
|
||||
|
||||
func (d *MockDispatcher) DeleteNotification(a *model.Application, n *model.DeleteNotification) error {
|
||||
// DeleteNotification mocks a function to send a notification to a given user that another notification is deleted
|
||||
func (*MockDispatcher) DeleteNotification(_ *model.Application, _ *model.DeleteNotification) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,10 +5,16 @@ import (
|
|||
"encoding/base64"
|
||||
)
|
||||
|
||||
func randStr(len int) string {
|
||||
buff := make([]byte, len)
|
||||
rand.Read(buff)
|
||||
func randStr(length int) string {
|
||||
buff := make([]byte, length)
|
||||
|
||||
_, err := rand.Read(buff)
|
||||
if err != nil {
|
||||
panic("cannot read random data")
|
||||
}
|
||||
|
||||
str := base64.StdEncoding.EncodeToString(buff)
|
||||
|
||||
// Base 64 can be longer than len
|
||||
return str[:len]
|
||||
return str[:length]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package tests provides definitions and functionality related to unit and integration tests.
|
||||
package tests
|
||||
|
||||
import (
|
||||
|
@ -23,14 +24,14 @@ type Request struct {
|
|||
// GetRequest returns a ResponseRecorder and gin context according to the data set in the Request.
|
||||
// String data is passed as is, all other data types are marshaled before.
|
||||
func (r *Request) GetRequest() (w *httptest.ResponseRecorder, c *gin.Context, err error) {
|
||||
var body io.Reader
|
||||
w = httptest.NewRecorder()
|
||||
var body io.Reader
|
||||
|
||||
switch r.Data.(type) {
|
||||
switch data := r.Data.(type) {
|
||||
case string:
|
||||
body = strings.NewReader(r.Data.(string))
|
||||
body = strings.NewReader(data)
|
||||
default:
|
||||
dataMarshaled, err := json.Marshal(r.Data)
|
||||
dataMarshaled, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 46e040f844c2b1c40599c8d4f1cdf277b197329e
|
Loading…
Add table
Reference in a new issue