Compare commits
44 Commits
0791dcaa44
...
6e519ee25a
Author | SHA1 | Date |
---|---|---|
renovate | 6e519ee25a | |
kolaente | 3d9fcb9ffb | |
kolaente | a6e214b654 | |
renovate | c47e07f9b0 | |
renovate | 3a4a04ee8e | |
kolaente | 96b5e93379 | |
kolaente | 87c2e442f2 | |
kolaente | 33e27c66a0 | |
kolaente | 986129a784 | |
kolaente | 3d7605591e | |
kolaente | 811514855b | |
kolaente | a9e6776abf | |
renovate | 15828df041 | |
kolaente | f9b48ec091 | |
kolaente | 3b0b4a8460 | |
kolaente | 2ef5e54588 | |
renovate | 65bca226e0 | |
renovate | 63a9148132 | |
renovate | b880d0e300 | |
renovate | 2c46fc25d4 | |
konrad | 641a9da93d | |
kolaente | 4d4dca12ef | |
kolaente | 622f2f0562 | |
kolaente | c495096444 | |
renovate | 649d1e3e6f | |
yverry | c83cb8480d | |
kolaente | 556abcd9d2 | |
kolaente | b10dbce1a1 | |
renovate | 7b77974b03 | |
renovate | 05358350af | |
kolaente | 9fc08a0790 | |
renovate | f6b897e8e7 | |
renovate | 8de78c48f8 | |
renovate | a13126d1dd | |
renovate | b96e681270 | |
kolaente | f5fd849a0b | |
renovate | 144e115394 | |
renovate | 815fc10135 | |
renovate | 955a1771ae | |
renovate | aca930655b | |
renovate | 2ba78d240f | |
renovate | cad18945bb | |
renovate | 9fc0fc184d | |
renovate | 1577c8d3f3 |
|
@ -1,4 +1,7 @@
|
|||
files/
|
||||
dist/
|
||||
logs/
|
||||
|
||||
Dockerfile
|
||||
docker-manifest.tmpl
|
||||
docker-manifest-unstable.tmpl
|
||||
|
|
198
.drone.yml
198
.drone.yml
|
@ -627,102 +627,11 @@ steps:
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-arm-release
|
||||
name: docker-release
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- "refs/tags/**"
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
commands:
|
||||
- git fetch --tags
|
||||
|
||||
- name: docker-arm-unstable
|
||||
image: plugins/docker:linux-arm
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
tags: unstable-linux-arm
|
||||
dockerfile: Dockerfile.arm32
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
|
||||
- name: docker-arm
|
||||
image: plugins/docker:linux-arm
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm
|
||||
dockerfile: Dockerfile.arm32
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
||||
- name: docker-arm64-unstable
|
||||
image: plugins/docker:linux-arm64
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
tags: unstable-linux-arm64
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
|
||||
- name: docker-arm64
|
||||
image: plugins/docker:linux-arm64
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm64
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-amd64-release
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
|
@ -735,7 +644,8 @@ steps:
|
|||
- git fetch --tags
|
||||
|
||||
- name: docker-unstable
|
||||
image: plugins/docker:linux-amd64
|
||||
image: thegeeklab/drone-docker-buildx
|
||||
privileged: true
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
|
@ -743,86 +653,36 @@ steps:
|
|||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
tags: unstable-linux-amd64
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
|
||||
- name: docker
|
||||
image: plugins/docker:linux-amd64
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-amd64
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-manifest
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
- "refs/tags/**"
|
||||
|
||||
depends_on:
|
||||
- docker-amd64-release
|
||||
- docker-arm-release
|
||||
|
||||
steps:
|
||||
- name: manifest-unstable
|
||||
pull: always
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
tags: unstable
|
||||
ignore_missing: true
|
||||
spec: docker-manifest-unstable.tmpl
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
platforms:
|
||||
- linux/386
|
||||
- linux/amd64
|
||||
- linux/arm/v6
|
||||
- linux/arm/v7
|
||||
- linux/arm64/v8
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- refs/heads/main
|
||||
|
||||
- name: manifest-release
|
||||
pull: always
|
||||
image: plugins/manifest
|
||||
- name: docker-release
|
||||
image: thegeeklab/drone-docker-buildx
|
||||
privileged: true
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: docker-manifest.tmpl
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
||||
- name: manifest-release-latest
|
||||
pull: always
|
||||
image: plugins/manifest
|
||||
depends_on:
|
||||
- clone
|
||||
settings:
|
||||
tags: latest
|
||||
ignore_missing: true
|
||||
spec: docker-manifest.tmpl
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
platforms:
|
||||
- linux/386
|
||||
- linux/amd64
|
||||
- linux/arm/v6
|
||||
- linux/arm/v7
|
||||
- linux/arm64/v8
|
||||
depends_on: [ fetch-tags ]
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
@ -841,9 +701,7 @@ depends_on:
|
|||
- testing
|
||||
- release
|
||||
- deploy-docs
|
||||
- docker-arm-release
|
||||
- docker-amd64-release
|
||||
- docker-manifest
|
||||
- docker-release
|
||||
|
||||
steps:
|
||||
- name: notify
|
||||
|
@ -861,6 +719,6 @@ steps:
|
|||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: 5500acb776acae4975592637767035c45af277f1f56c8d10ffd99f38761cd5c8
|
||||
hmac: 768d54fc8433705fb63f754adf19536a16152a4046a040afdbcbb7df60614056
|
||||
|
||||
...
|
||||
|
|
142
CHANGELOG.md
142
CHANGELOG.md
|
@ -7,6 +7,148 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
|
||||
All releases can be found on https://code.vikunja.io/api/releases.
|
||||
|
||||
## [0.20.1] - 2022-11-11
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(docs)* Add explanation on how to run the cli in docker
|
||||
* *(filter)* Also check for 0 values if the filter should include nulls
|
||||
* *(filter)* Only check for 0 values in filter fields with numeric values
|
||||
* *(filters)* Try to parse date filter fields of the provided dates are not valid iso dates
|
||||
* *(filters)* Try parsing dates without time
|
||||
* *(filters)* Try parsing invalid dates like 2022-11-1
|
||||
* *(metrics)* Make currently active users actually work
|
||||
* *(task)* Duplicate reminders when adding different ones between winter / summer time
|
||||
* *(tasks)* Allow sorting by task index* Make sure task indexes are calculated correctly when moving tasks between lists ([c495096](c4950964443a9bffc4cdd8fc25004ad951520f20))
|
||||
* Look for the default bucket based on the position instead of the index ([622f2f0](622f2f0562bd8e3a5c97ec0b001c646a33a86c2b))
|
||||
* Usage with postgres over unix socket (#1308) ([641a9da](641a9da93d24a18d6cbad2929eea1be6c1e0d0b2))
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update module github.com/prometheus/client_golang to v1.13.1 (#1307)
|
||||
* *(deps)* Update module github.com/spf13/viper to v1.14.0 (#1309)
|
||||
* *(deps)* Update module golang.org/x/sys to v0.2.0 (#1311)
|
||||
* *(deps)* Update module golang.org/x/term to v0.2.0 (#1312)
|
||||
* *(deps)* Update module github.com/prometheus/client_golang to v1.14.0 (#1313)
|
||||
* *(deps)* Update module github.com/getsentry/sentry-go to v0.15.0 (#1314)
|
||||
|
||||
### Features
|
||||
|
||||
* *(docs)* Add relase checklist
|
||||
|
||||
### Other
|
||||
|
||||
* *(other)* Nessecary is a common misspelling of necessary (#1304)
|
||||
|
||||
## [0.20.0] - 2022-10-28
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(caldav)* Make sure duration and due date follow rfc5545
|
||||
* *(caldav)* No failed login emails for tokens (#1252)
|
||||
* *(ci)* Make sure release zip files have a .zip ending
|
||||
* *(ci)* Make sure release os packages are properly named
|
||||
* *(docs)* Clarify using port 25 as mail port when mail does not work
|
||||
* *(docs)* Document pnpm instead of yarn
|
||||
* *(docs)* Fix redirect_url example (#50)
|
||||
* *(lists)* Return correct max right for lists where the user has created the namespace
|
||||
* *(mail)* Pass mail server timeout (#1253)
|
||||
* *(migration)* Properly parse duration
|
||||
* *(migration)* Expose ticktick migrator to /info
|
||||
* *(migration)* Make sure importing works when the csv file has errors and don't try to parse empty values as dates
|
||||
* *(namespaces)* Add list subscriptions (#1254)
|
||||
* *(todoist)* Properly import all done tasks* Properly log extra message ([c194797](c19479757a20d72484b4e071b45055746ff2b67e))
|
||||
* Don't try to compress riscv64 binaries in releases ([d8f387f](d8f387f7967ffb94035de2fcfc4578247ae1023e))
|
||||
* Preserve dates for repeating tasks (#47) ([090c671](090c67138a16258480b866b05c6fdc2e02d12c89))
|
||||
* Tasks with the same assignee as doer should not appear twice in overdue task mails ([45defeb](45defebcf435cade4b72763236e1e2dfdac770cc))
|
||||
* Don't allow setting a list namespace to 0 ([96ed1e3](96ed1e33e38beec1bb1ab0813074b035dd02fade))
|
||||
* Make sure pseudo namespaces and lists always have the current user as owner ([878d19b](878d19beb81869392e33a8ffc1ec247d1cf1e4d6))
|
||||
* Use connection string for postgres ([fcb205a](fcb205a842a4e828e6e933339b23f5aa8b297125))
|
||||
* Make sure user searches are always case-insensitive ([c076f73](c076f73a87bc9b39b17389e25d0186ab71aa24bf))
|
||||
* Make cover image id actually updatable ([0e1904d](0e1904d50b8576a2e9ea5812314aa3c8f304edb5))
|
||||
* Make cover image id actually updatable ([0eb4709](0eb47096db02ceb5032c7439b3b901fbadd0d1bb))
|
||||
* Make sure a user can only be assigned once to a task ([008908e](008908eb49eeb50a554c416422feb3b465efa165))
|
||||
* Make sure list subscriptions are set correctly when their namespace has a subscription already ([2fc690a](2fc690a783f5b702fad71da627aa616017727f56))
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update klakegg/hugo docker tag to v0.101.0
|
||||
* *(deps)* Update golang.org/x/sync digest to 8fcdb60
|
||||
* *(deps)* Update golang.org/x/oauth2 digest to f213421
|
||||
* *(deps)* Update module src.techknowlogick.com/xgo to v1.5.0+1.19
|
||||
* *(deps)* Update module github.com/coreos/go-oidc/v3 to v3.4.0
|
||||
* *(deps)* Update golang.org/x/image digest to e7cb969
|
||||
* *(deps)* Update golang.org/x/term digest to 7a66f97
|
||||
* *(deps)* Update module github.com/lib/pq to v1.10.7
|
||||
* *(deps)* Update module github.com/spf13/viper to v1.13.0 (#1260)
|
||||
* *(deps)* Update dependency golang to v1.19 (#1228)
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.2.8 (#1258)
|
||||
* *(deps)* Update module github.com/yuin/goldmark to v1.5.2 (#1261)
|
||||
* *(deps)* Update module src.techknowlogick.com/xormigrate to v1.5.0 (#1262)
|
||||
* *(deps)* Update module github.com/magefile/mage to v1.14.0 (#1259)
|
||||
* *(deps)* Update module github.com/swaggo/swag to v1.8.6 (#1243)
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.2.9 (#1264)
|
||||
* *(deps)* Update dependency klakegg/hugo to v0.102.3 (#1265)
|
||||
* *(deps)* Update module github.com/getsentry/sentry-go to v0.14.0 (#1266)
|
||||
* *(deps)* Update module github.com/labstack/gommon to v0.4.0 (#1269)
|
||||
* *(deps)* Update golang.org/x/crypto digest to 4161e89 (#1268)
|
||||
* *(deps)* Update golang.org/x/oauth2 digest to b44042a (#1270)
|
||||
* *(deps)* Update golang.org/x/sys digest to 84dc82d (#1271)
|
||||
* *(deps)* Update dependency klakegg/hugo to v0.104.2 (#1267)
|
||||
* *(deps)* Update golang.org/x/crypto digest to d6f0a8c (#1275)
|
||||
* *(deps)* Update golang.org/x/sys digest to 090e330 (#1276)
|
||||
* *(deps)* Update module github.com/spf13/cobra to v1.6.0 (#1277)
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.3.0 (#1278)
|
||||
* *(deps)* Update golang.org/x/crypto digest to 56aed06 (#1280)
|
||||
* *(deps)* Update golang.org/x/text to v0.3.8
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.3.1 (#1281)
|
||||
* *(deps)* Update module github.com/labstack/echo/v4 to v4.9.1 (#1282)
|
||||
* *(deps)* Update golang.org/x/sys digest to 95e765b (#1283)
|
||||
* *(deps)* Update golang.org/x/oauth2 digest to 6fdb5e3 (#1284)
|
||||
* *(deps)* Update golang.org/x/image digest to ffcb3fe (#1288)
|
||||
* *(deps)* Update module golang.org/x/sync to v0.1.0 (#1291)
|
||||
* *(deps)* Update module github.com/swaggo/swag to v1.8.7 (#1290)
|
||||
* *(deps)* Update golang.org/x/term digest to 8365914 (#1289)
|
||||
* *(deps)* Update module github.com/coreos/go-systemd/v22 to v22.4.0 (#1287)
|
||||
* *(deps)* Update module golang.org/x/oauth2 to v0.1.0 (#1296)
|
||||
* *(deps)* Update module golang.org/x/crypto to v0.1.0 (#1295)
|
||||
* *(deps)* Update module golang.org/x/image to v0.1.0 (#1293)
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.3.2 (#1297)
|
||||
* *(deps)* Update module github.com/stretchr/testify to v1.8.1 (#1298)
|
||||
* *(deps)* Update module github.com/spf13/cobra to v1.6.1 (#1299)
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.3.3 (#1300)
|
||||
* *(deps)* Update module github.com/wneessen/go-mail to v0.3.4 (#1302)
|
||||
* *(deps)* Update module github.com/mattn/go-sqlite3 to v1.14.16 (#1301)
|
||||
|
||||
### Features
|
||||
|
||||
* *(docs)* Add docs about how to deploy Vikunja in a subdirectory
|
||||
* *(docs)* Document pnpm (#1251)
|
||||
* *(migration)* Add TickTick migrator
|
||||
* *(migration)* Add routes for TickTick migrator
|
||||
* *(migration)* Generate swagger docs
|
||||
* *(task)* Add cover image attachment id property
|
||||
* *(task)* Add cover image attachment id property (#1263)* Add sponsor to readme (relm) ([f814dd0](f814dd03eb7f1ae08ea67ae0e3e89b8b4e684ce3))
|
||||
* Upgrade xorm ([b1fd13b](b1fd13bbcbc551d1bbfe78d91fe6209369709df5))
|
||||
* Upgrade xorm ([4323803](4323803fd6801e21121eac0d9f9cd62879f090f7))
|
||||
* Upgrade xorm (#1197) ([5341918](53419180be386d675b4513e7ec70aca85b5ac99b))
|
||||
* Add github issue templates ([9c4bb5a](9c4bb5a24429dec686e3ccdcd2b920ce5528031c))
|
||||
* Remove gitea issue template so that only the form is used ([ce621ee](ce621ee5d6b47a0776628073bbd53312a97d116b))
|
||||
* Add gitea issue template ([0612f4d](0612f4d0e03fbe85018f51056c4833557e78cd3f))
|
||||
* Provide default user settings for new users via config ([5a40100](5a40100ac5be33d2cbce3c25e355d4036b9b4d3f))
|
||||
* Add proper checks and errors to see if an attachment belongs to the task it's being used as cover image in ([631a265](631a265d2de9a6196faf28574023fc3cdcc0bfc7))
|
||||
* Allow a user to remove themselves from a team ([b8769c7](b8769c746ceddc9818f91d6a8a404293ea2e837e))
|
||||
* TickTick migrator (#1273) ([df2e36c](df2e36c2a378d4bd1b81d959da180b6e9b9a37b9))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
* Upgrade echo ([86ee827](86ee8273bce36c7b4767a34e0d878d63b37ea1b4))
|
||||
* Go mod tidy ([903b8ff](903b8ff43871234f41f706d571ee2caaba5f4232))
|
||||
* Generate swagger docs ([e113fe3](e113fe34d074f698f4b0cb237821f359976daa5c))
|
||||
* Remove unused dependencies ([f5fd849](f5fd849a0b93ff3bba53ac4907bb3fb04fa8692b))
|
||||
|
||||
## [0.19.2] - 2022-08-17
|
||||
|
||||
### Bug Fixes
|
||||
|
|
12
Dockerfile
12
Dockerfile
|
@ -1,9 +1,9 @@
|
|||
|
||||
##############
|
||||
# Build stage
|
||||
FROM golang:1.19-alpine AS build-env
|
||||
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:latest AS build-env
|
||||
|
||||
RUN apk --no-cache add build-base git && \
|
||||
RUN \
|
||||
go install github.com/magefile/mage@latest && \
|
||||
mv /go/bin/mage /usr/local/go/bin
|
||||
|
||||
|
@ -13,9 +13,11 @@ ARG VIKUNJA_VERSION
|
|||
COPY . /go/src/code.vikunja.io/api
|
||||
WORKDIR /go/src/code.vikunja.io/api
|
||||
|
||||
ARG TARGETOS TARGETARCH TARGETVARIANT
|
||||
# Checkout version if set
|
||||
RUN if [ -n "${VIKUNJA_VERSION}" ]; then git checkout "${VIKUNJA_VERSION}"; fi \
|
||||
&& mage build:clean build
|
||||
RUN if [ -n "${VIKUNJA_VERSION}" ]; then git checkout "${VIKUNJA_VERSION}"; fi && \
|
||||
mage build:clean && \
|
||||
mage release:xgo $TARGETOS/$TARGETARCH/$TARGETVARIANT
|
||||
|
||||
###################
|
||||
# The actual image
|
||||
|
@ -25,7 +27,7 @@ FROM alpine:3.16
|
|||
LABEL maintainer="maintainers@vikunja.io"
|
||||
|
||||
WORKDIR /app/vikunja/
|
||||
COPY --from=build-env /go/src/code.vikunja.io/api/vikunja .
|
||||
COPY --from=build-env /build/vikunja-* vikunja
|
||||
ENV VIKUNJA_SERVICE_ROOTPATH=/app/vikunja/
|
||||
|
||||
# Dynamic permission changing stuff
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
|
||||
##############
|
||||
# Build stage
|
||||
FROM golang:1.19-buster AS build-env
|
||||
|
||||
RUN go install github.com/magefile/mage@latest && \
|
||||
mv /go/bin/mage /usr/local/go/bin
|
||||
|
||||
ARG VIKUNJA_VERSION
|
||||
|
||||
# Setup repo
|
||||
COPY . /go/src/code.vikunja.io/api
|
||||
WORKDIR /go/src/code.vikunja.io/api
|
||||
|
||||
# Checkout version if set
|
||||
RUN if [ -n "${VIKUNJA_VERSION}" ]; then git checkout "${VIKUNJA_VERSION}"; fi \
|
||||
&& mage build:clean build
|
||||
|
||||
###################
|
||||
# The actual image
|
||||
# Note: I wanted to use the scratch image here, but unfortunatly the go-sqlite bindings require cgo and
|
||||
# because of this, the container would not start when I compiled the image without cgo.
|
||||
# We're using debian as a base image here because the latest alpine image does not work with arm.
|
||||
FROM debian:buster-slim
|
||||
LABEL maintainer="maintainers@vikunja.io"
|
||||
|
||||
WORKDIR /app/vikunja/
|
||||
COPY --from=build-env /go/src/code.vikunja.io/api/vikunja .
|
||||
ENV VIKUNJA_SERVICE_ROOTPATH=/app/vikunja/
|
||||
|
||||
# Dynamic permission changing stuff
|
||||
ENV PUID 1000
|
||||
ENV PGID 1000
|
||||
RUN addgroup --gid ${PGID} vikunja && \
|
||||
chown ${PUID} -R /app/vikunja && \
|
||||
useradd --shell /bin/sh --gid vikunja --uid ${PUID} --home-dir /app/vikunja vikunja
|
||||
COPY run.sh /run.sh
|
||||
|
||||
# Fix time zone settings not working
|
||||
RUN apt-get update && apt-get install -y tzdata && apt-get clean
|
||||
|
||||
# Files permissions
|
||||
RUN mkdir /app/vikunja/files && \
|
||||
chown -R vikunja /app/vikunja/files
|
||||
VOLUME /app/vikunja/files
|
||||
|
||||
CMD ["/run.sh"]
|
||||
EXPOSE 3456
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/api/status.svg)](https://drone.kolaente.de/vikunja/api)
|
||||
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
|
||||
[![Download](https://img.shields.io/badge/download-v0.19.2-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Download](https://img.shields.io/badge/download-v0.20.1-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Docker Pulls](https://img.shields.io/docker/pulls/vikunja/api.svg)](https://hub.docker.com/r/vikunja/api/)
|
||||
[![Swagger Docs](https://img.shields.io/badge/swagger-docs-brightgreen.svg)](https://try.vikunja.io/api/v1/docs)
|
||||
[![Go Report Card](https://goreportcard.com/badge/kolaente.dev/vikunja/api)](https://goreportcard.com/report/kolaente.dev/vikunja/api)
|
||||
|
|
|
@ -162,7 +162,7 @@ log:
|
|||
databaselevel: "WARNING"
|
||||
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||
http: "stdout"
|
||||
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
# Echo has its own logging which usually is unnecessary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
echo: "off"
|
||||
# Whether or not to log events. Useful for debugging. Possible values are stdout, stderr, file or off to disable events logging.
|
||||
events: "stdout"
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
image: vikunja/api:unstable
|
||||
manifests:
|
||||
-
|
||||
image: vikunja/api:unstable-linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/api:unstable-linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/api:unstable-linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
|
@ -1,23 +0,0 @@
|
|||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: "Releasing a new Vikunja version"
|
||||
date: 2022-10-28T13:06:05+02:00
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "development"
|
||||
---
|
||||
|
||||
# Releasing a new Vikunja version
|
||||
|
||||
This checklist is a collection of all steps usually involved when releasing a new version of Vikunja.
|
||||
Not all steps are necessary for every release.
|
||||
|
||||
* Website update :
|
||||
* New Features: If there are new features worth mentioning the feature page should be updated.
|
||||
* New Screenshots: If an overhaul of an existing feature happend so that it now looks different from the existing screenshot, a new one is required.
|
||||
* Generate changelogs: (with git-cliff)
|
||||
* Frontend
|
||||
* API
|
||||
* Desktop
|
||||
* Tag a new version: Include the changelog for that version as the tag message
|
||||
* Frontend
|
||||
* API
|
||||
* Desktop
|
||||
* Once built: Prune the cloudflare cache so that the new versions show up at dl.vikunja.io
|
||||
* Release Highlights Blogpost:
|
||||
* Include a section about Vikunja in general (totally fine to copy one from the earlier blog posts)
|
||||
* New Features & Improvements: Mention bigger features, potentially with screenshots. Things like refactoring are sometimes also worth mentioneing.
|
||||
* Publish:
|
||||
* Reddit
|
||||
* Twitter
|
||||
* Mastodon
|
||||
* Chat
|
||||
* Newsletter
|
||||
* Forum
|
||||
* If features in the release were sponsored, send an email to relevant stakeholders
|
||||
* Update Vikunja Cloud version and other instances
|
||||
|
|
@ -840,7 +840,7 @@ Environment path: `VIKUNJA_LOG_HTTP`
|
|||
|
||||
### echo
|
||||
|
||||
Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
Echo has its own logging which usually is unnecessary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
|
||||
Default: `off`
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ WantedBy=multi-user.target
|
|||
|
||||
If you've installed Vikunja to a directory other than `/opt/vikunja`, you need to adapt `WorkingDirectory` accordingly.
|
||||
|
||||
After you made all nessecary modifications, it's time to start the service:
|
||||
After you made all necessary modifications, it's time to start the service:
|
||||
|
||||
{{< highlight bash >}}
|
||||
sudo systemctl enable vikunja
|
||||
|
@ -97,7 +97,7 @@ To build vikunja from source, see [building from source]({{< ref "build-from-sou
|
|||
### Updating
|
||||
|
||||
Simply replace the binary and templates with the new version, then restart Vikunja.
|
||||
It will automatically run all nessecary database migrations.
|
||||
It will automatically run all necessary database migrations.
|
||||
**Make sure to take a look at the changelog for the new version to not miss any manual steps the update may involve!**
|
||||
|
||||
## Docker
|
||||
|
|
|
@ -35,7 +35,7 @@ Just open the file with a text editor - there are comments which will explain ho
|
|||
|
||||
## Docker
|
||||
|
||||
The docker image is based on nginx and just contains all nessecary files for the frontend.
|
||||
The docker image is based on nginx and just contains all necessary files for the frontend.
|
||||
|
||||
To run it, all you need is
|
||||
|
||||
|
|
|
@ -26,6 +26,15 @@ If you don't specify a command, the [`web`](#web) command will be executed.
|
|||
|
||||
All commands use the same standard [config file]({{< ref "../setup/config.md">}}).
|
||||
|
||||
## Using the cli in docker
|
||||
|
||||
When running Vikunja in docker, you'll need to execute all commands in the `api` container.
|
||||
Instead of running the `vikunja` binary directly, run it like this:
|
||||
|
||||
```
|
||||
docker exec <name of the vikunja api container> /app/vikunja/vikunja <subcommand>
|
||||
```
|
||||
|
||||
### `dump`
|
||||
|
||||
Creates a zip file with all vikunja-related files.
|
||||
|
|
41
go.mod
41
go.mod
|
@ -31,7 +31,7 @@ require (
|
|||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0
|
||||
github.com/gabriel-vasile/mimetype v1.4.1
|
||||
github.com/getsentry/sentry-go v0.14.0
|
||||
github.com/getsentry/sentry-go v0.15.0
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.8.1
|
||||
|
@ -45,29 +45,29 @@ require (
|
|||
github.com/labstack/gommon v0.4.0
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/magefile/mage v1.14.0
|
||||
github.com/mattn/go-sqlite3 v1.14.15
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/spf13/afero v1.9.2
|
||||
github.com/spf13/cobra v1.6.0
|
||||
github.com/spf13/viper v1.13.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/swaggo/swag v1.8.6
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/viper v1.14.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/swaggo/swag v1.8.7
|
||||
github.com/tkuchiki/go-timezone v0.2.2
|
||||
github.com/ulule/limiter/v3 v3.10.0
|
||||
github.com/vectordotdev/go-datemath v0.1.1-0.20211214182920-0a4ac8742b93
|
||||
github.com/wneessen/go-mail v0.3.1
|
||||
github.com/yuin/goldmark v1.5.2
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0
|
||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087
|
||||
github.com/wneessen/go-mail v0.3.4
|
||||
github.com/yuin/goldmark v1.5.3
|
||||
golang.org/x/crypto v0.2.0
|
||||
golang.org/x/image v0.1.0
|
||||
golang.org/x/oauth2 v0.1.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/sys v0.2.0
|
||||
golang.org/x/term v0.2.0
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
src.techknowlogick.com/xgo v1.5.1-0.20220906164532-735bfdfb90d9
|
||||
|
@ -88,7 +88,7 @@ require (
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/garyburd/redigo v1.6.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-chi/chi v4.0.2+incompatible // indirect
|
||||
|
@ -122,7 +122,7 @@ require (
|
|||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
|
@ -135,8 +135,8 @@ require (
|
|||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b // indirect
|
||||
golang.org/x/text v0.3.8 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
@ -147,9 +147,6 @@ require (
|
|||
)
|
||||
|
||||
replace (
|
||||
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4
|
||||
github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
|
||||
github.com/hpcloud/tail => github.com/jeffbean/tail v1.0.1 // See https://github.com/hpcloud/tail/pull/159
|
||||
github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
|
||||
gopkg.in/fsnotify.v1 => github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 // See https://github.com/fsnotify/fsnotify/issues/328 and https://github.com/golang/go/issues/26904
|
||||
)
|
||||
|
|
99
go.sum
99
go.sum
|
@ -149,7 +149,9 @@ github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE
|
|||
github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g=
|
||||
github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
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/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
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.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
|
@ -198,12 +200,16 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3
|
|||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q=
|
||||
github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
|
||||
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
|
||||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/getsentry/sentry-go v0.14.0 h1:rlOBkuFZRKKdUnKO+0U3JclRDQKlRu5vVQtkWSQvC70=
|
||||
github.com/getsentry/sentry-go v0.14.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
||||
github.com/getsentry/sentry-go v0.15.0 h1:CP9bmA7pralrVUedYZsmIHWpq/pBtXTSew7xvVpfLaA=
|
||||
github.com/getsentry/sentry-go v0.15.0/go.mod h1:RZPJKSw+adu8PBNygiri/A98FqVr2HtRckJk9XVxJ9I=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
|
||||
|
@ -244,7 +250,6 @@ github.com/go-testfixtures/testfixtures/v3 v3.8.1/go.mod h1:Kdu7YeMC0KRXVHdaQ91V
|
|||
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
|
@ -384,6 +389,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
|||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
|
@ -391,7 +397,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
|||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
|
@ -456,7 +461,6 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
|
|||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jeffbean/tail v1.0.1/go.mod h1:+MhJ+VPZMpv8Ui6WRzpJFuWFKxBCZgVOo5HAmlw1sFc=
|
||||
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
||||
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
|
@ -498,8 +502,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
|
||||
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
|
||||
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||
github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y=
|
||||
github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
|
@ -558,6 +560,8 @@ github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71
|
|||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
|
@ -642,6 +646,10 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
|
|||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_golang v1.13.1 h1:3gMjIY2+/hzmqhtUC/aQNYldJA6DtH3CgQvwS+02K1c=
|
||||
github.com/prometheus/client_golang v1.13.1/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
|
@ -649,6 +657,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
|
@ -704,10 +714,10 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD
|
|||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
|
||||
github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
|
@ -715,6 +725,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
|
||||
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
|
||||
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
|
||||
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
|
@ -722,6 +734,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
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=
|
||||
|
@ -731,10 +744,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/swaggo/swag v1.8.6 h1:2rgOaLbonWu1PLP6G+/rYjSvPg0jQE0HtrEKuE380eg=
|
||||
github.com/swaggo/swag v1.8.6/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
|
||||
github.com/swaggo/swag v1.8.7 h1:2K9ivTD3teEO+2fXV6zrZKDqk5IuU2aJtBDo8U7omWU=
|
||||
github.com/swaggo/swag v1.8.7/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tkuchiki/go-timezone v0.2.2 h1:MdHR65KwgVTwWFQrota4SKzc4L5EfuH5SdZZGtk/P2Q=
|
||||
|
@ -755,20 +770,25 @@ github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52
|
|||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vectordotdev/go-datemath v0.1.1-0.20211214182920-0a4ac8742b93 h1:bT0ZMfsMi2Xh8dopgxhFT+OJH88QITHpdppdkG1rXJQ=
|
||||
github.com/vectordotdev/go-datemath v0.1.1-0.20211214182920-0a4ac8742b93/go.mod h1:PnwzbSst7KD3vpBzzlntZU5gjVa455Uqa5QPiKSYJzQ=
|
||||
github.com/wneessen/go-mail v0.2.9 h1:HtjvJn0VBKOl9ptnWwCXgX8F8zQlwJL0cdqx36xU6Tg=
|
||||
github.com/wneessen/go-mail v0.2.9/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/wneessen/go-mail v0.3.0 h1:deMOckwamWEHAgfP5HsH5O0EX450ZYYchlN93HpFCLc=
|
||||
github.com/wneessen/go-mail v0.3.0/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/wneessen/go-mail v0.3.1 h1:9hSthi8T57gDHMI0fgl58O667OQJm9wZ1EHyuy3hclA=
|
||||
github.com/wneessen/go-mail v0.3.1/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/wneessen/go-mail v0.3.2 h1:nTjAF4Ek2+JG7qunyk6oImf5YKrAE5a7A3uIazYsdM0=
|
||||
github.com/wneessen/go-mail v0.3.2/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/wneessen/go-mail v0.3.3 h1:mhqM18uWiBFA3TdfO2IyFfQ6dEj4kGKW1KQJJNSKLME=
|
||||
github.com/wneessen/go-mail v0.3.3/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/wneessen/go-mail v0.3.4 h1:75G6lojt3CxwSq73csMduxF7DJ3hLF2s2KJXJVDOr0k=
|
||||
github.com/wneessen/go-mail v0.3.4/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
|
||||
github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
|
@ -813,11 +833,12 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
|
|||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 h1:x8vtB3zMecnlqZIwJNUUpwYKYSqCz5jXbiyv0ZJJZeI=
|
||||
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
|
||||
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE=
|
||||
golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -831,8 +852,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
|||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
|
||||
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -912,9 +933,12 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
|||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -936,10 +960,8 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j
|
|||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1 h1:3VPzK7eqH25j7GYw5w6g/GzNRc0/fYtrxz27z1gD4W0=
|
||||
golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk=
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=
|
||||
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -952,8 +974,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY=
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -1043,17 +1066,21 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
|
||||
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
|
||||
golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -1063,8 +1090,8 @@ 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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
14
magefile.go
14
magefile.go
|
@ -546,6 +546,20 @@ func (Release) Darwin() error {
|
|||
return runXgo("darwin-10.15/*")
|
||||
}
|
||||
|
||||
func (Release) Xgo(target string) error {
|
||||
parts := strings.Split(target, "/")
|
||||
if len(parts) < 2 {
|
||||
return fmt.Errorf("invalid target")
|
||||
}
|
||||
|
||||
variant := ""
|
||||
if len(parts) > 2 && parts[2] != "" {
|
||||
variant = "-" + strings.ReplaceAll(parts[2], "v", "")
|
||||
}
|
||||
|
||||
return runXgo(parts[0] + "/" + parts[1] + variant)
|
||||
}
|
||||
|
||||
// Compresses the built binaries in dist/binaries/ to reduce their filesize
|
||||
func (Release) Compress(ctx context.Context) error {
|
||||
// $(foreach file,$(filter-out $(wildcard $(wildcard $(DIST)/binaries/$(EXECUTABLE)-*mips*)),$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*)), upx -9 $(file);)
|
||||
|
|
25
pkg/db/db.go
25
pkg/db/db.go
|
@ -19,6 +19,7 @@ package db
|
|||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -147,14 +148,28 @@ func parsePostgreSQLHostPort(info string) (string, string) {
|
|||
return host, port
|
||||
}
|
||||
|
||||
// Copied and adopted from https://github.com/go-gitea/gitea/blob/f337c32e868381c6d2d948221aca0c59f8420c13/modules/setting/database.go#L176-L186
|
||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbSslMode, dbSslCert, dbSslKey, dbSslRootCert string) (connStr string) {
|
||||
dbParam := "?"
|
||||
if strings.Contains(dbName, dbParam) {
|
||||
dbParam = "&"
|
||||
}
|
||||
host, port := parsePostgreSQLHostPort(dbHost)
|
||||
if host[0] == '/' { // looks like a unix socket
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@:%s/%s%ssslmode=%s&sslcert=%s&sslkey=%s&sslrootcert=%s&host=%s",
|
||||
url.PathEscape(dbUser), url.PathEscape(dbPasswd), port, dbName, dbParam, dbSslMode, dbSslCert, dbSslKey, dbSslRootCert, host)
|
||||
} else {
|
||||
connStr = fmt.Sprintf("postgres://%s:%s@%s:%s/%s%ssslmode=%s&sslcert=%s&sslkey=%s&sslrootcert=%s",
|
||||
url.PathEscape(dbUser), url.PathEscape(dbPasswd), host, port, dbName, dbParam, dbSslMode, dbSslCert, dbSslKey, dbSslRootCert)
|
||||
}
|
||||
return connStr
|
||||
}
|
||||
|
||||
func initPostgresEngine() (engine *xorm.Engine, err error) {
|
||||
host, port := parsePostgreSQLHostPort(config.DatabaseHost.GetString())
|
||||
// postgresql://username:password@host:port/dbname[?paramspec]
|
||||
connStr := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s&sslcert=%s&sslkey=%s&sslrootcert=%s",
|
||||
connStr := getPostgreSQLConnectionString(
|
||||
config.DatabaseHost.GetString(),
|
||||
config.DatabaseUser.GetString(),
|
||||
config.DatabasePassword.GetString(),
|
||||
host,
|
||||
port,
|
||||
config.DatabaseDatabase.GetString(),
|
||||
config.DatabaseSslMode.GetString(),
|
||||
config.DatabaseSslCert.GetString(),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
list_id: 1
|
||||
created_by_id: 1
|
||||
limit: 9999999 # This bucket has a limit we will never exceed in the tests to make sure the logic allows for buckets with limits
|
||||
position: 2
|
||||
position: 1
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 2
|
||||
|
@ -11,7 +11,7 @@
|
|||
list_id: 1
|
||||
created_by_id: 1
|
||||
limit: 3
|
||||
position: 1
|
||||
position: 2
|
||||
created: 2020-04-18 21:13:52
|
||||
updated: 2020-04-18 21:13:52
|
||||
- id: 3
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
)
|
||||
|
||||
// SecondsUntilInactive defines the seconds until a user is considered inactive
|
||||
const SecondsUntilInactive = 60
|
||||
const SecondsUntilInactive = 30
|
||||
|
||||
// ActiveUsersKey is the key used to store active users in redis
|
||||
const ActiveUsersKey = `activeusers`
|
||||
|
@ -55,12 +55,13 @@ func init() {
|
|||
users: make(map[int64]*ActiveUser),
|
||||
mutex: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
func setupActiveUsersMetric() {
|
||||
err := registry.Register(promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Name: "vikunja_active_users",
|
||||
Help: "The currently active users on this node",
|
||||
Help: "The number of users active within the last 30 seconds on this node",
|
||||
}, func() float64 {
|
||||
|
||||
allActiveUsers, err := getActiveUsers()
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
@ -75,7 +76,10 @@ func init() {
|
|||
}
|
||||
}
|
||||
return float64(activeUsersCount)
|
||||
})
|
||||
}))
|
||||
if err != nil {
|
||||
log.Criticalf("Could not register metrics for currently active users: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetUserActive sets a user as active and pushes it to redis
|
||||
|
|
|
@ -113,7 +113,7 @@ func InitMetrics() {
|
|||
log.Criticalf("Could not register metrics for %s: %s", TaskCountKey, err)
|
||||
}
|
||||
|
||||
// Register total user count metric
|
||||
// Register total teams count metric
|
||||
err = registry.Register(promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Name: "vikunja_team_count",
|
||||
Help: "The total number of teams on this instance",
|
||||
|
@ -124,9 +124,11 @@ func InitMetrics() {
|
|||
if err != nil {
|
||||
log.Criticalf("Could not register metrics for %s: %s", TeamCountKey, err)
|
||||
}
|
||||
|
||||
setupActiveUsersMetric()
|
||||
}
|
||||
|
||||
// GetCount returns the current count from redis
|
||||
// GetCount returns the current count from keyvalue
|
||||
func GetCount(key string) (count int64, err error) {
|
||||
cnt, exists, err := keyvalue.Get(key)
|
||||
if err != nil {
|
||||
|
|
|
@ -81,7 +81,7 @@ func getDefaultBucket(s *xorm.Session, listID int64) (bucket *Bucket, err error)
|
|||
bucket = &Bucket{}
|
||||
_, err = s.
|
||||
Where("list_id = ?", listID).
|
||||
OrderBy("id asc").
|
||||
OrderBy("position asc").
|
||||
Get(bucket)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -49,22 +49,22 @@ func TestBucket_ReadAll(t *testing.T) {
|
|||
assert.Len(t, buckets, 3)
|
||||
|
||||
// Assert all tasks are in the right bucket
|
||||
assert.Len(t, buckets[0].Tasks, 3)
|
||||
assert.Len(t, buckets[1].Tasks, 12)
|
||||
assert.Len(t, buckets[0].Tasks, 12)
|
||||
assert.Len(t, buckets[1].Tasks, 3)
|
||||
assert.Len(t, buckets[2].Tasks, 3)
|
||||
|
||||
// Assert we have bucket 1, 2, 3 but not 4 (that belongs to a different list) and their position
|
||||
assert.Equal(t, int64(2), buckets[0].ID)
|
||||
assert.Equal(t, int64(1), buckets[1].ID)
|
||||
assert.Equal(t, int64(1), buckets[0].ID)
|
||||
assert.Equal(t, int64(2), buckets[1].ID)
|
||||
assert.Equal(t, int64(3), buckets[2].ID)
|
||||
|
||||
// Kinda assert all tasks are in the right buckets
|
||||
assert.Equal(t, int64(1), buckets[1].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(1), buckets[1].Tasks[1].BucketID)
|
||||
assert.Equal(t, int64(1), buckets[0].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(1), buckets[0].Tasks[1].BucketID)
|
||||
|
||||
assert.Equal(t, int64(2), buckets[0].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[0].Tasks[1].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[0].Tasks[2].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[1].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[1].Tasks[1].BucketID)
|
||||
assert.Equal(t, int64(2), buckets[1].Tasks[2].BucketID)
|
||||
|
||||
assert.Equal(t, int64(3), buckets[2].Tasks[0].BucketID)
|
||||
assert.Equal(t, int64(3), buckets[2].Tasks[1].BucketID)
|
||||
|
@ -89,8 +89,8 @@ func TestBucket_ReadAll(t *testing.T) {
|
|||
|
||||
buckets := bucketsInterface.([]*Bucket)
|
||||
assert.Len(t, buckets, 3)
|
||||
assert.Equal(t, int64(2), buckets[1].Tasks[0].ID)
|
||||
assert.Equal(t, int64(33), buckets[1].Tasks[1].ID)
|
||||
assert.Equal(t, int64(2), buckets[0].Tasks[0].ID)
|
||||
assert.Equal(t, int64(33), buckets[0].Tasks[1].ID)
|
||||
})
|
||||
t.Run("accessed by link share", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
|
|
@ -202,7 +202,7 @@ func (l *List) isOwner(u *user.User) bool {
|
|||
func (l *List) checkRight(s *xorm.Session, a web.Auth, rights ...Right) (bool, int, error) {
|
||||
|
||||
/*
|
||||
The following loop creates an sql condition like this one:
|
||||
The following loop creates a sql condition like this one:
|
||||
|
||||
(ul.user_id = 1 AND ul.right = 1) OR (un.user_id = 1 AND un.right = 1) OR
|
||||
(tm.user_id = 1 AND tn.right = 1) OR (tm2.user_id = 1 AND tl.right = 1) OR
|
||||
|
@ -242,16 +242,19 @@ func (l *List) checkRight(s *xorm.Session, a web.Auth, rights ...Right) (bool, i
|
|||
conds = append(conds, builder.Eq{"n.owner_id": a.GetID()})
|
||||
|
||||
type allListRights struct {
|
||||
UserNamespace NamespaceUser `xorm:"extends"`
|
||||
UserList ListUser `xorm:"extends"`
|
||||
UserNamespace *NamespaceUser `xorm:"extends"`
|
||||
UserList *ListUser `xorm:"extends"`
|
||||
|
||||
TeamNamespace TeamNamespace `xorm:"extends"`
|
||||
TeamList TeamList `xorm:"extends"`
|
||||
TeamNamespace *TeamNamespace `xorm:"extends"`
|
||||
TeamList *TeamList `xorm:"extends"`
|
||||
|
||||
NamespaceOwnerID int64 `xorm:"namespaces_owner_id"`
|
||||
}
|
||||
|
||||
r := &allListRights{}
|
||||
var maxRight = 0
|
||||
exists, err := s.
|
||||
Select("l.*, un.right, ul.right, tn.right, tl.right, n.owner_id as namespaces_owner_id").
|
||||
Table("lists").
|
||||
Alias("l").
|
||||
// User stuff
|
||||
|
@ -285,6 +288,9 @@ func (l *List) checkRight(s *xorm.Session, a web.Auth, rights ...Right) (bool, i
|
|||
if int(r.TeamList.Right) > maxRight {
|
||||
maxRight = int(r.TeamList.Right)
|
||||
}
|
||||
if r.NamespaceOwnerID == a.GetID() {
|
||||
maxRight = int(RightAdmin)
|
||||
}
|
||||
|
||||
return exists, maxRight, err
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ func validateTaskField(fieldName string) error {
|
|||
taskPropertyUpdated,
|
||||
taskPropertyPosition,
|
||||
taskPropertyKanbanPosition,
|
||||
taskPropertyBucketID:
|
||||
taskPropertyBucketID,
|
||||
taskPropertyIndex:
|
||||
return nil
|
||||
}
|
||||
return ErrInvalidTaskField{TaskField: fieldName}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
"github.com/vectordotdev/go-datemath"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
@ -44,10 +45,47 @@ const (
|
|||
taskFilterComparatorIn taskFilterComparator = "in"
|
||||
)
|
||||
|
||||
// Guess what you get back if you ask Safari for a rfc 3339 formatted date?
|
||||
const safariDateAndTime = "2006-01-02 15:04"
|
||||
const safariDate = "2006-01-02"
|
||||
|
||||
type taskFilter struct {
|
||||
field string
|
||||
value interface{} // Needs to be an interface to be able to hold the field's native value
|
||||
comparator taskFilterComparator
|
||||
isNumeric bool
|
||||
}
|
||||
|
||||
func parseTimeFromUserInput(timeString string) (value time.Time, err error) {
|
||||
value, err = time.Parse(time.RFC3339, timeString)
|
||||
if err != nil {
|
||||
value, err = time.Parse(safariDateAndTime, timeString)
|
||||
}
|
||||
if err != nil {
|
||||
value, err = time.Parse(safariDate, timeString)
|
||||
}
|
||||
if err != nil {
|
||||
// Here we assume a date like 2022-11-1 and try to parse it manually
|
||||
parts := strings.Split(timeString, "-")
|
||||
if len(parts) < 3 {
|
||||
return
|
||||
}
|
||||
year, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
month, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
day, err := strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
value = time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
||||
return value.In(config.GetTimeZone()), nil
|
||||
}
|
||||
return value.In(config.GetTimeZone()), err
|
||||
}
|
||||
|
||||
func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err error) {
|
||||
|
@ -90,8 +128,9 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
|
|||
}
|
||||
|
||||
// Cast the field value to its native type
|
||||
var reflectValue *reflect.StructField
|
||||
if len(c.FilterValue) > i {
|
||||
filter.value, err = getNativeValueForTaskField(filter.field, filter.comparator, c.FilterValue[i])
|
||||
reflectValue, filter.value, err = getNativeValueForTaskField(filter.field, filter.comparator, c.FilterValue[i])
|
||||
if err != nil {
|
||||
return nil, ErrInvalidTaskFilterValue{
|
||||
Value: filter.field,
|
||||
|
@ -99,6 +138,9 @@ func getTaskFiltersByCollections(c *TaskCollection) (filters []*taskFilter, err
|
|||
}
|
||||
}
|
||||
}
|
||||
if reflectValue != nil {
|
||||
filter.isNumeric = reflectValue.Type.Kind() == reflect.Int64
|
||||
}
|
||||
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
|
@ -165,8 +207,7 @@ func getValueForField(field reflect.StructField, rawValue string) (value interfa
|
|||
if err == nil {
|
||||
value = t.Time(datemath.WithLocation(config.GetTimeZone()))
|
||||
} else {
|
||||
value, err = time.Parse(time.RFC3339, rawValue)
|
||||
value = value.(time.Time).In(config.GetTimeZone())
|
||||
value, err = parseTimeFromUserInput(rawValue)
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
|
@ -192,7 +233,7 @@ func getValueForField(field reflect.StructField, rawValue string) (value interfa
|
|||
return
|
||||
}
|
||||
|
||||
func getNativeValueForTaskField(fieldName string, comparator taskFilterComparator, value string) (nativeValue interface{}, err error) {
|
||||
func getNativeValueForTaskField(fieldName string, comparator taskFilterComparator, value string) (reflectField *reflect.StructField, nativeValue interface{}, err error) {
|
||||
|
||||
realFieldName := strings.ReplaceAll(strcase.ToCamel(fieldName), "Id", "ID")
|
||||
|
||||
|
@ -203,11 +244,11 @@ func getNativeValueForTaskField(fieldName string, comparator taskFilterComparato
|
|||
for _, val := range vals {
|
||||
v, err := strconv.ParseInt(val, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
valueSlice = append(valueSlice, v)
|
||||
}
|
||||
return valueSlice, nil
|
||||
return nil, valueSlice, nil
|
||||
}
|
||||
|
||||
nativeValue, err = strconv.ParseInt(value, 10, 64)
|
||||
|
@ -217,12 +258,12 @@ func getNativeValueForTaskField(fieldName string, comparator taskFilterComparato
|
|||
if realFieldName == "Assignees" {
|
||||
vals := strings.Split(value, ",")
|
||||
valueSlice := append([]string{}, vals...)
|
||||
return valueSlice, nil
|
||||
return nil, valueSlice, nil
|
||||
}
|
||||
|
||||
field, ok := reflect.TypeOf(&Task{}).Elem().FieldByName(realFieldName)
|
||||
if !ok {
|
||||
return nil, ErrInvalidTaskField{TaskField: fieldName}
|
||||
return nil, nil, ErrInvalidTaskField{TaskField: fieldName}
|
||||
}
|
||||
|
||||
if comparator == taskFilterComparatorIn {
|
||||
|
@ -231,12 +272,13 @@ func getNativeValueForTaskField(fieldName string, comparator taskFilterComparato
|
|||
for _, val := range vals {
|
||||
v, err := getValueForField(field, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
valueSlice = append(valueSlice, v)
|
||||
}
|
||||
return valueSlice, nil
|
||||
return nil, valueSlice, nil
|
||||
}
|
||||
|
||||
return getValueForField(field, value)
|
||||
val, err := getValueForField(field, value)
|
||||
return &field, val, err
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ const (
|
|||
taskPropertyPosition string = "position"
|
||||
taskPropertyKanbanPosition string = "kanban_position"
|
||||
taskPropertyBucketID string = "bucket_id"
|
||||
taskPropertyIndex string = "index"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -202,7 +202,7 @@ func (rel *TaskRelation) Create(s *xorm.Session, a web.Auth) error {
|
|||
// @Param relation body models.TaskRelation true "The relation object"
|
||||
// @Param taskID path int true "Task ID"
|
||||
// @Param relationKind path string true "The kind of the relation. See the TaskRelation type for more info."
|
||||
// @Param otherTaskID path int true "The id of the other task."
|
||||
// @Param otherTaskId path int true "The id of the other task."
|
||||
// @Success 200 {object} models.Message "The task relation was successfully deleted."
|
||||
// @Failure 400 {object} web.HTTPError "Invalid task relation object provided."
|
||||
// @Failure 404 {object} web.HTTPError "The task relation was not found."
|
||||
|
|
|
@ -227,6 +227,9 @@ func getFilterCond(f *taskFilter, includeNulls bool) (cond builder.Cond, err err
|
|||
|
||||
if includeNulls {
|
||||
cond = builder.Or(cond, &builder.IsNull{field})
|
||||
if f.isNumeric {
|
||||
cond = builder.Or(cond, &builder.IsNull{field}, &builder.Eq{field: 0})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -306,10 +309,10 @@ func getRawTasksForLists(s *xorm.Session, lists []*List, a web.Auth, opts *taskO
|
|||
// Because it does not have support for NULLS FIRST or NULLS LAST we work around this by
|
||||
// first sorting for null (or not null) values and then the order we actually want to.
|
||||
if db.Type() == schemas.MYSQL {
|
||||
orderby += param.sortBy + " IS NULL, "
|
||||
orderby += "`" + param.sortBy + "` IS NULL, "
|
||||
}
|
||||
|
||||
orderby += param.sortBy + " " + param.orderBy.String()
|
||||
orderby += "`" + param.sortBy + "` " + param.orderBy.String()
|
||||
|
||||
// Postgres and sqlite allow us to control how columns with null values are sorted.
|
||||
// To make that consistent with the sort order we have and other dbms, we're adding a separate clause here.
|
||||
|
@ -580,7 +583,9 @@ func GetTasksByUIDs(s *xorm.Session, uids []string, a web.Auth) (tasks []*Task,
|
|||
|
||||
func getRemindersForTasks(s *xorm.Session, taskIDs []int64) (reminders []*TaskReminder, err error) {
|
||||
reminders = []*TaskReminder{}
|
||||
err = s.In("task_id", taskIDs).Find(&reminders)
|
||||
err = s.In("task_id", taskIDs).
|
||||
OrderBy("reminder asc").
|
||||
Find(&reminders)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -869,6 +874,19 @@ func calculateDefaultPosition(entityID int64, position float64) float64 {
|
|||
return position
|
||||
}
|
||||
|
||||
func getNextTaskIndex(s *xorm.Session, listID int64) (nextIndex int64, err error) {
|
||||
latestTask := &Task{}
|
||||
_, err = s.
|
||||
Where("list_id = ?", listID).
|
||||
OrderBy("`index` desc").
|
||||
Get(latestTask)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return latestTask.Index + 1, nil
|
||||
}
|
||||
|
||||
// Create is the implementation to create a list task
|
||||
// @Summary Create a task
|
||||
// @Description Inserts a task into a list.
|
||||
|
@ -920,16 +938,14 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
|
|||
}
|
||||
|
||||
// Get the index for this task
|
||||
latestTask := &Task{}
|
||||
_, err = s.Where("list_id = ?", t.ListID).OrderBy("id desc").Get(latestTask)
|
||||
t.Index, err = getNextTaskIndex(s, t.ListID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Index = latestTask.Index + 1
|
||||
// If no position was supplied, set a default one
|
||||
t.Position = calculateDefaultPosition(latestTask.ID+1, t.Position)
|
||||
t.KanbanPosition = calculateDefaultPosition(latestTask.ID+1, t.KanbanPosition)
|
||||
t.Position = calculateDefaultPosition(t.Index, t.Position)
|
||||
t.KanbanPosition = calculateDefaultPosition(t.Index, t.KanbanPosition)
|
||||
if _, err = s.Insert(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1047,13 +1063,10 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||
|
||||
// If the task is being moved between lists, make sure to move the bucket + index as well
|
||||
if t.ListID != 0 && ot.ListID != t.ListID {
|
||||
latestTask := &Task{}
|
||||
_, err = s.Where("list_id = ?", t.ListID).OrderBy("id desc").Get(latestTask)
|
||||
t.Index, err = getNextTaskIndex(s, t.ListID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Index = latestTask.Index + 1
|
||||
colsToUpdate = append(colsToUpdate, "index")
|
||||
}
|
||||
|
||||
|
@ -1392,8 +1405,14 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro
|
|||
return
|
||||
}
|
||||
|
||||
// Resolve duplicates and sort them
|
||||
reminderMap := make(map[string]time.Time, len(reminders))
|
||||
for _, reminder := range reminders {
|
||||
reminderMap[reminder.UTC().String()] = reminder
|
||||
}
|
||||
|
||||
// Loop through all reminders and add them
|
||||
for _, r := range reminders {
|
||||
for _, r := range reminderMap {
|
||||
_, err = s.Insert(&TaskReminder{TaskID: t.ID, Reminder: r})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -20,10 +20,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/events"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/events"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -324,6 +324,21 @@ func TestTask_Update(t *testing.T) {
|
|||
"bucket_id": 1,
|
||||
}, false)
|
||||
})
|
||||
t.Run("moving a task between lists should give it a correct index", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 12,
|
||||
ListID: 2, // From list 1
|
||||
}
|
||||
err := task.Update(s, u)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), task.Index)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTask_Delete(t *testing.T) {
|
||||
|
|
|
@ -47,7 +47,7 @@ func unsplashImage(url string, c echo.Context) error {
|
|||
// @Description Get an unsplash image. **Returns json on error.**
|
||||
// @tags list
|
||||
// @Produce octet-stream
|
||||
// @Param thumb path int true "Unsplash Image ID"
|
||||
// @Param image path int true "Unsplash Image ID"
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {} string "The image"
|
||||
// @Failure 404 {object} models.Message "The image does not exist."
|
||||
|
@ -67,7 +67,7 @@ func ProxyUnsplashImage(c echo.Context) error {
|
|||
// @Description Get an unsplash thumbnail image. The thumbnail is cropped to a max width of 200px. **Returns json on error.**
|
||||
// @tags list
|
||||
// @Produce octet-stream
|
||||
// @Param thumb path int true "Unsplash Image ID"
|
||||
// @Param image path int true "Unsplash Image ID"
|
||||
// @Security JWTKeyAuth
|
||||
// @Success 200 {} string "The thumbnail"
|
||||
// @Failure 404 {object} models.Message "The image does not exist."
|
||||
|
|
|
@ -95,7 +95,7 @@ const docTemplate = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "Unsplash Image ID",
|
||||
"name": "thumb",
|
||||
"name": "image",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ const docTemplate = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "Unsplash Image ID",
|
||||
"name": "thumb",
|
||||
"name": "image",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -5688,7 +5688,7 @@ const docTemplate = `{
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "The id of the other task.",
|
||||
"name": "otherTaskID",
|
||||
"name": "otherTaskId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "Unsplash Image ID",
|
||||
"name": "thumb",
|
||||
"name": "image",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -132,7 +132,7 @@
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "Unsplash Image ID",
|
||||
"name": "thumb",
|
||||
"name": "image",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
@ -5679,7 +5679,7 @@
|
|||
{
|
||||
"type": "integer",
|
||||
"description": "The id of the other task.",
|
||||
"name": "otherTaskID",
|
||||
"name": "otherTaskId",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
|
|
|
@ -1509,7 +1509,7 @@ paths:
|
|||
parameters:
|
||||
- description: Unsplash Image ID
|
||||
in: path
|
||||
name: thumb
|
||||
name: image
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
|
@ -1539,7 +1539,7 @@ paths:
|
|||
parameters:
|
||||
- description: Unsplash Image ID
|
||||
in: path
|
||||
name: thumb
|
||||
name: image
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
|
@ -5247,7 +5247,7 @@ paths:
|
|||
type: string
|
||||
- description: The id of the other task.
|
||||
in: path
|
||||
name: otherTaskID
|
||||
name: otherTaskId
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
|
|
Loading…
Reference in New Issue