32 Commits

Author SHA1 Message Date
e2c71dafbb Latest stuff
Some checks failed
Test and build / build (push) Failing after 6s
2024-02-11 17:02:44 +01:00
99c47abe2e Dependency updates etc 2024-02-11 17:01:25 +01:00
3da3083307 CI CD work
All checks were successful
Test and build / build (push) Successful in 40s
2024-02-11 16:28:13 +01:00
4f3ec5fd69 Updated swagger stuff
All checks were successful
Test and build / build (push) Successful in 39s
2024-02-04 06:46:48 +01:00
464e3e2851 Updating dbmate
All checks were successful
Test and build / build (push) Successful in 30s
2024-02-04 04:50:03 +01:00
277841a530 Updating postgres version
All checks were successful
Test and build / build (push) Successful in 27s
2024-02-04 04:33:02 +01:00
4846f43f98 Added deploy pipeline
All checks were successful
Test and build / build (push) Successful in 3s
2024-02-04 04:29:40 +01:00
618f67a48a Updated dependencies
All checks were successful
Test and build / build (push) Successful in 37s
2024-02-04 04:27:03 +01:00
8cf3e9a894 Added image push for db migrations
All checks were successful
Test and build / build (push) Successful in 23s
2024-02-04 04:23:21 +01:00
bb7ce13717 Updated workflow
All checks were successful
Test and build / build (push) Successful in 1m5s
2024-02-04 04:16:45 +01:00
247b411011 New pipelines and stuff 2024-02-04 04:15:44 +01:00
80ebba33bc Clean-up after drone pipelines
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-16 22:54:02 +02:00
9b0b929fd8 Merge branch 'master' of ssh://gitea.larvit.se:21022/pwrpln/auth-api
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-16 22:51:33 +02:00
1b043afb78 Disable external port for postgres
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-16 22:50:17 +02:00
26443fff40 Merge pull request 'Loads of updates' (#1) from tmp into master
Some checks failed
continuous-integration/drone/push Build encountered an error
continuous-integration/drone Build is failing
Reviewed-on: #1
2023-05-10 22:44:19 +02:00
9488f82b18 Updated drone pipeline 2023-05-10 22:37:12 +02:00
b880f2f48b New version yo 2023-05-10 22:35:38 +02:00
61590f684e Reworking stuff 2023-05-08 15:29:19 +02:00
c4a97644ed Updating versions and changing log system
Some checks failed
continuous-integration/drone Build encountered an error
2023-02-20 23:54:02 +01:00
16c57cc424 Fixed bug when trying to auth with empty username and empty password
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-26 13:42:59 +02:00
30dad5851a Improved step documentation in drone
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-22 14:33:14 +02:00
f96a9afd55 Upgraded everything
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-22 14:30:24 +02:00
9dcaa1ad39 Added some more logging and documented deploy procedures 2022-04-22 11:09:40 +02:00
c9871c817a Testing to clean up docker stuff before running tests
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-27 20:11:41 +01:00
0fe1e00737 Testing another postgres password
Some checks failed
continuous-integration/drone/push Build is failing
2022-02-27 19:29:56 +01:00
1859f8d7bc Updated all go modules
Some checks failed
continuous-integration/drone/push Build is failing
2022-02-27 17:34:42 +01:00
3abefb9adf Fixed internal server error for trying to login with wrong username
Some checks failed
continuous-integration/drone/push Build is failing
2022-02-27 17:32:30 +01:00
092a3f7712 Removed postgres open port
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-18 21:02:08 +01:00
00d051903f Updated all Go dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-18 19:38:35 +01:00
6decc6b139 Added license and upgraded postgres
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-18 19:27:02 +01:00
ce4be7e5a1 Fixed error in docker tagging
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-15 20:17:25 +01:00
dbf4512e9f Updated pipelines to always push the latest tag to docker hub
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-15 20:14:33 +01:00
34 changed files with 2069 additions and 1169 deletions

View File

@@ -1,72 +0,0 @@
kind: pipeline
type: docker
name: Tests
steps:
- name: Tests
image: docker/compose:1.29.2
volumes:
- name: docker-sock
path: /var/run/docker.sock
commands:
- docker-compose build
- docker-compose --profile tests build
- docker-compose run --rm db-migrations
- docker-compose up -d
- docker-compose run --rm tests
volumes:
- name: docker-sock
host:
path: /var/run/docker.sock
trigger:
event:
exclude:
- tag
---
kind: pipeline
type: docker
name: Deploy
steps:
- name: Tests
image: docker/compose:1.29.2
volumes:
- name: docker-sock
path: /var/run/docker.sock
commands:
- docker-compose build
- docker-compose --profile tests build
- docker-compose run --rm db-migrations
- docker-compose up -d
- docker-compose run --rm tests
- name: Build db migration
image: docker/compose:1.29.2
volumes:
- name: docker-sock
path: /var/run/docker.sock
commands:
- docker build -t lilleman/auth-api-db-migrate:$DRONE_TAG -f ./Dockerfile.migrations .
- docker build -t lilleman/auth-api:$DRONE_TAG -f ./Dockerfile .
- name: Push to Docker Hub
image: docker/compose:1.29.2
environment:
DOCKERHUB_TOKEN:
from_secret: dockerhub
volumes:
- name: docker-sock
path: /var/run/docker.sock
commands:
- docker login -u lilleman -p $DOCKERHUB_TOKEN
- docker push lilleman/auth-api-db-migrate:$DRONE_TAG
- docker push lilleman/auth-api:$DRONE_TAG
volumes:
- name: docker-sock
host:
path: /var/run/docker.sock
trigger:
event:
- tag

View File

@@ -1,4 +1,7 @@
ADMIN_API_KEY=changeMe
DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:5432/auth"
DATABASE_URL=postgres://postgres:puIleHgcpsvDr360ttUo@postgres:5432/auth?sslmode=disable
JWT_SHARED_SECRET=changeMe
LOG_MIN_LVL=Debug
PUBLIC_HOST=localhost:4000
URL_PREFIX=/
WEB_BIND_HOST=":4000"

19
.gitea/deploy.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
curl \
--fail-with-body \
-H "Authorization: ${RUNNER_API_KEY}" \
-XPOST 192.168.1.186 \
-H 'Content-Type: application/json; charset=utf-8' \
--data-binary @- <<EOF
{
"commands": [
"git clone --single-branch --branch ${GITHUB_REF_NAME} --depth 1 ssh://git@gitea.larvit.se:21022/pwrpln/auth-api.git",
"cd auth-api",
"echo \"${DOCKER_PASSWORD}\" | docker login gitea.larvit.se --username ${DOCKER_USERNAME} --password-stdin",
"docker pull gitea.larvit.se/pwrpln/auth-api:main",
"docker tag gitea.larvit.se/pwrpln/auth-api:main gitea.larvit.se/pwrpln/auth-api:${GITHUB_REF_NAME}",
"docker push gitea.larvit.se/pwrpln/auth-api:${GITHUB_REF_NAME}"
]
}
EOF

24
.gitea/test-and-build.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
curl \
--fail-with-body \
-H "Authorization: ${RUNNER_API_KEY}" \
-XPOST 192.168.1.186 \
-H 'Content-Type: application/json; charset=utf-8' \
--data-binary @- <<EOF
{
"commands": [
"git clone --single-branch --branch ${GITHUB_REF_NAME} --depth 1 ssh://git@gitea.larvit.se:21022/pwrpln/auth-api.git",
"cd auth-api",
"docker compose build",
"docker compose --profile tests build",
"docker compose run --rm tests",
"docker compose down -v --remove-orphans -t0",
"echo \"${DOCKER_PASSWORD}\" | docker login gitea.larvit.se --username ${DOCKER_USERNAME} --password-stdin",
"docker build -t gitea.larvit.se/pwrpln/auth-api:${GITHUB_REF_NAME} .",
"docker push gitea.larvit.se/pwrpln/auth-api:${GITHUB_REF_NAME}",
"docker build -t gitea.larvit.se/pwrpln/auth-api-db-migrations:${GITHUB_REF_NAME} -f ./Dockerfile.migrations .",
"docker push gitea.larvit.se/pwrpln/auth-api-db-migrations:${GITHUB_REF_NAME}"
]
}
EOF

View File

@@ -0,0 +1,22 @@
name: Test and build
run-name: ${{ gitea.actor }} testing and building
on:
push:
tags:
- '**'
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }}
RUNNER_API_KEY: ${{ secrets.RUNNER_API_KEY }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
.gitea/deploy.sh
sparse-checkout-cone-mode: false
- run: .gitea/deploy.sh

View File

@@ -0,0 +1,22 @@
name: Test and build
run-name: ${{ gitea.actor }} testing and building
on:
push:
branches:
- '**'
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }}
RUNNER_API_KEY: ${{ secrets.RUNNER_API_KEY }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
.gitea/test-and-build.sh
sparse-checkout-cone-mode: false
- run: .gitea/test-and-build.sh

View File

@@ -1,4 +1,4 @@
FROM golang:1.17.7-alpine3.15 AS builder
FROM golang:1.20.4-alpine3.17 AS builder
# Install missing pkgs
RUN apk add --no-cache git
@@ -10,7 +10,7 @@ ENV GO111MODULE=on \
GOARCH=amd64
# Set workdir in GOPATH
WORKDIR $GOPATH/src/gitlab.larvit.se/power-plan/auth
WORKDIR $GOPATH/src/gitea.larvit.se/power-plan/auth
# Copy and download dependency using go mod
COPY go.mod .

View File

@@ -1,3 +1,3 @@
FROM amacneil/dbmate:1.14.0
FROM amacneil/dbmate:2.11.0
COPY db /db

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2024 Larv IT AB
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -4,8 +4,6 @@ A tiny REST API for auth. Register accounts, auth with api-key or name/password,
## Quick start with docker compose
Migrate database: `docker-compose run --rm db-migrations`
Start the API (on port 4000 by default): `docker-compose up -d`
Point your browser to `http://localhost:4000` to view the swagger API documentation.
@@ -20,12 +18,25 @@ The account field "role" is a bit special, in that if it contains "admin" as one
## Tests
Run integration tests (Requires migrated database and started API): `docker-compose run --rm tests`
Run integration tests: `docker-compose run --rm tests`
## Deploy a new version
Everytime a push is done, tests are ran and if they are successful a new image will be published on https://gitea.larvit.se/pwrpln/auth-api:branch-name and https://gitea.larvit.se/pwrpln/auth-api-db-migrations:branch-name
## Some useful cURLs
Obtain an admin GWT: `curl -d '"api-key-goes-here"' -H "Content-Type: application/json" -i http://localhost:4000/auth/api-key`
Use a bearer token to make a call: `curl -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/account/{accountID}`
Use a bearer token to make a call: `curl -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/accounts/{accountID}`
Create account: `curl -d '{"name": "Bosse", "password": "Hemligt", "fields": [{ "name":"role", "values":["user"]}]}' -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/account`
Create account: `curl -d '{"name": "Bosse", "password": "Hemligt", "fields": [{ "name":"role", "values":["user"]}]}' -H "Content-Type: application/json" -H "Authorization: bearer your-JWT-token-goes-here" -i http://localhost:4000/account`
## Development
For local development, run with .env like: `eval $(cat .env) go run src/main.go`
To regenerate the swagger docs folder:
1. Make sure you have swag installed: https://github.com/swaggo/swag
2. cd src && swag init

56
compose.yml Normal file
View File

@@ -0,0 +1,56 @@
version: '3.9'
services:
postgres:
image: postgres:16.1-alpine3.19
user: postgres
environment:
- POSTGRES_PASSWORD=prutt
- POSTGRES_DB=auth
# ports:
# - 5432:5432
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 3s
timeout: 10s
retries: 5
start_period: 80s
db-migrations:
build:
context: .
dockerfile: Dockerfile.migrations
environment:
- DATABASE_URL=postgres://postgres:prutt@postgres:5432/auth?sslmode=disable
command: ["--wait", "up"]
depends_on:
postgres:
condition: service_healthy
api:
build: .
environment:
- ADMIN_API_KEY=hihi
- DATABASE_URL=postgres://postgres:prutt@postgres:5432/auth?sslmode=disable
- JWT_SHARED_SECRET=hihi
- LOG_MIN_LVL=Debug
- URL_PREFIX=/
- WEB_BIND_HOST=:4000
- PUBLIC_HOST=localhost:4000
depends_on:
db-migrations:
condition: service_completed_successfully
ports:
- 4000:4000
tests:
build:
context: tests
environment:
- ADMIN_API_KEY=hihi
- AUTH_URL=http://api:4000
- JWT_SHARED_SECRET=hihi
profiles: ["tests"]
depends_on:
api:
condition: service_started

View File

@@ -1,45 +0,0 @@
version: '3.9'
services:
postgres:
image: postgres:13.4-alpine3.14
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=auth
ports:
- 5432:5432
db-migrations:
build:
context: .
dockerfile: Dockerfile.migrations
environment:
- DATABASE_URL=postgres://postgres:postgres@postgres:5432/auth?sslmode=disable
command: ["--wait", "up"]
profiles: ["migrations"]
depends_on:
- postgres
auth-api:
build: .
environment:
- ADMIN_API_KEY=hihi
- DATABASE_URL=postgres://postgres:postgres@postgres:5432/auth?sslmode=disable
- JWT_SHARED_SECRET=hihi
- WEB_BIND_HOST=:4000
depends_on:
- postgres
ports:
- 4000:4000
tests:
build:
context: tests
environment:
- ADMIN_API_KEY=hihi
- AUTH_URL=http://auth-api:4000
- JWT_SHARED_SECRET=hihi
profiles: ["tests"]
depends_on:
- auth-api

101
go.mod
View File

@@ -1,51 +1,88 @@
module gitea.larvit.se/pwrpln/auth-api
go 1.17
go 1.21
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/arsmn/fiber-swagger/v2 v2.17.0
gitea.larvit.se/pwrpln/go_log v0.3.0
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gofiber/fiber/v2 v2.18.0
github.com/google/uuid v1.3.0
github.com/jackc/pgx/v4 v4.13.0
github.com/joho/godotenv v1.3.0
github.com/swaggo/swag v1.7.1
go.uber.org/zap v1.19.0
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
github.com/gofiber/fiber/v2 v2.52.0
<<<<<<< HEAD
github.com/gofiber/swagger v1.0.0
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v4 v4.18.1
github.com/swaggo/swag v1.16.3
golang.org/x/crypto v0.18.0
=======
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v4 v4.18.1
github.com/swaggo/swag v1.16.3
golang.org/x/crypto v0.19.0
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/brotli v1.0.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.3 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
<<<<<<< HEAD
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
=======
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/spec v0.20.14 // indirect
github.com/go-openapi/swag v0.22.9 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.0 // indirect
github.com/jackc/pgconn v1.14.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.8.1 // indirect
github.com/jackc/puddle v1.1.3 // indirect
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
<<<<<<< HEAD
github.com/jackc/pgtype v1.14.1 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.13.5 // indirect
github.com/klauspost/compress v1.17.5 // indirect
=======
github.com/jackc/pgtype v1.14.2 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.6 // indirect
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
<<<<<<< HEAD
github.com/pkg/errors v0.9.1 // indirect
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 // indirect
github.com/rivo/uniseg v0.4.6 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/urfave/cli/v2 v2.27.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.29.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect
golang.org/x/sys v0.0.0-20210903071746-97244b99971b // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.5 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
=======
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/swaggo/files v1.0.1 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

381
go.sum
View File

@@ -1,58 +1,106 @@
gitea.larvit.se/pwrpln/go_log v0.3.0 h1:zxCQ4vPU8gqsYT35DIrGm1qntd88oDbzd7SvzAmNi1s=
gitea.larvit.se/pwrpln/go_log v0.3.0/go.mod h1:CVZH9ge+rbQyhc+HYSI+B1A3j54wnQxzAcFgFMqsfLw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
<<<<<<< HEAD
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
=======
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM=
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/arsmn/fiber-swagger/v2 v2.17.0 h1:Y3mNtJdcRS1wakB033bmBXO/cXTWUeFMMktd1oYTVeQ=
github.com/arsmn/fiber-swagger/v2 v2.17.0/go.mod h1:LyEjt5PAUB2VDxPjsCwYQyLxDHxUOV35UHhHXKO95O0=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/arsmn/fiber-swagger/v2 v2.31.1 h1:VmX+flXiGGNqLX3loMEEzL3BMOZFSPwBEWR04GA6Mco=
github.com/arsmn/fiber-swagger/v2 v2.31.1/go.mod h1:ZHhMprtB3M6jd2mleG03lPGhHH0lk9u3PtfWS1cBhMA=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
<<<<<<< HEAD
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofiber/fiber/v2 v2.17.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg=
github.com/gofiber/fiber/v2 v2.18.0 h1:xCWYSVoTNibHpzfciPwUSZGiTyTpTXYchCwynuJU09s=
github.com/gofiber/fiber/v2 v2.18.0/go.mod h1:/LdZHMUXZvTTo7gU4+b1hclqCAdoQphNQ9bi9gutPyI=
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc=
github.com/gofiber/swagger v1.0.0/go.mod h1:QrYNF1Yrc7ggGK6ATsJ6yfH/8Zi5bu9lA7wB8TmCecg=
=======
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofiber/fiber/v2 v2.31.0/go.mod h1:1Ega6O199a3Y7yDGuM9FyXDPYQfv+7/y48wl6WCwUF4=
github.com/gofiber/fiber/v2 v2.44.0 h1:Z90bEvPcJM5GFJnu1py0E1ojoerkyew3iiNJ78MQCM8=
github.com/gofiber/fiber/v2 v2.44.0/go.mod h1:VTMtb/au8g01iqvHyaCzftuM/xmZgKOZCtFzz6CdV9w=
github.com/gofiber/fiber/v2 v2.49.2 h1:ONEN3/Vc+dUCxxDgZZwpqvhISgHqb+bu+isBiEyKEQs=
github.com/gofiber/fiber/v2 v2.49.2/go.mod h1:gNsKnyrmfEWFpJxQAV0qvW6l70K1dZGno12oLtukcts=
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
<<<<<<< HEAD
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
=======
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -63,8 +111,9 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4=
github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@@ -80,40 +129,62 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=
github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
<<<<<<< HEAD
github.com/jackc/pgtype v1.14.1 h1:LyDar7M2K0tShCWqzJ/ctzF1QC3Wzc9c8a6cHE0PFdc=
github.com/jackc/pgtype v1.14.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
=======
github.com/jackc/pgtype v1.14.2 h1:QBdZQTKpPdBlw2AdKwHEyqUcm/lrl2cwWAHjCMyln/o=
github.com/jackc/pgtype v1.14.2/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=
github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=
github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
<<<<<<< HEAD
github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E=
github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
=======
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -124,81 +195,139 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
<<<<<<< HEAD
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-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
=======
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
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-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
<<<<<<< HEAD
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
=======
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
<<<<<<< HEAD
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
=======
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/swag v1.7.1 h1:gY9ZakXlNWg/i/v5bQBic7VMZ4teq4m89lpiao74p/s=
github.com/swaggo/swag v1.7.1/go.mod h1:gAiHxNTb9cIpNmA/VEGUP+CyZMCP/EW7mdtc8Bny+p8=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/swag v1.8.1/go.mod h1:ugemnJsPZm/kRwFUnzBlbHRd0JY9zE1M4F+uy2pAaPQ=
github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto=
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.26.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA=
github.com/valyala/fasthttp v1.29.0 h1:F5GKpytwFk5OhCuRh6H+d4vZAcEeNAwPTdwQnm6IERY=
github.com/valyala/fasthttp v1.29.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0=
github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -206,35 +335,46 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
<<<<<<< HEAD
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
=======
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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=
<<<<<<< HEAD
=======
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -245,27 +385,44 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b h1:3Dq0eVHn0uaQJmPO+/aYPI/fRMqdrVDbu7MQcku54gg=
golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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=
<<<<<<< HEAD
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
=======
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
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/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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
<<<<<<< HEAD
=======
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@@ -273,32 +430,36 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
<<<<<<< HEAD
=======
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
>>>>>>> 99c47abe2ec3681425dbcdb775c844e4af95b28e
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@@ -10,33 +10,37 @@ import (
// AccountCreate writes a user to database
func (d Db) AccountCreate(input AccountCreateInput) (CreatedAccount, error) {
d.Log.Context = []interface{}{
"accountName", input.Name,
"id", input.ID,
}
accountSQL := "INSERT INTO accounts (id, name, \"apiKey\", password) VALUES($1,$2,$3,$4);"
_, err := d.DbPool.Exec(context.Background(), accountSQL, input.ID, input.Name, input.APIKey, input.Password)
if err != nil {
if strings.HasPrefix(err.Error(), "ERROR: duplicate key") {
d.Log.Debug("Duplicate name in accounts database", "name", input.Name)
d.Log.Debug("Duplicate name in accounts database")
} else {
d.Log.Error("Database error when trying to add account", "err", err.Error())
d.Log.Warn("Database error when trying to add account", "err", err.Error())
}
return CreatedAccount{}, err
}
d.Log.Info("Added account to database", "id", input.ID, "name", input.Name)
d.Log.Verbose("Added account to database", "id", input.ID)
accountFieldsSQL := "INSERT INTO \"accountsFields\" (id, \"accountId\", name, value) VALUES($1,$2,$3,$4);"
for _, field := range input.Fields {
newFieldID, uuidErr := uuid.NewRandom()
if uuidErr != nil {
d.Log.Error("Could not create new Uuid", "err", uuidErr.Error())
d.Log.Warn("Could not create new Uuid", "err", uuidErr.Error())
return CreatedAccount{}, uuidErr
}
_, err := d.DbPool.Exec(context.Background(), accountFieldsSQL, newFieldID, input.ID, field.Name, field.Values)
if err != nil {
//if strings.HasPrefix(err.Error(), "ERROR: duplicate key") {
d.Log.Error("Database error when trying to add account field", "err", err.Error(), "accountID", input.ID, "fieldName", field.Name, "fieldvalues", field.Values)
d.Log.Warn("Database error when trying to add account field", "err", err.Error(), "accountID", input.ID, "fieldName", field.Name, "fieldvalues", field.Values)
// }
}
@@ -51,29 +55,32 @@ func (d Db) AccountCreate(input AccountCreateInput) (CreatedAccount, error) {
}
func (d Db) AccountDel(accountID string) error {
d.Log.Info("Trying to delete account", "accountID", accountID)
d.Log.Context = []interface{}{
"accountID", accountID,
}
d.Log.Verbose("Trying to delete account")
_, renewalTokensErr := d.DbPool.Exec(context.Background(), "DELETE FROM \"renewalTokens\" WHERE \"accountId\" = $1;", accountID)
if renewalTokensErr != nil {
d.Log.Error("Could not remove renewal tokens for account", "err", renewalTokensErr.Error(), "accountID", accountID)
d.Log.Error("Could not remove renewal tokens for account", "err", renewalTokensErr.Error())
return renewalTokensErr
}
_, fieldsErr := d.DbPool.Exec(context.Background(), "DELETE FROM \"accountsFields\" WHERE \"accountId\" = $1;", accountID)
if fieldsErr != nil {
d.Log.Error("Could not remove account fields", "err", fieldsErr.Error(), "accountID", accountID)
d.Log.Error("Could not remove account fields", "err", fieldsErr.Error())
return fieldsErr
}
res, err := d.DbPool.Exec(context.Background(), "DELETE FROM accounts WHERE id = $1", accountID)
if err != nil {
d.Log.Error("Could not remove account", "err", err.Error(), "accountID", accountID)
d.Log.Error("Could not remove account", "err", err.Error())
return err
}
if string(res) == "DELETE 0" {
d.Log.Info("Tried to delete account, but none exists", "accountID", accountID)
err := errors.New("No account found for given accountID")
d.Log.Debug("Tried to delete account, but none exists")
err := errors.New("no account found for given accountID")
return err
}
@@ -81,8 +88,13 @@ func (d Db) AccountDel(accountID string) error {
}
// AccountGet fetches an account from the database
func (d Db) AccountGet(accountID string, APIKey string, Name string) (Account, error) {
d.Log.Debug("Trying to get account", "accountID", accountID, "len(APIKey)", len(APIKey))
func (d Db) AccountGet(accountID string, APIKey string, name string) (Account, error) {
d.Log.Context = []interface{}{
"accountID", accountID,
"len(APIKey)", len(APIKey),
"name", name,
}
d.Log.Debug("Trying to get account")
var account Account
var searchParam string
@@ -93,26 +105,30 @@ func (d Db) AccountGet(accountID string, APIKey string, Name string) (Account, e
} else if APIKey != "" {
accountSQL = accountSQL + "\"apiKey\" = $1"
searchParam = APIKey
} else if Name != "" {
} else if name != "" {
accountSQL = accountSQL + "name = $1"
searchParam = Name
searchParam = name
} else {
d.Log.Debug("No get criteria entered, returning empty response without calling the database")
return Account{}, errors.New("no rows in result set")
}
accountErr := d.DbPool.QueryRow(context.Background(), accountSQL, searchParam).Scan(&account.ID, &account.Created, &account.Name, &account.Password)
if accountErr != nil {
if accountErr.Error() == "no rows in result set" {
d.Log.Debug("No account found", "accountID", accountID, "APIKey", len(APIKey))
d.Log.Debug("No account found")
return Account{}, accountErr
}
d.Log.Error("Database error when fetching account", "err", accountErr.Error(), "accountID", accountID, "APIKey", len(APIKey))
d.Log.Error("Database error when fetching account", "err", accountErr.Error())
return Account{}, accountErr
}
fieldsSQL := "SELECT name, value FROM \"accountsFields\" WHERE \"accountId\" = $1"
rows, fieldsErr := d.DbPool.Query(context.Background(), fieldsSQL, account.ID)
if fieldsErr != nil {
d.Log.Error("Database error when fetching account fields", "err", accountErr.Error(), "accountID", accountID, "APIKey", len(APIKey))
d.Log.Error("Database error when fetching account fields", "err", accountErr.Error())
return Account{}, fieldsErr
}
@@ -122,7 +138,7 @@ func (d Db) AccountGet(accountID string, APIKey string, Name string) (Account, e
var value []string
err := rows.Scan(&name, &value)
if err != nil {
d.Log.Error("Could not get name or value from database row", "err", err.Error(), "accountID", accountID, "APIKey", len(APIKey))
d.Log.Error("Could not get name or value from database row", "err", err.Error())
return Account{}, err
}
account.Fields[name] = value
@@ -131,17 +147,76 @@ func (d Db) AccountGet(accountID string, APIKey string, Name string) (Account, e
return account, nil
}
// func (d Db) AccountsGet() ([]Account, error) {
// d.Log.Debug("Trying to get accounts", "name", name)
// var account Account
// var searchParam string
// accountSQL := "SELECT id, created, name, \"password\" FROM accounts WHERE "
// if accountID != "" {
// accountSQL = accountSQL + "id = $1"
// searchParam = accountID
// } else if APIKey != "" {
// accountSQL = accountSQL + "\"apiKey\" = $1"
// searchParam = APIKey
// } else if name != "" {
// accountSQL = accountSQL + "name = $1"
// searchParam = name
// } else {
// d.Log.Debug("No get criteria entered, returning empty response without calling the database")
// return Account{}, errors.New("no rows in result set")
// }
// accountErr := d.DbPool.QueryRow(context.Background(), accountSQL, searchParam).Scan(&account.ID, &account.Created, &account.Name, &account.Password)
// if accountErr != nil {
// if accountErr.Error() == "no rows in result set" {
// d.Log.Debug("No account found", "accountID", accountID, "APIKey", len(APIKey))
// return Account{}, accountErr
// }
// d.Log.Error("Database error when fetching account", "err", accountErr.Error(), "accountID", accountID, "APIKey", len(APIKey))
// return Account{}, accountErr
// }
// fieldsSQL := "SELECT name, value FROM \"accountsFields\" WHERE \"accountId\" = $1"
// rows, fieldsErr := d.DbPool.Query(context.Background(), fieldsSQL, account.ID)
// if fieldsErr != nil {
// d.Log.Error("Database error when fetching account fields", "err", accountErr.Error(), "accountID", accountID, "APIKey", len(APIKey))
// return Account{}, fieldsErr
// }
// account.Fields = make(map[string][]string)
// for rows.Next() {
// var name string
// var value []string
// err := rows.Scan(&name, &value)
// if err != nil {
// d.Log.Error("Could not get name or value from database row", "err", err.Error(), "accountID", accountID, "APIKey", len(APIKey))
// return Account{}, err
// }
// account.Fields[name] = value
// }
// return account, nil
// }
func (d Db) AccountUpdateFields(accountID string, fields []AccountCreateInputFields) (Account, error) {
d.Log.Context = []interface{}{
"accountID", accountID,
"fields", fields,
}
// Begin database transaction
conn, err := d.DbPool.Acquire(context.Background())
if err != nil {
d.Log.Error("Could not acquire database connection", "err", err.Error(), "accountID", accountID)
d.Log.Error("Could not acquire database connection", "err", err.Error())
return Account{}, err
}
tx, err := conn.Begin(context.Background())
if err != nil {
d.Log.Error("Could not begin database transaction", "err", err.Error(), "accountID", accountID)
d.Log.Error("Could not begin database transaction", "err", err.Error())
return Account{}, err
}
@@ -151,7 +226,7 @@ func (d Db) AccountUpdateFields(accountID string, fields []AccountCreateInputFie
_, err = tx.Exec(context.Background(), "DELETE FROM \"accountsFields\" WHERE \"accountId\" = $1;", accountID)
if err != nil {
d.Log.Error("Could not delete previous fields", "err", err.Error(), "accountID", accountID)
d.Log.Error("Could not delete previous fields", "err", err.Error())
return Account{}, err
}
@@ -165,7 +240,7 @@ func (d Db) AccountUpdateFields(accountID string, fields []AccountCreateInputFie
_, err = tx.Exec(context.Background(), accountFieldsSQL, newFieldID, accountID, field.Name, field.Values)
if err != nil {
d.Log.Error("Database error when trying to add account field", "err", err.Error(), "accountID", accountID, "fieldName", field.Name, "fieldvalues", field.Values)
d.Log.Error("Database error when trying to add account field", "err", err.Error(), "fieldName", field.Name, "fieldvalues", field.Values)
}
d.Log.Debug("Added account field", "accountID", accountID, "fieldName", field.Name, "fieldValues", field.Values)

View File

@@ -8,14 +8,18 @@ import (
// RenewalTokenCreate obtain a new renewal token
func (d Db) RenewalTokenCreate(accountID string) (string, error) {
d.Log.Debug("Creating new renewal token", "accountID", accountID)
d.Log.Context = []interface{}{
"accountID", accountID,
}
d.Log.Debug("Creating new renewal token")
newToken := utils.RandString(60)
insertSQL := "INSERT INTO \"renewalTokens\" (\"accountId\",token) VALUES($1,$2);"
_, insertErr := d.DbPool.Exec(context.Background(), insertSQL, accountID, newToken)
if insertErr != nil {
d.Log.Error("Could not insert into database table \"renewalTokens\"", "err", insertErr.Error(), "accountID", accountID)
d.Log.Error("Could not insert into database table \"renewalTokens\"", "err", insertErr.Error())
return "", insertErr
}

View File

@@ -3,9 +3,9 @@ package db
import (
"time"
"gitea.larvit.se/pwrpln/go_log"
"github.com/google/uuid"
"github.com/jackc/pgx/v4/pgxpool"
"go.uber.org/zap"
)
// Account is an account as represented in the database
@@ -42,5 +42,5 @@ type AccountCreateInput struct {
// Db struct
type Db struct {
DbPool *pgxpool.Pool
Log *zap.SugaredLogger
Log go_log.Log
}

View File

@@ -1,22 +1,13 @@
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import (
"bytes"
"encoding/json"
"strings"
import "github.com/swaggo/swag"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
)
var doc = `{
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{.Description}}",
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {
"name": "Power Plan",
@@ -31,7 +22,64 @@ var doc = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/account": {
"/accounts": {
"get": {
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get accounts",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/db.Account"
}
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
},
"415": {
"description": "Unsupported Media Type",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
}
}
},
"post": {
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -117,7 +165,7 @@ var doc = `{
}
}
},
"/account/:id": {
"/accounts/:id": {
"delete": {
"description": "Requires Authorization-header with role \"admin\" or a matching account id\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -201,7 +249,7 @@ var doc = `{
}
}
},
"/account/{id}": {
"/accounts/{id}": {
"get": {
"description": "Requires Authorization-header with either role \"admin\" or with a matching account id.\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -267,7 +315,7 @@ var doc = `{
}
}
},
"/account/{id}/fields": {
"/accounts/{id}/fields": {
"put": {
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -657,49 +705,20 @@ var doc = `{
}
}`
type swaggerInfo struct {
Version string
Host string
BasePath string
Schemes []string
Title string
Description string
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{
Version: "0.1",
Host: "",
BasePath: "/",
Schemes: []string{},
Title: "JWT Auth API",
Description: "This is a tiny http API for auth. Register accounts, auth with api-key or name/password, renew JWT tokens...",
}
type s struct{}
func (s *s) ReadDoc() string {
sInfo := SwaggerInfo
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}).Parse(doc)
if err != nil {
return doc
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, sInfo); err != nil {
return doc
}
return tpl.String()
var SwaggerInfo = &swag.Spec{
Version: "",
Host: "",
BasePath: "",
Schemes: []string{},
Title: "JWT Auth API",
Description: "This is a tiny http API for auth. Register accounts, auth with api-key or name/password, renew JWT tokens...",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(swag.Name, &s{})
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

View File

@@ -10,12 +10,67 @@
},
"license": {
"name": "MIT"
},
"version": "0.1"
}
},
"basePath": "/",
"paths": {
"/account": {
"/accounts": {
"get": {
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get accounts",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/db.Account"
}
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
},
"403": {
"description": "Forbidden",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
},
"415": {
"description": "Unsupported Media Type",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.ResJSONError"
}
}
}
}
},
"post": {
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -101,7 +156,7 @@
}
}
},
"/account/:id": {
"/accounts/:id": {
"delete": {
"description": "Requires Authorization-header with role \"admin\" or a matching account id\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -185,7 +240,7 @@
}
}
},
"/account/{id}": {
"/accounts/{id}": {
"get": {
"description": "Requires Authorization-header with either role \"admin\" or with a matching account id.\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [
@@ -251,7 +306,7 @@
}
}
},
"/account/{id}/fields": {
"/accounts/{id}/fields": {
"put": {
"description": "Requires Authorization-header with role \"admin\".\nExample: Authorization: bearer xxx\nWhere \"xxx\" is a valid JWT token",
"consumes": [

View File

@@ -1,4 +1,3 @@
basePath: /
definitions:
db.Account:
properties:
@@ -75,9 +74,49 @@ info:
license:
name: MIT
title: JWT Auth API
version: "0.1"
paths:
/account:
/accounts:
get:
consumes:
- application/json
description: |-
Requires Authorization-header with role "admin".
Example: Authorization: bearer xxx
Where "xxx" is a valid JWT token
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/db.Account'
type: array
"401":
description: Unauthorized
schema:
items:
$ref: '#/definitions/handlers.ResJSONError'
type: array
"403":
description: Forbidden
schema:
items:
$ref: '#/definitions/handlers.ResJSONError'
type: array
"415":
description: Unsupported Media Type
schema:
items:
$ref: '#/definitions/handlers.ResJSONError'
type: array
"500":
description: Internal Server Error
schema:
items:
$ref: '#/definitions/handlers.ResJSONError'
type: array
summary: Get accounts
post:
consumes:
- application/json
@@ -137,7 +176,7 @@ paths:
$ref: '#/definitions/handlers.ResJSONError'
type: array
summary: Create an account
/account/:id:
/accounts/:id:
delete:
consumes:
- application/json
@@ -196,7 +235,7 @@ paths:
$ref: '#/definitions/handlers.ResJSONError'
type: array
summary: Delete an account
/account/{id}:
/accounts/{id}:
get:
consumes:
- application/json
@@ -243,7 +282,7 @@ paths:
$ref: '#/definitions/handlers.ResJSONError'
type: array
summary: Get account by id
/account/{id}/fields:
/accounts/{id}/fields:
put:
consumes:
- application/json

View File

@@ -10,6 +10,7 @@ import (
// @Description Requires Authorization-header with role "admin" or a matching account id
// @Description Example: Authorization: bearer xxx
// @Description Where "xxx" is a valid JWT token
// @Param Authorization header string true "Insert your access token"
// @ID account-del
// @Accept json
// @Produce json
@@ -21,7 +22,7 @@ import (
// @Failure 404 {object} []ResJSONError
// @Failure 415 {object} []ResJSONError
// @Failure 500 {object} []ResJSONError
// @Router /account/:id [delete]
// @Router /accounts/:id [delete]
func (h Handlers) AccountDel(c *fiber.Ctx) error {
accountID := c.Params("accountID")
@@ -37,9 +38,10 @@ func (h Handlers) AccountDel(c *fiber.Ctx) error {
err := h.Db.AccountDel(accountID)
if err != nil {
if err.Error() == "No account found for given accountID" {
if err.Error() == "no account found for given accountID" {
return c.Status(404).JSON([]ResJSONError{{Error: err.Error()}})
} else {
h.Log.Error("Database error when trying to remove account", "err", err.Error())
return c.Status(500).JSON([]ResJSONError{{Error: "Database error when trying to remove account"}})
}
}

View File

@@ -10,6 +10,7 @@ import (
// @Description Requires Authorization-header with either role "admin" or with a matching account id.
// @Description Example: Authorization: bearer xxx
// @Description Where "xxx" is a valid JWT token
// @Param Authorization header string true "Insert your access token"
// @ID get-account-by-id
// @Accept json
// @Produce json
@@ -19,7 +20,7 @@ import (
// @Failure 403 {object} []ResJSONError
// @Failure 415 {object} []ResJSONError
// @Failure 500 {object} []ResJSONError
// @Router /account/{id} [get]
// @Router /accounts/{id} [get]
func (h Handlers) AccountGet(c *fiber.Ctx) error {
accountID := c.Params("accountID")
@@ -44,3 +45,37 @@ func (h Handlers) AccountGet(c *fiber.Ctx) error {
return c.JSON(account)
}
// AccountGet godoc
// @Summary Get accounts
// @Description Requires Authorization-header with role "admin".
// @Description Example: Authorization: bearer xxx
// @Description Where "xxx" is a valid JWT token
// @Param Authorization header string true "Insert your access token"
// @Accept json
// @Produce json
// @Success 200 {object} []db.Account
// @Failure 401 {object} []ResJSONError
// @Failure 403 {object} []ResJSONError
// @Failure 415 {object} []ResJSONError
// @Failure 500 {object} []ResJSONError
// @Router /accounts [get]
func (h Handlers) AccountsGet(c *fiber.Ctx) error {
accountID := c.Params("accountID")
authErr := h.RequireAdminRole(c)
if authErr != nil {
return c.Status(403).JSON([]ResJSONError{{Error: authErr.Error()}})
}
account, accountErr := h.Db.AccountGet(accountID, "", "")
if accountErr != nil {
if accountErr.Error() == "no rows in result set" {
return c.Status(404).JSON([]ResJSONError{{Error: "No account found for given accountID"}})
} else {
return c.Status(500).JSON([]ResJSONError{{Error: accountErr.Error()}})
}
}
return c.JSON(account)
}

View File

@@ -56,7 +56,7 @@ func (h Handlers) parseJWT(JWT string) (Claims, error) {
return Claims{}, err
}
if !token.Valid {
err := errors.New("Invalid token")
err := errors.New("invalid token")
return Claims{}, err
}
@@ -89,7 +89,7 @@ func (h Handlers) RequireAdminRole(c *fiber.Ctx) error {
headers := h.parseHeaders(c)
if headers["Authorization"] == "" {
return errors.New("Authorization header is missing")
return errors.New("authorization header is missing")
}
claims, claimsErr := h.parseJWT(headers["Authorization"])
@@ -98,11 +98,11 @@ func (h Handlers) RequireAdminRole(c *fiber.Ctx) error {
}
if claims.AccountFields == nil {
return errors.New("Account have no fields at all")
return errors.New("account have no fields at all")
}
if claims.AccountFields["role"] == nil {
return errors.New("Account have no field named \"role\"")
return errors.New("account have no field named \"role\"")
}
for _, role := range claims.AccountFields["role"] {
@@ -111,7 +111,7 @@ func (h Handlers) RequireAdminRole(c *fiber.Ctx) error {
}
}
return errors.New("No \"admin\" role found on account")
return errors.New("no \"admin\" role found on account")
}
// RequireAdminRoleOrAccountID returns nil if no error is found
@@ -119,7 +119,7 @@ func (h Handlers) RequireAdminRoleOrAccountID(c *fiber.Ctx, accountID string) er
headers := h.parseHeaders(c)
if headers["Authorization"] == "" {
return errors.New("Authorization header is missing")
return errors.New("authorization header is missing")
}
claims, claimsErr := h.parseJWT(headers["Authorization"])

View File

@@ -25,6 +25,7 @@ type AuthInput struct {
// @Description Requires Authorization-header with role "admin".
// @Description Example: Authorization: bearer xxx
// @Description Where "xxx" is a valid JWT token
// @Param Authorization header string true "Insert your access token"
// @ID account-create
// @Accept json
// @Produce json
@@ -36,7 +37,7 @@ type AuthInput struct {
// @Failure 409 {object} []ResJSONError
// @Failure 415 {object} []ResJSONError
// @Failure 500 {object} []ResJSONError
// @Router /account [post]
// @Router /accounts [post]
func (h Handlers) AccountCreate(c *fiber.Ctx) error {
authErr := h.RequireAdminRole(c)
if authErr != nil {
@@ -141,14 +142,16 @@ func (h Handlers) AccountAuthPassword(c *fiber.Ctx) error {
resolvedAccount, err := h.Db.AccountGet("", "", authInput.Name)
if err != nil {
if err.Error() == "No account found" {
if err.Error() == "no rows in result set" {
return c.Status(403).JSON([]ResJSONError{{Error: "Invalid name or password"}})
} else {
h.Log.Error("unknown error when resolving account", "err", err.Error())
}
return c.Status(500).JSON([]ResJSONError{{Error: err.Error()}})
}
if utils.CheckPasswordHash(authInput.Password, resolvedAccount.Password) == false {
if !utils.CheckPasswordHash(authInput.Password, resolvedAccount.Password) {
return c.Status(403).JSON([]ResJSONError{{Error: "Invalid name or password"}})
}

View File

@@ -11,6 +11,7 @@ import (
// @Description Requires Authorization-header with role "admin".
// @Description Example: Authorization: bearer xxx
// @Description Where "xxx" is a valid JWT token
// @Param Authorization header string true "Insert your access token"
// @ID account-update-fields
// @Accept json
// @Produce json
@@ -21,17 +22,23 @@ import (
// @Failure 403 {object} []ResJSONError
// @Failure 415 {object} []ResJSONError
// @Failure 500 {object} []ResJSONError
// @Router /account/{id}/fields [put]
// @Router /accounts/{id}/fields [put]
func (h Handlers) AccountUpdateFields(c *fiber.Ctx) error {
accountID := c.Params("accountID")
h.Log.Context = []interface{}{
"accountID", accountID,
}
_, uuidErr := uuid.Parse(accountID)
if uuidErr != nil {
h.Log.Debug("client supplied invalid uuid format")
return c.Status(400).JSON([]ResJSONError{{Error: "Invalid uuid format"}})
}
authErr := h.RequireAdminRole(c)
if authErr != nil {
h.Log.Debug("client does not have admin role")
return c.Status(403).JSON([]ResJSONError{{Error: authErr.Error()}})
}

View File

@@ -2,8 +2,8 @@ package handlers
import (
"gitea.larvit.se/pwrpln/auth-api/src/db"
"gitea.larvit.se/pwrpln/go_log"
jwt "github.com/dgrijalva/jwt-go"
"go.uber.org/zap"
)
// Claims is the JWT struct
@@ -18,7 +18,7 @@ type Claims struct {
type Handlers struct {
Db db.Db
JwtKey []byte
Log *zap.SugaredLogger
Log go_log.Log
}
// ResJSONError is an error field that is used in JSON error responses

View File

@@ -8,40 +8,39 @@ import (
"gitea.larvit.se/pwrpln/auth-api/src/db"
h "gitea.larvit.se/pwrpln/auth-api/src/handlers"
"gitea.larvit.se/pwrpln/auth-api/src/utils"
swagger "github.com/arsmn/fiber-swagger/v2"
"gitea.larvit.se/pwrpln/go_log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/swagger"
"github.com/google/uuid"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/joho/godotenv"
"go.uber.org/zap"
// docs are generated by Swag CLI, you have to import them.
_ "gitea.larvit.se/pwrpln/auth-api/src/docs"
"gitea.larvit.se/pwrpln/auth-api/src/docs"
)
// Don't put in utils, because it creates import cycle with db... just left it here for now
func createAdminAccount(Db db.Db, log *zap.SugaredLogger) {
func createAdminAccount(Db db.Db, log go_log.Log, ADMIN_API_KEY string) {
adminAccountID, uuidErr := uuid.NewRandom()
if uuidErr != nil {
log.Fatal("Could not create new Uuid", "err", uuidErr.Error())
log.Error("Could not create new Uuid", "err", uuidErr.Error())
os.Exit(1)
}
_, adminAccountErr := Db.AccountCreate(db.AccountCreateInput{
ID: adminAccountID,
Name: "admin",
APIKey: os.Getenv("ADMIN_API_KEY"),
APIKey: ADMIN_API_KEY,
Password: "",
Fields: []db.AccountCreateInputFields{{Name: "role", Values: []string{"admin"}}},
})
if adminAccountErr != nil && strings.HasPrefix(adminAccountErr.Error(), "ERROR: duplicate key") {
log.Info("Admin account already created, nothing written to database")
log.Verbose("Admin account already created, nothing written to database")
} else if adminAccountErr != nil {
log.Fatal("Could not create admin account", "err", adminAccountErr.Error())
log.Error("Could not create admin account", "err", adminAccountErr.Error())
os.Exit(1)
}
}
// @title JWT Auth API
// @version 0.1
// @description This is a tiny http API for auth. Register accounts, auth with api-key or name/password, renew JWT tokens...
// @contact.name Power Plan
@@ -49,31 +48,52 @@ func createAdminAccount(Db db.Db, log *zap.SugaredLogger) {
// @contact.email lilleman@larvit.se
// @license.name MIT
// @BasePath /
func main() {
log := utils.GetLog()
err := godotenv.Load()
if err != nil {
log.Warn("Error loading .env file, this could be ok if the env file does not exist", "err", err.Error())
}
log := go_log.GetLog()
if os.Getenv("JWT_SHARED_SECRET") == "changeMe" {
log.Error("JWT_SHARED_SECRET ENV is not set, using very insecure \"changeMe\"")
log.Warn("JWT_SHARED_SECRET ENV is not set, using very insecure \"changeMe\"")
}
if os.Getenv("ADMIN_API_KEY") == "changeMe" {
log.Error("ADMIN_API_KEY ENV is not set, using very insecure \"changeMe\"")
log.Warn("ADMIN_API_KEY ENV is not set, using very insecure \"changeMe\"")
}
if os.Getenv("LOG_MIN_LVL") == "" {
log.Info("LOG_MIN_LVL ENV is not set, using default \"Info\"")
log.MinLogLvl = go_log.LogLvlFromStr("Info")
} else {
minLogLvl := go_log.LogLvlFromStr(os.Getenv("LOG_MIN_LVL"))
if minLogLvl == 0 {
log.Warn("Invalid LOG_MIN_LVL ENV, using default \"Info\"")
log.MinLogLvl = go_log.LogLvlFromStr("Info")
} else {
log.MinLogLvl = minLogLvl
}
}
ADMIN_API_KEY := os.Getenv("ADMIN_API_KEY")
DATABASE_URL := os.Getenv("DATABASE_URL")
PUBLIC_HOST := os.Getenv("PUBLIC_HOST")
URL_PREFIX := os.Getenv("URL_PREFIX")
WEB_BIND_HOST := os.Getenv("WEB_BIND_HOST")
if URL_PREFIX == "" {
URL_PREFIX = "/"
}
docs.SwaggerInfo.Version = "0.3.9"
docs.SwaggerInfo.Host = PUBLIC_HOST
docs.SwaggerInfo.BasePath = URL_PREFIX
jwtKey := []byte(os.Getenv("JWT_SHARED_SECRET"))
dbPool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
dbPool, err := pgxpool.Connect(context.Background(), DATABASE_URL)
for err != nil {
log.Error("Failed to open connection to PostgreSQL database, retrying in 1 second", "err", err.Error())
log.Warn("Failed to open connection to PostgreSQL database, retrying in 1 second", "err", err.Error())
time.Sleep(1 * time.Second)
dbPool, err = pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))
dbPool, err = pgxpool.Connect(context.Background(), DATABASE_URL)
}
log.Info("Connected to PostgreSQL database")
log.Verbose("Connected to PostgreSQL database")
defer dbPool.Close()
app := fiber.New()
@@ -81,7 +101,7 @@ func main() {
Db := db.Db{DbPool: dbPool, Log: log}
handlers := h.Handlers{Db: Db, JwtKey: jwtKey, Log: log}
createAdminAccount(Db, log)
createAdminAccount(Db, log, ADMIN_API_KEY)
// Log all requests
app.Use(handlers.LogReq)
@@ -89,28 +109,27 @@ func main() {
// Always require application/json
app.Use(handlers.RequireJSON)
app.Get("/", func(c *fiber.Ctx) error { return c.Redirect("/swagger/index.html") })
app.Get("/swagger", func(c *fiber.Ctx) error { return c.Redirect("/swagger/index.html") })
app.Get("/swagger/*", swagger.Handler)
app.Get(URL_PREFIX, func(c *fiber.Ctx) error { return c.Redirect(URL_PREFIX + "swagger/index.html") })
app.Get(URL_PREFIX+"swagger", func(c *fiber.Ctx) error { return c.Redirect(URL_PREFIX + "swagger/index.html") })
app.Get(URL_PREFIX+"swagger/*", swagger.HandlerDefault)
app.Delete("/account/:accountID", handlers.AccountDel)
app.Get("/account/:accountID", handlers.AccountGet)
app.Post("/account", handlers.AccountCreate)
app.Post("/auth/api-key", handlers.AccountAuthAPIKey)
app.Post("/auth/password", handlers.AccountAuthPassword)
app.Post("/renew-token", handlers.RenewToken)
app.Put("/account/:accountID/fields", handlers.AccountUpdateFields)
// app.Put("")
app.Delete(URL_PREFIX+"accounts/:accountID", handlers.AccountDel)
app.Get(URL_PREFIX+"accounts/:accountID", handlers.AccountGet)
app.Post(URL_PREFIX+"accounts", handlers.AccountCreate)
// app.Get(URL_PREFIX+"accounts", handlers.AccountsGet)
app.Post(URL_PREFIX+"auth/api-key", handlers.AccountAuthAPIKey)
app.Post(URL_PREFIX+"auth/password", handlers.AccountAuthPassword)
app.Post(URL_PREFIX+"renew-token", handlers.RenewToken)
app.Put(URL_PREFIX+"accounts/:accountID/fields", handlers.AccountUpdateFields)
log.Info("Trying to start web server", "WEB_BIND_HOST", os.Getenv("WEB_BIND_HOST"))
log.Info("Starting web server", "WEB_BIND_HOST", WEB_BIND_HOST)
webBindHost := os.Getenv("WEB_BIND_HOST")
err = app.Listen(webBindHost)
err = app.Listen(WEB_BIND_HOST)
for err != nil {
log.Error("Could not start web server", "err", err.Error(), "WEB_BIND_HOST", webBindHost)
log.Warn("Could not start web server", "err", err.Error(), "WEB_BIND_HOST", WEB_BIND_HOST)
time.Sleep(1 * time.Second)
err = app.Listen(webBindHost)
err = app.Listen(WEB_BIND_HOST)
}
log.Info("Webb server closed, shutting down")
log.Info("Web server closed, shutting down")
}

View File

@@ -1,13 +1,10 @@
package utils
import (
"log"
"math/rand"
"strings"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"golang.org/x/crypto/bcrypt"
)
@@ -51,31 +48,3 @@ func RandString(n int) string {
return sb.String()
}
func SyslogTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
loc, _ := time.LoadLocation("UTC")
t = t.In(loc)
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
func GetLog() *zap.SugaredLogger {
cfg := zap.NewProductionConfig()
cfg.Development = true
cfg.DisableCaller = false
cfg.DisableStacktrace = false
cfg.Encoding = "console" // "console" or "json"
cfg.EncoderConfig.EncodeTime = SyslogTimeEncoder
cfg.OutputPaths = []string{"stdout"}
cfg.ErrorOutputPaths = []string{"stderr"}
cfg.Level.SetLevel(zap.DebugLevel)
logger, err := cfg.Build()
if err != nil {
log.Panicf("Could not build logger, err: %v", err)
}
defer logger.Sync() // Flushes buffer, if any
log := logger.Sugar()
return log
}

View File

@@ -1,3 +1,3 @@
ADMIN_API_KEY=hihi
AUTH_URL=http://localhost:4000
AUTH_URL=http://127.0.0.1:4000
JWT_SHARED_SECRET=hihi

View File

@@ -1,4 +1,4 @@
FROM node:16.8.0-slim
FROM node:18.16.0-alpine3.17
WORKDIR /srv

1767
tests/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,11 +10,11 @@
"author": "Lilleman",
"license": "ISC",
"dependencies": {
"dotenv": "10.0.0",
"got": "11.8.2",
"jsonwebtoken": "8.5.1",
"dotenv": "16.0.3",
"got": "12.6.0",
"jsonwebtoken": "9.0.0",
"tap-spec": "5.0.0",
"tape": "5.3.1",
"tape-es": "1.2.15"
"tape": "5.6.3",
"tape-es": "1.2.17"
}
}

View File

@@ -6,7 +6,7 @@ import setConfig from '../test-helpers/config.js';
test('test-cases/00start.js: Wait for auth API to be ready', async t => {
setConfig({ printConfig: true });
const backendHealthCheck = await got(process.env.AUTH_URL, { retry: 2000 });
const backendHealthCheck = await got(process.env.AUTH_URL, { retry: { limit: 2000 }});
t.equal(backendHealthCheck.statusCode, 200, 'Auth API should answer with status code 200');
});

View File

@@ -39,13 +39,13 @@ test('test-cases/01basic.js: Authing with configurated API KEY', async t => {
test('test-cases/01basic.js: GETting the admin account, with the token we just obtained', async t => {
try {
await got(`${process.env.AUTH_URL}/account/${adminJWT.accountId}`);
t.fail('Calling /account/{id} without proper auth token should give 403');
await got(`${process.env.AUTH_URL}/accounts/${adminJWT.accountId}`);
t.fail('Calling /accounts/{id} without proper auth token should give 403');
} catch (err) {
t.equal(err.message, 'Response code 403 (Forbidden)', 'Calling /account/{id} without proper auth token should give 403');
t.equal(err.message, 'Response code 403 (Forbidden)', 'Calling /accounts/{id} without proper auth token should give 403');
}
const accountRes = await got(`${process.env.AUTH_URL}/account/${adminJWT.accountId}`, {
const accountRes = await got(`${process.env.AUTH_URL}/accounts/${adminJWT.accountId}`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
responseType: 'json',
});
@@ -54,7 +54,7 @@ test('test-cases/01basic.js: GETting the admin account, with the token we just o
});
test('test-cases/01basic.js: Creating a new account', async t => {
const res = await got.post(`${process.env.AUTH_URL}/account`, {
const res = await got.post(`${process.env.AUTH_URL}/accounts`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
json: {
fields: [
@@ -79,7 +79,7 @@ test('test-cases/01basic.js: Creating a new account', async t => {
t.notEqual(user.apiKey, undefined, 'The new account should have an apiKey');
try {
await got.post(`${process.env.AUTH_URL}/account`, {
await got.post(`${process.env.AUTH_URL}/accounts`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
json: {
fields: [{name: 'role',values: ['user'],}],
@@ -110,8 +110,53 @@ test('test-cases/01basic.js: Auth by username and password', async t => {
t.equal(userJWT.accountName, userName, 'The verified account name should match the created user');
});
test('test-cases/01basic.js: PUT /account/{id}/fields', async t => {
const res = await got.put(`${process.env.AUTH_URL}/account/${user.id}/fields`, {
test('test-cases/01basic.js: Auth by username and wrong password', async t => {
try {
await got.post(`${process.env.AUTH_URL}/auth/password`, {
json: {
name: userName,
password: 'isWrong',
},
responseType: 'json',
});
t.fail('Trying to login with wrong password should fail with a 403');
} catch(err) {
t.equal(err.message, 'Response code 403 (Forbidden)', 'Trying to login with wrong password should fail with a 403');
}
});
test('test-cases/01basic.js: Auth by wrong username', async t => {
try {
await got.post(`${process.env.AUTH_URL}/auth/password`, {
json: {
name: 'lapptomte',
password: 'isWrong',
},
responseType: 'json',
});
t.fail('Trying to login with wrong username should fail with a 403');
} catch(err) {
t.equal(err.message, 'Response code 403 (Forbidden)', 'Trying to login with wrong username should fail with a 403');
}
});
test('test-cases/01basic.js: Auth by empty username and empty password', async t => {
try {
await got.post(`${process.env.AUTH_URL}/auth/password`, {
json: {
name: '',
password: '',
},
responseType: 'json',
});
t.fail('Trying to login with wrong username should fail with a 403');
} catch(err) {
t.equal(err.message, 'Response code 403 (Forbidden)', 'Trying to login with wrong username should fail with a 403');
}
});
test('test-cases/01basic.js: PUT /accounts/{id}/fields', async t => {
const res = await got.put(`${process.env.AUTH_URL}/accounts/${user.id}/fields`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
json: [
{
@@ -139,29 +184,29 @@ test('test-cases/01basic.js: PUT /account/{id}/fields', async t => {
test('test-cases/01basic.js: Remove an account', async t => {
try {
// Random uuid that should not exist in the db. The chance of this existing is... small
await got.delete(`${process.env.AUTH_URL}/account/a423e690-74b9-4f37-9976-f5bf75a5ea32`, {
await got.delete(`${process.env.AUTH_URL}/accounts/a423e690-74b9-4f37-9976-f5bf75a5ea32`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
responseType: 'json',
retry: 0,
retry: { limit: 0 },
});
t.fail('Response status for DELETing an account that does not exist should be 404');
} catch (err) {
t.equal(err.message, 'Response code 404 (Not Found)', 'Response status for DELETing an account that does not exist should be 404');
}
const delRes = await got.delete(`${process.env.AUTH_URL}/account/${user.id}`, {
const delRes = await got.delete(`${process.env.AUTH_URL}/accounts/${user.id}`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
responseType: 'json',
retry: 0,
retry: { limit: 0 },
});
t.equal(delRes.statusCode, 204, 'Response status for DELETE should be 204');
try {
await got(`${process.env.AUTH_URL}/account/${user.id}`, {
await got(`${process.env.AUTH_URL}/accounts/${user.id}`, {
headers: { 'Authorization': `bearer ${adminJWTString}`},
responseType: 'json',
retry: 0,
retry: { limit: 0 },
});
t.fail('Response status for GETing the account again should be 404');
} catch (err) {