Huge improvements for docs (#58)

This commit is contained in:
konrad 2019-02-17 19:53:04 +00:00 committed by Gitea
parent 5e7c9b9eb9
commit 2d88fad5b1
43 changed files with 1653 additions and 178 deletions

View File

@ -440,4 +440,60 @@ steps:
when:
status:
- success
- failure
- failure
---
kind: pipeline
name: deploy-docs
workspace:
base: /srv/app
path: src/code.vikunja.io/api
clone:
depth: 50
trigger:
event:
- push
branch:
- master
steps:
- name: submodules
image: docker:git
commands:
- git submodule update --init
- git submodule update --recursive --remote
- name: build
image: monachus/hugo:v0.54.0
pull: true
commands:
- cd docs
- hugo
- mv public/docs/* public # Hugo seems to be not capable of setting a different theme for a home page, so we do this ugly hack to fix it.
- name: docker
image: plugins/docker
pull: true
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: vikunja/docs
context: docs/
dockerfile: docs/Dockerfile
- name: rancher
image: peloton/drone-rancher
settings:
url: http://server01.kolaente.de:8080/v1
access_key:
from_secret: RANCHER_ACCESS_KEY
secret_key:
from_secret: RANCHER_SECRET_KEY
service: vikunja-website/docs
docker_image: vikunja/docs
confirm: true

View File

@ -0,0 +1,12 @@
# Description
# Checklist
* [ ] I added or improved tests
* [ ] I pushed new or updated dependencies to the repo using `go mod vendor`
* [ ] I added or improved docs for my feature
* [ ] Swagger (including `make do-the-swag`)
* [ ] Error codes
* [ ] New config options

5
.gitignore vendored
View File

@ -3,6 +3,7 @@
.idea/httpRequests
config.yml
config.yaml
!docs/config.yml
*.db
Run
dist/
@ -13,4 +14,6 @@ bin/
secrets
*.deb
debian/
logs/
logs/
docs/public/
docs/resources/

3
.gitmodules vendored Normal file
View File

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

View File

@ -3,49 +3,43 @@
This is the place where I write down ideas to work on at some point.
Sorry for some of them being in German, I'll tranlate them at some point.
## Feature-Ideen
## Feature Ideas
* [x] Priorities
* [x] Repeating tasks
* [x] Tagesübersicht ("Was ist heute/diese Woche due?") -> Machen letztenendes die Clients, wir brauchen nur nen endpoint, der alle tasks auskotzt, der Client macht dann die Sortierung.
* [x] Get all tasks which are due between two given dates
* [x] Subtasks
## Anderes
* [x] Refactor!!!! Alle Funktionen raus, die nicht mehr grbaucht werden + Funktionen vereinfachen/zusammenführen.
Wenn ein Objekt 5x hin und hergereicht wird, und jedesmal nur geringfügig was dran geändert wird sollte das
doch auch in einer Funktion machbar sein.
* [x] ganz viel in eigene neue Dateien + Packages auslagern, am besten eine package pro model mit allen methoden etc.
* [x] Alle alten dinger die nicht mehr gebraucht werden, weg.
* [x] Die alten handlerfunktionen alle in eine datei packen und erstmal "lagern", erstmal brauchen wir die noch für swagger.
* [x] Drone aufsetzen
* [x] Tests schreiben
* [x] Namen finden
* [x] Alle Packages umziehen
* [x] Swagger UI aufsetzen
+ [x] CORS fixen
* [x] Überall echo.NewHTTPError statt c.JSON(Message{}) benutzen
* [x] Bessere Fehlermeldungen wenn das Model was ankommt falsch ist und nicht geparst werden kann
* [x] Fehlerhandling irgendwie besser machen. Zb mit "World error messages"? Sprich, die Methode ruft einfach auf obs die entsprechende Fehlermeldung gibt und zeigt sonst 500 an.
* [x] Viper für config einbauen und ini rauswerfen
* [x] Docs für installationsanleitung
* [x] Tests für Rechtekram
* [x] "Apiformat" Methoden, damit in der Ausgabe zb kein Passwort drin ist..., oder created/updated von Nutzern... oder ownerID nicht drin ist sondern nur das ownerobject
* [x] Rechte überprüfen:
* [x] Listen erstellen
* [x] Listen bearbeiten (nur eigene im Moment)
* [x] Listenpunkte hinzufügen
* [x] Listenpunkte bearbeiten
* [x] Der -1 namespace sollte auch seperat angesprochen werden können, gibt sonst probleme mit der app.
* [x] Refactor!!!! Delete everything not being used anymore, simplify.
* [x] Drone
* [x] Tests
* [x] Find a nme
* [x] Move packages to a better structure
* [x] Swagger UI
+ [x] Fix CORS
* [x] Use echo.NewHTTPError instead of c.JSON(Message{})
* [x] Better error messages when the model which is sent to the server is wrong
* [x] Better error handling to show useful error messages and status codes
* [x] Viper for config instead of ini
* [x] Docs for installing
* [x] Tests for rights managemnt
* [x] Rights checks:
* [x] Create lists
* [x] Edit lists
* [x] Add tasks
* [x] Edit tasks
* [x] The -1 namespace should also be accessible seperately
### Short Term
* [x] Cacher konfigurierbar
* [x] Wenn die ID bei irgendeiner GetByID... Methode < 1 ist soll ein error not exist geworfen werden
* [x] /users sollte die Rechte mit ausgeben
* [x] Nen endpoint um /teams/members /list/users etc die Rechte updazudaten ohne erst zu löschen und dann neu einzufügen
* [x] namespaces & listen updaten geht nicht, gibt nen 500er zurück
* [x] Logging für alle Fehler irgendwohin, da gibts bestimmt ne coole library für
* [x] Cacher configurable
* [x] Should throw an error when an id < 1
* [x] /users should also return the rights
* [x] Extra endpoint /teams/members /list/users to update rights without needing to remove and re-add them
* [x] namespaces & listen update does not work, returns 500
* [x] Logging for all errors somewhere
* [x] Ne extra funktion für list exists machen, damit die nicht immer über GetListByID gehen, um sql-abfragen zu sparen
* [x] Rausfinden warum xorm teilweise beim einfügen IDs mit einfügen will -> Das schlägt dann wegen duplicate fehl
* [x] Bei den Structs "AfterLoad" raus, das verbraucht bei Gruppenabfragen zu viele SQL-Abfragen -> Die sollen einfach die entsprechenden Read()-Methoden verwenden (Krassestes bsp. ist GET /namespaces mit so ca 50 Abfragen)
@ -83,30 +77,28 @@ Sorry for some of them being in German, I'll tranlate them at some point.
### Docs
* [ ] Readme
* [x] Readme
* [x] Auch noch nen "link" zum Featurecreep
* [ ] ToC
* [ ] Logo
* [ ] How to build -> Docs
* [ ] How to use -> Docs
* [ ] How to dev -> Docs
* [ ] License
* [ ] Contributing
* [x] ToC
* [x] Logo
* [x] How to build -> Docs
* [x] How to dev -> Docs
* [x] License
* [x] Contributing
* [x] Redocs
* [x] Swaggerdocs verbessern
* [x] Descriptions in structs
* [x] Maxlength specify etc. (see swaggo docs)
* [x] Rights
* [x] API
* [ ] Anleitung zum Makefile
* [ ] Struktur erklären
* [ ] How to build from source
* [ ] Dev instructions (tests, makefile, mod, structure, etc.)
* [ ] Deploy in die docs
* [ ] Docker
* [ ] Native (systemd + nginx/apache)
* [ ] Backups
* [ ] Docs aufsetzen
* [x] Anleitung zum Makefile
* [x] How to build from source
* [x] Struktur erklären
* [x] Deploy in die docs
* [x] Docker
* [x] Native (systemd + nginx/apache)
* [x] Backups
* [x] Docs aufsetzen
### Tasks
@ -127,6 +119,10 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [ ] Task-Templates innerhalb namespaces und Listen (-> Mehrere, die auswählbar sind)
* [ ] Ein Task muss von mehreren Assignees abgehakt werden bis er als done markiert wird
* [ ] Besseres Rechtesystem, damit man so fine-graded sachen machen kann wie "Der da darf aber nur Tasks hinzufügen, aber keine abhaken"
* [ ] Roles which enable or disable chaning certain fields of a task -> includes custm fields
* [ ] Custom fields: Templates at List > Namespace > Global level, overwriting each other
* [ ] Related tasks -> settable with a "kind" of relation like blocked, or just related or so
* [ ] Description should be longtext
### General features
@ -134,7 +130,7 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [x] Performance bei rechtchecks verbessern
* User & Teamright sollte sich für n rechte in einer Funktion testen lassen
* [ ] Endpoint um die Rechte mit Beschreibung und code zu kriegen
* [ ] "Smart Lists", Listen nach bestimmten Kriterien gefiltert -> nur UI?
* [ ] "Smart Lists", Listen nach bestimmten Kriterien gefiltert -> speichern und im pseudonamespace
* [ ] "Performance-Statistik" -> Wie viele Tasks man in bestimmten Zeiträumen so geschafft hat etc
* [ ] IMAP-Integration -> Man schickt eine email an Vikunja und es macht daraus dann nen task -> Achtung missbrauchsmöglichkeiten
* [ ] In und Out webhooks, mit Templates vom Payload
@ -143,19 +139,30 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [ ] Per list
* [ ] For the current user
* [ ] ~~Websockets~~ SSE https://github.com/kljensen/golang-html5-sse-example
* User authenticates (with jwt)
* When updating/creating/etc an event struct is sent to the broker
* The broker has a list of subscribed users
* It then checks who is allowed to the see the event it recieved and sends it
* User authenticates (with jwt)
* When updating/creating/etc an event struct is sent to the broker
* The broker has a list of subscribed users
* It then checks who is allowed to the see the event it recieved and sends it
* [ ] Being able to define filters for notifications or turn them silent completely -> Probably frontend only
* [ ] mgl. zum Emailmaskieren haben (in den Nutzereinstellungen, wenn man seine Email nicht an alle Welt rausposaunen will)
* [ ] Mgl. zum Accountlöschen haben (so richtig krass mit emailverifiezierung und dass alle Privaten Listen gelöscht werden und man alle geteilten entweder wem übertragen muss oder auf privat stellen)
* [ ] /info endpoint, in dem dann zb die limits und version etc steht
* [ ] Deprecate /namespaces/{id}/lists in favour of namespace.ReadOne() <-- should also return the lists
* [ ] Bindata for templates
* [ ] `GetUserByID` and the likes should return pointers
* [ ] Colors for lists and namespaces -> Up to the frontend to implement these
* [ ] Some kind of milestones for tasks
* [ ] Create tasks from a text/markdown file (probably frontend only)
* [ ] Label-view: Get a bunch of tasks by label
* [ ] Better caldav support (VTODO)
* [ ] Debian package should have a service file
* [ ] Downloads should be served via nginx (with theme?), minio should only be used for pushing artifacts.
### Refactor
* [x] ListTaskRights, sollte überall gleich funktionieren, gibt ja mittlerweile auch eine Methode um liste von nem Task aus zu kriegen oder so
* [x] Re-check all `{List|Namespace}{User|Team}` if really all parameters need to be exposed via json or are overwritten via param anyway.
* [ ] Things like list/task order should use queries and not url params
### Linters
@ -206,3 +213,25 @@ Sorry for some of them being in German, I'll tranlate them at some point.
* [ ] Task dependencies
* [ ] Time tracking (possible plugin)
* [ ] IFTTT
* [ ] More sharing features (all of these with the already existing permissions)
* [ ] Invite users per mail
* [ ] Share a link with/without password
* [ ] Comments on tasks
* [ ] @mention users in tasks or comments to get them notified
* [ ] Summary of tasks to do in a configurable interval (every day/week or so)
* [ ] Importer (maybe frontend only)
* [ ] Trello
* [ ] Wunderlist
* [ ] Zenkit
* [ ] Asana
* [ ] Microsoft Todo
* [ ] Nozbe
* [ ] Lanes
* [ ] Nirvana
* [ ] Good ol' Caldav (Tasks)
* [ ] More auth providers
* [ ] LDAP/AD
* [ ] Kerberos
* [ ] SAML (what?)
* [ ] smtp
* [ ] OpenID

View File

@ -69,9 +69,6 @@ test:
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) -cover -coverprofile cover.out $(PACKAGES)
go tool cover -html=cover.out -o cover.html
required-gofmt-version:
@go version | grep -q '\(1.7\|1.8\|1.9\|1.10\|1.11\)' || { echo "We require go version 1.7, 1.8, 1.9, 1.10 or 1.11 to format code" >&2 && exit 1; }
.PHONY: lint
lint:
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
@ -80,11 +77,11 @@ lint:
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
.PHONY: fmt
fmt: required-gofmt-version
fmt:
$(GOFMT) -w $(GOFILES)
.PHONY: fmt-check
fmt-check: required-gofmt-version
fmt-check:
# get all go files and run go fmt on them
@diff=$$($(GOFMT) -d $(GOFILES)); \
if [ -n "$$diff" ]; then \
@ -93,10 +90,6 @@ fmt-check: required-gofmt-version
exit 1; \
fi;
.PHONY: install
install: $(wildcard *.go)
go install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build
build: $(EXECUTABLE)
@ -181,11 +174,13 @@ do-the-swag:
@hash swag > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go install $(GOFLAGS) github.com/swaggo/swag/cmd/swag; \
fi
swag init -g pkg/routes/routes.go;
swag init -g pkg/routes/routes.go -s ./pkg/swagger;
# Fix the generated swagger file, currently a workaround until swaggo can properly use go mod
sed -i '/"definitions": {/a "code.vikunja.io.web.HTTPError": {"type": "object","properties": {"code": {"type": "integer"},"message": {"type": "string"}}},' docs/docs.go;
sed -i 's/code.vikunja.io\/web.HTTPError/code.vikunja.io.web.HTTPError/g' docs/docs.go;
sed -i 's/` + \\"`\\" + `/` + "`" + `/g' docs/docs.go; # Replace replacements
sed -i 's/package\ docs/package\ swagger/g' docs/docs.go;
sed -i 's/` + \\"`\\" + `/` + "`" + `/g' docs/docs.go;
mv ./docs/docs.go ./pkg/swagger/docs.go;
.PHONY: misspell-check
misspell-check:
@ -208,26 +203,12 @@ gocyclo-check:
fi
for S in $(GOFILES); do gocyclo -over 14 $$S || exit 1; done;
.PHONY: gosimple-check
gosimple-check:
@hash gosimple > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get honnef.co/go/tools/cmd/gosimple; \
fi
for S in $(PACKAGES); do gosimple $$S || exit 1; done;
.PHONY: static-check
static-check:
@hash gocyclo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get honnef.co/go/tools/cmd/staticcheck; \
go install $(GOFLAGS) honnef.co/go/tools/cmd/staticcheck; \
fi
staticcheck;
.PHONY: unused-check
unused-check:
@hash unused > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get honnef.co/go/tools/cmd/unused; \
fi
unused;
staticcheck $(PACKAGES);
.PHONY: gosec-check
gosec-check:
@ -242,4 +223,3 @@ goconst-check:
go get github.com/jgautheron/goconst/cmd/goconst; \
fi
for S in $(PACKAGES); do goconst $$S || exit 1; done;

View File

@ -1,6 +1,4 @@
# Vikunja API
> The Todo-app to organize your life.
<img src="https://vikunja.io/images/vikunja-logo.svg" alt="" style="display: block;width: 50%;margin: 0 auto;" width="50%"/>
[![Build Status](https://drone1.kolaente.de/api/badges/vikunja/api/status.svg)](https://drone1.kolaente.de/vikunja/api)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](LICENSE)
@ -10,6 +8,18 @@
[![Go Report Card](https://goreportcard.com/badge/git.kolaente.de/vikunja/api)](https://goreportcard.com/report/git.kolaente.de/vikunja/api)
[![cover.run](https://cover.run/go/code.vikunja.io/api.svg?style=flat&tag=golang-1.10)](https://cover.run/go?tag=golang-1.10&repo=code.vikunja.io%2Fapi)
# Vikunja API
> The Todo-app to organize your life.
# Table of contents
* [Features](#features)
* [Docs](#docs)
* [Roadmap](#roadmap)
* [Contributing](#contributing)
* [License](#license)
## Features
* Create TODO lists with tasks
@ -17,7 +27,14 @@
* Namespaces: A "group" which bundels multiple lists
* Share lists and namespaces with teams and users with granular permissions
Try it under [try.vikunja.io](https://try.vikunja.io)!
Try it on [try.vikunja.io](https://try.vikunja.io)!
## Docs
* [Build from source](docs/setup/build-from-source.md)
* [Development setup](docs/development-setup.md)
* [Makefile](docs/development/make.md)
* [Testing](docs/development/test.md)
### Roadmap
@ -50,26 +67,10 @@ See [Featurecreep.md](Featurecreep.md) for even more! (mostly ideas, for now)
* [ ] [Mobile apps](https://code.vikunja.io/app) (seperate repo) *In Progress*
* [ ] [Webapp](https://code.vikunja.io/frontend) (seperate repo) *In Progress*
## Development
## Contributing
We use go modules to vendor libraries for Vikunja, so you'll need at least go `1.11`.
Fork -> Push -> Pull-Request. Also see the [dev docs](docs/development-setup.md) for more infos.
To contribute to Vikunja, fork the project and work on the master branch.
## License
Some internal packages are referenced using their respective package URL. This can become problematic. To “trick” the Go tool into thinking this is a clone from the official repository, download the source code into `$GOPATH/code.vikunja.io/api`. Fork the Vikunja repository, it should then be possible to switch the source directory on the command line.
```bash
cd $GOPATH/src/code.vikunja.io/api
```
To be able to create pull requests, the forked repository should be added as a remote to the Vikunja sources, otherwise changes cant be pushed.
```bash
git remote rename origin upstream
git remote add origin git@git.kolaente.de:<USERNAME>/api.git
git fetch --all --prune
```
This should provide a working development environment for Vikunja. Take a look at the Makefile to get an overview about the available tasks. The most common tasks should be `make test` which will start our test environment and `make build` which will build a vikunja binary into the working directory. Writing test cases is not mandatory to contribute, but it is highly encouraged and helps developers sleep at night.
Thats it! You are ready to hack on Vikunja. Test changes, push them to the repository, and open a pull request.
This project is licensed under the GPLv3 License. See the [LICENSE](LICENSE) file for the full license text.

View File

@ -45,9 +45,9 @@ redis:
# Whether to enable redis or not
enabled: false
# The host of the redis server including its port.
redishost: 'localhost:6379'
host: 'localhost:6379'
# The password used to authenicate against the redis server
redispassword: ''
password: ''
# 0 means default database
db: 0

3
docs/Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM nginx
ADD public /usr/share/nginx/html/docs
ADD nginx.conf /etc/nginx/conf.d/default.conf

39
docs/config.yml Normal file
View File

@ -0,0 +1,39 @@
baseurl: https://vikunja.io/docs/
title: Vikunja
theme: vikunja
enableRobotsTXT: true
canonifyURLs: true
pygmentsUseClasses: true
permalinks:
post: /:year/:month/:title/
doc: /:slug/
page: /:slug/
default: /:slug/
params:
description: The to-do app to organize your life
author: The Vikunja Authors
website: https://vikunja.io
fanthomEnabled: false
fathomUrl: fathom.kolaente.de
fathomSiteID: RYKSD
menu:
page:
- name: Home
url: https://vikunja.io/en/
weight: 10
- name: Features
url: https://vikunja.io/en/features
weight: 20
- name: Download
url: https://vikunja.io/en/download
weight: 30
- name: Docs
url: https://vikunja.io/docs
weight: 40
- name: Code
url: https://code.vikunja.io/
weight: 50

View File

@ -0,0 +1,25 @@
---
date: "2019-02-12:00:00+02:00"
title: "Docs"
draft: false
url: "/docs"
type: "doc"
weight: 10
---
# Documentation
This is the documentation for Vikunja.
You can find available articles in the menu on the left.
## About
To learn more about the what, why and how, take a look at [the features page](https://vikunja.io/en/features).
## Start
A good starting point if you want to install and host Vikunja on your server are [the install documentation](installing)
and [available configuration options](config-options).
## Developing
If you want to start contributing to Vikunja, take a look at [the development docs](development).

View File

@ -0,0 +1,61 @@
---
date: "2019-02-12:00:00+02:00"
title: "Development"
toc: true
draft: false
type: "doc"
menu:
sidebar:
parent: "development"
name: "Development"
---
# Development
We use go modules to vendor libraries for Vikunja, so you'll need at least go `1.11` to use these.
If you don't intend to add new dependencies, go `1.9` and above should be fine.
To contribute to Vikunja, fork the project and work on the master branch.
A lot of developing tasks are automated using a Makefile, so make sure to [take a look at it]({{< ref "make.md">}}).
## Libraries
We keep all libraries used for Vikunja around in the `vendor/` folder to still be able to build the project even if
some maintainers take their libraries down like [it happened in the past](https://github.com/jteeuwen/go-bindata/issues/5).
## Tests
See [testing]({{< ref "test.md">}}).
#### Development using go modules
If you're able to use go modules, you can clone the project wherever you want to and work from there.
However, when building or running tests, please supply the `-mod=vendor` flag to go so it builds using the
dependencies from the `vendor/` folder.
#### Development-setup without go modules
Some internal packages are referenced using their respective package URL. This can become problematic.
To “trick” the Go tool into thinking this is a clone from the official repository, download the source code
into `$GOPATH/code.vikunja.io/api`. Fork the Vikunja repository, it should then be possible to switch the source directory on the command line.
{{< highlight bash >}}
cd $GOPATH/src/code.vikunja.io/api
{{< /highlight >}}
To be able to create pull requests, the forked repository should be added as a remote to the Vikunja sources, otherwise changes cant be pushed.
{{< highlight bash >}}
git remote rename origin upstream
git remote add origin git@git.kolaente.de:<USERNAME>/api.git
git fetch --all --prune
{{< /highlight >}}
This should provide a working development environment for Vikunja. Take a look at the Makefile to get an overview about
the available tasks. The most common tasks should be `make test` which will start our test environment and `make build`
which will build a vikunja binary into the working directory. Writing test cases is not mandatory to contribute, but it
is highly encouraged and helps developers sleep at night.
Thats it! You are ready to hack on Vikunja. Test changes, push them to the repository, and open a pull request.

View File

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

View File

@ -0,0 +1,136 @@
---
date: "2019-02-12:00:00+02:00"
title: "Project structure"
draft: false
type: "doc"
menu:
sidebar:
parent: "development"
---
# Project structure
In general, this api repo has the following structure:
* `docker`
* `docs`
* `pkg`
* `caldav`
* `config`
* `log`
* `mail`
* `metrics`
* `models`
* `red`
* `routes`
* `api/v1`
* `swagger`
* `utils`
* `REST-Tests`
* `templates`
* `vendor`
This document will explain what these mean and what you can find where.
## Root level
The root directory is where [the config file]({{< ref "../setup/config.md">}}), [Makefile]({{< ref "make.md">}}), license, drone config,
application entry point (`main.go`) and so on are located.
## docker
This directory holds additonal files needed to build and run the docker container, mainly service configuration to properly run Vikunja inside a docker
container.
## pkg
This is where most of the magic happens. Most packages with actual code are located in this folder.
### caldav
This folder holds a simple caldav implementation which is responsible for returning the caldav feature.
### config
This package configures the config. It sets default values and sets up viper and tells it where to look for config files,
how to interpret which env variables for config etc.
If you want to add a new config parameter, you should add default value in this package.
### log
Similar to `config`, this will set up the logging, based on differen logging backends.
This init is called in `main.go` after the config init is done.
### mail
This package handles all mail sending. To learn how to send a mail, see [sending emails]({{< ref "../practical-instructions/mail.md">}}).
### metrics
This package handles all metrics which are exposed to the prometheus endpoint.
To learn how it works and how to add new metrics, take a look at [how metrics work]({{< ref "../practical-instructions/metrics.md">}}).
### models
This is where most of the magic happens.
When adding new features or upgrading existing ones, that most likely happens here.
Because this package is pretty huge, there are several documents and how-to's about it:
* [Adding a feature]({{< ref "../practical-instructions/feature.md">}})
* [Making calls to the database]({{< ref "../practical-instructions/database.md">}})
### red (redis)
This package initializes a connection to a redis server.
This inizialization is automatically done at the startup of vikunja.
It also has a function (`GetRedis()`) which returns a redis client object you can then use in your package
to talk to redis.
It uses the [go-redis](https://github.com/go-redis/redis) library, please see their configuration on how to use it.
### routes
This package defines all routes which are available for vikunja clients to use.
To add a new route, see [adding a new route]({{< ref "../practical-instructions/feature.md">}}).
#### api/v1
This is where all http-handler functions for the api are stored.
Every handler function which does not use the standard web handler should live here.
### swagger
This is where the [generated]({{< ref "make.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live.
You usually don't need to touch this package.
### utils
A small package, containing some helper functions:
* `MakeRandomString`: Generates a random string of a given length.
* `Sha256`: Calculates a sha256 hash from a given string.
See their function definitions for instructions on how to use them.
## REST-Tests
Holds all kinds of test files to directly test the api from inside of [jetbrains ide's](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html).
These files are currently more an experiment, maybe we will drop them in the future to use something we could integrate in the testing process with drone.
Therefore, this has no claim to be complete yet even working, you're free to change whatever is needed to get it working for you.
## templates
Holds the email templates used to send plain text and html emails for new user registration and password changes.
## vendor
All libraries needed to build Vikunja.
We keep all libraries used for Vikunja around in the `vendor/` folder to still be able to build the project even if
some maintainers take their libraries down like [it happened in the past](https://github.com/jteeuwen/go-bindata/issues/5).
When adding a new dependency, make sure to run `go mod vendor` to put it inside this directory.

View File

@ -1,10 +1,20 @@
---
date: "2019-02-12:00:00+02:00"
title: "Testing"
draft: false
type: "doc"
menu:
sidebar:
parent: "development"
---
# Testing
You can run unit tests with our `Makefile` with
You can run unit tests with [our `Makefile`]({{< ref "make.md">}}) with
```bash
{{< highlight bash >}}
make test
```
{{< /highlight >}}
### Running tests with config

View File

@ -0,0 +1,38 @@
---
date: "2019-02-12:00:00+02:00"
title: "Database"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Database
Vikunja uses [xorm](http://xorm.io/) as an abstraction layer to handle the database connection.
Please refer to [their](http://xorm.io/docs/) documentation on how to exactly use it.
Inside the `models` package, a variable `x` is available which contains a pointer to an instance of `xorm.Engine`.
This is used whenever you make a call to the database to get or update data.
This xorm instance is set up and initialized every time vikunja is started.
### Adding new database tables
To add a new table to the database, add a an instance of your struct to the `tables` variable in the
init function in `pkg/models/models.go`. Xorm will sync them automatically.
You also need to add a pointer to the `tablesWithPointer` slice to enable caching for all instances of this struct.
To learn more about how to configure your struct to create "good" tables, refer to [the xorm documentaion](http://xorm.io/docs/).
### Adding data to test fixtures
Adding data for test fixtures is done in via `yaml` files insinde of `pkg/models/fixtures`.
The name of the yaml file should equal the table name in the database.
Adding values to it is done via array definition inside of the yaml file.
**Note**: Table and column names need to be in snake_case as that's what is used internally in the database
and for mapping values from the database to xorm so your structs can use it.

View File

@ -0,0 +1,72 @@
---
date: "2019-02-12:00:00+02:00"
title: "Custom Errors"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Custom Errors
All custom errors are defined in `pkg/models/errors.go`.
You should add new ones in this file.
Custom errors usually have fields for the http return code, a [vikunja-specific error code]({{< ref "../usage/errors.md">}})
and a human-readable error message about what went wrong.
An error consists of multiple functions and definitions:
{{< highlight golang >}}
// This struct holds any information about this specific error.
// In this case, it contains the user ID of a nonexistand user.
// This type should always be a struct, even if it has no values in it.
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
type ErrUserDoesNotExist struct {
UserID int64
}
// This function is mostly used in unit tests to check if a returned error is of that type.
// Every error type should have one of these.
// The name should always start with IsErr... followed by the name of the error.
// IsErrUserDoesNotExist checks if an error is a ErrUserDoesNotExist.
func IsErrUserDoesNotExist(err error) bool {
_, ok := err.(ErrUserDoesNotExist)
return ok
}
// This is the definition of the actual error type.
// Your error type is _required_ to implement this in order to be able to be returned as an "error" from functions.
func (err ErrUserDoesNotExist) Error() string {
return fmt.Sprintf("User does not exist [user id: %d]", err.UserID)
}
// This const holds the vikunja error code used to be able to identify this error without having to
// rely on an error string.
// This needs to be unique, so you should check whether the error code exists or not.
// The general convention for error codes is as follows:
// * Every "group" errors lives in a thousend something. For example all user issues are 1000-something, all
// list errors are 3000-something and so on.
// * New error codes should be the current max error code + 1. Don't take free numbers to prevent old errors
// which are depricated and removed from being "new ones". For example, if there are error codes 1001, 1002, 1004,
// a new error should be 1005 and not 1003.
// ErrCodeUserDoesNotExist holds the unique world-error code of this error
const ErrCodeUserDoesNotExist = 1005
// This is the implementation which returns an http error which is then passed to the client.
// Here you define the http status code with which one the error will be returned, the vikunja error code and
// a human-readable error message.
// HTTPError holds the http error description
func (err ErrUserDoesNotExist) HTTPError() web.HTTPError {
return web.HTTPError{
HTTPCode: http.StatusNotFound,
Code: ErrCodeUserDoesNotExist,
Message: "The user does not exist.",
}
}
{{< /highlight >}}

View File

@ -0,0 +1,33 @@
---
date: "2019-02-12:00:00+02:00"
title: "Add a new api endpoint"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Add a new api endpoint/feature
Most of the api endpoints/features of Vikunja are using the [common web handler](https://code.vikunja.io/web).
This is a library created by Vikunja in an effort to facilitate the creation of REST endpoints.
This works by abstracting the handling of CRUD-Requests, including rights check.
You can learn more about the web handler on [the project's repo](https://code.vikunja.io/web).
### Helper for pagination
Pagination limits can be calculated with a helper function, `getLimitFromPageIndex(pageIndex)`
(only available in the `models` package) from any page number.
It returns the `limit` (max-length) and `offset` parameters needed for SQL-Queries.
You can feed this function directly into xorm's `Limit`-Function like so:
{{< highlight golang >}}
lists := []List{}
err := x.Limit(getLimitFromPageIndex(pageIndex)).Find(&lists)
{{< /highlight >}}
// TODO: Add a full example from start to finish, like a tutorial on how to create a new endpoint?

View File

@ -0,0 +1,84 @@
---
date: "2019-02-12:00:00+02:00"
title: "Mailer"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Mailer
This document explains how to use the mailer to send emails and what to do to create a new kind of email to be sent.
## Sending emails
**Note:** You should use mail templates whenever possible (see below).
To send an email, use the function `mail.SendMail(options)`. The options are defined as follows:
{{< highlight golang >}}
type Opts struct {
To string // The email address of the recipent
Subject string // The subject of the mail
Message string // The plaintext message in the mail
HTMLMessage string // The html message
ContentType ContentType // The content type of the mail. Can be either mail.ContentTypePlain, mail.ContentTypeHTML, mail.ContentTypeMultipart. You should set this according to the kind of mail you want to send.
Boundary string
Headers []*header // Other headers to set in the mail.
}
{{< /highlight >}}
## Sending emails based on a template
For each mail with a template, there are two email templates: One for plaintext emails, one for html emails.
These are located in the `templates/mail` folder and follow the conventions of `template-name.{plain|hmtl}.tmpl`,
both the plaintext and html templates are in the same folder.
To send a mail based on a template, use the function `mail.SendMailWithTemplate(to, subject, tpl string, data map[string]interface{})`.
`to` and `subject` are pretty much self-explanatory, `tpl` is the name of the template, without `.html.tmpl` or `.plain.tmpl`.
`data` is a map you can pass additional data to your template.
#### Sending a mail with a template
A basic html email template would look like this:
{{< highlight go-html-template >}}
{{template "mail-header.tmpl" .}}
<p>
Hey there!<br/>
This is a minimal html email example.<br/>
{{.Something}}
</p>
{{template "mail-footer.tmpl"}}
{{< /highlight >}}
And the corresponding plaintext template:
{{< highlight go-text-template >}}
Hey there!
This is a minimal html email example.
{{.Something}}
{{< /highlight >}}
You would then call this like so:
{{< highlight golang >}}
data := make(map[string]interface{})
data["Something"] = "I am some computed value"
to := "test@example.com"
subject := "A simple test mail"
tpl := "demo" // Assuming you saved the templates as demo.plain.tmpl and demo.html.tmpl
mail.SendMailWithTemplate(to, subject, tpl, data)
{{< /highlight >}}
The function does not return an error. If an error occures when sending a mail, it is logged but not returned because sending the mail happens asinchrounly.
Notice the `mail-header.tmpl` and `mail-footer.tmpl` in the template. These populate some basic css, a box for your content and the vikunja logo.
All that's left for you is to put the content in, which then will appear in a beautifully-styled box.
Remeber, these are email templates. This is different from normal html/css, you cannot use whatever you want (because most of the clients are wayyy to outdated).

View File

@ -0,0 +1,46 @@
---
date: "2019-02-12:00:00+02:00"
title: "Metrics"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Metrics
Metrics work by exposing a `/metrics` endpoint which can then be accessed by prometheus.
To keep the load on the database minimal, metrics are stored and updated in redis.
The `metrics` package provides several functions to create and update metrics.
## New metrics
First, define a `const` with the metric key in redis. This is done in `pkg/metrics/metrics.go`.
To expose a new metric, you need to register it in the `init` function inside of the `metrics` package like so:
{{< highlight golang >}}
// Register total user count metric
promauto.NewGaugeFunc(prometheus.GaugeOpts{
Name: "vikunja_team_count", // The key of the metric. Must be unique.
Help: "The total number of teams on this instance", // A description about the metric itself.
}, func() float64 {
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.
If metrics are enabled, it checks if a redis connection is available and then sets the initial values.
A convenience function is available if the metric is based on a database struct.
Because metrics are stored in redis, you are responsible to increase or decrease these based on criteria you define.
To do this, use `metrics.UpdateCount(value, key)` where `value` is the amount you want to cange it (you can pass
negative values to decrease it) and `key` it the redis key used to define the metric.
# Using it
A Prometheus config with a Grafana template is available at [our git repo](https://git.kolaente.de/vikunja/monitoring).

View File

@ -0,0 +1,31 @@
---
date: "2019-02-12:00:00+02:00"
title: "Adding new config options"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Adding new config options
Vikunja uses [viper](https://github.com/spf13/viper) to handle configuration options.
It handles parsing all different configuration sources.
The configuration is done in sections. These are represented with a `.` in viper.
Take a look at `pkg/config/config.go` to understand how these are set.
To add a new config option, you should add a default value to `pkg/config/config.go`.
Default values should always enable the feature to work somehow, or turn it off completely if it always needs
additional configuration.
Make sure to add the new config option to [the config document]({{< ref "../setup/config.md">}}) and the default config file
(`config.yml.sample` at the root of the repository) to make sure it is well documented.
If you're using a computed value as a default, make sure to update the sample config file and debian
post-install scripts to reflect that.
To get a configured option, use `viper.Get("config.option")`.
Take a look at [viper's documentation](https://github.com/spf13/viper#getting-values-from-viper) to learn of the
different ways available to get config options.

View File

@ -0,0 +1,47 @@
---
date: "2019-02-12:00:00+02:00"
title: "Modifying swagger api docs"
draft: false
type: "doc"
menu:
sidebar:
parent: "practical instructions"
---
# Adding/editing swagger api docs
The api documentation is generated using [swaggo](https://github.com/swaggo/swag) from comments.
### Documenting structs
You should always comment every field which will be exposed as a json in the api.
These comments will show up in the documentation, it'll make it easier for developers using the api.
As an example, this is the definition of a list with all comments:
{{< highlight golang >}}
// List represents a list of tasks
type List struct {
// The unique, numeric id of this list.
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"list"`
// The title of the list. You'll see this in the namespace overview.
Title string `xorm:"varchar(250)" json:"title" valid:"required,runelength(3|250)" minLength:"3" maxLength:"250"`
// The description of the list.
Description string `xorm:"varchar(1000)" json:"description" valid:"runelength(0|1000)" maxLength:"1000"`
OwnerID int64 `xorm:"int(11) INDEX" json:"-"`
NamespaceID int64 `xorm:"int(11) INDEX" json:"-" param:"namespace"`
// The user who created this list.
Owner User `xorm:"-" json:"owner" valid:"-"`
// An array of tasks which belong to the list.
Tasks []*ListTask `xorm:"-" json:"tasks"`
// A unix timestamp when this list was created. You cannot change this value.
Created int64 `xorm:"created" json:"created"`
// A unix timestamp when this list was last updated. You cannot change this value.
Updated int64 `xorm:"updated" json:"updated"`
web.CRUDable `xorm:"-" json:"-"`
web.Rights `xorm:"-" json:"-"`
}
{{< /highlight >}}

View File

@ -0,0 +1,34 @@
---
date: "2019-02-12:00:00+02:00"
title: "What to backup"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# What to backup
Vikunja does not store any data outside of the database.
So, all you need to backup are the contents of that database and maybe the config file.
## MySQL
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 >}}
## SQLite
To backup sqllite databases, it is enough to copy the database elsewhere.

View File

@ -0,0 +1,25 @@
---
date: "2019-02-12:00:00+02:00"
title: "Build from sources"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# Build Vikunja from source
Vikunja being a go application, has no other dependencies than go itself.
All libraries are bundeled inside the repo in the `vendor/` folder, so all it boils down to are these steps:
1. Make sure [Go](https://golang.org/doc/install) is properly installed on your system. You'll need at least Go `1.9`.
2. Make sure [Make](https://www.gnu.org/software/make/) is properly installed on your system.
3. Clone the repo with `git clone https://code.vikunja.io/api`
3. Run `make build` in the source of this repo. This will build a binary in the root of the repo which will be able to run on your system.
# Build for different architectures
To build for other platforms and architectures than the one you're currently on, simply run `make release` or `make release-{linux|windows|darwin}`.
More options are available, please refer to the [makefile docs]({{< ref "../development/make.md">}}) for more details.

View File

@ -1,3 +1,13 @@
---
date: "2019-02-12:00:00+02:00"
title: "Config options"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# Configuration options
You can either use a `config.yml` file in the root directory of vikunja or set all config option with
@ -6,16 +16,16 @@ environment variables. If you have both, the value set in the config file is use
Variables are nested in the `config.yml`, these nested variables become `VIKUNJA_FIRST_CHILD` when configuring via
environment variables. So setting
```bash
{{< highlight bash >}}
export VIKUNJA_FIRST_CHILD=true
```
{{< /highlight >}}
is the same as defining it in a `config.yml` like so:
```yaml
{{< highlight yaml >}}
first:
child: true
```
{{< /highlight >}}
## Config file locations
@ -30,7 +40,7 @@ Vikunja will search on various places for a config file:
This is the same as the `config.yml.sample` file you'll find in the root of vikunja.
```yaml
{{< highlight yaml >}}
service:
# This token is used to verify issued JWT tokens.
# Default is a random token which will be generated at each startup of vikunja.
@ -78,9 +88,9 @@ redis:
# Whether to enable redis or not
enabled: false
# The host of the redis server including its port.
redishost: 'localhost:6379'
host: 'localhost:6379'
# The password used to authenicate against the redis server
redispassword: ''
password: ''
# 0 means default database
db: 0
@ -119,4 +129,4 @@ log:
http: "stdout"
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
echo: "off"
```
{{< /highlight >}}

View File

@ -0,0 +1,110 @@
---
date: "2019-02-12:00:00+02:00"
title: "Full docker example"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# Full docker example
This docker compose configuration will run Vikunja with backend and frontend with a mariadb as database.
It uses an nginx container to proxy backend and frontend into a single port.
You'll need to save this nginx configuration on your host under `nginx.conf`
(or elsewhere, but then you'd need to adjust the proxy mount at the bottom of the compose file):
{{< highlight conf >}}
server {
listen 80;
location / {
proxy_pass http://frontend:80;
}
location /api/ {
proxy_pass http://api:3456;
}
}
{{< /highlight >}}
### Without redis
{{< highlight yaml >}}
version: '3'
services:
db:
image: mariadb:10
environment:
MYSQL_ROOT_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
api:
image: vikunja/api
environment:
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: supersecret
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_DATABASE: vikunja
depends_on:
- db
frontend:
image: vikunja/frontend
proxy:
image: nginx
ports:
- 80:80
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- frontend
{{< /highlight >}}
### With redis
{{< highlight yaml >}}
version: '3'
services:
db:
image: mariadb:10
environment:
MYSQL_ROOT_PASSWORD: supersecret
MYSQL_DATABASE: vikunja
volumes:
- ./db:/var/lib/mysql
redis:
image: redis
api:
image: vikunja/api
environment:
VIKUNJA_DATABASE_HOST: db
VIKUNJA_DATABASE_PASSWORD: supersecret
VIKUNJA_DATABASE_TYPE: mysql
VIKUNJA_DATABASE_USER: root
VIKUNJA_DATABASE_DATABASE: vikunja
VIKUNJA_REDIS_ENABLED: 1
VIKUNJA_REDIS_HOST: 'redis:6379'
VIKUNJA_CACHE_ENABLED: 1
VIKUNJA_CACHE_TYPE: redis
depends_on:
- db
- redis
frontend:
image: vikunja/frontend
proxy:
image: nginx
ports:
- 80:80
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- frontend
{{< /highlight >}}

View File

@ -0,0 +1,160 @@
---
date: "2019-02-12:00:00+02:00"
title: "Install Backend"
draft: false
type: "doc"
menu:
sidebar:
parent: "setup"
---
# Backend
## 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 `master` 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
Take the following `service` file 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=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 tw