Compare commits
682 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b8c9c698d | ||
|
|
80367d60d4 | ||
|
|
688ca65edf | ||
|
|
dcefc18b98 | ||
|
|
214f2f008e | ||
|
|
004e432e7c | ||
|
|
47486af06d | ||
|
|
8b9b1984fc | ||
|
|
6dd7bcb0fe | ||
|
|
b94950d7c2 | ||
|
|
618353bf95 | ||
|
|
1555081939 | ||
|
|
7fe9e6d3f6 | ||
|
|
cbba0695a8 | ||
|
|
921526e086 | ||
|
|
1b21339bf8 | ||
|
|
e9d6daa1a3 | ||
|
|
8b001313f4 | ||
|
|
e0c8eca669 | ||
|
|
c629130b3b | ||
|
|
9252225d7f | ||
|
|
38b5c7fb6c | ||
|
|
e26df26f78 | ||
|
|
158e07e581 | ||
|
|
699d3d6060 | ||
|
|
d56a611be7 | ||
|
|
bf5d8af3f6 | ||
|
|
0769098357 | ||
|
|
71094d981f | ||
|
|
32d97f1451 | ||
|
|
bf9d1c634a | ||
|
|
ebd96d7766 | ||
|
|
089d156259 | ||
|
|
dd589022e4 | ||
|
|
b850f65295 | ||
|
|
b3d09cd2d4 | ||
|
|
64d125afd9 | ||
|
|
dd5d64da3e | ||
|
|
1776eb56fe | ||
|
|
f8c135f22e | ||
|
|
0fb2edf051 | ||
|
|
a6fdf114d1 | ||
|
|
ca1b33d24d | ||
|
|
c98b9bbee6 | ||
|
|
19a0a85c73 | ||
|
|
77122b8f1b | ||
|
|
436af467d6 | ||
|
|
04130e4ea3 | ||
|
|
7e6e44e787 | ||
|
|
9e7ca8df51 | ||
|
|
6bdddd462a | ||
|
|
e5559137dd | ||
|
|
ecf09e17a8 | ||
|
|
14d706c91e | ||
|
|
5317a89623 | ||
|
|
e9b1786188 | ||
|
|
313289d28d | ||
|
|
05e237560d | ||
|
|
e4dd314079 | ||
|
|
118c7f25b5 | ||
|
|
b64a80da0b | ||
|
|
bb26c9d97c | ||
|
|
edf3854632 | ||
|
|
19a66450ec | ||
|
|
3a839dfb86 | ||
|
|
78b261e440 | ||
|
|
471d1b0ec5 | ||
|
|
58dfbe13ed | ||
|
|
1a4eef1056 | ||
|
|
8da7db3e26 | ||
|
|
d359130bcf | ||
|
|
05099e1784 | ||
|
|
79970ebb4a | ||
|
|
27b4086351 | ||
|
|
ae7eafd6ad | ||
|
|
21b5aee054 | ||
|
|
2b34a8d4e6 | ||
|
|
b4771c1bce | ||
|
|
c83858bf7e | ||
|
|
08b8964b3d | ||
|
|
d88551e99d | ||
|
|
ebd71d1f04 | ||
|
|
a61ab0c5cf | ||
|
|
fa718e2576 | ||
|
|
c517a87b85 | ||
|
|
28fd0e91ee | ||
|
|
14c27600d8 | ||
|
|
dedce20780 | ||
|
|
a58b932743 | ||
|
|
301bebf8d3 | ||
|
|
d192c36c39 | ||
|
|
bdfb804bb2 | ||
|
|
16dbcfda7e | ||
|
|
0169ecc37e | ||
|
|
4a70c81b33 | ||
|
|
5e84ce639f | ||
|
|
6c45388da9 | ||
|
|
bd8c1c3bb7 | ||
|
|
28b8cabea5 | ||
|
|
7c91803056 | ||
|
|
b375e1d043 | ||
|
|
d718d247c8 | ||
|
|
6a82d4e2af | ||
|
|
11722bf029 | ||
|
|
dfb7730b63 | ||
|
|
c9117dd037 | ||
|
|
e4539ef232 | ||
|
|
0ba6ae7a18 | ||
|
|
25ecc4a510 | ||
|
|
259c2195dc | ||
|
|
7bdc9dd428 | ||
|
|
5f8872f8cc | ||
|
|
5794ede6f6 | ||
|
|
41cf73a473 | ||
|
|
f8d84139fa | ||
|
|
d3964ff4bd | ||
|
|
9acba7d3f0 | ||
|
|
2d567bfe0f | ||
|
|
7207aa60fb | ||
|
|
2b9af951bf | ||
|
|
c84efcbbcc | ||
|
|
092aae3260 | ||
|
|
222582fb0c | ||
|
|
a99367bc5f | ||
|
|
c47d5c7228 | ||
|
|
3d709e3bb7 | ||
|
|
3a9360a57b | ||
|
|
dd3c4cd032 | ||
|
|
2a4a622518 | ||
|
|
4db06ba9a1 | ||
|
|
96f366f5e7 | ||
|
|
78791f31a4 | ||
|
|
ec3fa9300b | ||
|
|
9fa7e30a0a | ||
|
|
158d98c2bd | ||
|
|
ae12871bd7 | ||
|
|
7141050f8b | ||
|
|
5d900fd40d | ||
|
|
f57b7e989f | ||
|
|
556ccd68c1 | ||
|
|
1181039249 | ||
|
|
6da137cd4a | ||
|
|
9750a23dbe | ||
|
|
1c93aab7e0 | ||
|
|
53c4637fb6 | ||
|
|
3b2289c8fa | ||
|
|
b55b21373c | ||
|
|
53cfa99912 | ||
|
|
f30e720c7b | ||
|
|
57c84f3adc | ||
|
|
2820d92ab3 | ||
|
|
780a654f65 | ||
|
|
08205008e7 | ||
|
|
e17cac854a | ||
|
|
974d028e51 | ||
|
|
be11362533 | ||
|
|
912abb3a10 | ||
|
|
0add1dce01 | ||
|
|
922c41236c | ||
|
|
c8605d6b3d | ||
|
|
caee123f9d | ||
|
|
7b31301f09 | ||
|
|
f2025c2658 | ||
|
|
f324e3fb28 | ||
|
|
894d35e4fe | ||
|
|
1d9cd82d68 | ||
|
|
2bc26fe4fa | ||
|
|
4168b9ac26 | ||
|
|
8b030135de | ||
|
|
150c3f032c | ||
|
|
bfc4dd05ed | ||
|
|
cd812b4232 | ||
|
|
e4f150bbe3 | ||
|
|
db0126968a | ||
|
|
c12bac0c96 | ||
|
|
fba333866d | ||
|
|
d02d413c5e | ||
|
|
c51662c270 | ||
|
|
021e3e307b | ||
|
|
b4cdb75d15 | ||
|
|
ad6ef03c0c | ||
|
|
5930d5aabf | ||
|
|
3c5925c827 | ||
|
|
b7c8c1f533 | ||
|
|
bf41b2ed9f | ||
|
|
525a547500 | ||
|
|
ca4d5000ed | ||
|
|
24c1098736 | ||
|
|
47d7e713af | ||
|
|
3ba26ce67e | ||
|
|
ab1d58959f | ||
|
|
f17e378a93 | ||
|
|
f5e44d9eb3 | ||
|
|
d9d00ba60c | ||
|
|
a15b1bebba | ||
|
|
980be0e4e0 | ||
|
|
a0ffe89056 | ||
|
|
bd11c9650e | ||
|
|
32a5dff78d | ||
|
|
8ef2da8f5f | ||
|
|
60e802d77f | ||
|
|
966acf51d8 | ||
|
|
b5b84e6bbd | ||
|
|
f72aa4e52f | ||
|
|
d9f1d456d3 | ||
|
|
67f4c9f941 | ||
|
|
d63666cece | ||
|
|
d8a6acda96 | ||
|
|
03ef48a0ae | ||
|
|
8f35b9d579 | ||
|
|
ebfa982c72 | ||
|
|
2fa4fcc202 | ||
|
|
ad67154e26 | ||
|
|
250c45d1b9 | ||
|
|
dc036d44db | ||
|
|
9c4c265767 | ||
|
|
a525787ab7 | ||
|
|
aae1bc3cab | ||
|
|
42ddee8d6f | ||
|
|
54b18b3c59 | ||
|
|
5a04f1ecf4 | ||
|
|
a0fb8bd32d | ||
|
|
c685250c96 | ||
|
|
9ea1104764 | ||
|
|
2ef2c7523d | ||
|
|
e5e30d0915 | ||
|
|
c37b776f7a | ||
|
|
e9bc3246ce | ||
|
|
e89e6d47d4 | ||
|
|
292c815000 | ||
|
|
f0b1de5ec6 | ||
|
|
4204af255c | ||
|
|
b0948a37d4 | ||
|
|
cc47d11792 | ||
|
|
b6b82e6f54 | ||
|
|
16ed5fd982 | ||
|
|
24aa68f090 | ||
|
|
98ef052967 | ||
|
|
8a01b20e1e | ||
|
|
5a86f44fc3 | ||
|
|
fe43173b6c | ||
|
|
587ce92dc9 | ||
|
|
8758fb6ac5 | ||
|
|
92e5e2db6a | ||
|
|
851f0d6c08 | ||
|
|
333444b4e1 | ||
|
|
28a5c1f7b2 | ||
|
|
4533ac6b28 | ||
|
|
91a3b7aba2 | ||
|
|
4137d4aed2 | ||
|
|
cb095d70df | ||
|
|
e43e601912 | ||
|
|
55cd40d175 | ||
|
|
55cd74efca | ||
|
|
a9d0079bf3 | ||
|
|
8ac158cdb4 | ||
|
|
def2362682 | ||
|
|
18f6e31b54 | ||
|
|
7e1d0a81bf | ||
|
|
f30e405229 | ||
|
|
563fe16bd4 | ||
|
|
a30a358b73 | ||
|
|
497499e221 | ||
|
|
1fdc51078e | ||
|
|
60f343a926 | ||
|
|
fd3ccd6130 | ||
|
|
a97ce76f1f | ||
|
|
83cf6e191c | ||
|
|
f0ac6e89be | ||
|
|
23950c0602 | ||
|
|
19a05a7c2c | ||
|
|
299e856736 | ||
|
|
6d95dc16c2 | ||
|
|
fa28ddc2f7 | ||
|
|
56dbb564ea | ||
|
|
711124f5c0 | ||
|
|
5606f987fa | ||
|
|
f0cb3b5e36 | ||
|
|
231dc3913f | ||
|
|
2b1fc441e6 | ||
|
|
e433289832 | ||
|
|
86bdd1e386 | ||
|
|
4f1c0d99ea | ||
|
|
f0792cfb0b | ||
|
|
dece744685 | ||
|
|
68ab6d2e75 | ||
|
|
c69f0bf3cb | ||
|
|
59037e4eee | ||
|
|
1bd8348be7 | ||
|
|
ecdecdd94e | ||
|
|
28ec44c91f | ||
|
|
895d9613b5 | ||
|
|
87d0c9088d | ||
|
|
24904585a2 | ||
|
|
1dca8e96a7 | ||
|
|
e1ab2095fa | ||
|
|
12e18de8ad | ||
|
|
025b00247d | ||
|
|
5832991c4d | ||
|
|
81990f3f80 | ||
|
|
368cbdf979 | ||
|
|
b8d7c97eb7 | ||
|
|
fb8ac92abf | ||
|
|
0bfb3a4709 | ||
|
|
9559a68416 | ||
|
|
d28f005552 | ||
|
|
713560702b | ||
|
|
0ba121fdfb | ||
|
|
0e2449482f | ||
|
|
8d1a3f4fd7 | ||
|
|
1e0f2dde6a | ||
|
|
7abac07837 | ||
|
|
15d718fb1a | ||
|
|
4ca3f714ea | ||
|
|
6d1b123a25 | ||
|
|
13ebb98644 | ||
|
|
dda558fe1c | ||
|
|
76f19db6e7 | ||
|
|
f776b799b8 | ||
|
|
746a443660 | ||
|
|
026d3dc80d | ||
|
|
fd7dd47d5e | ||
|
|
1e5a1b4892 | ||
|
|
c7f6748761 | ||
|
|
7efa0069cb | ||
|
|
424cf80e5c | ||
|
|
1448d9de98 | ||
|
|
cbf01e118a | ||
|
|
191942fcd1 | ||
|
|
d5c8f2541d | ||
|
|
6e9ad3d808 | ||
|
|
36fdff5b8b | ||
|
|
8f4139f44a | ||
|
|
e7706a20dd | ||
|
|
6d1018e647 | ||
|
|
11246b948f | ||
|
|
7b619bd084 | ||
|
|
35f05cbbec | ||
|
|
0a1887d6e7 | ||
|
|
c6d22bb1f8 | ||
|
|
6132863f4a | ||
|
|
26ffd2fd22 | ||
|
|
51c74de1de | ||
|
|
5126330a10 | ||
|
|
4472020ee9 | ||
|
|
7de26a462f | ||
|
|
f2f17b11e8 | ||
|
|
cdd068cdb6 | ||
|
|
584e3af237 | ||
|
|
7ddfa76a84 | ||
|
|
1e03c39141 | ||
|
|
88bf8f3df0 | ||
|
|
c551706b52 | ||
|
|
1f039c4cda | ||
|
|
5901cf64b4 | ||
|
|
f87bb6d8ef | ||
|
|
021a715aff | ||
|
|
e95a6eeb11 | ||
|
|
f46e9cb64e | ||
|
|
ce5be947b4 | ||
|
|
7e42724439 | ||
|
|
caf91d1904 | ||
|
|
014ec2be69 | ||
|
|
5d96c62a47 | ||
|
|
1c3b35fa6f | ||
|
|
d84a160054 | ||
|
|
db2d868eed | ||
|
|
1f99238019 | ||
|
|
f603b41d99 | ||
|
|
2abb858859 | ||
|
|
a464d1760c | ||
|
|
fc65052ba0 | ||
|
|
b2b1546a8f | ||
|
|
7e9446ea07 | ||
|
|
8c33e24e92 | ||
|
|
0654ead831 | ||
|
|
2b80025cc0 | ||
|
|
3081338a37 | ||
|
|
9e39399689 | ||
|
|
1d2cdf0fb8 | ||
|
|
a0c5e4e985 | ||
|
|
402cef3c15 | ||
|
|
b9f0ba84ab | ||
|
|
7cdc997191 | ||
|
|
720df3cbed | ||
|
|
1a47d7d80d | ||
|
|
62e550bf35 | ||
|
|
10ab8ef4d9 | ||
|
|
d483fe8beb | ||
|
|
1555f04bd2 | ||
|
|
d8399e374c | ||
|
|
e890001ee1 | ||
|
|
38653b49c7 | ||
|
|
7e4deab8f7 | ||
|
|
d96831fe3a | ||
|
|
4c55b4455c | ||
|
|
b6308f783b | ||
|
|
843c1e5193 | ||
|
|
6045f4a426 | ||
|
|
7be14239a3 | ||
|
|
27ba31366c | ||
|
|
9dc419e854 | ||
|
|
37efd5cb49 | ||
|
|
dcec9511dc | ||
|
|
c203d73b33 | ||
|
|
9be5ab248c | ||
|
|
ed4c17892e | ||
|
|
886110a20a | ||
|
|
8948a5f219 | ||
|
|
89f385a53d | ||
|
|
82f11c4ec2 | ||
|
|
eb279fdba3 | ||
|
|
48e422b379 | ||
|
|
854fde1e4c | ||
|
|
b653278e42 | ||
|
|
35e4ab198f | ||
|
|
b81cd6128a | ||
|
|
2169464983 | ||
|
|
e2f481a6e5 | ||
|
|
af1d1e1921 | ||
|
|
8fe33fd616 | ||
|
|
1272255975 | ||
|
|
1e2ec17343 | ||
|
|
2f267f3f68 | ||
|
|
694fc62495 | ||
|
|
8d5a2685c4 | ||
|
|
71ef86e0df | ||
|
|
6140920cb8 | ||
|
|
fdd1624121 | ||
|
|
158ad631e1 | ||
|
|
c126d2f55c | ||
|
|
1d98e4cabe | ||
|
|
43676f045c | ||
|
|
ba7db545fe | ||
|
|
021ee48ec3 | ||
|
|
a324921dce | ||
|
|
8d57923a7d | ||
|
|
88ea66798b | ||
|
|
66cdd79666 | ||
|
|
be14634e1e | ||
|
|
8fbe721453 | ||
|
|
5d3e169883 | ||
|
|
208eb4e68c | ||
|
|
8d76280811 | ||
|
|
10fbbe1db6 | ||
|
|
4327a559e5 | ||
|
|
2e599e792e | ||
|
|
48826a6ed7 | ||
|
|
15a0963bd1 | ||
|
|
9a7d470ce2 | ||
|
|
ef0e7c9769 | ||
|
|
12eaddc8ee | ||
|
|
b63928850a | ||
|
|
4005cd2f32 | ||
|
|
5cc89fd474 | ||
|
|
e2d9de191d | ||
|
|
c3ea45d900 | ||
|
|
50ca8bd28e | ||
|
|
1f1a079fd3 | ||
|
|
f1d21ea52b | ||
|
|
fc3c5f2187 | ||
|
|
5d3b6573ca | ||
|
|
9930f98f8e | ||
|
|
16825ba7c6 | ||
|
|
4e8c2a7bf6 | ||
|
|
7f5a6e338c | ||
|
|
2763df20c9 | ||
|
|
9bc09ebbe5 | ||
|
|
6746984c97 | ||
|
|
7acf318b28 | ||
|
|
f638fae4fd | ||
|
|
bd930bf654 | ||
|
|
2cdb0eed3e | ||
|
|
85d08e5697 | ||
|
|
4c4eb76fed | ||
|
|
50d1f29125 | ||
|
|
3fffcbd986 | ||
|
|
24ce940885 | ||
|
|
802a13cffd | ||
|
|
6b348fad04 | ||
|
|
951d92b2f0 | ||
|
|
24f06b2da5 | ||
|
|
a7bbaf7c5f | ||
|
|
3356a691f7 | ||
|
|
7d46c88285 | ||
|
|
7107d030fc | ||
|
|
de24fcc2f8 | ||
|
|
3d7fd9ca20 | ||
|
|
2b160b73c3 | ||
|
|
24704bd4ed | ||
|
|
733d0be753 | ||
|
|
e40ab8652d | ||
|
|
b854112704 | ||
|
|
e2820e6806 | ||
|
|
a6e70f1b29 | ||
|
|
44223bde77 | ||
|
|
d404716342 | ||
|
|
7e38937efc | ||
|
|
ed671baf8f | ||
|
|
d4ff1eca80 | ||
|
|
6bab8fb769 | ||
|
|
07bdb08f9c | ||
|
|
1117b6eb47 | ||
|
|
10f80c190b | ||
|
|
c8858f6cfb | ||
|
|
dd1749f11f | ||
|
|
99f83542f6 | ||
|
|
5dc1fd0f86 | ||
|
|
b1701ca769 | ||
|
|
5cfaa0f322 | ||
|
|
7b332808f9 | ||
|
|
228c47d79e | ||
|
|
27339a7cf0 | ||
|
|
3adfb81d5e | ||
|
|
3872d1d8a7 | ||
|
|
46efcb1005 | ||
|
|
0ed6c6cc0d | ||
|
|
93dbaea303 | ||
|
|
55e33c1694 | ||
|
|
2d9d27f63c | ||
|
|
87873e53c5 | ||
|
|
19faee0102 | ||
|
|
ac5446e4f5 | ||
|
|
a799527a3c | ||
|
|
2b28ab12f1 | ||
|
|
be5a17e993 | ||
|
|
e21471a193 | ||
|
|
81f76f09ce | ||
|
|
c27e8fe2f1 | ||
|
|
8d78e473f5 | ||
|
|
5525ee0328 | ||
|
|
47352d3ed4 | ||
|
|
11e7c071ce | ||
|
|
1dc14d5ddf | ||
|
|
c42e22f352 | ||
|
|
72e64f7203 | ||
|
|
25999d9b69 | ||
|
|
eb4d38b5b8 | ||
|
|
65f428fe78 | ||
|
|
2cc968ec61 | ||
|
|
073aa9940f | ||
|
|
5506dd1ceb | ||
|
|
e47313b17e | ||
|
|
7cc172cc0c | ||
|
|
2bcd6e9cb6 | ||
|
|
afd55d8cf8 | ||
|
|
caed219d39 | ||
|
|
1b84292332 | ||
|
|
15ef6deabc | ||
|
|
a06a5fc4f4 | ||
|
|
2d88fad5b1 | ||
|
|
5e7c9b9eb9 | ||
|
|
9e635ea54e | ||
|
|
d0fa9ddaec | ||
|
|
add491d1e0 | ||
|
|
e735378caf | ||
|
|
f95aa431b8 | ||
|
|
01e7540530 | ||
|
|
75431e1ca5 | ||
|
|
f6c7c764d1 | ||
|
|
eedc19a49e | ||
|
|
c07e2b6cd4 | ||
|
|
08cbd18bc5 | ||
|
|
8362799a93 | ||
|
|
3edc728094 | ||
|
|
d3975193fe | ||
|
|
6b00ccc942 | ||
|
|
7e8c2bb53b | ||
|
|
20c4b249a4 | ||
|
|
0db5292f9f | ||
|
|
08e84f4e8d | ||
|
|
a791fe0fa7 | ||
|
|
19218b28a2 | ||
|
|
cf0b0a2853 | ||
|
|
17fefae8bb | ||
|
|
3d05d0aa45 | ||
|
|
cbcd12b5e5 | ||
|
|
8fdb8b7840 | ||
|
|
ed44478b20 | ||
|
|
0a092214c8 | ||
|
|
3ebf0da1b9 | ||
|
|
07bc4f3f01 | ||
|
|
cb58f55923 | ||
|
|
7cc078aaec | ||
|
|
d20a899f92 | ||
|
|
3cfe8c238a | ||
|
|
22495a66e0 | ||
|
|
d5e68ee037 | ||
|
|
318920fe29 | ||
|
|
364a172876 | ||
|
|
31c39388f1 | ||
|
|
056e37331e | ||
|
|
09787fb29b | ||
|
|
607dbd6ae8 | ||
|
|
3183d10dbe | ||
|
|
6b40df50d3 | ||
|
|
d39007baa0 | ||
|
|
37345e6bd3 | ||
|
|
018dd8164c | ||
|
|
3814b8a504 | ||
|
|
b050132f4f | ||
|
|
36eb580c8a | ||
|
|
067bb695b1 | ||
|
|
fd80094eca | ||
|
|
ce0bff8d9c | ||
|
|
5a93475be9 | ||
|
|
784b890f70 | ||
|
|
7322bfafb3 | ||
|
|
0edc5fd315 | ||
|
|
cbc5995ad3 | ||
|
|
3e4f7fb2f4 | ||
|
|
79b463d0af | ||
|
|
97febdb397 | ||
|
|
67e94f95b0 | ||
|
|
34af627b66 | ||
|
|
ec347a71bc | ||
|
|
777d63dff1 | ||
|
|
424dfe19af | ||
|
|
7b439cc568 | ||
|
|
553055f073 | ||
|
|
70f7f630a3 | ||
|
|
82340cb70b | ||
|
|
94fd2d758a | ||
|
|
e047673c6b | ||
|
|
ee398b5272 | ||
|
|
3e8fd1e619 | ||
|
|
0c1d786ade | ||
|
|
f1fca3346b | ||
|
|
07d6e1219b | ||
|
|
8e5ff72c4a | ||
|
|
fc17232819 | ||
|
|
8aed78b296 | ||
|
|
cc6b35e314 | ||
|
|
f2f881f505 | ||
|
|
6c5885747b | ||
|
|
007d8ec375 | ||
|
|
1b24cdf164 | ||
|
|
ce2cae9430 | ||
|
|
d9304f6996 | ||
|
|
d5e4195ee7 | ||
|
|
136adb69c8 | ||
|
|
06fc9f7886 | ||
|
|
3f44e3b83e | ||
|
|
b73786f7fe | ||
|
|
e5c1588576 | ||
|
|
d51ed8defd | ||
|
|
fded3c23d0 | ||
|
|
af0ce5bb96 | ||
|
|
0c112a0ca3 | ||
|
|
196b6728b8 | ||
|
|
5bc23458de | ||
|
|
0c544fe355 | ||
|
|
373bbd2202 | ||
|
|
d3de658882 | ||
|
|
ad74d24ee2 | ||
|
|
d232836423 | ||
|
|
0e7e1b7e38 | ||
|
|
f976fd9f54 | ||
|
|
d03fca801b | ||
|
|
31a4a1dd00 | ||
|
|
14207a0fa8 | ||
|
|
e906184bf7 | ||
|
|
1f41db9239 | ||
|
|
4f8a0f8739 | ||
|
|
c51ca4dd26 | ||
|
|
9e539b8c40 | ||
|
|
9a5eb577b8 | ||
|
|
4713023a97 | ||
|
|
301a4eedda | ||
|
|
3f9fad0e2a | ||
|
|
990740fc19 | ||
|
|
960d72aa1b | ||
|
|
f94bbf023c | ||
|
|
2556ef7624 | ||
|
|
321c0f2404 | ||
|
|
738c22b5f9 | ||
|
|
d0c30cb089 | ||
|
|
0cfea682ea | ||
|
|
95197ec6ed | ||
|
|
eecace511d | ||
|
|
6793e13e6d | ||
|
|
85da2d72aa |
170
.drone.yml
170
.drone.yml
@@ -1,170 +0,0 @@
|
||||
workspace:
|
||||
base: /srv/app
|
||||
path: src/code.vikunja.io/api
|
||||
|
||||
clone:
|
||||
git:
|
||||
image: plugins/git
|
||||
depth: 50
|
||||
tags: true
|
||||
|
||||
pipeline:
|
||||
build:
|
||||
image: webhippie/golang:edge
|
||||
pull: true
|
||||
group: build
|
||||
environment:
|
||||
TAGS: bindata sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make clean
|
||||
- make lint
|
||||
- make fmt-check
|
||||
- make swagger-check
|
||||
- make ineffassign-check
|
||||
- make misspell-check
|
||||
- make build
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
test:
|
||||
image: webhippie/golang:edge
|
||||
pull: true
|
||||
environment:
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make test
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
# Build a release when tagging
|
||||
before-static-build:
|
||||
image: karalabe/xgo-latest:latest
|
||||
pull: true
|
||||
environment:
|
||||
TAGS: bindata sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release-dirs
|
||||
when:
|
||||
event: [tag, push]
|
||||
|
||||
static-build-windows:
|
||||
image: karalabe/xgo-latest:latest
|
||||
pull: true
|
||||
group: build-static
|
||||
environment:
|
||||
TAGS: bindata sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release-windows
|
||||
when:
|
||||
event: [tag, push]
|
||||
|
||||
static-build-linux:
|
||||
image: karalabe/xgo-latest:latest
|
||||
pull: true
|
||||
group: build-static
|
||||
environment:
|
||||
TAGS: bindata sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release-linux
|
||||
when:
|
||||
event: [tag, push]
|
||||
|
||||
static-build-darwin:
|
||||
image: karalabe/xgo-latest:latest
|
||||
pull: true
|
||||
group: build-static
|
||||
environment:
|
||||
TAGS: bindata sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release-darwin
|
||||
when:
|
||||
event: [tag, push]
|
||||
|
||||
# static-build-frontend:
|
||||
# image: webhippie/nodejs:current
|
||||
# pull: true
|
||||
# group: build-static
|
||||
# commands:
|
||||
# - make release-frontend
|
||||
# when:
|
||||
# event: [push, tag ]
|
||||
|
||||
after-build-static:
|
||||
image: karalabe/xgo-latest:latest
|
||||
pull: true
|
||||
environment:
|
||||
TAGS: bindata sqlite
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- make release-copy
|
||||
- make release-check
|
||||
- make release-os-package
|
||||
- make release-zip
|
||||
when:
|
||||
event: [tag, push]
|
||||
|
||||
# Push the releases to our pseudo-s3-bucket
|
||||
release:
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
secrets: [ aws_access_key_id, aws_secret_access_key ]
|
||||
bucket: vikunja
|
||||
endpoint: https://storage.kolaente.de
|
||||
path_style: true
|
||||
strip_prefix: dist/zip/
|
||||
source: dist/zip/*
|
||||
target: /${DRONE_TAG##v}
|
||||
when:
|
||||
event: [ tag ]
|
||||
|
||||
release:
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
secrets: [ aws_access_key_id, aws_secret_access_key ]
|
||||
bucket: vikunja
|
||||
endpoint: https://storage.kolaente.de
|
||||
path_style: true
|
||||
strip_prefix: dist/zip/
|
||||
source: dist/zip/*
|
||||
target: /master
|
||||
when:
|
||||
event: [ push ]
|
||||
branch: [ master ]
|
||||
|
||||
# Build the docker image and push it to docker hub
|
||||
docker:
|
||||
image: plugins/docker
|
||||
pull: true
|
||||
secrets: [ docker_username, docker_password ]
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
when:
|
||||
event: [ push, tag ]
|
||||
branch: [ master ]
|
||||
|
||||
# Update the instance on try.vikunja.io
|
||||
rancher:
|
||||
image: peloton/drone-rancher
|
||||
url: http://server01.kolaente.de:8080/v1
|
||||
secrets: [ RANCHER_ACCESS_KEY, RANCHER_SECRET_KEY ]
|
||||
service: vikunja-dev/api
|
||||
docker_image: vikunja/api
|
||||
confirm: true
|
||||
when:
|
||||
event: [ push, tag ]
|
||||
branch: [ master ]
|
||||
|
||||
# Tell people vikunja was updated
|
||||
telegram:
|
||||
image: appleboy/drone-telegram
|
||||
secrets: [ TELEGRAM_TOKEN, TELEGRAM_TO ]
|
||||
message: >
|
||||
{{repo.owner}}/{{repo.name}}: \[{{build.status}}] Build {{build.number}}
|
||||
{{commit.author}} pushed to {{commit.branch}} {{commit.sha}}: `{{commit.message}}`
|
||||
Build started at {{datetime build.started "2006-Jan-02T15:04:05Z" "GMT+2"}} finished at {{datetime build.finished "2006-Jan-02T15:04:05Z" "GMT+2"}}.
|
||||
|
||||
714
.drone1.yml
Normal file
714
.drone1.yml
Normal file
@@ -0,0 +1,714 @@
|
||||
kind: pipeline
|
||||
name: testing
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/code.vikunja.io/api
|
||||
|
||||
services:
|
||||
- name: test-mysql-unit
|
||||
image: mariadb:10
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
- name: test-mysql-integration
|
||||
image: mariadb:10
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
- name: test-mysql-migration
|
||||
image: mariadb:10
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
- name: test-postgres-unit
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
- name: test-postgres-integration
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
- name: test-postgres-migration
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
include:
|
||||
- master
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
commands:
|
||||
- git fetch --tags
|
||||
|
||||
# We're statically compiling the magefile to avoid race condition issues caused by multiple pipeline steps
|
||||
# compiling the same magefile at the same time. It's also faster if each step does not need to compile it first.
|
||||
- name: mage
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- mage -compile ./mage-static
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: build
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ mage ]
|
||||
commands:
|
||||
- ./mage-static build:build
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: lint
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
depends_on: [ build ]
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static check:got-swag
|
||||
- wget -O - -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.31.0
|
||||
- ./mage-static check:golangci
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-migration-prepare
|
||||
image: kolaente/toolbox:latest
|
||||
pull: true
|
||||
commands:
|
||||
# Get the latest version
|
||||
- wget https://dl.vikunja.io/api/master/vikunja-master-linux-amd64-full.zip -q -O vikunja-latest.zip
|
||||
- unzip vikunja-latest.zip vikunja-master-linux-amd64
|
||||
|
||||
- name: test-migration-sqlite
|
||||
image: kolaente/toolbox:latest
|
||||
pull: true
|
||||
depends_on: [ test-migration-prepare, build ]
|
||||
environment:
|
||||
VIKUNJA_DATABASE_TYPE: sqlite
|
||||
VIKUNJA_DATABASE_PATH: ./vikunja-migration-test.db
|
||||
VIKUNJA_LOG_DATABASE: stdout
|
||||
VIKUNJA_LOG_DATABASELEVEL: debug
|
||||
commands:
|
||||
- ./vikunja-master-linux-amd64 migrate
|
||||
# Run the migrations from the binary build in the step before
|
||||
- ./vikunja migrate
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-migration-mysql
|
||||
image: kolaente/toolbox:latest
|
||||
pull: true
|
||||
depends_on: [ test-migration-prepare, build ]
|
||||
environment:
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_HOST: test-mysql-migration
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
VIKUNJA_LOG_DATABASE: stdout
|
||||
VIKUNJA_LOG_DATABASELEVEL: debug
|
||||
commands:
|
||||
- ./vikunja-master-linux-amd64 migrate
|
||||
# Run the migrations from the binary build in the step before
|
||||
- ./vikunja migrate
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-migration-psql
|
||||
image: kolaente/toolbox:latest
|
||||
pull: true
|
||||
depends_on: [ test-migration-prepare, build ]
|
||||
environment:
|
||||
VIKUNJA_DATABASE_TYPE: postgres
|
||||
VIKUNJA_DATABASE_HOST: test-postgres-migration
|
||||
VIKUNJA_DATABASE_USER: postgres
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
VIKUNJA_LOG_DATABASE: stdout
|
||||
VIKUNJA_LOG_DATABASELEVEL: debug
|
||||
commands:
|
||||
- ./vikunja-master-linux-amd64 migrate
|
||||
# Run the migrations from the binary build in the step before
|
||||
- ./vikunja migrate
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-sqlite
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: sqlite
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-mysql
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_HOST: test-mysql-unit
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-postgres
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: postgres
|
||||
VIKUNJA_DATABASE_HOST: test-postgres-unit
|
||||
VIKUNJA_DATABASE_USER: postgres
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:unit
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: integration-test
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: integration-test-sqlite
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: sqlite
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: integration-test-mysql
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_HOST: test-mysql-integration
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: integration-test-postgres
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: postgres
|
||||
VIKUNJA_DATABASE_HOST: test-postgres-integration
|
||||
VIKUNJA_DATABASE_USER: postgres
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static test:integration
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
---
|
||||
########
|
||||
# Build a release when tagging
|
||||
########
|
||||
|
||||
kind: pipeline
|
||||
name: release
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/code.vikunja.io/api
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/master
|
||||
- "refs/tags/**"
|
||||
|
||||
steps:
|
||||
# Needed to get the versions right as they depend on tags
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
commands:
|
||||
- git fetch --tags
|
||||
|
||||
# We're statically compiling the magefile to avoid race condition issues caused by multiple pipeline steps
|
||||
# compiling the same magefile at the same time. It's also faster if each step does not need to compile it first.
|
||||
- name: mage
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
GOPROXY: 'https://goproxy.kolaente.de'
|
||||
commands:
|
||||
- mage -compile ./mage-static
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: before-static-build
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static build:generate
|
||||
- ./mage-static release:dirs
|
||||
depends_on: [ fetch-tags, mage ]
|
||||
|
||||
- name: static-build-windows
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
environment:
|
||||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static release:windows
|
||||
depends_on: [ before-static-build ]
|
||||
|
||||
- name: static-build-linux
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
environment:
|
||||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static release:linux
|
||||
depends_on: [ before-static-build ]
|
||||
|
||||
- name: static-build-darwin
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
environment:
|
||||
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
|
||||
# Leaving this here until we know how to resolve this properly.
|
||||
GOPATH: /srv/app
|
||||
commands:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static release:darwin
|
||||
depends_on: [ before-static-build ]
|
||||
|
||||
- name: after-build-compress
|
||||
image: kolaente/upx
|
||||
pull: true
|
||||
depends_on:
|
||||
- static-build-windows
|
||||
- static-build-linux
|
||||
- static-build-darwin
|
||||
commands:
|
||||
- ./mage-static release:compress
|
||||
|
||||
- name: after-build-static
|
||||
image: techknowlogick/xgo:latest
|
||||
pull: true
|
||||
depends_on:
|
||||
- after-build-compress
|
||||
commands:
|
||||
- go install github.com/magefile/mage
|
||||
- ./mage-static release:copy
|
||||
- ./mage-static release:check
|
||||
- ./mage-static release:os-package
|
||||
- ./mage-static release:zip
|
||||
|
||||
- name: sign-release
|
||||
image: plugins/gpgsign:1
|
||||
pull: true
|
||||
depends_on: [ after-build-static ]
|
||||
settings:
|
||||
key:
|
||||
from_secret: gpg_privkey
|
||||
passphrase:
|
||||
from_secret: gpg_password
|
||||
files:
|
||||
- dist/zip/*
|
||||
detach_sign: true
|
||||
|
||||
# Push the releases to our pseudo-s3-bucket
|
||||
- name: release-latest
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
from_secret: aws_access_key_id
|
||||
secret_key:
|
||||
from_secret: aws_secret_access_key
|
||||
endpoint: https://s3.fr-par.scw.cloud
|
||||
region: fr-par
|
||||
path_style: true
|
||||
strip_prefix: dist/zip/
|
||||
source: dist/zip/*
|
||||
target: /api/master/
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
||||
depends_on: [ sign-release ]
|
||||
|
||||
- name: release-version
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
from_secret: aws_access_key_id
|
||||
secret_key:
|
||||
from_secret: aws_secret_access_key
|
||||
endpoint: https://s3.fr-par.scw.cloud
|
||||
region: fr-par
|
||||
path_style: true
|
||||
strip_prefix: dist/zip/
|
||||
source: dist/zip/*
|
||||
target: /api/${DRONE_TAG##v}/
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
depends_on: [ sign-release ]
|
||||
|
||||
# Build os packages and push it to our bucket
|
||||
- name: build-os-packages
|
||||
image: goreleaser/nfpm
|
||||
pull: true
|
||||
commands:
|
||||
- apk add git go
|
||||
- ./mage-static release:packages
|
||||
depends_on: [ static-build-linux ]
|
||||
|
||||
# Push the os releases to our pseudo-s3-bucket
|
||||
- name: release-os-latest
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
from_secret: aws_access_key_id
|
||||
secret_key:
|
||||
from_secret: aws_secret_access_key
|
||||
endpoint: https://s3.fr-par.scw.cloud
|
||||
region: fr-par
|
||||
path_style: true
|
||||
strip_prefix: dist/os-packages/
|
||||
source: dist/os-packages/*
|
||||
target: /api/master/
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
event:
|
||||
- push
|
||||
depends_on: [ build-os-packages ]
|
||||
|
||||
- name: release-os-version
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
from_secret: aws_access_key_id
|
||||
secret_key:
|
||||
from_secret: aws_secret_access_key
|
||||
endpoint: https://s3.fr-par.scw.cloud
|
||||
region: fr-par
|
||||
path_style: true
|
||||
strip_prefix: dist/os-packages/
|
||||
source: dist/os-packages/*
|
||||
target: /api/${DRONE_TAG##v}/
|
||||
when:
|
||||
event:
|
||||
- tag
|
||||
depends_on: [ build-os-packages ]
|
||||
|
||||
- name: deb-structure
|
||||
image: kolaente/reprepro
|
||||
pull: true
|
||||
environment:
|
||||
GPG_PRIVATE_KEY:
|
||||
from_secret: gpg_privatekey
|
||||
commands:
|
||||
- export GPG_TTY=$(tty)
|
||||
- gpg -qk
|
||||
- echo "use-agent" >> ~/.gnupg/gpg.conf
|
||||
- gpgconf --kill gpg-agent
|
||||
- echo $GPG_PRIVATE_KEY > ~/frederik.gpg
|
||||
- gpg --import ~/frederik.gpg
|
||||
- mkdir debian/conf -p
|
||||
- cp build/reprepro-dist-conf debian/conf/distributions
|
||||
- ./mage-static release:reprepro
|
||||
depends_on: [ build-os-packages ]
|
||||
|
||||
# Push the releases to our pseudo-s3-bucket
|
||||
- name: release-deb
|
||||
image: plugins/s3:1
|
||||
pull: true
|
||||
settings:
|
||||
bucket: vikunja-releases
|
||||
access_key:
|
||||
from_secret: aws_access_key_id
|
||||
secret_key:
|
||||
from_secret: aws_secret_access_key
|
||||
endpoint: https://s3.fr-par.scw.cloud
|
||||
region: fr-par
|
||||
path_style: true
|
||||
strip_prefix: debian
|
||||
source: debian/*/*/*/*/*
|
||||
target: /deb/
|
||||
depends_on: [ deb-structure ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: deploy-docs
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
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: theme
|
||||
image: kolaente/yarn
|
||||
pull: true
|
||||
group: build-static
|
||||
commands:
|
||||
- cd docs/themes/vikunja
|
||||
- yarn --production=false
|
||||
- ./node_modules/.bin/gulp prod
|
||||
|
||||
- name: build
|
||||
image: monachus/hugo:v0.54.0
|
||||
pull: true
|
||||
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
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-arm-release
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/master
|
||||
- "refs/tags/**"
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
commands:
|
||||
- git fetch --tags
|
||||
- name: docker
|
||||
image: plugins/docker:linux-arm
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-arm
|
||||
depends_on: [ fetch-tags ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-amd64-release
|
||||
|
||||
depends_on:
|
||||
- testing
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/master
|
||||
- "refs/tags/**"
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
image: docker:git
|
||||
commands:
|
||||
- git fetch --tags
|
||||
- name: docker
|
||||
image: plugins/docker:linux-amd64
|
||||
pull: true
|
||||
settings:
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
repo: vikunja/api
|
||||
auto_tag: true
|
||||
auto_tag_suffix: linux-amd64
|
||||
depends_on: [ fetch-tags ]
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: docker-manifest
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/master
|
||||
- "refs/tags/**"
|
||||
|
||||
depends_on:
|
||||
- docker-amd64-release
|
||||
- docker-arm-release
|
||||
|
||||
steps:
|
||||
- name: manifest
|
||||
pull: always
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
ignore_missing: true
|
||||
spec: docker-manifest.tmpl
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
from_secret: docker_username
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: notify
|
||||
|
||||
trigger:
|
||||
ref:
|
||||
- refs/heads/master
|
||||
- "refs/tags/**"
|
||||
|
||||
depends_on:
|
||||
- release
|
||||
- docker-manifest
|
||||
|
||||
steps:
|
||||
- name: telegram
|
||||
image: appleboy/drone-telegram:1-linux-amd64
|
||||
settings:
|
||||
token:
|
||||
from_secret: TELEGRAM_TOKEN
|
||||
to:
|
||||
from_secret: TELEGRAM_TO
|
||||
message: >
|
||||
{{repo.owner}}/{{repo.name}}: \[{{build.status}}] Build {{build.number}}
|
||||
{{commit.author}} pushed to {{commit.branch}} {{commit.sha}}: `{{commit.message}}`
|
||||
Build started at {{datetime build.started "2006-Jan-02T15:04:05Z" "GMT+2"}} finished at {{datetime build.finished "2006-Jan-02T15:04:05Z" "GMT+2"}}.
|
||||
when:
|
||||
status:
|
||||
- success
|
||||
- failure
|
||||
11
.gitea/pull_request_template.md
Normal file
11
.gitea/pull_request_template.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Description
|
||||
|
||||
|
||||
|
||||
# Checklist
|
||||
|
||||
* [ ] I added or improved tests
|
||||
* [ ] I added or improved docs for my feature
|
||||
* [ ] Swagger (including `mage do-the-swag`)
|
||||
* [ ] Error codes
|
||||
* [ ] New config options (including adding them to `config.yml.saml` and running `mage generate-docs`)
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://www.buymeacoffee.com/kolaente
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,9 +1,25 @@
|
||||
.idea/
|
||||
.idea/*/*
|
||||
.idea/httpRequests
|
||||
config.yml
|
||||
config.yaml
|
||||
!docs/config.yml
|
||||
*.db
|
||||
Run
|
||||
dist/
|
||||
cover.*
|
||||
/vikunja
|
||||
/vikunja
|
||||
Test_*
|
||||
bin/
|
||||
secrets
|
||||
*.deb
|
||||
debian/
|
||||
logs/
|
||||
docs/public/
|
||||
docs/resources/
|
||||
pkg/static/templates_vfsdata.go
|
||||
files/
|
||||
!pkg/files/
|
||||
vikunja-dump*
|
||||
vendor/
|
||||
os-packages/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "docs/themes/vikunja"]
|
||||
path = docs/themes/vikunja
|
||||
url = ../theme.git
|
||||
76
.golangci.yml
Normal file
76
.golangci.yml
Normal file
@@ -0,0 +1,76 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- goerr113
|
||||
- goheader
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- misspell
|
||||
disable:
|
||||
- scopelint # Obsolete, using exportloopref instead
|
||||
presets:
|
||||
- bugs
|
||||
- unused
|
||||
fast: false
|
||||
|
||||
linter-settings:
|
||||
nestif:
|
||||
min-complexity: 6
|
||||
goheader:
|
||||
template-path: code-hesader-template.txt
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- deadcode
|
||||
- path: pkg/integrations/*
|
||||
linters:
|
||||
- gocyclo
|
||||
- deadcode
|
||||
- varcheck
|
||||
- unparam
|
||||
- bodyclose
|
||||
- path: pkg/integrations/*
|
||||
text: "unlambda"
|
||||
linters:
|
||||
- gocritic
|
||||
- path: pkg/modules/background/unsplash/unsplash\.go
|
||||
linters:
|
||||
- bodyclose
|
||||
- path: pkg/migration/*
|
||||
linters:
|
||||
- exhaustive
|
||||
- goconst
|
||||
- goerr113
|
||||
- path: pkg/models/task_collection_filter\.go
|
||||
linters:
|
||||
- exhaustive
|
||||
- path: pkg/utils/random_string\.go
|
||||
text: "G404:" # We don't care about cryptographically secure randomness when we're using that utility function.
|
||||
linters:
|
||||
- gosec
|
||||
- path: pkg/modules/dump/*
|
||||
linters:
|
||||
- goerr113
|
||||
- path: pkg/
|
||||
text: "err113: do not define dynamic errors, use wrapped static errors instead:"
|
||||
linters:
|
||||
- goerr113
|
||||
- text: "commentFormatting: put a space between `//` and comment text"
|
||||
linters:
|
||||
- gocritic
|
||||
- path: pkg/modules/migration
|
||||
linters:
|
||||
- gocyclo
|
||||
663
CHANGELOG.md
Normal file
663
CHANGELOG.md
Normal file
@@ -0,0 +1,663 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
All releases can be found on https://code.vikunja.io/api/releases.
|
||||
|
||||
## [0.15.0] - 2020-10-19
|
||||
|
||||
### Added
|
||||
|
||||
* Add app support info for DAV (#692)
|
||||
* Add better tests for namespaces
|
||||
* Add caldav enabled/disabled to /info endpoint
|
||||
* Add checks if tasks exist in maps before trying to access them
|
||||
* Add config option to force ssl connections to connect with the mailer
|
||||
* Add dav proxy directions to example proxy configurations
|
||||
* Add docs about using vikunja with utf-8 characters
|
||||
* Add FreeBSD guide to installation docs
|
||||
* Add github sponsor link
|
||||
* Add Golangci Lint (#676)
|
||||
* Add mage command to create a new migration
|
||||
* Add option to configure legal urls
|
||||
* Add rootpath to deb command to not include everything in the deb file
|
||||
* Add toc to docs
|
||||
* Add update route to toggle team member admin status
|
||||
* Add util function to move files
|
||||
* Disable gocyclo for migration modules
|
||||
* Favorite lists (#654)
|
||||
* Favorite tasks (#653)
|
||||
* Generate config docs from sample config (#684)
|
||||
* Kanban bucket limits (#652)
|
||||
* Key-Value Storages (#674)
|
||||
* Manage users via cli (#632)
|
||||
* Mention client_max_body_size in nginx proxy settings
|
||||
* More avatar providers (#622)
|
||||
* Return rights when reading a single item (#626)
|
||||
* Saved filters (#655)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Cleanup references to make
|
||||
* Don't add a subtask to the top level of tasks to not add it twice in the list
|
||||
* Fetch tasks for caldav lists (#641)
|
||||
* Fix building for darwin with mage
|
||||
* Fix creating lists with non ascii characters (#607)
|
||||
* Fix decoding active users from redis
|
||||
* Fix dockerimage build
|
||||
* Fix docs index links
|
||||
* Fix duplicating a list with background
|
||||
* "Fix" gocyclo
|
||||
* Fix loading list background information for uploaded backgrounds
|
||||
* Fix migrating items with large items from todoist
|
||||
* Fix nfpm command in drone
|
||||
* Fix parsing todoist reminder dates
|
||||
* Fix reading passwords on windows
|
||||
* Fix release commands in drone
|
||||
* Fix release trigger
|
||||
* Fix release trigger in drone
|
||||
* Fix token renew for link shares
|
||||
* Fix trigger for pushing release artifacts to drone
|
||||
* Fix updating team admin status
|
||||
* Fix upload avatar not working
|
||||
* Fix users with disabled totp but not enrolled being unable to login
|
||||
* Makefile: make add EXTRA_GOFLAG to GOFLAGS (#605)
|
||||
* Make sure built binary files are executable when compressing with upx
|
||||
* Make sure lists which would have a duplicate identifier can still be duplicated
|
||||
* Make sure the metrics map accesses only happen explicitly
|
||||
* Make sure to copy the permissions as well when moving files
|
||||
* Make sure to only initialize all variables when needed
|
||||
* Make sure to require admin rights when modifying list/namespace users to be consistent with teams
|
||||
* Make sure we have git installed when building os packages
|
||||
* Make sure we have go installed when building os packages (for build step dependencies)
|
||||
* Only check if a bucket limit is exceeded when moving a task between buckets
|
||||
* Only try to download attachments from todoist when there is a url
|
||||
* Pin telegram notification plugin in drone
|
||||
* Regenerate swagger docs
|
||||
* Skip directories when moving build release artefacts in drone
|
||||
* Support absolute iCal timestamps in CalDAV requests (#691)
|
||||
* Work around tasks with attachments not being duplicated
|
||||
|
||||
### Changed
|
||||
|
||||
* Replace renovate tokens with env
|
||||
* Switch s3 release bucket to scaleway
|
||||
* Switch to mage (#651)
|
||||
* Testing improvements (#666)
|
||||
* Update docs with testmail command + reorder
|
||||
* Update github.com/asaskevich/govalidator commit hash to 29e1ff8 (#639)
|
||||
* Update github.com/asaskevich/govalidator commit hash to 50839af (#637)
|
||||
* Update github.com/asaskevich/govalidator commit hash to 7a23bdc (#657)
|
||||
* Update github.com/asaskevich/govalidator commit hash to df4adff (#552)
|
||||
* Update github.com/c2h5oh/datasize commit hash to 48ed595 (#644)
|
||||
* Update github.com/gordonklaus/ineffassign commit hash to e36bfde (#625)
|
||||
* Update github.com/jgautheron/goconst commit hash to 8f5268c (#658)
|
||||
* Update github.com/shurcooL/vfsgen commit hash to 0d455de (#642)
|
||||
* Update golang.org/x/crypto commit hash to 123391f (#619)
|
||||
* Update golang.org/x/crypto commit hash to 5c72a88 (#640)
|
||||
* Update golang.org/x/crypto commit hash to 7f63de1 (#672)
|
||||
* Update golang.org/x/crypto commit hash to 84dcc77 (#678)
|
||||
* Update golang.org/x/crypto commit hash to 948cd5f (#609)
|
||||
* Update golang.org/x/crypto commit hash to 9e8e0b3 (#685)
|
||||
* Update golang.org/x/crypto commit hash to ab33eee (#608)
|
||||
* Update golang.org/x/crypto commit hash to afb6bcd (#668)
|
||||
* Update golang.org/x/crypto commit hash to c90954c (#671)
|
||||
* Update golang.org/x/crypto commit hash to eb9a90e (#669)
|
||||
* Update golang.org/x/image commit hash to 4578eab (#663)
|
||||
* Update golang.org/x/image commit hash to a67d67e (#664)
|
||||
* Update golang.org/x/image commit hash to e162460 (#665)
|
||||
* Update golang.org/x/image commit hash to e59bae6 (#659)
|
||||
* Update golang.org/x/sync commit hash to 3042136 (#667)
|
||||
* Update golang.org/x/sync commit hash to b3e1573 (#675)
|
||||
* Update module 4d63.com/tz to v1.2.0 (#631)
|
||||
* Update module fzipp/gocyclo to v0.2.0 (#686)
|
||||
* Update module fzipp/gocyclo to v0.3.0 (#687)
|
||||
* Update module getsentry/sentry-go to v0.7.0 (#617)
|
||||
* Update module go-errors/errors to v1.1.1 (#677)
|
||||
* Update module go-testfixtures/testfixtures/v3 to v3.4.0 (#627)
|
||||
* Update module go-testfixtures/testfixtures/v3 to v3.4.1 (#693)
|
||||
* Update module iancoleman/strcase to v0.1.0 (#636)
|
||||
* Update module iancoleman/strcase to v0.1.1 (#645)
|
||||
* Update module iancoleman/strcase to v0.1.2 (#660)
|
||||
* Update module imdario/mergo to v0.3.10 (#615)
|
||||
* Update module imdario/mergo to v0.3.11 (#629)
|
||||
* Update module labstack/echo/v4 to v4.1.17 (#646)
|
||||
* Update module lib/pq to v1.7.1 (#616)
|
||||
* Update module lib/pq to v1.8.0 (#618)
|
||||
* Update module mattn/go-sqlite3 to v1.14.1 (#638)
|
||||
* Update module mattn/go-sqlite3 to v1.14.2 (#647)
|
||||
* Update module mattn/go-sqlite3 to v1.14.3 (#661)
|
||||
* Update module mattn/go-sqlite3 to v1.14.4 (#670)
|
||||
* Update module prometheus/client_golang to v1.8.0 (#681)
|
||||
* Update module spf13/afero to v1.3.2 (#610)
|
||||
* Update module spf13/afero to v1.3.3 (#623)
|
||||
* Update module spf13/afero to v1.3.4 (#628)
|
||||
* Update module spf13/afero to v1.3.5 (#650)
|
||||
* Update module spf13/afero to v1.4.0 (#662)
|
||||
* Update module spf13/afero to v1.4.1 (#673)
|
||||
* Update module spf13/cobra to v1.1.0 (#679)
|
||||
* Update module spf13/cobra to v1.1.1 (#690)
|
||||
* Update module spf13/viper to v1.7.1 (#620)
|
||||
* Update module src.techknowlogick.com/xgo to v1.1.0+1.15.0 (#630)
|
||||
* Update module src.techknowlogick.com/xgo to v1 (#613)
|
||||
* Update module swaggo/swag to v1.6.8 (#680)
|
||||
* Update renovate token
|
||||
* Update src.techknowlogick.com/xgo commit hash to 7c2e3c9 (#611)
|
||||
* Update src.techknowlogick.com/xgo commit hash to 96de19c (#612)
|
||||
* update theme
|
||||
* Update xgo to v1.0.0+1.14.6
|
||||
* Use db sessions for task-related things (#621)
|
||||
* Use nfpm to build deb, rpm and apk packages (#689)
|
||||
|
||||
## [0.14.1] - 2020-07-07
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix creating lists with non ascii characters (#607)
|
||||
* Fix decoding active users from redis
|
||||
* Fix parsing todoist reminder dates
|
||||
* Make sure the metrics map accesses only happen explicitly
|
||||
|
||||
### Changed
|
||||
|
||||
* Update docs theme
|
||||
|
||||
## [0.14.0] - 2020-07-01
|
||||
|
||||
### Added
|
||||
|
||||
* Add ability to run the docker container with configurable user and group ids
|
||||
* Add better errors if the sqlite db file is not writable
|
||||
* Add cache for initial unsplash collection
|
||||
* Add docker setup guide from start to finish
|
||||
* Add docs for restore
|
||||
* Add dump command (#592)
|
||||
* Add section to full-docker-example.md for Caddy v2 (#595)
|
||||
* Add go version to version command
|
||||
* Add list background information when getting all lists
|
||||
* Add logging if downloading an image from unsplash fails
|
||||
* Add migration test in drone (#585)
|
||||
* Add option to disable totp for everyone
|
||||
* Add plausible to docs
|
||||
* Add restarting commands to all example docker compose files
|
||||
* Add seperate docker pipeline for amd64 and arm
|
||||
* Add test mail command (#571)
|
||||
* Add todoist migrator to available migrators in info endpoint if it is enabled
|
||||
* Add unsplash image proxy for images and thumbnails
|
||||
* Add returning unsplash info when searching
|
||||
* Don't return all tasks when a user has no lists
|
||||
* Duplicate Lists (#603)
|
||||
* Enable upload backgrounds by default
|
||||
* Generate a random list identifier based on the list title
|
||||
* List Backgrounds (#568)
|
||||
* List Background upload (#582)
|
||||
* Repeat tasks after completion (#587)
|
||||
* Restore command (#593)
|
||||
* Sentry integration (#591)
|
||||
* Todoist Migration (#566)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Ensure consistent naming of title fields (#528)
|
||||
* Ensure task dates are in the future if a task has a repeating interval (#586)
|
||||
* Fix caching of initial unsplash results per page
|
||||
* Fix case-insensitive task search for postgresql (#524)
|
||||
* Fix docker manifest build
|
||||
* Fix docker multiarch build
|
||||
* Fix docs theme build
|
||||
* Fix getting unsplash thumbnails for non "photo-*" urls
|
||||
* Fix migration 20200425182634
|
||||
* Fix migration 20200516123847
|
||||
* Fix migration to add position to task
|
||||
* Fix misspell
|
||||
* Fix namespace title not being updated
|
||||
* Fix not loading timezones on all operating systems
|
||||
* Fix proxying unsplash images (security)
|
||||
* Fix removing existing sqlite files
|
||||
* Fix resetting list, label & namespace colors
|
||||
* Fix searching for unsplash pictures with words that contain a space
|
||||
* Fix setting a list identifier to empty
|
||||
* Fix sqlite db not working when creating a new one
|
||||
* Fix sqlite path in default config
|
||||
* Fix swagger docs
|
||||
* Fix updating the index when moving a task
|
||||
* Prevent crashing when trying to register with an empty payload
|
||||
* Properly ping unsplash when using unsplash images
|
||||
* Return errors when dumping
|
||||
* Set the list identifier when creating a new task
|
||||
|
||||
### Changed
|
||||
|
||||
* Expose namespace id when querying lists
|
||||
* Improve getting all namespaces performance (#526)
|
||||
* Improve memory usage of dump by not loading all files in memory prior to adding them to the zip
|
||||
* Improve metrics performance
|
||||
* Load the list when setting a background
|
||||
* Make the db timezone migration mysql compatible
|
||||
* Make the `_unix` suffix optional when sorting tasks
|
||||
* Migrate all timestamps to real iso dates (#594)
|
||||
* Make sure docker images are only built when tests pass
|
||||
* Remove build date from binary
|
||||
* Remove dependencies on build step to speed up test pipeline (#521)
|
||||
* Remove go mod vendor todo from pr template now that we don't keep dependencies in the repo anymore
|
||||
* Remove migration dependency to models
|
||||
* Remove min length for labels, lists, namespaces, tasks and teams
|
||||
* Remove vendored dependencies
|
||||
* Reorganize cmd init functions
|
||||
* Set unsplash empty collection caching to one hour
|
||||
* Simplify pipeline & add docker manifest step
|
||||
* Update alpine Docker tag to v3.12 (#573)
|
||||
* Update and fix staticcheck
|
||||
* Update dependency github.com/mattn/go-sqlite3 to v1.14.0
|
||||
* Update github.com/shurcooL/vfsgen commit hash to 92b8a71 (#599)
|
||||
* Update golang.org/x/crypto commit hash to 279210d (#577)
|
||||
* Update golang.org/x/crypto commit hash to 70a84ac (#578)
|
||||
* Update golang.org/x/crypto commit hash to 75b2880 (#596)
|
||||
* Update module go-redis/redis/v7 to v7.3.0 (#565)
|
||||
* Update module go-redis/redis/v7 to v7.4.0 (#579)
|
||||
* Update module go-testfixtures/testfixtures/v3 to v3.3.0 (#600)
|
||||
* Update module lib/pq to v1.6.0 (#572)
|
||||
* Update module lib/pq to v1.7.0 (#581)
|
||||
* Update module prometheus/client_golang to v1.7.0 (#589)
|
||||
* Update module prometheus/client_golang to v1.7.1 (#597)
|
||||
* Update module spf13/afero to v1.3.0 (#588)
|
||||
* Update module spf13/afero to v1.3.1 (#602)
|
||||
* Update module spf13/cobra to v1 (#511)
|
||||
* Update module src.techknowlogick.com/xormigrate to v1.2.1 (#574)
|
||||
* Update module src.techknowlogick.com/xormigrate to v1.3.0 (#590)
|
||||
* Update module stretchr/testify to v1.6.0 (#570)
|
||||
* Update module stretchr/testify to v1.6.1 (#580)
|
||||
* Update module swaggo/swag to v1.6.7 (#601)
|
||||
* Update src.techknowlogick.com/xgo commit hash to 209a5cf (#523)
|
||||
* Update src.techknowlogick.com/xgo commit hash to a09175e (#576)
|
||||
* Update src.techknowlogick.com/xgo commit hash to eeb7c0a (#575)
|
||||
* update theme
|
||||
* Update theme
|
||||
* Update web handler
|
||||
* Update xorm.io/xorm 1.0.1 -> 1.0.2
|
||||
* Use the db logger instance for logging migration related stuff
|
||||
|
||||
## [0.13.1] - 2020-05-19
|
||||
|
||||
### Fixed
|
||||
|
||||
* Don't get all tasks if a user has no lists
|
||||
|
||||
## [0.13] - 2020-05-12
|
||||
|
||||
#### Added
|
||||
|
||||
* Add 2fa for authentification (#383)
|
||||
* Add categories to error docs
|
||||
* Add changing email for users
|
||||
* Add community link
|
||||
* Add configuration options for log level
|
||||
* Add creating a new first bucket when creating a new list
|
||||
* Add docs for changing frontend url
|
||||
* Add endpoint to disable totp auth
|
||||
* Add endpoint to get the current users totp status
|
||||
* Add explanation to docs about cors
|
||||
* Add github token for renovate (#164)
|
||||
* Add gosec static analysis
|
||||
* Add moving tasks between lists (#389)
|
||||
* Add real buckets for tasks which don't have one (#446)
|
||||
* Add traefik 2 example configuration
|
||||
* Configure Renovate (#159)
|
||||
* Kanban (#393)
|
||||
* Task filters (#243)
|
||||
* Task Position (#412)
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Add checking and logging when trying to put a task into a nonexisting bucket
|
||||
* Fix bucket ID being reset with no need to do so
|
||||
* Fix creating new things with a link share auth
|
||||
* Fix dependencies
|
||||
* Fix gosec in drone
|
||||
* Fix link share creation & creating admin link shares without admin rights
|
||||
* Fix moving tasks back into the empty (ID: 0) bucket
|
||||
* Fix moving tasks in buckets
|
||||
* Fix not moving its bucket when moving a task between lists
|
||||
* Fix pagination count for task collection
|
||||
* Fix parsing array style comparators by query param
|
||||
* Fix reference to reverse proxies in docs
|
||||
* Fix removing the last bucket
|
||||
* Fix replace statements for tail
|
||||
* Fix team rights not updating for namespace rights
|
||||
* Fix tests after renaming json fields to snake_case
|
||||
* Fix total label count when getting all labels (#477)
|
||||
* Remove setting task bucket to 0
|
||||
* Task Filter Fixes (#495)
|
||||
|
||||
#### Changed
|
||||
|
||||
* Change all json fields to snake_case
|
||||
* Change totp secret datatype from varchar to text
|
||||
* Update alpine Docker tag to v3.11 (#160)
|
||||
* Update docs theme
|
||||
* Update github.com/c2h5oh/datasize commit hash to 28bbd47 (#212)
|
||||
* Update github.com/gordonklaus/ineffassign commit hash to 7953dde (#233)
|
||||
* Update github.com/jgautheron/goconst commit hash to cda7ea3 (#228)
|
||||
* Update github.com/shurcooL/httpfs commit hash to 8d4bc4b (#229)
|
||||
* Update golang.org/x/crypto commit hash to 056763e (#222)
|
||||
* Update golang.org/x/crypto commit hash to 06a226f (#504)
|
||||
* Update golang.org/x/crypto commit hash to 0848c95 (#371)
|
||||
* Update golang.org/x/crypto commit hash to 3c4aac8 (#419)
|
||||
* Update golang.org/x/crypto commit hash to 44a6062 (#429)
|
||||
* Update golang.org/x/crypto commit hash to 4b2356b (#475)
|
||||
* Update golang.org/x/crypto commit hash to 4bdfaf4 (#438)
|
||||
* Update golang.org/x/crypto commit hash to 729f1e8 (#458)
|
||||
* Update golang.org/x/crypto commit hash to a76a400 (#411)
|
||||
* Update golang.org/x/lint commit hash to 738671d (#223)
|
||||
* Update module go-redis/redis to v6.15.7 (#234)
|
||||
* Update module go-redis/redis to v6.15.7 (#290)
|
||||
* Update module go-redis/redis to v7 (#277)
|
||||
* Update module go-redis/redis to v7 (#309)
|
||||
* Update module go-testfixtures/testfixtures/v3 to v3.1.2 (#457)
|
||||
* Update module go-testfixtures/testfixtures/v3 to v3.2.0 (#505)
|
||||
* Update module imdario/mergo to v0.3.9 (#238)
|
||||
* Update module labstack/echo/v4 to v4.1.16 (#241)
|
||||
* Update module lib/pq to v1.4.0 (#428)
|
||||
* Update module lib/pq to v1.5.0 (#476)
|
||||
* Update module lib/pq to v1.5.1 (#485)
|
||||
* Update module lib/pq to v1.5.2 (#491)
|
||||
* Update module olekukonko/tablewriter to v0.0.4 (#240)
|
||||
* Update module prometheus/client_golang to v0.9.4 (#245)
|
||||
* Update module prometheus/client_golang to v1
|
||||
* Update module prometheus/client_golang to v1.6.0 (#463)
|
||||
* Update module spf13/cobra to v0.0.7 (#271)
|
||||
* Update module spf13/viper to v1.6.2 (#272)
|
||||
* Update module spf13/viper to v1.6.3 (#291)
|
||||
* Update module spf13/viper to v1.7.0 (#494)
|
||||
* Update module stretchr/testify to v1.5.1 (#274)
|
||||
* Update Renovate Configuration (#161)
|
||||
* Update src.techknowlogick.com/xgo commit hash to bb0faa3 (#279)
|
||||
* Update src.techknowlogick.com/xgo commit hash to c43d4c4 (#224)
|
||||
* Update xorm redis cacher to use the xorm logger instead of a special seperate one
|
||||
* Update xorm to v1 (#323)
|
||||
|
||||
## [0.12] - 2020-04-04
|
||||
|
||||
#### Added
|
||||
|
||||
* Add support for archiving lists and namespaces (#152)
|
||||
* Colors for lists and namespaces (#155)
|
||||
* Add build time to compile flags
|
||||
* Add proxying gravatar requests for user avatars (#148)
|
||||
* Add empty avatar provider (#149)
|
||||
* expand relative path ~/.config/vikunja to $HOME/.config/vikunja **WINDOWS** (#147)
|
||||
* Show lists as archived if their namespace is archived
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Workaround for timezones on windows (#151)
|
||||
* Fix getting one namespace
|
||||
* Fix getting the authenticated user with caldav
|
||||
* Fix searching for config in home directories
|
||||
* Fix updating lists with an identifier
|
||||
|
||||
#### Changed
|
||||
|
||||
* Change release bucket
|
||||
|
||||
## [0.11] - 2020-03-01
|
||||
|
||||
### Added
|
||||
|
||||
* Add config options for cors handling (#124)
|
||||
* Add config options for task attachments (#125)
|
||||
* Add generate as a make dependency for make build
|
||||
* Add logging for invalid model errors (#126)
|
||||
* Add more logging to web handler methods
|
||||
* Add postgres support (#135)
|
||||
* Add rate limit by ip for non-authenticated routes (#127)
|
||||
* Better efficency for loading teams (#128)
|
||||
* Expand relative path ~/.config/vikunja to $HOME/.config/vikunja (#146)
|
||||
* Task Comments (#138)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix typo in docker-compose example (#140)
|
||||
* Fix frontend url for wunderlist migration in docs
|
||||
* Fix inserting task structure with related tasks (#142)
|
||||
* Fix time zone settings not working in Docker
|
||||
* Fix updating dates when marking a task as done (#145)
|
||||
* Make sure the author is returned when creating a new comment
|
||||
* Remove double user field
|
||||
|
||||
### Changed
|
||||
|
||||
* Explicitly disable wunderlist migration by default (#141)
|
||||
* Migration Improvements (#122)
|
||||
* Refactor User and DB handling (#123)
|
||||
* Return iso dates for everything date related from the api (#130)
|
||||
* Update copyright header
|
||||
* Update theme
|
||||
* Update xorm to use the new import path (#133)
|
||||
* Use relative url in .gitmodules (#132)
|
||||
|
||||
## [0.10] - 2020-01-19
|
||||
|
||||
### Added
|
||||
|
||||
* Migration (#120)
|
||||
* Endpoint to get tasks on a list (#108)
|
||||
* Sort Order for tasks (#110)
|
||||
* Add files volume to docker compose docs
|
||||
* Add motd config option to docs
|
||||
* Add option to disable registration (#117)
|
||||
* Add task identifier (#115)
|
||||
* Add tests for md5 generation (#111)
|
||||
* Add user token renew (#113)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix new tasks not getting a new task index (#116)
|
||||
* Fix owner field being null for user shared namespaces (#119)
|
||||
* Fix passing sort_by and order_by as query path arrays
|
||||
* Fix sorting tasks by bool values
|
||||
* Fix task collection tests
|
||||
* Consistent copyright text in file headers (#112)
|
||||
|
||||
### Changed
|
||||
|
||||
* Task collection improvements (#109)
|
||||
* Update copyright year (#118)
|
||||
* Update docs with a traefik configuration
|
||||
* Use redis INCRBY and DECRBY when updating metrics values (#121)
|
||||
* Use utf8mb4 instead of plain utf8 (#114)
|
||||
* Update docs theme
|
||||
|
||||
## [0.9] - 2019-11-24
|
||||
|
||||
### Added
|
||||
|
||||
* Task Attachments (#104)
|
||||
* Task Relations (#103)
|
||||
* Add endpoint to get a single task (#106)
|
||||
* Add file volume to the docker image
|
||||
* Added extra depth to logging to correctly show the functions calling the logger in logs
|
||||
* Added more infos to a link share auth (#98)
|
||||
* Added percent done to tasks (#102)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fix default logging settings (#107)
|
||||
* Fixed a bug where adding assignees or reminders via an update would re-create them and not respect already inserted ones, leaving a lot of garbage
|
||||
* Fixed a bug where deleting an attachment would cause a nil panic
|
||||
* Fixed building docs theme
|
||||
* Fixed error when setting max file size on 32-Bit systems
|
||||
* Fixed labels being displayed multiple times if they were associated with more than one task (#99)
|
||||
* Fixed metrics on/off setting
|
||||
* Fixed migration for task relations
|
||||
* Fixed not getting all labels when retrieving a list with all tasks
|
||||
* Fixed panic when using link share and metrics
|
||||
* Fixed rate limit panic when authenticating with a link share auth token (#97)
|
||||
* Fixed removing reminders
|
||||
* Small link share fixes (#96)
|
||||
|
||||
### Changed
|
||||
|
||||
* Improve pagination (#105)
|
||||
* Moved `teams_{namespace|list}_*` to `{namespace|list}_teams_*` for better consistency (#101)
|
||||
* Refactored getting all lists for a namespace (#100)
|
||||
* Refactored getting task IDs for labels
|
||||
* Switched default logger to stdout instead of stderr
|
||||
* update docs theme
|
||||
|
||||
### Misc
|
||||
|
||||
* Move from markdown lists to Vikunja for roadmap
|
||||
|
||||
## [0.8] - 2019-09-01
|
||||
|
||||
### Added
|
||||
|
||||
* Better Caldav support (#73)
|
||||
* Added settings for max open/idle connections and max connection lifetime (#74)
|
||||
* /info endpoint (#85)
|
||||
* Added http endpoint to list all users on a list (#87)
|
||||
* Rate limits (#91)
|
||||
* Sharing of lists via public links (#94)
|
||||
|
||||
### Changed
|
||||
|
||||
* Reminders now use an extra table (#75)
|
||||
* Use the username instead of a full user object when adding a user to a team or giving it rights (#76)
|
||||
* Add the md5-hashed user email to user objects for use with gravatar (#78)
|
||||
* Use the auth methods to get IDs to avoid unneeded casts
|
||||
* Better config handling with constants (#83)
|
||||
* Statically compile templates in the final binary (#84)
|
||||
* Use longtext instead of varchar(1000) on description fields (#88)
|
||||
* Logger refactoring (#90)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed `listID` not being returned in tasks
|
||||
* Fixed tests (#72)
|
||||
* Fixed metrics endpoint not working
|
||||
* Fixed check if the user really exists before updating/deleting its rights (#77)
|
||||
* Fixed duedate spelling issue (#79)
|
||||
|
||||
### Misc
|
||||
|
||||
* Integration tests (#71)
|
||||
* Make sure the version works when building in drone
|
||||
* Switched to another version of xgo
|
||||
* Simplified the docker image (#80)
|
||||
* Update echo (#82)
|
||||
* Compress binaries after building them (#81)
|
||||
* Simplify structure by having less files (#86)
|
||||
* Limit the test pipeline to run only on pull requests (#89)
|
||||
* GetUser now returns a pointer (#93)
|
||||
* Refactor ListTask to Task (#92)
|
||||
|
||||
## [0.7] - 2019-04-05
|
||||
|
||||
### Added
|
||||
|
||||
* DB migrations (#67)
|
||||
* More cli options for Vikunja (#66 #68)
|
||||
* Use query params to sort tasks instead of url params (#61)
|
||||
* More config paths (#55)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed Priority not updating when setting it to 0
|
||||
* Fixed getting lists by namespace
|
||||
* Fixed rights check (#70 #62)
|
||||
* Fixed labels not being queried correctly on tasks
|
||||
* Fixed bulk update label tasks
|
||||
|
||||
### Changed
|
||||
|
||||
* Hide a user's email address everywhere (#69)
|
||||
* Refactored `canRead()` to get the list before checking rights #65
|
||||
* Let rights methods return errors (#64 #63)
|
||||
* Improved Swagger docs for label tasks
|
||||
* Docs improvements (#58)
|
||||
* Logging Handling (#57)
|
||||
* Rights performance improvements (#54)
|
||||
|
||||
### Misc
|
||||
|
||||
* Releases also as Debian packages (#56)
|
||||
|
||||
## [0.6] - 2019-01-16
|
||||
|
||||
### Added
|
||||
|
||||
* Added prometheus endpoint to get metrics (#33)
|
||||
* More unit tests (#34)
|
||||
* Tests can now use config files (#36)
|
||||
* Redoc for swagger ui (#39, #46)
|
||||
* Start and end dates for tasks (#40)
|
||||
* Get tasks between a date range (#41)
|
||||
* Bulk edit for tasks (#42)
|
||||
* More ci checks (#43)
|
||||
* Task assignees (#44, #47)
|
||||
* Task labels (#45, #48)
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed path to get all tasks (echo bug)
|
||||
* Explicitly get the peudonamespace with all shared lists (#32)
|
||||
* Properly init tabels Redis
|
||||
* unexpected EOF when using metrics (#35)
|
||||
* Task sorting in lists (#36)
|
||||
* Various user fixes (#38)
|
||||
* Fixed a bug where updating a list would update it with the same values it had
|
||||
|
||||
### Changed
|
||||
|
||||
* Simplified list rights check (#50)
|
||||
* Refactored some structs to not expose unneded values via json (#52)
|
||||
|
||||
### Misc
|
||||
|
||||
* Updated libraries
|
||||
* Updated drone to version 1
|
||||
* Releases are now signed with our pgp key (more info about this on [the download page](https://vikunja.io/en/download/)).
|
||||
|
||||
## [0.5] - 2018-12-02
|
||||
|
||||
### Added
|
||||
|
||||
* Shared lists are now shown in a pseudonamespace with all other namespaces, has the ID -1
|
||||
* Tasks can have multiple reminders
|
||||
* Tasks can have subtasks. Subtasks are fully-fleged tasks, but not shown in the task list of a list.
|
||||
* Tasks can have priorities
|
||||
|
||||
### Changed
|
||||
|
||||
* Validation not so verbose anymore
|
||||
* [License](https://git.kolaente.de/vikunja/api/src/branch/master/LICENSE) is now GPLv3
|
||||
* The crudhandler now has its [own repo](https://git.kolaente.de/vikunja/web) - you can use it in your own projects!
|
||||
|
||||
## [0.4] - 2018-11-16
|
||||
|
||||
#### Added
|
||||
|
||||
* Get all tasks for the authenticated user sorted by their due date
|
||||
* CalDAV support
|
||||
* Pagination for everything which returns an array
|
||||
* Search all the things
|
||||
* More validation for most of the structs
|
||||
* Improved Swagger docs (available on `/api/v1/swagger`)
|
||||
|
||||
## [0.3] - 2018-11-02
|
||||
|
||||
### Added
|
||||
|
||||
* Password reset
|
||||
* Email verification when registering
|
||||
|
||||
Misc bugfixes and improvements to the build process
|
||||
|
||||
## [0.2] - 2018-10-17
|
||||
|
||||
## [0.1] - 2018-09-20
|
||||
|
||||
62
Dockerfile
62
Dockerfile
@@ -1,41 +1,51 @@
|
||||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM golang:1.11-alpine3.7 AS build-env
|
||||
##############
|
||||
# Build stage
|
||||
FROM golang:1-alpine AS build-env
|
||||
|
||||
ARG VIKUNJA_VERSION
|
||||
ENV TAGS "sqlite"
|
||||
ENV GO111MODULE=on
|
||||
|
||||
#Build deps
|
||||
# Build deps
|
||||
RUN apk --no-cache add build-base git
|
||||
|
||||
#Setup repo
|
||||
# Setup repo
|
||||
COPY . ${GOPATH}/src/code.vikunja.io/api
|
||||
WORKDIR ${GOPATH}/src/code.vikunja.io/api
|
||||
|
||||
#Checkout version if set
|
||||
# Checkout version if set
|
||||
RUN if [ -n "${VIKUNJA_VERSION}" ]; then git checkout "${VIKUNJA_VERSION}"; fi \
|
||||
&& make clean build
|
||||
&& go install github.com/magefile/mage \
|
||||
&& mage build:clean build:build
|
||||
|
||||
FROM alpine:3.7
|
||||
###################
|
||||
# The actual image
|
||||
# Note: I wanted to use the scratch image here, but unfortunatly the go-sqlite bindings require cgo and
|
||||
# because of this, the container would not start when I compiled the image without cgo.
|
||||
FROM alpine:3.12
|
||||
LABEL maintainer="maintainers@vikunja.io"
|
||||
|
||||
WORKDIR /app/vikunja/
|
||||
COPY --from=build-env /go/src/code.vikunja.io/api/vikunja .
|
||||
ENV VIKUNJA_SERVICE_ROOTPATH=/app/vikunja/
|
||||
|
||||
# Dynamic permission changing stuff
|
||||
ENV PUID 1000
|
||||
ENV PGID 1000
|
||||
RUN apk --no-cache add shadow && \
|
||||
addgroup -g ${PGID} vikunja && \
|
||||
adduser -s /bin/sh -D -G vikunja -u ${PUID} vikunja -h /app/vikunja -H && \
|
||||
chown vikunja -R /app/vikunja
|
||||
COPY run.sh /run.sh
|
||||
|
||||
# Fix time zone settings not working
|
||||
RUN apk --no-cache add tzdata
|
||||
|
||||
# Files permissions
|
||||
RUN mkdir /app/vikunja/files && \
|
||||
chown -R vikunja /app/vikunja/files
|
||||
VOLUME /app/vikunja/files
|
||||
|
||||
CMD ["/run.sh"]
|
||||
EXPOSE 3456
|
||||
|
||||
RUN apk --no-cache add \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gettext \
|
||||
linux-pam \
|
||||
s6 \
|
||||
sqlite \
|
||||
su-exec \
|
||||
tzdata
|
||||
|
||||
COPY docker /
|
||||
COPY --from=build-env /go/src/code.vikunja.io/api/public /app/vikunja/
|
||||
COPY --from=build-env /go/src/code.vikunja.io/api/vikunja /app/vikunja/vikunja
|
||||
|
||||
ENTRYPOINT ["/bin/s6-svscan", "/etc/services.d"]
|
||||
CMD []
|
||||
|
||||
230
Featurecreep.md
230
Featurecreep.md
@@ -1,230 +0,0 @@
|
||||
# Featurecreep
|
||||
|
||||
* Listen erstellen, ändern, löschen
|
||||
* Todopunkte zu Listen hinzufügen, bearbeiten, löschen
|
||||
* Listen teilen (Email/Benutzername angeben, oder öffentlicher link (+einstellbar ob mit registrierung oder nicht, oder passwortgeschützt)
|
||||
* Rechtemanagement
|
||||
|
||||
### Todopunkte
|
||||
|
||||
* ID
|
||||
* Text
|
||||
* Description
|
||||
* Status (done, not done)
|
||||
* Fälligkeitsdatum
|
||||
* Erinnerungsdatum (und zeit)
|
||||
* Zuständig (später, mit teilen)
|
||||
* Liste wo der Punkt drauf ist
|
||||
* Timestamps
|
||||
|
||||
### Websockets
|
||||
|
||||
Das ganze soll als Websocket zur verfg gestellt werden, der dann automatisch bescheidsagt wenn sich was ändert. Benachrichtigungen machen clients.
|
||||
|
||||
## API-Roadmap
|
||||
|
||||
Ab v0.3 können wir mit clients anfangen.
|
||||
|
||||
#### v0.1
|
||||
|
||||
* [x] Listen erstellen/bearbeiten/löschen
|
||||
|
||||
* [x] Ansehen
|
||||
* [x] Übersicht
|
||||
* [x] Einzelne liste mit allen todopunkten
|
||||
* [x] Erstellen
|
||||
* [x] Bearbeiten
|
||||
* [x] Löschen
|
||||
|
||||
* [x] Todopunkte hinzufügen/abhaken/löschen
|
||||
|
||||
* [x] Erstellen
|
||||
* [x] Bearbeiten (abhaken)
|
||||
* [x] Löschen
|
||||
|
||||
* [x] Überall nochmal überprüfen dass der Nutzer auch das Recht hat die Liste zu löschen
|
||||
|
||||
* [x] Swaggerdocs !!!!
|
||||
|
||||
Neues Konzept: _Namespaces_
|
||||
|
||||
Ein Namespace kann Listen haben, es gibt mindestens einen Besiter pro Namespace. Wenn ein neuer Nutzer angelegt wird,
|
||||
wird automatisch einer für den Nutzer erstellt.
|
||||
|
||||
Es gibt Lese- und Schreibrechte pro Namespace und Nutzer.
|
||||
|
||||
Namespace:
|
||||
|
||||
* ID
|
||||
* Name
|
||||
* OwnerID
|
||||
* Timestamps
|
||||
|
||||
Teams:
|
||||
|
||||
* ID
|
||||
* Name
|
||||
* Description
|
||||
* Rights (Selbsthochzählende Konstanten als json-array abspeichern)
|
||||
* CreatedByUser
|
||||
* Timestamps
|
||||
|
||||
TeamMembers:
|
||||
|
||||
* ID
|
||||
* TeamID
|
||||
* MemberID
|
||||
* Timestamps
|
||||
|
||||
TeamNamespaces:
|
||||
|
||||
* ID
|
||||
* TeamID
|
||||
* NamespaceID
|
||||
* Timestamps
|
||||
|
||||
TeamLists:
|
||||
|
||||
* ID
|
||||
* TeamID
|
||||
* ListID
|
||||
* Timestamps
|
||||
|
||||
(+Check ob das Team schon Zugriff auf den Namespace hat und dafür sorgen dass das sich nicht überschneidet)
|
||||
Bsp: wenn ein Namespace-Team Schreibrechte hat, soll es nicht möglich sein dieses Team mit Schreibrechten
|
||||
zur Liste hinzuzufügen. Wenn das Team im Namespace aber nur Leserechte Hat soll es möglich sein dieses Team
|
||||
als Schreibend zur Liste hinzuzufügen.
|
||||
|
||||
Oder noch Besser: Man kann globale Rechte pro Namespace vergeben, die man dann wieder feinjustieren kann pro Liste.
|
||||
Es soll aber nicht mgl. sein, ein Team zu einer Liste hinzuzufügen was nicht im Namespace ist.
|
||||
Es muss also möglich sein, Teams zum Namespace hinzuzufügen die keinerlei Rechte haben (damit man
|
||||
denen dann wieder pro Liste welche geben kann)
|
||||
|
||||
Rechte:
|
||||
Erstmal nur 3: Lesen, Schreiben, Admin. Admins dürfen auch Namen ändern, Teams verwalten, neue Listen anlegen, etc.
|
||||
Owner haben immer Adminrechte. Später sollte es auch möglich sein, den ownership an andere zur übertragen.s
|
||||
|
||||
Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten.
|
||||
|
||||
#### Neues Todo
|
||||
|
||||
* [x] Teams
|
||||
|
||||
* [x] Erstellen
|
||||
* [x] Ansehen
|
||||
* [x] Bearbeiten
|
||||
* [x] Löschen
|
||||
|
||||
~~Ein zu lösendes Problem: Wie regelt man die Berechtigungen um Teams zu verwalten?~~
|
||||
|
||||
* [x] Namespaces
|
||||
|
||||
* [x] Erstellen
|
||||
* [x] Ansehen
|
||||
* [x] Bearbeiten
|
||||
* [x] Löschen
|
||||
* [x] Teams hinzufügen. Der Nutzer kriegt nur Teams angezeigt die er erstellt hat.
|
||||
* [x] Alle Listen eines Namespaces anzeigen
|
||||
|
||||
* [x] Listen
|
||||
|
||||
* [x] Listen zu einem Namespace hinzufügen
|
||||
|
||||
#### v0.2
|
||||
|
||||
* [x] Listen teilbar
|
||||
* [x] Mit anderen Nutzern
|
||||
* [x Namespaces
|
||||
* [x] Teams
|
||||
* [ ] Mit Link
|
||||
* [ ] Offen
|
||||
* [ ] Passwortgeschützt
|
||||
|
||||
* [x] Rechtemanagement (Und damit Unterscheidung zwischen Ownern und Mitgleidern)
|
||||
* [x] Mange Team members
|
||||
* [x] Hinzufügen
|
||||
* [x] Löschen
|
||||
|
||||
*Routen*
|
||||
|
||||
* [x] `namespaces/:id/teams`
|
||||
* [x] Create
|
||||
* [x] ReadAll
|
||||
* [x] Delete
|
||||
* [x] `lists/:id/teams`
|
||||
* [x] Create
|
||||
* [x] ReadAll
|
||||
* [x] Delete
|
||||
|
||||
* [x] /namespaces soll zumindest auch die namen (+id) der dazugehörigen Listen rausgeben
|
||||
|
||||
## Feature-Ideen
|
||||
|
||||
* [ ] Labels
|
||||
* [ ] Priorities
|
||||
* [ ] Assignees
|
||||
* [ ] Subtasks
|
||||
* [ ] Attachments
|
||||
* [ ] Repeating tasks
|
||||
* [ ] 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.
|
||||
|
||||
## Clients
|
||||
|
||||
* [ ] Webapp (vue.js) + Bulma
|
||||
* [ ] "Native" Clients (auf dem Rechner installiert (mit elektron oder so? Oder native mit qt oder so?)
|
||||
* [ ] Android (Flutter)
|
||||
* [ ] iOS (mit Framework???? (Ging das nich auch mit Flutter?))
|
||||
|
||||
## Anderes
|
||||
|
||||
* [ ] 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.
|
||||
* [ ] ganz viel in eigene neue Dateien + Packages auslagern, am besten eine package pro model mit allen methoden etc.
|
||||
* [ ] 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.
|
||||
* [ ] Endpoints neu organisieren? Also zb `namespaces/:nID/lists/:lID/items/:iID` statt einzelnen Endpoints für alles
|
||||
* [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
|
||||
|
||||
### 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] Ne extra funktion für list exists machen, damit die nicht immer über GetListByID gehen, um sql-abfragen zu sparen
|
||||
* [ ] Search endpoints /users?s=name und /teams?s=name, erstmal nur mit Namen suchen.
|
||||
* [ ] Userstuff aufräumen, auf neuen Handler umziehen?
|
||||
* [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)
|
||||
* [ ] Methode einbauen, um mit einem gültigen token ein neues gültiges zu kriegen
|
||||
* [ ] Wir brauchen noch ne gute idee, wie man die listen kriegt, auf die man nur so Zugriff hat (ohne namespace)
|
||||
* [ ] Validation der ankommenden structs, am besten mit https://github.com/go-validator/validator
|
||||
* [ ] Rausfinden warum xorm teilweise beim einfügen IDs mit einfügen will -> Das schlägt dann wegen duplicate fehl
|
||||
|
||||
### Later/Nice to have
|
||||
|
||||
* [ ] An "accepted" für post/put payloads schrauben, man soll da zb keine id/created/updated/etc übergeben können.
|
||||
* [ ] Globale Limits für anlegbare Listen + Namespaces
|
||||
* [ ] Mgl., dass die Instanz geschlossen ist, also sich keiner registrieren kann, und man sich einloggen muss
|
||||
* [ ] 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)
|
||||
* [ ] Deps nach mod (dem nachfolger von dep) umziehen, blocked by Go 1.11
|
||||
324
Gopkg.lock
generated
324
Gopkg.lock
generated
@@ -1,324 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
pruneopts = "UT"
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6cb5bf34f3e3bb61e76d976f5bd29d245b567f208e1fd7cb8104b736a5eb649d"
|
||||
name = "github.com/dgrijalva/jwt-go"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a539ee1a749a2b895533f979515ac7e6e0f5b650"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0594af97b2f4cec6554086eeace6597e20a4b69466eb4ada25adf9f4300dddd2"
|
||||
name = "github.com/garyburd/redigo"
|
||||
packages = [
|
||||
"internal",
|
||||
"redis",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "a69d19351219b6dd56f274f96d85a7014a2ec34e"
|
||||
version = "v1.6.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d2b67246c3fa959edfa1be84b407cf0a0a4cabbace0843814b1729b5af808d9e"
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ee359f95877bdef36cbb602711e49b6f0becfca9"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3f854136f2da9cde22fa0dd29e74fb7c920c5d79d060847fe9b96f2d3cfab746"
|
||||
name = "github.com/go-xorm/builder"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c8871c857d2555fbfbd8524f895be5386d3d8836"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ee0b218753df985dd6633b13a894a69f0c1ea385f092f4afa093d407bb165cc5"
|
||||
name = "github.com/go-xorm/core"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f43c33d9a48db006417a7ac4c16b08897e3e1458"
|
||||
version = "v0.5.8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8e0764e74dd6cf539d3c035772d3fd0009b89fff9889a1131139be6d871c90dd"
|
||||
name = "github.com/go-xorm/xorm"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "29d4a0330a00b9be468b70e3fb0f74109348c358"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d4a957ebe4ccebc299c1f6b6a78a0713af379721980cf14990b8ab8fa93cd92d"
|
||||
name = "github.com/go-xorm/xorm-redis-cache"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "859b313566b2ef090319245fd4fe7692f25dbd79"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8eb1de8112c9924d59bf1d3e5c26f5eaa2bfc2a5fcbb92dc1c2e4546d695f277"
|
||||
name = "github.com/imdario/mergo"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4"
|
||||
version = "v0.3.6"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:82c5850e702ccef3976cdfdf737725013587c64e02e5ecae57e86431163b27aa"
|
||||
name = "github.com/labstack/echo"
|
||||
packages = [
|
||||
".",
|
||||
"middleware",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "1049c9613cd371b7ea8f219404c9a821734781ed"
|
||||
version = "v3.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ef6158d11f87c8036114cf3d2bf5c50edb4aa0b4aab5deef26716a8bb674a9ad"
|
||||
name = "github.com/labstack/gommon"
|
||||
packages = [
|
||||
"bytes",
|
||||
"color",
|
||||
"log",
|
||||
"random",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "57409ada9da0f2afad6664c49502f8c50fbd8476"
|
||||
version = "0.2.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c2353362d570a7bfa228149c62842019201cfb71"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a36a1febe1240bb79a208390ad17e1080555b0031a9ed42a41eae173cca3fd74"
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ad5389df28cdac544c99bd7b9161a0b5b6ca9d1b"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb"
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a5cdd64afdee435007ee3e9f6ed4684af949d568"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3cafc6a5a1b8269605d9df4c6956d43d8011fc57f266ca6b9d04da6c09dee548"
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "25ecb14adfc7543176f7d85291ec7dba82c6f7e4"
|
||||
version = "v1.9.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5b3b29ce0e569f62935d9541dff2e16cc09df981ebde48e82259076a73a3d0c7"
|
||||
name = "github.com/op/go-logging"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "b2cb9fa56473e98db8caba80237377e83fe44db5"
|
||||
version = "v1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
pruneopts = "UT"
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6e30a27eac59a148b3f7a32e0ba54706b31dcde5a42f63b22cb47873b62fa343"
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "8fb642006536c8d3760c99d4fa2389f5e2205631"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a7496c8d47f794d15a6e41f8ada426d4a41c038fac9bd75c1abc30faacf469c9"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
pruneopts = "UT"
|
||||
revision = "87b1dfb5b2fa649f52695dd9eae19abe404a4308"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c468422f334a6b46a19448ad59aaffdfc0a36b08fdcc1c749a0b29b6453d7e59"
|
||||
name = "github.com/valyala/bytebufferpool"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:268b8bce0064e8c057d7b913605459f9a26dcab864c0886a56d196540fbf003f"
|
||||
name = "github.com/valyala/fasttemplate"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "dcecefd839c4193db0d35b88ec65b4c12d360ab0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:456c96b87c9481d98aac8ef9862053a8fc97b981d26f6cd8d63de783dd24acec"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"acme",
|
||||
"acme/autocert",
|
||||
"bcrypt",
|
||||
"blowfish",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "9419663f5a44be8b34ca85f08abc5fe1be11f8a3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f1275b0d1ed7d43eeb7a0464a148258f04e66a4783514d5c790d4e970cc7df6d"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
pruneopts = "UT"
|
||||
revision = "314a259e304ff91bd6985da2a7149bbf91237993"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8029e9743749d4be5bc9f7d42ea1659471767860f0cdc34d37c3111bd308a295"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"internal/gen",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"transform",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:59c645bd5db5ec4a889a1ac62ce9cb3c4483cc24c66907ea75de2516d6fe4716"
|
||||
name = "gopkg.in/testfixtures.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "1d98c34adfb14dbedeef37127968233b5d960f02"
|
||||
version = "v2.4.5"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:73e6fda93622790d2371344759df06ff5ff2fac64a6b6e8832b792e7402956e7"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "d670f9405373e636a5a2765eea47fac0c9bc91a4"
|
||||
version = "v2.0.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/dgrijalva/jwt-go",
|
||||
"github.com/go-sql-driver/mysql",
|
||||
"github.com/go-xorm/core",
|
||||
"github.com/go-xorm/xorm",
|
||||
"github.com/go-xorm/xorm-redis-cache",
|
||||
"github.com/imdario/mergo",
|
||||
"github.com/labstack/echo",
|
||||
"github.com/labstack/echo/middleware",
|
||||
"github.com/mattn/go-sqlite3",
|
||||
"github.com/op/go-logging",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"golang.org/x/crypto/bcrypt",
|
||||
"gopkg.in/testfixtures.v2",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
57
Gopkg.toml
57
Gopkg.toml
@@ -1,57 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/go-xorm/core"
|
||||
version = "0.5.7"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/labstack/echo"
|
||||
version = "3.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
version = "1.5.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/testfixtures.v2"
|
||||
version = "2.4.3"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/viper"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/imdario/mergo"
|
||||
version = "0.3.6"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/op/go-logging"
|
||||
version = "1.0.0"
|
||||
759
LICENSE
759
LICENSE
@@ -1,165 +1,674 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
0. Additional Definitions.
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
0. Definitions.
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
4. Combined Works.
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
1. Source Code.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
d) Do one of the following:
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
5. Combined Libraries.
|
||||
2. Basic Permissions.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
193
Makefile
193
Makefile
@@ -1,193 +0,0 @@
|
||||
DIST := dist
|
||||
IMPORT := code.vikunja.io/api
|
||||
|
||||
SED_INPLACE := sed -i
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
EXECUTABLE := vikunja.exe
|
||||
else
|
||||
EXECUTABLE := vikunja
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED_INPLACE := sed -i ''
|
||||
endif
|
||||
endif
|
||||
|
||||
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
|
||||
GOFMT ?= gofmt -s
|
||||
|
||||
GOFLAGS := -i -v
|
||||
EXTRA_GOFLAGS ?=
|
||||
|
||||
LDFLAGS := -X "main.Version=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')" -X "main.Tags=$(TAGS)"
|
||||
|
||||
PACKAGES ?= $(filter-out code.vikunja.io/api/integrations,$(shell go list ./... | grep -v /vendor/))
|
||||
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||
|
||||
TAGS ?=
|
||||
|
||||
TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'kasino-temp')
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
EXECUTABLE := vikunja.exe
|
||||
else
|
||||
EXECUTABLE := vikunja
|
||||
endif
|
||||
|
||||
ifneq ($(DRONE_TAG),)
|
||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||
else
|
||||
ifneq ($(DRONE_BRANCH),)
|
||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||
else
|
||||
VERSION ?= master
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
go clean -i ./...
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -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 or 1.10 to format code" >&2 && exit 1; }
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u golang.org/x/lint/golint; \
|
||||
fi
|
||||
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: required-gofmt-version
|
||||
$(GOFMT) -w $(GOFILES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: required-gofmt-version
|
||||
# get all go files and run go fmt on them
|
||||
@diff=$$($(GOFMT) -d $(GOFILES)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: install
|
||||
install: $(wildcard *.go)
|
||||
go install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
.PHONY: build
|
||||
build: $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(SOURCES)
|
||||
go build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: release-dirs release-windows release-linux release-darwin release-copy release-check release-os-package release-zip
|
||||
|
||||
.PHONY: release-dirs
|
||||
release-dirs:
|
||||
mkdir -p $(DIST)/binaries $(DIST)/release $(DIST)/zip
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/karalabe/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out vikunja-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
mv /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/karalabe/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out vikunja-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
mv /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/karalabe/xgo; \
|
||||
fi
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out vikunja-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
mv /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy:
|
||||
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
|
||||
mkdir $(DIST)/release/public
|
||||
cp public/ $(DIST)/release/ -R
|
||||
|
||||
.PHONY: release-check
|
||||
release-check:
|
||||
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
||||
|
||||
|
||||
.PHONY: release-os-package
|
||||
release-os-package:
|
||||
$(foreach file,$(filter-out %.sha256,$(wildcard $(DIST)/release/$(EXECUTABLE)-*)),mkdir $(file)-full;mv $(file) $(file)-full/; mv $(file).sha256 $(file)-full/; cp config.yml.sample $(file)-full/config.yml; cp $(DIST)/release/public $(file)-full/ -R; cp LICENSE $(file)-full/; )
|
||||
rm $(DIST)/release/public -rf
|
||||
|
||||
.PHONY: release-zip
|
||||
release-zip:
|
||||
$(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),cd $(file); zip -r ../../zip/$(shell basename $(file)).zip *; cd ../../../; )
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/go-swagger/go-swagger/cmd/swagger; \
|
||||
fi
|
||||
swagger generate spec -o ./public/swagger/swagger.v1.json
|
||||
|
||||
.PHONY: swagger-check
|
||||
swagger-check: generate-swagger
|
||||
@diff=$$(git diff public/swagger/swagger.v1.json); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/go-swagger/go-swagger/cmd/swagger; \
|
||||
fi
|
||||
swagger validate ./public/swagger/swagger.v1.json
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
for S in $(GOFILES); do misspell -error $$S || exit 1; done;
|
||||
|
||||
.PHONY: ineffassign-check
|
||||
ineffassign-check:
|
||||
@hash ineffassign > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/gordonklaus/ineffassign; \
|
||||
fi
|
||||
for S in $(GOFILES); do ineffassign $$S || exit 1; done;
|
||||
|
||||
.PHONY: gocyclo-check
|
||||
gocyclo-check:
|
||||
@hash gocyclo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get github.com/fzipp/gocyclo; \
|
||||
fi
|
||||
for S in $(GOFILES); do gocyclo -over 14 $$S || exit 1; done;
|
||||
91
README.md
91
README.md
@@ -1,53 +1,80 @@
|
||||
<img src="https://vikunja.io/images/vikunja-logo.svg" alt="" style="display: block;width: 50%;margin: 0 auto;" width="50%"/>
|
||||
|
||||
[](https://drone1.kolaente.de/vikunja/api)
|
||||
[](LICENSE)
|
||||
[](https://dl.vikunja.io)
|
||||
[](https://hub.docker.com/r/vikunja/api/)
|
||||
[](https://try.vikunja.io/api/v1/docs)
|
||||
[](https://goreportcard.com/report/git.kolaente.de/vikunja/api)
|
||||
|
||||
# Vikunja API
|
||||
|
||||
> The Todo-app to organize your life.
|
||||
|
||||
[](https://drone.kolaente.de/vikunja/api)
|
||||
[](LICENSE)
|
||||
[](https://storage.kolaente.de/minio/vikunja/)
|
||||
[](https://hub.docker.com/r/vikunja/api/)
|
||||
[](https://try.vikunja.io/api/v1/swagger)
|
||||
[](https://goreportcard.com/report/git.kolaente.de/vikunja/api
|
||||
)
|
||||
# Table of contents
|
||||
|
||||
* [Features](#features)
|
||||
* [Docs](#docs)
|
||||
* [Roadmap](#roadmap)
|
||||
* [Contributing](#contributing)
|
||||
* [License](#license)
|
||||
|
||||
## Features
|
||||
|
||||
* Create TODO lists with tasks
|
||||
* Reminder for tasks
|
||||
* Namespaces: A "group" which bundels multiple lists
|
||||
* Share lists and namespaces with teams and users with granular permissions
|
||||
* Plenty of details for tasks
|
||||
|
||||
Try it under [try.vikunja.io](https://try.vikunja.io)!
|
||||
See [the features page](https://vikunja.io/en/features/) on our website for a more exaustive list or
|
||||
try it on [try.vikunja.io](https://try.vikunja.io)!
|
||||
|
||||
## Docs
|
||||
|
||||
* [Installing](https://vikunja.io/docs/installing/)
|
||||
* [Build from source](https://vikunja.io/docs/build-from-sources/)
|
||||
* [Development setup](https://vikunja.io/docs/development/)
|
||||
* [Magefile](https://vikunja.io/docs/mage/)
|
||||
* [Testing](https://vikunja.io/docs/testing/)
|
||||
|
||||
All docs can be found on [the vikunja home page](https://vikunja.io/docs/).
|
||||
|
||||
### Roadmap
|
||||
|
||||
* [ ] Labels for todo lists and tasks
|
||||
* [ ] Prioritize tasks
|
||||
* [ ] More sharing features
|
||||
* [ ] Share with individual users
|
||||
* [ ] Share via a world-readable link with or without password, like Nextcloud
|
||||
> I know, it's still a long way to go. I'm currently working on a lot of "basic" features, the exiting things will come later. Don't worry, they'll come.
|
||||
|
||||
* [ ] Mobile apps (seperate repo)
|
||||
* [ ] Webapp (seperate repo)
|
||||
* [ ] "Native" clients (will probably be something with electron)
|
||||
* [x] Prioritize tasks
|
||||
* [x] Subtasks
|
||||
* [x] Repeating tasks
|
||||
* [x] Get tasks via caldav
|
||||
* [x] Get all your tasks for an interval (day/month/period)
|
||||
* [x] Labels for tasks
|
||||
* [x] Assign users to tasks
|
||||
* [x] Attachments on tasks
|
||||
* [x] More sharing features
|
||||
* [x] Share with individual users
|
||||
* [x] Share via a world-readable link with or without password, like Nextcloud
|
||||
* [x] Disable registration, making an instance "invite-only"
|
||||
* [ ] SSE to notify multiple clients of updates when something was changed
|
||||
* [ ] "Smart Lists" - Create lists based on filters
|
||||
* [ ] IMAP-Integration - Send an email to Vikunja to create a new task
|
||||
* [ ] Webhooks - Trigger other events when an action is done (like completing a task)
|
||||
* [ ] Performace statistics - Get an overview and beautiful charts about what you got done this month
|
||||
* [ ] Activity feeds - Get a quick overview about who did what
|
||||
* [ ] Bulk-edit multiple tasks at once
|
||||
* [ ] Team-efforts - Requiring a task to be marked as done by multiple members until it's done
|
||||
* [ ] Global limits for namespaces/lists/tasks
|
||||
|
||||
## Development
|
||||
See [our roadmap](https://my.vikunja.cloud/share/QFyzYEmEYfSyQfTOmIRSwLUpkFjboaBqQCnaPmWd/auth) (hosted on Vikunja!) for even more!
|
||||
|
||||
To contribute to Vikunja, fork the project and work on the master branch.
|
||||
* [ ] [Mobile apps](https://code.vikunja.io/app) (seperate repo) *In Progress*
|
||||
* [ ] [Webapp](https://code.vikunja.io/frontend) (seperate repo) *In Progress*
|
||||
|
||||
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.
|
||||
## Contributing
|
||||
|
||||
```bash
|
||||
cd $GOPATH/src/code.vikunja.io/api
|
||||
```
|
||||
Fork -> Push -> Pull-Request. Also see the [dev docs](https://vikunja.io/docs/development/) for more infos.
|
||||
|
||||
To be able to create pull requests, the forked repository should be added as a remote to the Vikunja sources, otherwise changes can’t be pushed.
|
||||
## License
|
||||
|
||||
```bash
|
||||
git remote rename origin upstream
|
||||
git remote add origin git@git.kolaente.de:<USERNAME>/vikunja.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.
|
||||
|
||||
That’s 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.
|
||||
|
||||
12
REST-Tests/attachment_from_scratch.sh
Executable file
12
REST-Tests/attachment_from_scratch.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl -X POST http://localhost:3456/api/v1/register -H 'Content-Type: application/json' -d '{"username":"demo","password":"demo","email":"demo@vikunja.io"}'
|
||||
BEARER=`curl -X POST -H 'Content-Type: application/json' -d '{"username": "demo", "password":"demo"}' localhost:3456/api/v1/login | jq -r '.token'`
|
||||
|
||||
echo "Bearer: $BEARER"
|
||||
|
||||
curl -X POST localhost:3456/api/v1/tokenTest -H "Authorization: Bearer $BEARER"
|
||||
|
||||
curl -X PUT localhost:3456/api/v1/namespaces/1/lists -H 'Content-Type: application/json' -H "Authorization: Bearer $BEARER" -d '{"title":"lorem"}'
|
||||
curl -X PUT localhost:3456/api/v1/lists/1 -H 'Content-Type: application/json' -H "Authorization: Bearer $BEARER" -d '{"text":"lorem"}'
|
||||
curl -X PUT -H "Authorization: Bearer $BEARER" localhost:3456/api/v1/tasks/1/attachments -F 'files=@/home/konrad/Pictures/Wallpaper/greg-rakozy-_Q4mepyyjMw-unsplash.jpg'
|
||||
@@ -3,23 +3,27 @@ POST http://localhost:8080/api/v1/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"user": "user",
|
||||
"username": "user3",
|
||||
"password": "1234"
|
||||
}
|
||||
|
||||
> {% client.global.set("auth_token", response.body.token); %}
|
||||
|
||||
###
|
||||
|
||||
## Register
|
||||
### Register
|
||||
|
||||
POST http://localhost:8080/api/v1/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "user3",
|
||||
"username": "user",
|
||||
"password": "1234",
|
||||
"email": "3@knt.li"
|
||||
"email": "5@knt.li"
|
||||
}
|
||||
|
||||
###
|
||||
# Token test
|
||||
POST http://localhost:8080/api/v1/tokenTest
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
|
||||
70
REST-Tests/labels.http
Normal file
70
REST-Tests/labels.http
Normal file
@@ -0,0 +1,70 @@
|
||||
# Get all labels
|
||||
GET http://localhost:8080/api/v1/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Add a new label
|
||||
PUT http://localhost:8080/api/v1/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "test5"
|
||||
}
|
||||
|
||||
###
|
||||
# Delete a label
|
||||
DELETE http://localhost:8080/api/v1/labels/6
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Update a label
|
||||
POST http://localhost:8080/api/v1/labels/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "testschinkenbrot",
|
||||
"description": "käsebrot"
|
||||
}
|
||||
|
||||
###
|
||||
# Get one label
|
||||
GET http://localhost:8080/api/v1/labels/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Get all labels on a task
|
||||
GET http://localhost:8080/api/v1/tasks/3565/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Add a new label to a task
|
||||
PUT http://localhost:8080/api/v1/tasks/35236365/labels
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"label_id": 1
|
||||
}
|
||||
|
||||
###
|
||||
# Delete a label from a task
|
||||
DELETE http://localhost:8080/api/v1/tasks/3565/labels/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
# Add a new label to a task
|
||||
POST http://localhost:8080/api/v1/tasks/3565/labels/bulk
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"labels": [
|
||||
{"id": 1},
|
||||
{"id": 2},
|
||||
{"id": 3}
|
||||
]
|
||||
}
|
||||
|
||||
###
|
||||
@@ -1,35 +1,40 @@
|
||||
# Get all lists
|
||||
GET http://localhost:8080/api/v1/lists
|
||||
GET http://localhost:8080/api/v1/namespaces/35/lists
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get one list
|
||||
GET http://localhost:8080/api/v1/lists/2
|
||||
GET http://localhost:8080/api/v1/lists/3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Add a new list
|
||||
PUT http://localhost:8080/api/v1/namespaces/1/lists
|
||||
PUT http://localhost:8080/api/v1/namespaces/35/lists
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{}
|
||||
{
|
||||
"title": "test"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Add a new list
|
||||
# Add a new item
|
||||
PUT http://localhost:8080/api/v1/lists/1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"text": "test2", "description": "Schinken"}
|
||||
{
|
||||
"text": "Task",
|
||||
"description": "Schinken"
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Delete a list from a list
|
||||
DELETE http://localhost:8080/api/v1/lists/28
|
||||
# Delete a task from a list
|
||||
DELETE http://localhost:8080/api/v1/lists/14
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
@@ -77,11 +82,11 @@ Authorization: Bearer {{auth_token}}
|
||||
###
|
||||
|
||||
# Give a user access to that list
|
||||
PUT http://localhost:8080/api/v1/lists/30/users
|
||||
PUT http://localhost:8080/api/v1/lists/3/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{"user_id":3, "right":1}
|
||||
{"userID":"user4", "right":1}
|
||||
|
||||
###
|
||||
|
||||
@@ -98,4 +103,75 @@ Content-Type: application/json
|
||||
DELETE http://localhost:8080/api/v1/lists/28/users/3
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
###
|
||||
|
||||
# Get all pending tasks
|
||||
GET http://localhost:8080/api/v1/tasks/all
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks with priorities
|
||||
GET http://localhost:8080/api/v1/tasks/all?sort=priorityasc
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks in a range
|
||||
GET http://localhost:8080/api/v1/tasks/all/dueadateasc/1546784000/1548784000
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Get all pending tasks in caldav
|
||||
GET http://localhost:8080/api/v1/tasks/caldav
|
||||
#Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Update a task
|
||||
POST http://localhost:8080/api/v1/tasks/3565
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"priority": 0
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Bulk update multiple tasks at once
|
||||
POST http://localhost:8080/api/v1/tasks/bulk
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"task_ids": [3518,3519,3521],
|
||||
"text":"bulkupdated"
|
||||
}
|
||||
|
||||
###
|
||||
# Get all assignees
|
||||
GET http://localhost:8080/api/v1/tasks/3565/assignees
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
# Add a bunch of assignees
|
||||
PUT http://localhost:8080/api/v1/tasks/3565/assignees/bulk
|
||||
Authorization: Bearer {{auth_token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"assignees": [
|
||||
{"id": 17}
|
||||
]
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
# Get all users who have access to a list
|
||||
GET http://localhost:8080/api/v1/lists/3/users
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
@@ -5,7 +5,7 @@ Authorization: Bearer {{auth_token}}
|
||||
###
|
||||
|
||||
# Get one namespaces
|
||||
GET http://localhost:8080/api/v1/namespaces/125476
|
||||
GET http://localhost:8080/api/v1/namespaces/-1
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
###
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
# Get all users
|
||||
GET http://localhost:8080/api/v1/users
|
||||
GET http://localhost:8080/api/v1/user
|
||||
Authorization: Bearer {{auth_token}}
|
||||
|
||||
######
|
||||
@@ -21,4 +21,33 @@ Content-Type: application/json
|
||||
"new_password": "1234"
|
||||
}
|
||||
|
||||
###
|
||||
### Request a password to reset a password
|
||||
POST http://localhost:8080/api/v1/user/password/token
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"email": "k@knt.li"
|
||||
}
|
||||
|
||||
### Request a token to reset a password
|
||||
POST http://localhost:8080/api/v1/user/password/reset
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"token": "eAsZzakgqARnjzXHqsHqZtSUKuiOhoJjHANhgTxUIDBSalhbtdpAdLeywGXzVDBuRQGNpHdMxoHXhLVSlzpJsFvuoJgMdkhRhkNhaQXfufuZCdtUlerZHSJQLgYMUryHIxIREcmZLtWoZVrYyARkCvkyFhcGtoCwQOEjAOEZMQQuxTVoGYfAqcfNggQnerUcXCiRIgRtkusXSnltomhaeyRwAbrckXFeXxUjslgplSGqSTOqJTYuhrSzAVTwNvuYyvuXLaZoNnJEyeVDWlRydnxfgUQjQZOKwCBRWVQPKpZhlslLUyUAMsRQkHITkruQCjDnOGCCRsSNplbNCEuDmMfpWYHSQAcQIDZtbQWkxzpfmHDMQvvKPPrxEnrTErlvTfKDKICFYPQxXNpNE",
|
||||
"new_password": "1234"
|
||||
}
|
||||
|
||||
### Confirm a users email address
|
||||
|
||||
POST http://localhost:8080/api/v1/user/confirm
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
|
||||
{
|
||||
"token": ""
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
7
build/after-install.sh
Normal file
7
build/after-install.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fix the config to contain proper values
|
||||
NEW_SECRET=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
|
||||
sed -i "s/<jwt-secret>/$NEW_SECRET/g" /etc/vikunja/config.yml
|
||||
sed -i "s/<rootpath>/\/opt\/vikunja\//g" /etc/vikunja/config.yml
|
||||
sed -i "s/Path: \"\.\/vikunja.db\"/Path: \"\\/opt\/vikunja\/vikunja.db\"/g" /etc/vikunja/config.yml
|
||||
8
build/reprepro-dist-conf
Normal file
8
build/reprepro-dist-conf
Normal file
@@ -0,0 +1,8 @@
|
||||
Origin: dl.vikunja.io
|
||||
Label: Vikunja
|
||||
Codename: strech
|
||||
Architectures: amd64
|
||||
Components: main
|
||||
Description: The debian repo for Vikunja builds.
|
||||
SignWith: yes
|
||||
Pull: strech
|
||||
15
code-header-template.txt
Normal file
15
code-header-template.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
Vikunja is a to-do list application to facilitate your life.
|
||||
Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
@@ -2,12 +2,41 @@ service:
|
||||
# This token is used to verify issued JWT tokens.
|
||||
# Default is a random token which will be generated at each startup of vikunja.
|
||||
# (This means all already issued tokens will be invalid once you restart vikunja)
|
||||
JWTSecret: "cei6gaezoosah2bao3ieZohkae5aicah"
|
||||
JWTSecret: "<jwt-secret>"
|
||||
# The interface on which to run the webserver
|
||||
interface: ":3456"
|
||||
# The URL of the frontend, used to send password reset emails.
|
||||
frontendurl: ""
|
||||
# The base path on the file system where the binary and assets are.
|
||||
# Vikunja will also look in this path for a config file, so you could provide only this variable to point to a folder
|
||||
# with a config file which will then be used.
|
||||
rootpath: <rootpath>
|
||||
# The max number of items which can be returned per page
|
||||
maxitemsperpage: 50
|
||||
# If set to true, enables a /metrics endpoint for prometheus to collect metrics about the system
|
||||
# You'll need to use redis for this in order to enable common metrics over multiple nodes
|
||||
enablemetrics: false
|
||||
# Enable the caldav endpoint, see the docs for more details
|
||||
enablecaldav: true
|
||||
# Set the motd message, available from the /info endpoint
|
||||
motd: ""
|
||||
# Enable sharing of lists via a link
|
||||
enablelinksharing: true
|
||||
# Whether to let new users registering themselves or not
|
||||
enableregistration: true
|
||||
# Whether to enable task attachments or not
|
||||
enabletaskattachments: true
|
||||
# The time zone all timestamps are in
|
||||
timezone: GMT
|
||||
# Whether task comments should be enabled or not
|
||||
enabletaskcomments: true
|
||||
# Whether totp is enabled. In most cases you want to leave that enabled.
|
||||
enabletotp: true
|
||||
# If not empty, enables logging of crashes and unhandled errors in sentry.
|
||||
sentrydsn: ''
|
||||
|
||||
database:
|
||||
# Database type to use. Supported types are mysql and sqlite.
|
||||
# Database type to use. Supported types are mysql, postgres and sqlite.
|
||||
type: "sqlite"
|
||||
# Database user which is used to connect to the database.
|
||||
user: "vikunja"
|
||||
@@ -18,18 +47,171 @@ database:
|
||||
# Databse to use
|
||||
database: "vikunja"
|
||||
# When using sqlite, this is the path where to store the data
|
||||
Path: "./vikunja.db"
|
||||
# Whether to show mysql queries or not. Useful for debugging.
|
||||
showqueries: "false"
|
||||
path: "./vikunja.db"
|
||||
# Sets the max open connections to the database. Only used when using mysql and postgres.
|
||||
maxopenconnections: 100
|
||||
# Sets the maximum number of idle connections to the db.
|
||||
maxidleconnections: 50
|
||||
# The maximum lifetime of a single db connection in miliseconds.
|
||||
maxconnectionlifetime: 10000
|
||||
# Secure connection mode. Only used with postgres.
|
||||
# (see https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters)
|
||||
sslmode: disable
|
||||
|
||||
cache:
|
||||
# If cache is enabled or not
|
||||
enabled: false
|
||||
# Cache type. Possible values are memory or redis
|
||||
type: memory
|
||||
# Cache type. Possible values are "keyvalue", "memory" or "redis".
|
||||
# When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
|
||||
# When choosing "redis" you will need to configure the redis connection seperately.
|
||||
type: keyvalue
|
||||
# When using memory this defines the maximum size an element can take
|
||||
maxelementsize: 1000
|
||||
# When using redis, this is the host of the redis server including its port.
|
||||
redishost: 'localhost:6379'
|
||||
# When using redis, this is the password used to authenicate against the redis server
|
||||
redispassword: ''
|
||||
|
||||
redis:
|
||||
# Whether to enable redis or not
|
||||
enabled: false
|
||||
# The host of the redis server including its port.
|
||||
host: 'localhost:6379'
|
||||
# The password used to authenicate against the redis server
|
||||
password: ''
|
||||
# 0 means default database
|
||||
db: 0
|
||||
|
||||
cors:
|
||||
# Whether to enable or disable cors headers.
|
||||
# Note: If you want to put the frontend and the api on seperate domains or ports, you will need to enable this.
|
||||
# Otherwise the frontend won't be able to make requests to the api through the browser.
|
||||
enable: true
|
||||
# A list of origins which may access the api.
|
||||
origins:
|
||||
- "*"
|
||||
# How long (in seconds) the results of a preflight request can be cached.
|
||||
maxage: 0
|
||||
|
||||
mailer:
|
||||
# Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
|
||||
enabled: false
|
||||
# SMTP Host
|
||||
host: ""
|
||||
# SMTP Host port
|
||||
port: 587
|
||||
# SMTP username
|
||||
username: "user"
|
||||
# SMTP password
|
||||
password: ""
|
||||
# Wether to skip verification of the tls certificate on the server
|
||||
skiptlsverify: false
|
||||
# The default from address when sending emails
|
||||
fromemail: "mail@vikunja"
|
||||
# The length of the mail queue.
|
||||
queuelength: 100
|
||||
# The timeout in seconds after which the current open connection to the mailserver will be closed.
|
||||
queuetimeout: 30
|
||||
# By default, vikunja will try to connect with starttls, use this option to force it to use ssl.
|
||||
forcessl: false
|
||||
|
||||
log:
|
||||
# A folder where all the logfiles should go.
|
||||
path: <rootpath>logs
|
||||
# Whether to show any logging at all or none
|
||||
enabled: true
|
||||
# Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
standard: "stdout"
|
||||
# Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
level: "INFO"
|
||||
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
||||
database: "off"
|
||||
# The log level for database log messages. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
databaselevel: "WARNING"
|
||||
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||
http: "stdout"
|
||||
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
echo: "off"
|
||||
|
||||
ratelimit:
|
||||
# whether or not to enable the rate limit
|
||||
enabled: false
|
||||
# The kind on which rates are based. Can be either "user" for a rate limit per user or "ip" for an ip-based rate limit.
|
||||
kind: user
|
||||
# The time period in seconds for the limit
|
||||
period: 60
|
||||
# The max number of requests a user is allowed to do in the configured time period
|
||||
limit: 100
|
||||
# The store where the limit counter for each user is stored.
|
||||
# Possible values are "keyvalue", "memory" or "redis".
|
||||
# When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
|
||||
store: keyvalue
|
||||
|
||||
files:
|
||||
# The path where files are stored
|
||||
basepath: ./files # relative to the binary
|
||||
# The maximum size of a file, as a human-readable string.
|
||||
# Warning: The max size is limited 2^64-1 bytes due to the underlying datatype
|
||||
maxsize: 20MB
|
||||
|
||||
migration:
|
||||
# These are the settings for the wunderlist migrator
|
||||
wunderlist:
|
||||
# Wheter to enable the wunderlist migrator or not
|
||||
enable: false
|
||||
# The client id, required for making requests to the wunderlist api
|
||||
# You need to register your vikunja instance at https://developer.wunderlist.com/apps/new to get this
|
||||
clientid:
|
||||
# The client secret, also required for making requests to the wunderlist api
|
||||
clientsecret:
|
||||
# The url where clients are redirected after they authorized Vikunja to access their wunderlist stuff.
|
||||
# This needs to match the url you entered when registering your Vikunja instance at wunderlist.
|
||||
# This is usually the frontend url where the frontend then makes a request to /migration/wunderlist/migrate
|
||||
# with the code obtained from the wunderlist api.
|
||||
# Note that the vikunja frontend expects this to be /migrate/wunderlist
|
||||
redirecturl:
|
||||
todoist:
|
||||
# Wheter to enable the todoist migrator or not
|
||||
enable: false
|
||||
# The client id, required for making requests to the wunderlist api
|
||||
# You need to register your vikunja instance at https://developer.todoist.com/appconsole.html to get this
|
||||
clientid:
|
||||
# The client secret, also required for making requests to the todoist api
|
||||
clientsecret:
|
||||
# The url where clients are redirected after they authorized Vikunja to access their todoist items.
|
||||
# This needs to match the url you entered when registering your Vikunja instance at todoist.
|
||||
# This is usually the frontend url where the frontend then makes a request to /migration/todoist/migrate
|
||||
# with the code obtained from the todoist api.
|
||||
# Note that the vikunja frontend expects this to be /migrate/todoist
|
||||
redirecturl:
|
||||
|
||||
avatar:
|
||||
# When using gravatar, this is the duration in seconds until a cached gravatar user avatar expires
|
||||
gravatarexpiration: 3600
|
||||
|
||||
backgrounds:
|
||||
# Whether to enable backgrounds for lists at all.
|
||||
enabled: true
|
||||
providers:
|
||||
upload:
|
||||
# Whethere to enable uploaded list backgrounds
|
||||
enabled: true
|
||||
unsplash:
|
||||
# Whether to enable setting backgrounds from unsplash as list backgrounds
|
||||
enabled: false
|
||||
# You need to create an application for your installation at https://unsplash.com/oauth/applications/new
|
||||
# and set the access token below.
|
||||
accesstoken:
|
||||
# The unsplash application id is only used for pingback and required as per their api guidelines.
|
||||
# You can find the Application ID in the dashboard for your API application. It should be a numeric ID.
|
||||
# It will only show in the UI if your application has been approved for Enterprise usage, therefore if
|
||||
# you’re in Demo mode, you can also find the ID in the URL at the end: https://unsplash.com/oauth/applications/:application_id
|
||||
applicationid:
|
||||
|
||||
# Legal urls
|
||||
# Will be shown in the frontend if configured here
|
||||
legal:
|
||||
imprinturl:
|
||||
privacyurl:
|
||||
|
||||
# Key Value Storage settings
|
||||
# The Key Value Storage is used for different kinds of things like metrics and a few cache systems.
|
||||
keyvalue:
|
||||
# The type of the storage backend. Can be either "memory" or "redis". If "redis" is chosen it needs to be configured seperately.
|
||||
type: "memory"
|
||||
|
||||
18
docker-manifest.tmpl
Normal file
18
docker-manifest.tmpl
Normal file
@@ -0,0 +1,18 @@
|
||||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: vikunja/api:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
architecture: arm
|
||||
os: linux
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
/bin/true
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
/bin/true
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/execlineb -P
|
||||
/app/vikunja/vikunja
|
||||
3
docs/Dockerfile
Normal file
3
docs/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM nginx
|
||||
ADD public /usr/share/nginx/html/docs
|
||||
ADD nginx.conf /etc/nginx/conf.d/default.conf
|
||||
@@ -1,76 +0,0 @@
|
||||
# Architectural concepts
|
||||
|
||||
Vikunja was built with a maximum flexibility in mind while developing. To achive this, I built a set of easy-to-use
|
||||
functions and respective web handlers, all represented through interfaces.
|
||||
|
||||
## CRUDable
|
||||
|
||||
This interface defines methods to Create/Read/ReadAll/Update/Delete something. In order to use the common web
|
||||
handler, the struct must implement this and the `Rights` interface.
|
||||
|
||||
The interface is defined as followed:
|
||||
|
||||
```go
|
||||
type CRUDable interface {
|
||||
Create(*User) error
|
||||
ReadOne() error
|
||||
ReadAll(*User) (interface{}, error)
|
||||
Update() error
|
||||
Delete() error
|
||||
}
|
||||
```
|
||||
|
||||
Each of these methods is called on an instance of a struct like so:
|
||||
|
||||
```go
|
||||
func (l *List) ReadOne() (err error) {
|
||||
*l, err = GetListByID(l.ID)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
In that case, it takes the `ID` saved in the struct instance, gets the full list object and fills the original object with it.
|
||||
(See parambinder to understand where that `ID` is coming from).
|
||||
|
||||
All functions should behave like this, if they create or update something, they should return the created/updated struct
|
||||
instance. The only exception is `ReadAll()` which returns an interface. Usually this is an array, because, well you cannot
|
||||
make an array of a set type (If you know a way to do this, don't hesitate to drop me a message).
|
||||
|
||||
## Rights
|
||||
|
||||
This interface defines methods to check for rights on structs. They accept a `User` as parameter and usually return a `bool`.
|
||||
|
||||
The interface is defined as followed:
|
||||
|
||||
```go
|
||||
type Rights interface {
|
||||
IsAdmin(*User) bool
|
||||
CanWrite(*User) bool
|
||||
CanRead(*User) bool
|
||||
CanDelete(*User) bool
|
||||
CanUpdate(*User) bool
|
||||
CanCreate(*User) bool
|
||||
}
|
||||
```
|
||||
|
||||
When using the standard web handler, all methods except `CanRead()` are called before their `CRUD` counterparts. `CanRead()`
|
||||
is called after `ReadOne()` was invoked as this would otherwise mean getting an object from the db to check if the user has the
|
||||
right to see it and then getting it again if thats the case. Calling the function afterwards means we only have to get the
|
||||
object once.
|
||||
|
||||
## Standard web handler
|
||||
|
||||
## Errors
|
||||
|
||||
Error types with their messages and http-codes are set in `models/error.go`. If the error type implements `HTTPError`, the server returns a user-friendly error message when this error occours. This means it returns a good HTTP status code, a message, and an error code. The error code should be unique across all error codes and can be used on the client to show a localized error message or do other stuff based on the exact error the server returns. That way the client won't have to "guess" that the error message remains the same over multiple versions of Vikunja.
|
||||
|
||||
An `HTTPError` is defined as follows:
|
||||
|
||||
```go
|
||||
type HTTPError struct {
|
||||
HTTPCode int `json:"-"` // Can be any valid HTTP status code, I'd reccomend to use the constants of the http package.
|
||||
Code int `json:"code"` // Must be a uniqe int identifier for this specific error. I'd reccomend defining a constant for this.
|
||||
Message string `json:"message"` // A user-readable message what went wrong.
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
# Configuration options
|
||||
|
||||
You can either use a `config.yml` file in the root directory of vikunja or set all config option with
|
||||
environment variables. If you have both, the value set in the config file is used.
|
||||
|
||||
Variables are nested in the `config.yml`, these nested variables become `VIKUNJA_FIRST_CHILD` when configuring via
|
||||
environment variables. So setting
|
||||
|
||||
```bash
|
||||
export VIKUNJA_FIRST_CHILD=true
|
||||
```
|
||||
|
||||
is the same as defining it in a `config.yml` like so:
|
||||
|
||||
```yaml
|
||||
first:
|
||||
child: true
|
||||
```
|
||||
|
||||
# Default configuration with explanations
|
||||
|
||||
This is the same as the `config.yaml` file you'll find in the root of vikunja.
|
||||
|
||||
```yaml
|
||||
service:
|
||||
# This token is used to verify issued JWT tokens.
|
||||
# Default is a random token which will be generated at each startup of vikunja.
|
||||
# (This means all already issued tokens will be invalid once you restart vikunja)
|
||||
JWTSecret: "cei6gaezoosah2bao3ieZohkae5aicah"
|
||||
# The interface on which to run the webserver
|
||||
interface: ":3456"
|
||||
|
||||
database:
|
||||
# Database type to use. Supported types are mysql and sqlite.
|
||||
type: "sqlite"
|
||||
# Database user which is used to connect to the database.
|
||||
user: "vikunja"
|
||||
# Databse password
|
||||
password: ""
|
||||
# Databse host
|
||||
host: "localhost"
|
||||
# Databse to use
|
||||
database: "vikunja"
|
||||
# When using sqlite, this is the path where to store the data
|
||||
Path: "./vikunja.db"
|
||||
# Whether to show mysql queries or not. Useful for debugging.
|
||||
showqueries: "false"
|
||||
# Sets the max open connections to the database. Only used when using mysql.
|
||||
openconnections: 100
|
||||
|
||||
|
||||
cache:
|
||||
# If cache is enabled or not
|
||||
enabled: false
|
||||
# Cache type. Possible values are memory or redis
|
||||
type: memory
|
||||
# When using memory this defines the maximum size an element can take
|
||||
maxelementsize: 1000
|
||||
# When using redis, this is the host of the redis server including its port.
|
||||
redishost: 'localhost:6379'
|
||||
# When using redis, this is the password used to authenicate against the redis server
|
||||
redispassword: ''
|
||||
```
|
||||
47
docs/config.yml
Normal file
47
docs/config.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
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
|
||||
plausibleEnabled: true
|
||||
plausibleDomain: vikunja.io
|
||||
plausibleURL: https://analytics.kolaente.de
|
||||
|
||||
markup:
|
||||
goldmark:
|
||||
renderer:
|
||||
unsafe: true
|
||||
|
||||
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
|
||||
- name: Community
|
||||
url: https://community.vikunja.io/
|
||||
weight: 60
|
||||
25
docs/content/doc/_index.md
Normal file
25
docs/content/doc/_index.md
Normal 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]({{< ref "./setup/install.md">}})
|
||||
and [available configuration options]({{< ref "./setup/config.md">}}).
|
||||
|
||||
## Developing
|
||||
|
||||
If you want to start contributing to Vikunja, take a look at [the development docs]({{< ref "./development/development.md">}}).
|
||||
34
docs/content/doc/development/cli.md
Normal file
34
docs/content/doc/development/cli.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
date: "2019-03-31:00:00+01:00"
|
||||
title: "Adding new cli commands"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "development"
|
||||
---
|
||||
|
||||
# Adding new cli commands
|
||||
|
||||
All cli-related functions are located in `pkg/cmd`.
|
||||
Each cli command usually calls a function in another package.
|
||||
For example, the `vikunja migrate` command calls `migration.Migrate()`.
|
||||
|
||||
Vikunja uses the amazing [cobra](https://github.com/spf13/cobra) library for its cli.
|
||||
Please refer to its documentation for informations about how to use flags etc.
|
||||
|
||||
To add a new cli command, add something like the following:
|
||||
|
||||
{{< highlight golang >}}
|
||||
func init() {
|
||||
rootCmd.AddCommand(myCmd)
|
||||
}
|
||||
|
||||
var myCmd = &cobra.Command{
|
||||
Use: "My-command",
|
||||
Short: "A short description about your command.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Call other functions
|
||||
},
|
||||
}
|
||||
{{</ highlight >}}
|
||||
73
docs/content/doc/development/db-migrations.md
Normal file
73
docs/content/doc/development/db-migrations.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
date: "2019-03-29:00:00+02:00"
|
||||
title: "Database migrations"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "development"
|
||||
---
|
||||
|
||||
# Database Migrations
|
||||
|
||||
Vikunja runs all database migrations automatically on each start if needed.
|
||||
Additionally, they can also be run directly by using the `migrate` command.
|
||||
|
||||
We use [xormigrate](https://github.com/techknowlogick/xormigrate) to handle migrations,
|
||||
which is based on gormigrate.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Add a new migration
|
||||
|
||||
All migrations are stored in `pkg/migrations` and files should have the same name as their id.
|
||||
|
||||
Each migration should have a function to apply and roll it back, as well as a numeric id (the datetime)
|
||||
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.
|
||||
|
||||
When you're adding a new struct, you also need to add it to the `models.GetTables()` function
|
||||
to ensure it will be created on new installations.
|
||||
|
||||
### Example
|
||||
|
||||
{{< highlight golang >}}
|
||||
package migration
|
||||
|
||||
import (
|
||||
"github.com/go-xorm/xorm"
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
)
|
||||
|
||||
// Used for rollback
|
||||
type teamMembersMigration20190328074430 struct {
|
||||
Updated int64 `xorm:"updated"`
|
||||
}
|
||||
|
||||
func (teamMembersMigration20190328074430) TableName() string {
|
||||
return "team_members"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20190328074430",
|
||||
Description: "Remove updated from team_members",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return dropTableColum(tx, "team_members", "updated")
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(teamMembersMigration20190328074430{})
|
||||
},
|
||||
})
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
You should always copy the changed parts of the struct you're changing when adding migraitons.
|
||||
67
docs/content/doc/development/development.md
Normal file
67
docs/content/doc/development/development.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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 Magefile, so make sure to [take a look at it]({{< ref "mage.md">}}).
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## 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.
|
||||
|
||||
#### 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 can’t 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 Magefile to get an overview about
|
||||
the available tasks. The most common tasks should be `mage test:unit` which will start our test environment and `mage build:build`
|
||||
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.
|
||||
|
||||
That’s it! You are ready to hack on Vikunja. Test changes, push them to the repository, and open a pull request.
|
||||
|
||||
## Static assets
|
||||
|
||||
Each Vikunja release contains all static assets directly compiled into the binary.
|
||||
To prevent this during development, use the `dev` tag when developing.
|
||||
|
||||
See the [mage docs](mage.md#statically-compile-all-templates-into-the-binary) about how to compile with static assets for a release.
|
||||
192
docs/content/doc/development/mage.md
Normal file
192
docs/content/doc/development/mage.md
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Magefile"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "development"
|
||||
---
|
||||
|
||||
# Mage
|
||||
|
||||
Vikunja uses [Mage](https://magefile.org/) to script common development tasks and even releasing.
|
||||
Mage is a pure go solution which allows for greater flexibility and things like better paralelization.
|
||||
|
||||
This document explains what taks are available and what they do.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Installation
|
||||
|
||||
To use mage, you'll need to install the mage cli.
|
||||
To install it, run the following command:
|
||||
|
||||
```
|
||||
go install github.com/magefile/mage
|
||||
```
|
||||
|
||||
## Categories
|
||||
|
||||
There are multiple categories of subcommands in the magefile:
|
||||
|
||||
* `build`: Contains commands to build a single binary
|
||||
* `check`: Contains commands to statically check the source code
|
||||
* `release`: Contains commands to release Vikunja with everything that's required
|
||||
* `test`: Contains commands to run all kinds of tests
|
||||
* `dev`: Contains commands to run development tasks
|
||||
* `misc`: Commands which do not belong in either of the other categories
|
||||
|
||||
## CI
|
||||
|
||||
These tasks are automatically run in our CI every time someone pushes to master or you update a pull request:
|
||||
|
||||
* `mage check:lint`
|
||||
* `mage check:fmt`
|
||||
* `mage check:ineffassign`
|
||||
* `mage check:misspell`
|
||||
* `mage check:goconst`
|
||||
* `mage build:generate`
|
||||
* `mage build:build`
|
||||
|
||||
## Build
|
||||
|
||||
### Build Vikunja
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage build:build
|
||||
{{< /highlight >}}
|
||||
|
||||
Builds a `vikunja`-binary in the root directory of the repo for the platform it is run on.
|
||||
|
||||
### Statically compile all templates into the binary
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage build:generate
|
||||
{{< /highlight >}}
|
||||
|
||||
This generates static code with all templates, meaning no template need to be referenced at runtime.
|
||||
|
||||
### clean
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage build:clean
|
||||
{{< /highlight >}}
|
||||
|
||||
Cleans all build, executable and bindata files
|
||||
|
||||
## Check
|
||||
|
||||
All check sub-commands exit with a status code of 1 if the check fails.
|
||||
|
||||
Various code-checks are available:
|
||||
|
||||
* `mage check:all`: Runs fmt-check, lint, got-swag, misspell-check, ineffasign-check, gocyclo-check, static-check, gosec-check, goconst-check all in parallel
|
||||
* `mage check:fmt`: Checks if the code is properly formatted with go fmt
|
||||
* `mage check:go-sec`: Checks the source code for potential security issues by scanning the Go AST using the [gosec tool](https://github.com/securego/gosec)
|
||||
* `mage check:goconst`: Checks for repeated strings that could be replaced by a constant using [goconst](https://github.com/jgautheron/goconst/)
|
||||
* `mage check:gocyclo`: Checks for the cyclomatic complexity of the source code using [gocyclo](https://github.com/fzipp/gocyclo)
|
||||
* `mage check:got-swag`: Checks if the swagger docs need to be re-generated from the code annotations
|
||||
* `mage check:ineffassign`: Checks the source code for ineffectual assigns using [ineffassign](https://github.com/gordonklaus/ineffassign)
|
||||
* `mage check:lint`: Runs golint on all packages
|
||||
* `mage check:misspell`: Checks the source code for misspellings
|
||||
* `mage check:static`: Statically analyzes the source code about a range of different problems using [staticcheck](https://staticcheck.io/docs/)
|
||||
|
||||
## Release
|
||||
|
||||
### Build Releases
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage release
|
||||
{{< /highlight >}}
|
||||
|
||||
Builds binaries for all platforms and zips them with a copy of the `templates/` folder.
|
||||
All built zip files are stored into `dist/zips/`. Binaries are stored in `dist/binaries/`,
|
||||
binaries bundled with `templates` are stored in `dist/releases/`.
|
||||
|
||||
All cross-platform binaries built using this series of commands are built with the help of
|
||||
[xgo](https://github.com/techknowlogick/xgo). The mage command will automatically install the
|
||||
binary to be able to use it.
|
||||
|
||||
`mage release:release` is a shortcut to execute `mage release:dirs release:windows release:linux release:darwin release:copy release:check release:os-package release:zip`.
|
||||
|
||||
* `mage release:dirs` creates all directories needed
|
||||
* `mage release:windows`/`release:linux`/`release:darwin` execute xgo to build for their respective platforms
|
||||
* `mage release:copy` bundles binaries with a copy of the `LICENSE` and sample config files to then be zipped
|
||||
* `mage release:check` creates sha256 checksums for each binary which will be included in the zip file
|
||||
* `mage release:os-package` bundles a binary with the `sha256` checksum file, a sample `config.yml` and a copy of the license in a folder for each architecture
|
||||
* `mage release:compress` compresses all build binaries with `upx` to save space
|
||||
* `mage release:zip` paclages a zip file for the files created by `release:os-package`
|
||||
|
||||
### Build os packages
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage release:packages
|
||||
{{< /highlight >}}
|
||||
|
||||
Will build `.deb`, `.rpm` and `.apk` packages to `dist/os-packages`.
|
||||
|
||||
### Make a debian repo
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage release:reprepro
|
||||
{{< /highlight >}}
|
||||
|
||||
Takes an already built debian package and creates a debian repo structure around it.
|
||||
|
||||
Used to be run inside a [docker container](https://git.kolaente.de/konrad/reprepro-docker) in the CI process when releasing.
|
||||
|
||||
## Test
|
||||
|
||||
### unit
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage test:unit
|
||||
{{< /highlight >}}
|
||||
|
||||
Runs all tests except integration tests.
|
||||
|
||||
### coverage
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage test:coverage
|
||||
{{< /highlight >}}
|
||||
|
||||
Runs all tests except integration tests and generates a `coverage.html` file to inspect the code coverage.
|
||||
|
||||
### integration
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage test:integration
|
||||
{{< /highlight >}}
|
||||
|
||||
Runs all integration tests.
|
||||
|
||||
## Dev
|
||||
|
||||
### Create a new migration
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage dev:create-migration
|
||||
{{< /highlight >}}
|
||||
|
||||
Creates a new migration with the current date.
|
||||
Will ask for the name of the struct you want to create a migration for.
|
||||
|
||||
## Misc
|
||||
|
||||
### Format the code
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage fmt
|
||||
{{< /highlight >}}
|
||||
|
||||
Formats all source code using `go fmt`.
|
||||
|
||||
### Generate swagger definitions from code comments
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage do-the-swag
|
||||
{{< /highlight >}}
|
||||
|
||||
Generates swagger definitions from the comment annotations in the code.
|
||||
98
docs/content/doc/development/migration.md
Normal file
98
docs/content/doc/development/migration.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
date: "2020-01-19:16:00+02:00"
|
||||
title: "Migrations"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "development"
|
||||
---
|
||||
|
||||
# Writing a migrator for Vikunja
|
||||
|
||||
It is possible to migrate data from other to-do services to Vikunja.
|
||||
To make this easier, we have put together a few helpers which are documented on this page.
|
||||
|
||||
In general, each migrator implements a migrator interface which is then called from a client.
|
||||
The interface makes it possible to use helper methods which handle http an focus only on the implementation of the migrator itself.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Structure
|
||||
|
||||
All migrator implementations live in their own package in `pkg/modules/migration/<name-of-the-service>`.
|
||||
When creating a new migrator, you should place all related code inside that module.
|
||||
|
||||
## Migrator interface
|
||||
|
||||
The migrator interface is defined as follows:
|
||||
|
||||
```go
|
||||
// Migrator is the basic migrator interface which is shared among all migrators
|
||||
type Migrator interface {
|
||||
// Migrate is the interface used to migrate a user's tasks from another platform to vikunja.
|
||||
// The user object is the user who's tasks will be migrated.
|
||||
Migrate(user *models.User) error
|
||||
// AuthURL returns a url for clients to authenticate against.
|
||||
// The use case for this are Oauth flows, where the server token should remain hidden and not
|
||||
// known to the frontend.
|
||||
AuthURL() string
|
||||
// Name holds the name of the migration.
|
||||
// This is used to show the name to users and to keep track of users who already migrated.
|
||||
Name() string
|
||||
}
|
||||
```
|
||||
|
||||
## Defining http routes
|
||||
|
||||
Once your migrator implements the migration interface, it becomes possible to use the helper http handlers.
|
||||
Their usage is very similar to the [general web handler](https://kolaente.dev/vikunja/web#user-content-defining-routes-using-the-standard-web-handler):
|
||||
|
||||
The `RegisterRoutes(m)` method registers all routes with the scheme `/[MigratorName]/(auth|migrate|status)` for the
|
||||
authUrl, Status and Migrate methods.
|
||||
|
||||
```go
|
||||
// This is an example for the Wunderlist migrator
|
||||
if config.MigrationWunderlistEnable.GetBool() {
|
||||
wunderlistMigrationHandler := &migrationHandler.MigrationWeb{
|
||||
MigrationStruct: func() migration.Migrator {
|
||||
return &wunderlist.Migration{}
|
||||
},
|
||||
}
|
||||
wunderlistMigrationHandler.RegisterRoutes(m)
|
||||
}
|
||||
```
|
||||
|
||||
You should also document the routes with [swagger annotations]({{< ref "../practical-instructions/swagger-docs.md" >}}).
|
||||
|
||||
## Insertion helper method
|
||||
|
||||
There is a method available in the `migration` package which takes a fully nested Vikunja structure and creates it with all relations.
|
||||
This means you start by adding a namespace, then add lists inside of that namespace, then tasks in the lists and so on.
|
||||
|
||||
The root structure must be present as `[]*models.NamespaceWithLists`.
|
||||
|
||||
Then call the method like so:
|
||||
|
||||
```go
|
||||
fullVikunjaHierachie, err := convertWunderlistToVikunja(wContent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = migration.InsertFromStructure(fullVikunjaHierachie, user)
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
You should add at least an option to enable or disable the migration.
|
||||
Chances are, you'll need some more options for things like client ID and secret
|
||||
(if the other service uses oAuth as an authentication flow).
|
||||
|
||||
The easiest way to implement an on/off switch is to check whether your migration service is enabled or not when
|
||||
registering the routes, and then simply don't registering the routes in the case it is disabled.
|
||||
|
||||
### Making the migrator public in `/info`
|
||||
|
||||
You should make your migrator available in the `/info` endpoint so that frontends can display options to enable them or not.
|
||||
To do this, add an entry to `pkg/routes/api/v1/info.go`.
|
||||
201
docs/content/doc/development/structure.md
Normal file
201
docs/content/doc/development/structure.md
Normal file
@@ -0,0 +1,201 @@
|
||||
---
|
||||
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`
|
||||
* `cmd`
|
||||
* `config`
|
||||
* `db`
|
||||
* `fixtures`
|
||||
* `files`
|
||||
* `integration`
|
||||
* `log`
|
||||
* `mail`
|
||||
* `metrics`
|
||||
* `migration`
|
||||
* `models`
|
||||
* `modules`
|
||||
* `migration`
|
||||
* `handler`
|
||||
* `wunderlist`
|
||||
* `red`
|
||||
* `routes`
|
||||
* `api/v1`
|
||||
* `static`
|
||||
* `swagger`
|
||||
* `user`
|
||||
* `utils`
|
||||
* `version`
|
||||
* `REST-Tests`
|
||||
* `templates`
|
||||
* `vendor`
|
||||
|
||||
This document will explain what these mean and what you can find where.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Root level
|
||||
|
||||
The root directory is where [the config file]({{< ref "../setup/config.md">}}), [Magefile]({{< ref "mage.md">}}), license, drone config,
|
||||
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.
|
||||
|
||||
### cmd
|
||||
|
||||
This package contains all cli-related files and functions.
|
||||
|
||||
To learn more about how to add a new command, see [the cli docs]({{< ref "cli.md">}}).
|
||||
|
||||
To learn more about how to use this cli, see [the cli usage docs]({{< ref "../usage/cli.md">}}).
|
||||
|
||||
### 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.
|
||||
|
||||
### db
|
||||
|
||||
This package contains the db connection handling and db fixtures for testing.
|
||||
Each other package gets its db connection object from this package.
|
||||
|
||||
### files
|
||||
|
||||
This package is responsible for all file-related things.
|
||||
This means it handles saving and retrieving files from the db and the underlying file system.
|
||||
|
||||
### integration
|
||||
|
||||
All integration tests live here.
|
||||
See [integration tests]({{< ref "test.md" >}}#integration-tests) for more details.
|
||||
|
||||
### log
|
||||
|
||||
Similar to `config`, this will set up the logging, based on differen logging backends.
|
||||
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">}}).
|
||||
|
||||
### migration
|
||||
|
||||
This package handles all migrations.
|
||||
All migrations are stored and executed here.
|
||||
|
||||
To learn more, take a look at the [migrations docs]({{< ref "../development/db-migrations.md">}}).
|
||||
|
||||
### 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">}})
|
||||
|
||||
### modules
|
||||
|
||||
#### migration
|
||||
|
||||
See [writing a migrator]({{< ref "migration.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.
|
||||
|
||||
### static
|
||||
|
||||
All static files generated by `mage generate` live here.
|
||||
|
||||
### swagger
|
||||
|
||||
This is where the [generated]({{< ref "mage.md#generate-swagger-definitions-from-code-comments">}} [api docs]({{< ref "../usage/api.md">}}) live.
|
||||
You usually don't need to touch this package.
|
||||
|
||||
### user
|
||||
|
||||
All user-related things like registration etc. live in this package.
|
||||
|
||||
### utils
|
||||
|
||||
A small package, containing some helper functions:
|
||||
|
||||
* `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.
|
||||
|
||||
### version
|
||||
|
||||
The single purpouse of this package is to hold the current vikunja version which gets overridden through build flags
|
||||
each time `mage release` or `mage build` is run.
|
||||
It is a seperate package to avoid import cycles with other packages.
|
||||
|
||||
## REST-Tests
|
||||
|
||||
Holds all kinds of test files to directly test the api from inside of [jetbrains ide's](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html).
|
||||
|
||||
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.
|
||||
72
docs/content/doc/development/test.md
Normal file
72
docs/content/doc/development/test.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
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 `Magefile`]({{< ref "mage.md">}}) with
|
||||
|
||||
{{< highlight bash >}}
|
||||
mage test:unit
|
||||
{{< /highlight >}}
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Running tests with config
|
||||
|
||||
You can run tests with all available config variables if you want, enabeling you to run tests for a lot of scenarios.
|
||||
|
||||
To use the normal config set the enviroment variable `VIKUNJA_TESTS_USE_CONFIG=1`.
|
||||
|
||||
## Show sql queries
|
||||
|
||||
When `UNIT_TESTS_VERBOSE=1` is set, all sql queries will be shown when tests are run.
|
||||
|
||||
## Fixtures
|
||||
|
||||
All tests are run against a set of db fixtures.
|
||||
These fixtures are defined in `pkg/models/fixtures` in YAML-Files which represent the database structure.
|
||||
|
||||
When you add a new test case which requires new database entries to test against, update these files.
|
||||
|
||||
## Integration tests
|
||||
|
||||
All integration tests live in `pkg/integrations`.
|
||||
You can run them by executing `mage test:integration`.
|
||||
|
||||
The integration tests use the same config and fixtures as the unit tests and therefor have the same options available,
|
||||
see at the beginning of this document.
|
||||
|
||||
To run integration tests, use `mage test:integration`.
|
||||
|
||||
## Initializing db fixtures when writing tests
|
||||
|
||||
All db fixtures for all tests live in the `pkg/db/fixtures/` folder as yaml files.
|
||||
Each file has the same name as the table the fixtures are for.
|
||||
You should put new fixtures in this folder.
|
||||
|
||||
When initializing db fixtures, you are responsible for defining which tables your package needs in your test init function.
|
||||
Usually, this is done as follows (this code snippet is taken from the `user` package):
|
||||
|
||||
```go
|
||||
err = db.InitTestFixtures("users")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
In your actual tests, you then load the fixtures into the in-memory db like so:
|
||||
|
||||
```go
|
||||
db.LoadAndAssertFixtures(t)
|
||||
```
|
||||
|
||||
This will load all fixtures you defined in your test init method.
|
||||
You should always use this method to load fixtures, the only exception is when your package tests require extra test
|
||||
fixtures other than db fixtures (like files).
|
||||
40
docs/content/doc/practical-instructions/database.md
Normal file
40
docs/content/doc/practical-instructions/database.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
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.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## 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.
|
||||
72
docs/content/doc/practical-instructions/errors.md
Normal file
72
docs/content/doc/practical-instructions/errors.md
Normal 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 >}}
|
||||
33
docs/content/doc/practical-instructions/feature.md
Normal file
33
docs/content/doc/practical-instructions/feature.md
Normal 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, itemsPerPage)).Find(&lists)
|
||||
{{< /highlight >}}
|
||||
|
||||
// TODO: Add a full example from start to finish, like a tutorial on how to create a new endpoint?
|
||||
86
docs/content/doc/practical-instructions/mail.md
Normal file
86
docs/content/doc/practical-instructions/mail.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
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.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## 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).
|
||||
|
||||
48
docs/content/doc/practical-instructions/metrics.md
Normal file
48
docs/content/doc/practical-instructions/metrics.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
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.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## 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).
|
||||
@@ -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.
|
||||
47
docs/content/doc/practical-instructions/swagger-docs.md
Normal file
47
docs/content/doc/practical-instructions/swagger-docs.md
Normal 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 >}}
|
||||
54
docs/content/doc/setup/backups.md
Normal file
54
docs/content/doc/setup/backups.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
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.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## 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 >}}
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
To create a backup from PostgreSQL use the `pg_dump` command:
|
||||
|
||||
{{< highlight bash >}}
|
||||
pg_dump -U <user> -h <db-host> <database> > vikunja-backup.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
You might be prompted for the password of the database user.
|
||||
|
||||
To restore it, simply pipe it back into the `psql` command:
|
||||
|
||||
{{< highlight bash >}}
|
||||
psql -U <user> -h <db-host> <database> < vikunja-backup.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
For more information, please visit the [relevant PostgreSQL documentation](https://www.postgresql.org/docs/12/backup-dump.html).
|
||||
|
||||
## SQLite
|
||||
|
||||
To backup sqllite databases, it is enough to copy the database elsewhere.
|
||||
29
docs/content/doc/setup/build-from-source.md
Normal file
29
docs/content/doc/setup/build-from-source.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
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 [Mage](https://magefile) is properly installed on your system.
|
||||
3. Clone the repo with `git clone https://code.vikunja.io/api`
|
||||
3. Run `mage build:build` in the source of this repo. This will build a binary in the root of the repo which will be able to run on your system.
|
||||
|
||||
*Note:* Static ressources such as email templates are built into the binary.
|
||||
For these to work, you may need to run `mage build:generate` before building the vikunja binary.
|
||||
When builing entirely with `mage`, you dont need to do this, `mage build:generate` will be run automatically when running `mage build:build`.
|
||||
|
||||
# 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}`.
|
||||
|
||||
More options are available, please refer to the [magefile docs]({{< ref "../development/mage.md">}}) for more details.
|
||||
565
docs/content/doc/setup/config.md
Normal file
565
docs/content/doc/setup/config.md
Normal file
@@ -0,0 +1,565 @@
|
||||
---
|
||||
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
|
||||
environment variables. If you have both, the value set in the config file is used.
|
||||
|
||||
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 >}}
|
||||
first:
|
||||
child: true
|
||||
{{< /highlight >}}
|
||||
|
||||
# Formats
|
||||
|
||||
Vikunja supports using `toml`, `yaml`, `hcl`, `ini`, `json`, envfile, env variables and Java Properties files.
|
||||
We reccomend yaml or toml, but you're free to use whatever you want.
|
||||
|
||||
Vikunja provides a default [`config.yml`](https://kolaente.dev/vikunja/api/src/branch/master/config.yml.sample) file which you can use as a starting point.
|
||||
|
||||
# Config file locations
|
||||
|
||||
Vikunja will search on various places for a config file:
|
||||
|
||||
* Next to the location of the binary
|
||||
* In the `service.rootpath` location set in a config (remember you can set config arguments via environment variables)
|
||||
* In `/etc/vikunja`
|
||||
* In `~/.config/vikunja`
|
||||
|
||||
# Default configuration with explanations
|
||||
|
||||
The following explains all possible config variables and their defaults.
|
||||
You can find a full example configuration file in [here](https://code.vikunja.io/api/src/branch/master/config.yml.sample).
|
||||
|
||||
If you don't provide a value in your config file, their default will be used.
|
||||
|
||||
## Nesting
|
||||
|
||||
Most config variables are nested under some "higher-level" key.
|
||||
For example, the `interface` config variable is a child of the `service` key.
|
||||
|
||||
The docs below aim to reflect that leveling, but please also have a lookt at [the default config](https://code.vikunja.io/api/src/branch/master/config.yml.sample) file
|
||||
to better grasp how the nesting looks like.
|
||||
|
||||
<!-- Generated config will be injected here -->
|
||||
|
||||
---
|
||||
|
||||
## service
|
||||
|
||||
|
||||
|
||||
### JWTSecret
|
||||
|
||||
This token is used to verify issued JWT tokens.
|
||||
Default is a random token which will be generated at each startup of vikunja.
|
||||
(This means all already issued tokens will be invalid once you restart vikunja)
|
||||
|
||||
Default: `<jwt-secret>`
|
||||
|
||||
### interface
|
||||
|
||||
The interface on which to run the webserver
|
||||
|
||||
Default: `:3456`
|
||||
|
||||
### frontendurl
|
||||
|
||||
The URL of the frontend, used to send password reset emails.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### rootpath
|
||||
|
||||
The base path on the file system where the binary and assets are.
|
||||
Vikunja will also look in this path for a config file, so you could provide only this variable to point to a folder
|
||||
with a config file which will then be used.
|
||||
|
||||
Default: `<rootpath>`
|
||||
|
||||
### maxitemsperpage
|
||||
|
||||
The max number of items which can be returned per page
|
||||
|
||||
Default: `50`
|
||||
|
||||
### enablemetrics
|
||||
|
||||
If set to true, enables a /metrics endpoint for prometheus to collect metrics about the system
|
||||
You'll need to use redis for this in order to enable common metrics over multiple nodes
|
||||
|
||||
Default: `false`
|
||||
|
||||
### enablecaldav
|
||||
|
||||
Enable the caldav endpoint, see the docs for more details
|
||||
|
||||
Default: `true`
|
||||
|
||||
### motd
|
||||
|
||||
Set the motd message, available from the /info endpoint
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### enablelinksharing
|
||||
|
||||
Enable sharing of lists via a link
|
||||
|
||||
Default: `true`
|
||||
|
||||
### enableregistration
|
||||
|
||||
Whether to let new users registering themselves or not
|
||||
|
||||
Default: `true`
|
||||
|
||||
### enabletaskattachments
|
||||
|
||||
Whether to enable task attachments or not
|
||||
|
||||
Default: `true`
|
||||
|
||||
### timezone
|
||||
|
||||
The time zone all timestamps are in
|
||||
|
||||
Default: `GMT`
|
||||
|
||||
### enabletaskcomments
|
||||
|
||||
Whether task comments should be enabled or not
|
||||
|
||||
Default: `true`
|
||||
|
||||
### enabletotp
|
||||
|
||||
Whether totp is enabled. In most cases you want to leave that enabled.
|
||||
|
||||
Default: `true`
|
||||
|
||||
### sentrydsn
|
||||
|
||||
If not empty, enables logging of crashes and unhandled errors in sentry.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
---
|
||||
|
||||
## database
|
||||
|
||||
|
||||
|
||||
### type
|
||||
|
||||
Database type to use. Supported types are mysql, postgres and sqlite.
|
||||
|
||||
Default: `sqlite`
|
||||
|
||||
### user
|
||||
|
||||
Database user which is used to connect to the database.
|
||||
|
||||
Default: `vikunja`
|
||||
|
||||
### password
|
||||
|
||||
Databse password
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### host
|
||||
|
||||
Databse host
|
||||
|
||||
Default: `localhost`
|
||||
|
||||
### database
|
||||
|
||||
Databse to use
|
||||
|
||||
Default: `vikunja`
|
||||
|
||||
### path
|
||||
|
||||
When using sqlite, this is the path where to store the data
|
||||
|
||||
Default: `./vikunja.db`
|
||||
|
||||
### maxopenconnections
|
||||
|
||||
Sets the max open connections to the database. Only used when using mysql and postgres.
|
||||
|
||||
Default: `100`
|
||||
|
||||
### maxidleconnections
|
||||
|
||||
Sets the maximum number of idle connections to the db.
|
||||
|
||||
Default: `50`
|
||||
|
||||
### maxconnectionlifetime
|
||||
|
||||
The maximum lifetime of a single db connection in miliseconds.
|
||||
|
||||
Default: `10000`
|
||||
|
||||
### sslmode
|
||||
|
||||
Secure connection mode. Only used with postgres.
|
||||
(see https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters)
|
||||
|
||||
Default: `disable`
|
||||
|
||||
---
|
||||
|
||||
## cache
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
If cache is enabled or not
|
||||
|
||||
Default: `false`
|
||||
|
||||
### type
|
||||
|
||||
Cache type. Possible values are "keyvalue", "memory" or "redis".
|
||||
When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
|
||||
When choosing "redis" you will need to configure the redis connection seperately.
|
||||
|
||||
Default: `keyvalue`
|
||||
|
||||
### maxelementsize
|
||||
|
||||
When using memory this defines the maximum size an element can take
|
||||
|
||||
Default: `1000`
|
||||
|
||||
---
|
||||
|
||||
## redis
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
Whether to enable redis or not
|
||||
|
||||
Default: `false`
|
||||
|
||||
### host
|
||||
|
||||
The host of the redis server including its port.
|
||||
|
||||
Default: `localhost:6379`
|
||||
|
||||
### password
|
||||
|
||||
The password used to authenicate against the redis server
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### db
|
||||
|
||||
0 means default database
|
||||
|
||||
Default: `0`
|
||||
|
||||
---
|
||||
|
||||
## cors
|
||||
|
||||
|
||||
|
||||
### enable
|
||||
|
||||
Whether to enable or disable cors headers.
|
||||
Note: If you want to put the frontend and the api on seperate domains or ports, you will need to enable this.
|
||||
Otherwise the frontend won't be able to make requests to the api through the browser.
|
||||
|
||||
Default: `true`
|
||||
|
||||
### origins
|
||||
|
||||
A list of origins which may access the api.
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### maxage
|
||||
|
||||
How long (in seconds) the results of a preflight request can be cached.
|
||||
|
||||
Default: `0`
|
||||
|
||||
---
|
||||
|
||||
## mailer
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
Whether to enable the mailer or not. If it is disabled, all users are enabled right away and password reset is not possible.
|
||||
|
||||
Default: `false`
|
||||
|
||||
### host
|
||||
|
||||
SMTP Host
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### port
|
||||
|
||||
SMTP Host port
|
||||
|
||||
Default: `587`
|
||||
|
||||
### username
|
||||
|
||||
SMTP username
|
||||
|
||||
Default: `user`
|
||||
|
||||
### password
|
||||
|
||||
SMTP password
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### skiptlsverify
|
||||
|
||||
Wether to skip verification of the tls certificate on the server
|
||||
|
||||
Default: `false`
|
||||
|
||||
### fromemail
|
||||
|
||||
The default from address when sending emails
|
||||
|
||||
Default: `mail@vikunja`
|
||||
|
||||
### queuelength
|
||||
|
||||
The length of the mail queue.
|
||||
|
||||
Default: `100`
|
||||
|
||||
### queuetimeout
|
||||
|
||||
The timeout in seconds after which the current open connection to the mailserver will be closed.
|
||||
|
||||
Default: `30`
|
||||
|
||||
### forcessl
|
||||
|
||||
By default, vikunja will try to connect with starttls, use this option to force it to use ssl.
|
||||
|
||||
Default: `false`
|
||||
|
||||
---
|
||||
|
||||
## log
|
||||
|
||||
|
||||
|
||||
### path
|
||||
|
||||
A folder where all the logfiles should go.
|
||||
|
||||
Default: `<rootpath>logs`
|
||||
|
||||
### enabled
|
||||
|
||||
Whether to show any logging at all or none
|
||||
|
||||
Default: `true`
|
||||
|
||||
### standard
|
||||
|
||||
Where the normal log should go. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
|
||||
Default: `stdout`
|
||||
|
||||
### level
|
||||
|
||||
Change the log level. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
|
||||
Default: `INFO`
|
||||
|
||||
### database
|
||||
|
||||
Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
||||
|
||||
Default: `off`
|
||||
|
||||
### databaselevel
|
||||
|
||||
The log level for database log messages. Possible values (case-insensitive) are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||
|
||||
Default: `WARNING`
|
||||
|
||||
### http
|
||||
|
||||
Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||
|
||||
Default: `stdout`
|
||||
|
||||
### echo
|
||||
|
||||
Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||
|
||||
Default: `off`
|
||||
|
||||
---
|
||||
|
||||
## ratelimit
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
whether or not to enable the rate limit
|
||||
|
||||
Default: `false`
|
||||
|
||||
### kind
|
||||
|
||||
The kind on which rates are based. Can be either "user" for a rate limit per user or "ip" for an ip-based rate limit.
|
||||
|
||||
Default: `user`
|
||||
|
||||
### period
|
||||
|
||||
The time period in seconds for the limit
|
||||
|
||||
Default: `60`
|
||||
|
||||
### limit
|
||||
|
||||
The max number of requests a user is allowed to do in the configured time period
|
||||
|
||||
Default: `100`
|
||||
|
||||
### store
|
||||
|
||||
The store where the limit counter for each user is stored.
|
||||
Possible values are "keyvalue", "memory" or "redis".
|
||||
When choosing "keyvalue" this setting follows the one configured in the "keyvalue" section.
|
||||
|
||||
Default: `keyvalue`
|
||||
|
||||
---
|
||||
|
||||
## files
|
||||
|
||||
|
||||
|
||||
### basepath
|
||||
|
||||
The path where files are stored
|
||||
|
||||
Default: `./files`
|
||||
|
||||
### maxsize
|
||||
|
||||
The maximum size of a file, as a human-readable string.
|
||||
Warning: The max size is limited 2^64-1 bytes due to the underlying datatype
|
||||
|
||||
Default: `20MB`
|
||||
|
||||
---
|
||||
|
||||
## migration
|
||||
|
||||
|
||||
|
||||
### wunderlist
|
||||
|
||||
These are the settings for the wunderlist migrator
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### todoist
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
---
|
||||
|
||||
## avatar
|
||||
|
||||
|
||||
|
||||
### gravatarexpiration
|
||||
|
||||
When using gravatar, this is the duration in seconds until a cached gravatar user avatar expires
|
||||
|
||||
Default: `3600`
|
||||
|
||||
---
|
||||
|
||||
## backgrounds
|
||||
|
||||
|
||||
|
||||
### enabled
|
||||
|
||||
Whether to enable backgrounds for lists at all.
|
||||
|
||||
Default: `true`
|
||||
|
||||
### providers
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
---
|
||||
|
||||
## legal
|
||||
|
||||
Legal urls
|
||||
Will be shown in the frontend if configured here
|
||||
|
||||
|
||||
|
||||
### imprinturl
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
### privacyurl
|
||||
|
||||
Default: `<empty>`
|
||||
|
||||
---
|
||||
|
||||
## keyvalue
|
||||
|
||||
Key Value Storage settings
|
||||
The Key Value Storage is used for different kinds of things like metrics and a few cache systems.
|
||||
|
||||
|
||||
|
||||
### type
|
||||
|
||||
The type of the storage backend. Can be either "memory" or "redis". If "redis" is chosen it needs to be configured seperately.
|
||||
|
||||
Default: `memory`
|
||||
|
||||
231
docs/content/doc/setup/docker-start-to-finish.md
Normal file
231
docs/content/doc/setup/docker-start-to-finish.md
Normal file
@@ -0,0 +1,231 @@
|
||||
---
|
||||
date: "2020-05-24:00:00+02:00"
|
||||
title: "Docker Walkthrough"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Setup with docker from start to finish
|
||||
|
||||
This tutorial assumes basic knowledge of docker.
|
||||
It is aimed at beginners and should get you up and running quickly.
|
||||
|
||||
We'll use [docker compose](https://docs.docker.com/compose/) to make handling the bunch of containers easier.
|
||||
|
||||
> If you have any issues setting up vikunja, please don't hesitate to reach out to us via [matrix](https://riot.im/app/#/room/!dCRiCiLaCCFVNlDnYs:matrix.org?via=matrix.org), the [community forum](https://community.vikunja.io/) or even [email](mailto:hello@vikunja.io).
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Preparations (optional)
|
||||
|
||||
Create a directory for the project where all data and the compose file will live in.
|
||||
|
||||
## Create all necessary files
|
||||
|
||||
Create a `docker-compose.yml` file with the following contents in your directory:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
restart: unless-stopped
|
||||
proxy:
|
||||
image: nginx
|
||||
ports:
|
||||
- 80:80
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- api
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
{{< /highlight >}}
|
||||
|
||||
This defines four services, each with their own container:
|
||||
|
||||
* An api service which runs the vikunja api. Most of the core logic lives here.
|
||||
* The frontend which will make vikunja actually usable for most people.
|
||||
* A database container which will store all lists, tasks, etc. We're using mariadb here, but you're free to use mysql or postgres if you want.
|
||||
* A proxy service which makes the frontend and api available on the same port, redirecting all requests to `/api` to the api container.
|
||||
If you already have a proxy on your host, you may want to check out the [reverse proxy examples]() to use that.
|
||||
By default, it uses port 80 on the host.
|
||||
To change to something different, you'll need to change the `ports` section in the service definition.
|
||||
The number before the colon is the host port - This is where you can reach vikunja from the outside once all is up and running.
|
||||
|
||||
For the proxy service we'll need another bit of configuration.
|
||||
Create an `nginx.conf` in your directory (next to the `docker-compose.yml` file) and add the following contents to it:
|
||||
|
||||
{{< highlight conf >}}
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
proxy_pass http://frontend:80;
|
||||
}
|
||||
|
||||
location ~* ^/(api|dav|\.well-known)/ {
|
||||
proxy_pass http://api:3456;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
This is a simple proxy configuration which will forward all requests to `/api/` to the api container and everything else to the frontend.
|
||||
|
||||
<div class="notification is-info">
|
||||
<b>NOTE:</b> Even if you want to make your installation available under a different port, you don't need to change anything in this configuration.
|
||||
</div>
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> If you change the max upload size in Vikunja's settings, you'll need to also change the <code>client_max_body_size</code> in the nginx proxy config.
|
||||
</div>
|
||||
|
||||
## Run it
|
||||
|
||||
Run `sudo docker-compose up` in your directory and take a look at the output you get.
|
||||
When first started, Vikunja will set up the database and run all migrations etc.
|
||||
Once it is ready, you should see a message like this one in your console:
|
||||
|
||||
```
|
||||
api_1 | 2020-05-24T11:15:37.560386009Z: INFO ▶ cmd/func1 025 Vikunja version 0.13.1+19-e9bc3246ce, built at Sun, 24 May 2020 11:10:36 +0000
|
||||
api_1 | ⇨ http server started on [::]:3456
|
||||
```
|
||||
|
||||
This indicates all setup has been successful.
|
||||
If you get any errors, see below:
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Vikunja might not run on the first try.
|
||||
There are a few potential issues that could be causing this.
|
||||
|
||||
#### No connection to the database
|
||||
|
||||
Indicated by an error message like this one from the api container:
|
||||
|
||||
```
|
||||
2020/05/23 15:37:59 Config File "config" Not Found in "[/app/vikunja /etc/vikunja /app/vikunja/.config/vikunja]"
|
||||
2020/05/23 15:37:59 Using default config.
|
||||
2020-05-23T15:37:59.974435725Z: CRITICAL ▶ migration/Migrate 002 Migration failed: dial tcp 172.19.0.2:3306: connect: connection refused
|
||||
```
|
||||
|
||||
Especially when using mysql, this can happen on first start, because the mysql database container will take a few seconds to start.
|
||||
Vikunja does not know the container is not ready, therefore it will just try to connect to the db, fail since it is not ready and exit.
|
||||
|
||||
If you're using the docker compose example from above, you may notice the `restart: unless-stopped` option at the api service.
|
||||
This tells docker to restart the api container if it exits, unless you explicitly stop it.
|
||||
Therefore, it should "magically fix itself" by automatically restarting the container.
|
||||
|
||||
After a few seconds (or minutes) you should see a log message like this one from the mariadb container:
|
||||
|
||||
```
|
||||
2020-05-24 11:42:15 0 [Note] mysqld: ready for connections.
|
||||
Version: '10.4.12-MariaDB-1:10.4.12+maria~bionic' socket: '/var/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
|
||||
```
|
||||
|
||||
The next restart of Vikunja should be successful.
|
||||
If not, there might be a different error or a bug with Vikunja, please reach out to us in that case.
|
||||
|
||||
(If you have an idea about how we could improve this, we'd like to hear it!)
|
||||
|
||||
#### "Not a directory"
|
||||
|
||||
If you get an error like this one:
|
||||
|
||||
```
|
||||
ERROR: for vikunja_proxy_1 Cannot start service proxy: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"vikunja/nginx.conf\\\" to rootfs \\\"/var/lib/docker/overlay2/9c8b8f9419c29dad0d1233fbb0a3c36cf403dabd7a55d6f0a47b0c1dd6029994/merged\\\" at \\\"/var/lib/docker/overlay2/9c8b8f9419c29dad0d1233fbb0a3c36cf403dabd7a55d6f0a47b0c1dd6029994/merged/etc/nginx/conf.d/default.conf\\\" caused \\\"not a directory\\\"\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
|
||||
```
|
||||
|
||||
this means docker tried to mount a directory from the host to a file in the container.
|
||||
This can happen if you did not create the `nginx.conf` file.
|
||||
Because there is a volume mount for it in the `docker-compose.yml`, Docker will create a folder because non exists, assuming you want to mount a folder into the container.
|
||||
|
||||
To fix this, create the file and restart the containers again.
|
||||
|
||||
#### Migration failed: commands out of sync
|
||||
|
||||
If you get an error like this one:
|
||||
|
||||
```
|
||||
2020/05/23 15:53:38 Config File "config" Not Found in "[/app/vikunja /etc/vikunja /app/vikunja/.config/vikunja]"
|
||||
2020/05/23 15:53:38 Using default config.
|
||||
2020-05-23T15:53:38.762747276Z: CRITICAL ▶ migration/Migrate 002 Migration failed: commands out of sync. Did you run multiple statements at once?
|
||||
```
|
||||
|
||||
This is a mysql issue.
|
||||
Currently, we don't have a better solution than to completely wipe the database files and start over.
|
||||
To do this, first stop everything by running `sudo docker-compose down`, then remove the `db/` folder in your current folder with `sudo rm -rf db` and start the whole stack again with `sudo docker-compose up -d`.
|
||||
|
||||
## Try it
|
||||
|
||||
Head over to `http://<host-ip or url>/api/v1/info` in a browser.
|
||||
You should see something like this:
|
||||
|
||||
{{< highlight json >}}
|
||||
{
|
||||
"version": "0.13.1+19-e9bc3246ce",
|
||||
"frontend_url": "http://localhost:8080/",
|
||||
"motd": "test",
|
||||
"link_sharing_enabled": true,
|
||||
"max_file_size": "20MB",
|
||||
"registration_enabled": true,
|
||||
"available_migrators": [
|
||||
"wunderlist",
|
||||
"todoist"
|
||||
],
|
||||
"task_attachments_enabled": true
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
This shows you can reach the api through the api proxy.
|
||||
|
||||
Now head over to `http://<host-ip or url>/` which should show the login mask.
|
||||
|
||||
## Make it persistent
|
||||
|
||||
Currently, Vikunja runs in foreground in your terminal.
|
||||
For a real-world scenario this is not the best way.
|
||||
|
||||
Back in your terminal, stop the stack by pressing `CTRL-C` on your keyboard.
|
||||
Then run `sudo docker-compose up -d` in your again.
|
||||
The `-d` flag at the end of the command will tell docker to run the containers in the background.
|
||||
If you need to check the logs after that, you can run `sudo docker-compose logs`.
|
||||
|
||||
Vikunja does not have any default users, you'll need to register and account.
|
||||
After that, you can use it.
|
||||
|
||||
## Tear it all down
|
||||
|
||||
If you want to completely stop all containers run `sudo docker-compose down` in your terminal.
|
||||
|
||||
## Improve this guide
|
||||
|
||||
We'll happily accept suggestions and improvements for this guide.
|
||||
Please [reach out to us](https://vikunja.io/contact/) if you have any.
|
||||
293
docs/content/doc/setup/full-docker-example.md
Normal file
293
docs/content/doc/setup/full-docker-example.md
Normal file
@@ -0,0 +1,293 @@
|
||||
---
|
||||
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 or traefik on the host to proxy backend and frontend into a single port.
|
||||
|
||||
For all available configuration options, see [configuration]({{< ref "config.md">}}).
|
||||
|
||||
<div class="notification is-warning">
|
||||
<b>NOTE:</b> If you intend to run Vikunja with mysql and/or to use non-latin characters
|
||||
<a href="{{< ref "utf-8.md">}}">make sure your db is utf-8 compatible</a>.<br/>
|
||||
All examples on this page already reflect this and do not require additional work.
|
||||
</div>
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Redis
|
||||
|
||||
To use redis, you'll need to add this to the config examples below:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_REDIS_ENABLED: 1
|
||||
VIKUNJA_REDIS_HOST: 'redis:6379'
|
||||
VIKUNJA_CACHE_ENABLED: 1
|
||||
VIKUNJA_CACHE_TYPE: redis
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
redis:
|
||||
image: redis
|
||||
{{< /highlight >}}
|
||||
|
||||
## Example with traefik 2
|
||||
|
||||
This example assumes [traefik](https://traefik.io) version 2 installed and configured to [use docker as a configuration provider](https://docs.traefik.io/providers/docker/).
|
||||
|
||||
We also make a few assumtions here which you'll most likely need to adjust for your traefik setup:
|
||||
|
||||
* Your domain is `vikunja.example.com`
|
||||
* The entrypoint you want to make vikunja available from is called `https`
|
||||
* The tls cert resolver is called `acme`
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
networks:
|
||||
- web
|
||||
- default
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.vikunja-api.rule=Host(`vikunja.example.com`) && PathPrefix(`/api/v1`, `/dav/`, `/.well-known/`)"
|
||||
- "traefik.http.routers.vikunja-api.entrypoints=https"
|
||||
- "traefik.http.routers.vikunja-api.tls.certResolver=acme"
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.vikunja-frontend.rule=Host(`vikunja.example.com`)"
|
||||
- "traefik.http.routers.vikunja-frontend.entrypoints=https"
|
||||
- "traefik.http.routers.vikunja-frontend.tls.certResolver=acme"
|
||||
networks:
|
||||
- web
|
||||
- default
|
||||
restart: unless-stopped
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersupersecret
|
||||
MYSQL_USER: vikunja
|
||||
MYSQL_PASSWORD: supersecret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
command: --max-connections=1000
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
{{< /highlight >}}
|
||||
|
||||
## Example with traefik 1
|
||||
|
||||
This example assumes [traefik](https://traefik.io) in version 1 installed and configured to [use docker as a configuration provider](https://docs.traefik.io/v1.7/configuration/backends/docker/).
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: vikunja
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
networks:
|
||||
- web
|
||||
- default
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.frontend.rule=Host:vikunja.example.com;PathPrefix:/api/v1,/dav/,/.well-known"
|
||||
- "traefik.port=3456"
|
||||
- "traefik.protocol=http"
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
labels:
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.frontend.rule=Host:vikunja.example.com;PathPrefix:/"
|
||||
- "traefik.port=80"
|
||||
- "traefik.protocol=http"
|
||||
networks:
|
||||
- web
|
||||
- default
|
||||
restart: unless-stopped
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersupersecret
|
||||
MYSQL_USER: vikunja
|
||||
MYSQL_PASSWORD: supersecret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
command: --max-connections=1000
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
{{< /highlight >}}
|
||||
|
||||
## Example with nginx as proxy
|
||||
|
||||
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_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
restart: unless-stopped
|
||||
proxy:
|
||||
image: nginx
|
||||
ports:
|
||||
- 80:80
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- api
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
{{< /highlight >}}
|
||||
|
||||
## Example with Caddy v2 as proxy
|
||||
|
||||
You will need the following `Caddyfile` on your host (or elsewhere, but then you'd need to adjust the proxy mount at the bottom of the compose file):
|
||||
|
||||
{{< highlight conf >}}
|
||||
vikunja.example.com {
|
||||
reverse_proxy /api/* api:3456
|
||||
reverse_proxy /.well-known/* api:3456
|
||||
reverse_proxy /dav/* api:3456
|
||||
reverse_proxy frontend:80
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
`docker-compose.yml` config:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
api:
|
||||
image: vikunja/api
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
frontend:
|
||||
image: vikunja/frontend
|
||||
restart: unless-stopped
|
||||
caddy:
|
||||
image: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
depends_on:
|
||||
- api
|
||||
- frontend
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
{{< /highlight >}}
|
||||
277
docs/content/doc/setup/install-backend.md
Normal file
277
docs/content/doc/setup/install-backend.md
Normal file
@@ -0,0 +1,277 @@
|
||||
---
|
||||
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 `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=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.
|
||||
|
||||
Save the file to `/etc/systemd/system/vikunja.service`
|
||||
|
||||
After you made all nessecary 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 nessecary 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 familarity 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 eviroment variables or `.env` files to configure Vikunja in docker.
|
||||
See [config]({{< ref "config.md">}}) for a list of available configuration options.
|
||||
|
||||
### Files volume
|
||||
|
||||
By default the container stores all files uploaded and used through vikunja inside of `/app/vikunja/files` which is created as a docker volume.
|
||||
You should mount the volume somewhere to the host to permanently store the files and don't loose them if the container restarts.
|
||||
|
||||
### Setting user and group id of the user running vikunja
|
||||
|
||||
You can set the user and group id of the user running vikunja with the `PUID` and `PGID` evironment variables.
|
||||
This follows the pattern used by [the linuxserver.io](https://docs.linuxserver.io/general/understanding-puid-and-pgid) docker images.
|
||||
|
||||
This is useful to solve general permission problems when host-mounting volumes such as the volume used for task attachments.
|
||||
|
||||
### Docker compose
|
||||
|
||||
To run the backend with a mariadb database you can use this example [docker-compose](https://docs.docker.com/compose/) file:
|
||||
|
||||
{{< highlight yaml >}}
|
||||
version: '2'
|
||||
services:
|
||||
api:
|
||||
image: vikunja/api:latest
|
||||
environment:
|
||||
VIKUNJA_DATABASE_HOST: db
|
||||
VIKUNJA_DATABASE_PASSWORD: supersecret
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_SERVICE_JWTSECRET: <generated secret>
|
||||
volumes:
|
||||
- ./files:/app/vikunja/files
|
||||
db:
|
||||
image: mariadb:10
|
||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: supersecret
|
||||
MYSQL_DATABASE: vikunja
|
||||
volumes:
|
||||
- ./db:/var/lib/mysql
|
||||
{{< /highlight >}}
|
||||
|
||||
See [full docker example]({{< ref "full-docker-example.md">}}) for more varations 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">}}).
|
||||
142
docs/content/doc/setup/install-frontend.md
Normal file
142
docs/content/doc/setup/install-frontend.md
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
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 seperate servers or even just seperate
|
||||
ports on the same server with [the use of a reverse proxy]({{< ref "reverse-proxies.md">}}).
|
||||
|
||||
To make configurations like this possible, the api url can be set in the `index.html` file of the frontend releases.
|
||||
Just open the file with a text editor - there are comments which will explain how to set the url.
|
||||
|
||||
**Note:** This needs to be done again after every update.
|
||||
(If you have a good idea for a better solution than this, we'd love to [hear it](https://vikunja.io/contact/))
|
||||
|
||||
## Docker
|
||||
|
||||
The docker image is based on nginx and just contains all nessecary files for the frontend.
|
||||
|
||||
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 varations of this config.
|
||||
|
||||
### Setting user and group id of the user running vikunja
|
||||
|
||||
You can set the user and group id of the user running vikunja with the `PUID` and `PGID` evironment variables.
|
||||
This follows the pattern used by [the linuxserver.io](https://docs.linuxserver.io/general/understanding-puid-and-pgid) docker images.
|
||||
|
||||
### API URL configuration in docker
|
||||
|
||||
When running the frontend with docker, it is possible to set the environment variable `$VIKUNJA_API_URL` to the api url.
|
||||
It is therefore not needed to change the url manually inside the docker container.
|
||||
|
||||
## NGINX
|
||||
|
||||
Below are two example configurations which you can put in your `nginx.conf`:
|
||||
|
||||
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 ^\/?(config\.json|favicon\.ico|css|fonts|images|img|js) - [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.
|
||||
41
docs/content/doc/setup/install.md
Normal file
41
docs/content/doc/setup/install.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Installing"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
weight: 10
|
||||
---
|
||||
|
||||
# Installing
|
||||
|
||||
Vikunja consists of two parts: [Backend](https://code.vikunja.io/api) and [frontend](https://code.vikunja.io/frontend).
|
||||
While the backend is required, the frontend is not.
|
||||
You don't neccesarily need to have a web-frontend, using Vikunja via the [mobile app](https://code.vikunja.io/app) is totally fine.
|
||||
|
||||
However, using the web frontend is highly reccommended.
|
||||
|
||||
Vikunja can be installed in various forms.
|
||||
This document provides an overview and instructions for the different methods.
|
||||
|
||||
* [Backend]({{< 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">}})
|
||||
* [Reverse proxies]({{< ref "reverse-proxies.md">}})
|
||||
* [Full docker example]({{< ref "full-docker-example.md">}})
|
||||
* [Backups]({{< ref "backups.md">}})
|
||||
111
docs/content/doc/setup/reverse-proxies.md
Normal file
111
docs/content/doc/setup/reverse-proxies.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Reverse Proxy"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# Setup behind a reverse proxy which also serves the frontend
|
||||
|
||||
These examples assume you have an instance of the backend running on your server listening on port `3456`.
|
||||
If you've changed this setting, you need to update the server configurations accordingly.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## NGINX
|
||||
|
||||
Below are two example configurations which you can put in your `nginx.conf`:
|
||||
|
||||
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;
|
||||
|
||||
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.
|
||||
</div>
|
||||
|
||||
## Apache
|
||||
|
||||
Put the following config in `cat /etc/apache2/sites-available/vikunja.conf`:
|
||||
|
||||
{{< highlight aconf >}}
|
||||
<VirtualHost *:80>
|
||||
ServerName localhost
|
||||
|
||||
<Proxy *>
|
||||
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 ^\/?(config\.json|favicon\.ico|css|fonts|images|img|js|api|dav|\.well-known) - [L]
|
||||
RewriteRule ^(.*)$ /index.html [QSA,L]
|
||||
</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">}}).
|
||||
108
docs/content/doc/setup/utf-8.md
Normal file
108
docs/content/doc/setup/utf-8.md
Normal file
@@ -0,0 +1,108 @@
|
||||
---
|
||||
date: "2020-07-06:00:00+02:00"
|
||||
title: "UTF-8 Settings"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "setup"
|
||||
---
|
||||
|
||||
# UTF-8 Settings
|
||||
|
||||
Vikunja itself is always fully capable of handling utf-8 characters.
|
||||
However, your database might be not.
|
||||
Vikunja itself will work just fine until you want to use non-latin characters in your tasks/lists/etc.
|
||||
|
||||
On this page, you will find information about how to fully ensure non-latin characters like aüäß or emojis work
|
||||
with your installation.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Postgresql & SQLite
|
||||
|
||||
Postgresql and SQLite should handle utf-8 just fine - If you discover any issues nonetheless, please
|
||||
[drop us a message](https://vikunja.io/contact/).
|
||||
|
||||
## MySQL
|
||||
|
||||
MySQL is not able to handle utf-8 by default.
|
||||
To fix this, follow the steps below.
|
||||
|
||||
To find out if your db supports utf-8, run the following in a shell or similar, assuming the database
|
||||
you're using for vikunja is called `vikunja`:
|
||||
|
||||
{{< highlight sql >}}
|
||||
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'vikunja';
|
||||
{{< /highlight >}}
|
||||
|
||||
This will get you a result like the following:
|
||||
|
||||
```
|
||||
+----------------------------+
|
||||
| default_character_set_name |
|
||||
+----------------------------+
|
||||
| latin1 |
|
||||
+----------------------------+
|
||||
1 row in set (0.001 sec)
|
||||
```
|
||||
|
||||
The charset `latin1` means the db is encoded in the `latin1` encoding which does not support utf-8 characters.
|
||||
|
||||
(The following guide is based on [this thread from stackoverflow](https://dba.stackexchange.com/a/104866))
|
||||
|
||||
### 0. Backup your database
|
||||
|
||||
Before attempting any conversion, please [back up your database]({{< ref "backups.md">}}).
|
||||
|
||||
### 1. Create a pre-conversion script
|
||||
|
||||
Copy the following sql statements in a file called `preAlterTables.sql` and replace all occurences of `vikunja` with
|
||||
the name of your database:
|
||||
|
||||
{{< highlight sql >}}
|
||||
use information_schema;
|
||||
SELECT concat("ALTER DATABASE `",table_schema,"` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;") as _sql
|
||||
FROM `TABLES` where table_schema like 'vikunja' and TABLE_TYPE='BASE TABLE' group by table_schema;
|
||||
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name,"` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") as _sql
|
||||
FROM `TABLES` where table_schema like 'vikunja' and TABLE_TYPE='BASE TABLE' group by table_schema, table_name;
|
||||
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type,"(",character_maximum_length,") CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") as _sql
|
||||
FROM `COLUMNS` where table_schema like 'vikunja' and data_type in ('varchar','char');
|
||||
SELECT concat("ALTER TABLE `",table_schema,"`.`",table_name, "` CHANGE `",column_name,"` `",column_name,"` ",data_type," CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",IF(is_nullable="YES"," NULL"," NOT NULL"),";") as _sql
|
||||
FROM `COLUMNS` where table_schema like 'vikunja' and data_type in ('text','tinytext','mediumtext','longtext');
|
||||
{{< /highlight >}}
|
||||
|
||||
### 2. Run the pre-conversion script
|
||||
|
||||
Running this will create the actual migration script for your particular database structure and save it in a file called `alterTables.sql`:
|
||||
|
||||
{{< highlight bash >}}
|
||||
mysql -uroot < preAlterTables.sql | egrep '^ALTER' > alterTables.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
### 3. Convert the database
|
||||
|
||||
At this point converting is just a matter of executing the previously generated sql script:
|
||||
|
||||
{{< highlight bash >}}
|
||||
mysql -uroot < alterTables.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
### 4. Verify it was successfully converted
|
||||
|
||||
If everything worked as intended, your db collation should now look like this:
|
||||
|
||||
{{< highlight sql >}}
|
||||
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'vikunja';
|
||||
{{< /highlight >}}
|
||||
|
||||
Should get you:
|
||||
|
||||
```
|
||||
+----------------------------+
|
||||
| default_character_set_name |
|
||||
+----------------------------+
|
||||
| utf8mb4 |
|
||||
+----------------------------+
|
||||
1 row in set (0.001 sec)
|
||||
```
|
||||
19
docs/content/doc/usage/api.md
Normal file
19
docs/content/doc/usage/api.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "API Documentation"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# API Documentation
|
||||
|
||||
You can find the api docs under `http://vikunja.tld/api/v1/docs` of your instance.
|
||||
A public instance is available on [try.vikunja.io](http://try.vikunja.io/api/v1/docs).
|
||||
|
||||
These docs are autgenerated from annotations in the code with swagger.
|
||||
|
||||
The specification is hosted at `http://vikunja.tld/api/v1/docs.json`.
|
||||
You can use this to embed it into other openapi compatible applications if you want.
|
||||
144
docs/content/doc/usage/caldav.md
Normal file
144
docs/content/doc/usage/caldav.md
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
date: "2019-05-12:00:00+01:00"
|
||||
title: "Caldav"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# Caldav
|
||||
|
||||
> **Warning:** The caldav integration is in an early alpha stage and has bugs.
|
||||
> It works well with some clients while having issues with others.
|
||||
> If you encounter issues, please [report them](https://code.vikunja.io/api/issues/new?body=[caldav])
|
||||
|
||||
Vikunja supports managing tasks via the [caldav VTODO](https://tools.ietf.org/html/rfc5545#section-3.6.2) extension.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## URLs
|
||||
|
||||
All urls are located under the `/dav` subspace.
|
||||
|
||||
Urls are:
|
||||
|
||||
* `/principals/<username>/`: Returns urls for list discovery. *Use this url to initially make connections to new clients.*
|
||||
* `/lists/`: Used to manage lists
|
||||
* `/lists/<List ID>/`: Used to manage a single list
|
||||
* `/lists/<List ID>/<Task UID>`: Used to manage a task on a list
|
||||
|
||||
## Supported properties
|
||||
|
||||
Vikunja currently supports the following properties:
|
||||
|
||||
* `UID`
|
||||
* `SUMMARY`
|
||||
* `DESCRIPTION`
|
||||
* `PRIORITY`
|
||||
* `COMPLETED`
|
||||
* `DUE`
|
||||
* `DTSTART`
|
||||
* `DURATION`
|
||||
* `ORGANIZER`
|
||||
* `RELATED-TO`
|
||||
* `CREATED`
|
||||
* `DTSTAMP`
|
||||
* `LAST-MODIFIED`
|
||||
|
||||
Vikunja **currently does not** support these properties:
|
||||
|
||||
* `ATTACH`
|
||||
* `CATEGORIES`
|
||||
* `CLASS`
|
||||
* `COMMENT`
|
||||
* `GEO`
|
||||
* `LOCATION`
|
||||
* `PERCENT-COMPLETE`
|
||||
* `RESOURCES`
|
||||
* `STATUS`
|
||||
* `CONTACT`
|
||||
* `RECURRENCE-ID`
|
||||
* `URL`
|
||||
* Recurrence
|
||||
* `SEQUENCE`
|
||||
|
||||
## Tested Clients
|
||||
|
||||
### Working
|
||||
|
||||
* [Evolution](https://wiki.gnome.org/Apps/Evolution/)
|
||||
* [OpenTasks](https://opentasks.app/) + [DAVx⁵](https://www.davx5.com/)
|
||||
|
||||
### Not working
|
||||
|
||||
* [Tasks (Android)](https://tasks.org/)
|
||||
* [Thunderbird (68)](https://www.thunderbird.net/)
|
||||
|
||||
## Dev logs
|
||||
|
||||
The whole thing is not optimized at all and probably pretty inefficent.
|
||||
|
||||
Request body and headers are logged if the debug output is enabled.
|
||||
|
||||
```
|
||||
Creating a new task:
|
||||
PUT /dav/lists/1/cd4dd0e1b3c19cc9d787829b6e08be536e3df3a4.ics
|
||||
|
||||
Body:
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
CALSCALE:GREGORIAN
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTODO
|
||||
UID:cd4dd0e1b3c19cc9d787829b6e08be536e3df3a4
|
||||
DTSTAMP:20190508T134538Z
|
||||
SUMMARY:test2000
|
||||
PRIORITY:0
|
||||
CLASS:PUBLIC
|
||||
CREATED:20190508T134710Z
|
||||
LAST-MODIFIED:20190508T134710Z
|
||||
END:VTODO
|
||||
END:VCALENDAR
|
||||
|
||||
|
||||
Marking a task as done:
|
||||
|
||||
BEGIN:VCALENDAR
|
||||
CALSCALE:GREGORIAN
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
BEGIN:VTODO
|
||||
UID:3ada92f28b4ceda38562ebf047c6ff05400d4c572352a
|
||||
DTSTAMP:20190511T183631
|
||||
DTSTART:19700101T000000
|
||||
DTEND:19700101T000000
|
||||
SUMMARY:sdgs
|
||||
ORGANIZER;CN=:user
|
||||
CREATED:20190511T183631
|
||||
PRIORITY:0
|
||||
LAST-MODIFIED:20190512T193428Z
|
||||
COMPLETED:20190512T193428Z
|
||||
PERCENT-COMPLETE:100
|
||||
STATUS:COMPLETED
|
||||
END:VTODO
|
||||
END:VCALENDAR
|
||||
|
||||
Requests from the app:::
|
||||
|
||||
[CALDAV] Request Body: <?xml version="1.0" encoding="UTF-8" ?><propfind xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CARD="urn:ietf:params:xml:ns:carddav"><prop><current-user-principal /></prop></propfind>
|
||||
[CALDAV] GetResources: rpath: /dav/
|
||||
2019-05-18T23:25:49.971140654+02:00: WEB ▶ 192.168.1.134 PROPFIND 207 /dav/ 1.021705664s - okhttp/3.12.2
|
||||
|
||||
[CALDAV] Request Body: <?xml version="1.0" encoding="UTF-8" ?><propfind xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CARD="urn:ietf:params:xml:ns:carddav"><prop><CAL:calendar-home-set /></prop></propfind>
|
||||
[CALDAV] GetResources: rpath: /dav/
|
||||
2019-05-18T23:25:52.166996113+02:00: WEB ▶ 192.168.1.134 PROPFIND 207 /dav/ 1.042834467s - okhttp/3.12.2
|
||||
|
||||
And then it just stops.
|
||||
... and complains about not being able to find the home set
|
||||
... without even requesting it...
|
||||
|
||||
|
||||
```
|
||||
183
docs/content/doc/usage/cli.md
Normal file
183
docs/content/doc/usage/cli.md
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
date: "2019-03-31:00:00+01:00"
|
||||
title: "CLI"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# Command line interface
|
||||
|
||||
You can interact with Vikunja using its `cli` interface.
|
||||
The following commands are available:
|
||||
|
||||
* [dump](#dump)
|
||||
* [help](#help)
|
||||
* [migrate](#migrate)
|
||||
* [restore](#restore)
|
||||
* [testmail](#testmail)
|
||||
* [user](#user)
|
||||
* [version](#version)
|
||||
* [web](#web)
|
||||
|
||||
If you don't specify a command, the [`web`](#web) command will be executed.
|
||||
|
||||
All commands use the same standard [config file]({{< ref "../setup/config.md">}}).
|
||||
|
||||
### `dump`
|
||||
|
||||
Creates a zip file with all vikunja-related files.
|
||||
This includes config, version, all files and the full database.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja dump
|
||||
{{< /highlight >}}
|
||||
|
||||
### `help`
|
||||
|
||||
Shows more detailed help about any command.
|
||||
|
||||
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.
|
||||
|
||||
### `restore`
|
||||
|
||||
Restores a previously created dump from a zip file, see `dump`.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja restore <path to dump zip file>
|
||||
{{< /highlight >}}
|
||||
|
||||
### `testmail`
|
||||
|
||||
Sends a test mail using the configured smtp connection.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja testmail <email to send the test mail to>
|
||||
{{< /highlight >}}
|
||||
|
||||
### `user`
|
||||
|
||||
Bundles a few commands to manage users.
|
||||
|
||||
#### `user change-status`
|
||||
|
||||
Enable or disable a user. Will toggle the current status if no flag (`--enable` or `--disable`) is provided.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja user change-status <user id> <flags>
|
||||
{{< /highlight >}}
|
||||
|
||||
Flags:
|
||||
* `-d`, `--disable`: Disable the user.
|
||||
* `-e`, `--enable`: Enable the user.
|
||||
|
||||
#### `user create`
|
||||
|
||||
Create a new user.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja user create <flags>
|
||||
{{< /highlight >}}
|
||||
|
||||
Flags:
|
||||
* `-a`, `--avatar-provider`: The avatar provider of the new user. Optional.
|
||||
* `-e`, `--email`: The email address of the new user.
|
||||
* `-p`, `--password`: The password of the new user. You will be asked to enter it if not provided through the flag.
|
||||
* `-u`, `--username`: The username of the new user.
|
||||
|
||||
#### `user list`
|
||||
|
||||
Shows a list of all users.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja user list
|
||||
{{< /highlight >}}
|
||||
|
||||
#### `user reset-password`
|
||||
|
||||
Reset a users password, either through mailing them a reset link or directly.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja user reset-password <flags>
|
||||
{{< /highlight >}}
|
||||
|
||||
Flags:
|
||||
* `-d`, `--direct`: If provided, reset the password directly instead of sending the user a reset mail.
|
||||
* `-p`, `--password`: The new password of the user. Only used in combination with --direct. You will be asked to enter it if not provided through the flag.
|
||||
|
||||
#### `user update`
|
||||
|
||||
Update an existing user.
|
||||
|
||||
Usage:
|
||||
{{< highlight bash >}}
|
||||
$ vikunja user update <user id>
|
||||
{{< /highlight >}}
|
||||
|
||||
Flags:
|
||||
* `-a`, `--avatar-provider`: The new avatar provider of the new user.
|
||||
* `-e`, `--email`: The new email address of the user.
|
||||
* `-u`, `--username`: The new username of the user.
|
||||
|
||||
### `version`
|
||||
|
||||
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 >}}
|
||||
144
docs/content/doc/usage/errors.md
Normal file
144
docs/content/doc/usage/errors.md
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Errors"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# Errors
|
||||
|
||||
This document describes the different errors Vikunja can return.
|
||||
|
||||
{{< table_of_contents >}}
|
||||
|
||||
## Generic
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 0001 | 403 | Generic forbidden error. |
|
||||
|
||||
## User
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 1001 | 400 | A user with this username already exists. |
|
||||
| 1002 | 400 | A user with this email address already exists. |
|
||||
| 1004 | 400 | No username and password specified. |
|
||||
| 1005 | 404 | The user does not exist. |
|
||||
| 1006 | 400 | Could not get the user id. |
|
||||
| 1008 | 412 | No password reset token provided. |
|
||||
| 1009 | 412 | Invalid password reset token. |
|
||||
| 1010 | 412 | Invalid email confirm token. |
|
||||
| 1011 | 412 | Wrong username or password. |
|
||||
| 1012 | 412 | Email address of the user not confirmed. |
|
||||
| 1013 | 412 | New password is empty. |
|
||||
| 1014 | 412 | Old password is empty. |
|
||||
| 1015 | 412 | Totp is already enabled for this user. |
|
||||
| 1016 | 412 | Totp is not enabled for this user. |
|
||||
| 1017 | 412 | The provided Totp passcode is invalid. |
|
||||
| 1018 | 412 | The provided user avatar provider type setting is invalid. |
|
||||
|
||||
## Validation
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 2001 | 400 | ID cannot be empty or 0. |
|
||||
| 2002 | 400 | Some of the request data was invalid. The response contains an aditional array with all invalid fields. |
|
||||
|
||||
## List
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 3001 | 404 | The list does not exist. |
|
||||
| 3004 | 403 | The user needs to have read permissions on that list to perform that action. |
|
||||
| 3005 | 400 | The list title cannot be empty. |
|
||||
| 3006 | 404 | The list share does not exist. |
|
||||
| 3007 | 400 | A list with this identifier already exists. |
|
||||
| 3008 | 412 | The list is archived and can therefore only be accessed read only. This is also true for all tasks associated with this list. |
|
||||
|
||||
## Task
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 4001 | 400 | The list task text cannot be empty. |
|
||||
| 4002 | 404 | The list task does not exist. |
|
||||
| 4003 | 403 | All bulk editing tasks must belong to the same list. |
|
||||
| 4004 | 403 | Need at least one task when bulk editing tasks. |
|
||||
| 4005 | 403 | The user does not have the right to see the task. |
|
||||
| 4006 | 403 | The user tried to set a parent task as the task itself. |
|
||||
| 4007 | 400 | The user tried to create a task relation with an invalid kind of relation. |
|
||||
| 4008 | 409 | The user tried to create a task relation which already exists. |
|
||||
| 4009 | 404 | The task relation does not exist. |
|
||||
| 4010 | 400 | Cannot relate a task with itself. |
|
||||
| 4011 | 404 | The task attachment does not exist. |
|
||||
| 4012 | 400 | The task attachment is too large. |
|
||||
| 4013 | 400 | The task sort param is invalid. |
|
||||
| 4014 | 400 | The task sort order is invalid. |
|
||||
| 4015 | 404 | The task comment does not exist. |
|
||||
| 4016 | 403 | Invalid task field. |
|
||||
| 4017 | 403 | Invalid task filter comparator. |
|
||||
| 4018 | 403 | Invalid task filter concatinator. |
|
||||
| 4019 | 403 | Invalid task filter value. |
|
||||
|
||||
## Namespace
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 5001 | 404 | The namspace does not exist. |
|
||||
| 5003 | 403 | The user does not have access to the specified namespace. |
|
||||
| 5006 | 400 | The namespace name cannot be empty. |
|
||||
| 5009 | 403 | The user needs to have namespace read access to perform that action. |
|
||||
| 5010 | 403 | This team does not have access to that namespace. |
|
||||
| 5011 | 409 | This user has already access to that namespace. |
|
||||
| 5012 | 412 | The namespace is archived and can therefore only be accessed read only. |
|
||||
|
||||
## Team
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 6001 | 400 | The team name cannot be emtpy. |
|
||||
| 6002 | 404 | The team does not exist. |
|
||||
| 6004 | 409 | The team already has access to that namespace or list. |
|
||||
| 6005 | 409 | The user is already a member of that team. |
|
||||
| 6006 | 400 | Cannot delete the last team member. |
|
||||
| 6007 | 403 | The team does not have access to the list to perform that action. |
|
||||
|
||||
## User List Access
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 7002 | 409 | The user already has access to that list. |
|
||||
| 7003 | 403 | The user does not have access to that list. |
|
||||
|
||||
## Label
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 8001 | 403 | This label already exists on that task. |
|
||||
| 8002 | 404 | The label does not exist. |
|
||||
| 8003 | 403 | The user does not have access to this label. |
|
||||
|
||||
## Right
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 9001 | 403 | The right is invalid. |
|
||||
|
||||
## Kanban
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 10001 | 404 | The bucket does not exist. |
|
||||
| 10002 | 400 | The bucket does not belong to that list. |
|
||||
| 10003 | 412 | You cannot remove the last bucket on a list. |
|
||||
| 10004 | 412 | You cannot add the task to this bucket as it already exceeded the limit of tasks it can hold. |
|
||||
|
||||
## Saved Filters
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 11001 | 404 | The saved filter does not exist. |
|
||||
| 11002 | 412 | Saved filters are not available for link shares. |
|
||||
25
docs/content/doc/usage/relation_kinds.md
Normal file
25
docs/content/doc/usage/relation_kinds.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
date: "2019-09-25:00:00+02:00"
|
||||
title: "Task Relation kinds"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# Available task relation kinds
|
||||
|
||||
| Code | Description |
|
||||
|------|-------------|
|
||||
| subtask | Task is a subtask of the other task. This is the opposite of `parenttask`. |
|
||||
| parenttask | Task is a parent task of the other task. This is the opposite of `subtask`. |
|
||||
| related | Both tasks are related to each other. How is not more specified. |
|
||||
| duplicateof | Task is a duplicate of the other task. This is the opposite of `duplicates`. |
|
||||
| duplicates | Task duplicates the other task. This is the opposite of `duplicateof`. |
|
||||
| blocking | Task is blocking the other task. This is the opposite of `blocked`. |
|
||||
| blocked | Task is blocked by the other task. This is the opposite of `blocking`. |
|
||||
| precedes | Task precedes the other task. This is the opposite of `follows`. |
|
||||
| follows | Task follows the other task. This is the opposite of `precedes`. |
|
||||
| copiedfrom | Task is copied from the other task. This is the opposite of `copiedto`. |
|
||||
| copiedto | Task is copied to the other task. This is the opposite of `copiedfrom`. |
|
||||
29
docs/content/doc/usage/rights.md
Normal file
29
docs/content/doc/usage/rights.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
date: "2019-02-12:00:00+02:00"
|
||||
title: "Rights"
|
||||
draft: false
|
||||
type: "doc"
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
---
|
||||
|
||||
# List and namespace rights for teams and users
|
||||
|
||||
Whenever you share a list or namespace with a user or team, you can specify a `rights` parameter.
|
||||
This parameter controls the rights that team or user is going to have (or has, if you request the current sharing status).
|
||||
|
||||
Rights are being specified using integers.
|
||||
|
||||
The following values are possible:
|
||||
|
||||
| Right (int) | Meaning |
|
||||
|-------------|---------|
|
||||
| 0 (Default) | Read only. Anything which is shared with this right cannot be edited. |
|
||||
| 1 | Read and write. Namespaces or lists shared with this right can be read and written to by the team or user. |
|
||||
| 2 | Admin. Can do anything like read and write, but can additionally manage sharing options. |
|
||||
|
||||
## Team admins
|
||||
|
||||
When adding or querying a team, every member has an additional boolean value stating if it is admin or not.
|
||||
A team admin can also add and remove team members and also change whether a user in the team is admin or not.
|
||||
@@ -1,34 +0,0 @@
|
||||
# Errors
|
||||
|
||||
This document describes the different errors Vikunja can return.
|
||||
|
||||
| ErrorCode | HTTP Status Code | Description |
|
||||
|-----------|------------------|-------------|
|
||||
| 1001 | 400 | A user with this username already exists. |
|
||||
| 1002 | 400 | A user with this email address already exists. |
|
||||
| 1004 | 400 | No username and password specified. |
|
||||
| 1005 | 404 | The user does not exist. |
|
||||
| 1006 | 400 | Could not get the user id. |
|
||||
| 1007 | 409 | Cannot delete the last user on the system. |
|
||||
| 2001 | 400 | ID cannot be empty or 0. |
|
||||
| 3001 | 404 | The list does not exist. |
|
||||
| 3004 | 403 | The user needs to have read permissions on that list to perform that action. |
|
||||
| 3005 | 400 | The list title cannot be empty. |
|
||||
| 4001 | 400 | The list task text cannot be empty. |
|
||||
| 4002 | 404 | The list task does not exist. |
|
||||
| 5001 | 404 | The namspace does not exist. |
|
||||
| 5003 | 403 | The user does not have access to the specified namespace. |
|
||||
| 5006 | 400 | The namespace name cannot be empty. |
|
||||
| 5009 | 403 | The user needs to have namespace read access to perform that action. |
|
||||
| 5010 | 403 | This team does not have access to that namespace. |
|
||||
| 5011 | 409 | This user has already access to that namespace. |
|
||||
| 6001 | 400 | The team name cannot be emtpy. |
|
||||
| 6002 | 404 | The team does not exist. |
|
||||
| 6003 | 400 | The provided team right is invalid. |
|
||||
| 6004 | 409 | The team already has access to that namespace or list. |
|
||||
| 6005 | 409 | The user is already a member of that team. |
|
||||
| 6006 | 400 | Cannot delete the last team member. |
|
||||
| 6007 | 403 | The team does not have access to the list to perform that action. |
|
||||
| 7001 | 400 | The user right is invalid. |
|
||||
| 7002 | 409 | The user already has access to that list. |
|
||||
| 7003 | 403 | The user does not have access to that list. |
|
||||
21
docs/nginx.conf
Normal file
21
docs/nginx.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
error_page 404 /docs/404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
location /docs/contact {
|
||||
return 301 $scheme://vikunja.io/en/contact;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# How the binder works
|
||||
|
||||
The binder binds all values inside the url to their respective fields in a struct. Those fields need to have a tag
|
||||
"param" with the name of the url placeholder which must be the same as in routes.
|
||||
|
||||
Whenever one of the standard CRUD methods is invoked, this binder is called, which enables one handler method
|
||||
to handle all kinds of different urls with different parameters.
|
||||
1
docs/themes/vikunja
vendored
Submodule
1
docs/themes/vikunja
vendored
Submodule
Submodule docs/themes/vikunja added at 958219fc84
127
go.mod
127
go.mod
@@ -1,34 +1,101 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
module code.vikunja.io/api
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.30.0 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6 // indirect
|
||||
github.com/dgrijalva/jwt-go v0.0.0-20170608005149-a539ee1a749a
|
||||
github.com/garyburd/redigo v1.6.0 // indirect
|
||||
github.com/go-sql-driver/mysql v0.0.0-20171007150158-ee359f95877b
|
||||
github.com/go-xorm/builder v0.0.0-20170519032130-c8871c857d25 // indirect
|
||||
github.com/go-xorm/core v0.5.8
|
||||
github.com/go-xorm/tests v0.5.6 // indirect
|
||||
github.com/go-xorm/xorm v0.0.0-20170930012613-29d4a0330a00
|
||||
github.com/go-xorm/xorm-redis-cache v0.0.0-20180727005610-859b313566b2
|
||||
github.com/google/go-cmp v0.2.0 // indirect
|
||||
github.com/imdario/mergo v0.3.6
|
||||
github.com/joho/godotenv v1.3.0 // indirect
|
||||
github.com/labstack/echo v3.1.0+incompatible
|
||||
github.com/labstack/gommon v0.0.0-20170925052817-57409ada9da0 // indirect
|
||||
github.com/lib/pq v1.0.0 // indirect
|
||||
github.com/mattn/go-colorable v0.0.0-20170816031813-ad5389df28cd // indirect
|
||||
github.com/mattn/go-isatty v0.0.0-20170925054904-a5cdd64afdee // indirect
|
||||
github.com/mattn/go-oci8 v0.0.0-20181011085415-1a014d1384b5 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.9.0
|
||||
github.com/op/go-logging v0.0.0-20160211212156-b2cb9fa56473
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/spf13/viper v1.2.0
|
||||
github.com/stretchr/testify v0.0.0-20171231124224-87b1dfb5b2fa
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect
|
||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44
|
||||
gopkg.in/testfixtures.v2 v2.4.5
|
||||
4d63.com/tz v1.2.0
|
||||
code.vikunja.io/web v0.0.0-20200809154828-8767618f181f
|
||||
gitea.com/xorm/xorm-redis-cache v0.2.0
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2
|
||||
github.com/client9/misspell v0.3.4
|
||||
github.com/cweill/gotests v1.5.3
|
||||
github.com/d4l3k/messagediff v1.2.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/fzipp/gocyclo v0.3.0
|
||||
github.com/gabriel-vasile/mimetype v1.1.1
|
||||
github.com/getsentry/sentry-go v0.7.0
|
||||
github.com/go-errors/errors v1.1.1
|
||||
github.com/go-openapi/swag v0.19.9 // indirect
|
||||
github.com/go-redis/redis/v7 v7.4.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.4.1
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/go-cmp v0.5.2 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20200809085317-e36bfde3bb78
|
||||
github.com/iancoleman/strcase v0.1.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/jgautheron/goconst v0.0.0-20200920201509-8f5268ce89d5
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.1.17
|
||||
github.com/labstack/gommon v0.3.0
|
||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
||||
github.com/lib/pq v1.8.0
|
||||
github.com/magefile/mage v1.10.0
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.4
|
||||
github.com/mitchellh/mapstructure v1.3.2 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/onsi/ginkgo v1.13.0 // indirect
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pelletier/go-toml v1.8.0 // indirect
|
||||
github.com/pquerna/otp v1.2.0
|
||||
github.com/prometheus/client_golang v1.8.0
|
||||
github.com/samedi/caldav-go v3.0.0+incompatible
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
|
||||
github.com/spf13/afero v1.4.1
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/swaggo/swag v1.6.8
|
||||
github.com/ulule/limiter/v3 v3.5.0
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
||||
golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0 // indirect
|
||||
golang.org/x/sync v0.0.0-20201008141435-b3e1573b7520
|
||||
golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.57.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c
|
||||
honnef.co/go/tools v0.0.1-2020.1.5
|
||||
src.techknowlogick.com/xgo v1.1.1-0.20200811225412-bff6512e7c9c
|
||||
src.techknowlogick.com/xormigrate v1.3.0
|
||||
xorm.io/builder v0.3.7
|
||||
xorm.io/core v0.7.3
|
||||
xorm.io/xorm v1.0.2
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4
|
||||
github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
|
||||
github.com/hpcloud/tail => github.com/jeffbean/tail v1.0.1 // See https://github.com/hpcloud/tail/pull/159
|
||||
github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
|
||||
gopkg.in/fsnotify.v1 => github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 // See https://github.com/fsnotify/fsnotify/issues/328 and https://github.com/golang/go/issues/26904
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
||||
925
magefile.go
Normal file
925
magefile.go
Normal file
@@ -0,0 +1,925 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// +build mage
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"github.com/magefile/mage/mg"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
PACKAGE = `code.vikunja.io/api`
|
||||
DIST = `dist`
|
||||
)
|
||||
|
||||
var (
|
||||
Goflags = []string{
|
||||
"-v",
|
||||
}
|
||||
Executable = "vikunja"
|
||||
Ldflags = ""
|
||||
Tags = ""
|
||||
VersionNumber = "dev"
|
||||
Version = "master" // This holds the built version, master by default, when building from a tag or release branch, their name
|
||||
BinLocation = ""
|
||||
PkgVersion = "master"
|
||||
ApiPackages = []string{}
|
||||
RootPath = ""
|
||||
GoFiles = []string{}
|
||||
|
||||
// Aliases are mage aliases of targets
|
||||
Aliases = map[string]interface{}{
|
||||
"build": Build.Build,
|
||||
"do-the-swag": DoTheSwag,
|
||||
"check:got-swag": Check.GotSwag,
|
||||
"release:os-package": Release.OsPackage,
|
||||
"dev:create-migration": Dev.CreateMigration,
|
||||
"generate-docs": GenerateDocs,
|
||||
}
|
||||
)
|
||||
|
||||
func setVersion() {
|
||||
versionCmd := exec.Command("git", "describe", "--tags", "--always", "--abbrev=10")
|
||||
version, err := versionCmd.Output()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting version: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
VersionNumber = strings.Trim(string(version), "\n")
|
||||
VersionNumber = strings.Replace(VersionNumber, "-", "+", 1)
|
||||
VersionNumber = strings.Replace(VersionNumber, "-g", "-", 1)
|
||||
|
||||
if os.Getenv("DRONE_TAG") != "" {
|
||||
Version = os.Getenv("DRONE_TAG")
|
||||
} else if os.Getenv("DRONE_BRANCH") != "" {
|
||||
Version = strings.Replace(os.Getenv("DRONE_BRANCH"), "release/v", "", 1)
|
||||
}
|
||||
}
|
||||
|
||||
func setBinLocation() {
|
||||
if os.Getenv("DRONE_WORKSPACE") != "" {
|
||||
BinLocation = DIST + `/binaries/` + Executable + `-` + Version + `-linux-amd64`
|
||||
} else {
|
||||
BinLocation = Executable
|
||||
}
|
||||
}
|
||||
|
||||
func setPkgVersion() {
|
||||
if Version == "master" {
|
||||
PkgVersion = VersionNumber
|
||||
}
|
||||
}
|
||||
|
||||
func setExecutable() {
|
||||
if runtime.GOOS == "windows" {
|
||||
Executable += ".exe"
|
||||
}
|
||||
}
|
||||
|
||||
func setApiPackages() {
|
||||
cmd := exec.Command("go", "list", "all")
|
||||
pkgs, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting packages: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, p := range strings.Split(string(pkgs), "\n") {
|
||||
if strings.Contains(p, "code.vikunja.io/api") && !strings.Contains(p, "code.vikunja.io/api/pkg/integrations") {
|
||||
ApiPackages = append(ApiPackages, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setRootPath() {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting pwd: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.Setenv("VIKUNJA_SERVICE_ROOTPATH", pwd); err != nil {
|
||||
fmt.Printf("Error setting root path: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
RootPath = pwd
|
||||
}
|
||||
|
||||
func setGoFiles() {
|
||||
// GOFILES := $(shell find . -name "*.go" -type f ! -path "*/bindata.go")
|
||||
cmd := exec.Command("find", ".", "-name", "*.go", "-type", "f", "!", "-path", "*/bindata.go")
|
||||
files, err := cmd.Output()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting go files: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, f := range strings.Split(string(files), "\n") {
|
||||
if strings.HasSuffix(f, ".go") {
|
||||
GoFiles = append(GoFiles, RootPath+strings.TrimLeft(f, "."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Some variables can always get initialized, so we do just that.
|
||||
func init() {
|
||||
setExecutable()
|
||||
setRootPath()
|
||||
}
|
||||
|
||||
// Some variables have external dependencies (like git) which may not always be available.
|
||||
func initVars() {
|
||||
Tags = os.Getenv("TAGS")
|
||||
setVersion()
|
||||
setBinLocation()
|
||||
setPkgVersion()
|
||||
setApiPackages()
|
||||
setGoFiles()
|
||||
Ldflags = `-X "` + PACKAGE + `/pkg/version.Version=` + VersionNumber + `" -X "main.Tags=` + Tags + `"`
|
||||
}
|
||||
|
||||
func runAndStreamOutput(cmd string, args ...string) {
|
||||
c := exec.Command(cmd, args...)
|
||||
|
||||
c.Env = os.Environ()
|
||||
c.Dir = RootPath
|
||||
|
||||
fmt.Printf("%s\n\n", c.String())
|
||||
|
||||
stdout, _ := c.StdoutPipe()
|
||||
errbuf := bytes.Buffer{}
|
||||
c.Stderr = &errbuf
|
||||
c.Start()
|
||||
|
||||
reader := bufio.NewReader(stdout)
|
||||
line, err := reader.ReadString('\n')
|
||||
for err == nil {
|
||||
fmt.Print(line)
|
||||
line, err = reader.ReadString('\n')
|
||||
}
|
||||
|
||||
if err := c.Wait(); err != nil {
|
||||
fmt.Printf(errbuf.String())
|
||||
fmt.Printf("Error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Will check if the tool exists and if not install it from the provided import path
|
||||
// If any errors occur, it will exit with a status code of 1.
|
||||
func checkAndInstallGoTool(tool, importPath string) {
|
||||
if err := exec.Command(tool).Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
|
||||
fmt.Printf("%s not installed, installing %s...\n", tool, importPath)
|
||||
if err := exec.Command("go", "install", Goflags[0], importPath).Run(); err != nil {
|
||||
fmt.Printf("Error installing %s\n", tool)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("Installed.")
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates a hash of a file
|
||||
func calculateSha256FileHash(path string) (hash string, err error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// Copy the src file to dst. Any existing file will be overwritten and will not
|
||||
// copy file attributes.
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
si, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chmod(dst, si.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return out.Close()
|
||||
}
|
||||
|
||||
// os.Rename has issues with moving files between docker volumes.
|
||||
// Because of this limitaion, it fails in drone.
|
||||
// Source: https://gist.github.com/var23rav/23ae5d0d4d830aff886c3c970b8f6c6b
|
||||
func moveFile(src, dst string) error {
|
||||
inputFile, err := os.Open(src)
|
||||
defer inputFile.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't open source file: %s", err)
|
||||
}
|
||||
|
||||
outputFile, err := os.Create(dst)
|
||||
defer outputFile.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't open dest file: %s", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(outputFile, inputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing to output file failed: %s", err)
|
||||
}
|
||||
|
||||
// Make sure to copy copy the permissions of the original file as well
|
||||
si, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chmod(dst, si.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The copy was successful, so now delete the original file
|
||||
err = os.Remove(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed removing original file: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Formats the code using go fmt
|
||||
func Fmt() {
|
||||
mg.Deps(initVars)
|
||||
args := append([]string{"-s", "-w"}, GoFiles...)
|
||||
runAndStreamOutput("gofmt", args...)
|
||||
}
|
||||
|
||||
// Generates the swagger docs from the code annotations
|
||||
func DoTheSwag() {
|
||||
mg.Deps(initVars)
|
||||
checkAndInstallGoTool("swag", "github.com/swaggo/swag/cmd/swag")
|
||||
runAndStreamOutput("swag", "init", "-g", "./pkg/routes/routes.go", "--parseDependency", "-d", RootPath, "-o", RootPath+"/pkg/swagger")
|
||||
}
|
||||
|
||||
type Test mg.Namespace
|
||||
|
||||
// Runs all tests except integration tests
|
||||
func (Test) Unit() {
|
||||
mg.Deps(initVars)
|
||||
// We run everything sequentially and not in parallel to prevent issues with real test databases
|
||||
args := append([]string{"test", Goflags[0], "-p", "1"}, ApiPackages...)
|
||||
runAndStreamOutput("go", args...)
|
||||
}
|
||||
|
||||
// Runs the tests and builds the coverage html file from coverage output
|
||||
func (Test) Coverage() {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(Test.Unit)
|
||||
runAndStreamOutput("go", "tool", "cover", "-html=cover.out", "-o", "cover.html")
|
||||
}
|
||||
|
||||
// Runs the integration tests
|
||||
func (Test) Integration() {
|
||||
mg.Deps(initVars)
|
||||
// We run everything sequentially and not in parallel to prevent issues with real test databases
|
||||
runAndStreamOutput("go", "test", Goflags[0], "-p", "1", PACKAGE+"/pkg/integrations")
|
||||
}
|
||||
|
||||
type Check mg.Namespace
|
||||
|
||||
// Checks if the swagger docs need to be re-generated from the code annotations
|
||||
func (Check) GotSwag() {
|
||||
mg.Deps(initVars)
|
||||
// The check is pretty cheaply done: We take the hash of the swagger.json file, generate the docs,
|
||||
// hash the file again and compare the two hashes to see if anything changed. If that's the case,
|
||||
// regenerating the docs is necessary.
|
||||
// swag is not capable of just outputting the generated docs to stdout, therefore we need to do it this way.
|
||||
// Another drawback of this is obviously it will only work once - we're not resetting the newly generated
|
||||
// docs after the check. This behaviour is good enough for ci though.
|
||||
oldHash, err := calculateSha256FileHash(RootPath + "/pkg/swagger/swagger.json")
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting old hash of the swagger docs: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
DoTheSwag()
|
||||
|
||||
newHash, err := calculateSha256FileHash(RootPath + "/pkg/swagger/swagger.json")
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting new hash of the swagger docs: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if oldHash != newHash {
|
||||
fmt.Println("Swagger docs are not up to date.")
|
||||
fmt.Println("Please run 'mage do-the-swag' and commit the result.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func checkGolangCiLintInstalled() {
|
||||
mg.Deps(initVars)
|
||||
if err := exec.Command("golangci-lint").Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
|
||||
fmt.Println("Please manually install golangci-lint by running")
|
||||
fmt.Println("curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.31.0")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (Check) Golangci() {
|
||||
checkGolangCiLintInstalled()
|
||||
runAndStreamOutput("golangci-lint", "run")
|
||||
}
|
||||
|
||||
func (Check) GolangciFix() {
|
||||
checkGolangCiLintInstalled()
|
||||
runAndStreamOutput("golangci-lint", "run", "--fix")
|
||||
}
|
||||
|
||||
// Runs fmt-check, lint, got-swag, misspell-check, ineffasign-check, gocyclo-check, static-check, gosec-check, goconst-check all in parallel
|
||||
func (Check) All() {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(
|
||||
Check.Golangci,
|
||||
Check.GotSwag,
|
||||
)
|
||||
}
|
||||
|
||||
type Build mg.Namespace
|
||||
|
||||
// Cleans all build, executable and bindata files
|
||||
func (Build) Clean() error {
|
||||
mg.Deps(initVars)
|
||||
if err := exec.Command("go", "clean", "./...").Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Remove(Executable); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(DIST); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(BinLocation); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generates static content into the final binary
|
||||
func (Build) Generate() {
|
||||
mg.Deps(initVars)
|
||||
runAndStreamOutput("go", "generate", PACKAGE+"/pkg/static")
|
||||
}
|
||||
|
||||
// Builds a vikunja binary, ready to run
|
||||
func (Build) Build() {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(Build.Generate)
|
||||
runAndStreamOutput("go", "build", Goflags[0], "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
|
||||
}
|
||||
|
||||
type Release mg.Namespace
|
||||
|
||||
// Runs all steps in the right order to create release packages for various platforms
|
||||
func (Release) Release(ctx context.Context) error {
|
||||
mg.Deps(initVars)
|
||||
mg.Deps(Build.Generate, Release.Dirs)
|
||||
mg.Deps(Release.Windows, Release.Linux, Release.Darwin)
|
||||
|
||||
// Run compiling in parallel to speed it up
|
||||
errs, _ := errgroup.WithContext(ctx)
|
||||
errs.Go((Release{}).Windows)
|
||||
errs.Go((Release{}).Linux)
|
||||
errs.Go((Release{}).Darwin)
|
||||
if err := errs.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := (Release{}).Compress(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (Release{}).Copy(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (Release{}).Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (Release{}).OsPackage(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := (Release{}).Zip(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates all directories needed to release vikunja
|
||||
func (Release) Dirs() error {
|
||||
for _, d := range []string{"binaries", "release", "zip"} {
|
||||
if err := os.MkdirAll(RootPath+"/"+DIST+"/"+d, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runXgo(targets string) error {
|
||||
mg.Deps(initVars)
|
||||
checkAndInstallGoTool("xgo", "src.techknowlogick.com/xgo")
|
||||
|
||||
extraLdflags := `-linkmode external -extldflags "-static" `
|
||||
|
||||
// See https://github.com/techknowlogick/xgo/issues/79
|
||||
if strings.HasPrefix(targets, "darwin") {
|
||||
extraLdflags = ""
|
||||
}
|
||||
|
||||
runAndStreamOutput("xgo",
|
||||
"-dest", RootPath+"/"+DIST+"/binaries",
|
||||
"-tags", "netgo "+Tags,
|
||||
"-ldflags", extraLdflags+Ldflags,
|
||||
"-targets", targets,
|
||||
"-out", Executable+"-"+Version,
|
||||
RootPath)
|
||||
if os.Getenv("DRONE_WORKSPACE") != "" {
|
||||
return filepath.Walk("/build/", func(path string, info os.FileInfo, err error) error {
|
||||
// Skip directories
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return moveFile(path, RootPath+"/"+DIST+"/binaries/"+info.Name())
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Builds binaries for windows
|
||||
func (Release) Windows() error {
|
||||
return runXgo("windows/*")
|
||||
}
|
||||
|
||||
// Builds binaries for linux
|
||||
func (Release) Linux() error {
|
||||
return runXgo("linux/*")
|
||||
}
|
||||
|
||||
// Builds binaries for darwin
|
||||
func (Release) Darwin() error {
|
||||
return runXgo("darwin/*")
|
||||
}
|
||||
|
||||
// Compresses the built binaries in dist/binaries/ to reduce their filesize
|
||||
func (Release) Compress(ctx context.Context) error {
|
||||
// $(foreach file,$(filter-out $(wildcard $(wildcard $(DIST)/binaries/$(EXECUTABLE)-*mips*)),$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*)), upx -9 $(file);)
|
||||
|
||||
errs, _ := errgroup.WithContext(ctx)
|
||||
|
||||
filepath.Walk(RootPath+"/"+DIST+"/binaries/", func(path string, info os.FileInfo, err error) error {
|
||||
// Only executable files
|
||||
if !strings.Contains(info.Name(), Executable) {
|
||||
return nil
|
||||
}
|
||||
// No mips or s390x for you today
|
||||
if strings.Contains(info.Name(), "mips") || strings.Contains(info.Name(), "s390x") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Runs compressing in parallel since upx is single-threaded
|
||||
errs.Go(func() error {
|
||||
runAndStreamOutput("chmod", "+x", path) // Make sure all binaries are executable. Sometimes the CI does weired things and they're not.
|
||||
runAndStreamOutput("upx", "-9", path)
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return errs.Wait()
|
||||
}
|
||||
|
||||
// Copies all built binaries to dist/release/ in preparation for creating the os packages
|
||||
func (Release) Copy() error {
|
||||
return filepath.Walk(RootPath+"/"+DIST+"/binaries/", func(path string, info os.FileInfo, err error) error {
|
||||
// Only executable files
|
||||
if !strings.Contains(info.Name(), Executable) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return copyFile(path, RootPath+"/"+DIST+"/release/"+info.Name())
|
||||
})
|
||||
}
|
||||
|
||||
// Creates sha256 checksum files for each binary in dist/release/
|
||||
func (Release) Check() error {
|
||||
p := RootPath + "/" + DIST + "/release/"
|
||||
return filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Create(p + info.Name() + ".sha256")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hash, err := calculateSha256FileHash(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = f.WriteString(hash + " " + info.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.Close()
|
||||
})
|
||||
}
|
||||
|
||||
// Creates a folder for each
|
||||
func (Release) OsPackage() error {
|
||||
p := RootPath + "/" + DIST + "/release/"
|
||||
|
||||
// We first put all files in a map to then iterate over it since the walk function would otherwise also iterate
|
||||
// over the newly created files, creating some kind of endless loop.
|
||||
bins := make(map[string]os.FileInfo)
|
||||
if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
|
||||
if strings.Contains(info.Name(), ".sha256") || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
bins[path] = info
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for path, info := range bins {
|
||||
folder := p + info.Name() + "-full/"
|
||||
if err := os.Mkdir(folder, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := moveFile(p+info.Name()+".sha256", folder+info.Name()+".sha256"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := moveFile(path, folder+info.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyFile(RootPath+"/config.yml.sample", folder+"config.yml.sample"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := copyFile(RootPath+"/LICENSE", folder+"LICENSE"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a zip file from all os-package folders in dist/release
|
||||
func (Release) Zip() error {
|
||||
p := RootPath + "/" + DIST + "/release/"
|
||||
if err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() || info.Name() == "release" {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Zipping %s...\n", info.Name())
|
||||
|
||||
c := exec.Command("zip", "-r", RootPath+"/"+DIST+"/zip/"+info.Name(), ".", "-i", "*")
|
||||
c.Dir = path
|
||||
out, err := c.Output()
|
||||
fmt.Print(string(out))
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates a debian repo structure
|
||||
func (Release) Reprepro() {
|
||||
mg.Deps(setVersion, setBinLocation)
|
||||
runAndStreamOutput("reprepro_expect", "debian", "includedeb", "strech", RootPath+"/"+DIST+"/os-packages/"+Executable+"_"+strings.ReplaceAll(VersionNumber, "v0", "0")+"_amd64.deb")
|
||||
}
|
||||
|
||||
// Creates deb, rpm and apk packages
|
||||
func (Release) Packages() error {
|
||||
mg.Deps(initVars)
|
||||
var err error
|
||||
binpath := "nfpm"
|
||||
err = exec.Command(binpath).Run()
|
||||
if err != nil && strings.Contains(err.Error(), "executable file not found") {
|
||||
binpath = "/nfpm"
|
||||
err = exec.Command(binpath).Run()
|
||||
}
|
||||
if err != nil && strings.Contains(err.Error(), "executable file not found") {
|
||||
fmt.Println("Please manually install nfpm by running")
|
||||
fmt.Println("curl -sfL https://install.goreleaser.com/github.com/goreleaser/nfpm.sh | sh -s -- -b $(go env GOPATH)/bin")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Because nfpm does not support templating, we replace the values in the config file and restore it after running
|
||||
nfpmConfigPath := RootPath + "/nfpm.yaml"
|
||||
nfpmconfig, err := ioutil.ReadFile(nfpmConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fixedConfig := strings.ReplaceAll(string(nfpmconfig), "<version>", VersionNumber)
|
||||
fixedConfig = strings.ReplaceAll(fixedConfig, "<binlocation>", BinLocation)
|
||||
if err := ioutil.WriteFile(nfpmConfigPath, []byte(fixedConfig), 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
releasePath := RootPath + "/" + DIST + "/os-packages/"
|
||||
if err := os.MkdirAll(releasePath, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
runAndStreamOutput(binpath, "pkg", "--packager", "deb", "--target", releasePath)
|
||||
runAndStreamOutput(binpath, "pkg", "--packager", "rpm", "--target", releasePath)
|
||||
runAndStreamOutput(binpath, "pkg", "--packager", "apk", "--target", releasePath)
|
||||
|
||||
return ioutil.WriteFile(nfpmConfigPath, nfpmconfig, 0)
|
||||
}
|
||||
|
||||
type Dev mg.Namespace
|
||||
|
||||
// Creates a new bare db migration skeleton in pkg/migration with the current date
|
||||
func (Dev) CreateMigration() error {
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("Enter the name of the struct: ")
|
||||
str, _ := reader.ReadString('\n')
|
||||
str = strings.Trim(str, "\n")
|
||||
|
||||
date := time.Now().Format("20060102150405")
|
||||
|
||||
migration := `// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"src.techknowlogick.com/xormigrate"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type ` + str + date + ` struct {
|
||||
}
|
||||
|
||||
func (` + str + date + `) TableName() string {
|
||||
return "` + str + `"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "` + date + `",
|
||||
Description: "",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(` + str + date + `{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
`
|
||||
f, err := os.Create(RootPath + "/pkg/migration/" + date + ".go")
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = f.WriteString(migration)
|
||||
return err
|
||||
}
|
||||
|
||||
type configOption struct {
|
||||
key string
|
||||
description string
|
||||
defaultValue string
|
||||
|
||||
children []*configOption
|
||||
}
|
||||
|
||||
func parseYamlConfigNode(node *yaml.Node) (config *configOption) {
|
||||
config = &configOption{
|
||||
key: node.Value,
|
||||
description: strings.ReplaceAll(node.HeadComment, "# ", ""),
|
||||
}
|
||||
|
||||
valMap := make(map[string]*configOption)
|
||||
|
||||
var lastOption *configOption
|
||||
|
||||
for i, n2 := range node.Content {
|
||||
coo := &configOption{
|
||||
key: n2.Value,
|
||||
description: strings.ReplaceAll(n2.HeadComment, "# ", ""),
|
||||
}
|
||||
|
||||
// If there's a key in valMap for the current key we should use that to append etc
|
||||
// Else we just create a new configobject
|
||||
co, exists := valMap[n2.Value]
|
||||
if exists {
|
||||
co.description = coo.description
|
||||
} else {
|
||||
valMap[n2.Value] = coo
|
||||
config.children = append(config.children, coo)
|
||||
}
|
||||
|
||||
// fmt.Println(i, coo.key, coo.description, n2.Value)
|
||||
|
||||
if i%2 == 0 {
|
||||
lastOption = coo
|
||||
continue
|
||||
} else {
|
||||
lastOption.defaultValue = n2.Value
|
||||
}
|
||||
|
||||
if i-1 >= 0 && i-1 <= len(node.Content) && node.Content[i-1].Value != "" {
|
||||
coo.defaultValue = n2.Value
|
||||
coo.key = node.Content[i-1].Value
|
||||
}
|
||||
|
||||
if len(n2.Content) > 0 {
|
||||
for _, n := range n2.Content {
|
||||
coo.children = append(coo.children, parseYamlConfigNode(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func printConfig(config []*configOption, level int) (rendered string) {
|
||||
|
||||
// Keep track of what we already printed to prevent printing things twice
|
||||
printed := make(map[string]bool)
|
||||
|
||||
for _, option := range config {
|
||||
|
||||
if option.key != "" {
|
||||
|
||||
// Filter out all config objects where the default value == key
|
||||
// Yaml is weired: It gives you a slice with an entry each for the key and their value.
|
||||
if printed[option.key] {
|
||||
continue
|
||||
}
|
||||
|
||||
if level == 0 {
|
||||
rendered += "---\n\n"
|
||||
}
|
||||
|
||||
rendered += "#"
|
||||
for i := 0; i <= level; i++ {
|
||||
rendered += "#"
|
||||
}
|
||||
rendered += " " + option.key + "\n\n"
|
||||
|
||||
if option.description != "" {
|
||||
rendered += option.description + "\n\n"
|
||||
}
|
||||
|
||||
// Top level config values never have a default value
|
||||
if level > 0 {
|
||||
rendered += "Default: `" + option.defaultValue
|
||||
if option.defaultValue == "" {
|
||||
rendered += "<empty>"
|
||||
}
|
||||
rendered += "`\n"
|
||||
}
|
||||
}
|
||||
|
||||
printed[option.key] = true
|
||||
rendered += "\n" + printConfig(option.children, level+1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
configDocPath = `docs/content/doc/setup/config.md`
|
||||
configInjectComment = `<!-- Generated config will be injected here -->`
|
||||
)
|
||||
|
||||
// Generates the error docs from a commented config.yml.sample file in the repo root.
|
||||
func GenerateDocs() error {
|
||||
|
||||
config, err := ioutil.ReadFile("config.yml.sample")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var d yaml.Node
|
||||
err = yaml.Unmarshal(config, &d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf := []*configOption{}
|
||||
|
||||
for _, node := range d.Content {
|
||||
for _, n := range node.Content {
|
||||
co := parseYamlConfigNode(n)
|
||||
conf = append(conf, co)
|
||||
}
|
||||
}
|
||||
|
||||
renderedConfig := printConfig(conf, 0)
|
||||
|
||||
// Rebuild the config
|
||||
file, err := os.OpenFile(configDocPath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// We read the config doc up until the marker, then stop and append our generated config
|
||||
fullConfig := ""
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
t := scanner.Text()
|
||||
fullConfig += t + "\n"
|
||||
|
||||
if t == configInjectComment {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fullConfig += "\n" + renderedConfig
|
||||
|
||||
// We write the full file to prevent old content leftovers at the end
|
||||
// I know, there are probably better ways to do this.
|
||||
if err := ioutil.WriteFile(configDocPath, []byte(fullConfig), 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
73
main.go
73
main.go
@@ -1,60 +1,23 @@
|
||||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/models"
|
||||
"code.vikunja.io/api/routes"
|
||||
|
||||
"context"
|
||||
"github.com/spf13/viper"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Version sets the version to be printed to the user. Gets overwritten by "make release" or "make build" with last git commit or tag.
|
||||
var Version = "0.1"
|
||||
import "code.vikunja.io/api/pkg/cmd"
|
||||
|
||||
func main() {
|
||||
|
||||
// Init logging
|
||||
models.InitLogger()
|
||||
|
||||
// Init Config
|
||||
err := models.InitConfig()
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set Engine
|
||||
err = models.SetEngine()
|
||||
if err != nil {
|
||||
models.Log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Version notification
|
||||
models.Log.Infof("Vikunja version %s", Version)
|
||||
|
||||
// Start the webserver
|
||||
e := routes.NewEcho()
|
||||
routes.RegisterRoutes(e)
|
||||
// Start server
|
||||
go func() {
|
||||
if err := e.Start(viper.GetString("service.interface")); err != nil {
|
||||
e.Logger.Info("shutting down...")
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for interrupt signal to gracefully shutdown the server with
|
||||
// a timeout of 10 seconds.
|
||||
quit := make(chan os.Signal)
|
||||
signal.Notify(quit, os.Interrupt)
|
||||
<-quit
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
models.Log.Infof("Sutting down...")
|
||||
if err := e.Shutdown(ctx); err != nil {
|
||||
e.Logger.Fatal(err)
|
||||
}
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/spf13/viper"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// InitConfig initializes the config, sets defaults etc.
|
||||
func InitConfig() (err error) {
|
||||
|
||||
// Set defaults
|
||||
// Service config
|
||||
random, err := random(32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viper.SetDefault("service.JWTSecret", random)
|
||||
viper.SetDefault("service.interface", ":3456")
|
||||
// Database
|
||||
viper.SetDefault("database.type", "sqlite")
|
||||
viper.SetDefault("database.host", "localhost")
|
||||
viper.SetDefault("database.user", "vikunja")
|
||||
viper.SetDefault("database.password", "")
|
||||
viper.SetDefault("database.database", "vikunja")
|
||||
viper.SetDefault("database.path", "./vikunja.db")
|
||||
viper.SetDefault("database.showqueries", false)
|
||||
viper.SetDefault("database.openconnections", 100)
|
||||
// Cacher
|
||||
viper.SetDefault("cache.enabled", false)
|
||||
viper.SetDefault("cache.type", "memory")
|
||||
viper.SetDefault("cache.maxelementsize", 1000)
|
||||
viper.SetDefault("cache.redishost", "localhost:6379")
|
||||
viper.SetDefault("cache.redispassword", "")
|
||||
|
||||
// Init checking for environment variables
|
||||
viper.SetEnvPrefix("vikunja")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// Load the config file
|
||||
viper.AddConfigPath(".")
|
||||
viper.SetConfigName("config")
|
||||
err = viper.ReadInConfig()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Using defaults.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func random(length int) (string, error) {
|
||||
b := make([]byte, length)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%X", b), nil
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package models
|
||||
|
||||
// CRUDable defines the crud methods
|
||||
type CRUDable interface {
|
||||
Create(*User) error
|
||||
ReadOne() error
|
||||
ReadAll(*User) (interface{}, error)
|
||||
Update() error
|
||||
Delete() error
|
||||
}
|
||||
692
models/error.go
692
models/error.go
@@ -1,692 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HTTPErrorProcessor is executed when the defined error is thrown, it will make sure the user sees an appropriate error message and http status code
|
||||
type HTTPErrorProcessor interface {
|
||||
HTTPError() HTTPError
|
||||
}
|
||||
|
||||
// HTTPError holds informations about an http error
|
||||
type HTTPError struct {
|
||||
HTTPCode int `json:"-"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// =====================
|
||||
// User Operation Errors
|
||||
// =====================
|
||||
|
||||
// ErrUsernameExists represents a "UsernameAlreadyExists" kind of error.
|
||||
type ErrUsernameExists struct {
|
||||
UserID int64
|
||||
Username string
|
||||
}
|
||||
|
||||
// IsErrUsernameExists checks if an error is a ErrUsernameExists.
|
||||
func IsErrUsernameExists(err error) bool {
|
||||
_, ok := err.(ErrUsernameExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUsernameExists) Error() string {
|
||||
return fmt.Sprintf("User with that username already exists [user id: %d, username: %s]", err.UserID, err.Username)
|
||||
}
|
||||
|
||||
// ErrorCodeUsernameExists holds the unique world-error code of this error
|
||||
const ErrorCodeUsernameExists = 1001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUsernameExists) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUsernameExists, Message: "A user with this username already exists."}
|
||||
}
|
||||
|
||||
// ErrUserEmailExists represents a "UserEmailExists" kind of error.
|
||||
type ErrUserEmailExists struct {
|
||||
UserID int64
|
||||
Email string
|
||||
}
|
||||
|
||||
// IsErrUserEmailExists checks if an error is a ErrUserEmailExists.
|
||||
func IsErrUserEmailExists(err error) bool {
|
||||
_, ok := err.(ErrUserEmailExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserEmailExists) Error() string {
|
||||
return fmt.Sprintf("User with that email already exists [user id: %d, email: %s]", err.UserID, err.Email)
|
||||
}
|
||||
|
||||
// ErrorCodeUserEmailExists holds the unique world-error code of this error
|
||||
const ErrorCodeUserEmailExists = 1002
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserEmailExists) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrorCodeUserEmailExists, Message: "A user with this email address already exists."}
|
||||
}
|
||||
|
||||
// ErrNoUsernamePassword represents a "NoUsernamePassword" kind of error.
|
||||
type ErrNoUsernamePassword struct{}
|
||||
|
||||
// IsErrNoUsernamePassword checks if an error is a ErrNoUsernamePassword.
|
||||
func IsErrNoUsernamePassword(err error) bool {
|
||||
_, ok := err.(ErrNoUsernamePassword)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNoUsernamePassword) Error() string {
|
||||
return fmt.Sprintf("No username and password provided")
|
||||
}
|
||||
|
||||
// ErrCodeNoUsernamePassword holds the unique world-error code of this error
|
||||
const ErrCodeNoUsernamePassword = 1004
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrNoUsernamePassword) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNoUsernamePassword, Message: "Please specify a username and a password."}
|
||||
}
|
||||
|
||||
// ErrUserDoesNotExist represents a "UserDoesNotExist" kind of error.
|
||||
type ErrUserDoesNotExist struct {
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrUserDoesNotExist checks if an error is a ErrUserDoesNotExist.
|
||||
func IsErrUserDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrUserDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("User does not exist [user id: %d]", err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeUserDoesNotExist holds the unique world-error code of this error
|
||||
const ErrCodeUserDoesNotExist = 1005
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserDoesNotExist) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeUserDoesNotExist, Message: "The user does not exist."}
|
||||
}
|
||||
|
||||
// ErrCouldNotGetUserID represents a "ErrCouldNotGetUserID" kind of error.
|
||||
type ErrCouldNotGetUserID struct{}
|
||||
|
||||
// IsErrCouldNotGetUserID checks if an error is a ErrCouldNotGetUserID.
|
||||
func IsErrCouldNotGetUserID(err error) bool {
|
||||
_, ok := err.(ErrCouldNotGetUserID)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrCouldNotGetUserID) Error() string {
|
||||
return fmt.Sprintf("Could not get user ID")
|
||||
}
|
||||
|
||||
// ErrCodeCouldNotGetUserID holds the unique world-error code of this error
|
||||
const ErrCodeCouldNotGetUserID = 1006
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrCouldNotGetUserID) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCouldNotGetUserID, Message: "Could not get user id."}
|
||||
}
|
||||
|
||||
// ErrCannotDeleteLastUser represents a "ErrCannotDeleteLastUser" kind of error.
|
||||
type ErrCannotDeleteLastUser struct{}
|
||||
|
||||
// IsErrCannotDeleteLastUser checks if an error is a ErrCannotDeleteLastUser.
|
||||
func IsErrCannotDeleteLastUser(err error) bool {
|
||||
_, ok := err.(ErrCannotDeleteLastUser)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrCannotDeleteLastUser) Error() string {
|
||||
return fmt.Sprintf("Cannot delete last user")
|
||||
}
|
||||
|
||||
// ErrCodeCannotDeleteLastUser holds the unique world-error code of this error
|
||||
const ErrCodeCannotDeleteLastUser = 1007
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrCannotDeleteLastUser) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeCannotDeleteLastUser, Message: "Cannot delete the last user on the server."}
|
||||
}
|
||||
|
||||
// ===================
|
||||
// Empty things errors
|
||||
// ===================
|
||||
|
||||
// ErrIDCannotBeZero represents a "IDCannotBeZero" kind of error. Used if an ID (of something, not defined) is 0 where it should not.
|
||||
type ErrIDCannotBeZero struct{}
|
||||
|
||||
// IsErrIDCannotBeZero checks if an error is a ErrIDCannotBeZero.
|
||||
func IsErrIDCannotBeZero(err error) bool {
|
||||
_, ok := err.(ErrIDCannotBeZero)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrIDCannotBeZero) Error() string {
|
||||
return fmt.Sprintf("ID cannot be empty or 0")
|
||||
}
|
||||
|
||||
// ErrCodeIDCannotBeZero holds the unique world-error code of this error
|
||||
const ErrCodeIDCannotBeZero = 2001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrIDCannotBeZero) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeIDCannotBeZero, Message: "The ID cannot be empty or 0."}
|
||||
}
|
||||
|
||||
// ===========
|
||||
// List errors
|
||||
// ===========
|
||||
|
||||
// ErrListDoesNotExist represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist.
|
||||
type ErrListDoesNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrListDoesNotExist checks if an error is a ErrListDoesNotExist.
|
||||
func IsErrListDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrListDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrListDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("List does not exist [ID: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ErrCodeListDoesNotExist holds the unique world-error code of this error
|
||||
const ErrCodeListDoesNotExist = 3001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrListDoesNotExist) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListDoesNotExist, Message: "This list does not exist."}
|
||||
}
|
||||
|
||||
// ErrNeedToHaveListReadAccess represents an error, where the user dont has read access to that List
|
||||
type ErrNeedToHaveListReadAccess struct {
|
||||
ListID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrNeedToHaveListReadAccess checks if an error is a ErrListDoesNotExist.
|
||||
func IsErrNeedToHaveListReadAccess(err error) bool {
|
||||
_, ok := err.(ErrNeedToHaveListReadAccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNeedToHaveListReadAccess) Error() string {
|
||||
return fmt.Sprintf("User needs to have read access to that list [ListID: %d, UserID: %d]", err.ListID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeNeedToHaveListReadAccess holds the unique world-error code of this error
|
||||
const ErrCodeNeedToHaveListReadAccess = 3004
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrNeedToHaveListReadAccess) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveListReadAccess, Message: "You need to have read access to this list."}
|
||||
}
|
||||
|
||||
// ErrListTitleCannotBeEmpty represents a "ErrListTitleCannotBeEmpty" kind of error. Used if the list does not exist.
|
||||
type ErrListTitleCannotBeEmpty struct{}
|
||||
|
||||
// IsErrListTitleCannotBeEmpty checks if an error is a ErrListTitleCannotBeEmpty.
|
||||
func IsErrListTitleCannotBeEmpty(err error) bool {
|
||||
_, ok := err.(ErrListTitleCannotBeEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrListTitleCannotBeEmpty) Error() string {
|
||||
return fmt.Sprintf("List title cannot be empty.")
|
||||
}
|
||||
|
||||
// ErrCodeListTitleCannotBeEmpty holds the unique world-error code of this error
|
||||
const ErrCodeListTitleCannotBeEmpty = 3005
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrListTitleCannotBeEmpty) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTitleCannotBeEmpty, Message: "You must provide at least a list title."}
|
||||
}
|
||||
|
||||
// ================
|
||||
// List task errors
|
||||
// ================
|
||||
|
||||
// ErrListTaskCannotBeEmpty represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist.
|
||||
type ErrListTaskCannotBeEmpty struct{}
|
||||
|
||||
// IsErrListTaskCannotBeEmpty checks if an error is a ErrListDoesNotExist.
|
||||
func IsErrListTaskCannotBeEmpty(err error) bool {
|
||||
_, ok := err.(ErrListTaskCannotBeEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrListTaskCannotBeEmpty) Error() string {
|
||||
return fmt.Sprintf("List task text cannot be empty.")
|
||||
}
|
||||
|
||||
// ErrCodeListTaskCannotBeEmpty holds the unique world-error code of this error
|
||||
const ErrCodeListTaskCannotBeEmpty = 4001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrListTaskCannotBeEmpty) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeListTaskCannotBeEmpty, Message: "You must provide at least a list task text."}
|
||||
}
|
||||
|
||||
// ErrListTaskDoesNotExist represents a "ErrListDoesNotExist" kind of error. Used if the list does not exist.
|
||||
type ErrListTaskDoesNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrListTaskDoesNotExist checks if an error is a ErrListDoesNotExist.
|
||||
func IsErrListTaskDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrListTaskDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrListTaskDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("List task does not exist. [ID: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ErrCodeListTaskDoesNotExist holds the unique world-error code of this error
|
||||
const ErrCodeListTaskDoesNotExist = 4002
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrListTaskDoesNotExist) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeListTaskDoesNotExist, Message: "This list task does not exist"}
|
||||
}
|
||||
|
||||
// =================
|
||||
// Namespace errors
|
||||
// =================
|
||||
|
||||
// ErrNamespaceDoesNotExist represents a "ErrNamespaceDoesNotExist" kind of error. Used if the namespace does not exist.
|
||||
type ErrNamespaceDoesNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrNamespaceDoesNotExist checks if an error is a ErrNamespaceDoesNotExist.
|
||||
func IsErrNamespaceDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrNamespaceDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNamespaceDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("Namespace does not exist [ID: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ErrCodeNamespaceDoesNotExist holds the unique world-error code of this error
|
||||
const ErrCodeNamespaceDoesNotExist = 5001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrNamespaceDoesNotExist) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeNamespaceDoesNotExist, Message: "Namespace not found."}
|
||||
}
|
||||
|
||||
// ErrUserDoesNotHaveAccessToNamespace represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
|
||||
type ErrUserDoesNotHaveAccessToNamespace struct {
|
||||
NamespaceID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrUserDoesNotHaveAccessToNamespace checks if an error is a ErrNamespaceDoesNotExist.
|
||||
func IsErrUserDoesNotHaveAccessToNamespace(err error) bool {
|
||||
_, ok := err.(ErrUserDoesNotHaveAccessToNamespace)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserDoesNotHaveAccessToNamespace) Error() string {
|
||||
return fmt.Sprintf("User does not have access to the namespace [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeUserDoesNotHaveAccessToNamespace holds the unique world-error code of this error
|
||||
const ErrCodeUserDoesNotHaveAccessToNamespace = 5003
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserDoesNotHaveAccessToNamespace) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToNamespace, Message: "This user does not have access to the namespace."}
|
||||
}
|
||||
|
||||
// ErrNamespaceNameCannotBeEmpty represents an error, where a namespace name is empty.
|
||||
type ErrNamespaceNameCannotBeEmpty struct {
|
||||
NamespaceID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrNamespaceNameCannotBeEmpty checks if an error is a ErrNamespaceDoesNotExist.
|
||||
func IsErrNamespaceNameCannotBeEmpty(err error) bool {
|
||||
_, ok := err.(ErrNamespaceNameCannotBeEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNamespaceNameCannotBeEmpty) Error() string {
|
||||
return fmt.Sprintf("Namespace name cannot be empty [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeNamespaceNameCannotBeEmpty holds the unique world-error code of this error
|
||||
const ErrCodeNamespaceNameCannotBeEmpty = 5006
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrNamespaceNameCannotBeEmpty) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeNamespaceNameCannotBeEmpty, Message: "The namespace name cannot be empty."}
|
||||
}
|
||||
|
||||
// ErrNeedToHaveNamespaceReadAccess represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace)
|
||||
type ErrNeedToHaveNamespaceReadAccess struct {
|
||||
NamespaceID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrNeedToHaveNamespaceReadAccess checks if an error is a ErrNamespaceDoesNotExist.
|
||||
func IsErrNeedToHaveNamespaceReadAccess(err error) bool {
|
||||
_, ok := err.(ErrNeedToHaveNamespaceReadAccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNeedToHaveNamespaceReadAccess) Error() string {
|
||||
return fmt.Sprintf("User does not have access to that namespace [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeNeedToHaveNamespaceReadAccess holds the unique world-error code of this error
|
||||
const ErrCodeNeedToHaveNamespaceReadAccess = 5009
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrNeedToHaveNamespaceReadAccess) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeNeedToHaveNamespaceReadAccess, Message: "You need to have namespace read access to do this."}
|
||||
}
|
||||
|
||||
// ErrTeamDoesNotHaveAccessToNamespace represents an error, where the Team is not the owner of that namespace (used i.e. when deleting a namespace)
|
||||
type ErrTeamDoesNotHaveAccessToNamespace struct {
|
||||
NamespaceID int64
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
// IsErrTeamDoesNotHaveAccessToNamespace checks if an error is a ErrNamespaceDoesNotExist.
|
||||
func IsErrTeamDoesNotHaveAccessToNamespace(err error) bool {
|
||||
_, ok := err.(ErrTeamDoesNotHaveAccessToNamespace)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamDoesNotHaveAccessToNamespace) Error() string {
|
||||
return fmt.Sprintf("Team does not have access to that namespace [NamespaceID: %d, TeamID: %d]", err.NamespaceID, err.TeamID)
|
||||
}
|
||||
|
||||
// ErrCodeTeamDoesNotHaveAccessToNamespace holds the unique world-error code of this error
|
||||
const ErrCodeTeamDoesNotHaveAccessToNamespace = 5010
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTeamDoesNotHaveAccessToNamespace) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToNamespace, Message: "You need to have access to this namespace to do this."}
|
||||
}
|
||||
|
||||
// ErrUserAlreadyHasNamespaceAccess represents an error where a user already has access to a namespace
|
||||
type ErrUserAlreadyHasNamespaceAccess struct {
|
||||
UserID int64
|
||||
NamespaceID int64
|
||||
}
|
||||
|
||||
// IsErrUserAlreadyHasNamespaceAccess checks if an error is ErrUserAlreadyHasNamespaceAccess.
|
||||
func IsErrUserAlreadyHasNamespaceAccess(err error) bool {
|
||||
_, ok := err.(ErrUserAlreadyHasNamespaceAccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserAlreadyHasNamespaceAccess) Error() string {
|
||||
return fmt.Sprintf("User already has access to that namespace. [User ID: %d, Namespace ID: %d]", err.UserID, err.NamespaceID)
|
||||
}
|
||||
|
||||
// ErrCodeUserAlreadyHasNamespaceAccess holds the unique world-error code of this error
|
||||
const ErrCodeUserAlreadyHasNamespaceAccess = 5011
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserAlreadyHasNamespaceAccess) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasNamespaceAccess, Message: "This user already has access to this namespace."}
|
||||
}
|
||||
|
||||
// ============
|
||||
// Team errors
|
||||
// ============
|
||||
|
||||
// ErrTeamNameCannotBeEmpty represents an error where a team name is empty.
|
||||
type ErrTeamNameCannotBeEmpty struct {
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
// IsErrTeamNameCannotBeEmpty checks if an error is a ErrNamespaceDoesNotExist.
|
||||
func IsErrTeamNameCannotBeEmpty(err error) bool {
|
||||
_, ok := err.(ErrTeamNameCannotBeEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamNameCannotBeEmpty) Error() string {
|
||||
return fmt.Sprintf("Team name cannot be empty [Team ID: %d]", err.TeamID)
|
||||
}
|
||||
|
||||
// ErrCodeTeamNameCannotBeEmpty holds the unique world-error code of this error
|
||||
const ErrCodeTeamNameCannotBeEmpty = 6001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTeamNameCannotBeEmpty) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeTeamNameCannotBeEmpty, Message: "The team name cannot be empty"}
|
||||
}
|
||||
|
||||
// ErrTeamDoesNotExist represents an error where a team does not exist
|
||||
type ErrTeamDoesNotExist struct {
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
// IsErrTeamDoesNotExist checks if an error is ErrTeamDoesNotExist.
|
||||
func IsErrTeamDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrTeamDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("Team does not exist [Team ID: %d]", err.TeamID)
|
||||
}
|
||||
|
||||
// ErrCodeTeamDoesNotExist holds the unique world-error code of this error
|
||||
const ErrCodeTeamDoesNotExist = 6002
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTeamDoesNotExist) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusNotFound, Code: ErrCodeTeamDoesNotExist, Message: "This team does not exist."}
|
||||
}
|
||||
|
||||
// ErrInvalidTeamRight represents an error where a team right is invalid
|
||||
type ErrInvalidTeamRight struct {
|
||||
Right TeamRight
|
||||
}
|
||||
|
||||
// IsErrInvalidTeamRight checks if an error is ErrInvalidTeamRight.
|
||||
func IsErrInvalidTeamRight(err error) bool {
|
||||
_, ok := err.(ErrInvalidTeamRight)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidTeamRight) Error() string {
|
||||
return fmt.Sprintf("Team right invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidTeamRight holds the unique world-error code of this error
|
||||
const ErrCodeInvalidTeamRight = 6003
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidTeamRight) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidTeamRight, Message: "The team right is invalid."}
|
||||
}
|
||||
|
||||
// ErrTeamAlreadyHasAccess represents an error where a team already has access to a list/namespace
|
||||
type ErrTeamAlreadyHasAccess struct {
|
||||
TeamID int64
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrTeamAlreadyHasAccess checks if an error is ErrTeamAlreadyHasAccess.
|
||||
func IsErrTeamAlreadyHasAccess(err error) bool {
|
||||
_, ok := err.(ErrTeamAlreadyHasAccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamAlreadyHasAccess) Error() string {
|
||||
return fmt.Sprintf("Team already has access. [Team ID: %d, ID: %d]", err.TeamID, err.ID)
|
||||
}
|
||||
|
||||
// ErrCodeTeamAlreadyHasAccess holds the unique world-error code of this error
|
||||
const ErrCodeTeamAlreadyHasAccess = 6004
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTeamAlreadyHasAccess) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeTeamAlreadyHasAccess, Message: "This team already has access."}
|
||||
}
|
||||
|
||||
// ErrUserIsMemberOfTeam represents an error where a user is already member of a team.
|
||||
type ErrUserIsMemberOfTeam struct {
|
||||
TeamID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrUserIsMemberOfTeam checks if an error is ErrUserIsMemberOfTeam.
|
||||
func IsErrUserIsMemberOfTeam(err error) bool {
|
||||
_, ok := err.(ErrUserIsMemberOfTeam)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserIsMemberOfTeam) Error() string {
|
||||
return fmt.Sprintf("User is already a member of that team. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeUserIsMemberOfTeam holds the unique world-error code of this error
|
||||
const ErrCodeUserIsMemberOfTeam = 6005
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserIsMemberOfTeam) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserIsMemberOfTeam, Message: "This user is already a member of that team."}
|
||||
}
|
||||
|
||||
// ErrCannotDeleteLastTeamMember represents an error where a user wants to delete the last member of a team (probably himself)
|
||||
type ErrCannotDeleteLastTeamMember struct {
|
||||
TeamID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrCannotDeleteLastTeamMember checks if an error is ErrCannotDeleteLastTeamMember.
|
||||
func IsErrCannotDeleteLastTeamMember(err error) bool {
|
||||
_, ok := err.(ErrCannotDeleteLastTeamMember)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrCannotDeleteLastTeamMember) Error() string {
|
||||
return fmt.Sprintf("Cannot delete last team member. [Team ID: %d, User ID: %d]", err.TeamID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeCannotDeleteLastTeamMember holds the unique world-error code of this error
|
||||
const ErrCodeCannotDeleteLastTeamMember = 6006
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrCannotDeleteLastTeamMember) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeCannotDeleteLastTeamMember, Message: "You cannot delete the last member of a team."}
|
||||
}
|
||||
|
||||
// ErrTeamDoesNotHaveAccessToList represents an error, where the Team is not the owner of that List (used i.e. when deleting a List)
|
||||
type ErrTeamDoesNotHaveAccessToList struct {
|
||||
ListID int64
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
// IsErrTeamDoesNotHaveAccessToList checks if an error is a ErrListDoesNotExist.
|
||||
func IsErrTeamDoesNotHaveAccessToList(err error) bool {
|
||||
_, ok := err.(ErrTeamDoesNotHaveAccessToList)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamDoesNotHaveAccessToList) Error() string {
|
||||
return fmt.Sprintf("Team does not have access to the list [ListID: %d, TeamID: %d]", err.ListID, err.TeamID)
|
||||
}
|
||||
|
||||
// ErrCodeTeamDoesNotHaveAccessToList holds the unique world-error code of this error
|
||||
const ErrCodeTeamDoesNotHaveAccessToList = 6007
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrTeamDoesNotHaveAccessToList) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeTeamDoesNotHaveAccessToList, Message: "This team does not have access to the list."}
|
||||
}
|
||||
|
||||
// ====================
|
||||
// User <-> List errors
|
||||
// ====================
|
||||
|
||||
// ErrInvalidUserRight represents an error where a user right is invalid
|
||||
type ErrInvalidUserRight struct {
|
||||
Right UserRight
|
||||
}
|
||||
|
||||
// IsErrInvalidUserRight checks if an error is ErrInvalidUserRight.
|
||||
func IsErrInvalidUserRight(err error) bool {
|
||||
_, ok := err.(ErrInvalidUserRight)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidUserRight) Error() string {
|
||||
return fmt.Sprintf("User right is invalid [Right: %d]", err.Right)
|
||||
}
|
||||
|
||||
// ErrCodeInvalidUserRight holds the unique world-error code of this error
|
||||
const ErrCodeInvalidUserRight = 7001
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrInvalidUserRight) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusBadRequest, Code: ErrCodeInvalidUserRight, Message: "The user right is invalid."}
|
||||
}
|
||||
|
||||
// ErrUserAlreadyHasAccess represents an error where a user already has access to a list/namespace
|
||||
type ErrUserAlreadyHasAccess struct {
|
||||
UserID int64
|
||||
ListID int64
|
||||
}
|
||||
|
||||
// IsErrUserAlreadyHasAccess checks if an error is ErrUserAlreadyHasAccess.
|
||||
func IsErrUserAlreadyHasAccess(err error) bool {
|
||||
_, ok := err.(ErrUserAlreadyHasAccess)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserAlreadyHasAccess) Error() string {
|
||||
return fmt.Sprintf("User already has access to that list. [User ID: %d, List ID: %d]", err.UserID, err.ListID)
|
||||
}
|
||||
|
||||
// ErrCodeUserAlreadyHasAccess holds the unique world-error code of this error
|
||||
const ErrCodeUserAlreadyHasAccess = 7002
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserAlreadyHasAccess) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusConflict, Code: ErrCodeUserAlreadyHasAccess, Message: "This user already has access to this list."}
|
||||
}
|
||||
|
||||
// ErrUserDoesNotHaveAccessToList represents an error, where the user is not the owner of that List (used i.e. when deleting a List)
|
||||
type ErrUserDoesNotHaveAccessToList struct {
|
||||
ListID int64
|
||||
UserID int64
|
||||
}
|
||||
|
||||
// IsErrUserDoesNotHaveAccessToList checks if an error is a ErrListDoesNotExist.
|
||||
func IsErrUserDoesNotHaveAccessToList(err error) bool {
|
||||
_, ok := err.(ErrUserDoesNotHaveAccessToList)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserDoesNotHaveAccessToList) Error() string {
|
||||
return fmt.Sprintf("User does not have access to the list [ListID: %d, UserID: %d]", err.ListID, err.UserID)
|
||||
}
|
||||
|
||||
// ErrCodeUserDoesNotHaveAccessToList holds the unique world-error code of this error
|
||||
const ErrCodeUserDoesNotHaveAccessToList = 7003
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrUserDoesNotHaveAccessToList) HTTPError() HTTPError {
|
||||
return HTTPError{HTTPCode: http.StatusForbidden, Code: ErrCodeUserDoesNotHaveAccessToList, Message: "This user does not have access to the list."}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
-
|
||||
id: 1
|
||||
title: Test1
|
||||
description: Lorem Ipsum
|
||||
owner_id: 1
|
||||
namespace_id: 1
|
||||
-
|
||||
id: 2
|
||||
title: Test2
|
||||
description: Lorem Ipsum
|
||||
owner_id: 3
|
||||
namespace_id: 1
|
||||
-
|
||||
id: 3
|
||||
title: Test3
|
||||
description: Lorem Ipsum
|
||||
owner_id: 3
|
||||
namespace_id: 2
|
||||
@@ -1,10 +0,0 @@
|
||||
-
|
||||
id: 1
|
||||
name: testnamespace
|
||||
description: Lorem Ipsum
|
||||
owner_id: 1
|
||||
-
|
||||
id: 2
|
||||
name: testnamespace2
|
||||
description: Lorem Ipsum
|
||||
owner_id: 2
|
||||
@@ -1,7 +0,0 @@
|
||||
-
|
||||
team_id: 1
|
||||
user_id: 1
|
||||
admin: true
|
||||
-
|
||||
team_id: 1
|
||||
user_id: 2
|
||||
@@ -1,5 +0,0 @@
|
||||
-
|
||||
id: 1
|
||||
name: testteam1
|
||||
description: Lorem Ipsum
|
||||
created_by_id: 1
|
||||
@@ -1,15 +0,0 @@
|
||||
-
|
||||
id: 1
|
||||
username: 'user1'
|
||||
password: '1234'
|
||||
email: 'johndoe@example.com'
|
||||
-
|
||||
id: 2
|
||||
username: 'user2'
|
||||
password: '1234'
|
||||
email: 'johndoe@example.com'
|
||||
-
|
||||
id: 3
|
||||
username: 'user3'
|
||||
password: '1234'
|
||||
email: 'johndoe@example.com'
|
||||
143
models/list.go
143
models/list.go
@@ -1,143 +0,0 @@
|
||||
package models
|
||||
|
||||
// List represents a list of tasks
|
||||
type List struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"list"`
|
||||
Title string `xorm:"varchar(250)" json:"title"`
|
||||
Description string `xorm:"varchar(1000)" json:"description"`
|
||||
OwnerID int64 `xorm:"int(11) INDEX" json:"-"`
|
||||
NamespaceID int64 `xorm:"int(11) INDEX" json:"-" param:"namespace"`
|
||||
|
||||
Owner User `xorm:"-" json:"owner"`
|
||||
Tasks []*ListTask `xorm:"-" json:"tasks"`
|
||||
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
Updated int64 `xorm:"updated" json:"updated"`
|
||||
|
||||
CRUDable `xorm:"-" json:"-"`
|
||||
Rights `xorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// GetListsByNamespaceID gets all lists in a namespace
|
||||
func GetListsByNamespaceID(nID int64) (lists []*List, err error) {
|
||||
err = x.Where("namespace_id = ?", nID).Find(&lists)
|
||||
return lists, err
|
||||
}
|
||||
|
||||
// ReadAll gets all lists a user has access to
|
||||
func (l *List) ReadAll(user *User) (interface{}, error) {
|
||||
lists := []*List{}
|
||||
fullUser, err := GetUserByID(user.ID)
|
||||
if err != nil {
|
||||
return lists, err
|
||||
}
|
||||
|
||||
// Gets all Lists where the user is either owner or in a team which has access to the list
|
||||
// Or in a team which has namespace read access
|
||||
err = x.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("INNER", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
||||
Join("LEFT", []string{"team_namespaces", "tn"}, "tn.namespace_id = n.id").
|
||||
Join("LEFT", []string{"team_members", "tm"}, "tm.team_id = tn.team_id").
|
||||
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
|
||||
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||
Where("tm.user_id = ?", fullUser.ID).
|
||||
Or("tm2.user_id = ?", fullUser.ID).
|
||||
Or("l.owner_id = ?", fullUser.ID).
|
||||
Or("ul.user_id = ?", fullUser.ID).
|
||||
Or("un.user_id = ?", fullUser.ID).
|
||||
GroupBy("l.id").
|
||||
Find(&lists)
|
||||
|
||||
// Add more list details
|
||||
addListDetails(lists)
|
||||
|
||||
return lists, err
|
||||
}
|
||||
|
||||
// ReadOne gets one list by its ID
|
||||
func (l *List) ReadOne() (err error) {
|
||||
err = l.GetSimpleByID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get list tasks
|
||||
l.Tasks, err = GetTasksByListID(l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get list owner
|
||||
l.Owner, err = GetUserByID(l.OwnerID)
|
||||
return
|
||||
}
|
||||
|
||||
// GetSimpleByID gets a list with only the basic items, aka no tasks or user objects. Returns an error if the list does not exist.
|
||||
func (l *List) GetSimpleByID() (err error) {
|
||||
if l.ID < 1 {
|
||||
return ErrListDoesNotExist{ID: l.ID}
|
||||
}
|
||||
|
||||
// We need to re-init our list object, because otherwise xorm creates a "where for every item in that list object,
|
||||
// leading to not finding anything if the id is good, but for example the title is different.
|
||||
id := l.ID
|
||||
*l = List{}
|
||||
exists, err := x.Where("id = ?", id).Get(l)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return ErrListDoesNotExist{ID: l.ID}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Adds owner user objects and list tasks to all lists in the slice
|
||||
func addListDetails(lists []*List) (err error) {
|
||||
var listIDs []int64
|
||||
var ownerIDs []int64
|
||||
for _, l := range lists {
|
||||
listIDs = append(listIDs, l.ID)
|
||||
ownerIDs = append(ownerIDs, l.OwnerID)
|
||||
}
|
||||
|
||||
// Get all tasks
|
||||
tasks := []*ListTask{}
|
||||
err = x.In("list_id", listIDs).Find(&tasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get all list owners
|
||||
owners := []*User{}
|
||||
err = x.In("id", ownerIDs).Find(&owners)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Build it all into the lists slice
|
||||
for in, list := range lists {
|
||||
// Owner
|
||||
for _, owner := range owners {
|
||||
if list.OwnerID == owner.ID {
|
||||
lists[in].Owner = *owner
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Tasks
|
||||
for _, task := range tasks {
|
||||
if task.ListID == list.ID {
|
||||
lists[in].Tasks = append(lists[in].Tasks, task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestList_Create(t *testing.T) {
|
||||
// Create test database
|
||||
//assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
// Get our doer
|
||||
doer, err := GetUserByID(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Dummy list for testing
|
||||
dummylist := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
NamespaceID: 1,
|
||||
}
|
||||
|
||||
// Check if the user can create
|
||||
assert.True(t, dummylist.CanCreate(&doer))
|
||||
|
||||
// Create it
|
||||
err = dummylist.Create(&doer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the list
|
||||
newdummy := List{ID: dummylist.ID}
|
||||
err = newdummy.ReadOne()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, dummylist.Title, newdummy.Title)
|
||||
assert.Equal(t, dummylist.Description, newdummy.Description)
|
||||
assert.Equal(t, dummylist.OwnerID, doer.ID)
|
||||
|
||||
// Check if the user can see it
|
||||
assert.True(t, dummylist.CanRead(&doer))
|
||||
|
||||
// Try updating a list
|
||||
assert.True(t, dummylist.CanUpdate(&doer))
|
||||
dummylist.Description = "Lorem Ipsum dolor sit amet."
|
||||
err = dummylist.Update()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete it
|
||||
assert.True(t, dummylist.CanDelete(&doer))
|
||||
|
||||
err = dummylist.Delete()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try updating a nonexistant list
|
||||
err = dummylist.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
|
||||
// Delete a nonexistant list
|
||||
err = dummylist.Delete()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
|
||||
// Check failing with no title
|
||||
list2 := List{}
|
||||
err = list2.Create(&doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListTitleCannotBeEmpty(err))
|
||||
|
||||
// Check creation with a nonexistant namespace
|
||||
list3 := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
NamespaceID: 876694,
|
||||
}
|
||||
|
||||
err = list3.Create(&doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
|
||||
// Try creating with a nonexistant owner
|
||||
nUser := &User{ID: 9482385}
|
||||
err = dummylist.Create(nUser)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserDoesNotExist(err))
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package models
|
||||
|
||||
// CreateOrUpdateList updates a list or creates it if it doesn't exist
|
||||
func CreateOrUpdateList(list *List) (err error) {
|
||||
|
||||
// Check we have at least a title
|
||||
if list.Title == "" {
|
||||
return ErrListTitleCannotBeEmpty{}
|
||||
}
|
||||
|
||||
// Check if the namespace exists
|
||||
if list.NamespaceID != 0 {
|
||||
_, err = GetNamespaceByID(list.NamespaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if list.ID == 0 {
|
||||
_, err = x.Insert(list)
|
||||
} else {
|
||||
_, err = x.ID(list.ID).Update(list)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = list.ReadOne()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Update implements the update method of CRUDable
|
||||
func (l *List) Update() (err error) {
|
||||
// Check if it exists
|
||||
if err = l.GetSimpleByID(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return CreateOrUpdateList(l)
|
||||
}
|
||||
|
||||
// Create implements the create method of CRUDable
|
||||
func (l *List) Create(doer *User) (err error) {
|
||||
// Check rights
|
||||
user, err := GetUserByID(doer.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
l.OwnerID = user.ID
|
||||
l.Owner.ID = user.ID
|
||||
l.ID = 0 // Otherwise only the first time a new list would be created
|
||||
|
||||
return CreateOrUpdateList(l)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package models
|
||||
|
||||
// Delete implements the delete method of CRUDable
|
||||
func (l *List) Delete() (err error) {
|
||||
// Check if the list exists
|
||||
if err = l.GetSimpleByID(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the list
|
||||
_, err = x.ID(l.ID).Delete(&List{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete all todotasks on that list
|
||||
_, err = x.Where("list_id = ?", l.ID).Delete(&ListTask{})
|
||||
return
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package models
|
||||
|
||||
// ListTask represents an task in a todolist
|
||||
type ListTask struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"listtask"`
|
||||
Text string `xorm:"varchar(250)" json:"text"`
|
||||
Description string `xorm:"varchar(250)" json:"description"`
|
||||
Done bool `xorm:"INDEX" json:"done"`
|
||||
DueDateUnix int64 `xorm:"int(11) INDEX" json:"dueDate"`
|
||||
ReminderUnix int64 `xorm:"int(11) INDEX" json:"reminderDate"`
|
||||
CreatedByID int64 `xorm:"int(11)" json:"-"` // ID of the user who put that task on the list
|
||||
ListID int64 `xorm:"int(11) INDEX" json:"listID" param:"list"`
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
Updated int64 `xorm:"updated" json:"updated"`
|
||||
|
||||
CreatedBy User `xorm:"-" json:"createdBy"`
|
||||
|
||||
CRUDable `xorm:"-" json:"-"`
|
||||
Rights `xorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// TableName returns the table name for listtasks
|
||||
func (ListTask) TableName() string {
|
||||
return "tasks"
|
||||
}
|
||||
|
||||
// GetTasksByListID gets all todotasks for a list
|
||||
func GetTasksByListID(listID int64) (tasks []*ListTask, err error) {
|
||||
err = x.Where("list_id = ?", listID).Find(&tasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// No need to iterate over users if the list doesn't has tasks
|
||||
if len(tasks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Get all users and put them into the array
|
||||
var userIDs []int64
|
||||
for _, i := range tasks {
|
||||
found := false
|
||||
for _, u := range userIDs {
|
||||
if i.CreatedByID == u {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
userIDs = append(userIDs, i.CreatedByID)
|
||||
}
|
||||
}
|
||||
|
||||
var users []User
|
||||
err = x.In("id", userIDs).Find(&users)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for in, task := range tasks {
|
||||
for _, user := range users {
|
||||
if task.CreatedByID == user.ID {
|
||||
tasks[in].CreatedBy = user
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// obsfucate the user password
|
||||
tasks[in].CreatedBy.Password = ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetListTaskByID returns all tasks a list has
|
||||
func GetListTaskByID(listTaskID int64) (listTask ListTask, err error) {
|
||||
if listTaskID < 1 {
|
||||
return ListTask{}, ErrListTaskDoesNotExist{listTaskID}
|
||||
}
|
||||
|
||||
exists, err := x.ID(listTaskID).Get(&listTask)
|
||||
if err != nil {
|
||||
return ListTask{}, err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return ListTask{}, ErrListTaskDoesNotExist{listTaskID}
|
||||
}
|
||||
|
||||
user, err := GetUserByID(listTask.CreatedByID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
listTask.CreatedBy = user
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/imdario/mergo"
|
||||
)
|
||||
|
||||
// Create is the implementation to create a list task
|
||||
func (i *ListTask) Create(doer *User) (err error) {
|
||||
i.ID = 0
|
||||
|
||||
// Check if we have at least a text
|
||||
if i.Text == "" {
|
||||
return ErrListTaskCannotBeEmpty{}
|
||||
}
|
||||
|
||||
// Check if the list exists
|
||||
l := &List{ID: i.ListID}
|
||||
if err = l.GetSimpleByID(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := GetUserByID(doer.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i.CreatedByID = user.ID
|
||||
i.CreatedBy = user
|
||||
_, err = x.Cols("text", "description", "done", "due_date_unix", "reminder_unix", "created_by_id", "list_id", "created", "updated").Insert(i)
|
||||
return err
|
||||
}
|
||||
|
||||
// Update updates a list task
|
||||
func (i *ListTask) Update() (err error) {
|
||||
// Check if the task exists
|
||||
ot, err := GetListTaskByID(i.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// For whatever reason, xorm dont detect if done is updated, so we need to update this every time by hand
|
||||
// Which is why we merge the actual task struct with the one we got from the user.
|
||||
// The user struct ovverrides values in the actual one.
|
||||
if err := mergo.Merge(&ot, i, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// And because a false is considered to be a null value, we need to explicitly check that case here.
|
||||
if i.Done == false {
|
||||
ot.Done = false
|
||||
}
|
||||
|
||||
_, err = x.ID(i.ID).Cols("text", "description", "done", "due_date_unix", "reminder_unix").Update(ot)
|
||||
*i = ot
|
||||
return
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package models
|
||||
|
||||
// Delete implements the delete method for listTask
|
||||
func (i *ListTask) Delete() (err error) {
|
||||
|
||||
// Check if it exists
|
||||
_, err = GetListTaskByID(i.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = x.ID(i.ID).Delete(ListTask{})
|
||||
return
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package models
|
||||
|
||||
// CanDelete checks if the user can delete an task
|
||||
func (i *ListTask) CanDelete(doer *User) bool {
|
||||
// Get the task
|
||||
lI, err := GetListTaskByID(i.ID)
|
||||
if err != nil {
|
||||
Log.Error("Error occurred during CanDelete for ListTask: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// A user can delete an task if he has write acces to its list
|
||||
list := &List{ID: lI.ListID}
|
||||
list.ReadOne()
|
||||
return list.CanWrite(doer)
|
||||
}
|
||||
|
||||
// CanUpdate determines if a user has the right to update a list task
|
||||
func (i *ListTask) CanUpdate(doer *User) bool {
|
||||
// Get the task
|
||||
lI, err := GetListTaskByID(i.ID)
|
||||
if err != nil {
|
||||
Log.Error("Error occurred during CanDelete for ListTask: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// A user can update an task if he has write acces to its list
|
||||
list := &List{ID: lI.ListID}
|
||||
list.ReadOne()
|
||||
return list.CanWrite(doer)
|
||||
}
|
||||
|
||||
// CanCreate determines if a user has the right to create a list task
|
||||
func (i *ListTask) CanCreate(doer *User) bool {
|
||||
// A user can create an task if he has write acces to its list
|
||||
list := &List{ID: i.ListID}
|
||||
list.ReadOne()
|
||||
return list.CanWrite(doer)
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListTask_Create(t *testing.T) {
|
||||
//assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
// Fake list task
|
||||
listtask := ListTask{
|
||||
Text: "Lorem",
|
||||
Description: "Lorem Ipsum BACKERY",
|
||||
ListID: 1,
|
||||
}
|
||||
|
||||
// Add one point to a list
|
||||
doer, err := GetUserByID(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, listtask.CanCreate(&doer))
|
||||
|
||||
err = listtask.Create(&doer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Update it
|
||||
listtask.Text = "Test34"
|
||||
assert.True(t, listtask.CanUpdate(&doer))
|
||||
err = listtask.Update()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check if it was updated
|
||||
li, err := GetListTaskByID(listtask.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, li.Text, "Test34")
|
||||
|
||||
// Delete the task
|
||||
assert.True(t, listtask.CanDelete(&doer))
|
||||
err = listtask.Delete()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete a nonexistant task
|
||||
listtask.ID = 0
|
||||
err = listtask.Delete()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListTaskDoesNotExist(err))
|
||||
|
||||
// Try adding a list task with an empty text
|
||||
listtask.Text = ""
|
||||
err = listtask.Create(&doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListTaskCannotBeEmpty(err))
|
||||
|
||||
// Try adding one to a nonexistant list
|
||||
listtask.ListID = 99993939
|
||||
listtask.Text = "Lorem Ipsum"
|
||||
err = listtask.Create(&doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
|
||||
// Try updating a nonexistant task
|
||||
listtask.ID = 94829352
|
||||
err = listtask.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListTaskDoesNotExist(err))
|
||||
|
||||
// Try inserting an task with a nonexistant user
|
||||
nUser := &User{ID: 9482385}
|
||||
listtask.ListID = 1
|
||||
err = listtask.Create(nUser)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserDoesNotExist(err))
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package models
|
||||
|
||||
// IsAdmin returns whether the user has admin rights on the list or not
|
||||
func (l *List) IsAdmin(user *User) bool {
|
||||
// Owners are always admins
|
||||
if l.Owner.ID == user.ID {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check individual rights
|
||||
if l.checkListUserRight(user, UserRightAdmin) {
|
||||
return true
|
||||
}
|
||||
|
||||
return l.checkListTeamRight(user, TeamRightAdmin)
|
||||
}
|
||||
|
||||
// CanWrite return whether the user can write on that list or not
|
||||
func (l *List) CanWrite(user *User) bool {
|
||||
// Admins always have write access
|
||||
if l.IsAdmin(user) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check individual rights
|
||||
if l.checkListUserRight(user, UserRightWrite) {
|
||||
return true
|
||||
}
|
||||
|
||||
return l.checkListTeamRight(user, TeamRightWrite)
|
||||
}
|
||||
|
||||
// CanRead checks if a user has read access to a list
|
||||
func (l *List) CanRead(user *User) bool {
|
||||
// Admins always have read access
|
||||
if l.IsAdmin(user) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check individual rights
|
||||
if l.checkListUserRight(user, UserRightRead) {
|
||||
return true
|
||||
}
|
||||
|
||||
return l.checkListTeamRight(user, TeamRightRead)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a list
|
||||
func (l *List) CanDelete(doer *User) bool {
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
Log.Error("Error occurred during CanDelete for List: %s", err)
|
||||
return false
|
||||
}
|
||||
return l.IsAdmin(doer)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a list
|
||||
func (l *List) CanUpdate(doer *User) bool {
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
Log.Error("Error occurred during CanUpdate for List: %s", err)
|
||||
return false
|
||||
}
|
||||
return l.CanWrite(doer)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can update a list
|
||||
func (l *List) CanCreate(doer *User) bool {
|
||||
// A user can create a list if he has write access to the namespace
|
||||
n, _ := GetNamespaceByID(l.NamespaceID)
|
||||
return n.CanWrite(doer)
|
||||
}
|
||||
|
||||
func (l *List) checkListTeamRight(user *User, r TeamRight) bool {
|
||||
exists, err := x.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("LEFT", []string{"team_namespaces", "tn"}, " l.namespace_id = tn.namespace_id").
|
||||
Join("LEFT", []string{"team_members", "tm"}, "tm.team_id = tn.team_id").
|
||||
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
|
||||
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
||||
Where("((tm.user_id = ? AND tn.right = ?) OR (tm2.user_id = ? AND tl.right = ?)) AND l.id = ?",
|
||||
user.ID, r, user.ID, r, l.ID).
|
||||
Exist(&List{})
|
||||
if err != nil {
|
||||
Log.Error("Error occurred during checkListTeamRight for List: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
func (l *List) checkListUserRight(user *User, r UserRight) bool {
|
||||
exists, err := x.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||
Join("LEFT", []string{"namespaces", "n"}, "n.id = l.namespace_id").
|
||||
Where("((ul.user_id = ? AND ul.right = ?) "+
|
||||
"OR (un.user_id = ? AND un.right = ?) "+
|
||||
"OR n.owner_id = ?)"+
|
||||
"AND l.id = ?",
|
||||
user.ID, r, user.ID, r, user.ID, l.ID).
|
||||
Exist(&List{})
|
||||
if err != nil {
|
||||
Log.Error("Error occurred during checkListUserRight for List: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return exists
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package models
|
||||
|
||||
// ListUser represents a list <-> user relation
|
||||
type ListUser struct {
|
||||
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id" param:"namespace"`
|
||||
UserID int64 `xorm:"int(11) not null INDEX" json:"user_id" param:"user"`
|
||||
ListID int64 `xorm:"int(11) not null INDEX" json:"list_id" param:"list"`
|
||||
Right UserRight `xorm:"int(11) INDEX" json:"right"`
|
||||
|
||||
Created int64 `xorm:"created" json:"created"`
|
||||
Updated int64 `xorm:"updated" json:"updated"`
|
||||
|
||||
CRUDable `xorm:"-" json:"-"`
|
||||
Rights `xorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
// TableName is the table name for ListUser
|
||||
func (ListUser) TableName() string {
|
||||
return "users_list"
|
||||
}
|
||||
|
||||
type userWithRight struct {
|
||||
User `xorm:"extends"`
|
||||
Right UserRight `json:"right"`
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package models
|
||||
|
||||
// Create creates a new list <-> user relation
|
||||
func (ul *ListUser) Create(user *User) (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := ul.Right.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the list exists
|
||||
l := &List{ID: ul.ListID}
|
||||
if err = l.GetSimpleByID(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
if _, err = GetUserByID(ul.UserID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the user already has access or is owner of that list
|
||||
// We explicitly DONT check for teams here
|
||||
if l.OwnerID == ul.UserID {
|
||||
return ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
|
||||
}
|
||||
|
||||
exist, err := x.Where("list_id = ? AND user_id = ?", ul.ListID, ul.UserID).Get(&ListUser{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if exist {
|
||||
return ErrUserAlreadyHasAccess{UserID: ul.UserID, ListID: ul.ListID}
|
||||
}
|
||||
|
||||
// Insert user <-> list relation
|
||||
_, err = x.Insert(ul)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package models
|
||||
|
||||
// Delete deletes a list <-> user relation
|
||||
func (lu *ListUser) Delete() (err error) {
|
||||
|
||||
// Check if the user exists
|
||||
_, err = GetUserByID(lu.UserID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user has access to the list
|
||||
has, err := x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||
Get(&ListUser{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !has {
|
||||
return ErrUserDoesNotHaveAccessToList{ListID: lu.ListID, UserID: lu.UserID}
|
||||
}
|
||||
|
||||
_, err = x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||
Delete(&ListUser{})
|
||||
return
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package models
|
||||
|
||||
// ReadAll gets all users who have access to a list
|
||||
func (ul *ListUser) ReadAll(user *User) (interface{}, error) {
|
||||
// Check if the user has access to the list
|
||||
l := &List{ID: ul.ListID}
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !l.CanRead(user) {
|
||||
return nil, ErrNeedToHaveListReadAccess{}
|
||||
}
|
||||
|
||||
// Get all users
|
||||
all := []*userWithRight{}
|
||||
err := x.
|
||||
Join("INNER", "users_list", "user_id = users.id").
|
||||
Where("users_list.list_id = ?", ul.ListID).
|
||||
Find(&all)
|
||||
|
||||
return all, err
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package models
|
||||
|
||||
// UserRight defines the rights users can have for lists/namespaces
|
||||
type UserRight int
|
||||
|
||||
// define unknown user right
|
||||
const (
|
||||
UserRightUnknown = -1
|
||||
)
|
||||
|
||||
// Enumerate all the user rights
|
||||
const (
|
||||
// Can read lists in a User
|
||||
UserRightRead UserRight = iota
|
||||
// Can write tasks in a User like lists and todo tasks. Cannot create new lists.
|
||||
UserRightWrite
|
||||
// Can manage a list/namespace, can do everything
|
||||
UserRightAdmin
|
||||
)
|
||||
|
||||
func (r UserRight) isValid() error {
|
||||
if r != UserRightAdmin && r != UserRightRead && r != UserRightWrite {
|
||||
return ErrInvalidUserRight{r}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can create a new user <-> list relation
|
||||
func (lu *ListUser) CanCreate(doer *User) bool {
|
||||
// Get the list and check if the user has write access on it
|
||||
l := List{ID: lu.ListID}
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
Log.Error("Error occurred during CanCreate for ListUser: %s", err)
|
||||
return false
|
||||
}
|
||||
return l.CanWrite(doer)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a user <-> list relation
|
||||
func (lu *ListUser) CanDelete(doer *User) bool {
|
||||
// Get the list and check if the user has write access on it
|
||||
l := List{ID: lu.ListID}
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
Log.Error("Error occurred during CanDelete for ListUser: %s", err)
|
||||
return false
|
||||
}
|
||||
return l.CanWrite(doer)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a user <-> list relation
|
||||
func (lu *ListUser) CanUpdate(doer *User) bool {
|
||||
// Get the list and check if the user has write access on it
|
||||
l := List{ID: lu.ListID}
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
Log.Error("Error occurred during CanUpdate for ListUser: %s", err)
|
||||
return false
|
||||
}
|
||||
return l.CanWrite(doer)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package models
|
||||
|
||||
// Update updates a user <-> list relation
|
||||
func (lu *ListUser) Update() (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := lu.Right.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = x.
|
||||
Where("list_id = ? AND user_id = ?", lu.ListID, lu.UserID).
|
||||
Cols("right").
|
||||
Update(lu)
|
||||
return
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestList_ReadAll(t *testing.T) {
|
||||
// Create test database
|
||||
//assert.NoError(t, PrepareTestDatabase())
|
||||
|
||||
// Get all lists for our namespace
|
||||
lists, err := GetListsByNamespaceID(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(lists), 2)
|
||||
|
||||
// Get all lists our user has access to
|
||||
user, err := GetUserByID(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
lists2 := List{}
|
||||
lists3, err := lists2.ReadAll(&user)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(lists3)
|
||||
assert.Equal(t, s.Len(), 1)
|
||||
|
||||
// Try getting lists for a nonexistant user
|
||||
_, err = lists2.ReadAll(&User{ID: 984234})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserDoesNotExist(err))
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/op/go-logging"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Log is the handler for the logger
|
||||
var Log = logging.MustGetLogger("vikunja")
|
||||
|
||||
var format = logging.MustStringFormatter(
|
||||
`%{color}%{time:2006-01-02 15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
|
||||
)
|
||||
|
||||
// InitLogger initializes the global log handler
|
||||
func InitLogger() {
|
||||
backend := logging.NewLogBackend(os.Stderr, "", 0)
|
||||
backendFormatter := logging.NewBackendFormatter(backend, format)
|
||||
logging.SetBackend(backendFormatter)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package models
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
MainTest(m, "..")
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package models
|
||||
|
||||
// Message is a standard message
|
||||
type Message struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user