forked from vikunja/vikunja
Compare commits
149 Commits
renovate/f
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
6987ddae2a | ||
|
fe262014d3 | ||
|
4f474117b0 | ||
|
4bb09b69be | ||
379b0b24b3 | |||
6db8728420 | |||
a34ca20c1a | |||
a4a0ea973a | |||
fc4303a778 | |||
4f1f96f1e9 | |||
10ff864e0c | |||
89b01e86bc | |||
a3932a0a19 | |||
e42a605597 | |||
67f55510bf | |||
178cd8c392 | |||
ed4da96ab1 | |||
f18cde269b | |||
09d446765d | |||
a99e7f9aa3 | |||
e93e48c4c2 | |||
06688fa8d4 | |||
1a15c865e2 | |||
7b9a72ebe1 | |||
a0f4bdbdad | |||
eea356e570 | |||
5b70609ba7 | |||
6b1e67485b | |||
5d127c2897 | |||
1275dfc260 | |||
997fb6bc54 | |||
b2e5de88ff | |||
baa5d14ca6 | |||
2d5c496397 | |||
b8533d2bfc | |||
8a82093233 | |||
3d39fc3960 | |||
43e13d9cdd | |||
a0e812395f | |||
2e5c19352e | |||
1ffb93b63c | |||
1bf8659423 | |||
7629c8426e | |||
a83acc0300 | |||
837360f122 | |||
34c31e2f03 | |||
829f504d3b | |||
899bc67853 | |||
b419df3156 | |||
52d0930034 | |||
318f00d252 | |||
4d11dd0383 | |||
e40a0043d4 | |||
e532979101 | |||
503036abff | |||
70265e176a | |||
68873e1d0d | |||
be66ec8608 | |||
814c142b71 | |||
ea75657d45 | |||
ff1730e323 | |||
f120d72211 | |||
a2951570a7 | |||
f4efdaa5de | |||
32edef2d38 | |||
44c1f0d281 | |||
2dab2ccedd | |||
827c43fe12 | |||
415c6380a5 | |||
ebe25ee2d7 | |||
cc5f48eb74 | |||
3969f6ae66 | |||
5ab720d709 | |||
7ae38c5ac1 | |||
162741e940 | |||
205f330f8a | |||
a12c169ce8 | |||
2facbae0d7 | |||
77a779acea | |||
89e349f2fd | |||
77feae47d6 | |||
2fb263a109 | |||
641fec1215 | |||
18374c2e52 | |||
9921551f96 | |||
b26c359ce6 | |||
d7d3ffeebd | |||
0b61885e89 | |||
8d5cb335bd | |||
71e62541a1 | |||
ee065d9238 | |||
28ed754c66 | |||
390f71b0c6 | |||
8c6d98bb02 | |||
ef30868514 | |||
75eb878e33 | |||
1ab6fef70a | |||
6defc4cba2 | |||
3abdf2ccdd | |||
001268a33e | |||
3129051a9c | |||
45c97a456d | |||
1255bdc4ab | |||
99856b2031 | |||
eec53e8a54 | |||
d4a389279c | |||
3a65324a7d | |||
bec11d42fc | |||
d2eeac2dbe | |||
05e7468135 | |||
4babe72f24 | |||
e9fe927644 | |||
7cefb86a78 | |||
6c4122264a | |||
4162c64ed9 | |||
2e57b6e409 | |||
8752ae2a0b | |||
7edb53ca12 | |||
25a03d1789 | |||
5ab9fb89bb | |||
55e271b329 | |||
6ea5266c55 | |||
ddf6a30e19 | |||
b1745edfa6 | |||
1984527fae | |||
11b72765c9 | |||
d36b1608cf | |||
adf7b73b6b | |||
e12e9f1bf2 | |||
60006aeac5 | |||
ca68b52991 | |||
2d32d900c8 | |||
119c68be9d | |||
78df83ee69 | |||
db2ec45378 | |||
d7dc209f15 | |||
0f28767acc | |||
a0e770438d | |||
1899f16207 | |||
f77ba0d4f1 | |||
b3228794c7 | |||
4a66c2202f | |||
aab36eb89c | |||
8b3cf2ed7e | |||
9c45d9ca15 | |||
81455242ae | |||
6c5194b892 | |||
392f5cd468 | |||
9633a705cd |
|
@ -1,9 +1,15 @@
|
|||
files/
|
||||
dist/
|
||||
logs/
|
||||
docs/
|
||||
|
||||
Dockerfile
|
||||
docker-manifest.tmpl
|
||||
docker-manifest-unstable.tmpl
|
||||
*.db
|
||||
*.zip
|
||||
|
||||
# Frontend
|
||||
/frontend/node_modules/
|
||||
/frontend/.direnv
|
||||
/frontend/dist
|
||||
|
|
681
.drone.yml
681
.drone.yml
File diff suppressed because it is too large
Load Diff
102
CHANGELOG.md
102
CHANGELOG.md
|
@ -7,6 +7,108 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
|
||||
All releases can be found on https://code.vikunja.io/api/releases.
|
||||
|
||||
## [0.23.0] - 2024-02-10
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* *(assignees)* Use correct amount of spacing in assignee selection
|
||||
* *(ci)* cd to frontend in frontend pipelines
|
||||
* *(ci)* Deploy packages into the correct directory
|
||||
* *(ci)* Swagger docs generate should use the correct url
|
||||
* *(ci)* Typo
|
||||
* *(ci)* Update shasum
|
||||
* *(docs)* Old install pages redirect
|
||||
* *(editor)* Don't set editor content intitially
|
||||
* *(export)* Don't crash when an exported file does not exist
|
||||
* *(filters)* Add explicit check for string slice filter
|
||||
* *(gantt)* Correctly import languages from dayjs
|
||||
* *(kanban)* Assignee spacing
|
||||
* *(kanban)* Bottom spacing of labels
|
||||
* *(notifications)* Mark all notifications as read in ui directly when marking as read on the server
|
||||
* *(progress)* Cleanup unused css
|
||||
* *(progress)* Less rounding
|
||||
* *(reminders)* Set reminder date on datepicker when editing a reminder
|
||||
* *(task)* Make sure the drag handle is shown as intended
|
||||
* *(task)* Move cover image setter to store
|
||||
* *(task)* Remove default task color
|
||||
* *(tasks)* Check for cycles during creation of task relations and prevent them
|
||||
* *(tasks)* Show any errors happening during task load
|
||||
* *(tests)* Adjust gantt rows identifier
|
||||
* *(webhook)* Fetch all event details before sending the webhook
|
||||
|
||||
### Features
|
||||
|
||||
* Merge API, Frontend and Desktop repos
|
||||
* *(ci)* Combine api and frontend drone configs
|
||||
* *(ci)* Merge desktop ci config
|
||||
* *(ci)* Save .tags file to generate release tags
|
||||
* *(ci)* Run desktop build without waiting on the frontend when not doing release builds
|
||||
* *(ci)* Run desktop pipeline only on PRs
|
||||
* *(editor)* Use primary color for currently selected node
|
||||
* *(filters)* Log type if unknown filter type
|
||||
* *(progress)* Move customizations into progress bar component
|
||||
|
||||
### Dependencies
|
||||
|
||||
* *(deps)* Update dependency @4tw/cypress-drag-drop to v1.8.1 (#693)
|
||||
* *(deps)* Update dependency @fortawesome/vue-fontawesome to v3.0.6
|
||||
* *(deps)* Update dependency @kyvg/vue3-notification to v3.1.4
|
||||
* *(deps)* Update dependency @types/node to v20.11.10
|
||||
* *(deps)* Update dependency autoprefixer to v10.3.3 (#684)
|
||||
* *(deps)* Update dependency autoprefixer to v10.3.4 (#697)
|
||||
* *(deps)* Update dependency axios to v0.21.2 (#698)
|
||||
* *(deps)* Update dependency axios to v0.21.3 (#700)
|
||||
* *(deps)* Update dependency cypress to v8.3.1 (#689)
|
||||
* *(deps)* Update dependency electron to v28.2.1 (#186)
|
||||
* *(deps)* Update dependency electron to v28.2.2 (#187)
|
||||
* *(deps)* Update dependency esbuild to v0.12.23 (#683)
|
||||
* *(deps)* Update dependency esbuild to v0.12.24 (#688)
|
||||
* *(deps)* Update dependency esbuild to v0.12.25 (#696)
|
||||
* *(deps)* Update dependency esbuild to v0.14.53 (#2217)
|
||||
* *(deps)* Update dependency eslint-plugin-vue to v7.17.0 (#686)
|
||||
* *(deps)* Update dependency floating-vue to v5.2.1
|
||||
* *(deps)* Update dependency floating-vue to v5.2.2
|
||||
* *(deps)* Update dependency jest to v27.1.0 (#687)
|
||||
* *(deps)* Update dependency marked to v3.0.1 (#677)
|
||||
* *(deps)* Update dependency marked to v3.0.2 (#682)
|
||||
* *(deps)* Update dependency postcss to v8.4.19 (#2673)
|
||||
* *(deps)* Update dependency sass to v1.38.1 (#679)
|
||||
* *(deps)* Update dependency sass to v1.38.2 (#690)
|
||||
* *(deps)* Update dependency sass to v1.39.0 (#695)
|
||||
* *(deps)* Update dependency typescript to v4.4.2 (#685)
|
||||
* *(deps)* Update dependency ufo to v1.4.0
|
||||
* *(deps)* Update dependency vite to v2.5.1 (#680)
|
||||
* *(deps)* Update dependency vite to v2.5.2 (#692)
|
||||
* *(deps)* Update dependency vite to v2.5.3 (#694)
|
||||
* *(deps)* Update dependency vite-plugin-pwa to v0.11.2 (#681)
|
||||
* *(deps)* Update dependency vue to v3.2.45
|
||||
* *(deps)* Update dependency vue-i18n to v9.9.1
|
||||
* *(deps)* Update goreleaser/nfpm docker tag to v2.35.3 (#1692)
|
||||
* *(deps)* Update module github.com/arran4/golang-ical to v0.2.4
|
||||
* *(deps)* Update module github.com/mattn/go-sqlite3 to v1.14.21
|
||||
* *(deps)* Update module github.com/mattn/go-sqlite3 to v1.14.22
|
||||
* *(deps)* Update module github.com/swaggo/swag to v1.16.3
|
||||
* *(deps)* Update module github.com/yuin/goldmark to v1.7.0
|
||||
* *(deps)* Update pnpm to v8.15.0
|
||||
* *(deps)* Update pnpm to v8.15.1
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.100.1
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.17.2 (#2587)
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.19.0 (#2670)
|
||||
* *(deps)* Update sentry-javascript monorepo to v7.99.0
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 45b9ea6
|
||||
* *(deps)* Update src.techknowlogick.com/xgo digest to 5aae655
|
||||
* *(deps)* Update tiptap to v2.2.0
|
||||
* *(deps)* Update tiptap to v2.2.1
|
||||
* *(deps)* Update typescript-eslint monorepo to v4.29.3 (#676)
|
||||
* *(deps)* Update typescript-eslint monorepo to v4.30.0 (#691)
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
* *(Expandable)* Spelling ⛈
|
||||
* *(deps)* Move renovate config
|
||||
* *(deps)* Remove redundant renovate config
|
||||
* *(quick actions)* Format
|
||||
|
||||
## [0.22.1] - 2024-01-28
|
||||
|
||||
### Bug Fixes
|
||||
|
|
37
Dockerfile
37
Dockerfile
|
@ -1,15 +1,25 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
# ┬─┐┬ ┐o┬ ┬─┐
|
||||
# │─││ │││ │ │
|
||||
# ┘─┘┘─┘┘┘─┘┘─┘
|
||||
FROM --platform=$BUILDPLATFORM node:20.11.0-alpine AS frontendbuilder
|
||||
|
||||
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.21.x AS builder
|
||||
WORKDIR /build
|
||||
|
||||
ENV PNPM_CACHE_FOLDER .cache/pnpm/
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD true
|
||||
|
||||
COPY frontend/ ./
|
||||
|
||||
RUN corepack enable && \
|
||||
pnpm install && \
|
||||
pnpm run build
|
||||
|
||||
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.21.x AS apibuilder
|
||||
|
||||
RUN go install github.com/magefile/mage@latest && \
|
||||
mv /go/bin/mage /usr/local/go/bin
|
||||
|
||||
WORKDIR /go/src/code.vikunja.io/api
|
||||
COPY . ./
|
||||
COPY --from=frontendbuilder /build/dist ./frontend/dist
|
||||
|
||||
ARG TARGETOS TARGETARCH TARGETVARIANT
|
||||
|
||||
|
@ -23,22 +33,15 @@ 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
|
||||
ENV VIKUNJA_DATABASE_PATH=/db/vikunja.db
|
||||
|
||||
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
|
||||
|
||||
COPY --from=builder /build/vikunja-* vikunja
|
||||
COPY --from=apibuilder /build/vikunja-* vikunja
|
||||
COPY --from=apibuilder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
|
12
README.md
12
README.md
|
@ -2,15 +2,18 @@
|
|||
|
||||
[![Build Status](https://drone.kolaente.de/api/badges/vikunja/vikunjaa/status.svg)](https://drone.kolaente.de/vikunja/vikunja)
|
||||
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)
|
||||
[![Download](https://img.shields.io/badge/download-v0.22.1-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Download](https://img.shields.io/badge/download-v0.23.0-brightgreen.svg)](https://dl.vikunja.io)
|
||||
[![Docker Pulls](https://img.shields.io/docker/pulls/vikunja/vikunja.svg)](https://hub.docker.com/r/vikunja/vikunja/)
|
||||
[![Swagger Docs](https://img.shields.io/badge/swagger-docs-brightgreen.svg)](https://try.vikunja.io/api/v1/docs)
|
||||
[![Go Report Card](https://goreportcard.com/badge/kolaente.dev/vikunja/vikunja)](https://goreportcard.com/report/kolaente.dev/vikunja/vikunja)
|
||||
|
||||
# Vikunja API
|
||||
# Vikunja
|
||||
|
||||
> The Todo-app to organize your life.
|
||||
|
||||
If Vikunja is useful to you, please consider [buying me a coffee](https://www.buymeacoffee.com/kolaente), [sponsoring me on GitHub](https://github.com/sponsors/kolaente) or buying [a sticker pack](https://vikunja.cloud/stickers).
|
||||
I'm also offering [a hosted version of Vikunja](https://vikunja.cloud/) if you want a hassle-free solution for yourself or your team.
|
||||
|
||||
# Table of contents
|
||||
|
||||
* [Security Reports](#security-reports)
|
||||
|
@ -43,12 +46,9 @@ All docs can be found on [the Vikunja home page](https://vikunja.io/docs/).
|
|||
|
||||
See [the roadmap](https://my.vikunja.cloud/share/QFyzYEmEYfSyQfTOmIRSwLUpkFjboaBqQCnaPmWd/auth) (hosted on Vikunja!) for more!
|
||||
|
||||
* [ ] [Mobile apps](https://code.vikunja.io/app) (separate repo) *In Progress*
|
||||
* [ ] [Webapp](https://code.vikunja.io/frontend) (separate repo) *In Progress*
|
||||
|
||||
## Contributing
|
||||
|
||||
Fork -> Push -> Pull-Request. Also see the [dev docs](https://vikunja.io/docs/development/) for more info.
|
||||
Please check out the contribuition guidelines on [the website](https://vikunja.io/docs/development/).
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ service:
|
|||
# The duration of the issued JWT tokens in seconds.
|
||||
# The default is 259200 seconds (3 Days).
|
||||
jwtttl: 259200
|
||||
# The duration of the "remember me" time in seconds. When the login request is made with
|
||||
# The duration of the "remember me" time in seconds. When the login request is made with
|
||||
# the long param set, the token returned will be valid for this period.
|
||||
# The default is 2592000 seconds (30 Days).
|
||||
jwtttllong: 2592000
|
||||
|
@ -16,14 +16,12 @@ service:
|
|||
unixsocket:
|
||||
# Permission bits for the Unix socket. Note that octal values must be prefixed by "0o", e.g. 0o660
|
||||
unixsocketmode:
|
||||
# The URL of the frontend, used to send password reset emails.
|
||||
frontendurl: ""
|
||||
# The public facing URL where your users can reach Vikunja. Used in emails and for the communication between api and frontend.
|
||||
publicurl: ""
|
||||
# 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: <rootpath>
|
||||
# Path on the file system to serve static files from. Set to the path of the frontend files to host frontend alongside the api.
|
||||
staticpath: ""
|
||||
# The max number of items which can be returned per page
|
||||
maxitemsperpage: 50
|
||||
# Enable the caldav endpoint, see the docs for more details
|
||||
|
@ -42,8 +40,6 @@ service:
|
|||
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: ''
|
||||
# If not empty, this will enable `/test/{table}` endpoints which allow to put any content in the database.
|
||||
# Used to reset the db before frontend tests. Because this is quite a dangerous feature allowing for lots of harm,
|
||||
# each request made to this endpoint needs to provide an `Authorization: <token>` header with the token from below. <br/>
|
||||
|
@ -52,7 +48,7 @@ service:
|
|||
# If enabled, vikunja will send an email to everyone who is either assigned to a task or created it when a task reminder
|
||||
# is due.
|
||||
enableemailreminders: true
|
||||
# If true, will allow users to request the complete deletion of their account. When using external authentication methods
|
||||
# If true, will allow users to request the complete deletion of their account. When using external authentication methods
|
||||
# it may be required to coordinate with them in order to delete the account. This setting will not affect the cli commands
|
||||
# for user deletion.
|
||||
enableuserdeletion: true
|
||||
|
@ -62,9 +58,25 @@ service:
|
|||
# If set to true, the frontend will show a big red warning not to use this instance for real data as it will be cleared out.
|
||||
# You probably don't need to set this value, it was created specifically for usage on [try](https://try.vikunja.io).
|
||||
demomode: false
|
||||
# Allow changing the logo and other icons based on various occasions throughout the year.
|
||||
allowiconchanges: true
|
||||
# Allow using a custom logo via external URL.
|
||||
customlogourl: ''
|
||||
|
||||
sentry:
|
||||
# If set to true, enables anonymous error tracking of api errors via Sentry. This allows us to gather more
|
||||
# information about errors in order to debug and fix it.
|
||||
enabled: false
|
||||
# Configure the Sentry dsn used for api error tracking. Only used when Sentry is enabled for the api.
|
||||
dsn: "https://440eedc957d545a795c17bbaf477497c@o1047380.ingest.sentry.io/4504254983634944"
|
||||
# If set to true, enables anonymous error tracking of frontend errors via Sentry. This allows us to gather more
|
||||
# information about errors in order to debug and fix it.
|
||||
frontendenabled: false
|
||||
# Configure the Sentry dsn used for frontend error tracking. Only used when Sentry is enabled for the frontend.
|
||||
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"
|
||||
|
@ -97,7 +109,7 @@ database:
|
|||
typesense:
|
||||
# Whether to enable the Typesense integration. If true, all tasks will be synced to the configured Typesense
|
||||
# instance and all search and filtering will run through Typesense instead of only through the database.
|
||||
# Typesense allows fast fulltext search including fuzzy matching support. It may return different results than
|
||||
# Typesense allows fast fulltext search including fuzzy matching support. It may return different results than
|
||||
# what you'd get with a database-only search.
|
||||
enabled: false
|
||||
# The url to the Typesense instance you want to use. Can be hosted locally or in Typesense Cloud as long
|
||||
|
@ -120,7 +132,7 @@ cors:
|
|||
# Whether to enable or disable cors headers.
|
||||
# Note: If you want to put the frontend and the api on separate 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
|
||||
enable: false
|
||||
# A list of origins which may access the api. These need to include the protocol (`http://` or `https://`) and port, if any.
|
||||
origins:
|
||||
- "*"
|
||||
|
@ -191,7 +203,7 @@ ratelimit:
|
|||
# Possible values are "keyvalue", "memory" or "redis".
|
||||
# When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
|
||||
store: keyvalue
|
||||
# The number of requests a user can make from the same IP to all unauthenticated routes (login, register,
|
||||
# The number of requests a user can make from the same IP to all unauthenticated routes (login, register,
|
||||
# password confirmation, email verification, password reset request) per minute. This limit cannot be disabled.
|
||||
# You should only change this if you know what you're doing.
|
||||
noauthlimit: 10
|
||||
|
@ -289,13 +301,11 @@ auth:
|
|||
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:** Some openid providers (like gitlab) only make the email of the user available through openid claims if they have set it to be publicly visible.
|
||||
# **Note:** Some openid providers (like Gitlab) only make the email of the user available through OpenID if they have set it to be publicly visible.
|
||||
# If the email is not public in those cases, authenticating will fail.
|
||||
# **Note 2:** 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 in your third party
|
||||
# auth service accordingly if you're using the default vikunja frontend.
|
||||
# The frontend will automatically provide the api with the redirect url, composed from the current url where it's hosted.
|
||||
# If you want to use the desktop client with openid, make sure to allow redirects to `127.0.0.1`.
|
||||
# **Note 2:** The frontend expects the third party to rediect the user <frontend-url>/auth/openid/<auth key> after authentication. Please make sure to configure the redirect url in your third party auth service accordingly if you're using the default vikunja frontend.
|
||||
# The frontend will automatically provide the API with the redirect url, composed from the current url where it's hosted.
|
||||
# If you want to use the desktop client with OpenID, make sure to allow redirects to `127.0.0.1`.
|
||||
# Take a look at the [default config file](https://kolaente.dev/vikunja/vikunja/src/branch/main/config.yml.sample) for more information about how to configure openid authentication.
|
||||
openid:
|
||||
# Enable or disable OpenID Connect authentication
|
||||
|
@ -313,6 +323,10 @@ auth:
|
|||
clientid:
|
||||
# The client secret used to authenticate Vikunja at the OpenID Connect provider.
|
||||
clientsecret:
|
||||
# The scope necessary to use oidc.
|
||||
# If you want to use the Feature to create and assign to vikunja teams via oidc, you have to add the custom "vikunja_scope" and check [openid.md](https://vikunja.io/docs/openid/).
|
||||
# e.g. scope: openid email profile vikunja_scope
|
||||
scope: openid email profile
|
||||
|
||||
# Prometheus metrics endpoint
|
||||
metrics:
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
THIS CHANGELOG ONLY EXISTS FOR HISTORICAL REASONS.
|
||||
Starting with version 0.23.0, all changes are logged in the CHANGELOG.md in the root of this repository since the repos were merged.
|
||||
|
||||
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/),
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
set -xe
|
||||
|
||||
frontend_version=$(sed -n 's/.*"VERSION": "\([^"]*\)".*/\1/p' ./frontend/version.json)
|
||||
frontend_version=$(git describe --tags --always --abbrev=10)
|
||||
|
||||
sed -i "s/\${version}/$frontend_version/g" package.json
|
||||
|
||||
sed -i "s/\"version\": \".*\"/\"version\": \"$frontend_version\"/" package.json
|
||||
|
||||
|
|
|
@ -51,11 +51,11 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "28.2.2",
|
||||
"electron-builder": "24.9.1"
|
||||
"electron": "29.1.0",
|
||||
"electron-builder": "24.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
"express": "^4.17.1"
|
||||
"connect-history-api-fallback": "2.0.0",
|
||||
"express": "4.18.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,10 +166,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.23.tgz#676fa0883450ed9da0bb24156213636290892806"
|
||||
integrity sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==
|
||||
|
||||
"@types/node@^18.11.18":
|
||||
version "18.15.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
|
||||
integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==
|
||||
"@types/node@^20.9.0":
|
||||
version "20.11.19"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.19.tgz#b466de054e9cb5b3831bee38938de64ac7f81195"
|
||||
integrity sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/plist@^3.0.1":
|
||||
version "3.0.2"
|
||||
|
@ -251,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"
|
||||
|
@ -265,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"
|
||||
|
@ -347,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"
|
||||
|
@ -361,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"
|
||||
|
||||
|
@ -416,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"
|
||||
|
@ -558,7 +559,7 @@ config-file-ts@^0.2.4:
|
|||
glob "^7.1.6"
|
||||
typescript "^4.0.2"
|
||||
|
||||
connect-history-api-fallback@^2.0.0:
|
||||
connect-history-api-fallback@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8"
|
||||
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
|
||||
|
@ -570,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==
|
||||
|
@ -688,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"
|
||||
|
@ -738,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"
|
||||
|
@ -755,26 +756,26 @@ 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@28.2.2:
|
||||
version "28.2.2"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-28.2.2.tgz#d5aa4a33c00927d83ca893f8726f7c62aad98c41"
|
||||
integrity sha512-8UcvIGFcjplHdjPFNAHVFg5bS0atDyT3Zx21WwuE4iLfxcAMsyMEOgrQX3im5LibA8srwsUZs7Cx0JAUfcQRpw==
|
||||
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" "^18.11.18"
|
||||
"@types/node" "^20.9.0"
|
||||
extract-zip "^2.0.1"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
|
@ -829,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.17.1:
|
||||
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"
|
||||
|
@ -1568,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"
|
||||
|
@ -1903,6 +1904,11 @@ typescript@^4.0.2:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a"
|
||||
integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
|
|
|
@ -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
|
|
@ -19,7 +19,7 @@ Please refer to its documentation for information about how to use flags etc.
|
|||
|
||||
To add a new cli command, add something like the following:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
func init() {
|
||||
rootCmd.AddCommand(myCmd)
|
||||
}
|
||||
|
@ -31,4 +31,4 @@ var myCmd = &cobra.Command{
|
|||
// Call other functions
|
||||
},
|
||||
}
|
||||
{{</ highlight >}}
|
||||
```
|
||||
|
|
|
@ -32,10 +32,10 @@ Then run `mage generate-docs` to generate the configuration docs from the sample
|
|||
To retrieve a configured value call the key with a getter for the type you need.
|
||||
For example:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
if config.CacheEnabled.GetBool() {
|
||||
// Do something with enabled caches
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Take a look at the methods declared on the type to see what's available.
|
||||
|
|
|
@ -16,13 +16,13 @@ The package exposes a `cron.Schedule` method with two arguments: The first one t
|
|||
|
||||
A basic function to register a cron task looks like this:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
func RegisterSomeCronTask() {
|
||||
err := cron.Schedule("0 * * * *", func() {
|
||||
// Do something every hour
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Call the register method in the `FullInit()` method of the `init` package to actually register it.
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ and a more in-depth description of what the migration actually does.
|
|||
|
||||
To easily get a new id, run the following on any unix system:
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
date +%Y%m%d%H%M%S
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
New migrations should be added via the `init()` function to the `migrations` variable.
|
||||
All migrations are sorted before being executed, since `init()` does not guarantee the order.
|
||||
|
@ -44,7 +44,7 @@ It will ask you for a table name and generate an empty migration similar to the
|
|||
|
||||
### Example
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
package migration
|
||||
|
||||
import (
|
||||
|
@ -73,6 +73,6 @@ func init() {
|
|||
},
|
||||
})
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
You should always copy the changed parts of the struct you're changing when adding migrations.
|
||||
|
|
|
@ -18,7 +18,7 @@ and a human-readable error message about what went wrong.
|
|||
|
||||
An error consists of multiple functions and definitions:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
// This struct holds any information about this specific error.
|
||||
// In this case, it contains the user ID of a nonexistent user.
|
||||
// This type should always be a struct, even if it has no values in it.
|
||||
|
@ -69,4 +69,4 @@ func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
|
|||
Message: "The user does not exist.",
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
|
@ -28,11 +28,11 @@ This document explains how events and listeners work in Vikunja, how to use them
|
|||
|
||||
Each event has to implement this interface:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
type Event interface {
|
||||
Name() string
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
An event can contain whatever data you need.
|
||||
|
||||
|
@ -75,7 +75,7 @@ To dispatch an event, simply call the `events.Dispatch` method and pass in the e
|
|||
|
||||
The `TaskCreatedEvent` is declared in the `pkg/models/events.go` file as follows:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
// TaskCreatedEvent represents an event where a task has been created
|
||||
type TaskCreatedEvent struct {
|
||||
Task *Task
|
||||
|
@ -86,11 +86,11 @@ type TaskCreatedEvent struct {
|
|||
func (t *TaskCreatedEvent) Name() string {
|
||||
return "task.created"
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
It is dispatched in the `createTask` function of the `models` package:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err error) {
|
||||
|
||||
// ...
|
||||
|
@ -102,7 +102,7 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
|
|||
|
||||
// ...
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
As you can see, the current task and doer are injected into it.
|
||||
|
||||
|
@ -122,13 +122,13 @@ A single event can have multiple listeners who are independent of each other.
|
|||
|
||||
All listeners must implement this interface:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
// Listener represents something that listens to events
|
||||
type Listener interface {
|
||||
Handle(msg *message.Message) error
|
||||
Name() string
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
The `Handle` method is executed when the event this listener listens on is dispatched.
|
||||
* As the single parameter, it gets the payload of the event, which is the event struct when it was dispatched decoded as json object and passed as a slice of bytes.
|
||||
|
@ -165,7 +165,7 @@ See the example below.
|
|||
|
||||
### Example
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
// RegisterListeners registers all event listeners
|
||||
func RegisterListeners() {
|
||||
events.RegisterListener((&ListCreatedEvent{}).Name(), &IncreaseListCounter{})
|
||||
|
@ -183,22 +183,22 @@ func (s *IncreaseTaskCounter) Name() string {
|
|||
func (s *IncreaseTaskCounter) Handle(payload message.Payload) (err error) {
|
||||
return keyvalue.IncrBy(metrics.TaskCountKey, 1)
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
When testing, you should call the `events.Fake()` method in the `TestMain` function of the package you want to test.
|
||||
This prevents any events from being fired and lets you assert an event has been dispatched like so:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
events.AssertDispatched(t, &TaskCreatedEvent{})
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
### Testing a listener
|
||||
|
||||
You can call an event listener manually with the `events.TestListener` method like so:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
ev := &TaskCommentCreatedEvent{
|
||||
Task: &task,
|
||||
Doer: u,
|
||||
|
@ -206,6 +206,6 @@ ev := &TaskCommentCreatedEvent{
|
|||
}
|
||||
|
||||
events.TestListener(t, ev, &SendTaskCommentNotification{})
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
This will call the listener's `Handle` method and assert it did not return an error when calling.
|
||||
|
|
|
@ -25,9 +25,9 @@ It returns the `limit` (max-length) and `offset` parameters needed for SQL-Queri
|
|||
|
||||
You can feed this function directly into xorm's `Limit`-Function like so:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
projects := []*Project{}
|
||||
err := x.Limit(getLimitFromPageIndex(pageIndex, itemsPerPage)).Find(&projects)
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
// TODO: Add a full example from start to finish, like a tutorial on how to create a new endpoint?
|
||||
|
|
|
@ -41,35 +41,24 @@ There are multiple categories of subcommands in the magefile:
|
|||
|
||||
These tasks are automatically run in our CI every time someone pushes to main 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 lint`
|
||||
* `mage build:build`
|
||||
|
||||
## Build
|
||||
|
||||
### Build Vikunja
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage build:build
|
||||
{{< /highlight >}}
|
||||
|
||||
or
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
mage build
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Builds a `vikunja`-binary in the root directory of the repo for the platform it is run on.
|
||||
|
||||
### clean
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
mage build:clean
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Cleans all build and executable files
|
||||
|
||||
|
@ -79,24 +68,17 @@ 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/)
|
||||
* `mage check:all`: Runs golangci and swagger documentation check
|
||||
* `mage lint`: Checks if the code follows the rules as defined in the `.golangci.yml` config file.
|
||||
* `mage lint:fix`: Fixes all code style issues which are easily fixable.
|
||||
|
||||
## 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/`,
|
||||
|
@ -118,17 +100,17 @@ binary to be able to use it.
|
|||
|
||||
### 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.
|
||||
|
||||
|
@ -138,25 +120,25 @@ Used to be run inside a [docker container](https://git.kolaente.de/konrad/reprep
|
|||
|
||||
### 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.
|
||||
|
||||
|
@ -164,9 +146,9 @@ Runs all integration tests.
|
|||
|
||||
### 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.
|
||||
|
@ -177,16 +159,16 @@ See also [migration docs]({{< ref "mage.md" >}}).
|
|||
|
||||
### 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.
|
||||
|
|
|
@ -23,7 +23,7 @@ First, define a `const` with the metric key in redis. This is done in `pkg/metri
|
|||
|
||||
To expose a new metric, you need to register it in the `init` function inside of the `metrics` package like so:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
// Register total user count metric
|
||||
promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
||||
Name: "vikunja_team_count", // The key of the metric. Must be unique.
|
||||
|
@ -32,7 +32,7 @@ promauto.NewGaugeFunc(prometheus.GaugeOpts{
|
|||
count, _ := GetCount(TeamCountKey) // TeamCountKey is the const we defined earlier.
|
||||
return float64(count)
|
||||
})
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Then you'll need to set the metrics initial value on every startup of Vikunja.
|
||||
This is done in `pkg/routes/routes.go` to avoid cyclic imports.
|
||||
|
|
|
@ -18,13 +18,13 @@ Vikunja provides a simple abstraction to send notifications per mail and in the
|
|||
|
||||
Each notification has to implement this interface:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
type Notification interface {
|
||||
ToMail() *Mail
|
||||
ToDB() interface{}
|
||||
Name() string
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Both functions return the formatted messages for mail and database.
|
||||
|
||||
|
@ -35,7 +35,7 @@ For example, if your notification should not be recorded in the database but onl
|
|||
|
||||
A list of chainable functions is available to compose a mail:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
mail := NewMail().
|
||||
// The optional sender of the mail message.
|
||||
From("test@example.com").
|
||||
|
@ -54,7 +54,7 @@ mail := NewMail().
|
|||
Action("The Action", "https://example.com").
|
||||
// Another line of text.
|
||||
Line("This should be an outro line").
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
If not provided, the `from` field of the mail contains the value configured in [`mailer.fromemail`](https://vikunja.io/docs/config-options/#fromemail).
|
||||
|
||||
|
@ -74,14 +74,14 @@ It takes the name of the notification and the package where the notification wil
|
|||
Notifiables can receive a notification.
|
||||
A notifiable is defined with this interface:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
type Notifiable interface {
|
||||
// Should return the email address this notifiable has.
|
||||
RouteForMail() string
|
||||
// Should return the id of the notifiable entity
|
||||
RouteForDB() int64
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
The `User` type from the `user` package implements this interface.
|
||||
|
||||
|
@ -92,7 +92,7 @@ It takes a notifiable and a notification as input.
|
|||
|
||||
For example, the email confirm notification when a new user registers is sent like this:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
n := &EmailConfirmNotification{
|
||||
User: update.User,
|
||||
IsNew: false,
|
||||
|
@ -100,7 +100,7 @@ n := &EmailConfirmNotification{
|
|||
|
||||
err = notifications.Notify(update.User, n)
|
||||
return
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
|
@ -110,9 +110,9 @@ If it was called, no mails are being sent and you can instead assert they have b
|
|||
When testing, you should call the `notifications.Fake()` method in the `TestMain` function of the package you want to test.
|
||||
This prevents any notifications from being sent and lets you assert a notifications has been sent like this:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
notifications.AssertSent(t, &ReminderDueNotification{})
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
|
|
|
@ -16,13 +16,7 @@ Not all steps are necessary for every release.
|
|||
* New Features: If there are new features worth mentioning the feature page should be updated.
|
||||
* New Screenshots: If an overhaul of an existing feature happened so that it now looks different from the existing screenshot, a new one is required.
|
||||
* Generate changelogs (with git-cliff)
|
||||
* Frontend
|
||||
* API
|
||||
* Desktop
|
||||
* Tag a new version: Include the changelog for that version as the tag message
|
||||
* Frontend
|
||||
* API
|
||||
* Desktop
|
||||
* Once built: Prune the cloudflare cache so that the new versions show up at [dl.vikunja.io](https://dl.vikunja.io/)
|
||||
* Update the [Flathub desktop package](https://github.com/flathub/io.vikunja.Vikunja)
|
||||
* Release Highlights Blogpost
|
||||
|
|
|
@ -21,7 +21,7 @@ These comments will show up in the documentation, it'll make it easier for devel
|
|||
|
||||
As an example, this is the definition of a project with all comments:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
type Project struct {
|
||||
// The unique, numeric id of this project.
|
||||
ID int64 `xorm:"bigint autoincr not null unique pk" json:"id" param:"project"`
|
||||
|
@ -69,7 +69,7 @@ type Project struct {
|
|||
web.CRUDable `xorm:"-" json:"-"`
|
||||
web.Rights `xorm:"-" json:"-"`
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
## Documenting api Endpoints
|
||||
|
||||
|
@ -78,7 +78,7 @@ When generating the api docs with mage, the swagger cli will pick these up and p
|
|||
|
||||
A comment looks like this:
|
||||
|
||||
{{< highlight golang >}}
|
||||
```go
|
||||
// @Summary Login
|
||||
// @Description Logs a user in. Returns a JWT-Token to authenticate further requests.
|
||||
// @tags user
|
||||
|
@ -93,4 +93,4 @@ A comment looks like this:
|
|||
func Login(c echo.Context) error {
|
||||
// Handler logic
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
|
|
@ -27,9 +27,9 @@ The easies way to do that is to set the environment variable `VIKUNJA_SERVICE_RO
|
|||
|
||||
To run unit tests with [mage]({{< ref "mage.md">}}), execute
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
mage test:unit
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
In Vikunja, everything that is not an integration test counts as unit test - even if it accesses the db.
|
||||
This definition is a bit blurry, but we haven't found a better one yet.
|
||||
|
@ -71,18 +71,18 @@ 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):
|
||||
|
||||
{{< highlight go >}}
|
||||
```go
|
||||
err = db.InitTestFixtures("users")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
In your actual tests, you then load the fixtures into the in-memory db like so:
|
||||
|
||||
{{< highlight go >}}
|
||||
```go
|
||||
db.LoadAndAssertFixtures(t)
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
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
|
||||
|
@ -97,13 +97,13 @@ Check out the docs [in the frontend repo](https://kolaente.dev/vikunja/vikunja/s
|
|||
|
||||
To run the frontend unit tests, run
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
pnpm run test:unit
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
The frontend also has a watcher available that re-runs all unit tests every time you change something.
|
||||
To use it, simply run
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
pnpm run test:unit-watch
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
|
|
@ -24,33 +24,33 @@ To back up attachments and other files, it is enough to copy them [from the atta
|
|||
|
||||
To create a backup from mysql use the `mysqldump` command:
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
mysqldump -u <user> -p -h <db-host> <database> > vkunja-backup.sql
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
You will be prompted for the password of the mysql user.
|
||||
|
||||
To restore it, simply pipe it back into the `mysql` command:
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
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).
|
||||
|
||||
|
|
|
@ -10,10 +10,24 @@ menu:
|
|||
|
||||
# Build Vikunja from source
|
||||
|
||||
To completely build Vikunja from source, you need to build the api and frontend.
|
||||
To fully build Vikunja from source files, you need to build the api and frontend.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## General Preparations
|
||||
|
||||
1. Make sure you have git installed
|
||||
2. Clone the repo with `git clone https://code.vikunja.io/vikunja` and switch into the directory.
|
||||
|
||||
## Frontend
|
||||
|
||||
The code for the frontend is located in the `frontend/` sub folder of the main repo.
|
||||
|
||||
1. Make sure you have [pnpm](https://pnpm.io/installation) properly installed on your system.
|
||||
2. Install all dependencies with `pnpm install`
|
||||
3. Build the frontend with `pnpm run build`. This will result in a static js bundle in the `dist/` folder.
|
||||
4. You can either deploy that static js bundle directly, or read on to learn how to bundle it all up in a static binary with the api.
|
||||
|
||||
## API
|
||||
|
||||
The Vikunja API has no other dependencies than go itself.
|
||||
|
@ -21,20 +35,11 @@ That means compiling it boils down to these steps:
|
|||
|
||||
1. Make sure [Go](https://golang.org/doc/install) is properly installed on your system. You'll need at least Go `1.21`.
|
||||
2. Make sure [Mage](https://magefile.org) is properly installed on your system.
|
||||
3. Clone the repo with `git clone https://code.vikunja.io/api` and switch into the directory.
|
||||
4. Run `mage 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.
|
||||
3. If you did not build the frontend in the steps before, you need to either do that or create a dummy index file with `mkdir -p frontend/dist && touch index.html`.
|
||||
4. Run `mage build` in the source of the main 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 `mage release` or `mage release:{linux|windows|darwin}`.
|
||||
|
||||
More options are available, please refer to the [magefile docs]({{< ref "../development/mage.md">}}) for more details.
|
||||
|
||||
## Frontend
|
||||
|
||||
The code for the frontend is located at [code.vikunja.io/frontend](https://code.vikunja.io/frontend).
|
||||
|
||||
1. Make sure you have [pnpm](https://pnpm.io/installation) properly installed on your system.
|
||||
2. Clone the repo with `git clone https://code.vikunja.io/frontend` and switch into the directory.
|
||||
3. Install all dependencies with `pnpm install`
|
||||
4. Build the frontend with `pnpm run build`. This will result in a static js bundle in the `dist/` folder which you can deploy.
|
||||
|
|
|
@ -16,16 +16,17 @@ Right now it is not possible to configure openid authentication via environment
|
|||
Variables are nested in the `config.yml`, these nested variables become `VIKUNJA_FIRST_CHILD` when configuring via
|
||||
environment variables. So setting
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
export VIKUNJA_FIRST_CHILD=true
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
is the same as defining it in a `config.yml` like so:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
```yaml
|
||||
```yaml
|
||||
first:
|
||||
child: true
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
# Formats
|
||||
|
||||
|
@ -93,7 +94,7 @@ Environment path: `VIKUNJA_SERVICE_JWTTTL`
|
|||
|
||||
### jwtttllong
|
||||
|
||||
The duration of the "remember me" time in seconds. When the login request is made with
|
||||
The duration of the "remember me" time in seconds. When the login request is made with
|
||||
the long param set, the token returned will be valid for this period.
|
||||
The default is 2592000 seconds (30 Days).
|
||||
|
||||
|
@ -137,15 +138,15 @@ Full path: `service.unixsocketmode`
|
|||
Environment path: `VIKUNJA_SERVICE_UNIXSOCKETMODE`
|
||||
|
||||
|
||||
### frontendurl
|
||||
### publicurl
|
||||
|
||||
The URL of the frontend, used to send password reset emails.
|
||||
The public facing URL where your users can reach Vikunja. Used in emails and for the communication between api and frontend.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `service.frontendurl`
|
||||
Full path: `service.publicurl`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_FRONTENDURL`
|
||||
Environment path: `VIKUNJA_SERVICE_PUBLICURL`
|
||||
|
||||
|
||||
### rootpath
|
||||
|
@ -161,17 +162,6 @@ Full path: `service.rootpath`
|
|||
Environment path: `VIKUNJA_SERVICE_ROOTPATH`
|
||||
|
||||
|
||||
### staticpath
|
||||
|
||||
Path on the file system to serve static files from. Set to the path of the frontend files to host frontend alongside the api.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `service.staticpath`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_STATICPATH`
|
||||
|
||||
|
||||
### maxitemsperpage
|
||||
|
||||
The max number of items which can be returned per page
|
||||
|
@ -271,17 +261,6 @@ Full path: `service.enabletotp`
|
|||
Environment path: `VIKUNJA_SERVICE_ENABLETOTP`
|
||||
|
||||
|
||||
### sentrydsn
|
||||
|
||||
If not empty, enables logging of crashes and unhandled errors in sentry.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `service.sentrydsn`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_SENTRYDSN`
|
||||
|
||||
|
||||
### testingtoken
|
||||
|
||||
If not empty, this will enable `/test/{table}` endpoints which allow to put any content in the database.
|
||||
|
@ -310,7 +289,7 @@ Environment path: `VIKUNJA_SERVICE_ENABLEEMAILREMINDERS`
|
|||
|
||||
### enableuserdeletion
|
||||
|
||||
If true, will allow users to request the complete deletion of their account. When using external authentication methods
|
||||
If true, will allow users to request the complete deletion of their account. When using external authentication methods
|
||||
it may be required to coordinate with them in order to delete the account. This setting will not affect the cli commands
|
||||
for user deletion.
|
||||
|
||||
|
@ -345,6 +324,80 @@ Full path: `service.demomode`
|
|||
Environment path: `VIKUNJA_SERVICE_DEMOMODE`
|
||||
|
||||
|
||||
### allowiconchanges
|
||||
|
||||
Allow changing the logo and other icons based on various occasions throughout the year.
|
||||
|
||||
Default: `true`
|
||||
|
||||
Full path: `service.allowiconchanges`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_ALLOWICONCHANGES`
|
||||
|
||||
|
||||
### customlogourl
|
||||
|
||||
Allow using a custom logo via external URL.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
Full path: `service.customlogourl`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_CUSTOMLOGOURL`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## sentry
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
If set to true, enables anonymous error tracking of api errors via Sentry. This allows us to gather more
|
||||
information about errors in order to debug and fix it.
|
||||
|
||||
Default: `false`
|
||||
|
||||
Full path: `sentry.enabled`
|
||||
|
||||
Environment path: `VIKUNJA_SENTRY_ENABLED`
|
||||
|
||||
|
||||
### dsn
|
||||
|
||||
Configure the Sentry dsn used for api error tracking. Only used when Sentry is enabled for the api.
|
||||
|
||||
Default: `https://440eedc957d545a795c17bbaf477497c@o1047380.ingest.sentry.io/4504254983634944`
|
||||
|
||||
Full path: `sentry.dsn`
|
||||
|
||||
Environment path: `VIKUNJA_SENTRY_DSN`
|
||||
|
||||
|
||||
### frontendenabled
|
||||
|
||||
If set to true, enables anonymous error tracking of frontend errors via Sentry. This allows us to gather more
|
||||
information about errors in order to debug and fix it.
|
||||
|
||||
Default: `false`
|
||||
|
||||
Full path: `sentry.frontendenabled`
|
||||
|
||||
Environment path: `VIKUNJA_SENTRY_FRONTENDENABLED`
|
||||
|
||||
|
||||
### frontenddsn
|
||||
|
||||
Configure the Sentry dsn used for frontend error tracking. Only used when Sentry is enabled for the frontend.
|
||||
|
||||
Default: `https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480`
|
||||
|
||||
Full path: `sentry.frontenddsn`
|
||||
|
||||
Environment path: `VIKUNJA_SENTRY_FRONTENDDSN`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## database
|
||||
|
@ -353,7 +406,7 @@ Environment path: `VIKUNJA_SERVICE_DEMOMODE`
|
|||
|
||||
### 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`
|
||||
|
||||
|
@ -516,7 +569,7 @@ Environment path: `VIKUNJA_DATABASE_TLS`
|
|||
|
||||
Whether to enable the Typesense integration. If true, all tasks will be synced to the configured Typesense
|
||||
instance and all search and filtering will run through Typesense instead of only through the database.
|
||||
Typesense allows fast fulltext search including fuzzy matching support. It may return different results than
|
||||
Typesense allows fast fulltext search including fuzzy matching support. It may return different results than
|
||||
what you'd get with a database-only search.
|
||||
|
||||
Default: `false`
|
||||
|
@ -611,7 +664,7 @@ Whether to enable or disable cors headers.
|
|||
Note: If you want to put the frontend and the api on separate 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`
|
||||
Default: `false`
|
||||
|
||||
Full path: `cors.enable`
|
||||
|
||||
|
@ -971,7 +1024,7 @@ Environment path: `VIKUNJA_RATELIMIT_STORE`
|
|||
|
||||
### noauthlimit
|
||||
|
||||
The number of requests a user can make from the same IP to all unauthenticated routes (login, register,
|
||||
The number of requests a user can make from the same IP to all unauthenticated routes (login, register,
|
||||
password confirmation, email verification, password reset request) per minute. This limit cannot be disabled.
|
||||
You should only change this if you know what you're doing.
|
||||
|
||||
|
@ -1156,11 +1209,11 @@ Environment path: `VIKUNJA_AUTH_LOCAL`
|
|||
|
||||
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:** Some openid providers (like gitlab) only make the email of the user available through openid claims if they have set it to be publicly visible.
|
||||
**Note:** Some openid providers (like Gitlab) only make the email of the user available through OpenID if they have set it to be publicly visible.
|
||||
If the email is not public in those cases, authenticating will fail.
|
||||
**Note 2:** 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 accordingly if you're using the default vikunja frontend.
|
||||
**Note 2:** The frontend expects the third party to rediect the user <frontend-url>/auth/openid/<auth key> after authentication. Please make sure to configure the redirect url in your third party auth service accordingly if you're using the default vikunja frontend.
|
||||
The frontend will automatically provide the API with the redirect url, composed from the current url where it's hosted.
|
||||
If you want to use the desktop client with OpenID, make sure to allow redirects to `127.0.0.1`.
|
||||
Take a look at the [default config file](https://kolaente.dev/vikunja/vikunja/src/branch/main/config.yml.sample) for more information about how to configure openid authentication.
|
||||
|
||||
Default: `<empty>`
|
||||
|
@ -1320,7 +1373,7 @@ Environment path: `VIKUNJA_DEFAULTSETTINGS_WEEK_START`
|
|||
|
||||
### language
|
||||
|
||||
The language of the user interface. Must be an ISO 639-1 language code followed by an ISO 3166-1 alpha-2 country code. Check https://kolaente.dev/vikunja/vikunja/src/branch/main/frontend/src/i18n/lang for a list of possible languages. Will default to the browser language the user uses when signing up.
|
||||
The language of the user interface. Must be an ISO 639-1 language code followed by an ISO 3166-1 alpha-2 country code. Check https://kolaente.dev/vikunja/vikunja/frontend/src/branch/main/src/i18n/lang for a list of possible languages. Will default to the browser language the user uses when signing up.
|
||||
|
||||
Default: `<unset>`
|
||||
|
||||
|
|
30
docs/content/doc/setup/desktop.md
Normal file
30
docs/content/doc/setup/desktop.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
title: "Desktop Packages"
|
||||
date: 2024-02-11T15:58:18+01:00
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Desktop Packages
|
||||
|
||||
Vikunja is available as an electron-based desktop application for Linux and Windows.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the latest release for your platform from [the download page](https://dl.vikunja.io/desktop/).
|
||||
* For Windows, choose the file with the `.exe` or `.msi` file ending
|
||||
* For a Linux-based operating system, choose a file with an ending for your operating system - we have builds for Alpine, AppImage, Arch Linux, Debian-based systems, FreeBSD, Fedora and Snap.
|
||||
2. Run the downloaded package in the same way you would normally install a package for your OS.
|
||||
|
||||
## Flatpack
|
||||
|
||||
Vikunja Desktop can be installed via the [Flathub](https://flathub.org/apps/io.vikunja.Vikunja).
|
||||
|
||||
To install it, run the following command:
|
||||
|
||||
```
|
||||
flatpak install flathub io.vikunja.Vikunja
|
||||
```
|
|
@ -27,89 +27,69 @@ 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:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_USER: vikunja
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
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 >}}
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
environment:
|
||||
VIKUNJA_SERVICE_PUBLICURL: http://<the public url where vikunja is reachable>
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: changeme
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
ports:
|
||||
- 3456:3456
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_USER: vikunja
|
||||
MYSQL_PASSWORD: changeme
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u $$MYSQL_USER --password=$$MYSQL_PASSWORD"]
|
||||
interval: 2s
|
||||
start_period: 30s
|
||||
```
|
||||
|
||||
This defines four services, each with their own container:
|
||||
This defines two 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 Vikunja service which runs the vikunja api and hosts its frontend.
|
||||
* A database container which will store all projects, 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.
|
||||
|
||||
If you already have a proxy on your host, you may want to check out the [reverse proxy examples]({{< ref "reverse-proxies.md" >}}) to use that.
|
||||
By default, Vikunja will be exposed on port 3456 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:
|
||||
You'll need to change the value of the `VIKUNJA_SERVICE_PUBLICURL` environment variable to the public port or hostname where Vikunja is reachable.
|
||||
|
||||
{{< highlight conf >}}
|
||||
server {
|
||||
listen 80;
|
||||
## Ensure adequate file permissions
|
||||
|
||||
location / {
|
||||
proxy_pass http://frontend:80;
|
||||
}
|
||||
Vikunja runs as user `1000` and no group by default.
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://api:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
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:
|
||||
|
||||
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>
|
||||
```
|
||||
mkdir $PWD/files
|
||||
chown 1000 $PWD/files
|
||||
```
|
||||
|
||||
## Run it
|
||||
|
||||
|
@ -118,8 +98,8 @@ 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
|
||||
vikunja_1 | 2024-02-09T14:44:06.990677157+01:00: INFO ▶ cmd/func29 05d Vikunja version 0.23.0
|
||||
vikunja_1 | ⇨ http server started on [::]:3456
|
||||
```
|
||||
|
||||
This indicates all setup has been successful.
|
||||
|
@ -159,20 +139,6 @@ If not, there might be a different error or a bug with Vikunja, please reach out
|
|||
|
||||
(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:
|
||||
|
@ -192,20 +158,46 @@ To do this, first stop everything by running `sudo docker-compose down`, then re
|
|||
Head over to `http://<host-ip or url>/api/v1/info` in a browser.
|
||||
You should see something like this:
|
||||
|
||||
{{< highlight json >}}
|
||||
```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": [
|
||||
"todoist"
|
||||
],
|
||||
"task_attachments_enabled": true
|
||||
"version": "v0.23.0",
|
||||
"frontend_url": "https://try.vikunja.io/",
|
||||
"motd": "",
|
||||
"link_sharing_enabled": true,
|
||||
"max_file_size": "20MB",
|
||||
"registration_enabled": true,
|
||||
"available_migrators": [
|
||||
"vikunja-file",
|
||||
"ticktick",
|
||||
"todoist"
|
||||
],
|
||||
"task_attachments_enabled": true,
|
||||
"enabled_background_providers": [
|
||||
"upload",
|
||||
"unsplash"
|
||||
],
|
||||
"totp_enabled": false,
|
||||
"legal": {
|
||||
"imprint_url": "",
|
||||
"privacy_policy_url": ""
|
||||
},
|
||||
"caldav_enabled": true,
|
||||
"auth": {
|
||||
"local": {
|
||||
"enabled": true
|
||||
},
|
||||
"openid_connect": {
|
||||
"enabled": false,
|
||||
"providers": null
|
||||
}
|
||||
},
|
||||
"email_reminders_enabled": true,
|
||||
"user_deletion_enabled": true,
|
||||
"task_comments_enabled": true,
|
||||
"demo_mode_enabled": true,
|
||||
"webhooks_enabled": true
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
This shows you can reach the api through the api proxy.
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@ menu:
|
|||
|
||||
# Full docker example
|
||||
|
||||
This docker compose configuration will run Vikunja with backend and frontend with a mariadb database.
|
||||
It uses an nginx container or traefik on the host to proxy backend and frontend into a single port.
|
||||
This docker compose configuration will run Vikunja with a mariadb database.
|
||||
It uses a proxy configuration to make it available under a domain.
|
||||
|
||||
For all available configuration options, see [configuration]({{< ref "config.md">}}).
|
||||
|
||||
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> 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/>
|
||||
|
@ -23,63 +25,58 @@ All examples on this page already reflect this and do not require additional wor
|
|||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Redis
|
||||
## File permissions
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
To use redis, you'll need to add this to the config examples below:
|
||||
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:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '3'
|
||||
```
|
||||
mkdir $PWD/files
|
||||
chown 1000 $PWD/files
|
||||
```
|
||||
|
||||
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 >}}
|
||||
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.
|
||||
To use postgres as a database backend, change the `db` section of the examples to this:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
```yaml
|
||||
db:
|
||||
image: postgres:13
|
||||
image: postgres:16
|
||||
environment:
|
||||
POSTGRES_PASSWORD: secret
|
||||
POSTGRES_PASSWORD: changeme
|
||||
POSTGRES_USER: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
{{< /highlight >}}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -h localhost -U $$POSTGRES_USER"]
|
||||
interval: 2s
|
||||
```
|
||||
|
||||
You'll also need to change the `VIKUNJA_DATABASE_TYPE` to `postgres` on the api container declaration.
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> The mariadb container can sometimes take a while to initialize, especially on the first run. During this time, the api container will fail to start at all. It will automatically restart every few seconds.
|
||||
</div>
|
||||
|
||||
## Sqlite
|
||||
|
||||
Vikunja supports postgres, mysql and sqlite as a database backend. The examples on this page use mysql with a mariadb container.
|
||||
To use sqlite as a database backend, change the `api` section of the examples to this:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
api:
|
||||
image: vikunja/api
|
||||
```yaml
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
environment:
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: http://<your public frontend url with slash>/
|
||||
# Note the default path is /app/vikunja/vikunja.db This moves to a different folder so you can use a volume and store this outside of the container so state is persisted even if container destroyed.
|
||||
VIKUNJA_SERVICE_PUBLICURL: http://<your public frontend url with slash>/
|
||||
# Note the default path is /app/vikunja/vikunja.db.
|
||||
# This config variable moves it to a different folder so you can use a volume and
|
||||
# store the database file outside the container so state is persisted even if the container is destroyed.
|
||||
VIKUNJA_DATABASE_PATH: /db/vikunja.db
|
||||
ports:
|
||||
- 3456:3456
|
||||
|
@ -87,66 +84,80 @@ api:
|
|||
- ./files:/app/vikunja/files
|
||||
- ./db:/db
|
||||
restart: unless-stopped
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
The default path Vikunja uses for sqlite is relative to the binary, which in the docker container would be `/app/vikunja/vikunja.db`. This config moves to a different folder using `VIKUNJA_DATABASE_PATH` so you can use a volume at `/db` and store this outside of the container so state is persisted even if container destroyed.
|
||||
The default path Vikunja uses for sqlite is relative to the binary, which in the docker container would be `/app/vikunja/vikunja.db`.
|
||||
The `VIKUNJA_DATABASE_PATH` environment variable moves changes it so that the database file is stored in a volume at `/db`, to persist state across restarts.
|
||||
|
||||
You'll also need to remove or change the `VIKUNJA_DATABASE_TYPE` to `sqlite` on the api container declaration.
|
||||
You'll also need to remove or change the `VIKUNJA_DATABASE_TYPE` to `sqlite` on the container declaration.
|
||||
|
||||
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>
|
||||
|
||||
## Example without any proxy
|
||||
|
||||
This example lets you host Vikunja without any reverse proxy in front of it. This is the absolute minimum configuration you need to get something up and running. If you want to host Vikunja on one single port instead of two different ones or need tls termination, check out one of the other examples.
|
||||
This example lets you host Vikunja without any reverse proxy in front of it.
|
||||
This is the absolute minimum configuration you need to get something up and running.
|
||||
If you want to make Vikunja available on a domain or need tls termination, check out one of the other examples.
|
||||
|
||||
Note that you need to change the `VIKUNJA_API_URL` environment variable to the ip (the docker host you're running this on) is reachable at. Because the browser you'll use to access the Vikunja frontend uses that url to make the requests, it has to be able to reach that ip + port from the outside. Putting everything in a private network won't work.
|
||||
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.
|
||||
|
||||
{{< highlight yaml >}}
|
||||
<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'
|
||||
|
||||
services:
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
environment:
|
||||
VIKUNJA_SERVICE_PUBLICURL: http://<the public ip or host where vikunja is reachable>
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: changeme
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
ports:
|
||||
- 3456:3456
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_USER: vikunja
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_PASSWORD: changeme
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: http://<your public frontend url with slash>/
|
||||
ports:
|
||||
- 3456:3456
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
VIKUNJA_API_URL: http://<your-ip-here>:3456/api/v1
|
||||
restart: unless-stopped
|
||||
{{< /highlight >}}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u $$MYSQL_USER --password=$$MYSQL_PASSWORD"]
|
||||
interval: 2s
|
||||
start_period: 30s
|
||||
```
|
||||
|
||||
## Example with traefik 2
|
||||
## 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/).
|
||||
|
||||
|
@ -156,263 +167,135 @@ 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`
|
||||
|
||||
{{< highlight yaml >}}
|
||||
<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'
|
||||
|
||||
services:
|
||||
api:
|
||||
image: vikunja/api
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
environment:
|
||||
VIKUNJA_SERVICE_PUBLICURL: http://<the public url where vikunja is reachable>
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_PASSWORD: changeme
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
networks:
|
||||
- web
|
||||
- default
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.vikunja-api.rule=Host(`vikunja.example.com`) && (PathPrefix(`/api/v1`) || PathPrefix(`/dav/`) || PathPrefix(`/.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
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.http.routers.vikunja.rule=Host(`vikunja.example.com`)"
|
||||
- "traefik.http.routers.vikunja.entrypoints=https"
|
||||
- "traefik.http.routers.vikunja.tls.certResolver=acme"
|
||||
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_PASSWORD: changeme
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
command: --max-connections=1000
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u $$MYSQL_USER --password=$$MYSQL_PASSWORD"]
|
||||
interval: 2s
|
||||
start_period: 30s
|
||||
|
||||
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
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
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
|
||||
|
||||
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):
|
||||
|
||||
{{< 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 >}}
|
||||
|
||||
<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:
|
||||
|
||||
{{< 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_USER: vikunja
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
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 >}}
|
||||
```
|
||||
|
||||
## 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 >}}
|
||||
```conf
|
||||
vikunja.example.com {
|
||||
reverse_proxy /api/* api:3456
|
||||
reverse_proxy /.well-known/* api:3456
|
||||
reverse_proxy /dav/* api:3456
|
||||
reverse_proxy frontend:80
|
||||
reverse_proxy api:3456
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
`docker-compose.yml` config:
|
||||
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.
|
||||
|
||||
{{< highlight yaml >}}
|
||||
<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
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
environment:
|
||||
VIKUNJA_SERVICE_PUBLICURL: http://<the public url where vikunja is reachable>
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: changeme
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
ports:
|
||||
- 3456:3456
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_USER: vikunja
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_PASSWORD: changeme
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u $$MYSQL_USER --password=$$MYSQL_PASSWORD"]
|
||||
interval: 2s
|
||||
start_period: 30s
|
||||
caddy:
|
||||
image: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
depends_on:
|
||||
- api
|
||||
- frontend
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
{{< /highlight >}}
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
```
|
||||
|
||||
## Setup on a Synology NAS
|
||||
|
||||
There is a proxy preinstalled in DSM, so if you want to access vikunja from outside,
|
||||
you can prepare 2 proxy rules:
|
||||
|
||||
* a redirection rule for vikunja's api (see example screenshot using port 3456)
|
||||
* a similar redirection rule for vikunja's frontend (using port 4321)
|
||||
There is a proxy preinstalled in DSM, so if you want to access Vikunja from outside,
|
||||
you need to prepare a proxy rule the Vikunja Service.
|
||||
|
||||
![Synology Proxy Settings](/docs/synology-proxy-1.png)
|
||||
|
||||
|
@ -423,64 +306,49 @@ docker main folders:
|
|||
* vikunja
|
||||
* mariadb
|
||||
|
||||
Synology has its own GUI for managing Docker containers... But it's easier via docker compose.
|
||||
Synology has its own GUI for managing Docker containers, but it's easier via docker compose.
|
||||
|
||||
To do that, you can
|
||||
|
||||
* either activate SSH and paste the adapted compose file in a terminal (using Putty or similar)
|
||||
* without activating SSH as a "custom script" (go to Control Panel / Task Scheduler / Create / Scheduled Task / User-defined script)
|
||||
* without activating SSH, by using Portainer (you have to install first, check out [this tutorial](https://www.portainer.io/blog/how-to-install-portainer-on-a-synology-nas) for exmple):
|
||||
* Either activate SSH and paste the adapted compose file in a terminal (using Putty or similar)
|
||||
* Without activating SSH as a "custom script" (go to Control Panel / Task Scheduler / Create / Scheduled Task / User-defined script)
|
||||
* Without activating SSH, by using Portainer (you have to install first, check out [this tutorial](https://www.portainer.io/blog/how-to-install-portainer-on-a-synology-nas) for exmple):
|
||||
1. Go to **Dashboard / Stacks** click the button **"Add Stack"**
|
||||
2. Give it the name Vikunja and paste the adapted docker compose file
|
||||
3. Deploy the Stack with the "Deploy Stack" button:
|
||||
|
||||
![Portainer Stack deploy](/docs/synology-proxy-2.png)
|
||||
|
||||
The docker-compose file we're going to use is very similar to the [example without any proxy](#example-without-any-proxy) above:
|
||||
|
||||
{{< 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_USER: vikunja
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <a super secure random secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
ports:
|
||||
- 3456:3456
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
ports:
|
||||
- 4321:80
|
||||
environment:
|
||||
VIKUNJA_API_URL: http://vikunja-api-domain.tld/api/v1
|
||||
restart: unless-stopped
|
||||
{{< /highlight >}}
|
||||
The docker-compose file we're going to use is exactly the same from the [example without any proxy](#example-without-any-proxy) above.
|
||||
|
||||
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-backend.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.
|
||||
|
||||
To use redis, you'll need to add this to the config examples below:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
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
|
||||
```
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Install Backend"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
{{< highlight bash >}}
|
||||
wget <download-url>
|
||||
{{< /highlight >}}
|
||||
|
||||
### Verify the GPG signature
|
||||
|
||||
Starting with version `0.7`, all releases are signed using pgp.
|
||||
Releases from `main` will always be signed.
|
||||
|
||||
To validate the downloaded zip file use the signiture file `.asc` and the key `FF054DACD908493A`:
|
||||
|
||||
{{< highlight bash >}}
|
||||
gpg --keyserver keyserver.ubuntu.com --recv FF054DACD908493A
|
||||
gpg --verify vikunja-0.7-linux-amd64-full.zip.asc vikunja-0.7-linux-amd64-full.zip
|
||||
{{< /highlight >}}
|
||||
|
||||
### Set it up
|
||||
|
||||
Once you've verified the signature, you need to unzip it and make it executable, you'll also need to
|
||||
create a symlink to it so you can execute Vikunja by typing `vikunja` on your system.
|
||||
We'll install vikunja to `/opt/vikunja`, change the path where needed if you want to install it elsewhere.
|
||||
|
||||
{{< highlight bash >}}
|
||||
mkdir -p /opt/vikunja
|
||||
unzip <vikunja-zip-file> -d /opt/vikunja
|
||||
chmod +x /opt/vikunja
|
||||
ln -s /opt/vikunja/vikunja /usr/bin/vikunja
|
||||
{{< /highlight >}}
|
||||
|
||||
### Systemd service
|
||||
|
||||
Save the following service file to `/etc/systemd/system/vikunja.service` and adapt it to your needs:
|
||||
|
||||
{{< highlight service >}}
|
||||
[Unit]
|
||||
Description=Vikunja
|
||||
After=syslog.target
|
||||
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]
|
||||
RestartSec=2s
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/vikunja
|
||||
ExecStart=/usr/bin/vikunja
|
||||
Restart=always
|
||||
# If you want to bind Vikunja to a port below 1024 uncomment
|
||||
# the two values below
|
||||
###
|
||||
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
{{< /highlight >}}
|
||||
|
||||
If you've installed Vikunja to a directory other than `/opt/vikunja`, you need to adapt `WorkingDirectory` accordingly.
|
||||
|
||||
After you made all necessary modifications, it's time to start the service:
|
||||
|
||||
{{< highlight bash >}}
|
||||
sudo systemctl enable vikunja
|
||||
sudo systemctl start vikunja
|
||||
{{< /highlight >}}
|
||||
|
||||
### Build from source
|
||||
|
||||
To build vikunja from source, see [building from source]({{< ref "build-from-source.md">}}).
|
||||
|
||||
### Updating
|
||||
|
||||
Simply replace the binary and templates with the new version, then restart Vikunja.
|
||||
It will automatically run all necessary database migrations.
|
||||
**Make sure to take a look at the changelog for the new version to not miss any manual steps the update may involve!**
|
||||
|
||||
## Docker
|
||||
|
||||
(Note: this assumes some familiarity with docker)
|
||||
|
||||
Usage with docker is pretty straightforward:
|
||||
|
||||
{{< highlight bash >}}
|
||||
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.
|
||||
|
||||
You can mount a local configuration like so:
|
||||
|
||||
{{< highlight bash >}}
|
||||
docker run -p 3456:3456 -v /path/to/config/on/host.yml:/app/vikunja/config.yml:ro vikunja/api
|
||||
{{< /highlight >}}
|
||||
|
||||
Though it is recommended to use environment 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` 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
|
||||
|
||||
To run the backend with a mariadb database you can use this example [docker-compose](https://docs.docker.com/compose/) file:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '2'
|
||||
services:
|
||||
api:
|
||||
image: vikunja/api:latest
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <generated secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://<your public frontend url with slash>/
|
||||
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_USER: vikunja
|
||||
MYSQL_PASSWORD: secret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
{{< /highlight >}}
|
||||
|
||||
See [full docker example]({{< ref "full-docker-example.md">}}) for more variations of this config.
|
||||
|
||||
## Debian packages
|
||||
|
||||
Since version 0.7 Vikunja is also released as debian packages.
|
||||
|
||||
To install these, grab a copy from [the download page](https://vikunja.io/en/download/) and run
|
||||
|
||||
{{< highlight bash >}}
|
||||
dpkg -i vikunja.deb
|
||||
{{< /highlight >}}
|
||||
|
||||
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">}}).
|
||||
|
||||
## Default Password
|
||||
|
||||
After successfully installing Vikunja, there is no default user or password.
|
||||
You only need to register a new account and set all the details when creating it.
|
|
@ -1,138 +0,0 @@
|
|||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Install Frontend"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Frontend
|
||||
|
||||
Installing the frontend is just a matter of hosting a bunch of static files somewhere.
|
||||
|
||||
With nginx or apache, you have to [download](https://vikunja.io/en/download/) the frontend files first.
|
||||
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 separate servers or even just separate 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 necessary files for the frontend.
|
||||
|
||||
To run it, all you need is
|
||||
|
||||
{{< highlight bash >}}
|
||||
docker run -p 80:80 vikunja/frontend
|
||||
{{< /highlight >}}
|
||||
|
||||
which will run the docker image and expose port 80 on the host.
|
||||
|
||||
See [full docker example]({{< ref "full-docker-example.md">}}) for more variations of this config.
|
||||
|
||||
The docker container runs as an unprivileged user and does not mount anything.
|
||||
|
||||
### 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`:
|
||||
|
||||
You may need to adjust `server_name` and `root` accordingly.
|
||||
|
||||
After configuring them, you need to reload nginx (`service nginx reload`).
|
||||
|
||||
### with gzip enabled (recommended)
|
||||
|
||||
{{< highlight conf >}}
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_min_length 256;
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
### without gzip
|
||||
|
||||
{{< highlight conf >}}
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
## Apache
|
||||
|
||||
Apache needs to have `mod_rewrite` enabled for this to work properly:
|
||||
|
||||
{{< highlight bash >}}
|
||||
a2enmod rewrite
|
||||
service apache2 restart
|
||||
{{< /highlight >}}
|
||||
|
||||
Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
|
||||
|
||||
{{< highlight aconf >}}
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
DocumentRoot /path/to/vikunja/static/frontend/files
|
||||
RewriteEngine On
|
||||
RewriteRule ^\/?(favicon\.ico|assets|audio|fonts|images|manifest\.webmanifest|robots\.txt|sw\.js|workbox-.*|api|dav|\.well-known) - [L]
|
||||
RewriteRule ^(.*)$ /index.html [QSA,L]
|
||||
</VirtualHost>
|
||||
{{< /highlight >}}
|
||||
|
||||
You probably want to adjust `ServerName` and `DocumentRoot`.
|
||||
|
||||
Once you've customized your config, you need to enable it:
|
||||
|
||||
{{< highlight bash >}}
|
||||
a2ensite vikunja
|
||||
service apache2 reload
|
||||
{{< /highlight >}}
|
||||
|
||||
## Updating
|
||||
|
||||
To update, it should be enough to download the new files and overwrite the old ones.
|
||||
The paths contain hashes, so all caches are invalidated automatically.
|
|
@ -11,42 +11,289 @@ menu:
|
|||
|
||||
# Installing
|
||||
|
||||
Vikunja consists of two parts: [API](https://code.vikunja.io/api) and [frontend](https://code.vikunja.io/frontend).
|
||||
Architecturally, Vikunja is made up of two parts: [API](https://code.vikunja.io/api) and [frontend](https://code.vikunja.io/api/frontend).
|
||||
|
||||
You will always need to install at least the API.
|
||||
To actually use Vikunja you'll also need to somehow install a frontend to use it.
|
||||
You can either:
|
||||
Both are bundled into one single deployable binary (or docker container).
|
||||
That means you only need to install one thing to be able to use Vikunja.
|
||||
|
||||
* [Install the web frontend]({{< ref "install-frontend.md">}})
|
||||
* Use the desktop app, which is essentially a web frontend packaged for easy installation on desktop devices
|
||||
You can also:
|
||||
|
||||
* Use the desktop app, which is essentially the web frontend packaged for easy installation on desktop devices
|
||||
* Use the mobile app only, but as of right now it only supports the very basic features of Vikunja
|
||||
|
||||
Vikunja can be installed in various ways.
|
||||
This document provides an overview and instructions for the different methods.
|
||||
<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>
|
||||
|
||||
* [API]({{< ref "install-backend.md">}})
|
||||
* [Installing from binary]({{< ref "install-backend.md#install-from-binary">}})
|
||||
* [Verify the GPG signature]({{< ref "install-backend.md#verify-the-gpg-signature">}})
|
||||
* [Set it up]({{< ref "install-backend.md#set-it-up">}})
|
||||
* [Systemd service]({{< ref "install-backend.md#systemd-service">}})
|
||||
* [Updating]({{< ref "install-backend.md#updating">}})
|
||||
* [Build from source]({{< ref "install-backend.md#build-from-source">}})
|
||||
* [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">}})
|
||||
* [Apache]({{< ref "install-frontend.md#apache">}})
|
||||
* [Updating]({{< ref "install-frontend.md#updating">}})
|
||||
Vikunja can be installed in various ways.
|
||||
This document provides an overview and instructions for the different methods:
|
||||
|
||||
* [Installing from binary (manual)](#install-from-binary)
|
||||
* [Build from source]({{< ref "build-from-source.md">}})
|
||||
* [Docker](#docker)
|
||||
* [Debian](#debian-packages)
|
||||
* [RPM](#rpm)
|
||||
* [FreeBSD](#freebsd--freenas)
|
||||
* [Kubernetes]({{< ref "k8s.md" >}})
|
||||
|
||||
And after you installed Vikunja, you may want to check out these other ressources:
|
||||
|
||||
* [Configuration]({{< ref "config.md">}})
|
||||
* [UTF-8 Settings]({{< ref "utf-8.md">}})
|
||||
* [Reverse proxies]({{< ref "reverse-proxies.md">}})
|
||||
* [Full docker example]({{< ref "full-docker-example.md">}})
|
||||
* [Backups]({{< ref "backups.md">}})
|
||||
|
||||
## Installation on kubernetes
|
||||
## Install from binary
|
||||
|
||||
A third-party Helm Chart is available from the k8s-at-home project [here](https://github.com/k8s-at-home/charts/tree/master/charts/stable/vikunja).
|
||||
Download a copy of Vikunja from the [download page](https://dl.vikunja.io/vikunja) for your architecture.
|
||||
|
||||
```
|
||||
wget <download-url>
|
||||
```
|
||||
|
||||
### Verify the GPG signature
|
||||
|
||||
All releases are signed using GPG.
|
||||
|
||||
To validate the downloaded zip file use the signiture file `.asc` and the key `FF054DACD908493A`:
|
||||
|
||||
```
|
||||
gpg --keyserver keyserver.ubuntu.com --recv FF054DACD908493A
|
||||
gpg --verify vikunja-<vikunja version>-linux-amd64-full.zip.asc vikunja-<vikunja version>-linux-amd64-full.zip
|
||||
```
|
||||
|
||||
### Set it up
|
||||
|
||||
Once you've verified the signature, you need to unzip and make it executable.
|
||||
You'll also need to create a symlink to the binary, so that you can execute Vikunja by typing `vikunja` on your system.
|
||||
We'll install vikunja to `/opt/vikunja`, change the path where needed if you want to install it elsewhere.
|
||||
|
||||
Run these commands to install it:
|
||||
|
||||
```
|
||||
mkdir -p /opt/vikunja
|
||||
unzip <vikunja-zip-file> -d /opt/vikunja
|
||||
chmod +x /opt/vikunja
|
||||
sudo ln -s /opt/vikunja/vikunja /usr/bin/vikunja
|
||||
```
|
||||
|
||||
### Systemd service
|
||||
|
||||
To automatically start Vikunja when your system boots and to ensure all dependent services are met, you want to use an init system like systemd.
|
||||
|
||||
Save the following service file to `/etc/systemd/system/vikunja.service` and adapt it to your needs:
|
||||
|
||||
```unit file (systemd)
|
||||
[Unit]
|
||||
Description=Vikunja
|
||||
After=syslog.target
|
||||
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]
|
||||
RestartSec=2s
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/vikunja
|
||||
ExecStart=/usr/bin/vikunja
|
||||
Restart=always
|
||||
# If you want to bind Vikunja to a port below 1024 uncomment
|
||||
# the two values below
|
||||
###
|
||||
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
If you've installed Vikunja to a directory other than `/opt/vikunja`, you need to adapt `WorkingDirectory` accordingly.
|
||||
|
||||
After you made all necessary modifications, it's time to start the service:
|
||||
|
||||
```
|
||||
sudo systemctl enable vikunja
|
||||
sudo systemctl start vikunja
|
||||
```
|
||||
|
||||
### Build from source
|
||||
|
||||
To build vikunja from source, see [building from source]({{< ref "build-from-source.md">}}).
|
||||
|
||||
### Updating
|
||||
|
||||
[Make a backup first]({{< ref "backups.md" >}}).
|
||||
|
||||
Simply replace the binary with the new version, then restart Vikunja.
|
||||
It will automatically run all necessary database migrations.
|
||||
**Make sure to take a look at the changelog for the new version to not miss any manual steps the update may involve!**
|
||||
|
||||
## Docker
|
||||
|
||||
(Note: this assumes some familiarity with docker)
|
||||
|
||||
To get up and running quickly, use this command:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
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.
|
||||
See [config]({{< ref "config.md">}}) for a list of available configuration options.
|
||||
|
||||
Check out the [docker examples]({{<ref "full-docker-example.md">}}) for more advanced configuration using mysql / postgres and a reverse proxy.
|
||||
|
||||
### 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 lose them if the container restarts.
|
||||
|
||||
### Docker compose
|
||||
|
||||
Check out the [docker examples]({{<ref "full-docker-example.md">}}) for more advanced configuration using docker compose.
|
||||
|
||||
## Debian packages
|
||||
|
||||
Vikunja is available as deb package for installation on debian-like systems.
|
||||
|
||||
To install these, grab a `.deb` file from [the download page](https://dl.vikunja.io/vikunja) and run
|
||||
|
||||
```
|
||||
dpkg -i vikunja.deb
|
||||
```
|
||||
|
||||
This will install Vikunja to `/opt/vikunja`.
|
||||
To configure it, use the config file in `/etc/vikunja/config.yml`.
|
||||
|
||||
## RPM
|
||||
|
||||
Vikunja is available as rpm package for installation on Fedora, CentOS and others.
|
||||
|
||||
To install these, grab a `.rpm` file from [the download page](https://dl.vikunja.io/vikunja) and run
|
||||
|
||||
```
|
||||
rpm -i vikunja.rpm
|
||||
```
|
||||
|
||||
To configure Vikunja, 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 a 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
|
||||
|
||||
```
|
||||
pkg update && pkg upgrade -y
|
||||
pkg install nano git go gmake
|
||||
go install github.com/magefile/mage
|
||||
```
|
||||
|
||||
### Clone vikunja repo
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
### Compile binaries
|
||||
|
||||
```
|
||||
cd frontend
|
||||
pnpm install
|
||||
pnpm run build
|
||||
cd ..
|
||||
mage build
|
||||
```
|
||||
|
||||
### Create folder to install Vikunja into
|
||||
|
||||
```
|
||||
mkdir /mnt/vikunja
|
||||
cp /mnt/GO/code.vikunja.io/api/vikunja /mnt/vikunja
|
||||
cd /mnt/vikunja
|
||||
chmod +x /mnt/vikunja
|
||||
```
|
||||
|
||||
### Set vikunja to boot on startup
|
||||
|
||||
```
|
||||
nano /etc/rc.d/vikunja
|
||||
```
|
||||
|
||||
Then paste into the file:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name=vikunja
|
||||
rcvar=vikunja_enable
|
||||
|
||||
command="/mnt/vikunja/${name}"
|
||||
|
||||
load_rc_config $name
|
||||
run_rc_command "$1"
|
||||
```
|
||||
|
||||
Save and exit. Then execute:
|
||||
|
||||
```
|
||||
chmod +x /etc/rc.d/vikunja
|
||||
nano /etc/rc.conf
|
||||
```
|
||||
|
||||
Then add line to bottom of file:
|
||||
|
||||
```
|
||||
vikunja_enable="YES"
|
||||
```
|
||||
|
||||
Test vikunja now works with
|
||||
|
||||
```
|
||||
service vikunja start
|
||||
```
|
||||
|
||||
Vikunja is now available through IP:
|
||||
|
||||
```
|
||||
192.168.1.XXX:3456
|
||||
```
|
||||
|
||||
## Other installation resources
|
||||
|
||||
|
@ -57,3 +304,12 @@ A third-party Helm Chart is available from the k8s-at-home project [here](https:
|
|||
* [Self-Hosted To-Do List with Vikunja in Docker](https://www.youtube.com/watch?v=DqyqDWpEvKI) (Youtube)
|
||||
* [Vikunja self-hosted (step by step)](https://nguyenminhhung.com/vikunja-self-hosted-step-by-step/)
|
||||
* [How to Install Vikunja on Your Synology NAS](https://mariushosting.com/how-to-install-vikunja-on-your-synology-nas/)
|
||||
|
||||
## Configuration
|
||||
|
||||
See [available configuration options]({{< ref "config.md">}}).
|
||||
|
||||
## Default Password
|
||||
|
||||
After successfully installing Vikunja, there is no default user or password.
|
||||
You only need to register a new account and set all the details when creating it.
|
||||
|
|
|
@ -10,7 +10,7 @@ menu:
|
|||
|
||||
# OpenID example configurations
|
||||
|
||||
On this page you will find examples about how to set up Vikunja with a third-party OpenID provider.
|
||||
On this page you will find examples about how to set up Vikunja with a third-party OAuth 2.0 provider using OpenID Connect.
|
||||
To add another example, please [edit this document](https://kolaente.dev/vikunja/vikunja/src/branch/main/docs/content/doc/setup/openid-examples.md) and send a PR.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
@ -67,7 +67,7 @@ Google config:
|
|||
|
||||
Note that there currently seems to be no way to stop creation of new users, even when `enableregistration` is `false` in the configuration. This means that this approach works well only with an "Internal Organization" app for Google Workspace, which limits the allowed users to organizational accounts only. External / public applications will potentially allow every Google user to register.
|
||||
|
||||
## Keycloak
|
||||
## Keycloak
|
||||
|
||||
Vikunja Config:
|
||||
```yaml
|
||||
|
@ -111,4 +111,7 @@ auth:
|
|||
clientsecret: "" # copy from Authentik
|
||||
```
|
||||
|
||||
**Note:** The `authurl` that Vikunja requires is not the `Authorize URL` that you can see in the Provider. Vikunja uses Open ID Discovery to find the correct endpoint to use. Vikunja does this by automatically accessing the `OpenID Configuration URL` (usually `https://authentik.mydomain.com/application/o/vikunja/.well-known/openid-configuration`). Use this URL without the `.well-known/openid-configuration` as the `authurl`.
|
||||
**Note:** The `authurl` that Vikunja requires is not the `Authorize URL` that you can see in the Provider.
|
||||
OpenID Discovery is used to find the correct endpoint to use automatically, by accessing the `OpenID Configuration URL` (usually `https://authentik.mydomain.com/application/o/vikunja/.well-known/openid-configuration`).
|
||||
Use this URL without the `.well-known/openid-configuration` as the `authurl`.
|
||||
Typically this URL can be found in the metadata section within your identity provider.
|
||||
|
|
180
docs/content/doc/setup/openid.md
Normal file
180
docs/content/doc/setup/openid.md
Normal file
|
@ -0,0 +1,180 @@
|
|||
---
|
||||
date: "2022-08-09:00:00+02:00"
|
||||
title: "OpenID"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# OpenID
|
||||
|
||||
Vikunja allows for authentication with an external identity source such as Authentik, Keycloak or similar via the
|
||||
[OpenID Connect](https://openid.net/developers/specs/) standard.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## OpenID Connect Overview
|
||||
|
||||
OpenID Connect is a standardized identity layer built on top of the more generic OAuth 2.0 specification, simplying interaction between the involved parties significantly.
|
||||
While the [OpenID specification](https://openid.net/specs/openid-connect-core-1_0.html#Overview) is worth a read, we summarize the most important basics here.
|
||||
|
||||
The involved parties are:
|
||||
|
||||
- **Resource Owner:** typically the end-user
|
||||
- **Resource Server:** the application server handling requests from the client, the Vikunja API in our case
|
||||
- **Client:** the application or client accessing the RS on behalf of the RO. Vikunja web frontend or any of the apps
|
||||
- **Authorization Server:** the server verifying the user identity and issuing tokens. These docs also use the words `OAuth 2.0 provider`, `Identity Provider` interchangeably.
|
||||
|
||||
After the user is authenticated, the provider issues a token to the user, containing various claims.
|
||||
There's different types of tokens (ID token, access token, refresh token), and all of them are created as [JSON Web Token](https://www.rfc-editor.org/info/rfc7519).
|
||||
Claims in turn are assertions containing information about the token bearer, usually the user.
|
||||
|
||||
**Scopes** are requested by the client when redirecting the end-user to the Authorization Server for authentication, and indirectly control which claims are included in the resulting tokens.
|
||||
There's certain default scopes, but its also possible to define custom scopes, which are used by the feature assigning users to Teams automatically.
|
||||
|
||||
## Configuring OIDC Authentication
|
||||
|
||||
To achieve authentication via an external provider, it is required to (a) configure a confidential Client on your OAuth 2.0 provider and (b) configure Vikunja to authenticate against this provider.
|
||||
[Example configurations]({{< ref "openid-examples.md">}}) are provided for various different identity providers, below you can find generic guides though.
|
||||
|
||||
OpenID Connect defines various flow types indicating how exactly the interaction between the involved parties work, Vikunja makes use of the standard **Authorization Code Flow**.
|
||||
|
||||
### Step 1: Configure your Authorization Server
|
||||
|
||||
The first step is to configure the Authorization Server to correctly handle requests coming from Vikunja.
|
||||
In general, this involves the following steps at a minimum:
|
||||
|
||||
- Create a confidential client and obtain the client ID and client secret
|
||||
- Configure (whitelist) redirect URLs that can be used by Vikunja
|
||||
- Make sure the required scopes (`openid profile email` are the default scopes used by Vikunja) are supported
|
||||
- Optional: configure an additional scope for automatic team assignment, see below for details
|
||||
|
||||
More detailled instructions for various different identity providers can be [found here]({{< ref "openid-examples.md">}})
|
||||
|
||||
### Step 2: Configure Vikunja
|
||||
|
||||
Vikunja has to be configured to use the identity provider. Please note that there is currently no option to configure these settings via environment variables, they have to be defined using the configuration file. The configuration schema is as follows:
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
openid:
|
||||
enabled: true
|
||||
redirecturl: https://vikunja.mydomain.com/auth/openid/ <---- slash at the end is important
|
||||
providers:
|
||||
- name: <provider-name>
|
||||
authurl: <auth-url>
|
||||
clientid: <vikunja client-id>
|
||||
clientsecret: <vikunja client-secret>
|
||||
scope: openid profile email
|
||||
```
|
||||
|
||||
The values for `authurl` can be obtained from the Metadata of your provider, while `clientid` and `clientsecret` are obtained when configuring the client.
|
||||
The scope usually doesn't need to be specified or changed, unless you want to configure the automatic team assignment.
|
||||
|
||||
Optionally it is possible to disable local authentication and therefore forcing users to login via OpenID connect:
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
local:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
## Automatically assign users to teams
|
||||
|
||||
Vikunja is capable of automatically adding users to a team based on OIDC claims added by the identity provider.
|
||||
If configured, Vikunja will sync teams, automatically create new ones and make sure the members are part of the configured teams.
|
||||
Teams which exist only because they were created from oidc attributes are not editable in Vikunja.
|
||||
|
||||
To distinguish between teams created in Vikunja and teams generated automatically via oidc, generated teams have an `oidcID` assigned internally.
|
||||
Within the UI, the teams created through OIDC get a `(OIDC)` suffix to make them distinguishable from locally created teams.
|
||||
|
||||
On a high level, you need to make sure that the **ID token** issued by your identity provider contains a `vikunja_groups` claim, following the structure defined below.
|
||||
It depends on the provider being used as well as the preferences of the administrator how this is achieved.
|
||||
Typically you'd want to request an additional scope (e.g. `vikunja_scope`) which then triggers the identity provider to add the claim.
|
||||
If the `vikunja_groups` is part of the **ID token**, Vikunja will start the procedure and import teams and team memberships.
|
||||
|
||||
The claim structure expexted by Vikunja is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"vikunja_groups": [
|
||||
{
|
||||
"name": "team 1",
|
||||
"oidcID": 33349
|
||||
},
|
||||
{
|
||||
"name": "team 2",
|
||||
"oidcID": 35933
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For each team, you need to define a team `name` and an `oidcID`, where the `oidcID` can be any string with a length of less than 250 characters.
|
||||
The `oidcID` is used to uniquely identify the team, so please make sure to keep this unique.
|
||||
|
||||
Below you'll find two example implementations for Authentik and Keycloak.
|
||||
If you've successfully implemented this with another identity provider, please let us know and submit a PR to improve the docs.
|
||||
|
||||
### Setup in Authentik
|
||||
|
||||
To configure automatic team management through Authentik, we assume you have already [set up Authentik]({{< ref "openid-examples.md">}}#authentik) as an OIDC provider for authentication with Vikunja.
|
||||
|
||||
To use Authentik's group assignment feature, follow these steps:
|
||||
|
||||
1. Edit [your config]({{< ref "config.md">}}) to include the following scopes: `openid profile email vikunja_scope`
|
||||
2. Open `<your authentik url>/if/admin/#/core/property-mappings`
|
||||
3. Create a new property mapping called `vikunja_scope` as scope mapping. There is a field `expression` to enter python expressions that will be delivered with the oidc token.
|
||||
4. Write a small script like the following to add group information to `vikunja_scope`:
|
||||
|
||||
```python
|
||||
groupsDict = {"vikunja_groups": []}
|
||||
for group in request.user.ak_groups.all():
|
||||
groupsDict["vikunja_groups"].append({"name": group.name, "oidcID": group.num_pk})
|
||||
return groupsDict
|
||||
```
|
||||
|
||||
5. In Authentik's menu on the left, go to Applications > Providers > Select the Vikunja provider. Then click on "Edit", on the bottom open "Advanced protocol settings", select the newly created property mapping under "Scopes". Save the provider.
|
||||
|
||||
Now when you log into Vikunja via Authentik it will show you a list of scopes you are claiming.
|
||||
You should see the description you entered on the OIDC provider's admin area.
|
||||
|
||||
Proceed to vikunja and open the teams page in the sidebar menu.
|
||||
You should see "(OIDC)" written next to each team you were assigned through OIDC.
|
||||
|
||||
### Setup in Keycloak
|
||||
|
||||
The kind people from Makerspace Darmstadt e.V. have written [a guide on how to create a mapper for Vikunja here](https://github.com/makerspace-darmstadt/keycloak-vikunja-mapper).
|
||||
|
||||
## Use cases
|
||||
|
||||
All examples assume one team called "Team 1" to be configured within your provider.
|
||||
|
||||
* *Token delivers team.name +team.oidcID and Vikunja team does not exist:* \
|
||||
New team will be created called "Team 1" with attribute oidcID: "33929"
|
||||
|
||||
2. *In Vikunja Team with name "team 1" already exists in vikunja, but has no oidcID set:* \
|
||||
new team will be created called "team 1" with attribute oidcID: "33929"
|
||||
|
||||
3. *In Vikunja Team with name "team 1" already exists in vikunja, but has different oidcID set:* \
|
||||
new team will be created called "team 1" with attribute oidcID: "33929"
|
||||
|
||||
4. *In Vikunja Team with oidcID "33929" already exists in vikunja, but has different name than "team1":* \
|
||||
new team will be created called "team 1" with attribute oidcID: "33929"
|
||||
|
||||
5. *Scope vikunja_scope is not set:* \
|
||||
nothing happens
|
||||
|
||||
6. *oidcID is not set:* \
|
||||
You'll get error.
|
||||
Custom Scope malformed
|
||||
"The custom scope set by the OIDC provider is malformed. Please make sure the openid provider sets the data correctly for your scope. Check especially to have set an oidcID."
|
||||
|
||||
7. *In Vikunja I am in "team 3" with oidcID "", but the token does not deliver any data for "team 3":* \
|
||||
You will stay in team 3 since it was not set by the oidc provider
|
||||
|
||||
8. *In Vikunja I am in "team 3" with oidcID "12345", but the token does not deliver any data for "team 3"*:\
|
||||
You will be signed out of all teams, which have an oidcID set and are not contained in the token.
|
||||
Especially if you've been the last team member, the team will be deleted.
|
|
@ -8,73 +8,28 @@ menu:
|
|||
parent: "setup"
|
||||
---
|
||||
|
||||
# Setup behind a reverse proxy which also serves the frontend
|
||||
# Setup behind a reverse proxy
|
||||
|
||||
These examples assume you have an instance of the backend running on your server listening on port `3456`.
|
||||
These examples assume you have an instance of Vikunja 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`:
|
||||
|
||||
You may need to adjust `server_name` and `root` accordingly.
|
||||
|
||||
### with gzip enabled (recommended)
|
||||
|
||||
{{< highlight conf >}}
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_min_length 256;
|
||||
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype image/svg+xml;
|
||||
|
||||
```conf
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
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 >}}
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /path/to/vikunja/static/frontend/files;
|
||||
try_files $uri $uri/ /;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
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.
|
||||
|
@ -82,33 +37,28 @@ server {
|
|||
|
||||
## NGINX Proxy Manager (NPM)
|
||||
|
||||
### Method 1
|
||||
|
||||
Following the [Docker Walkthrough]({{< ref "docker-start-to-finish.md" >}}) guide, you should be able to get Vikunja to work via HTTP connection to your server IP.
|
||||
|
||||
From there, all you have to do is adjust the following things:
|
||||
|
||||
#### In `docker-compose.yml`
|
||||
|
||||
Under `api:`,
|
||||
|
||||
1. Change `VIKUNJA_SERVICE_FRONTENDURL:` to your desired domain with `https://` and `/`.
|
||||
### In `docker-compose.yml`
|
||||
|
||||
1. Change `VIKUNJA_SERVICE_PUBLICURL:` to your desired domain with `https://` and `/`.
|
||||
2. Expose your desired port on host under `ports:`.
|
||||
|
||||
example:
|
||||
|
||||
```yaml
|
||||
api:
|
||||
image: vikunja/api
|
||||
vikunja:
|
||||
image: vikunja/vikunja
|
||||
environment:
|
||||
VIKUNJA_SERVICE_PUBLICURL: https://vikunja.your-domain.com/ # change vikunja.your-domain.com to your desired domain/subdomain.
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: secret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
VIKUNJA_SERVICE_JWTSECRET: <your-random-secret>
|
||||
VIKUNJA_SERVICE_FRONTENDURL: https://vikunja.your-domain.com/ # change vikunja.your-domain.com to your desired domain/subdomain.
|
||||
ports:
|
||||
- 3456:3456 # Change 3456 on the left to the port of your choice.
|
||||
volumes:
|
||||
|
@ -118,56 +68,17 @@ example:
|
|||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Under `frontend:`,
|
||||
### In your DNS provider
|
||||
|
||||
1. Add `VIKUNJA_API_URL:` under `environment:` and input your desired `API` domain with `https://` and `/api/v1/`. The `API` domain should be different from the one in `VIKUNJA_SERVICE_FRONTENDURL:`.
|
||||
Add an `A` records that points to your server IP.
|
||||
|
||||
example:
|
||||
You are of course free to change them to whatever domain/subdomain you desire and modify the `docker-compose.yml` accordingly.
|
||||
|
||||
```yaml
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
environment:
|
||||
VIKUNJA_API_URL: https://api.your-domain.com/api/v1/ # change api.your-domain.com to your desired domain/subdomain, it should be different from your frontend domain
|
||||
restart: unless-stopped
|
||||
```
|
||||
(Tested on Cloudflare DNS. Settings are different for different DNS provider, in this case the end result should be `vikunja.your-domain.com`)
|
||||
|
||||
Under `proxy:`,
|
||||
### In Nginx Proxy Manager
|
||||
|
||||
1. Since we'll be using Nginx Proxy Manager, it should by default uses the port `80` and thus you should change `ports:` to expose another port not occupied by any service.
|
||||
|
||||
example:
|
||||
|
||||
```yaml
|
||||
proxy:
|
||||
image: nginx
|
||||
ports:
|
||||
- 1078:80 # change the number infront (host port) to whatever you desire, but make sure it's not 80 which will be used by Nginx Proxy Manager
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- api
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
#### In your DNS provider
|
||||
|
||||
Add two `A` records that points to your server IP.
|
||||
|
||||
1. `vikunja` for accessing the frontend
|
||||
|
||||
2. `api` for accessing the api
|
||||
|
||||
You are of course free to change them to whatever domain/subdomain you desire and modify the `docker-compose.yml` accordingly but the two should be different.
|
||||
|
||||
(Tested on Cloudflare DNS. Settings are different for different DNS provider, in this case the end result should bei `vikunja.your-domain.com` and `api.your-domain.com` respectively.)
|
||||
|
||||
#### In Nginx Proxy Manager
|
||||
|
||||
Add two Proxy Host as you normally would, and you don't have to add anything extra in Advanced.
|
||||
|
||||
##### Frontend
|
||||
Add a Proxy Host as you normally would, and you don't have to add anything extra in Advanced.
|
||||
|
||||
Under `Details`:
|
||||
|
||||
|
@ -178,46 +89,6 @@ Scheme:
|
|||
http
|
||||
Forward Hostname/IP:
|
||||
your-server-ip
|
||||
Forward Port:
|
||||
1078
|
||||
Cached Assets:
|
||||
Optional.
|
||||
Block Common Exploits:
|
||||
Toggled.
|
||||
Websockets Support:
|
||||
Toggled.
|
||||
```
|
||||
|
||||
Under `SSL`:
|
||||
|
||||
```
|
||||
SSL Certificate:
|
||||
However you prefer.
|
||||
Force SSL:
|
||||
Toggled.
|
||||
HTTP/2 Support:
|
||||
Toggled.
|
||||
HSTS Enabled:
|
||||
Toggled.
|
||||
HSTS Subdomains:
|
||||
Toggled.
|
||||
Use a DNS Challenge:
|
||||
Not toggled.
|
||||
Email Address for Let's Encrypt:
|
||||
your-email@email.com
|
||||
```
|
||||
|
||||
##### API
|
||||
|
||||
Under `Details`:
|
||||
|
||||
```
|
||||
Domain Names:
|
||||
api.your-domain.com
|
||||
Scheme:
|
||||
http
|
||||
Forward Hostname/IP:
|
||||
your-server-ip
|
||||
Forward Port:
|
||||
3456
|
||||
Cached Assets:
|
||||
|
@ -249,27 +120,11 @@ Email Address for Let's Encrypt:
|
|||
|
||||
Your Vikunja service should now work and your HTTPS frontend should be able to reach the API after `docker-compose`.
|
||||
|
||||
### Method 2
|
||||
|
||||
1. Create a standard Proxy Host for the Vikunja Frontend within NPM and point it to the URL you plan to use. The next several steps will enable the Proxy Host to successfully navigate to the API (on port 3456).
|
||||
2. Verify that the page will pull up in your browser. (Do not bother trying to log in. It won't work. Trust me.)
|
||||
3. Now, we'll work with the NPM container, so you need to identify the container name for your NPM installation. e.g. NGINX-PM
|
||||
4. From the command line, enter `sudo docker exec -it [NGINX-PM container name] /bin/bash` and navigate to the proxy hosts folder where the `.conf` files are stashed. Probably `/data/nginx/proxy_host`. (This folder is a persistent folder created in the NPM container and mounted by NPM.)
|
||||
5. Locate the `.conf` file where the server_name inside the file matches your Vikunja Proxy Host. Once found, add the following code, unchanged, just above the existing location block in that file. (They are listed by number, not name.)
|
||||
```nginx
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://api:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
```
|
||||
6. After saving the edited file, return to NPM's UI browser window and refresh the page to verify your Proxy Host for Vikunja is still online.
|
||||
7. Now, switch over to your Vikunja browser window and hit refresh. If you configured your URL correctly in original Vikunja container, you should be all set and the browser will correctly show Vikunja. If not, you'll need to adjust the address in the top of the login subscreen to match your proxy address.
|
||||
|
||||
## Apache
|
||||
|
||||
Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
|
||||
|
||||
{{< highlight aconf >}}
|
||||
```aconf
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
|
||||
|
@ -277,40 +132,21 @@ Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
|
|||
Order Deny,Allow
|
||||
Allow from all
|
||||
</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 ^\/?(favicon\.ico|assets|audio|fonts|images|manifest\.webmanifest|robots\.txt|sw\.js|workbox-.*|api|dav|\.well-known) - [L]
|
||||
RewriteRule ^(.*)$ /index.html [QSA,L]
|
||||
ProxyPass / http://localhost:3456/
|
||||
ProxyPassReverse / http://localhost:3456/
|
||||
</VirtualHost>
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
**Note:** The apache modules `proxy`, `proxy_http` and `rewrite` must be enabled for this.
|
||||
|
||||
For more details see the [frontend apache configuration]({{< ref "install-frontend.md#apache">}}).
|
||||
|
||||
## Caddy
|
||||
|
||||
{{< highlight conf >}}
|
||||
Use the following Caddyfile to get Vikunja up and running:
|
||||
|
||||
```conf
|
||||
vikunja.domainname.tld {
|
||||
@paths {
|
||||
path /api/* /.well-known/* /dav/*
|
||||
}
|
||||
handle @paths {
|
||||
handle /* {
|
||||
reverse_proxy 127.0.0.1:3456
|
||||
}
|
||||
|
||||
handle {
|
||||
encode zstd gzip
|
||||
root * /var/www/html/vikunja
|
||||
try_files {path} index.html
|
||||
file_server
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
|
|
@ -38,9 +38,7 @@ After saving, build Vikunja as normal.
|
|||
pnpm run build
|
||||
```
|
||||
|
||||
Once you have the build files you can deploy them as usual.
|
||||
Note that when deploying in docker you'll need to put the files in a web container yourself, you
|
||||
can't use the `Dockerfile` in the repo without modifications.
|
||||
Once you have the frontend built, you can proceed to build the binary as outlined in [building from source]({{< ref "build-from-source.md">}}#api).
|
||||
|
||||
## API
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ 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 >}}
|
||||
```sql
|
||||
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'vikunja';
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
This will get you a result like the following:
|
||||
|
||||
|
@ -57,7 +57,7 @@ Before attempting any conversion, please [back up your database]({{< ref "backup
|
|||
|
||||
Copy the following sql statements in a file called `preAlterTables.sql` and replace all occurrences of `vikunja` with the name of your database:
|
||||
|
||||
{{< highlight sql >}}
|
||||
```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;
|
||||
|
@ -67,31 +67,31 @@ SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column
|
|||
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 >}}
|
||||
```sql
|
||||
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'vikunja';
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Should get you:
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ The demo instance at [try.vikunja.io](https://try.vikunja.io) automatically upda
|
|||
|
||||
First you should create a backup of your current setup!
|
||||
|
||||
Switching between versions is the same process as [upgrading]({{< ref install-backend.md >}}#updating).
|
||||
Switching between versions is the same process as [upgrading]({{< ref install.md >}}#updating).
|
||||
Simply replace the stable binary with an unstable one or vice-versa.
|
||||
|
||||
For installations using docker, it is as simple as using the `unstable` or `latest` tag to switch between versions.
|
||||
|
|
|
@ -41,9 +41,9 @@ 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`
|
||||
|
||||
|
@ -51,37 +51,37 @@ Shows more detailed help about any command.
|
|||
|
||||
Usage:
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja help [command]
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
### `migrate`
|
||||
|
||||
Run all database migrations which didn't already run.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja migrate [flags]
|
||||
$ vikunja migrate [command]
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
#### `migrate list`
|
||||
|
||||
Shows a list with all database migrations.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja migrate list
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
#### `migrate rollback`
|
||||
|
||||
Roll migrations back until a certain point.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja migrate rollback [flags]
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Flags:
|
||||
* `-n`, `--name` string: The id of the migration you want to roll back until.
|
||||
|
@ -91,18 +91,18 @@ Flags:
|
|||
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`
|
||||
|
||||
|
@ -113,9 +113,9 @@ Bundles a few commands to manage users.
|
|||
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.
|
||||
|
@ -126,9 +126,9 @@ Flags:
|
|||
Create a new user.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja user create <flags>
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Flags:
|
||||
* `-a`, `--avatar-provider`: The avatar provider of the new user. Optional.
|
||||
|
@ -144,9 +144,9 @@ With the flag the user is deleted **immediately**.
|
|||
|
||||
**USE WITH CAUTION.**
|
||||
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja user delete <id> <flags>
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Flags:
|
||||
* `-n`, `--now` If provided, deletes the user immediately instead of emailing them first.
|
||||
|
@ -156,18 +156,18 @@ Flags:
|
|||
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.
|
||||
|
@ -178,9 +178,9 @@ Flags:
|
|||
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.
|
||||
|
@ -193,15 +193,15 @@ Prints the version of Vikunja.
|
|||
This is either the semantic version (something like `0.7`) or version + git commit hash.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja version
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
### `web`
|
||||
|
||||
Starts Vikunja's REST api server.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
```
|
||||
$ vikunja web
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
|
|
@ -44,6 +44,7 @@ This document describes the different errors Vikunja can return.
|
|||
| 1020 | 412 | This user account is disabled. |
|
||||
| 1021 | 412 | This account is managed by a third-party authentication provider. |
|
||||
| 1021 | 412 | The username must not contain spaces. |
|
||||
| 1022 | 412 | The custom scope set by the OIDC provider is malformed. Please make sure the openid provider sets the data correctly for your scope. Check especially to have set an oidcID. |
|
||||
|
||||
## Validation
|
||||
|
||||
|
@ -70,30 +71,31 @@ This document describes the different errors Vikunja can return.
|
|||
|
||||
## Task
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 4001 | 400 | The project task text cannot be empty. |
|
||||
| 4002 | 404 | The project task does not exist. |
|
||||
| 4003 | 403 | All bulk editing tasks must belong to the same project. |
|
||||
| 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. |
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|----------------------------------------------------------------------------|
|
||||
| 4001 | 400 | The project task text cannot be empty. |
|
||||
| 4002 | 404 | The project task does not exist. |
|
||||
| 4003 | 403 | All bulk editing tasks must belong to the same project. |
|
||||
| 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 | 400 | Invalid task field. |
|
||||
| 4017 | 400 | Invalid task filter comparator. |
|
||||
| 4018 | 400 | Invalid task filter concatinator. |
|
||||
| 4019 | 400 | Invalid task filter value. |
|
||||
| 4020 | 400 | The provided attachment does not belong to that task. |
|
||||
| 4021 | 400 | This user is already assigned to that task. |
|
||||
| 4022 | 400 | The task has a relative reminder which does not specify relative to what. |
|
||||
| 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 | 400 | Invalid task field. |
|
||||
| 4017 | 400 | Invalid task filter comparator. |
|
||||
| 4018 | 400 | Invalid task filter concatinator. |
|
||||
| 4019 | 400 | Invalid task filter value. |
|
||||
| 4020 | 400 | The provided attachment does not belong to that task. |
|
||||
| 4021 | 400 | This user is already assigned to that task. |
|
||||
| 4022 | 400 | The task has a relative reminder which does not specify relative to what. |
|
||||
| 4023 | 409 | Tried to create a task relation which would create a cycle. |
|
||||
|
||||
## Team
|
||||
|
||||
|
@ -105,6 +107,9 @@ This document describes the different errors Vikunja can return.
|
|||
| 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 project to perform that action. |
|
||||
| 6008 | 400 | There are no teams found with that team name. |
|
||||
| 6009 | 400 | There is no oidc team with that team name and oidcId. |
|
||||
| 6010 | 400 | There are no oidc teams found for the user. |
|
||||
|
||||
## User Project Access
|
||||
|
||||
|
|
|
@ -22,4 +22,12 @@ server {
|
|||
location /docs/docs {
|
||||
return 301 $scheme://vikunja.io/docs;
|
||||
}
|
||||
|
||||
location /docs/install-backend {
|
||||
return 301 $scheme://vikunja.io/docs/installing;
|
||||
}
|
||||
|
||||
location /docs/install-frontend {
|
||||
return 301 $scheme://vikunja.io/docs/installing;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
# Changelog
|
||||
|
||||
THIS CHANGELOG ONLY EXISTS FOR HISTORICAL REASONS.
|
||||
Starting with version 0.23.0, all changes are logged in the CHANGELOG.md in the root of this repository since the repos were merged.
|
||||
|
||||
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
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
# ┬─┐┬ ┐o┬ ┬─┐
|
||||
# │─││ │││ │ │
|
||||
# ┘─┘┘─┘┘┘─┘┘─┘
|
||||
|
||||
FROM --platform=$BUILDPLATFORM node:20.11.0-alpine AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
ARG USE_RELEASE=false
|
||||
ARG RELEASE_VERSION=unstable
|
||||
ENV PNPM_CACHE_FOLDER .cache/pnpm/
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD true
|
||||
|
||||
COPY package.json ./
|
||||
COPY pnpm-lock.yaml ./
|
||||
COPY patches ./patches/
|
||||
|
||||
RUN if [ "$USE_RELEASE" != true ]; then \
|
||||
# https://pnpm.io/installation#using-corepack
|
||||
corepack enable && \
|
||||
pnpm install; \
|
||||
fi
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN if [ "$USE_RELEASE" != true ]; then \
|
||||
apk add --no-cache --virtual .build-deps git jq && \
|
||||
git describe --tags --always --abbrev=10 | sed 's/-/+/; s/^v//; s/-g/-/' | \
|
||||
xargs -0 -I{} jq -Mcnr --arg version {} '{VERSION:$version}' | \
|
||||
tee src/version.json && \
|
||||
apk del .build-deps; \
|
||||
fi
|
||||
|
||||
RUN if [ "$USE_RELEASE" = true ]; then \
|
||||
wget "https://dl.vikunja.io/frontend/vikunja-frontend-${RELEASE_VERSION}.zip" -O frontend-release.zip && \
|
||||
unzip frontend-release.zip -d dist/; \
|
||||
else \
|
||||
# we don't use corepack prepare here by intend since
|
||||
# we have renovate to keep our dependencies up to date
|
||||
# Build the frontend
|
||||
pnpm run build; \
|
||||
fi
|
||||
|
||||
# ┌┐┐┌─┐o┌┐┐┐ │
|
||||
# ││││ ┬││││┌┼┘
|
||||
# ┘└┘┘─┘┘┘└┘┘ └
|
||||
|
||||
FROM nginx:stable-alpine AS runner
|
||||
WORKDIR /usr/share/nginx/html
|
||||
LABEL maintainer="maintainers@vikunja.io"
|
||||
|
||||
ENV VIKUNJA_HTTP_PORT 80
|
||||
ENV VIKUNJA_HTTP2_PORT 81
|
||||
ENV VIKUNJA_LOG_FORMAT main
|
||||
ENV VIKUNJA_API_URL /api/v1
|
||||
ENV VIKUNJA_SENTRY_ENABLED false
|
||||
ENV VIKUNJA_SENTRY_DSN https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480
|
||||
ENV VIKUNJA_PROJECT_INFINITE_NESTING_ENABLED false
|
||||
ENV VIKUNJA_ALLOW_ICON_CHANGES true
|
||||
ENV VIKUNJA_CUSTOM_LOGO_URL "''"
|
||||
|
||||
COPY docker/injector.sh /docker-entrypoint.d/50-injector.sh
|
||||
COPY docker/ipv6-disable.sh /docker-entrypoint.d/60-ipv6-disable.sh
|
||||
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY docker/templates/. /etc/nginx/templates/
|
||||
# copy compiled files from stage 1
|
||||
COPY --from=builder /build/dist ./
|
||||
# manage permissions
|
||||
RUN chmod 0755 /docker-entrypoint.d/*.sh /etc/nginx/templates && \
|
||||
chmod -R 0644 /etc/nginx/nginx.conf && \
|
||||
chown -R nginx:nginx ./ /etc/nginx/conf.d /etc/nginx/templates && \
|
||||
rm -f /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
|
|
@ -1,18 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
echo "info: API URL is $VIKUNJA_API_URL"
|
||||
echo "info: Sentry enabled: $VIKUNJA_SENTRY_ENABLED"
|
||||
|
||||
# Escape the variable to prevent sed from complaining
|
||||
VIKUNJA_API_URL="$(echo "$VIKUNJA_API_URL" | sed -r 's/([:;])/\\\1/g')"
|
||||
VIKUNJA_SENTRY_DSN="$(echo "$VIKUNJA_SENTRY_DSN" | sed -r 's/([:;])/\\\1/g')"
|
||||
|
||||
sed -ri "s:^(\s*window.API_URL\s*=)\s*.+:\1 '${VIKUNJA_API_URL}':g" /usr/share/nginx/html/index.html
|
||||
sed -ri "s:^(\s*window.SENTRY_ENABLED\s*=)\s*.+:\1 ${VIKUNJA_SENTRY_ENABLED}:g" /usr/share/nginx/html/index.html
|
||||
sed -ri "s:^(\s*window.SENTRY_DSN\s*=)\s*.+:\1 '${VIKUNJA_SENTRY_DSN}':g" /usr/share/nginx/html/index.html
|
||||
sed -ri "s:^(\s*window.PROJECT_INFINITE_NESTING_ENABLED\s*=)\s*.+:\1 '${VIKUNJA_PROJECT_INFINITE_NESTING_ENABLED}':g" /usr/share/nginx/html/index.html
|
||||
sed -ri "s:^(\s*window.ALLOW_ICON_CHANGES\s*=)\s*.+:\1 ${VIKUNJA_ALLOW_ICON_CHANGES}:g" /usr/share/nginx/html/index.html
|
||||
sed -ri "s:^(\s*window.CUSTOM_LOGO_URL\s*=)\s*.+:\1 ${VIKUNJA_CUSTOM_LOGO_URL}:g" /usr/share/nginx/html/index.html
|
||||
|
||||
date -uIseconds | xargs echo 'info: started at'
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
if [ ! -f "/proc/net/if_inet6" ]; then
|
||||
echo "info: IPv6 is not available! Removing IPv6 listen configuration"
|
||||
find /etc/nginx/conf.d -name '*.conf' -type f | \
|
||||
while IFS= read -r CONFIG; do
|
||||
sed -r '/^\s*listen\s*\[::\]:.+$/d' "$CONFIG" > "$CONFIG.temp"
|
||||
if ! diff -U 5 "$CONFIG" "$CONFIG.temp" > "$CONFIG.diff"; then
|
||||
echo "info: Removing IPv6 lines from $CONFIG" | \
|
||||
cat - "$CONFIG.diff"
|
||||
echo "# IPv6 is disabled because /proc/net/if_inet6 was not found" | \
|
||||
cat - "$CONFIG.temp" > "$CONFIG"
|
||||
else
|
||||
echo "info: Skipping $CONFIG because it does not have IPv6 listen"
|
||||
fi
|
||||
rm -f "$CONFIG.temp" "$CONFIG.diff"
|
||||
done
|
||||
fi
|
|
@ -1,111 +0,0 @@
|
|||
# Generated by nginxconfig.io
|
||||
# https://www.digitalocean.com/community/tools/nginx?domains.0.server.domain=localhost&domains.0.server.documentRoot=%2Fusr%2Fshare%2Fnginx%2Fhtml&domains.0.server.cdnSubdomain=true&domains.0.https.https=false&domains.0.php.php=false&domains.0.routing.index=index.html&domains.0.routing.fallbackHtml=true&domains.0.routing.fallbackPhp=false&global.performance.assetsExpiration=1d&global.performance.mediaExpiration=1d&global.performance.svgExpiration=1d&global.performance.fontsExpiration=1d&global.logging.accessLog=%2Fdev%2Fstdout&global.logging.errorLog=%2Fdev%2Fstderr%20warn&global.logging.logNotFound=true&global.nginx.user=nginx&global.nginx.pid=%2Fvar%2Frun%2Fnginx.pid&global.nginx.clientMaxBodySize=50&global.docker.dockerfile=true&global.tools.modularizedStructure=false&global.tools.symlinkVhost=false
|
||||
# and then edited manually ;)
|
||||
|
||||
pid /tmp/nginx.pid;
|
||||
worker_processes auto;
|
||||
|
||||
events {
|
||||
multi_accept on;
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
charset utf-8;
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
server_tokens off;
|
||||
types_hash_max_size 2048;
|
||||
types_hash_bucket_size 64;
|
||||
|
||||
# rootless
|
||||
client_body_temp_path /tmp/client_temp;
|
||||
proxy_temp_path /tmp/proxy_temp_path;
|
||||
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||
scgi_temp_path /tmp/scgi_temp;
|
||||
|
||||
# MIME
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
types {
|
||||
application/manifest+json webmanifest;
|
||||
}
|
||||
|
||||
# Logging
|
||||
log_format json escape=json
|
||||
'{'
|
||||
'"bytes_sent": "$bytes_sent",'
|
||||
'"http_user_agent": "$http_user_agent",'
|
||||
'"nginx_version": "$nginx_version",'
|
||||
'"query_string": "$query_string",'
|
||||
'"realip_remote_addr": "$realip_remote_addr",'
|
||||
'"remote_addr": "$remote_addr",'
|
||||
'"remote_user": "$remote_user",'
|
||||
'"request_length": "$request_length",'
|
||||
'"request_method": "$request_method",'
|
||||
'"request_time": "$request_time",'
|
||||
'"server_addr": "$server_addr",'
|
||||
'"server_port": "$server_port",'
|
||||
'"server_protocol": "$server_protocol",'
|
||||
'"status": "$status",'
|
||||
'"time_local": "$time_local",'
|
||||
'"uri": "$uri"'
|
||||
'}';
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /dev/stdout main;
|
||||
error_log /dev/stderr warn;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
# compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_min_length 256;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
application/json
|
||||
application/x-javascript
|
||||
application/javascript
|
||||
text/xml
|
||||
application/xml
|
||||
application/xml+rss
|
||||
text/javascript
|
||||
application/vnd.ms-fontobject
|
||||
application/x-font-ttf
|
||||
font/opentype
|
||||
image/svg+xml
|
||||
image/x-icon
|
||||
audio/wav;
|
||||
|
||||
map_hash_max_size 128;
|
||||
map_hash_bucket_size 128;
|
||||
|
||||
map $sent_http_content_type $expires {
|
||||
default off;
|
||||
text/css max;
|
||||
application/javascript max;
|
||||
text/javascript max;
|
||||
application/vnd.ms-fontobject max;
|
||||
application/x-font-ttf max;
|
||||
font/opentype max;
|
||||
font/woff2 max;
|
||||
image/svg+xml max;
|
||||
image/x-icon max;
|
||||
audio/wav max;
|
||||
~images/ max;
|
||||
~font/ max;
|
||||
}
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
server {
|
||||
listen ${VIKUNJA_HTTP_PORT};
|
||||
listen [::]:${VIKUNJA_HTTP_PORT};
|
||||
## Needed when behind HAProxy with SSL termination + HTTP/2 support
|
||||
listen ${VIKUNJA_HTTP2_PORT} default_server http2 proxy_protocol;
|
||||
listen [::]:${VIKUNJA_HTTP2_PORT} default_server http2 proxy_protocol;
|
||||
|
||||
server_name _;
|
||||
expires $expires;
|
||||
root /usr/share/nginx/html;
|
||||
access_log /dev/stdout ${VIKUNJA_LOG_FORMAT};
|
||||
# security headers
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
add_header Content-Security-Policy "default-src 'self' http: https: ws: wss: data: blob: 'unsafe-inline'; frame-ancestors 'self';" always;
|
||||
add_header Permissions-Policy "interest-cohort=()" always;
|
||||
|
||||
# . files
|
||||
location ~ /\.(?!well-known) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# assume that everything else is handled by the application router, by injecting the index.html.
|
||||
location / {
|
||||
autoindex off;
|
||||
expires off;
|
||||
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
|
||||
try_files $uri /index.html =404;
|
||||
}
|
||||
|
||||
# Disable caching for sw
|
||||
location = /sw.js {
|
||||
autoindex off;
|
||||
expires off;
|
||||
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
|
||||
}
|
||||
|
||||
# Disable caching for webmanifest
|
||||
location = /manifest.webmanifest {
|
||||
autoindex off;
|
||||
expires off;
|
||||
add_header Cache-Control "public, max-age=0, s-maxage=0, must-revalidate" always;
|
||||
}
|
||||
|
||||
# favicon.ico
|
||||
location = /favicon.ico {
|
||||
log_not_found off;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# robots.txt
|
||||
location = /robots.txt {
|
||||
log_not_found off;
|
||||
access_log off;
|
||||
expires -1; # no-cache
|
||||
}
|
||||
|
||||
location = /ready {
|
||||
return 200 "";
|
||||
access_log off;
|
||||
expires -1; # no-cache
|
||||
}
|
||||
|
||||
# all assets contain hash in filename, cache forever
|
||||
location ^~ /assets/ {
|
||||
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# all workbox scripts are compiled with hash in filename, cache forever3
|
||||
location ^~ /workbox- {
|
||||
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# assets, media
|
||||
location ~* .(txt|webmanifest|css|js|mjs|map|svg|jpg|jpeg|png|ico|ttf|woff|woff2|wav)$ {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html { }
|
||||
|
||||
}
|
22
frontend/embed.go
Normal file
22
frontend/embed.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-present 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 Affero General Public Licensee 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 Affero General Public Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package frontend
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed dist
|
||||
var Files embed.FS
|
|
@ -23,17 +23,6 @@
|
|||
// It has to be the full url, including the last /api/v1 part and port.
|
||||
// You can change this if your api is not reachable on the same port as the frontend.
|
||||
window.API_URL = 'http://localhost:3456/api/v1'
|
||||
// Enable error tracking with sentry. If this is set to true, will send anonymized data to
|
||||
// our sentry instance to notify us of potential problems.
|
||||
window.SENTRY_ENABLED = false
|
||||
window.SENTRY_DSN = 'https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480'
|
||||
// If enabled, allows the user to nest projects infinitely, instead of the default 2 levels.
|
||||
// This setting might change in the future or be removed completely.
|
||||
window.PROJECT_INFINITE_NESTING_ENABLED = false
|
||||
// Allow changing the logo and other icons based on various occasions throughout the year.
|
||||
window.ALLOW_ICON_CHANGES = true
|
||||
// Allow using a custom logo via external URL.
|
||||
window.CUSTOM_LOGO_URL = ''
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"homepage": "https://vikunja.io/",
|
||||
"funding": "https://opencollective.com/vikunja",
|
||||
"packageManager": "pnpm@8.15.1",
|
||||
"packageManager": "pnpm@8.15.4",
|
||||
"keywords": [
|
||||
"todo",
|
||||
"productivity",
|
||||
|
@ -24,16 +24,19 @@
|
|||
],
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"serve": "vite",
|
||||
"dev": "vite",
|
||||
"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",
|
||||
|
@ -54,53 +57,53 @@
|
|||
"@github/hotkey": "3.1.0",
|
||||
"@infectoone/vue-ganttastic": "2.2.0",
|
||||
"@intlify/unplugin-vue-i18n": "2.0.0",
|
||||
"@kyvg/vue3-notification": "3.1.4",
|
||||
"@sentry/tracing": "7.100.1",
|
||||
"@sentry/vue": "7.100.1",
|
||||
"@tiptap/core": "2.2.1",
|
||||
"@tiptap/extension-blockquote": "2.2.1",
|
||||
"@tiptap/extension-bold": "2.2.1",
|
||||
"@tiptap/extension-bullet-list": "2.2.1",
|
||||
"@tiptap/extension-code": "2.2.1",
|
||||
"@tiptap/extension-code-block-lowlight": "2.2.1",
|
||||
"@tiptap/extension-document": "2.2.1",
|
||||
"@tiptap/extension-dropcursor": "2.2.1",
|
||||
"@tiptap/extension-gapcursor": "2.2.1",
|
||||
"@tiptap/extension-hard-break": "2.2.1",
|
||||
"@tiptap/extension-heading": "2.2.1",
|
||||
"@tiptap/extension-history": "2.2.1",
|
||||
"@tiptap/extension-horizontal-rule": "2.2.1",
|
||||
"@tiptap/extension-image": "2.2.1",
|
||||
"@tiptap/extension-italic": "2.2.1",
|
||||
"@tiptap/extension-link": "2.2.1",
|
||||
"@tiptap/extension-list-item": "2.2.1",
|
||||
"@tiptap/extension-ordered-list": "2.2.1",
|
||||
"@tiptap/extension-paragraph": "2.2.1",
|
||||
"@tiptap/extension-placeholder": "2.2.1",
|
||||
"@tiptap/extension-strike": "2.2.1",
|
||||
"@tiptap/extension-table": "2.2.1",
|
||||
"@tiptap/extension-table-cell": "2.2.1",
|
||||
"@tiptap/extension-table-header": "2.2.1",
|
||||
"@tiptap/extension-table-row": "2.2.1",
|
||||
"@tiptap/extension-task-item": "2.2.1",
|
||||
"@tiptap/extension-task-list": "2.2.1",
|
||||
"@tiptap/extension-text": "2.2.1",
|
||||
"@tiptap/extension-typography": "2.2.1",
|
||||
"@tiptap/extension-underline": "2.2.1",
|
||||
"@tiptap/pm": "2.2.1",
|
||||
"@tiptap/suggestion": "2.2.1",
|
||||
"@tiptap/vue-3": "2.2.1",
|
||||
"@kyvg/vue3-notification": "3.2.0",
|
||||
"@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.7.2",
|
||||
"@vueuse/router": "10.7.2",
|
||||
"@vueuse/core": "10.9.0",
|
||||
"@vueuse/router": "10.9.0",
|
||||
"axios": "1.6.7",
|
||||
"blurhash": "2.0.5",
|
||||
"bulma-css-variables": "0.9.33",
|
||||
"camel-case": "4.1.2",
|
||||
"date-fns": "3.3.1",
|
||||
"dayjs": "1.11.10",
|
||||
"dompurify": "3.0.8",
|
||||
"dompurify": "3.0.9",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"flatpickr": "4.6.13",
|
||||
"flexsearch": "0.7.31",
|
||||
|
@ -115,11 +118,11 @@
|
|||
"sortablejs": "1.15.2",
|
||||
"tippy.js": "6.3.7",
|
||||
"ufo": "1.4.0",
|
||||
"vue": "3.4.15",
|
||||
"vue": "3.4.21",
|
||||
"vue-advanced-cropper": "2.8.8",
|
||||
"vue-flatpickr-component": "11.0.3",
|
||||
"vue-flatpickr-component": "11.0.4",
|
||||
"vue-i18n": "9.9.1",
|
||||
"vue-router": "4.2.5",
|
||||
"vue-router": "4.3.0",
|
||||
"workbox-precaching": "7.0.0",
|
||||
"zhyswan-vuedraggable": "4.1.3"
|
||||
},
|
||||
|
@ -127,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",
|
||||
|
@ -138,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": "6.20.0",
|
||||
"@typescript-eslint/parser": "6.20.0",
|
||||
"@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
|
@ -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!
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
4a7c1293c7b12e9ab476cdf35251a407c6a1cd005d22c06df994222cccfb25cde5f47d15866a098c9d739778fee4dc19 ./scripts/deploy-preview-netlify.mjs
|
||||
2ba5ae4c831fd749296d92f92c5f89339030e22b80be62b1253dc26982e8fd0082e354f884a3ba15293e0b96317ec758 ./scripts/deploy-preview-netlify.mjs
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
class="base-button"
|
||||
:href="href"
|
||||
rel="noreferrer noopener nofollow"
|
||||
target="_blank"
|
||||
:target="openExternalInNewTab ? '_blank' : undefined"
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
|
@ -69,6 +69,7 @@ export interface BaseButtonProps extends /* @vue-ignore */ HTMLAttributes {
|
|||
disabled?: boolean
|
||||
to?: RouteLocationRaw
|
||||
href?: string
|
||||
openExternalInNewTab?: boolean
|
||||
}
|
||||
|
||||
export interface BaseButtonEmits {
|
||||
|
@ -78,6 +79,7 @@ export interface BaseButtonEmits {
|
|||
const {
|
||||
type = BASE_BUTTON_TYPES_MAP.BUTTON,
|
||||
disabled = false,
|
||||
openExternalInNewTab = true,
|
||||
} = defineProps<BaseButtonProps>()
|
||||
|
||||
const emit = defineEmits<BaseButtonEmits>()
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
</ProjectSettingsDropdown>
|
||||
</div>
|
||||
<ProjectsNavigation
|
||||
v-if="canNestDeeper && childProjectsOpen && canCollapse"
|
||||
v-if="childProjectsOpen && canCollapse"
|
||||
:model-value="childProjects"
|
||||
:can-edit-order="true"
|
||||
:can-collapse="canCollapse"
|
||||
|
@ -84,9 +84,10 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref} from 'vue'
|
||||
import {computed} from 'vue'
|
||||
import {useProjectStore} from '@/stores/projects'
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useStorage} from '@vueuse/core'
|
||||
|
||||
import type {IProject} from '@/modelTypes/IProject'
|
||||
|
||||
|
@ -95,7 +96,6 @@ import ProjectSettingsDropdown from '@/components/project/project-settings-dropd
|
|||
import {getProjectTitle} from '@/helpers/getProjectTitle'
|
||||
import ColorBubble from '@/components/misc/colorBubble.vue'
|
||||
import ProjectsNavigation from '@/components/home/ProjectsNavigation.vue'
|
||||
import {canNestProjectDeeper} from '@/helpers/canNestProjectDeeper'
|
||||
|
||||
const {
|
||||
project,
|
||||
|
@ -113,19 +113,24 @@ const projectStore = useProjectStore()
|
|||
const baseStore = useBaseStore()
|
||||
const currentProject = computed(() => baseStore.currentProject)
|
||||
|
||||
const childProjectsOpen = ref(true)
|
||||
// Persist open state across browser reloads. Using a seperate ref for the state
|
||||
// allows us to use only one entry in local storage instead of one for every project id.
|
||||
type openState = { [key: number]: boolean }
|
||||
const childProjectsOpenState = useStorage<openState>('navigation-child-projects-open', {})
|
||||
const childProjectsOpen = computed({
|
||||
get() {
|
||||
return childProjectsOpenState.value[project.id] ?? true
|
||||
},
|
||||
set(open) {
|
||||
childProjectsOpenState.value[project.id] = open
|
||||
},
|
||||
})
|
||||
|
||||
const childProjects = computed(() => {
|
||||
if (!canNestDeeper.value) {
|
||||
return []
|
||||
}
|
||||
|
||||
return projectStore.getChildProjects(project.id)
|
||||
.filter(p => !p.isArchived)
|
||||
.sort((a, b) => a.position - b.position)
|
||||
})
|
||||
|
||||
const canNestDeeper = computed(() => canNestProjectDeeper(level))
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -122,7 +122,7 @@ const labelStore = useLabelStore()
|
|||
labelStore.loadAllLabels()
|
||||
|
||||
const projectStore = useProjectStore()
|
||||
projectStore.loadProjects()
|
||||
projectStore.loadAllProjects()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -594,27 +594,21 @@ function clickTasklistCheckbox(event) {
|
|||
|
||||
watch(
|
||||
() => isEditing.value,
|
||||
editing => {
|
||||
nextTick(() => {
|
||||
const checkboxes = tiptapInstanceRef.value?.querySelectorAll('[data-checked]')
|
||||
async editing => {
|
||||
await nextTick()
|
||||
|
||||
let checkboxes = tiptapInstanceRef.value?.querySelectorAll('[data-checked]')
|
||||
if (typeof checkboxes === 'undefined' || checkboxes.length === 0) {
|
||||
// For some reason, this works when we check a second time.
|
||||
await nextTick()
|
||||
|
||||
checkboxes = tiptapInstanceRef.value?.querySelectorAll('[data-checked]')
|
||||
if (typeof checkboxes === 'undefined' || checkboxes.length === 0) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (editing) {
|
||||
checkboxes.forEach(check => {
|
||||
if (check.children.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
// We assume the first child contains the label element with the checkbox and the second child the actual label
|
||||
// When the actual label is clicked, we forward that click to the checkbox.
|
||||
check.children[1].removeEventListener('click', clickTasklistCheckbox)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (editing) {
|
||||
checkboxes.forEach(check => {
|
||||
if (check.children.length < 2) {
|
||||
return
|
||||
|
@ -622,8 +616,21 @@ watch(
|
|||
|
||||
// We assume the first child contains the label element with the checkbox and the second child the actual label
|
||||
// When the actual label is clicked, we forward that click to the checkbox.
|
||||
check.children[1].addEventListener('click', clickTasklistCheckbox)
|
||||
check.children[1].removeEventListener('click', clickTasklistCheckbox)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
checkboxes.forEach(check => {
|
||||
if (check.children.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
// We assume the first child contains the label element with the checkbox and the second child the actual label
|
||||
// When the actual label is clicked, we forward that click to the checkbox.
|
||||
check.children[1].removeEventListener('click', clickTasklistCheckbox)
|
||||
check.children[1].addEventListener('click', clickTasklistCheckbox)
|
||||
})
|
||||
},
|
||||
{immediate: true},
|
||||
|
@ -781,6 +788,7 @@ watch(
|
|||
|
||||
.ProseMirror {
|
||||
/* Table-specific styling */
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
|
@ -836,6 +844,7 @@ watch(
|
|||
}
|
||||
|
||||
// Lists
|
||||
|
||||
ul {
|
||||
margin-left: .5rem;
|
||||
margin-top: 0 !important;
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
autocomplete="current-password"
|
||||
:tabindex="props.tabindex"
|
||||
@keyup.enter="e => $emit('submit', e)"
|
||||
@focusout="validate"
|
||||
@focusout="() => {validate(); validateAfterFirst = true}"
|
||||
@keyup="() => {validateAfterFirst ? validate() : null}"
|
||||
@input="handleInput"
|
||||
>
|
||||
<BaseButton
|
||||
|
@ -23,16 +24,17 @@
|
|||
</BaseButton>
|
||||
</div>
|
||||
<p
|
||||
v-if="!isValid"
|
||||
v-if="isValid !== true"
|
||||
class="help is-danger"
|
||||
>
|
||||
{{ $t('user.auth.passwordRequired') }}
|
||||
{{ isValid }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, watch} from 'vue'
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -40,13 +42,17 @@ const props = defineProps({
|
|||
modelValue: String,
|
||||
// This prop is a workaround to trigger validation from the outside when the user never had focus in the input.
|
||||
validateInitially: Boolean,
|
||||
validateMinLength: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['submit', 'update:modelValue'])
|
||||
|
||||
const {t} = useI18n()
|
||||
const passwordFieldType = ref('password')
|
||||
const password = ref('')
|
||||
const isValid = ref(!props.validateInitially)
|
||||
const isValid = ref<true | string>(props.validateInitially === true ? true : '')
|
||||
const validateAfterFirst = ref(false)
|
||||
|
||||
watch(
|
||||
() => props.validateInitially,
|
||||
|
@ -55,7 +61,22 @@ watch(
|
|||
)
|
||||
|
||||
const validate = useDebounceFn(() => {
|
||||
isValid.value = password.value !== ''
|
||||
if (password.value === '') {
|
||||
isValid.value = t('user.auth.passwordRequired')
|
||||
return
|
||||
}
|
||||
|
||||
if (props.validateMinLength && password.value.length < 8) {
|
||||
isValid.value = t('user.auth.passwordNotMin')
|
||||
return
|
||||
}
|
||||
|
||||
if (props.validateMinLength && password.value.length > 250) {
|
||||
isValid.value = t('user.auth.passwordNotMax')
|
||||
return
|
||||
}
|
||||
|
||||
isValid.value = true
|
||||
}, 100)
|
||||
|
||||
function togglePasswordFieldType() {
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {defineProps} from 'vue'
|
||||
|
||||
defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -27,7 +27,8 @@ defineProps({
|
|||
display: inline;
|
||||
|
||||
:deep(.tag) {
|
||||
margin-bottom: .25rem;
|
||||
margin-top: .125rem;
|
||||
margin-bottom: .125rem;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -261,13 +261,18 @@ const foundTasks = ref<ITask[]>([])
|
|||
|
||||
async function findTasks(newQuery: string) {
|
||||
query.value = newQuery
|
||||
foundTasks.value = await taskService.getAll({}, {s: newQuery})
|
||||
const result = await taskService.getAll({}, {
|
||||
s: newQuery,
|
||||
sort_by: 'done',
|
||||
})
|
||||
|
||||
foundTasks.value = mapRelatedTasks(result)
|
||||
}
|
||||
|
||||
function mapRelatedTasks(tasks: ITask[]) {
|
||||
return tasks.map(task => {
|
||||
// by doing this here once we can save a lot of duplicate calls in the template
|
||||
const project = projectStore.projects[task.ProjectId]
|
||||
const project = projectStore.projects[task.projectId]
|
||||
|
||||
return {
|
||||
...task,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface SortBy {
|
|||
percent_done?: Order
|
||||
created?: Order
|
||||
updated?: Order
|
||||
done_at?: Order,
|
||||
}
|
||||
|
||||
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
export function canNestProjectDeeper(level: number) {
|
||||
if (level < 2) {
|
||||
return true
|
||||
}
|
||||
|
||||
return level >= 2 && window.PROJECT_INFINITE_NESTING_ENABLED
|
||||
}
|
|
@ -11,14 +11,17 @@ export function getRedirectUrlFromCurrentFrontendPath(provider: IProvider): stri
|
|||
|
||||
export const redirectToProvider = (provider: IProvider) => {
|
||||
|
||||
console.log({provider})
|
||||
|
||||
const redirectUrl = getRedirectUrlFromCurrentFrontendPath(provider)
|
||||
const state = createRandomID(24)
|
||||
localStorage.setItem('state', state)
|
||||
|
||||
window.location.href = `${provider.authUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUrl}&response_type=code&scope=openid email profile&state=${state}`
|
||||
let scope = 'openid email profile'
|
||||
if (provider.scope !== null){
|
||||
scope = provider.scope
|
||||
}
|
||||
window.location.href = `${provider.authUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUrl}&response_type=code&scope=${scope}&state=${state}`
|
||||
}
|
||||
|
||||
export const redirectToProviderOnLogout = (provider: IProvider) => {
|
||||
if (provider.logoutUrl.length > 0) {
|
||||
window.location.href = `${provider.logoutUrl}`
|
||||
|
|
|
@ -57,7 +57,11 @@
|
|||
"logout": "Logout",
|
||||
"emailInvalid": "Please enter a valid email address.",
|
||||
"usernameRequired": "Please provide a username.",
|
||||
"usernameMustNotContainSpace": "The username must not contain spaces.",
|
||||
"usernameMustNotLookLikeUrl": "The username must not look like a URL.",
|
||||
"passwordRequired": "Please provide a password.",
|
||||
"passwordNotMin": "Password must have at least 8 characters.",
|
||||
"passwordNotMax": "Password must have at most 250 characters.",
|
||||
"showPassword": "Show the password",
|
||||
"hidePassword": "Hide the password",
|
||||
"noAccountYet": "Don't have an account yet?",
|
||||
|
@ -712,7 +716,8 @@
|
|||
"repeat": "Repeat",
|
||||
"startDate": "Start Date",
|
||||
"title": "Title",
|
||||
"updated": "Updated"
|
||||
"updated": "Updated",
|
||||
"doneAt": "Done At"
|
||||
},
|
||||
"subscription": {
|
||||
"subscribedTaskThroughParentProject": "You can't unsubscribe here because you are subscribed to this task through its project.",
|
||||
|
@ -973,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",
|
||||
|
@ -1093,8 +1100,7 @@
|
|||
},
|
||||
"about": {
|
||||
"title": "About",
|
||||
"frontendVersion": "Frontend Version: {version}",
|
||||
"apiVersion": "API Version: {version}"
|
||||
"version": "Version: {version}"
|
||||
},
|
||||
"time": {
|
||||
"units": {
|
||||
|
|
|
@ -5,7 +5,6 @@ import pinia from './pinia'
|
|||
import router from './router'
|
||||
import App from './App.vue'
|
||||
import {error, success} from './message'
|
||||
import {VERSION} from './version.json'
|
||||
|
||||
// Notifications
|
||||
import Notifications from '@kyvg/vue3-notification'
|
||||
|
@ -19,16 +18,13 @@ import {getBrowserLanguage, i18n, setLanguage} from './i18n'
|
|||
declare global {
|
||||
interface Window {
|
||||
API_URL: string;
|
||||
SENTRY_ENABLED: boolean;
|
||||
SENTRY_DSN: string;
|
||||
PROJECT_INFINITE_NESTING_ENABLED: boolean;
|
||||
SENTRY_ENABLED?: boolean;
|
||||
SENTRY_DSN?: string;
|
||||
ALLOW_ICON_CHANGES: boolean;
|
||||
CUSTOM_LOGO_URL?: string;
|
||||
}
|
||||
}
|
||||
|
||||
console.info(`Vikunja frontend version ${VERSION}`)
|
||||
|
||||
// Check if we have an api url in local storage and use it if that's the case
|
||||
const apiUrlFromStorage = localStorage.getItem('API_URL')
|
||||
if (apiUrlFromStorage !== null) {
|
||||
|
|
|
@ -9,6 +9,7 @@ export interface ITeam extends IAbstract {
|
|||
description: string
|
||||
members: ITeamMember[]
|
||||
right: Right
|
||||
oidcId: string
|
||||
|
||||
createdBy: IUser
|
||||
created: Date
|
||||
|
|
|
@ -13,6 +13,7 @@ export default class TeamModel extends AbstractModel<ITeam> implements ITeam {
|
|||
description = ''
|
||||
members: ITeamMember[] = []
|
||||
right: Right = RIGHTS.READ
|
||||
oidcId = ''
|
||||
|
||||
createdBy: IUser = {} // FIXME: seems wrong
|
||||
created: Date = null
|
||||
|
|
|
@ -8,7 +8,7 @@ export default async function setupSentry(app: App, router: Router) {
|
|||
|
||||
Sentry.init({
|
||||
app,
|
||||
dsn: window.SENTRY_DSN,
|
||||
dsn: window.SENTRY_DSN ?? '',
|
||||
release: import.meta.env.VITE_PLUGIN_SENTRY_CONFIG.release,
|
||||
dist: import.meta.env.VITE_PLUGIN_SENTRY_CONFIG.dist,
|
||||
integrations: [
|
||||
|
|
|
@ -111,13 +111,13 @@ export function useSavedFilter(projectId?: MaybeRef<IProject['id']>) {
|
|||
|
||||
async function createFilter() {
|
||||
filter.value = await filterService.create(filter.value)
|
||||
await projectStore.loadProjects()
|
||||
await projectStore.loadAllProjects()
|
||||
router.push({name: 'project.index', params: {projectId: getProjectId(filter.value)}})
|
||||
}
|
||||
|
||||
async function saveFilter() {
|
||||
const response = await filterService.update(filter.value)
|
||||
await projectStore.loadProjects()
|
||||
await projectStore.loadAllProjects()
|
||||
success({message: t('filters.edit.success')})
|
||||
response.filters = objectToSnakeCase(response.filters)
|
||||
filter.value = response
|
||||
|
@ -130,7 +130,7 @@ export function useSavedFilter(projectId?: MaybeRef<IProject['id']>) {
|
|||
|
||||
async function deleteFilter() {
|
||||
await filterService.delete(filter.value)
|
||||
await projectStore.loadProjects()
|
||||
await projectStore.loadAllProjects()
|
||||
success({message: t('filters.delete.success')})
|
||||
router.push({name: 'projects.index'})
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -175,20 +175,28 @@ export const useProjectStore = defineStore('project', () => {
|
|||
}
|
||||
}
|
||||
|
||||
async function loadProjects() {
|
||||
async function loadAllProjects() {
|
||||
const cancel = setModuleLoading(setIsLoading)
|
||||
|
||||
const projectService = new ProjectService()
|
||||
const loadedProjects: IProject[] = []
|
||||
let page = 1
|
||||
try {
|
||||
const loadedProjects = await projectService.getAll({}, {is_archived: true}) as IProject[]
|
||||
projects.value = {}
|
||||
setProjects(loadedProjects)
|
||||
loadedProjects.forEach(p => add(p))
|
||||
|
||||
return loadedProjects
|
||||
do {
|
||||
const newProjects = await projectService.getAll({}, {is_archived: true}, page) as IProject[]
|
||||
loadedProjects.push(...newProjects)
|
||||
page++
|
||||
} while (page <= projectService.totalPages)
|
||||
|
||||
} finally {
|
||||
cancel()
|
||||
}
|
||||
|
||||
projects.value = {}
|
||||
setProjects(loadedProjects)
|
||||
loadedProjects.forEach(p => add(p))
|
||||
|
||||
return loadedProjects
|
||||
}
|
||||
|
||||
function getAncestors(project: IProject): IProject[] {
|
||||
|
@ -222,7 +230,7 @@ export const useProjectStore = defineStore('project', () => {
|
|||
setProjects,
|
||||
removeProjectById,
|
||||
toggleProjectFavorite,
|
||||
loadProjects,
|
||||
loadAllProjects,
|
||||
createProject,
|
||||
updateProject,
|
||||
deleteProject,
|
||||
|
|
|
@ -473,7 +473,7 @@ export const useTaskStore = defineStore('task', () => {
|
|||
task = await taskService.update(task)
|
||||
|
||||
// reloading the projects list so that the Favorites project shows up or is hidden when there are (or are not) favorite tasks
|
||||
await projectStore.loadProjects()
|
||||
await projectStore.loadAllProjects()
|
||||
|
||||
return task
|
||||
}
|
||||
|
|
|
@ -4,4 +4,5 @@ export interface IProvider {
|
|||
authUrl: string;
|
||||
clientId: string;
|
||||
logoutUrl: string;
|
||||
scope: string;
|
||||
}
|
||||
|
|
|
@ -13,10 +13,7 @@
|
|||
>
|
||||
<div class="p-4">
|
||||
<p>
|
||||
{{ $t('about.frontendVersion', {version: frontendVersion}) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('about.apiVersion', {version: apiVersion}) }}
|
||||
{{ $t('about.version', {version: apiVersion}) }}
|
||||
</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
|
@ -34,10 +31,8 @@
|
|||
<script setup lang="ts">
|
||||
import {computed} from 'vue'
|
||||
|
||||
import {VERSION} from '@/version.json'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
const configStore = useConfigStore()
|
||||
const apiVersion = computed(() => configStore.version)
|
||||
const frontendVersion = VERSION
|
||||
</script>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<h1>{{ $t('migrate.titleService', {name: migrator.name}) }}</h1>
|
||||
<p>{{ $t('migrate.descriptionDo') }}</p>
|
||||
|
||||
<template v-if="message === '' && lastMigrationFinishedAt === null">
|
||||
<template v-if="message === '' && lastMigrationStartedAt === null">
|
||||
<template v-if="isMigrating === false">
|
||||
<template v-if="migrator.isFileMigrator">
|
||||
<p>{{ $t('migrate.importUpload', {name: migrator.name}) }}</p>
|
||||
|
@ -27,6 +27,7 @@
|
|||
:loading="migrationService.loading"
|
||||
:disabled="migrationService.loading || undefined"
|
||||
:href="authUrl"
|
||||
:open-external-in-new-tab="false"
|
||||
>
|
||||
{{ $t('migrate.getStarted') }}
|
||||
</x-button>
|
||||
|
@ -53,10 +54,10 @@
|
|||
<p>{{ $t('migrate.inProgress') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="lastMigrationStartedAt && lastMigrationFinishedAt === null">
|
||||
<p>
|
||||
<div v-else-if="!migrationJustStarted && lastMigrationStartedAt && lastMigrationFinishedAt === null">
|
||||
<Message class="mb-4">
|
||||
{{ $t('migrate.migrationInProgress') }}
|
||||
</p>
|
||||
</Message>
|
||||
<x-button :to="{name: 'home'}">
|
||||
{{ $t('home.goToOverview') }}
|
||||
</x-button>
|
||||
|
@ -145,6 +146,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])
|
||||
|
||||
|
@ -170,19 +172,18 @@ async function initMigration() {
|
|||
if (!migratorAuthCode.value) {
|
||||
return
|
||||
}
|
||||
const {startedAt, finishedAt} = await migrationService.getStatus()
|
||||
if (startedAt) {
|
||||
lastMigrationStartedAt.value = parseDateOrNull(startedAt)
|
||||
const {started_at, finished_at} = await migrationService.getStatus()
|
||||
if (started_at) {
|
||||
lastMigrationStartedAt.value = parseDateOrNull(started_at)
|
||||
}
|
||||
if (finishedAt) {
|
||||
lastMigrationFinishedAt.value = parseDateOrNull(finishedAt)
|
||||
if (finished_at) {
|
||||
lastMigrationFinishedAt.value = parseDateOrNull(finished_at)
|
||||
if (lastMigrationFinishedAt.value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (lastMigrationStartedAt.value && lastMigrationFinishedAt.value === null) {
|
||||
// Migration already in progress
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -208,12 +209,17 @@ 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.loadAllProjects()
|
||||
}
|
||||
|
||||
await migrationService.migrate(migrationConfig as MigrationConfig)
|
||||
migrationJustStarted.value = true
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
} finally {
|
||||
isMigrating.value = false
|
||||
}
|
||||
|
|
|
@ -82,14 +82,15 @@
|
|||
>
|
||||
<div class="control">
|
||||
<input
|
||||
ref="bucketLimitInputRef"
|
||||
v-focus.always
|
||||
:value="bucket.limit"
|
||||
class="input"
|
||||
type="number"
|
||||
min="0"
|
||||
@keyup.esc="() => showSetLimitInput = false"
|
||||
@keyup.enter="() => showSetLimitInput = false"
|
||||
@input="(event) => setBucketLimit(bucket.id, parseInt((event.target as HTMLInputElement).value))"
|
||||
@keyup.enter="() => {setBucketLimit(bucket.id, true); showSetLimitInput = false}"
|
||||
@input="setBucketLimit(bucket.id)"
|
||||
>
|
||||
</div>
|
||||
<div class="control">
|
||||
|
@ -98,6 +99,7 @@
|
|||
:disabled="bucket.limit < 0"
|
||||
:icon="['far', 'save']"
|
||||
:shadow="false"
|
||||
@click="setBucketLimit(bucket.id, true)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -320,6 +322,7 @@ const taskStore = useTaskStore()
|
|||
const projectStore = useProjectStore()
|
||||
|
||||
const taskContainerRefs = ref<{[id: IBucket['id']]: HTMLElement}>({})
|
||||
const bucketLimitInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
const drag = ref(false)
|
||||
const dragBucket = ref(false)
|
||||
|
@ -413,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']) {
|
||||
|
@ -615,7 +618,7 @@ function updateBucketPosition(e: {newIndex: number}) {
|
|||
})
|
||||
}
|
||||
|
||||
async function setBucketLimit(bucketId: IBucket['id'], limit: number) {
|
||||
async function saveBucketLimit(bucketId: IBucket['id'], limit: number) {
|
||||
if (limit < 0) {
|
||||
return
|
||||
}
|
||||
|
@ -627,6 +630,22 @@ async function setBucketLimit(bucketId: IBucket['id'], limit: number) {
|
|||
success({message: t('project.kanban.bucketLimitSavedSuccess')})
|
||||
}
|
||||
|
||||
const setBucketLimitCancel = ref<number|null>(null)
|
||||
|
||||
async function setBucketLimit(bucketId: IBucket['id'], now: boolean = false) {
|
||||
const limit = parseInt(bucketLimitInputRef.value?.value || '')
|
||||
|
||||
if (setBucketLimitCancel.value !== null) {
|
||||
clearTimeout(setBucketLimitCancel.value)
|
||||
}
|
||||
|
||||
if (now) {
|
||||
return saveBucketLimit(bucketId, limit)
|
||||
}
|
||||
|
||||
setBucketLimitCancel.value = setTimeout(saveBucketLimit, 2500, bucketId, limit)
|
||||
}
|
||||
|
||||
function shouldAcceptDrop(bucket: IBucket) {
|
||||
return (
|
||||
// When dragging from a bucket who has its limit reached, dragging should still be possible
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
<Fancycheckbox v-model="activeColumns.percentDone">
|
||||
{{ $t('task.attributes.percentDone') }}
|
||||
</Fancycheckbox>
|
||||
<Fancycheckbox v-model="activeColumns.doneAt">
|
||||
{{ $t('task.attributes.doneAt') }}
|
||||
</Fancycheckbox>
|
||||
<Fancycheckbox v-model="activeColumns.created">
|
||||
{{ $t('task.attributes.created') }}
|
||||
</Fancycheckbox>
|
||||
|
@ -144,6 +147,13 @@
|
|||
@click="sort('percent_done')"
|
||||
/>
|
||||
</th>
|
||||
<th v-if="activeColumns.doneAt">
|
||||
{{ $t('task.attributes.doneAt') }}
|
||||
<Sort
|
||||
:order="sortBy.done_at"
|
||||
@click="sort('done_at')"
|
||||
/>
|
||||
</th>
|
||||
<th v-if="activeColumns.created">
|
||||
{{ $t('task.attributes.created') }}
|
||||
<Sort
|
||||
|
@ -223,6 +233,10 @@
|
|||
<td v-if="activeColumns.percentDone">
|
||||
{{ t.percentDone * 100 }}%
|
||||
</td>
|
||||
<DateTableCell
|
||||
v-if="activeColumns.doneAt"
|
||||
:date="t.doneAt"
|
||||
/>
|
||||
<DateTableCell
|
||||
v-if="activeColumns.created"
|
||||
:date="t.created"
|
||||
|
@ -297,6 +311,7 @@ const ACTIVE_COLUMNS_DEFAULT = {
|
|||
created: false,
|
||||
updated: false,
|
||||
createdBy: false,
|
||||
doneAt: false,
|
||||
}
|
||||
|
||||
const SORT_BY_DEFAULT: SortBy = {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
:class="{ 'is-loading': teamService.loading }"
|
||||
>
|
||||
<card
|
||||
v-if="userIsAdmin"
|
||||
v-if="userIsAdmin && !team.oidcId"
|
||||
class="is-fullwidth"
|
||||
:title="title"
|
||||
>
|
||||
|
@ -77,7 +77,7 @@
|
|||
:padding="false"
|
||||
>
|
||||
<div
|
||||
v-if="userIsAdmin"
|
||||
v-if="userIsAdmin && !team.oidcId"
|
||||
class="p-4"
|
||||
>
|
||||
<div class="field has-addons">
|
||||
|
|
|
@ -17,11 +17,13 @@
|
|||
class="teams box"
|
||||
>
|
||||
<li
|
||||
v-for="team in teams"
|
||||
:key="team.id"
|
||||
v-for="t in teams"
|
||||
:key="t.id"
|
||||
>
|
||||
<router-link :to="{name: 'teams.edit', params: {id: team.id}}">
|
||||
{{ team.name }}
|
||||
<router-link :to="{name: 'teams.edit', params: {id: t.id}}">
|
||||
<p>
|
||||
{{ t.name }}
|
||||
</p>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -63,7 +65,7 @@ ul.teams {
|
|||
li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid $border;
|
||||
border-bottom: 1px solid var(--grey-200);
|
||||
|
||||
a {
|
||||
color: var(--text);
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
v-model="password"
|
||||
tabindex="2"
|
||||
:validate-initially="validatePasswordInitially"
|
||||
:validate-min-length="false"
|
||||
@submit="submit"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -28,21 +28,24 @@
|
|||
type="text"
|
||||
autocomplete="username"
|
||||
@keyup.enter="submit"
|
||||
@focusout="validateUsername"
|
||||
@focusout="() => {validateUsername(); validateUsernameAfterFirst = true}"
|
||||
@keyup="() => {validateUsernameAfterFirst ? validateUsername() : null}"
|
||||
>
|
||||
</div>
|
||||
<p
|
||||
v-if="!usernameValid"
|
||||
v-if="usernameValid !== true"
|
||||
class="help is-danger"
|
||||
>
|
||||
{{ $t('user.auth.usernameRequired') }}
|
||||
{{ usernameValid }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
class="label"
|
||||
for="email"
|
||||
>{{ $t('user.auth.email') }}</label>
|
||||
>
|
||||
{{ $t('user.auth.email') }}
|
||||
</label>
|
||||
<div class="control">
|
||||
<input
|
||||
id="email"
|
||||
|
@ -53,7 +56,8 @@
|
|||
required
|
||||
type="email"
|
||||
@keyup.enter="submit"
|
||||
@focusout="validateEmail"
|
||||
@focusout="() => {validateEmail(); validateEmailAfterFirst = true}"
|
||||
@keyup="() => {validateEmailAfterFirst ? validateEmail() : null}"
|
||||
>
|
||||
</div>
|
||||
<p
|
||||
|
@ -84,7 +88,7 @@
|
|||
>
|
||||
{{ $t('user.auth.createAccount') }}
|
||||
</x-button>
|
||||
|
||||
|
||||
<Message
|
||||
v-if="configStore.demoModeEnabled"
|
||||
variant="warning"
|
||||
|
@ -94,7 +98,7 @@
|
|||
{{ $t('demo.accountWillBeDeleted') }}<br>
|
||||
<strong class="is-uppercase">{{ $t('demo.everythingWillBeDeleted') }}</strong>
|
||||
</Message>
|
||||
|
||||
|
||||
<p class="mt-2">
|
||||
{{ $t('user.auth.alreadyHaveAnAccount') }}
|
||||
<router-link :to="{ name: 'user.login' }">
|
||||
|
@ -107,7 +111,8 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import {useDebounceFn} from '@vueuse/core'
|
||||
import {ref, reactive, toRaw, computed, onBeforeMount} from 'vue'
|
||||
import {computed, onBeforeMount, reactive, ref, toRaw} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
|
||||
import router from '@/router'
|
||||
import Message from '@/components/misc/message.vue'
|
||||
|
@ -117,6 +122,7 @@ import Password from '@/components/input/password.vue'
|
|||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
const {t} = useI18n()
|
||||
const authStore = useAuthStore()
|
||||
const configStore = useConfigStore()
|
||||
|
||||
|
@ -142,13 +148,30 @@ const DEBOUNCE_TIME = 100
|
|||
|
||||
// debouncing to prevent error messages when clicking on the log in button
|
||||
const emailValid = ref(true)
|
||||
const validateEmailAfterFirst = ref(false)
|
||||
const validateEmail = useDebounceFn(() => {
|
||||
emailValid.value = isEmail(credentials.email)
|
||||
}, DEBOUNCE_TIME)
|
||||
|
||||
const usernameValid = ref(true)
|
||||
const usernameValid = ref<true | string>(true)
|
||||
const validateUsernameAfterFirst = ref(false)
|
||||
const validateUsername = useDebounceFn(() => {
|
||||
usernameValid.value = credentials.username !== ''
|
||||
if (credentials.username === '') {
|
||||
usernameValid.value = t('user.auth.usernameRequired')
|
||||
return
|
||||
}
|
||||
|
||||
if(credentials.username.indexOf(' ') !== -1) {
|
||||
usernameValid.value = t('user.auth.usernameMustNotContainSpace')
|
||||
return
|
||||
}
|
||||
|
||||
if(credentials.username.indexOf('://') !== -1) {
|
||||
usernameValid.value = t('user.auth.usernameMustNotLookLikeUrl')
|
||||
return
|
||||
}
|
||||
|
||||
usernameValid.value = true
|
||||
}, DEBOUNCE_TIME)
|
||||
|
||||
const everythingValid = computed(() => {
|
||||
|
|
|
@ -286,16 +286,15 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
:key="group"
|
||||
class="mb-2"
|
||||
>
|
||||
<strong class="is-capitalized">{{ formatPermissionTitle(group) }}</strong><br>
|
||||
<template
|
||||
v-if="Object.keys(routes).length > 1"
|
||||
v-if="Object.keys(routes).length >= 1"
|
||||
>
|
||||
<Fancycheckbox
|
||||
v-model="newTokenPermissionsGroup[group]"
|
||||
class="mr-2 is-italic"
|
||||
class="mr-2 is-capitalized has-text-weight-bold"
|
||||
@update:modelValue="checked => selectPermissionGroup(group, checked)"
|
||||
>
|
||||
{{ $t('user.settings.apiTokens.selectAll') }}
|
||||
{{ formatPermissionTitle(group) }}
|
||||
</Fancycheckbox>
|
||||
<br>
|
||||
</template>
|
||||
|
@ -305,7 +304,7 @@ function toggleGroupPermissionsFromChild(group: string, checked: boolean) {
|
|||
>
|
||||
<Fancycheckbox
|
||||
v-model="newTokenPermissions[group][route]"
|
||||
class="mr-2 is-capitalized"
|
||||
class="ml-4 mr-2 is-capitalized"
|
||||
@update:modelValue="checked => toggleGroupPermissionsFromChild(group, checked)"
|
||||
>
|
||||
{{ formatPermissionTitle(route) }}
|
||||
|
|
|
@ -19,8 +19,6 @@ import postcssEasingGradients from 'postcss-easing-gradients'
|
|||
|
||||
const pathSrc = fileURLToPath(new URL('./src', import.meta.url))
|
||||
|
||||
import {VERSION} from './src/version.json'
|
||||
|
||||
// the @use rules have to be the first in the compiled stylesheets
|
||||
const PREFIXED_SCSS_STYLES = `@use "sass:math";
|
||||
@import "${pathSrc}/styles/common-imports";`
|
||||
|
@ -46,7 +44,6 @@ function getSentryConfig(env: ImportMetaEnv): ViteSentryPluginOptions {
|
|||
authToken: env.SENTRY_AUTH_TOKEN,
|
||||
org: env.SENTRY_ORG,
|
||||
project: env.SENTRY_PROJECT,
|
||||
release: VERSION,
|
||||
cleanSourcemapsAfterUpload: true,
|
||||
legacyErrorHandlingMode: true,
|
||||
deploy: {
|
||||
|
|
41
go.mod
41
go.mod
|
@ -21,7 +21,7 @@ require (
|
|||
dario.cat/mergo v1.0.0
|
||||
github.com/ThreeDotsLabs/watermill v1.3.5
|
||||
github.com/adlio/trello v1.10.0
|
||||
github.com/arran4/golang-ical v0.2.4
|
||||
github.com/arran4/golang-ical v0.2.6
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/bbrks/go-blurhash v1.1.1
|
||||
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500
|
||||
|
@ -31,14 +31,15 @@ require (
|
|||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2
|
||||
github.com/gabriel-vasile/mimetype v1.4.3
|
||||
github.com/getsentry/sentry-go v0.26.0
|
||||
github.com/getsentry/sentry-go v0.27.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6
|
||||
|
@ -51,8 +52,8 @@ 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/redis/go-redis/v9 v9.4.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
|
||||
github.com/spf13/afero v1.11.0
|
||||
|
@ -65,12 +66,12 @@ 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.18.0
|
||||
golang.org/x/crypto v0.20.0
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/oauth2 v0.16.0
|
||||
golang.org/x/oauth2 v0.17.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sys v0.16.0
|
||||
golang.org/x/term v0.16.0
|
||||
golang.org/x/sys v0.17.0
|
||||
golang.org/x/term v0.17.0
|
||||
golang.org/x/text v0.14.0
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
|
@ -78,14 +79,14 @@ require (
|
|||
src.techknowlogick.com/xgo v1.7.1-0.20240206231429-45b9ea635e03
|
||||
src.techknowlogick.com/xormigrate v1.7.1
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.7
|
||||
xorm.io/xorm v1.3.8
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ClickHouse/ch-go v0.55.0 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.9.1 // indirect
|
||||
github.com/ClickHouse/ch-go v0.58.2 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
|
@ -141,13 +142,13 @@ require (
|
|||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/onsi/ginkgo v1.16.4 // indirect
|
||||
github.com/onsi/gomega v1.16.0 // indirect
|
||||
github.com/paulmach/orb v0.9.0 // indirect
|
||||
github.com/paulmach/orb v0.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
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
|
||||
|
@ -167,17 +168,17 @@ require (
|
|||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 // indirect
|
||||
go.opentelemetry.io/otel v1.15.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.15.0 // indirect
|
||||
go.opentelemetry.io/otel v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.22.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
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
|
||||
|
|
49
go.sum
49
go.sum
|
@ -11,8 +11,12 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ClickHouse/ch-go v0.55.0 h1:jw4Tpx887YXrkyL5DfgUome/po8MLz92nz2heOQ6RjQ=
|
||||
github.com/ClickHouse/ch-go v0.55.0/go.mod h1:kQT2f+yp2p+sagQA/7kS6G3ukym+GQ5KAu1kuFAFDiU=
|
||||
github.com/ClickHouse/ch-go v0.58.2 h1:jSm2szHbT9MCAB1rJ3WuCJqmGLi5UTjlNu+f530UTS0=
|
||||
github.com/ClickHouse/ch-go v0.58.2/go.mod h1:Ap/0bEmiLa14gYjCiRkYGbXvbe8vwdrfTYWhsuQ99aw=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.9.1 h1:IeE2bwVvAba7Yw5ZKu98bKI4NpDmykEy6jUaQdJJCk8=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.9.1/go.mod h1:teXfZNM90iQ99Jnuht+dxQXCuhDZ8nvvMoTJOFrcmcg=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0 h1:O1LicIeg2JS2V29fKRH4+yT3f6jvvcJBm506dpVQ4mQ=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0/go.mod h1:ztQvX6wm7kAbhJslS87EXEhOVNY/TObXwyURnGju5FQ=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
|
@ -26,6 +30,8 @@ github.com/adlio/trello v1.10.0 h1:ia/rzoBwJJKr4IqnMlrU6n09CVqeyaahSkEVcV5/gPc=
|
|||
github.com/adlio/trello v1.10.0/go.mod h1:I4Lti4jf2KxjTNgTqs5W3lLuE78QZZdYbbPnQQGwjOo=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
|
@ -33,6 +39,10 @@ github.com/arran4/golang-ical v0.2.3 h1:C4Vj7+BjJBIrAJhHgi6Ku+XUkQVugRq4re5Cqj5Q
|
|||
github.com/arran4/golang-ical v0.2.3/go.mod h1:RqMuPGmwRRwjkb07hmm+JBqcWa1vF1LvVmPtSZN2OhQ=
|
||||
github.com/arran4/golang-ical v0.2.4 h1:0/rTXn2qqEekLKec3SzRRy+z7pCLtniMb0KD/dPogUo=
|
||||
github.com/arran4/golang-ical v0.2.4/go.mod h1:RqMuPGmwRRwjkb07hmm+JBqcWa1vF1LvVmPtSZN2OhQ=
|
||||
github.com/arran4/golang-ical v0.2.5 h1:zaAdee/cOnOCeSuxUSgkWnF9jZl/oYq2ZgDk+LU3wGs=
|
||||
github.com/arran4/golang-ical v0.2.5/go.mod h1:RqMuPGmwRRwjkb07hmm+JBqcWa1vF1LvVmPtSZN2OhQ=
|
||||
github.com/arran4/golang-ical v0.2.6 h1:WRpbLKSIMjujycCNKGAjOALyj6evvklVpWXH+Hp72G4=
|
||||
github.com/arran4/golang-ical v0.2.6/go.mod h1:RqMuPGmwRRwjkb07hmm+JBqcWa1vF1LvVmPtSZN2OhQ=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/bbrks/go-blurhash v1.1.1 h1:uoXOxRPDca9zHYabUTwvS4KnY++KKUbwFo+Yxb8ME4M=
|
||||
|
@ -113,6 +123,8 @@ github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX
|
|||
github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/getsentry/sentry-go v0.26.0 h1:IX3++sF6/4B5JcevhdZfdKIHfyvMmAq/UnqcyT2H6mA=
|
||||
github.com/getsentry/sentry-go v0.26.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
|
@ -158,6 +170,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
|||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0 h1:938g5V+GWLVejm3Hc+nWCuEXRlcglZDDlN/t1gWzcSY=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.9.0/go.mod h1:cdsKD2ApFBjdog9jRsz6EJqF+LClq/hrwE9K/1Dzo4s=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0 h1:BrBwN7AuC+74g5qtk9D59TLGOaEa8Bw1WmIsf+SyzWc=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0/go.mod h1:z8RoleoNtibi6Ar8ziCW7e6PQ+jWiqbUWvuv8AMe4lo=
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA=
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
||||
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
|
@ -219,6 +233,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO
|
|||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346 h1:Odeq5rB6OZSkib5gqTG+EM1iF0bUVjYYd33XB1ULv00=
|
||||
github.com/hhsnopek/etag v0.0.0-20171206181245-aea95f647346/go.mod h1:4ggHM2qnyyZjenBb7RpwVzIj+JMsu9kHCVxMjB30hGs=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
|
@ -410,11 +426,15 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0C
|
|||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/paulmach/orb v0.9.0 h1:MwA1DqOKtvCgm7u9RZ/pnYejTeDJPnr0+0oFajBbJqk=
|
||||
github.com/paulmach/orb v0.9.0/go.mod h1:SudmOk85SXtmXAB3sLGyJ6tZy/8pdfrV0o6ef98Xc30=
|
||||
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
|
||||
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
|
@ -430,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=
|
||||
|
@ -438,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=
|
||||
|
@ -446,6 +470,10 @@ github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21M
|
|||
github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
|
||||
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.5.0 h1:Xe9TKMmZv939gwTBcvc0n1tzK5l2re0pKw/W/tN3amw=
|
||||
github.com/redis/go-redis/v9 v9.5.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
|
@ -556,10 +584,15 @@ github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRla
|
|||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
||||
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
|
||||
go.opentelemetry.io/otel v1.15.0 h1:NIl24d4eiLJPM0vKn4HjLYM+UZf6gSfi9Z+NmCxkWbk=
|
||||
go.opentelemetry.io/otel v1.15.0/go.mod h1:qfwLEbWhLPk5gyWrne4XnF0lC8wtywbuJbgfAE3zbek=
|
||||
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
|
||||
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
|
||||
go.opentelemetry.io/otel/trace v1.15.0 h1:5Fwje4O2ooOxkfyqI/kJwxWotggDLix4BSAvpE1wlpo=
|
||||
go.opentelemetry.io/otel/trace v1.15.0/go.mod h1:CUsmE2Ht1CRkvE8OsMESvraoZrrcgD1J2W8GV1ev0Y4=
|
||||
go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
|
||||
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
|
@ -597,6 +630,10 @@ golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
|||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
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=
|
||||
|
@ -635,10 +672,14 @@ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
|||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
|
||||
golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -682,6 +723,8 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
|||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
@ -690,6 +733,8 @@ golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
|||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
@ -739,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=
|
||||
|
@ -833,3 +880,5 @@ xorm.io/xorm v1.3.6 h1:hfpWHkDIWWqUi8FRF2H2M9O8lO3Ov47rwFcS9gPzPkU=
|
|||
xorm.io/xorm v1.3.6/go.mod h1:qFJGFoVYbbIdnz2vaL5OxSQ2raleMpyRRalnq3n9OJo=
|
||||
xorm.io/xorm v1.3.7 h1:mLceAGu0b87r9pD4qXyxGHxifOXIIrAdVcA6k95/osw=
|
||||
xorm.io/xorm v1.3.7/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
|
||||
xorm.io/xorm v1.3.8 h1:CJmplmWqfSRpLWSPMmqz+so8toBp3m7ehuRehIWedZo=
|
||||
xorm.io/xorm v1.3.8/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw=
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"github.com/iancoleman/strcase"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -34,6 +33,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
|
||||
"github.com/magefile/mage/mg"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
@ -64,13 +65,15 @@ var (
|
|||
"build": Build.Build,
|
||||
"do-the-swag": DoTheSwag,
|
||||
"check:got-swag": Check.GotSwag,
|
||||
"release": Release.Release,
|
||||
"release:os-package": Release.OsPackage,
|
||||
"dev:make-migration": Dev.MakeMigration,
|
||||
"dev:make-event": Dev.MakeEvent,
|
||||
"dev:make-listener": Dev.MakeListener,
|
||||
"dev:make-notification": Dev.MakeNotification,
|
||||
"generate-docs": GenerateDocs,
|
||||
"check:golangci-fix": Check.GolangciFix,
|
||||
"lint": Check.Golangci,
|
||||
"lint:fix": Check.GolangciFix,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -156,7 +159,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)
|
||||
|
|
|
@ -47,7 +47,7 @@ var testmailCmd = &cobra.Command{
|
|||
Subject("Test from Vikunja").
|
||||
Line("This is a test mail!").
|
||||
Line("If you received this, Vikunja is correctly set up to send emails.").
|
||||
Action("Go to your instance", config.ServiceFrontendurl.GetString())
|
||||
Action("Go to your instance", config.ServicePublicURL.GetString())
|
||||
|
||||
opts, err := notifications.RenderMail(message)
|
||||
if err != nil {
|
||||
|
|
|
@ -44,10 +44,9 @@ const (
|
|||
ServiceInterface Key = `service.interface`
|
||||
ServiceUnixSocket Key = `service.unixsocket`
|
||||
ServiceUnixSocketMode Key = `service.unixsocketmode`
|
||||
ServiceFrontendurl Key = `service.frontendurl`
|
||||
ServicePublicURL Key = `service.publicurl`
|
||||
ServiceEnableCaldav Key = `service.enablecaldav`
|
||||
ServiceRootpath Key = `service.rootpath`
|
||||
ServiceStaticpath Key = `service.staticpath`
|
||||
ServiceMaxItemsPerPage Key = `service.maxitemsperpage`
|
||||
ServiceDemoMode Key = `service.demomode`
|
||||
// Deprecated: Use metrics.enabled
|
||||
|
@ -59,11 +58,17 @@ const (
|
|||
ServiceTimeZone Key = `service.timezone`
|
||||
ServiceEnableTaskComments Key = `service.enabletaskcomments`
|
||||
ServiceEnableTotp Key = `service.enabletotp`
|
||||
ServiceSentryDsn Key = `service.sentrydsn`
|
||||
ServiceTestingtoken Key = `service.testingtoken`
|
||||
ServiceEnableEmailReminders Key = `service.enableemailreminders`
|
||||
ServiceEnableUserDeletion Key = `service.enableuserdeletion`
|
||||
ServiceMaxAvatarSize Key = `service.maxavatarsize`
|
||||
ServiceAllowIconChanges Key = `service.allowiconchanges`
|
||||
ServiceCustomLogoURL Key = `service.customlogourl`
|
||||
|
||||
SentryEnabled Key = `sentry.enabled`
|
||||
SentryDsn Key = `sentry.dsn`
|
||||
SentryFrontendEnabled Key = `sentry.frontendenabled`
|
||||
SentryFrontendDsn Key = `sentry.frontenddsn`
|
||||
|
||||
AuthLocalEnabled Key = `auth.local.enabled`
|
||||
AuthOpenIDEnabled Key = `auth.openid.enabled`
|
||||
|
@ -289,11 +294,10 @@ func InitDefaultConfig() {
|
|||
ServiceJWTTTLLong.setDefault(2592000) // 30 days
|
||||
ServiceInterface.setDefault(":3456")
|
||||
ServiceUnixSocket.setDefault("")
|
||||
ServiceFrontendurl.setDefault("")
|
||||
ServicePublicURL.setDefault("")
|
||||
ServiceEnableCaldav.setDefault(true)
|
||||
|
||||
ServiceRootpath.setDefault(getBinaryDirLocation())
|
||||
ServiceStaticpath.setDefault("")
|
||||
ServiceMaxItemsPerPage.setDefault(50)
|
||||
ServiceEnableMetrics.setDefault(false)
|
||||
ServiceMotd.setDefault("")
|
||||
|
@ -307,6 +311,11 @@ func InitDefaultConfig() {
|
|||
ServiceEnableUserDeletion.setDefault(true)
|
||||
ServiceMaxAvatarSize.setDefault(1024)
|
||||
ServiceDemoMode.setDefault(false)
|
||||
ServiceAllowIconChanges.setDefault(true)
|
||||
|
||||
// Sentry
|
||||
SentryDsn.setDefault("https://440eedc957d545a795c17bbaf477497c@o1047380.ingest.sentry.io/4504254983634944")
|
||||
SentryFrontendDsn.setDefault("https://85694a2d757547cbbc90cd4b55c5a18d@o1047380.ingest.sentry.io/6024480")
|
||||
|
||||
// Auth
|
||||
AuthLocalEnabled.setDefault(true)
|
||||
|
@ -372,7 +381,7 @@ func InitDefaultConfig() {
|
|||
FilesBasePath.setDefault("files")
|
||||
FilesMaxSize.setDefault("20MB")
|
||||
// Cors
|
||||
CorsEnable.setDefault(true)
|
||||
CorsEnable.setDefault(false)
|
||||
CorsOrigins.setDefault([]string{"*"})
|
||||
CorsMaxAge.setDefault(0)
|
||||
// Migration
|
||||
|
@ -446,20 +455,20 @@ func InitConfig() {
|
|||
RateLimitStore.Set(KeyvalueType.GetString())
|
||||
}
|
||||
|
||||
if ServiceFrontendurl.GetString() != "" && !strings.HasSuffix(ServiceFrontendurl.GetString(), "/") {
|
||||
ServiceFrontendurl.Set(ServiceFrontendurl.GetString() + "/")
|
||||
if ServicePublicURL.GetString() != "" && !strings.HasSuffix(ServicePublicURL.GetString(), "/") {
|
||||
ServicePublicURL.Set(ServicePublicURL.GetString() + "/")
|
||||
}
|
||||
|
||||
if MigrationTodoistRedirectURL.GetString() == "" {
|
||||
MigrationTodoistRedirectURL.Set(ServiceFrontendurl.GetString() + "migrate/todoist")
|
||||
MigrationTodoistRedirectURL.Set(ServicePublicURL.GetString() + "migrate/todoist")
|
||||
}
|
||||
|
||||
if MigrationTrelloRedirectURL.GetString() == "" {
|
||||
MigrationTrelloRedirectURL.Set(ServiceFrontendurl.GetString() + "migrate/trello")
|
||||
MigrationTrelloRedirectURL.Set(ServicePublicURL.GetString() + "migrate/trello")
|
||||
}
|
||||
|
||||
if MigrationMicrosoftTodoRedirectURL.GetString() == "" {
|
||||
MigrationMicrosoftTodoRedirectURL.Set(ServiceFrontendurl.GetString() + "migrate/microsoft-todo")
|
||||
MigrationMicrosoftTodoRedirectURL.Set(ServicePublicURL.GetString() + "migrate/microsoft-todo")
|
||||
}
|
||||
|
||||
if DefaultSettingsTimezone.GetString() == "" {
|
||||
|
|
|
@ -179,6 +179,10 @@ func initSqliteEngine() (engine *xorm.Engine, err error) {
|
|||
path = "./db.db"
|
||||
}
|
||||
|
||||
if path == "memory" {
|
||||
return xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -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
|
||||
-
|
||||
|
|
|
@ -55,3 +55,7 @@
|
|||
team_id: 13
|
||||
user_id: 10
|
||||
created: 2018-12-01 15:13:12
|
||||
-
|
||||
team_id: 14
|
||||
user_id: 10
|
||||
created: 2018-12-01 15:13:12
|
|
@ -28,4 +28,8 @@
|
|||
created_by_id: 7
|
||||
- id: 13
|
||||
name: testteam13
|
||||
created_by_id: 7
|
||||
created_by_id: 7
|
||||
- id: 14
|
||||
name: testteam14
|
||||
created_by_id: 7
|
||||
oidc_id: 14
|
|
@ -17,7 +17,9 @@
|
|||
package files
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
gofs "io/fs"
|
||||
)
|
||||
|
||||
// Dump dumps all saved files
|
||||
|
@ -31,8 +33,13 @@ func Dump() (allFiles map[int64]io.ReadCloser, err error) {
|
|||
|
||||
allFiles = make(map[int64]io.ReadCloser, len(files))
|
||||
for _, file := range files {
|
||||
if err := file.LoadFileByID(); err != nil {
|
||||
return nil, err
|
||||
err = file.LoadFileByID()
|
||||
if err != nil {
|
||||
var pathError *gofs.PathError
|
||||
if errors.As(err, &pathError) {
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
allFiles[file.ID] = file.File
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user