Compare commits

..

3 Commits

Author SHA1 Message Date
kolaente 37e4aa9c4f
[skip ci] 2019-05-25 10:31:12 +02:00
kolaente b7918a694e
Added test for uid when creating a new team 2019-05-25 10:29:14 +02:00
kolaente 6627c074bd
Added uid to teams 2019-05-25 10:26:32 +02:00
2000 changed files with 799858 additions and 48398 deletions

View File

@ -6,45 +6,16 @@ workspace:
path: src/code.vikunja.io/api
services:
- name: test-mysql-unit
- name: test-db-unit
image: mariadb:10
environment:
MYSQL_ROOT_PASSWORD: vikunjatest
MYSQL_DATABASE: vikunjatest
- name: test-mysql-integration
- name: test-db-integration
image: mariadb:10
environment:
MYSQL_ROOT_PASSWORD: vikunjatest
MYSQL_DATABASE: vikunjatest
- name: test-mysql-migration
image: mariadb:10
environment:
MYSQL_ROOT_PASSWORD: vikunjatest
MYSQL_DATABASE: vikunjatest
- name: test-postgres-unit
image: postgres:12
environment:
POSTGRES_PASSWORD: vikunjatest
POSTGRES_DB: vikunjatest
- name: test-postgres-integration
image: postgres:12
environment:
POSTGRES_PASSWORD: vikunjatest
POSTGRES_DB: vikunjatest
- name: test-postgres-migration
image: postgres:12
environment:
POSTGRES_PASSWORD: vikunjatest
POSTGRES_DB: vikunjatest
trigger:
branch:
include:
- master
event:
include:
- push
- pull_request
steps:
- name: fetch-tags
@ -52,115 +23,30 @@ steps:
commands:
- git fetch --tags
# We're statically compiling the magefile to avoid race condition issues caused by multiple pipeline 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
environment:
GOPROXY: 'https://goproxy.kolaente.de'
commands:
- mage -compile ./mage-static
when:
event: [ push, tag, pull_request ]
- name: build
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
depends_on: [ mage ]
GOFLAGS: '-mod=vendor'
commands:
- ./mage-static build:build
when:
event: [ push, tag, pull_request ]
- name: lint
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
depends_on: [ build ]
commands:
- ./mage-static build:generate
- ./mage-static check:got-swag
- wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.31.0
- ./mage-static check:golangci
when:
event: [ push, tag, pull_request ]
- name: test-migration-prepare
image: kolaente/toolbox:latest
pull: true
commands:
# Get the latest version
- wget https://dl.vikunja.io/api/master/vikunja-master-linux-amd64-full.zip -q -O vikunja-latest.zip
- unzip vikunja-latest.zip vikunja-master-linux-amd64
- name: test-migration-sqlite
image: kolaente/toolbox:latest
pull: true
depends_on: [ test-migration-prepare, build ]
environment:
VIKUNJA_DATABASE_TYPE: sqlite
VIKUNJA_DATABASE_PATH: ./vikunja-migration-test.db
VIKUNJA_LOG_DATABASE: stdout
VIKUNJA_LOG_DATABASELEVEL: debug
commands:
- ./vikunja-master-linux-amd64 migrate
# Run the migrations from the binary build in the step before
- ./vikunja migrate
when:
event: [ push, tag, pull_request ]
- name: test-migration-mysql
image: kolaente/toolbox:latest
pull: true
depends_on: [ test-migration-prepare, build ]
environment:
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_HOST: test-mysql-migration
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_PASSWORD: vikunjatest
VIKUNJA_DATABASE_DATABASE: vikunjatest
VIKUNJA_LOG_DATABASE: stdout
VIKUNJA_LOG_DATABASELEVEL: debug
commands:
- ./vikunja-master-linux-amd64 migrate
# Run the migrations from the binary build in the step before
- ./vikunja migrate
when:
event: [ push, tag, pull_request ]
- name: test-migration-psql
image: kolaente/toolbox:latest
pull: true
depends_on: [ test-migration-prepare, build ]
environment:
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_HOST: test-postgres-migration
VIKUNJA_DATABASE_USER: postgres
VIKUNJA_DATABASE_PASSWORD: vikunjatest
VIKUNJA_DATABASE_DATABASE: vikunjatest
VIKUNJA_DATABASE_SSLMODE: disable
VIKUNJA_LOG_DATABASE: stdout
VIKUNJA_LOG_DATABASELEVEL: debug
commands:
- ./vikunja-master-linux-amd64 migrate
# Run the migrations from the binary build in the step before
- ./vikunja migrate
- make lint
- make fmt-check
# - make got-swag # Commented out until we figured out how to get this working on drone
- make ineffassign-check
- make misspell-check
- make goconst-check
- make gocyclo-check
- make static-check
- make build
when:
event: [ push, tag, pull_request ]
- name: test
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
commands:
- ./mage-static build:generate
- ./mage-static test:unit
depends_on: [ fetch-tags, mage ]
- make test
depends_on: [ build ]
when:
event: [ push, tag, pull_request ]
@ -168,13 +54,11 @@ steps:
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1
VIKUNJA_DATABASE_TYPE: sqlite
commands:
- ./mage-static build:generate
- ./mage-static test:unit
depends_on: [ fetch-tags, mage ]
- make test
depends_on: [ build ]
when:
event: [ push, tag, pull_request ]
@ -182,48 +66,24 @@ steps:
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_HOST: test-mysql-unit
VIKUNJA_DATABASE_HOST: test-db-unit
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_PASSWORD: vikunjatest
VIKUNJA_DATABASE_DATABASE: vikunjatest
commands:
- ./mage-static build:generate
- ./mage-static test:unit
depends_on: [ fetch-tags, mage ]
when:
event: [ push, tag, pull_request ]
- name: test-postgres
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_HOST: test-postgres-unit
VIKUNJA_DATABASE_USER: postgres
VIKUNJA_DATABASE_PASSWORD: vikunjatest
VIKUNJA_DATABASE_DATABASE: vikunjatest
VIKUNJA_DATABASE_SSLMODE: disable
commands:
- ./mage-static build:generate
- ./mage-static test:unit
depends_on: [ fetch-tags, mage ]
- make test
depends_on: [ build ]
when:
event: [ push, tag, pull_request ]
- name: integration-test
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
commands:
- ./mage-static build:generate
- ./mage-static test:integration
depends_on: [ fetch-tags, mage ]
- make integration-test
depends_on: [ build ]
when:
event: [ push, tag, pull_request ]
@ -231,13 +91,11 @@ steps:
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1
VIKUNJA_DATABASE_TYPE: sqlite
commands:
- ./mage-static build:generate
- ./mage-static test:integration
depends_on: [ fetch-tags, mage ]
- make integration-test
depends_on: [ build ]
when:
event: [ push, tag, pull_request ]
@ -245,47 +103,26 @@ steps:
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_HOST: test-mysql-integration
VIKUNJA_DATABASE_HOST: test-db-integration
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_PASSWORD: vikunjatest
VIKUNJA_DATABASE_DATABASE: vikunjatest
commands:
- ./mage-static build:generate
- ./mage-static test:integration
depends_on: [ fetch-tags, mage ]
when:
event: [ push, tag, pull_request ]
- name: integration-test-postgres
image: vikunja/golang-build:latest
pull: true
environment:
GOPROXY: 'https://goproxy.kolaente.de'
VIKUNJA_TESTS_USE_CONFIG: 1
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_HOST: test-postgres-integration
VIKUNJA_DATABASE_USER: postgres
VIKUNJA_DATABASE_PASSWORD: vikunjatest
VIKUNJA_DATABASE_DATABASE: vikunjatest
VIKUNJA_DATABASE_SSLMODE: disable
commands:
- ./mage-static build:generate
- ./mage-static test:integration
depends_on: [ fetch-tags, mage ]
- make integration-test
depends_on: [ build ]
when:
event: [ push, tag, pull_request ]
---
########
# Build a release when tagging
# Build a release when pushing to master
# We need to copy this because drone currently can't run a pipeline on either a push to master or a tag.
########
kind: pipeline
name: release
name: deploy-master
depends_on:
- testing
@ -294,9 +131,10 @@ workspace:
path: src/code.vikunja.io/api
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
branch:
- master
event:
- push
steps:
# Needed to get the versions right as they depend on tags
@ -305,27 +143,12 @@ steps:
commands:
- git fetch --tags
# We're statically compiling the magefile to avoid race condition issues caused by multiple pipeline 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
environment:
GOPROXY: 'https://goproxy.kolaente.de'
commands:
- mage -compile ./mage-static
when:
event: [ push, tag, pull_request ]
- name: before-static-build
image: techknowlogick/xgo:latest
pull: true
commands:
- export PATH=$PATH:$GOPATH/bin
- go install github.com/magefile/mage
- ./mage-static build:generate
- ./mage-static release:dirs
depends_on: [ fetch-tags, mage ]
- make release-dirs
- name: static-build-windows
image: techknowlogick/xgo:latest
@ -336,8 +159,7 @@ steps:
GOPATH: /srv/app
commands:
- export PATH=$PATH:$GOPATH/bin
- go install github.com/magefile/mage
- ./mage-static release:windows
- make release-windows
depends_on: [ before-static-build ]
- name: static-build-linux
@ -349,8 +171,7 @@ steps:
GOPATH: /srv/app
commands:
- export PATH=$PATH:$GOPATH/bin
- go install github.com/magefile/mage
- ./mage-static release:linux
- make release-linux
depends_on: [ before-static-build ]
- name: static-build-darwin
@ -362,31 +183,21 @@ steps:
GOPATH: /srv/app
commands:
- export PATH=$PATH:$GOPATH/bin
- go install github.com/magefile/mage
- ./mage-static release:darwin
- make release-darwin
depends_on: [ before-static-build ]
- name: after-build-compress
image: kolaente/upx
- name: after-build-static
image: techknowlogick/xgo:latest
pull: true
depends_on:
- static-build-windows
- static-build-linux
- static-build-darwin
commands:
- ./mage-static release:compress
- name: after-build-static
image: techknowlogick/xgo:latest
pull: true
depends_on:
- after-build-compress
commands:
- go install github.com/magefile/mage
- ./mage-static release:copy
- ./mage-static release:check
- ./mage-static release:os-package
- ./mage-static release:zip
- make release-copy
- make release-check
- make release-os-package
- make release-zip
- name: sign-release
image: plugins/gpgsign:1
@ -401,101 +212,31 @@ steps:
- dist/zip/*
detach_sign: true
# Push the releases to our pseudo-s3-bucket
# Push the releases to our pseudo-s3-bucket
- name: release-latest
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
bucket: vikunja
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
endpoint: https://storage.kolaente.de
path_style: true
strip_prefix: dist/zip/
source: dist/zip/*
target: /api/master/
when:
branch:
- master
event:
- push
target: /master/
depends_on: [ sign-release ]
- name: release-version
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
strip_prefix: dist/zip/
source: dist/zip/*
target: /api/${DRONE_TAG##v}/
when:
event:
- tag
depends_on: [ sign-release ]
# Build os packages and push it to our bucket
- name: build-os-packages
image: goreleaser/nfpm
# Build a debian package and push it to our bucket
- name: build-deb
image: kolaente/fpm
pull: true
commands:
- apk add git go
- ./mage-static release:packages
- make build-deb
depends_on: [ static-build-linux ]
# Push the os releases to our pseudo-s3-bucket
- name: release-os-latest
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
strip_prefix: dist/os-packages/
source: dist/os-packages/*
target: /api/master/
when:
branch:
- master
event:
- push
depends_on: [ build-os-packages ]
- name: release-os-version
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
path_style: true
strip_prefix: dist/os-packages/
source: dist/os-packages/*
target: /api/${DRONE_TAG##v}/
when:
event:
- tag
depends_on: [ build-os-packages ]
- name: deb-structure
image: kolaente/reprepro
pull: true
@ -511,27 +252,254 @@ steps:
- gpg --import ~/frederik.gpg
- mkdir debian/conf -p
- cp build/reprepro-dist-conf debian/conf/distributions
- ./mage-static release:reprepro
depends_on: [ build-os-packages ]
- make reprepro
depends_on: [ build-deb ]
# Push the releases to our pseudo-s3-bucket
- name: release-deb
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-releases
bucket: vikunja-deb
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
endpoint: https://storage.kolaente.de
path_style: true
strip_prefix: debian
source: debian/*/*/*/*/*
target: /deb/
target: /
depends_on: [ deb-structure ]
# Build the docker image and push it to docker hub
- name: docker
image: plugins/docker
pull: true
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: vikunja/api
auto_tag: true
# Update the instance on try.vikunja.io
- name: rancher
image: peloton/drone-rancher
settings:
url: http://server01.kolaente.de:8080/v1
access_key:
from_secret: RANCHER_ACCESS_KEY
secret_key:
from_secret: RANCHER_SECRET_KEY
service: vikunja-dev/api
docker_image: vikunja/api
confirm: true
depends_on: [ docker ]
- name: telegram
image: appleboy/drone-telegram
depends_on:
- rancher
- release-latest
settings:
token:
from_secret: TELEGRAM_TOKEN
to:
from_secret: TELEGRAM_TO
message: >
{{repo.owner}}/{{repo.name}}: \[{{build.status}}] Build {{build.number}}
{{commit.author}} pushed to {{commit.branch}} {{commit.sha}}: `{{commit.message}}`
Build started at {{datetime build.started "2006-Jan-02T15:04:05Z" "GMT+2"}} finished at {{datetime build.finished "2006-Jan-02T15:04:05Z" "GMT+2"}}.
when:
status:
- success
- failure
---
########
# Build a release when tagging
# We need to copy this because drone currently can't run a pipeline on either a push to master or a tag.
########
kind: pipeline
name: deploy-version
depends_on:
- testing
workspace:
base: /go
path: src/code.vikunja.io/api
trigger:
event:
- tag
steps:
- name: fetch-tags
image: docker:git
commands:
- git fetch --tags
- name: before-static-build
image: techknowlogick/xgo:latest
pull: true
commands:
- make release-dirs
- name: static-build-windows
image: techknowlogick/xgo:latest
pull: true
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.
GOPATH: /srv/app
commands:
- export PATH=$PATH:$GOPATH/bin
- make release-windows
depends_on: [ before-static-build ]
- name: static-build-linux
image: techknowlogick/xgo:latest
pull: true
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.
GOPATH: /srv/app
commands:
- export PATH=$PATH:$GOPATH/bin
- make release-linux
depends_on: [ before-static-build ]
- name: static-build-darwin
image: techknowlogick/xgo:latest
pull: true
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.
GOPATH: /srv/app
commands:
- export PATH=$PATH:$GOPATH/bin
- make release-darwin
depends_on: [ before-static-build ]
- name: after-build-static
image: techknowlogick/xgo:latest
pull: true
depends_on:
- static-build-windows
- static-build-linux
- static-build-darwin
commands:
- make release-copy
- make release-check
- make release-os-package
- make release-zip
- name: sign-release
image: plugins/gpgsign:1
pull: true
depends_on: [ after-build-static ]
settings:
key:
from_secret: gpg_privkey
passphrase:
from_secret: gpg_password
files:
- dist/zip/*
detach_sign: true
# Push the releases to our pseudo-s3-bucket
- name: release-version
image: plugins/s3:1
pull: true
settings:
bucket: vikunja
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://storage.kolaente.de
path_style: true
strip_prefix: dist/zip/
source: dist/zip/*
target: /${DRONE_TAG##v}/
depends_on: [ sign-release ]
# Build a debian package and push it to our bucket
- name: build-deb
image: kolaente/fpm
pull: true
commands:
- make build-deb
depends_on: [ static-build-linux ]
- name: deb-structure
image: kolaente/reprepro
pull: true
environment:
GPG_PRIVATE_KEY:
from_secret: gpg_privatekey
commands:
- export GPG_TTY=$(tty)
- gpg -qk
- echo "use-agent" >> ~/.gnupg/gpg.conf
- gpgconf --kill gpg-agent
- echo $GPG_PRIVATE_KEY > ~/frederik.gpg
- gpg --import ~/frederik.gpg
- mkdir debian/conf -p
- cp build/reprepro-dist-conf debian/conf/distributions
- make reprepro
depends_on: [ build-deb ]
# Push the releases to our pseudo-s3-bucket
- name: release-deb
image: plugins/s3:1
pull: true
settings:
bucket: vikunja-deb
access_key:
from_secret: aws_access_key_id
secret_key:
from_secret: aws_secret_access_key
endpoint: https://storage.kolaente.de
path_style: true
strip_prefix: debian
source: debian/*/*/*/*/*
target: /
depends_on: [ deb-structure ]
# Build the docker image and push it to docker hub
- name: docker
image: plugins/docker
pull: true
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: vikunja/api
auto_tag: true
- name: telegram
image: appleboy/drone-telegram
depends_on:
- docker
- release-version
settings:
token:
from_secret: TELEGRAM_TOKEN
to:
from_secret: TELEGRAM_TO
message: >
{{repo.owner}}/{{repo.name}}: \[{{build.status}}] Build {{build.number}}
{{commit.author}} pushed to {{commit.branch}} {{commit.sha}}: `{{commit.message}}`
Build started at {{datetime build.started "2006-Jan-02T15:04:05Z" "GMT+2"}} finished at {{datetime build.finished "2006-Jan-02T15:04:05Z" "GMT+2"}}.
when:
status:
- success
- failure
---
kind: pipeline
name: deploy-docs
@ -556,15 +524,6 @@ steps:
- git submodule update --init
- git submodule update --recursive --remote
- name: theme
image: kolaente/yarn
pull: true
group: build-static
commands:
- cd docs/themes/vikunja
- yarn --production=false
- ./node_modules/.bin/gulp prod
- name: build
image: monachus/hugo:v0.54.0
pull: true
@ -585,130 +544,14 @@ steps:
context: docs/
dockerfile: docs/Dockerfile
---
kind: pipeline
type: docker
name: docker-arm-release
depends_on:
- testing
platform:
os: linux
arch: arm
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
steps:
- name: fetch-tags
image: docker:git
commands:
- git fetch --tags
- name: docker
image: plugins/docker:linux-arm
pull: true
- name: rancher
image: peloton/drone-rancher
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: vikunja/api
auto_tag: true
auto_tag_suffix: linux-arm
depends_on: [ fetch-tags ]
---
kind: pipeline
type: docker
name: docker-amd64-release
depends_on:
- testing
platform:
os: linux
arch: amd64
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
steps:
- name: fetch-tags
image: docker:git
commands:
- git fetch --tags
- name: docker
image: plugins/docker:linux-amd64
pull: true
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: vikunja/api
auto_tag: true
auto_tag_suffix: linux-amd64
depends_on: [ fetch-tags ]
---
kind: pipeline
type: docker
name: docker-manifest
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
depends_on:
- docker-amd64-release
- docker-arm-release
steps:
- name: manifest
pull: always
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
spec: docker-manifest.tmpl
password:
from_secret: docker_password
username:
from_secret: docker_username
---
kind: pipeline
type: docker
name: notify
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
depends_on:
- release
- docker-manifest
steps:
- name: telegram
image: appleboy/drone-telegram:1-linux-amd64
settings:
token:
from_secret: TELEGRAM_TOKEN
to:
from_secret: TELEGRAM_TO
message: >
{{repo.owner}}/{{repo.name}}: \[{{build.status}}] Build {{build.number}}
{{commit.author}} pushed to {{commit.branch}} {{commit.sha}}: `{{commit.message}}`
Build started at {{datetime build.started "2006-Jan-02T15:04:05Z" "GMT+2"}} finished at {{datetime build.finished "2006-Jan-02T15:04:05Z" "GMT+2"}}.
when:
status:
- success
- failure
url: http://server01.kolaente.de:8080/v1
access_key:
from_secret: RANCHER_ACCESS_KEY
secret_key:
from_secret: RANCHER_SECRET_KEY
service: vikunja-website/docs
docker_image: vikunja/docs
confirm: true

View File

@ -5,7 +5,8 @@
# Checklist
* [ ] I added or improved tests
* [ ] I pushed new or updated dependencies to the repo using `go mod vendor`
* [ ] I added or improved docs for my feature
* [ ] Swagger (including `mage do-the-swag`)
* [ ] Swagger (including `make do-the-swag`)
* [ ] Error codes
* [ ] New config options (including adding them to `config.yml.saml` and running `mage generate-docs`)
* [ ] New config options

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
custom: https://www.buymeacoffee.com/kolaente

6
.gitignore vendored
View File

@ -17,9 +17,3 @@ debian/
logs/
docs/public/
docs/resources/
pkg/static/templates_vfsdata.go
files/
!pkg/files/
vikunja-dump*
vendor/
os-packages/

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "docs/themes/vikunja"]
path = docs/themes/vikunja
url = ../theme.git
url = https://git.kolaente.de/vikunja/theme.git

View File

@ -1,76 +0,0 @@
run:
timeout: 5m
tests: true
linters:
enable:
- megacheck
- govet
- goconst
- gocritic
- gocyclo
- goerr113
- goheader
- gofmt
- goimports
- golint
- misspell
disable:
- scopelint # Obsolete, using exportloopref instead
presets:
- bugs
- unused
fast: false
linter-settings:
nestif:
min-complexity: 6
goheader:
template-path: code-hesader-template.txt
issues:
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- deadcode
- path: pkg/integrations/*
linters:
- gocyclo
- deadcode
- varcheck
- unparam
- bodyclose
- path: pkg/integrations/*
text: "unlambda"
linters:
- gocritic
- path: pkg/modules/background/unsplash/unsplash\.go
linters:
- bodyclose
- path: pkg/migration/*
linters:
- exhaustive
- goconst
- goerr113
- path: pkg/models/task_collection_filter\.go
linters:
- exhaustive
- path: pkg/utils/random_string\.go
text: "G404:" # We don't care about cryptographically secure randomness when we're using that utility function.
linters:
- gosec
- path: pkg/modules/dump/*
linters:
- goerr113
- path: pkg/
text: "err113: do not define dynamic errors, use wrapped static errors instead:"
linters:
- goerr113
- text: "commentFormatting: put a space between `//` and comment text"
linters:
- gocritic
- path: pkg/modules/migration
linters:
- gocyclo

View File

@ -1,669 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
All releases can be found on https://code.vikunja.io/api/releases.
## [0.15.1] - 2020-10-20
### Fixed
* Fix not possible to create tasks if metrics were enabled
## [0.15.0] - 2020-10-19
### Added
* Add app support info for DAV (#692)
* Add better tests for namespaces
* Add caldav enabled/disabled to /info endpoint
* Add checks if tasks exist in maps before trying to access them
* Add config option to force ssl connections to connect with the mailer
* Add dav proxy directions to example proxy configurations
* Add docs about using vikunja with utf-8 characters
* Add FreeBSD guide to installation docs
* Add github sponsor link
* Add Golangci Lint (#676)
* Add mage command to create a new migration
* Add option to configure legal urls
* Add rootpath to deb command to not include everything in the deb file
* Add toc to docs
* Add update route to toggle team member admin status
* Add util function to move files
* Disable gocyclo for migration modules
* Favorite lists (#654)
* Favorite tasks (#653)
* Generate config docs from sample config (#684)
* Kanban bucket limits (#652)
* Key-Value Storages (#674)
* Manage users via cli (#632)
* Mention client_max_body_size in nginx proxy settings
* More avatar providers (#622)
* Return rights when reading a single item (#626)
* Saved filters (#655)
### Fixed
* Cleanup references to make
* Don't add a subtask to the top level of tasks to not add it twice in the list
* Fetch tasks for caldav lists (#641)
* Fix building for darwin with mage
* Fix creating lists with non ascii characters (#607)
* Fix decoding active users from redis
* Fix dockerimage build
* Fix docs index links
* Fix duplicating a list with background
* "Fix" gocyclo
* Fix loading list background information for uploaded backgrounds
* Fix migrating items with large items from todoist
* Fix nfpm command in drone
* Fix parsing todoist reminder dates
* Fix reading passwords on windows
* Fix release commands in drone
* Fix release trigger
* Fix release trigger in drone
* Fix token renew for link shares
* Fix trigger for pushing release artifacts to drone
* Fix updating team admin status
* Fix upload avatar not working
* Fix users with disabled totp but not enrolled being unable to login
* Makefile: make add EXTRA_GOFLAG to GOFLAGS (#605)
* Make sure built binary files are executable when compressing with upx
* Make sure lists which would have a duplicate identifier can still be duplicated
* Make sure the metrics map accesses only happen explicitly
* Make sure to copy the permissions as well when moving files
* Make sure to only initialize all variables when needed
* Make sure to require admin rights when modifying list/namespace users to be consistent with teams
* Make sure we have git installed when building os packages
* Make sure we have go installed when building os packages (for build step dependencies)
* Only check if a bucket limit is exceeded when moving a task between buckets
* Only try to download attachments from todoist when there is a url
* Pin telegram notification plugin in drone
* Regenerate swagger docs
* Skip directories when moving build release artefacts in drone
* Support absolute iCal timestamps in CalDAV requests (#691)
* Work around tasks with attachments not being duplicated
### Changed
* Replace renovate tokens with env
* Switch s3 release bucket to scaleway
* Switch to mage (#651)
* Testing improvements (#666)
* Update docs with testmail command + reorder
* Update github.com/asaskevich/govalidator commit hash to 29e1ff8 (#639)
* Update github.com/asaskevich/govalidator commit hash to 50839af (#637)
* Update github.com/asaskevich/govalidator commit hash to 7a23bdc (#657)
* Update github.com/asaskevich/govalidator commit hash to df4adff (#552)
* Update github.com/c2h5oh/datasize commit hash to 48ed595 (#644)
* Update github.com/gordonklaus/ineffassign commit hash to e36bfde (#625)
* Update github.com/jgautheron/goconst commit hash to 8f5268c (#658)
* Update github.com/shurcooL/vfsgen commit hash to 0d455de (#642)
* Update golang.org/x/crypto commit hash to 123391f (#619)
* Update golang.org/x/crypto commit hash to 5c72a88 (#640)
* Update golang.org/x/crypto commit hash to 7f63de1 (#672)
* Update golang.org/x/crypto commit hash to 84dcc77 (#678)
* Update golang.org/x/crypto commit hash to 948cd5f (#609)
* Update golang.org/x/crypto commit hash to 9e8e0b3 (#685)
* Update golang.org/x/crypto commit hash to ab33eee (#608)
* Update golang.org/x/crypto commit hash to afb6bcd (#668)
* Update golang.org/x/crypto commit hash to c90954c (#671)
* Update golang.org/x/crypto commit hash to eb9a90e (#669)
* Update golang.org/x/image commit hash to 4578eab (#663)
* Update golang.org/x/image commit hash to a67d67e (#664)
* Update golang.org/x/image commit hash to e162460 (#665)
* Update golang.org/x/image commit hash to e59bae6 (#659)
* Update golang.org/x/sync commit hash to 3042136 (#667)
* Update golang.org/x/sync commit hash to b3e1573 (#675)
* Update module 4d63.com/tz to v1.2.0 (#631)
* Update module fzipp/gocyclo to v0.2.0 (#686)
* Update module fzipp/gocyclo to v0.3.0 (#687)
* Update module getsentry/sentry-go to v0.7.0 (#617)
* Update module go-errors/errors to v1.1.1 (#677)
* Update module go-testfixtures/testfixtures/v3 to v3.4.0 (#627)
* Update module go-testfixtures/testfixtures/v3 to v3.4.1 (#693)
* Update module iancoleman/strcase to v0.1.0 (#636)
* Update module iancoleman/strcase to v0.1.1 (#645)
* Update module iancoleman/strcase to v0.1.2 (#660)
* Update module imdario/mergo to v0.3.10 (#615)
* Update module imdario/mergo to v0.3.11 (#629)
* Update module labstack/echo/v4 to v4.1.17 (#646)
* Update module lib/pq to v1.7.1 (#616)
* Update module lib/pq to v1.8.0 (#618)
* Update module mattn/go-sqlite3 to v1.14.1 (#638)
* Update module mattn/go-sqlite3 to v1.14.2 (#647)
* Update module mattn/go-sqlite3 to v1.14.3 (#661)
* Update module mattn/go-sqlite3 to v1.14.4 (#670)
* Update module prometheus/client_golang to v1.8.0 (#681)
* Update module spf13/afero to v1.3.2 (#610)
* Update module spf13/afero to v1.3.3 (#623)
* Update module spf13/afero to v1.3.4 (#628)
* Update module spf13/afero to v1.3.5 (#650)
* Update module spf13/afero to v1.4.0 (#662)
* Update module spf13/afero to v1.4.1 (#673)
* Update module spf13/cobra to v1.1.0 (#679)
* Update module spf13/cobra to v1.1.1 (#690)
* Update module spf13/viper to v1.7.1 (#620)
* Update module src.techknowlogick.com/xgo to v1.1.0+1.15.0 (#630)
* Update module src.techknowlogick.com/xgo to v1 (#613)
* Update module swaggo/swag to v1.6.8 (#680)
* Update renovate token
* Update src.techknowlogick.com/xgo commit hash to 7c2e3c9 (#611)
* Update src.techknowlogick.com/xgo commit hash to 96de19c (#612)
* update theme
* Update xgo to v1.0.0+1.14.6
* Use db sessions for task-related things (#621)
* Use nfpm to build deb, rpm and apk packages (#689)
## [0.14.1] - 2020-07-07
### Fixed
* Fix creating lists with non ascii characters (#607)
* Fix decoding active users from redis
* Fix parsing todoist reminder dates
* Make sure the metrics map accesses only happen explicitly
### Changed
* Update docs theme
## [0.14.0] - 2020-07-01
### Added
* Add ability to run the docker container with configurable user and group ids
* Add better errors if the sqlite db file is not writable
* Add cache for initial unsplash collection
* Add docker setup guide from start to finish
* Add docs for restore
* Add dump command (#592)
* Add section to full-docker-example.md for Caddy v2 (#595)
* Add go version to version command
* Add list background information when getting all lists
* Add logging if downloading an image from unsplash fails
* Add migration test in drone (#585)
* Add option to disable totp for everyone
* Add plausible to docs
* Add restarting commands to all example docker compose files
* Add seperate docker pipeline for amd64 and arm
* Add test mail command (#571)
* Add todoist migrator to available migrators in info endpoint if it is enabled
* Add unsplash image proxy for images and thumbnails
* Add returning unsplash info when searching
* Don't return all tasks when a user has no lists
* Duplicate Lists (#603)
* Enable upload backgrounds by default
* Generate a random list identifier based on the list title
* List Backgrounds (#568)
* List Background upload (#582)
* Repeat tasks after completion (#587)
* Restore command (#593)
* Sentry integration (#591)
* Todoist Migration (#566)
### Fixed
* Ensure consistent naming of title fields (#528)
* Ensure task dates are in the future if a task has a repeating interval (#586)
* Fix caching of initial unsplash results per page
* Fix case-insensitive task search for postgresql (#524)
* Fix docker manifest build
* Fix docker multiarch build
* Fix docs theme build
* Fix getting unsplash thumbnails for non "photo-*" urls
* Fix migration 20200425182634
* Fix migration 20200516123847
* Fix migration to add position to task
* Fix misspell
* Fix namespace title not being updated
* Fix not loading timezones on all operating systems
* Fix proxying unsplash images (security)
* Fix removing existing sqlite files
* Fix resetting list, label & namespace colors
* Fix searching for unsplash pictures with words that contain a space
* Fix setting a list identifier to empty
* Fix sqlite db not working when creating a new one
* Fix sqlite path in default config
* Fix swagger docs
* Fix updating the index when moving a task
* Prevent crashing when trying to register with an empty payload
* Properly ping unsplash when using unsplash images
* Return errors when dumping
* Set the list identifier when creating a new task
### Changed
* Expose namespace id when querying lists
* Improve getting all namespaces performance (#526)
* Improve memory usage of dump by not loading all files in memory prior to adding them to the zip
* Improve metrics performance
* Load the list when setting a background
* Make the db timezone migration mysql compatible
* Make the `_unix` suffix optional when sorting tasks
* Migrate all timestamps to real iso dates (#594)
* Make sure docker images are only built when tests pass
* Remove build date from binary
* Remove dependencies on build step to speed up test pipeline (#521)
* Remove go mod vendor todo from pr template now that we don't keep dependencies in the repo anymore
* Remove migration dependency to models
* Remove min length for labels, lists, namespaces, tasks and teams
* Remove vendored dependencies
* Reorganize cmd init functions
* Set unsplash empty collection caching to one hour
* Simplify pipeline & add docker manifest step
* Update alpine Docker tag to v3.12 (#573)
* Update and fix staticcheck
* Update dependency github.com/mattn/go-sqlite3 to v1.14.0
* Update github.com/shurcooL/vfsgen commit hash to 92b8a71 (#599)
* Update golang.org/x/crypto commit hash to 279210d (#577)
* Update golang.org/x/crypto commit hash to 70a84ac (#578)
* Update golang.org/x/crypto commit hash to 75b2880 (#596)
* Update module go-redis/redis/v7 to v7.3.0 (#565)
* Update module go-redis/redis/v7 to v7.4.0 (#579)
* Update module go-testfixtures/testfixtures/v3 to v3.3.0 (#600)
* Update module lib/pq to v1.6.0 (#572)
* Update module lib/pq to v1.7.0 (#581)
* Update module prometheus/client_golang to v1.7.0 (#589)
* Update module prometheus/client_golang to v1.7.1 (#597)
* Update module spf13/afero to v1.3.0 (#588)
* Update module spf13/afero to v1.3.1 (#602)
* Update module spf13/cobra to v1 (#511)
* Update module src.techknowlogick.com/xormigrate to v1.2.1 (#574)
* Update module src.techknowlogick.com/xormigrate to v1.3.0 (#590)
* Update module stretchr/testify to v1.6.0 (#570)
* Update module stretchr/testify to v1.6.1 (#580)
* Update module swaggo/swag to v1.6.7 (#601)
* Update src.techknowlogick.com/xgo commit hash to 209a5cf (#523)
* Update src.techknowlogick.com/xgo commit hash to a09175e (#576)
* Update src.techknowlogick.com/xgo commit hash to eeb7c0a (#575)
* update theme
* Update theme
* Update web handler
* Update xorm.io/xorm 1.0.1 -> 1.0.2
* Use the db logger instance for logging migration related stuff
## [0.13.1] - 2020-05-19
### Fixed
* Don't get all tasks if a user has no lists
## [0.13] - 2020-05-12
#### Added
* Add 2fa for authentification (#383)
* Add categories to error docs
* Add changing email for users
* Add community link
* Add configuration options for log level
* Add creating a new first bucket when creating a new list
* Add docs for changing frontend url
* Add endpoint to disable totp auth
* Add endpoint to get the current users totp status
* Add explanation to docs about cors
* Add github token for renovate (#164)
* Add gosec static analysis
* Add moving tasks between lists (#389)
* Add real buckets for tasks which don't have one (#446)
* Add traefik 2 example configuration
* Configure Renovate (#159)
* Kanban (#393)
* Task filters (#243)
* Task Position (#412)
#### Fixed
* Add checking and logging when trying to put a task into a nonexisting bucket
* Fix bucket ID being reset with no need to do so
* Fix creating new things with a link share auth
* Fix dependencies
* Fix gosec in drone
* Fix link share creation & creating admin link shares without admin rights
* Fix moving tasks back into the empty (ID: 0) bucket
* Fix moving tasks in buckets
* Fix not moving its bucket when moving a task between lists
* Fix pagination count for task collection
* Fix parsing array style comparators by query param
* Fix reference to reverse proxies in docs
* Fix removing the last bucket
* Fix replace statements for tail
* Fix team rights not updating for namespace rights
* Fix tests after renaming json fields to snake_case
* Fix total label count when getting all labels (#477)
* Remove setting task bucket to 0
* Task Filter Fixes (#495)
#### Changed
* Change all json fields to snake_case
* Change totp secret datatype from varchar to text
* Update alpine Docker tag to v3.11 (#160)
* Update docs theme
* Update github.com/c2h5oh/datasize commit hash to 28bbd47 (#212)
* Update github.com/gordonklaus/ineffassign commit hash to 7953dde (#233)
* Update github.com/jgautheron/goconst commit hash to cda7ea3 (#228)
* Update github.com/shurcooL/httpfs commit hash to 8d4bc4b (#229)
* Update golang.org/x/crypto commit hash to 056763e (#222)
* Update golang.org/x/crypto commit hash to 06a226f (#504)
* Update golang.org/x/crypto commit hash to 0848c95 (#371)
* Update golang.org/x/crypto commit hash to 3c4aac8 (#419)
* Update golang.org/x/crypto commit hash to 44a6062 (#429)
* Update golang.org/x/crypto commit hash to 4b2356b (#475)
* Update golang.org/x/crypto commit hash to 4bdfaf4 (#438)
* Update golang.org/x/crypto commit hash to 729f1e8 (#458)
* Update golang.org/x/crypto commit hash to a76a400 (#411)
* Update golang.org/x/lint commit hash to 738671d (#223)
* Update module go-redis/redis to v6.15.7 (#234)
* Update module go-redis/redis to v6.15.7 (#290)
* Update module go-redis/redis to v7 (#277)
* Update module go-redis/redis to v7 (#309)
* Update module go-testfixtures/testfixtures/v3 to v3.1.2 (#457)
* Update module go-testfixtures/testfixtures/v3 to v3.2.0 (#505)
* Update module imdario/mergo to v0.3.9 (#238)
* Update module labstack/echo/v4 to v4.1.16 (#241)
* Update module lib/pq to v1.4.0 (#428)
* Update module lib/pq to v1.5.0 (#476)
* Update module lib/pq to v1.5.1 (#485)
* Update module lib/pq to v1.5.2 (#491)
* Update module olekukonko/tablewriter to v0.0.4 (#240)
* Update module prometheus/client_golang to v0.9.4 (#245)
* Update module prometheus/client_golang to v1
* Update module prometheus/client_golang to v1.6.0 (#463)
* Update module spf13/cobra to v0.0.7 (#271)
* Update module spf13/viper to v1.6.2 (#272)
* Update module spf13/viper to v1.6.3 (#291)
* Update module spf13/viper to v1.7.0 (#494)
* Update module stretchr/testify to v1.5.1 (#274)
* Update Renovate Configuration (#161)
* Update src.techknowlogick.com/xgo commit hash to bb0faa3 (#279)
* Update src.techknowlogick.com/xgo commit hash to c43d4c4 (#224)
* Update xorm redis cacher to use the xorm logger instead of a special seperate one
* Update xorm to v1 (#323)
## [0.12] - 2020-04-04
#### Added
* Add support for archiving lists and namespaces (#152)
* Colors for lists and namespaces (#155)
* Add build time to compile flags
* Add proxying gravatar requests for user avatars (#148)
* Add empty avatar provider (#149)
* expand relative path ~/.config/vikunja to $HOME/.config/vikunja **WINDOWS** (#147)
* Show lists as archived if their namespace is archived
#### Fixed
* Workaround for timezones on windows (#151)
* Fix getting one namespace
* Fix getting the authenticated user with caldav
* Fix searching for config in home directories
* Fix updating lists with an identifier
#### Changed
* Change release bucket
## [0.11] - 2020-03-01
### Added
* Add config options for cors handling (#124)
* Add config options for task attachments (#125)
* Add generate as a make dependency for make build
* Add logging for invalid model errors (#126)
* Add more logging to web handler methods
* Add postgres support (#135)
* Add rate limit by ip for non-authenticated routes (#127)
* Better efficency for loading teams (#128)
* Expand relative path ~/.config/vikunja to $HOME/.config/vikunja (#146)
* Task Comments (#138)
### Fixed
* Fix typo in docker-compose example (#140)
* Fix frontend url for wunderlist migration in docs
* Fix inserting task structure with related tasks (#142)
* Fix time zone settings not working in Docker
* Fix updating dates when marking a task as done (#145)
* Make sure the author is returned when creating a new comment
* Remove double user field
### Changed
* Explicitly disable wunderlist migration by default (#141)
* Migration Improvements (#122)
* Refactor User and DB handling (#123)
* Return iso dates for everything date related from the api (#130)
* Update copyright header
* Update theme
* Update xorm to use the new import path (#133)
* Use relative url in .gitmodules (#132)
## [0.10] - 2020-01-19
### Added
* Migration (#120)
* Endpoint to get tasks on a list (#108)
* Sort Order for tasks (#110)
* Add files volume to docker compose docs
* Add motd config option to docs
* Add option to disable registration (#117)
* Add task identifier (#115)
* Add tests for md5 generation (#111)
* Add user token renew (#113)
### Fixed
* Fix new tasks not getting a new task index (#116)
* Fix owner field being null for user shared namespaces (#119)
* Fix passing sort_by and order_by as query path arrays
* Fix sorting tasks by bool values
* Fix task collection tests
* Consistent copyright text in file headers (#112)
### Changed
* Task collection improvements (#109)
* Update copyright year (#118)
* Update docs with a traefik configuration
* Use redis INCRBY and DECRBY when updating metrics values (#121)
* Use utf8mb4 instead of plain utf8 (#114)
* Update docs theme
## [0.9] - 2019-11-24
### Added
* Task Attachments (#104)
* Task Relations (#103)
* Add endpoint to get a single task (#106)
* Add file volume to the docker image
* Added extra depth to logging to correctly show the functions calling the logger in logs
* Added more infos to a link share auth (#98)
* Added percent done to tasks (#102)
### Fixed
* Fix default logging settings (#107)
* Fixed a bug where adding assignees or reminders via an update would re-create them and not respect already inserted ones, leaving a lot of garbage
* Fixed a bug where deleting an attachment would cause a nil panic
* Fixed building docs theme
* Fixed error when setting max file size on 32-Bit systems
* Fixed labels being displayed multiple times if they were associated with more than one task (#99)
* Fixed metrics on/off setting
* Fixed migration for task relations
* Fixed not getting all labels when retrieving a list with all tasks
* Fixed panic when using link share and metrics
* Fixed rate limit panic when authenticating with a link share auth token (#97)
* Fixed removing reminders
* Small link share fixes (#96)
### Changed
* Improve pagination (#105)
* Moved `teams_{namespace|list}_*` to `{namespace|list}_teams_*` for better consistency (#101)
* Refactored getting all lists for a namespace (#100)
* Refactored getting task IDs for labels
* Switched default logger to stdout instead of stderr
* update docs theme
### Misc
* Move from markdown lists to Vikunja for roadmap
## [0.8] - 2019-09-01
### Added
* Better Caldav support (#73)
* Added settings for max open/idle connections and max connection lifetime (#74)
* /info endpoint (#85)
* Added http endpoint to list all users on a list (#87)
* Rate limits (#91)
* Sharing of lists via public links (#94)
### Changed
* Reminders now use an extra table (#75)
* Use the username instead of a full user object when adding a user to a team or giving it rights (#76)
* Add the md5-hashed user email to user objects for use with gravatar (#78)
* Use the auth methods to get IDs to avoid unneeded casts
* Better config handling with constants (#83)
* Statically compile templates in the final binary (#84)
* Use longtext instead of varchar(1000) on description fields (#88)
* Logger refactoring (#90)
### Fixed
* Fixed `listID` not being returned in tasks
* Fixed tests (#72)
* Fixed metrics endpoint not working
* Fixed check if the user really exists before updating/deleting its rights (#77)
* Fixed duedate spelling issue (#79)
### Misc
* Integration tests (#71)
* Make sure the version works when building in drone
* Switched to another version of xgo
* Simplified the docker image (#80)
* Update echo (#82)
* Compress binaries after building them (#81)
* Simplify structure by having less files (#86)
* Limit the test pipeline to run only on pull requests (#89)
* GetUser now returns a pointer (#93)
* Refactor ListTask to Task (#92)
## [0.7] - 2019-04-05
### Added
* DB migrations (#67)
* More cli options for Vikunja (#66 #68)
* Use query params to sort tasks instead of url params (#61)
* More config paths (#55)
### Fixed
* Fixed Priority not updating when setting it to 0
* Fixed getting lists by namespace
* Fixed rights check (#70 #62)
* Fixed labels not being queried correctly on tasks
* Fixed bulk update label tasks
### Changed
* Hide a user's email address everywhere (#69)
* Refactored `canRead()` to get the list before checking rights #65
* Let rights methods return errors (#64 #63)
* Improved Swagger docs for label tasks
* Docs improvements (#58)
* Logging Handling (#57)
* Rights performance improvements (#54)
### Misc
* Releases also as Debian packages (#56)
## [0.6] - 2019-01-16
### Added
* Added prometheus endpoint to get metrics (#33)
* More unit tests (#34)
* Tests can now use config files (#36)
* Redoc for swagger ui (#39, #46)
* Start and end dates for tasks (#40)
* Get tasks between a date range (#41)
* Bulk edit for tasks (#42)
* More ci checks (#43)
* Task assignees (#44, #47)
* Task labels (#45, #48)
### Fixed
* Fixed path to get all tasks (echo bug)
* Explicitly get the peudonamespace with all shared lists (#32)
* Properly init tabels Redis
* unexpected EOF when using metrics (#35)
* Task sorting in lists (#36)
* Various user fixes (#38)
* Fixed a bug where updating a list would update it with the same values it had
### Changed
* Simplified list rights check (#50)
* Refactored some structs to not expose unneded values via json (#52)
### Misc
* Updated libraries
* Updated drone to version 1
* Releases are now signed with our pgp key (more info about this on [the download page](https://vikunja.io/en/download/)).
## [0.5] - 2018-12-02
### Added
* Shared lists are now shown in a pseudonamespace with all other namespaces, has the ID -1
* Tasks can have multiple reminders
* Tasks can have subtasks. Subtasks are fully-fleged tasks, but not shown in the task list of a list.
* Tasks can have priorities
### Changed
* Validation not so verbose anymore
* [License](https://git.kolaente.de/vikunja/api/src/branch/master/LICENSE) is now GPLv3
* The crudhandler now has its [own repo](https://git.kolaente.de/vikunja/web) - you can use it in your own projects!
## [0.4] - 2018-11-16
#### Added
* Get all tasks for the authenticated user sorted by their due date
* CalDAV support
* Pagination for everything which returns an array
* Search all the things
* More validation for most of the structs
* Improved Swagger docs (available on `/api/v1/swagger`)
## [0.3] - 2018-11-02
### Added
* Password reset
* Email verification when registering
Misc bugfixes and improvements to the build process
## [0.2] - 2018-10-17
## [0.1] - 2018-09-20

View File

@ -1,51 +1,45 @@
##############
# Build stage
FROM golang:1-alpine AS build-env
###################################
#Build stage
FROM golang:1.11-alpine AS build-env
ARG VIKUNJA_VERSION
ENV TAGS "sqlite"
ENV GO111MODULE=on
ENV GOFLAGS=-mod=vendor
# Build deps
#Build deps
RUN apk --no-cache add build-base git
# Setup repo
#Setup repo
COPY . ${GOPATH}/src/code.vikunja.io/api
WORKDIR ${GOPATH}/src/code.vikunja.io/api
# Checkout version if set
#Checkout version if set
RUN if [ -n "${VIKUNJA_VERSION}" ]; then git checkout "${VIKUNJA_VERSION}"; fi \
&& go install github.com/magefile/mage \
&& mage build:clean build:build
&& make clean build
###################
# The actual image
# Note: I wanted to use the scratch image here, but unfortunatly the go-sqlite bindings require cgo and
# because of this, the container would not start when I compiled the image without cgo.
FROM alpine:3.12
FROM alpine:3.7
LABEL maintainer="maintainers@vikunja.io"
WORKDIR /app/vikunja/
COPY --from=build-env /go/src/code.vikunja.io/api/vikunja .
EXPOSE 3456
RUN apk --no-cache add \
bash \
ca-certificates \
curl \
gettext \
linux-pam \
s6 \
sqlite \
su-exec \
tzdata
COPY docker /
COPY --from=build-env /go/src/code.vikunja.io/api/templates /app/vikunja/templates
COPY --from=build-env /go/src/code.vikunja.io/api/vikunja /app/vikunja/vikunja
ENV VIKUNJA_SERVICE_ROOTPATH=/app/vikunja/
# Dynamic permission changing stuff
ENV PUID 1000
ENV PGID 1000
RUN apk --no-cache add shadow && \
addgroup -g ${PGID} vikunja && \
adduser -s /bin/sh -D -G vikunja -u ${PUID} vikunja -h /app/vikunja -H && \
chown vikunja -R /app/vikunja
COPY run.sh /run.sh
# Fix time zone settings not working
RUN apk --no-cache add tzdata
# Files permissions
RUN mkdir /app/vikunja/files && \
chown -R vikunja /app/vikunja/files
VOLUME /app/vikunja/files
CMD ["/run.sh"]
EXPOSE 3456
ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"]
CMD []

309
Featurecreep.md Normal file
View File

@ -0,0 +1,309 @@
# Featurecreep
This is the place where I write down ideas to work on at some point.
Sorry for some of them being in German, I'll tranlate them at some point.
## Feature Ideas
* [x] Priorities
* [x] Repeating tasks
* [x] Get all tasks which are due between two given dates
* [x] Subtasks
## Anderes
* [x] Refactor!!!! Delete everything not being used anymore, simplify.
* [x] Drone
* [x] Tests
* [x] Find a nme
* [x] Move packages to a better structure
* [x] Swagger UI
+ [x] Fix CORS
* [x] Use echo.NewHTTPError instead of c.JSON(Message{})
* [x] Better error messages when the model which is sent to the server is wrong
* [x] Better error handling to show useful error messages and status codes
* [x] Viper for config instead of ini
* [x] Docs for installing
* [x] Tests for rights managemnt
* [x] Rights checks:
* [x] Create lists
* [x] Edit lists
* [x] Add tasks
* [x] Edit tasks
* [x] The -1 namespace should also be accessible seperately
### Short Term
* [x] Cacher configurable
* [x] Should throw an error when an id < 1
* [x] /users should also return the rights
* [x] Extra endpoint /teams/members /list/users to update rights without needing to remove and re-add them
* [x] namespaces & listen update does not work, returns 500
* [x] Logging for all errors somewhere
* [x] Ne extra funktion für list exists machen, damit die nicht immer über GetListByID gehen, um sql-abfragen zu sparen
* [x] Rausfinden warum xorm teilweise beim einfügen IDs mit einfügen will -> Das schlägt dann wegen duplicate fehl
* [x] Bei den Structs "AfterLoad" raus, das verbraucht bei Gruppenabfragen zu viele SQL-Abfragen -> Die sollen einfach die entsprechenden Read()-Methoden verwenden (Krassestes bsp. ist GET /namespaces mit so ca 50 Abfragen)
* [x] General search endpoints
* [x] Validation der ankommenden structs, am besten mit https://github.com/go-validator/validator oder mit dem Ding von echo
* [x] Pagination
* Sollte in der Config definierbar sein, wie viel pro Seite angezeigt werden soll, die CRUD-Methoden übergeben dann ein "gibt mir die Seite sowieso" an die CRUDable-Funktionenen, die müssen das dann Auswerten. Geht leider nicht anders, wenn man erst 2342352 Einträge hohlt und die dann nachträglich auf 200 begrenzt ist das ne massive Ressourcenverschwendung.
* [x] Testen, ob man über die Routen methode von echo irgendwie ein swagger spec generieren könnte -> Andere Swagger library
* [x] CalDAV
* [x] Basics
* [x] Reminders
* [x] Discovery, stichwort PROPFIND
* [x] Wir brauchen noch ne gute idee, wie man die listen kriegt, auf die man nur so Zugriff hat (ohne namespace)
* Dazu am Besten nen pseudonamespace anlegen (id -1 oder so), der hat das dann alles
* [x] Testing mit locust: https://locust.io/
* [ ] Endpoint to get all users who have access to a list - regardless of via team, user share or via namespace
#### Userstuff
* [x] Userstuff aufräumen
-> Soweit es geht und Sinnvoll ist auf den neuen Handler umziehen
-> Login/Register/Password-reset geht natürlich nicht
-> Bleibt noch Profile abrufen und Einstellungen -> Macht also keinen Sinn das auf den neuen Handler umzuziehen
* [x] Email-Verifizierung beim Registrieren
* [x] Password Reset
* [ ] New field in user model which holds a url of an avatar image - for now just Gravatar, later more
* [ ] Settings
* [ ] Password update
* [ ] Email update
* [ ] Ob man über email oder Benutzernamen gefunden werden darf
### Bugfixes
* [x] Panic wenn mailer nicht erreichbar -> Als workaround mailer deaktivierbar machen, bzw keine mails verschicken
* [x] "unexpected EOF"
* [x] Beim Login & Password reset gibt die API zurück dass der Nutzer nicht existiert
* [x] Re-check rights checks to see if all information which is compared against is properly read from the db and not only based on user input
* [x] Lists
* [x] List users
* [x] List Teams
* [x] Labels
* [x] Tasks
* [x] Namespaces
* [x] Namespace users
* [x] Namespace teams
* [x] Teams
* [x] Team member handling
* [x] Also check `ReadOne()` for unnessecary database operations since the inital query is already done in `CanRead()`
* [x] Add a `User.AfterLoad()` which obfuscates the email address
* [x] Fix priority not updating to 0
### Docs
* [x] Readme
* [x] Auch noch nen "link" zum Featurecreep
* [x] ToC
* [x] Logo
* [x] How to build -> Docs
* [x] How to dev -> Docs
* [x] License
* [x] Contributing
* [x] Redocs
* [x] Swaggerdocs verbessern
* [x] Descriptions in structs
* [x] Maxlength specify etc. (see swaggo docs)
* [x] Rights
* [x] API
* [x] Anleitung zum Makefile
* [x] How to build from source
* [x] Struktur erklären
* [x] Deploy in die docs
* [x] Docker
* [x] Native (systemd + nginx/apache)
* [x] Backups
* [x] Docs aufsetzen
### Tasks
* [x] Start/Enddatum für Tasks
* [x] Timeline/Calendar view -> Dazu tasks die in einem Bestimmten Bereich due sind, macht dann das Frontend
* [x] Tasks innerhalb eines definierbarem Bereich, sollte aber trotzdem der server machen, so à la "Gib mir alles für diesen Monat"
* [x] Bulk-edit -> Transactions
* [x] Assignees
* [x] Check if something changed at all before running everything
* [x] Don't use `list.ReadOne()`, gets too much unnessecary shit
* [x] Wegen Performance auf eigene endpoints umziehen, wie labels
* [x] "One endpoint to rule them all" -> Array-addable
* [x] Labels
* [x] Check if something changed at all before running everything
* [x] Editable via task edit, like assignees
* [x] "One endpoint to rule them all" -> Array-addable
* [ ] Attachments
* [ ] Task-Templates innerhalb namespaces und Listen (-> Mehrere, die auswählbar sind)
* [ ] Ein Task muss von mehreren Assignees abgehakt werden bis er als done markiert wird
* [ ] Besseres Rechtesystem, damit man so fine-graded sachen machen kann wie "Der da darf aber nur Tasks hinzufügen, aber keine abhaken"
* [ ] Roles which enable or disable chaning certain fields of a task -> includes custm fields
* [ ] Custom fields: Templates at List > Namespace > Global level, overwriting each other
* [ ] Related tasks -> settable with a "kind" of relation like blocked, or just related or so
* [ ] Description should be longtext
* [ ] Pecent done - For now just a float, may later depend on how many sub tasks are done or so
### General features
* [x] Deps nach mod umziehen
* [x] Performance bei rechtchecks verbessern
* User & Teamright sollte sich für n rechte in einer Funktion testen lassen
* [ ] Endpoint um die Rechte mit Beschreibung und code zu kriegen
* [ ] "Smart Lists", Listen nach bestimmten Kriterien gefiltert -> speichern und im pseudonamespace
* [ ] "Performance-Statistik" -> Wie viele Tasks man in bestimmten Zeiträumen so geschafft hat etc
* [ ] IMAP-Integration -> Man schickt eine email an Vikunja und es macht daraus dann nen task -> Achtung missbrauchsmöglichkeiten
* [ ] In und Out webhooks, mit Templates vom Payload
* [ ] Reminders via mail
* [ ] Activity Feed, so à la "der und der hat das und das gemacht etc"
* [ ] Per list
* [ ] For the current user
* [ ] ~~Websockets~~ SSE https://github.com/kljensen/golang-html5-sse-example
* User authenticates (with jwt)
* When updating/creating/etc an event struct is sent to the broker
* The broker has a list of subscribed users
* It then checks who is allowed to the see the event it recieved and sends it
* [ ] Being able to define filters for notifications or turn them silent completely -> Probably frontend only
* [ ] mgl. zum Emailmaskieren haben (in den Nutzereinstellungen, wenn man seine Email nicht an alle Welt rausposaunen will)
* [ ] Mgl. zum Accountlöschen haben (so richtig krass mit emailverifiezierung und dass alle Privaten Listen gelöscht werden und man alle geteilten entweder wem übertragen muss oder auf privat stellen)
* [ ] /info endpoint, in dem dann zb die limits und version etc steht
* [ ] Deprecate /namespaces/{id}/lists in favour of namespace.ReadOne() <-- should also return the lists
* [ ] Bindata for templates
* [ ] `GetUserByID` and the likes should return pointers
* [ ] Colors for lists and namespaces -> Up to the frontend to implement these
* [ ] Some kind of milestones for tasks
* [ ] Create tasks from a text/markdown file (probably frontend only)
* [ ] Label-view: Get a bunch of tasks by label
* [ ] Debian package should have a service file
* [ ] Downloads should be served via nginx (with theme?), minio should only be used for pushing artifacts.
* [ ] User struct should have a field for the avatar url (-> gravatar md5 calculated by the backend)
* [ ] All `ReadAll` methods should return the number of items per page, the number of items on this page, the total pages and the items
-> Check if there's a way to do that efficently. Maybe only implementing it in the web handler.
* [ ] List stats to see how many tasks are done, how many are there in total, how many people have acces to a list etc
* [ ] Colors for tasks
* [ ] Better caldav support
* [x] VTODO
* [x] Fix organizer prop
* [x] Depricate the events thing for now
* [x] PROPFIND/OPTIONS : caldav discovery
* [x] Create new tasks
* [x] Save uid from the client
* [x] Update tasks
* [x] Marking as done
* [x] Fix OPTIONS Requests to the rest of the api being broken
* [x] Parse all props defined in rfc5545
* [x] COMPLETED -> Need to actually save the time the task was completed
* [x] Whenever a task ist created/updated, update the `updated` prop on the list so the etag changes and clients get notified
* [x] Fix not all tasks being displayed (My guess: Something about that f*cking etag)
* [x] Delete tasks
* [x] Last modified
* [x] Content Size
* [x] Modify the caldav lib as proposed in the pr
* [x] Improve login performance, each request taking > 1.5 sec is just too much, maybe just use the default value for hash iterations in the login/register function
* [x] Only show priority when we have one
* [x] Show a proper calendar title
* [x] Fix home principal propfind stuff
* [x] Docs
* [x] Setting to disable caldav completely
* [ ] Make it work with the app
* [ ] Cleanup the whole mess I made with the handlers and storage providers etc -> Probably a good idea to create a seperate storage provider etc for lists and tasks
* [ ] Tests
* [ ] Check if only needed things are queried from the db when accessing dav (for ex. no need to get all tasks when we act
### Refactor
* [x] ListTaskRights, sollte überall gleich funktionieren, gibt ja mittlerweile auch eine Methode um liste von nem Task aus zu kriegen oder so
* [x] Re-check all `{List|Namespace}{User|Team}` if really all parameters need to be exposed via json or are overwritten via param anyway.
* [x] Things like list/task order should use queries and not url params
* [x] Fix lint errors
* [x] Add settings for max open/idle connections and max connection lifetime
* [x] Reminders should use an extra table so we can make reverse lookups aka "give me all tasks with reminders in this period" which we'll need for things like email reminders notifications
* [x] Teams and users should also have uuids (for users these can be the username)
* [ ] When giving a team or user access to a list/namespace, they should be reffered to by uuid, not numeric id
* [ ] Adding users to a team should also use uuid
* [ ] Check if the team/user really exist before updating them on lists/namespaces
* [ ] Check if the email is properly obfuscated everywhere -> alter GetUser() and add a new method GetUserWithEmail
### Linters
* [x] goconst
* [x] Staticcheck
* [x] gocyclo-check
* [ ] gosec-check -> waiting for mod
* [x] goconst-check
* [ ] golangci -> docker in drone, will probably make all other linters obsolete
### More server settings
* [x] Caldav disable/enable
* [ ] Assignees disable/enable
* [ ] List/Namespace limits
* [ ] Attachements disable/enable
* [ ] Attachements size
* [ ] Templates disable/enable
* [ ] Stats disable/enable
* [ ] Activity notifications disable/enable
* [ ] IMAP integration disable/enable
* [ ] Reminders via mail disable/enable
### Later
* [ ] Backgrounds for lists -> needs uploading and storing and so on
* [ ] Plugins
* [ ] Rename Namespaces to collections (or spaces?)
* [ ] Collections n-n (one list can be in multiple collections)?
* [ ] Rename lists to projects
* [ ] Per-User limits of lists/namespaces
* [ ] Admin-Interface to do stuff like settings and user management
* [ ] Enable/Disable users
* [ ] Better rights, fine-graded
* [ ] Enable/disable allowing user adding to lists/namespaces for specific lists or namespaces
* [ ] Admins should be able to see and mange all the boards
* [ ] Limit registration to users with a defined email domain
* [ ] Close the instance, either no registration or only one with defined email
* [ ] 2fa
* [ ] Custom fields for tasks
* [ ] Sorting lists by members, tasks, teams, last modified, etc
* [ ] "Favourite lists" -> A user can favourize boards which will then show up in a pseudonamespace
* [ ] Public lists
* [ ] Internal lists -> Only registered users can see the list
* [ ] Rights management for both public and internal lists
* [ ] Add new users via to a list which don't have an account yet, they'd get a link to sign up for vikunja.
* [ ] Respect registration email domain limits
* [ ] Export all data from Vikunja to json
* [ ] Watch a (n internal) list -> Will get notification for everything
* [ ] Archive a task instead of deleting
* [ ] Task dependencies
* [ ] Time tracking (possible plugin)
* [ ] IFTTT
* [ ] More sharing features (all of these with the already existing permissions)
* [ ] Invite users per mail
* [ ] Share a link with/without password
* [ ] Comments on tasks
* [ ] @mention users in tasks or comments to get them notified
* [ ] Summary of tasks to do in a configurable interval (every day/week or so)
* [ ] Disable/enable task fields in a list
* [ ] With inheritence from namespaces
* [ ] Custom statuses for tasks, configurable in the list settings
* [ ] With inheritence from namespaces
* [ ] better filters
* [ ] by lables
* [ ] Due dates
* [ ] Start/End dates
* [ ] Assignees
* [ ] Priorities
* [ ] Importer (maybe frontend only)
* [ ] Trello
* [ ] Wunderlist
* [ ] Zenkit
* [ ] Asana
* [ ] Microsoft Todo
* [ ] Nozbe
* [ ] Lanes
* [ ] Nirvana
* [ ] Any.do
* [ ] Good ol' Caldav (Tasks)
* [ ] ClickUp
* [ ] More auth providers
* [ ] LDAP/AD
* [ ] Kerberos
* [ ] SAML (what?)
* [ ] smtp
* [ ] OpenID

230
Makefile Normal file
View File

@ -0,0 +1,230 @@
DIST := dist
IMPORT := code.vikunja.io/api
SED_INPLACE := sed -i
ifeq ($(OS), Windows_NT)
EXECUTABLE := vikunja.exe
else
EXECUTABLE := vikunja
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SED_INPLACE := sed -i ''
endif
endif
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
GOFMT ?= gofmt -s
GOFLAGS := -v -mod=vendor
EXTRA_GOFLAGS ?=
LDFLAGS := -X "code.vikunja.io/api/pkg/cmd.Version=$(shell git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')" -X "main.Tags=$(TAGS)"
PACKAGES ?= $(filter-out code.vikunja.io/api/pkg/integrations,$(shell go list -mod=vendor ./... | grep -v /vendor/))
SOURCES ?= $(shell find . -name "*.go" -type f)
TAGS ?=
ifeq ($(OS), Windows_NT)
EXECUTABLE := vikunja.exe
else
EXECUTABLE := vikunja
endif
ifneq ($(DRONE_TAG),)
VERSION ?= $(subst v,,$(DRONE_TAG))
else
ifneq ($(DRONE_BRANCH),)
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
else
VERSION ?= master
endif
endif
ifeq ($(DRONE_WORKSPACE),'')
BINLOCATION := $(EXECUTABLE)
else
BINLOCATION := $(DIST)/binaries/$(EXECUTABLE)-$(VERSION)-linux-amd64
endif
ifeq ($(VERSION),master)
PKGVERSION := $(shell git describe --tags --always --abbrev=10 | sed 's/-/+/' | sed 's/^v//' | sed 's/-g/-/')
else
PKGVERSION := $(VERSION)
endif
.PHONY: all
all: build
.PHONY: clean
clean:
go clean ./...
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA)
.PHONY: test
test:
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) -cover -coverprofile cover.out $(PACKAGES)
go tool cover -html=cover.out -o cover.html
.PHONY: integration-test
integration-test:
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) code.vikunja.io/api/pkg/integrations
.PHONY: lint
lint:
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) golang.org/x/lint/golint; \
fi
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
.PHONY: fmt
fmt:
$(GOFMT) -w $(GOFILES)
.PHONY: fmt-check
fmt-check:
# get all go files and run go fmt on them
@diff=$$($(GOFMT) -d $(GOFILES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: build
build: $(EXECUTABLE)
$(EXECUTABLE): $(SOURCES)
go build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
.PHONY: release
release: release-dirs release-windows release-linux release-darwin release-copy release-check release-os-package release-zip
.PHONY: release-dirs
release-dirs:
mkdir -p $(DIST)/binaries $(DIST)/release $(DIST)/zip
.PHONY: release-windows
release-windows:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) src.techknowlogick.com/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out vikunja-$(VERSION) .
ifneq ($(DRONE_WORKSPACE),'')
mv /build/* $(DIST)/binaries
endif
.PHONY: release-linux
release-linux:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) src.techknowlogick.com/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out vikunja-$(VERSION) .
ifneq ($(DRONE_WORKSPACE),'')
mv /build/* $(DIST)/binaries
endif
.PHONY: release-darwin
release-darwin:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) src.techknowlogick.com/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out vikunja-$(VERSION) .
ifneq ($(DRONE_WORKSPACE),'')
mv /build/* $(DIST)/binaries
endif
.PHONY: release-copy
release-copy:
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
mkdir $(DIST)/release/templates -p
cp templates/ $(DIST)/templates/ -R
.PHONY: release-check
release-check:
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
.PHONY: release-os-package
release-os-package:
$(foreach file,$(filter-out %.sha256,$(wildcard $(DIST)/release/$(EXECUTABLE)-*)),mkdir $(file)-full;mv $(file) $(file)-full/; mv $(file).sha256 $(file)-full/; cp config.yml.sample $(file)-full/config.yml; cp $(DIST)/release/templates $(file)-full/ -R; cp LICENSE $(file)-full/; )
.PHONY: release-zip
release-zip:
$(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),cd $(file); zip -r ../../zip/$(shell basename $(file)).zip *; cd ../../../; )
# Builds a deb package using fpm from a previously created binary (using make build)
.PHONY: build-deb
build-deb:
fpm -s dir -t deb --url https://vikunja.io -n vikunja -v $(PKGVERSION) --license GPLv3 --directories /opt/vikunja --after-install ./build/after-install.sh --description 'Vikunja is an open-source todo application, written in Go. It lets you create lists,tasks and share them via teams or directly between users.' -m maintainers@vikunja.io ./$(BINLOCATION)=/opt/vikunja/vikunja ./templates=/opt/vikunja ./config.yml.sample=/etc/vikunja/config.yml;
.PHONY: reprepro
reprepro:
reprepro_expect debian includedeb strech ./$(EXECUTABLE)_$(PKGVERSION)_amd64.deb
.PHONY: got-swag
got-swag: do-the-swag
@diff=$$(git diff docs/swagger/swagger.json); \
if [ -n "$$diff" ]; then \
echo "Please run 'make do-the-swag' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: do-the-swag
do-the-swag:
@hash swag > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) github.com/swaggo/swag/cmd/swag; \
fi
swag init -g pkg/routes/routes.go -s ./pkg/swagger;
# Fix the generated swagger file, currently a workaround until swaggo can properly use go mod
sed -i '/"definitions": {/a "code.vikunja.io.web.HTTPError": {"type": "object","properties": {"code": {"type": "integer"},"message": {"type": "string"}}},' docs/docs.go;
sed -i 's/code.vikunja.io\/web.HTTPError/code.vikunja.io.web.HTTPError/g' docs/docs.go;
sed -i 's/package\ docs/package\ swagger/g' docs/docs.go;
sed -i 's/` + \\"`\\" + `/` + "`" + `/g' docs/docs.go;
mv ./docs/docs.go ./pkg/swagger/docs.go;
.PHONY: misspell-check
misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) github.com/client9/misspell/cmd/misspell; \
fi
for S in $(GOFILES); do misspell -error $$S || exit 1; done;
.PHONY: ineffassign-check
ineffassign-check:
@hash ineffassign > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) github.com/gordonklaus/ineffassign; \
fi
for S in $(GOFILES); do ineffassign $$S || exit 1; done;
.PHONY: gocyclo-check
gocyclo-check:
@hash gocyclo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/fzipp/gocyclo; \
go install $(GOFLAGS) github.com/fzipp/gocyclo; \
fi
for S in $(GOFILES); do gocyclo -over 17 $$S || exit 1; done;
.PHONY: static-check
static-check:
@hash staticcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u honnef.co/go/tools; \
go install $(GOFLAGS) honnef.co/go/tools/cmd/staticcheck; \
fi
staticcheck $(PACKAGES);
.PHONY: gosec-check
gosec-check:
@hash ./bin/gosec > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s 1.2.0; \
fi
for S in $(PACKAGES); do ./bin/gosec $$S || exit 1; done;
.PHONY: goconst-check
goconst-check:
@hash goconst > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/jgautheron/goconst/cmd/goconst; \
go install $(GOFLAGS) github.com/jgautheron/goconst/cmd/goconst; \
fi
for S in $(PACKAGES); do goconst $$S || exit 1; done;

View File

@ -1,11 +1,12 @@
<img src="https://vikunja.io/images/vikunja-logo.svg" alt="" style="display: block;width: 50%;margin: 0 auto;" width="50%"/>
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/api/status.svg)](https://drone.kolaente.de/vikunja/api)
[![Build Status](https://drone1.kolaente.de/api/badges/vikunja/api/status.svg)](https://drone1.kolaente.de/vikunja/api)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](LICENSE)
[![Download](https://img.shields.io/badge/download-v0.15.1-brightgreen.svg)](https://dl.vikunja.io)
[![Download](https://img.shields.io/badge/download-v0.8-brightgreen.svg)](https://storage.kolaente.de/minio/vikunja/)
[![Docker Pulls](https://img.shields.io/docker/pulls/vikunja/api.svg)](https://hub.docker.com/r/vikunja/api/)
[![Swagger Docs](https://img.shields.io/badge/swagger-docs-brightgreen.svg)](https://try.vikunja.io/api/v1/docs)
[![Go Report Card](https://goreportcard.com/badge/git.kolaente.de/vikunja/api)](https://goreportcard.com/report/git.kolaente.de/vikunja/api)
[![cover.run](https://cover.run/go/code.vikunja.io/api.svg?style=flat&tag=golang-1.10)](https://cover.run/go?tag=golang-1.10&repo=code.vikunja.io%2Fapi)
# Vikunja API
@ -25,17 +26,15 @@
* Reminder for tasks
* Namespaces: A "group" which bundels multiple lists
* Share lists and namespaces with teams and users with granular permissions
* Plenty of details for tasks
See [the features page](https://vikunja.io/en/features/) on our website for a more exaustive list or
try it on [try.vikunja.io](https://try.vikunja.io)!
Try it on [try.vikunja.io](https://try.vikunja.io)!
## Docs
* [Installing](https://vikunja.io/docs/installing/)
* [Build from source](https://vikunja.io/docs/build-from-sources/)
* [Development setup](https://vikunja.io/docs/development/)
* [Magefile](https://vikunja.io/docs/mage/)
* [Makefile](https://vikunja.io/docs/makefile/)
* [Testing](https://vikunja.io/docs/testing/)
All docs can be found on [the vikunja home page](https://vikunja.io/docs/).
@ -51,12 +50,11 @@ All docs can be found on [the vikunja home page](https://vikunja.io/docs/).
* [x] Get all your tasks for an interval (day/month/period)
* [x] Labels for tasks
* [x] Assign users to tasks
* [x] Attachments on tasks
* [x] More sharing features
* [ ] Attachments on tasks
* [ ] More sharing features
* [x] Share with individual users
* [x] Share via a world-readable link with or without password, like Nextcloud
* [x] Disable registration, making an instance "invite-only"
* [ ] SSE to notify multiple clients of updates when something was changed
* [ ] Share via a world-readable link with or without password, like Nextcloud
* [ ] Read-only websocket to notify multiple clients of updates when something was changed
* [ ] "Smart Lists" - Create lists based on filters
* [ ] IMAP-Integration - Send an email to Vikunja to create a new task
* [ ] Webhooks - Trigger other events when an action is done (like completing a task)
@ -65,8 +63,9 @@ All docs can be found on [the vikunja home page](https://vikunja.io/docs/).
* [ ] Bulk-edit multiple tasks at once
* [ ] Team-efforts - Requiring a task to be marked as done by multiple members until it's done
* [ ] Global limits for namespaces/lists/tasks
* [ ] Disable registration, making an instance "invite-only"
See [our roadmap](https://my.vikunja.cloud/share/QFyzYEmEYfSyQfTOmIRSwLUpkFjboaBqQCnaPmWd/auth) (hosted on Vikunja!) for even more!
See [Featurecreep.md](Featurecreep.md) for even more! (mostly ideas, for now)
* [ ] [Mobile apps](https://code.vikunja.io/app) (seperate repo) *In Progress*
* [ ] [Webapp](https://code.vikunja.io/frontend) (seperate repo) *In Progress*

View File

@ -1,12 +0,0 @@
#!/usr/bin/env bash
curl -X POST http://localhost:3456/api/v1/register -H 'Content-Type: application/json' -d '{"username":"demo","password":"demo","email":"demo@vikunja.io"}'
BEARER=`curl -X POST -H 'Content-Type: application/json' -d '{"username": "demo", "password":"demo"}' localhost:3456/api/v1/login | jq -r '.token'`
echo "Bearer: $BEARER"
curl -X POST localhost:3456/api/v1/tokenTest -H "Authorization: Bearer $BEARER"
curl -X PUT localhost:3456/api/v1/namespaces/1/lists -H 'Content-Type: application/json' -H "Authorization: Bearer $BEARER" -d '{"title":"lorem"}'
curl -X PUT localhost:3456/api/v1/lists/1 -H 'Content-Type: application/json' -H "Authorization: Bearer $BEARER" -d '{"text":"lorem"}'
curl -X PUT -H "Authorization: Bearer $BEARER" localhost:3456/api/v1/tasks/1/attachments -F 'files=@/home/konrad/Pictures/Wallpaper/greg-rakozy-_Q4mepyyjMw-unsplash.jpg'

View File

@ -3,7 +3,7 @@ POST http://localhost:8080/api/v1/login
Content-Type: application/json
{
"username": "user3",
"username": "user6",
"password": "1234"
}

View File

@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}}
###
# Get one list
GET http://localhost:8080/api/v1/lists/3
GET http://localhost:8080/api/v1/lists/1172
Authorization: Bearer {{auth_token}}
###
@ -82,11 +82,11 @@ Authorization: Bearer {{auth_token}}
###
# Give a user access to that list
PUT http://localhost:8080/api/v1/lists/3/users
PUT http://localhost:8080/api/v1/lists/1172/users
Authorization: Bearer {{auth_token}}
Content-Type: application/json
{"userID":"user4", "right":1}
{"user_id":1, "right":1}
###
@ -169,9 +169,3 @@ Content-Type: application/json
}
###
# Get all users who have access to a list
GET http://localhost:8080/api/v1/lists/3/users
Authorization: Bearer {{auth_token}}
###

View File

@ -1,4 +1,5 @@
#!/bin/bash
ln -s /opt/vikunja/vikunja /usr/bin/vikunja
# Fix the config to contain proper values
NEW_SECRET=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)

View File

@ -1,15 +0,0 @@
Vikunja is a to-do list application to facilitate your life.
Copyright 2018-2020 Vikunja and contributors. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

View File

@ -11,32 +11,16 @@ service:
# Vikunja will also look in this path for a config file, so you could provide only this variable to point to a folder
# with a config file which will then be used.
rootpath: <rootpath>
# The max number of items which can be returned per page
maxitemsperpage: 50
# The number of items which gets returned per page
pagecount: 50
# If set to true, enables a /metrics endpoint for prometheus to collect metrics about the system
# You'll need to use redis for this in order to enable common metrics over multiple nodes
enablemetrics: false
# Enable the caldav endpoint, see the docs for more details
enablecaldav: true
# Set the motd message, available from the /info endpoint
motd: ""
# Enable sharing of lists via a link
enablelinksharing: true
# Whether to let new users registering themselves or not
enableregistration: true
# Whether to enable task attachments or not
enabletaskattachments: true
# The time zone all timestamps are in
timezone: GMT
# Whether task comments should be enabled or not
enabletaskcomments: true
# Whether totp is enabled. In most cases you want to leave that enabled.
enabletotp: true
# If not empty, enables logging of crashes and unhandled errors in sentry.
sentrydsn: ''
database:
# Database type to use. Supported types are mysql, postgres and sqlite.
# Database type to use. Supported types are mysql and sqlite.
type: "sqlite"
# Database user which is used to connect to the database.
user: "vikunja"
@ -47,24 +31,19 @@ database:
# Databse to use
database: "vikunja"
# When using sqlite, this is the path where to store the data
path: "./vikunja.db"
# Sets the max open connections to the database. Only used when using mysql and postgres.
Path: "./vikunja.db"
# Sets the max open connections to the database. Only used when using mysql.
maxopenconnections: 100
# Sets the maximum number of idle connections to the db.
maxidleconnections: 50
# The maximum lifetime of a single db connection in miliseconds.
maxconnectionlifetime: 10000
# Secure connection mode. Only used with postgres.
# (see https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters)
sslmode: disable
cache:
# If cache is enabled or not
enabled: false
# Cache type. Possible values are "keyvalue", "memory" or "redis".
# When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
# When choosing "redis" you will need to configure the redis connection seperately.
type: keyvalue
# Cache type. Possible values are memory or redis, you'll need to enable redis below when using redis
type: memory
# When using memory this defines the maximum size an element can take
maxelementsize: 1000
@ -78,17 +57,6 @@ redis:
# 0 means default database
db: 0
cors:
# Whether to enable or disable cors headers.
# Note: If you want to put the frontend and the api on seperate domains or ports, you will need to enable this.
# Otherwise the frontend won't be able to make requests to the api through the browser.
enable: true
# A list of origins which may access the api.
origins:
- "*"
# How long (in seconds) the results of a preflight request can be cached.
maxage: 0
mailer:
# Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
enabled: false
@ -108,139 +76,19 @@ mailer:
queuelength: 100
# The timeout in seconds after which the current open connection to the mailserver will be closed.
queuetimeout: 30
# By default, vikunja will try to connect with starttls, use this option to force it to use ssl.
forcessl: false
log:
# A folder where all the logfiles should go.
path: <rootpath>logs
# Whether to show any logging at all or none
enabled: true
# Where the error log should go. Possible values are stdout, stderr, file or off to disable error logging.
errors: "stdout"
# Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
standard: "stdout"
# Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
level: "INFO"
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
database: "off"
# The log level for database log messages. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
databaselevel: "WARNING"
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
http: "stdout"
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
echo: "off"
ratelimit:
# whether or not to enable the rate limit
enabled: false
# The kind on which rates are based. Can be either "user" for a rate limit per user or "ip" for an ip-based rate limit.
kind: user
# The time period in seconds for the limit
period: 60
# The max number of requests a user is allowed to do in the configured time period
limit: 100
# The store where the limit counter for each user is stored.
# Possible values are "keyvalue", "memory" or "redis".
# When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
store: keyvalue
files:
# The path where files are stored
basepath: ./files # relative to the binary
# The maximum size of a file, as a human-readable string.
# Warning: The max size is limited 2^64-1 bytes due to the underlying datatype
maxsize: 20MB
migration:
# These are the settings for the wunderlist migrator
wunderlist:
# Wheter to enable the wunderlist migrator or not
enable: false
# The client id, required for making requests to the wunderlist api
# You need to register your vikunja instance at https://developer.wunderlist.com/apps/new to get this
clientid:
# The client secret, also required for making requests to the wunderlist api
clientsecret:
# The url where clients are redirected after they authorized Vikunja to access their wunderlist stuff.
# This needs to match the url you entered when registering your Vikunja instance at wunderlist.
# This is usually the frontend url where the frontend then makes a request to /migration/wunderlist/migrate
# with the code obtained from the wunderlist api.
# Note that the vikunja frontend expects this to be /migrate/wunderlist
redirecturl:
todoist:
# Wheter to enable the todoist migrator or not
enable: false
# The client id, required for making requests to the wunderlist api
# You need to register your vikunja instance at https://developer.todoist.com/appconsole.html to get this
clientid:
# The client secret, also required for making requests to the todoist api
clientsecret:
# The url where clients are redirected after they authorized Vikunja to access their todoist items.
# This needs to match the url you entered when registering your Vikunja instance at todoist.
# This is usually the frontend url where the frontend then makes a request to /migration/todoist/migrate
# with the code obtained from the todoist api.
# Note that the vikunja frontend expects this to be /migrate/todoist
redirecturl:
avatar:
# When using gravatar, this is the duration in seconds until a cached gravatar user avatar expires
gravatarexpiration: 3600
backgrounds:
# Whether to enable backgrounds for lists at all.
enabled: true
providers:
upload:
# Whethere to enable uploaded list backgrounds
enabled: true
unsplash:
# Whether to enable setting backgrounds from unsplash as list backgrounds
enabled: false
# You need to create an application for your installation at https://unsplash.com/oauth/applications/new
# and set the access token below.
accesstoken:
# The unsplash application id is only used for pingback and required as per their api guidelines.
# You can find the Application ID in the dashboard for your API application. It should be a numeric ID.
# It will only show in the UI if your application has been approved for Enterprise usage, therefore if
# youre in Demo mode, you can also find the ID in the URL at the end: https://unsplash.com/oauth/applications/:application_id
applicationid:
# Legal urls
# Will be shown in the frontend if configured here
legal:
imprinturl:
privacyurl:
# Key Value Storage settings
# The Key Value Storage is used for different kinds of things like metrics and a few cache systems.
keyvalue:
# The type of the storage backend. Can be either "memory" or "redis". If "redis" is chosen it needs to be configured seperately.
type: "memory"
auth:
# Local authentication will let users log in and register (if enabled) through the db.
# This is the default auth mechanism and does not require any additional configuration.
local:
# Enable or disable local authentication
enabled: true
# OpenID configuration will allow users to authenticate through a third-party OpenID Connect compatible provider.<br/>
# The provider needs to support the `openid`, `profile` and `email` scopes.<br/>
# **Note:** The frontend expects to be redirected after authentication by the third party
# to <frontend-url>/auth/openid/<auth key>. Please make sure to configure the redirect url with your third party
# auth service accordingy if you're using the default vikunja frontend.
# Take a look at the [default config file](https://kolaente.dev/vikunja/api/src/branch/master/config.yml.sample) for more information about how to configure openid authentication.
openid:
# Enable or disable OpenID Connect authentication
enabled: false
# The url to redirect clients to. Defaults to the configured frontend url. If you're using Vikunja with the official
# frontend, you don't need to change this value.
redirecturl: <frontend url>
# A list of enabled providers
providers:
# The name of the provider as it will appear in the frontend.
- name:
# The auth url to send users to if they want to authenticate using OpenID Connect.
authurl:
# The client ID used to authenticate Vikunja at the OpenID Connect provider.
clientid:
# The client secret used to authenticate Vikunja at the OpenID Connect provider.
clientsecret:
echo: "off"

View File

@ -1,18 +0,0 @@
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
-
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux
-
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
platform:
architecture: arm
os: linux

View File

@ -0,0 +1,2 @@
#!/bin/sh
/bin/true

View File

@ -0,0 +1,2 @@
#!/bin/sh
/bin/true

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/app/vikunja/vikunja

View File

@ -16,14 +16,9 @@ params:
description: The to-do app to organize your life
author: The Vikunja Authors
website: https://vikunja.io
plausibleEnabled: true
plausibleDomain: vikunja.io
plausibleURL: https://analytics.kolaente.de
markup:
goldmark:
renderer:
unsafe: true
fanthomEnabled: false
fathomUrl: fathom.kolaente.de
fathomSiteID: RYKSD
menu:
page:
@ -42,6 +37,3 @@ menu:
- name: Code
url: https://code.vikunja.io/
weight: 50
- name: Community
url: https://community.vikunja.io/
weight: 60

View File

@ -17,9 +17,9 @@ To learn more about the what, why and how, take a look at [the features page](ht
## Start
A good starting point if you want to install and host Vikunja on your server are [the install documentation]({{< ref "./setup/install.md">}})
and [available configuration options]({{< ref "./setup/config.md">}}).
A good starting point if you want to install and host Vikunja on your server are [the install documentation](installing)
and [available configuration options](config-options).
## Developing
If you want to start contributing to Vikunja, take a look at [the development docs]({{< ref "./development/development.md">}}).
If you want to start contributing to Vikunja, take a look at [the development docs](development).

View File

@ -17,9 +17,7 @@ If you don't intend to add new dependencies, go `1.9` and above should be fine.
To contribute to Vikunja, fork the project and work on the master branch.
A lot of developing tasks are automated using a Magefile, so make sure to [take a look at it]({{< ref "mage.md">}}).
{{< table_of_contents >}}
A lot of developing tasks are automated using a Makefile, so make sure to [take a look at it]({{< ref "make.md">}}).
## Libraries
@ -34,6 +32,9 @@ See [testing]({{< ref "test.md">}}).
If you're able to use go modules, you can clone the project wherever you want to and work from there.
However, when building or running tests, please supply the `-mod=vendor` flag to go so it builds using the
dependencies from the `vendor/` folder.
#### Development-setup without go modules
Some internal packages are referenced using their respective package URL. This can become problematic.
@ -52,16 +53,9 @@ git remote add origin git@git.kolaente.de:<USERNAME>/api.git
git fetch --all --prune
{{< /highlight >}}
This should provide a working development environment for Vikunja. Take a look at the Magefile to get an overview about
the available tasks. The most common tasks should be `mage test:unit` which will start our test environment and `mage build:build`
This should provide a working development environment for Vikunja. Take a look at the Makefile to get an overview about
the available tasks. The most common tasks should be `make test` which will start our test environment and `make build`
which will build a vikunja binary into the working directory. Writing test cases is not mandatory to contribute, but it
is highly encouraged and helps developers sleep at night.
Thats it! You are ready to hack on Vikunja. Test changes, push them to the repository, and open a pull request.
## Static assets
Each Vikunja release contains all static assets directly compiled into the binary.
To prevent this during development, use the `dev` tag when developing.
See the [mage docs](mage.md#statically-compile-all-templates-into-the-binary) about how to compile with static assets for a release.

View File

@ -1,192 +0,0 @@
---
date: "2019-02-12:00:00+02:00"
title: "Magefile"
draft: false
type: "doc"
menu:
sidebar:
parent: "development"
---
# Mage
Vikunja uses [Mage](https://magefile.org/) to script common development tasks and even releasing.
Mage is a pure go solution which allows for greater flexibility and things like better paralelization.
This document explains what taks are available and what they do.
{{< table_of_contents >}}
## Installation
To use mage, you'll need to install the mage cli.
To install it, run the following command:
```
go install github.com/magefile/mage
```
## Categories
There are multiple categories of subcommands in the magefile:
* `build`: Contains commands to build a single binary
* `check`: Contains commands to statically check the source code
* `release`: Contains commands to release Vikunja with everything that's required
* `test`: Contains commands to run all kinds of tests
* `dev`: Contains commands to run development tasks
* `misc`: Commands which do not belong in either of the other categories
## CI
These tasks are automatically run in our CI every time someone pushes to master or you update a pull request:
* `mage check:lint`
* `mage check:fmt`
* `mage check:ineffassign`
* `mage check:misspell`
* `mage check:goconst`
* `mage build:generate`
* `mage build:build`
## Build
### Build Vikunja
{{< highlight bash >}}
mage build:build
{{< /highlight >}}
Builds a `vikunja`-binary in the root directory of the repo for the platform it is run on.
### Statically compile all templates into the binary
{{< highlight bash >}}
mage build:generate
{{< /highlight >}}
This generates static code with all templates, meaning no template need to be referenced at runtime.
### clean
{{< highlight bash >}}
mage build:clean
{{< /highlight >}}
Cleans all build, executable and bindata files
## Check
All check sub-commands exit with a status code of 1 if the check fails.
Various code-checks are available:
* `mage check:all`: Runs fmt-check, lint, got-swag, misspell-check, ineffasign-check, gocyclo-check, static-check, gosec-check, goconst-check all in parallel
* `mage check:fmt`: Checks if the code is properly formatted with go fmt
* `mage check:go-sec`: Checks the source code for potential security issues by scanning the Go AST using the [gosec tool](https://github.com/securego/gosec)
* `mage check:goconst`: Checks for repeated strings that could be replaced by a constant using [goconst](https://github.com/jgautheron/goconst/)
* `mage check:gocyclo`: Checks for the cyclomatic complexity of the source code using [gocyclo](https://github.com/fzipp/gocyclo)
* `mage check:got-swag`: Checks if the swagger docs need to be re-generated from the code annotations
* `mage check:ineffassign`: Checks the source code for ineffectual assigns using [ineffassign](https://github.com/gordonklaus/ineffassign)
* `mage check:lint`: Runs golint on all packages
* `mage check:misspell`: Checks the source code for misspellings
* `mage check:static`: Statically analyzes the source code about a range of different problems using [staticcheck](https://staticcheck.io/docs/)
## Release
### Build Releases
{{< highlight bash >}}
mage release
{{< /highlight >}}
Builds binaries for all platforms and zips them with a copy of the `templates/` folder.
All built zip files are stored into `dist/zips/`. Binaries are stored in `dist/binaries/`,
binaries bundled with `templates` are stored in `dist/releases/`.
All cross-platform binaries built using this series of commands are built with the help of
[xgo](https://github.com/techknowlogick/xgo). The mage command will automatically install the
binary to be able to use it.
`mage release:release` is a shortcut to execute `mage release:dirs release:windows release:linux release:darwin release:copy release:check release:os-package release:zip`.
* `mage release:dirs` creates all directories needed
* `mage release:windows`/`release:linux`/`release:darwin` execute xgo to build for their respective platforms
* `mage release:copy` bundles binaries with a copy of the `LICENSE` and sample config files to then be zipped
* `mage release:check` creates sha256 checksums for each binary which will be included in the zip file
* `mage release:os-package` bundles a binary with the `sha256` checksum file, a sample `config.yml` and a copy of the license in a folder for each architecture
* `mage release:compress` compresses all build binaries with `upx` to save space
* `mage release:zip` paclages a zip file for the files created by `release:os-package`
### Build os packages
{{< highlight bash >}}
mage release:packages
{{< /highlight >}}
Will build `.deb`, `.rpm` and `.apk` packages to `dist/os-packages`.
### Make a debian repo
{{< highlight bash >}}
mage release:reprepro
{{< /highlight >}}
Takes an already built debian package and creates a debian repo structure around it.
Used to be run inside a [docker container](https://git.kolaente.de/konrad/reprepro-docker) in the CI process when releasing.
## Test
### unit
{{< highlight bash >}}
mage test:unit
{{< /highlight >}}
Runs all tests except integration tests.
### coverage
{{< highlight bash >}}
mage test:coverage
{{< /highlight >}}
Runs all tests except integration tests and generates a `coverage.html` file to inspect the code coverage.
### integration
{{< highlight bash >}}
mage test:integration
{{< /highlight >}}
Runs all integration tests.
## Dev
### Create a new migration
{{< highlight bash >}}
mage dev:create-migration
{{< /highlight >}}
Creates a new migration with the current date.
Will ask for the name of the struct you want to create a migration for.
## Misc
### Format the code
{{< highlight bash >}}
mage fmt
{{< /highlight >}}
Formats all source code using `go fmt`.
### Generate swagger definitions from code comments
{{< highlight bash >}}
mage do-the-swag
{{< /highlight >}}
Generates swagger definitions from the comment annotations in the code.

View File

@ -0,0 +1,132 @@
---
date: "2019-02-12:00:00+02:00"
title: "Makefile"
draft: false
type: "doc"
menu:
sidebar:
parent: "development"
---
# Makefile
We scripted a lot of tasks used mostly for developing into the makefile. This documents explains what
taks are available and what they do.
## CI
These tasks are automatically run in our CI every time someone pushes to master or you update a pull request:
* `make lint`
* `make fmt-check`
* `make ineffassign-check`
* `make misspell-check`
* `make goconst-check`
* `make build`
### clean
{{< highlight bash >}}
make clean
{{< /highlight >}}
Clears all builds and binaries.
### test
{{< highlight bash >}}
make test
{{< /highlight >}}
Runs all tests in Vikunja.
### Format the code
{{< highlight bash >}}
make fmt
{{< /highlight >}}
Formats all source code using `go fmt`.
#### Check formatting
{{< highlight bash >}}
make fmt-check
{{< /highlight >}}
Checks if the code needs to be formatted. Fails if it does.
### Build Vikunja
{{< highlight bash >}}
make build
{{< /highlight >}}
Builds a `vikunja`-binary in the root directory of the repo for the platform it is run on.
### Build Releases
{{< highlight bash >}}
make build
{{< /highlight >}}
Builds binaries for all platforms and zips them with a copy of the `templates/` folder.
All built zip files are stored into `dist/zips/`. Binaries are stored in `dist/binaries/`,
binaries bundled with `templates` are stored in `dist/releases/`.
All cross-platform binaries built using this series of commands are built with the help of
[xgo](https://github.com/karalabe/xgo). The make command will automatically install the
binary to be able to use it.
`make release` is actually just a shortcut to execute `make release-dirs release-windows release-linux release-darwin release-copy release-check release-os-package release-zip`.
* `release-dirs` creates all directories needed
* `release-windows`/`release-linux`/`release-darwin` execute xgo to build for their respective platforms
* `release-copy` bundles binaries with a copy of `templates/` to then be zipped
* `release-check` creates sha256 checksums for each binary which will be included in the zip file
* `release-os-package` bundles a binary with a copy of the `templates/` folder, the `sha256` checksum file, a sample `config.yml` and a copy of the license in a folder for each architecture
* `release-zip` makes a zip file for the files created by `release-os-package`
### Build debian packages
{{< highlight bash >}}
make build-deb
{{< /highlight >}}
Will build a `.deb` package into the current folder. You need to have [fpm](https://fpm.readthedocs.io/en/latest/intro.html) installed to be able to do this.
#### Make a debian repo
{{< highlight bash >}}
make reprepro
{{< /highlight >}}
Takes an already built debian package and creates a debian repo structure around it.
Used to be run inside a [docker container](https://git.kolaente.de/konrad/reprepro-docker) in the CI process when releasing.
### Generate swagger definitions from code comments
{{< highlight bash >}}
make do-the-swag
{{< /highlight >}}
Generates swagger definitions from the comments in the code.
#### Check if swagger generation is needed
{{< highlight bash >}}
make got-swag
{{< /highlight >}}
This command is currently more an experiment, use it with caution.
It may bring up wrong results.
### Code-Checks
* `misspell-check`: Checks for commonly misspelled words
* `ineffassign-check`: Checks for ineffectual assignments in the code using [ineffassign](https://github.com/gordonklaus/ineffassign).
* `gocyclo-check`: Calculates cyclomatic complexities of functions using [gocyclo](https://github.com/fzipp/gocyclo).
* `static-check`: Analyzes the code for bugs, improvements and more using [staticcheck](https://staticcheck.io/docs/).
* `gosec-check`: Inspects source code for security problems by scanning the Go AST using the [gosec tool](https://github.com/securego/gosec).
* `goconst-check`: Finds repeated strings that could be replaced by a constant using [goconst](https://github.com/jgautheron/goconst/).

View File

@ -1,98 +0,0 @@
---
date: "2020-01-19:16:00+02:00"
title: "Migrations"
draft: false
type: "doc"
menu:
sidebar:
parent: "development"
---
# Writing a migrator for Vikunja
It is possible to migrate data from other to-do services to Vikunja.
To make this easier, we have put together a few helpers which are documented on this page.
In general, each migrator implements a migrator interface which is then called from a client.
The interface makes it possible to use helper methods which handle http an focus only on the implementation of the migrator itself.
{{< table_of_contents >}}
## Structure
All migrator implementations live in their own package in `pkg/modules/migration/<name-of-the-service>`.
When creating a new migrator, you should place all related code inside that module.
## Migrator interface
The migrator interface is defined as follows:
```go
// Migrator is the basic migrator interface which is shared among all migrators
type Migrator interface {
// Migrate is the interface used to migrate a user's tasks from another platform to vikunja.
// The user object is the user who's tasks will be migrated.
Migrate(user *models.User) error
// AuthURL returns a url for clients to authenticate against.
// The use case for this are Oauth flows, where the server token should remain hidden and not
// known to the frontend.
AuthURL() string
// Name holds the name of the migration.
// This is used to show the name to users and to keep track of users who already migrated.
Name() string
}
```
## Defining http routes
Once your migrator implements the migration interface, it becomes possible to use the helper http handlers.
Their usage is very similar to the [general web handler](https://kolaente.dev/vikunja/web#user-content-defining-routes-using-the-standard-web-handler):
The `RegisterRoutes(m)` method registers all routes with the scheme `/[MigratorName]/(auth|migrate|status)` for the
authUrl, Status and Migrate methods.
```go
// This is an example for the Wunderlist migrator
if config.MigrationWunderlistEnable.GetBool() {
wunderlistMigrationHandler := &migrationHandler.MigrationWeb{
MigrationStruct: func() migration.Migrator {
return &wunderlist.Migration{}
},
}
wunderlistMigrationHandler.RegisterRoutes(m)
}
```
You should also document the routes with [swagger annotations]({{< ref "../practical-instructions/swagger-docs.md" >}}).
## Insertion helper method
There is a method available in the `migration` package which takes a fully nested Vikunja structure and creates it with all relations.
This means you start by adding a namespace, then add lists inside of that namespace, then tasks in the lists and so on.
The root structure must be present as `[]*models.NamespaceWithLists`.
Then call the method like so:
```go
fullVikunjaHierachie, err := convertWunderlistToVikunja(wContent)
if err != nil {
return
}
err = migration.InsertFromStructure(fullVikunjaHierachie, user)
```
## Configuration
You should add at least an option to enable or disable the migration.
Chances are, you'll need some more options for things like client ID and secret
(if the other service uses oAuth as an authentication flow).
The easiest way to implement an on/off switch is to check whether your migration service is enabled or not when
registering the routes, and then simply don't registering the routes in the case it is disabled.
### Making the migrator public in `/info`
You should make your migrator available in the `/info` endpoint so that frontends can display options to enable them or not.
To do this, add an entry to `pkg/routes/api/v1/info.go`.

View File

@ -16,8 +16,6 @@ Additionally, they can also be run directly by using the `migrate` command.
We use [xormigrate](https://github.com/techknowlogick/xormigrate) to handle migrations,
which is based on gormigrate.
{{< table_of_contents >}}
## Add a new migration
All migrations are stored in `pkg/migrations` and files should have the same name as their id.

View File

@ -16,40 +16,26 @@ In general, this api repo has the following structure:
* `docs`
* `pkg`
* `caldav`
* `cmd`
* `config`
* `db`
* `fixtures`
* `files`
* `integration`
* `log`
* `mail`
* `metrics`
* `migration`
* `models`
* `modules`
* `migration`
* `handler`
* `wunderlist`
* `red`
* `routes`
* `api/v1`
* `static`
* `swagger`
* `user`
* `utils`
* `version`
* `REST-Tests`
* `templates`
* `vendor`
This document will explain what these mean and what you can find where.
{{< table_of_contents >}}
## Root level
The root directory is where [the config file]({{< ref "../setup/config.md">}}), [Magefile]({{< ref "mage.md">}}), license, drone config,
The root directory is where [the config file]({{< ref "../setup/config.md">}}), [Makefile]({{< ref "make.md">}}), license, drone config,
application entry point (`main.go`) and so on are located.
## docker
@ -80,21 +66,6 @@ how to interpret which env variables for config etc.
If you want to add a new config parameter, you should add default value in this package.
### db
This package contains the db connection handling and db fixtures for testing.
Each other package gets its db connection object from this package.
### files
This package is responsible for all file-related things.
This means it handles saving and retrieving files from the db and the underlying file system.
### integration
All integration tests live here.
See [integration tests]({{< ref "test.md" >}}#integration-tests) for more details.
### log
Similar to `config`, this will set up the logging, based on differen logging backends.
@ -114,7 +85,7 @@ To learn how it works and how to add new metrics, take a look at [how metrics wo
This package handles all migrations.
All migrations are stored and executed here.
To learn more, take a look at the [migrations docs]({{< ref "../development/db-migrations.md">}}).
To learn more, take a look at the [migrations docs]({{< ref "../development/migrations.md">}}).
### models
@ -126,12 +97,6 @@ Because this package is pretty huge, there are several documents and how-to's ab
* [Adding a feature]({{< ref "../practical-instructions/feature.md">}})
* [Making calls to the database]({{< ref "../practical-instructions/database.md">}})
### modules
#### migration
See [writing a migrator]({{< ref "migration.md" >}}).
### red (redis)
This package initializes a connection to a redis server.
@ -152,19 +117,11 @@ To add a new route, see [adding a new route]({{< ref "../practical-instructions/
This is where all http-handler functions for the api are stored.
Every handler function which does not use the standard web handler should live here.
### static
All static files generated by `mage generate` live here.
### swagger
This is where the [generated]({{< ref "mage.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live.
This is where the [generated]({{< ref "make.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live.
You usually don't need to touch this package.
### user
All user-related things like registration etc. live in this package.
### utils
A small package, containing some helper functions:
@ -174,12 +131,6 @@ A small package, containing some helper functions:
See their function definitions for instructions on how to use them.
### version
The single purpouse of this package is to hold the current vikunja version which gets overridden through build flags
each time `mage release` or `mage build` is run.
It is a seperate package to avoid import cycles with other packages.
## REST-Tests
Holds all kinds of test files to directly test the api from inside of [jetbrains ide's](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html).

View File

@ -10,63 +10,35 @@ menu:
# Testing
You can run unit tests with [our `Magefile`]({{< ref "mage.md">}}) with
You can run unit tests with [our `Makefile`]({{< ref "make.md">}}) with
{{< highlight bash >}}
mage test:unit
make test
{{< /highlight >}}
{{< table_of_contents >}}
## Running tests with config
### Running tests with config
You can run tests with all available config variables if you want, enabeling you to run tests for a lot of scenarios.
To use the normal config set the enviroment variable `VIKUNJA_TESTS_USE_CONFIG=1`.
## Show sql queries
### Show sql queries
When `UNIT_TESTS_VERBOSE=1` is set, all sql queries will be shown when tests are run.
## Fixtures
### Fixtures
All tests are run against a set of db fixtures.
These fixtures are defined in `pkg/models/fixtures` in YAML-Files which represent the database structure.
When you add a new test case which requires new database entries to test against, update these files.
## Integration tests
# Integration tests
All integration tests live in `pkg/integrations`.
You can run them by executing `mage test:integration`.
You can run them by executing `make integration-test`.
The integration tests use the same config and fixtures as the unit tests and therefor have the same options available,
see at the beginning of this document.
To run integration tests, use `mage test:integration`.
## Initializing db fixtures when writing tests
All db fixtures for all tests live in the `pkg/db/fixtures/` folder as yaml files.
Each file has the same name as the table the fixtures are for.
You should put new fixtures in this folder.
When initializing db fixtures, you are responsible for defining which tables your package needs in your test init function.
Usually, this is done as follows (this code snippet is taken from the `user` package):
```go
err = db.InitTestFixtures("users")
if err != nil {
log.Fatal(err)
}
```
In your actual tests, you then load the fixtures into the in-memory db like so:
```go
db.LoadAndAssertFixtures(t)
```
This will load all fixtures you defined in your test init method.
You should always use this method to load fixtures, the only exception is when your package tests require extra test
fixtures other than db fixtures (like files).
To run integration tests, use `make integration-test`.

View File

@ -18,9 +18,7 @@ This is used whenever you make a call to the database to get or update data.
This xorm instance is set up and initialized every time vikunja is started.
{{< table_of_contents >}}
## Adding new database tables
### Adding new database tables
To add a new table to the database, add a an instance of your struct to the `tables` variable in the
init function in `pkg/models/models.go`. Xorm will sync them automatically.
@ -29,7 +27,7 @@ You also need to add a pointer to the `tablesWithPointer` slice to enable cachin
To learn more about how to configure your struct to create "good" tables, refer to [the xorm documentaion](http://xorm.io/docs/).
## Adding data to test fixtures
### Adding data to test fixtures
Adding data for test fixtures is done in via `yaml` files insinde of `pkg/models/fixtures`.

View File

@ -27,7 +27,7 @@ You can feed this function directly into xorm's `Limit`-Function like so:
{{< highlight golang >}}
lists := []List{}
err := x.Limit(getLimitFromPageIndex(pageIndex, itemsPerPage)).Find(&lists)
err := x.Limit(getLimitFromPageIndex(pageIndex)).Find(&lists)
{{< /highlight >}}
// TODO: Add a full example from start to finish, like a tutorial on how to create a new endpoint?

View File

@ -12,8 +12,6 @@ menu:
This document explains how to use the mailer to send emails and what to do to create a new kind of email to be sent.
{{< table_of_contents >}}
## Sending emails
**Note:** You should use mail templates whenever possible (see below).
@ -32,7 +30,7 @@ type Opts struct {
}
{{< /highlight >}}
### Sending emails based on a template
## Sending emails based on a template
For each mail with a template, there are two email templates: One for plaintext emails, one for html emails.
@ -43,7 +41,7 @@ To send a mail based on a template, use the function `mail.SendMailWithTemplate(
`to` and `subject` are pretty much self-explanatory, `tpl` is the name of the template, without `.html.tmpl` or `.plain.tmpl`.
`data` is a map you can pass additional data to your template.
### Sending a mail with a template
#### Sending a mail with a template
A basic html email template would look like this:

View File

@ -15,8 +15,6 @@ Metrics work by exposing a `/metrics` endpoint which can then be accessed by pro
To keep the load on the database minimal, metrics are stored and updated in redis.
The `metrics` package provides several functions to create and update metrics.
{{< table_of_contents >}}
## New metrics
First, define a `const` with the metric key in redis. This is done in `pkg/metrics/metrics.go`.
@ -43,6 +41,6 @@ Because metrics are stored in redis, you are responsible to increase or decrease
To do this, use `metrics.UpdateCount(value, key)` where `value` is the amount you want to cange it (you can pass
negative values to decrease it) and `key` it the redis key used to define the metric.
## Using it
# Using it
A Prometheus config with a Grafana template is available at [our git repo](https://git.kolaente.de/vikunja/monitoring).

View File

@ -12,7 +12,7 @@ menu:
The api documentation is generated using [swaggo](https://github.com/swaggo/swag) from comments.
## Documenting structs
### Documenting structs
You should always comment every field which will be exposed as a json in the api.
These comments will show up in the documentation, it'll make it easier for developers using the api.

View File

@ -13,8 +13,6 @@ menu:
Vikunja does not store any data outside of the database.
So, all you need to backup are the contents of that database and maybe the config file.
{{< table_of_contents >}}
## MySQL
To create a backup from mysql use the `mysqldump` command:
@ -31,24 +29,6 @@ To restore it, simply pipe it back into the `mysql` command:
mysql -u <user> -p -h <db-host> <database> < vkunja-backup.sql
{{< /highlight >}}
## PostgreSQL
To create a backup from PostgreSQL use the `pg_dump` command:
{{< highlight bash >}}
pg_dump -U <user> -h <db-host> <database> > vikunja-backup.sql
{{< /highlight >}}
You might be prompted for the password of the database user.
To restore it, simply pipe it back into the `psql` command:
{{< highlight bash >}}
psql -U <user> -h <db-host> <database> < vikunja-backup.sql
{{< /highlight >}}
For more information, please visit the [relevant PostgreSQL documentation](https://www.postgresql.org/docs/12/backup-dump.html).
## SQLite
To backup sqllite databases, it is enough to copy the database elsewhere.

View File

@ -14,16 +14,12 @@ Vikunja being a go application, has no other dependencies than go itself.
All libraries are bundeled inside the repo in the `vendor/` folder, so all it boils down to are these steps:
1. Make sure [Go](https://golang.org/doc/install) is properly installed on your system. You'll need at least Go `1.9`.
2. Make sure [Mage](https://magefile) is properly installed on your system.
2. Make sure [Make](https://www.gnu.org/software/make/) is properly installed on your system.
3. Clone the repo with `git clone https://code.vikunja.io/api`
3. Run `mage build:build` in the source of this repo. This will build a binary in the root of the repo which will be able to run on your system.
*Note:* Static ressources such as email templates are built into the binary.
For these to work, you may need to run `mage build:generate` before building the vikunja binary.
When builing entirely with `mage`, you dont need to do this, `mage build:generate` will be run automatically when running `mage build:build`.
3. Run `make build` in the source of this repo. This will build a binary in the root of the repo which will be able to run on your system.
# Build for different architectures
To build for other platforms and architectures than the one you're currently on, simply run `mage release:release` or `mage release:{linux|windows|darwin}`.
To build for other platforms and architectures than the one you're currently on, simply run `make release` or `make release-{linux|windows|darwin}`.
More options are available, please refer to the [magefile docs]({{< ref "../development/mage.md">}}) for more details.
More options are available, please refer to the [makefile docs]({{< ref "../development/make.md">}}) for more details.

View File

@ -27,14 +27,7 @@ first:
child: true
{{< /highlight >}}
# Formats
Vikunja supports using `toml`, `yaml`, `hcl`, `ini`, `json`, envfile, env variables and Java Properties files.
We reccomend yaml or toml, but you're free to use whatever you want.
Vikunja provides a default [`config.yml`](https://kolaente.dev/vikunja/api/src/branch/master/config.yml.sample) file which you can use as a starting point.
# Config file locations
## Config file locations
Vikunja will search on various places for a config file:
@ -45,545 +38,101 @@ Vikunja will search on various places for a config file:
# Default configuration with explanations
The following explains all possible config variables and their defaults.
You can find a full example configuration file in [here](https://code.vikunja.io/api/src/branch/master/config.yml.sample).
If you don't provide a value in your config file, their default will be used.
## Nesting
Most config variables are nested under some "higher-level" key.
For example, the `interface` config variable is a child of the `service` key.
The docs below aim to reflect that leveling, but please also have a lookt at [the default config](https://code.vikunja.io/api/src/branch/master/config.yml.sample) file
to better grasp how the nesting looks like.
<!-- Generated config will be injected here -->
---
## service
### JWTSecret
This token is used to verify issued JWT tokens.
Default is a random token which will be generated at each startup of vikunja.
(This means all already issued tokens will be invalid once you restart vikunja)
Default: `<jwt-secret>`
### interface
The interface on which to run the webserver
Default: `:3456`
### frontendurl
The URL of the frontend, used to send password reset emails.
Default: `<empty>`
### rootpath
The base path on the file system where the binary and assets are.
Vikunja will also look in this path for a config file, so you could provide only this variable to point to a folder
with a config file which will then be used.
Default: `<rootpath>`
### maxitemsperpage
The max number of items which can be returned per page
Default: `50`
### enablemetrics
If set to true, enables a /metrics endpoint for prometheus to collect metrics about the system
You'll need to use redis for this in order to enable common metrics over multiple nodes
Default: `false`
### enablecaldav
Enable the caldav endpoint, see the docs for more details
Default: `true`
### motd
Set the motd message, available from the /info endpoint
Default: `<empty>`
### enablelinksharing
Enable sharing of lists via a link
Default: `true`
### enableregistration
Whether to let new users registering themselves or not
Default: `true`
### enabletaskattachments
Whether to enable task attachments or not
Default: `true`
### timezone
The time zone all timestamps are in
Default: `GMT`
### enabletaskcomments
Whether task comments should be enabled or not
Default: `true`
### enabletotp
Whether totp is enabled. In most cases you want to leave that enabled.
Default: `true`
### sentrydsn
If not empty, enables logging of crashes and unhandled errors in sentry.
Default: `<empty>`
---
## database
### type
Database type to use. Supported types are mysql, postgres and sqlite.
Default: `sqlite`
### user
Database user which is used to connect to the database.
Default: `vikunja`
### password
Databse password
Default: `<empty>`
### host
Databse host
Default: `localhost`
### database
Databse to use
Default: `vikunja`
### path
When using sqlite, this is the path where to store the data
Default: `./vikunja.db`
### maxopenconnections
Sets the max open connections to the database. Only used when using mysql and postgres.
Default: `100`
### maxidleconnections
Sets the maximum number of idle connections to the db.
Default: `50`
### maxconnectionlifetime
The maximum lifetime of a single db connection in miliseconds.
Default: `10000`
### sslmode
Secure connection mode. Only used with postgres.
(see https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters)
Default: `disable`
---
## cache
### enabled
If cache is enabled or not
Default: `false`
### type
Cache type. Possible values are "keyvalue", "memory" or "redis".
When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
When choosing "redis" you will need to configure the redis connection seperately.
Default: `keyvalue`
### maxelementsize
When using memory this defines the maximum size an element can take
Default: `1000`
---
## redis
### enabled
Whether to enable redis or not
Default: `false`
### host
The host of the redis server including its port.
Default: `localhost:6379`
### password
The password used to authenicate against the redis server
Default: `<empty>`
### db
0 means default database
Default: `0`
---
## cors
### enable
Whether to enable or disable cors headers.
Note: If you want to put the frontend and the api on seperate domains or ports, you will need to enable this.
Otherwise the frontend won't be able to make requests to the api through the browser.
Default: `true`
### origins
A list of origins which may access the api.
Default: `<empty>`
### maxage
How long (in seconds) the results of a preflight request can be cached.
Default: `0`
---
## mailer
### enabled
Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
Default: `false`
### host
SMTP Host
Default: `<empty>`
### port
SMTP Host port
Default: `587`
### username
SMTP username
Default: `user`
### password
SMTP password
Default: `<empty>`
### skiptlsverify
Wether to skip verification of the tls certificate on the server
Default: `false`
### fromemail
The default from address when sending emails
Default: `mail@vikunja`
### queuelength
The length of the mail queue.
Default: `100`
### queuetimeout
The timeout in seconds after which the current open connection to the mailserver will be closed.
Default: `30`
### forcessl
By default, vikunja will try to connect with starttls, use this option to force it to use ssl.
Default: `false`
---
## log
### path
A folder where all the logfiles should go.
Default: `<rootpath>logs`
### enabled
Whether to show any logging at all or none
Default: `true`
### standard
Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
Default: `stdout`
### level
Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
Default: `INFO`
### database
Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
Default: `off`
### databaselevel
The log level for database log messages. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
Default: `WARNING`
### http
Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
Default: `stdout`
### echo
Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
Default: `off`
---
## ratelimit
### enabled
whether or not to enable the rate limit
Default: `false`
### kind
The kind on which rates are based. Can be either "user" for a rate limit per user or "ip" for an ip-based rate limit.
Default: `user`
### period
The time period in seconds for the limit
Default: `60`
### limit
The max number of requests a user is allowed to do in the configured time period
Default: `100`
### store
The store where the limit counter for each user is stored.
Possible values are "keyvalue", "memory" or "redis".
When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
Default: `keyvalue`
---
## files
### basepath
The path where files are stored
Default: `./files`
### maxsize
The maximum size of a file, as a human-readable string.
Warning: The max size is limited 2^64-1 bytes due to the underlying datatype
Default: `20MB`
---
## migration
### wunderlist
These are the settings for the wunderlist migrator
Default: `<empty>`
### todoist
Default: `<empty>`
---
## avatar
### gravatarexpiration
When using gravatar, this is the duration in seconds until a cached gravatar user avatar expires
Default: `3600`
---
## backgrounds
### enabled
Whether to enable backgrounds for lists at all.
Default: `true`
### providers
Default: `<empty>`
---
## legal
Legal urls
Will be shown in the frontend if configured here
### imprinturl
Default: `<empty>`
### privacyurl
Default: `<empty>`
---
## keyvalue
Key Value Storage settings
The Key Value Storage is used for different kinds of things like metrics and a few cache systems.
### type
The type of the storage backend. Can be either "memory" or "redis". If "redis" is chosen it needs to be configured seperately.
Default: `memory`
---
## auth
### local
Local authentication will let users log in and register (if enabled) through the db.
This is the default auth mechanism and does not require any additional configuration.
Default: `<empty>`
### openid
OpenID configuration will allow users to authenticate through a third-party OpenID Connect compatible provider.<br/>
The provider needs to support the `openid`, `profile` and `email` scopes.<br/>
**Note:** The frontend expects to be redirected after authentication by the third party
to <frontend-url>/auth/openid/<auth key>. Please make sure to configure the redirect url with your third party
auth service accordingy if you're using the default vikunja frontend.
Take a look at the [default config file](https://kolaente.dev/vikunja/api/src/branch/master/config.yml.sample) for more information about how to configure openid authentication.
Default: `<empty>`
This is the same as the `config.yml.sample` file you'll find in the root of vikunja.
{{< highlight yaml >}}
service:
# This token is used to verify issued JWT tokens.
# Default is a random token which will be generated at each startup of vikunja.
# (This means all already issued tokens will be invalid once you restart vikunja)
JWTSecret: "cei6gaezoosah2bao3ieZohkae5aicah"
# The interface on which to run the webserver
interface: ":3456"
# The URL of the frontend, used to send password reset emails.
frontendurl: ""
# The base path on the file system where the binary and assets are.
# Vikunja will also look in this path for a config file, so you could provide only this variable to point to a folder
# with a config file which will then be used.
rootpath: <the path of the executable>
# The number of items which gets returned per page
pagecount: 50
# If set to true, enables a /metrics endpoint for prometheus to collect metrics about the system
# You'll need to use redis for this in order to enable common metrics over multiple nodes
enablemetrics: false
# Enable the caldav endpoint, see the docs for more details
enablecaldav: true
database:
# Database type to use. Supported types are mysql and sqlite.
type: "sqlite"
# Database user which is used to connect to the database.
user: "vikunja"
# Databse password
password: ""
# Databse host
host: "localhost"
# Databse to use
database: "vikunja"
# When using sqlite, this is the path where to store the data
Path: "./vikunja.db"
# Sets the max open connections to the database. Only used when using mysql.
maxopenconnections: 100
# Sets the maximum number of idle connections to the db.
maxidleconnections: 50
# The maximum lifetime of a single db connection in miliseconds.
maxconnectionlifetime: 10000
cache:
# If cache is enabled or not
enabled: false
# Cache type. Possible values are memory or redis, you'll need to enable redis below when using redis
type: memory
# When using memory this defines the maximum size an element can take
maxelementsize: 1000
redis:
# Whether to enable redis or not
enabled: false
# The host of the redis server including its port.
host: 'localhost:6379'
# The password used to authenicate against the redis server
password: ''
# 0 means default database
db: 0
mailer:
# Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
enabled: false
# SMTP Host
host: ""
# SMTP Host port
port: 587
# SMTP username
username: "user"
# SMTP password
password: ""
# Wether to skip verification of the tls certificate on the server
skiptlsverify: false
# The default from address when sending emails
fromemail: "mail@vikunja"
# The length of the mail queue.
queuelength: 100
# The timeout in seconds after which the current open connection to the mailserver will be closed.
queuetimeout: 30
log:
# A folder where all the logfiles should go.
path: <rootpath>logs
# Whether to show any logging at all or none
enabled: true
# Where the error log should go. Possible values are stdout, stderr, file or off to disable error logging.
errors: "stdout"
# Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
standard: "stdout"
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
database: "off"
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
http: "stdout"
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
echo: "off"
{{< /highlight >}}

View File

@ -1,231 +0,0 @@
---
date: "2020-05-24:00:00+02:00"
title: "Docker Walkthrough"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# Setup with docker from start to finish
This tutorial assumes basic knowledge of docker.
It is aimed at beginners and should get you up and running quickly.
We'll use [docker compose](https://docs.docker.com/compose/) to make handling the bunch of containers easier.
> If you have any issues setting up vikunja, please don't hesitate to reach out to us via [matrix](https://riot.im/app/#/room/!dCRiCiLaCCFVNlDnYs:matrix.org?via=matrix.org), the [community forum](https://community.vikunja.io/) or even [email](mailto:hello@vikunja.io).
{{< table_of_contents >}}
## Preparations (optional)
Create a directory for the project where all data and the compose file will live in.
## Create all necessary files
Create a `docker-compose.yml` file with the following contents in your directory:
{{< highlight yaml >}}
version: '3'
services:
db:
image: mariadb:10
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
restart: unless-stopped
api:
image: vikunja/api
environment:
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: supersecret
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_DATABASE: vikunja
volumes:
- ./files:/app/vikunja/files
depends_on:
- db
restart: unless-stopped
frontend:
image: vikunja/frontend
restart: unless-stopped
proxy:
image: nginx
ports:
- 80:80
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- frontend
restart: unless-stopped
{{< /highlight >}}
This defines four services, each with their own container:
* An api service which runs the vikunja api. Most of the core logic lives here.
* The frontend which will make vikunja actually usable for most people.
* A database container which will store all lists, tasks, etc. We're using mariadb here, but you're free to use mysql or postgres if you want.
* A proxy service which makes the frontend and api available on the same port, redirecting all requests to `/api` to the api container.
If you already have a proxy on your host, you may want to check out the [reverse proxy examples]() to use that.
By default, it uses port 80 on the host.
To change to something different, you'll need to change the `ports` section in the service definition.
The number before the colon is the host port - This is where you can reach vikunja from the outside once all is up and running.
For the proxy service we'll need another bit of configuration.
Create an `nginx.conf` in your directory (next to the `docker-compose.yml` file) and add the following contents to it:
{{< highlight conf >}}
server {
listen 80;
location / {
proxy_pass http://frontend:80;
}
location ~* ^/(api|dav|\.well-known)/ {
proxy_pass http://api:3456;
client_max_body_size 20M;
}
}
{{< /highlight >}}
This is a simple proxy configuration which will forward all requests to `/api/` to the api container and everything else to the frontend.
<div class="notification is-info">
<b>NOTE:</b> Even if you want to make your installation available under a different port, you don't need to change anything in this configuration.
</div>
<div class="notification is-warning">
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
</div>
## Run it
Run `sudo docker-compose up` in your directory and take a look at the output you get.
When first started, Vikunja will set up the database and run all migrations etc.
Once it is ready, you should see a message like this one in your console:
```
api_1 | 2020-05-24T11:15:37.560386009Z: INFO ▶ cmd/func1 025 Vikunja version 0.13.1+19-e9bc3246ce, built at Sun, 24 May 2020 11:10:36 +0000
api_1 | ⇨ http server started on [::]:3456
```
This indicates all setup has been successful.
If you get any errors, see below:
### Troubleshooting
Vikunja might not run on the first try.
There are a few potential issues that could be causing this.
#### No connection to the database
Indicated by an error message like this one from the api container:
```
2020/05/23 15:37:59 Config File "config" Not Found in "[/app/vikunja /etc/vikunja /app/vikunja/.config/vikunja]"
2020/05/23 15:37:59 Using default config.
2020-05-23T15:37:59.974435725Z: CRITICAL ▶ migration/Migrate 002 Migration failed: dial tcp 172.19.0.2:3306: connect: connection refused
```
Especially when using mysql, this can happen on first start, because the mysql database container will take a few seconds to start.
Vikunja does not know the container is not ready, therefore it will just try to connect to the db, fail since it is not ready and exit.
If you're using the docker compose example from above, you may notice the `restart: unless-stopped` option at the api service.
This tells docker to restart the api container if it exits, unless you explicitly stop it.
Therefore, it should "magically fix itself" by automatically restarting the container.
After a few seconds (or minutes) you should see a log message like this one from the mariadb container:
```
2020-05-24 11:42:15 0 [Note] mysqld: ready for connections.
Version: '10.4.12-MariaDB-1:10.4.12+maria~bionic' socket: '/var/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
```
The next restart of Vikunja should be successful.
If not, there might be a different error or a bug with Vikunja, please reach out to us in that case.
(If you have an idea about how we could improve this, we'd like to hear it!)
#### "Not a directory"
If you get an error like this one:
```
ERROR: for vikunja_proxy_1 Cannot start service proxy: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"vikunja/nginx.conf\\\" to rootfs \\\"/var/lib/docker/overlay2/9c8b8f9419c29dad0d1233fbb0a3c36cf403dabd7a55d6f0a47b0c1dd6029994/merged\\\" at \\\"/var/lib/docker/overlay2/9c8b8f9419c29dad0d1233fbb0a3c36cf403dabd7a55d6f0a47b0c1dd6029994/merged/etc/nginx/conf.d/default.conf\\\" caused \\\"not a directory\\\"\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
```
this means docker tried to mount a directory from the host to a file in the container.
This can happen if you did not create the `nginx.conf` file.
Because there is a volume mount for it in the `docker-compose.yml`, Docker will create a folder because non exists, assuming you want to mount a folder into the container.
To fix this, create the file and restart the containers again.
#### Migration failed: commands out of sync
If you get an error like this one:
```
2020/05/23 15:53:38 Config File "config" Not Found in "[/app/vikunja /etc/vikunja /app/vikunja/.config/vikunja]"
2020/05/23 15:53:38 Using default config.
2020-05-23T15:53:38.762747276Z: CRITICAL ▶ migration/Migrate 002 Migration failed: commands out of sync. Did you run multiple statements at once?
```
This is a mysql issue.
Currently, we don't have a better solution than to completely wipe the database files and start over.
To do this, first stop everything by running `sudo docker-compose down`, then remove the `db/` folder in your current folder with `sudo rm -rf db` and start the whole stack again with `sudo docker-compose up -d`.
## Try it
Head over to `http://<host-ip or url>/api/v1/info` in a browser.
You should see something like this:
{{< highlight json >}}
{
"version": "0.13.1+19-e9bc3246ce",
"frontend_url": "http://localhost:8080/",
"motd": "test",
"link_sharing_enabled": true,
"max_file_size": "20MB",
"registration_enabled": true,
"available_migrators": [
"wunderlist",
"todoist"
],
"task_attachments_enabled": true
}
{{< /highlight >}}
This shows you can reach the api through the api proxy.
Now head over to `http://<host-ip or url>/` which should show the login mask.
## Make it persistent
Currently, Vikunja runs in foreground in your terminal.
For a real-world scenario this is not the best way.
Back in your terminal, stop the stack by pressing `CTRL-C` on your keyboard.
Then run `sudo docker-compose up -d` in your again.
The `-d` flag at the end of the command will tell docker to run the containers in the background.
If you need to check the logs after that, you can run `sudo docker-compose logs`.
Vikunja does not have any default users, you'll need to register and account.
After that, you can use it.
## Tear it all down
If you want to completely stop all containers run `sudo docker-compose down` in your terminal.
## Improve this guide
We'll happily accept suggestions and improvements for this guide.
Please [reach out to us](https://vikunja.io/contact/) if you have any.

View File

@ -11,164 +11,7 @@ menu:
# Full docker example
This docker compose configuration will run Vikunja with backend and frontend with a mariadb as 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">}}).
<div class="notification is-warning">
<b>NOTE:</b> If you intend to run Vikunja with mysql and/or to use non-latin characters
<a href="{{< ref "utf-8.md">}}">make sure your db is utf-8 compatible</a>.<br/>
All examples on this page already reflect this and do not require additional work.
</div>
{{< table_of_contents >}}
## Redis
To use redis, you'll need to add this to the config examples below:
{{< highlight yaml >}}
version: '3'
services:
api:
image: vikunja/api
environment:
VIKUNJA_REDIS_ENABLED: 1
VIKUNJA_REDIS_HOST: 'redis:6379'
VIKUNJA_CACHE_ENABLED: 1
VIKUNJA_CACHE_TYPE: redis
volumes:
- ./files:/app/vikunja/files
redis:
image: redis
{{< /highlight >}}
## Example with traefik 2
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:
* Your domain is `vikunja.example.com`
* The entrypoint you want to make vikunja available from is called `https`
* The tls cert resolver is called `acme`
{{< highlight yaml >}}
version: '3'
services:
api:
image: vikunja/api
environment:
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: supersecret
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: vikunja
VIKUNJA_DATABASE_DATABASE: vikunja
volumes:
- ./files:/app/vikunja/files
networks:
- web
- default
depends_on:
- db
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.vikunja-api.rule=Host(`vikunja.example.com`) && PathPrefix(`/api/v1`, `/dav/`, `/.well-known/`)"
- "traefik.http.routers.vikunja-api.entrypoints=https"
- "traefik.http.routers.vikunja-api.tls.certResolver=acme"
frontend:
image: vikunja/frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.vikunja-frontend.rule=Host(`vikunja.example.com`)"
- "traefik.http.routers.vikunja-frontend.entrypoints=https"
- "traefik.http.routers.vikunja-frontend.tls.certResolver=acme"
networks:
- web
- default
restart: unless-stopped
db:
image: mariadb:10
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: supersupersecret
MYSQL_USER: vikunja
MYSQL_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
restart: unless-stopped
command: --max-connections=1000
networks:
web:
external: true
{{< /highlight >}}
## Example with traefik 1
This example assumes [traefik](https://traefik.io) in version 1 installed and configured to [use docker as a configuration provider](https://docs.traefik.io/v1.7/configuration/backends/docker/).
{{< highlight yaml >}}
version: '3'
services:
api:
image: vikunja/api
environment:
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: supersecret
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: vikunja
VIKUNJA_DATABASE_DATABASE: vikunja
volumes:
- ./files:/app/vikunja/files
networks:
- web
- default
depends_on:
- db
restart: unless-stopped
labels:
- "traefik.docker.network=web"
- "traefik.enable=true"
- "traefik.frontend.rule=Host:vikunja.example.com;PathPrefix:/api/v1,/dav/,/.well-known"
- "traefik.port=3456"
- "traefik.protocol=http"
frontend:
image: vikunja/frontend
labels:
- "traefik.docker.network=web"
- "traefik.enable=true"
- "traefik.frontend.rule=Host:vikunja.example.com;PathPrefix:/"
- "traefik.port=80"
- "traefik.protocol=http"
networks:
- web
- default
restart: unless-stopped
db:
image: mariadb:10
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: supersupersecret
MYSQL_USER: vikunja
MYSQL_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
restart: unless-stopped
command: --max-connections=1000
networks:
web:
external: true
{{< /highlight >}}
## Example with nginx as proxy
It uses an nginx container to proxy backend and frontend into a single port.
You'll need to save this nginx configuration on your host under `nginx.conf`
(or elsewhere, but then you'd need to adjust the proxy mount at the bottom of the compose file):
@ -181,18 +24,13 @@ server {
proxy_pass http://frontend:80;
}
location ~* ^/(api|dav|\.well-known)/ {
location /api/ {
proxy_pass http://api:3456;
client_max_body_size 20M;
}
}
{{< /highlight >}}
<div class="notification is-warning">
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
</div>
`docker-compose.yml` config:
### Without redis
{{< highlight yaml >}}
version: '3'
@ -200,13 +38,11 @@ version: '3'
services:
db:
image: mariadb:10
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
restart: unless-stopped
api:
image: vikunja/api
environment:
@ -215,14 +51,10 @@ services:
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_DATABASE: vikunja
volumes:
- ./files:/app/vikunja/files
depends_on:
- db
restart: unless-stopped
frontend:
image: vikunja/frontend
restart: unless-stopped
proxy:
image: nginx
ports:
@ -232,23 +64,9 @@ services:
depends_on:
- api
- frontend
restart: unless-stopped
{{< /highlight >}}
## Example with Caddy v2 as proxy
You will need the following `Caddyfile` on your host (or elsewhere, but then you'd need to adjust the proxy mount at the bottom of the compose file):
{{< highlight conf >}}
vikunja.example.com {
reverse_proxy /api/* api:3456
reverse_proxy /.well-known/* api:3456
reverse_proxy /dav/* api:3456
reverse_proxy frontend:80
}
{{< /highlight >}}
`docker-compose.yml` config:
### With redis
{{< highlight yaml >}}
version: '3'
@ -256,13 +74,13 @@ version: '3'
services:
db:
image: mariadb:10
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
restart: unless-stopped
redis:
image: redis
api:
image: vikunja/api
environment:
@ -271,23 +89,22 @@ services:
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_DATABASE: vikunja
volumes:
- ./files:/app/vikunja/files
VIKUNJA_REDIS_ENABLED: 1
VIKUNJA_REDIS_HOST: 'redis:6379'
VIKUNJA_CACHE_ENABLED: 1
VIKUNJA_CACHE_TYPE: redis
depends_on:
- db
restart: unless-stopped
- redis
frontend:
image: vikunja/frontend
restart: unless-stopped
caddy:
image: caddy
restart: unless-stopped
proxy:
image: nginx
ports:
- "80:80"
- "443:443"
- 80:80
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- frontend
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
{{< /highlight >}}
{{< /highlight >}}

View File

@ -10,13 +10,6 @@ menu:
# Backend
<div class="notification is-warning">
<b>NOTE:</b> If you intend to run Vikunja with mysql and/or to use non-latin characters
<a href="{{< ref "utf-8.md">}}">make sure your db is utf-8 compatible</a>.
</div>
{{< table_of_contents >}}
## Install from binary
Download a copy of Vikunja from the [download page](https://vikunja.io/en/download/) for your architecture.
@ -62,7 +55,6 @@ After=network.target
# Depending on how you configured Vikunja, you may want to uncomment these:
#Requires=mysql.service
#Requires=mariadb.service
#Requires=postgresql.service
#Requires=redis.service
[Service]
@ -113,7 +105,7 @@ docker run -p 3456:3456 vikunja/api
{{< /highlight >}}
to run with a standard configuration.
This will expose vikunja on port `3456` on the host running the container.
This will expose
You can mount a local configuration like so:
@ -124,18 +116,6 @@ docker run -p 3456:3456 -v /path/to/config/on/host.yml:/app/vikunja/config.yml:r
Though it is recommended to use eviroment variables or `.env` files to configure Vikunja in docker.
See [config]({{< ref "config.md">}}) for a list of available configuration options.
### Files volume
By default the container stores all files uploaded and used through vikunja inside of `/app/vikunja/files` which is created as a docker volume.
You should mount the volume somewhere to the host to permanently store the files and don't loose them if the container restarts.
### Setting user and group id of the user running vikunja
You can set the user and group id of the user running vikunja with the `PUID` and `PGID` evironment variables.
This follows the pattern used by [the linuxserver.io](https://docs.linuxserver.io/general/understanding-puid-and-pgid) docker images.
This is useful to solve general permission problems when host-mounting volumes such as the volume used for task attachments.
### Docker compose
To run the backend with a mariadb database you can use this example [docker-compose](https://docs.docker.com/compose/) file:
@ -151,16 +131,13 @@ services:
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: root
VIKUNJA_SERVICE_JWTSECRET: <generated secret>
volumes:
- ./files:/app/vikunja/files
db:
image: mariadb:10
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
MYSQL_ROOT_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
- ./db:/var/lib/mysql
{{< /highlight >}}
See [full docker example]({{< ref "full-docker-example.md">}}) for more varations of this config.
@ -178,100 +155,6 @@ dpkg -i vikunja.deb
This will install the backend to `/opt/vikunja`.
To configure it, use the config file in `/etc/vikunja/config.yml`.
## FreeBSD / FreeNAS
Unfortunately, we currently can't provide pre-built binaries for FreeBSD.
As a workaround, it is possible to compile vikunja for FreeBSD directly on a FreeBSD machine, a guide is available below:
*Thanks to HungrySkeleton who originally created this guide [in the forum](https://community.vikunja.io/t/freebsd-support/69/11).*
### Jail Setup
1. Create jail named ```vikunja```
2. Set jail properties to 'auto start'
3. Mount storage (```/mnt``` to ```jailData/vikunja```)
4. Start jail & SSH into it
### Installing packages
{{< highlight bash >}}
pkg update && pkg upgrade -y
pkg install nano git go gmake
go install github.com/magefile/mage
{{< /highlight >}}
### Clone vikunja repo
{{< highlight bash >}}
mkdir /mnt/GO/code.vikunja.io
cd /mnt/GO/code.vikunja.io
git clone https://code.vikunja.io/api
cd /mnt/GO/code.vikunja.io/api
{{< /highlight >}}
### Compile binaries
{{< highlight bash >}}
go install
mage build
{{< /highlight >}}
### Create folder to install backend server into
{{< highlight bash >}}
mkdir /mnt/backend
cp /mnt/GO/code.vikunja.io/api/vikunja /mnt/backend/vikunja
cd /mnt/backend
chmod +x /mnt/backend/vikunja
{{< /highlight >}}
### Set vikunja to boot on startup
{{< highlight bash >}}
nano /etc/rc.d/vikunja
{{< /highlight >}}
Then paste into the file:
{{< highlight bash >}}
#!/bin/sh
. /etc/rc.subr
name=vikunja
rcvar=vikunja_enable
command="/mnt/backend/${name}"
load_rc_config $name
run_rc_command "$1"
{{< /highlight >}}
Save and exit. Then execute:
{{< highlight bash >}}
chmod +x /etc/rc.d/vikunja
nano /etc/rc.conf
{{< /highlight >}}
Then add line to bottom of file:
{{< highlight bash >}}
vikunja_enable="YES"
{{< /highlight >}}
Test vikunja now works with
{{< highlight bash >}}
service vikunja start
{{< /highlight >}}
The API is now available through IP:
```
192.168.1.XXX:3456
```
## Configuration
See [available configuration options]({{< ref "config.md">}}).

View File

@ -17,22 +17,6 @@ Unzip them and store them somewhere your server can access them.
You also need to configure a rewrite condition to internally redirect all requests to `index.html` which handles all urls.
{{< table_of_contents >}}
## API URL configuration
By default, the frontend assumes it can reach the api at `/api/v1` relative to the frontend url.
This means that if you make the frontend available at, say `https://vikunja.example.com`, it tries to reach the api
at `https://vikunja.example.com/api/v1`.
In this scenario it is not possible for the frontend and the api to live on seperate servers or even just seperate
ports on the same server with [the use of a reverse proxy]({{< ref "reverse-proxies.md">}}).
To make configurations like this possible, the api url can be set in the `index.html` file of the frontend releases.
Just open the file with a text editor - there are comments which will explain how to set the url.
**Note:** This needs to be done again after every update.
(If you have a good idea for a better solution than this, we'd love to [hear it](https://vikunja.io/contact/))
## Docker
The docker image is based on nginx and just contains all nessecary files for the frontend.
@ -47,16 +31,6 @@ which will run the docker image and expose port 80 on the host.
See [full docker example]({{< ref "full-docker-example.md">}}) for more varations of this config.
### Setting user and group id of the user running vikunja
You can set the user and group id of the user running vikunja with the `PUID` and `PGID` evironment variables.
This follows the pattern used by [the linuxserver.io](https://docs.linuxserver.io/general/understanding-puid-and-pgid) docker images.
### API URL configuration in docker
When running the frontend with docker, it is possible to set the environment variable `$VIKUNJA_API_URL` to the api url.
It is therefore not needed to change the url manually inside the docker container.
## NGINX
Below are two example configurations which you can put in your `nginx.conf`:

View File

@ -30,7 +30,6 @@ This document provides an overview and instructions for the different methods.
* [Docker]({{< ref "install-backend.md#docker">}})
* [Debian packages]({{< ref "install-backend.md#debian-packages">}})
* [Configuration]({{< ref "config.md">}})
* [UTF-8 Settings]({{< ref "utf-8.md">}})
* [Frontend]({{< ref "install-frontend.md">}})
* [Docker]({{< ref "install-frontend.md#docker">}})
* [NGINX]({{< ref "install-frontend.md#nginx">}})

View File

@ -13,8 +13,6 @@ menu:
These examples assume you have an instance of the backend running on your server listening on port `3456`.
If you've changed this setting, you need to update the server configurations accordingly.
{{< table_of_contents >}}
## NGINX
Below are two example configurations which you can put in your `nginx.conf`:
@ -45,17 +43,12 @@ server {
index index.html index.htm;
}
location ~* ^/(api|dav|\.well-known)/ {
location /api/ {
proxy_pass http://localhost:3456;
client_max_body_size 20M;
}
}
{{< /highlight >}}
<div class="notification is-warning">
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
</div>
### without gzip
{{< highlight conf >}}
@ -69,17 +62,12 @@ server {
index index.html index.htm;
}
location ~* ^/(api|dav|\.well-known)/ {
location /api/ {
proxy_pass http://localhost:3456;
client_max_body_size 20M;
}
}
{{< /highlight >}}
<div class="notification is-warning">
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
</div>
## Apache
Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
@ -94,18 +82,14 @@ Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
</Proxy>
ProxyPass /api http://localhost:3456/api
ProxyPassReverse /api http://localhost:3456/api
ProxyPass /dav http://localhost:3456/dav
ProxyPassReverse /dav http://localhost:3456/dav
ProxyPass /.well-known http://localhost:3456/.well-known
ProxyPassReverse /.well-known http://localhost:3456/.well-known
DocumentRoot /var/www/html
RewriteEngine On
RewriteRule ^\/?(config\.json|favicon\.ico|css|fonts|images|img|js|api|dav|\.well-known) - [L]
RewriteRule ^\/?(config\.json|favicon\.ico|css|fonts|images|img|js|api) - [L]
RewriteRule ^(.*)$ /index.html [QSA,L]
</VirtualHost>
{{< /highlight >}}
**Note:** The apache modules `proxy`, `proxy_http` and `rewrite` must be enabled for this.
**Note:** The apache modules `proxy` and `proxy_http` must be enabled for this.
For more details see the [frontend apache configuration]({{< ref "install-frontend.md#apache">}}).

View File

@ -1,108 +0,0 @@
---
date: "2020-07-06:00:00+02:00"
title: "UTF-8 Settings"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# UTF-8 Settings
Vikunja itself is always fully capable of handling utf-8 characters.
However, your database might be not.
Vikunja itself will work just fine until you want to use non-latin characters in your tasks/lists/etc.
On this page, you will find information about how to fully ensure non-latin characters like aüäß or emojis work
with your installation.
{{< table_of_contents >}}
## Postgresql & SQLite
Postgresql and SQLite should handle utf-8 just fine - If you discover any issues nonetheless, please
[drop us a message](https://vikunja.io/contact/).
## MySQL
MySQL is not able to handle utf-8 by default.
To fix this, follow the steps below.
To find out if your db supports utf-8, run the following in a shell or similar, assuming the database
you're using for vikunja is called `vikunja`:
{{< highlight sql >}}
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'vikunja';
{{< /highlight >}}
This will get you a result like the following:
```
+----------------------------+
| default_character_set_name |
+----------------------------+
| latin1 |
+----------------------------+
1 row in set (0.001 sec)
```
The charset `latin1` means the db is encoded in the `latin1` encoding which does not support utf-8 characters.
(The following guide is based on [this thread from stackoverflow](https://dba.stackexchange.com/a/104866))
### 0. Backup your database
Before attempting any conversion, please [back up your database]({{< ref "backups.md">}}).
### 1. Create a pre-conversion script
Copy the following sql statements in a file called `preAlterTables.sql` and replace all occurences of `vikunja` with
the name of your database:
{{< highlight sql >}}
use information_schema;
SELECT concat("ALTER DATABASE `",table_schema,"` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") as _sql
FROM `TABLES` where table_schema like 'vikunja' and TABLE_TYPE='BASE TABLE' group by table_schema;
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name,"` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as _sql
FROM `TABLES` where table_schema like 'vikunja' and TABLE_TYPE='BASE TABLE' group by table_schema, table_name;
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type,"(",character_maximum_length,") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") as _sql
FROM `COLUMNS` where table_schema like 'vikunja' and data_type in ('varchar','char');
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type," CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") as _sql
FROM `COLUMNS` where table_schema like 'vikunja' and data_type in ('text','tinytext','mediumtext','longtext');
{{< /highlight >}}
### 2. Run the pre-conversion script
Running this will create the actual migration script for your particular database structure and save it in a file called `alterTables.sql`:
{{< highlight bash >}}
mysql -uroot < preAlterTables.sql | egrep '^ALTER' > alterTables.sql
{{< /highlight >}}
### 3. Convert the database
At this point converting is just a matter of executing the previously generated sql script:
{{< highlight bash >}}
mysql -uroot < alterTables.sql
{{< /highlight >}}
### 4. Verify it was successfully converted
If everything worked as intended, your db collation should now look like this:
{{< highlight sql >}}
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'vikunja';
{{< /highlight >}}
Should get you:
```
+----------------------------+
| default_character_set_name |
+----------------------------+
| utf8mb4 |
+----------------------------+
1 row in set (0.001 sec)
```

View File

@ -16,8 +16,6 @@ menu:
Vikunja supports managing tasks via the [caldav VTODO](https://tools.ietf.org/html/rfc5545#section-3.6.2) extension.
{{< table_of_contents >}}
## URLs
All urls are located under the `/dav` subspace.
@ -66,15 +64,13 @@ Vikunja **currently does not** support these properties:
## Tested Clients
### Working
#### Working
* [Evolution](https://wiki.gnome.org/Apps/Evolution/)
* [OpenTasks](https://opentasks.app/) + [DAVx⁵](https://www.davx5.com/)
### Not working
#### Not working
* [Tasks (Android)](https://tasks.org/)
* [Thunderbird (68)](https://www.thunderbird.net/)
## Dev logs

View File

@ -13,12 +13,8 @@ menu:
You can interact with Vikunja using its `cli` interface.
The following commands are available:
* [dump](#dump)
* [help](#help)
* [migrate](#migrate)
* [restore](#restore)
* [testmail](#testmail)
* [user](#user)
* [version](#version)
* [web](#web)
@ -26,16 +22,6 @@ If you don't specify a command, the [`web`](#web) command will be executed.
All commands use the same standard [config file]({{< ref "../setup/config.md">}}).
### `dump`
Creates a zip file with all vikunja-related files.
This includes config, version, all files and the full database.
Usage:
{{< highlight bash >}}
$ vikunja dump
{{< /highlight >}}
### `help`
Shows more detailed help about any command.
@ -77,91 +63,6 @@ $ vikunja migrate rollback [flags]
Flags:
* `-n`, `--name` string: The id of the migration you want to roll back until.
### `restore`
Restores a previously created dump from a zip file, see `dump`.
Usage:
{{< highlight bash >}}
$ vikunja restore <path to dump zip file>
{{< /highlight >}}
### `testmail`
Sends a test mail using the configured smtp connection.
Usage:
{{< highlight bash >}}
$ vikunja testmail <email to send the test mail to>
{{< /highlight >}}
### `user`
Bundles a few commands to manage users.
#### `user change-status`
Enable or disable a user. Will toggle the current status if no flag (`--enable` or `--disable`) is provided.
Usage:
{{< highlight bash >}}
$ vikunja user change-status <user id> <flags>
{{< /highlight >}}
Flags:
* `-d`, `--disable`: Disable the user.
* `-e`, `--enable`: Enable the user.
#### `user create`
Create a new user.
Usage:
{{< highlight bash >}}
$ vikunja user create <flags>
{{< /highlight >}}
Flags:
* `-a`, `--avatar-provider`: The avatar provider of the new user. Optional.
* `-e`, `--email`: The email address of the new user.
* `-p`, `--password`: The password of the new user. You will be asked to enter it if not provided through the flag.
* `-u`, `--username`: The username of the new user.
#### `user list`
Shows a list of all users.
Usage:
{{< highlight bash >}}
$ vikunja user list
{{< /highlight >}}
#### `user reset-password`
Reset a users password, either through mailing them a reset link or directly.
Usage:
{{< highlight bash >}}
$ vikunja user reset-password <flags>
{{< /highlight >}}
Flags:
* `-d`, `--direct`: If provided, reset the password directly instead of sending the user a reset mail.
* `-p`, `--password`: The new password of the user. Only used in combination with --direct. You will be asked to enter it if not provided through the flag.
#### `user update`
Update an existing user.
Usage:
{{< highlight bash >}}
$ vikunja user update <user id>
{{< /highlight >}}
Flags:
* `-a`, `--avatar-provider`: The new avatar provider of the new user.
* `-e`, `--email`: The new email address of the user.
* `-u`, `--username`: The new username of the user.
### `version`
@ -180,4 +81,4 @@ Starts Vikunja's REST api server.
Usage:
{{< highlight bash >}}
$ vikunja web
{{< /highlight >}}
{{< /highlight >}}

View File

@ -12,16 +12,6 @@ menu:
This document describes the different errors Vikunja can return.
{{< table_of_contents >}}
## Generic
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 0001 | 403 | Generic forbidden error. |
## User
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 1001 | 400 | A user with this username already exists. |
@ -36,109 +26,31 @@ This document describes the different errors Vikunja can return.
| 1012 | 412 | Email address of the user not confirmed. |
| 1013 | 412 | New password is empty. |
| 1014 | 412 | Old password is empty. |
| 1015 | 412 | Totp is already enabled for this user. |
| 1016 | 412 | Totp is not enabled for this user. |
| 1017 | 412 | The provided Totp passcode is invalid. |
| 1018 | 412 | The provided user avatar provider type setting is invalid. |
## Validation
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 2001 | 400 | ID cannot be empty or 0. |
| 2002 | 400 | Some of the request data was invalid. The response contains an aditional array with all invalid fields. |
## List
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 3001 | 404 | The list does not exist. |
| 3004 | 403 | The user needs to have read permissions on that list to perform that action. |
| 3005 | 400 | The list title cannot be empty. |
| 3006 | 404 | The list share does not exist. |
| 3007 | 400 | A list with this identifier already exists. |
| 3008 | 412 | The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list. |
## Task
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 4001 | 400 | The list task text cannot be empty. |
| 4002 | 404 | The list task does not exist. |
| 4003 | 403 | All bulk editing tasks must belong to the same list. |
| 4004 | 403 | Need at least one task when bulk editing tasks. |
| 4005 | 403 | The user does not have the right to see the task. |
| 4006 | 403 | The user tried to set a parent task as the task itself. |
| 4007 | 400 | The user tried to create a task relation with an invalid kind of relation. |
| 4008 | 409 | The user tried to create a task relation which already exists. |
| 4009 | 404 | The task relation does not exist. |
| 4010 | 400 | Cannot relate a task with itself. |
| 4011 | 404 | The task attachment does not exist. |
| 4012 | 400 | The task attachment is too large. |
| 4013 | 400 | The task sort param is invalid. |
| 4014 | 400 | The task sort order is invalid. |
| 4015 | 404 | The task comment does not exist. |
| 4016 | 403 | Invalid task field. |
| 4017 | 403 | Invalid task filter comparator. |
| 4018 | 403 | Invalid task filter concatinator. |
| 4019 | 403 | Invalid task filter value. |
## Namespace
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 5001 | 404 | The namspace does not exist. |
| 5003 | 403 | The user does not have access to the specified namespace. |
| 5006 | 400 | The namespace name cannot be empty. |
| 5009 | 403 | The user needs to have namespace read access to perform that action. |
| 5010 | 403 | This team does not have access to that namespace. |
| 5011 | 409 | This user has already access to that namespace. |
| 5012 | 412 | The namespace is archived and can therefore only be accessed read only. |
## Team
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 6001 | 400 | The team name cannot be emtpy. |
| 6002 | 404 | The team does not exist. |
| 6004 | 409 | The team already has access to that namespace or list. |
| 6005 | 409 | The user is already a member of that team. |
| 6006 | 400 | Cannot delete the last team member. |
| 6007 | 403 | The team does not have access to the list to perform that action. |
## User List Access
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 7002 | 409 | The user already has access to that list. |
| 7003 | 403 | The user does not have access to that list. |
## Label
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 8001 | 403 | This label already exists on that task. |
| 8002 | 404 | The label does not exist. |
| 8003 | 403 | The user does not have access to this label. |
## Right
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 9001 | 403 | The right is invalid. |
## Kanban
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 10001 | 404 | The bucket does not exist. |
| 10002 | 400 | The bucket does not belong to that list. |
| 10003 | 412 | You cannot remove the last bucket on a list. |
| 10004 | 412 | You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold. |
## Saved Filters
| ErrorCode | HTTP Status Code | Description |
|-----------|------------------|-------------|
| 11001 | 404 | The saved filter does not exist. |
| 11002 | 412 | Saved filters are not available for link shares. |
| 9001 | 403 | The right is invalid. |

View File

@ -1,25 +0,0 @@
---
date: "2019-09-25:00:00+02:00"
title: "Task Relation kinds"
draft: false
type: "doc"
menu:
sidebar:
parent: "usage"
---
# Available task relation kinds
| Code | Description |
|------|-------------|
| subtask | Task is a subtask of the other task. This is the opposite of `parenttask`. |
| parenttask | Task is a parent task of the other task. This is the opposite of `subtask`. |
| related | Both tasks are related to each other. How is not more specified. |
| duplicateof | Task is a duplicate of the other task. This is the opposite of `duplicates`. |
| duplicates | Task duplicates the other task. This is the opposite of `duplicateof`. |
| blocking | Task is blocking the other task. This is the opposite of `blocked`. |
| blocked | Task is blocked by the other task. This is the opposite of `blocking`. |
| precedes | Task precedes the other task. This is the opposite of `follows`. |
| follows | Task follows the other task. This is the opposite of `precedes`. |
| copiedfrom | Task is copied from the other task. This is the opposite of `copiedto`. |
| copiedto | Task is copied to the other task. This is the opposite of `copiedfrom`. |

View File

@ -23,7 +23,7 @@ The following values are possible:
| 1 | Read and write. Namespaces or lists shared with this right can be read and written to by the team or user. |
| 2 | Admin. Can do anything like read and write, but can additionally manage sharing options. |
## Team admins
### Team admins
When adding or querying a team, every member has an additional boolean value stating if it is admin or not.
A team admin can also add and remove team members and also change whether a user in the team is admin or not.

2
docs/themes/vikunja vendored

@ -1 +1 @@
Subproject commit 958219fc84db455ed58d7a4380bbffc8d04fd5cf
Subproject commit 611b91ba5df3c88e9262e040eb1e6fa772919fc8

120
go.mod
View File

@ -1,4 +1,4 @@
// Vikunja is a to-do list application to facilitate your life.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
@ -17,91 +17,65 @@
module code.vikunja.io/api
require (
4d63.com/tz v1.2.0
code.vikunja.io/web v0.0.0-20200809154828-8767618f181f
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 // indirect
gitea.com/xorm/xorm-redis-cache v0.2.0
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
cloud.google.com/go v0.34.0 // indirect
code.vikunja.io/web v0.0.0-20190507193736-edb39812af9c
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/beevik/etree v1.1.0 // indirect
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2
github.com/client9/misspell v0.3.4
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/cweill/gotests v1.5.3
github.com/d4l3k/messagediff v1.2.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/disintegration/imaging v1.6.2
github.com/dustinkirkland/golang-petname v0.0.0-20191129215211-8e5a1ed0cff0
github.com/fzipp/gocyclo v0.3.1
github.com/gabriel-vasile/mimetype v1.1.2
github.com/getsentry/sentry-go v0.8.0
github.com/go-errors/errors v1.1.1
github.com/go-openapi/swag v0.19.9 // indirect
github.com/go-redis/redis/v7 v7.4.0
github.com/go-sql-driver/mysql v1.5.0
github.com/go-testfixtures/testfixtures/v3 v3.4.1
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-cmp v0.5.2 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063
github.com/iancoleman/strcase v0.1.2
github.com/imdario/mergo v0.3.11
github.com/jgautheron/goconst v0.0.0-20201117150253-ccae5bf973f3
github.com/kr/text v0.2.0 // indirect
github.com/labstack/echo/v4 v4.1.17
github.com/labstack/gommon v0.3.0
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835
github.com/garyburd/redigo v1.6.0 // indirect
github.com/go-openapi/jsonpointer v0.19.0 // indirect
github.com/go-openapi/jsonreference v0.19.0 // indirect
github.com/go-openapi/spec v0.19.0 // indirect
github.com/go-openapi/swag v0.19.0 // indirect
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-sql-driver/mysql v1.4.1
github.com/go-xorm/builder v0.3.4
github.com/go-xorm/core v0.6.2
github.com/go-xorm/tests v0.5.6 // indirect
github.com/go-xorm/xorm v0.7.1
github.com/go-xorm/xorm-redis-cache v0.0.0-20180727005610-859b313566b2
github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc
github.com/imdario/mergo v0.3.7
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jgautheron/goconst v0.0.0-20170703170152-9740945f5dcb
github.com/labstack/echo/v4 v4.1.5
github.com/labstack/gommon v0.2.8
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
github.com/lib/pq v1.8.0
github.com/magefile/mage v1.10.0
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-sqlite3 v1.14.5
github.com/mitchellh/mapstructure v1.3.2 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/onsi/ginkgo v1.13.0 // indirect
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 // indirect
github.com/mattn/go-oci8 v0.0.0-20181130072307-052f5d97b9b6 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mattn/go-sqlite3 v1.10.0
github.com/olekukonko/tablewriter v0.0.1
github.com/onsi/ginkgo v1.7.0 // indirect
github.com/onsi/gomega v1.4.3 // indirect
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml v1.8.0 // indirect
github.com/pquerna/otp v1.3.0
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
github.com/prometheus/client_golang v1.8.0
github.com/pelletier/go-toml v1.4.0 // indirect
github.com/prometheus/client_golang v0.9.2
github.com/samedi/caldav-go v3.0.0+incompatible
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
github.com/spf13/afero v1.4.1
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/cobra v1.1.1
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
github.com/swaggo/swag v1.6.9
github.com/ulule/limiter/v3 v3.5.0
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 // indirect
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 // indirect
github.com/spf13/viper v1.3.2
github.com/stretchr/testify v1.3.0
github.com/swaggo/swag v1.5.0
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284
golang.org/x/lint v0.0.0-20190409202823-959b441ac422
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c // indirect
google.golang.org/appengine v1.5.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/d4l3k/messagediff.v1 v1.2.1
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.57.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
honnef.co/go/tools v0.0.1-2020.1.5
src.techknowlogick.com/xgo v1.1.1-0.20200811225412-bff6512e7c9c
src.techknowlogick.com/xormigrate v1.4.0
xorm.io/builder v0.3.7
xorm.io/core v0.7.3
xorm.io/xorm v1.0.2
gopkg.in/testfixtures.v2 v2.5.3
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
src.techknowlogick.com/xgo v0.0.0-20190507142556-a5b29ecb0ff4
src.techknowlogick.com/xormigrate v0.0.0-20190321151057-24497c23c09c
)
replace (
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4
github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
github.com/hpcloud/tail => github.com/jeffbean/tail v1.0.1 // See https://github.com/hpcloud/tail/pull/159
github.com/labstack/echo/v4 => github.com/kolaente/echo/v4 v4.0.0-20190507190305-3725a216d803 // Branch: feature/report-method, PR https://github.com/labstack/echo/pull/1332
github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
gopkg.in/fsnotify.v1 => github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 // See https://github.com/fsnotify/fsnotify/issues/328 and https://github.com/golang/go/issues/26904
)
go 1.13

1270
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -1,925 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// +build mage
package main
import (
"bufio"
"bytes"
"context"
"crypto/sha256"
"fmt"
"github.com/magefile/mage/mg"
"golang.org/x/sync/errgroup"
"gopkg.in/yaml.v3"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
)
const (
PACKAGE = `code.vikunja.io/api`
DIST = `dist`
)
var (
Goflags = []string{
"-v",
}
Executable = "vikunja"
Ldflags = ""
Tags = ""
VersionNumber = "dev"
Version = "master" // This holds the built version, master by default, when building from a tag or release branch, their name
BinLocation = ""
PkgVersion = "master"
ApiPackages = []string{}
RootPath = ""
GoFiles = []string{}
// Aliases are mage aliases of targets
Aliases = map[string]interface{}{
"build": Build.Build,
"do-the-swag": DoTheSwag,
"check:got-swag": Check.GotSwag,
"release:os-package": Release.OsPackage,
"dev:create-migration": Dev.CreateMigration,
"generate-docs": GenerateDocs,
}
)
func setVersion() {
versionCmd := exec.Command("git", "describe", "--tags", "--always", "--abbrev=10")
version, err := versionCmd.Output()
if err != nil {
fmt.Printf("Error getting version: %s\n", err)
os.Exit(1)
}
VersionNumber = strings.Trim(string(version), "\n")
VersionNumber = strings.Replace(VersionNumber, "-", "+", 1)
VersionNumber = strings.Replace(VersionNumber, "-g", "-", 1)
if os.Getenv("DRONE_TAG") != "" {
Version = os.Getenv("DRONE_TAG")
} else if os.Getenv("DRONE_BRANCH") != "" {
Version = strings.Replace(os.Getenv("DRONE_BRANCH"), "release/v", "", 1)
}
}
func setBinLocation() {
if os.Getenv("DRONE_WORKSPACE") != "" {
BinLocation = DIST + `/binaries/` + Executable + `-` + Version + `-linux-amd64`
} else {
BinLocation = Executable
}
}
func setPkgVersion() {
if Version == "master" {
PkgVersion = VersionNumber
}
}
func setExecutable() {
if runtime.GOOS == "windows" {
Executable += ".exe"
}
}
func setApiPackages() {
cmd := exec.Command("go", "list", "all")
pkgs, err := cmd.Output()
if err != nil {
fmt.Printf("Error getting packages: %s\n", err)
os.Exit(1)
}
for _, p := range strings.Split(string(pkgs), "\n") {
if strings.Contains(p, "code.vikunja.io/api") && !strings.Contains(p, "code.vikunja.io/api/pkg/integrations") {
ApiPackages = append(ApiPackages, p)
}
}
}
func setRootPath() {
pwd, err := os.Getwd()
if err != nil {
fmt.Printf("Error getting pwd: %s\n", err)
os.Exit(1)
}
if err := os.Setenv("VIKUNJA_SERVICE_ROOTPATH", pwd); err != nil {
fmt.Printf("Error setting root path: %s\n", err)
os.Exit(1)
}
RootPath = pwd
}
func setGoFiles() {
// GOFILES := $(shell find . -name "*.go" -type f ! -path "*/bindata.go")
cmd := exec.Command("find", ".", "-name", "*.go", "-type", "f", "!", "-path", "*/bindata.go")
files, err := cmd.Output()
if err != nil {
fmt.Printf("Error getting go files: %s\n", err)
os.Exit(1)
}
for _, f := range strings.Split(string(files), "\n") {
if strings.HasSuffix(f, ".go") {
GoFiles = append(GoFiles, RootPath+strings.TrimLeft(f, "."))
}
}
}
// Some variables can always get initialized, so we do just that.
func init() {
setExecutable()
setRootPath()
}
// Some variables have external dependencies (like git) which may not always be available.
func initVars() {
Tags = os.Getenv("TAGS")
setVersion()
setBinLocation()
setPkgVersion()
setApiPackages()
setGoFiles()
Ldflags = `-X "` + PACKAGE + `/pkg/version.Version=` + VersionNumber + `" -X "main.Tags=` + Tags + `"`
}
func runAndStreamOutput(cmd string, args ...string) {
c := exec.Command(cmd, args...)
c.Env = os.Environ()
c.Dir = RootPath
fmt.Printf("%s\n\n", c.String())
stdout, _ := c.StdoutPipe()
errbuf := bytes.Buffer{}
c.Stderr = &errbuf
c.Start()
reader := bufio.NewReader(stdout)
line, err := reader.ReadString('\n')
for err == nil {
fmt.Print(line)
line, err = reader.ReadString('\n')
}
if err := c.Wait(); err != nil {
fmt.Printf(errbuf.String())
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
}
// Will check if the tool exists and if not install it from the provided import path
// If any errors occur, it will exit with a status code of 1.
func checkAndInstallGoTool(tool, importPath string) {
if err := exec.Command(tool).Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
fmt.Printf("%s not installed, installing %s...\n", tool, importPath)
if err := exec.Command("go", "install", Goflags[0], importPath).Run(); err != nil {
fmt.Printf("Error installing %s\n", tool)
os.Exit(1)
}
fmt.Println("Installed.")
}
}
// Calculates a hash of a file
func calculateSha256FileHash(path string) (hash string, err error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
// Copy the src file to dst. Any existing file will be overwritten and will not
// copy file attributes.
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
si, err := os.Stat(src)
if err != nil {
return err
}
if err := os.Chmod(dst, si.Mode()); err != nil {
return err
}
return out.Close()
}
// os.Rename has issues with moving files between docker volumes.
// Because of this limitaion, it fails in drone.
// Source: https://gist.github.com/var23rav/23ae5d0d4d830aff886c3c970b8f6c6b
func moveFile(src, dst string) error {
inputFile, err := os.Open(src)
defer inputFile.Close()
if err != nil {
return fmt.Errorf("couldn't open source file: %s", err)
}
outputFile, err := os.Create(dst)
defer outputFile.Close()
if err != nil {
return fmt.Errorf("couldn't open dest file: %s", err)
}
_, err = io.Copy(outputFile, inputFile)
if err != nil {
return fmt.Errorf("writing to output file failed: %s", err)
}
// Make sure to copy copy the permissions of the original file as well
si, err := os.Stat(src)
if err != nil {
return err
}
if err := os.Chmod(dst, si.Mode()); err != nil {
return err
}
// The copy was successful, so now delete the original file
err = os.Remove(src)
if err != nil {
return fmt.Errorf("failed removing original file: %s", err)
}
return nil
}
// Formats the code using go fmt
func Fmt() {
mg.Deps(initVars)
args := append([]string{"-s", "-w"}, GoFiles...)
runAndStreamOutput("gofmt", args...)
}
// Generates the swagger docs from the code annotations
func DoTheSwag() {
mg.Deps(initVars)
checkAndInstallGoTool("swag", "github.com/swaggo/swag/cmd/swag")
runAndStreamOutput("swag", "init", "-g", "./pkg/routes/routes.go", "--parseDependency", "-d", RootPath, "-o", RootPath+"/pkg/swagger")
}
type Test mg.Namespace
// Runs all tests except integration tests
func (Test) Unit() {
mg.Deps(initVars)
// We run everything sequentially and not in parallel to prevent issues with real test databases
args := append([]string{"test", Goflags[0], "-p", "1"}, ApiPackages...)
runAndStreamOutput("go", args...)
}
// Runs the tests and builds the coverage html file from coverage output
func (Test) Coverage() {
mg.Deps(initVars)
mg.Deps(Test.Unit)
runAndStreamOutput("go", "tool", "cover", "-html=cover.out", "-o", "cover.html")
}
// Runs the integration tests
func (Test) Integration() {
mg.Deps(initVars)
// We run everything sequentially and not in parallel to prevent issues with real test databases
runAndStreamOutput("go", "test", Goflags[0], "-p", "1", PACKAGE+"/pkg/integrations")
}
type Check mg.Namespace
// Checks if the swagger docs need to be re-generated from the code annotations
func (Check) GotSwag() {
mg.Deps(initVars)
// The check is pretty cheaply done: We take the hash of the swagger.json file, generate the docs,
// hash the file again and compare the two hashes to see if anything changed. If that's the case,
// regenerating the docs is necessary.
// swag is not capable of just outputting the generated docs to stdout, therefore we need to do it this way.
// Another drawback of this is obviously it will only work once - we're not resetting the newly generated
// docs after the check. This behaviour is good enough for ci though.
oldHash, err := calculateSha256FileHash(RootPath + "/pkg/swagger/swagger.json")
if err != nil {
fmt.Printf("Error getting old hash of the swagger docs: %s", err)
os.Exit(1)
}
DoTheSwag()
newHash, err := calculateSha256FileHash(RootPath + "/pkg/swagger/swagger.json")
if err != nil {
fmt.Printf("Error getting new hash of the swagger docs: %s", err)
os.Exit(1)
}
if oldHash != newHash {
fmt.Println("Swagger docs are not up to date.")
fmt.Println("Please run 'mage do-the-swag' and commit the result.")
os.Exit(1)
}
}
func checkGolangCiLintInstalled() {
mg.Deps(initVars)
if err := exec.Command("golangci-lint").Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
fmt.Println("Please manually install golangci-lint by running")
fmt.Println("curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.31.0")
os.Exit(1)
}
}
func (Check) Golangci() {
checkGolangCiLintInstalled()
runAndStreamOutput("golangci-lint", "run")
}
func (Check) GolangciFix() {
checkGolangCiLintInstalled()
runAndStreamOutput("golangci-lint", "run", "--fix")
}
// Runs fmt-check, lint, got-swag, misspell-check, ineffasign-check, gocyclo-check, static-check, gosec-check, goconst-check all in parallel
func (Check) All() {
mg.Deps(initVars)
mg.Deps(
Check.Golangci,
Check.GotSwag,
)
}
type Build mg.Namespace
// Cleans all build, executable and bindata files
func (Build) Clean() error {
mg.Deps(initVars)
if err := exec.Command("go", "clean", "./...").Run(); err != nil {
return err
}
if err := os.Remove(Executable); err != nil && !os.IsNotExist(err) {
return err
}
if err := os.RemoveAll(DIST); err != nil && !os.IsNotExist(err) {
return err
}
if err := os.RemoveAll(BinLocation); err != nil && !os.IsNotExist(err) {
return err
}
return nil
}
// Generates static content into the final binary
func (Build) Generate() {
mg.Deps(initVars)
runAndStreamOutput("go", "generate", PACKAGE+"/pkg/static")
}
// Builds a vikunja binary, ready to run
func (Build) Build() {
mg.Deps(initVars)
mg.Deps(Build.Generate)
runAndStreamOutput("go", "build", Goflags[0], "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
}
type Release mg.Namespace
// Runs all steps in the right order to create release packages for various platforms
func (Release) Release(ctx context.Context) error {
mg.Deps(initVars)
mg.Deps(Build.Generate, Release.Dirs)
mg.Deps(Release.Windows, Release.Linux, Release.Darwin)
// Run compiling in parallel to speed it up
errs, _ := errgroup.WithContext(ctx)
errs.Go((Release{}).Windows)
errs.Go((Release{}).Linux)
errs.Go((Release{}).Darwin)
if err := errs.Wait(); err != nil {
return err
}
if err := (Release{}).Compress(ctx); err != nil {
return err
}
if err := (Release{}).Copy(); err != nil {
return err
}
if err := (Release{}).Check(); err != nil {
return err
}
if err := (Release{}).OsPackage(); err != nil {
return err
}
if err := (Release{}).Zip(); err != nil {
return err
}
return nil
}
// Creates all directories needed to release vikunja
func (Release) Dirs() error {
for _, d := range []string{"binaries", "release", "zip"} {
if err := os.MkdirAll(RootPath+"/"+DIST+"/"+d, 0755); err != nil {
return err
}
}
return nil
}
func runXgo(targets string) error {
mg.Deps(initVars)
checkAndInstallGoTool("xgo", "src.techknowlogick.com/xgo")
extraLdflags := `-linkmode external -extldflags "-static" `
// See https://github.com/techknowlogick/xgo/issues/79
if strings.HasPrefix(targets, "darwin") {
extraLdflags = ""
}
runAndStreamOutput("xgo",
"-dest", RootPath+"/"+DIST+"/binaries",
"-tags", "netgo "+Tags,
"-ldflags", extraLdflags+Ldflags,
"-targets", targets,
"-out", Executable+"-"+Version,
RootPath)
if os.Getenv("DRONE_WORKSPACE") != "" {
return filepath.Walk("/build/", func(path string, info os.FileInfo, err error) error {
// Skip directories
if info.IsDir() {
return nil
}
return moveFile(path, RootPath+"/"+DIST+"/binaries/"+info.Name())
})
}
return nil
}
// Builds binaries for windows
func (Release) Windows() error {
return runXgo("windows/*")
}
// Builds binaries for linux
func (Release) Linux() error {
return runXgo("linux/*")
}
// Builds binaries for darwin
func (Release) Darwin() error {
return runXgo("darwin/*")
}
// Compresses the built binaries in dist/binaries/ to reduce their filesize
func (Release) Compress(ctx context.Context) error {
// $(foreach file,$(filter-out $(wildcard $(wildcard $(DIST)/binaries/$(EXECUTABLE)-*mips*)),$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*)), upx -9 $(file);)
errs, _ := errgroup.WithContext(ctx)
filepath.Walk(RootPath+"/"+DIST+"/binaries/", func(path string, info os.FileInfo, err error) error {
// Only executable files
if !strings.Contains(info.Name(), Executable) {
return nil
}
// No mips or s390x for you today
if strings.Contains(info.Name(), "mips") || strings.Contains(info.Name(), "s390x") {
return nil
}
// Runs compressing in parallel since upx is single-threaded
errs.Go(func() error {
runAndStreamOutput("chmod", "+x", path) // Make sure all binaries are executable. Sometimes the CI does weired things and they're not.
runAndStreamOutput("upx", "-9", path)
return nil
})
return nil
})
return errs.Wait()
}
// Copies all built binaries to dist/release/ in preparation for creating the os packages
func (Release) Copy() error {
return filepath.Walk(RootPath+"/"+DIST+"/binaries/", func(path string, info os.FileInfo, err error) error {
// Only executable files
if !strings.Contains(info.Name(), Executable) {
return nil
}
return copyFile(path, RootPath+"/"+DIST+"/release/"+info.Name())
})
}
// Creates sha256 checksum files for each binary in dist/release/
func (Release) Check() error {
p := RootPath + "/" + DIST + "/release/"
return filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
f, err := os.Create(p + info.Name() + ".sha256")
if err != nil {
return err
}
hash, err := calculateSha256FileHash(path)
if err != nil {
return err
}
_, err = f.WriteString(hash + " " + info.Name())
if err != nil {
return err
}
return f.Close()
})
}
// Creates a folder for each
func (Release) OsPackage() error {
p := RootPath + "/" + DIST + "/release/"
// We first put all files in a map to then iterate over it since the walk function would otherwise also iterate
// over the newly created files, creating some kind of endless loop.
bins := make(map[string]os.FileInfo)
if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
if strings.Contains(info.Name(), ".sha256") || info.IsDir() {
return nil
}
bins[path] = info
return nil
}); err != nil {
return err
}
for path, info := range bins {
folder := p + info.Name() + "-full/"
if err := os.Mkdir(folder, 0755); err != nil {
return err
}
if err := moveFile(p+info.Name()+".sha256", folder+info.Name()+".sha256"); err != nil {
return err
}
if err := moveFile(path, folder+info.Name()); err != nil {
return err
}
if err := copyFile(RootPath+"/config.yml.sample", folder+"config.yml.sample"); err != nil {
return err
}
if err := copyFile(RootPath+"/LICENSE", folder+"LICENSE"); err != nil {
return err
}
}
return nil
}
// Creates a zip file from all os-package folders in dist/release
func (Release) Zip() error {
p := RootPath + "/" + DIST + "/release/"
if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() || info.Name() == "release" {
return nil
}
fmt.Printf("Zipping %s...\n", info.Name())
c := exec.Command("zip", "-r", RootPath+"/"+DIST+"/zip/"+info.Name(), ".", "-i", "*")
c.Dir = path
out, err := c.Output()
fmt.Print(string(out))
return err
}); err != nil {
return err
}
return nil
}
// Creates a debian repo structure
func (Release) Reprepro() {
mg.Deps(setVersion, setBinLocation)
runAndStreamOutput("reprepro_expect", "debian", "includedeb", "strech", RootPath+"/"+DIST+"/os-packages/"+Executable+"_"+strings.ReplaceAll(VersionNumber, "v0", "0")+"_amd64.deb")
}
// Creates deb, rpm and apk packages
func (Release) Packages() error {
mg.Deps(initVars)
var err error
binpath := "nfpm"
err = exec.Command(binpath).Run()
if err != nil && strings.Contains(err.Error(), "executable file not found") {
binpath = "/nfpm"
err = exec.Command(binpath).Run()
}
if err != nil && strings.Contains(err.Error(), "executable file not found") {
fmt.Println("Please manually install nfpm by running")
fmt.Println("curl -sfL https://install.goreleaser.com/github.com/goreleaser/nfpm.sh | sh -s -- -b $(go env GOPATH)/bin")
os.Exit(1)
}
// Because nfpm does not support templating, we replace the values in the config file and restore it after running
nfpmConfigPath := RootPath + "/nfpm.yaml"
nfpmconfig, err := ioutil.ReadFile(nfpmConfigPath)
if err != nil {
return err
}
fixedConfig := strings.ReplaceAll(string(nfpmconfig), "<version>", VersionNumber)
fixedConfig = strings.ReplaceAll(fixedConfig, "<binlocation>", BinLocation)
if err := ioutil.WriteFile(nfpmConfigPath, []byte(fixedConfig), 0); err != nil {
return err
}
releasePath := RootPath + "/" + DIST + "/os-packages/"
if err := os.MkdirAll(releasePath, 0755); err != nil {
return err
}
runAndStreamOutput(binpath, "pkg", "--packager", "deb", "--target", releasePath)
runAndStreamOutput(binpath, "pkg", "--packager", "rpm", "--target", releasePath)
runAndStreamOutput(binpath, "pkg", "--packager", "apk", "--target", releasePath)
return ioutil.WriteFile(nfpmConfigPath, nfpmconfig, 0)
}
type Dev mg.Namespace
// Creates a new bare db migration skeleton in pkg/migration with the current date
func (Dev) CreateMigration() error {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter the name of the struct: ")
str, _ := reader.ReadString('\n')
str = strings.Trim(str, "\n")
date := time.Now().Format("20060102150405")
migration := `// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
type ` + str + date + ` struct {
}
func (` + str + date + `) TableName() string {
return "` + str + `"
}
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "` + date + `",
Description: "",
Migrate: func(tx *xorm.Engine) error {
return tx.Sync2(` + str + date + `{})
},
Rollback: func(tx *xorm.Engine) error {
return nil
},
})
}
`
f, err := os.Create(RootPath + "/pkg/migration/" + date + ".go")
defer f.Close()
if err != nil {
return err
}
_, err = f.WriteString(migration)
return err
}
type configOption struct {
key string
description string
defaultValue string
children []*configOption
}
func parseYamlConfigNode(node *yaml.Node) (config *configOption) {
config = &configOption{
key: node.Value,
description: strings.ReplaceAll(node.HeadComment, "# ", ""),
}
valMap := make(map[string]*configOption)
var lastOption *configOption
for i, n2 := range node.Content {
coo := &configOption{
key: n2.Value,
description: strings.ReplaceAll(n2.HeadComment, "# ", ""),
}
// If there's a key in valMap for the current key we should use that to append etc
// Else we just create a new configobject
co, exists := valMap[n2.Value]
if exists {
co.description = coo.description
} else {
valMap[n2.Value] = coo
config.children = append(config.children, coo)
}
// fmt.Println(i, coo.key, coo.description, n2.Value)
if i%2 == 0 {
lastOption = coo
continue
} else {
lastOption.defaultValue = n2.Value
}
if i-1 >= 0 && i-1 <= len(node.Content) && node.Content[i-1].Value != "" {
coo.defaultValue = n2.Value
coo.key = node.Content[i-1].Value
}
if len(n2.Content) > 0 {
for _, n := range n2.Content {
coo.children = append(coo.children, parseYamlConfigNode(n))
}
}
}
return config
}
func printConfig(config []*configOption, level int) (rendered string) {
// Keep track of what we already printed to prevent printing things twice
printed := make(map[string]bool)
for _, option := range config {
if option.key != "" {
// Filter out all config objects where the default value == key
// Yaml is weired: It gives you a slice with an entry each for the key and their value.
if printed[option.key] {
continue
}
if level == 0 {
rendered += "---\n\n"
}
rendered += "#"
for i := 0; i <= level; i++ {
rendered += "#"
}
rendered += " " + option.key + "\n\n"
if option.description != "" {
rendered += option.description + "\n\n"
}
// Top level config values never have a default value
if level > 0 {
rendered += "Default: `" + option.defaultValue
if option.defaultValue == "" {
rendered += "<empty>"
}
rendered += "`\n"
}
}
printed[option.key] = true
rendered += "\n" + printConfig(option.children, level+1)
}
return
}
const (
configDocPath = `docs/content/doc/setup/config.md`
configInjectComment = `<!-- Generated config will be injected here -->`
)
// Generates the error docs from a commented config.yml.sample file in the repo root.
func GenerateDocs() error {
config, err := ioutil.ReadFile("config.yml.sample")
if err != nil {
return err
}
var d yaml.Node
err = yaml.Unmarshal(config, &d)
if err != nil {
return err
}
conf := []*configOption{}
for _, node := range d.Content {
for _, n := range node.Content {
co := parseYamlConfigNode(n)
conf = append(conf, co)
}
}
renderedConfig := printConfig(conf, 0)
// Rebuild the config
file, err := os.OpenFile(configDocPath, os.O_RDWR, 0)
if err != nil {
return err
}
defer file.Close()
// We read the config doc up until the marker, then stop and append our generated config
fullConfig := ""
scanner := bufio.NewScanner(file)
for scanner.Scan() {
t := scanner.Text()
fullConfig += t + "\n"
if t == configInjectComment {
break
}
}
if err := scanner.Err(); err != nil {
return err
}
fullConfig += "\n" + renderedConfig
// We write the full file to prevent old content leftovers at the end
// I know, there are probably better ways to do this.
if err := ioutil.WriteFile(configDocPath, []byte(fullConfig), 0); err != nil {
return err
}
return nil
}

24
main.go
View File

@ -1,18 +1,18 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package main

View File

@ -1,18 +0,0 @@
name: "vikunja"
arch: "amd64"
platform: "linux"
version: "<version>"
description: "Vikunja is an open-source todo application, written in Go. It lets you create lists,tasks and share them via teams or directly between users."
maintainer: "Vikunja Maintainers <maintainers@vikunja.io>"
homepage: "https://vikunja.io"
section: "default"
priority: "extra"
license: "GPLv3"
files:
<binlocation>: /opt/vikunja/vikunja
config_files:
./config.yml.sample: /etc/vikunja/config.yml
symlinks:
/opt/vikunja/vikunja: /usr/local/bin/vikunja
scripts:
postinstall: ./build/after-install.sh

View File

@ -1,34 +1,30 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package caldav
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/utils"
"fmt"
"strconv"
"time"
)
// DateFormat is the caldav date format
// DateFormat ist the caldav date format
const DateFormat = `20060102T150405`
// Event holds a single caldav event
@ -38,37 +34,37 @@ type Event struct {
UID string
Alarms []Alarm
Timestamp time.Time
Start time.Time
End time.Time
TimestampUnix int64
StartUnix int64
EndUnix int64
}
// Todo holds a single VTODO
type Todo struct {
// Required
Timestamp time.Time
UID string
TimestampUnix int64
UID string
// Optional
Summary string
Description string
Completed time.Time
Organizer *user.User
Priority int64 // 0-9, 1 is highest
RelatedToUID string
Summary string
Description string
CompletedUnix int64
Organizer *models.User
Priority int64 // 0-9, 1 is highest
RelatedToUID string
Start time.Time
End time.Time
DueDate time.Time
Duration time.Duration
StartUnix int64
EndUnix int64
DueDateUnix int64
Duration time.Duration
Created time.Time
Updated time.Time // last-mod
CreatedUnix int64
UpdatedUnix int64 // last-mod
}
// Alarm holds infos about an alarm from a caldav event
type Alarm struct {
Time time.Time
TimeUnix int64
Description string
}
@ -90,23 +86,17 @@ PRODID:-//` + config.ProdID + `//EN`
for _, e := range events {
if e.UID == "" {
e.UID = makeCalDavTimeFromTimeStamp(e.Timestamp) + utils.Sha256(e.Summary)
}
formattedDescription := ""
if e.Description != "" {
re := regexp.MustCompile(`\r?\n`)
formattedDescription = re.ReplaceAllString(e.Description, "\\n")
e.UID = makeCalDavTimeFromUnixTime(e.TimestampUnix) + utils.Sha256(e.Summary)
}
caldavevents += `
BEGIN:VEVENT
UID:` + e.UID + `
SUMMARY:` + e.Summary + `
DESCRIPTION:` + formattedDescription + `
DTSTAMP:` + makeCalDavTimeFromTimeStamp(e.Timestamp) + `
DTSTART:` + makeCalDavTimeFromTimeStamp(e.Start) + `
DTEND:` + makeCalDavTimeFromTimeStamp(e.End)
DESCRIPTION:` + e.Description + `
DTSTAMP:` + makeCalDavTimeFromUnixTime(e.TimestampUnix) + `
DTSTART:` + makeCalDavTimeFromUnixTime(e.StartUnix) + `
DTEND:` + makeCalDavTimeFromUnixTime(e.EndUnix)
for _, a := range e.Alarms {
if a.Description == "" {
@ -115,7 +105,7 @@ DTEND:` + makeCalDavTimeFromTimeStamp(e.End)
caldavevents += `
BEGIN:VALARM
TRIGGER:` + calcAlarmDateFromReminder(e.Start, a.Time) + `
TRIGGER:` + calcAlarmDateFromReminder(e.StartUnix, a.TimeUnix) + `
ACTION:DISPLAY
DESCRIPTION:` + a.Description + `
END:VALARM`
@ -141,33 +131,30 @@ PRODID:-//` + config.ProdID + `//EN`
for _, t := range todos {
if t.UID == "" {
t.UID = makeCalDavTimeFromTimeStamp(t.Timestamp) + utils.Sha256(t.Summary)
t.UID = makeCalDavTimeFromUnixTime(t.TimestampUnix) + utils.Sha256(t.Summary)
}
caldavtodos += `
BEGIN:VTODO
UID:` + t.UID + `
DTSTAMP:` + makeCalDavTimeFromTimeStamp(t.Timestamp) + `
DTSTAMP:` + makeCalDavTimeFromUnixTime(t.TimestampUnix) + `
SUMMARY:` + t.Summary
if t.Start.Unix() > 0 {
if t.StartUnix != 0 {
caldavtodos += `
DTSTART: ` + makeCalDavTimeFromTimeStamp(t.Start)
DTSTART: ` + makeCalDavTimeFromUnixTime(t.StartUnix)
}
if t.End.Unix() > 0 {
if t.EndUnix != 0 {
caldavtodos += `
DTEND: ` + makeCalDavTimeFromTimeStamp(t.End)
DTEND: ` + makeCalDavTimeFromUnixTime(t.EndUnix)
}
if t.Description != "" {
re := regexp.MustCompile(`\r?\n`)
formattedDescription := re.ReplaceAllString(t.Description, "\\n")
caldavtodos += `
DESCRIPTION:` + formattedDescription
DESCRIPTION:` + t.Description
}
if t.Completed.Unix() > 0 {
if t.CompletedUnix != 0 {
caldavtodos += `
COMPLETED:` + makeCalDavTimeFromTimeStamp(t.Completed) + `
STATUS:COMPLETED`
COMPLETED: ` + makeCalDavTimeFromUnixTime(t.CompletedUnix)
}
if t.Organizer != nil {
caldavtodos += `
@ -179,14 +166,14 @@ ORGANIZER;CN=:` + t.Organizer.Username
RELATED-TO:` + t.RelatedToUID
}
if t.DueDate.Unix() > 0 {
if t.DueDateUnix != 0 {
caldavtodos += `
DUE:` + makeCalDavTimeFromTimeStamp(t.DueDate)
DUE:` + makeCalDavTimeFromUnixTime(t.DueDateUnix)
}
if t.Created.Unix() > 0 {
if t.CreatedUnix != 0 {
caldavtodos += `
CREATED:` + makeCalDavTimeFromTimeStamp(t.Created)
CREATED:` + makeCalDavTimeFromUnixTime(t.CreatedUnix)
}
if t.Duration != 0 {
@ -200,7 +187,7 @@ PRIORITY:` + strconv.Itoa(int(t.Priority))
}
caldavtodos += `
LAST-MODIFIED:` + makeCalDavTimeFromTimeStamp(t.Updated)
LAST-MODIFIED:` + makeCalDavTimeFromUnixTime(t.UpdatedUnix)
caldavtodos += `
END:VTODO`
@ -212,19 +199,21 @@ END:VCALENDAR` // Need a line break
return
}
func makeCalDavTimeFromTimeStamp(ts time.Time) (caldavtime string) {
return ts.In(config.GetTimeZone()).Format(DateFormat)
func makeCalDavTimeFromUnixTime(unixtime int64) (caldavtime string) {
tz, _ := time.LoadLocation("UTC")
tm := time.Unix(unixtime, 0).In(tz)
return tm.Format(DateFormat)
}
func calcAlarmDateFromReminder(eventStart, reminder time.Time) (alarmTime string) {
diff := reminder.Sub(eventStart)
diffStr := strings.ToUpper(diff.String())
if diff < 0 {
func calcAlarmDateFromReminder(eventStartUnix, reminderUnix int64) (alarmTime string) {
if eventStartUnix > reminderUnix {
alarmTime += `-`
// We append the - at the beginning of the caldav flag, that would get in the way if the minutes
// themselves are also containing it
diffStr = diffStr[1:]
}
alarmTime += `PT` + diffStr
alarmTime += `PT`
diff := eventStartUnix - reminderUnix
if diff < 0 { // Make it positive
diff = diff * -1
}
alarmTime += strconv.Itoa(int(diff/60)) + "M"
return
}

View File

@ -1,28 +1,22 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package caldav
import (
"testing"
"time"
"code.vikunja.io/api/pkg/config"
"github.com/stretchr/testify/assert"
)
import "testing"
func TestParseEvents(t *testing.T) {
type args struct {
@ -43,26 +37,26 @@ func TestParseEvents(t *testing.T) {
},
events: []*Event{
{
Summary: "Event #1",
Description: "Lorem Ipsum",
UID: "randommduid",
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
Start: time.Unix(1543626724, 0).In(config.GetTimeZone()),
End: time.Unix(1543627824, 0).In(config.GetTimeZone()),
Summary: "Event #1",
Description: "Lorem Ipsum",
UID: "randommduid",
TimestampUnix: 1543626724,
StartUnix: 1543626724,
EndUnix: 1543627824,
},
{
Summary: "Event #2",
UID: "randommduidd",
Timestamp: time.Unix(1543726724, 0).In(config.GetTimeZone()),
Start: time.Unix(1543726724, 0).In(config.GetTimeZone()),
End: time.Unix(1543738724, 0).In(config.GetTimeZone()),
Summary: "Event #2",
UID: "randommduidd",
TimestampUnix: 1543726724,
StartUnix: 1543726724,
EndUnix: 1543738724,
},
{
Summary: "Event #3 with empty uid",
UID: "20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83",
Timestamp: time.Unix(1543726824, 0).In(config.GetTimeZone()),
Start: time.Unix(1543726824, 0).In(config.GetTimeZone()),
End: time.Unix(1543727000, 0).In(config.GetTimeZone()),
Summary: "Event #3 with empty uid",
UID: "20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83",
TimestampUnix: 1543726824,
StartUnix: 1543726824,
EndUnix: 1543727000,
},
},
},
@ -107,47 +101,47 @@ END:VCALENDAR`,
},
events: []*Event{
{
Summary: "Event #1",
Description: "Lorem Ipsum",
UID: "randommduid",
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
Start: time.Unix(1543626724, 0).In(config.GetTimeZone()),
End: time.Unix(1543627824, 0).In(config.GetTimeZone()),
Summary: "Event #1",
Description: "Lorem Ipsum",
UID: "randommduid",
TimestampUnix: 1543626724,
StartUnix: 1543626724,
EndUnix: 1543627824,
Alarms: []Alarm{
{Time: time.Unix(1543626524, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543626224, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543626024, 0)},
{TimeUnix: 1543626524},
{TimeUnix: 1543626224},
{TimeUnix: 1543626024},
},
},
{
Summary: "Event #2",
UID: "randommduidd",
Timestamp: time.Unix(1543726724, 0).In(config.GetTimeZone()),
Start: time.Unix(1543726724, 0).In(config.GetTimeZone()),
End: time.Unix(1543738724, 0).In(config.GetTimeZone()),
Summary: "Event #2",
UID: "randommduidd",
TimestampUnix: 1543726724,
StartUnix: 1543726724,
EndUnix: 1543738724,
Alarms: []Alarm{
{Time: time.Unix(1543626524, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543626224, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543626024, 0).In(config.GetTimeZone())},
{TimeUnix: 1543626524},
{TimeUnix: 1543626224},
{TimeUnix: 1543626024},
},
},
{
Summary: "Event #3 with empty uid",
Timestamp: time.Unix(1543726824, 0).In(config.GetTimeZone()),
Start: time.Unix(1543726824, 0).In(config.GetTimeZone()),
End: time.Unix(1543727000, 0).In(config.GetTimeZone()),
Summary: "Event #3 with empty uid",
TimestampUnix: 1543726824,
StartUnix: 1543726824,
EndUnix: 1543727000,
Alarms: []Alarm{
{Time: time.Unix(1543626524, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543626224, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543626024, 0).In(config.GetTimeZone())},
{Time: time.Unix(1543826824, 0).In(config.GetTimeZone())},
{TimeUnix: 1543626524},
{TimeUnix: 1543626224},
{TimeUnix: 1543626024},
{TimeUnix: 1543826824},
},
},
{
Summary: "Event #4 without any",
Timestamp: time.Unix(1543726824, 0),
Start: time.Unix(1543726824, 0),
End: time.Unix(1543727000, 0),
Summary: "Event #4 without any",
TimestampUnix: 1543726824,
StartUnix: 1543726824,
EndUnix: 1543727000,
},
},
},
@ -165,17 +159,17 @@ DTSTAMP:20181201T011204
DTSTART:20181201T011204
DTEND:20181201T013024
BEGIN:VALARM
TRIGGER:-PT3M20S
TRIGGER:-PT3M
ACTION:DISPLAY
DESCRIPTION:Event #1
END:VALARM
BEGIN:VALARM
TRIGGER:-PT8M20S
TRIGGER:-PT8M
ACTION:DISPLAY
DESCRIPTION:Event #1
END:VALARM
BEGIN:VALARM
TRIGGER:-PT11M40S
TRIGGER:-PT11M
ACTION:DISPLAY
DESCRIPTION:Event #1
END:VALARM
@ -188,17 +182,17 @@ DTSTAMP:20181202T045844
DTSTART:20181202T045844
DTEND:20181202T081844
BEGIN:VALARM
TRIGGER:-PT27H50M0S
TRIGGER:-PT1670M
ACTION:DISPLAY
DESCRIPTION:Event #2
END:VALARM
BEGIN:VALARM
TRIGGER:-PT27H55M0S
TRIGGER:-PT1675M
ACTION:DISPLAY
DESCRIPTION:Event #2
END:VALARM
BEGIN:VALARM
TRIGGER:-PT27H58M20S
TRIGGER:-PT1678M
ACTION:DISPLAY
DESCRIPTION:Event #2
END:VALARM
@ -211,22 +205,22 @@ DTSTAMP:20181202T050024
DTSTART:20181202T050024
DTEND:20181202T050320
BEGIN:VALARM
TRIGGER:-PT27H51M40S
TRIGGER:-PT1671M
ACTION:DISPLAY
DESCRIPTION:Event #3 with empty uid
END:VALARM
BEGIN:VALARM
TRIGGER:-PT27H56M40S
TRIGGER:-PT1676M
ACTION:DISPLAY
DESCRIPTION:Event #3 with empty uid
END:VALARM
BEGIN:VALARM
TRIGGER:-PT28H0M0S
TRIGGER:-PT1680M
ACTION:DISPLAY
DESCRIPTION:Event #3 with empty uid
END:VALARM
BEGIN:VALARM
TRIGGER:PT27H46M40S
TRIGGER:PT1666M
ACTION:DISPLAY
DESCRIPTION:Event #3 with empty uid
END:VALARM
@ -239,133 +233,14 @@ DTSTAMP:20181202T050024
DTSTART:20181202T050024
DTEND:20181202T050320
END:VEVENT
END:VCALENDAR`,
},
{
name: "Test caldavparsing with multiline description",
args: args{
config: &Config{
Name: "test",
ProdID: "RandomProdID which is not random",
},
events: []*Event{
{
Summary: "Event #1",
Description: `Lorem Ipsum
Dolor sit amet`,
UID: "randommduid",
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
Start: time.Unix(1543626724, 0).In(config.GetTimeZone()),
End: time.Unix(1543627824, 0).In(config.GetTimeZone()),
},
},
},
wantCaldavevents: `BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
X-PUBLISHED-TTL:PT4H
X-WR-CALNAME:test
PRODID:-//RandomProdID which is not random//EN
BEGIN:VEVENT
UID:randommduid
SUMMARY:Event #1
DESCRIPTION:Lorem Ipsum\nDolor sit amet
DTSTAMP:20181201T011204
DTSTART:20181201T011204
DTEND:20181201T013024
END:VEVENT
END:VCALENDAR`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCaldavevents := ParseEvents(tt.args.config, tt.args.events)
assert.Equal(t, gotCaldavevents, tt.wantCaldavevents)
})
}
}
func TestParseTodos(t *testing.T) {
type args struct {
config *Config
todos []*Todo
}
tests := []struct {
name string
args args
wantCaldavtasks string
}{
{
name: "Test caldavparsing with multiline description",
args: args{
config: &Config{
Name: "test",
ProdID: "RandomProdID which is not random",
},
todos: []*Todo{
{
Summary: "Todo #1",
Description: `Lorem Ipsum
Dolor sit amet`,
UID: "randommduid",
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
},
},
},
wantCaldavtasks: `BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
X-PUBLISHED-TTL:PT4H
X-WR-CALNAME:test
PRODID:-//RandomProdID which is not random//EN
BEGIN:VTODO
UID:randommduid
DTSTAMP:20181201T011204
SUMMARY:Todo #1
DESCRIPTION:Lorem Ipsum\nDolor sit amet
LAST-MODIFIED:00010101T000000
END:VTODO
END:VCALENDAR`,
},
{
name: "Test caldavparsing with completed task",
args: args{
config: &Config{
Name: "test",
ProdID: "RandomProdID which is not random",
},
todos: []*Todo{
{
Summary: "Todo #1",
Description: "Lorem Ipsum",
UID: "randommduid",
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
Completed: time.Unix(1543627824, 0).In(config.GetTimeZone()),
},
},
},
wantCaldavtasks: `BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
X-PUBLISHED-TTL:PT4H
X-WR-CALNAME:test
PRODID:-//RandomProdID which is not random//EN
BEGIN:VTODO
UID:randommduid
DTSTAMP:20181201T011204
SUMMARY:Todo #1
DESCRIPTION:Lorem Ipsum
COMPLETED:20181201T013024
STATUS:COMPLETED
LAST-MODIFIED:00010101T000000
END:VTODO
END:VCALENDAR`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotCaldavtasks := ParseTodos(tt.args.config, tt.args.todos)
assert.Equal(t, gotCaldavtasks, tt.wantCaldavtasks)
if gotCaldavevents := ParseEvents(tt.args.config, tt.args.events); gotCaldavevents != tt.wantCaldavevents {
t.Errorf("ParseEvents() = %v, want %v", gotCaldavevents, tt.wantCaldavevents)
}
})
}
}

View File

@ -1,28 +1,40 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2019 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/migration"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/red"
"fmt"
"os"
"github.com/spf13/cobra"
"os"
)
// Version sets the version to be printed to the user. Gets overwritten by "make release" or "make build" with last git commit or tag.
var Version = "0.1"
func init() {
cobra.OnInitialize(initialize)
}
var rootCmd = &cobra.Command{
Use: "vikunja",
Short: "Vikunja is the to-do app to organize your life.",
@ -35,8 +47,7 @@ alpine areas of the Andes and a relative of the llama.
Vikunja is a self-hosted To-Do list application with a web app and mobile apps for all platforms. It is licensed under the GPLv3.
Find more info at vikunja.io.`,
PreRun: webCmd.PreRun,
Run: webCmd.Run,
Run: webCmd.Run,
}
// Execute starts the application
@ -46,3 +57,27 @@ func Execute() {
os.Exit(1)
}
}
// Initializes all kinds of things in the right order
func initialize() {
// Init the config
config.InitConfig()
// Init redis
red.InitRedis()
// Set logger
log.InitLogger()
// Run the migrations
migration.Migrate(nil)
// Set Engine
err := models.SetEngine()
if err != nil {
log.Log.Fatal(err.Error())
}
// Start the mail daemon
mail.StartMailDaemon()
}

View File

@ -1,44 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"time"
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/modules/dump"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(dumpCmd)
}
var dumpCmd = &cobra.Command{
Use: "dump",
Short: "Dump all vikunja data into a zip file. Includes config, files and db.",
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Run: func(cmd *cobra.Command, args []string) {
filename := "vikunja-dump_" + time.Now().Format("2006-01-02_15-03-05") + ".zip"
if err := dump.Dump(filename); err != nil {
log.Critical(err.Error())
}
},
}

View File

@ -1,23 +1,22 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2019 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/migration"
"github.com/spf13/cobra"
)
@ -25,7 +24,7 @@ import (
func init() {
migrateCmd.AddCommand(migrateListCmd)
migrationRollbackCmd.Flags().StringVarP(&rollbackUntilFlag, "name", "n", "", "The id of the migration you want to roll back until.")
_ = migrationRollbackCmd.MarkFlagRequired("name")
migrationRollbackCmd.MarkFlagRequired("name")
migrateCmd.AddCommand(migrationRollbackCmd)
rootCmd.AddCommand(migrateCmd)
}
@ -36,9 +35,6 @@ func init() {
var migrateCmd = &cobra.Command{
Use: "migrate",
Short: "Run all database migrations which didn't already run.",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
initialize.LightInit()
},
Run: func(cmd *cobra.Command, args []string) {
migration.Migrate(nil)
},

View File

@ -1,42 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/modules/dump"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(restoreCmd)
}
var restoreCmd = &cobra.Command{
Use: "restore [filename]",
Short: "Restores all vikunja data from a vikunja dump.",
Args: cobra.ExactArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Run: func(cmd *cobra.Command, args []string) {
if err := dump.Restore(args[0]); err != nil {
log.Critical(err.Error())
}
},
}

View File

@ -1,49 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(testmailCmd)
}
var testmailCmd = &cobra.Command{
Use: "testmail [email]",
Short: "Send a test mail using the configured smtp connection",
Args: cobra.ExactArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
initialize.LightInit()
// Start the mail daemon
mail.StartMailDaemon()
},
Run: func(cmd *cobra.Command, args []string) {
log.Info("Sending testmail...")
email := args[0]
if err := mail.SendTestMail(email); err != nil {
log.Errorf("Error sending test mail: %s", err.Error())
return
}
log.Info("Testmail successfully sent.")
},
}

View File

@ -1,255 +0,0 @@
// Copyright 2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"fmt"
"os"
"strconv"
"strings"
"time"
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
)
var (
userFlagUsername string
userFlagEmail string
userFlagPassword string
userFlagAvatar = "default"
userFlagResetPasswordDirectly bool
userFlagEnableUser bool
userFlagDisableUser bool
)
func init() {
// User create flags
userCreateCmd.Flags().StringVarP(&userFlagUsername, "username", "u", "", "The username of the new user.")
_ = userCreateCmd.MarkFlagRequired("username")
userCreateCmd.Flags().StringVarP(&userFlagEmail, "email", "e", "", "The email address of the new user.")
_ = userCreateCmd.MarkFlagRequired("email")
userCreateCmd.Flags().StringVarP(&userFlagPassword, "password", "p", "", "The password of the new user. You will be asked to enter it if not provided through the flag.")
userCreateCmd.Flags().StringVarP(&userFlagAvatar, "avatar-provider", "a", "", "The avatar provider of the new user. Optional.")
// User update flags
userUpdateCmd.Flags().StringVarP(&userFlagUsername, "username", "u", "", "The new username of the user.")
userUpdateCmd.Flags().StringVarP(&userFlagEmail, "email", "e", "", "The new email address of the user.")
userUpdateCmd.Flags().StringVarP(&userFlagAvatar, "avatar-provider", "a", "", "The new avatar provider of the new user.")
// Reset PW flags
userResetPasswordCmd.Flags().BoolVarP(&userFlagResetPasswordDirectly, "direct", "d", false, "If provided, reset the password directly instead of sending the user a reset mail.")
userResetPasswordCmd.Flags().StringVarP(&userFlagPassword, "password", "p", "", "The new password of the user. Only used in combination with --direct. You will be asked to enter it if not provided through the flag.")
// Change status flags
userChangeEnabledCmd.Flags().BoolVarP(&userFlagDisableUser, "disable", "d", false, "Disable the user.")
userChangeEnabledCmd.Flags().BoolVarP(&userFlagEnableUser, "enable", "e", false, "Enable the user.")
userCmd.AddCommand(userListCmd, userCreateCmd, userUpdateCmd, userResetPasswordCmd, userChangeEnabledCmd)
rootCmd.AddCommand(userCmd)
}
func getPasswordFromFlagOrInput() (pw string) {
pw = userFlagPassword
if userFlagPassword == "" {
fmt.Print("Enter Password: ")
bytePW, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Fatalf("Error reading password: %s", err)
}
fmt.Printf("\nConfirm Password: ")
byteConfirmPW, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Fatalf("Error reading password: %s", err)
}
if string(bytePW) != string(byteConfirmPW) {
log.Critical("Passwords don't match!")
}
fmt.Printf("\n")
pw = strings.TrimSpace(string(bytePW))
}
return
}
func getUserFromArg(arg string) *user.User {
id, err := strconv.ParseInt(arg, 10, 64)
if err != nil {
log.Fatalf("Invalid user id: %s", err)
}
u, err := user.GetUserByID(id)
if err != nil {
log.Fatalf("Could not get user: %s", err)
}
return u
}
var userCmd = &cobra.Command{
Use: "user",
Short: "Manage users locally through the cli.",
}
var userListCmd = &cobra.Command{
Use: "list",
Short: "Shows a list of all users.",
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Run: func(cmd *cobra.Command, args []string) {
users, err := user.ListUsers("")
if err != nil {
log.Fatalf("Error getting users: %s", err)
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{
"ID",
"Username",
"Email",
"Active",
"Created",
"Updated",
})
for _, u := range users {
table.Append([]string{
strconv.FormatInt(u.ID, 10),
u.Username,
u.Email,
strconv.FormatBool(u.IsActive),
u.Created.Format(time.RFC3339),
u.Updated.Format(time.RFC3339),
})
}
table.Render()
},
}
var userCreateCmd = &cobra.Command{
Use: "create",
Short: "Create a new user.",
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Run: func(cmd *cobra.Command, args []string) {
u := &user.User{
Username: userFlagUsername,
Email: userFlagEmail,
Password: getPasswordFromFlagOrInput(),
}
newUser, err := user.CreateUser(u)
if err != nil {
log.Fatalf("Error creating new user: %s", err)
}
err = models.CreateNewNamespaceForUser(newUser)
if err != nil {
log.Fatalf("Error creating new namespace for user: %s", err)
}
fmt.Printf("\nUser was created successfully.\n")
},
}
var userUpdateCmd = &cobra.Command{
Use: "update [user id]",
Short: "Update an existing user.",
Args: cobra.ExactArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Run: func(cmd *cobra.Command, args []string) {
u := getUserFromArg(args[0])
if userFlagUsername != "" {
u.Username = userFlagUsername
}
if userFlagEmail != "" {
u.Email = userFlagEmail
}
if userFlagAvatar != "default" {
u.AvatarProvider = userFlagAvatar
}
_, err := user.UpdateUser(u)
if err != nil {
log.Fatalf("Error updating the user: %s", err)
}
fmt.Println("User updated successfully.")
},
}
var userResetPasswordCmd = &cobra.Command{
Use: "reset-password [user id]",
Short: "Reset a users password, either through mailing them a reset link or directly.",
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
u := getUserFromArg(args[0])
// By default we reset as usual, only with specific flag directly.
if userFlagResetPasswordDirectly {
err := user.UpdateUserPassword(u, getPasswordFromFlagOrInput())
if err != nil {
log.Fatalf("Could not update user password: %s", err)
}
fmt.Println("Password updated successfully.")
} else {
err := user.RequestUserPasswordResetToken(u)
if err != nil {
log.Fatalf("Could not send password reset email: %s", err)
}
fmt.Println("Password reset email sent successfully.")
}
},
}
var userChangeEnabledCmd = &cobra.Command{
Use: "change-status [user id]",
Short: "Enable or disable a user. Will toggle the current status if no flag (--enable or --disable) is provided.",
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
u := getUserFromArg(args[0])
if userFlagEnableUser {
u.IsActive = true
} else if userFlagDisableUser {
u.IsActive = false
} else {
u.IsActive = !u.IsActive
}
_, err := user.UpdateUser(u)
if err != nil {
log.Fatalf("Could not enable the user")
}
fmt.Printf("User status successfully changed, user is now active: %t.\n", u.IsActive)
},
}

View File

@ -1,26 +1,23 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2019 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"fmt"
"runtime"
"code.vikunja.io/api/pkg/version"
"github.com/spf13/cobra"
)
@ -32,7 +29,6 @@ var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of Vikunja",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Vikunja api version %s\n", version.Version)
fmt.Printf("Built with %s\n", runtime.Version())
fmt.Println("Vikunja api version " + Version)
},
}

View File

@ -1,34 +1,32 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2019 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package cmd
import (
"context"
"os"
"os/signal"
"time"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/routes"
"code.vikunja.io/api/pkg/swagger"
"code.vikunja.io/api/pkg/version"
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"os/signal"
"time"
)
func init() {
@ -38,23 +36,20 @@ func init() {
var webCmd = &cobra.Command{
Use: "web",
Short: "Starts the rest api web server",
PreRun: func(cmd *cobra.Command, args []string) {
initialize.FullInit()
},
Run: func(cmd *cobra.Command, args []string) {
// Version notification
log.Infof("Vikunja version %s", version.Version)
fmt.Printf("Vikunja version %s\n", Version)
// Additional swagger information
swagger.SwaggerInfo.Version = version.Version
swagger.SwaggerInfo.Version = Version
// Start the webserver
e := routes.NewEcho()
routes.RegisterRoutes(e)
// Start server
go func() {
if err := e.Start(config.ServiceInterface.GetString()); err != nil {
if err := e.Start(viper.GetString("service.interface")); err != nil {
e.Logger.Info("shutting down...")
}
}()
@ -66,7 +61,7 @@ var webCmd = &cobra.Command{
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
log.Infof("Shutting down...")
log.Log.Infof("Shutting down...")
if err := e.Shutdown(ctx); err != nil {
e.Logger.Fatal(err)
}

View File

@ -1,307 +1,93 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2018 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package config
import (
"code.vikunja.io/api/pkg/log"
"crypto/rand"
"fmt"
"log"
"github.com/spf13/viper"
"os"
"path"
"path/filepath"
"strings"
"time"
"4d63.com/tz"
"github.com/spf13/viper"
)
// Key is used as a config key
type Key string
// InitConfig initializes the config, sets defaults etc.
func InitConfig() {
// These constants hold all config value keys
const (
// #nosec
ServiceJWTSecret Key = `service.JWTSecret`
ServiceInterface Key = `service.interface`
ServiceFrontendurl Key = `service.frontendurl`
ServiceEnableCaldav Key = `service.enablecaldav`
ServiceRootpath Key = `service.rootpath`
ServiceMaxItemsPerPage Key = `service.maxitemsperpage`
ServiceEnableMetrics Key = `service.enablemetrics`
ServiceMotd Key = `service.motd`
ServiceEnableLinkSharing Key = `service.enablelinksharing`
ServiceEnableRegistration Key = `service.enableregistration`
ServiceEnableTaskAttachments Key = `service.enabletaskattachments`
ServiceTimeZone Key = `service.timezone`
ServiceEnableTaskComments Key = `service.enabletaskcomments`
ServiceEnableTotp Key = `service.enabletotp`
ServiceSentryDsn Key = `service.sentrydsn`
AuthLocalEnabled Key = `auth.local.enabled`
AuthOpenIDEnabled Key = `auth.openid.enabled`
AuthOpenIDRedirectURL Key = `auth.openid.redirecturl`
AuthOpenIDProviders Key = `auth.openid.providers`
LegalImprintURL Key = `legal.imprinturl`
LegalPrivacyURL Key = `legal.privacyurl`
DatabaseType Key = `database.type`
DatabaseHost Key = `database.host`
DatabaseUser Key = `database.user`
DatabasePassword Key = `database.password`
DatabaseDatabase Key = `database.database`
DatabasePath Key = `database.path`
DatabaseMaxOpenConnections Key = `database.maxopenconnections`
DatabaseMaxIdleConnections Key = `database.maxidleconnections`
DatabaseMaxConnectionLifetime Key = `database.maxconnectionlifetime`
DatabaseSslMode Key = `database.sslmode`
CacheEnabled Key = `cache.enabled`
CacheType Key = `cache.type`
CacheMaxElementSize Key = `cache.maxelementsize`
MailerEnabled Key = `mailer.enabled`
MailerHost Key = `mailer.host`
MailerPort Key = `mailer.port`
MailerUsername Key = `mailer.username`
MailerPassword Key = `mailer.password`
MailerSkipTLSVerify Key = `mailer.skiptlsverify`
MailerFromEmail Key = `mailer.fromemail`
MailerQueuelength Key = `mailer.queuelength`
MailerQueueTimeout Key = `mailer.queuetimeout`
MailerForceSSL Key = `mailer.forcessl`
RedisEnabled Key = `redis.enabled`
RedisHost Key = `redis.host`
RedisPassword Key = `redis.password`
RedisDB Key = `redis.db`
LogEnabled Key = `log.enabled`
LogStandard Key = `log.standard`
LogLevel Key = `log.level`
LogDatabase Key = `log.database`
LogDatabaseLevel Key = `log.databaselevel`
LogHTTP Key = `log.http`
LogEcho Key = `log.echo`
LogPath Key = `log.path`
RateLimitEnabled Key = `ratelimit.enabled`
RateLimitKind Key = `ratelimit.kind`
RateLimitPeriod Key = `ratelimit.period`
RateLimitLimit Key = `ratelimit.limit`
RateLimitStore Key = `ratelimit.store`
FilesBasePath Key = `files.basepath`
FilesMaxSize Key = `files.maxsize`
MigrationWunderlistEnable Key = `migration.wunderlist.enable`
MigrationWunderlistClientID Key = `migration.wunderlist.clientid`
MigrationWunderlistClientSecret Key = `migration.wunderlist.clientsecret`
MigrationWunderlistRedirectURL Key = `migration.wunderlist.redirecturl`
MigrationTodoistEnable Key = `migration.todoist.enable`
MigrationTodoistClientID Key = `migration.todoist.clientid`
MigrationTodoistClientSecret Key = `migration.todoist.clientsecret`
MigrationTodoistRedirectURL Key = `migration.todoist.redirecturl`
CorsEnable Key = `cors.enable`
CorsOrigins Key = `cors.origins`
CorsMaxAge Key = `cors.maxage`
AvatarGravaterExpiration Key = `avatar.gravatarexpiration`
BackgroundsEnabled Key = `backgrounds.enabled`
BackgroundsUploadEnabled Key = `backgrounds.providers.upload.enabled`
BackgroundsUnsplashEnabled Key = `backgrounds.providers.unsplash.enabled`
BackgroundsUnsplashAccessToken Key = `backgrounds.providers.unsplash.accesstoken`
BackgroundsUnsplashApplicationID Key = `backgrounds.providers.unsplash.applicationid`
KeyvalueType Key = `keyvalue.type`
)
// GetString returns a string config value
func (k Key) GetString() string {
return viper.GetString(string(k))
}
// GetBool returns a bool config value
func (k Key) GetBool() bool {
return viper.GetBool(string(k))
}
// GetInt returns an int config value
func (k Key) GetInt() int {
return viper.GetInt(string(k))
}
// GetInt64 returns an int64 config value
func (k Key) GetInt64() int64 {
return viper.GetInt64(string(k))
}
// GetDuration returns a duration config value
func (k Key) GetDuration() time.Duration {
return viper.GetDuration(string(k))
}
// GetStringSlice returns a string slice from a config option
func (k Key) GetStringSlice() []string {
return viper.GetStringSlice(string(k))
}
// Get returns the raw value from a config option
func (k Key) Get() interface{} {
return viper.Get(string(k))
}
var timezone *time.Location
// GetTimeZone returns the time zone configured for vikunja
// It is a separate function and not done through viper because that makes handling
// it way easier, especially when testing.
func GetTimeZone() *time.Location {
if timezone == nil {
loc, err := tz.LoadLocation(ServiceTimeZone.GetString())
if err != nil {
fmt.Printf("Error parsing time zone: %s", err)
os.Exit(1)
}
timezone = loc
}
return timezone
}
// Set sets a value
func (k Key) Set(i interface{}) {
viper.Set(string(k), i)
}
// sets the default config value
func (k Key) setDefault(i interface{}) {
viper.SetDefault(string(k), i)
}
// InitDefaultConfig sets default config values
// This is an extra function so we can call it when initializing tests without initializing the full config
func InitDefaultConfig() {
// Set defaults
// Service config
random, err := random(32)
if err != nil {
log.Fatal(err.Error())
log.Log.Fatal(err.Error())
}
// Service
ServiceJWTSecret.setDefault(random)
ServiceInterface.setDefault(":3456")
ServiceFrontendurl.setDefault("")
ServiceEnableCaldav.setDefault(true)
viper.SetDefault("service.JWTSecret", random)
viper.SetDefault("service.interface", ":3456")
viper.SetDefault("service.frontendurl", "")
viper.SetDefault("service.enablecaldav", true)
ex, err := os.Executable()
if err != nil {
panic(err)
}
exPath := filepath.Dir(ex)
ServiceRootpath.setDefault(exPath)
ServiceMaxItemsPerPage.setDefault(50)
ServiceEnableMetrics.setDefault(false)
ServiceMotd.setDefault("")
ServiceEnableLinkSharing.setDefault(true)
ServiceEnableRegistration.setDefault(true)
ServiceEnableTaskAttachments.setDefault(true)
ServiceTimeZone.setDefault("GMT")
ServiceEnableTaskComments.setDefault(true)
ServiceEnableTotp.setDefault(true)
// Auth
AuthLocalEnabled.setDefault(true)
AuthOpenIDEnabled.setDefault(false)
viper.SetDefault("service.rootpath", exPath)
viper.SetDefault("service.pagecount", 50)
viper.SetDefault("service.enablemetrics", false)
// Database
DatabaseType.setDefault("sqlite")
DatabaseHost.setDefault("localhost")
DatabaseUser.setDefault("vikunja")
DatabasePassword.setDefault("")
DatabaseDatabase.setDefault("vikunja")
DatabasePath.setDefault("./vikunja.db")
DatabaseMaxOpenConnections.setDefault(100)
DatabaseMaxIdleConnections.setDefault(50)
DatabaseMaxConnectionLifetime.setDefault(10000)
DatabaseSslMode.setDefault("disable")
viper.SetDefault("database.type", "sqlite")
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.user", "vikunja")
viper.SetDefault("database.password", "")
viper.SetDefault("database.database", "vikunja")
viper.SetDefault("database.path", "./vikunja.db")
viper.SetDefault("database.maxopenconnections", 100)
viper.SetDefault("database.maxidleconnections", 50)
viper.SetDefault("database.maxconnectionlifetime", 10000)
// Cacher
CacheEnabled.setDefault(false)
CacheType.setDefault("memory")
CacheMaxElementSize.setDefault(1000)
viper.SetDefault("cache.enabled", false)
viper.SetDefault("cache.type", "memory")
viper.SetDefault("cache.maxelementsize", 1000)
// Mailer
MailerEnabled.setDefault(false)
MailerHost.setDefault("")
MailerPort.setDefault("587")
MailerUsername.setDefault("user")
MailerPassword.setDefault("")
MailerSkipTLSVerify.setDefault(false)
MailerFromEmail.setDefault("mail@vikunja")
MailerQueuelength.setDefault(100)
MailerQueueTimeout.setDefault(30)
MailerForceSSL.setDefault(false)
viper.SetDefault("mailer.enabled", false)
viper.SetDefault("mailer.host", "")
viper.SetDefault("mailer.port", "587")
viper.SetDefault("mailer.user", "user")
viper.SetDefault("mailer.password", "")
viper.SetDefault("mailer.skiptlsverify", false)
viper.SetDefault("mailer.fromemail", "mail@vikunja")
viper.SetDefault("mailer.queuelength", 100)
viper.SetDefault("mailer.queuetimeout", 30)
// Redis
RedisEnabled.setDefault(false)
RedisHost.setDefault("localhost:6379")
RedisPassword.setDefault("")
RedisDB.setDefault(0)
viper.SetDefault("redis.enabled", false)
viper.SetDefault("redis.host", "localhost:6379")
viper.SetDefault("redis.password", "")
viper.SetDefault("redis.db", 0)
// Logger
LogEnabled.setDefault(true)
LogStandard.setDefault("stdout")
LogLevel.setDefault("INFO")
LogDatabase.setDefault("off")
LogDatabaseLevel.setDefault("WARNING")
LogHTTP.setDefault("stdout")
LogEcho.setDefault("off")
LogPath.setDefault(ServiceRootpath.GetString() + "/logs")
// Rate Limit
RateLimitEnabled.setDefault(false)
RateLimitKind.setDefault("user")
RateLimitLimit.setDefault(100)
RateLimitPeriod.setDefault(60)
RateLimitStore.setDefault("memory")
// Files
FilesBasePath.setDefault("files")
FilesMaxSize.setDefault("20MB")
// Cors
CorsEnable.setDefault(true)
CorsOrigins.setDefault([]string{"*"})
CorsMaxAge.setDefault(0)
// Migration
MigrationWunderlistEnable.setDefault(false)
MigrationTodoistEnable.setDefault(false)
// Avatar
AvatarGravaterExpiration.setDefault(3600)
// List Backgrounds
BackgroundsEnabled.setDefault(true)
BackgroundsUploadEnabled.setDefault(true)
BackgroundsUnsplashEnabled.setDefault(false)
// Key Value
KeyvalueType.setDefault("memory")
}
// InitConfig initializes the config, sets defaults etc.
func InitConfig() {
// Set defaults
InitDefaultConfig()
viper.SetDefault("log.enabled", true)
viper.SetDefault("log.errors", "stdout")
viper.SetDefault("log.standard", "stdout")
viper.SetDefault("log.database", "off")
viper.SetDefault("log.http", "stdout")
viper.SetDefault("log.echo", "off")
viper.SetDefault("log.path", viper.GetString("service.rootpath")+"/logs")
// Init checking for environment variables
viper.SetEnvPrefix("vikunja")
@ -309,38 +95,16 @@ func InitConfig() {
viper.AutomaticEnv()
// Load the config file
viper.AddConfigPath(ServiceRootpath.GetString())
viper.AddConfigPath(viper.GetString("service.rootpath"))
viper.AddConfigPath("/etc/vikunja/")
homeDir, err := os.UserHomeDir()
if err != nil {
log.Printf("No home directory found, not using config from ~/.config/vikunja/. Error was: %s\n", err.Error())
} else {
viper.AddConfigPath(path.Join(homeDir, ".config", "vikunja"))
}
viper.AddConfigPath("~/.config/vikunja")
viper.AddConfigPath(".")
viper.SetConfigName("config")
err = viper.ReadInConfig()
if err != nil {
log.Println(err.Error())
log.Println("Using default config.")
return
log.Log.Info(err)
log.Log.Info("Using defaults.")
}
if CacheType.GetString() == "keyvalue" {
CacheType.Set(KeyvalueType.GetString())
}
if RateLimitStore.GetString() == "keyvalue" {
RateLimitStore.Set(KeyvalueType.GetString())
}
if AuthOpenIDRedirectURL.GetString() == "" {
AuthOpenIDRedirectURL.Set(ServiceFrontendurl.GetString() + "auth/openid/")
}
log.Printf("Using config file: %s", viper.ConfigFileUsed())
}
func random(length int) (string, error) {

View File

@ -1,169 +1,77 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2019 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package db
import (
"encoding/gob"
"fmt"
"net/url"
"os"
"strconv"
"strings"
"time"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
xrc "gitea.com/xorm/xorm-redis-cache"
"xorm.io/core"
"xorm.io/xorm"
"xorm.io/xorm/caches"
"fmt"
"github.com/go-xorm/core"
"github.com/go-xorm/xorm"
"github.com/spf13/viper"
"strconv"
"time"
_ "github.com/go-sql-driver/mysql" // Because.
_ "github.com/lib/pq" // Because.
_ "github.com/mattn/go-sqlite3" // Because.
)
// We only want one instance of the engine, so we can reate it once and reuse it
var x *xorm.Engine
// CreateDBEngine initializes a db engine from the config
func CreateDBEngine() (engine *xorm.Engine, err error) {
if x != nil {
return x, nil
}
// If the database type is not set, this likely means we need to initialize the config first
if config.DatabaseType.GetString() == "" {
if viper.GetString("database.type") == "" {
config.InitConfig()
}
// Use Mysql if set
switch config.DatabaseType.GetString() {
case "mysql":
if viper.GetString("database.type") == "mysql" {
engine, err = initMysqlEngine()
if err != nil {
return
}
case "postgres":
engine, err = initPostgresEngine()
if err != nil {
return
}
case "sqlite":
} else {
// Otherwise use sqlite
engine, err = initSqliteEngine()
if err != nil {
return
}
default:
log.Fatalf("Unknown database type %s", config.DatabaseType.GetString())
}
engine.SetTZLocation(config.GetTimeZone()) // Vikunja's timezone
loc, err := time.LoadLocation("GMT") // The db data timezone
if err != nil {
log.Fatalf("Error parsing time zone: %s", err)
}
engine.SetTZDatabase(loc)
engine.SetMapper(core.GonicMapper{})
logger := log.NewXormLogger("")
engine.SetLogger(logger)
engine.ShowSQL(viper.GetString("log.database") != "off")
engine.SetLogger(xorm.NewSimpleLogger(log.GetLogWriter("database")))
// Cache
// We have to initialize the cache here to avoid import cycles
if config.CacheEnabled.GetBool() {
switch config.CacheType.GetString() {
case "memory":
cacher := caches.NewLRUCacher(caches.NewMemoryStore(), config.CacheMaxElementSize.GetInt())
engine.SetDefaultCacher(cacher)
case "redis":
cacher := xrc.NewRedisCacher(config.RedisHost.GetString(), config.RedisPassword.GetString(), xrc.DEFAULT_EXPIRATION, engine.Logger())
engine.SetDefaultCacher(cacher)
default:
log.Info("Did not find a valid cache type. Caching disabled. Please refer to the docs for poosible cache types.")
}
}
x = engine
return
}
// RegisterTableStructsForCache registers tables in gob encoding for redis cache
func RegisterTableStructsForCache(val interface{}) {
gob.Register(val)
}
func initMysqlEngine() (engine *xorm.Engine, err error) {
// We're using utf8mb here instead of just utf8 because we want to use non-BMP characters.
// See https://stackoverflow.com/a/30074553/10924593 for more info.
connStr := fmt.Sprintf(
"%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=true",
config.DatabaseUser.GetString(),
config.DatabasePassword.GetString(),
config.DatabaseHost.GetString(),
config.DatabaseDatabase.GetString())
"%s:%s@tcp(%s)/%s?charset=utf8&parseTime=true",
viper.GetString("database.user"),
viper.GetString("database.password"),
viper.GetString("database.host"),
viper.GetString("database.database"))
engine, err = xorm.NewEngine("mysql", connStr)
if err != nil {
return
}
engine.SetMaxOpenConns(config.DatabaseMaxOpenConnections.GetInt())
engine.SetMaxIdleConns(config.DatabaseMaxIdleConnections.GetInt())
max, err := time.ParseDuration(strconv.Itoa(config.DatabaseMaxConnectionLifetime.GetInt()) + `ms`)
if err != nil {
return
}
engine.SetConnMaxLifetime(max)
return
}
// parsePostgreSQLHostPort parses given input in various forms defined in
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
// and returns proper host and port number.
func parsePostgreSQLHostPort(info string) (string, string) {
host, port := "127.0.0.1", "5432"
if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
idx := strings.LastIndex(info, ":")
host = info[:idx]
port = info[idx+1:]
} else if len(info) > 0 {
host = info
}
return host, port
}
func initPostgresEngine() (engine *xorm.Engine, err error) {
host, port := parsePostgreSQLHostPort(config.DatabaseHost.GetString())
connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
host,
port,
url.PathEscape(config.DatabaseUser.GetString()),
url.PathEscape(config.DatabasePassword.GetString()),
config.DatabaseDatabase.GetString(),
config.DatabaseSslMode.GetString(),
)
engine, err = xorm.NewEngine("postgres", connStr)
if err != nil {
return
}
engine.SetMaxOpenConns(config.DatabaseMaxOpenConnections.GetInt())
engine.SetMaxIdleConns(config.DatabaseMaxIdleConnections.GetInt())
max, err := time.ParseDuration(strconv.Itoa(config.DatabaseMaxConnectionLifetime.GetInt()) + `ms`)
engine.SetMaxOpenConns(viper.GetInt("database.maxopenconnections"))
engine.SetMaxIdleConns(viper.GetInt("database.maxidleconnections"))
max, err := time.ParseDuration(strconv.Itoa(viper.GetInt("database.maxconnectionlifetime")) + `ms`)
if err != nil {
return
}
@ -172,42 +80,10 @@ func initPostgresEngine() (engine *xorm.Engine, err error) {
}
func initSqliteEngine() (engine *xorm.Engine, err error) {
path := config.DatabasePath.GetString()
path := viper.GetString("database.path")
if path == "" {
path = "./db.db"
}
// Try opening the db file to return a better error message if that does not work
var exists = true
if _, err := os.Stat(path); err != nil {
exists = !os.IsNotExist(err)
}
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0)
if err != nil {
return nil, fmt.Errorf("could not open database file [uid=%d, gid=%d]: %s", os.Getuid(), os.Getgid(), err)
}
_ = file.Close() // We directly close the file because we only want to check if it is writable. It will be reopened lazily later by xorm.
if !exists {
_ = os.Remove(path) // Remove the file to not prevent the db from creating another one
}
return xorm.NewEngine("sqlite3", path)
}
// WipeEverything wipes all tables and their data. Use with caution...
func WipeEverything() error {
tables, err := x.DBMetas()
if err != nil {
return err
}
for _, t := range tables {
if err := x.DropTables(t.Name); err != nil {
return err
}
}
return nil
}

View File

@ -1,54 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package db
import "encoding/json"
// Dump dumps all database tables
func Dump() (data map[string][]byte, err error) {
tables, err := x.DBMetas()
if err != nil {
return
}
data = make(map[string][]byte, len(tables))
for _, table := range tables {
entries := []map[string]interface{}{}
err := x.Table(table.Name).Find(&entries)
if err != nil {
return nil, err
}
data[table.Name], err = json.Marshal(entries)
if err != nil {
return nil, err
}
}
return
}
// Restore restores a table with all its entries
func Restore(table string, contents []map[string]interface{}) (err error) {
for _, content := range contents {
if _, err := x.Table(table).Insert(content); err != nil {
return err
}
}
return
}

View File

@ -1,209 +0,0 @@
- id: 1
title: testbucket1
list_id: 1
created_by_id: 1
limit: 9999999 # This bucket has a limit we will never exceed in the tests to make sure the logic allows for buckets with limits
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 2
title: testbucket2
list_id: 1
created_by_id: 1
limit: 3
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 3
title: testbucket3
list_id: 1
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 4
title: testbucket4 - other list
list_id: 2
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
# The following are not or only partly owned by user 1
- id: 5
title: testbucket5
list_id: 20
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 6
title: testbucket6
list_id: 6
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 7
title: testbucket7
list_id: 7
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 8
title: testbucket8
list_id: 8
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 9
title: testbucket9
list_id: 9
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 10
title: testbucket10
list_id: 10
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 11
title: testbucket11
list_id: 11
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 12
title: testbucket13
list_id: 12
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 13
title: testbucket13
list_id: 13
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 14
title: testbucket14
list_id: 14
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 15
title: testbucket15
list_id: 15
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 16
title: testbucket16
list_id: 16
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 17
title: testbucket17
list_id: 17
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 18
title: testbucket18
list_id: 5
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 19
title: testbucket19
list_id: 21
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 20
title: testbucket20
list_id: 22
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 21
title: testbucket21
list_id: 3
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
# Duplicate buckets to make deletion of one of them possible
- id: 22
title: testbucket22
list_id: 6
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 23
title: testbucket23
list_id: 7
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 24
title: testbucket24
list_id: 8
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 25
title: testbucket25
list_id: 9
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 26
title: testbucket26
list_id: 10
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 27
title: testbucket27
list_id: 11
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 28
title: testbucket28
list_id: 12
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 29
title: testbucket29
list_id: 13
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 30
title: testbucket30
list_id: 14
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 31
title: testbucket31
list_id: 15
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 32
title: testbucket32
list_id: 16
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
- id: 33
title: testbucket33
list_id: 17
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52
# This bucket is the last one in its list
- id: 34
title: testbucket34
list_id: 18
created_by_id: 1
created: 2020-04-18 21:13:52
updated: 2020-04-18 21:13:52

View File

@ -1,5 +0,0 @@
- id: 1
name: test
size: 100
created: 2019-10-13 20:33:11
created_by_id: 1

View File

@ -1,16 +0,0 @@
- id: 1
task_id: 1
label_id: 4
created: 2018-12-01 15:13:12
- id: 2
task_id: 2
label_id: 4
created: 2018-12-01 15:13:12
- id: 3
task_id: 35
label_id: 4
created: 2018-12-01 15:13:12
- id: 4
task_id: 36
label_id: 4
created: 2018-12-01 15:13:12

View File

@ -1,20 +0,0 @@
- id: 1
title: 'Label #1'
created_by_id: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 2
title: 'Label #2'
created_by_id: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 3
title: 'Label #3 - other user'
created_by_id: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 4
title: 'Label #4 - visible via other task'
created_by_id: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,24 +0,0 @@
- id: 1
hash: test
list_id: 1
right: 0
sharing_type: 1
shared_by_id: 1
created: 2018-12-01 15:13:12
updated: 2018-12-02 15:13:12
- id: 2
hash: test2
list_id: 2
right: 1
sharing_type: 1
shared_by_id: 1
created: 2018-12-01 15:13:12
updated: 2018-12-02 15:13:12
- id: 3
hash: test3
list_id: 3
right: 2
sharing_type: 1
shared_by_id: 1
created: 2018-12-01 15:13:12
updated: 2018-12-02 15:13:12

View File

@ -1,211 +0,0 @@
-
id: 1
title: Test1
description: Lorem Ipsum
identifier: test1
owner_id: 1
namespace_id: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 2
title: Test2
description: Lorem Ipsum
identifier: test2
owner_id: 3
namespace_id: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 3
title: Test3
description: Lorem Ipsum
identifier: test3
owner_id: 3
namespace_id: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 4
title: Test4
description: Lorem Ipsum
identifier: test4
owner_id: 3
namespace_id: 3
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 5
title: Test5
description: Lorem Ipsum
identifier: test5
owner_id: 5
namespace_id: 5
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 6
title: Test6
description: Lorem Ipsum
identifier: test6
owner_id: 6
namespace_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 7
title: Test7
description: Lorem Ipsum
identifier: test7
owner_id: 6
namespace_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 8
title: Test8
description: Lorem Ipsum
identifier: test8
owner_id: 6
namespace_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 9
title: Test9
description: Lorem Ipsum
identifier: test9
owner_id: 6
namespace_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 10
title: Test10
description: Lorem Ipsum
identifier: test10
owner_id: 6
namespace_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 11
title: Test11
description: Lorem Ipsum
identifier: test11
owner_id: 6
namespace_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 12
title: Test12
description: Lorem Ipsum
identifier: test12
owner_id: 6
namespace_id: 7
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 13
title: Test13
description: Lorem Ipsum
identifier: test13
owner_id: 6
namespace_id: 8
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 14
title: Test14
description: Lorem Ipsum
identifier: test14
owner_id: 6
namespace_id: 9
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 15
title: Test15
description: Lorem Ipsum
identifier: test15
owner_id: 6
namespace_id: 10
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 16
title: Test16
description: Lorem Ipsum
identifier: test16
owner_id: 6
namespace_id: 11
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 17
title: Test17
description: Lorem Ipsum
identifier: test17
owner_id: 6
namespace_id: 12
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This list is owned by user 7, and several other users have access to it via different methods.
# It is used to test the listUsers method.
-
id: 18
title: Test18
description: Lorem Ipsum
identifier: test18
owner_id: 7
namespace_id: 13
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 19
title: Test19
description: Lorem Ipsum
identifier: test19
owner_id: 7
namespace_id: 14
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 20
title: Test20
description: Lorem Ipsum
identifier: test20
owner_id: 13
namespace_id: 15
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 21
title: Test21 archived through namespace
description: Lorem Ipsum
identifier: test21
owner_id: 1
namespace_id: 16
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 22
title: Test22 archived individually
description: Lorem Ipsum
identifier: test22
owner_id: 1
namespace_id: 1
is_archived: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 23
title: Test23
description: Lorem Ipsum
identifier: test23
owner_id: 12
namespace_id: 17
is_favorite: true
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,90 +0,0 @@
- id: 1
title: testnamespace
description: Lorem Ipsum
owner_id: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 2
title: testnamespace2
description: Lorem Ipsum
owner_id: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 3
title: testnamespace3
description: Lorem Ipsum
owner_id: 3
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 6
title: testnamespace6
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 7
title: testnamespace7
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
title: testnamespace8
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 9
title: testnamespace9
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 10
title: testnamespace10
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 11
title: testnamespace11
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 12
title: testnamespace12
description: Lorem Ipsum
owner_id: 6
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 13
title: testnamespace13
description: Lorem Ipsum
owner_id: 7
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 14
title: testnamespace14
description: Lorem Ipsum
owner_id: 7
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 15
title: testnamespace15
description: Lorem Ipsum
owner_id: 13
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 16
title: Archived testnamespace16
owner_id: 1
is_archived: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 17
title: testnamespace17
description: Lorem Ipsum
owner_id: 12
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,6 +0,0 @@
- id: 1
filters: '{"sort_by":null,"order_by":null,"filter_by":["start_date","end_date","due_date"],"filter_value":["2018-12-11T03:46:40+00:00","2018-12-13T11:20:01+00:00","2018-11-29T14:00:00+00:00"],"filter_comparator":["greater","less","greater"],"filter_concat":"","filter_include_nulls":false}'
title: testfilter1
owner_id: 1
updated: 2020-09-08 15:13:12
created: 2020-09-08 14:13:12

View File

@ -1,16 +0,0 @@
- id: 1
task_id: 30
user_id: 1
created: 2018-12-01 15:13:12
- id: 2
task_id: 30
user_id: 2
created: 2018-12-01 15:13:12
- id: 3
task_id: 35
user_id: 2
created: 2018-12-01 15:13:12
- id: 4
task_id: 36
user_id: 2
created: 2018-12-01 15:13:12

View File

@ -1,11 +0,0 @@
- id: 1
task_id: 1
file_id: 1
created_by_id: 1
created: 2018-12-01 15:13:12
# The file for this attachment does not exist
- id: 2
task_id: 1
file_id: 9999
created_by_id: 1
created: 2018-12-01 15:13:12

View File

@ -1,96 +0,0 @@
- id: 1
comment: Lorem Ipsum Dolor Sit Amet
author_id: 1
task_id: 1
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 2
comment: comment 2
author_id: 5
task_id: 14
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 3
comment: comment 3
author_id: 5
task_id: 15
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 4
comment: comment 4
author_id: 6
task_id: 16
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 5
comment: comment 5
author_id: 6
task_id: 17
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 6
comment: comment 6
author_id: 6
task_id: 18
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 7
comment: comment 7
author_id: 6
task_id: 19
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 8
comment: comment 8
author_id: 6
task_id: 20
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 9
comment: comment 9
author_id: 6
task_id: 21
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 10
comment: comment 10
author_id: 6
task_id: 22
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 11
comment: comment 11
author_id: 6
task_id: 23
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 12
comment: comment 12
author_id: 6
task_id: 24
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 13
comment: comment 13
author_id: 6
task_id: 25
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 14
comment: comment 14
author_id: 6
task_id: 26
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 15
comment: comment 15
author_id: 1
task_id: 35
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06
- id: 16
comment: comment 16
author_id: 1
task_id: 36
created: 2020-02-19 18:07:06
updated: 2020-02-19 18:07:06

View File

@ -1,36 +0,0 @@
- id: 1
task_id: 1
other_task_id: 29
relation_kind: 'subtask'
created_by_id: 1
created: 2018-12-01 15:13:12
- id: 2
task_id: 29
other_task_id: 1
relation_kind: 'parenttask'
created_by_id: 1
created: 2018-12-01 15:13:12
- id: 3
task_id: 35
other_task_id: 1
relation_kind: 'related'
created_by_id: 1
created: 2018-12-01 15:13:12
- id: 4
task_id: 35
other_task_id: 1
relation_kind: 'related'
created_by_id: 1
created: 2018-12-01 15:13:12
- id: 5
task_id: 36
other_task_id: 1
relation_kind: 'related'
created_by_id: 1
created: 2018-12-01 15:13:12
- id: 6
task_id: 36
other_task_id: 1
relation_kind: 'related'
created_by_id: 1
created: 2018-12-01 15:13:12

View File

@ -1,8 +0,0 @@
- id: 1
task_id: 27
reminder: 2018-12-01 01:12:04
created: 2018-12-01 01:12:04
- id: 2
task_id: 27
reminder: 2018-12-01 01:13:44
created: 2018-12-01 01:12:04

View File

@ -1,342 +0,0 @@
- id: 1
title: 'task #1'
description: 'Lorem Ipsum'
done: false
created_by_id: 1
list_id: 1
index: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
bucket_id: 1
is_favorite: true
- id: 2
title: 'task #2 done'
done: true
created_by_id: 1
list_id: 1
index: 2
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
bucket_id: 1
- id: 3
title: 'task #3 high prio'
done: false
created_by_id: 1
list_id: 1
index: 3
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
priority: 100
bucket_id: 2
- id: 4
title: 'task #4 low prio'
done: false
created_by_id: 1
list_id: 1
index: 4
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
priority: 1
bucket_id: 2
- id: 5
title: 'task #5 higher due date'
done: false
created_by_id: 1
list_id: 1
index: 5
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
due_date: 2018-12-01 03:58:44
bucket_id: 2
- id: 6
title: 'task #6 lower due date'
done: false
created_by_id: 1
list_id: 1
index: 6
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
due_date: 2018-11-30 22:25:24
bucket_id: 3
- id: 7
title: 'task #7 with start date'
done: false
created_by_id: 1
list_id: 1
index: 7
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
start_date: 2018-12-12 07:33:20
bucket_id: 3
- id: 8
title: 'task #8 with end date'
done: false
created_by_id: 1
list_id: 1
index: 8
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
end_date: 2018-12-13 11:20:00
bucket_id: 3
- id: 9
title: 'task #9 with start and end date'
done: false
created_by_id: 1
list_id: 1
index: 9
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
start_date: 2018-12-12 07:33:20
end_date: 2018-12-13 11:20:00
bucket_id: 1
- id: 10
title: 'task #10 basic'
done: false
created_by_id: 1
list_id: 1
index: 10
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 11
title: 'task #11 basic'
done: false
created_by_id: 1
list_id: 1
index: 11
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 12
title: 'task #12 basic'
done: false
created_by_id: 1
list_id: 1
index: 12
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 13
title: 'task #13 basic other list'
done: false
created_by_id: 1
list_id: 2
index: 1
bucket_id: 4
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 14
title: 'task #14 basic other list'
done: false
created_by_id: 5
list_id: 5
index: 1
bucket_id: 18
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 15
title: 'task #15'
done: false
created_by_id: 6
list_id: 6
index: 1
bucket_id: 6
is_favorite: true
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 16
title: 'task #16'
done: false
created_by_id: 6
list_id: 7
index: 1
bucket_id: 7
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 17
title: 'task #17'
done: false
created_by_id: 6
list_id: 8
index: 1
bucket_id: 8
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 18
title: 'task #18'
done: false
created_by_id: 6
list_id: 9
index: 1
bucket_id: 9
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 19
title: 'task #19'
done: false
created_by_id: 6
list_id: 10
index: 1
bucket_id: 10
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 20
title: 'task #20'
done: false
created_by_id: 6
list_id: 11
index: 1
bucket_id: 11
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 21
title: 'task #21'
done: false
created_by_id: 6
list_id: 12
index: 1
bucket_id: 12
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 22
title: 'task #22'
done: false
created_by_id: 6
list_id: 13
index: 1
bucket_id: 13
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 23
title: 'task #23'
done: false
created_by_id: 6
list_id: 14
index: 1
bucket_id: 14
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 24
title: 'task #24'
done: false
created_by_id: 6
list_id: 15
index: 1
bucket_id: 15
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 25
title: 'task #25'
done: false
created_by_id: 6
list_id: 16
index: 1
bucket_id: 16
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 26
title: 'task #26'
done: false
created_by_id: 6
list_id: 17
index: 1
bucket_id: 17
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 27
title: 'task #27 with reminders'
done: false
created_by_id: 1
list_id: 1
index: 12
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 28
title: 'task #28 with repeat after'
done: false
created_by_id: 1
repeat_after: 3600
list_id: 1
index: 13
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 29
title: 'task #29 with parent task (1)'
done: false
created_by_id: 1
list_id: 1
index: 14
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 30
title: 'task #30 with assignees'
done: false
created_by_id: 1
list_id: 1
index: 15
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 31
title: 'task #31 with color'
done: false
created_by_id: 1
list_id: 1
index: 16
hex_color: f0f0f0
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 32
title: 'task #32'
done: false
created_by_id: 1
list_id: 3
index: 1
bucket_id: 21
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 33
title: 'task #33 with percent done'
done: false
created_by_id: 1
list_id: 1
index: 17
percent_done: 0.5
bucket_id: 1
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
# This task is forbidden for user1
- id: 34
title: 'task #34'
done: false
created_by_id: 13
list_id: 20
index: 20
bucket_id: 5
is_favorite: true
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 35
title: 'task #35'
done: false
created_by_id: 1
list_id: 21
index: 1
bucket_id: 19
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04
- id: 36
title: 'task #36'
done: false
created_by_id: 1
list_id: 22
index: 1
bucket_id: 20
created: 2018-12-01 01:12:04
updated: 2018-12-01 01:12:04

View File

@ -1,48 +0,0 @@
- id: 1
team_id: 1
list_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This team has read only access on list 6
- id: 2
team_id: 2
list_id: 6
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This team has write access on list 7
- id: 3
team_id: 3
list_id: 7
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This team has admin access on list 8
- id: 4
team_id: 4
list_id: 8
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# Readonly acces on list 19
- id: 5
team_id: 8
list_id: 19
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# Write acces on list 19
- id: 6
team_id: 9
list_id: 19
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# Admin acces on list 19
- id: 7
team_id: 10
list_id: 19
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,57 +0,0 @@
-
team_id: 1
user_id: 1
admin: true
created: 2018-12-01 15:13:12
-
team_id: 1
user_id: 2
created: 2018-12-01 15:13:12
-
team_id: 2
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 3
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 4
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 5
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 6
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 7
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 8
user_id: 1
created: 2018-12-01 15:13:12
-
team_id: 9
user_id: 2
created: 2018-12-01 15:13:12
-
team_id: 10
user_id: 3
created: 2018-12-01 15:13:12
-
team_id: 11
user_id: 8
created: 2018-12-01 15:13:12
-
team_id: 12
user_id: 9
created: 2018-12-01 15:13:12
-
team_id: 13
user_id: 10
created: 2018-12-01 15:13:12

View File

@ -1,52 +0,0 @@
- id: 1
team_id: 1
namespace_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 2
team_id: 2
namespace_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 3
team_id: 5
namespace_id: 7
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 4
team_id: 6
namespace_id: 8
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 5
team_id: 7
namespace_id: 9
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 6
team_id: 11
namespace_id: 14
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 7
team_id: 12
namespace_id: 14
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
team_id: 13
namespace_id: 14
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,119 +0,0 @@
-
id: 1
username: 'user1'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user1@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 2
username: 'user2'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user2@example.com'
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 3
username: 'user3'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user3@example.com'
password_reset_token: passwordresettesttoken
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 4
username: 'user4'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user4@example.com'
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
id: 5
username: 'user5'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user5@example.com'
email_confirm_token: tiepiQueed8ahc7zeeFe1eveiy4Ein8osooxegiephauph2Ael
is_active: false
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
# This use is used to create a whole bunch of lists which are then shared directly with a user
- id: 6
username: 'user6'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user6@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 7
username: 'user7'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user7@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
username: 'user8'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user8@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 9
username: 'user9'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user9@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 10
username: 'user10'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user10@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 11
username: 'user11'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user11@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 12
username: 'user12'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user12@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 13
username: 'user13'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user14@example.com'
is_active: true
issuer: local
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 14
username: 'user14'
password: '$2a$14$dcadBoMBL9jQoOcZK8Fju.cy0Ptx2oZECkKLnaa8ekRoTFe1w7To.' # 1234
email: 'user15@some.service.com'
is_active: true
issuer: 'https://some.service.com'
subject: '12345'
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,48 +0,0 @@
- id: 1
user_id: 1
list_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 2
user_id: 2
list_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 3
user_id: 1
list_id: 9
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 4
user_id: 1
list_id: 10
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 5
user_id: 1
list_id: 11
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 6
user_id: 4
list_id: 19
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 7
user_id: 5
list_id: 19
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
user_id: 6
list_id: 19
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,52 +0,0 @@
- id: 1
user_id: 1
namespace_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 2
user_id: 2
namespace_id: 3
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 3
user_id: 1
namespace_id: 10
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 4
user_id: 1
namespace_id: 11
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 5
user_id: 1
namespace_id: 12
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 6
user_id: 11
namespace_id: 14
right: 0
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 7
user_id: 12
namespace_id: 14
right: 1
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
- id: 8
user_id: 13
namespace_id: 14
right: 2
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12

View File

@ -1,104 +0,0 @@
// Copyright2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package db
import (
"fmt"
"os"
"testing"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"github.com/stretchr/testify/assert"
"xorm.io/core"
"xorm.io/xorm"
)
// CreateTestEngine creates an instance of the db engine which lives in memory
func CreateTestEngine() (engine *xorm.Engine, err error) {
if x != nil {
return x, nil
}
if os.Getenv("VIKUNJA_TESTS_USE_CONFIG") == "1" {
config.InitConfig()
engine, err = CreateDBEngine()
if err != nil {
return nil, err
}
} else {
engine, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
if err != nil {
return nil, err
}
}
engine.SetMapper(core.GonicMapper{})
logger := log.NewXormLogger("DEBUG")
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
engine.SetLogger(logger)
engine.SetTZLocation(config.GetTimeZone())
x = engine
return
}
// InitTestFixtures populates the db with all fixtures from the fixtures folder
func InitTestFixtures(tablenames ...string) (err error) {
// Create all fixtures
config.InitDefaultConfig()
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Sync fixtures
err = InitFixtures(tablenames...)
if err != nil {
log.Fatal(err)
}
return nil
}
// AssertExists checks and asserts the existence of certain entries in the db
func AssertExists(t *testing.T, table string, values map[string]interface{}, custom bool) {
var exists bool
var err error
v := make(map[string]interface{})
// Postgres sometimes needs to build raw sql. Because it won't always need to do this and this isn't fun, it's a flag.
if custom {
//#nosec
sql := "SELECT * FROM " + table + " WHERE "
for col, val := range values {
sql += col + "=" + fmt.Sprintf("%v", val) + " AND "
}
sql = sql[:len(sql)-5]
exists, err = x.SQL(sql).Get(&v)
} else {
exists, err = x.Table(table).Where(values).Get(&v)
}
assert.NoError(t, err, fmt.Sprintf("Failed to assert entries exist in db, error was: %s", err))
assert.True(t, exists, fmt.Sprintf("Entries %v do not exist in table %s", values, table))
}
// AssertMissing checks and asserts the nonexiste nce of certain entries in the db
func AssertMissing(t *testing.T, table string, values map[string]interface{}) {
v := make(map[string]interface{})
exists, err := x.Table(table).Where(values).Exist(&v)
assert.NoError(t, err, fmt.Sprintf("Failed to assert entries don't exist in db, error was: %s", err))
assert.False(t, exists, fmt.Sprintf("Entries %v exist in table %s", values, table))
}

View File

@ -1,117 +0,0 @@
// Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package db
import (
"fmt"
"path/filepath"
"testing"
"code.vikunja.io/api/pkg/config"
"github.com/go-testfixtures/testfixtures/v3"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/schemas"
)
var fixtures *testfixtures.Loader
// InitFixtures initialize test fixtures for a test database
func InitFixtures(tablenames ...string) (err error) {
var testfiles func(loader *testfixtures.Loader) error
dir := filepath.Join(config.ServiceRootpath.GetString(), "pkg", "db", "fixtures")
// If fixture table names are specified, load them
// Otherwise, load all fixtures
if len(tablenames) > 0 {
for i, name := range tablenames {
tablenames[i] = filepath.Join(dir, name+".yml")
}
testfiles = testfixtures.Files(tablenames...)
} else {
testfiles = testfixtures.Directory(dir)
}
loaderOptions := []func(loader *testfixtures.Loader) error{
testfixtures.Database(x.DB().DB),
testfixtures.Dialect(config.DatabaseType.GetString()),
testfixtures.DangerousSkipTestDatabaseCheck(),
testfixtures.Location(config.GetTimeZone()),
testfiles,
}
if config.DatabaseType.GetString() == "postgres" {
loaderOptions = append(loaderOptions, testfixtures.SkipResetSequences())
}
fixtures, err = testfixtures.New(loaderOptions...)
if err != nil {
return err
}
return err
}
// LoadFixtures load fixtures for a test database
func LoadFixtures() error {
err := fixtures.Load()
if err != nil {
return err
}
// Copied from https://github.com/go-gitea/gitea/blob/master/models/test_fixtures.go#L39
// Now if we're running postgres we need to tell it to update the sequences
if x.Dialect().URI().DBType == schemas.POSTGRES {
results, err := x.QueryString(`SELECT 'SELECT SETVAL(' ||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
pg_depend AS D,
pg_class AS T,
pg_attribute AS C,
pg_tables AS PGT
WHERE S.relkind = 'S'
AND S.oid = D.objid
AND D.refobjid = T.oid
AND D.refobjid = C.attrelid
AND D.refobjsubid = C.attnum
AND T.relname = PGT.tablename
ORDER BY S.relname;`)
if err != nil {
fmt.Printf("Failed to generate sequence update: %v\n", err)
return err
}
for _, r := range results {
for _, value := range r {
_, err = x.Exec(value)
if err != nil {
fmt.Printf("Failed to update sequence: %s Error: %v\n", value, err)
return err
}
}
}
}
return err
}
// LoadAndAssertFixtures loads all fixtures defined before and asserts they are correctly loaded
func LoadAndAssertFixtures(t *testing.T) {
err := LoadFixtures()
assert.NoError(t, err)
}

View File

@ -1,49 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package files
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"xorm.io/xorm"
)
var x *xorm.Engine
// SetEngine sets the xorm.Engine
func SetEngine() (err error) {
x, err = db.CreateDBEngine()
if err != nil {
log.Criticalf("Could not connect to db: %v", err.Error())
return
}
// Cache
if config.CacheEnabled.GetBool() && config.CacheType.GetString() == "redis" {
db.RegisterTableStructsForCache(GetTables())
}
return nil
}
// GetTables returns all structs which are also a table.
func GetTables() []interface{} {
return []interface{}{
&File{},
}
}

View File

@ -1,41 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package files
import (
"io"
)
// Dump dumps all saved files
// This only includes the raw files, no db entries.
func Dump() (allFiles map[int64]io.ReadCloser, err error) {
files := []*File{}
err = x.Find(&files)
if err != nil {
return
}
allFiles = make(map[int64]io.ReadCloser, len(files))
for _, file := range files {
if err := file.LoadFileByID(); err != nil {
return nil, err
}
allFiles[file.ID] = file.File
}
return
}

View File

@ -1,69 +0,0 @@
// Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package files
import "fmt"
// ErrFileDoesNotExist defines an error where a file does not exist in the db
type ErrFileDoesNotExist struct {
FileID int64
}
// Error is the error implementation of ErrFileDoesNotExist
func (err ErrFileDoesNotExist) Error() string {
return fmt.Sprintf("file %d does not exist", err.FileID)
}
// IsErrFileDoesNotExist checks if an error is ErrFileDoesNotExist
func IsErrFileDoesNotExist(err error) bool {
_, ok := err.(ErrFileDoesNotExist)
return ok
}
// ErrFileIsTooLarge defines an error where a file is larger than the configured limit
type ErrFileIsTooLarge struct {
Size uint64
}
// Error is the error implementation of ErrFileIsTooLarge
func (err ErrFileIsTooLarge) Error() string {
return fmt.Sprintf("file is too large [Size: %d]", err.Size)
}
// IsErrFileIsTooLarge checks if an error is ErrFileIsTooLarge
func IsErrFileIsTooLarge(err error) bool {
_, ok := err.(ErrFileIsTooLarge)
return ok
}
// ErrFileIsNotUnsplashFile defines an error where a file is not downloaded from unsplash.
// Used in cases whenever unsplash information about a file is requested, but the file was not downloaded from unsplash.
type ErrFileIsNotUnsplashFile struct {
FileID int64
}
// Error is the error implementation of ErrFileIsNotUnsplashFile
func (err ErrFileIsNotUnsplashFile) Error() string {
return fmt.Sprintf("file was not downloaded from unsplash [FileID: %d]", err.FileID)
}
// IsErrFileIsNotUnsplashFile checks if an error is ErrFileIsNotUnsplashFile
func IsErrFileIsNotUnsplashFile(err error) bool {
_, ok := err.(ErrFileIsNotUnsplashFile)
return ok
}

View File

@ -1,85 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package files
import (
"os"
"testing"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
)
// This file handles storing and retrieving a file for different backends
var fs afero.Fs
var afs *afero.Afero
// InitFileHandler creates a new file handler for the file backend we want to use
func InitFileHandler() {
fs = afero.NewOsFs()
afs = &afero.Afero{Fs: fs}
}
// InitTestFileHandler initializes a new memory file system for testing
func InitTestFileHandler() {
fs = afero.NewMemMapFs()
afs = &afero.Afero{Fs: fs}
}
func initFixtures(t *testing.T) {
// DB fixtures
db.LoadAndAssertFixtures(t)
// File fixtures
InitTestFileFixtures(t)
}
// InitTestFileFixtures initializes file fixtures
func InitTestFileFixtures(t *testing.T) {
// Init fixture files
filename := config.FilesBasePath.GetString() + "/1"
err := afero.WriteFile(afs, filename, []byte("testfile1"), 0644)
assert.NoError(t, err)
}
// InitTests handles the actual bootstrapping of the test env
func InitTests() {
var err error
x, err = db.CreateTestEngine()
if err != nil {
log.Fatal(err)
}
err = x.Sync2(GetTables()...)
if err != nil {
log.Fatal(err)
}
err = db.InitTestFixtures("files")
if err != nil {
log.Fatal(err)
}
InitTestFileHandler()
}
// FileStat stats a file. This is an exported function to be able to test this from outide of the package
func FileStat(filename string) (os.FileInfo, error) {
return afs.Stat(filename)
}

View File

@ -1,123 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package files
import (
"io"
"strconv"
"time"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/web"
"github.com/c2h5oh/datasize"
"github.com/spf13/afero"
)
// File holds all information about a file
type File struct {
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
Name string `xorm:"text not null" json:"name"`
Mime string `xorm:"text null" json:"mime"`
Size uint64 `xorm:"int(11) not null" json:"size"`
Created time.Time `xorm:"created" json:"created"`
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
File afero.File `xorm:"-" json:"-"`
// This ReadCloser is only used for migration purposes. Use with care!
// There is currentlc no better way of doing this.
FileContent []byte `xorm:"-" json:"-"`
}
// TableName is the table name for the files table
func (File) TableName() string {
return "files"
}
func (f *File) getFileName() string {
return config.FilesBasePath.GetString() + "/" + strconv.FormatInt(f.ID, 10)
}
// LoadFileByID returns a file by its ID
func (f *File) LoadFileByID() (err error) {
f.File, err = afs.Open(f.getFileName())
return
}
// LoadFileMetaByID loads everything about a file without loading the actual file
func (f *File) LoadFileMetaByID() (err error) {
exists, err := x.Where("id = ?", f.ID).Get(f)
if !exists {
return ErrFileDoesNotExist{FileID: f.ID}
}
return
}
// Create creates a new file from an FileHeader
func Create(f io.Reader, realname string, realsize uint64, a web.Auth) (file *File, err error) {
return CreateWithMime(f, realname, realsize, a, "")
}
// CreateWithMime creates a new file from an FileHeader and sets its mime type
func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, mime string) (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() {
return nil, ErrFileIsTooLarge{Size: realsize}
}
// We first insert the file into the db to get it's ID
file = &File{
Name: realname,
Size: realsize,
CreatedByID: a.GetID(),
Mime: mime,
}
_, err = x.Insert(file)
if err != nil {
return
}
// Save the file to storage with its new ID as path
err = file.Save(f)
return
}
// Delete removes a file from the DB and the file system
func (f *File) Delete() (err error) {
deleted, err := x.Where("id = ?", f.ID).Delete(f)
if err != nil {
return err
}
if deleted == 0 {
return ErrFileDoesNotExist{FileID: f.ID}
}
err = afs.Remove(f.getFileName())
return
}
// Save saves a file to storage
func (f *File) Save(fcontent io.Reader) error {
return afs.WriteReader(f.getFileName(), fcontent)
}

View File

@ -1,132 +0,0 @@
// Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package files
import (
"io"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
type testfile struct {
content []byte
done bool
}
func (t *testfile) Read(p []byte) (n int, err error) {
if t.done {
return 0, io.EOF
}
copy(p, t.content)
t.done = true
return len(p), nil
}
func (t *testfile) Close() error {
return nil
}
type testauth struct {
id int64
}
func (a *testauth) GetID() int64 {
return a.id
}
func TestCreate(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
initFixtures(t)
tf := &testfile{
content: []byte("testfile"),
}
ta := &testauth{id: 1}
createdFile, err := Create(tf, "testfile", 100, ta)
assert.NoError(t, err)
// Check the file was created correctly
file := &File{ID: createdFile.ID}
err = file.LoadFileMetaByID()
assert.NoError(t, err)
assert.Equal(t, int64(1), file.CreatedByID)
assert.Equal(t, "testfile", file.Name)
assert.Equal(t, uint64(100), file.Size)
})
t.Run("Too Large", func(t *testing.T) {
initFixtures(t)
tf := &testfile{
content: []byte("testfile"),
}
ta := &testauth{id: 1}
_, err := Create(tf, "testfile", 99999999999, ta)
assert.Error(t, err)
assert.True(t, IsErrFileIsTooLarge(err))
})
}
func TestFile_Delete(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
initFixtures(t)
f := &File{ID: 1}
err := f.Delete()
assert.NoError(t, err)
})
t.Run("Nonexisting", func(t *testing.T) {
initFixtures(t)
f := &File{ID: 9999}
err := f.Delete()
assert.Error(t, err)
assert.True(t, IsErrFileDoesNotExist(err))
})
}
func TestFile_LoadFileByID(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
initFixtures(t)
f := &File{ID: 1}
err := f.LoadFileByID()
assert.NoError(t, err)
})
t.Run("Nonexisting", func(t *testing.T) {
initFixtures(t)
f := &File{ID: 9999}
err := f.LoadFileByID()
assert.Error(t, err)
assert.True(t, os.IsNotExist(err))
})
}
func TestFile_LoadFileMetaByID(t *testing.T) {
t.Run("Normal", func(t *testing.T) {
initFixtures(t)
f := &File{ID: 1}
err := f.LoadFileMetaByID()
assert.NoError(t, err)
assert.Equal(t, "test", f.Name)
})
t.Run("Nonexisting", func(t *testing.T) {
initFixtures(t)
f := &File{ID: 9999}
err := f.LoadFileMetaByID()
assert.Error(t, err)
assert.True(t, IsErrFileDoesNotExist(err))
})
}

View File

@ -1,29 +0,0 @@
// Copyright 2018-2020 Vikunja and contriubtors. All rights reserved.
//
// This file is part of Vikunja.
//
// Vikunja is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Vikunja is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Vikunja. If not, see <https://www.gnu.org/licenses/>.
package files
import (
"os"
"testing"
)
// TestMain is the main test function used to bootstrap the test env
func TestMain(m *testing.M) {
InitTests()
os.Exit(m.Run())
}

View File

@ -1,83 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package initialize
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/migration"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/keyvalue"
migrator "code.vikunja.io/api/pkg/modules/migration"
"code.vikunja.io/api/pkg/red"
"code.vikunja.io/api/pkg/user"
)
// LightInit will only fullInit config, redis, logger but no db connection.
func LightInit() {
// Init the config
config.InitConfig()
// Init redis
red.InitRedis()
// Init keyvalue store
keyvalue.InitStorage()
// Set logger
log.InitLogger()
}
// InitEngines intializes all db connections
func InitEngines() {
err := models.SetEngine()
if err != nil {
log.Fatal(err.Error())
}
err = user.InitDB()
if err != nil {
log.Fatal(err.Error())
}
err = files.SetEngine()
if err != nil {
log.Fatal(err.Error())
}
err = migrator.InitDB()
if err != nil {
log.Fatal(err.Error())
}
}
// FullInit initializes all kinds of things in the right order
func FullInit() {
LightInit()
// Run the migrations
migration.Migrate(nil)
// Set Engine
InitEngines()
// Initialize the files handler
files.InitFileHandler()
// Start the mail daemon
mail.StartMailDaemon()
}

View File

@ -1,5 +1,5 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
// Vikunja is a todo-list application to facilitate your life.
// Copyright 2019 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by

Some files were not shown because too many files have changed in this diff Show More