Compare commits
21 Commits
1b686b834b
...
369319c709
Author | SHA1 | Date |
---|---|---|
renovate | 369319c709 | |
renovate | fecce19f06 | |
kolaente | 1971df7b84 | |
kooshi | 31a1452839 | |
kolaente | 530bb0a63c | |
kolaente | 7bf7a13bb9 | |
renovate | 10e6843b11 | |
renovate | 78f43829cf | |
renovate | acd31c4ff7 | |
renovate | 1c02114cf5 | |
kolaente | 82f4a5ad50 | |
kolaente | 04614614fe | |
kolaente | 608bde9806 | |
renovate | 568cc16797 | |
Rein | 4b5e65d4c2 | |
renovate | 3329d83363 | |
renovate | c4e5e722e4 | |
Dominik Pschenitschni | 508a3157e2 | |
TheDubliner | 321a8f7e2b | |
renovate | dd7dcdd0cc | |
renovate | cb6368036c |
93
.drone.yml
93
.drone.yml
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: testing
|
||||
|
||||
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.
|
||||
- name: mage
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
|
@ -122,7 +123,7 @@ steps:
|
|||
|
||||
- name: build
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ mage ]
|
||||
|
@ -133,7 +134,7 @@ steps:
|
|||
|
||||
- name: lint
|
||||
image: golang:1.19-alpine
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ build ]
|
||||
|
@ -147,7 +148,7 @@ steps:
|
|||
|
||||
- name: test-migration-prepare
|
||||
image: kolaente/toolbox:latest
|
||||
pull: true
|
||||
pull: always
|
||||
commands:
|
||||
# Get the latest version
|
||||
- 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
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
depends_on: [ test-migration-prepare, build ]
|
||||
environment:
|
||||
VIKUNJA_DATABASE_TYPE: sqlite
|
||||
|
@ -174,7 +175,7 @@ steps:
|
|||
|
||||
- name: test-migration-mysql
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
depends_on: [ test-migration-prepare, build ]
|
||||
environment:
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
|
@ -193,7 +194,7 @@ steps:
|
|||
|
||||
- name: test-migration-psql
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
depends_on: [ test-migration-prepare, build ]
|
||||
environment:
|
||||
VIKUNJA_DATABASE_TYPE: postgres
|
||||
|
@ -213,7 +214,7 @@ steps:
|
|||
|
||||
- name: test
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
|
@ -224,7 +225,7 @@ steps:
|
|||
|
||||
- name: test-sqlite
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
|
@ -241,7 +242,7 @@ steps:
|
|||
|
||||
- name: test-mysql
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
|
@ -258,7 +259,7 @@ steps:
|
|||
|
||||
- name: test-postgres
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
|
@ -276,7 +277,7 @@ steps:
|
|||
|
||||
- name: integration-test
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
|
@ -287,7 +288,7 @@ steps:
|
|||
|
||||
- name: integration-test-sqlite
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
|
@ -304,7 +305,7 @@ steps:
|
|||
|
||||
- name: integration-test-mysql
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
|
@ -321,7 +322,7 @@ steps:
|
|||
|
||||
- name: integration-test-postgres
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
|
@ -343,6 +344,7 @@ steps:
|
|||
########
|
||||
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: release
|
||||
|
||||
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.
|
||||
- name: mage
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
|
@ -378,7 +380,7 @@ steps:
|
|||
|
||||
- name: before-static-build
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
pull: always
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
|
@ -387,7 +389,7 @@ steps:
|
|||
|
||||
- name: static-build-windows
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
# 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.
|
||||
|
@ -400,7 +402,7 @@ steps:
|
|||
|
||||
- name: static-build-linux
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
# 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.
|
||||
|
@ -413,7 +415,7 @@ steps:
|
|||
|
||||
- name: static-build-darwin
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
pull: always
|
||||
environment:
|
||||
# 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.
|
||||
|
@ -426,7 +428,7 @@ steps:
|
|||
|
||||
- name: after-build-compress
|
||||
image: kolaente/upx
|
||||
pull: true
|
||||
pull: always
|
||||
depends_on:
|
||||
- static-build-windows
|
||||
- static-build-linux
|
||||
|
@ -436,7 +438,7 @@ steps:
|
|||
|
||||
- name: after-build-static
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
pull: always
|
||||
depends_on:
|
||||
- after-build-compress
|
||||
commands:
|
||||
|
@ -448,7 +450,7 @@ steps:
|
|||
|
||||
- name: sign-release
|
||||
image: plugins/gpgsign:1
|
||||
pull: true
|
||||
pull: always
|
||||
depends_on: [ after-build-static ]
|
||||
settings:
|
||||
key:
|
||||
|
@ -462,7 +464,7 @@ steps:
|
|||
# Push the releases to our pseudo-s3-bucket
|
||||
- name: release-latest
|
||||
image: plugins/s3
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
|
@ -484,7 +486,7 @@ steps:
|
|||
|
||||
- name: release-version
|
||||
image: plugins/s3
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
|
@ -504,8 +506,8 @@ steps:
|
|||
|
||||
# Build os packages and push it to our bucket
|
||||
- name: build-os-packages-unstable
|
||||
image: goreleaser/nfpm:v2.22.2
|
||||
pull: true
|
||||
image: goreleaser/nfpm:v2.23.0
|
||||
pull: always
|
||||
commands:
|
||||
- apk add git go
|
||||
- ./mage-static release:packages
|
||||
|
@ -520,8 +522,8 @@ steps:
|
|||
depends_on: [ after-build-compress ]
|
||||
|
||||
- name: build-os-packages-version
|
||||
image: goreleaser/nfpm:v2.22.2
|
||||
pull: true
|
||||
image: goreleaser/nfpm:v2.23.0
|
||||
pull: always
|
||||
commands:
|
||||
- apk add git go
|
||||
- ./mage-static release:packages
|
||||
|
@ -536,7 +538,7 @@ steps:
|
|||
# Push the os releases to our pseudo-s3-bucket
|
||||
- name: release-os-latest
|
||||
image: plugins/s3
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
|
@ -558,7 +560,7 @@ steps:
|
|||
|
||||
- name: release-os-version
|
||||
image: plugins/s3
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
|
@ -578,6 +580,7 @@ steps:
|
|||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: deploy-docs
|
||||
|
||||
workspace:
|
||||
|
@ -596,8 +599,7 @@ trigger:
|
|||
steps:
|
||||
- name: theme
|
||||
image: kolaente/toolbox
|
||||
pull: true
|
||||
group: build-static
|
||||
pull: always
|
||||
commands:
|
||||
- mkdir docs/themes/vikunja -p
|
||||
- cd docs/themes/vikunja
|
||||
|
@ -605,8 +607,8 @@ steps:
|
|||
- tar -xzf vikunja-theme.tar.gz
|
||||
|
||||
- name: build
|
||||
image: klakegg/hugo:0.104.2
|
||||
pull: true
|
||||
image: klakegg/hugo:0.107.0
|
||||
pull: always
|
||||
commands:
|
||||
- cd docs
|
||||
- hugo
|
||||
|
@ -614,7 +616,7 @@ steps:
|
|||
|
||||
- name: docker
|
||||
image: plugins/docker
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
|
@ -646,7 +648,7 @@ steps:
|
|||
- name: docker-unstable
|
||||
image: thegeeklab/drone-docker-buildx
|
||||
privileged: true
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
|
@ -665,24 +667,33 @@ steps:
|
|||
ref:
|
||||
- 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
|
||||
image: thegeeklab/drone-docker-buildx
|
||||
privileged: true
|
||||
pull: true
|
||||
pull: always
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
platforms:
|
||||
- linux/386
|
||||
- linux/amd64
|
||||
- linux/arm/v6
|
||||
- linux/arm/v7
|
||||
- linux/arm64/v8
|
||||
depends_on: [ fetch-tags ]
|
||||
depends_on: [ generate-tags ]
|
||||
when:
|
||||
ref:
|
||||
- "refs/tags/**"
|
||||
|
@ -719,6 +730,6 @@ steps:
|
|||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: f8ce17f7158088a124039f579ba10364788d306feac3feeb51689dce440d6213
|
||||
hmac: ede99d3c09466ea04c070a3cf75b454a232c42e2a46a5c5835135267d50a48e7
|
||||
|
||||
...
|
||||
|
|
|
@ -10,7 +10,7 @@ menu:
|
|||
|
||||
# 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.
|
||||
|
||||
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
|
||||
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
|
||||
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/).
|
||||
|
||||
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`
|
||||
* The entrypoint you want to make vikunja available from is called `https`
|
||||
|
@ -398,7 +398,7 @@ docker main folders:
|
|||
* vikunja
|
||||
* 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
|
||||
|
||||
|
@ -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):
|
||||
1. Go to **Dashboard / Stacks** click the button **"Add Stack"**
|
||||
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)
|
||||
|
||||
|
|
19
go.mod
19
go.mod
|
@ -31,17 +31,18 @@ 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.16.0
|
||||
github.com/getsentry/sentry-go v0.17.0
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-sql-driver/mysql v1.7.0
|
||||
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/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/imdario/mergo v0.3.13
|
||||
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/gommon v0.4.0
|
||||
github.com/lib/pq v1.10.7
|
||||
|
@ -55,15 +56,15 @@ require (
|
|||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/spf13/afero v1.9.3
|
||||
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/swaggo/swag v1.8.9
|
||||
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.7
|
||||
github.com/wneessen/go-mail v0.3.8
|
||||
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/oauth2 v0.4.0
|
||||
golang.org/x/sync v0.1.0
|
||||
|
@ -71,7 +72,7 @@ require (
|
|||
golang.org/x/term v0.4.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
|
||||
src.techknowlogick.com/xgo v1.7.1-0.20230117190652-94aee174ab86
|
||||
src.techknowlogick.com/xormigrate v1.5.0
|
||||
xorm.io/builder v0.3.12
|
||||
xorm.io/xorm v1.3.2
|
||||
|
@ -110,7 +111,7 @@ require (
|
|||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef // 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/mattn/go-colorable v0.1.13 // 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/oklog/ulid v1.3.1 // 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/pmezard/go-difflib v1.0.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/jwalterweatherman v1.1.0 // 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/urfave/cli/v2 v2.3.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
|
|
22
go.sum
22
go.sum
|
@ -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/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.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/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
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-testfixtures/testfixtures/v3 v3.8.1 h1:uonwvepqRvSgddcrReZQhojTlWlmOlHkYAb9ZaOMWgU=
|
||||
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.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
||||
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/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.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.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
|
||||
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/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.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-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
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/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.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/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=
|
||||
|
@ -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/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.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-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
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/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.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/go.mod h1:ezQVUUhly8dludpVk+/PuwJWvLLanB13ygV5Pr9enSk=
|
||||
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.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.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/yuin/goldmark v1.1.25/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.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.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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
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=
|
||||
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.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/go.mod h1:QOCnBeWralVncPn9eZlM4w/rglFK8o1vYpemzPenkBM=
|
||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||
|
|
|
@ -225,7 +225,7 @@ var userUpdateCmd = &cobra.Command{
|
|||
u.AvatarProvider = userFlagAvatar
|
||||
}
|
||||
|
||||
_, err := user.UpdateUser(s, u)
|
||||
_, err := user.UpdateUser(s, u, false)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Error updating the user: %s", err)
|
||||
|
@ -299,7 +299,7 @@ var userChangeEnabledCmd = &cobra.Command{
|
|||
u.Status = user.StatusActive
|
||||
}
|
||||
}
|
||||
_, err := user.UpdateUser(s, u)
|
||||
_, err := user.UpdateUser(s, u, false)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Could not enable the user")
|
||||
|
|
|
@ -83,7 +83,7 @@ func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, m
|
|||
s := db.NewSession()
|
||||
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 {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
|
@ -91,14 +91,14 @@ func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, m
|
|||
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
|
||||
var maxSize datasize.ByteSize
|
||||
err = maxSize.UnmarshalText([]byte(config.FilesMaxSize.GetString()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if realsize > maxSize.Bytes() {
|
||||
if realsize > maxSize.Bytes() && checkFileSizeLimit {
|
||||
return nil, ErrFileIsTooLarge{Size: realsize}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
if config.MailerForceSSL.GetBool() {
|
||||
opts = append(opts, mail.WithSSL())
|
||||
}
|
||||
|
||||
if config.MailerUsername.GetString() != "" && config.MailerPassword.GetString() != "" {
|
||||
opts = append(opts, mail.WithSMTPAuth(authType))
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ func ExportUserData(s *xorm.Session, u *user.User) (err error) {
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
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
|
||||
var bucket *Bucket
|
||||
if task.Done && originalTask != nil && !originalTask.Done {
|
||||
bucket, err := getDoneBucketForList(s, task.ListID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if bucket != nil {
|
||||
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) {
|
||||
bucket, err = getDefaultBucket(s, task.ListID)
|
||||
if err != nil {
|
||||
return err
|
||||
return
|
||||
}
|
||||
task.BucketID = bucket.ID
|
||||
}
|
||||
|
@ -846,7 +846,7 @@ func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, doCheckBucke
|
|||
if bucket == nil {
|
||||
bucket, err = getBucketByID(s, task.BucketID)
|
||||
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
|
||||
if doCheckBucketLimit {
|
||||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
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
|
||||
err = setTaskBucket(s, t, nil, true)
|
||||
_, err = setTaskBucket(s, t, nil, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -1028,13 +1028,21 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
|
|||
ot.Reminders[i] = r.Reminder
|
||||
}
|
||||
|
||||
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
|
||||
updateDone(&ot, t)
|
||||
|
||||
if err := setTaskBucket(s, t, &ot, t.BucketID != 0 && t.BucketID != ot.BucketID); err != nil {
|
||||
targetBucket, err := setTaskBucket(s, t, &ot, t.BucketID != 0 && t.BucketID != ot.BucketID)
|
||||
if err != nil {
|
||||
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
|
||||
if err := ot.updateTaskAssignees(s, t.Assignees, a); err != nil {
|
||||
return err
|
||||
|
@ -1411,9 +1419,9 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro
|
|||
}
|
||||
|
||||
// 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 {
|
||||
reminderMap[reminder.UTC().String()] = reminder
|
||||
reminderMap[reminder.UTC().Unix()] = reminder
|
||||
}
|
||||
|
||||
// Loop through all reminders and add them
|
||||
|
|
|
@ -243,6 +243,33 @@ func TestTask_Update(t *testing.T) {
|
|||
"bucket_id": 3,
|
||||
}, 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) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
@ -308,8 +335,9 @@ func TestTask_Update(t *testing.T) {
|
|||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 28,
|
||||
Done: true,
|
||||
ID: 28,
|
||||
Done: true,
|
||||
RepeatAfter: 3600,
|
||||
}
|
||||
err := task.Update(s, u)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -267,7 +267,7 @@ func getOrCreateUser(s *xorm.Session, cl *claims, issuer, subject string) (u *us
|
|||
Name: u.Name,
|
||||
Issuer: issuer,
|
||||
Subject: subject,
|
||||
})
|
||||
}, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -56,12 +56,22 @@ func DownloadFileWithHeaders(url string, headers http.Header) (buf *bytes.Buffer
|
|||
|
||||
// DoPost makes a form encoded post request
|
||||
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()))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
for key, value := range headers {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
|
||||
hc := http.Client{}
|
||||
return hc.Do(req)
|
||||
}
|
||||
|
|
|
@ -27,10 +27,11 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/migration"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"github.com/gocarina/gocsv"
|
||||
)
|
||||
|
||||
const timeISO = "2006-01-02T15:04:05-0700"
|
||||
|
@ -39,23 +40,39 @@ type Migrator struct {
|
|||
}
|
||||
|
||||
type tickTickTask struct {
|
||||
FolderName string
|
||||
ListName string
|
||||
Title string
|
||||
Tags []string
|
||||
Content string
|
||||
IsChecklist bool
|
||||
StartDate time.Time
|
||||
DueDate time.Time
|
||||
Reminder time.Duration
|
||||
Repeat string
|
||||
Priority int
|
||||
Status string
|
||||
CreatedTime time.Time
|
||||
CompletedTime time.Time
|
||||
Order float64
|
||||
TaskID int64
|
||||
ParentID int64
|
||||
FolderName string `csv:"Folder Name"`
|
||||
ListName string `csv:"List Name"`
|
||||
Title string `csv:"Title"`
|
||||
TagsList string `csv:"Tags"`
|
||||
Tags []string `csv:"-"`
|
||||
Content string `csv:"Content"`
|
||||
IsChecklistString string `csv:"Is Check list"`
|
||||
IsChecklist bool `csv:"-"`
|
||||
StartDate tickTickTime `csv:"Start Date"`
|
||||
DueDate tickTickTime `csv:"Due Date"`
|
||||
ReminderDuration string `csv:"Reminder"`
|
||||
Reminder time.Duration `csv:"-"`
|
||||
Repeat string `csv:"Repeat"`
|
||||
Priority int `csv:"Priority"`
|
||||
Status string `csv:"Status"`
|
||||
CreatedTime tickTickTime `csv:"Created Time"`
|
||||
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
|
||||
|
@ -119,19 +136,22 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.Namespace
|
|||
ID: t.TaskID,
|
||||
Title: t.Title,
|
||||
Description: t.Content,
|
||||
StartDate: t.StartDate,
|
||||
EndDate: t.DueDate,
|
||||
DueDate: t.DueDate,
|
||||
Reminders: []time.Time{
|
||||
t.DueDate.Add(t.Reminder * -1),
|
||||
},
|
||||
Done: t.Status == "1",
|
||||
DoneAt: t.CompletedTime,
|
||||
Position: t.Order,
|
||||
Labels: labels,
|
||||
StartDate: t.StartDate.Time,
|
||||
EndDate: t.DueDate.Time,
|
||||
DueDate: t.DueDate.Time,
|
||||
Done: t.Status == "1",
|
||||
DoneAt: t.CompletedTime.Time,
|
||||
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 {
|
||||
task.RelatedTasks = map[models.RelationKind][]*models.Task{
|
||||
models.RelationKindParenttask: {{ID: t.ParentID}},
|
||||
|
@ -165,6 +185,22 @@ func (m *Migrator) Name() string {
|
|||
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.
|
||||
// @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.
|
||||
|
@ -178,85 +214,26 @@ func (m *Migrator) Name() string {
|
|||
// @Router /migration/ticktick/migrate [post]
|
||||
func (m *Migrator) Migrate(user *user.User, file io.ReaderAt, size int64) error {
|
||||
fr := io.NewSectionReader(file, 0, size)
|
||||
r := csv.NewReader(fr)
|
||||
//r := csv.NewReader(fr)
|
||||
|
||||
allTasks := []*tickTickTask{}
|
||||
line := 0
|
||||
for {
|
||||
decode := newLineSkipDecoder(fr, 3)
|
||||
err := gocsv.UnmarshalDecoder(decode, &allTasks)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record, err := r.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
log.Debugf("[TickTick Migration] CSV parse error: %s", err)
|
||||
for _, task := range allTasks {
|
||||
if task.IsChecklistString == "Y" {
|
||||
task.IsChecklist = true
|
||||
}
|
||||
|
||||
line++
|
||||
if line <= 4 {
|
||||
continue
|
||||
reminder := parseDuration(task.ReminderDuration)
|
||||
if reminder > 0 {
|
||||
task.Reminder = reminder
|
||||
}
|
||||
|
||||
priority, err := strconv.Atoi(record[10])
|
||||
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)
|
||||
task.Tags = strings.Split(task.TagsList, ", ")
|
||||
}
|
||||
|
||||
vikunjaTasks := convertTickTickToVikunja(allTasks)
|
||||
|
|
|
@ -26,12 +26,15 @@ import (
|
|||
)
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
time3 := tickTickTime{Time: t3}
|
||||
duration, err := time.ParseDuration("24h")
|
||||
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].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].EndDate, tickTickTasks[0].DueDate)
|
||||
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].DueDate, tickTickTasks[0].DueDate)
|
||||
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.Time)
|
||||
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{
|
||||
{Title: "label1"},
|
||||
{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].Position, tickTickTasks[1].Order)
|
||||
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{
|
||||
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].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].EndDate, tickTickTasks[2].DueDate)
|
||||
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].DueDate, tickTickTasks[2].DueDate)
|
||||
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.Time)
|
||||
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{
|
||||
{Title: "label1"},
|
||||
{Title: "label2"},
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
@ -76,7 +77,6 @@ type dueDate struct {
|
|||
|
||||
type item struct {
|
||||
ID string `json:"id"`
|
||||
LegacyID string `json:"legacy_id"`
|
||||
UserID string `json:"user_id"`
|
||||
ProjectID string `json:"project_id"`
|
||||
Content string `json:"content"`
|
||||
|
@ -310,6 +310,12 @@ func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVi
|
|||
}
|
||||
|
||||
for _, i := range sync.Items {
|
||||
|
||||
if i == nil {
|
||||
// This should never happen
|
||||
continue
|
||||
}
|
||||
|
||||
task := &models.TaskWithComments{
|
||||
Task: models.Task{
|
||||
Title: i.Content,
|
||||
|
@ -524,11 +530,14 @@ func (m *Migration) Migrate(u *user.User) (err error) {
|
|||
|
||||
// Get everything with the sync api
|
||||
form := url.Values{
|
||||
"token": []string{token},
|
||||
"sync_token": []string{"*"},
|
||||
"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 {
|
||||
return
|
||||
}
|
||||
|
@ -547,7 +556,7 @@ func (m *Migration) Migrate(u *user.User) (err error) {
|
|||
doneItems := make(map[string]*doneItem)
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -571,21 +580,26 @@ func (m *Migration) Migrate(u *user.User) (err error) {
|
|||
doneItems[i.TaskID] = i
|
||||
|
||||
// need to get done item data
|
||||
resp, err = migration.DoPost("https://api.todoist.com/sync/v9/items/get", url.Values{
|
||||
"token": []string{token},
|
||||
resp, err = migration.DoPostWithHeaders("https://api.todoist.com/sync/v9/items/get", url.Values{
|
||||
"item_id": []string{i.TaskID},
|
||||
})
|
||||
}, bearerHeader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
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{}
|
||||
err = json.NewDecoder(resp.Body).Decode(doneI)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
// 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 {
|
||||
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] 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 {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -205,7 +205,7 @@ func UploadAvatar(c echo.Context) (err error) {
|
|||
u.AvatarFileID = f.ID
|
||||
u.AvatarProvider = "upload"
|
||||
|
||||
if _, err := user.UpdateUser(s, u); err != nil {
|
||||
if _, err := user.UpdateUser(s, u, false); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ func ChangeUserAvatarProvider(c echo.Context) error {
|
|||
|
||||
user.AvatarProvider = uap.AvatarProvider
|
||||
|
||||
_, err = user2.UpdateUser(s, user)
|
||||
_, err = user2.UpdateUser(s, user, false)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
|
@ -199,7 +199,7 @@ func UpdateGeneralUserSettings(c echo.Context) error {
|
|||
user.Timezone = us.Timezone
|
||||
user.OverdueTasksRemindersTime = us.OverdueTasksRemindersTime
|
||||
|
||||
_, err = user2.UpdateUser(s, user)
|
||||
_, err = user2.UpdateUser(s, user, true)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
|
|
|
@ -419,7 +419,7 @@ func GetUserFromClaims(claims jwt.MapClaims) (user *User, err error) {
|
|||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
if user.Name == "" {
|
||||
if user.Name == "" && !forceOverride {
|
||||
user.Name = theUser.Name
|
||||
}
|
||||
|
||||
|
|
|
@ -292,7 +292,7 @@ func TestUpdateUser(t *testing.T) {
|
|||
ID: 1,
|
||||
Password: "LoremIpsum",
|
||||
Email: "testing@example.com",
|
||||
})
|
||||
}, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", uuser.Password) // Password should not change
|
||||
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{
|
||||
ID: 1,
|
||||
Username: "changedname",
|
||||
})
|
||||
}, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.", uuser.Password) // Password should not change
|
||||
assert.Equal(t, "changedname", uuser.Username)
|
||||
|
@ -317,7 +317,7 @@ func TestUpdateUser(t *testing.T) {
|
|||
|
||||
_, err := UpdateUser(s, &User{
|
||||
ID: 99999,
|
||||
})
|
||||
}, false)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserDoesNotExist(err))
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue