Compare commits

...

27 Commits

Author SHA1 Message Date
kolaente f18cde269b
chore: remove unused docker entrypoint script 2024-03-01 11:47:29 +01:00
kolaente 09d446765d
docs: update config docs 2024-03-01 11:47:29 +01:00
renovate a99e7f9aa3 fix(deps): update dependency express to v4.18.3 2024-02-29 12:05:06 +00:00
renovate e93e48c4c2 fix(deps): update vueuse to v10.9.0 2024-02-29 10:05:25 +00:00
renovate 06688fa8d4 fix(deps): update sentry-javascript monorepo to v7.103.0 2024-02-29 09:05:28 +00:00
renovate 1a15c865e2 fix(deps): update module golang.org/x/crypto to v0.20.0 2024-02-29 08:18:30 +00:00
renovate 7b9a72ebe1 fix(deps): update module github.com/prometheus/client_golang to v1.19.0 2024-02-29 08:15:51 +00:00
renovate a0f4bdbdad fix(deps): update dependency vue to v3.4.21 2024-02-28 22:06:03 +00:00
renovate eea356e570 chore(deps): update dev-dependencies 2024-02-28 18:05:31 +00:00
kolaente 5b70609ba7
fix: usage of limit and order by usage in recursive cte 2024-02-28 14:35:09 +01:00
kolaente 6b1e67485b
feat: fetch all projects with a recursive cte instead of recursive query
This change modifies the fetching of all projects to use a recursive common table expression instead of recursively calling the method.
2024-02-28 13:42:45 +01:00
kolaente 5d127c2897 feat: run frontend tests with api build from the same branch (#2137)
Reviewed-on: vikunja/vikunja#2137
Co-authored-by: kolaente <k@knt.li>
Co-committed-by: kolaente <k@knt.li>
2024-02-27 18:06:34 +00:00
kolaente 1275dfc260
fix: lint 2024-02-27 16:38:20 +01:00
kolaente 997fb6bc54
fix(migration): show correct help message when a migration was started 2024-02-27 16:36:18 +01:00
kolaente b2e5de88ff
fix(labels): make sure labels are aligned in the middle 2024-02-27 16:29:27 +01:00
kolaente baa5d14ca6
fix(assignees): spacing of users 2024-02-27 16:15:35 +01:00
kolaente 2d5c496397
fix(kanban): pass active filters down to task lazy loading
Before this change, applying a filter and then scrolling a bucket would not use that filter when lazy loading the tasks in that bucket. That resulted in all tasks being loaded, regardless if the filter applied to them.
2024-02-27 16:10:19 +01:00
kolaente b8533d2bfc
fix(gantt): use color variables for gantt header so that it works in dark mode 2024-02-27 15:55:07 +01:00
kolaente 8a82093233
fix(project): don't allow archival or deletion of default projects in UI 2024-02-27 15:37:30 +01:00
kolaente 3d39fc3960
fix(ci): sign drone config 2024-02-26 10:56:21 +01:00
kolaente 43e13d9cdd
fix(ci): run test db in memory 2024-02-26 10:54:53 +01:00
kolaente a0e812395f
feat(docker)!: use scratch as base image
This change modifies the docker image so that it uses the scratch image instead of alpine. This is possible because the Vikunja binary is now entirely statically compiled, wich no dependencies on any system c libraries.

This also changes the default path for the sqlite file in the docker image (breaking). When using sqlite in docker, the VIKUNJA_DATABASE_PATH config variable or the file mount must be changed to prevent data loss.
2024-02-25 22:49:04 +01:00
kolaente 2e5c19352e
docs: update publiccode.yml [skip ci] 2024-02-25 14:06:23 +01:00
kolaente 1ffb93b63c
fix: clarify preview deployment text and fix typo 2024-02-25 13:51:43 +01:00
renovate 1bf8659423 fix(deps): update sentry-javascript monorepo to v7.102.1 2024-02-24 14:43:38 +00:00
renovate 7629c8426e chore(deps): update pnpm to v8.15.4 2024-02-24 14:05:08 +00:00
renovate a83acc0300 fix(deps): update tiptap to v2.2.4 2024-02-23 13:06:11 +00:00
34 changed files with 1651 additions and 1567 deletions

View File

@ -1,6 +1,7 @@
files/
dist/
logs/
docs/
Dockerfile
docker-manifest.tmpl

View File

@ -1,7 +1,7 @@
---
kind: pipeline
type: docker
name: build-and-test-api
name: build-and-test
workspace:
base: /go
@ -122,7 +122,7 @@ steps:
when:
event: [ push, tag, pull_request ]
- name: build
- name: api-build
image: vikunja/golang-build:latest
pull: always
environment:
@ -133,7 +133,7 @@ steps:
when:
event: [ push, tag, pull_request ]
- name: lint
- name: api-lint
image: golangci/golangci-lint:v1.55.2
pull: always
environment:
@ -156,7 +156,9 @@ steps:
- name: test-migration-sqlite
image: vikunja/golang-build:latest
pull: always
depends_on: [ test-migration-prepare, build ]
depends_on:
- test-migration-prepare
- api-build
environment:
VIKUNJA_DATABASE_TYPE: sqlite
VIKUNJA_DATABASE_PATH: /db/vikunja-migration-test.db
@ -175,7 +177,9 @@ steps:
- name: test-migration-mysql
image: vikunja/golang-build:latest
pull: always
depends_on: [ test-migration-prepare, build ]
depends_on:
- test-migration-prepare
- api-build
environment:
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_HOST: test-mysql-migration
@ -194,7 +198,9 @@ steps:
- name: test-migration-psql
image: vikunja/golang-build:latest
pull: always
depends_on: [ test-migration-prepare, build ]
depends_on:
- test-migration-prepare
- api-build
environment:
VIKUNJA_DATABASE_TYPE: postgres
VIKUNJA_DATABASE_HOST: test-postgres-migration
@ -211,7 +217,7 @@ steps:
when:
event: [ push, tag, pull_request ]
- name: test
- name: api-test-unit
image: vikunja/golang-build:latest
pull: always
environment:
@ -222,7 +228,7 @@ steps:
when:
event: [ push, tag, pull_request ]
- name: test-sqlite
- name: api-test-unit-sqlite
image: vikunja/golang-build:latest
pull: always
environment:
@ -239,7 +245,7 @@ steps:
when:
event: [ push, tag, pull_request ]
- name: test-mysql
- name: api-test-unit-mysql
image: vikunja/golang-build:latest
pull: always
environment:
@ -256,7 +262,7 @@ steps:
when:
event: [ push, tag, pull_request ]
- name: test-postgres
- name: api-test-unit-postgres
image: vikunja/golang-build:latest
pull: always
environment:
@ -337,31 +343,22 @@ steps:
when:
event: [ push, tag, pull_request ]
---
kind: pipeline
type: docker
name: build-and-test-frontend
trigger:
branch:
include:
- main
event:
include:
- push
- pull_request
services:
- name: api
image: vikunja/vikunja:unstable
- name: test-api-run
image: vikunja/golang-build:latest
pull: always
environment:
VIKUNJA_SERVICE_TESTINGTOKEN: averyLongSecretToSe33dtheDB
VIKUNJA_LOG_LEVEL: DEBUG
VIKUNJA_CORS_ENABLE: 1
VIKUNJA_DATABASE_PATH: memory
VIKUNJA_DATABASE_TYPE: sqlite
commands:
- ./vikunja
detach: true
depends_on:
- api-build
steps:
- name: dependencies
- name: frontend-dependencies
image: node:20.11.0-alpine
pull: always
environment:
@ -375,7 +372,7 @@ steps:
# depends_on:
# - restore-cache
- name: lint
- name: frontend-lint
image: node:20.11.0-alpine
pull: always
environment:
@ -385,9 +382,9 @@ steps:
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run lint
depends_on:
- dependencies
- frontend-dependencies
- name: build-prod
- name: frontend-build-prod
image: node:20.11.0-alpine
pull: always
environment:
@ -395,11 +392,11 @@ steps:
commands:
- cd frontend
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run build
- pnpm run build:test
depends_on:
- dependencies
- frontend-dependencies
- name: test-unit
- name: frontend-test-unit
image: node:20.11.0-alpine
pull: always
commands:
@ -407,9 +404,9 @@ steps:
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run test:unit
depends_on:
- dependencies
- frontend-dependencies
- name: typecheck
- name: frontend-typecheck
failure: ignore
image: node:20.11.0-alpine
pull: always
@ -420,13 +417,13 @@ steps:
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm run typecheck
depends_on:
- dependencies
- frontend-dependencies
- name: test-frontend
- name: frontend-test
image: cypress/browsers:node18.12.0-chrome107
pull: always
environment:
CYPRESS_API_URL: http://api:3456/api/v1
CYPRESS_API_URL: http://test-api-run:3456/api/v1
CYPRESS_TEST_SECRET: averyLongSecretToSe33dtheDB
PNPM_CACHE_FOLDER: .cache/pnpm
CYPRESS_CACHE_FOLDER: .cache/cypress
@ -435,14 +432,15 @@ steps:
from_secret: cypress_project_key
commands:
- cd frontend
- sed -i 's/localhost/api/g' dist/index.html
- sed -i 's/localhost/test-api-run/g' dist-test/index.html
- corepack enable && pnpm config set store-dir .cache/pnpm
- pnpm cypress install
- pnpm run test:e2e-record
- pnpm run test:e2e-record-test
depends_on:
- build-prod
- frontend-build-prod
- test-api-run
- name: deploy-preview
- name: frontend-deploy-preview
image: williamjackson/netlify-cli
pull: always
user: root # The rest runs as root and thus the permissions wouldn't work
@ -455,7 +453,7 @@ steps:
from_secret: gitea_token
commands:
- cd frontend
- cp -r dist dist-preview
- cp -r dist-test dist-preview
# Override the default api url used for preview
- sed -i 's|http://localhost:3456|https://try.vikunja.io|g' dist-preview/index.html
- apk add --no-cache perl-utils
@ -464,7 +462,7 @@ steps:
- shasum -a 384 -c ./scripts/deploy-preview-netlify.mjs.sha384
- node ./scripts/deploy-preview-netlify.mjs
depends_on:
- build-prod
- frontend-build-prod
when:
event:
include:
@ -476,7 +474,7 @@ type: docker
name: generate-swagger-docs
depends_on:
- build-and-test-api
- build-and-test
workspace:
base: /go
@ -520,8 +518,7 @@ type: docker
name: release
depends_on:
- build-and-test-api
- build-and-test-frontend
- build-and-test
workspace:
base: /source
@ -805,8 +802,7 @@ type: docker
name: docker-release
depends_on:
- build-and-test-api
- build-and-test-frontend
- build-and-test
trigger:
ref:
@ -880,7 +876,7 @@ type: docker
name: frontend-release-unstable
depends_on:
- build-and-test-frontend
- build-and-test
trigger:
branch:
@ -943,7 +939,7 @@ type: docker
name: frontend-release-version
depends_on:
- build-and-test-frontend
- build-and-test
trigger:
event:
@ -1366,8 +1362,7 @@ trigger:
- "refs/tags/**"
depends_on:
- build-and-test-api
- build-and-test-frontend
- build-and-test
- release
- deploy-docs
- docker-release
@ -1389,6 +1384,6 @@ steps:
- failure
---
kind: signature
hmac: aa9bd51fc7d73686ee169060dcb4d6540214825a0d5134035f477a97f77dd24d
hmac: 008b86263a8d03806da907c128a837a380901f1a2190a658c22d4e06cadc1b64
...

View File

@ -33,22 +33,14 @@ RUN export PATH=$PATH:$GOPATH/bin && \
# ┘└┘┘─┘┘└┘┘└┘┴─┘┘└┘
# 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.19 AS runner
FROM scratch
LABEL maintainer="maintainers@vikunja.io"
WORKDIR /app/vikunja
ENTRYPOINT [ "/sbin/tini", "-g", "--", "/entrypoint.sh" ]
ENTRYPOINT [ "/app/vikunja/vikunja" ]
EXPOSE 3456
USER 1000
ENV VIKUNJA_SERVICE_ROOTPATH=/app/vikunja/
ENV PUID 1000
ENV PGID 1000
RUN apk --update --no-cache add tzdata tini shadow && \
addgroup vikunja && \
adduser -s /bin/sh -D -G vikunja vikunja -h /app/vikunja -H
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod 0755 /entrypoint.sh && mkdir files
ENV VIKUNJA_DATABASE_PATH=/db/vikunja.db
COPY --from=apibuilder /build/vikunja-* vikunja

View File

@ -76,7 +76,7 @@ sentry:
frontenddsn: "https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480"
database:
# Database type to use. Supported types are mysql, postgres and sqlite.
# Database type to use. Supported values are mysql, postgres and sqlite. Vikunja is able to run with MySQL 8.0+, Mariadb 10.2+, PostgreSQL 12+, and sqlite.
type: "sqlite"
# Database user which is used to connect to the database.
user: "vikunja"

View File

@ -51,11 +51,11 @@
}
},
"devDependencies": {
"electron": "29.0.0",
"electron-builder": "24.9.1"
"electron": "29.1.0",
"electron-builder": "24.12.0"
},
"dependencies": {
"connect-history-api-fallback": "2.0.0",
"express": "4.18.2"
"express": "4.18.3"
}
}

View File

@ -253,12 +253,11 @@ app-builder-bin@4.0.0:
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-4.0.0.tgz#1df8e654bd1395e4a319d82545c98667d7eed2f0"
integrity sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==
app-builder-lib@24.9.1:
version "24.9.1"
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-24.9.1.tgz#bf3568529298b4de8595ed1acbb351fe27db5ba4"
integrity sha512-Q1nYxZcio4r+W72cnIRVYofEAyjBd3mG47o+zms8HlD51zWtA/YxJb01Jei5F+jkWhge/PTQK+uldsPh6d0/4g==
app-builder-lib@24.12.0:
version "24.12.0"
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-24.12.0.tgz#2e985968c341d28fc887be3ecee658e6a240e147"
integrity sha512-t/xinVrMbsEhwljLDoFOtGkiZlaxY1aceZbHERGAS02EkUHJp9lgs/+L8okXLlYCaDSqYdB05Yb8Co+krvguXA==
dependencies:
"7zip-bin" "~5.2.0"
"@develar/schema-utils" "~2.6.5"
"@electron/notarize" "2.1.0"
"@electron/osx-sign" "1.0.5"
@ -267,12 +266,12 @@ app-builder-lib@24.9.1:
"@types/fs-extra" "9.0.13"
async-exit-hook "^2.0.1"
bluebird-lst "^1.0.9"
builder-util "24.8.1"
builder-util "24.9.4"
builder-util-runtime "9.2.3"
chromium-pickle-js "^0.2.0"
debug "^4.3.4"
ejs "^3.1.8"
electron-publish "24.8.1"
electron-publish "24.9.4"
form-data "^4.0.0"
fs-extra "^10.1.0"
hosted-git-info "^4.1.0"
@ -349,13 +348,13 @@ bluebird@^3.5.5:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
body-parser@1.20.1:
version "1.20.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
body-parser@1.20.2:
version "1.20.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
@ -363,7 +362,7 @@ body-parser@1.20.1:
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.1"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@ -418,10 +417,10 @@ builder-util-runtime@9.2.3:
debug "^4.3.4"
sax "^1.2.4"
builder-util@24.8.1:
version "24.8.1"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-24.8.1.tgz#594d45b0c86d1d17f5c7bebbb77405080b2571c2"
integrity sha512-ibmQ4BnnqCnJTNrdmdNlnhF48kfqhNzSeqFMXHLIl+o9/yhn6QfOaVrloZ9YUu3m0k3rexvlT5wcki6LWpjTZw==
builder-util@24.9.4:
version "24.9.4"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-24.9.4.tgz#8cde880e7c719285e9cb30e6850ddd5bf475ac04"
integrity sha512-YNon3rYjPSm4XDDho9wD6jq7vLRJZUy9FR+yFZnHoWvvdVCnZakL4BctTlPABP41MvIH5yk2cTZ2YfkOhGistQ==
dependencies:
"7zip-bin" "~5.2.0"
"@types/debug" "^4.1.6"
@ -572,7 +571,7 @@ content-disposition@0.5.4:
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4:
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
@ -690,13 +689,13 @@ dir-compare@^3.0.0:
buffer-equal "^1.0.0"
minimatch "^3.0.4"
dmg-builder@24.9.1:
version "24.9.1"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-24.9.1.tgz#04bf6c0dcd235f6214511f2358a78ed2b9379421"
integrity sha512-huC+O6hvHd24Ubj3cy2GMiGLe2xGFKN3klqVMLAdcbB6SWMd1yPSdZvV8W1O01ICzCCRlZDHiv4VrNUgnPUfbQ==
dmg-builder@24.12.0:
version "24.12.0"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-24.12.0.tgz#62a08162f2b3160a286d03ebb6db65c36a3711c7"
integrity sha512-nS22OyHUIYcK40UnILOtqC5Qffd1SN1Ljqy/6e+QR2H1wM3iNBrKJoEbDRfEmYYaALKNFRkKPqSbZKRsGUBdPw==
dependencies:
app-builder-lib "24.9.1"
builder-util "24.8.1"
app-builder-lib "24.12.0"
builder-util "24.9.4"
builder-util-runtime "9.2.3"
fs-extra "^10.1.0"
iconv-lite "^0.6.2"
@ -740,16 +739,16 @@ ejs@^3.1.8:
dependencies:
jake "^10.8.5"
electron-builder@24.9.1:
version "24.9.1"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-24.9.1.tgz#4aee03947963b829a7f48a850fe02c219311ef63"
integrity sha512-v7BuakDuY6sKMUYM8mfQGrwyjBpZ/ObaqnenU0H+igEL10nc6ht049rsCw2HghRBdEwJxGIBuzs3jbEhNaMDmg==
electron-builder@24.12.0:
version "24.12.0"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-24.12.0.tgz#95c41d14b3b1cc177db62715e42ef9fd27344491"
integrity sha512-dH4O9zkxFxFbBVFobIR5FA71yJ1TZSCvjZ2maCskpg7CWjBF+SNRSQAThlDyUfRuB+jBTMwEMzwARywmap0CSw==
dependencies:
app-builder-lib "24.9.1"
builder-util "24.8.1"
app-builder-lib "24.12.0"
builder-util "24.9.4"
builder-util-runtime "9.2.3"
chalk "^4.1.2"
dmg-builder "24.9.1"
dmg-builder "24.12.0"
fs-extra "^10.1.0"
is-ci "^3.0.0"
lazy-val "^1.0.5"
@ -757,23 +756,23 @@ electron-builder@24.9.1:
simple-update-notifier "2.0.0"
yargs "^17.6.2"
electron-publish@24.8.1:
version "24.8.1"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-24.8.1.tgz#4216740372bf4297a429543402a1a15ce8c3560b"
integrity sha512-IFNXkdxMVzUdweoLJNXSupXkqnvgbrn3J4vognuOY06LaS/m0xvfFYIf+o1CM8if6DuWYWoQFKPcWZt/FUjZPw==
electron-publish@24.9.4:
version "24.9.4"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-24.9.4.tgz#70db542763a78e4980e4e6409c203aef320d0d05"
integrity sha512-FghbeVMfxHneHjsG2xUSC0NMZYWOOWhBxfZKPTbibcJ0CjPH0Ph8yb5CUO62nqywXfA5u1Otq6K8eOdOixxmNg==
dependencies:
"@types/fs-extra" "^9.0.11"
builder-util "24.8.1"
builder-util "24.9.4"
builder-util-runtime "9.2.3"
chalk "^4.1.2"
fs-extra "^10.1.0"
lazy-val "^1.0.5"
mime "^2.5.2"
electron@29.0.0:
version "29.0.0"
resolved "https://registry.yarnpkg.com/electron/-/electron-29.0.0.tgz#2c1d34a95fc23a5b3530aa53de6eacca89a410b1"
integrity sha512-HhrRC5vWb6fAbWXP3A6ABwKUO9JvYSC4E141RzWFgnDBqNiNtabfmgC8hsVeCR65RQA2MLSDgC8uP52I9zFllQ==
electron@29.1.0:
version "29.1.0"
resolved "https://registry.yarnpkg.com/electron/-/electron-29.1.0.tgz#37f0e4915226db3c87bc54b187795272bf61fc39"
integrity sha512-giJVIm0sWVp+8V1GXrKqKTb+h7no0P3ooYqEd34AD9wMJzGnAeL+usj+R0155/0pdvvP1mgydnA7lcaFA2M9lw==
dependencies:
"@electron/get" "^2.0.0"
"@types/node" "^20.9.0"
@ -831,14 +830,14 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@4.18.2:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
express@4.18.3:
version "4.18.3"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.3.tgz#6870746f3ff904dee1819b82e4b51509afffb0d4"
integrity sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.1"
body-parser "1.20.2"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.5.0"
@ -1570,10 +1569,10 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"

View File

@ -1,15 +0,0 @@
#!/usr/bin/env sh
set -e
if [ -n "$PUID" ] && [ "$PUID" -ne 0 ] && \
[ -n "$PGID" ] && [ "$PGID" -ne 0 ] ; then
echo "info: creating the new user vikunja with $PUID:$PGID"
groupmod -g "$PGID" -o vikunja
usermod -u "$PUID" -o vikunja
chown -R vikunja:vikunja ./files/
chown vikunja:vikunja .
exec su vikunja -c /app/vikunja/vikunja "$@"
else
echo "info: creation of non-root user is skipped"
exec /app/vikunja/vikunja "$@"
fi

View File

@ -406,7 +406,7 @@ Environment path: `VIKUNJA_SENTRY_FRONTENDDSN`
### type
Database type to use. Supported types are mysql, postgres and sqlite.
Database type to use. Supported values are mysql, postgres and sqlite. Vikunja is able to run with MySQL 8.0+, Mariadb 10.2+, PostgreSQL 12+, and sqlite.
Default: `sqlite`

View File

@ -27,7 +27,6 @@ Create a directory for the project where all data and the compose file will live
Create a `docker-compose.yml` file with the following contents in your directory:
```yaml
version: '3'
@ -80,6 +79,18 @@ The number before the colon is the host port - This is where you can reach vikun
You'll need to change the value of the `VIKUNJA_SERVICE_PUBLICURL` environment variable to the public port or hostname where Vikunja is reachable.
## Ensure adequate file permissions
Vikunja runs as user `1000` and no group by default.
To be able to upload task attachments or change the background of project, Vikunja must be able to write into the `files` directory.
To do this, create the folder and chown it before starting the stack:
```
mkdir $PWD/files
chown 1000 $PWD/files
```
## Run it
Run `sudo docker-compose up` in your directory and take a look at the output you get.

View File

@ -15,8 +15,6 @@ It uses a proxy configuration to make it available under a domain.
For all available configuration options, see [configuration]({{< ref "config.md">}}).
Once deployed, you might want to change the [`PUID` and `GUID` settings]({{< ref "install.md">}}#setting-user-and-group-id-of-the-user-running-vikunja) or [set the time zone]({{< ref "config.md">}}#timezone).
After registering all your users, you might also want to [disable the user registration]({{<ref "config.md">}}#enableregistration).
<div class="notification is-warning">
@ -27,6 +25,23 @@ All examples on this page already reflect this and do not require additional wor
{{< table_of_contents >}}
## File permissions
Vikunja runs as user `1000` and no group by default.
You can use Docker's [`--user`](https://docs.docker.com/engine/reference/run/#user) flag to change that.
You must ensure Vikunja is able to write into the `files` directory.
To do this, create the folder and chown it before starting the stack:
```
mkdir $PWD/files
chown 1000 $PWD/files
```
You'll need to do this before running any of the examples on this page.
Vikunja will not try to aquire ownership of the files folder, as that would mean it had to run as root.
## PostgreSQL
Vikunja supports postgres, mysql and sqlite as a database backend. The examples on this page use mysql with a mariadb container.
@ -78,6 +93,13 @@ You'll also need to remove or change the `VIKUNJA_DATABASE_TYPE` to `sqlite` on
You can also remove the db section.
To run the container, you need to create the directories first and make sure they have all required permissions:
```
mkdir $PWD/files $PWD/db
chown 1000 $PWD/files $PWD/db
```
<div class="notification is-warning">
<b>NOTE:</b> If you'll use your instance with more than a handful of users, we recommend using mysql or postgres.
</div>
@ -91,6 +113,11 @@ If you want to make Vikunja available on a domain or need tls termination, check
Note that you need to change the [`VIKUNJA_SERVICE_PUBLICURL`]({{< ref "config.md" >}}#publicurl) environment variable to the public ip or hostname including the port (the docker host you're running this on) is reachable at, prefixed with `http://`.
Because the browser you'll use to access the Vikunja frontend uses that url to make the requests, it has to be able to reach it from the outside.
<div class="notification is-warning">
<b>NOTE:</b> You must ensure Vikunja has write permissions on the `files` directory before starting the stack.
To do this, <a href="#file-permissions">check out the related commands here</a>.
</div>
```yaml
version: '3'
@ -140,6 +167,11 @@ We also make a few assumptions here which you'll most likely need to adjust for
* The entrypoint you want to make vikunja available from is called `https`
* The tls cert resolver is called `acme`
<div class="notification is-warning">
<b>NOTE:</b> You must ensure Vikunja has write permissions on the `files` directory before starting the stack.
To do this, <a href="#file-permissions">check out the related commands here</a>.
</div>
```yaml
version: '3'
@ -203,6 +235,11 @@ vikunja.example.com {
Note that you need to change the [`VIKUNJA_SERVICE_PUBLICURL`]({{< ref "config.md" >}}#publicurl) environment variable to the ip (the docker host you're running this on) is reachable at.
Because the browser you'll use to access the Vikunja frontend uses that url to make the requests, it has to be able to reach that ip + port from the outside.
<div class="notification is-warning">
<b>NOTE:</b> You must ensure Vikunja has write permissions on the `files` directory before starting the stack.
To do this, <a href="#file-permissions">check out the related commands here</a>.
</div>
Docker Compose config:
```yaml
@ -286,10 +323,13 @@ The docker-compose file we're going to use is exactly the same from the [example
You may want to change the volumes to match the rest of your setup.
Once deployed, you might want to change the [`PUID` and `GUID` settings]({{< ref "install.md">}}#setting-user-and-group-id-of-the-user-running-vikunja) or [set the time zone]({{< ref "config.md">}}#timezone).
After registering all your users, you might also want to [disable the user registration]({{<ref "config.md">}}#enableregistration).
<div class="notification is-warning">
<b>NOTE:</b> You must ensure Vikunja has write permissions on the `files` directory before starting the stack.
To do this, <a href="#file-permissions">check out the related commands here</a>.
</div>
## Redis
While Vikunja has support to use redis as a caching backend, you'll probably not need it unless you're using Vikunja with more than a handful of users.

View File

@ -140,17 +140,23 @@ It will automatically run all necessary database migrations.
To get up and running quickly, use this command:
```
touch vikunja.db
docker run -p 3456:3456 -v $PWD/files:/app/vikunja/files -v $PWD/vikunja.db:/app/vikunja/vikunja.db vikunja/vikunja
mkdir $PWD/files $PWD/db
chown 1000 $PWD/files $PWD/db
docker run -p 3456:3456 -v $PWD/files:/app/vikunja/files -v $PWD/db:/db vikunja/vikunja
```
This will expose vikunja on port `3456` on the host running the container and use sqlite as database backend.
**Note**: The container runs as the user `1000` and no group by default.
You can use Docker's [`--user`](https://docs.docker.com/engine/reference/run/#user) flag to change that.
Make sure the new user has required permissions on the `db` and `files` folder.
You can mount a local configuration like so:
```
touch vikunja.db
docker run -p 3456:3456 -v /path/to/config/on/host.yml:/app/vikunja/config.yml:ro -v $PWD/files:/app/vikunja/files -v $PWD/vikunja.db:/app/vikunja/vikunja.db vikunja/vikunja
mkdir $PWD/files $PWD/db
chown 1000 $PWD/files $PWD/db
docker run -p 3456:3456 -v /path/to/config/on/host.yml:/app/vikunja/config.yml:ro -v $PWD/files:/app/vikunja/files -v $PWD/db:/db vikunja/vikunja
```
Though it is recommended to use environment variables or `.env` files to configure Vikunja in docker.
@ -163,13 +169,6 @@ Check out the [docker examples]({{<ref "full-docker-example.md">}}) for more adv
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 lose 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` environment 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
Check out the [docker examples]({{<ref "full-docker-example.md">}}) for more advanced configuration using docker compose.

View File

@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@8.15.3",
"packageManager": "pnpm@8.15.4",
"keywords": [
"todo",
"productivity",
@ -28,13 +28,15 @@
"serve": "pnpm run dev",
"preview": "vite preview --port 4173",
"preview:dev": "vite preview --outDir dist-dev --mode development --port 4173",
"preview:test": "vite preview --port 4173 --outDir dist-test",
"build": "vite build && workbox copyLibraries dist/",
"build:test": "vite build --outDir dist-test && workbox copyLibraries dist-dev/",
"build:modern-only": "BUILD_MODERN_ONLY=true vite build && workbox copyLibraries dist/",
"build:dev": "vite build --mode development --outDir dist-dev/",
"lint": "eslint 'src/**/*.{js,ts,vue}'",
"lint:fix": "pnpm run lint --fix",
"test:e2e": "start-server-and-test preview http://127.0.0.1:4173 'cypress run --e2e --browser chrome'",
"test:e2e-record": "start-server-and-test preview http://127.0.0.1:4173 'cypress run --e2e --browser chrome --record'",
"test:e2e-record-test": "start-server-and-test preview:test http://127.0.0.1:4173 'cypress run --e2e --browser chrome --record'",
"test:e2e-dev-dev": "start-server-and-test preview:dev http://127.0.0.1:4173 'cypress open --e2e'",
"test:e2e-dev": "start-server-and-test preview http://127.0.0.1:4173 'cypress open --e2e'",
"test:unit": "vitest --dir ./src",
@ -56,45 +58,45 @@
"@infectoone/vue-ganttastic": "2.2.0",
"@intlify/unplugin-vue-i18n": "2.0.0",
"@kyvg/vue3-notification": "3.2.0",
"@sentry/tracing": "7.102.0",
"@sentry/vue": "7.102.0",
"@tiptap/core": "2.2.3",
"@tiptap/extension-blockquote": "2.2.3",
"@tiptap/extension-bold": "2.2.3",
"@tiptap/extension-bullet-list": "2.2.3",
"@tiptap/extension-code": "2.2.3",
"@tiptap/extension-code-block-lowlight": "2.2.3",
"@tiptap/extension-document": "2.2.3",
"@tiptap/extension-dropcursor": "2.2.3",
"@tiptap/extension-gapcursor": "2.2.3",
"@tiptap/extension-hard-break": "2.2.3",
"@tiptap/extension-heading": "2.2.3",
"@tiptap/extension-history": "2.2.3",
"@tiptap/extension-horizontal-rule": "2.2.3",
"@tiptap/extension-image": "2.2.3",
"@tiptap/extension-italic": "2.2.3",
"@tiptap/extension-link": "2.2.3",
"@tiptap/extension-list-item": "2.2.3",
"@tiptap/extension-ordered-list": "2.2.3",
"@tiptap/extension-paragraph": "2.2.3",
"@tiptap/extension-placeholder": "2.2.3",
"@tiptap/extension-strike": "2.2.3",
"@tiptap/extension-table": "2.2.3",
"@tiptap/extension-table-cell": "2.2.3",
"@tiptap/extension-table-header": "2.2.3",
"@tiptap/extension-table-row": "2.2.3",
"@tiptap/extension-task-item": "2.2.3",
"@tiptap/extension-task-list": "2.2.3",
"@tiptap/extension-text": "2.2.3",
"@tiptap/extension-typography": "2.2.3",
"@tiptap/extension-underline": "2.2.3",
"@tiptap/pm": "2.2.3",
"@tiptap/suggestion": "2.2.3",
"@tiptap/vue-3": "2.2.3",
"@sentry/tracing": "7.103.0",
"@sentry/vue": "7.103.0",
"@tiptap/core": "2.2.4",
"@tiptap/extension-blockquote": "2.2.4",
"@tiptap/extension-bold": "2.2.4",
"@tiptap/extension-bullet-list": "2.2.4",
"@tiptap/extension-code": "2.2.4",
"@tiptap/extension-code-block-lowlight": "2.2.4",
"@tiptap/extension-document": "2.2.4",
"@tiptap/extension-dropcursor": "2.2.4",
"@tiptap/extension-gapcursor": "2.2.4",
"@tiptap/extension-hard-break": "2.2.4",
"@tiptap/extension-heading": "2.2.4",
"@tiptap/extension-history": "2.2.4",
"@tiptap/extension-horizontal-rule": "2.2.4",
"@tiptap/extension-image": "2.2.4",
"@tiptap/extension-italic": "2.2.4",
"@tiptap/extension-link": "2.2.4",
"@tiptap/extension-list-item": "2.2.4",
"@tiptap/extension-ordered-list": "2.2.4",
"@tiptap/extension-paragraph": "2.2.4",
"@tiptap/extension-placeholder": "2.2.4",
"@tiptap/extension-strike": "2.2.4",
"@tiptap/extension-table": "2.2.4",
"@tiptap/extension-table-cell": "2.2.4",
"@tiptap/extension-table-header": "2.2.4",
"@tiptap/extension-table-row": "2.2.4",
"@tiptap/extension-task-item": "2.2.4",
"@tiptap/extension-task-list": "2.2.4",
"@tiptap/extension-text": "2.2.4",
"@tiptap/extension-typography": "2.2.4",
"@tiptap/extension-underline": "2.2.4",
"@tiptap/pm": "2.2.4",
"@tiptap/suggestion": "2.2.4",
"@tiptap/vue-3": "2.2.4",
"@types/is-touch-device": "1.0.2",
"@types/lodash.clonedeep": "4.5.9",
"@vueuse/core": "10.8.0",
"@vueuse/router": "10.8.0",
"@vueuse/core": "10.9.0",
"@vueuse/router": "10.9.0",
"axios": "1.6.7",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
@ -116,7 +118,7 @@
"sortablejs": "1.15.2",
"tippy.js": "6.3.7",
"ufo": "1.4.0",
"vue": "3.4.19",
"vue": "3.4.21",
"vue-advanced-cropper": "2.8.8",
"vue-flatpickr-component": "11.0.4",
"vue-i18n": "9.9.1",
@ -128,9 +130,9 @@
"@4tw/cypress-drag-drop": "2.2.5",
"@cypress/vite-dev-server": "5.0.7",
"@cypress/vue": "6.0.0",
"@faker-js/faker": "8.4.0",
"@faker-js/faker": "8.4.1",
"@histoire/plugin-screenshot": "0.17.8",
"@histoire/plugin-vue": "0.17.9",
"@histoire/plugin-vue": "0.17.12",
"@rushstack/eslint-patch": "1.7.2",
"@tsconfig/node18": "18.2.2",
"@types/codemirror": "5.60.15",
@ -139,43 +141,43 @@
"@types/is-touch-device": "1.0.2",
"@types/lodash.debounce": "4.0.9",
"@types/marked": "5.0.2",
"@types/node": "20.11.10",
"@types/node": "20.11.22",
"@types/postcss-preset-env": "7.7.0",
"@types/sortablejs": "1.15.7",
"@typescript-eslint/eslint-plugin": "7.0.1",
"@typescript-eslint/parser": "7.0.1",
"@vitejs/plugin-legacy": "5.3.0",
"@vitejs/plugin-vue": "5.0.3",
"@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "7.1.0",
"@typescript-eslint/parser": "7.1.0",
"@vitejs/plugin-legacy": "5.3.1",
"@vitejs/plugin-vue": "5.0.4",
"@vue/eslint-config-typescript": "12.0.0",
"@vue/test-utils": "2.4.4",
"@vue/tsconfig": "0.5.1",
"autoprefixer": "10.4.17",
"browserslist": "4.22.3",
"caniuse-lite": "1.0.30001581",
"css-has-pseudo": "6.0.1",
"browserslist": "4.23.0",
"caniuse-lite": "1.0.30001591",
"css-has-pseudo": "6.0.2",
"csstype": "3.1.3",
"cypress": "13.6.3",
"esbuild": "0.20.0",
"eslint": "8.56.0",
"eslint-plugin-vue": "9.20.1",
"happy-dom": "13.3.5",
"cypress": "13.6.6",
"esbuild": "0.20.1",
"eslint": "8.57.0",
"eslint-plugin-vue": "9.22.0",
"happy-dom": "13.6.2",
"histoire": "0.17.9",
"postcss": "8.4.33",
"postcss": "8.4.35",
"postcss-easing-gradients": "3.0.1",
"postcss-easings": "4.0.0",
"postcss-focus-within": "8.0.1",
"postcss-preset-env": "9.3.0",
"rollup": "4.9.6",
"postcss-preset-env": "9.4.0",
"rollup": "4.12.0",
"rollup-plugin-visualizer": "5.12.0",
"sass": "1.70.0",
"sass": "1.71.1",
"start-server-and-test": "2.0.3",
"typescript": "5.3.3",
"vite": "5.0.12",
"vite": "5.1.4",
"vite-plugin-inject-preload": "1.3.3",
"vite-plugin-pwa": "0.17.5",
"vite-plugin-sentry": "1.3.0",
"vite-plugin-pwa": "0.19.1",
"vite-plugin-sentry": "1.4.0",
"vite-svg-loader": "5.1.0",
"vitest": "1.2.2",
"vitest": "1.3.1",
"vue-tsc": "1.8.27",
"wait-on": "7.2.0",
"workbox-cli": "7.0.0"

File diff suppressed because it is too large Load Diff

View File

@ -51,10 +51,12 @@ Hi ${process.env.DRONE_COMMIT_AUTHOR}!
Thank you for creating a PR!
I've deployed the changes of this PR on a preview environment under this URL: ${fullPreviewUrl}
I've deployed the frontend changes of this PR on a preview environment under this URL: ${fullPreviewUrl}
You can use this url to view the changes live and test them out.
You will need to manually connect this to an api running somehwere. The easiest to use is https://try.vikunja.io/.
You will need to manually connect this to an api running somewhere. The easiest to use is https://try.vikunja.io/.
This preview does not contain any changes made to the api, only the frontend.
Have a nice day!

View File

@ -1 +1 @@
d58cd9ebc135407aa29d093b046d84b72ec7073b3f08cedfdbb936318a0ad3e272fab921d3ff91a82c1a7059fcdecd7b ./scripts/deploy-preview-netlify.mjs
2ba5ae4c831fd749296d92f92c5f89339030e22b80be62b1253dc26982e8fd0082e354f884a3ba15293e0b96317ec758 ./scripts/deploy-preview-netlify.mjs

View File

@ -1,5 +1,8 @@
<template>
<BaseButton class="dropdown-item">
<BaseButton
class="dropdown-item"
:class="{'is-disabled': disabled}"
>
<span
v-if="icon"
class="icon is-small"
@ -21,6 +24,7 @@ import type {IconProp} from '@fortawesome/fontawesome-svg-core'
export interface DropDownItemProps extends /* @vue-ignore */ BaseButtonProps {
icon?: IconProp,
iconClass?: object | string,
disabled?: boolean,
}
defineProps<DropDownItemProps>()

View File

@ -67,8 +67,10 @@
{{ $t('menu.duplicate') }}
</DropdownItem>
<DropdownItem
v-tooltip="isDefaultProject ? $t('menu.cantArchiveIsDefault') : ''"
:to="{ name: 'project.settings.archive', params: { projectId: project.id } }"
icon="archive"
:disabled="isDefaultProject"
>
{{ $t('menu.archive') }}
</DropdownItem>
@ -95,9 +97,11 @@
{{ $t('menu.createProject') }}
</DropdownItem>
<DropdownItem
v-tooltip="isDefaultProject ? $t('menu.cantDeleteIsDefault') : ''"
:to="{ name: 'project.settings.delete', params: { projectId: project.id } }"
icon="trash-alt"
class="has-text-danger"
:disabled="isDefaultProject"
>
{{ $t('menu.delete') }}
</DropdownItem>
@ -106,7 +110,7 @@
</template>
<script setup lang="ts">
import {ref, computed, watchEffect, type PropType} from 'vue'
import {computed, type PropType, ref, watchEffect} from 'vue'
import BaseButton from '@/components/base/BaseButton.vue'
import Dropdown from '@/components/misc/dropdown.vue'
@ -118,6 +122,7 @@ import type {ISubscription} from '@/modelTypes/ISubscription'
import {isSavedFilter} from '@/services/savedFilter'
import {useConfigStore} from '@/stores/config'
import {useProjectStore} from '@/stores/projects'
import {useAuthStore} from '@/stores/auth'
const props = defineProps({
project: {
@ -146,4 +151,7 @@ function setSubscriptionInStore(sub: ISubscription) {
}
projectStore.setProject(updatedProject)
}
const authStore = useAuthStore()
const isDefaultProject = computed(() => props.project?.id === authStore.settings.defaultProjectId)
</script>

View File

@ -17,6 +17,7 @@
bar-end="endDate"
:grid="true"
:width="ganttChartWidth"
:color-scheme="GANTT_COLOR_SCHEME"
@dragendBar="updateGanttTask"
@dblclickBar="openTask"
>
@ -59,7 +60,7 @@ import {
extendDayjs,
GGanttChart,
GGanttRow,
type GanttBarObject,
type GanttBarObject, type ColorScheme,
} from '@infectoone/vue-ganttastic'
import Loading from '@/components/misc/loading.vue'
@ -113,6 +114,16 @@ const ganttChartWidth = computed(() => {
const ganttBars = ref<GanttBarObject[][]>([])
const GANTT_COLOR_SCHEME: ColorScheme = {
primary: 'var(--grey-100)',
secondary: 'var(--grey-300)',
ternary: 'var(--grey-500)',
quartenary: 'var(--grey-600)',
hoverHighlight: 'var(--grey-700)',
text: 'var(--grey-800)',
background: 'var(--white)',
}
/**
* Update ganttBars when tasks change
*/

View File

@ -59,7 +59,7 @@ const hasDelete = computed(() => typeof remove !== 'undefined' && !disabled)
}
&:hover .assignee:not(:first-child) {
margin-left: -1rem;
margin-left: -0.5rem;
}
}
@ -68,7 +68,7 @@ const hasDelete = computed(() => typeof remove !== 'undefined' && !disabled)
transition: all $transition;
&:not(:first-child) {
margin-left: -1.5rem;
margin-left: -1rem;
}
:deep(.user img) {

View File

@ -27,7 +27,8 @@ defineProps({
display: inline;
:deep(.tag) {
margin-bottom: .25rem;
margin-top: .125rem;
margin-bottom: .125rem;
}
}
</style>

View File

@ -103,7 +103,7 @@ import {getHexColor} from '@/models/task'
import type {ITask} from '@/modelTypes/ITask'
import PriorityLabel from '@/components/tasks/partials/priorityLabel.vue'
import Labels from '@/components/tasks/partials//labels.vue'
import Labels from '@/components/tasks/partials/labels.vue'
import ChecklistSummary from '@/components/tasks/partials/checklist-summary.vue'
import ColorBubble from '@/components/misc/colorBubble.vue'
@ -197,6 +197,7 @@ const project = computed(() => projectStore.projects[task.projectId])
span.parent-tasks {
color: var(--grey-500);
width: auto;
margin-left: .25rem;
}
}
</style>

View File

@ -978,7 +978,9 @@
"setBackground": "Set background",
"share": "Share",
"newProject": "New project",
"createProject": "Create project"
"createProject": "Create project",
"cantArchiveIsDefault": "You cannot archive this because it is your default project.",
"cantDeleteIsDefault": "You cannot delete this because it is your default project."
},
"apiConfig": {
"url": "Vikunja URL",

View File

@ -246,8 +246,9 @@ export const useKanbanStore = defineStore('kanban', () => {
}
async function loadNextTasksForBucket(
{projectId, ps = {}, bucketId} :
{projectId: IProject['id'], ps, bucketId: IBucket['id']},
projectId: IProject['id'],
ps,
bucketId: IBucket['id'],
) {
const isLoading = bucketLoading.value[bucketId] ?? false
if (isLoading) {

View File

@ -53,7 +53,7 @@
<p>{{ $t('migrate.inProgress') }}</p>
</div>
</template>
<div v-else-if="lastMigrationStartedAt && lastMigrationFinishedAt === null">
<div v-else-if="!migrationJustStarted && lastMigrationStartedAt && lastMigrationFinishedAt === null">
<Message class="mb-4">
{{ $t('migrate.migrationInProgress') }}
</Message>
@ -145,6 +145,7 @@ const lastMigrationFinishedAt = ref<Date | null>(null)
const lastMigrationStartedAt = ref<Date | null>(null)
const message = ref('')
const migratorAuthCode = ref('')
const migrationJustStarted = ref(false)
const migrator = computed<Migrator>(() => MIGRATORS[props.service])
@ -207,12 +208,15 @@ async function migrate() {
}
try {
const result = migrator.value.isFileMigrator
? await migrationFileService.migrate(migrationConfig as File)
: await migrationService.migrate(migrationConfig as MigrationConfig)
message.value = result.message
const projectStore = useProjectStore()
return projectStore.loadProjects()
if (migrator.value.isFileMigrator) {
const result = await migrationFileService.migrate(migrationConfig as File)
message.value = result.message
const projectStore = useProjectStore()
return projectStore.loadProjects()
}
await migrationService.migrate(migrationConfig as MigrationConfig)
migrationJustStarted.value = true
} catch (e) {
console.log(e)
} finally {

View File

@ -416,11 +416,11 @@ function handleTaskContainerScroll(id: IBucket['id'], projectId: IProject['id'],
return
}
kanbanStore.loadNextTasksForBucket({
projectId: projectId,
params: params.value,
bucketId: id,
})
kanbanStore.loadNextTasksForBucket(
projectId,
params.value,
id,
)
}
function updateTasks(bucketId: IBucket['id'], tasks: IBucket['tasks']) {

8
go.mod
View File

@ -52,7 +52,7 @@ require (
github.com/olekukonko/tablewriter v0.0.5
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.18.0
github.com/prometheus/client_golang v1.19.0
github.com/redis/go-redis/v9 v9.5.1
github.com/robfig/cron/v3 v3.0.1
github.com/samedi/caldav-go v3.0.0+incompatible
@ -66,7 +66,7 @@ require (
github.com/ulule/limiter/v3 v3.11.2
github.com/wneessen/go-mail v0.4.0
github.com/yuin/goldmark v1.7.0
golang.org/x/crypto v0.19.0
golang.org/x/crypto v0.20.0
golang.org/x/image v0.15.0
golang.org/x/oauth2 v0.17.0
golang.org/x/sync v0.6.0
@ -148,7 +148,7 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@ -178,7 +178,7 @@ require (
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.13.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/protobuf v1.31.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect

8
go.sum
View File

@ -450,6 +450,8 @@ github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
@ -458,6 +460,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
@ -628,6 +632,8 @@ golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -778,6 +784,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -158,7 +158,7 @@ func setRootPath() {
func setGoFiles() {
// GOFILES := $(shell find . -name "*.go" -type f ! -path "*/bindata.go")
files, err := runCmdWithOutput("find", ".", "-name", "*.go", "-type", "f", "!", "-path", "*/bindata.go")
files, err := runCmdWithOutput("find", "./pkg", "-name", "*.go", "-type", "f", "!", "-path", "*/bindata.go")
if err != nil {
fmt.Printf("Error getting go files: %s\n", err)
os.Exit(1)

View File

@ -234,6 +234,7 @@
title: Test25
owner_id: 6
parent_project_id: 12
position: 25
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-
@ -241,6 +242,7 @@
title: Test26
owner_id: 6
parent_project_id: 25
position: 26
updated: 2018-12-02 15:13:12
created: 2018-12-01 15:13:12
-

View File

@ -368,7 +368,7 @@ func TestTaskCollection(t *testing.T) {
t.Run("by priority", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}}, nil)
require.NoError(t, err)
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
})
t.Run("by priority desc", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"desc"}}, nil)
@ -378,7 +378,7 @@ func TestTaskCollection(t *testing.T) {
t.Run("by priority asc", func(t *testing.T) {
rec, err := testHandler.testReadAllWithUser(url.Values{"sort_by": []string{"priority"}, "order_by": []string{"asc"}}, nil)
require.NoError(t, err)
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
assert.Contains(t, rec.Body.String(), `{"id":33,"title":"task #33 with percent done","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0.5,"identifier":"test1-17","index":17,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":35,"title":"task #35","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":21,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":[{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"labels":[{"id":4,"title":"Label #4 - visible via other task","description":"","hex_color":"","created_by":{"id":2,"name":"","username":"user2","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"},"created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}],"hex_color":"","percent_done":0,"identifier":"test21-1","index":1,"related_tasks":{"related":[{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null},{"id":1,"title":"task #1","description":"Lorem Ipsum","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":1,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"","index":1,"related_tasks":null,"attachments":null,"cover_image_attachment_id":0,"is_favorite":true,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":1,"position":2,"kanban_position":0,"created_by":null}]},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":19,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}},{"id":39,"title":"task #39","description":"","done":false,"done_at":"0001-01-01T00:00:00Z","due_date":"0001-01-01T00:00:00Z","reminders":null,"project_id":25,"repeat_after":0,"repeat_mode":0,"priority":0,"start_date":"0001-01-01T00:00:00Z","end_date":"0001-01-01T00:00:00Z","assignees":null,"labels":null,"hex_color":"","percent_done":0,"identifier":"#0","index":0,"related_tasks":{},"attachments":null,"cover_image_attachment_id":0,"is_favorite":false,"created":"2018-12-01T01:12:04Z","updated":"2018-12-01T01:12:04Z","bucket_id":0,"position":0,"kanban_position":0,"created_by":{"id":1,"name":"","username":"user1","created":"2018-12-01T15:13:12Z","updated":"2018-12-02T15:13:12Z"}}]`)
})
// should equal duedate asc
t.Run("by due_date", func(t *testing.T) {

View File

@ -17,6 +17,7 @@
package models
import (
"fmt"
"math"
"strconv"
"strings"
@ -418,67 +419,49 @@ func getUserProjectsStatement(parentProjectIDs []int64, userID int64, search str
parentCondition,
builder.NotIn("l.id", parentProjectIDs),
)).
OrderBy("position").
GroupBy("l.id")
}
func getAllProjectsForUser(s *xorm.Session, userID int64, parentProjectIDs []int64, opts *projectOptions, projects *[]*Project, oldTotalCount int64, archivedProjects map[int64]bool) (resultCount int, totalCount int64, err error) {
func getAllProjectsForUser(s *xorm.Session, userID int64, opts *projectOptions) (projects []*Project, totalCount int64, err error) {
limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
query := getUserProjectsStatement(parentProjectIDs, userID, opts.search, opts.getArchived)
if limit > 0 {
query = query.Limit(limit, start)
query := getUserProjectsStatement(nil, userID, opts.search, opts.getArchived)
querySQLString, args, err := query.ToSQL()
if err != nil {
return nil, 0, err
}
var limitSQL string
if limit > 0 {
limitSQL = fmt.Sprintf("LIMIT %d OFFSET %d", limit, start)
}
baseQuery := querySQLString + `
UNION ALL
SELECT p.* FROM projects p
INNER JOIN all_projects ap ON p.parent_project_id = ap.id`
currentProjects := []*Project{}
err = s.SQL(query).Find(&currentProjects)
err = s.SQL(`WITH RECURSIVE all_projects as (`+baseQuery+`)
SELECT DISTINCT * FROM all_projects ORDER BY position `+limitSQL, args...).Find(&currentProjects)
if err != nil {
return 0, 0, err
return
}
if len(currentProjects) == 0 {
return 0, oldTotalCount, err
return nil, 0, err
}
query = getUserProjectsStatement(parentProjectIDs, userID, opts.search, opts.getArchived)
totalCount, err = s.
SQL(query.Select("count(*)")).
SQL(`WITH RECURSIVE all_projects as (`+baseQuery+`)
SELECT count(*) FROM all_projects GROUP BY all_projects.id`, args...).
Count(&Project{})
if err != nil {
return 0, 0, err
return nil, 0, err
}
parentIDsMap := make(map[int64]bool, len(parentProjectIDs))
for _, id := range parentProjectIDs {
parentIDsMap[id] = true
}
for _, project := range currentProjects {
parentIDsMap[project.ID] = true
}
newParentIDs := []int64{}
for _, project := range currentProjects {
if project.IsArchived {
archivedProjects[project.ID] = true
}
if archivedProjects[project.ParentProjectID] {
project.IsArchived = true
}
// Filter out parent project ids which we're not looking for to avoid leaking
// information about parent projects
if !parentIDsMap[project.ParentProjectID] {
project.ParentProjectID = 0
}
newParentIDs = append(newParentIDs, project.ID)
}
*projects = append(*projects, currentProjects...)
// If we don't reset the limit for subprojects, it will be impossible to fetch all subprojects.
opts.page = -1
return getAllProjectsForUser(s, userID, newParentIDs, opts, projects, oldTotalCount+totalCount, archivedProjects)
return currentProjects, totalCount, err
}
// Gets the projects with their children without any tasks
@ -488,9 +471,7 @@ func getRawProjectsForUser(s *xorm.Session, opts *projectOptions) (projects []*P
return nil, 0, 0, err
}
allProjects := []*Project{}
archivedProjects := make(map[int64]bool)
resultCount, totalItems, err = getAllProjectsForUser(s, fullUser.ID, nil, opts, &allProjects, 0, archivedProjects)
allProjects, totalItems, err := getAllProjectsForUser(s, fullUser.ID, opts)
if err != nil {
return
}

View File

@ -349,11 +349,9 @@ func TestProject_ReadAll(t *testing.T) {
t.Run("all", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
projects := []*Project{}
archivedProjects := make(map[int64]bool)
_, _, err := getAllProjectsForUser(s, 1, nil, &projectOptions{}, &projects, 0, archivedProjects)
projects, _, err := getAllProjectsForUser(s, 6, &projectOptions{})
require.NoError(t, err)
assert.Len(t, projects, 24)
assert.Len(t, projects, 25)
_ = s.Close()
})
t.Run("only child projects for one project", func(t *testing.T) {
@ -369,12 +367,12 @@ func TestProject_ReadAll(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, reflect.Slice, reflect.TypeOf(projects3).Kind())
ls := projects3.([]*Project)
assert.Len(t, ls, 26)
assert.Len(t, ls, 28)
assert.Equal(t, int64(3), ls[0].ID) // Project 3 has a position of 1 and should be sorted first
assert.Equal(t, int64(1), ls[1].ID)
assert.Equal(t, int64(6), ls[2].ID)
assert.Equal(t, int64(-1), ls[24].ID)
assert.Equal(t, int64(-2), ls[25].ID)
assert.Equal(t, int64(-1), ls[26].ID)
assert.Equal(t, int64(-2), ls[27].ID)
_ = s.Close()
})
t.Run("projects for nonexistant user", func(t *testing.T) {

View File

@ -656,6 +656,18 @@ func TestTaskCollection_ReadAll(t *testing.T) {
Created: time.Unix(1543626724, 0).In(loc),
Updated: time.Unix(1543626724, 0).In(loc),
}
task39 := &Task{
ID: 39,
Title: "task #39",
Identifier: "#0",
CreatedByID: 1,
CreatedBy: user1,
ProjectID: 25,
RelatedTasks: map[RelationKind][]*Task{},
BucketID: 0,
Created: time.Unix(1543626724, 0).In(loc),
Updated: time.Unix(1543626724, 0).In(loc),
}
type fields struct {
ProjectID int64
@ -728,6 +740,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
task32,
task33,
task35,
task39,
},
wantErr: false,
},
@ -772,6 +785,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
task3,
task1,
task2,
task39,
},
wantErr: false,
},
@ -943,6 +957,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
task32, // has nil dates
task33, // has nil dates
task35, // has nil dates
task39, // has nil dates
},
wantErr: false,
},
@ -1202,6 +1217,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
task32,
task33,
task35,
task39,
},
},
{
@ -1218,6 +1234,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
task6,
task5,
// The other ones don't have a due date
task39,
task35,
task33,
task32,

View File

@ -1,82 +1,83 @@
publiccode-yml-version: '0.2'
name: 'Vikunja'
url: 'https://vikunja.io'
url: 'https://code.vikunja.io/vikunja'
landingURL: 'https://vikunja.io'
isBasedOn: 'https://go.dev/'
softwareVersion: 'v0.23.0'
releaseDate: '2024-02-10'
logo: 'https://vikunja.io/images/vikunja-logo.svg'
platforms:
- 'linux'
- 'lxc'
- 'docker'
- 'k8s'
- 'web'
- 'linux'
- 'windows'
- 'mac'
- 'lxc'
- 'docker'
- 'k8s'
- 'debian'
categories:
- 'agile-project-management'
- 'enterprise-project-management'
- 'project-management'
- 'task-management'
- 'workflow-management'
- 'agile-project-management'
- 'enterprise-project-management'
- 'project-management'
- 'task-management'
- 'workflow-management'
usedBy:
- 'City of Treuchtlingen'
roadmap:
'https://my.vikunja.cloud/projects/16/list#share-auth-token=QFyzYEmEYfSyQfTOmIRSwLUpkFjboaBqQCnaPmWd'
- 'City of Treuchtlingen'
roadmap: 'https://my.vikunja.cloud/projects/16/list#share-auth-token=QFyzYEmEYfSyQfTOmIRSwLUpkFjboaBqQCnaPmWd'
developmentStatus: 'stable'
softwareType: 'webservice'
softwareType: 'standalone/web'
intendedAudience:
scope:
- 'Providers of standard based workflow management services who
wish or are required to use Open Source solutions'
scope:
- 'Providers of standard based workflow management services who wish or are required to use Open Source solutions'
description:
en: ' The open-source, self-hostable to-do app. Organize everything,
on all platforms.'
en: ' The open-source, self-hostable to-do app. Organize everything, on all platforms.'
documentation: 'https://vikunja.io/docs/'
apiDocumentation: 'https://vikunja.io/docs/api-documentation/'
features:
- 'workflow management'
- 'CalDAV provider'
- 'kanban tool'
- 'gantt display tool'
- 'ticket system'
- 'workflow management'
- 'CalDAV provider'
- 'kanban tool'
- 'gantt display tool'
- 'ticket system'
screenshots:
- 'https://vikunja.io/images/vikunja/09-task-detail-dark.webp'
- 'https://vikunja.io/images/vikunja/09-task-detail-dark.webp'
videos:
- 'none'
- 'none'
legal:
license: 'GNU AFFERO GENERAL PUBLIC LICENSE Version 3'
mainCopyrightOwner: 'Konrad Langenberg Software'
repoOwner: 'Konrad Langenberg Software'
authorsFile: 'hello@vikunja.io'
license: 'GNU AFFERO GENERAL PUBLIC LICENSE Version 3'
mainCopyrightOwner: 'Konrad Langenberg Software'
repoOwner: 'Konrad Langenberg Software'
authorsFile: 'hello@vikunja.io'
maintenance:
type: 'https://vikunja.cloud/'
contractors:
- name: 'Vikunja Cloud '
type: 'https://vikunja.cloud/'
contractors:
- name: 'Vikunja Cloud '
localisation:
localisationReady: 'true'
availableLanguages:
- 'en'
- 'de-de'
- 'ru-ru'
- 'fr-fr'
- 'vi-vn'
- 'it-it'
- 'cs-cz'
- 'pl-pl'
- 'nl-nl'
- 'pt-pt'
- 'zh-cn'
- 'no-no'
- 'es-es'
- 'da-dk'
- 'ja-jp'
- 'hu-hu'
- 'sl-si'
localisationReady: 'true'
availableLanguages:
- 'en'
- 'de-de'
- 'ru-ru'
- 'fr-fr'
- 'vi-vn'
- 'it-it'
- 'cs-cz'
- 'pl-pl'
- 'nl-nl'
- 'pt-pt'
- 'zh-cn'
- 'no-no'
- 'es-es'
- 'da-dk'
- 'ja-jp'
- 'hu-hu'
- 'sl-si'
dependsOn:
open:
- name: 'CalDAV'
- name: 'GoLANG'
- name: 'Vue.js'
- name: 'HTTP-Proxy'
- name: 'SSL'
- name: 'Unit file (SystemD)'
- name: 'Firewall Rules (Web: 443)'
open:
- name: 'CalDAV'
- name: 'GoLANG'
- name: 'Vue.js'
- name: 'HTTP-Proxy'
- name: 'SSL'
- name: 'Unit file (SystemD)'
- name: 'Firewall Rules (Web: 443)'