Compare commits

..

21 Commits

Author SHA1 Message Date
renovate 369319c709 fix(deps): update module golang.org/x/oauth2 to v0.4.0
continuous-integration/drone/pr Build is passing Details
2023-01-24 16:01:23 +00:00
renovate fecce19f06 fix(deps): update module github.com/labstack/echo-jwt/v4 to v4.0.1 (#1369)
continuous-integration/drone/push Build is passing Details
Reviewed-on: vikunja/api#1369
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-24 15:34:54 +00:00
kolaente 1971df7b84
fix(migration): use the proper authorization method for Todoist's api, fix issues with importing deleted items
continuous-integration/drone/push Build is passing Details
2023-01-24 15:45:56 +01:00
kooshi 31a1452839 fix(migration): import TickTick data by column name instead of index (#1356)
continuous-integration/drone/push Build is passing Details
Resolves: https://github.com/go-vikunja/api/issues/61
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/api#1356
Co-authored-by: kooshi <kolaente.dev@pat.de.com>
Co-committed-by: kooshi <kolaente.dev@pat.de.com>
2023-01-24 13:58:18 +00:00
kolaente 530bb0a63c
fix(user): make reset the user's name to empty actually work
continuous-integration/drone/push Build is failing Details
2023-01-23 18:30:01 +01:00
kolaente 7bf7a13bb9
fix(reminders): prevent duplicate reminders when updating task details
continuous-integration/drone/push Build is passing Details
2023-01-23 18:14:15 +01:00
renovate 10e6843b11 fix(deps): update module github.com/spf13/viper to v1.15.0 (#1365)
continuous-integration/drone/push Build is passing Details
Reviewed-on: vikunja/api#1365
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-19 16:34:04 +00:00
renovate 78f43829cf fix(deps): update module src.techknowlogick.com/xgo to v1.7.0+1.19.5 (#1364)
continuous-integration/drone/push Build is passing Details
Reviewed-on: vikunja/api#1364
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-17 21:05:33 +00:00
renovate acd31c4ff7 fix(deps): update module github.com/getsentry/sentry-go to v0.17.0 (#1361)
continuous-integration/drone/push Build is failing Details
Reviewed-on: vikunja/api#1361
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-12 13:25:10 +00:00
renovate 1c02114cf5 chore(deps): update klakegg/hugo docker tag to v0.107.0 (#1272)
continuous-integration/drone/push Build is passing Details
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/api#1272
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-11 19:48:22 +00:00
kolaente 82f4a5ad50
fix(export): ignore file size for export files
continuous-integration/drone/push Build is passing Details
2023-01-11 18:56:30 +01:00
kolaente 04614614fe
fix(tasks): don't set a repeating task done when moving it do the done bucket
continuous-integration/drone/push Build is failing Details
2023-01-11 18:46:24 +01:00
kolaente 608bde9806
fix(ci): tagging logic for release docker images
continuous-integration/drone/push Build is passing Details
2023-01-11 18:13:11 +01:00
renovate 568cc16797 fix(deps): update module src.techknowlogick.com/xgo to v1.6.0+1.19.5 (#1358)
continuous-integration/drone/push Build is passing Details
Reviewed-on: vikunja/api#1358
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-10 19:21:26 +00:00
Rein 4b5e65d4c2
fix(mailer): forcessl config (#60)
continuous-integration/drone/push Build is passing Details
Co-authored-by: Rein-R3 <rein@reinsan.top>
Reviewed-At: https://github.com/go-vikunja/api/pull/60
2023-01-09 12:39:43 +01:00
renovate 3329d83363 fix(deps): update module github.com/wneessen/go-mail to v0.3.8 (#1357)
continuous-integration/drone/push Build is passing Details
Reviewed-on: vikunja/api#1357
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-07 12:52:06 +00:00
renovate c4e5e722e4 chore(deps): update goreleaser/nfpm docker tag to v2.23.0 (#1347)
continuous-integration/drone/push Build is failing Details
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/api#1347
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-06 11:05:47 +00:00
Dominik Pschenitschni 508a3157e2 fix(drone): add type, fix pull, remove group (#1355)
continuous-integration/drone/push Build is failing Details
Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Co-authored-by: kolaente <k@knt.li>
Reviewed-on: vikunja/api#1355
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
2023-01-06 09:46:52 +00:00
TheDubliner 321a8f7e2b
fix(docs): fix a few minor typos (#59)
continuous-integration/drone/push Build is passing Details
2023-01-06 10:33:43 +01:00
renovate dd7dcdd0cc fix(deps): update module golang.org/x/crypto to v0.5.0 (#1353)
continuous-integration/drone/push Build is failing Details
Reviewed-on: vikunja/api#1353
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 18:37:35 +00:00
renovate cb6368036c fix(deps): update module golang.org/x/term to v0.4.0 (#1352)
continuous-integration/drone/push Build encountered an error Details
Reviewed-on: vikunja/api#1352
Co-authored-by: renovate <renovatebot@kolaente.de>
Co-committed-by: renovate <renovatebot@kolaente.de>
2023-01-04 18:23:30 +00:00
19 changed files with 284 additions and 206 deletions

View File

@ -1,5 +1,6 @@
--- ---
kind: pipeline kind: pipeline
type: docker
name: testing name: testing
workspace: workspace:
@ -111,7 +112,7 @@ steps:
# compiling the same magefile at the same time. It's also faster if each step does not need to compile it first. # compiling the same magefile at the same time. It's also faster if each step does not need to compile it first.
- name: mage - name: mage
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
commands: commands:
@ -122,7 +123,7 @@ steps:
- name: build - name: build
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
depends_on: [ mage ] depends_on: [ mage ]
@ -133,7 +134,7 @@ steps:
- name: lint - name: lint
image: golang:1.19-alpine image: golang:1.19-alpine
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
depends_on: [ build ] depends_on: [ build ]
@ -147,7 +148,7 @@ steps:
- name: test-migration-prepare - name: test-migration-prepare
image: kolaente/toolbox:latest image: kolaente/toolbox:latest
pull: true pull: always
commands: commands:
# Get the latest version # Get the latest version
- wget https://dl.vikunja.io/api/unstable/vikunja-unstable-linux-amd64-full.zip -q -O vikunja-latest.zip - wget https://dl.vikunja.io/api/unstable/vikunja-unstable-linux-amd64-full.zip -q -O vikunja-latest.zip
@ -155,7 +156,7 @@ steps:
- name: test-migration-sqlite - name: test-migration-sqlite
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
depends_on: [ test-migration-prepare, build ] depends_on: [ test-migration-prepare, build ]
environment: environment:
VIKUNJA_DATABASE_TYPE: sqlite VIKUNJA_DATABASE_TYPE: sqlite
@ -174,7 +175,7 @@ steps:
- name: test-migration-mysql - name: test-migration-mysql
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
depends_on: [ test-migration-prepare, build ] depends_on: [ test-migration-prepare, build ]
environment: environment:
VIKUNJA_DATABASE_TYPE: mysql VIKUNJA_DATABASE_TYPE: mysql
@ -193,7 +194,7 @@ steps:
- name: test-migration-psql - name: test-migration-psql
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
depends_on: [ test-migration-prepare, build ] depends_on: [ test-migration-prepare, build ]
environment: environment:
VIKUNJA_DATABASE_TYPE: postgres VIKUNJA_DATABASE_TYPE: postgres
@ -213,7 +214,7 @@ steps:
- name: test - name: test
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
commands: commands:
@ -224,7 +225,7 @@ steps:
- name: test-sqlite - name: test-sqlite
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1 VIKUNJA_TESTS_USE_CONFIG: 1
@ -241,7 +242,7 @@ steps:
- name: test-mysql - name: test-mysql
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1 VIKUNJA_TESTS_USE_CONFIG: 1
@ -258,7 +259,7 @@ steps:
- name: test-postgres - name: test-postgres
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1 VIKUNJA_TESTS_USE_CONFIG: 1
@ -276,7 +277,7 @@ steps:
- name: integration-test - name: integration-test
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
commands: commands:
@ -287,7 +288,7 @@ steps:
- name: integration-test-sqlite - name: integration-test-sqlite
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1 VIKUNJA_TESTS_USE_CONFIG: 1
@ -304,7 +305,7 @@ steps:
- name: integration-test-mysql - name: integration-test-mysql
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1 VIKUNJA_TESTS_USE_CONFIG: 1
@ -321,7 +322,7 @@ steps:
- name: integration-test-postgres - name: integration-test-postgres
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1 VIKUNJA_TESTS_USE_CONFIG: 1
@ -343,6 +344,7 @@ steps:
######## ########
kind: pipeline kind: pipeline
type: docker
name: release name: release
depends_on: depends_on:
@ -368,7 +370,7 @@ steps:
# compiling the same magefile at the same time. It's also faster if each step does not need to compile it first. # compiling the same magefile at the same time. It's also faster if each step does not need to compile it first.
- name: mage - name: mage
image: vikunja/golang-build:latest image: vikunja/golang-build:latest
pull: true pull: always
environment: environment:
GOPROXY: 'https://goproxy.kolaente.de' GOPROXY: 'https://goproxy.kolaente.de'
commands: commands:
@ -378,7 +380,7 @@ steps:
- name: before-static-build - name: before-static-build
image: techknowlogick/xgo:latest image: techknowlogick/xgo:latest
pull: true pull: always
commands: commands:
- export PATH=$PATH:$GOPATH/bin - export PATH=$PATH:$GOPATH/bin
- go install github.com/magefile/mage - go install github.com/magefile/mage
@ -387,7 +389,7 @@ steps:
- name: static-build-windows - name: static-build-windows
image: techknowlogick/xgo:latest image: techknowlogick/xgo:latest
pull: true pull: always
environment: environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why. # This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
# Leaving this here until we know how to resolve this properly. # Leaving this here until we know how to resolve this properly.
@ -400,7 +402,7 @@ steps:
- name: static-build-linux - name: static-build-linux
image: techknowlogick/xgo:latest image: techknowlogick/xgo:latest
pull: true pull: always
environment: environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why. # This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
# Leaving this here until we know how to resolve this properly. # Leaving this here until we know how to resolve this properly.
@ -413,7 +415,7 @@ steps:
- name: static-build-darwin - name: static-build-darwin
image: techknowlogick/xgo:latest image: techknowlogick/xgo:latest
pull: true pull: always
environment: environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why. # This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
# Leaving this here until we know how to resolve this properly. # Leaving this here until we know how to resolve this properly.
@ -426,7 +428,7 @@ steps:
- name: after-build-compress - name: after-build-compress
image: kolaente/upx image: kolaente/upx
pull: true pull: always
depends_on: depends_on:
- static-build-windows - static-build-windows
- static-build-linux - static-build-linux
@ -436,7 +438,7 @@ steps:
- name: after-build-static - name: after-build-static
image: techknowlogick/xgo:latest image: techknowlogick/xgo:latest
pull: true pull: always
depends_on: depends_on:
- after-build-compress - after-build-compress
commands: commands:
@ -448,7 +450,7 @@ steps:
- name: sign-release - name: sign-release
image: plugins/gpgsign:1 image: plugins/gpgsign:1
pull: true pull: always
depends_on: [ after-build-static ] depends_on: [ after-build-static ]
settings: settings:
key: key:
@ -462,7 +464,7 @@ steps:
# Push the releases to our pseudo-s3-bucket # Push the releases to our pseudo-s3-bucket
- name: release-latest - name: release-latest
image: plugins/s3 image: plugins/s3
pull: true pull: always
settings: settings:
bucket: vikunja-releases bucket: vikunja-releases
access_key: access_key:
@ -484,7 +486,7 @@ steps:
- name: release-version - name: release-version
image: plugins/s3 image: plugins/s3
pull: true pull: always
settings: settings:
bucket: vikunja-releases bucket: vikunja-releases
access_key: access_key:
@ -504,8 +506,8 @@ steps:
# Build os packages and push it to our bucket # Build os packages and push it to our bucket
- name: build-os-packages-unstable - name: build-os-packages-unstable
image: goreleaser/nfpm:v2.22.2 image: goreleaser/nfpm:v2.23.0
pull: true pull: always
commands: commands:
- apk add git go - apk add git go
- ./mage-static release:packages - ./mage-static release:packages
@ -520,8 +522,8 @@ steps:
depends_on: [ after-build-compress ] depends_on: [ after-build-compress ]
- name: build-os-packages-version - name: build-os-packages-version
image: goreleaser/nfpm:v2.22.2 image: goreleaser/nfpm:v2.23.0
pull: true pull: always
commands: commands:
- apk add git go - apk add git go
- ./mage-static release:packages - ./mage-static release:packages
@ -536,7 +538,7 @@ steps:
# Push the os releases to our pseudo-s3-bucket # Push the os releases to our pseudo-s3-bucket
- name: release-os-latest - name: release-os-latest
image: plugins/s3 image: plugins/s3
pull: true pull: always
settings: settings:
bucket: vikunja-releases bucket: vikunja-releases
access_key: access_key:
@ -558,7 +560,7 @@ steps:
- name: release-os-version - name: release-os-version
image: plugins/s3 image: plugins/s3
pull: true pull: always
settings: settings:
bucket: vikunja-releases bucket: vikunja-releases
access_key: access_key:
@ -578,6 +580,7 @@ steps:
--- ---
kind: pipeline kind: pipeline
type: docker
name: deploy-docs name: deploy-docs
workspace: workspace:
@ -596,8 +599,7 @@ trigger:
steps: steps:
- name: theme - name: theme
image: kolaente/toolbox image: kolaente/toolbox
pull: true pull: always
group: build-static
commands: commands:
- mkdir docs/themes/vikunja -p - mkdir docs/themes/vikunja -p
- cd docs/themes/vikunja - cd docs/themes/vikunja
@ -605,8 +607,8 @@ steps:
- tar -xzf vikunja-theme.tar.gz - tar -xzf vikunja-theme.tar.gz
- name: build - name: build
image: klakegg/hugo:0.104.2 image: klakegg/hugo:0.107.0
pull: true pull: always
commands: commands:
- cd docs - cd docs
- hugo - hugo
@ -614,7 +616,7 @@ steps:
- name: docker - name: docker
image: plugins/docker image: plugins/docker
pull: true pull: always
settings: settings:
username: username:
from_secret: docker_username from_secret: docker_username
@ -646,7 +648,7 @@ steps:
- name: docker-unstable - name: docker-unstable
image: thegeeklab/drone-docker-buildx image: thegeeklab/drone-docker-buildx
privileged: true privileged: true
pull: true pull: always
settings: settings:
username: username:
from_secret: docker_username from_secret: docker_username
@ -665,24 +667,33 @@ steps:
ref: ref:
- refs/heads/main - refs/heads/main
- name: generate-tags
image: thegeeklab/docker-autotag
environment:
DOCKER_AUTOTAG_VERSION: ${DRONE_TAG}
DOCKER_AUTOTAG_EXTRA_TAGS: latest
depends_on: [ fetch-tags ]
when:
ref:
- "refs/tags/**"
- name: docker-release - name: docker-release
image: thegeeklab/drone-docker-buildx image: thegeeklab/drone-docker-buildx
privileged: true privileged: true
pull: true pull: always
settings: settings:
username: username:
from_secret: docker_username from_secret: docker_username
password: password:
from_secret: docker_password from_secret: docker_password
repo: vikunja/api repo: vikunja/api
auto_tag: true
platforms: platforms:
- linux/386 - linux/386
- linux/amd64 - linux/amd64
- linux/arm/v6 - linux/arm/v6
- linux/arm/v7 - linux/arm/v7
- linux/arm64/v8 - linux/arm64/v8
depends_on: [ fetch-tags ] depends_on: [ generate-tags ]
when: when:
ref: ref:
- "refs/tags/**" - "refs/tags/**"
@ -719,6 +730,6 @@ steps:
- failure - failure
--- ---
kind: signature kind: signature
hmac: f8ce17f7158088a124039f579ba10364788d306feac3feeb51689dce440d6213 hmac: ede99d3c09466ea04c070a3cf75b454a232c42e2a46a5c5835135267d50a48e7
... ...

View File

@ -10,7 +10,7 @@ menu:
# Full docker example # Full docker example
This docker compose configuration will run Vikunja with backend and frontend with a mariadb as database. This docker compose configuration will run Vikunja with backend and frontend with a mariadb database.
It uses an nginx container or traefik on the host to proxy backend and frontend into a single port. It uses an nginx container or traefik on the host to proxy backend and frontend into a single port.
For all available configuration options, see [configuration]({{< ref "config.md">}}). For all available configuration options, see [configuration]({{< ref "config.md">}}).
@ -76,7 +76,7 @@ This example lets you host Vikunja without any reverse proxy in front of it. Thi
you need to get something up and running. If you want to host Vikunja on one single port instead of two different ones you need to get something up and running. If you want to host Vikunja on one single port instead of two different ones
or need tls termination, check out one of the other examples. or need tls termination, check out one of the other examples.
Not that you need to change the `VIKUNJA_API_URL` environment variable to the ip (the docker host you're running this on) Note that you need to change the `VIKUNJA_API_URL` environment variable to the ip (the docker host you're running this on)
is reachable at. Because the browser you'll use to access the Vikunja frontend uses that url to make the requests, it is reachable at. Because the browser you'll use to access the Vikunja frontend uses that url to make the requests, it
has to be able to reach that ip + port from the outside. Putting everything in a private network won't work. has to be able to reach that ip + port from the outside. Putting everything in a private network won't work.
@ -125,7 +125,7 @@ services:
This example assumes [traefik](https://traefik.io) version 2 installed and configured to [use docker as a configuration provider](https://docs.traefik.io/providers/docker/). This example assumes [traefik](https://traefik.io) version 2 installed and configured to [use docker as a configuration provider](https://docs.traefik.io/providers/docker/).
We also make a few assumtions here which you'll most likely need to adjust for your traefik setup: We also make a few assumptions here which you'll most likely need to adjust for your traefik setup:
* Your domain is `vikunja.example.com` * Your domain is `vikunja.example.com`
* The entrypoint you want to make vikunja available from is called `https` * The entrypoint you want to make vikunja available from is called `https`
@ -398,7 +398,7 @@ docker main folders:
* vikunja * vikunja
* mariadb * mariadb
Synology has it's own GUI for managing Docker containers... But it's easier via docker compose. Synology has its own GUI for managing Docker containers... But it's easier via docker compose.
To do that, you can To do that, you can
@ -407,7 +407,7 @@ To do that, you can
* without activating SSH, by using Portainer (you have to install first, check out [this tutorial](https://www.portainer.io/blog/how-to-install-portainer-on-a-synology-nas) for exmple): * without activating SSH, by using Portainer (you have to install first, check out [this tutorial](https://www.portainer.io/blog/how-to-install-portainer-on-a-synology-nas) for exmple):
1. Go to **Dashboard / Stacks** click the button **"Add Stack"** 1. Go to **Dashboard / Stacks** click the button **"Add Stack"**
2. Give it the name Vikunja and paste the adapted docker compose file 2. Give it the name Vikunja and paste the adapted docker compose file
3. Deploy the Stack with the "Delpoy Stack" button: 3. Deploy the Stack with the "Deploy Stack" button:
![Portainer Stack deploy](/docs/synology-proxy-2.png) ![Portainer Stack deploy](/docs/synology-proxy-2.png)

19
go.mod
View File

@ -31,17 +31,18 @@ require (
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0 github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0
github.com/gabriel-vasile/mimetype v1.4.1 github.com/gabriel-vasile/mimetype v1.4.1
github.com/getsentry/sentry-go v0.16.0 github.com/getsentry/sentry-go v0.17.0
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
github.com/go-sql-driver/mysql v1.7.0 github.com/go-sql-driver/mysql v1.7.0
github.com/go-testfixtures/testfixtures/v3 v3.8.1 github.com/go-testfixtures/testfixtures/v3 v3.8.1
github.com/gocarina/gocsv v0.0.0-20221216233619-1fea7ae8d380
github.com/golang-jwt/jwt/v4 v4.4.3 github.com/golang-jwt/jwt/v4 v4.4.3
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/iancoleman/strcase v0.2.0 github.com/iancoleman/strcase v0.2.0
github.com/imdario/mergo v0.3.13 github.com/imdario/mergo v0.3.13
github.com/jinzhu/copier v0.3.5 github.com/jinzhu/copier v0.3.5
github.com/labstack/echo-jwt/v4 v4.0.0 github.com/labstack/echo-jwt/v4 v4.0.1
github.com/labstack/echo/v4 v4.10.0 github.com/labstack/echo/v4 v4.10.0
github.com/labstack/gommon v0.4.0 github.com/labstack/gommon v0.4.0
github.com/lib/pq v1.10.7 github.com/lib/pq v1.10.7
@ -55,15 +56,15 @@ require (
github.com/samedi/caldav-go v3.0.0+incompatible github.com/samedi/caldav-go v3.0.0+incompatible
github.com/spf13/afero v1.9.3 github.com/spf13/afero v1.9.3
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.14.0 github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
github.com/swaggo/swag v1.8.9 github.com/swaggo/swag v1.8.9
github.com/tkuchiki/go-timezone v0.2.2 github.com/tkuchiki/go-timezone v0.2.2
github.com/ulule/limiter/v3 v3.10.0 github.com/ulule/limiter/v3 v3.10.0
github.com/vectordotdev/go-datemath v0.1.1-0.20211214182920-0a4ac8742b93 github.com/vectordotdev/go-datemath v0.1.1-0.20211214182920-0a4ac8742b93
github.com/wneessen/go-mail v0.3.7 github.com/wneessen/go-mail v0.3.8
github.com/yuin/goldmark v1.5.3 github.com/yuin/goldmark v1.5.3
golang.org/x/crypto v0.4.0 golang.org/x/crypto v0.5.0
golang.org/x/image v0.3.0 golang.org/x/image v0.3.0
golang.org/x/oauth2 v0.4.0 golang.org/x/oauth2 v0.4.0
golang.org/x/sync v0.1.0 golang.org/x/sync v0.1.0
@ -71,7 +72,7 @@ require (
golang.org/x/term v0.4.0 golang.org/x/term v0.4.0
gopkg.in/d4l3k/messagediff.v1 v1.2.1 gopkg.in/d4l3k/messagediff.v1 v1.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
src.techknowlogick.com/xgo v1.5.1-0.20220906164532-735bfdfb90d9 src.techknowlogick.com/xgo v1.7.1-0.20230117190652-94aee174ab86
src.techknowlogick.com/xormigrate v1.5.0 src.techknowlogick.com/xormigrate v1.5.0
xorm.io/builder v0.3.12 xorm.io/builder v0.3.12
xorm.io/xorm v1.3.2 xorm.io/xorm v1.3.2
@ -110,7 +111,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef // indirect github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef // indirect
github.com/lithammer/shortuuid/v3 v3.0.4 // indirect github.com/lithammer/shortuuid/v3 v3.0.4 // indirect
github.com/magiconair/properties v1.8.6 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-isatty v0.0.16 // indirect
@ -121,7 +122,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect github.com/oklog/ulid v1.3.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
@ -131,7 +132,7 @@ require (
github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.1 // indirect github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect github.com/urfave/cli/v2 v2.3.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect

22
go.sum
View File

@ -209,6 +209,8 @@ github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/getsentry/sentry-go v0.16.0 h1:owk+S+5XcgJLlGR/3+3s6N4d+uKwqYvh/eS0AIMjPWo= github.com/getsentry/sentry-go v0.16.0 h1:owk+S+5XcgJLlGR/3+3s6N4d+uKwqYvh/eS0AIMjPWo=
github.com/getsentry/sentry-go v0.16.0/go.mod h1:ZXCloQLj0pG7mja5NK6NPf2V4A88YJ4pNlc2mOHwh6Y= github.com/getsentry/sentry-go v0.16.0/go.mod h1:ZXCloQLj0pG7mja5NK6NPf2V4A88YJ4pNlc2mOHwh6Y=
github.com/getsentry/sentry-go v0.17.0 h1:UustVWnOoDFHBS7IJUB2QK/nB5pap748ZEp0swnQJak=
github.com/getsentry/sentry-go v0.17.0/go.mod h1:B82dxtBvxG0KaPD8/hfSV+VcHD+Lg/xUS4JuQn1P4cM=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs=
@ -249,6 +251,8 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-testfixtures/testfixtures/v3 v3.8.1 h1:uonwvepqRvSgddcrReZQhojTlWlmOlHkYAb9ZaOMWgU= github.com/go-testfixtures/testfixtures/v3 v3.8.1 h1:uonwvepqRvSgddcrReZQhojTlWlmOlHkYAb9ZaOMWgU=
github.com/go-testfixtures/testfixtures/v3 v3.8.1/go.mod h1:Kdu7YeMC0KRXVHdaQ91Vmx3pcjoTF63h4f1qTJDdXLA= github.com/go-testfixtures/testfixtures/v3 v3.8.1/go.mod h1:Kdu7YeMC0KRXVHdaQ91Vmx3pcjoTF63h4f1qTJDdXLA=
github.com/gocarina/gocsv v0.0.0-20221216233619-1fea7ae8d380 h1:JJq8YZiS07gFIMYZxkbbiMrXIglG3k5JPPtdvckcnfQ=
github.com/gocarina/gocsv v0.0.0-20221216233619-1fea7ae8d380/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 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 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@ -505,6 +509,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo-jwt/v4 v4.0.0 h1:MFdURJRtBNWzADUdXYlj++71UZ5MmjUtce7nSsCH8NY= github.com/labstack/echo-jwt/v4 v4.0.0 h1:MFdURJRtBNWzADUdXYlj++71UZ5MmjUtce7nSsCH8NY=
github.com/labstack/echo-jwt/v4 v4.0.0/go.mod h1:DHSSaL6cTgczdPXjf8qrTHRbrau2flcddV7CPMs2U/Y= github.com/labstack/echo-jwt/v4 v4.0.0/go.mod h1:DHSSaL6cTgczdPXjf8qrTHRbrau2flcddV7CPMs2U/Y=
github.com/labstack/echo-jwt/v4 v4.0.1 h1:rxFj0gUPv+1EEhbyfpv463FunuNvW+6MDRGYve7LUxM=
github.com/labstack/echo-jwt/v4 v4.0.1/go.mod h1:DHSSaL6cTgczdPXjf8qrTHRbrau2flcddV7CPMs2U/Y=
github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA= github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ= github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ=
@ -530,6 +536,8 @@ github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 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.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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@ -624,6 +632,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@ -720,6 +730,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/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= 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/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 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/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= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
@ -740,6 +752,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/swaggo/swag v1.8.9 h1:kHtaBe/Ob9AZzAANfcn5c6RyCke9gG9QpH0jky0I/sA= github.com/swaggo/swag v1.8.9 h1:kHtaBe/Ob9AZzAANfcn5c6RyCke9gG9QpH0jky0I/sA=
github.com/swaggo/swag v1.8.9/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk= github.com/swaggo/swag v1.8.9/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
@ -767,6 +781,8 @@ github.com/wneessen/go-mail v0.3.6 h1:hT8PMIBdcTkoiDwoUGJssPYOe1Gg1/cUcp2o9+ls63
github.com/wneessen/go-mail v0.3.6/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E= github.com/wneessen/go-mail v0.3.6/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
github.com/wneessen/go-mail v0.3.7 h1:loEAGLvsDZLSiE6c+keBfg0gpias/R3ocFU8Eoh3Pq4= github.com/wneessen/go-mail v0.3.7 h1:loEAGLvsDZLSiE6c+keBfg0gpias/R3ocFU8Eoh3Pq4=
github.com/wneessen/go-mail v0.3.7/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E= github.com/wneessen/go-mail v0.3.7/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
github.com/wneessen/go-mail v0.3.8 h1:ja5D/o/RVwrtRIYFlrO7GmtcjDNeMakGQuwQRZYv0JM=
github.com/wneessen/go-mail v0.3.8/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 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.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -824,6 +840,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/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.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1508,6 +1526,10 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
src.techknowlogick.com/xgo v1.5.1-0.20220906164532-735bfdfb90d9 h1:7u6pOCURebyXlDy0OhWdnsEBf/KgVKnExA9/w/yCT0A= src.techknowlogick.com/xgo v1.5.1-0.20220906164532-735bfdfb90d9 h1:7u6pOCURebyXlDy0OhWdnsEBf/KgVKnExA9/w/yCT0A=
src.techknowlogick.com/xgo v1.5.1-0.20220906164532-735bfdfb90d9/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU= src.techknowlogick.com/xgo v1.5.1-0.20220906164532-735bfdfb90d9/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xgo v1.6.1-0.20230110184414-1fd3d5b59de3 h1:y5MVUua3J82o91nQk1gBGrJA1FJn1X6sLXar4Ec08W8=
src.techknowlogick.com/xgo v1.6.1-0.20230110184414-1fd3d5b59de3/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xgo v1.7.1-0.20230117190652-94aee174ab86 h1:VybPMHRdCLbdCttI8fMXOaGpoJGSG9+W/5cfRgr1Xjc=
src.techknowlogick.com/xgo v1.7.1-0.20230117190652-94aee174ab86/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xormigrate v1.5.0 h1:6mWTh8d0sWjMTLUgJqiLe0e0Teu+1j+RgI7ErAeOEV0= src.techknowlogick.com/xormigrate v1.5.0 h1:6mWTh8d0sWjMTLUgJqiLe0e0Teu+1j+RgI7ErAeOEV0=
src.techknowlogick.com/xormigrate v1.5.0/go.mod h1:QOCnBeWralVncPn9eZlM4w/rglFK8o1vYpemzPenkBM= src.techknowlogick.com/xormigrate v1.5.0/go.mod h1:QOCnBeWralVncPn9eZlM4w/rglFK8o1vYpemzPenkBM=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=

View File

@ -225,7 +225,7 @@ var userUpdateCmd = &cobra.Command{
u.AvatarProvider = userFlagAvatar u.AvatarProvider = userFlagAvatar
} }
_, err := user.UpdateUser(s, u) _, err := user.UpdateUser(s, u, false)
if err != nil { if err != nil {
_ = s.Rollback() _ = s.Rollback()
log.Fatalf("Error updating the user: %s", err) log.Fatalf("Error updating the user: %s", err)
@ -299,7 +299,7 @@ var userChangeEnabledCmd = &cobra.Command{
u.Status = user.StatusActive u.Status = user.StatusActive
} }
} }
_, err := user.UpdateUser(s, u) _, err := user.UpdateUser(s, u, false)
if err != nil { if err != nil {
_ = s.Rollback() _ = s.Rollback()
log.Fatalf("Could not enable the user") log.Fatalf("Could not enable the user")

View File

@ -83,7 +83,7 @@ func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, m
s := db.NewSession() s := db.NewSession()
defer s.Close() defer s.Close()
file, err = CreateWithMimeAndSession(s, f, realname, realsize, a, mime) file, err = CreateWithMimeAndSession(s, f, realname, realsize, a, mime, true)
if err != nil { if err != nil {
_ = s.Rollback() _ = s.Rollback()
return return
@ -91,14 +91,14 @@ func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, m
return return
} }
func CreateWithMimeAndSession(s *xorm.Session, f io.Reader, realname string, realsize uint64, a web.Auth, mime string) (file *File, err error) { func CreateWithMimeAndSession(s *xorm.Session, f io.Reader, realname string, realsize uint64, a web.Auth, mime string, checkFileSizeLimit bool) (file *File, err error) {
// Get and parse the configured file size // Get and parse the configured file size
var maxSize datasize.ByteSize var maxSize datasize.ByteSize
err = maxSize.UnmarshalText([]byte(config.FilesMaxSize.GetString())) err = maxSize.UnmarshalText([]byte(config.FilesMaxSize.GetString()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if realsize > maxSize.Bytes() { if realsize > maxSize.Bytes() && checkFileSizeLimit {
return nil, ErrFileIsTooLarge{Size: realsize} return nil, ErrFileIsTooLarge{Size: realsize}
} }

View File

@ -58,6 +58,10 @@ func getClient() (*mail.Client, error) {
mail.WithTimeout((config.MailerQueueTimeout.GetDuration() + 3) * time.Second), // 3s more for us to close before mail server timeout mail.WithTimeout((config.MailerQueueTimeout.GetDuration() + 3) * time.Second), // 3s more for us to close before mail server timeout
} }
if config.MailerForceSSL.GetBool() {
opts = append(opts, mail.WithSSL())
}
if config.MailerUsername.GetString() != "" && config.MailerPassword.GetString() != "" { if config.MailerUsername.GetString() != "" && config.MailerPassword.GetString() != "" {
opts = append(opts, mail.WithSMTPAuth(authType)) opts = append(opts, mail.WithSMTPAuth(authType))
} }

View File

@ -97,7 +97,7 @@ func ExportUserData(s *xorm.Session, u *user.User) (err error) {
return err return err
} }
exportFile, err := files.CreateWithMimeAndSession(s, exported, tmpFilename, uint64(stat.Size()), u, "application/zip") exportFile, err := files.CreateWithMimeAndSession(s, exported, tmpFilename, uint64(stat.Size()), u, "application/zip", false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -817,13 +817,13 @@ func checkBucketLimit(s *xorm.Session, t *Task, bucket *Bucket) (err error) {
} }
// Contains all the task logic to figure out what bucket to use for this task. // Contains all the task logic to figure out what bucket to use for this task.
func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucketLimit bool) (err error) { func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucketLimit bool) (targetBucket *Bucket, err error) {
// Make sure we have a bucket // Make sure we have a bucket
var bucket *Bucket var bucket *Bucket
if task.Done && originalTask != nil && !originalTask.Done { if task.Done && originalTask != nil && !originalTask.Done {
bucket, err := getDoneBucketForList(s, task.ListID) bucket, err := getDoneBucketForList(s, task.ListID)
if err != nil { if err != nil {
return err return nil, err
} }
if bucket != nil { if bucket != nil {
task.BucketID = bucket.ID task.BucketID = bucket.ID
@ -838,7 +838,7 @@ func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucke
if task.BucketID == 0 || (originalTask != nil && task.ListID != 0 && originalTask.ListID != task.ListID) { if task.BucketID == 0 || (originalTask != nil && task.ListID != 0 && originalTask.ListID != task.ListID) {
bucket, err = getDefaultBucket(s, task.ListID) bucket, err = getDefaultBucket(s, task.ListID)
if err != nil { if err != nil {
return err return
} }
task.BucketID = bucket.ID task.BucketID = bucket.ID
} }
@ -846,7 +846,7 @@ func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucke
if bucket == nil { if bucket == nil {
bucket, err = getBucketByID(s, task.BucketID) bucket, err = getBucketByID(s, task.BucketID)
if err != nil { if err != nil {
return err return
} }
} }
@ -860,7 +860,7 @@ func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucke
// Only check the bucket limit if the task is being moved between buckets, allow reordering the task within a bucket // Only check the bucket limit if the task is being moved between buckets, allow reordering the task within a bucket
if doCheckBucketLimit { if doCheckBucketLimit {
if err := checkBucketLimit(s, task, bucket); err != nil { if err := checkBucketLimit(s, task, bucket); err != nil {
return err return nil, err
} }
} }
@ -868,7 +868,7 @@ func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucke
task.Done = true task.Done = true
} }
return nil return bucket, nil
} }
func calculateDefaultPosition(entityID int64, position float64) float64 { func calculateDefaultPosition(entityID int64, position float64) float64 {
@ -937,7 +937,7 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
} }
// Get the default bucket and move the task there // Get the default bucket and move the task there
err = setTaskBucket(s, t, nil, true) _, err = setTaskBucket(s, t, nil, true)
if err != nil { if err != nil {
return return
} }
@ -1028,13 +1028,21 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
ot.Reminders[i] = r.Reminder ot.Reminders[i] = r.Reminder
} }
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone targetBucket, err := setTaskBucket(s, t, &ot, t.BucketID != 0 && t.BucketID != ot.BucketID)
updateDone(&ot, t) if err != nil {
if err := setTaskBucket(s, t, &ot, t.BucketID != 0 && t.BucketID != ot.BucketID); err != nil {
return err return err
} }
// If the task was moved into the done bucket and the task has a repeating cycle we should not update
// the bucket.
if targetBucket.IsDoneBucket && t.RepeatAfter > 0 {
t.Done = true // This will trigger the correct re-scheduling of the task (happening in updateDone later)
t.BucketID = ot.BucketID
}
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
updateDone(&ot, t)
// Update the assignees // Update the assignees
if err := ot.updateTaskAssignees(s, t.Assignees, a); err != nil { if err := ot.updateTaskAssignees(s, t.Assignees, a); err != nil {
return err return err
@ -1411,9 +1419,9 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro
} }
// Resolve duplicates and sort them // Resolve duplicates and sort them
reminderMap := make(map[string]time.Time, len(reminders)) reminderMap := make(map[int64]time.Time, len(reminders))
for _, reminder := range reminders { for _, reminder := range reminders {
reminderMap[reminder.UTC().String()] = reminder reminderMap[reminder.UTC().Unix()] = reminder
} }
// Loop through all reminders and add them // Loop through all reminders and add them

View File

@ -243,6 +243,33 @@ func TestTask_Update(t *testing.T) {
"bucket_id": 3, "bucket_id": 3,
}, false) }, false)
}) })
t.Run("moving a repeating task to the done bucket", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
task := &Task{
ID: 28,
Title: "test updated",
ListID: 1,
BucketID: 3, // Bucket 3 is the done bucket
RepeatAfter: 3600,
}
err := task.Update(s, u)
assert.NoError(t, err)
err = s.Commit()
assert.NoError(t, err)
assert.False(t, task.Done)
assert.Equal(t, int64(1), task.BucketID) // Bucket should not be updated
db.AssertExists(t, "tasks", map[string]interface{}{
"id": 28,
"done": false,
"title": "test updated",
"list_id": 1,
"bucket_id": 1,
}, false)
})
t.Run("default bucket when moving a task between lists", func(t *testing.T) { t.Run("default bucket when moving a task between lists", func(t *testing.T) {
db.LoadAndAssertFixtures(t) db.LoadAndAssertFixtures(t)
s := db.NewSession() s := db.NewSession()
@ -308,8 +335,9 @@ func TestTask_Update(t *testing.T) {
defer s.Close() defer s.Close()
task := &Task{ task := &Task{
ID: 28, ID: 28,
Done: true, Done: true,
RepeatAfter: 3600,
} }
err := task.Update(s, u) err := task.Update(s, u)
assert.NoError(t, err) assert.NoError(t, err)

View File

@ -267,7 +267,7 @@ func getOrCreateUser(s *xorm.Session, cl *claims, issuer, subject string) (u *us
Name: u.Name, Name: u.Name,
Issuer: issuer, Issuer: issuer,
Subject: subject, Subject: subject,
}) }, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -56,12 +56,22 @@ func DownloadFileWithHeaders(url string, headers http.Header) (buf *bytes.Buffer
// DoPost makes a form encoded post request // DoPost makes a form encoded post request
func DoPost(url string, form url.Values) (resp *http.Response, err error) { func DoPost(url string, form url.Values) (resp *http.Response, err error) {
return DoPostWithHeaders(url, form, map[string]string{})
}
// DoPostWithHeaders does an api request and allows to pass in arbitrary headers
func DoPostWithHeaders(url string, form url.Values, headers map[string]string) (resp *http.Response, err error) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, url, strings.NewReader(form.Encode())) req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, url, strings.NewReader(form.Encode()))
if err != nil { if err != nil {
return return
} }
req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
for key, value := range headers {
req.Header.Add(key, value)
}
hc := http.Client{} hc := http.Client{}
return hc.Do(req) return hc.Do(req)
} }

View File

@ -27,10 +27,11 @@ import (
"time" "time"
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/migration" "code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/user" "code.vikunja.io/api/pkg/user"
"github.com/gocarina/gocsv"
) )
const timeISO = "2006-01-02T15:04:05-0700" const timeISO = "2006-01-02T15:04:05-0700"
@ -39,23 +40,39 @@ type Migrator struct {
} }
type tickTickTask struct { type tickTickTask struct {
FolderName string FolderName string `csv:"Folder Name"`
ListName string ListName string `csv:"List Name"`
Title string Title string `csv:"Title"`
Tags []string TagsList string `csv:"Tags"`
Content string Tags []string `csv:"-"`
IsChecklist bool Content string `csv:"Content"`
StartDate time.Time IsChecklistString string `csv:"Is Check list"`
DueDate time.Time IsChecklist bool `csv:"-"`
Reminder time.Duration StartDate tickTickTime `csv:"Start Date"`
Repeat string DueDate tickTickTime `csv:"Due Date"`
Priority int ReminderDuration string `csv:"Reminder"`
Status string Reminder time.Duration `csv:"-"`
CreatedTime time.Time Repeat string `csv:"Repeat"`
CompletedTime time.Time Priority int `csv:"Priority"`
Order float64 Status string `csv:"Status"`
TaskID int64 CreatedTime tickTickTime `csv:"Created Time"`
ParentID int64 CompletedTime tickTickTime `csv:"Completed Time"`
Order float64 `csv:"Order"`
TaskID int64 `csv:"taskId"`
ParentID int64 `csv:"parentId"`
}
type tickTickTime struct {
time.Time
}
func (date *tickTickTime) UnmarshalCSV(csv string) (err error) {
date.Time = time.Time{}
if csv == "" {
return nil
}
date.Time, err = time.Parse(timeISO, csv)
return err
} }
// Copied from https://stackoverflow.com/a/57617885 // Copied from https://stackoverflow.com/a/57617885
@ -119,19 +136,22 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.Namespace
ID: t.TaskID, ID: t.TaskID,
Title: t.Title, Title: t.Title,
Description: t.Content, Description: t.Content,
StartDate: t.StartDate, StartDate: t.StartDate.Time,
EndDate: t.DueDate, EndDate: t.DueDate.Time,
DueDate: t.DueDate, DueDate: t.DueDate.Time,
Reminders: []time.Time{ Done: t.Status == "1",
t.DueDate.Add(t.Reminder * -1), DoneAt: t.CompletedTime.Time,
}, Position: t.Order,
Done: t.Status == "1", Labels: labels,
DoneAt: t.CompletedTime,
Position: t.Order,
Labels: labels,
}, },
} }
if !t.DueDate.IsZero() && t.Reminder > 0 {
task.Task.Reminders = []time.Time{
t.DueDate.Add(t.Reminder * -1),
}
}
if t.ParentID != 0 { if t.ParentID != 0 {
task.RelatedTasks = map[models.RelationKind][]*models.Task{ task.RelatedTasks = map[models.RelationKind][]*models.Task{
models.RelationKindParenttask: {{ID: t.ParentID}}, models.RelationKindParenttask: {{ID: t.ParentID}},
@ -165,6 +185,22 @@ func (m *Migrator) Name() string {
return "ticktick" return "ticktick"
} }
func newLineSkipDecoder(r io.Reader, linesToSkip int) gocsv.SimpleDecoder {
reader := csv.NewReader(r)
// reader.FieldsPerRecord = -1
for i := 0; i < linesToSkip; i++ {
_, err := reader.Read()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
log.Debugf("[TickTick Migration] CSV parse error: %s", err)
}
}
reader.FieldsPerRecord = 0
return gocsv.NewSimpleDecoderFromCSVReader(reader)
}
// Migrate takes a ticktick export, parses it and imports everything in it into Vikunja. // Migrate takes a ticktick export, parses it and imports everything in it into Vikunja.
// @Summary Import all lists, tasks etc. from a TickTick backup export // @Summary Import all lists, tasks etc. from a TickTick backup export
// @Description Imports all projects, tasks, notes, reminders, subtasks and files from a TickTick backup export into Vikunja. // @Description Imports all projects, tasks, notes, reminders, subtasks and files from a TickTick backup export into Vikunja.
@ -178,85 +214,26 @@ func (m *Migrator) Name() string {
// @Router /migration/ticktick/migrate [post] // @Router /migration/ticktick/migrate [post]
func (m *Migrator) Migrate(user *user.User, file io.ReaderAt, size int64) error { func (m *Migrator) Migrate(user *user.User, file io.ReaderAt, size int64) error {
fr := io.NewSectionReader(file, 0, size) fr := io.NewSectionReader(file, 0, size)
r := csv.NewReader(fr) //r := csv.NewReader(fr)
allTasks := []*tickTickTask{} allTasks := []*tickTickTask{}
line := 0 decode := newLineSkipDecoder(fr, 3)
for { err := gocsv.UnmarshalDecoder(decode, &allTasks)
if err != nil {
return err
}
record, err := r.Read() for _, task := range allTasks {
if err != nil { if task.IsChecklistString == "Y" {
if errors.Is(err, io.EOF) { task.IsChecklist = true
break
}
log.Debugf("[TickTick Migration] CSV parse error: %s", err)
} }
line++ reminder := parseDuration(task.ReminderDuration)
if line <= 4 { if reminder > 0 {
continue task.Reminder = reminder
} }
priority, err := strconv.Atoi(record[10]) task.Tags = strings.Split(task.TagsList, ", ")
if err != nil {
return err
}
order, err := strconv.ParseFloat(record[14], 64)
if err != nil {
return err
}
taskID, err := strconv.ParseInt(record[21], 10, 64)
if err != nil {
return err
}
parentID, err := strconv.ParseInt(record[21], 10, 64)
if err != nil {
return err
}
reminder := parseDuration(record[8])
t := &tickTickTask{
ListName: record[1],
Title: record[2],
Tags: strings.Split(record[3], ", "),
Content: record[4],
IsChecklist: record[5] == "Y",
Reminder: reminder,
Repeat: record[9],
Priority: priority,
Status: record[11],
Order: order,
TaskID: taskID,
ParentID: parentID,
}
if record[6] != "" {
t.StartDate, err = time.Parse(timeISO, record[6])
if err != nil {
return err
}
}
if record[7] != "" {
t.DueDate, err = time.Parse(timeISO, record[7])
if err != nil {
return err
}
}
if record[12] != "" {
t.StartDate, err = time.Parse(timeISO, record[12])
if err != nil {
return err
}
}
if record[13] != "" {
t.CompletedTime, err = time.Parse(timeISO, record[13])
if err != nil {
return err
}
}
allTasks = append(allTasks, t)
} }
vikunjaTasks := convertTickTickToVikunja(allTasks) vikunjaTasks := convertTickTickToVikunja(allTasks)

View File

@ -26,12 +26,15 @@ import (
) )
func TestConvertTicktickTasksToVikunja(t *testing.T) { func TestConvertTicktickTasksToVikunja(t *testing.T) {
time1, err := time.Parse(time.RFC3339Nano, "2022-11-18T03:00:00.4770000Z") t1, err := time.Parse(time.RFC3339Nano, "2022-11-18T03:00:00.4770000Z")
require.NoError(t, err) require.NoError(t, err)
time2, err := time.Parse(time.RFC3339Nano, "2022-12-18T03:00:00.4770000Z") time1 := tickTickTime{Time: t1}
t2, err := time.Parse(time.RFC3339Nano, "2022-12-18T03:00:00.4770000Z")
require.NoError(t, err) require.NoError(t, err)
time3, err := time.Parse(time.RFC3339Nano, "2022-12-10T03:00:00.4770000Z") time2 := tickTickTime{Time: t2}
t3, err := time.Parse(time.RFC3339Nano, "2022-12-10T03:00:00.4770000Z")
require.NoError(t, err) require.NoError(t, err)
time3 := tickTickTime{Time: t3}
duration, err := time.ParseDuration("24h") duration, err := time.ParseDuration("24h")
require.NoError(t, err) require.NoError(t, err)
@ -91,9 +94,9 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Title, tickTickTasks[0].Title) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Title, tickTickTasks[0].Title)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Description, tickTickTasks[0].Content) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Description, tickTickTasks[0].Content)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].StartDate, tickTickTasks[0].StartDate) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].StartDate, tickTickTasks[0].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].EndDate, tickTickTasks[0].DueDate) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].EndDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].DueDate, tickTickTasks[0].DueDate) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].DueDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Labels, []*models.Label{ assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Labels, []*models.Label{
{Title: "label1"}, {Title: "label1"},
{Title: "label2"}, {Title: "label2"},
@ -105,7 +108,7 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Title, tickTickTasks[1].Title) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Title, tickTickTasks[1].Title)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Position, tickTickTasks[1].Order) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Position, tickTickTasks[1].Order)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Done, true) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Done, true)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].DoneAt, tickTickTasks[1].CompletedTime) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].DoneAt, tickTickTasks[1].CompletedTime.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].RelatedTasks, models.RelatedTaskMap{ assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].RelatedTasks, models.RelatedTaskMap{
models.RelationKindParenttask: []*models.Task{ models.RelationKindParenttask: []*models.Task{
{ {
@ -116,9 +119,9 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Title, tickTickTasks[2].Title) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Title, tickTickTasks[2].Title)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Description, tickTickTasks[2].Content) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Description, tickTickTasks[2].Content)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].StartDate, tickTickTasks[2].StartDate) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].StartDate, tickTickTasks[2].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].EndDate, tickTickTasks[2].DueDate) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].EndDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].DueDate, tickTickTasks[2].DueDate) assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].DueDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Labels, []*models.Label{ assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Labels, []*models.Label{
{Title: "label1"}, {Title: "label1"},
{Title: "label2"}, {Title: "label2"},

View File

@ -20,6 +20,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"net/url" "net/url"
"sort" "sort"
"strconv" "strconv"
@ -76,7 +77,6 @@ type dueDate struct {
type item struct { type item struct {
ID string `json:"id"` ID string `json:"id"`
LegacyID string `json:"legacy_id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
ProjectID string `json:"project_id"` ProjectID string `json:"project_id"`
Content string `json:"content"` Content string `json:"content"`
@ -310,6 +310,12 @@ func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVi
} }
for _, i := range sync.Items { for _, i := range sync.Items {
if i == nil {
// This should never happen
continue
}
task := &models.TaskWithComments{ task := &models.TaskWithComments{
Task: models.Task{ Task: models.Task{
Title: i.Content, Title: i.Content,
@ -524,11 +530,14 @@ func (m *Migration) Migrate(u *user.User) (err error) {
// Get everything with the sync api // Get everything with the sync api
form := url.Values{ form := url.Values{
"token": []string{token},
"sync_token": []string{"*"}, "sync_token": []string{"*"},
"resource_types": []string{"[\"all\"]"}, "resource_types": []string{"[\"all\"]"},
} }
resp, err := migration.DoPost("https://api.todoist.com/sync/v9/sync", form) bearerHeader := map[string]string{
"Authorization": "Bearer " + token,
}
resp, err := migration.DoPostWithHeaders("https://api.todoist.com/sync/v9/sync", form, bearerHeader)
if err != nil { if err != nil {
return return
} }
@ -547,7 +556,7 @@ func (m *Migration) Migrate(u *user.User) (err error) {
doneItems := make(map[string]*doneItem) doneItems := make(map[string]*doneItem)
for { for {
resp, err = migration.DoPost("https://api.todoist.com/sync/v9/completed/get_all?limit=200&offset="+strconv.Itoa(offset), form) resp, err = migration.DoPostWithHeaders("https://api.todoist.com/sync/v9/completed/get_all?limit=200&offset="+strconv.Itoa(offset), form, bearerHeader)
if err != nil { if err != nil {
return return
} }
@ -571,21 +580,26 @@ func (m *Migration) Migrate(u *user.User) (err error) {
doneItems[i.TaskID] = i doneItems[i.TaskID] = i
// need to get done item data // need to get done item data
resp, err = migration.DoPost("https://api.todoist.com/sync/v9/items/get", url.Values{ resp, err = migration.DoPostWithHeaders("https://api.todoist.com/sync/v9/items/get", url.Values{
"token": []string{token},
"item_id": []string{i.TaskID}, "item_id": []string{i.TaskID},
}) }, bearerHeader)
if err != nil { if err != nil {
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
// Done items of deleted projects may show up here but since the project is already deleted
// we can't show them individually and the api returns a 404.
continue
}
doneI := &itemWrapper{} doneI := &itemWrapper{}
err = json.NewDecoder(resp.Body).Decode(doneI) err = json.NewDecoder(resp.Body).Decode(doneI)
if err != nil { if err != nil {
return return
} }
log.Debugf("[Todoist Migration] Retrieved full task data for done task %s", i.TaskID) log.Debugf("[Todoist Migration] Retrieved full task data for done task %s", i.ID)
syncResponse.Items = append(syncResponse.Items, doneI.Item) syncResponse.Items = append(syncResponse.Items, doneI.Item)
} }
@ -600,7 +614,7 @@ func (m *Migration) Migrate(u *user.User) (err error) {
log.Debugf("[Todoist Migration] Getting archived projects for user %d", u.ID) log.Debugf("[Todoist Migration] Getting archived projects for user %d", u.ID)
// Get all archived projects // Get all archived projects
resp, err = migration.DoPost("https://api.todoist.com/sync/v9/projects/get_archived", form) resp, err = migration.DoPostWithHeaders("https://api.todoist.com/sync/v9/projects/get_archived", form, bearerHeader)
if err != nil { if err != nil {
return return
} }
@ -616,9 +630,9 @@ func (m *Migration) Migrate(u *user.User) (err error) {
log.Debugf("[Todoist Migration] Got %d archived projects for user %d", len(archivedProjects), u.ID) log.Debugf("[Todoist Migration] Got %d archived projects for user %d", len(archivedProjects), u.ID)
log.Debugf("[Todoist Migration] Getting data for archived projects for user %d", u.ID) log.Debugf("[Todoist Migration] Getting data for archived projects for user %d", u.ID)
// Project data is not included in the regular sync for archived projects so we need to get all of those by hand // Project data is not included in the regular sync for archived projects, so we need to get all of those by hand
for _, p := range archivedProjects { for _, p := range archivedProjects {
resp, err = migration.DoPost("https://api.todoist.com/sync/v9/projects/get_data?project_id="+p.ID, form) resp, err = migration.DoPostWithHeaders("https://api.todoist.com/sync/v9/projects/get_data?project_id="+p.ID, form, bearerHeader)
if err != nil { if err != nil {
return return
} }

View File

@ -205,7 +205,7 @@ func UploadAvatar(c echo.Context) (err error) {
u.AvatarFileID = f.ID u.AvatarFileID = f.ID
u.AvatarProvider = "upload" u.AvatarProvider = "upload"
if _, err := user.UpdateUser(s, u); err != nil { if _, err := user.UpdateUser(s, u, false); err != nil {
_ = s.Rollback() _ = s.Rollback()
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
} }

View File

@ -133,7 +133,7 @@ func ChangeUserAvatarProvider(c echo.Context) error {
user.AvatarProvider = uap.AvatarProvider user.AvatarProvider = uap.AvatarProvider
_, err = user2.UpdateUser(s, user) _, err = user2.UpdateUser(s, user, false)
if err != nil { if err != nil {
_ = s.Rollback() _ = s.Rollback()
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)
@ -199,7 +199,7 @@ func UpdateGeneralUserSettings(c echo.Context) error {
user.Timezone = us.Timezone user.Timezone = us.Timezone
user.OverdueTasksRemindersTime = us.OverdueTasksRemindersTime user.OverdueTasksRemindersTime = us.OverdueTasksRemindersTime
_, err = user2.UpdateUser(s, user) _, err = user2.UpdateUser(s, user, true)
if err != nil { if err != nil {
_ = s.Rollback() _ = s.Rollback()
return handler.HandleHTTPError(err, c) return handler.HandleHTTPError(err, c)

View File

@ -419,7 +419,7 @@ func GetUserFromClaims(claims jwt.MapClaims) (user *User, err error) {
} }
// UpdateUser updates a user // UpdateUser updates a user
func UpdateUser(s *xorm.Session, user *User) (updatedUser *User, err error) { func UpdateUser(s *xorm.Session, user *User, forceOverride bool) (updatedUser *User, err error) {
// Check if it exists // Check if it exists
theUser, err := GetUserWithEmail(s, &User{ID: user.ID}) theUser, err := GetUserWithEmail(s, &User{ID: user.ID})
@ -442,7 +442,7 @@ func UpdateUser(s *xorm.Session, user *User) (updatedUser *User, err error) {
} }
// Check if we have a name // Check if we have a name
if user.Name == "" { if user.Name == "" && !forceOverride {
user.Name = theUser.Name user.Name = theUser.Name
} }

View File

@ -292,7 +292,7 @@ func TestUpdateUser(t *testing.T) {
ID: 1, ID: 1,
Password: "LoremIpsum", Password: "LoremIpsum",
Email: "testing@example.com", Email: "testing@example.com",
}) }, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", uuser.Password) // Password should not change assert.Equal(t, "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", uuser.Password) // Password should not change
assert.Equal(t, "user1", uuser.Username) // Username should not change either assert.Equal(t, "user1", uuser.Username) // Username should not change either
@ -305,7 +305,7 @@ func TestUpdateUser(t *testing.T) {
uuser, err := UpdateUser(s, &User{ uuser, err := UpdateUser(s, &User{
ID: 1, ID: 1,
Username: "changedname", Username: "changedname",
}) }, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", uuser.Password) // Password should not change assert.Equal(t, "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", uuser.Password) // Password should not change
assert.Equal(t, "changedname", uuser.Username) assert.Equal(t, "changedname", uuser.Username)
@ -317,7 +317,7 @@ func TestUpdateUser(t *testing.T) {
_, err := UpdateUser(s, &User{ _, err := UpdateUser(s, &User{
ID: 99999, ID: 99999,
}) }, false)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, IsErrUserDoesNotExist(err)) assert.True(t, IsErrUserDoesNotExist(err))
}) })