Compare commits
2195 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
06e7e40b15 | ||
![]() |
6766041328 | ||
![]() |
2d96cec464 | ||
![]() |
843d229c3d | ||
![]() |
b034e972b0 | ||
![]() |
cdbf81c51c | ||
![]() |
d81e4dbe99 | ||
![]() |
a789649d97 | ||
![]() |
ce32089cc4 | ||
![]() |
f0ee2e14fb | ||
![]() |
9b8a490061 | ||
![]() |
4da49c8d59 | ||
![]() |
3960093231 | ||
![]() |
3003bdd507 | ||
![]() |
7909b30b4b | ||
![]() |
905a34188d | ||
![]() |
261cab8450 | ||
![]() |
7c2137fcda | ||
![]() |
2c3cb7f516 | ||
![]() |
3d7aa7a4b9 | ||
![]() |
3e8bff03e7 | ||
![]() |
584082a1df | ||
![]() |
49644ce18a | ||
![]() |
7432ef9e19 | ||
![]() |
7e271129c7 | ||
![]() |
b5f95a351c | ||
![]() |
5e10befe28 | ||
![]() |
98ebb095cc | ||
![]() |
c3b5b47b22 | ||
![]() |
3c133c36bd | ||
![]() |
9ceb2e6084 | ||
![]() |
2b322b638e | ||
![]() |
0d298d743a | ||
![]() |
ec5d80585d | ||
![]() |
88e71e12b1 | ||
![]() |
63bb72caab | ||
![]() |
240c7913e9 | ||
![]() |
582629a996 | ||
![]() |
939dd17910 | ||
![]() |
d58b1a7de7 | ||
![]() |
a938895e5e | ||
![]() |
9d9111e4d8 | ||
![]() |
79dbfce36f | ||
![]() |
e7f162e5e5 | ||
![]() |
f23d10f000 | ||
![]() |
67dd86988b | ||
![]() |
e16ab324f4 | ||
![]() |
317926f379 | ||
![]() |
fe61ca2c97 | ||
![]() |
2940686fba | ||
![]() |
2ef2a561ef | ||
![]() |
7ae31def4f | ||
![]() |
8f038d7d26 | ||
![]() |
b9a2652013 | ||
![]() |
dce4166bc8 | ||
![]() |
3c5f509bc7 | ||
![]() |
182cea3385 | ||
![]() |
0ba1ba55bd | ||
![]() |
45440eec1d | ||
![]() |
49fad14920 | ||
![]() |
0f1e31643d | ||
![]() |
c17c291b1c | ||
![]() |
666641f990 | ||
![]() |
268f99f8ac | ||
![]() |
216f048466 | ||
![]() |
ed8d5f1a80 | ||
![]() |
27a1ef25a5 | ||
![]() |
ecbc8165d5 | ||
![]() |
128594584e | ||
![]() |
8b798013c1 | ||
![]() |
8c19c2c2e9 | ||
![]() |
6f0fee4c43 | ||
![]() |
a5b4a7caad | ||
![]() |
c1b9db19c6 | ||
![]() |
40f88faf37 | ||
![]() |
81e6228ab3 | ||
![]() |
157951343b | ||
![]() |
9325eff6fc | ||
![]() |
8c8f366e0f | ||
![]() |
542221a38d | ||
![]() |
aa7faaaa72 | ||
![]() |
c96db31006 | ||
![]() |
c66de5931c | ||
![]() |
fdec13ef81 | ||
![]() |
9aee44f363 | ||
![]() |
2caa2d5b32 | ||
![]() |
66c7f44bea | ||
![]() |
99e4a79cb8 | ||
![]() |
f7f0df60ec | ||
![]() |
e012262301 | ||
![]() |
5676155e4e | ||
![]() |
d98bfa5bed | ||
![]() |
7ccac8053b | ||
![]() |
40cf46fe7d | ||
![]() |
f5c05e1283 | ||
![]() |
330e47f0b7 | ||
![]() |
1e9378b429 | ||
![]() |
af58fb5fa3 | ||
![]() |
3d6fb2383a | ||
![]() |
2407798d2e | ||
![]() |
f9194bd28c | ||
![]() |
816f020cc3 | ||
![]() |
9191aa32df | ||
![]() |
0a6395507e | ||
![]() |
eff68c601c | ||
![]() |
3f5540f35b | ||
![]() |
e5f5030e9c | ||
![]() |
df39d37ca9 | ||
![]() |
bea81d0449 | ||
![]() |
d261845fc6 | ||
![]() |
eac0b295d2 | ||
![]() |
f4eefcea13 | ||
![]() |
268715711f | ||
![]() |
c5f70b4401 | ||
![]() |
912caf84a6 | ||
![]() |
448fa4d35f | ||
![]() |
cd8d4c357d | ||
![]() |
bdcc6f861d | ||
![]() |
d9d6b7b151 | ||
![]() |
4517692c20 | ||
![]() |
609b9e3369 | ||
![]() |
be8ca110f4 | ||
![]() |
f1da37dd12 | ||
![]() |
ecca51dbdd | ||
![]() |
d19015579c | ||
![]() |
6179ca5668 | ||
![]() |
6c70db31bd | ||
![]() |
b0790d7010 | ||
![]() |
eb8158673f | ||
![]() |
409f17980f | ||
![]() |
654ef06682 | ||
![]() |
bca95a4972 | ||
![]() |
4df065d8d5 | ||
![]() |
a358477cda | ||
![]() |
44d6db8c47 | ||
![]() |
49e627d2fd | ||
![]() |
c3a8d93eb4 | ||
![]() |
7a648c6465 | ||
![]() |
5c1c5b5b0d | ||
![]() |
811da4bac5 | ||
![]() |
23b2fbef45 | ||
![]() |
f1b52b495a | ||
![]() |
0bff3891bd | ||
![]() |
a7b1658ee1 | ||
![]() |
c274bbcddf | ||
![]() |
57f32e5360 | ||
![]() |
b4ecf8e28e | ||
![]() |
06cfba8c7e | ||
![]() |
5603834282 | ||
![]() |
33134d4529 | ||
![]() |
bf57b6e4a2 | ||
![]() |
b63f87a5b5 | ||
![]() |
68e3612d36 | ||
![]() |
616a826b8a | ||
![]() |
09d62d76b2 | ||
![]() |
deab7794d6 | ||
![]() |
bc892059a1 | ||
![]() |
6d0fdc7510 | ||
![]() |
dd1d4b86d2 | ||
![]() |
65eeea9453 | ||
![]() |
b2b202586c | ||
![]() |
49341260dc | ||
![]() |
dfcef81001 | ||
![]() |
a834a6c874 | ||
![]() |
ad5188a280 | ||
![]() |
53c2a0c724 | ||
![]() |
3a8cc31f1b | ||
![]() |
0f6b452d17 | ||
![]() |
e994bcf737 | ||
![]() |
5432700b0d | ||
![]() |
7b7534c952 | ||
![]() |
b4570545ef | ||
![]() |
6478db13e6 | ||
![]() |
1d7ddcc10d | ||
![]() |
30508c6c2c | ||
![]() |
18f43c5757 | ||
![]() |
52498efd14 | ||
![]() |
40cb721d16 | ||
![]() |
1c2699b16e | ||
![]() |
d98a016087 | ||
![]() |
bc5b6db031 | ||
![]() |
4a4f252ad8 | ||
![]() |
c81dd1a478 | ||
![]() |
31016156be | ||
![]() |
dad352f05e | ||
![]() |
95e94618d8 | ||
![]() |
045a401cd7 | ||
![]() |
d5d7e2edbc | ||
![]() |
64e1a6ec7e | ||
![]() |
2221b425ad | ||
![]() |
86b2ccae94 | ||
![]() |
0bd67a54ab | ||
![]() |
306f254218 | ||
![]() |
449add88a6 | ||
![]() |
941ba8d689 | ||
![]() |
813335f8eb | ||
![]() |
f0cef2f42f | ||
![]() |
e767eb38f4 | ||
![]() |
71cbef4c13 | ||
![]() |
834ad1ef84 | ||
![]() |
753e6661bc | ||
![]() |
1ce19f5444 | ||
![]() |
0de1230a1a | ||
![]() |
5ff304324d | ||
![]() |
37f7ef41f2 | ||
![]() |
4022284059 | ||
![]() |
dde6a2eb7d | ||
![]() |
1083ed4e40 | ||
![]() |
c111825b1e | ||
![]() |
e36c22d29a | ||
![]() |
c192931015 | ||
![]() |
ae895a4aec | ||
![]() |
301ad7e07d | ||
![]() |
cc93616019 | ||
![]() |
493d9875d4 | ||
![]() |
3fb8d05f0d | ||
![]() |
88066563d3 | ||
![]() |
9e3590352c | ||
![]() |
86ad52639f | ||
![]() |
c00be946a5 | ||
![]() |
322aeeb552 | ||
![]() |
501d4cafa9 | ||
![]() |
e556fb3e3a | ||
![]() |
bb930297d6 | ||
![]() |
83f10167e5 | ||
![]() |
d47bb21389 | ||
![]() |
4dedff00b8 | ||
![]() |
2414dad656 | ||
![]() |
8f98cb4860 | ||
![]() |
6e96b7e00a | ||
![]() |
04acce4916 | ||
![]() |
1b7a304149 | ||
![]() |
00287b27ab | ||
![]() |
8f18b7fd6c | ||
![]() |
dde7771dc6 | ||
![]() |
4165184e42 | ||
![]() |
f3c8211ba4 | ||
![]() |
e4953a756a | ||
![]() |
471ac63a3a | ||
![]() |
f358eda5c5 | ||
![]() |
035130ecdc | ||
![]() |
908fc8573a | ||
![]() |
ca0e86757b | ||
![]() |
d4153607c9 | ||
![]() |
99d0a0845d | ||
![]() |
5fae5a9ee0 | ||
![]() |
b5a75be1db | ||
![]() |
93ad5433e4 | ||
![]() |
eb5e0e0b9b | ||
![]() |
5ead8de0bb | ||
![]() |
dc90f58391 | ||
![]() |
ca43c71cf5 | ||
![]() |
931a311c48 | ||
![]() |
9f9d7da1ce | ||
![]() |
d00e8d3b0f | ||
![]() |
d4124bae0c | ||
![]() |
c062eb751e | ||
![]() |
e273a594ba | ||
![]() |
ebb724e687 | ||
![]() |
58eb2d6f63 | ||
![]() |
b14bf8a96d | ||
![]() |
01987f1b51 | ||
![]() |
0a35358e8d | ||
![]() |
5bacb85c33 | ||
![]() |
6933ac523f | ||
![]() |
ba9120b417 | ||
![]() |
3e71f5810f | ||
![]() |
296a0edae4 | ||
![]() |
cdf5602dfb | ||
![]() |
e214f719c9 | ||
![]() |
08fbcf5158 | ||
![]() |
10ca515ac5 | ||
![]() |
e59a14852b | ||
![]() |
c696b4f2f2 | ||
![]() |
553153ba92 | ||
![]() |
9d2bcf807e | ||
![]() |
422ac9befe | ||
![]() |
793f641af6 | ||
![]() |
0ea5f5d584 | ||
![]() |
c024b846c3 | ||
![]() |
37b3fde4e1 | ||
![]() |
e89ef5de25 | ||
![]() |
06cac44d02 | ||
![]() |
488fe28ad3 | ||
![]() |
50f474ae92 | ||
![]() |
78ca2ffaba | ||
![]() |
911f5bc78e | ||
![]() |
b227427916 | ||
![]() |
b5f77fd6e7 | ||
![]() |
4fe966f534 | ||
![]() |
bcce0838dd | ||
![]() |
76e43bcb89 | ||
![]() |
c666be32f4 | ||
![]() |
2d850795d8 | ||
![]() |
784982718e | ||
![]() |
2f8d263c9c | ||
![]() |
b214163af3 | ||
![]() |
a4fe1000c2 | ||
![]() |
9a275fa4ed | ||
![]() |
f2c83f51de | ||
![]() |
fb76b72787 | ||
![]() |
bec6c4511c | ||
![]() |
77b9988d05 | ||
![]() |
3e49f93816 | ||
![]() |
32f6932faf | ||
![]() |
db76e1d65f | ||
![]() |
0136ba504b | ||
![]() |
459e026f16 | ||
![]() |
b03a723c3e | ||
![]() |
7562636151 | ||
![]() |
3acc65ca0d | ||
![]() |
fde0f4ca0a | ||
![]() |
73cab2af2d | ||
![]() |
94d2198b30 | ||
![]() |
88a67c8703 | ||
![]() |
ccf9b1291e | ||
![]() |
47dae716ae | ||
![]() |
ea26e1c72f | ||
![]() |
e6d79f0673 | ||
![]() |
bf7002d0ae | ||
![]() |
cbae145da5 | ||
![]() |
a728502988 | ||
![]() |
9419f74d13 | ||
![]() |
50523c1566 | ||
![]() |
e9ef3e270d | ||
![]() |
9078f7beef | ||
![]() |
ac2c5abb09 | ||
![]() |
a02235eb2b | ||
![]() |
7658761940 | ||
![]() |
3f03f076cf | ||
![]() |
141e6de88f | ||
![]() |
88bbfe5961 | ||
![]() |
d60569b6d6 | ||
![]() |
91165e80ba | ||
![]() |
a15f9552eb | ||
![]() |
501d225f93 | ||
![]() |
4942f244da | ||
![]() |
1be5c9af56 | ||
![]() |
fab7abb85b | ||
![]() |
818d383f2e | ||
![]() |
5fffa32630 | ||
![]() |
19d5feb483 | ||
![]() |
199fc6be94 | ||
![]() |
865729c033 | ||
![]() |
6aa9071e24 | ||
![]() |
6db3fc2eea | ||
![]() |
65ffaaa67c | ||
![]() |
440467e304 | ||
![]() |
07f2c9c1c6 | ||
![]() |
74422dd000 | ||
![]() |
78d663bbb4 | ||
![]() |
6cc2fa5306 | ||
![]() |
db0a58ea04 | ||
![]() |
eba1e69e64 | ||
![]() |
a4e7877033 | ||
![]() |
c62260ab02 | ||
![]() |
b58550bb79 | ||
![]() |
d02c7df75c | ||
![]() |
6dbebf4806 | ||
![]() |
1019660f6a | ||
![]() |
bfd11060ec | ||
![]() |
7b6dccf5ef | ||
![]() |
d76eccad1c | ||
![]() |
6f0ac7ae45 | ||
![]() |
8f17ed1eb9 | ||
![]() |
9737e4a24d | ||
![]() |
805c17565b | ||
![]() |
8e9d1cdd18 | ||
![]() |
1d3300fb34 | ||
![]() |
26d93cf3be | ||
![]() |
4e3183ee65 | ||
![]() |
8370ec58c1 | ||
![]() |
0d1bcd3c13 | ||
![]() |
a456b968af | ||
![]() |
2b5562e376 | ||
![]() |
3a242dc296 | ||
![]() |
5aff9b6fdb | ||
![]() |
35b3216fee | ||
![]() |
fac6fe0c2e | ||
![]() |
6f8020e30d | ||
![]() |
efbc2a5715 | ||
![]() |
0bee3901b6 | ||
![]() |
7106c68032 | ||
![]() |
be707536bd | ||
![]() |
d18eebf97d | ||
![]() |
60cc7afc72 | ||
![]() |
b7949d2e69 | ||
![]() |
48175d5b8e | ||
![]() |
cb8fd6597d | ||
![]() |
4754ac2bd1 | ||
![]() |
3cca77e748 | ||
![]() |
4ec1aaabe6 | ||
![]() |
0baacbef98 | ||
![]() |
70bc70ec97 | ||
![]() |
be2b59431f | ||
![]() |
c43193b17d | ||
![]() |
2147af0e6a | ||
![]() |
d8261b3359 | ||
![]() |
ed2524cbbb | ||
![]() |
76a4ae2aae | ||
![]() |
2666e70706 | ||
![]() |
19cf66be0f | ||
![]() |
f3d1bd25ad | ||
![]() |
3ed6d4bc7a | ||
![]() |
1ecb26a3fb | ||
![]() |
8b26ddcd2c | ||
![]() |
47786dcb8c | ||
![]() |
5d91d6a885 | ||
![]() |
559f7c2683 | ||
![]() |
3b4da70c85 | ||
![]() |
95199bd325 | ||
![]() |
a8887b211e | ||
![]() |
15488fbfd0 | ||
![]() |
9a758fc3dc | ||
![]() |
edc9c3f01c | ||
![]() |
83d769251d | ||
![]() |
bd66333147 | ||
![]() |
2228678520 | ||
![]() |
9a042118f9 | ||
![]() |
787ee454ff | ||
![]() |
32a4587bd3 | ||
![]() |
474050ba6b | ||
![]() |
34657fd675 | ||
![]() |
18747db17f | ||
![]() |
dccea9434a | ||
![]() |
85bf92ad2f | ||
![]() |
40b07572a9 | ||
![]() |
94afd4f1e2 | ||
![]() |
1218944680 | ||
![]() |
9147e3a0bd | ||
![]() |
ed25212654 | ||
![]() |
ce5fe61e67 | ||
![]() |
9e9266b92a | ||
![]() |
e329d72e8b | ||
![]() |
92ec3fc060 | ||
![]() |
3afb3a905c | ||
![]() |
b3e6f04b30 | ||
![]() |
0a29f51862 | ||
![]() |
7149d407cd | ||
![]() |
fbb17df916 | ||
![]() |
34b317da7a | ||
![]() |
c161829803 | ||
![]() |
eda69dc881 | ||
![]() |
500e5c41ff | ||
![]() |
7c2ae129d7 | ||
![]() |
c49afa7caa | ||
![]() |
534c157809 | ||
![]() |
cae4d3fae3 | ||
![]() |
f59500c809 | ||
![]() |
b75bf255ba | ||
![]() |
1588242876 | ||
![]() |
00eff651e6 | ||
![]() |
a83d6a691a | ||
![]() |
5065f2cb80 | ||
![]() |
a4b36b041a | ||
![]() |
c360d9fa18 | ||
![]() |
78258eb9cb | ||
![]() |
71e5b5cf72 | ||
![]() |
4b0229584e | ||
![]() |
b2df8297e1 | ||
![]() |
95048b14fd | ||
![]() |
3360791a31 | ||
![]() |
4c65ecbe89 | ||
![]() |
65a56a1da0 | ||
![]() |
645f9b2ee2 | ||
![]() |
117157f02c | ||
![]() |
acab197a45 | ||
![]() |
feed7cd556 | ||
![]() |
c79b70fffd | ||
![]() |
42a5709202 | ||
![]() |
806f1ec0a3 | ||
![]() |
ccee85a05e | ||
![]() |
a27fb173dd | ||
![]() |
e4885badfc | ||
![]() |
62c488aff6 | ||
![]() |
afcb5fe3cf | ||
![]() |
d38bed1334 | ||
![]() |
f6a9d5b038 | ||
![]() |
863258f23d | ||
![]() |
3b76fa3f92 | ||
![]() |
023a42fa07 | ||
![]() |
537515432c | ||
![]() |
f93783052b | ||
![]() |
991bc1a1ce | ||
![]() |
1b5c557c44 | ||
![]() |
5794faef6c | ||
![]() |
ec01c436ea | ||
![]() |
9bb5568d8e | ||
![]() |
c25a107c04 | ||
![]() |
8f152aac69 | ||
![]() |
1cd5de697e | ||
![]() |
ce8c812669 | ||
![]() |
2c3ac053d8 | ||
![]() |
ca3bb6a540 | ||
![]() |
0932f095e1 | ||
![]() |
3d3807ce41 | ||
![]() |
30834eb8ff | ||
![]() |
1c4e74920a | ||
![]() |
e6efff426a | ||
![]() |
cacf60fdd9 | ||
![]() |
352f94ff2b | ||
![]() |
47530d274f | ||
![]() |
be13ec822f | ||
![]() |
c6da0cc9a2 | ||
![]() |
07623f9883 | ||
![]() |
6e0d334a0c | ||
![]() |
e514b99c57 | ||
![]() |
cffdaefe2f | ||
![]() |
9de4ca61e8 | ||
![]() |
d7919c45a3 | ||
![]() |
d24826a58e | ||
![]() |
3dfadcc397 | ||
![]() |
98d677dc0b | ||
![]() |
4f28225188 | ||
![]() |
8059039ef4 | ||
![]() |
682f3fdc3e | ||
![]() |
6f7aaba7fa | ||
![]() |
ea7a1012b9 | ||
![]() |
f24373699e | ||
![]() |
f74e15840d | ||
![]() |
c2c8a27545 | ||
![]() |
0979f3c5c3 | ||
![]() |
6f2fd1e2da | ||
![]() |
597effc856 | ||
![]() |
30b3510fbd | ||
![]() |
0cf9b11c3e | ||
![]() |
0388ce3e5b | ||
![]() |
1ea6a14437 | ||
![]() |
e8a073d538 | ||
![]() |
c8ac686b22 | ||
![]() |
81cfd13b4a | ||
![]() |
73a3abe535 | ||
![]() |
9006dd12f3 | ||
![]() |
2bcbb89175 | ||
![]() |
39daddef34 | ||
![]() |
ff4538612e | ||
![]() |
1782d00cc5 | ||
![]() |
d4f468208e | ||
![]() |
c60b708b9c | ||
![]() |
3dee012415 | ||
![]() |
a1d1fb962b | ||
![]() |
24e02a6c5f | ||
![]() |
4f352391ae | ||
![]() |
4667e0bf88 | ||
![]() |
9544e6c757 | ||
![]() |
83b5e1a49d | ||
![]() |
4f287b5ecd | ||
![]() |
4c346be367 | ||
![]() |
c87ac6f1ad | ||
![]() |
02dc395880 | ||
![]() |
63a3e0b325 | ||
![]() |
01ae5688d7 | ||
![]() |
ec8b10c85b | ||
![]() |
19ff8339be | ||
![]() |
a440b712de | ||
![]() |
3653045922 | ||
![]() |
0d7438e398 | ||
![]() |
81b17bec69 | ||
![]() |
e43c446c38 | ||
![]() |
0cb442c6e0 | ||
![]() |
17ea079fcd | ||
![]() |
2270aefdee | ||
![]() |
30828bcbe0 | ||
![]() |
4667adc64d | ||
![]() |
de779d453c | ||
![]() |
fe959b30c6 | ||
![]() |
3fcbb17a15 | ||
![]() |
cdfde1d91f | ||
![]() |
64a5b24e12 | ||
![]() |
7d29bd216d | ||
![]() |
1abd7cc2a0 | ||
![]() |
78608d92b4 | ||
![]() |
54cbacf4f4 | ||
![]() |
b43aae84ab | ||
![]() |
9d9789953b | ||
![]() |
614eb930d3 | ||
![]() |
62094a2098 | ||
![]() |
84ed805fd7 | ||
![]() |
82a158e803 | ||
![]() |
1527ae1472 | ||
![]() |
5d1e86fdc3 | ||
![]() |
a6d2a390f0 | ||
![]() |
a47ec7c77d | ||
![]() |
ee1404d99e | ||
![]() |
2caba558d7 | ||
![]() |
5e4119f6a9 | ||
![]() |
6b46b43367 | ||
![]() |
460f10f46a | ||
![]() |
37c6201a5a | ||
![]() |
4184988c0c | ||
![]() |
3264015dac | ||
![]() |
d1c785d1d0 | ||
![]() |
abd63df75b | ||
![]() |
57f35292d5 | ||
![]() |
7bd6f4a4ea | ||
![]() |
35de8e6ad5 | ||
![]() |
1549edfd55 | ||
![]() |
f33cf6cc2e | ||
![]() |
3a8cffe3ce | ||
![]() |
58066443de | ||
![]() |
20566b6571 | ||
![]() |
af52544792 | ||
![]() |
594445f6dd | ||
![]() |
c3e86f0f21 | ||
![]() |
d80102a7a4 | ||
![]() |
e886c38b45 | ||
![]() |
33a5823801 | ||
![]() |
b220d15fff | ||
![]() |
6fb856ee70 | ||
![]() |
3922588716 | ||
![]() |
940b12a908 | ||
![]() |
db5349881d | ||
![]() |
0edfe83a23 | ||
![]() |
7abc6440f6 | ||
![]() |
168ce2111d | ||
![]() |
617d0bfee7 | ||
![]() |
0d0da0d623 | ||
![]() |
dc657f2eb0 | ||
![]() |
ef2b4a7536 | ||
![]() |
e41d75c374 | ||
![]() |
eb55f5655f | ||
![]() |
9f72bf7745 | ||
![]() |
f34202a82a | ||
![]() |
f8c8161a3e | ||
![]() |
63aafd3133 | ||
![]() |
e2303235cd | ||
![]() |
1771d18a21 | ||
![]() |
d02a0b2213 | ||
![]() |
6cbbd0c515 | ||
![]() |
cae26f4f70 | ||
![]() |
f328d1461f | ||
![]() |
d3e9799279 | ||
![]() |
14eefe1f5d | ||
![]() |
6c2cc4cf50 | ||
![]() |
440f4729ac | ||
![]() |
e1c6042189 | ||
![]() |
55a057307c | ||
![]() |
8a5176e593 | ||
![]() |
a757778fba | ||
![]() |
1259911275 | ||
![]() |
4e4035a867 | ||
![]() |
9bd87e78dc | ||
![]() |
12a1c4ccbf | ||
![]() |
888e17cb91 | ||
![]() |
0fa48cea2a | ||
![]() |
9b7d393d5d | ||
![]() |
cc702cbdfa | ||
![]() |
e8ddb0c427 | ||
![]() |
feb677e656 | ||
![]() |
c19dd81ecf | ||
![]() |
01a50a3a98 | ||
![]() |
8920a32c75 | ||
![]() |
4773d0bb7f | ||
![]() |
20a6c5e7b7 | ||
![]() |
0826e0f96b | ||
![]() |
bd374f4c36 | ||
![]() |
bac8849f2d | ||
![]() |
ad87c3c87d | ||
![]() |
4c7eb34290 | ||
![]() |
a1fd9f7310 | ||
![]() |
9433908218 | ||
![]() |
8f1c4cd9c4 | ||
![]() |
85b210ebf6 | ||
![]() |
af7ffa3878 | ||
![]() |
96a84d16a6 | ||
![]() |
0737ea30e8 | ||
![]() |
1807811903 | ||
![]() |
a33dce1948 | ||
![]() |
ef4dc1b49e | ||
![]() |
0f886be109 | ||
![]() |
73a597855f | ||
![]() |
86d9c94962 | ||
![]() |
21ec2dfa68 | ||
![]() |
296d1d1b61 | ||
![]() |
af536dfefb | ||
![]() |
7d7c5207d7 | ||
![]() |
52f0a3dfb9 | ||
![]() |
4532bed7bc | ||
![]() |
d3bccc049b | ||
![]() |
333321e600 | ||
![]() |
081f11af40 | ||
![]() |
9f0f458a02 | ||
![]() |
e74c716588 | ||
![]() |
89bd0791dc | ||
![]() |
a591614a39 | ||
![]() |
0085c8351e | ||
![]() |
4df7f92a56 | ||
![]() |
1841cefbd8 | ||
![]() |
f13ae930a5 | ||
![]() |
dbe233f0ae | ||
![]() |
17e5f6d76b | ||
![]() |
f52d167da3 | ||
![]() |
63aa7128a7 | ||
![]() |
d30a652fc1 | ||
![]() |
1cd0684f62 | ||
![]() |
bc97a13a62 | ||
![]() |
db98eed392 | ||
![]() |
76115d6a81 | ||
![]() |
9e2fd52434 | ||
![]() |
7ca12c3dc4 | ||
![]() |
3dffa09977 | ||
![]() |
7c313eed33 | ||
![]() |
383cf7f4d5 | ||
![]() |
4babf0d102 | ||
![]() |
df4567f9e4 | ||
![]() |
db2bf66eee | ||
![]() |
36eaafa52a | ||
![]() |
f4d09d46f4 | ||
![]() |
01c1b42319 | ||
![]() |
472a3a54ab | ||
![]() |
9fbf9923dc | ||
![]() |
9d3242c13e | ||
![]() |
08c6a21bbb | ||
![]() |
7d18be9928 | ||
![]() |
748fe3b89f | ||
![]() |
b383e7dd08 | ||
![]() |
33ab69e010 | ||
![]() |
c52070d554 | ||
![]() |
1d67c32351 | ||
![]() |
23d6589a73 | ||
![]() |
3ae73e7df5 | ||
![]() |
8f3f60d249 | ||
![]() |
16664789d2 | ||
![]() |
f97aae1fd8 | ||
![]() |
7e1bbecc9f | ||
![]() |
376a56df26 | ||
![]() |
c41b86eb68 | ||
![]() |
77cc7cebf9 | ||
![]() |
f61105c041 | ||
![]() |
b4dc073a48 | ||
![]() |
2324f91622 | ||
![]() |
6b5c89b6f8 | ||
![]() |
e5e94ffb6d | ||
![]() |
e3584eb61e | ||
![]() |
bb5d8497c9 | ||
![]() |
3ea6028c9f | ||
![]() |
6e901ff362 | ||
![]() |
d1032e4d7d | ||
![]() |
67efda7cf4 | ||
![]() |
0545d9bab7 | ||
![]() |
a233e5d404 | ||
![]() |
2484563ac7 | ||
![]() |
4d4ed7bc7b | ||
![]() |
e75ea257e8 | ||
![]() |
ff9df4a6ed | ||
![]() |
fdc051114b | ||
![]() |
7aa8dcb0e3 | ||
![]() |
37531ec5e0 | ||
![]() |
f8991ca8e0 | ||
![]() |
cd9c46e88e | ||
![]() |
04c28d4105 | ||
![]() |
59975087c5 | ||
![]() |
0f804937f7 | ||
![]() |
f3cda7d551 | ||
![]() |
209dba6a4a | ||
![]() |
43bc42826b | ||
![]() |
004375b478 | ||
![]() |
49a1c3f8c2 | ||
![]() |
c748394ff0 | ||
![]() |
9d246b1ad4 | ||
![]() |
038dc21c78 | ||
![]() |
7c758c0750 | ||
![]() |
0925c90047 | ||
![]() |
74b94bc66a | ||
![]() |
65e2177964 | ||
![]() |
a48c6e5b43 | ||
![]() |
b00f3102a6 | ||
![]() |
f8679a251e | ||
![]() |
9484eb9f6b | ||
![]() |
dd6c25be13 | ||
![]() |
4e917b249d | ||
![]() |
a87f96d780 | ||
![]() |
b959455ff8 | ||
![]() |
1e6c666df4 | ||
![]() |
1fd0f45a02 | ||
![]() |
93487b1aeb | ||
![]() |
0772231ac0 | ||
![]() |
37a5627b40 | ||
![]() |
a679a1966a | ||
![]() |
b21e8a77be | ||
![]() |
049a2dde96 | ||
![]() |
78a4891dfc | ||
![]() |
5c9c10a6db | ||
![]() |
16cb3ee779 | ||
![]() |
5bfadc2147 | ||
![]() |
80d05add03 | ||
![]() |
3493f4da7b | ||
![]() |
374ce47fcd | ||
![]() |
bc58e459ea | ||
![]() |
25c8fe5c0e | ||
![]() |
dad9de3be3 | ||
![]() |
f8d1fc749c | ||
![]() |
bbd4da5a27 | ||
![]() |
acc94dcde0 | ||
![]() |
72685b0330 | ||
![]() |
cffd59f9ba | ||
![]() |
c5e55be84a | ||
![]() |
03b48078df | ||
![]() |
f0908f3a58 | ||
![]() |
49e6e54e0b | ||
![]() |
cbe4e698c5 | ||
![]() |
aa39abc41a | ||
![]() |
c0ce6e28d4 | ||
![]() |
9b42c0aa50 | ||
![]() |
f7caad9af9 | ||
![]() |
992406d5a0 | ||
![]() |
60ad0bb4e2 | ||
![]() |
f23d53fe1a | ||
![]() |
12fa3c7417 | ||
![]() |
fc695896dd | ||
![]() |
13885968e3 | ||
![]() |
e331f23bb0 | ||
![]() |
d89ac59004 | ||
![]() |
5b365a15c5 | ||
![]() |
d8617746eb | ||
![]() |
1a830eb555 | ||
![]() |
bb0e858dfe | ||
![]() |
4377998232 | ||
![]() |
6852127c33 | ||
![]() |
83e40f3e87 | ||
![]() |
31d96ea85b | ||
![]() |
9f895fe65f | ||
![]() |
84f96d7a98 | ||
![]() |
a6144e9692 | ||
![]() |
88d5bdc8bf | ||
![]() |
c2fae07c11 | ||
![]() |
630cdaf08f | ||
![]() |
2309b12a31 | ||
![]() |
8dc57e8e87 | ||
![]() |
da4c6e8fb6 | ||
![]() |
a1a01870e6 | ||
![]() |
80d2f34ad0 | ||
![]() |
b5b7cf67bf | ||
![]() |
ca87b6dbfd | ||
![]() |
7cfad2d7ef | ||
![]() |
ae18b82282 | ||
![]() |
701506e569 | ||
![]() |
2f04e0de85 | ||
![]() |
a561cfe808 | ||
![]() |
9ce3708647 | ||
![]() |
9dcfda1f41 | ||
![]() |
e23859f776 | ||
![]() |
ae94b90fe4 | ||
![]() |
cbac6fc8d5 | ||
![]() |
5941c383b6 | ||
![]() |
396c3fb554 | ||
![]() |
dbdd9b93f8 | ||
![]() |
84e7e74d00 | ||
![]() |
0103e6546f | ||
![]() |
a1f09bdc4e | ||
![]() |
ba561604f0 | ||
![]() |
bad4be4cbc | ||
![]() |
2f7c351039 | ||
![]() |
4b05a18e3e | ||
![]() |
b7352e255e | ||
![]() |
1408270fb1 | ||
![]() |
cd46b7ae45 | ||
![]() |
6ad5d19cf8 | ||
![]() |
66bbf322c5 | ||
![]() |
7bdd397c78 | ||
![]() |
86069c291f | ||
![]() |
ba87146c61 | ||
![]() |
47e177a575 | ||
![]() |
b700b3d1f7 | ||
![]() |
11a377b973 | ||
![]() |
e889c2e54e | ||
![]() |
ab81439aac | ||
![]() |
75b4d46f4c | ||
![]() |
d680711ffc | ||
![]() |
ba38c2ad63 | ||
![]() |
140b179607 | ||
![]() |
49d7492e05 | ||
![]() |
c8ad4e9bcd | ||
![]() |
49bc8f95f6 | ||
![]() |
50bab47f43 | ||
![]() |
1e288100a9 | ||
![]() |
d13fdd0791 | ||
![]() |
73a8569d21 | ||
![]() |
2a47b3f1a1 | ||
![]() |
41494ee689 | ||
![]() |
8bf6fadf0e | ||
![]() |
13b2c438b9 | ||
![]() |
368f7ab0ed | ||
![]() |
8b23a5e4c1 | ||
![]() |
60a87f1541 | ||
![]() |
0827553a69 | ||
![]() |
1688af7a0d | ||
![]() |
1412daf615 | ||
![]() |
57db05f95b | ||
![]() |
1c1e7183e3 | ||
![]() |
ed48001fb2 | ||
![]() |
d864734bc5 | ||
![]() |
150740766f | ||
![]() |
848f52688b | ||
![]() |
bcab0e8579 | ||
![]() |
e6cce4069e | ||
![]() |
ddfeeb6af7 | ||
![]() |
5c2b524d07 | ||
![]() |
17d51a1cf6 | ||
![]() |
01ce6c5f6f | ||
![]() |
79aa56c898 | ||
![]() |
93e8d49540 | ||
![]() |
c5f31481b9 | ||
![]() |
56cdbc0c60 | ||
![]() |
d0a5160978 | ||
![]() |
106b55fc93 | ||
![]() |
573da8bd03 | ||
![]() |
7845cece6b | ||
![]() |
74dea42687 | ||
![]() |
0d610e364c | ||
![]() |
0476c7204f | ||
![]() |
e0683c19c8 | ||
![]() |
a7e1c1c4d0 | ||
![]() |
335b8c63e0 | ||
![]() |
f9bbb9a8c3 | ||
![]() |
fe5293ba36 | ||
![]() |
3297596f2e | ||
![]() |
5b5e8d7860 | ||
![]() |
7049d04d71 | ||
![]() |
17ddf18a67 | ||
![]() |
e60bfda93f | ||
![]() |
40dda1e0f7 | ||
![]() |
3715afbb97 | ||
![]() |
6f22f720e9 | ||
![]() |
785fcfb998 | ||
![]() |
ddce6e1f0a | ||
![]() |
199affb244 | ||
![]() |
899e3eed1b | ||
![]() |
744a0f1c8c | ||
![]() |
c6fc435869 | ||
![]() |
81ef51fd01 | ||
![]() |
0eaa1e3385 | ||
![]() |
e9a3a53849 | ||
![]() |
4ae4fb65e6 | ||
![]() |
95a4d7e8a9 | ||
![]() |
bf30297829 | ||
![]() |
f5f6985e4e | ||
![]() |
efe5df3459 | ||
![]() |
99409f258f | ||
![]() |
4400915e0c | ||
![]() |
37f6b249dc | ||
![]() |
4826c645cd | ||
![]() |
545b8e2b6e | ||
![]() |
d6744f0996 | ||
![]() |
55b980991b | ||
![]() |
add09db40a | ||
![]() |
f08901c639 | ||
![]() |
fd58096112 | ||
![]() |
3a652a8941 | ||
![]() |
52e6b45580 | ||
![]() |
7b7ba83a87 | ||
![]() |
8ba282362d | ||
![]() |
4451acbf0b | ||
![]() |
d257eeeabf | ||
![]() |
9f062fa14b | ||
![]() |
ba1038b4a8 | ||
![]() |
ef1b971fca | ||
![]() |
3444f9e5f2 | ||
![]() |
af768b8d66 | ||
![]() |
fbe0cdadd4 | ||
![]() |
ff3d455f45 | ||
![]() |
2eba2942d0 | ||
![]() |
556143a941 | ||
![]() |
e82011a91d | ||
![]() |
f2f80685a8 | ||
![]() |
7aa77eab07 | ||
![]() |
419fe7fc68 | ||
![]() |
1d4b5e243c | ||
![]() |
d25604da81 | ||
![]() |
17617b82c6 | ||
![]() |
3f89fdf300 | ||
![]() |
0cc9b7e357 | ||
![]() |
32e1188207 | ||
![]() |
c634e6a801 | ||
![]() |
d1a7557405 | ||
![]() |
b1a7c27d96 | ||
![]() |
5ebf8534ba | ||
![]() |
65bb5d067a | ||
![]() |
0a5da90f84 | ||
![]() |
59827290dc | ||
![]() |
b0f8d15542 | ||
![]() |
366ffec165 | ||
![]() |
0cd3059831 | ||
![]() |
db4da7092b | ||
![]() |
c6851c9f70 | ||
![]() |
7c4a098977 | ||
![]() |
cfb3db311c | ||
![]() |
b049ad4f3a | ||
![]() |
fd5a761c8c | ||
![]() |
e9befc44ec | ||
![]() |
180c82c694 | ||
![]() |
36b041376c | ||
![]() |
387db5ffb6 | ||
![]() |
ee27e71e29 | ||
![]() |
189110ec09 | ||
![]() |
4fc44060ea | ||
![]() |
c945b90b64 | ||
![]() |
7113a18f3b | ||
![]() |
921000625e | ||
![]() |
b272d695e3 | ||
![]() |
e93c85ba92 | ||
![]() |
6d58491d1c | ||
![]() |
413897639f | ||
![]() |
065d9bc1e3 | ||
![]() |
bb56254e88 | ||
![]() |
8b01336d54 | ||
![]() |
4d5f1bd81a | ||
![]() |
78efd4961e | ||
![]() |
1ab157c2a9 | ||
![]() |
10bde76494 | ||
![]() |
81499665d8 | ||
![]() |
8621546736 | ||
![]() |
abd27342b6 | ||
![]() |
f9a3a7f2c6 | ||
![]() |
165b476635 | ||
![]() |
7448609ba7 | ||
![]() |
7380e056cd | ||
![]() |
0b6c749b60 | ||
![]() |
1ba1df2529 | ||
![]() |
c7541cb516 | ||
![]() |
8ef913b117 | ||
![]() |
e1c29d09e7 | ||
![]() |
1b7f1614b5 | ||
![]() |
dcb8c17c75 | ||
![]() |
f42811285d | ||
![]() |
f9aa40fb57 | ||
![]() |
d3900536a1 | ||
![]() |
01c139ef2d | ||
![]() |
6442f1c572 | ||
![]() |
8b5a35cc70 | ||
![]() |
8d443f212a | ||
![]() |
6ab2e881a3 | ||
![]() |
c9baa76afd | ||
![]() |
66b9a99e71 | ||
![]() |
27a835a69c | ||
![]() |
1830cd856a | ||
![]() |
c656610c93 | ||
![]() |
e32f63d76e | ||
![]() |
7eca68dfc0 | ||
![]() |
75e3506046 | ||
![]() |
c839a5dd55 | ||
![]() |
7349ab063d | ||
![]() |
165f1d3341 | ||
![]() |
2a91a09835 | ||
![]() |
8f20003687 | ||
![]() |
94e07fee96 | ||
![]() |
c6618b2cc0 | ||
![]() |
7af291622f | ||
![]() |
e7603eff5f | ||
![]() |
866f365e0b | ||
![]() |
eac40ccd26 | ||
![]() |
1e6d9dda40 | ||
![]() |
7ecf42d70f | ||
![]() |
d92e48bc33 | ||
![]() |
51be0f07a5 | ||
![]() |
6751ed33af | ||
![]() |
5313909cd8 | ||
![]() |
a6e65583f2 | ||
![]() |
e17a33e110 | ||
![]() |
2a99765020 | ||
![]() |
98f2ba949c | ||
![]() |
66318b7e39 | ||
![]() |
eb91b02ebf | ||
![]() |
c643c0fe36 | ||
![]() |
285579822a | ||
![]() |
adacb2ab92 | ||
![]() |
e338274669 | ||
![]() |
7ce65cbbaa | ||
![]() |
7e0345cda6 | ||
![]() |
c27a7ac60a | ||
![]() |
0e7f03c595 | ||
![]() |
6bc44fd93f | ||
![]() |
074decc320 | ||
![]() |
ccc99a45f9 | ||
![]() |
dab8b6ac87 | ||
![]() |
a134ca098a | ||
![]() |
8d2a809df2 | ||
![]() |
0b8db1fec9 | ||
![]() |
791e6cf54f | ||
![]() |
14a117acfa | ||
![]() |
6eef72d524 | ||
![]() |
05543e5341 | ||
![]() |
4b64b80d86 | ||
![]() |
c20c1f96ca | ||
![]() |
7194915bf3 | ||
![]() |
8156580e4f | ||
![]() |
b63a01f527 | ||
![]() |
10b16dbcdf | ||
![]() |
4d24f92914 | ||
![]() |
8db6d9cd34 | ||
![]() |
821398437f | ||
![]() |
1a2213b1fb | ||
![]() |
e49b4c137e | ||
![]() |
cabb5cafca | ||
![]() |
016f7d90dc | ||
![]() |
934d5f7164 | ||
![]() |
a2c0d6d920 | ||
![]() |
7e1161db62 | ||
![]() |
4cfdb54a0e | ||
![]() |
b35544a44d | ||
![]() |
46a4ec7dd0 | ||
![]() |
35202662f3 | ||
![]() |
d1267dc398 | ||
![]() |
37b1b9ae39 | ||
![]() |
31f9938d1b | ||
![]() |
f8b5ec0f7d | ||
![]() |
1dc474d33d | ||
![]() |
1dfe86a069 | ||
![]() |
63778935b0 | ||
![]() |
39f7f5e512 | ||
![]() |
e320c32182 | ||
![]() |
cd8c524b19 | ||
![]() |
f7554e7f33 | ||
![]() |
6ae39361a5 | ||
![]() |
dfe88864ad | ||
![]() |
8c14d05494 | ||
![]() |
bc77cbf956 | ||
![]() |
0bb9f47590 | ||
![]() |
effa3cd3a5 | ||
![]() |
fd811d68ff | ||
![]() |
fda64e81a5 | ||
![]() |
3c598dfca9 | ||
![]() |
0c1e1b6b68 | ||
![]() |
7f2f4c666e | ||
![]() |
94e6e36f26 | ||
![]() |
45a534e176 | ||
![]() |
3303ee1afc | ||
![]() |
337330a508 | ||
![]() |
8b6001c328 | ||
![]() |
9ebf3c270c | ||
![]() |
fbde0ce88f | ||
![]() |
5009989a59 | ||
![]() |
436368ad22 | ||
![]() |
cc7bbeca97 | ||
![]() |
d608d6a441 | ||
![]() |
cd80f7ddbe | ||
![]() |
5bff97ec96 | ||
![]() |
23c1ea1b79 | ||
![]() |
097d1a55fe | ||
![]() |
13426d3c3f | ||
![]() |
e4859412bc | ||
![]() |
95309ad4ae | ||
![]() |
41bdf1bcc9 | ||
![]() |
04a4cd7d76 | ||
![]() |
582f7a9301 | ||
![]() |
9bc48fed73 | ||
![]() |
3e7e7023fc | ||
![]() |
8c853e56e4 | ||
![]() |
1941be8f53 | ||
![]() |
06253ed8f2 | ||
![]() |
ec5416abfd | ||
![]() |
940d056657 | ||
![]() |
a10d331296 | ||
![]() |
c2035b4324 | ||
![]() |
879a239bcd | ||
![]() |
4d1e33af9e | ||
![]() |
64c7699a19 | ||
![]() |
0219aa97fe | ||
![]() |
657ed211c0 | ||
![]() |
305666378b | ||
![]() |
50102af229 | ||
![]() |
ff4fa4f164 | ||
![]() |
3e35d9cd8c | ||
![]() |
e1a36972b1 | ||
![]() |
250575acb9 | ||
![]() |
d9cd312d07 | ||
![]() |
d733de9d9f | ||
![]() |
088cc9cd71 | ||
![]() |
895781c5a3 | ||
![]() |
f493bd539d | ||
![]() |
de9fbd48df | ||
![]() |
1e58b0c43e | ||
![]() |
68cfde5b65 | ||
![]() |
99157b76b2 | ||
![]() |
5f58a329d3 | ||
![]() |
67b9977d57 | ||
![]() |
b469937d4d | ||
![]() |
30b540045e | ||
![]() |
87af195862 | ||
![]() |
0ff001b4de | ||
![]() |
d6280b7939 | ||
![]() |
07f10d3907 | ||
![]() |
9fb18802fc | ||
![]() |
0936a2c474 | ||
![]() |
edd21d1233 | ||
![]() |
86e4ec03dd | ||
![]() |
994d502c41 | ||
![]() |
3d3de5d0fe | ||
![]() |
f303a1c423 | ||
![]() |
1fa814e6e5 | ||
![]() |
0aed34cb71 | ||
![]() |
abddbb11c1 | ||
![]() |
a25ba99bfb | ||
![]() |
512615631a | ||
![]() |
98086fd04b | ||
![]() |
fe2aa2bbf3 | ||
![]() |
da361e9d64 | ||
![]() |
f75d4ccc89 | ||
![]() |
c2621f4633 | ||
![]() |
e6602f1153 | ||
![]() |
069e78881d | ||
![]() |
e4e7ee8433 | ||
![]() |
ddb25ce281 | ||
![]() |
b4863173bd | ||
![]() |
140dbe1686 | ||
![]() |
f2f7b71367 | ||
![]() |
10a57687a3 | ||
![]() |
f84be89e49 | ||
![]() |
fb6369f43c | ||
![]() |
4b7c8c6157 | ||
![]() |
81e8317326 | ||
![]() |
3d6ca2cda3 | ||
![]() |
0740473ecc | ||
![]() |
1bce697475 | ||
![]() |
baaae7f76c | ||
![]() |
bb19d4de9f | ||
![]() |
6880f72472 | ||
![]() |
89fda615f6 | ||
![]() |
2c84d29050 | ||
![]() |
85399cf99f | ||
![]() |
e655b40d3a | ||
![]() |
3e94f3c58c | ||
![]() |
61a42f45ab | ||
![]() |
4e7863c27f | ||
![]() |
f9a5081438 | ||
![]() |
ea5d7ac6f0 | ||
![]() |
b318c0ab04 | ||
![]() |
3d0a7c73e9 | ||
![]() |
e98bf547e5 | ||
![]() |
8cb2e588fb | ||
![]() |
e4f99de13d | ||
![]() |
206d5be46c | ||
![]() |
e0f80c4364 | ||
![]() |
86891323ea | ||
![]() |
69e315ee8a | ||
![]() |
ced3aefc54 | ||
![]() |
6abf26bd99 | ||
![]() |
4667a7a6a2 | ||
![]() |
6eb5f3a518 | ||
![]() |
6f87b7830d | ||
![]() |
ccf974b1b7 | ||
![]() |
d9016ed619 | ||
![]() |
bd2eade33d | ||
![]() |
11e8ea471d | ||
![]() |
992bef1553 | ||
![]() |
396235be3e | ||
![]() |
9662463510 | ||
![]() |
64e1590d9e | ||
![]() |
c751690e79 | ||
![]() |
f76915117b | ||
![]() |
ba915844a4 | ||
![]() |
59d96d170e | ||
![]() |
a9a27eda51 | ||
![]() |
249de7ec6d | ||
![]() |
8aabf4805a | ||
![]() |
2ab06d7294 | ||
![]() |
e71f38deea | ||
![]() |
0d70925abe | ||
![]() |
89bb73aa25 | ||
![]() |
1c93f26034 | ||
![]() |
86e44da695 | ||
![]() |
530ad72e18 | ||
![]() |
ae185a1848 | ||
![]() |
e3682b69f7 | ||
![]() |
fe72fc173d | ||
![]() |
3b4663639d | ||
![]() |
51b20a6e17 | ||
![]() |
ad9122cd4f | ||
![]() |
7d8bf75e44 | ||
![]() |
e36e4f61b3 | ||
![]() |
5612ad08dd | ||
![]() |
7bc8325df9 | ||
![]() |
2aca64bfcd | ||
![]() |
e9956234a3 | ||
![]() |
d656de8883 | ||
![]() |
32f0ebaac3 | ||
![]() |
73700437e4 | ||
![]() |
14d96350c3 | ||
![]() |
4e9c4ebd02 | ||
![]() |
097fe424c2 | ||
![]() |
12abb592be | ||
![]() |
4f0951402a | ||
![]() |
b555b3664f | ||
![]() |
2dcacaee14 | ||
![]() |
bf2cdae3b1 | ||
![]() |
64304c5121 | ||
![]() |
cd43bc1f66 | ||
![]() |
1b24136590 | ||
![]() |
e8d5b549de | ||
![]() |
3866a77d68 | ||
![]() |
360fe68f92 | ||
![]() |
c9229f9c80 | ||
![]() |
e5b92d895d | ||
![]() |
a7643c1c29 | ||
![]() |
45fd4e8565 | ||
![]() |
211a9d720a | ||
![]() |
da9c2735b4 | ||
![]() |
d31baca442 | ||
![]() |
499d6f79aa | ||
![]() |
49e049f1c5 | ||
![]() |
7411d7c1e2 | ||
![]() |
3f5d3d15fd | ||
![]() |
68fb85ceca | ||
![]() |
3ebe6d5aef | ||
![]() |
566b3ed53d | ||
![]() |
a688c747d7 | ||
![]() |
83b90e1416 | ||
![]() |
23c6f849d6 | ||
![]() |
ba6843df83 | ||
![]() |
0b90e69119 | ||
![]() |
dbe820323e | ||
![]() |
7680bf7c0d | ||
![]() |
47d95d5b0a | ||
![]() |
757507e634 | ||
![]() |
971192f6f3 | ||
![]() |
07d3f47978 | ||
![]() |
fbfc7df593 | ||
![]() |
3c7644ce79 | ||
![]() |
4e4a8bcca8 | ||
![]() |
dc5586b16d | ||
![]() |
1d87b48471 | ||
![]() |
353a3432df | ||
![]() |
efe94bbf18 | ||
![]() |
cea6ec09bc | ||
![]() |
4a3276f69c | ||
![]() |
f164def2ed | ||
![]() |
511c76ae7a | ||
![]() |
bfa6dda0de | ||
![]() |
54af734971 | ||
![]() |
5b9763ecb4 | ||
![]() |
438bad23e8 | ||
![]() |
ae5a205ba3 | ||
![]() |
19b683e7b0 | ||
![]() |
0c92027414 | ||
![]() |
c4e3b2ca2f | ||
![]() |
ba67b89db7 | ||
![]() |
06f308ede3 | ||
![]() |
214fb8204b | ||
![]() |
2a2d7be9f9 | ||
![]() |
23304c3746 | ||
![]() |
20829c90b2 | ||
![]() |
33cc3ce7e0 | ||
![]() |
3487b23e88 | ||
![]() |
ec707bd02a | ||
![]() |
3cc463b2c3 | ||
![]() |
99e5cf0cdc | ||
![]() |
ab0ec0d5a5 | ||
![]() |
e0a2af3d98 | ||
![]() |
524bc2722f | ||
![]() |
4718fe13c2 | ||
![]() |
8592acf6b8 | ||
![]() |
88042d7072 | ||
![]() |
bf92e52d5c | ||
![]() |
419580b3be | ||
![]() |
5ee1f6b82b | ||
![]() |
410bb6a84e | ||
![]() |
af2b5fef13 | ||
![]() |
372ac3a40c | ||
![]() |
a1507d6079 | ||
![]() |
9242a8901a | ||
![]() |
1d4f25f930 | ||
![]() |
7323ec8d16 | ||
![]() |
39d45367d0 | ||
![]() |
a6521952b0 | ||
![]() |
7148c10f1b | ||
![]() |
4a1a66248d | ||
![]() |
67d0773231 | ||
![]() |
ff370172b5 | ||
![]() |
1f707e86cc | ||
![]() |
a3dae02cfb | ||
![]() |
3eae8a2210 | ||
![]() |
0f18fe1853 | ||
![]() |
513c61eb79 | ||
![]() |
4031233fd4 | ||
![]() |
76ff6100a3 | ||
![]() |
05c36f91cf | ||
![]() |
6625f8962a | ||
![]() |
63402b70d2 | ||
![]() |
2ae4a7806d | ||
![]() |
fcc4ecd007 | ||
![]() |
45497250cd | ||
![]() |
7dd957140e | ||
![]() |
4cd772a39e | ||
![]() |
b3906e7bc1 | ||
![]() |
e5009b4cd2 | ||
![]() |
10bf9fd1f8 | ||
![]() |
bd8e68692e | ||
![]() |
b8386a1531 | ||
![]() |
61534fb29d | ||
![]() |
9ad81be38c | ||
![]() |
cf7048e336 | ||
![]() |
a6105b3ad3 | ||
![]() |
a262a82cad | ||
![]() |
635c96accf | ||
![]() |
5c174a69c2 | ||
![]() |
de08d17835 | ||
![]() |
ca1e838c52 | ||
![]() |
3b73146ecd | ||
![]() |
8e3822f8e6 | ||
![]() |
3b912a0de1 | ||
![]() |
c72791bd21 | ||
![]() |
80ba5b561f | ||
![]() |
f9f4d4c937 | ||
![]() |
0419c52d35 | ||
![]() |
93a79be1e2 | ||
![]() |
f8afbae2cd | ||
![]() |
4a52d346f9 | ||
![]() |
7d59d11725 | ||
![]() |
5886348188 | ||
![]() |
a8135528b1 | ||
![]() |
df6b991161 | ||
![]() |
3f33f66387 | ||
![]() |
870808f3c2 | ||
![]() |
b12fcca20d | ||
![]() |
61b47e358f | ||
![]() |
40a2fb7936 | ||
![]() |
9afc8ba43d | ||
![]() |
b122a05c71 | ||
![]() |
f568a9fdfa | ||
![]() |
a350bb3086 | ||
![]() |
8ae4b7560b | ||
![]() |
96f4924911 | ||
![]() |
f3703fc6e3 | ||
![]() |
0d5fd229bf | ||
![]() |
1519e66550 | ||
![]() |
fcae461430 | ||
![]() |
039d797dfc | ||
![]() |
4f61868b28 | ||
![]() |
9efbc55fd6 | ||
![]() |
2e4e88114e | ||
![]() |
8ee2e8b23d | ||
![]() |
37287c3e7e | ||
![]() |
814d90745b | ||
![]() |
48e8076e34 | ||
![]() |
edb9d934b2 | ||
![]() |
ed44f0cb04 | ||
![]() |
1c2f3cf9af | ||
![]() |
13afd25690 | ||
![]() |
f9b6374685 | ||
![]() |
56c9b578e3 | ||
![]() |
e792a00feb | ||
![]() |
c35c4eade9 | ||
![]() |
5bf725546b | ||
![]() |
6b6862705e | ||
![]() |
b89b51b121 | ||
![]() |
1b31388232 | ||
![]() |
8256e73f04 | ||
![]() |
fc4e59ec00 | ||
![]() |
428e00ba23 | ||
![]() |
2ccbb08c3f | ||
![]() |
eefc026a17 | ||
![]() |
d89022a280 | ||
![]() |
93bd24c9d2 | ||
![]() |
197aec2945 | ||
![]() |
e16d58cff5 | ||
![]() |
92667009be | ||
![]() |
c93657f40e | ||
![]() |
16658f5939 | ||
![]() |
ec0af59596 | ||
![]() |
9e1f9d6d9d | ||
![]() |
e652b927ce | ||
![]() |
5a97f3f94b | ||
![]() |
9494c243a4 | ||
![]() |
c0a1be4325 | ||
![]() |
70fda8e2f8 | ||
![]() |
5765893f69 | ||
![]() |
dc26c9b7cc | ||
![]() |
17b9d7eb42 | ||
![]() |
0a897cae32 | ||
![]() |
37f56e824a | ||
![]() |
a8e7ec0e96 | ||
![]() |
b86d853cf1 | ||
![]() |
da0c94c9a1 | ||
![]() |
0547fd2760 | ||
![]() |
e0a6df8435 | ||
![]() |
c2d4ca7566 | ||
![]() |
56ce278bbd | ||
![]() |
e112541f5b | ||
![]() |
17c7b0840b | ||
![]() |
68658bd407 | ||
![]() |
9184845a9d | ||
![]() |
8ddb2fb305 | ||
![]() |
96f53986b7 | ||
![]() |
8566682209 | ||
![]() |
4f9ae17059 | ||
![]() |
7f9f805c5f | ||
![]() |
5208e81715 | ||
![]() |
01a786c117 | ||
![]() |
8bf4f31954 | ||
![]() |
07ab3e98ed | ||
![]() |
ba5899de7b | ||
![]() |
a4fef056ed | ||
![]() |
c266cd6aae | ||
![]() |
43d05b2e6d | ||
![]() |
6f0c14cc07 | ||
![]() |
2eac8fa91c | ||
![]() |
31f5e178b3 | ||
![]() |
c3d7088168 | ||
![]() |
a9e11b1cb7 | ||
![]() |
ee51a0be13 | ||
![]() |
1091387f48 | ||
![]() |
4db75537cb | ||
![]() |
cb4e738a0f | ||
![]() |
0cc32e7ed7 | ||
![]() |
94f90e1e81 | ||
![]() |
ce8f315747 | ||
![]() |
b761a549c0 | ||
![]() |
dd7c5da256 | ||
![]() |
02dd7ec615 | ||
![]() |
b7b448b870 | ||
![]() |
b7326afe5a | ||
![]() |
a3c4e4c6b6 | ||
![]() |
645297d68c | ||
![]() |
6d8782f771 | ||
![]() |
2728183eee | ||
![]() |
3d0891c73b | ||
![]() |
8226a8793e | ||
![]() |
d01477b337 | ||
![]() |
6bb07c72ab | ||
![]() |
a009462b80 | ||
![]() |
9111b130e4 | ||
![]() |
1dbd7b9bb4 | ||
![]() |
2f95dfc0e7 | ||
![]() |
acc317ddfd | ||
![]() |
1fbd3935ea | ||
![]() |
ca2bf962e9 | ||
![]() |
1d27a3a14b | ||
![]() |
8e1a9dde05 | ||
![]() |
8eabf8c77a | ||
![]() |
ec4ec41552 | ||
![]() |
8960b0300f | ||
![]() |
d6a2672cab | ||
![]() |
4b281ca89d | ||
![]() |
808b507b0f | ||
![]() |
ab47d03e1a | ||
![]() |
aca999090b | ||
![]() |
1322aaf4da | ||
![]() |
c49471fb3b | ||
![]() |
d13baab0a6 | ||
![]() |
359b46c15b | ||
![]() |
3b83e9a43d | ||
![]() |
ab7a499e8f | ||
![]() |
fffe4f694f | ||
![]() |
18c028cafd | ||
![]() |
4e289c7dab | ||
![]() |
3dfe5c9262 | ||
![]() |
be87caf7f6 | ||
![]() |
b7063b199a | ||
![]() |
1ed9c245f5 | ||
![]() |
fb1e9fe66a | ||
![]() |
38a386d5ae | ||
![]() |
726114575e | ||
![]() |
026c213ea4 | ||
![]() |
cd85d4e86a | ||
![]() |
e906bf58f0 | ||
![]() |
87d2209e6d | ||
![]() |
704f8ea680 | ||
![]() |
1e101fb3db | ||
![]() |
a16643ee29 | ||
![]() |
b783e0e211 | ||
![]() |
1ec4570c8d | ||
![]() |
434194d290 | ||
![]() |
f62e64357b | ||
![]() |
b9f49c5eca | ||
![]() |
13a839de57 | ||
![]() |
7eb4253c00 | ||
![]() |
dab210a183 | ||
![]() |
5319d4782a | ||
![]() |
0463c3fe54 | ||
![]() |
fe84430679 | ||
![]() |
9514e79bfb | ||
![]() |
b6756595d9 | ||
![]() |
8ddb3e80b7 | ||
![]() |
52bc1a62e1 | ||
![]() |
cd72ed2cec | ||
![]() |
ccaee4ce62 | ||
![]() |
0e596bd1fc | ||
![]() |
21fafd3255 | ||
![]() |
fda2bfbea7 | ||
![]() |
d26c46e034 | ||
![]() |
27cb243a2f | ||
![]() |
d3514fc5f9 | ||
![]() |
4954851b68 | ||
![]() |
6bcb12ddc9 | ||
![]() |
126a4ec21f | ||
![]() |
0aa084c792 | ||
![]() |
8f6e1360e1 | ||
![]() |
b7e570aba0 | ||
![]() |
ce2bae12af | ||
![]() |
b6111d8da6 | ||
![]() |
2fb1132b69 | ||
![]() |
9a04bc1beb | ||
![]() |
b39c3f7866 | ||
![]() |
740237a8fa | ||
![]() |
391db73ea8 | ||
![]() |
b6ff88645b | ||
![]() |
4cd1672094 | ||
![]() |
630cd814e2 | ||
![]() |
6606d7572c | ||
![]() |
b331e3388f | ||
![]() |
b6428aa85f | ||
![]() |
fa6d554d1f | ||
![]() |
816871eed3 | ||
![]() |
ee04a9226b | ||
![]() |
6b15093c43 | ||
![]() |
b9d0954924 | ||
![]() |
a94a81d839 | ||
![]() |
46be3924e4 | ||
![]() |
316a2469b8 | ||
![]() |
2e21fbf619 | ||
![]() |
e1254a053a | ||
![]() |
50bcd3daf1 | ||
![]() |
e3ba968fef | ||
![]() |
7725973b62 | ||
![]() |
30bf7418f5 | ||
![]() |
eb611b2c41 | ||
![]() |
e4909c3133 | ||
![]() |
2216330118 | ||
![]() |
ca090aa2fa | ||
![]() |
ec7ca2c352 | ||
![]() |
2eb5e289ce | ||
![]() |
40ce38254b | ||
![]() |
0ad2b05455 | ||
![]() |
c74d261f6a | ||
![]() |
68fe18fe36 | ||
![]() |
47313db2c7 | ||
![]() |
127129fbeb | ||
![]() |
f43a8d6f2e | ||
![]() |
7cde850d98 | ||
![]() |
1bcfc5b54d | ||
![]() |
7a8494da4d | ||
![]() |
65733863b1 | ||
![]() |
e62ecefcd7 | ||
![]() |
0aacebf783 | ||
![]() |
43b1700e91 | ||
![]() |
9b4625b4a5 | ||
![]() |
27998df6e4 | ||
![]() |
eb5eabf3c6 | ||
![]() |
76cee78408 | ||
![]() |
0ba35a6eb5 | ||
![]() |
6a58bd30ad | ||
![]() |
e71d055ef2 | ||
![]() |
cb2eff47cc | ||
![]() |
4696f9896b | ||
![]() |
424835880d | ||
![]() |
e0e0ee4a77 | ||
![]() |
5eca0ce554 | ||
![]() |
f5a06ac0dd | ||
![]() |
81ea8873e0 | ||
![]() |
63b4ccacde | ||
![]() |
ccc39e0e55 | ||
![]() |
d264df1504 | ||
![]() |
9b7bc16b3e | ||
![]() |
f7f51f4f73 | ||
![]() |
143ae1092c | ||
![]() |
6470a443ac | ||
![]() |
7795baf989 | ||
![]() |
5ab717c6be | ||
![]() |
fac265486a | ||
![]() |
707efff644 | ||
![]() |
15291fd659 | ||
![]() |
6535a20b21 | ||
![]() |
8c922dbffc | ||
![]() |
874f5fd2ca | ||
![]() |
50864b10dd | ||
![]() |
57a5df1fce | ||
![]() |
14828b15d8 | ||
![]() |
5f03e9e4cc | ||
![]() |
c2b82fa063 | ||
![]() |
40c4cfda75 | ||
![]() |
5c5ce6eedb | ||
![]() |
f9263ddb62 | ||
![]() |
2fd1074729 | ||
![]() |
2e379ad422 | ||
![]() |
c1cecb3e0e | ||
![]() |
b0d13bed72 | ||
![]() |
d23a0d230c | ||
![]() |
91b87f4e2c | ||
![]() |
a77c1f3ad1 | ||
![]() |
4c3cdaab0e | ||
![]() |
3e86c3d0d0 | ||
![]() |
5a40d3c6fe | ||
![]() |
f79c1e0bcc | ||
![]() |
8f14ff7210 | ||
![]() |
90732d6a2f | ||
![]() |
f179ff9da4 | ||
![]() |
90fd999220 | ||
![]() |
04afbdd938 | ||
![]() |
5d3ef3d338 | ||
![]() |
4477d083e8 | ||
![]() |
b7b3f54617 | ||
![]() |
5fc9f3f4da | ||
![]() |
c4b31f2f72 | ||
![]() |
6c6b0511a6 | ||
![]() |
6d20fc14ab | ||
![]() |
895c10600d | ||
![]() |
c024efa578 | ||
![]() |
0022588b02 | ||
![]() |
47ec3d9149 | ||
![]() |
811b82181d | ||
![]() |
32318ddbf6 | ||
![]() |
65a416fdae | ||
![]() |
58d1f98368 | ||
![]() |
4cd7bf7123 | ||
![]() |
d4dea13eee | ||
![]() |
507cdca757 | ||
![]() |
27bfb2d3b0 | ||
![]() |
43118a1ffd | ||
![]() |
729b305860 | ||
![]() |
72950d7872 | ||
![]() |
364ffe8f38 | ||
![]() |
84d822b497 | ||
![]() |
df99035c93 | ||
![]() |
63fc68cf83 | ||
![]() |
9c742fbf08 | ||
![]() |
9965c35d2f | ||
![]() |
0af567bc72 | ||
![]() |
0959a4e915 | ||
![]() |
83be71f622 | ||
![]() |
9c985b36b3 | ||
![]() |
4ef0e20b5c | ||
![]() |
d1cef044e5 | ||
![]() |
f1d4860c79 | ||
![]() |
f1d6dd7d36 | ||
![]() |
46ae7f16dd | ||
![]() |
5da441a001 | ||
![]() |
d495bfab51 | ||
![]() |
4b4f3e76cc | ||
![]() |
ab0e97c1a0 | ||
![]() |
381af329bd | ||
![]() |
4a0d17fc57 | ||
![]() |
e61f042547 | ||
![]() |
77815af5fb | ||
![]() |
e9cdb21164 | ||
![]() |
59bc467c50 | ||
![]() |
254dc62ca6 | ||
![]() |
253bde4e25 | ||
![]() |
ea9e852216 | ||
![]() |
89150e99bf | ||
![]() |
5fd05f3d20 | ||
![]() |
6c1a5f1a98 | ||
![]() |
7815a011af | ||
![]() |
efca0083bf | ||
![]() |
4c983b0e25 | ||
![]() |
d81f64ec06 | ||
![]() |
5735313392 | ||
![]() |
259b2fe429 | ||
![]() |
bb739a55a5 | ||
![]() |
7436b75566 | ||
![]() |
573ac882f5 | ||
![]() |
7f70a886d5 | ||
![]() |
4a1c0c2971 | ||
![]() |
ad37ccd76d | ||
![]() |
d3a4b17d59 | ||
![]() |
4233d73569 | ||
![]() |
6d9a0162b2 | ||
![]() |
72f89de994 | ||
![]() |
bf01799d57 | ||
![]() |
6b63d3855b | ||
![]() |
f0e0e780c5 | ||
![]() |
646803b93e | ||
![]() |
3026b59af4 | ||
![]() |
5adee00ca1 | ||
![]() |
47e887dac5 | ||
![]() |
70395a035c | ||
![]() |
2f63dcb3b5 | ||
![]() |
468ea09965 | ||
![]() |
b6a94d33b8 | ||
![]() |
40c89a8a38 | ||
![]() |
92a61902b5 | ||
![]() |
23ee01b1ce | ||
![]() |
fc727e0472 | ||
![]() |
9427c70b10 | ||
![]() |
cba71ed8e0 | ||
![]() |
728778bdf4 | ||
![]() |
30a92e25c1 | ||
![]() |
4862eb5e84 | ||
![]() |
42fd62ecfd | ||
![]() |
67437f7b9c | ||
![]() |
6c961dfad9 | ||
![]() |
b0b5f9e0a1 | ||
![]() |
69df7f8ef6 | ||
![]() |
0bb0fc5a19 | ||
![]() |
be15e86458 | ||
![]() |
24eff6b02b | ||
![]() |
ddb96829ca | ||
![]() |
4290f38e81 | ||
![]() |
ee4a5d53ad | ||
![]() |
1f4d2e76d7 | ||
![]() |
9816efb816 | ||
![]() |
8da1521508 | ||
![]() |
a7846925b1 | ||
![]() |
d678bd71e8 | ||
![]() |
67754efd36 | ||
![]() |
80e0fc21cc | ||
![]() |
1025f6ccb5 | ||
![]() |
ede06f4fd4 | ||
![]() |
6dd0de0201 | ||
![]() |
7f01ead374 | ||
![]() |
cba507258d | ||
![]() |
ae93999f6a | ||
![]() |
1ff7a89a35 | ||
![]() |
31cd32406c | ||
![]() |
e5f750dcbe | ||
![]() |
59ecff8701 | ||
![]() |
80e91cde70 | ||
![]() |
b1c2c79d80 | ||
![]() |
5b2576ace4 | ||
![]() |
da9f370924 | ||
![]() |
e56daf07d4 | ||
![]() |
9e3caa57ec | ||
![]() |
4fd7b9ef1f | ||
![]() |
12235cc853 | ||
![]() |
56e4264394 | ||
![]() |
baf8203b80 | ||
![]() |
41d966895f | ||
![]() |
551792274f | ||
![]() |
c7abdb61e8 | ||
![]() |
72ebe7df58 | ||
![]() |
97f3c214e8 | ||
![]() |
47842bac1c | ||
![]() |
ede498fa52 | ||
![]() |
c8a06416e7 | ||
![]() |
5fea7f2bc4 | ||
![]() |
15420137fa | ||
![]() |
73791e0e50 | ||
![]() |
7af0f12e6b | ||
![]() |
895f9c911b | ||
![]() |
204f47265b | ||
![]() |
b240b4821a | ||
![]() |
249f6e4f27 | ||
![]() |
32facd2877 | ||
![]() |
fd3cb6e2ca | ||
![]() |
1df6d857e5 | ||
![]() |
3b9c2fd4d2 | ||
![]() |
baa130e646 | ||
![]() |
c3ec40189f | ||
![]() |
686ae5b213 | ||
![]() |
5c19a8bb70 | ||
![]() |
efa7b7b0b5 | ||
![]() |
e466c31bb3 | ||
![]() |
942acf5940 | ||
![]() |
b38d8d8640 | ||
![]() |
c4c8a96f25 | ||
![]() |
65377d881c | ||
![]() |
b847e0c65e | ||
![]() |
6ab884a95c | ||
![]() |
4b17a485ae | ||
![]() |
e5a4715161 | ||
![]() |
53b7b14467 | ||
![]() |
5eeb012557 | ||
![]() |
cf9884aaba | ||
![]() |
d811cab57e | ||
![]() |
d5a3443076 | ||
![]() |
bd11d3a34f | ||
![]() |
5f6a202d65 | ||
![]() |
6db3f7023f | ||
![]() |
8a64898798 | ||
![]() |
6ad14d63d6 | ||
![]() |
a26150ca40 | ||
![]() |
57c9066f81 | ||
![]() |
fe3c3b8946 | ||
![]() |
76686c22fc | ||
![]() |
bba4057616 | ||
![]() |
10a1f34164 | ||
![]() |
4516a5def5 | ||
![]() |
4ef4af452a | ||
![]() |
8f1e95a0ce | ||
![]() |
824aea0c7b | ||
![]() |
7d54f0e336 | ||
![]() |
961b47239b | ||
![]() |
248f19a550 | ||
![]() |
29e6529884 | ||
![]() |
cb4f822a19 | ||
![]() |
2159fefc87 | ||
![]() |
d341bf14ff | ||
![]() |
c793ac9c6c | ||
![]() |
289589654b | ||
![]() |
8cb9c9faf9 | ||
![]() |
1c0069e95e | ||
![]() |
8f23642e7d | ||
![]() |
b59758379e | ||
![]() |
d91267ee91 | ||
![]() |
6196bc7b2e | ||
![]() |
adfe5c123c | ||
![]() |
573726d0b2 | ||
![]() |
5cb95b6e55 | ||
![]() |
4ce79955f8 | ||
![]() |
3d320b4e61 | ||
![]() |
420feb3a52 | ||
![]() |
4d425cf7b8 | ||
![]() |
f716300152 | ||
![]() |
fea625e443 | ||
![]() |
068fdbd3f9 | ||
![]() |
fd2cbc13ff | ||
![]() |
6a70b9c4b4 | ||
![]() |
4ee7d16d3e | ||
![]() |
6fd25da80d | ||
![]() |
2fb286cd43 | ||
![]() |
3a75b2571a | ||
![]() |
66fe821b04 | ||
![]() |
ba478c6cbb | ||
![]() |
7b1145c75e | ||
![]() |
b45630080b | ||
![]() |
170ddbc76f | ||
![]() |
91b364f531 | ||
![]() |
aadcc85e73 | ||
![]() |
548b1ea7ec | ||
![]() |
463696e6a8 | ||
![]() |
66f7ae3773 | ||
![]() |
cc2b836646 | ||
![]() |
816a4c1887 | ||
![]() |
604da64f52 | ||
![]() |
247edc89f0 | ||
![]() |
6a1f5fb4cd | ||
![]() |
01c11f9fa0 | ||
![]() |
9434851c54 | ||
![]() |
a331e4b49c | ||
![]() |
772f5cb1a8 | ||
![]() |
0fa9d71da8 | ||
![]() |
3872c3c49b | ||
![]() |
018b53db1b | ||
![]() |
33ed2c8851 | ||
![]() |
c75e30fd28 | ||
![]() |
7cc940a16c | ||
![]() |
ba853be00d | ||
![]() |
a720ed6a77 | ||
![]() |
d84ca511d4 | ||
![]() |
95fe803bf6 | ||
![]() |
87c364a104 | ||
![]() |
7233695824 | ||
![]() |
3d3300ac32 | ||
![]() |
ef7775b658 | ||
![]() |
a8776603c3 | ||
![]() |
ee92ab1136 | ||
![]() |
83c88ca472 | ||
![]() |
6f22297158 | ||
![]() |
df87599f1b | ||
![]() |
367b5e73cb | ||
![]() |
d6cbea97f9 | ||
![]() |
e43ab23a45 | ||
![]() |
706b1f4622 | ||
![]() |
5b75321a31 | ||
![]() |
2f2b5b90ea | ||
![]() |
cf3dedf4bb | ||
![]() |
d874145f52 | ||
![]() |
800f4deb90 | ||
![]() |
c22af0a782 | ||
![]() |
8465b8cfca | ||
![]() |
6ae65e0ab4 | ||
![]() |
6a64bf6fc1 | ||
![]() |
f038bb90bb | ||
![]() |
a5ba14e446 | ||
![]() |
70f657beff | ||
![]() |
b1085c36ff | ||
![]() |
1896aa7da1 | ||
![]() |
087856c535 | ||
![]() |
27f1364caa | ||
![]() |
bec5365ac9 | ||
![]() |
0db14d6f74 | ||
![]() |
ccb44e1bd1 | ||
![]() |
8c3db627e4 | ||
![]() |
56204f8497 | ||
![]() |
fc75e88cb7 | ||
![]() |
d2719b5309 | ||
![]() |
e5254abfcd | ||
![]() |
96b8d35a00 | ||
![]() |
035b0a449b | ||
![]() |
6b20177b09 | ||
![]() |
15861ea41a | ||
![]() |
d836756e19 | ||
![]() |
191d9a7b2b | ||
![]() |
0394cd7631 | ||
![]() |
c1ddff4ee5 | ||
![]() |
55cc428cd3 | ||
![]() |
39505c9764 | ||
![]() |
887a2f9be1 | ||
![]() |
1328fa9e5e | ||
![]() |
513830f319 | ||
![]() |
440abcda7c | ||
![]() |
6f3fdbecea | ||
![]() |
3a46cc33ee | ||
![]() |
ae3eb84e01 | ||
![]() |
7a73e18596 | ||
![]() |
c89779e8fc | ||
![]() |
5ca8d10d04 | ||
![]() |
1ce6aef57c | ||
![]() |
49b23aafa3 | ||
![]() |
f888647b12 | ||
![]() |
065ff6eaf5 | ||
![]() |
bf976fe188 | ||
![]() |
5ce73574c8 | ||
![]() |
5d94a983d2 | ||
![]() |
25366af4bb | ||
![]() |
f397c5472c | ||
![]() |
6273eb0061 | ||
![]() |
734fd7c0fc | ||
![]() |
8797a58efc | ||
![]() |
8da85d3609 | ||
![]() |
127d30918d | ||
![]() |
3b553f6455 | ||
![]() |
6d934da5dd | ||
![]() |
aa3d91a338 | ||
![]() |
d64818b46c | ||
![]() |
99a18516b2 | ||
![]() |
30b0a30146 | ||
![]() |
cb10617979 | ||
![]() |
265432f2a5 | ||
![]() |
a13e9f23b1 | ||
![]() |
65b37f61ca | ||
![]() |
7751755399 | ||
![]() |
14e2ad7bc4 | ||
![]() |
dfc23a2b38 | ||
![]() |
d2fc840293 | ||
![]() |
37fe6fb9c3 | ||
![]() |
a21ec76997 | ||
![]() |
501d8d9683 | ||
![]() |
8562ca9a77 | ||
![]() |
29641e5d66 | ||
![]() |
ee7308be2d | ||
![]() |
ef4009e94f | ||
![]() |
27d2ae6976 | ||
![]() |
0f9675f9d6 | ||
![]() |
bac4a63cc8 | ||
![]() |
0453787d38 | ||
![]() |
afc3e41f13 | ||
![]() |
86d6316cc9 | ||
![]() |
7b2c1f82f5 | ||
![]() |
e2a932d744 | ||
![]() |
b978994525 | ||
![]() |
6da237dd9e | ||
![]() |
50c1978d36 | ||
![]() |
fdb310c497 | ||
![]() |
ce121a261d | ||
![]() |
ebdfd4241a | ||
![]() |
9cbb1c5726 | ||
![]() |
85dabccbe7 | ||
![]() |
a9a8189d4b | ||
![]() |
30579112d2 | ||
![]() |
ccfd009c1a | ||
![]() |
044a939623 | ||
![]() |
203bc162cd | ||
![]() |
31f03ef1d3 | ||
![]() |
4d3552dc64 | ||
![]() |
ea8a52404f | ||
![]() |
0ae9aecdef | ||
![]() |
4de4789605 | ||
![]() |
950bb46827 | ||
![]() |
44936dc5f0 | ||
![]() |
1140a878b4 | ||
![]() |
efb49af7ac | ||
![]() |
b5a8106a6a | ||
![]() |
0f80eee54e | ||
![]() |
0e237fa459 | ||
![]() |
702b985ceb | ||
![]() |
7d87bcbb98 | ||
![]() |
340521aa0d | ||
![]() |
7bc557a999 | ||
![]() |
dfa7cdf47e | ||
![]() |
0d78e58d77 | ||
![]() |
58df3d5767 | ||
![]() |
4e4d6e806c | ||
![]() |
6ff99945f3 | ||
![]() |
b7f1b9f8ad | ||
![]() |
08a44cf468 | ||
![]() |
a1162d6d5a | ||
![]() |
1c81d88013 | ||
![]() |
1e4ec7e29e | ||
![]() |
2c4e34dd0c | ||
![]() |
cb308fae7b | ||
![]() |
3f03d51b24 | ||
![]() |
831db6ab87 | ||
![]() |
43fdf634f2 | ||
![]() |
f07a6b4586 | ||
![]() |
2fcf484229 | ||
![]() |
8bf4241b16 | ||
![]() |
56bd966c02 | ||
![]() |
416101d557 | ||
![]() |
c330cca2c9 | ||
![]() |
7e88085377 | ||
![]() |
5e669534f2 | ||
![]() |
98b147b622 | ||
![]() |
df6c59bc4f | ||
![]() |
6e48da41e5 | ||
![]() |
5c8a01a6e8 | ||
![]() |
3d0a52c25f | ||
![]() |
43c729568b | ||
![]() |
62caeed283 | ||
![]() |
12836d4c68 | ||
![]() |
b48e67d714 | ||
![]() |
f91f4d71bb | ||
![]() |
0a1f264c71 | ||
![]() |
64d61ae2fa | ||
![]() |
5f0e800f6e | ||
![]() |
8b2965d55b | ||
![]() |
ed478a1d73 | ||
![]() |
13e91d8c95 | ||
![]() |
6ac90181cb | ||
![]() |
d6c3471909 | ||
![]() |
5b56fad9c7 | ||
![]() |
ed0b1fe115 | ||
![]() |
4211153527 | ||
![]() |
2f85461109 | ||
![]() |
3fa7dcb0cb | ||
![]() |
857fe3a55c | ||
![]() |
dd19ea46fe | ||
![]() |
21740a9d87 | ||
![]() |
658fb2f208 | ||
![]() |
252d4cb513 | ||
![]() |
5aed41223b | ||
![]() |
45dfbf3747 | ||
![]() |
e4fe5bebab | ||
![]() |
04519ee623 | ||
![]() |
6c8f010f7a | ||
![]() |
ed84cf26e7 | ||
![]() |
1bc961f0c0 | ||
![]() |
77d745381f | ||
![]() |
7082cb9c36 | ||
![]() |
34e84cc757 | ||
![]() |
9246411610 | ||
![]() |
8330b3598c | ||
![]() |
1d002149dc | ||
![]() |
8d6071e977 | ||
![]() |
7d67766508 | ||
![]() |
887dd122fe | ||
![]() |
a1293c77b9 | ||
![]() |
ee9a73aa95 | ||
![]() |
9df332b614 | ||
![]() |
d13e86a892 | ||
![]() |
69d7f8c180 | ||
![]() |
1ba89ddd09 | ||
![]() |
0c40a28ad3 | ||
![]() |
2b7424c42a | ||
![]() |
a9f1766d1c | ||
![]() |
fca8576d80 | ||
![]() |
05f59e7d5e | ||
![]() |
c9511680b3 | ||
![]() |
0ed001c56e | ||
![]() |
1e5a418191 | ||
![]() |
e05735bc0f | ||
![]() |
7621e10840 | ||
![]() |
d90080f325 | ||
![]() |
0c676b90f2 | ||
![]() |
c2d8bda83c | ||
![]() |
302ebf737e | ||
![]() |
816c95a4ae | ||
![]() |
40106f6fcc | ||
![]() |
61143b3ad1 | ||
![]() |
9b64eebd10 | ||
![]() |
731418349f | ||
![]() |
5306d58991 | ||
![]() |
7728920670 | ||
![]() |
f555bb95ae | ||
![]() |
dcb17f2f21 | ||
![]() |
37b3a30619 | ||
![]() |
b0fed61a01 | ||
![]() |
4a471138b2 | ||
![]() |
41540a3a5f | ||
![]() |
23d8305027 | ||
![]() |
1950f805b3 | ||
![]() |
d275a75517 | ||
![]() |
1576e07011 | ||
![]() |
07faba3983 | ||
![]() |
d9547e87af | ||
![]() |
d672455ad8 | ||
![]() |
96a572b7a6 | ||
![]() |
2a7bb27a78 | ||
![]() |
3f73cd5bfc | ||
![]() |
9c427f202d | ||
![]() |
bc02dc30b1 | ||
![]() |
8080b7a119 | ||
![]() |
b0fa0f2319 | ||
![]() |
6c5f9bbabb | ||
![]() |
a5edbcf9ef | ||
![]() |
a6bd118f5e | ||
![]() |
a73ff4ffa6 | ||
![]() |
14d04ff8d2 | ||
![]() |
d7cefabe98 | ||
![]() |
c926e0242a | ||
![]() |
9ac19417e7 | ||
![]() |
795dbc16d2 | ||
![]() |
29a7737829 | ||
![]() |
3e0c3c0bb2 | ||
![]() |
cde867c9c6 | ||
![]() |
ff8eb7fbce | ||
![]() |
3766d8687f | ||
![]() |
8f08201633 | ||
![]() |
893dcd9f28 | ||
![]() |
79059b9aa7 | ||
![]() |
861b2efb1d | ||
![]() |
cbf844e7db | ||
![]() |
88b39faa7e | ||
![]() |
08495deebb | ||
![]() |
05b9af6195 | ||
![]() |
26784a5325 |
9
.build-config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"qpdf": {
|
||||
"version": "10.6.3"
|
||||
},
|
||||
"jbig2enc": {
|
||||
"version": "0.29",
|
||||
"git_tag": "0.29"
|
||||
}
|
||||
}
|
@@ -17,3 +17,5 @@
|
||||
**/htmlcov
|
||||
/src/.pytest_cache
|
||||
.idea
|
||||
.venv/
|
||||
.vscode/
|
||||
|
@@ -18,7 +18,13 @@ max_line_length = off
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[*.yml]
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
|
||||
[*.rst]
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
|
||||
# Tests don't get a line width restriction. It's still a good idea to follow
|
||||
@@ -26,3 +32,6 @@ indent_style = space
|
||||
# violate it.
|
||||
[**/test_*.py]
|
||||
max_line_length = off
|
||||
|
||||
[Dockerfile*]
|
||||
indent_style = space
|
||||
|
3
.env
@@ -1 +1,2 @@
|
||||
COMPOSE_PROJECT_NAME=paperless
|
||||
COMPOSE_PROJECT_NAME=paperless
|
||||
export PROMPT="(pipenv-projectname)$P$G"
|
||||
|
86
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
name: Bug report
|
||||
description: Something is not working
|
||||
title: "[BUG] Concise description of the issue"
|
||||
labels: ["bug", "unconfirmed"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Have a question? 👉 [Start a new discussion](https://github.com/paperless-ngx/paperless-ngx/discussions/new) or [ask in chat](https://matrix.to/#/#paperless:adnidor.de).
|
||||
|
||||
Before opening an issue, please double check:
|
||||
|
||||
- [The troubleshooting documentation](https://paperless-ngx.readthedocs.io/en/latest/troubleshooting.html).
|
||||
- [The installation instructions](https://paperless-ngx.readthedocs.io/en/latest/setup.html#installation).
|
||||
- [Existing issues and discussions](https://github.com/paperless-ngx/paperless-ngx/search?q=&type=issues).
|
||||
|
||||
If you encounter issues while installing or configuring Paperless-ngx, please post in the ["Support" section of the discussions](https://github.com/paperless-ngx/paperless-ngx/discussions/new?category=support).
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of what the bug is. If applicable, add screenshots to help explain your problem.
|
||||
placeholder: |
|
||||
Currently Paperless does not work when...
|
||||
|
||||
[Screenshot if applicable]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Webserver logs
|
||||
description: If available, post any logs from the web server related to your issue.
|
||||
render: bash
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Paperless-ngx version
|
||||
placeholder: e.g. 1.6.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: host-os
|
||||
attributes:
|
||||
label: Host OS
|
||||
description: Host OS of the machine running paperless-ngx. Please add the architecture (uname -m) if applicable.
|
||||
placeholder: e.g. Archlinux / Ubuntu 20.04 / Raspberry Pi `arm64`
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: install-method
|
||||
attributes:
|
||||
label: Installation method
|
||||
options:
|
||||
- Docker
|
||||
- Bare metal
|
||||
- Other (please describe above)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: browser
|
||||
attributes:
|
||||
label: Browser
|
||||
description: Which browser you are using, if relevant.
|
||||
placeholder: e.g. Chrome, Safari
|
||||
- type: input
|
||||
id: config-changes
|
||||
attributes:
|
||||
label: Configuration changes
|
||||
description: Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.
|
||||
- type: input
|
||||
id: other
|
||||
attributes:
|
||||
label: Other
|
||||
description: Any other relevant details.
|
48
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,48 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Something is not working
|
||||
title: "[BUG] Concise description of the issue"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
=> Before opening an issue, please check the documentation and see if it helps you resolve your issue: https://paperless-ng.readthedocs.io/en/latest/troubleshooting.html
|
||||
=> Please also make sure that you followed the installation instructions.
|
||||
=> Please search the issues and look for similar issues before opening a bug report.
|
||||
|
||||
=> If you would like to submit a feature request please submit one under https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests
|
||||
|
||||
=> If you encounter issues while installing of configuring Paperless-ng, please post that in the "Support" section of the discussions. Remember that Paperless successfully runs on a variety of different systems. If paperless does not start, it's probably an issue with your system, and not an issue of paperless.
|
||||
|
||||
=> Don't remove the [BUG] prefix from the title.
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Webserver logs**
|
||||
```
|
||||
If available, post any logs from the web server related to your issue.
|
||||
```
|
||||
|
||||
**Relevant information**
|
||||
- Host OS of the machine running paperless: [e.g. Archlinux / Ubuntu 20.04]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 1.0.0]
|
||||
- Installation method: [docker / bare metal]
|
||||
- Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.
|
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🤔 Questions and Help
|
||||
url: https://github.com/paperless-ngx/paperless-ngx/discussions
|
||||
about: This issue tracker is not for support questions. Please refer to our Discussions.
|
||||
- name: 💬 Chat
|
||||
url: https://matrix.to/#/#paperless:adnidor.de
|
||||
about: Want to discuss Paperless-ngx with others? Check out our chat.
|
||||
- name: 🚀 Feature Request
|
||||
url: https://github.com/paperless-ngx/paperless-ngx/discussions/new?category=feature-requests
|
||||
about: Remember to search for existing feature requests and "up-vote" any you like
|
20
.github/ISSUE_TEMPLATE/other.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Other
|
||||
about: Anything that is not a feature request or bug.
|
||||
title: "[Other] Title of your issue"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
=> Discussions, Feedback and other suggestions belong in the "Discussion" section and not on the issue tracker.
|
||||
|
||||
=> If you would like to submit a feature request please submit one under https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests
|
||||
|
||||
=> If you encounter issues while installing of configuring Paperless-ng, please post that in the "Support" section of the discussions. Remember that Paperless successfully runs on a variety of different systems. If paperless does not start, it's probably is an issue with your system, and not an issue of paperless.
|
||||
|
||||
=> Don't remove the [Other] prefix from the title.
|
||||
|
||||
-->
|
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<!--
|
||||
Note: All PRs with code changes should be targeted to the `dev` branch, pure documentation changes can target `main`
|
||||
-->
|
||||
|
||||
## Proposed change
|
||||
|
||||
<!--
|
||||
Please include a summary of the change and which issue is fixed (if any) and any relevant motivation / context. List any dependencies that are required for this change. If appropriate, please include an explanation of how your proposed change can be tested. Screenshots and / or videos can also be helpful if appropriate.
|
||||
-->
|
||||
|
||||
Fixes # (issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
<!--
|
||||
What type of change does your PR introduce to Paperless-ngx?
|
||||
NOTE: Please check only one box!
|
||||
-->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Other (please explain)
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] I have read & agree with the [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md).
|
||||
- [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
|
||||
- [ ] If applicable, I have checked that all tests pass, see [documentation](https://paperless-ngx.readthedocs.io/en/latest/extending.html#back-end-development).
|
||||
- [ ] I have run all `pre-commit` hooks, see [documentation](https://paperless-ngx.readthedocs.io/en/latest/extending.html#code-formatting-with-pre-commit-hooks).
|
||||
- [ ] I have made corresponding changes to the documentation as needed.
|
||||
- [ ] I have checked my modifications for any breaking changes.
|
48
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
# Enable version updates for npm
|
||||
- package-ecosystem: "npm"
|
||||
target-branch: "dev"
|
||||
# Look for `package.json` and `lock` files in the `/src-ui` directory
|
||||
directory: "/src-ui"
|
||||
# Check the npm registry for updates every month
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
labels:
|
||||
- "frontend"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/frontend"
|
||||
|
||||
# Enable version updates for Python
|
||||
- package-ecosystem: "pip"
|
||||
target-branch: "dev"
|
||||
# Look for a `Pipfile` in the `root` directory
|
||||
directory: "/"
|
||||
# Check for updates once a week
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "backend"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/backend"
|
||||
|
||||
# Enable updates for Github Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
target-branch: "dev"
|
||||
directory: "/"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every month
|
||||
interval: "monthly"
|
||||
labels:
|
||||
- "ci-cd"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
37
.github/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
categories:
|
||||
- title: 'Breaking Changes'
|
||||
labels:
|
||||
- 'breaking-change'
|
||||
- title: 'Features'
|
||||
labels:
|
||||
- 'enhancement'
|
||||
- title: 'Bug Fixes'
|
||||
labels:
|
||||
- 'bug'
|
||||
- title: 'Documentation'
|
||||
label: 'documentation'
|
||||
- title: 'Maintenance'
|
||||
labels:
|
||||
- 'chore'
|
||||
- 'deployment'
|
||||
- 'translation'
|
||||
- title: 'Dependencies'
|
||||
collapse-after: 3
|
||||
label: 'dependencies'
|
||||
include-labels:
|
||||
- 'enhancement'
|
||||
- 'bug'
|
||||
- 'chore'
|
||||
- 'deployment'
|
||||
- 'translation'
|
||||
- 'dependencies'
|
||||
replacers: # Changes "Feature: Update checker" to "Update checker"
|
||||
- search: '/Feature:|Feat:|\[feature\]/gi'
|
||||
replace: ''
|
||||
category-template: '### $TITLE'
|
||||
change-template: '- $TITLE [@$AUTHOR](https://github.com/$AUTHOR) ([#$NUMBER]($URL))'
|
||||
change-title-escapes: '\<*_&#@'
|
||||
template: |
|
||||
## paperless-ngx $RESOLVED_VERSION
|
||||
|
||||
$CHANGES
|
27
.github/scripts/common.py
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
def get_image_tag(
|
||||
repo_name: str,
|
||||
pkg_name: str,
|
||||
pkg_version: str,
|
||||
) -> str:
|
||||
"""
|
||||
Returns a string representing the normal image for a given package
|
||||
"""
|
||||
return f"ghcr.io/{repo_name}/builder/{pkg_name}:{pkg_version}"
|
||||
|
||||
|
||||
def get_cache_image_tag(
|
||||
repo_name: str,
|
||||
pkg_name: str,
|
||||
pkg_version: str,
|
||||
branch_name: str,
|
||||
) -> str:
|
||||
"""
|
||||
Returns a string representing the expected image cache tag for a given package
|
||||
|
||||
Registry type caching is utilized for the builder images, to allow fast
|
||||
rebuilds, generally almost instant for the same version
|
||||
"""
|
||||
return f"ghcr.io/{repo_name}/builder/cache/{pkg_name}:{pkg_version}"
|
102
.github/scripts/get-build-json.py
vendored
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
This is a helper script for the mutli-stage Docker image builder.
|
||||
It provides a single point of configuration for package version control.
|
||||
The output JSON object is used by the CI workflow to determine what versions
|
||||
to build and pull into the final Docker image.
|
||||
|
||||
Python package information is obtained from the Pipfile.lock. As this is
|
||||
kept updated by dependabot, it usually will need no further configuration.
|
||||
The sole exception currently is pikepdf, which has a dependency on qpdf,
|
||||
and is configured here to use the latest version of qpdf built by the workflow.
|
||||
|
||||
Other package version information is configured directly below, generally by
|
||||
setting the version and Git information, if any.
|
||||
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
from common import get_cache_image_tag
|
||||
from common import get_image_tag
|
||||
|
||||
|
||||
def _main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a JSON object of information required to build the given package, based on the Pipfile.lock",
|
||||
)
|
||||
parser.add_argument(
|
||||
"package",
|
||||
help="The name of the package to generate JSON for",
|
||||
)
|
||||
|
||||
PIPFILE_LOCK_PATH: Final[Path] = Path("Pipfile.lock")
|
||||
BUILD_CONFIG_PATH: Final[Path] = Path(".build-config.json")
|
||||
|
||||
# Read the main config file
|
||||
build_json: Final = json.loads(BUILD_CONFIG_PATH.read_text())
|
||||
|
||||
# Read Pipfile.lock file
|
||||
pipfile_data: Final = json.loads(PIPFILE_LOCK_PATH.read_text())
|
||||
|
||||
args: Final = parser.parse_args()
|
||||
|
||||
# Read from environment variables set by GitHub Actions
|
||||
repo_name: Final[str] = os.environ["GITHUB_REPOSITORY"]
|
||||
branch_name: Final[str] = os.environ["GITHUB_REF_NAME"]
|
||||
|
||||
# Default output values
|
||||
version = None
|
||||
git_tag = None
|
||||
extra_config = {}
|
||||
|
||||
if args.package in pipfile_data["default"]:
|
||||
# Read the version from Pipfile.lock
|
||||
pkg_data = pipfile_data["default"][args.package]
|
||||
pkg_version = pkg_data["version"].split("==")[-1]
|
||||
version = pkg_version
|
||||
|
||||
# Based on the package, generate the expected Git tag name
|
||||
if args.package == "pikepdf":
|
||||
git_tag = f"v{pkg_version}"
|
||||
elif args.package == "psycopg2":
|
||||
git_tag = pkg_version.replace(".", "_")
|
||||
|
||||
# Any extra/special values needed
|
||||
if args.package == "pikepdf":
|
||||
extra_config["qpdf_version"] = build_json["qpdf"]["version"]
|
||||
|
||||
elif args.package in build_json:
|
||||
version = build_json[args.package]["version"]
|
||||
|
||||
if "git_tag" in build_json[args.package]:
|
||||
git_tag = build_json[args.package]["git_tag"]
|
||||
else:
|
||||
raise NotImplementedError(args.package)
|
||||
|
||||
# The JSON object we'll output
|
||||
output = {
|
||||
"name": args.package,
|
||||
"version": version,
|
||||
"git_tag": git_tag,
|
||||
"image_tag": get_image_tag(repo_name, args.package, version),
|
||||
"cache_tag": get_cache_image_tag(
|
||||
repo_name,
|
||||
args.package,
|
||||
version,
|
||||
branch_name,
|
||||
),
|
||||
}
|
||||
|
||||
# Add anything special a package may need
|
||||
output.update(extra_config)
|
||||
|
||||
# Output the JSON info to stdout
|
||||
print(json.dumps(output))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
_main()
|
15
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 30
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
onlyLabels:
|
||||
- unconfirmed
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
77
.github/workflows/ansible.yml
vendored
@@ -1,77 +0,0 @@
|
||||
---
|
||||
name: Ansible Role
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
|
||||
jobs:
|
||||
# https://molecule.readthedocs.io/en/latest/ci.html#github-actions
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the codebase
|
||||
uses: actions/checkout@v2
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
path: "${{ github.repository }}"
|
||||
- name: Check out the codebase
|
||||
uses: actions/checkout@v2
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
# merge commit is not available from tree at this point in time
|
||||
# https://github.com/actions/checkout#checkout-pull-request-head-commit-instead-of-merge-commit
|
||||
ref: "${{ github.event.pull_request.head.sha }}"
|
||||
path: "${{ github.repository }}"
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
- name: Set up Docker
|
||||
uses: docker-practice/actions-setup-docker@master
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install molecule[ansible,docker] jmespath
|
||||
ansible --version
|
||||
docker --version
|
||||
molecule --version
|
||||
python --version
|
||||
- name: Test installation/build/upgrade with molecule
|
||||
run: |
|
||||
cd ansible
|
||||
molecule create
|
||||
molecule verify
|
||||
molecule converge
|
||||
molecule idempotence
|
||||
molecule verify
|
||||
molecule destroy
|
||||
working-directory: "${{ github.repository }}"
|
||||
# # https://galaxy.ansible.com/docs/contributing/importing.html
|
||||
# release:
|
||||
# runs-on: ubuntu-latest
|
||||
# needs:
|
||||
# - test
|
||||
# # https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions#github-context
|
||||
# if: contains(github.ref, 'refs/tags/')
|
||||
# steps:
|
||||
# - name: Check out the codebase
|
||||
# uses: actions/checkout@v2
|
||||
# with:
|
||||
# path: "${{ github.repository }}"
|
||||
# - name: Set up Python
|
||||
# uses: actions/setup-python@v2
|
||||
# - name: Install dependencies
|
||||
# run: |
|
||||
# python3 -m pip install --upgrade ansible-base
|
||||
# ansible --version
|
||||
# python --version
|
||||
# - name: Trigger a new import on Galaxy
|
||||
# # TODO Check if source if pulled from cwd or imported from github
|
||||
# # https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/galaxy.py
|
||||
# run: |
|
||||
# cd ansible
|
||||
# ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2)
|
||||
# working-directory: "${{ github.repository }}"
|
544
.github/workflows/ci.yml
vendored
@@ -2,7 +2,11 @@ name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ng-*
|
||||
tags:
|
||||
# https://semver.org/#spec-item-2
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
# https://semver.org/#spec-item-9
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-beta.rc[0-9]+'
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
pull_request:
|
||||
@@ -11,257 +15,199 @@ on:
|
||||
|
||||
jobs:
|
||||
documentation:
|
||||
name: "Build Documentation"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Install pipenv
|
||||
run: pipx install pipenv
|
||||
-
|
||||
name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.8
|
||||
-
|
||||
name: Get pip cache dir
|
||||
id: pip-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
-
|
||||
name: Persistent Github pip cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pip3.8}
|
||||
python-version: 3.9
|
||||
cache: "pipenv"
|
||||
cache-dependency-path: 'Pipfile.lock'
|
||||
-
|
||||
name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev
|
||||
pip install --upgrade pipenv
|
||||
pipenv install --system --dev --ignore-pipfile
|
||||
pipenv sync --dev
|
||||
-
|
||||
name: Make documentation
|
||||
run: |
|
||||
cd docs/
|
||||
make html
|
||||
pipenv run make html
|
||||
-
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: documentation
|
||||
path: docs/_build/html/
|
||||
|
||||
tests:
|
||||
ci-backend:
|
||||
uses: ./.github/workflows/reusable-ci-backend.yml
|
||||
|
||||
ci-frontend:
|
||||
uses: ./.github/workflows/reusable-ci-frontend.yml
|
||||
|
||||
prepare-docker-build:
|
||||
name: Prepare Docker Pipeline Data
|
||||
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || contains(github.ref, 'beta.rc') || startsWith(github.ref, 'refs/tags/v'))
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.6', '3.7', '3.8', '3.9']
|
||||
fail-fast: false
|
||||
needs:
|
||||
- documentation
|
||||
- ci-backend
|
||||
- ci-frontend
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "${{ matrix.python-version }}"
|
||||
python-version: "3.9"
|
||||
-
|
||||
name: Get pip cache dir
|
||||
id: pip-cache
|
||||
name: Setup qpdf image
|
||||
id: qpdf-setup
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
-
|
||||
name: Persistent Github pip cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pip${{ matrix.python-version }}
|
||||
-
|
||||
name: Prepare tests
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng
|
||||
pip install --upgrade pipenv
|
||||
pipenv install --system --dev --ignore-pipfile
|
||||
-
|
||||
name: Tests
|
||||
run: |
|
||||
cd src/
|
||||
pytest
|
||||
-
|
||||
name: Codestyle
|
||||
run: |
|
||||
cd src/
|
||||
pycodestyle
|
||||
-
|
||||
name: Publish coverage results
|
||||
if: matrix.python-version == '3.8'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# https://github.com/coveralls-clients/coveralls-python/issues/251
|
||||
run: |
|
||||
cd src/
|
||||
coveralls --service=github
|
||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py qpdf)
|
||||
|
||||
frontend:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '15'
|
||||
-
|
||||
name: Build frontend
|
||||
run: ./compile-frontend.sh
|
||||
-
|
||||
name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
echo ${build_json}
|
||||
|
||||
build-release:
|
||||
needs: [frontend, documentation, tests]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
echo ::set-output name=qpdf-json::${build_json}
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
-
|
||||
name: Install dependencies
|
||||
name: Setup psycopg2 image
|
||||
id: psycopg2-setup
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev gettext liblept5
|
||||
pip3 install -r requirements.txt
|
||||
-
|
||||
name: Download frontend artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
-
|
||||
name: Download documentation artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: documentation
|
||||
path: docs/_build/html/
|
||||
-
|
||||
name: Move files
|
||||
run: |
|
||||
mkdir dist
|
||||
mkdir dist/paperless-ng
|
||||
mkdir dist/paperless-ng/scripts
|
||||
cp .dockerignore .env Dockerfile Pipfile Pipfile.lock LICENSE README.md requirements.txt dist/paperless-ng/
|
||||
cp paperless.conf.example dist/paperless-ng/paperless.conf
|
||||
cp gunicorn.conf.py dist/paperless-ng/gunicorn.conf.py
|
||||
cp docker/ dist/paperless-ng/docker -r
|
||||
cp scripts/*.service scripts/*.sh dist/paperless-ng/scripts/
|
||||
cp src/ dist/paperless-ng/src -r
|
||||
cp docs/_build/html/ dist/paperless-ng/docs -r
|
||||
-
|
||||
name: Compile messages
|
||||
run: |
|
||||
cd dist/paperless-ng/src
|
||||
python3 manage.py compilemessages
|
||||
-
|
||||
name: Collect static files
|
||||
run: |
|
||||
cd dist/paperless-ng/src
|
||||
python3 manage.py collectstatic --no-input
|
||||
-
|
||||
name: Make release package
|
||||
run: |
|
||||
cd dist
|
||||
find . -name __pycache__ | xargs rm -r
|
||||
tar -cJf paperless-ng.tar.xz paperless-ng/
|
||||
-
|
||||
name: Upload release artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: release
|
||||
path: dist/paperless-ng.tar.xz
|
||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py psycopg2)
|
||||
|
||||
publish-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-release
|
||||
if: contains(github.ref, 'refs/tags/ng-')
|
||||
steps:
|
||||
echo ${build_json}
|
||||
|
||||
echo ::set-output name=psycopg2-json::${build_json}
|
||||
-
|
||||
name: Download release artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: release
|
||||
path: ./
|
||||
-
|
||||
name: Get version
|
||||
id: get_version
|
||||
name: Setup pikepdf image
|
||||
id: pikepdf-setup
|
||||
run: |
|
||||
echo ::set-output name=version::${GITHUB_REF#refs/tags/ng-}
|
||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py pikepdf)
|
||||
|
||||
echo ${build_json}
|
||||
|
||||
echo ::set-output name=pikepdf-json::${build_json}
|
||||
-
|
||||
name: Create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ng-${{ steps.get_version.outputs.version }}
|
||||
release_name: Paperless-ng ${{ steps.get_version.outputs.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: |
|
||||
For a complete list of changes, see the changelog at https://paperless-ng.readthedocs.io/en/latest/changelog.html.
|
||||
-
|
||||
name: Upload release archive
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./paperless-ng.tar.xz
|
||||
asset_name: paperless-ng-${{ steps.get_version.outputs.version }}.tar.xz
|
||||
asset_content_type: application/x-xz
|
||||
name: Setup jbig2enc image
|
||||
id: jbig2enc-setup
|
||||
run: |
|
||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py jbig2enc)
|
||||
|
||||
echo ${build_json}
|
||||
|
||||
echo ::set-output name=jbig2enc-json::${build_json}
|
||||
|
||||
outputs:
|
||||
|
||||
qpdf-json: ${{ steps.qpdf-setup.outputs.qpdf-json }}
|
||||
|
||||
pikepdf-json: ${{ steps.pikepdf-setup.outputs.pikepdf-json }}
|
||||
|
||||
psycopg2-json: ${{ steps.psycopg2-setup.outputs.psycopg2-json }}
|
||||
|
||||
jbig2enc-json: ${{ steps.jbig2enc-setup.outputs.jbig2enc-json}}
|
||||
|
||||
build-qpdf-debs:
|
||||
name: qpdf
|
||||
needs:
|
||||
- prepare-docker-build
|
||||
uses: ./.github/workflows/reusable-workflow-builder.yml
|
||||
with:
|
||||
dockerfile: ./docker-builders/Dockerfile.qpdf
|
||||
build-json: ${{ needs.prepare-docker-build.outputs.qpdf-json }}
|
||||
build-args: |
|
||||
QPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }}
|
||||
|
||||
build-jbig2enc:
|
||||
name: jbig2enc
|
||||
needs:
|
||||
- prepare-docker-build
|
||||
uses: ./.github/workflows/reusable-workflow-builder.yml
|
||||
with:
|
||||
dockerfile: ./docker-builders/Dockerfile.jbig2enc
|
||||
build-json: ${{ needs.prepare-docker-build.outputs.jbig2enc-json }}
|
||||
build-args: |
|
||||
JBIG2ENC_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.jbig2enc-json).version }}
|
||||
|
||||
build-psycopg2-wheel:
|
||||
name: psycopg2
|
||||
needs:
|
||||
- prepare-docker-build
|
||||
uses: ./.github/workflows/reusable-workflow-builder.yml
|
||||
with:
|
||||
dockerfile: ./docker-builders/Dockerfile.psycopg2
|
||||
build-json: ${{ needs.prepare-docker-build.outputs.psycopg2-json }}
|
||||
build-args: |
|
||||
PSYCOPG2_GIT_TAG=${{ fromJSON(needs.prepare-docker-build.outputs.psycopg2-json).git_tag }}
|
||||
PSYCOPG2_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.psycopg2-json).version }}
|
||||
|
||||
build-pikepdf-wheel:
|
||||
name: pikepdf
|
||||
needs:
|
||||
- prepare-docker-build
|
||||
- build-qpdf-debs
|
||||
uses: ./.github/workflows/reusable-workflow-builder.yml
|
||||
with:
|
||||
dockerfile: ./docker-builders/Dockerfile.pikepdf
|
||||
build-json: ${{ needs.prepare-docker-build.outputs.pikepdf-json }}
|
||||
build-args: |
|
||||
REPO=${{ github.repository }}
|
||||
QPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }}
|
||||
PIKEPDF_GIT_TAG=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).git_tag }}
|
||||
PIKEPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).version }}
|
||||
|
||||
# build and push image to docker hub.
|
||||
build-docker-image:
|
||||
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
|
||||
runs-on: ubuntu-latest
|
||||
needs: [frontend, tests]
|
||||
runs-on: ubuntu-20.04
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-build-docker-image-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
needs:
|
||||
- prepare-docker-build
|
||||
- build-psycopg2-wheel
|
||||
- build-jbig2enc
|
||||
- build-qpdf-debs
|
||||
- build-pikepdf-wheel
|
||||
steps:
|
||||
-
|
||||
name: Prepare
|
||||
id: prepare
|
||||
name: Check pushing to Docker Hub
|
||||
id: docker-hub
|
||||
# Only push to Dockerhub from the main repo
|
||||
# Otherwise forks would require a Docker Hub account and secrets setup
|
||||
run: |
|
||||
IMAGE_NAME=jonaswinkler/paperless-ng
|
||||
if [[ $GITHUB_REF == refs/tags/ng-* ]]; then
|
||||
TAGS=${IMAGE_NAME}:${GITHUB_REF#refs/tags/ng-},${IMAGE_NAME}:latest
|
||||
INSPECT_TAG=${IMAGE_NAME}:latest
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
TAGS=${IMAGE_NAME}:${GITHUB_REF#refs/heads/}
|
||||
INSPECT_TAG=${TAGS}
|
||||
if [[ ${{ github.repository }} == "paperless-ngx/paperless-ngx" ]] ; then
|
||||
echo ::set-output name=enable::"true"
|
||||
else
|
||||
exit 1
|
||||
echo ::set-output name=enable::"false"
|
||||
fi
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=inspect_tag::${INSPECT_TAG}
|
||||
-
|
||||
name: Gather Docker metadata
|
||||
id: docker-meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ github.repository }}
|
||||
name=paperlessngx/paperless-ngx,enable=${{ steps.docker-hub.outputs.enable }}
|
||||
tags: |
|
||||
# Tag branches with branch name
|
||||
type=ref,event=branch
|
||||
# Process semver tags
|
||||
# For a tag x.y.z or vX.Y.Z, output an x.y.z and x.y image tag
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Download frontend artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
@@ -269,19 +215,20 @@ jobs:
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
-
|
||||
name: Login to DockerHub
|
||||
name: Login to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
# Don't attempt to login is not pushing to Docker Hub
|
||||
if: steps.docker-hub.outputs.enable == 'true'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
@@ -289,11 +236,166 @@ jobs:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prepare.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.docker-meta.outputs.tags }}
|
||||
labels: ${{ steps.docker-meta.outputs.labels }}
|
||||
build-args: |
|
||||
JBIG2ENC_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.jbig2enc-json).version }}
|
||||
QPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }}
|
||||
PIKEPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).version }}
|
||||
PSYCOPG2_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.psycopg2-json).version }}
|
||||
# Get cache layers from this branch, then dev, then main
|
||||
# This allows new branches to get at least some cache benefits, generally from dev
|
||||
cache-from: |
|
||||
type=registry,ref=ghcr.io/${{ github.repository }}/builder/cache/app:${{ github.ref_name }}
|
||||
type=registry,ref=ghcr.io/${{ github.repository }}/builder/cache/app:dev
|
||||
type=registry,ref=ghcr.io/${{ github.repository }}/builder/cache/app:main
|
||||
cache-to: |
|
||||
type=registry,mode=max,ref=ghcr.io/${{ github.repository }}/builder/cache/app:${{ github.ref_name }}
|
||||
-
|
||||
name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ steps.prepare.outputs.inspect_tag }}
|
||||
docker buildx imagetools inspect ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||
-
|
||||
name: Export frontend artifact from docker
|
||||
run: |
|
||||
docker create --name frontend-extract ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
|
||||
-
|
||||
name: Upload frontend artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
|
||||
build-release:
|
||||
needs:
|
||||
- build-docker-image
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: 3.9
|
||||
-
|
||||
name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends gettext liblept5
|
||||
pip3 install --upgrade pip setuptools wheel
|
||||
pip3 install -r requirements.txt
|
||||
-
|
||||
name: Download frontend artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
-
|
||||
name: Download documentation artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: documentation
|
||||
path: docs/_build/html/
|
||||
-
|
||||
name: Move files
|
||||
run: |
|
||||
mkdir dist
|
||||
mkdir dist/paperless-ngx
|
||||
mkdir dist/paperless-ngx/scripts
|
||||
cp .dockerignore .env Dockerfile Pipfile Pipfile.lock LICENSE README.md requirements.txt dist/paperless-ngx/
|
||||
cp paperless.conf.example dist/paperless-ngx/paperless.conf
|
||||
cp gunicorn.conf.py dist/paperless-ngx/gunicorn.conf.py
|
||||
cp docker/ dist/paperless-ngx/docker -r
|
||||
cp scripts/*.service scripts/*.sh dist/paperless-ngx/scripts/
|
||||
cp src/ dist/paperless-ngx/src -r
|
||||
cp docs/_build/html/ dist/paperless-ngx/docs -r
|
||||
-
|
||||
name: Compile messages
|
||||
run: |
|
||||
cd dist/paperless-ngx/src
|
||||
python3 manage.py compilemessages
|
||||
-
|
||||
name: Collect static files
|
||||
run: |
|
||||
cd dist/paperless-ngx/src
|
||||
python3 manage.py collectstatic --no-input
|
||||
-
|
||||
name: Make release package
|
||||
run: |
|
||||
cd dist
|
||||
find . -name __pycache__ | xargs rm -r
|
||||
tar -cJf paperless-ngx.tar.xz paperless-ngx/
|
||||
-
|
||||
name: Upload release artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: release
|
||||
path: dist/paperless-ngx.tar.xz
|
||||
|
||||
publish-release:
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- build-release
|
||||
if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc'))
|
||||
steps:
|
||||
-
|
||||
name: Download release artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: release
|
||||
path: ./
|
||||
-
|
||||
name: Get version
|
||||
id: get_version
|
||||
run: |
|
||||
echo ::set-output name=version::${{ github.ref_name }}
|
||||
if [[ ${{ contains(github.ref_name, '-beta.rc') }} == 'true' ]]; then
|
||||
echo ::set-output name=prerelease::true
|
||||
else
|
||||
echo ::set-output name=prerelease::false
|
||||
fi
|
||||
-
|
||||
name: Create Release and Changelog
|
||||
id: create-release
|
||||
uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
name: Paperless-ngx ${{ steps.get_version.outputs.version }}
|
||||
tag: ${{ steps.get_version.outputs.version }}
|
||||
version: ${{ steps.get_version.outputs.version }}
|
||||
prerelease: ${{ steps.get_version.outputs.prerelease }}
|
||||
publish: true # ensures release is not marked as draft
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Upload release archive
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create-release.outputs.upload_url }}
|
||||
asset_path: ./paperless-ngx.tar.xz
|
||||
asset_name: paperless-ngx-${{ steps.get_version.outputs.version }}.tar.xz
|
||||
asset_content_type: application/x-xz
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main
|
||||
-
|
||||
name: Append Changelog to docs
|
||||
id: append-Changelog
|
||||
working-directory: docs
|
||||
run: |
|
||||
echo -e "# Changelog\n\n${{ steps.create-release.outputs.body }}\n" > changelog-new.md
|
||||
CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
|
||||
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
|
||||
mv changelog-new.md changelog.md
|
||||
git config --global user.name "github-actions"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git commit -am "Changelog ${{ steps.get_version.outputs.version }} - GHA"
|
||||
git push origin HEAD:main
|
||||
|
54
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ dev ]
|
||||
schedule:
|
||||
- cron: '28 13 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript', 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
47
.github/workflows/project-actions.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Project Automations
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
pull_request_target: #_target allows access to secrets
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
env:
|
||||
todo: Todo
|
||||
done: Done
|
||||
in_progress: In Progress
|
||||
|
||||
jobs:
|
||||
issue_opened_or_reopened:
|
||||
name: issue_opened_or_reopened
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened')
|
||||
steps:
|
||||
- name: Set issue status to ${{ env.todo }}
|
||||
uses: leonsteinhaeuser/project-beta-automations@v1.2.1
|
||||
with:
|
||||
gh_token: ${{ secrets.GH_TOKEN }}
|
||||
organization: paperless-ngx
|
||||
project_id: 2
|
||||
resource_node_id: ${{ github.event.issue.node_id }}
|
||||
status_value: ${{ env.todo }} # Target status
|
||||
pr_opened_or_reopened:
|
||||
name: pr_opened_or_reopened
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened')
|
||||
steps:
|
||||
- name: Set PR status to ${{ env.in_progress }}
|
||||
uses: leonsteinhaeuser/project-beta-automations@v1.2.1
|
||||
with:
|
||||
gh_token: ${{ secrets.GH_TOKEN }}
|
||||
organization: paperless-ngx
|
||||
project_id: 2
|
||||
resource_node_id: ${{ github.event.pull_request.node_id }}
|
||||
status_value: ${{ env.in_progress }} # Target status
|
108
.github/workflows/reusable-ci-backend.yml
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
name: Backend CI Jobs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
|
||||
code-checks-backend:
|
||||
name: "Code Style Checks"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Install checkers
|
||||
run: |
|
||||
pipx install reorder-python-imports
|
||||
pipx install yesqa
|
||||
pipx install add-trailing-comma
|
||||
pipx install flake8
|
||||
-
|
||||
name: Run reorder-python-imports
|
||||
run: |
|
||||
find src/ -type f -name '*.py' ! -path "*/migrations/*" | xargs reorder-python-imports
|
||||
-
|
||||
name: Run yesqa
|
||||
run: |
|
||||
find src/ -type f -name '*.py' ! -path "*/migrations/*" | xargs yesqa
|
||||
-
|
||||
name: Run add-trailing-comma
|
||||
run: |
|
||||
find src/ -type f -name '*.py' ! -path "*/migrations/*" | xargs add-trailing-comma
|
||||
# black is placed after add-trailing-comma because it may format differently
|
||||
# if a trailing comma is added
|
||||
-
|
||||
name: Run black
|
||||
uses: psf/black@stable
|
||||
with:
|
||||
options: "--check --diff"
|
||||
version: "22.3.0"
|
||||
-
|
||||
name: Run flake8 checks
|
||||
run: |
|
||||
cd src/
|
||||
flake8 --max-line-length=88 --ignore=E203,W503
|
||||
|
||||
tests-backend:
|
||||
name: "Tests (${{ matrix.python-version }})"
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- code-checks-backend
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.8', '3.9', '3.10']
|
||||
fail-fast: false
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
-
|
||||
name: Install pipenv
|
||||
run: pipx install pipenv
|
||||
-
|
||||
name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "${{ matrix.python-version }}"
|
||||
cache: "pipenv"
|
||||
cache-dependency-path: 'Pipfile.lock'
|
||||
-
|
||||
name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng libzbar0 poppler-utils
|
||||
-
|
||||
name: Install Python dependencies
|
||||
run: |
|
||||
pipenv sync --dev
|
||||
-
|
||||
name: Tests
|
||||
run: |
|
||||
cd src/
|
||||
pipenv run pytest
|
||||
-
|
||||
name: Get changed files
|
||||
id: changed-files-specific
|
||||
uses: tj-actions/changed-files@v19
|
||||
with:
|
||||
files: |
|
||||
src/**
|
||||
-
|
||||
name: List all changed files
|
||||
run: |
|
||||
for file in ${{ steps.changed-files-specific.outputs.all_changed_files }}; do
|
||||
echo "${file} was changed"
|
||||
done
|
||||
-
|
||||
name: Publish coverage results
|
||||
if: matrix.python-version == '3.9' && steps.changed-files-specific.outputs.any_changed == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# https://github.com/coveralls-clients/coveralls-python/issues/251
|
||||
run: |
|
||||
cd src/
|
||||
pipenv run coveralls --service=github
|
42
.github/workflows/reusable-ci-frontend.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Frontend CI Jobs
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
|
||||
code-checks-frontend:
|
||||
name: "Code Style Checks"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
-
|
||||
name: Install prettier
|
||||
run: |
|
||||
npm install prettier
|
||||
-
|
||||
name: Run prettier
|
||||
run:
|
||||
npx prettier --check --ignore-path Pipfile.lock **/*.js **/*.ts *.md **/*.md
|
||||
tests-frontend:
|
||||
name: "Tests"
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- code-checks-frontend
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: cd src-ui && npm ci
|
||||
- run: cd src-ui && npm run test
|
||||
- run: cd src-ui && npm run e2e:ci
|
53
.github/workflows/reusable-workflow-builder.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Reusable Image Builder
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
dockerfile:
|
||||
required: true
|
||||
type: string
|
||||
build-json:
|
||||
required: true
|
||||
type: string
|
||||
build-args:
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ fromJSON(inputs.build-json).name }}-${{ fromJSON(inputs.build-json).version }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
name: Build ${{ fromJSON(inputs.build-json).name }} @ ${{ fromJSON(inputs.build-json).version }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Login to Github Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
-
|
||||
name: Build ${{ fromJSON(inputs.build-json).name }}
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: ${{ inputs.dockerfile }}
|
||||
tags: ${{ fromJSON(inputs.build-json).image_tag }}
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
build-args: ${{ inputs.build-args }}
|
||||
push: true
|
||||
cache-from: type=registry,ref=${{ fromJSON(inputs.build-json).cache_tag }}
|
||||
cache-to: type=registry,mode=max,ref=${{ fromJSON(inputs.build-json).cache_tag }}
|
3
.gitignore
vendored
@@ -61,6 +61,9 @@ target/
|
||||
# PyCharm
|
||||
.idea
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
|
||||
# Other stuff that doesn't belong
|
||||
.virtualenv
|
||||
virtualenv
|
||||
|
94
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,94 @@
|
||||
# This file configures pre-commit hooks.
|
||||
# See https://pre-commit.com/ for general information
|
||||
# See https://pre-commit.com/hooks.html for a listing of possible hooks
|
||||
|
||||
repos:
|
||||
# General hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.2.0
|
||||
hooks:
|
||||
- id: check-docstring-first
|
||||
- id: check-json
|
||||
exclude: "tsconfig.*json"
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: check-executables-have-shebangs
|
||||
- id: end-of-file-fixer
|
||||
exclude_types:
|
||||
- svg
|
||||
- pofile
|
||||
exclude: "(^LICENSE$)"
|
||||
- id: mixed-line-ending
|
||||
args:
|
||||
- "--fix=lf"
|
||||
- id: trailing-whitespace
|
||||
exclude_types:
|
||||
- svg
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v2.6.2"
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or:
|
||||
- javascript
|
||||
- ts
|
||||
- markdown
|
||||
exclude: "(^Pipfile\\.lock$)"
|
||||
# Python hooks
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v3.1.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
exclude: "(migrations)"
|
||||
- repo: https://github.com/asottile/yesqa
|
||||
rev: "v1.3.0"
|
||||
hooks:
|
||||
- id: yesqa
|
||||
exclude: "(migrations)"
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: "v2.2.3"
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
exclude: "(migrations)"
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
files: ^src/
|
||||
args:
|
||||
- "--config=./src/setup.cfg"
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.32.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
exclude: "(migrations)"
|
||||
args:
|
||||
- "--py38-plus"
|
||||
# Dockerfile hooks
|
||||
- repo: https://github.com/AleksaC/hadolint-py
|
||||
rev: v2.10.0
|
||||
hooks:
|
||||
- id: hadolint
|
||||
args:
|
||||
- --ignore
|
||||
- DL3008 # https://github.com/hadolint/hadolint/wiki/DL3008 (should probably do this at some point)
|
||||
- --ignore
|
||||
- DL3013 # https://github.com/hadolint/hadolint/wiki/DL3013 (should probably do this too at some point)
|
||||
- --ignore
|
||||
- DL3003 # https://github.com/hadolint/hadolint/wiki/DL3003 (seems excessive to use WORKDIR so much)
|
||||
# Shell script hooks
|
||||
- repo: https://github.com/lovesegfault/beautysh
|
||||
rev: v6.2.1
|
||||
hooks:
|
||||
- id: beautysh
|
||||
args:
|
||||
- "--tab"
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: "v0.8.0.4"
|
||||
hooks:
|
||||
- id: shellcheck
|
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
# https://prettier.io/docs/en/options.html#semicolons
|
||||
semi: false
|
||||
# https://prettier.io/docs/en/options.html#quotes
|
||||
singleQuote: true
|
@@ -11,6 +11,6 @@ sphinx:
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
version: "3.8"
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
|
10
CODEOWNERS
Normal file
@@ -0,0 +1,10 @@
|
||||
/.github/workflows/ @paperless-ngx/ci-cd
|
||||
/docker/ @paperless-ngx/ci-cd
|
||||
/scripts/ @paperless-ngx/ci-cd
|
||||
|
||||
/src-ui/ @paperless-ngx/frontend
|
||||
|
||||
/src/ @paperless-ngx/backend
|
||||
Pipfile* @paperless-ngx/backend
|
||||
*.py @paperless-ngx/backend
|
||||
requirements.txt @paperless-ngx/backend
|
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
hello@paperless-ngx.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
124
CONTRIBUTING.md
@@ -1,30 +1,132 @@
|
||||
# Contributing
|
||||
|
||||
There's still lots of things to be done, just have a look at that issue log. If you feel like conctributing to the project, please do! Bug fixes and improvements to the front end (I just can't seem to get some of these CSS things right) are always welcome.
|
||||
If you feel like contributing to the project, please do! Bug fixes and improvements are always welcome.
|
||||
|
||||
If you want to implement something big: Please start a discussion about that in the issues! Maybe I've already had something similar in mind and we can make it happen together. However, keep in mind that the general roadmap is to make the existing features stable and get them tested. See the roadmap in the readme.
|
||||
If you want to implement something big:
|
||||
|
||||
* When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
|
||||
* Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
|
||||
- Please start a discussion about that in the issues! Maybe something similar is already in development and we can make it happen together.
|
||||
- When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
|
||||
- Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
|
||||
- Please see the [paperless-ngx merge process](#merging-prs) below.
|
||||
|
||||
## Python
|
||||
|
||||
Use python 3.6 for development. Paperless supports python 3.6, 3.7 and 3.8.
|
||||
Paperless supports python 3.8 and 3.9. We format Python code with [Black](https://github.com/psf/black).
|
||||
|
||||
## Branches
|
||||
|
||||
master always reflects the latest release.
|
||||
`main` always reflects the latest release. Apart from changes to the documentation or readme, absolutely no functional changes on this branch in between releases.
|
||||
|
||||
dev contains all changes that will be part of the next release. Use this branch to start making your changes.
|
||||
`dev` contains all changes that will be part of the next release. Use this branch to start making your changes.
|
||||
|
||||
feature-X branches is for experimental stuff that will eventually be merged into dev, and then released as part of the next release.
|
||||
`feature-X` branches are for experimental stuff that will eventually be merged into dev.
|
||||
|
||||
## Testing:
|
||||
|
||||
I'm trying to get most of paperless tested, so please do the same for your code! I know its a hassle, but it makes sure that your code works now and will allow us to detect regressions easily.
|
||||
Please format and test your code! I know it's a hassle, but it makes sure that your code works now and will allow us to detect regressions easily.
|
||||
|
||||
To test your code, execute `pytest` in the src/ directory. Executing that in the project root is no good. This also generates a html coverage report, which you can use to see if you missed anything important during testing.
|
||||
To test your code, execute `pytest` in the src/ directory. This also generates a html coverage report, which you can use to see if you missed anything important during testing.
|
||||
|
||||
Before you can run `pytest`, ensure to [properly set up your local environment](https://paperless-ngx.readthedocs.io/en/latest/extending.html#initial-setup-and-first-start).
|
||||
|
||||
## More info:
|
||||
|
||||
... is available in the documentation. https://paperless-ng.readthedocs.io/en/latest/extending.html
|
||||
... is available in the documentation. https://paperless-ngx.readthedocs.io/en/latest/extending.html
|
||||
|
||||
# Merging PRs
|
||||
|
||||
Once you have submitted a **P**ull **R**equest it will be reviewed, approved, and merged by one or more community members of any team. Automated code tests and formatting checks must be passed.
|
||||
|
||||
## Non-Trivial Requests
|
||||
|
||||
PRs deemed `non-trivial` will go through a stricter review process before being merged into `dev`. This is to ensure code quality and complete functionality (free of side effects).
|
||||
|
||||
Examples of `non-trivial` PRs might include:
|
||||
|
||||
- Additional features
|
||||
- Large changes to many distinct files
|
||||
- Breaking or depreciation of existing features
|
||||
|
||||
Our community review process for `non-trivial` PRs is the following:
|
||||
|
||||
1. Must pass usual automated code tests and formatting checks.
|
||||
2. The PR will be assigned and pinged to the appropriately experienced team (i.e. @paperless-ngx/backend for backend changes).
|
||||
3. Development team will check and test code manually (possibly over several days).
|
||||
- You may be asked to make changes or rebase.
|
||||
- The team may ask for additional testing done by @paperless-ngx/test
|
||||
4. **At least two** members of the team will approve and finally merge the request into `dev` 🎉.
|
||||
|
||||
This process might be slow as community members have different schedules and time to dedicate to the Paperless project. However it ensures community code reviews are as brilliantly thorough as they once were with @jonaswinkler.
|
||||
|
||||
# Translating Paperless-ngx
|
||||
|
||||
Some notes about translation:
|
||||
|
||||
- There are two resources:
|
||||
- `src-ui/messages.xlf` contains the translation strings for the front end. This is the most important.
|
||||
- `django.po` contains strings for the administration section of paperless, which is nice to have translated.
|
||||
- Most of the front-end strings are used on buttons, menu items, etc., so ideally the translated string should not be much longer than the English original.
|
||||
- Translation units may contain placeholders. These usually mean that there's a name of a tag or document or something in the string. You can click on the placeholders to copy them.
|
||||
- Translation units may contain plural expressions such as `{PLURAL_VAR, plural, =1 {one result} =0 {no results} other {<placeholder> results}}`. Copy these verbatim and translate only the content in the inner `{}` brackets. Example: `{PLURAL_VAR, plural, =1 {Ein Ergebnis} =0 {Keine Ergebnisse} other {<placeholder> Ergebnisse}}`
|
||||
- Changes to translations on Crowdin will get pushed into the repository automatically.
|
||||
|
||||
## Adding new languages to the codebase
|
||||
|
||||
If a language has already been added, and you would like to contribute new translations or change existing translations, please read the "Translation" section in the README.md file for further details on that.
|
||||
|
||||
If you would like the project to be translated to another language, first head over to https://crwd.in/paperless-ngx to check if that language has already been enabled for translation.
|
||||
If not, please request the language to be added by creating an issue on GitHub. The issue should contain:
|
||||
|
||||
- English name of the language (the localized name can be added on Crowdin).
|
||||
- ISO language code. A list of those can be found here: https://support.crowdin.com/enterprise/language-codes/
|
||||
- Date format commonly used for the language, e.g. dd/mm/yyyy, mm/dd/yyyy, etc.
|
||||
|
||||
After the language has been added and some translations have been made on Crowdin, the language needs to be enabled in the code.
|
||||
Note that there is no need to manually add a .po of .xlf file as those will be automatically generated and imported from Crowdin.
|
||||
The following files need to be changed:
|
||||
|
||||
- src-ui/angular.json (under the _projects/paperless-ui/i18n/locales_ JSON key)
|
||||
- src/paperless/settings.py (in the _LANGUAGES_ array)
|
||||
- src-ui/src/app/services/settings.service.ts (inside the _getLanguageOptions_ method)
|
||||
- src-ui/src/app/app.module.ts (import locale from _angular/common/locales_ and call _registerLocaleData_)
|
||||
|
||||
Please add the language in the correct order, alphabetically by locale.
|
||||
Note that _en-us_ needs to stay on top of the list, as it is the default project language
|
||||
|
||||
If you are familiar with Git, feel free to send a Pull Request with those changes.
|
||||
If not, let us know in the issue you created for the language, so that another developer can make these changes.
|
||||
|
||||
# Organization Structure & Membership
|
||||
|
||||
Paperless-ngx is a community project. We do our best to delegate permission and responsibility among a team of people to ensure the longevity of the project.
|
||||
|
||||
## Structure
|
||||
|
||||
As of writing, there are 21 members in paperless-ngx. 4 of these people have complete administrative privileges to the repo:
|
||||
|
||||
- [@shamoon](https://github.com/shamoon)
|
||||
- [@bauerj](https://github.com/bauerj)
|
||||
- [@qcasey](https://github.com/qcasey)
|
||||
- [@FrankStrieter](https://github.com/FrankStrieter)
|
||||
|
||||
There are 5 teams collaborating on specific tasks within paperless-ngx:
|
||||
|
||||
- @paperless-ngx/backend (Python / django)
|
||||
- @paperless-ngx/frontend (JavaScript / Typescript)
|
||||
- @paperless-ngx/ci-cd (GitHub Actions / Deployment)
|
||||
- @paperless-ngx/issues (Issue triage)
|
||||
- @paperless-ngx/test (General testing for larger PRs)
|
||||
|
||||
## Permissions
|
||||
|
||||
All team members are notified when mentioned or assigned to a relevant issue or pull request. Additionally, each team has slightly different access to paperless-ngx:
|
||||
|
||||
- The **test** team has no special permissions.
|
||||
- The **issues** team has `triage` access. This means they can organize issues and pull requests.
|
||||
- The **backend**, **frontend**, and **ci-cd** teams have `write` access. This means they can approve PRs and push code, containers, releases, and more.
|
||||
|
||||
## Joining
|
||||
|
||||
We are not overly strict with inviting people to the organization. If you have read the [team permissions](#permissions) and think having additional access would enhance your contributions, please reach out to an [admin](#structure) of the team.
|
||||
|
||||
The admins occasionally invite contributors directly if we believe having them on a team will accelerate their work.
|
||||
|
281
Dockerfile
@@ -1,111 +1,216 @@
|
||||
FROM ubuntu:20.04 AS jbig2enc
|
||||
# syntax=docker/dockerfile:1.4
|
||||
|
||||
WORKDIR /usr/src/jbig2enc
|
||||
# Pull the installer images from the library
|
||||
# These are all built previously
|
||||
# They provide either a .deb or .whl
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential automake libtool libleptonica-dev zlib1g-dev git ca-certificates
|
||||
ARG JBIG2ENC_VERSION
|
||||
ARG QPDF_VERSION
|
||||
ARG PIKEPDF_VERSION
|
||||
ARG PSYCOPG2_VERSION
|
||||
|
||||
RUN git clone https://github.com/agl/jbig2enc .
|
||||
RUN ./autogen.sh
|
||||
RUN ./configure && make
|
||||
FROM ghcr.io/paperless-ngx/paperless-ngx/builder/jbig2enc:${JBIG2ENC_VERSION} as jbig2enc-builder
|
||||
FROM ghcr.io/paperless-ngx/paperless-ngx/builder/qpdf:${QPDF_VERSION} as qpdf-builder
|
||||
FROM ghcr.io/paperless-ngx/paperless-ngx/builder/pikepdf:${PIKEPDF_VERSION} as pikepdf-builder
|
||||
FROM ghcr.io/paperless-ngx/paperless-ngx/builder/psycopg2:${PSYCOPG2_VERSION} as psycopg2-builder
|
||||
|
||||
FROM python:3.7-slim
|
||||
FROM --platform=$BUILDPLATFORM node:16-bullseye-slim AS compile-frontend
|
||||
|
||||
WORKDIR /usr/src/paperless/
|
||||
# This stage compiles the frontend
|
||||
# This stage runs once for the native platform, as the outputs are not
|
||||
# dependent on target arch
|
||||
# Inputs: None
|
||||
|
||||
COPY requirements.txt ./
|
||||
COPY ./src-ui /src/src-ui
|
||||
|
||||
# Binary dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get -y --no-install-recommends install \
|
||||
# Basic dependencies
|
||||
curl \
|
||||
file \
|
||||
# fonts for text file thumbnail generation
|
||||
fonts-liberation \
|
||||
# for making translations further down
|
||||
gettext \
|
||||
gnupg \
|
||||
imagemagick \
|
||||
# for Numpy
|
||||
libatlas-base-dev \
|
||||
libxslt1-dev \
|
||||
mime-support \
|
||||
# thumbnail size reduction
|
||||
optipng \
|
||||
sudo \
|
||||
tzdata \
|
||||
# OCRmyPDF dependencies
|
||||
ghostscript \
|
||||
icc-profiles-free \
|
||||
liblept5 \
|
||||
libxml2 \
|
||||
pngquant \
|
||||
qpdf \
|
||||
tesseract-ocr \
|
||||
tesseract-ocr-eng \
|
||||
tesseract-ocr-deu \
|
||||
tesseract-ocr-fra \
|
||||
tesseract-ocr-ita \
|
||||
tesseract-ocr-spa \
|
||||
unpaper \
|
||||
zlib1g \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /src/src-ui
|
||||
RUN set -eux \
|
||||
&& npm update npm -g \
|
||||
&& npm ci --no-optional
|
||||
RUN set -eux \
|
||||
&& ./node_modules/.bin/ng build --configuration production
|
||||
|
||||
# This pulls in updated dependencies from bullseye to fix some issues with file type detection.
|
||||
# TODO: Remove this once bullseye releases.
|
||||
RUN echo "deb http://deb.debian.org/debian bullseye main" > /etc/apt/sources.list.d/bullseye.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y file libmagic-dev \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm /etc/apt/sources.list.d/bullseye.list
|
||||
FROM python:3.9-slim-bullseye as main-app
|
||||
|
||||
# Python dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get -y --no-install-recommends install \
|
||||
build-essential \
|
||||
libpoppler-cpp-dev \
|
||||
libpq-dev \
|
||||
libqpdf-dev \
|
||||
&& python3 -m pip install --upgrade --no-cache-dir supervisor \
|
||||
&& python3 -m pip install --no-cache-dir -r requirements.txt \
|
||||
&& apt-get -y purge build-essential libqpdf-dev \
|
||||
&& apt-get -y autoremove --purge \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& mkdir /var/log/supervisord /var/run/supervisord
|
||||
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
||||
LABEL org.opencontainers.image.documentation="https://paperless-ngx.readthedocs.io/en/latest/"
|
||||
LABEL org.opencontainers.image.source="https://github.com/paperless-ngx/paperless-ngx"
|
||||
LABEL org.opencontainers.image.url="https://github.com/paperless-ngx/paperless-ngx"
|
||||
LABEL org.opencontainers.image.licenses="GPL-3.0-only"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# copy scripts
|
||||
# this fixes issues with imagemagick and PDF
|
||||
COPY docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
|
||||
|
||||
COPY gunicorn.conf.py ./
|
||||
COPY docker/supervisord.conf /etc/supervisord.conf
|
||||
COPY docker/docker-entrypoint.sh /sbin/docker-entrypoint.sh
|
||||
#
|
||||
# Begin installation and configuration
|
||||
# Order the steps below from least often changed to most
|
||||
#
|
||||
|
||||
# copy jbig2enc
|
||||
COPY --from=jbig2enc /usr/src/jbig2enc/src/.libs/libjbig2enc* /usr/local/lib/
|
||||
COPY --from=jbig2enc /usr/src/jbig2enc/src/jbig2 /usr/local/bin/
|
||||
COPY --from=jbig2enc /usr/src/jbig2enc/src/*.h /usr/local/include/
|
||||
# Basically will never change again
|
||||
COPY --from=jbig2enc-builder /usr/src/jbig2enc/src/.libs/libjbig2enc* /usr/local/lib/
|
||||
COPY --from=jbig2enc-builder /usr/src/jbig2enc/src/jbig2 /usr/local/bin/
|
||||
COPY --from=jbig2enc-builder /usr/src/jbig2enc/src/*.h /usr/local/include/
|
||||
|
||||
# Packages need for running
|
||||
ARG RUNTIME_PACKAGES="\
|
||||
curl \
|
||||
file \
|
||||
# fonts for text file thumbnail generation
|
||||
fonts-liberation \
|
||||
gettext \
|
||||
ghostscript \
|
||||
gnupg \
|
||||
gosu \
|
||||
icc-profiles-free \
|
||||
imagemagick \
|
||||
media-types \
|
||||
liblept5 \
|
||||
libpq5 \
|
||||
libxml2 \
|
||||
liblcms2-2 \
|
||||
libtiff5 \
|
||||
libxslt1.1 \
|
||||
libfreetype6 \
|
||||
libwebp6 \
|
||||
libopenjp2-7 \
|
||||
libimagequant0 \
|
||||
libraqm0 \
|
||||
libgnutls30 \
|
||||
libjpeg62-turbo \
|
||||
optipng \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-setuptools \
|
||||
postgresql-client \
|
||||
# For Numpy
|
||||
libatlas3-base \
|
||||
# thumbnail size reduction
|
||||
pngquant \
|
||||
# OCRmyPDF dependencies
|
||||
tesseract-ocr \
|
||||
tesseract-ocr-eng \
|
||||
tesseract-ocr-deu \
|
||||
tesseract-ocr-fra \
|
||||
tesseract-ocr-ita \
|
||||
tesseract-ocr-spa \
|
||||
tzdata \
|
||||
unpaper \
|
||||
# Mime type detection
|
||||
zlib1g \
|
||||
# Barcode splitter
|
||||
libzbar0 \
|
||||
poppler-utils"
|
||||
|
||||
# copy app
|
||||
COPY src/ ./src/
|
||||
# Install basic runtime packages.
|
||||
# These change very infrequently
|
||||
RUN set -eux \
|
||||
echo "Installing system packages" \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& echo "Installing supervisor" \
|
||||
&& python3 -m pip install --default-timeout=1000 --upgrade --no-cache-dir supervisor==4.2.4
|
||||
|
||||
# add users, setup scripts
|
||||
RUN addgroup --gid 1000 paperless \
|
||||
&& useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
|
||||
&& chown -R paperless:paperless . \
|
||||
&& chmod 755 /sbin/docker-entrypoint.sh
|
||||
# Copy gunicorn config
|
||||
# Changes very infrequently
|
||||
WORKDIR /usr/src/paperless/
|
||||
|
||||
COPY gunicorn.conf.py .
|
||||
|
||||
# setup docker-specific things
|
||||
# Use mounts to avoid copying installer files into the image
|
||||
# These change sometimes, but rarely
|
||||
WORKDIR /usr/src/paperless/src/docker/
|
||||
|
||||
RUN --mount=type=bind,readwrite,source=docker,target=./ \
|
||||
set -eux \
|
||||
&& echo "Configuring ImageMagick" \
|
||||
&& cp imagemagick-policy.xml /etc/ImageMagick-6/policy.xml \
|
||||
&& echo "Configuring supervisord" \
|
||||
&& mkdir /var/log/supervisord /var/run/supervisord \
|
||||
&& cp supervisord.conf /etc/supervisord.conf \
|
||||
&& echo "Setting up Docker scripts" \
|
||||
&& cp docker-entrypoint.sh /sbin/docker-entrypoint.sh \
|
||||
&& chmod 755 /sbin/docker-entrypoint.sh \
|
||||
&& cp docker-prepare.sh /sbin/docker-prepare.sh \
|
||||
&& chmod 755 /sbin/docker-prepare.sh \
|
||||
&& cp wait-for-redis.py /sbin/wait-for-redis.py \
|
||||
&& chmod 755 /sbin/wait-for-redis.py \
|
||||
&& echo "Installing managment commands" \
|
||||
&& chmod +x install_management_commands.sh \
|
||||
&& ./install_management_commands.sh
|
||||
|
||||
# Install the built packages from the installer library images
|
||||
# Use mounts to avoid copying installer files into the image
|
||||
# These change sometimes
|
||||
RUN --mount=type=bind,from=qpdf-builder,target=/qpdf \
|
||||
--mount=type=bind,from=psycopg2-builder,target=/psycopg2 \
|
||||
--mount=type=bind,from=pikepdf-builder,target=/pikepdf \
|
||||
set -eux \
|
||||
&& echo "Installing qpdf" \
|
||||
&& apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/libqpdf28_*.deb \
|
||||
&& apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/qpdf_*.deb \
|
||||
&& echo "Installing pikepdf and dependencies" \
|
||||
&& python3 -m pip install --no-cache-dir /pikepdf/usr/src/pikepdf/wheels/packaging*.whl \
|
||||
&& python3 -m pip install --no-cache-dir /pikepdf/usr/src/pikepdf/wheels/lxml*.whl \
|
||||
&& python3 -m pip install --no-cache-dir /pikepdf/usr/src/pikepdf/wheels/Pillow*.whl \
|
||||
&& python3 -m pip install --no-cache-dir /pikepdf/usr/src/pikepdf/wheels/pyparsing*.whl \
|
||||
&& python3 -m pip install --no-cache-dir /pikepdf/usr/src/pikepdf/wheels/pikepdf*.whl \
|
||||
&& python -m pip list \
|
||||
&& echo "Installing psycopg2" \
|
||||
&& python3 -m pip install --no-cache-dir /psycopg2/usr/src/psycopg2/wheels/psycopg2*.whl \
|
||||
&& python -m pip list
|
||||
|
||||
# Python dependencies
|
||||
# Change pretty frequently
|
||||
COPY requirements.txt ../
|
||||
|
||||
# Packages needed only for building a few quick Python
|
||||
# dependencies
|
||||
ARG BUILD_PACKAGES="\
|
||||
build-essential \
|
||||
python3-dev"
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Installing build system packages" \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade wheel \
|
||||
&& echo "Installing Python requirements" \
|
||||
&& python3 -m pip install --default-timeout=1000 --no-cache-dir -r ../requirements.txt \
|
||||
&& echo "Cleaning up image" \
|
||||
&& apt-get -y purge ${BUILD_PACKAGES} \
|
||||
&& apt-get -y autoremove --purge \
|
||||
&& apt-get clean --yes \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -rf /tmp/* \
|
||||
&& rm -rf /var/tmp/* \
|
||||
&& rm -rf /var/cache/apt/archives/* \
|
||||
&& truncate -s 0 /var/log/*log
|
||||
|
||||
WORKDIR /usr/src/paperless/src/
|
||||
|
||||
RUN sudo -HEu paperless python3 manage.py collectstatic --clear --no-input
|
||||
# copy backend
|
||||
COPY ./src ./
|
||||
|
||||
RUN sudo -HEu paperless python3 manage.py compilemessages
|
||||
# copy frontend
|
||||
COPY --from=compile-frontend /src/src/documents/static/frontend/ ./documents/static/frontend/
|
||||
|
||||
# add users, setup scripts
|
||||
RUN set -eux \
|
||||
&& addgroup --gid 1000 paperless \
|
||||
&& useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
|
||||
&& chown -R paperless:paperless ../ \
|
||||
&& gosu paperless python3 manage.py collectstatic --clear --no-input \
|
||||
&& gosu paperless python3 manage.py compilemessages
|
||||
|
||||
VOLUME ["/usr/src/paperless/data", \
|
||||
"/usr/src/paperless/media", \
|
||||
"/usr/src/paperless/consume", \
|
||||
"/usr/src/paperless/export"]
|
||||
|
||||
VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/usr/src/paperless/consume", "/usr/src/paperless/export"]
|
||||
ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
|
||||
EXPOSE 8000
|
||||
CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
|
||||
LABEL maintainer="Jonas Winkler <dev@jpwinkler.de>"
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"]
|
||||
|
48
Pipfile
@@ -9,40 +9,36 @@ verify_ssl = true
|
||||
name = "piwheels"
|
||||
|
||||
[packages]
|
||||
dateparser = "~=0.7.6"
|
||||
django = "~=3.1.3"
|
||||
dateparser = "~=1.1"
|
||||
django = "~=4.0"
|
||||
django-cors-headers = "*"
|
||||
django-extensions = "*"
|
||||
django-filter = "~=2.4.0"
|
||||
django-q = "~=1.3.4"
|
||||
djangorestframework = "~=3.12.2"
|
||||
django-filter = "~=21.1"
|
||||
django-q = "~=1.3"
|
||||
djangorestframework = "~=3.13"
|
||||
filelock = "*"
|
||||
fuzzywuzzy = {extras = ["speedup"], version = "*"}
|
||||
gunicorn = "*"
|
||||
imap-tools = "*"
|
||||
imap-tools = "~=0.54.0"
|
||||
langdetect = "*"
|
||||
# numpy 1.20.0 drops python 3.6 support
|
||||
numpy = "~=1.19.5"
|
||||
pdftotext = "*"
|
||||
pathvalidate = "*"
|
||||
# pinned to 8.1.0, since aarch64 wheels might not be available beyond that https://github.com/python-pillow/Pillow/issues/5202
|
||||
pillow = "==8.1.0"
|
||||
pikepdf = "~=2.5.0"
|
||||
pillow = "~=9.1"
|
||||
# Any version update to pikepdf requires a base image update
|
||||
pikepdf = "~=5.1"
|
||||
python-gnupg = "*"
|
||||
python-dotenv = "*"
|
||||
python-dateutil = "*"
|
||||
python-magic = "*"
|
||||
psycopg2-binary = "*"
|
||||
# Any version update to psycopg2 requires a base image update
|
||||
psycopg2 = "*"
|
||||
redis = "*"
|
||||
# Pinned because aarch64 wheels and updates cause warnings when loading the classifier model.
|
||||
scikit-learn="==0.24.0"
|
||||
# Prevent scipy updates because 1.6 is incompatible with python 3.6
|
||||
scipy="~=1.5.4"
|
||||
whitenoise = "~=5.2.0"
|
||||
watchdog = "*"
|
||||
scikit-learn="==1.0.2"
|
||||
whitenoise = "~=6.0.0"
|
||||
watchdog = "~=2.1.0"
|
||||
whoosh="~=2.7.4"
|
||||
inotifyrecursive = "~=0.3.4"
|
||||
ocrmypdf = "~=11.6"
|
||||
inotifyrecursive = "~=0.3"
|
||||
ocrmypdf = "~=13.4"
|
||||
tqdm = "*"
|
||||
tika = "*"
|
||||
# TODO: This will sadly also install daphne+dependencies,
|
||||
@@ -51,7 +47,12 @@ channels = "~=3.0"
|
||||
channels-redis = "*"
|
||||
uvicorn = {extras = ["standard"], version = "*"}
|
||||
concurrent-log-handler = "*"
|
||||
django-redis = "*"
|
||||
"pdfminer.six" = "*"
|
||||
"backports.zoneinfo" = {version = "*", markers = "python_version < '3.9'"}
|
||||
"importlib-resources" = {version = "*", markers = "python_version < '3.9'"}
|
||||
zipp = {version = "*", markers = "python_version < '3.9'"}
|
||||
pyzbar = "*"
|
||||
pdf2image = "*"
|
||||
|
||||
[dev-packages]
|
||||
coveralls = "*"
|
||||
@@ -63,6 +64,9 @@ pytest-django = "*"
|
||||
pytest-env = "*"
|
||||
pytest-sugar = "*"
|
||||
pytest-xdist = "*"
|
||||
sphinx = "~=3.4.2"
|
||||
sphinx = "~=4.5.0"
|
||||
sphinx_rtd_theme = "*"
|
||||
tox = "*"
|
||||
black = "*"
|
||||
pre-commit = "*"
|
||||
myst-parser = "*"
|
||||
|
2616
Pipfile.lock
generated
148
README.md
@@ -1,106 +1,120 @@
|
||||
[](https://github.com/jonaswinkler/paperless-ng/actions)
|
||||

|
||||
[](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://hub.docker.com/r/jonaswinkler/paperless-ng)
|
||||
[](https://coveralls.io/github/jonaswinkler/paperless-ng?branch=master)
|
||||
[](https://github.com/paperless-ngx/paperless-ngx/actions)
|
||||
[](https://crowdin.com/project/paperless-ngx)
|
||||
[](https://paperless-ngx.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://coveralls.io/github/paperless-ngx/paperless-ngx?branch=master)
|
||||
[](https://matrix.to/#/#paperless:adnidor.de)
|
||||
|
||||
# Paperless-ng
|
||||
<p align="center">
|
||||
<img src="https://github.com/paperless-ngx/paperless-ngx/raw/main/resources/logo/web/png/Black%20logo%20-%20no%20background.png#gh-light-mode-only" width="50%" />
|
||||
<img src="https://github.com/paperless-ngx/paperless-ngx/raw/main/resources/logo/web/png/White%20logo%20-%20no%20background.png#gh-dark-mode-only" width="50%" />
|
||||
</p>
|
||||
|
||||
[Paperless](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents.
|
||||
<!-- omit in toc -->
|
||||
|
||||
Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation.
|
||||
# Paperless-ngx
|
||||
|
||||
# How it Works
|
||||
Paperless-ngx is a document management system that transforms your physical documents into a searchable online archive so you can keep, well, _less paper_.
|
||||
|
||||
Paperless does not control your scanner, it only helps you deal with what your scanner produces.
|
||||
Paperless-ngx forked from [paperless-ng](https://github.com/jonaswinkler/paperless-ng) to continue the great work and distribute responsibility of supporting and advancing the project among a team of people. [Consider joining us!](#community-support) Discussion of this transition can be found in issues
|
||||
[#1599](https://github.com/jonaswinkler/paperless-ng/issues/1599) and [#1632](https://github.com/jonaswinkler/paperless-ng/issues/1632).
|
||||
|
||||
1. Buy a document scanner that can write to a place on your network. If you need some inspiration, have a look at the [scanner recommendations](https://paperless-ng.readthedocs.io/en/latest/scanners.html) page. Set it up to "scan to FTP" or something similar. It should be able to push scanned images to a server without you having to do anything. Of course if your scanner doesn't know how to automatically upload the file somewhere, you can always do that manually. Paperless doesn't care how the documents get into its local consumption directory.
|
||||
A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. _Note: demo content is reset frequently and confidential information should not be uploaded._
|
||||
|
||||
- Alternatively, you can use any of the mobile scanning apps out there. We have an app that allows you to share documents with paperless, if you're on Android. See the section on affiliated projects.
|
||||
|
||||
2. Wait for paperless to process your files. OCR is expensive, and depending on the power of your machine, this might take a bit of time.
|
||||
3. Use the web frontend to sift through the database and find what you want.
|
||||
4. Download the PDF you need/want via the web interface and do whatever you like with it. You can even print it and send it as if it's the original. In most cases, no one will care or notice.
|
||||
|
||||
Here's what you get:
|
||||
|
||||

|
||||
- [Features](#features)
|
||||
- [Getting started](#getting-started)
|
||||
- [Contributing](#contributing)
|
||||
- [Community Support](#community-support)
|
||||
- [Translation](#translation)
|
||||
- [Feature Requests](#feature-requests)
|
||||
- [Bugs](#bugs)
|
||||
- [Affiliated Projects](#affiliated-projects)
|
||||
- [Important Note](#important-note)
|
||||
|
||||
# Features
|
||||
|
||||
* Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
|
||||
* Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents).
|
||||
* Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ng.readthedocs.io/en/latest/configuration.html#tika-settings))
|
||||
* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and can be configured freely.
|
||||
* Single page application front end. Should be pretty snappy. Will be mobile friendly in the future.
|
||||
* Includes a dashboard that shows basic statistics and has document upload.
|
||||
* Filtering by tags, correspondents, types, and more.
|
||||
* Customizable views can be saved and displayed on the dashboard.
|
||||
* Full text search helps you find what you need.
|
||||
* Auto completion suggests relevant words from your documents.
|
||||
* Results are sorted by relevance to your search query.
|
||||
* Highlighting shows you which parts of the document matched the query.
|
||||
* Searching for similar documents ("More like this")
|
||||
* Email processing: Paperless adds documents from your email accounts.
|
||||
* Configure multiple accounts and filters for each account.
|
||||
* When adding documents from mails, paperless can move these mails to a new folder, mark them as read, flag them or delete them.
|
||||
* Machine learning powered document matching.
|
||||
* Paperless learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
|
||||
* A task processor that processes documents in parallel and also tells you when something goes wrong. On modern multi core systems, consumption is blazing fast.
|
||||

|
||||

|
||||
|
||||
If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). However, some parts of the UI have changed since I took these.
|
||||
|
||||
For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html)
|
||||
- Organize and index your scanned documents with tags, correspondents, types, and more.
|
||||
- Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
|
||||
- Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents).
|
||||
- Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ngx.readthedocs.io/en/latest/configuration.html#tika-settings))
|
||||
- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely.
|
||||
- Single page application front end.
|
||||
- Includes a dashboard that shows basic statistics and has document upload.
|
||||
- Filtering by tags, correspondents, types, and more.
|
||||
- Customizable views can be saved and displayed on the dashboard.
|
||||
- Full text search helps you find what you need.
|
||||
- Auto completion suggests relevant words from your documents.
|
||||
- Results are sorted by relevance to your search query.
|
||||
- Highlighting shows you which parts of the document matched the query.
|
||||
- Searching for similar documents ("More like this")
|
||||
- Email processing: Paperless adds documents from your email accounts.
|
||||
- Configure multiple accounts and filters for each account.
|
||||
- When adding documents from mail, paperless can move these mail to a new folder, mark them as read, flag them as important or delete them.
|
||||
- Machine learning powered document matching.
|
||||
- Paperless-ngx learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
|
||||
- Optimized for multi core systems: Paperless-ngx consumes multiple documents in parallel.
|
||||
- The integrated sanity checker makes sure that your document archive is in good health.
|
||||
- [More screenshots are available in the documentation](https://paperless-ngx.readthedocs.io/en/latest/screenshots.html).
|
||||
|
||||
# Getting started
|
||||
|
||||
The recommended way to deploy paperless is docker-compose. The files in the /docker/hub directory are configured to pull the image from Docker Hub.
|
||||
The easiest way to deploy paperless is docker-compose. The files in the [`/docker/compose` directory](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose) are configured to pull the image from Github Packages.
|
||||
|
||||
Read the [documentation](https://paperless-ng.readthedocs.io/en/latest/setup.html#installation) on how to get started.
|
||||
If you'd like to jump right in, you can configure a docker-compose environment with our install script:
|
||||
|
||||
Alternatively, you can install the dependencies and setup apache and a database server yourself. The documenation has a step by step guide on how to do it.
|
||||
```bash
|
||||
bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
|
||||
```
|
||||
|
||||
# Migrating to paperless-ng
|
||||
Alternatively, you can install the dependencies and setup apache and a database server yourself. The [documentation](https://paperless-ngx.readthedocs.io/en/latest/setup.html#installation) has a step by step guide on how to do it.
|
||||
|
||||
Read the section about [migration](https://paperless-ng.readthedocs.io/en/latest/setup.html#migration-to-paperless-ng) in the documentation. Its also entirely possible to go back to paperless by reverting the database migrations.
|
||||
Migrating from Paperless-ng is easy, just drop in the new docker image! See the [documentation on migrating](https://paperless-ngx.readthedocs.io/en/latest/setup.html#migrating-from-paperless-ng) for more details.
|
||||
|
||||
# Documentation
|
||||
<!-- omit in toc -->
|
||||
|
||||
The documentation for Paperless-ng is available on [ReadTheDocs](https://paperless-ng.readthedocs.io/).
|
||||
### Documentation
|
||||
|
||||
# Translation
|
||||
The documentation for Paperless-ngx is available on [ReadTheDocs](https://paperless-ngx.readthedocs.io/).
|
||||
|
||||
Paperless is currently available in English, German, Dutch and French. Translation is coordinated at transifex: https://www.transifex.com/paperless/paperless-ng
|
||||
# Contributing
|
||||
|
||||
If you want to see paperless in your own language, request that language at transifex and you can start translating after I approve the language.
|
||||
If you feel like contributing to the project, please do! Bug fixes, enhancements, visual fixes etc. are always welcome. If you want to implement something big: Please start a discussion about that! The [documentation](https://paperless-ngx.readthedocs.io/en/latest/extending.html) has some basic information on how to get started.
|
||||
|
||||
# Feature Requests
|
||||
## Community Support
|
||||
|
||||
Feature requests can be submitted via [GitHub Discussions](https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests), you can search for existing ideas, add your own and vote for the ones you care about! Note that some older feature requests can also be found under [issues](https://github.com/jonaswinkler/paperless-ng/issues).
|
||||
People interested in continuing the work on paperless-ngx are encouraged to reach out here on github and in the [Matrix Room](https://matrix.to/#/#paperless:adnidor.de). If you would like to contribute to the project on an ongoing basis there are multiple [teams](https://github.com/orgs/paperless-ngx/people) (frontend, ci/cd, etc) that could use your help so please reach out!
|
||||
|
||||
# Questions? Something not working?
|
||||
## Translation
|
||||
|
||||
For bugs please [open an issue](https://github.com/jonaswinkler/paperless-ng/issues) or [start a discussion](https://github.com/jonaswinkler/paperless-ng/discussions) if you have questions.
|
||||
Paperless-ngx is available in many languages that are coordinated on Crowdin. If you want to help out by translating paperless-ngx into your language, please head over to https://crwd.in/paperless-ngx, and thank you! More details can be found in [CONTRIBUTING.md](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md#translating-paperless-ngx).
|
||||
|
||||
## Feel like helping out?
|
||||
## Feature Requests
|
||||
|
||||
There's still lots of things to be done, just have a look at open issues & discussions. If you feel like contributing to the project, please do! Bug fixes and improvements to the front end (I just can't seem to get some of these CSS things right) are always welcome. The documentation has some basic information on how to get started.
|
||||
Feature requests can be submitted via [GitHub Discussions](https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests), you can search for existing ideas, add your own and vote for the ones you care about.
|
||||
|
||||
If you want to implement something big: Please start a discussion about that in the issues! Maybe I've already had something similar in mind and we can make it happen together. However, keep in mind that the general roadmap is to make the existing features stable and get them tested. See the roadmap above.
|
||||
## Bugs
|
||||
|
||||
For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/issues) or [start a discussion](https://github.com/paperless-ngx/paperless-ngx/discussions) if you have questions.
|
||||
|
||||
# Affiliated Projects
|
||||
|
||||
Paperless has been around a while now, and people are starting to build stuff on top of it. If you're one of those people, we can add your project to this list:
|
||||
Paperless has been around a while now, and people are starting to build stuff on top of it. If you're one of those people, we can add your project to this list:
|
||||
|
||||
* [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless. Updated to work with paperless-ng.
|
||||
* [Paperless Share](https://github.com/qcasey/paperless_share). Share any files from your Android application with paperless. Very simple, but works with all of the mobile scanning apps out there that allow you to share scanned documents.
|
||||
- [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless-ngx. Also works with the original Paperless and Paperless-ngx.
|
||||
- [Paperless Share](https://github.com/qcasey/paperless_share). Share any files from your Android application with paperless. Very simple, but works with all of the mobile scanning apps out there that allow you to share scanned documents.
|
||||
- [Scan to Paperless](https://github.com/sbrunner/scan-to-paperless): Scan and prepare (crop, deskew, OCR, ...) your documents for Paperless.
|
||||
|
||||
These projects also exist, but their status and compatibility with paperless-ng is unknown.
|
||||
These projects also exist, but their status and compatibility with paperless-ngx is unknown.
|
||||
|
||||
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
|
||||
* [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
|
||||
- [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
|
||||
|
||||
This project also exists, but needs updates to be compatible with paperless-ngx.
|
||||
|
||||
- [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
|
||||
Known issues on Mac: (Could not load reminders and documents)
|
||||
|
||||
# Important Note
|
||||
|
||||
Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption by default (it needs to be searchable, so if someone has ideas on how to do that on encrypted data, I'm all ears). This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home.
|
||||
Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption. This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home.
|
||||
|
@@ -1,114 +0,0 @@
|
||||
Ansible Role: paperless-ng
|
||||
==========================
|
||||
|
||||
Installs and configures paperless-ng EDMS on Debian/Ubuntu systems.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
No special system requirements. Ansible 2.7 or newer is required.
|
||||
|
||||
Note that this role requires root access, so either run it in a playbook with a global `become: yes`, or invoke the role in your playbook like:
|
||||
|
||||
- hosts: all
|
||||
roles:
|
||||
- role: ansible
|
||||
become: yes
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
Most configuration variables from paperless-ng itself are available and accept their respective arguments.
|
||||
Every `PAPERLESS_*` configuration varaible is lowercased and instead prefixed with `paperlessng_*` in `defaults/main.yml`.
|
||||
|
||||
For a full listing including explainations and allowed values, see the current [documentation](https://paperless-ng.readthedocs.io/en/ng-0.9.14/configuration.html).
|
||||
|
||||
Additional variables available in this role are listed below, along with default values:
|
||||
|
||||
paperlessng_version: 0.9.14
|
||||
|
||||
The [release](https://github.com/jonaswinkler/paperless-ng/releases) archive version of paperless-ng to install.
|
||||
|
||||
paperlessng_redis_host: localhost
|
||||
paperlessng_redis_port: 6379
|
||||
|
||||
Seperate configuration values that combine into `PAPERLESS_REDIS`.
|
||||
|
||||
paperlessng_db_type: sqlite
|
||||
|
||||
Database to use. Default is file-based SQLite.
|
||||
|
||||
paperlessng_db_host: localhost
|
||||
paperlessng_db_port: 5432
|
||||
paperlessng_db_name: paperlessng
|
||||
paperlessng_db_user: paperlessng
|
||||
paperlessng_db_pass: paperlessng
|
||||
paperlessng_db_sslmode: prefer
|
||||
|
||||
Database configuration (only applicable if `paperlessng_db_type == 'postgresql'`).
|
||||
|
||||
paperlessng_directory: /opt/paperless-ng
|
||||
|
||||
Root directory paperless-ng is installed into.
|
||||
|
||||
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
|
||||
|
||||
Directory used for the virtual environment for paperless-ng.
|
||||
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
|
||||
List of OCR languages to install and configure (`apt search tesseract-ocr-*`).
|
||||
|
||||
paperlessng_use_jbig2enc: True
|
||||
|
||||
Whether to install and use [jbig2enc](https://github.com/agl/jbig2enc) for OCRmyPDF.
|
||||
|
||||
paperlessng_big2enc_lossy: False
|
||||
|
||||
Whether to use jbig2enc's lossy compression mode.
|
||||
|
||||
paperlessng_superuser_name: paperlessng
|
||||
paperlessng_superuser_email: paperlessng@example.com
|
||||
paperlessng_superuser_password: paperlessng
|
||||
|
||||
Credentials of the initial superuser in paperless-ng.
|
||||
|
||||
paperlessng_system_user: paperlessng
|
||||
paperlessng_system_group: paperlessng
|
||||
|
||||
System user and group to run the paperless-ng services as (will be created if required).
|
||||
|
||||
paperlessng_listen_address: 127.0.0.1
|
||||
paperlessng_listen_port: 8000
|
||||
|
||||
Address and port for the paperless-ng service to listen on.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
No ansible dependencies.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
`playbook.yml`:
|
||||
|
||||
- hosts: all
|
||||
become: yes
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
roles:
|
||||
- ansible
|
||||
|
||||
`vars/main.yml`:
|
||||
|
||||
paperlessng_media_root: /mnt/media/smbshare
|
||||
|
||||
paperlessng_db_type: postgresql
|
||||
paperlessng_db_pass: PLEASEPROVIDEASTRONGPASSWORDHERE
|
||||
|
||||
paperlessng_secret_key: AGAINPLEASECHANGETHISNOW
|
||||
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
- deu
|
@@ -1,78 +0,0 @@
|
||||
---
|
||||
paperlessng_version: latest # 'latest', release number, or github branch/tag/commit/ref
|
||||
|
||||
# Required services
|
||||
paperlessng_redis_host: localhost
|
||||
paperlessng_redis_port: 6379
|
||||
paperlessng_db_type: sqlite # or postgresql
|
||||
# Below entries only apply for paperlessng_db_type=='postgresql'
|
||||
paperlessng_db_host: localhost
|
||||
paperlessng_db_port: 5432
|
||||
paperlessng_db_name: paperlessng
|
||||
paperlessng_db_user: paperlessng
|
||||
paperlessng_db_pass: paperlessng
|
||||
paperlessng_db_sslmode: prefer
|
||||
|
||||
# Paths and folders
|
||||
paperlessng_directory: /opt/paperless-ng
|
||||
paperlessng_consumption_dir: "{{ paperlessng_directory }}/consumption"
|
||||
paperlessng_data_dir: "{{ paperlessng_directory }}/data"
|
||||
paperlessng_media_root: "{{ paperlessng_directory }}/media"
|
||||
paperlessng_static_dir: "{{ paperlessng_directory }}/static"
|
||||
paperlessng_filename_format:
|
||||
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
|
||||
|
||||
# Hosting & Security
|
||||
paperlessng_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD
|
||||
paperlessng_allowed_hosts: "*"
|
||||
paperlessng_cors_allowed_hosts: http://localhost:8000
|
||||
paperlessng_force_script_name:
|
||||
paperlessng_static_url: /static/
|
||||
paperlessng_auto_login_username:
|
||||
paperlessng_cookie_prefix: ""
|
||||
paperlessng_enable_http_remote_user: False
|
||||
|
||||
# OCR settings
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
paperlessng_ocr_mode: skip
|
||||
paperlessng_ocr_output_type: pdfa
|
||||
paperlessng_ocr_pages: 0
|
||||
paperlessng_ocr_image_dpi:
|
||||
# see https://ocrmypdf.readthedocs.io/en/latest/api.html#ocrmypdf.ocr
|
||||
paperlessng_ocr_user_args:
|
||||
#- "deskew": True # https://github.com/jonaswinkler/paperless-ng/issues/231
|
||||
- "optimize": 1
|
||||
paperlessng_use_jbig2enc: True
|
||||
paperlessng_big2enc_lossy: False
|
||||
|
||||
# Tika settings
|
||||
paperlessng_tika_enabled: False
|
||||
paperlessng_tika_endpoint: http://localhost:9998
|
||||
paperlessng_tika_gotenberg_endpoint: http://localhost:3000
|
||||
|
||||
# Software tweaks
|
||||
paperlessng_time_zone: Europe/Berlin
|
||||
paperlessng_consumer_polling: 0
|
||||
paperlessng_consumer_delete_duplicates: False
|
||||
paperlessng_consumer_recursive: False
|
||||
paperlessng_consumer_subdirs_as_tags: False
|
||||
paperlessng_optimize_thumbnails: True
|
||||
paperlessng_post_consume_script:
|
||||
paperlessng_filename_date_order:
|
||||
paperlessng_filename_parse_transforms:
|
||||
paperlessng_thumbnail_font_name: /usr/share/fonts/liberation/LiberationSerif-Regular.ttf
|
||||
paperlessng_ignore_dates: ""
|
||||
|
||||
# Superuser settings
|
||||
paperlessng_superuser_name: paperlessng
|
||||
paperlessng_superuser_email: paperlessng@example.com
|
||||
paperlessng_superuser_password: paperlessng
|
||||
|
||||
# System user settings
|
||||
paperlessng_system_user: paperlessng
|
||||
paperlessng_system_group: paperlessng
|
||||
|
||||
# Webserver settings
|
||||
paperlessng_listen_address: 127.0.0.1
|
||||
paperlessng_listen_port: 8000
|
@@ -1,17 +0,0 @@
|
||||
dependencies: []
|
||||
|
||||
galaxy_info:
|
||||
author: C0nsultant
|
||||
description: Bare-metal deployment of paperless-ng DMS
|
||||
license: license (GPLv3)
|
||||
min_ansible_version: 2.7
|
||||
|
||||
platforms:
|
||||
- name: Debian
|
||||
versions:
|
||||
- buster
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- focal
|
||||
|
||||
galaxy_tags: [EDMS, django, python, web]
|
@@ -1,10 +0,0 @@
|
||||
---
|
||||
- name: update previous release to newest release
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: set current github commit as version when available
|
||||
set_fact:
|
||||
paperlessng_version: "{{ lookup('env', 'GITHUB_SHA') | default('master', True) }}"
|
||||
- name: update to newest paperless-ng release
|
||||
include_role:
|
||||
name: ansible
|
@@ -1,35 +0,0 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: ubuntu_focal
|
||||
image: jrei/systemd-ubuntu:20.04
|
||||
privileged: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /run
|
||||
- /run/lock
|
||||
override_command: False
|
||||
# ubuntu 18.04 bionic works except that
|
||||
# the default redis configuration expects IPv6 which is not enabled in docker by default
|
||||
# the default Python environment is configured for ASCII instead of UTF-8
|
||||
# ubuntu 16.04 xenial only has Python 3.5 which is EOL and breaks multiple dependencies
|
||||
- name: debian_buster
|
||||
image: jrei/systemd-debian:10
|
||||
privileged: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /run
|
||||
- /run/lock
|
||||
override_command: False
|
||||
# debian 9 stretch only has Python 3.5 which is EOL and breaks multiple dependencies
|
||||
provisioner:
|
||||
name: ansible
|
||||
verifier:
|
||||
name: ansible
|
@@ -1,10 +0,0 @@
|
||||
- name: install previous release
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: set previous version as installation target
|
||||
set_fact:
|
||||
paperlessng_version: latest
|
||||
|
||||
- name: install previous paperless-ng release
|
||||
include_role:
|
||||
name: ansible
|
@@ -1,94 +0,0 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
|
||||
vars_files:
|
||||
- ../../defaults/main.yml
|
||||
|
||||
tasks:
|
||||
- name: check if webserver is up
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}"
|
||||
status_code: [200, 302]
|
||||
return_content: yes
|
||||
register: landingpage
|
||||
failed_when: "'Sign in</button>' not in landingpage.content"
|
||||
|
||||
- name: generate random name and content
|
||||
set_fact:
|
||||
content: "{{ lookup('password', '/dev/null length=65536 chars=ascii_letters') }}"
|
||||
filename: "{{ lookup('password', '/dev/null length=8 chars=ascii_letters') }}"
|
||||
|
||||
- name: check if document posting works
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/post_document/"
|
||||
method: POST
|
||||
body_format: form-multipart
|
||||
body:
|
||||
document:
|
||||
content: "{{ content }}"
|
||||
filename: "{{ filename }}.txt"
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
Content-Type: text/plain
|
||||
return_content: yes
|
||||
register: post_document
|
||||
failed_when: "'OK' not in post_document.content"
|
||||
|
||||
- name: verify uploaded document has been accepted
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/"
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: logs
|
||||
failed_when: "('Consuming ' + filename + '.txt') not in logs.content"
|
||||
|
||||
- name: sleep till consumption finished
|
||||
pause:
|
||||
seconds: 10
|
||||
|
||||
- name: verify uploaded document has been consumed
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/"
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: logs
|
||||
failed_when: "filename + ' consumption finished' not in logs.content"
|
||||
|
||||
- name: get documents
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/"
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: documents
|
||||
|
||||
- name: set document index
|
||||
set_fact:
|
||||
index: "{{ documents.json['results'][0]['id'] }}"
|
||||
|
||||
- name: verify uploaded document is avaiable
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/{{ index }}/"
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: document
|
||||
failed_when: "'Not found.' in document.content or content not in document.json['content']"
|
||||
|
||||
- name: check if deleting uploaded document works
|
||||
uri:
|
||||
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/bulk_edit/"
|
||||
method: POST
|
||||
body_format: json
|
||||
body:
|
||||
documents: ["{{ index }}"]
|
||||
method: delete
|
||||
parameters: {}
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
register: delete_document
|
||||
failed_when: "'OK' not in delete_document.json['result']"
|
@@ -1,6 +0,0 @@
|
||||
---
|
||||
- name: extract paperless-ng
|
||||
unarchive:
|
||||
src: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz"
|
||||
remote_src: yes
|
||||
dest: "{{ tempdir.path }}"
|
@@ -1,112 +0,0 @@
|
||||
---
|
||||
- name: install dev dependencies
|
||||
apt:
|
||||
pkg:
|
||||
- git
|
||||
- npm
|
||||
- gettext
|
||||
|
||||
- name: create output directories
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ paperlessng_system_user }}"
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: "750"
|
||||
with_items:
|
||||
- "{{ tempdir.path }}/paperless-ng"
|
||||
- "{{ tempdir.path }}/paperless-ng/scripts"
|
||||
|
||||
- block:
|
||||
- name: create temporary git directory
|
||||
tempfile:
|
||||
state: directory
|
||||
path: "{{ paperlessng_directory }}"
|
||||
register: gitdir
|
||||
|
||||
- name: pull paperless-ng
|
||||
git:
|
||||
repo: https://github.com/jonaswinkler/paperless-ng.git
|
||||
dest: "{{ gitdir.path }}"
|
||||
version: "{{ paperlessng_version }}"
|
||||
refspec: "+refs/pull/*:refs/pull/*"
|
||||
|
||||
- name: compile frontend
|
||||
command:
|
||||
cmd: "{{ item }}"
|
||||
args:
|
||||
chdir: "{{ gitdir.path }}/src-ui"
|
||||
failed_when: false
|
||||
with_items:
|
||||
- npm install -g @angular/cli
|
||||
- npm install
|
||||
- ./node_modules/.bin/ng build --prod
|
||||
|
||||
- name: copy application into place
|
||||
copy:
|
||||
src: "{{ gitdir.path }}/{{ item.src }}"
|
||||
remote_src: yes
|
||||
dest: "{{ tempdir.path }}/paperless-ng/{{ item.dest | default('') }}"
|
||||
with_items:
|
||||
- src: CONTRIBUTING.md
|
||||
- src: LICENSE
|
||||
- src: Pipfile
|
||||
- src: Pipfile.lock
|
||||
- src: README.md
|
||||
- src: requirements.txt
|
||||
- src: gunicorn.conf.py
|
||||
- src: paperless.conf.example
|
||||
dest: "paperless.conf"
|
||||
|
||||
- name: glob all scripts
|
||||
find:
|
||||
paths: ["{{ gitdir.path }}/scripts/"]
|
||||
patterns:
|
||||
- "*.service"
|
||||
- "*.sh"
|
||||
register: glob
|
||||
|
||||
- name: copy scripts
|
||||
copy:
|
||||
src: "{{ item.path }}"
|
||||
remote_src: yes
|
||||
dest: "{{ tempdir.path }}/paperless-ng/scripts/"
|
||||
with_items:
|
||||
- "{{ glob.files }}"
|
||||
|
||||
- name: copy sources
|
||||
command:
|
||||
cmd: "cp -r src/ {{ tempdir.path }}/paperless-ng/src"
|
||||
args:
|
||||
chdir: "{{ gitdir.path }}"
|
||||
|
||||
- name: create paperlessng venv
|
||||
command:
|
||||
cmd: "python3 -m virtualenv {{ gitdir.path }}/.venv/ -p /usr/bin/python3"
|
||||
|
||||
- name: install paperlessng requirements
|
||||
command:
|
||||
cmd: "{{ gitdir.path }}/.venv/bin/python3 -m pip install -r {{ gitdir.path }}/requirements.txt"
|
||||
|
||||
- name: compile messages
|
||||
command: "{{ gitdir.path }}/.venv/bin/python3 manage.py compilemessages"
|
||||
args:
|
||||
chdir: "{{ tempdir.path }}/paperless-ng/src/"
|
||||
|
||||
- name: collect static files
|
||||
command: "{{ gitdir.path }}/.venv/bin/python3 manage.py collectstatic --no-input"
|
||||
args:
|
||||
chdir: "{{ tempdir.path }}/paperless-ng/src/"
|
||||
|
||||
- name: remove pycache directories
|
||||
shell: find . -name __pycache__ | xargs rm -r
|
||||
args:
|
||||
chdir: "{{ tempdir.path }}"
|
||||
|
||||
- name: remove temporary git directory
|
||||
file:
|
||||
path: "{{ gitdir.path }}"
|
||||
state: absent
|
||||
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
@@ -1,540 +0,0 @@
|
||||
---
|
||||
- name: verify operating system
|
||||
fail:
|
||||
msg: Sorry, only Debian and Ubuntu supported at the moment.
|
||||
when: not(ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu')
|
||||
|
||||
- name: install base dependencies
|
||||
apt:
|
||||
update_cache: yes
|
||||
pkg:
|
||||
# paperless-ng
|
||||
- python3-pip
|
||||
- python3-dev
|
||||
- fonts-liberation
|
||||
- imagemagick
|
||||
- optipng
|
||||
- gnupg
|
||||
- libpoppler-cpp-dev
|
||||
- libpq-dev
|
||||
- libmagic-dev
|
||||
- mime-support
|
||||
# OCRmyPDF
|
||||
- unpaper
|
||||
- ghostscript
|
||||
- icc-profiles-free
|
||||
- qpdf
|
||||
- liblept5
|
||||
- libxml2
|
||||
- pngquant
|
||||
- zlib1g
|
||||
- tesseract-ocr
|
||||
# dev
|
||||
- sudo
|
||||
- build-essential
|
||||
- python3-setuptools
|
||||
- python3-wheel
|
||||
|
||||
# upstream virtualenv in Ubuntu 20.04 is broken
|
||||
# https://github.com/pypa/virtualenv/issues/1873
|
||||
- name: install python virtualenv
|
||||
pip:
|
||||
name: virtualenv
|
||||
extra_args: --upgrade
|
||||
|
||||
- name: install ocr languages
|
||||
apt:
|
||||
pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | list }}"
|
||||
|
||||
- name: set up notesalexp repository key (for jbig2enc)
|
||||
apt_key:
|
||||
url: https://notesalexp.org/debian/alexp_key.asc
|
||||
state: present
|
||||
when: paperlessng_use_jbig2enc
|
||||
|
||||
- name: set up notesalexp repository (for jbig2enc)
|
||||
apt_repository:
|
||||
repo: "deb https://notesalexp.org/debian/{{ ansible_distribution_release }}/ {{ ansible_distribution_release }} main"
|
||||
state: present
|
||||
when: paperlessng_use_jbig2enc
|
||||
|
||||
- name: set up notesalexp repository pinning
|
||||
copy:
|
||||
content: |
|
||||
Package: *
|
||||
Pin: release o=notesalexp.org
|
||||
Pin-Priority: 1
|
||||
|
||||
Package: jbig2enc
|
||||
Pin: release o=notesalexp.org
|
||||
Pin-Priority: 500
|
||||
dest: /etc/apt/preferences.d/notesalexp
|
||||
when: paperlessng_use_jbig2enc
|
||||
|
||||
- name: install jbig2enc
|
||||
apt:
|
||||
pkg: jbig2enc
|
||||
update_cache: yes
|
||||
when: paperlessng_use_jbig2enc
|
||||
|
||||
- name: install redis
|
||||
apt:
|
||||
pkg: redis-server
|
||||
when: paperlessng_redis_host == 'localhost' or paperlessng_redis_host == '127.0.0.1'
|
||||
|
||||
- name: enable redis
|
||||
systemd:
|
||||
name: redis-server
|
||||
enabled: yes
|
||||
masked: no
|
||||
state: started
|
||||
when: paperlessng_redis_host == 'localhost' or paperlessng_redis_host == '127.0.0.1'
|
||||
|
||||
- name: create paperless system group
|
||||
group:
|
||||
name: "{{ paperlessng_system_group }}"
|
||||
|
||||
- name: create paperless system user
|
||||
user:
|
||||
name: "{{ paperlessng_system_user }}"
|
||||
groups:
|
||||
- "{{ paperlessng_system_group }}"
|
||||
shell: /usr/sbin/nologin
|
||||
# GNUPG_HOME required due to paperless db.py
|
||||
create_home: yes
|
||||
|
||||
- block:
|
||||
- name: get latest release version
|
||||
uri:
|
||||
url: https://api.github.com/repos/jonaswinkler/paperless-ng/releases/latest
|
||||
method: GET
|
||||
register: latest_release
|
||||
- name: parse latest release version
|
||||
set_fact:
|
||||
paperlessng_version: "{{ latest_release.json['tag_name'] }}"
|
||||
when: paperlessng_version == "latest"
|
||||
|
||||
- name: check if version is ref
|
||||
fail:
|
||||
msg: "Specifying `paperlessng_version` as git ref may not work as expected!"
|
||||
ignore_errors: True # Output failure (as warning), but don't consider play failed
|
||||
when: paperlessng_version.startswith('refs/')
|
||||
|
||||
- block:
|
||||
- name: sanitize version string
|
||||
set_fact:
|
||||
paperlessng_version: "{{ paperlessng_version | regex_replace('^ng-(\\d+\\.\\d+\\.\\d+)$', '\\1') }}"
|
||||
- name: get tag data
|
||||
uri:
|
||||
url: https://api.github.com/repos/jonaswinkler/paperless-ng/tags
|
||||
method: GET
|
||||
register: tags
|
||||
- name: get commit for target tag
|
||||
set_fact:
|
||||
paperlessng_commit: "{{ tags.json | json_query('[?name==`ng-' + paperlessng_version +'`] | [0].commit.sha') }}"
|
||||
when: paperlessng_version | regex_search("^(ng-)?(\d+\.\d+\.\d+)$")
|
||||
|
||||
- block:
|
||||
- name: check if version is branch
|
||||
uri:
|
||||
url: "https://api.github.com/repos/jonaswinkler/paperless-ng/branches/{{ paperlessng_version }}"
|
||||
method: GET
|
||||
status_code: [200, 404]
|
||||
register: branch
|
||||
- name: get commit for target branch
|
||||
set_fact:
|
||||
paperlessng_commit: "{{ branch.json | json_query('commit.sha') }}"
|
||||
when: branch.status == 200
|
||||
- block:
|
||||
- name: check if version is commit-or-ref
|
||||
uri:
|
||||
url: "https://api.github.com/repos/jonaswinkler/paperless-ng/commits/{{ paperlessng_version }}"
|
||||
method: GET
|
||||
status_code: [200, 404, 422]
|
||||
register: commit
|
||||
- name: get commit for target commit-or-ref
|
||||
set_fact:
|
||||
paperlessng_commit: "{{ commit.json | json_query('sha') }}"
|
||||
when: commit.status == 200
|
||||
- name: fail
|
||||
fail:
|
||||
msg: "Can not determine commit from `paperlessng_version=={{ paperlessng_version }}`!"
|
||||
when: commit.status != 200
|
||||
when: branch.status == 404
|
||||
when: not(paperlessng_version | regex_search("^(ng-)?(\d+\.\d+\.\d+)$"))
|
||||
|
||||
- name: check for paperless-ng installation
|
||||
command:
|
||||
cmd: "cat {{ paperlessng_directory }}/.installed_version"
|
||||
changed_when: '"No such file or directory" in paperlessng_current_commit.stderr or paperlessng_current_commit.stdout != paperlessng_commit | string'
|
||||
failed_when: false
|
||||
ignore_errors: yes
|
||||
register: paperlessng_current_commit
|
||||
|
||||
- name: register current state
|
||||
set_fact:
|
||||
fresh_installation: '{{ "No such file or directory" in paperlessng_current_commit.stderr }}'
|
||||
update_installation: '{{ "No such file or directory" not in paperlessng_current_commit.stderr and paperlessng_current_commit.stdout != paperlessng_commit | string }}'
|
||||
reconfigure_only: "{{ paperlessng_current_commit.stdout == paperlessng_commit | string }}"
|
||||
|
||||
- block:
|
||||
- name: backup current paperless-ng installation
|
||||
copy:
|
||||
src: "{{ paperlessng_directory }}"
|
||||
remote_src: yes
|
||||
dest: "{{ paperlessng_directory }}-{{ ansible_date_time.iso8601 }}/"
|
||||
- name: remove current paperless sources
|
||||
file:
|
||||
path: "{{ paperlessng_directory }}/{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- docker
|
||||
- docs
|
||||
- scripts
|
||||
- src
|
||||
- static
|
||||
when: update_installation
|
||||
|
||||
- block:
|
||||
- name: create paperless-ng directory and set permissions
|
||||
file:
|
||||
path: "{{ paperlessng_directory }}"
|
||||
state: directory
|
||||
owner: "{{ paperlessng_system_user }}"
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: "750"
|
||||
- name: create temporary directory
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
tempfile:
|
||||
state: directory
|
||||
path: "{{ paperlessng_directory }}"
|
||||
register: tempdir
|
||||
- name: check if version is available as release archive
|
||||
uri:
|
||||
url: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz"
|
||||
method: GET
|
||||
status_code: [200, 404]
|
||||
register: release_archive
|
||||
- name: install paperless-ng from source
|
||||
include_tasks: install-source.yml
|
||||
when: release_archive.status == 404
|
||||
- name: install paperless-ng from release archive
|
||||
include_tasks: install-release.yml
|
||||
when: release_archive.status == 200
|
||||
- name: change owner and permissions of paperless-ng
|
||||
command:
|
||||
cmd: "{{ item }}"
|
||||
warn: false
|
||||
with_items:
|
||||
- "chown -R {{ paperlessng_system_user }}:{{ paperlessng_system_group }} {{ tempdir.path }}"
|
||||
- "find {{ tempdir.path }} -type d -exec chmod 0750 {} ;"
|
||||
- "find {{ tempdir.path }} -type f -exec chmod 0640 {} ;"
|
||||
- name: move paperless-ng
|
||||
command:
|
||||
cmd: "cp -a {{ tempdir.path }}/paperless-ng/. {{ paperlessng_directory }}"
|
||||
- name: store commit hash of installed version
|
||||
copy:
|
||||
content: "{{ paperlessng_commit }}"
|
||||
dest: "{{ paperlessng_directory }}/.installed_version"
|
||||
owner: "{{ paperlessng_system_user }}"
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: "0440"
|
||||
- name: remove temporary directory
|
||||
file:
|
||||
path: "{{ tempdir.path }}"
|
||||
state: absent
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: create paperless-ng directories and set permissions
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ paperlessng_system_user }}"
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: "750"
|
||||
with_items:
|
||||
- "{{ paperlessng_consumption_dir }}"
|
||||
- "{{ paperlessng_data_dir }}"
|
||||
- "{{ paperlessng_media_root }}"
|
||||
- "{{ paperlessng_static_dir }}"
|
||||
|
||||
- name: rename initial config
|
||||
command:
|
||||
cmd: "mv -f {{ paperlessng_directory }}/paperless.conf {{ paperlessng_directory }}/paperless.conf.template"
|
||||
removes: "{{ paperlessng_directory }}/paperless.conf"
|
||||
|
||||
- name: configure paperless-ng
|
||||
lineinfile:
|
||||
path: "{{ paperlessng_directory }}/paperless.conf.template"
|
||||
regexp: "^#?{{ item.regexp }}="
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
# Required services
|
||||
- regexp: PAPERLESS_REDIS
|
||||
line: "PAPERLESS_REDIS=redis://{{ paperlessng_redis_host }}:{{ paperlessng_redis_port }}"
|
||||
# Paths and folders
|
||||
- regexp: PAPERLESS_CONSUMPTION_DIR
|
||||
line: "PAPERLESS_CONSUMPTION_DIR={{ paperlessng_consumption_dir }}"
|
||||
- regexp: PAPERLESS_DATA_DIR
|
||||
line: "PAPERLESS_DATA_DIR={{ paperlessng_data_dir }}"
|
||||
- regexp: PAPERLESS_MEDIA_ROOT
|
||||
line: "PAPERLESS_MEDIA_ROOT={{ paperlessng_media_root }}"
|
||||
- regexp: PAPERLESS_STATICDIR
|
||||
line: "PAPERLESS_STATICDIR={{ paperlessng_static_dir }}"
|
||||
- regexp: PAPERLESS_FILENAME_FORMAT
|
||||
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
|
||||
# Hosting & Security
|
||||
- regexp: PAPERLESS_SECRET_KEY
|
||||
line: "PAPERLESS_SECRET_KEY={{ paperlessng_secret_key }}"
|
||||
- regexp: PAPERLESS_ALLOWED_HOSTS
|
||||
line: "PAPERLESS_ALLOWED_HOSTS={{ paperlessng_allowed_hosts }}"
|
||||
- regexp: PAPERLESS_CORS_ALLOWED_HOSTS
|
||||
line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperlessng_cors_allowed_hosts }}"
|
||||
- regexp: PAPERLESS_FORCE_SCRIPT_NAME
|
||||
line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperlessng_force_script_name }}"
|
||||
- regexp: PAPERLESS_STATIC_URL
|
||||
line: "PAPERLESS_STATIC_URL={{ paperlessng_static_url }}"
|
||||
- regexp: PAPERLESS_AUTO_LOGIN_USERNAME
|
||||
line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperlessng_auto_login_username }}"
|
||||
- regexp: PAPERLESS_COOKIE_PREFIX
|
||||
line: "PAPERLESS_COOKIE_PREFIX={{ paperlessng_cookie_prefix }}"
|
||||
- regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER
|
||||
line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperlessng_enable_http_remote_user }}"
|
||||
# OCR settings
|
||||
- regexp: PAPERLESS_OCR_LANGUAGE
|
||||
line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}"
|
||||
- regexp: PAPERLESS_OCR_MODE
|
||||
line: "PAPERLESS_OCR_MODE={{ paperlessng_ocr_mode }}"
|
||||
- regexp: PAPERLESS_OCR_OUTPUT_TYPE
|
||||
line: "PAPERLESS_OCR_OUTPUT_TYPE={{ paperlessng_ocr_output_type }}"
|
||||
- regexp: PAPERLESS_OCR_PAGES
|
||||
line: "PAPERLESS_OCR_PAGES={{ paperlessng_ocr_pages }}"
|
||||
- regexp: PAPERLESS_OCR_IMAGE_DPI
|
||||
line: "PAPERLESS_OCR_IMAGE_DPI={{ paperlessng_ocr_image_dpi }}"
|
||||
- regexp: PAPERLESS_OCR_USER_ARGS
|
||||
line: "PAPERLESS_OCR_USER_ARGS={{ paperlessng_ocr_user_args | combine({'jbig2_lossy': true} if paperlessng_big2enc_lossy else {}) | to_json }}"
|
||||
# Tika settings
|
||||
- regexp: PAPERLESS_TIKA_ENABLED
|
||||
line: "PAPERLESS_TIKA_ENABLED={{ paperlessng_tika_enabled }}"
|
||||
- regexp: PAPERLESS_TIKA_ENDPOINT
|
||||
line: "PAPERLESS_TIKA_ENDPOINT={{ paperlessng_tika_endpoint }}"
|
||||
- regexp: PAPERLESS_TIKA_GOTENBERG_ENDPOINT
|
||||
line: "PAPERLESS_TIKA_GOTENBERG_ENDPOINT={{ paperlessng_tika_endpoint }}"
|
||||
# Software tweaks
|
||||
- regexp: PAPERLESS_TIME_ZONE
|
||||
line: "PAPERLESS_TIME_ZONE={{ paperlessng_time_zone }}"
|
||||
- regexp: PAPERLESS_CONSUMER_POLLING
|
||||
line: "PAPERLESS_CONSUMER_POLLING={{ paperlessng_consumer_polling }}"
|
||||
- regexp: PAPERLESS_CONSUMER_DELETE_DUPLICATES
|
||||
line: "PAPERLESS_CONSUMER_DELETE_DUPLICATES={{ paperlessng_consumer_delete_duplicates }}"
|
||||
- regexp: PAPERLESS_CONSUMER_RECURSIVE
|
||||
line: "PAPERLESS_CONSUMER_RECURSIVE={{ paperlessng_consumer_recursive }}"
|
||||
- regexp: PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS
|
||||
line: "PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS={{ paperlessng_consumer_subdirs_as_tags }}"
|
||||
- regexp: PAPERLESS_OPTIMIZE_THUMBNAILS
|
||||
line: "PAPERLESS_OPTIMIZE_THUMBNAILS={{ paperlessng_optimize_thumbnails }}"
|
||||
- regexp: PAPERLESS_POST_CONSUME_SCRIPT
|
||||
line: "PAPERLESS_POST_CONSUME_SCRIPT={{ paperlessng_post_consume_script }}"
|
||||
- regexp: PAPERLESS_FILENAME_DATE_ORDER
|
||||
line: "PAPERLESS_FILENAME_DATE_ORDER={{ paperlessng_filename_date_order }}"
|
||||
- regexp: PAPERLESS_THUMBNAIL_FONT_NAME
|
||||
line: "PAPERLESS_THUMBNAIL_FONT_NAME={{ paperlessng_thumbnail_font_name }}"
|
||||
- regexp: PAPERLESS_IGNORE_DATES
|
||||
line: "PAPERLESS_IGNORE_DATES={{ paperlessng_ignore_dates }}"
|
||||
no_log: yes
|
||||
|
||||
- name: configure paperless-ng database [sqlite]
|
||||
lineinfile:
|
||||
path: "{{ paperlessng_directory }}/paperless.conf.template"
|
||||
regexp: "^#?PAPERLESS_DBHOST=(.*)$"
|
||||
line: '#PAPERLESS_DBHOST=\1'
|
||||
backrefs: yes
|
||||
when: paperlessng_db_type == 'sqlite'
|
||||
|
||||
- name: configure paperless-ng database [postgresql]
|
||||
lineinfile:
|
||||
path: "{{ paperlessng_directory }}/paperless.conf.template"
|
||||
regexp: "^#?{{ item.regexp }}="
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
- regexp: PAPERLESS_DBHOST
|
||||
line: "PAPERLESS_DBHOST={{ paperlessng_db_host }}"
|
||||
- regexp: PAPERLESS_DBPORT
|
||||
line: "PAPERLESS_DBPORT={{ paperlessng_db_port }}"
|
||||
- regexp: PAPERLESS_DBNAME
|
||||
line: "PAPERLESS_DBNAME={{ paperlessng_db_name }}"
|
||||
- regexp: PAPERLESS_DBUSER
|
||||
line: "PAPERLESS_DBUSER={{ paperlessng_db_user }}"
|
||||
- regexp: PAPERLESS_DBPASS
|
||||
line: "PAPERLESS_DBPASS={{ paperlessng_db_pass }}"
|
||||
- regexp: PAPERLESS_DBSSLMODE
|
||||
line: "PAPERLESS_DBSSLMODE={{ paperlessng_db_sslmode }}"
|
||||
when: paperlessng_db_type == 'postgresql'
|
||||
no_log: yes
|
||||
|
||||
- name: deploy paperless-ng configuration
|
||||
copy:
|
||||
src: "{{ paperlessng_directory }}/paperless.conf.template"
|
||||
remote_src: yes
|
||||
dest: /etc/paperless.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
register: configuration
|
||||
|
||||
- name: create paperlessng venv
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
command:
|
||||
cmd: "python3 -m virtualenv {{ paperlessng_virtualenv }} -p /usr/bin/python3"
|
||||
creates: "{{ paperlessng_virtualenv }}"
|
||||
register: venv
|
||||
|
||||
- block:
|
||||
- name: install paperlessng requirements
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
pip:
|
||||
requirements: "{{ paperlessng_directory }}/requirements.txt"
|
||||
executable: "{{ paperlessng_virtualenv }}/bin/pip3"
|
||||
extra_args: --upgrade
|
||||
- name: migrate database schema
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py migrate"
|
||||
register: database_schema
|
||||
changed_when: '"No migrations to apply." not in database_schema.stdout'
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: configure paperless superuser
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
# "manage.py createsuperuser" only works on interactive TTYs
|
||||
vars:
|
||||
creation_script: |
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.hashers import get_hasher
|
||||
|
||||
if User.objects.filter(username='{{ paperlessng_superuser_name }}').exists():
|
||||
user = User.objects.get(username='{{ paperlessng_superuser_name }}')
|
||||
old = user.__dict__.copy()
|
||||
|
||||
user.is_superuser = True
|
||||
user.email = '{{ paperlessng_superuser_email }}'
|
||||
user.set_password('{{ paperlessng_superuser_password }}')
|
||||
user.save()
|
||||
new = user.__dict__
|
||||
|
||||
algorithm, iterations, old_salt, old_hash = old['password'].split('$')
|
||||
new_password_old_salt = get_hasher(algorithm).encode(password='{{ paperlessng_superuser_password }}', salt=old_salt, iterations=int(iterations))
|
||||
_, _, _, new_hash = new_password_old_salt.split('$')
|
||||
if not (old_hash == new_hash and old['is_superuser'] == new['is_superuser'] and old['email'] == new['email']):
|
||||
print('changed')
|
||||
else:
|
||||
User.objects.create_superuser('{{ paperlessng_superuser_name }}', '{{ paperlessng_superuser_email }}', '{{ paperlessng_superuser_password }}')
|
||||
print('changed')
|
||||
command: '{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py shell -c "{{ creation_script }}"'
|
||||
register: superuser
|
||||
changed_when: superuser.stdout == 'changed'
|
||||
no_log: yes
|
||||
|
||||
- name: set ownership and permissions on paperlessng venv
|
||||
file:
|
||||
path: "{{ paperlessng_virtualenv }}"
|
||||
state: directory
|
||||
recurse: yes
|
||||
owner: "{{ paperlessng_system_user }}"
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: g-w,o-rwx
|
||||
when: venv.changed or not reconfigure_only
|
||||
|
||||
- name: configure ghostscript for PDF
|
||||
lineinfile:
|
||||
path: /etc/ImageMagick-6/policy.xml
|
||||
regexp: '(\s+)<policy domain="coder" rights=".*" pattern="PDF" />'
|
||||
line: '\1<policy domain="coder" rights="read|write" pattern="PDF" />'
|
||||
backrefs: yes
|
||||
|
||||
- name: configure gunicorn web server
|
||||
lineinfile:
|
||||
path: "{{ paperlessng_directory }}/gunicorn.conf.py"
|
||||
regexp: '^bind = '
|
||||
line: "bind = '{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}'"
|
||||
|
||||
- name: configure systemd services
|
||||
ini_file:
|
||||
path: "{{ paperlessng_directory }}/scripts/{{ item[0] }}"
|
||||
section: "Service"
|
||||
option: "{{ item[1].option }}"
|
||||
value: "{{ item[1].value }}"
|
||||
with_nested:
|
||||
- [
|
||||
paperless-consumer.service,
|
||||
paperless-scheduler.service,
|
||||
paperless-webserver.service,
|
||||
]
|
||||
- [
|
||||
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html
|
||||
{ option: "User", value: "{{ paperlessng_system_user }}" },
|
||||
{ option: "Group", value: "{{ paperlessng_system_group }}" },
|
||||
{ option: "WorkingDirectory", value: "{{ paperlessng_directory }}/src" },
|
||||
{ option: "ProtectSystem", value: "full" },
|
||||
{ option: "NoNewPrivileges", value: "true" },
|
||||
{ option: "PrivateUsers", value: "true" },
|
||||
{ option: "PrivateDevices", value: "true" },
|
||||
]
|
||||
|
||||
- name: configure paperless-consumer service
|
||||
ini_file:
|
||||
path: "{{ paperlessng_directory }}/scripts/paperless-consumer.service"
|
||||
section: "Service"
|
||||
option: "ExecStart"
|
||||
value: "{{ paperlessng_virtualenv }}/bin/python3 manage.py document_consumer"
|
||||
|
||||
- name: configure paperless-scheduler service
|
||||
ini_file:
|
||||
path: "{{ paperlessng_directory }}/scripts/paperless-scheduler.service"
|
||||
section: "Service"
|
||||
option: "ExecStart"
|
||||
value: "{{ paperlessng_virtualenv }}/bin/python3 manage.py qcluster"
|
||||
|
||||
- name: configure paperless-webserver service
|
||||
ini_file:
|
||||
path: "{{ paperlessng_directory }}/scripts/paperless-webserver.service"
|
||||
section: "Service"
|
||||
option: "ExecStart"
|
||||
value: "{{ paperlessng_virtualenv }}/bin/gunicorn -c {{ paperlessng_directory }}/gunicorn.conf.py paperless.asgi:application"
|
||||
|
||||
- name: copy systemd services
|
||||
copy:
|
||||
src: "{{ paperlessng_directory }}/scripts/{{ item }}"
|
||||
remote_src: yes
|
||||
dest: "/etc/systemd/system/{{ item }}"
|
||||
with_items:
|
||||
- paperless-consumer.service
|
||||
- paperless-scheduler.service
|
||||
- paperless-webserver.service
|
||||
register: paperless_services
|
||||
|
||||
- name: reload systemd daemon
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
state: restarted
|
||||
daemon_reload: yes
|
||||
with_items:
|
||||
- paperless-consumer
|
||||
- paperless-scheduler
|
||||
- paperless-webserver
|
||||
when: paperless_services.changed or configuration.changed
|
||||
|
||||
- name: enable paperlessng services
|
||||
systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: yes
|
||||
masked: no
|
||||
state: started
|
||||
with_items:
|
||||
- paperless-consumer
|
||||
- paperless-scheduler
|
||||
- paperless-webserver
|
42
build-docker-image.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Helper script for building the Docker image locally.
|
||||
# Parses and provides the nessecary versions of other images to Docker
|
||||
# before passing in the rest of script args.
|
||||
|
||||
# First Argument: The Dockerfile to build
|
||||
# Other Arguments: Additional arguments to docker build
|
||||
|
||||
# Example Usage:
|
||||
# ./build-docker-image.sh Dockerfile -t paperless-ngx:my-awesome-feature
|
||||
|
||||
set -eux
|
||||
|
||||
if ! command -v jq; then
|
||||
echo "jq required"
|
||||
exit 1
|
||||
elif [ ! -f "$1" ]; then
|
||||
echo "$1 is not a file, please provide the Dockerfile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse what we can from Pipfile.lock
|
||||
pikepdf_version=$(jq ".default.pikepdf.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
|
||||
psycopg2_version=$(jq ".default.psycopg2.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
|
||||
# Read this from the other config file
|
||||
qpdf_version=$(jq ".qpdf.version" .build-config.json | sed 's/"//g')
|
||||
jbig2enc_version=$(jq ".jbig2enc.version" .build-config.json | sed 's/"//g')
|
||||
# Get the branch name (used for caching)
|
||||
branch_name=$(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
# https://docs.docker.com/develop/develop-images/build_enhancements/
|
||||
# Required to use cache-from
|
||||
export DOCKER_BUILDKIT=1
|
||||
|
||||
docker build --file "$1" \
|
||||
--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/app:"${branch_name}" \
|
||||
--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/app:dev \
|
||||
--build-arg JBIG2ENC_VERSION="${jbig2enc_version}" \
|
||||
--build-arg QPDF_VERSION="${qpdf_version}" \
|
||||
--build-arg PIKEPDF_VERSION="${pikepdf_version}" \
|
||||
--build-arg PSYCOPG2_VERSION="${psycopg2_version}" "${@:2}" .
|
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd src-ui
|
||||
npm install
|
||||
./node_modules/.bin/ng build --prod
|
@@ -1,5 +1,6 @@
|
||||
commit_message: '[ci skip]'
|
||||
files:
|
||||
- source: /src/locale/en-us/LC_MESSAGES/django.po
|
||||
translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po
|
||||
- source: /src/locale/en_US/LC_MESSAGES/django.po
|
||||
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
|
||||
- source: /src-ui/messages.xlf
|
||||
translation: /src-ui/src/locale/messages.%two_letters_code%.xlf
|
||||
translation: /src-ui/src/locale/messages.%locale_with_underscore%.xlf
|
||||
|
14
docker-builders/Dockerfile.frontend
Normal file
@@ -0,0 +1,14 @@
|
||||
# This Dockerfile compiles the frontend
|
||||
# Inputs: None
|
||||
|
||||
FROM node:16-bullseye-slim AS compile-frontend
|
||||
|
||||
COPY ./src /src/src
|
||||
COPY ./src-ui /src/src-ui
|
||||
|
||||
WORKDIR /src/src-ui
|
||||
RUN set -eux \
|
||||
&& npm update npm -g \
|
||||
&& npm ci --no-optional
|
||||
RUN set -eux \
|
||||
&& ./node_modules/.bin/ng build --configuration production
|
39
docker-builders/Dockerfile.jbig2enc
Normal file
@@ -0,0 +1,39 @@
|
||||
# This Dockerfile compiles the jbig2enc library
|
||||
# Inputs:
|
||||
# - JBIG2ENC_VERSION - the Git tag to checkout and build
|
||||
|
||||
FROM debian:bullseye-slim as main
|
||||
|
||||
LABEL org.opencontainers.image.description="A intermediate image with jbig2enc built"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG BUILD_PACKAGES="\
|
||||
build-essential \
|
||||
automake \
|
||||
libtool \
|
||||
libleptonica-dev \
|
||||
zlib1g-dev \
|
||||
git \
|
||||
ca-certificates"
|
||||
|
||||
WORKDIR /usr/src/jbig2enc
|
||||
|
||||
# As this is an base image for a multi-stage final image
|
||||
# the added size of the install is basically irrelevant
|
||||
RUN apt-get update --quiet \
|
||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Layers after this point change according to required version
|
||||
# For better caching, seperate the basic installs from
|
||||
# the building
|
||||
|
||||
ARG JBIG2ENC_VERSION
|
||||
|
||||
RUN set -eux \
|
||||
&& git clone --quiet --branch $JBIG2ENC_VERSION https://github.com/agl/jbig2enc .
|
||||
RUN set -eux \
|
||||
&& ./autogen.sh
|
||||
RUN set -eux \
|
||||
&& ./configure && make
|
92
docker-builders/Dockerfile.pikepdf
Normal file
@@ -0,0 +1,92 @@
|
||||
# This Dockerfile builds the pikepdf wheel
|
||||
# Inputs:
|
||||
# - REPO - Docker repository to pull qpdf from
|
||||
# - QPDF_VERSION - The image qpdf version to copy .deb files from
|
||||
# - PIKEPDF_GIT_TAG - The Git tag to clone and build from
|
||||
# - PIKEPDF_VERSION - Used to force the built pikepdf version to match
|
||||
|
||||
# Default to pulling from the main repo registry when manually building
|
||||
ARG REPO="paperless-ngx/paperless-ngx"
|
||||
|
||||
ARG QPDF_VERSION
|
||||
FROM ghcr.io/${REPO}/builder/qpdf:${QPDF_VERSION} as qpdf-builder
|
||||
|
||||
# This does nothing, except provide a name for a copy below
|
||||
|
||||
FROM python:3.9-slim-bullseye as main
|
||||
|
||||
LABEL org.opencontainers.image.description="A intermediate image with pikepdf wheel built"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG BUILD_PACKAGES="\
|
||||
build-essential \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
git \
|
||||
# qpdf requirement - https://github.com/qpdf/qpdf#crypto-providers
|
||||
libgnutls28-dev \
|
||||
# lxml requrements - https://lxml.de/installation.html
|
||||
libxml2-dev \
|
||||
libxslt1-dev \
|
||||
# Pillow requirements - https://pillow.readthedocs.io/en/stable/installation.html#external-libraries
|
||||
# JPEG functionality
|
||||
libjpeg62-turbo-dev \
|
||||
# conpressed PNG
|
||||
zlib1g-dev \
|
||||
# compressed TIFF
|
||||
libtiff-dev \
|
||||
# type related services
|
||||
libfreetype-dev \
|
||||
# color management
|
||||
liblcms2-dev \
|
||||
# WebP format
|
||||
libwebp-dev \
|
||||
# JPEG 2000
|
||||
libopenjp2-7-dev \
|
||||
# improved color quantization
|
||||
libimagequant-dev \
|
||||
# complex text layout support
|
||||
libraqm-dev"
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
COPY --from=qpdf-builder /usr/src/qpdf/*.deb ./
|
||||
|
||||
# As this is an base image for a multi-stage final image
|
||||
# the added size of the install is basically irrelevant
|
||||
|
||||
RUN set -eux \
|
||||
&& apt-get update --quiet \
|
||||
&& apt-get install --yes --quiet --no-install-recommends $BUILD_PACKAGES \
|
||||
&& dpkg --install libqpdf28_*.deb \
|
||||
&& dpkg --install libqpdf-dev_*.deb \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade \
|
||||
pip \
|
||||
wheel \
|
||||
# https://pikepdf.readthedocs.io/en/latest/installation.html#requirements
|
||||
pybind11 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Layers after this point change according to required version
|
||||
# For better caching, seperate the basic installs from
|
||||
# the building
|
||||
|
||||
ARG PIKEPDF_GIT_TAG
|
||||
ARG PIKEPDF_VERSION
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "building pikepdf wheel" \
|
||||
# Note the v in the tag name here
|
||||
&& git clone --quiet --depth 1 --branch "${PIKEPDF_GIT_TAG}" https://github.com/pikepdf/pikepdf.git \
|
||||
&& cd pikepdf \
|
||||
# pikepdf seems to specifciy either a next version when built OR
|
||||
# a post release tag.
|
||||
# In either case, this won't match what we want from requirements.txt
|
||||
# Directly modify the setup.py to set the version we just checked out of Git
|
||||
&& sed -i "s/use_scm_version=True/version=\"${PIKEPDF_VERSION}\"/g" setup.py \
|
||||
# https://github.com/pikepdf/pikepdf/issues/323
|
||||
&& rm pyproject.toml \
|
||||
&& mkdir wheels \
|
||||
&& python3 -m pip wheel . --wheel-dir wheels \
|
||||
&& ls -ahl wheels
|
45
docker-builders/Dockerfile.psycopg2
Normal file
@@ -0,0 +1,45 @@
|
||||
# This Dockerfile builds the psycopg2 wheel
|
||||
# Inputs:
|
||||
# - PSYCOPG2_GIT_TAG - The Git tag to clone and build from
|
||||
# - PSYCOPG2_VERSION - Unused, kept for future possible usage
|
||||
|
||||
FROM python:3.9-slim-bullseye as main
|
||||
|
||||
LABEL org.opencontainers.image.description="A intermediate image with psycopg2 wheel built"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG BUILD_PACKAGES="\
|
||||
build-essential \
|
||||
git \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
# https://www.psycopg.org/docs/install.html#prerequisites
|
||||
libpq-dev"
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
# As this is an base image for a multi-stage final image
|
||||
# the added size of the install is basically irrelevant
|
||||
|
||||
RUN set -eux \
|
||||
&& apt-get update --quiet \
|
||||
&& apt-get install --yes --quiet --no-install-recommends $BUILD_PACKAGES \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade pip wheel
|
||||
|
||||
# Layers after this point change according to required version
|
||||
# For better caching, seperate the basic installs from
|
||||
# the building
|
||||
|
||||
ARG PSYCOPG2_GIT_TAG
|
||||
ARG PSYCOPG2_VERSION
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Building psycopg2 wheel" \
|
||||
&& cd /usr/src \
|
||||
&& git clone --quiet --depth 1 --branch ${PSYCOPG2_GIT_TAG} https://github.com/psycopg/psycopg2.git \
|
||||
&& cd psycopg2 \
|
||||
&& mkdir wheels \
|
||||
&& python3 -m pip wheel . --wheel-dir wheels \
|
||||
&& ls -ahl wheels/
|
53
docker-builders/Dockerfile.qpdf
Normal file
@@ -0,0 +1,53 @@
|
||||
FROM debian:bullseye-slim as main
|
||||
|
||||
LABEL org.opencontainers.image.description="A intermediate image with qpdf built"
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
ARG BUILD_PACKAGES="\
|
||||
build-essential \
|
||||
debhelper \
|
||||
debian-keyring \
|
||||
devscripts \
|
||||
equivs \
|
||||
libtool \
|
||||
# https://qpdf.readthedocs.io/en/stable/installation.html#system-requirements
|
||||
libjpeg62-turbo-dev \
|
||||
libgnutls28-dev \
|
||||
packaging-dev \
|
||||
zlib1g-dev"
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
# As this is an base image for a multi-stage final image
|
||||
# the added size of the install is basically irrelevant
|
||||
|
||||
RUN set -eux \
|
||||
&& apt-get update --quiet \
|
||||
&& apt-get install --yes --quiet --no-install-recommends $BUILD_PACKAGES \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Layers after this point change according to required version
|
||||
# For better caching, seperate the basic installs from
|
||||
# the building
|
||||
|
||||
# This must match to pikepdf's minimum at least
|
||||
ARG QPDF_VERSION
|
||||
|
||||
# In order to get the required version of qpdf, it is backported from bookwork
|
||||
# and then built from source
|
||||
RUN set -eux \
|
||||
&& echo "Building qpdf" \
|
||||
&& echo "deb-src http://deb.debian.org/debian/ bookworm main" > /etc/apt/sources.list.d/bookworm-src.list \
|
||||
&& apt-get update \
|
||||
&& mkdir qpdf \
|
||||
&& cd qpdf \
|
||||
&& apt-get source --yes --quiet qpdf=${QPDF_VERSION}-1/bookworm \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& cd qpdf-$QPDF_VERSION \
|
||||
# We don't need to build the tests (also don't run them)
|
||||
&& rm -rf libtests \
|
||||
&& DEBEMAIL=hello@paperless-ngx.com debchange --bpo \
|
||||
&& export DEB_BUILD_OPTIONS="terse nocheck nodoc parallel=2" \
|
||||
&& dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes \
|
||||
&& ls -ahl ../*.deb
|
@@ -1 +1 @@
|
||||
COMPOSE_PROJECT_NAME=paperless
|
||||
COMPOSE_PROJECT_NAME=paperless
|
||||
|
@@ -22,6 +22,10 @@
|
||||
# Docker setup does not use the configuration file.
|
||||
# A few commonly adjusted settings are provided below.
|
||||
|
||||
# This is required if you will be exposing Paperless-ngx on a public domain
|
||||
# (if doing so please consider security measures such as reverse proxy)
|
||||
#PAPERLESS_URL=https://paperless.example.com
|
||||
|
||||
# Adjust this key if you plan to make paperless available publicly. It should
|
||||
# be a very long sequence of random characters. You don't need to remember it.
|
||||
#PAPERLESS_SECRET_KEY=change-me
|
||||
|
97
docker/compose/docker-compose.portainer.yml
Normal file
@@ -0,0 +1,97 @@
|
||||
# docker-compose file for running paperless from the Docker Hub.
|
||||
# This file contains everything paperless needs to run.
|
||||
# Paperless supports amd64, arm and arm64 hardware.
|
||||
#
|
||||
# All compose files of paperless configure paperless in the following way:
|
||||
#
|
||||
# - Paperless is (re)started on system boot, if it was running before shutdown.
|
||||
# - Docker volumes for storing data are managed by Docker.
|
||||
# - Folders for importing and exporting files are created in the same directory
|
||||
# as this file and mounted to the correct folders inside the container.
|
||||
# - Paperless listens on port 8010.
|
||||
#
|
||||
# In addition to that, this docker-compose file adds the following optional
|
||||
# configurations:
|
||||
#
|
||||
# - Instead of SQLite (default), PostgreSQL is used as the database server.
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
# - Open portainer Stacks list and click 'Add stack'
|
||||
# - Paste the contents of this file and assign a name, e.g. 'Paperless'
|
||||
# - Click 'Deploy the stack' and wait for it to be deployed
|
||||
# - Open the list of containers, select paperless_webserver_1
|
||||
# - Click 'Console' and then 'Connect' to open the command line inside the container
|
||||
# - Run 'python3 manage.py createsuperuser' to create a user
|
||||
# - Exit the console
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
version: "3.4"
|
||||
services:
|
||||
broker:
|
||||
image: redis:6.0
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
db:
|
||||
image: postgres:13
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: paperless
|
||||
POSTGRES_USER: paperless
|
||||
POSTGRES_PASSWORD: paperless
|
||||
|
||||
webserver:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
- broker
|
||||
ports:
|
||||
- 8010:8000
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
volumes:
|
||||
- data:/usr/src/paperless/data
|
||||
- media:/usr/src/paperless/media
|
||||
- ./export:/usr/src/paperless/export
|
||||
- ./consume:/usr/src/paperless/consume
|
||||
environment:
|
||||
PAPERLESS_REDIS: redis://broker:6379
|
||||
PAPERLESS_DBHOST: db
|
||||
# The UID and GID of the user used to run paperless in the container. Set this
|
||||
# to your UID and GID on the host so that you have write access to the
|
||||
# consumption directory.
|
||||
USERMAP_UID: 1000
|
||||
USERMAP_GID: 100
|
||||
# Additional languages to install for text recognition, separated by a
|
||||
# whitespace. Note that this is
|
||||
# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
|
||||
# language used for OCR.
|
||||
# The container installs English, German, Italian, Spanish and French by
|
||||
# default.
|
||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
|
||||
# for available languages.
|
||||
#PAPERLESS_OCR_LANGUAGES: tur ces
|
||||
# Adjust this key if you plan to make paperless available publicly. It should
|
||||
# be a very long sequence of random characters. You don't need to remember it.
|
||||
#PAPERLESS_SECRET_KEY: change-me
|
||||
# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC.
|
||||
#PAPERLESS_TIME_ZONE: America/Los_Angeles
|
||||
# The default language to use for OCR. Set this to the language most of your
|
||||
# documents are written in.
|
||||
#PAPERLESS_OCR_LANGUAGE: eng
|
||||
|
||||
volumes:
|
||||
data:
|
||||
media:
|
||||
pgdata:
|
||||
redisdata:
|
@@ -1,4 +1,4 @@
|
||||
# docker-compose file for running paperless from the Docker Hub.
|
||||
# docker-compose file for running paperless from the docker container registry.
|
||||
# This file contains everything paperless needs to run.
|
||||
# Paperless supports amd64, arm and arm64 hardware.
|
||||
#
|
||||
@@ -35,6 +35,8 @@ services:
|
||||
broker:
|
||||
image: redis:6.0
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
db:
|
||||
image: postgres:13
|
||||
@@ -47,7 +49,7 @@ services:
|
||||
POSTGRES_PASSWORD: paperless
|
||||
|
||||
webserver:
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -57,7 +59,7 @@ services:
|
||||
ports:
|
||||
- 8000:8000
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
@@ -75,16 +77,18 @@ services:
|
||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||
|
||||
gotenberg:
|
||||
image: thecodingmachine/gotenberg
|
||||
image: gotenberg/gotenberg:7.4
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DISABLE_GOOGLE_CHROME: 1
|
||||
command:
|
||||
- "gotenberg"
|
||||
- "--chromium-disable-routes=true"
|
||||
|
||||
tika:
|
||||
image: apache/tika
|
||||
image: ghcr.io/paperless-ngx/tika:latest
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
data:
|
||||
media:
|
||||
pgdata:
|
||||
redisdata:
|
||||
|
@@ -31,6 +31,8 @@ services:
|
||||
broker:
|
||||
image: redis:6.0
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
db:
|
||||
image: postgres:13
|
||||
@@ -43,7 +45,7 @@ services:
|
||||
POSTGRES_PASSWORD: paperless
|
||||
|
||||
webserver:
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
@@ -51,7 +53,7 @@ services:
|
||||
ports:
|
||||
- 8000:8000
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
@@ -70,3 +72,4 @@ volumes:
|
||||
data:
|
||||
media:
|
||||
pgdata:
|
||||
redisdata:
|
||||
|
@@ -1,7 +1,6 @@
|
||||
# docker-compose file for running paperless from the Docker Hub.
|
||||
# docker-compose file for running paperless from the docker container registry.
|
||||
# This file contains everything paperless needs to run.
|
||||
# Paperless supports amd64, arm and arm64 hardware.
|
||||
#
|
||||
# All compose files of paperless configure paperless in the following way:
|
||||
#
|
||||
# - Paperless is (re)started on system boot, if it was running before shutdown.
|
||||
@@ -36,9 +35,11 @@ services:
|
||||
broker:
|
||||
image: redis:6.0
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
webserver:
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- broker
|
||||
@@ -47,7 +48,7 @@ services:
|
||||
ports:
|
||||
- 8000:8000
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
@@ -64,15 +65,17 @@ services:
|
||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||
|
||||
gotenberg:
|
||||
image: thecodingmachine/gotenberg
|
||||
image: gotenberg/gotenberg:7.4
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DISABLE_GOOGLE_CHROME: 1
|
||||
command:
|
||||
- "gotenberg"
|
||||
- "--chromium-disable-routes=true"
|
||||
|
||||
tika:
|
||||
image: apache/tika
|
||||
image: ghcr.io/paperless-ngx/tika:latest
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
data:
|
||||
media:
|
||||
redisdata:
|
||||
|
@@ -28,16 +28,18 @@ services:
|
||||
broker:
|
||||
image: redis:6.0
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
webserver:
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- broker
|
||||
ports:
|
||||
- 8000:8000
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000"]
|
||||
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
@@ -54,3 +56,4 @@ services:
|
||||
volumes:
|
||||
data:
|
||||
media:
|
||||
redisdata:
|
||||
|
133
docker/docker-entrypoint.sh
Normal file → Executable file
@@ -1,92 +1,40 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Source: https://github.com/sameersbn/docker-gitlab/
|
||||
map_uidgid() {
|
||||
USERMAP_ORIG_UID=$(id -u paperless)
|
||||
USERMAP_ORIG_GID=$(id -g paperless)
|
||||
USERMAP_NEW_UID=${USERMAP_UID:-$USERMAP_ORIG_UID}
|
||||
USERMAP_NEW_GID=${USERMAP_GID:-${USERMAP_ORIG_GID:-$USERMAP_NEW_UID}}
|
||||
if [[ ${USERMAP_NEW_UID} != "${USERMAP_ORIG_UID}" || ${USERMAP_NEW_GID} != "${USERMAP_ORIG_GID}" ]]; then
|
||||
echo "Mapping UID and GID for paperless:paperless to $USERMAP_NEW_UID:$USERMAP_NEW_GID"
|
||||
usermod -u "${USERMAP_NEW_UID}" paperless
|
||||
groupmod -o -g "${USERMAP_NEW_GID}" paperless
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
wait_for_postgres() {
|
||||
attempt_num=1
|
||||
max_attempts=5
|
||||
|
||||
echo "Waiting for PostgreSQL to start..."
|
||||
|
||||
host="${PAPERLESS_DBHOST}"
|
||||
port="${PAPERLESS_DBPORT}"
|
||||
|
||||
if [[ -z $port ]] ;
|
||||
then
|
||||
port="5432"
|
||||
USERMAP_ORIG_UID=$(id -u paperless)
|
||||
USERMAP_ORIG_GID=$(id -g paperless)
|
||||
USERMAP_NEW_UID=${USERMAP_UID:-$USERMAP_ORIG_UID}
|
||||
USERMAP_NEW_GID=${USERMAP_GID:-${USERMAP_ORIG_GID:-$USERMAP_NEW_UID}}
|
||||
if [[ ${USERMAP_NEW_UID} != "${USERMAP_ORIG_UID}" || ${USERMAP_NEW_GID} != "${USERMAP_ORIG_GID}" ]]; then
|
||||
echo "Mapping UID and GID for paperless:paperless to $USERMAP_NEW_UID:$USERMAP_NEW_GID"
|
||||
usermod -o -u "${USERMAP_NEW_UID}" paperless
|
||||
groupmod -o -g "${USERMAP_NEW_GID}" paperless
|
||||
fi
|
||||
|
||||
while !</dev/tcp/$host/$port ;
|
||||
do
|
||||
|
||||
if [ $attempt_num -eq $max_attempts ]
|
||||
then
|
||||
echo "Unable to connect to database."
|
||||
exit 1
|
||||
else
|
||||
echo "Attempt $attempt_num failed! Trying again in 5 seconds..."
|
||||
|
||||
fi
|
||||
|
||||
attempt_num=$(expr "$attempt_num" + 1)
|
||||
sleep 5
|
||||
done
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
migrations() {
|
||||
|
||||
if [[ -n "${PAPERLESS_DBHOST}" ]]
|
||||
then
|
||||
wait_for_postgres
|
||||
fi
|
||||
|
||||
(
|
||||
# flock is in place to prevent multiple containers from doing migrations
|
||||
# simultaneously. This also ensures that the db is ready when the command
|
||||
# of the current container starts.
|
||||
flock 200
|
||||
echo "Apply database migrations..."
|
||||
sudo -HEu paperless python3 manage.py migrate
|
||||
) 200>/usr/src/paperless/data/migration_lock
|
||||
|
||||
}
|
||||
|
||||
initialize() {
|
||||
map_uidgid
|
||||
|
||||
for dir in export data data/index media media/documents media/documents/originals media/documents/thumbnails; do
|
||||
if [[ ! -d "../$dir" ]]
|
||||
then
|
||||
echo "creating directory ../$dir"
|
||||
if [[ ! -d "../$dir" ]]; then
|
||||
echo "Creating directory ../$dir"
|
||||
mkdir ../$dir
|
||||
fi
|
||||
done
|
||||
|
||||
echo "creating directory /tmp/paperless"
|
||||
echo "Creating directory /tmp/paperless"
|
||||
mkdir -p /tmp/paperless
|
||||
|
||||
chown -R paperless:paperless ../
|
||||
set +e
|
||||
echo "Adjusting permissions of paperless files. This may take a while."
|
||||
chown -R paperless:paperless /tmp/paperless
|
||||
find .. -not \( -user paperless -and -group paperless \) -exec chown paperless:paperless {} +
|
||||
set -e
|
||||
|
||||
migrations
|
||||
|
||||
gosu paperless /sbin/docker-prepare.sh
|
||||
}
|
||||
|
||||
install_languages() {
|
||||
@@ -102,44 +50,43 @@ install_languages() {
|
||||
apt-get update
|
||||
|
||||
for lang in "${langs[@]}"; do
|
||||
pkg="tesseract-ocr-$lang"
|
||||
# English is installed by default
|
||||
#if [[ "$lang" == "eng" ]]; then
|
||||
# continue
|
||||
#fi
|
||||
pkg="tesseract-ocr-$lang"
|
||||
# English is installed by default
|
||||
#if [[ "$lang" == "eng" ]]; then
|
||||
# continue
|
||||
#fi
|
||||
|
||||
if dpkg -s $pkg &> /dev/null; then
|
||||
echo "package $pkg already installed!"
|
||||
continue
|
||||
fi
|
||||
if dpkg -s "$pkg" &>/dev/null; then
|
||||
echo "Package $pkg already installed!"
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! apt-cache show $pkg &> /dev/null; then
|
||||
echo "package $pkg not found! :("
|
||||
continue
|
||||
fi
|
||||
if ! apt-cache show "$pkg" &>/dev/null; then
|
||||
echo "Package $pkg not found! :("
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Installing package $pkg..."
|
||||
if ! apt-get -y install "$pkg" &> /dev/null; then
|
||||
echo "Could not install $pkg"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "Installing package $pkg..."
|
||||
if ! apt-get -y install "$pkg" &>/dev/null; then
|
||||
echo "Could not install $pkg"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
echo "Paperless-ng docker container starting..."
|
||||
echo "Paperless-ngx docker container starting..."
|
||||
|
||||
# Install additional languages if specified
|
||||
if [[ ! -z "$PAPERLESS_OCR_LANGUAGES" ]]; then
|
||||
install_languages "$PAPERLESS_OCR_LANGUAGES"
|
||||
if [[ -n "$PAPERLESS_OCR_LANGUAGES" ]]; then
|
||||
install_languages "$PAPERLESS_OCR_LANGUAGES"
|
||||
fi
|
||||
|
||||
initialize
|
||||
|
||||
if [[ "$1" != "/"* ]]; then
|
||||
echo Executing management command "$@"
|
||||
exec sudo -HEu paperless python3 manage.py "$@"
|
||||
exec gosu paperless python3 manage.py "$@"
|
||||
else
|
||||
echo Executing "$@"
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
|
81
docker/docker-prepare.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
wait_for_postgres() {
|
||||
attempt_num=1
|
||||
max_attempts=5
|
||||
|
||||
echo "Waiting for PostgreSQL to start..."
|
||||
|
||||
host="${PAPERLESS_DBHOST:=localhost}"
|
||||
port="${PAPERLESS_DBPORT:=5432}"
|
||||
|
||||
|
||||
while [ ! "$(pg_isready -h $host -p $port)" ]; do
|
||||
|
||||
if [ $attempt_num -eq $max_attempts ]; then
|
||||
echo "Unable to connect to database."
|
||||
exit 1
|
||||
else
|
||||
echo "Attempt $attempt_num failed! Trying again in 5 seconds..."
|
||||
|
||||
fi
|
||||
|
||||
attempt_num=$(("$attempt_num" + 1))
|
||||
sleep 5
|
||||
done
|
||||
}
|
||||
|
||||
wait_for_redis() {
|
||||
# We use a Python script to send the Redis ping
|
||||
# instead of installing redis-tools just for 1 thing
|
||||
if ! python3 /sbin/wait-for-redis.py; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
migrations() {
|
||||
(
|
||||
# flock is in place to prevent multiple containers from doing migrations
|
||||
# simultaneously. This also ensures that the db is ready when the command
|
||||
# of the current container starts.
|
||||
flock 200
|
||||
echo "Apply database migrations..."
|
||||
python3 manage.py migrate
|
||||
) 200>/usr/src/paperless/data/migration_lock
|
||||
}
|
||||
|
||||
search_index() {
|
||||
index_version=1
|
||||
index_version_file=/usr/src/paperless/data/.index_version
|
||||
|
||||
if [[ (! -f "$index_version_file") || $(<$index_version_file) != "$index_version" ]]; then
|
||||
echo "Search index out of date. Updating..."
|
||||
python3 manage.py document_index reindex
|
||||
echo $index_version | tee $index_version_file >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
superuser() {
|
||||
if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then
|
||||
python3 manage.py manage_superuser
|
||||
fi
|
||||
}
|
||||
|
||||
do_work() {
|
||||
if [[ -n "${PAPERLESS_DBHOST}" ]]; then
|
||||
wait_for_postgres
|
||||
fi
|
||||
|
||||
wait_for_redis
|
||||
|
||||
migrations
|
||||
|
||||
search_index
|
||||
|
||||
superuser
|
||||
|
||||
}
|
||||
|
||||
do_work
|
10
docker/install_management_commands.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
for command in document_archiver document_exporter document_importer mail_fetcher document_create_classifier document_index document_renamer document_retagger document_thumbnails document_sanity_checker manage_superuser;
|
||||
do
|
||||
echo "installing $command..."
|
||||
sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command
|
||||
chmod +x /usr/local/bin/$command
|
||||
done
|
15
docker/management_script.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
cd /usr/src/paperless/src/
|
||||
|
||||
if [[ $(id -u) == 0 ]] ;
|
||||
then
|
||||
gosu paperless python3 manage.py management_command "$@"
|
||||
elif [[ $(id -un) == "paperless" ]] ;
|
||||
then
|
||||
python3 manage.py management_command "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
fi
|
@@ -28,6 +28,7 @@ stderr_logfile_maxbytes=0
|
||||
[program:scheduler]
|
||||
command=python3 manage.py qcluster
|
||||
user=paperless
|
||||
stopasgroup = true
|
||||
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
|
42
docker/wait-for-redis.py
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple script which attempts to ping the Redis broker as set in the environment for
|
||||
a certain number of times, waiting a little bit in between
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Final
|
||||
|
||||
from redis import Redis
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
MAX_RETRY_COUNT: Final[int] = 5
|
||||
RETRY_SLEEP_SECONDS: Final[int] = 5
|
||||
|
||||
REDIS_URL: Final[str] = os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
|
||||
|
||||
print(f"Waiting for Redis: {REDIS_URL}", flush=True)
|
||||
|
||||
attempt = 0
|
||||
with Redis.from_url(url=REDIS_URL) as client:
|
||||
while attempt < MAX_RETRY_COUNT:
|
||||
try:
|
||||
client.ping()
|
||||
break
|
||||
except Exception:
|
||||
print(
|
||||
f"Redis ping #{attempt} failed, waiting {RETRY_SLEEP_SECONDS}s",
|
||||
flush=True,
|
||||
)
|
||||
time.sleep(RETRY_SLEEP_SECONDS)
|
||||
attempt += 1
|
||||
|
||||
if attempt >= MAX_RETRY_COUNT:
|
||||
print(f"Failed to connect to: {REDIS_URL}")
|
||||
sys.exit(os.EX_UNAVAILABLE)
|
||||
else:
|
||||
print(f"Connected to Redis broker: {REDIS_URL}")
|
||||
sys.exit(os.EX_OK)
|
@@ -1,11 +1,10 @@
|
||||
FROM python:3.5.1
|
||||
MAINTAINER Pit Kleyersburg <pitkley@googlemail.com>
|
||||
|
||||
# Install Sphinx and Pygments
|
||||
RUN pip install Sphinx Pygments
|
||||
RUN pip install --no-cache-dir Sphinx Pygments \
|
||||
# Setup directories, copy data
|
||||
&& mkdir /build
|
||||
|
||||
# Setup directories, copy data
|
||||
RUN mkdir /build
|
||||
COPY . /build
|
||||
WORKDIR /build/docs
|
||||
|
||||
|
592
docs/_static/css/custom.css
vendored
Normal file
@@ -0,0 +1,592 @@
|
||||
/* Variables */
|
||||
:root {
|
||||
--color-text-body: #5c5962;
|
||||
--color-text-body-light: #fcfcfc;
|
||||
--color-text-anchor: #7253ed;
|
||||
--color-text-alt: rgba(0, 0, 0, 0.3);
|
||||
--color-text-title: #27262b;
|
||||
--color-text-code-inline: #e74c3c;
|
||||
--color-text-code-nt: #062873;
|
||||
--color-text-selection: #b19eff;
|
||||
--color-bg-body: #fcfcfc;
|
||||
--color-bg-body-alt: #f3f6f6;
|
||||
--color-bg-side-nav: #f5f6fa;
|
||||
--color-bg-side-nav-hover: #ebedf5;
|
||||
--color-bg-code-block: var(--color-bg-side-nav);
|
||||
--color-border: #eeebee;
|
||||
--color-btn-neutral-bg: #f3f6f6;
|
||||
--color-btn-neutral-bg-hover: #e5ebeb;
|
||||
--color-success-title: #1abc9c;
|
||||
--color-success-body: #dbfaf4;
|
||||
--color-warning-title: #f0b37e;
|
||||
--color-warning-body: #ffedcc;
|
||||
--color-danger-title: #f29f97;
|
||||
--color-danger-body: #fdf3f2;
|
||||
--color-info-title: #6ab0de;
|
||||
--color-info-body: #e7f2fa;
|
||||
}
|
||||
|
||||
.dark-mode {
|
||||
--color-text-body: #abb2bf;
|
||||
--color-text-body-light: #9499a2;
|
||||
--color-text-alt: rgba(0255, 255, 255, 0.5);
|
||||
--color-text-title: var(--color-text-anchor);
|
||||
--color-text-code-inline: #abb2bf;
|
||||
--color-text-code-nt: #2063f3;
|
||||
--color-text-selection: #030303;
|
||||
--color-bg-body: #1d1d20 !important;
|
||||
--color-bg-body-alt: #131315;
|
||||
--color-bg-side-nav: #18181a;
|
||||
--color-bg-side-nav-hover: #101216;
|
||||
--color-bg-code-block: #101216;
|
||||
--color-border: #47494f;
|
||||
--color-btn-neutral-bg: #242529;
|
||||
--color-btn-neutral-bg-hover: #101216;
|
||||
--color-success-title: #02120f;
|
||||
--color-success-body: #041b17;
|
||||
--color-warning-title: #1b0e03;
|
||||
--color-warning-body: #371d06;
|
||||
--color-danger-title: #120902;
|
||||
--color-danger-body: #1b0503;
|
||||
--color-info-title: #020608;
|
||||
--color-info-body: #06141e;
|
||||
}
|
||||
|
||||
* {
|
||||
transition: background-color 0.3s ease, border-color 0.3s ease;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
body {
|
||||
font-family: system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
|
||||
font-size: inherit;
|
||||
line-height: 1.4;
|
||||
color: var(--color-text-body);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.rst-content .toctree-wrapper>p.caption, .rst-content h1, .rst-content h2, .rst-content h3, .rst-content h4, .rst-content h5, .rst-content h6 {
|
||||
padding-top: .5em;
|
||||
}
|
||||
|
||||
p, .main-content-wrap, .rst-content .section ul, .rst-content .toctree-wrapper ul, .rst-content section ul, .wy-plain-list-disc, article ul {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
pre, .code, .rst-content .linenodiv pre, .rst-content div[class^=highlight] pre, .rst-content pre.literal-block {
|
||||
font-family: "SFMono-Regular", Menlo,Consolas, Monospace;
|
||||
font-size: 0.75em;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4 {
|
||||
font-size: 1rem
|
||||
}
|
||||
|
||||
.rst-versions {
|
||||
font-family: inherit;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
footer, footer p {
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
footer .rst-footer-buttons {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
/* break code lines on mobile */
|
||||
pre, code {
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Layout */
|
||||
.wy-side-nav-search, .wy-menu-vertical {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.wy-nav-side {
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--color-bg-side-nav);
|
||||
}
|
||||
|
||||
.wy-side-scroll {
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 66.5rem) {
|
||||
.wy-side-scroll {
|
||||
width:264px
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 50rem) {
|
||||
.wy-nav-side {
|
||||
flex-wrap: nowrap;
|
||||
position: fixed;
|
||||
width: 248px;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid var(--color-border);
|
||||
align-items:flex-end
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 66.5rem) {
|
||||
.wy-nav-side {
|
||||
width: calc((100% - 1064px) / 2 + 264px);
|
||||
min-width:264px
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 50rem) {
|
||||
.wy-nav-content-wrap {
|
||||
position: relative;
|
||||
max-width: 800px;
|
||||
margin-left:248px
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 66.5rem) {
|
||||
.wy-nav-content-wrap {
|
||||
margin-left:calc((100% - 1064px) / 2 + 264px)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Colors */
|
||||
body.wy-body-for-nav,
|
||||
.wy-nav-content {
|
||||
background: var(--color-bg-body);
|
||||
}
|
||||
|
||||
.wy-nav-side {
|
||||
border-right: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.wy-side-nav-search, .wy-nav-top {
|
||||
background: var(--color-bg-side-nav);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.wy-nav-content-wrap {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.wy-side-nav-search > a, .wy-nav-top a, .wy-nav-top i {
|
||||
color: var(--color-text-title);
|
||||
}
|
||||
|
||||
.wy-side-nav-search > a:hover, .wy-nav-top a:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.wy-side-nav-search > div.version {
|
||||
color: var(--color-text-alt)
|
||||
}
|
||||
|
||||
.wy-side-nav-search > div[role="search"] {
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2.current>a, .wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,
|
||||
.wy-menu-vertical li.toctree-l3.current>a, .wy-menu-vertical li.toctree-l3.current li.toctree-l4>a {
|
||||
background: var(--color-bg-side-nav);
|
||||
}
|
||||
|
||||
.rst-content .highlighted {
|
||||
background: #eedd85;
|
||||
box-shadow: 0 0 0 2px #eedd85;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type=text],
|
||||
html.writer-html5 .rst-content table.docutils th {
|
||||
color: var(--color-text-body);
|
||||
}
|
||||
|
||||
.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,
|
||||
.wy-table-backed,
|
||||
.wy-table-odd td,
|
||||
.wy-table-striped tr:nth-child(2n-1) td {
|
||||
background-color: var(--color-bg-body-alt);
|
||||
}
|
||||
|
||||
.rst-content table.docutils,
|
||||
.wy-table-bordered-all,
|
||||
html.writer-html5 .rst-content table.docutils th,
|
||||
.rst-content table.docutils td,
|
||||
.wy-table-bordered-all td,
|
||||
hr {
|
||||
border-color: var(--color-border) !important;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-text-selection);
|
||||
}
|
||||
|
||||
/* Ridiculous rules are taken from sphinx_rtd */
|
||||
.rst-content .admonition-title,
|
||||
.wy-alert-title {
|
||||
color: var(--color-text-body-light);
|
||||
}
|
||||
|
||||
.rst-content .hint,
|
||||
.rst-content .important,
|
||||
.rst-content .tip,
|
||||
.rst-content .wy-alert-success,
|
||||
.wy-alert.wy-alert-success {
|
||||
background: var(--color-success-body);
|
||||
}
|
||||
|
||||
.rst-content .hint .admonition-title,
|
||||
.rst-content .hint .wy-alert-title,
|
||||
.rst-content .important .admonition-title,
|
||||
.rst-content .important .wy-alert-title,
|
||||
.rst-content .tip .admonition-title,
|
||||
.rst-content .tip .wy-alert-title,
|
||||
.rst-content .wy-alert-success .admonition-title,
|
||||
.rst-content .wy-alert-success .wy-alert-title,
|
||||
.wy-alert.wy-alert-success .rst-content .admonition-title,
|
||||
.wy-alert.wy-alert-success .wy-alert-title {
|
||||
background-color: var(--color-success-title);
|
||||
}
|
||||
|
||||
.rst-content .admonition-todo,
|
||||
.rst-content .attention,
|
||||
.rst-content .caution,
|
||||
.rst-content .warning,
|
||||
.rst-content .wy-alert-warning,
|
||||
.wy-alert.wy-alert-warning {
|
||||
background: var(--color-warning-body);
|
||||
}
|
||||
|
||||
.rst-content .admonition-todo .admonition-title,
|
||||
.rst-content .admonition-todo .wy-alert-title,
|
||||
.rst-content .attention .admonition-title,
|
||||
.rst-content .attention .wy-alert-title,
|
||||
.rst-content .caution .admonition-title,
|
||||
.rst-content .caution .wy-alert-title,
|
||||
.rst-content .warning .admonition-title,
|
||||
.rst-content .warning .wy-alert-title,
|
||||
.rst-content .wy-alert-warning .admonition-title,
|
||||
.rst-content .wy-alert-warning .wy-alert-title,
|
||||
.rst-content .wy-alert.wy-alert-warning .admonition-title,
|
||||
.wy-alert.wy-alert-warning .rst-content .admonition-title,
|
||||
.wy-alert.wy-alert-warning .wy-alert-title {
|
||||
background: var(--color-warning-title);
|
||||
}
|
||||
|
||||
.rst-content .danger,
|
||||
.rst-content .error,
|
||||
.rst-content .wy-alert-danger,
|
||||
.wy-alert.wy-alert-danger {
|
||||
background: var(--color-danger-body);
|
||||
}
|
||||
|
||||
.rst-content .danger .admonition-title,
|
||||
.rst-content .danger .wy-alert-title,
|
||||
.rst-content .error .admonition-title,
|
||||
.rst-content .error .wy-alert-title,
|
||||
.rst-content .wy-alert-danger .admonition-title,
|
||||
.rst-content .wy-alert-danger .wy-alert-title,
|
||||
.wy-alert.wy-alert-danger .rst-content .admonition-title,
|
||||
.wy-alert.wy-alert-danger .wy-alert-title {
|
||||
background: var(--color-danger-title);
|
||||
}
|
||||
|
||||
.rst-content .note,
|
||||
.rst-content .seealso,
|
||||
.rst-content .wy-alert-info,
|
||||
.wy-alert.wy-alert-info {
|
||||
background: var(--color-info-body);
|
||||
}
|
||||
|
||||
.rst-content .note .admonition-title,
|
||||
.rst-content .note .wy-alert-title,
|
||||
.rst-content .seealso .admonition-title,
|
||||
.rst-content .seealso .wy-alert-title,
|
||||
.rst-content .wy-alert-info .admonition-title,
|
||||
.rst-content .wy-alert-info .wy-alert-title,
|
||||
.wy-alert.wy-alert-info .rst-content .admonition-title,
|
||||
.wy-alert.wy-alert-info .wy-alert-title {
|
||||
background: var(--color-info-title);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Links */
|
||||
a, a:visited,
|
||||
.wy-menu-vertical a,
|
||||
a.icon.icon-home,
|
||||
.wy-menu-vertical li.toctree-l1.current > a.current {
|
||||
color: var(--color-text-anchor);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, .wy-breadcrumbs-aside a {
|
||||
color: var(--color-text-anchor); /* reset */
|
||||
}
|
||||
|
||||
.rst-versions a, .rst-versions .rst-current-version {
|
||||
color: #var(--color-text-anchor);
|
||||
}
|
||||
|
||||
.wy-nav-content a.reference, .wy-nav-content a:not([class]) {
|
||||
background-image: linear-gradient(var(--color-border) 0%, var(--color-border) 100%);
|
||||
background-repeat: repeat-x;
|
||||
background-position: 0 100%;
|
||||
background-size: 1px 1px;
|
||||
}
|
||||
|
||||
.wy-nav-content a.reference:hover, .wy-nav-content a:not([class]):hover {
|
||||
background-image: linear-gradient(rgba(114,83,237,0.45) 0%, rgba(114,83,237,0.45) 100%);
|
||||
background-size: 1px 1px;
|
||||
}
|
||||
|
||||
.wy-menu-vertical a:hover,
|
||||
.wy-menu-vertical li.current a:hover,
|
||||
.wy-menu-vertical a:active {
|
||||
background: var(--color-bg-side-nav-hover) !important;
|
||||
color: var(--color-text-body);
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l1.current>a,
|
||||
.wy-menu-vertical li.current>a,
|
||||
.wy-menu-vertical li.on a {
|
||||
background-color: var(--color-bg-side-nav-hover);
|
||||
border: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current a {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.toctree-l2 a,
|
||||
.wy-menu-vertical li.toctree-l3 a,
|
||||
.wy-menu-vertical li.toctree-l4 a,
|
||||
.wy-menu-vertical li.toctree-l5 a,
|
||||
.wy-menu-vertical li.toctree-l6 a,
|
||||
.wy-menu-vertical li.toctree-l7 a,
|
||||
.wy-menu-vertical li.toctree-l8 a,
|
||||
.wy-menu-vertical li.toctree-l9 a,
|
||||
.wy-menu-vertical li.toctree-l10 a {
|
||||
color: var(--color-text-body);
|
||||
}
|
||||
|
||||
a.image-reference, a.image-reference:hover {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
a.image-reference img {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
|
||||
/* Code blocks */
|
||||
.rst-content code, .rst-content tt, code {
|
||||
padding: 0.25em;
|
||||
font-weight: 400;
|
||||
background-color: var(--color-bg-code-block);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.rst-content div[class^=highlight], .rst-content pre.literal-block {
|
||||
padding: 0.7rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.75rem;
|
||||
overflow-x: auto;
|
||||
background-color: var(--color-bg-side-nav);
|
||||
border-color: var(--color-border);
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.rst-content .admonition-title,
|
||||
.rst-content div.admonition,
|
||||
.wy-alert-title {
|
||||
padding: 10px 12px;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.highlight .go {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.highlight .nt {
|
||||
color: var(--color-text-code-nt);
|
||||
}
|
||||
|
||||
.rst-content code.literal,
|
||||
.rst-content tt.literal {
|
||||
border-color: var(--color-border);
|
||||
background-color: var(--color-border);
|
||||
color: var(--color-text-code-inline)
|
||||
}
|
||||
|
||||
|
||||
/* Search */
|
||||
.wy-side-nav-search input[type=text] {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
font-family: inherit;
|
||||
font-size: .85rem;
|
||||
box-shadow: none;
|
||||
padding: .7rem 1rem .7rem 2.8rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#rtd-search-form {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#rtd-search-form:before {
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
content: "\f002";
|
||||
color: var(--color-text-alt);
|
||||
position: absolute;
|
||||
left: 1.5rem;
|
||||
top: .7rem;
|
||||
}
|
||||
|
||||
/* Side nav */
|
||||
.wy-side-nav-search {
|
||||
padding: 1rem 0 0 0;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li a button.toctree-expand {
|
||||
float: right;
|
||||
margin-right: -1.5em;
|
||||
padding: 0 .5em;
|
||||
}
|
||||
|
||||
.wy-menu-vertical a,
|
||||
.wy-menu-vertical li.current>a,
|
||||
.wy-menu-vertical li.current li>a {
|
||||
padding-right: 1.5em !important;
|
||||
}
|
||||
|
||||
.wy-menu-vertical li.current li>a.current {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Misc spacing */
|
||||
.rst-content .admonition-title, .wy-alert-title {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
padding: 0.3em 1em;
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
color: #var(--color-text-anchor);
|
||||
text-decoration: none;
|
||||
vertical-align: baseline;
|
||||
background-color: #f7f7f7;
|
||||
border-width: 0;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.12),0 3px 10px rgba(0,0,0,0.08);
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
padding: 0.3em 1em;
|
||||
}
|
||||
|
||||
.rst-content .btn:focus {
|
||||
outline: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.rst-content .btn-neutral, .rst-content .btn span.fa {
|
||||
color: var(--color-text-body) !important;
|
||||
}
|
||||
|
||||
.btn-neutral {
|
||||
background-color: var(--color-btn-neutral-bg) !important;
|
||||
color: var(--color-btn-neutral-text) !important;
|
||||
border: 1px solid var(--color-btn-neutral-bg);
|
||||
}
|
||||
|
||||
.btn:hover, .btn-neutral:hover {
|
||||
background-color: var(--color-btn-neutral-bg-hover) !important;
|
||||
}
|
||||
|
||||
|
||||
/* Icon overrides */
|
||||
.wy-side-nav-search a.icon-home:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before {
|
||||
content: "\f106"; /* fa-angle-up */
|
||||
}
|
||||
|
||||
.fa-plus-square-o:before, .wy-menu-vertical li button.toctree-expand:before {
|
||||
content: "\f107"; /* fa-angle-down */
|
||||
}
|
||||
|
||||
|
||||
/* Misc */
|
||||
.wy-nav-top {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.wy-nav-top > i {
|
||||
font-size: 24px;
|
||||
padding: 8px 0 0 2px;
|
||||
color:#var(--color-text-anchor);
|
||||
}
|
||||
|
||||
.rst-content table.docutils td,
|
||||
.rst-content table.docutils th,
|
||||
.rst-content table.field-list td,
|
||||
.rst-content table.field-list th,
|
||||
.wy-table td,
|
||||
.wy-table th {
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.dark-mode-toggle {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
height: 20px;
|
||||
width: 24px;
|
||||
z-index: 10;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.wy-nav-content-wrap {
|
||||
z-index: 20;
|
||||
}
|
14
docs/_static/custom.css
vendored
@@ -1,14 +0,0 @@
|
||||
/* override table width restrictions */
|
||||
@media screen and (min-width: 767px) {
|
||||
|
||||
.wy-table-responsive table td {
|
||||
/* !important prevents the common CSS stylesheets from
|
||||
overriding this as on RTD they are loaded after this stylesheet */
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
.wy-table-responsive {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
}
|
47
docs/_static/js/darkmode.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
let toggleButton;
|
||||
let icon;
|
||||
|
||||
function load() {
|
||||
"use strict";
|
||||
|
||||
toggleButton = document.createElement("button");
|
||||
toggleButton.setAttribute("title", "Toggle dark mode");
|
||||
toggleButton.classList.add("dark-mode-toggle");
|
||||
icon = document.createElement("i");
|
||||
icon.classList.add("fa", darkModeState ? "fa-sun-o" : "fa-moon-o");
|
||||
toggleButton.appendChild(icon);
|
||||
document.body.prepend(toggleButton);
|
||||
|
||||
// Listen for changes in the OS settings
|
||||
// addListener is used because older versions of Safari don't support addEventListener
|
||||
// prefersDarkQuery set in <head>
|
||||
if (prefersDarkQuery) {
|
||||
prefersDarkQuery.addListener(function (evt) {
|
||||
toggleDarkMode(evt.matches);
|
||||
});
|
||||
}
|
||||
|
||||
// Initial setting depending on the prefers-color-mode or localstorage
|
||||
// darkModeState should be set in the document <head> to prevent flash
|
||||
if (darkModeState == undefined) darkModeState = false;
|
||||
toggleDarkMode(darkModeState);
|
||||
|
||||
// Toggles the "dark-mode" class on click and sets localStorage state
|
||||
toggleButton.addEventListener("click", () => {
|
||||
darkModeState = !darkModeState;
|
||||
|
||||
toggleDarkMode(darkModeState);
|
||||
localStorage.setItem("dark-mode", darkModeState);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleDarkMode(state) {
|
||||
document.documentElement.classList.toggle("dark-mode", state);
|
||||
document.documentElement.classList.toggle("light-mode", !state);
|
||||
icon.classList.remove("fa-sun-o");
|
||||
icon.classList.remove("fa-moon-o");
|
||||
icon.classList.add(state ? "fa-sun-o" : "fa-moon-o");
|
||||
darkModeState = state;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", load);
|
BIN
docs/_static/screenshot.png
vendored
Before Width: | Height: | Size: 445 KiB |
BIN
docs/_static/screenshots/bulk-edit.png
vendored
Normal file
After Width: | Height: | Size: 661 KiB |
BIN
docs/_static/screenshots/correspondents.png
vendored
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 457 KiB |
BIN
docs/_static/screenshots/dashboard.png
vendored
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 436 KiB |
BIN
docs/_static/screenshots/documents-filter.png
vendored
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 462 KiB |
BIN
docs/_static/screenshots/documents-largecards.png
vendored
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 608 KiB |
BIN
docs/_static/screenshots/documents-smallcards-dark.png
vendored
Normal file
After Width: | Height: | Size: 698 KiB |
BIN
docs/_static/screenshots/documents-smallcards.png
vendored
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 706 KiB |
BIN
docs/_static/screenshots/documents-table.png
vendored
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 480 KiB |
BIN
docs/_static/screenshots/documents-wchrome-dark.png
vendored
Normal file
After Width: | Height: | Size: 680 KiB |
BIN
docs/_static/screenshots/documents-wchrome.png
vendored
Normal file
After Width: | Height: | Size: 686 KiB |
BIN
docs/_static/screenshots/editing.png
vendored
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 848 KiB |
BIN
docs/_static/screenshots/logs.png
vendored
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 703 KiB |
BIN
docs/_static/screenshots/mobile.png
vendored
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 388 KiB |
BIN
docs/_static/screenshots/new-tag.png
vendored
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 26 KiB |
BIN
docs/_static/screenshots/search-preview.png
vendored
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 54 KiB |
BIN
docs/_static/screenshots/search-results.png
vendored
Before Width: | Height: | Size: 261 KiB After Width: | Height: | Size: 517 KiB |
13
docs/_templates/layout.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{% extends "!layout.html" %}
|
||||
{% block extrahead %}
|
||||
<script>
|
||||
// MediaQueryList object
|
||||
const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
const lsDark = localStorage.getItem("dark-mode");
|
||||
let darkModeState = lsDark !== null ? lsDark == "true" : prefersDarkQuery.matches;
|
||||
|
||||
document.documentElement.classList.toggle("dark-mode", darkModeState);
|
||||
document.documentElement.classList.toggle("light-mode", !darkModeState);
|
||||
</script>
|
||||
{{ super() }}
|
||||
{% endblock %}
|
@@ -23,6 +23,12 @@ Options available to any installation of paperless:
|
||||
* The document exporter is also able to update an already existing export.
|
||||
Therefore, incremental backups with ``rsync`` are entirely possible.
|
||||
|
||||
.. caution::
|
||||
|
||||
You cannot import the export generated with one version of paperless in a
|
||||
different version of paperless. The export contains an exact image of the
|
||||
database, and migrations may change the database layout.
|
||||
|
||||
Options available to docker installations:
|
||||
|
||||
* Backup the docker volumes. These usually reside within
|
||||
@@ -58,9 +64,9 @@ Updating Paperless
|
||||
Docker Route
|
||||
============
|
||||
|
||||
If a new release of paperless-ng is available, upgrading depends on how you
|
||||
installed paperless-ng in the first place. The releases are available at the
|
||||
`release page <https://github.com/jonaswinkler/paperless-ng/releases>`_.
|
||||
If a new release of paperless-ngx is available, upgrading depends on how you
|
||||
installed paperless-ngx in the first place. The releases are available at the
|
||||
`release page <https://github.com/paperless-ngx/paperless-ngx/releases>`_.
|
||||
|
||||
First of all, ensure that paperless is stopped.
|
||||
|
||||
@@ -86,32 +92,48 @@ B. If you built the image yourself, do the following:
|
||||
.. code:: shell-session
|
||||
|
||||
$ git pull
|
||||
$ ./compile-frontend.sh
|
||||
$ docker-compose build
|
||||
$ docker-compose up
|
||||
|
||||
Running ``docker-compose up`` will also apply any new database migrations.
|
||||
If you see everything working, press CTRL+C once to gracefully stop paperless.
|
||||
Then you can start paperless-ng with ``-d`` to have it run in the background.
|
||||
Then you can start paperless-ngx with ``-d`` to have it run in the background.
|
||||
|
||||
.. note::
|
||||
|
||||
In version 0.9.14, the update process was changed. In 0.9.13 and earlier, the
|
||||
docker-compose files specified exact versions and pull won't automatically
|
||||
update to newer versions. In order to enable updates as described above, either
|
||||
get the new ``docker-compose.yml`` file from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
|
||||
get the new ``docker-compose.yml`` file from `here <https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose>`_
|
||||
or edit the ``docker-compose.yml`` file, find the line that says
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
image: jonaswinkler/paperless-ng:0.9.x
|
||||
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:0.9.x
|
||||
|
||||
and replace the version with ``latest``:
|
||||
|
||||
.. code::
|
||||
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
|
||||
.. note::
|
||||
In version 1.7.1 and onwards, the Docker image can now pinned to a release series.
|
||||
This is often combined with automatic updaters such as Watchtower to allow safer
|
||||
unattended upgrading to new bugfix releases only. It is still recommended to always
|
||||
review release notes before upgrading. To ping your install to a release series, edit
|
||||
the ``docker-compose.yml`` find the line that says
|
||||
|
||||
.. code::
|
||||
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
|
||||
and replace the version with the series you want to track, for example:
|
||||
|
||||
.. code::
|
||||
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:1.7
|
||||
|
||||
Bare Metal Route
|
||||
================
|
||||
|
||||
@@ -138,59 +160,65 @@ After grabbing the new release and unpacking the contents, do the following:
|
||||
This might not actually do anything. Not every new paperless version comes with new
|
||||
database migrations.
|
||||
|
||||
Ansible Route
|
||||
=============
|
||||
Downgrading Paperless
|
||||
#####################
|
||||
|
||||
Most of the update process is automated when using the ansible role.
|
||||
Downgrades are possible. However, some updates also contain database migrations (these change the layout of the database and may move data).
|
||||
In order to move back from a version that applied database migrations, you'll have to revert the database migration *before* downgrading,
|
||||
and then downgrade paperless.
|
||||
|
||||
1. Backup your defined role variables file outside the paperless source-tree:
|
||||
This table lists the compatible versions for each database migration number.
|
||||
|
||||
.. code:: shell-session
|
||||
+------------------+-----------------+
|
||||
| Migration number | Version range |
|
||||
+------------------+-----------------+
|
||||
| 1011 | 1.0.0 |
|
||||
+------------------+-----------------+
|
||||
| 1012 | 1.1.0 - 1.2.1 |
|
||||
+------------------+-----------------+
|
||||
| 1014 | 1.3.0 - 1.3.1 |
|
||||
+------------------+-----------------+
|
||||
| 1016 | 1.3.2 - current |
|
||||
+------------------+-----------------+
|
||||
|
||||
$ cp ansible/vars.yml ~/vars.yml.old
|
||||
Execute the following management command to migrate your database:
|
||||
|
||||
2. Pull the release tag you want to update to:
|
||||
.. code:: shell-session
|
||||
|
||||
.. code:: shell-session
|
||||
$ python3 manage.py migrate documents <migration number>
|
||||
|
||||
$ git fetch --all
|
||||
$ git checkout ng-0.9.14
|
||||
.. note::
|
||||
|
||||
3. Update the role variable definitions ``ansible/vars.yml`` (where appropriate).
|
||||
|
||||
4. Run the ansible playbook you created created during :ref:`installation <setup-ansible>` again:
|
||||
|
||||
.. note::
|
||||
|
||||
When ansible detects that an update run is in progress, it backs up the entire ``paperlessng_directory`` to ``paperlessng_directory-TIMESTAMP``.
|
||||
Updates can be rolled back by simply moving the timestamped folder back to the original location.
|
||||
If the update succeeds and you want to continue using the new release, please don't forget to delete the backup folder.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ ansible-playbook playbook.yml
|
||||
Some migrations cannot be undone. The command will issue errors if that happens.
|
||||
|
||||
.. _utilities-management-commands:
|
||||
|
||||
Management utilities
|
||||
####################
|
||||
|
||||
Paperless comes with some management commands that perform various maintenance
|
||||
tasks on your paperless instance. You can invoke these commands either by
|
||||
tasks on your paperless instance. You can invoke these commands in the following way:
|
||||
|
||||
With docker-compose, while paperless is running:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless
|
||||
$ docker-compose run --rm webserver <command> <arguments>
|
||||
$ docker-compose exec webserver <command> <arguments>
|
||||
|
||||
or
|
||||
With docker, while paperless is running:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ docker exec -it <container-name> <command> <arguments>
|
||||
|
||||
Bare metal:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless/src
|
||||
$ python3 manage.py <command> <arguments>
|
||||
|
||||
depending on whether you use docker or not.
|
||||
|
||||
All commands have built-in help, which can be accessed by executing them with
|
||||
the argument ``--help``.
|
||||
|
||||
@@ -202,6 +230,8 @@ Document exporter
|
||||
The document exporter exports all your data from paperless into a folder for
|
||||
backup or migration to another DMS.
|
||||
|
||||
If you use the document exporter within a cronjob to backup your data you might use the ``-T`` flag behind exec to suppress "The input device is not a TTY" errors. For example: ``docker-compose exec -T webserver document_exporter ../export``
|
||||
|
||||
.. code::
|
||||
|
||||
document_exporter target [-c] [-f] [-d]
|
||||
@@ -210,7 +240,7 @@ backup or migration to another DMS.
|
||||
-c, --compare-checksums
|
||||
-f, --use-filename-format
|
||||
-d, --delete
|
||||
|
||||
|
||||
``target`` is a folder to which the data gets written. This includes documents,
|
||||
thumbnails and a ``manifest.json`` file. The manifest contains all metadata from
|
||||
the database (correspondents, tags, etc).
|
||||
@@ -356,7 +386,7 @@ the naming scheme.
|
||||
|
||||
.. warning::
|
||||
|
||||
Since this command moves you documents around alot, it is advised to to
|
||||
Since this command moves you documents around a lot, it is advised to to
|
||||
a backup before. The renaming logic is robust and will never overwrite
|
||||
or delete a file, but you can't ever be careful enough.
|
||||
|
||||
@@ -366,6 +396,36 @@ the naming scheme.
|
||||
|
||||
The command takes no arguments and processes all your documents at once.
|
||||
|
||||
Learn how to use :ref:`Management Utilities<utilities-management-commands>`.
|
||||
|
||||
|
||||
.. _utilities-sanity-checker:
|
||||
|
||||
Sanity checker
|
||||
==============
|
||||
|
||||
Paperless has a built-in sanity checker that inspects your document collection for issues.
|
||||
|
||||
The issues detected by the sanity checker are as follows:
|
||||
|
||||
* Missing original files.
|
||||
* Missing archive files.
|
||||
* Inaccessible original files due to improper permissions.
|
||||
* Inaccessible archive files due to improper permissions.
|
||||
* Corrupted original documents by comparing their checksum against what is stored in the database.
|
||||
* Corrupted archive documents by comparing their checksum against what is stored in the database.
|
||||
* Missing thumbnails.
|
||||
* Inaccessible thumbnails due to improper permissions.
|
||||
* Documents without any content (warning).
|
||||
* Orphaned files in the media directory (warning). These are files that are not referenced by any document im paperless.
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
document_sanity_checker
|
||||
|
||||
The command takes no arguments. Depending on the size of your document archive, this may take some time.
|
||||
|
||||
|
||||
Fetching e-mail
|
||||
===============
|
||||
@@ -425,7 +485,7 @@ Documents can be stored in Paperless using GnuPG encryption.
|
||||
|
||||
.. danger::
|
||||
|
||||
Encryption is deprecated since paperless-ng 0.9 and doesn't really provide any
|
||||
Encryption is deprecated since paperless-ngx 0.9 and doesn't really provide any
|
||||
additional security, since you have to store the passphrase in a configuration
|
||||
file on the same system as the encrypted documents for paperless to work.
|
||||
Furthermore, the entire text content of the documents is stored plain in the
|
||||
|
@@ -10,22 +10,21 @@ easier.
|
||||
Matching tags, correspondents and document types
|
||||
################################################
|
||||
|
||||
After the consumer has tried to figure out what it could from the file name,
|
||||
it starts looking at the content of the document itself. It will compare the
|
||||
matching algorithms defined by every tag and correspondent already set in your
|
||||
database to see if they apply to the text in that document. In other words,
|
||||
if you defined a tag called ``Home Utility`` that had a ``match`` property of
|
||||
``bc hydro`` and a ``matching_algorithm`` of ``literal``, Paperless will
|
||||
automatically tag your newly-consumed document with your ``Home Utility`` tag
|
||||
so long as the text ``bc hydro`` appears in the body of the document somewhere.
|
||||
Paperless will compare the matching algorithms defined by every tag and
|
||||
correspondent already set in your database to see if they apply to the text in
|
||||
a document. In other words, if you defined a tag called ``Home Utility``
|
||||
that had a ``match`` property of ``bc hydro`` and a ``matching_algorithm`` of
|
||||
``literal``, Paperless will automatically tag your newly-consumed document with
|
||||
your ``Home Utility`` tag so long as the text ``bc hydro`` appears in the body
|
||||
of the document somewhere.
|
||||
|
||||
The matching logic is quite powerful, and supports searching the text of your
|
||||
The matching logic is quite powerful. It supports searching the text of your
|
||||
document with different algorithms, and as such, some experimentation may be
|
||||
necessary to get things right.
|
||||
|
||||
In order to have a tag, correspondent or type assigned automatically to newly
|
||||
In order to have a tag, correspondent, or type assigned automatically to newly
|
||||
consumed documents, assign a match and matching algorithm using the web
|
||||
interface. These settings define when to assign correspondents, tags and types
|
||||
interface. These settings define when to assign correspondents, tags, and types
|
||||
to documents.
|
||||
|
||||
The following algorithms are available:
|
||||
@@ -35,16 +34,16 @@ The following algorithms are available:
|
||||
either of these terms.
|
||||
* **All:** Requires that every word provided appears in the PDF, albeit not in the
|
||||
order provided.
|
||||
* **Literal:** Matches only if the match appears exactly as provided in the PDF.
|
||||
* **Literal:** Matches only if the match appears exactly as provided (i.e. preserve ordering) in the PDF.
|
||||
* **Regular expression:** Parses the match as a regular expression and tries to
|
||||
find a match within the document.
|
||||
* **Fuzzy match:** I dont know. Look at the source.
|
||||
* **Auto:** Tries to automatically match new documents. This does not require you
|
||||
to set a match. See the notes below.
|
||||
|
||||
When using the "any" or "all" matching algorithms, you can search for terms
|
||||
When using the *any* or *all* matching algorithms, you can search for terms
|
||||
that consist of multiple words by enclosing them in double quotes. For example,
|
||||
defining a match text of ``"Bank of America" BofA`` using the "any" algorithm,
|
||||
defining a match text of ``"Bank of America" BofA`` using the *any* algorithm,
|
||||
will match documents that contain either "Bank of America" or "BofA", but will
|
||||
not match documents containing "Bank of South America".
|
||||
|
||||
@@ -58,9 +57,9 @@ automatically tagged with the appropriate data.
|
||||
Automatic matching
|
||||
==================
|
||||
|
||||
Paperless-ng comes with a new matching algorithm called *Auto*. This matching
|
||||
algorithm tries to assign tags, correspondents and document types to your
|
||||
documents based on how you have assigned these on existing documents. It
|
||||
Paperless-ngx comes with a new matching algorithm called *Auto*. This matching
|
||||
algorithm tries to assign tags, correspondents, and document types to your
|
||||
documents based on how you have already assigned these on existing documents. It
|
||||
uses a neural network under the hood.
|
||||
|
||||
If, for example, all your bank statements of your account 123 at the Bank of
|
||||
@@ -77,11 +76,11 @@ feature:
|
||||
changes. Paperless periodically (default: once each hour) checks for changes
|
||||
and does this automatically for you.
|
||||
* The Auto matching algorithm only takes documents into account which are NOT
|
||||
placed in your inbox (i.e., have inbox tags assigned to them). This ensures
|
||||
placed in your inbox (i.e. have any inbox tags assigned to them). This ensures
|
||||
that the neural network only learns from documents which you have correctly
|
||||
tagged before.
|
||||
* The matching algorithm can only work if there is a correlation between the
|
||||
tag, correspondent or document type and the document itself. Your bank
|
||||
tag, correspondent, or document type and the document itself. Your bank
|
||||
statements usually contain your bank account number and the name of the bank,
|
||||
so this works reasonably well, However, tags such as "TODO" cannot be
|
||||
automatically assigned.
|
||||
@@ -105,7 +104,7 @@ you execute scripts of your own choosing just before or after a document is
|
||||
consumed using a couple simple hooks.
|
||||
|
||||
Just write a script, put it somewhere that Paperless can read & execute, and
|
||||
then put the path to that script in ``paperless.conf`` with the variable name
|
||||
then put the path to that script in ``paperless.conf`` or ``docker-compose.env`` with the variable name
|
||||
of either ``PAPERLESS_PRE_CONSUME_SCRIPT`` or
|
||||
``PAPERLESS_POST_CONSUME_SCRIPT``.
|
||||
|
||||
@@ -168,12 +167,41 @@ into paperless. It receives the following arguments:
|
||||
* Correspondent
|
||||
* Tags
|
||||
|
||||
The script can be in any language you like, but for a simple shell script
|
||||
example, you can take a look at ``post-consumption-example.sh`` in the
|
||||
``scripts`` directory in this project.
|
||||
The script can be in any language, but for a simple shell script
|
||||
example, you can take a look at `post-consumption-example.sh`_ in this project.
|
||||
|
||||
The post consumption script cannot cancel the consumption process.
|
||||
|
||||
Docker
|
||||
------
|
||||
Assumed you have ``/home/foo/paperless-ngx/scripts/post-consumption-example.sh``.
|
||||
|
||||
You can pass that script into the consumer container via a host mount in your ``docker-compose.yml``.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
...
|
||||
consumer:
|
||||
...
|
||||
volumes:
|
||||
...
|
||||
- /home/paperless-ngx/scripts:/path/in/container/scripts/
|
||||
...
|
||||
|
||||
Example (docker-compose.yml): ``- /home/foo/paperless-ngx/scripts:/usr/src/paperless/scripts``
|
||||
|
||||
which in turn requires the variable ``PAPERLESS_POST_CONSUME_SCRIPT`` in ``docker-compose.env`` to point to ``/path/in/container/scripts/post-consumption-example.sh``.
|
||||
|
||||
Example (docker-compose.env): ``PAPERLESS_POST_CONSUME_SCRIPT=/usr/src/paperless/scripts/post-consumption-example.sh``
|
||||
|
||||
Troubleshooting:
|
||||
|
||||
- Monitor the docker-compose log ``cd ~/paperless-ngx; docker-compose logs -f``
|
||||
- Check your script's permission e.g. in case of permission error ``sudo chmod 755 post-consumption-example.sh``
|
||||
- Pipe your scripts's output to a log file e.g. ``echo "${DOCUMENT_ID}" | tee --append /usr/src/paperless/scripts/post-consumption-example.log``
|
||||
|
||||
.. _post-consumption-example.sh: https://github.com/paperless-ngx/paperless-ngx/blob/main/scripts/post-consumption-example.sh
|
||||
|
||||
.. _advanced-file_name_handling:
|
||||
|
||||
File name handling
|
||||
@@ -215,20 +243,21 @@ will create a directory structure as follows:
|
||||
last filename a document was stored as. If you do rename a file, paperless will
|
||||
report your files as missing and won't be able to find them.
|
||||
|
||||
Paperless provides the following placeholders withing filenames:
|
||||
Paperless provides the following placeholders within filenames:
|
||||
|
||||
* ``{asn}``: The archive serial number of the document, or "none".
|
||||
* ``{correspondent}``: The name of the correspondent, or "none".
|
||||
* ``{document_type}``: The name of the document type, or "none".
|
||||
* ``{tag_list}``: A comma separated list of all tags assigned to the document.
|
||||
* ``{title}``: The title of the document.
|
||||
* ``{created}``: The full date and time the document was created.
|
||||
* ``{created}``: The full date (ISO format) the document was created.
|
||||
* ``{created_year}``: Year created only.
|
||||
* ``{created_month}``: Month created only (number 1-12).
|
||||
* ``{created_day}``: Day created only (number 1-31).
|
||||
* ``{added}``: The full date and time the document was added to paperless.
|
||||
* ``{created_month}``: Month created only (number 01-12).
|
||||
* ``{created_day}``: Day created only (number 01-31).
|
||||
* ``{added}``: The full date (ISO format) the document was added to paperless.
|
||||
* ``{added_year}``: Year added only.
|
||||
* ``{added_month}``: Month added only (number 1-12).
|
||||
* ``{added_day}``: Day added only (number 1-31).
|
||||
* ``{added_month}``: Month added only (number 01-12).
|
||||
* ``{added_day}``: Day added only (number 01-31).
|
||||
|
||||
|
||||
Paperless will try to conserve the information from your database as much as possible.
|
||||
|
164
docs/api.rst
@@ -60,7 +60,7 @@ The endpoints correctly serve the response header fields ``Content-Disposition``
|
||||
and ``Content-Type`` to indicate the filename for download and the type of content of
|
||||
the document.
|
||||
|
||||
In order to download or preview the original document when an archied document is available,
|
||||
In order to download or preview the original document when an archived document is available,
|
||||
supply the query parameter ``original=true``.
|
||||
|
||||
.. hint::
|
||||
@@ -147,93 +147,57 @@ The REST api provides three different forms of authentication.
|
||||
Searching for documents
|
||||
#######################
|
||||
|
||||
Paperless-ng offers API endpoints for full text search. These are as follows:
|
||||
Full text searching is available on the ``/api/documents/`` endpoint. Two specific
|
||||
query parameters cause the API to return full text search results:
|
||||
|
||||
``/api/search/``
|
||||
================
|
||||
* ``/api/documents/?query=your%20search%20query``: Search for a document using a full text query.
|
||||
For details on the syntax, see :ref:`basic-usage_searching`.
|
||||
|
||||
Get search results based on a query.
|
||||
* ``/api/documents/?more_like=1234``: Search for documents similar to the document with id 1234.
|
||||
|
||||
Query parameters:
|
||||
Pagination works exactly the same as it does for normal requests on this endpoint.
|
||||
|
||||
* ``query``: The query string. See
|
||||
`here <https://whoosh.readthedocs.io/en/latest/querylang.html>`_
|
||||
for details on the syntax.
|
||||
* ``page``: Specify the page you want to retrieve. Each page
|
||||
contains 10 search results and the first page is ``page=1``, which
|
||||
is the default if this is omitted.
|
||||
Certain limitations apply to full text queries:
|
||||
|
||||
Result list object returned by the endpoint:
|
||||
* Results are always sorted by search score. The results matching the query best will show up first.
|
||||
|
||||
.. code:: json
|
||||
* Only a small subset of filtering parameters are supported.
|
||||
|
||||
Furthermore, each returned document has an additional ``__search_hit__`` attribute with various information
|
||||
about the search results:
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
"count": 1,
|
||||
"page": 1,
|
||||
"page_count": 1,
|
||||
"corrected_query": "",
|
||||
"count": 31,
|
||||
"next": "http://localhost:8000/api/documents/?page=2&query=test",
|
||||
"previous": null,
|
||||
"results": [
|
||||
|
||||
...
|
||||
|
||||
{
|
||||
"id": 123,
|
||||
"title": "title",
|
||||
"content": "content",
|
||||
|
||||
...
|
||||
|
||||
"__search_hit__": {
|
||||
"score": 0.343,
|
||||
"highlights": "text <span class=\"match\">Test</span> text",
|
||||
"rank": 23
|
||||
}
|
||||
},
|
||||
|
||||
...
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
* ``count``: The approximate total number of results.
|
||||
* ``page``: The page returned to you. This might be different from
|
||||
the page you requested, if you requested a page that is behind
|
||||
the last page. In that case, the last page is returned.
|
||||
* ``page_count``: The total number of pages.
|
||||
* ``corrected_query``: Corrected version of the query string. Can be null.
|
||||
If not null, can be used verbatim to start a new query.
|
||||
* ``results``: A list of result objects on the current page.
|
||||
|
||||
Result object:
|
||||
|
||||
.. code:: json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"highlights": [
|
||||
|
||||
],
|
||||
"score": 6.34234,
|
||||
"rank": 23,
|
||||
"document": {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
* ``id``: the primary key of the found document
|
||||
* ``highlights``: an object containing parsable highlights for the result.
|
||||
See below.
|
||||
* ``score``: The score assigned to the document. A higher score indicates a
|
||||
better match with the query. Search results are sorted descending by score.
|
||||
* ``rank``: the position of the document within the entire search results list.
|
||||
* ``document``: The full json of the document, as returned by
|
||||
``/api/documents/<id>/``.
|
||||
|
||||
Highlights object:
|
||||
|
||||
Highlights are provided as a list of fragments. A fragment is a longer section of
|
||||
text from the original document.
|
||||
Each fragment contains a list of strings, and some of them are marked as a highlight.
|
||||
|
||||
.. code:: json
|
||||
|
||||
[
|
||||
[
|
||||
{"text": "This is a sample text with a ", "highlight": false},
|
||||
{"text": "highlighted", "highlight": true},
|
||||
{"text": " word.", "highlight": false}
|
||||
],
|
||||
[
|
||||
{"text": "Another", "highlight": true},
|
||||
{"text": " fragment with a highlight.", "highlight": false}
|
||||
]
|
||||
]
|
||||
|
||||
A client may use this example to produce the following output:
|
||||
|
||||
... This is a sample text with a **highlighted** word. ... **Another** fragment with a highlight. ...
|
||||
* ``score`` is an indication how well this document matches the query relative to the other search results.
|
||||
* ``highlights`` is an excerpt from the document content and highlights the search terms with ``<span>`` tags as shown above.
|
||||
* ``rank`` is the index of the search results. The first result will have rank 0.
|
||||
|
||||
``/api/search/autocomplete/``
|
||||
=============================
|
||||
@@ -284,3 +248,53 @@ The endpoint supports the following optional form fields:
|
||||
The endpoint will immediately return "OK" if the document consumption process
|
||||
was started successfully. No additional status information about the consumption
|
||||
process itself is available, since that happens in a different process.
|
||||
|
||||
|
||||
.. _api-versioning:
|
||||
|
||||
API Versioning
|
||||
##############
|
||||
|
||||
The REST API is versioned since Paperless-ngx 1.3.0.
|
||||
|
||||
* Versioning ensures that changes to the API don't break older clients.
|
||||
* Clients specify the specific version of the API they wish to use with every request and Paperless will handle the request using the specified API version.
|
||||
* Even if the underlying data model changes, older API versions will always serve compatible data.
|
||||
* If no version is specified, Paperless will serve version 1 to ensure compatibility with older clients that do not request a specific API version.
|
||||
|
||||
API versions are specified by submitting an additional HTTP ``Accept`` header with every request:
|
||||
|
||||
.. code::
|
||||
|
||||
Accept: application/json; version=6
|
||||
|
||||
If an invalid version is specified, Paperless 1.3.0 will respond with "406 Not Acceptable" and an error message in the body.
|
||||
Earlier versions of Paperless will serve API version 1 regardless of whether a version is specified via the ``Accept`` header.
|
||||
|
||||
If a client wishes to verify whether it is compatible with any given server, the following procedure should be performed:
|
||||
|
||||
1. Perform an *authenticated* request against any API endpoint. If the server is on version 1.3.0 or newer, the server will
|
||||
add two custom headers to the response:
|
||||
|
||||
.. code::
|
||||
|
||||
X-Api-Version: 2
|
||||
X-Version: 1.3.0
|
||||
|
||||
2. Determine whether the client is compatible with this server based on the presence/absence of these headers and their values if present.
|
||||
|
||||
|
||||
API Changelog
|
||||
=============
|
||||
|
||||
Version 1
|
||||
---------
|
||||
|
||||
Initial API version.
|
||||
|
||||
Version 2
|
||||
---------
|
||||
|
||||
* Added field ``Tag.color``. This read/write string field contains a hex color such as ``#a6cee3``.
|
||||
* Added read-only field ``Tag.text_color``. This field contains the text color to use for a specific tag, which is either black or white depending on the brightness of ``Tag.color``.
|
||||
* Removed field ``Tag.colour``.
|
||||
|
1947
docs/changelog.md
Normal file
1330
docs/changelog.rst
210
docs/conf.py
@@ -2,33 +2,39 @@ import sphinx_rtd_theme
|
||||
|
||||
|
||||
__version__ = None
|
||||
__full_version_str__ = None
|
||||
__major_minor_version_str__ = None
|
||||
exec(open("../src/paperless/version.py").read())
|
||||
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.imgmath',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx_rtd_theme',
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.todo",
|
||||
"sphinx.ext.imgmath",
|
||||
"sphinx.ext.viewcode",
|
||||
"sphinx_rtd_theme",
|
||||
"myst_parser",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
# templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
source_suffix = {
|
||||
".rst": "restructuredtext",
|
||||
".md": "markdown",
|
||||
}
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = u'Paperless-ng'
|
||||
copyright = u'2021, Daniel Quinn, Jonas Winkler'
|
||||
project = "Paperless-ngx"
|
||||
copyright = "2015-2022, Daniel Quinn, Jonas Winkler, and the paperless-ngx team"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -41,186 +47,190 @@ copyright = u'2021, Daniel Quinn, Jonas Winkler'
|
||||
#
|
||||
|
||||
# The short X.Y version.
|
||||
version = ".".join([str(_) for _ in __version__[:2]])
|
||||
version = __major_minor_version_str__
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = ".".join([str(_) for _ in __version__[:3]])
|
||||
release = __full_version_str__
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
exclude_patterns = ["_build"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
# keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# These paths are either relative to html_static_path
|
||||
# or fully qualified paths (eg. https://...)
|
||||
html_css_files = [
|
||||
"css/custom.css",
|
||||
]
|
||||
|
||||
html_js_files = [
|
||||
"js/darkmode.js",
|
||||
]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
# html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'paperless'
|
||||
htmlhelp_basename = "paperless"
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'paperless.tex', u'Paperless Documentation',
|
||||
u'Daniel Quinn', 'manual'),
|
||||
("index", "paperless.tex", "Paperless Documentation", "Daniel Quinn", "manual"),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'paperless', u'Paperless Documentation',
|
||||
[u'Daniel Quinn'], 1)
|
||||
]
|
||||
man_pages = [("index", "paperless", "Paperless Documentation", ["Daniel Quinn"], 1)]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
@@ -229,93 +239,99 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'Paperless', u'Paperless Documentation',
|
||||
u'Daniel Quinn', 'paperless', 'Scan, index, and archive all of your paper documents.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
"index",
|
||||
"Paperless",
|
||||
"Paperless Documentation",
|
||||
"Daniel Quinn",
|
||||
"paperless",
|
||||
"Scan, index, and archive all of your paper documents.",
|
||||
"Miscellaneous",
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
# -- Options for Epub output ----------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = u'Paperless'
|
||||
epub_author = u'Daniel Quinn'
|
||||
epub_publisher = u'Daniel Quinn'
|
||||
epub_copyright = u'2015, Daniel Quinn'
|
||||
epub_title = "Paperless"
|
||||
epub_author = "Daniel Quinn"
|
||||
epub_publisher = "Daniel Quinn"
|
||||
epub_copyright = "2015, Daniel Quinn"
|
||||
|
||||
# The basename for the epub file. It defaults to the project name.
|
||||
#epub_basename = u'Paperless'
|
||||
# epub_basename = u'Paperless'
|
||||
|
||||
# The HTML theme for the epub output. Since the default themes are not optimized
|
||||
# for small screen space, using the same theme for HTML and epub output is
|
||||
# usually not wise. This defaults to 'epub', a theme designed to save visual
|
||||
# space.
|
||||
#epub_theme = 'epub'
|
||||
# epub_theme = 'epub'
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
#epub_language = ''
|
||||
# epub_language = ''
|
||||
|
||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
#epub_scheme = ''
|
||||
# epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#epub_identifier = ''
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#epub_uid = ''
|
||||
# epub_uid = ''
|
||||
|
||||
# A tuple containing the cover image and cover page html template filenames.
|
||||
#epub_cover = ()
|
||||
# epub_cover = ()
|
||||
|
||||
# A sequence of (type, uri, title) tuples for the guide element of content.opf.
|
||||
#epub_guide = ()
|
||||
# epub_guide = ()
|
||||
|
||||
# HTML files that should be inserted before the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
#epub_pre_files = []
|
||||
# epub_pre_files = []
|
||||
|
||||
# HTML files shat should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
#epub_post_files = []
|
||||
# epub_post_files = []
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
epub_exclude_files = ["search.html"]
|
||||
|
||||
# The depth of the table of contents in toc.ncx.
|
||||
#epub_tocdepth = 3
|
||||
# epub_tocdepth = 3
|
||||
|
||||
# Allow duplicate toc entries.
|
||||
#epub_tocdup = True
|
||||
# epub_tocdup = True
|
||||
|
||||
# Choose between 'default' and 'includehidden'.
|
||||
#epub_tocscope = 'default'
|
||||
# epub_tocscope = 'default'
|
||||
|
||||
# Fix unsupported image types using the PIL.
|
||||
#epub_fix_images = False
|
||||
# epub_fix_images = False
|
||||
|
||||
# Scale large images.
|
||||
#epub_max_image_width = 0
|
||||
# epub_max_image_width = 0
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#epub_show_urls = 'inline'
|
||||
# epub_show_urls = 'inline'
|
||||
|
||||
# If false, no index is generated.
|
||||
#epub_use_index = True
|
||||
# epub_use_index = True
|
||||
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
||||
intersphinx_mapping = {"http://docs.python.org/": None}
|
||||
|
@@ -80,6 +80,15 @@ PAPERLESS_DATA_DIR=<path>
|
||||
|
||||
Defaults to "../data/", relative to the "src" directory.
|
||||
|
||||
PAPERLESS_TRASH_DIR=<path>
|
||||
Instead of removing deleted documents, they are moved to this directory.
|
||||
|
||||
This must be writeable by the user running paperless. When running inside
|
||||
docker, ensure that this path is within a permanent volume (such as
|
||||
"../media/trash") so it won't get lost on upgrades.
|
||||
|
||||
Defaults to empty (i.e. really delete documents).
|
||||
|
||||
PAPERLESS_MEDIA_ROOT=<path>
|
||||
This is where your documents and thumbnails are stored.
|
||||
|
||||
@@ -121,6 +130,8 @@ PAPERLESS_LOGROTATE_MAX_BACKUPS=<num>
|
||||
|
||||
Defaults to 20.
|
||||
|
||||
.. _hosting-and-security:
|
||||
|
||||
Hosting & Security
|
||||
##################
|
||||
|
||||
@@ -133,7 +144,24 @@ PAPERLESS_SECRET_KEY=<key>
|
||||
|
||||
Default is listed in the file ``src/paperless/settings.py``.
|
||||
|
||||
PAPERLESS_ALLOWED_HOSTS<comma-separated-list>
|
||||
PAPERLESS_URL=<url>
|
||||
This setting can be used to set the three options below (ALLOWED_HOSTS,
|
||||
CORS_ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS). If the other options are
|
||||
set the values will be combined with this one. Do not include a trailing
|
||||
slash. E.g. https://paperless.domain.com
|
||||
|
||||
Defaults to empty string, leaving the other settings unaffected.
|
||||
|
||||
PAPERLESS_CSRF_TRUSTED_ORIGINS=<comma-separated-list>
|
||||
A list of trusted origins for unsafe requests (e.g. POST). As of Django 4.0
|
||||
this is required to access the Django admin via the web.
|
||||
See https://docs.djangoproject.com/en/4.0/ref/settings/#csrf-trusted-origins
|
||||
|
||||
Can also be set using PAPERLESS_URL (see above).
|
||||
|
||||
Defaults to empty string, which does not add any origins to the trusted list.
|
||||
|
||||
PAPERLESS_ALLOWED_HOSTS=<comma-separated-list>
|
||||
If you're planning on putting Paperless on the open internet, then you
|
||||
really should set this value to the domain name you're using. Failing to do
|
||||
so leaves you open to HTTP host header attacks:
|
||||
@@ -142,22 +170,25 @@ PAPERLESS_ALLOWED_HOSTS<comma-separated-list>
|
||||
Just remember that this is a comma-separated list, so "example.com" is fine,
|
||||
as is "example.com,www.example.com", but NOT " example.com" or "example.com,"
|
||||
|
||||
Can also be set using PAPERLESS_URL (see above).
|
||||
|
||||
If manually set, please remember to include "localhost". Otherwise docker
|
||||
healthcheck will fail.
|
||||
|
||||
Defaults to "*", which is all hosts.
|
||||
|
||||
PAPERLESS_CORS_ALLOWED_HOSTS<comma-separated-list>
|
||||
PAPERLESS_CORS_ALLOWED_HOSTS=<comma-separated-list>
|
||||
You need to add your servers to the list of allowed hosts that can do CORS
|
||||
calls. Set this to your public domain name.
|
||||
|
||||
Can also be set using PAPERLESS_URL (see above).
|
||||
|
||||
Defaults to "http://localhost:8000".
|
||||
|
||||
PAPERLESS_FORCE_SCRIPT_NAME=<path>
|
||||
To host paperless under a subpath url like example.com/paperless you set
|
||||
this value to /paperless. No trailing slash!
|
||||
|
||||
.. note::
|
||||
|
||||
I don't know if this works in paperless-ng. Probably not.
|
||||
|
||||
Defaults to none, which hosts paperless at "/".
|
||||
|
||||
PAPERLESS_STATIC_URL=<path>
|
||||
@@ -177,6 +208,30 @@ PAPERLESS_AUTO_LOGIN_USERNAME=<username>
|
||||
|
||||
Defaults to none, which disables this feature.
|
||||
|
||||
PAPERLESS_ADMIN_USER=<username>
|
||||
If this environment variable is specified, Paperless automatically creates
|
||||
a superuser with the provided username at start. This is useful in cases
|
||||
where you can not run the `createsuperuser` command separately, such as Kubernetes
|
||||
or AWS ECS.
|
||||
|
||||
Requires `PAPERLESS_ADMIN_PASSWORD` to be set.
|
||||
|
||||
.. note::
|
||||
|
||||
This will not change an existing [super]user's password, nor will
|
||||
it recreate a user that already exists. You can leave this throughout
|
||||
the lifecycle of the containers.
|
||||
|
||||
PAPERLESS_ADMIN_MAIL=<email>
|
||||
(Optional) Specify superuser email address. Only used when
|
||||
`PAPERLESS_ADMIN_USER` is set.
|
||||
|
||||
Defaults to ``root@localhost``.
|
||||
|
||||
PAPERLESS_ADMIN_PASSWORD=<password>
|
||||
Only used when `PAPERLESS_ADMIN_USER` is set.
|
||||
This will be the password of the automatically created superuser.
|
||||
|
||||
|
||||
PAPERLESS_COOKIE_PREFIX=<str>
|
||||
Specify a prefix that is added to the cookies used by paperless to identify
|
||||
@@ -191,8 +246,35 @@ PAPERLESS_ENABLE_HTTP_REMOTE_USER=<bool>
|
||||
Allows authentication via HTTP_REMOTE_USER which is used by some SSO
|
||||
applications.
|
||||
|
||||
.. warning::
|
||||
|
||||
This will allow authentication by simply adding a ``Remote-User: <username>`` header
|
||||
to a request. Use with care! You especially *must* ensure that any such header is not
|
||||
passed from your proxy server to paperless.
|
||||
|
||||
If you're exposing paperless to the internet directly, do not use this.
|
||||
|
||||
Also see the warning `in the official documentation <https://docs.djangoproject.com/en/3.1/howto/auth-remote-user/#configuration>`.
|
||||
|
||||
Defaults to `false` which disables this feature.
|
||||
|
||||
PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=<str>
|
||||
If `PAPERLESS_ENABLE_HTTP_REMOTE_USER` is enabled, this property allows to
|
||||
customize the name of the HTTP header from which the authenticated username
|
||||
is extracted. Values are in terms of
|
||||
[HttpRequest.META](https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpRequest.META).
|
||||
Thus, the configured value must start with `HTTP_` followed by the
|
||||
normalized actual header name.
|
||||
|
||||
Defaults to `HTTP_REMOTE_USER`.
|
||||
|
||||
PAPERLESS_LOGOUT_REDIRECT_URL=<str>
|
||||
URL to redirect the user to after a logout. This can be used together with
|
||||
`PAPERLESS_ENABLE_HTTP_REMOTE_USER` to redirect the user back to the SSO
|
||||
application's logout page.
|
||||
|
||||
Defaults to None, which disables this feature.
|
||||
|
||||
.. _configuration-ocr:
|
||||
|
||||
OCR settings
|
||||
@@ -202,7 +284,6 @@ Paperless uses `OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/>`_ for
|
||||
performing OCR on documents and images. Paperless uses sensible defaults for
|
||||
most settings, but all of them can be configured to your needs.
|
||||
|
||||
|
||||
PAPERLESS_OCR_LANGUAGE=<lang>
|
||||
Customize the language that paperless will attempt to use when
|
||||
parsing documents.
|
||||
@@ -219,6 +300,8 @@ PAPERLESS_OCR_LANGUAGE=<lang>
|
||||
|
||||
Defaults to "eng".
|
||||
|
||||
Note: If your language contains a '-' such as chi-sim, you must use chi_sim
|
||||
|
||||
PAPERLESS_OCR_MODE=<mode>
|
||||
Tell paperless when and how to perform ocr on your documents. Four modes
|
||||
are available:
|
||||
@@ -245,6 +328,54 @@ PAPERLESS_OCR_MODE=<mode>
|
||||
The default is ``skip``, which only performs OCR when necessary and always
|
||||
creates archived documents.
|
||||
|
||||
Read more about this in the `OCRmyPDF documentation <https://ocrmypdf.readthedocs.io/en/latest/advanced.html#when-ocr-is-skipped>`_.
|
||||
|
||||
PAPERLESS_OCR_CLEAN=<mode>
|
||||
Tells paperless to use ``unpaper`` to clean any input document before
|
||||
sending it to tesseract. This uses more resources, but generally results
|
||||
in better OCR results. The following modes are available:
|
||||
|
||||
* ``clean``: Apply unpaper.
|
||||
* ``clean-final``: Apply unpaper, and use the cleaned images to build the
|
||||
output file instead of the original images.
|
||||
* ``none``: Do not apply unpaper.
|
||||
|
||||
Defaults to ``clean``.
|
||||
|
||||
.. note::
|
||||
|
||||
``clean-final`` is incompatible with ocr mode ``redo``. When both
|
||||
``clean-final`` and the ocr mode ``redo`` is configured, ``clean``
|
||||
is used instead.
|
||||
|
||||
PAPERLESS_OCR_DESKEW=<bool>
|
||||
Tells paperless to correct skewing (slight rotation of input images mainly
|
||||
due to improper scanning)
|
||||
|
||||
Defaults to ``true``, which enables this feature.
|
||||
|
||||
.. note::
|
||||
|
||||
Deskewing is incompatible with ocr mode ``redo``. Deskewing will get
|
||||
disabled automatically if ``redo`` is used as the ocr mode.
|
||||
|
||||
PAPERLESS_OCR_ROTATE_PAGES=<bool>
|
||||
Tells paperless to correct page rotation (90°, 180° and 270° rotation).
|
||||
|
||||
If you notice that paperless is not rotating incorrectly rotated
|
||||
pages (or vice versa), try adjusting the threshold up or down (see below).
|
||||
|
||||
Defaults to ``true``, which enables this feature.
|
||||
|
||||
|
||||
PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=<num>
|
||||
Adjust the threshold for automatic page rotation by ``PAPERLESS_OCR_ROTATE_PAGES``.
|
||||
This is an arbitrary value reported by tesseract. "15" is a very conservative value,
|
||||
whereas "2" is a very aggressive option and will often result in correctly rotated pages
|
||||
being rotated as well.
|
||||
|
||||
Defaults to "12".
|
||||
|
||||
PAPERLESS_OCR_OUTPUT_TYPE=<type>
|
||||
Specify the the type of PDF documents that paperless should produce.
|
||||
|
||||
@@ -271,7 +402,6 @@ PAPERLESS_OCR_PAGES=<num>
|
||||
|
||||
Defaults to 0, which disables this feature and always uses all pages.
|
||||
|
||||
|
||||
PAPERLESS_OCR_IMAGE_DPI=<num>
|
||||
Paperless will OCR any images you put into the system and convert them
|
||||
into PDF documents. This is useful if your scanner produces images.
|
||||
@@ -282,9 +412,18 @@ PAPERLESS_OCR_IMAGE_DPI=<num>
|
||||
|
||||
Set this to the DPI your scanner produces images at.
|
||||
|
||||
Default is none, which causes paperless to fail if no DPI information is
|
||||
present in an image.
|
||||
Default is none, which will automatically calculate image DPI so that
|
||||
the produced PDF documents are A4 sized.
|
||||
|
||||
PAPERLESS_OCR_MAX_IMAGE_PIXELS=<num>
|
||||
Paperless will not OCR images that have more pixels than this limit.
|
||||
This is intended to prevent decompression bombs from overloading paperless.
|
||||
Increasing this limit is desired if you face a DecompressionBombError despite
|
||||
the concerning file not being malicious; this could e.g. be caused by invalidly
|
||||
recognized metadata.
|
||||
If you have enough resources or if you are certain that your uploaded files
|
||||
are not malicious you can increase this value to your needs.
|
||||
The default value is 256000000, an image with more pixels than that would not be parsed.
|
||||
|
||||
PAPERLESS_OCR_USER_ARGS=<json>
|
||||
OCRmyPDF offers many more options. Use this parameter to specify any
|
||||
@@ -292,7 +431,7 @@ PAPERLESS_OCR_USER_ARGS=<json>
|
||||
the API of OCRmyPDF, you have to specify these in a format that can be
|
||||
passed to the API. See `the API reference of OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/api.html#reference>`_
|
||||
for valid parameters. All command line options are supported, but they
|
||||
use underscores instead of dashed.
|
||||
use underscores instead of dashes.
|
||||
|
||||
.. caution::
|
||||
|
||||
@@ -314,7 +453,7 @@ Tika settings
|
||||
#############
|
||||
|
||||
Paperless can make use of `Tika <https://tika.apache.org/>`_ and
|
||||
`Gotenberg <https://thecodingmachine.github.io/gotenberg/>`_ for parsing and
|
||||
`Gotenberg <https://gotenberg.dev/>`_ for parsing and
|
||||
converting "Office" documents (such as ".doc", ".xlsx" and ".odt"). If you
|
||||
wish to use this, you must provide a Tika server and a Gotenberg server,
|
||||
configure their endpoints, and enable the feature.
|
||||
@@ -335,7 +474,7 @@ PAPERLESS_TIKA_GOTENBERG_ENDPOINT=<url>
|
||||
Defaults to "http://localhost:3000".
|
||||
|
||||
If you run paperless on docker, you can add those services to the docker-compose
|
||||
file (see the provided ``docker-compose.tika.yml`` file for reference). The changes
|
||||
file (see the provided ``docker-compose.sqlite-tika.yml`` file for reference). The changes
|
||||
requires are as follows:
|
||||
|
||||
.. code:: yaml
|
||||
@@ -352,23 +491,26 @@ requires are as follows:
|
||||
PAPERLESS_TIKA_ENABLED: 1
|
||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||
|
||||
|
||||
# ...
|
||||
|
||||
gotenberg:
|
||||
image: thecodingmachine/gotenberg
|
||||
image: gotenberg/gotenberg:7.4
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DISABLE_GOOGLE_CHROME: 1
|
||||
command:
|
||||
- "gotenberg"
|
||||
- "--chromium-disable-routes=true"
|
||||
|
||||
tika:
|
||||
image: apache/tika
|
||||
image: ghcr.io/paperless-ngx/tika:latest
|
||||
restart: unless-stopped
|
||||
|
||||
Add the configuration variables to the environment of the webserver (alternatively
|
||||
put the configuration in the ``docker-compose.env`` file) and add the additional
|
||||
services below the webserver service. Watch out for indentation.
|
||||
|
||||
Make sure to use the correct format `PAPERLESS_TIKA_ENABLED = 1` so python_dotenv can parse the statement correctly.
|
||||
|
||||
Software tweaks
|
||||
###############
|
||||
|
||||
@@ -419,6 +561,16 @@ PAPERLESS_THREADS_PER_WORKER=<num>
|
||||
PAPERLESS_THREADS_PER_WORKER automatically.
|
||||
|
||||
|
||||
PAPERLESS_WORKER_TIMEOUT=<num>
|
||||
Machines with few cores or weak ones might not be able to finish OCR on
|
||||
large documents within the default 1800 seconds. So extending this timeout
|
||||
may prove to be useful on weak hardware setups.
|
||||
|
||||
PAPERLESS_WORKER_RETRY=<num>
|
||||
If PAPERLESS_WORKER_TIMEOUT has been configured, the retry time for a task can
|
||||
also be configured. By default, this value will be set to 10s more than the
|
||||
worker timeout. This value should never be set less than the worker timeout.
|
||||
|
||||
PAPERLESS_TIME_ZONE=<timezone>
|
||||
Set the time zone here.
|
||||
See https://docs.djangoproject.com/en/3.1/ref/settings/#std:setting-TIME_ZONE
|
||||
@@ -466,6 +618,38 @@ PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=<bool>
|
||||
|
||||
Defaults to false.
|
||||
|
||||
PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>
|
||||
Enables the scanning and page separation based on detected barcodes.
|
||||
This allows for scanning and adding multiple documents per uploaded
|
||||
file, which are separated by one or multiple barcode pages.
|
||||
|
||||
For ease of use, it is suggested to use a standardized separation page,
|
||||
e.g. `here <https://www.alliancegroup.co.uk/patch-codes.htm>`_.
|
||||
|
||||
If no barcodes are detected in the uploaded file, no page separation
|
||||
will happen.
|
||||
|
||||
The original document will be removed and the separated pages will be
|
||||
saved as pdf.
|
||||
|
||||
Defaults to false.
|
||||
|
||||
PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT=<bool>
|
||||
Whether TIFF image files should be scanned for barcodes.
|
||||
This will automatically convert any TIFF image(s) to pdfs for later
|
||||
processing.
|
||||
This only has an effect, if PAPERLESS_CONSUMER_ENABLE_BARCODES has been
|
||||
enabled.
|
||||
|
||||
Defaults to false.
|
||||
|
||||
PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT
|
||||
Defines the string to be detected as a separator barcode.
|
||||
If paperless is used with the PATCH-T separator pages, users
|
||||
shouldn't change this.
|
||||
|
||||
Defaults to "PATCHT"
|
||||
|
||||
|
||||
PAPERLESS_CONVERT_MEMORY_LIMIT=<num>
|
||||
On smaller systems, or even in the case of Very Large Documents, the consumer
|
||||
@@ -536,6 +720,20 @@ PAPERLESS_IGNORE_DATES=<string>
|
||||
|
||||
Defaults to an empty string to not ignore any dates.
|
||||
|
||||
PAPERLESS_DATE_ORDER=<format>
|
||||
Paperless will try to determine the document creation date from its contents.
|
||||
Specify the date format Paperless should expect to see within your documents.
|
||||
|
||||
This option defaults to DMY which translates to day first, month second, and year
|
||||
last order. Characters D, M, or Y can be shuffled to meet the required order.
|
||||
|
||||
PAPERLESS_CONSUMER_IGNORE_PATTERNS=<json>
|
||||
By default, paperless ignores certain files and folders in the consumption
|
||||
directory, such as system files created by the Mac OS.
|
||||
|
||||
This can be adjusted by configuring a custom json array with patterns to exclude.
|
||||
|
||||
Defaults to ``[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini"]``.
|
||||
|
||||
Binaries
|
||||
########
|
||||
@@ -555,3 +753,99 @@ PAPERLESS_GS_BINARY=<path>
|
||||
|
||||
PAPERLESS_OPTIPNG_BINARY=<path>
|
||||
Defaults to "/usr/bin/optipng".
|
||||
|
||||
|
||||
.. _configuration-docker:
|
||||
|
||||
Docker-specific options
|
||||
#######################
|
||||
|
||||
These options don't have any effect in ``paperless.conf``. These options adjust
|
||||
the behavior of the docker container. Configure these in `docker-compose.env`.
|
||||
|
||||
PAPERLESS_WEBSERVER_WORKERS=<num>
|
||||
The number of worker processes the webserver should spawn. More worker processes
|
||||
usually result in the front end to load data much quicker. However, each worker process
|
||||
also loads the entire application into memory separately, so increasing this value
|
||||
will increase RAM usage.
|
||||
|
||||
Consider configuring this to 1 on low power devices with limited amount of RAM.
|
||||
|
||||
Defaults to 2.
|
||||
|
||||
PAPERLESS_PORT=<port>
|
||||
The port number the webserver will listen on inside the container. There are
|
||||
special setups where you may need this to avoid collisions with other
|
||||
services (like using podman with multiple containers in one pod).
|
||||
|
||||
Don't change this when using Docker. To change the port the webserver is
|
||||
reachable outside of the container, instead refer to the "ports" key in
|
||||
``docker-compose.yml``.
|
||||
|
||||
Defaults to 8000.
|
||||
|
||||
USERMAP_UID=<uid>
|
||||
The ID of the paperless user in the container. Set this to your actual user ID on the
|
||||
host system, which you can get by executing
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ id -u
|
||||
|
||||
Paperless will change ownership on its folders to this user, so you need to get this right
|
||||
in order to be able to write to the consumption directory.
|
||||
|
||||
Defaults to 1000.
|
||||
|
||||
USERMAP_GID=<gid>
|
||||
The ID of the paperless Group in the container. Set this to your actual group ID on the
|
||||
host system, which you can get by executing
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ id -g
|
||||
|
||||
Paperless will change ownership on its folders to this group, so you need to get this right
|
||||
in order to be able to write to the consumption directory.
|
||||
|
||||
Defaults to 1000.
|
||||
|
||||
PAPERLESS_OCR_LANGUAGES=<list>
|
||||
Additional OCR languages to install. By default, paperless comes with
|
||||
English, German, Italian, Spanish and French. If your language is not in this list, install
|
||||
additional languages with this configuration option:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
PAPERLESS_OCR_LANGUAGES=tur ces
|
||||
|
||||
To actually use these languages, also set the default OCR language of paperless:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
PAPERLESS_OCR_LANGUAGE=tur
|
||||
|
||||
Defaults to none, which does not install any additional languages.
|
||||
|
||||
|
||||
.. _configuration-update-checking:
|
||||
|
||||
Update Checking
|
||||
###############
|
||||
|
||||
PAPERLESS_ENABLE_UPDATE_CHECK=<bool>
|
||||
Enable (or disable) the automatic check for available updates. This feature is disabled
|
||||
by default but if it is not explicitly set Paperless-ngx will show a message about this.
|
||||
|
||||
If enabled, the feature works by pinging the the Github API for the latest release e.g.
|
||||
https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest
|
||||
to determine whether a new version is available.
|
||||
|
||||
Actual updating of the app must still be performed manually.
|
||||
|
||||
Note that for users of thirdy-party containers e.g. linuxserver.io this notification
|
||||
may be 'ahead' of a new release from the third-party maintainers.
|
||||
|
||||
In either case, no tracking data is collected by the app in any way.
|
||||
|
||||
Defaults to none, which disables the feature.
|
||||
|
@@ -1,145 +0,0 @@
|
||||
.. _contributing:
|
||||
|
||||
Contributing to Paperless
|
||||
#########################
|
||||
|
||||
.. warning::
|
||||
|
||||
This section is not updated to paperless-ng yet.
|
||||
|
||||
Maybe you've been using Paperless for a while and want to add a feature or two,
|
||||
or maybe you've come across a bug that you have some ideas how to solve. The
|
||||
beauty of Free software is that you can see what's wrong and help to get it
|
||||
fixed for everyone!
|
||||
|
||||
|
||||
How to Get Your Changes Rolled Into Paperless
|
||||
=============================================
|
||||
|
||||
If you've found a bug, but don't know how to fix it, you can always post an
|
||||
issue on `GitHub`_ in the hopes that someone will have the time to fix it for
|
||||
you. If however you're the one with the time, pull requests are always
|
||||
welcome, you just have to make sure that your code conforms to a few standards:
|
||||
|
||||
Pep8
|
||||
----
|
||||
|
||||
It's the standard for all Python development, so it's `very well documented`_.
|
||||
The short version is:
|
||||
|
||||
* Lines should wrap at 79 characters
|
||||
* Use ``snake_case`` for variables, ``CamelCase`` for classes, and ``ALL_CAPS``
|
||||
for constants.
|
||||
* Space out your operators: ``stuff + 7`` instead of ``stuff+7``
|
||||
* Two empty lines between classes, and functions, but 1 empty line between
|
||||
class methods.
|
||||
|
||||
There's more to it than that, but if you follow those, you'll probably be
|
||||
alright. When you submit your pull request, there's a pep8 checker that'll
|
||||
look at your code to see if anything is off. If it finds anything, it'll
|
||||
complain at you until you fix it.
|
||||
|
||||
|
||||
Additional Style Guides
|
||||
-----------------------
|
||||
|
||||
Where pep8 is ambiguous, I've tried to be a little more specific. These rules
|
||||
aren't hard-and-fast, but if you can conform to them, I'll appreciate it and
|
||||
spend less time trying to conform your PR before merging:
|
||||
|
||||
|
||||
Function calls
|
||||
..............
|
||||
|
||||
If you're calling a function and that necessitates more than one line of code,
|
||||
please format it like this:
|
||||
|
||||
.. code:: python
|
||||
|
||||
my_function(
|
||||
argument1,
|
||||
kwarg1="x",
|
||||
kwarg2="y"
|
||||
another_really_long_kwarg="some big value"
|
||||
a_kwarg_calling_another_long_function=another_function(
|
||||
another_arg,
|
||||
another_kwarg="kwarg!"
|
||||
)
|
||||
)
|
||||
|
||||
This is all in the interest of code uniformity rather than anything else. If
|
||||
we stick to a style, everything is understandable in the same way.
|
||||
|
||||
|
||||
Quoting Strings
|
||||
...............
|
||||
|
||||
pep8 is a little too open-minded on this for my liking. Python strings should
|
||||
be quoted with double quotes (``"``) except in cases where the resulting string
|
||||
would require too much escaping of a double quote, in which case, a single
|
||||
quoted, or triple-quoted string will do:
|
||||
|
||||
.. code:: python
|
||||
|
||||
my_string = "This is my string"
|
||||
problematic_string = 'This is a "string" with "quotes" in it'
|
||||
|
||||
In HTML templates, please use double-quotes for tag attributes, and single
|
||||
quotes for arguments passed to Django template tags:
|
||||
|
||||
.. code:: html
|
||||
|
||||
<div class="stuff">
|
||||
<a href="{% url 'some-url-name' pk='w00t' %}">link this</a>
|
||||
</div>
|
||||
|
||||
This is to keep linters happy they look at an HTML file and see an attribute
|
||||
closing the ``"`` before it should have been.
|
||||
|
||||
--
|
||||
|
||||
That's all there is in terms of guidelines, so I hope it's not too daunting.
|
||||
|
||||
|
||||
Indentation & Spacing
|
||||
.....................
|
||||
|
||||
When it comes to indentation:
|
||||
|
||||
* For Python, the rule is: follow pep8 and use 4 spaces.
|
||||
* For Javascript, CSS, and HTML, please use 1 tab.
|
||||
|
||||
Additionally, Django templates making use of block elements like ``{% if %}``,
|
||||
``{% for %}``, and ``{% block %}`` etc. should be indented:
|
||||
|
||||
Good:
|
||||
|
||||
.. code:: html
|
||||
|
||||
{% block stuff %}
|
||||
<h1>This is the stuff</h1>
|
||||
{% endblock %}
|
||||
|
||||
Bad:
|
||||
|
||||
.. code:: html
|
||||
|
||||
{% block stuff %}
|
||||
<h1>This is the stuff</h1>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
The Code of Conduct
|
||||
===================
|
||||
|
||||
Paperless has a `code of conduct`_. It's a lot like the other ones you see out
|
||||
there, with a few small changes, but basically it boils down to:
|
||||
|
||||
> Don't be an ass, or you might get banned.
|
||||
|
||||
I'm proud to say that the CoC has never had to be enforced because everyone has
|
||||
been awesome, friendly, and professional.
|
||||
|
||||
.. _GitHub: https://github.com/the-paperless-project/paperless/issues
|
||||
.. _very well documented: https://www.python.org/dev/peps/pep-0008/
|
||||
.. _code of conduct: https://github.com/the-paperless-project/paperless/blob/master/CODE_OF_CONDUCT.md
|
@@ -1,76 +1,163 @@
|
||||
.. _extending:
|
||||
|
||||
Paperless development
|
||||
#####################
|
||||
Paperless-ngx Development
|
||||
#########################
|
||||
|
||||
This section describes the steps you need to take to start development on paperless-ng.
|
||||
This section describes the steps you need to take to start development on paperless-ngx.
|
||||
|
||||
1. Check out the source from github. The repository is organized in the following way:
|
||||
Check out the source from github. The repository is organized in the following way:
|
||||
|
||||
* ``master`` always represents the latest release and will only see changes
|
||||
when a new release is made.
|
||||
* ``dev`` contains the code that will be in the next release.
|
||||
* ``feature-X`` contain bigger changes that will be in some release, but not
|
||||
necessarily the next one.
|
||||
|
||||
Apart from that, the folder structure is as follows:
|
||||
* ``main`` always represents the latest release and will only see changes
|
||||
when a new release is made.
|
||||
* ``dev`` contains the code that will be in the next release.
|
||||
* ``feature-X`` contain bigger changes that will be in some release, but not
|
||||
necessarily the next one.
|
||||
|
||||
* ``docs/`` - Documentation.
|
||||
* ``src-ui/`` - Code of the front end.
|
||||
* ``src/`` - Code of the back end.
|
||||
* ``scripts/`` - Various scripts that help with different parts of development.
|
||||
* ``docker/`` - Files required to build the docker image.
|
||||
When making functional changes to paperless, *always* make your changes on the ``dev`` branch.
|
||||
|
||||
2. Install some dependencies.
|
||||
Apart from that, the folder structure is as follows:
|
||||
|
||||
* Python 3.6.
|
||||
* All dependencies listed in the :ref:`Bare metal route <setup-bare_metal>`
|
||||
* redis. You can either install redis or use the included scritps/start-services.sh
|
||||
to use docker to fire up a redis instance (and some other services such as tika,
|
||||
gotenberg and a postgresql server).
|
||||
* ``docs/`` - Documentation.
|
||||
* ``src-ui/`` - Code of the front end.
|
||||
* ``src/`` - Code of the back end.
|
||||
* ``scripts/`` - Various scripts that help with different parts of development.
|
||||
* ``docker/`` - Files required to build the docker image.
|
||||
|
||||
Contributing to Paperless
|
||||
=========================
|
||||
|
||||
Maybe you've been using Paperless for a while and want to add a feature or two,
|
||||
or maybe you've come across a bug that you have some ideas how to solve. The
|
||||
beauty of open source software is that you can see what's wrong and help to get
|
||||
it fixed for everyone!
|
||||
|
||||
Before contributing please review our `code of conduct`_ and other important
|
||||
information in the `contributing guidelines`_.
|
||||
|
||||
.. _code-formatting-with-pre-commit-hooks:
|
||||
|
||||
Code formatting with pre-commit Hooks
|
||||
=====================================
|
||||
|
||||
To ensure a consistent style and formatting across the project source, the project
|
||||
utilizes a Git `pre-commit` hook to perform some formatting and linting before a
|
||||
commit is allowed. That way, everyone uses the same style and some common issues
|
||||
can be caught early on. See below for installation instructions.
|
||||
|
||||
Once installed, hooks will run when you commit. If the formatting isn't quite right
|
||||
or a linter catches something, the commit will be rejected. You'll need to look at the
|
||||
output and fix the issue. Some hooks, such as the Python formatting tool `black`,
|
||||
will format failing files, so all you need to do is `git add` those files again and
|
||||
retry your commit.
|
||||
|
||||
Initial setup and first start
|
||||
=============================
|
||||
|
||||
After you forked and cloned the code from github you need to perform a first-time setup.
|
||||
To do the setup you need to perform the steps from the following chapters in a certain order:
|
||||
|
||||
1. Install prerequisites + pipenv as mentioned in :ref:`Bare metal route <setup-bare_metal>`
|
||||
2. Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
|
||||
3. Install the Angular CLI interface:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ npm install -g @angular/cli
|
||||
|
||||
4. Install pre-commit
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
pre-commit install
|
||||
|
||||
5. Create ``consume`` and ``media`` folders in the cloned root folder.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
mkdir -p consume media
|
||||
|
||||
6. You can now either ...
|
||||
|
||||
* install redis or
|
||||
* use the included scripts/start-services.sh to use docker to fire up a redis instance (and some other services such as tika, gotenberg and a postgresql server) or
|
||||
* spin up a bare redis container
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
docker run -d -p 6379:6379 --restart unless-stopped redis:latest
|
||||
|
||||
7. Install the python dependencies by performing in the src/ directory.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
pipenv install --dev
|
||||
|
||||
* Make sure you're using python 3.9.x or lower. Otherwise you might get issues with building dependencies. You can use `pyenv <https://github.com/pyenv/pyenv>`_ to install a specific python version.
|
||||
|
||||
8. Generate the static UI so you can perform a login to get session that is required for frontend development (this needs to be done one time only). From src-ui directory:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
npm install .
|
||||
./node_modules/.bin/ng build --configuration production
|
||||
|
||||
9. Apply migrations and create a superuser for your dev instance:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
python3 manage.py migrate
|
||||
python3 manage.py createsuperuser
|
||||
|
||||
10. Now spin up the dev backend. Depending on which part of paperless you're developing for, you need to have some or all of them running.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
|
||||
|
||||
11. Login with the superuser credentials provided in step 8 at ``http://localhost:8000`` to create a session that enables you to use the backend.
|
||||
|
||||
Backend development environment is now ready, to start Frontend development go to ``/src-ui`` and run ``ng serve``. From there you can use ``http://localhost:4200`` for a preview.
|
||||
|
||||
Back end development
|
||||
====================
|
||||
|
||||
The backend is a django application. I use PyCharm for development, but you can use whatever
|
||||
The backend is a django application. PyCharm works well for development, but you can use whatever
|
||||
you want.
|
||||
|
||||
Install the python dependencies by performing ``pipenv install --dev`` in the src/ directory.
|
||||
This will also create a virtual environment, which you can enter with ``pipenv shell`` or
|
||||
execute one-shot commands in with ``pipenv run``.
|
||||
|
||||
Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
|
||||
|
||||
Configure the IDE to use the src/ folder as the base source folder. Configure the following
|
||||
launch configurations in your IDE:
|
||||
|
||||
* python3 manage.py runserver
|
||||
* python3 manage.py qcluster
|
||||
* python3 manage.py consumer
|
||||
* python3 manage.py document_consumer
|
||||
|
||||
Depending on which part of paperless you're developing for, you need to have some or all of
|
||||
them running.
|
||||
To start them all:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
|
||||
|
||||
Testing and code style:
|
||||
|
||||
* Run ``pytest`` in the src/ directory to execute all tests. This also generates a HTML coverage
|
||||
report. When runnings test, paperless.conf is loaded as well. However: the tests rely on the default
|
||||
configuration. This is not ideal. But for now, make sure no settings except for DEBUG are overridden when testing.
|
||||
* Run ``pycodestyle`` to test your code for issues with the configured code style settings.
|
||||
* Coding style is enforced by the Git pre-commit hooks. These will ensure your code is formatted and do some
|
||||
linting when you do a `git commit`.
|
||||
* You can also run ``black`` manually to format your code
|
||||
|
||||
.. note::
|
||||
|
||||
The line length rule E501 is generally useful for getting multiple source files
|
||||
next to each other on the screen. However, in some cases, its just not possible
|
||||
to make some lines fit, especially complicated IF cases. Append `` # NOQA: E501``
|
||||
to make some lines fit, especially complicated IF cases. Append ``# NOQA: E501``
|
||||
to disable this check for certain lines.
|
||||
|
||||
Front end development
|
||||
=====================
|
||||
|
||||
The front end is build using angular. I use the ``Code - OSS`` IDE for development.
|
||||
|
||||
In order to get started, you need ``npm``. Install the Angular CLI interface with
|
||||
The front end is built using Angular. In order to get started, you need ``npm``.
|
||||
Install the Angular CLI interface with
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
@@ -99,6 +186,31 @@ X-Frame-Options are in place so that the front end behaves exactly as in product
|
||||
relies on you being logged into the back end. Without a valid session, The front end will simply
|
||||
not work.
|
||||
|
||||
Testing and code style:
|
||||
|
||||
* The frontend code (.ts, .html, .scss) use ``prettier`` for code formatting via the Git
|
||||
``pre-commit`` hooks which run automatically on commit. See
|
||||
:ref:`above <code-formatting-with-pre-commit-hooks>` for installation. You can also run this
|
||||
via cli with a command such as
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
|
||||
|
||||
* Frontend testing uses jest and cypress. There is currently a need for significantly more
|
||||
frontend tests. Unit tests and e2e tests, respectively, can be run non-interactively with:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ ng test
|
||||
$ npm run e2e:ci
|
||||
|
||||
Cypress also includes a UI which can be run from within the ``src-ui`` directory with
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ ./node_modules/.bin/cypress open
|
||||
|
||||
In order to build the front end and serve it as part of django, execute
|
||||
|
||||
.. code:: shell-session
|
||||
@@ -109,6 +221,131 @@ This will build the front end and put it in a location from which the Django ser
|
||||
it as static content. This way, you can verify that authentication is working.
|
||||
|
||||
|
||||
Localization
|
||||
============
|
||||
|
||||
Paperless is available in many different languages. Since paperless consists both of a django
|
||||
application and an Angular front end, both these parts have to be translated separately.
|
||||
|
||||
Front end localization
|
||||
----------------------
|
||||
|
||||
* The Angular front end does localization according to the `Angular documentation <https://angular.io/guide/i18n>`_.
|
||||
* The source language of the project is "en_US".
|
||||
* The source strings end up in the file "src-ui/messages.xlf".
|
||||
* The translated strings need to be placed in the "src-ui/src/locale/" folder.
|
||||
* In order to extract added or changed strings from the source files, call ``ng xi18n --ivy``.
|
||||
|
||||
Adding new languages requires adding the translated files in the "src-ui/src/locale/" folder and adjusting a couple files.
|
||||
|
||||
1. Adjust "src-ui/angular.json":
|
||||
|
||||
.. code:: json
|
||||
|
||||
"i18n": {
|
||||
"sourceLocale": "en-US",
|
||||
"locales": {
|
||||
"de": "src/locale/messages.de.xlf",
|
||||
"nl-NL": "src/locale/messages.nl_NL.xlf",
|
||||
"fr": "src/locale/messages.fr.xlf",
|
||||
"en-GB": "src/locale/messages.en_GB.xlf",
|
||||
"pt-BR": "src/locale/messages.pt_BR.xlf",
|
||||
"language-code": "language-file"
|
||||
}
|
||||
}
|
||||
|
||||
2. Add the language to the available options in "src-ui/src/app/services/settings.service.ts":
|
||||
|
||||
.. code:: typescript
|
||||
|
||||
getLanguageOptions(): LanguageOption[] {
|
||||
return [
|
||||
{code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
|
||||
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
|
||||
{code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
|
||||
{code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
|
||||
{code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
|
||||
{code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
|
||||
// Add your new language here
|
||||
]
|
||||
}
|
||||
|
||||
``dateInputFormat`` is a special string that defines the behavior of the date input fields and absolutely needs to contain "dd", "mm" and "yyyy".
|
||||
|
||||
3. Import and register the Angular data for this locale in "src-ui/src/app/app.module.ts":
|
||||
|
||||
.. code:: typescript
|
||||
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
registerLocaleData(localeDe)
|
||||
|
||||
Back end localization
|
||||
---------------------
|
||||
|
||||
A majority of the strings that appear in the back end appear only when the admin is used. However,
|
||||
some of these are still shown on the front end (such as error messages).
|
||||
|
||||
* The django application does localization according to the `django documentation <https://docs.djangoproject.com/en/3.1/topics/i18n/translation/>`_.
|
||||
* The source language of the project is "en_US".
|
||||
* Localization files end up in the folder "src/locale/".
|
||||
* In order to extract strings from the application, call ``python3 manage.py makemessages -l en_US``. This is important after making changes to translatable strings.
|
||||
* The message files need to be compiled for them to show up in the application. Call ``python3 manage.py compilemessages`` to do this. The generated files don't get
|
||||
committed into git, since these are derived artifacts. The build pipeline takes care of executing this command.
|
||||
|
||||
Adding new languages requires adding the translated files in the "src/locale/" folder and adjusting the file "src/paperless/settings.py" to include the new language:
|
||||
|
||||
.. code:: python
|
||||
|
||||
LANGUAGES = [
|
||||
("en-us", _("English (US)")),
|
||||
("en-gb", _("English (GB)")),
|
||||
("de", _("German")),
|
||||
("nl-nl", _("Dutch")),
|
||||
("fr", _("French")),
|
||||
("pt-br", _("Portuguese (Brazil)")),
|
||||
# Add language here.
|
||||
]
|
||||
|
||||
|
||||
Building the documentation
|
||||
==========================
|
||||
|
||||
The documentation is built using sphinx. I've configured ReadTheDocs to automatically build
|
||||
the documentation when changes are pushed. If you want to build the documentation locally,
|
||||
this is how you do it:
|
||||
|
||||
1. Install python dependencies.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless
|
||||
$ pipenv install --dev
|
||||
|
||||
2. Build the documentation
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless/docs
|
||||
$ pipenv run make clean html
|
||||
|
||||
This will build the HTML documentation, and put the resulting files in the ``_build/html``
|
||||
directory.
|
||||
|
||||
Building the Docker image
|
||||
=========================
|
||||
|
||||
The docker image is primarily built by the GitHub actions workflow, but it can be
|
||||
faster when developing to build and tag an image locally.
|
||||
|
||||
To provide the build arguments automatically, build the image using the helper
|
||||
script ``build-docker-image.sh``.
|
||||
|
||||
Building the docker image from source:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
./build-docker-image.sh Dockerfile -t <your-tag>
|
||||
|
||||
Extending Paperless
|
||||
===================
|
||||
|
||||
@@ -143,7 +380,7 @@ methods ``parse`` and ``get_thumbnail``. You can provide your own implementation
|
||||
|
||||
# The content of the document.
|
||||
self.text = "content"
|
||||
|
||||
|
||||
# Optional: path to a PDF document that you created from the original.
|
||||
self.archive_path = os.path.join(self.tempdir, "archived.pdf")
|
||||
|
||||
@@ -189,3 +426,6 @@ that returns information about your parser:
|
||||
download. We could guess that from the file extensions, but some mime types have many extensions
|
||||
associated with them and the python methods responsible for guessing the extension do not always
|
||||
return the same value.
|
||||
|
||||
.. _code of conduct: https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md
|
||||
.. _contributing guidelines: https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md
|
||||
|
39
docs/faq.rst
@@ -3,17 +3,13 @@
|
||||
Frequently asked questions
|
||||
**************************
|
||||
|
||||
**Q:** *What's the general plan for Paperless-ng?*
|
||||
**Q:** *What's the general plan for Paperless-ngx?*
|
||||
|
||||
**A:** Paperless-ng is already almost feature-complete. This project will remain
|
||||
as simple as it is right now. It will see improvements to features that are already there.
|
||||
If you need advanced features such as document versions,
|
||||
workflows or multi-user with customizable access to individual files, this is
|
||||
not the tool for you.
|
||||
|
||||
Features that *are* planned are some more quality of life extensions for the searching
|
||||
(i.e., search for similar documents, group results by correspondents with "more from this"
|
||||
links, etc), bulk editing and hierarchical tags.
|
||||
**A:** While Paperless-ngx is already considered largely "feature-complete" it is a community-driven
|
||||
project and development will be guided in this way. New features can be submitted via
|
||||
GitHub discussions and "up-voted" by the community but this is not a guarantee the feature
|
||||
will be implemented. This project will always be open to collaboration in the form of PRs,
|
||||
ideas etc.
|
||||
|
||||
**Q:** *I'm using docker. Where are my documents?*
|
||||
|
||||
@@ -33,19 +29,19 @@ is
|
||||
files around manually. This folder is meant to be entirely managed by docker
|
||||
and paperless.
|
||||
|
||||
**Q:** *Let's say you don't support this project anymore in a year. Can I easily move to other systems?*
|
||||
**Q:** *Let's say I want to switch tools in a year. Can I easily move to other systems?*
|
||||
|
||||
**A:** Your documents are stored as plain files inside the media folder. You can always drag those files
|
||||
out of that folder to use them elsewhere. Here are a couple notes about that.
|
||||
|
||||
* Paperless never modifies your original documents. It keeps checksums of all documents and uses a
|
||||
* Paperless-ngx never modifies your original documents. It keeps checksums of all documents and uses a
|
||||
scheduled sanity checker to check that they remain the same.
|
||||
* By default, paperless uses the internal ID of each document as its filename. This might not be very
|
||||
convenient for export. However, you can adjust the way files are stored in paperless by
|
||||
:ref:`configuring the filename format <advanced-file_name_handling>`.
|
||||
* :ref:`The exporter <utilities-exporter>` is another easy way to get your files out of paperless with reasonable file names.
|
||||
|
||||
**Q:** *What file types does paperless-ng support?*
|
||||
**Q:** *What file types does paperless-ngx support?*
|
||||
|
||||
**A:** Currently, the following files are supported:
|
||||
|
||||
@@ -55,10 +51,10 @@ out of that folder to use them elsewhere. Here are a couple notes about that.
|
||||
* With the optional Tika integration enabled (see :ref:`Configuration <configuration-tika>`), Paperless also supports various
|
||||
Office documents (.docx, .doc, odt, .ppt, .pptx, .odp, .xls, .xlsx, .ods).
|
||||
|
||||
Paperless determines the type of a file by inspecting its content. The
|
||||
Paperless-ngx determines the type of a file by inspecting its content. The
|
||||
file extensions do not matter.
|
||||
|
||||
**Q:** *Will paperless-ng run on Raspberry Pi?*
|
||||
**Q:** *Will paperless-ngx run on Raspberry Pi?*
|
||||
|
||||
**A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B.
|
||||
The long answer is that certain parts of
|
||||
@@ -68,12 +64,12 @@ reuse the text. The web interface is a lot snappier, since it runs
|
||||
in your browser and paperless has to do much less work to serve the data.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
You can adjust some of the settings so that paperless uses less processing
|
||||
power. See :ref:`setup-less_powerful_devices` for details.
|
||||
|
||||
|
||||
**Q:** *How do I install paperless-ng on Raspberry Pi?*
|
||||
|
||||
**Q:** *How do I install paperless-ngx on Raspberry Pi?*
|
||||
|
||||
**A:** Docker images are available for arm and arm64 hardware, so just follow
|
||||
the docker-compose instructions. Apart from more required disk space compared to
|
||||
@@ -85,11 +81,10 @@ python requirements do not have precompiled packages for ARM / ARM64. Installati
|
||||
of these will require additional development libraries and compilation will take
|
||||
a long time.
|
||||
|
||||
**Q:** *How do I run this on unRaid?*
|
||||
**Q:** *How do I run this on Unraid?*
|
||||
|
||||
**A:** Head over to `<https://github.com/selfhosters/unRAID-CA-templates>`_,
|
||||
`Uli Fahrer <https://github.com/Tooa>`_ created a container template for that.
|
||||
I don't exactly know how to use that though, since I don't use unRaid.
|
||||
**A:** Paperless-ngx is available as `community app <https://unraid.net/community/apps?q=paperless-ngx>`_
|
||||
in Unraid. `Uli Fahrer <https://github.com/Tooa>`_ created a container template for that.
|
||||
|
||||
**Q:** *How do I run this on my toaster?*
|
||||
|
||||
|
@@ -22,19 +22,21 @@ finding stuff again. I feed documents right from the post box into the scanner
|
||||
and then shred them. Perhaps you might find it useful too.
|
||||
|
||||
|
||||
Paperless-ng
|
||||
============
|
||||
Paperless-ngx
|
||||
=============
|
||||
|
||||
Paperless-ng is a fork of the original paperless project. It changes many
|
||||
things both on the surface and under the hood. Paperless-ng was created
|
||||
because I feel that these changes are too big to be pushed into the main
|
||||
repository right away.
|
||||
Paperless-ngx is a document management system that transforms your physical
|
||||
documents into a searchable online archive so you can keep, well, *less paper*.
|
||||
|
||||
Paperless-ngx forked from paperless-ng to continue the great work and
|
||||
distribute responsibility of supporting and advancing the project among a team
|
||||
of people.
|
||||
|
||||
NG stands for both Angular (the framework used for the
|
||||
Frontend) and next-gen. Publishing this project under a different name also
|
||||
avoids confusion between paperless and paperless-ng.
|
||||
avoids confusion between paperless and paperless-ngx.
|
||||
|
||||
If you want to learn about what's different in paperless-ng, check out these
|
||||
If you want to learn about what's different in paperless-ngx from Paperless, check out these
|
||||
resources in the documentation:
|
||||
|
||||
* :ref:`Some screenshots <screenshots>` of the new UI are available.
|
||||
@@ -46,16 +48,12 @@ resources in the documentation:
|
||||
the consumption directory. This means that you can select text in
|
||||
image-only documents coming from your scanner.
|
||||
* See :ref:`this note <utilities-encyption>` about GnuPG encryption in
|
||||
paperless-ng.
|
||||
paperless-ngx.
|
||||
* Paperless is now integrated with a
|
||||
:ref:`task processing queue <setup-task_processor>` that tells you
|
||||
at a glance when and why something is not working.
|
||||
* The :ref:`changelog <paperless_changelog>` contains a detailed list of all changes
|
||||
in paperless-ng.
|
||||
|
||||
It would be great if this project could eventually merge back into the main
|
||||
repository, but it needs a lot more work before that can happen.
|
||||
|
||||
at a glance when and why something is not working.
|
||||
* The :doc:`changelog </changelog>` contains a detailed list of all changes
|
||||
in paperless-ngx.
|
||||
|
||||
Contents
|
||||
========
|
||||
@@ -72,7 +70,6 @@ Contents
|
||||
faq
|
||||
troubleshooting
|
||||
extending
|
||||
contributing
|
||||
scanners
|
||||
screenshots
|
||||
changelog
|
||||
|
@@ -13,60 +13,121 @@ that works right for you based on recommendations from other Paperless users.
|
||||
Physical scanners
|
||||
=================
|
||||
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Brand | Model | Supports | Recommended By |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| | | FTP | NFS | SMB | |
|
||||
+=========+================+=====+=====+=====+================+
|
||||
| Brother | `ADS-1500W`_ | yes | no | yes | `danielquinn`_ |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Brother | `MFC-J6930DW`_ | yes | | | `ayounggun`_ |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Brother | `MFC-J5910DW`_ | yes | | | `bmsleight`_ |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Brother | `MFC-9142CDN`_ | yes | | yes | `REOLDEV`_ |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Fujitsu | `ix500`_ | yes | | yes | `eonist`_ |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Epson | `WF-7710DWF`_ | yes | | yes | `Skylinar`_ |
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
| Fujitsu | `S1300i`_ | yes | | yes | `jonaswinkler`_|
|
||||
+---------+----------------+-----+-----+-----+----------------+
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brand | Model | Supports | Recommended By |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| | | FTP | SFTP | NFS | SMB | SMTP | API [1]_ | |
|
||||
+=========+================+=====+======+=====+=====+======+==========+================+
|
||||
| Brother | `ADS-1700W`_ | yes | | | yes | yes | |`holzhannes`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `ADS-1600W`_ | yes | | | yes | yes | |`holzhannes`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `ADS-1500W`_ | yes | | | yes | yes | |`danielquinn`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `ADS-1100W`_ | yes | | | | | |`ytzelf`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `ADS-2800W`_ | yes | yes | | yes | yes | |`philpagel`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `MFC-J6930DW`_ | yes | | | | | |`ayounggun`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `MFC-L5850DW`_ | yes | | | | yes | |`holzhannes`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `MFC-L2750DW`_ | yes | | | yes | yes | |`muued`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `MFC-J5910DW`_ | yes | | | | | |`bmsleight`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `MFC-8950DW`_ | yes | | | yes | yes | |`philpagel`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Brother | `MFC-9142CDN`_ | yes | | | yes | | |`REOLDEV`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Fujitsu | `ix500`_ | yes | | | yes | | |`eonist`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Epson | `ES-580W`_ | yes | | | yes | yes | |`fignew`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Epson | `WF-7710DWF`_ | yes | | | yes | | |`Skylinar`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Fujitsu | `S1300i`_ | yes | | | yes | | |`jonaswinkler`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
| Doxie | `Q2`_ | | | | | | yes |`Unkn0wnCat`_ |
|
||||
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
|
||||
|
||||
.. _MFC-L5850DW: https://www.brother-usa.com/products/mfcl5850dw
|
||||
.. _MFC-L2750DW: https://www.brother.de/drucker/laserdrucker/mfc-l2750dw
|
||||
.. _ADS-1700W: https://www.brother-usa.com/products/ads1700w
|
||||
.. _ADS-1600W: https://www.brother-usa.com/products/ads1600w
|
||||
.. _ADS-1500W: https://www.brother.ca/en/p/ads1500w
|
||||
.. _ADS-1100W: https://support.brother.com/g/b/downloadtop.aspx?c=fr&lang=fr&prod=ads1100w_eu_as_cn
|
||||
.. _ADS-2800W: https://www.brother-usa.com/products/ads2800w
|
||||
.. _MFC-J6930DW: https://www.brother.ca/en/p/MFCJ6930DW
|
||||
.. _MFC-J5910DW: https://www.brother.co.uk/printers/inkjet-printers/mfcj5910dw
|
||||
.. _MFC-8950DW: https://www.brother-usa.com/products/mfc8950dw
|
||||
.. _MFC-9142CDN: https://www.brother.co.uk/printers/laser-printers/mfc9140cdn
|
||||
.. _ix500: http://www.fujitsu.com/us/products/computing/peripheral/scanners/scansnap/ix500/
|
||||
.. _ES-580W: https://epson.com/Support/Scanners/ES-Series/Epson-WorkForce-ES-580W/s/SPT_B11B258201
|
||||
.. _WF-7710DWF: https://www.epson.de/en/products/printers/inkjet-printers/for-home/workforce-wf-7710dwf
|
||||
.. _ix500: http://www.fujitsu.com/us/products/computing/peripheral/scanners/scansnap/ix500/
|
||||
.. _S1300i: https://www.fujitsu.com/global/products/computing/peripheral/scanners/soho/s1300i/
|
||||
.. _Q2: https://www.getdoxie.com/product/doxie-q/
|
||||
|
||||
.. _danielquinn: https://github.com/danielquinn
|
||||
.. _ayounggun: https://github.com/ayounggun
|
||||
.. _bmsleight: https://github.com/bmsleight
|
||||
.. _danielquinn: https://github.com/danielquinn
|
||||
.. _eonist: https://github.com/eonist
|
||||
.. _fignew: https://github.com/fignew
|
||||
.. _holzhannes: https://github.com/holzhannes
|
||||
.. _jonaswinkler: https://github.com/jonaswinkler
|
||||
.. _REOLDEV: https://github.com/REOLDEV
|
||||
.. _Skylinar: https://github.com/Skylinar
|
||||
.. _jonaswinkler: https://github.com/jonaswinkler
|
||||
.. _ytzelf: https://github.com/ytzelf
|
||||
.. _Unkn0wnCat: https://github.com/Unkn0wnCat
|
||||
.. _muued: https://github.com/muued
|
||||
.. _philpagel: https://github.com/philpagel
|
||||
|
||||
.. [1] Scanners with API Integration allow to push scanned documents directly to :ref:`Paperless API <api-file_uploads>`, sometimes referred to as Webhook or Document POST.
|
||||
|
||||
Mobile phone software
|
||||
=====================
|
||||
|
||||
You can use your phone to "scan" documents. The regular camera app will work, but may have too low contrast for OCR to work well. Apps specifically for scanning are recommended.
|
||||
|
||||
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
|
||||
| Name | OS | Supports | Recommended By |
|
||||
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
|
||||
| | | FTP | NFS | SMB | Email | WebDav | |
|
||||
+===================+================+=====+=====+=====+=======+========+================+
|
||||
| `Office Lens`_ | Android | ? | ? | ? | ? | ? | `jonaswinkler`_|
|
||||
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
|
||||
| `Genius Scan`_ | Android | yes | no | yes | yes | yes | `hannahswain`_ |
|
||||
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
|
||||
+-----------------------------+----------------+-----+-----+-----+-------+--------+------------------+
|
||||
| Name | OS | Supports | Recommended By |
|
||||
+-----------------------------+----------------+-----+-----+-----+-------+--------+------------------+
|
||||
| | | FTP | NFS | SMB | Email | WebDAV | |
|
||||
+=============================+================+=====+=====+=====+=======+========+==================+
|
||||
| `Office Lens`_ | Android | ? | ? | ? | ? | ? | `jonaswinkler`_ |
|
||||
+-----------------------------+----------------+-----+-----+-----+-------+--------+------------------+
|
||||
| `Genius Scan`_ | Android | yes | no | yes | yes | yes | `hannahswain`_ |
|
||||
+-----------------------------+----------------+-----+-----+-----+-------+--------+------------------+
|
||||
| `OpenScan`_ | Android | no | no | no | no | no | `benjaminfrank`_ |
|
||||
+-----------------------------+----------------+-----+-----+-----+-------+--------+------------------+
|
||||
| `OCR Scanner - QuickScan`_ | iOS | no | no | no | no | yes | `holzhannes`_ |
|
||||
+-----------------------------+----------------+-----+-----+-----+-------+--------+------------------+
|
||||
|
||||
On Android, you can use these applications in combination with one of the :ref:`Paperless-ng compatible apps <usage-mobile_upload>` to "Share" the documents produced by these scanner apps with paperless.
|
||||
On Android, you can use these applications in combination with one of the :ref:`Paperless-ngx compatible apps <usage-mobile_upload>` to "Share" the documents produced by these scanner apps with paperless. On iOS, you can share the scanned documents via iOS-Sharing to other mail, WebDav or FTP apps.
|
||||
|
||||
.. _Office Lens: https://play.google.com/store/apps/details?id=com.microsoft.office.officelens
|
||||
.. _Genius Scan: https://play.google.com/store/apps/details?id=com.thegrizzlylabs.geniusscan.free
|
||||
.. _OCR Scanner - QuickScan: https://apps.apple.com/us/app/quickscan-scanner-text-ocr/id1513790291
|
||||
.. _OpenScan: https://github.com/Ethereal-Developers-Inc/OpenScan
|
||||
|
||||
.. _hannahswain: https://github.com/hannahswain
|
||||
.. _benjaminfrank: https://github.com/benjaminfrank
|
||||
|
||||
API Scanning Setup
|
||||
==================
|
||||
|
||||
This sections contains information on how to set up scanners to post directly to :ref:`Paperless API <api-file_uploads>`.
|
||||
|
||||
Doxie Q2
|
||||
--------
|
||||
|
||||
This part assumes your Doxie is connected to WiFi and you know its IP.
|
||||
|
||||
1. Open your Doxie web UI by navigating to its IP address
|
||||
2. Navigate to Options -> Webhook
|
||||
3. Set the *URL* to ``https://[your-paperless-ngx-instance]/api/documents/post_document/``
|
||||
4. Set the *File Parameter Name* to ``document``
|
||||
5. Add the username and password to the respective fields (Consider creating a user just for your Doxie)
|
||||
6. Click *Submit* at the bottom of the page
|
||||
|
||||
Congrats, you can now scan directly from your Doxie to your Paperless-ngx instance!
|
||||
|
@@ -4,42 +4,60 @@
|
||||
Screenshots
|
||||
***********
|
||||
|
||||
This is what paperless-ng looks like. You shouldn't use paperless to index
|
||||
research papers though, its a horrible tool for that job.
|
||||
This is what Paperless-ngx looks like.
|
||||
|
||||
The dashboard shows customizable views on your document and allows document uploads:
|
||||
|
||||
.. image:: _static/screenshots/dashboard.png
|
||||
:target: _static/screenshots/dashboard.png
|
||||
|
||||
The document list provides three different styles to scroll through your documents:
|
||||
|
||||
.. image:: _static/screenshots/documents-table.png
|
||||
:target: _static/screenshots/documents-table.png
|
||||
.. image:: _static/screenshots/documents-smallcards.png
|
||||
:target: _static/screenshots/documents-smallcards.png
|
||||
.. image:: _static/screenshots/documents-largecards.png
|
||||
:target: _static/screenshots/documents-largecards.png
|
||||
|
||||
Paperless-ngx also supports "dark mode":
|
||||
|
||||
.. image:: _static/screenshots/documents-smallcards-dark.png
|
||||
:target: _static/screenshots/documents-smallcards-dark.png
|
||||
|
||||
Extensive filtering mechanisms:
|
||||
|
||||
.. image:: _static/screenshots/documents-filter.png
|
||||
:target: _static/screenshots/documents-filter.png
|
||||
|
||||
Side-by-side editing of documents. Optimized for 1080p.
|
||||
Bulk editing of document tags, correspondents, etc.:
|
||||
|
||||
.. image:: _static/screenshots/bulk-edit.png
|
||||
:target: _static/screenshots/bulk-edit.png
|
||||
|
||||
Side-by-side editing of documents:
|
||||
|
||||
.. image:: _static/screenshots/editing.png
|
||||
:target: _static/screenshots/editing.png
|
||||
|
||||
Tag editing. This looks about the same for correspondents and document types.
|
||||
|
||||
.. image:: _static/screenshots/new-tag.png
|
||||
:target: _static/screenshots/new-tag.png
|
||||
|
||||
Searching provides auto complete and highlights the results.
|
||||
|
||||
.. image:: _static/screenshots/search-preview.png
|
||||
:target: _static/screenshots/search-preview.png
|
||||
.. image:: _static/screenshots/search-results.png
|
||||
:target: _static/screenshots/search-results.png
|
||||
|
||||
Fancy mail filters!
|
||||
|
||||
.. image:: _static/screenshots/mail-rules-edited.png
|
||||
:target: _static/screenshots/mail-rules-edited.png
|
||||
|
||||
Mobile support in the future? This kinda works, however some layouts are still
|
||||
too wide.
|
||||
Mobile devices are supported.
|
||||
|
||||
.. image:: _static/screenshots/mobile.png
|
||||
|
||||
:target: _static/screenshots/mobile.png
|
||||
|
254
docs/setup.rst
@@ -3,10 +3,10 @@
|
||||
Setup
|
||||
*****
|
||||
|
||||
Overview of Paperless-ng
|
||||
########################
|
||||
Overview of Paperless-ngx
|
||||
#########################
|
||||
|
||||
Compared to paperless, paperless-ng works a little different under the hood and has
|
||||
Compared to paperless, paperless-ngx works a little different under the hood and has
|
||||
more moving parts that work together. While this increases the complexity of
|
||||
the system, it also brings many benefits.
|
||||
|
||||
@@ -85,22 +85,16 @@ You can go multiple routes to setup and run Paperless:
|
||||
* :ref:`Pull the image from Docker Hub <setup-docker_hub>`
|
||||
* :ref:`Build the Docker image yourself <setup-docker_build>`
|
||||
* :ref:`Install Paperless directly on your system manually (bare metal) <setup-bare_metal>`
|
||||
* :ref:`Use ansible to install Paperless on your system automatically (bare metal) <setup-ansible>`
|
||||
|
||||
The Docker routes are quick & easy. These are the recommended routes. This configures all the stuff
|
||||
from the above automatically so that it just works and uses sensible defaults for all configuration options.
|
||||
Here you find a cheat-sheet for docker beginners: `CLI Basics <https://sehn.tech/post/devops-with-docker/>`_
|
||||
Here you find a cheat-sheet for docker beginners: `CLI Basics <https://www.sehn.tech/refs/devops-with-docker/>`_
|
||||
|
||||
The bare metal route is complicated to setup but makes it easier
|
||||
should you want to contribute some code back. You need to configure and
|
||||
run the above mentioned components yourself.
|
||||
|
||||
The ansible route combines benefits of both options:
|
||||
the setup process is fully automated, reproducible and `idempotent <https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency>`_,
|
||||
it includes the same sensible defaults, and it simultaneously provides the flexibility of a bare metal installation.
|
||||
|
||||
.. _CLI Basics: https://sehn.tech/post/devops-with-docker/
|
||||
.. _idempotent: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency
|
||||
.. _CLI Basics: https://www.sehn.tech/refs/devops-with-docker/
|
||||
|
||||
.. _setup-docker_script:
|
||||
|
||||
@@ -116,18 +110,16 @@ performs all the steps described in :ref:`setup-docker_hub` automatically.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ wget https://raw.githubusercontent.com/jonaswinkler/paperless-ng/master/install-paperless-ng.sh
|
||||
$ chmod +x install-paperless-ng.sh
|
||||
$ ./install-paperless-ng.sh
|
||||
$ bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
|
||||
|
||||
.. _setup-docker_hub:
|
||||
|
||||
Install Paperless from Docker Hub
|
||||
=================================
|
||||
|
||||
1. Login with your user and create a folder in your home-directory `mkdir -v ~/paperless-ng` to have a place for your configuration files and consumption directory.
|
||||
1. Login with your user and create a folder in your home-directory `mkdir -v ~/paperless-ngx` to have a place for your configuration files and consumption directory.
|
||||
|
||||
2. Go to the `/docker/compose directory on the project page <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
|
||||
2. Go to the `/docker/compose directory on the project page <https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose>`_
|
||||
and download one of the `docker-compose.*.yml` files, depending on which database backend you
|
||||
want to use. Rename this file to `docker-compose.yml`.
|
||||
If you want to enable optional support for Office documents, download a file with `-tika` in the file name.
|
||||
@@ -173,6 +165,24 @@ Install Paperless from Docker Hub
|
||||
|
||||
Don't change the part after the colon or paperless wont find your documents.
|
||||
|
||||
You may also need to change the default port that the webserver will use
|
||||
from the default (8000):
|
||||
|
||||
.. code::
|
||||
|
||||
ports:
|
||||
- 8000:8000
|
||||
|
||||
Replace the part BEFORE the colon with a port of your choice:
|
||||
|
||||
.. code::
|
||||
|
||||
ports:
|
||||
- 8010:8000
|
||||
|
||||
Don't change the part after the colon or edit other lines that refer to
|
||||
port 8000. Modifying the part before the colon will map requests on another
|
||||
port to the webserver running on the default port.
|
||||
|
||||
5. Modify ``docker-compose.env``, following the comments in the file. The
|
||||
most important change is to set ``USERMAP_UID`` and ``USERMAP_GID``
|
||||
@@ -228,7 +238,7 @@ Build the Docker image yourself
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
git clone https://github.com/jonaswinkler/paperless-ng
|
||||
git clone https://github.com/paperless-ngx/paperless-ngx
|
||||
|
||||
The master branch always reflects the latest stable version.
|
||||
|
||||
@@ -241,7 +251,7 @@ Build the Docker image yourself
|
||||
.. code:: yaml
|
||||
|
||||
webserver:
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
|
||||
and replace it with a line that instructs docker-compose to build the image from the current working directory instead:
|
||||
|
||||
@@ -250,9 +260,7 @@ Build the Docker image yourself
|
||||
webserver:
|
||||
build: .
|
||||
|
||||
4. Run the ``compile-frontend.sh`` script. This requires ``node`` and ``npm >= v15``.
|
||||
|
||||
5. Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run
|
||||
4. Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run
|
||||
``docker-compose pull`` to pull the image, do
|
||||
|
||||
.. code:: shell-session
|
||||
@@ -272,7 +280,7 @@ writing. Windows is not and will never be supported.
|
||||
|
||||
1. Install dependencies. Paperless requires the following packages.
|
||||
|
||||
* ``python3`` 3.6, 3.7, 3.8, 3.9
|
||||
* ``python3`` 3.8, 3.9
|
||||
* ``python3-pip``
|
||||
* ``python3-dev``
|
||||
|
||||
@@ -280,10 +288,17 @@ writing. Windows is not and will never be supported.
|
||||
* ``imagemagick`` >= 6 for PDF conversion
|
||||
* ``optipng`` for optimizing thumbnails
|
||||
* ``gnupg`` for handling encrypted documents
|
||||
* ``libpoppler-cpp-dev`` for PDF to text conversion
|
||||
* ``libpq-dev`` for PostgreSQL
|
||||
* ``libmagic-dev`` for mime type detection
|
||||
* ``mime-support`` for mime type detection
|
||||
* ``libzbar0`` for barcode detection
|
||||
* ``poppler-utils`` for barcode detection
|
||||
|
||||
Use this list for your preferred package management:
|
||||
|
||||
.. code::
|
||||
|
||||
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support libzbar0 poppler-utils
|
||||
|
||||
These dependencies are required for OCRmyPDF, which is used for text recognition.
|
||||
|
||||
@@ -298,6 +313,12 @@ writing. Windows is not and will never be supported.
|
||||
* ``tesseract-ocr`` >= 4.0.0 for OCR
|
||||
* ``tesseract-ocr`` language packs (``tesseract-ocr-eng``, ``tesseract-ocr-deu``, etc)
|
||||
|
||||
Use this list for your preferred package management:
|
||||
|
||||
.. code::
|
||||
|
||||
unpaper ghostscript icc-profiles-free qpdf liblept5 libxml2 pngquant zlib1g tesseract-ocr
|
||||
|
||||
On Raspberry Pi, these libraries are required as well:
|
||||
|
||||
* ``libatlas-base-dev``
|
||||
@@ -311,7 +332,7 @@ writing. Windows is not and will never be supported.
|
||||
3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish
|
||||
to use PostgreSQL, SQLite is available as well.
|
||||
|
||||
4. Get the release archive from `<https://github.com/jonaswinkler/paperless-ng/releases>`_.
|
||||
4. Get the release archive from `<https://github.com/paperless-ngx/paperless-ngx/releases>`_.
|
||||
If you clone the git repo as it is, you also have to compile the front end by yourself.
|
||||
Extract the archive to a place from where you wish to execute it, such as ``/opt/paperless``.
|
||||
|
||||
@@ -326,6 +347,8 @@ writing. Windows is not and will never be supported.
|
||||
paperless stores its data. If you like, you can point both to the same directory.
|
||||
* ``PAPERLESS_SECRET_KEY`` should be a random sequence of characters. It's used for authentication. Failure
|
||||
to do so allows third parties to forge authentication credentials.
|
||||
* ``PAPERLESS_URL`` if you are behind a reverse proxy. This should point to your domain. Please see
|
||||
:ref:`configuration` for more information.
|
||||
|
||||
Many more adjustments can be made to paperless, especially the OCR part. The following options are recommended
|
||||
for everyone:
|
||||
@@ -349,12 +372,16 @@ writing. Windows is not and will never be supported.
|
||||
Adjust as necessary if you configured different folders.
|
||||
|
||||
8. Install python requirements from the ``requirements.txt`` file.
|
||||
It is up to you if you wish to use a virtual environment or not.
|
||||
It is up to you if you wish to use a virtual environment or not. First you should update your pip, so it gets the actual packages.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
sudo -Hu paperless pip3 install --upgrade pip
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
sudo -Hu paperless pip3 install -r requirements.txt
|
||||
|
||||
|
||||
This will install all python dependencies in the home directory of
|
||||
the new paperless user.
|
||||
|
||||
@@ -396,6 +423,10 @@ writing. Windows is not and will never be supported.
|
||||
``consumer`` script to watch the input folder, and the ``scheduler``
|
||||
script to run tasks such as email checking and document consumption.
|
||||
|
||||
The ``socket`` script enables ``gunicorn`` to run on port 80 without
|
||||
root privileges. For this you need to uncomment the ``Require=paperless-webserver.socket``
|
||||
in the ``webserver`` script and configure ``gunicorn`` to listen on port 80 (see ``paperless/gunicorn.conf.py``).
|
||||
|
||||
You may need to adjust the path to the ``gunicorn`` executable. This
|
||||
will be installed as part of the python dependencies, and is either located
|
||||
in the ``bin`` folder of your virtual environment, or in ``~/.local/bin/`` if
|
||||
@@ -441,133 +472,48 @@ writing. Windows is not and will never be supported.
|
||||
to compile this by yourself, because this software has been patented until around 2017 and
|
||||
binary packages are not available for most distributions.
|
||||
|
||||
.. _setup-ansible:
|
||||
Migrating to Paperless-ngx
|
||||
##########################
|
||||
|
||||
Install Paperless using ansible
|
||||
===============================
|
||||
Migration is possible both from Paperless-ng or directly from the 'original' Paperless.
|
||||
|
||||
.. note::
|
||||
Migrating from Paperless-ng
|
||||
===========================
|
||||
|
||||
This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts.
|
||||
Paperless-ngx is meant to be a drop-in replacement for Paperless-ng and thus upgrading should be
|
||||
trivial for most users, especially when using docker. However, as with any major change, it is
|
||||
recommended to take a full backup first. Once you are ready, simply change the docker image to
|
||||
point to the new source. E.g. if using Docker Compose, edit ``docker-compose.yml`` and change:
|
||||
|
||||
1. Install ansible 2.7+ on the management node.
|
||||
This may be the target host paperless-ng is being installed on or any remote host which can access the target host.
|
||||
For further details, check the ansible `inventory <https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html>`_ documentation.
|
||||
.. code::
|
||||
|
||||
On Debian and Ubuntu, the official repositories should provide a suitable version:
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
|
||||
.. code:: bash
|
||||
to
|
||||
|
||||
apt install ansible
|
||||
ansible --version
|
||||
.. code::
|
||||
|
||||
Alternatively, you can install the most recent ansible release using PyPI:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
|
||||
.. code:: bash
|
||||
and then run ``docker-compose up -d`` which will pull the new image recreate the container.
|
||||
That's it!
|
||||
|
||||
python3 -m pip install ansible
|
||||
ansible --version
|
||||
Users who installed with the bare-metal route should also update their Git clone to point to
|
||||
``https://github.com/paperless-ngx/paperless-ngx``, e.g. using the command
|
||||
``git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`` and then pull the
|
||||
lastest version.
|
||||
|
||||
Make sure your taget hosts are accessible:
|
||||
Migrating from Paperless
|
||||
========================
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible -m ping YourAnsibleTargetHostGoesHere
|
||||
|
||||
2. Clone the repository of paperless-ng:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
git clone https://github.com/jonaswinkler/paperless-ng
|
||||
|
||||
Checkout the latest release tag:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
cd paperless-ng
|
||||
git checkout ng-1.0.0
|
||||
|
||||
3. Create an ansible ``playbook.yml`` in the paperless-ng root directory:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
- hosts: YourAnsibleTargetHostGoesHere
|
||||
become: yes
|
||||
vars_files:
|
||||
- ansible/vars.yml
|
||||
roles:
|
||||
- ansible
|
||||
|
||||
Optional: If you also want to use PostgreSQL on the target system, install and add (for example) the `geerlingguy.postgresql <https://github.com/geerlingguy/ansible-role-postgresql>`_ role:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible-galaxy install geerlingguy.postgresql
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
- hosts: YourAnsibleTargetHostGoesHere
|
||||
become: yes
|
||||
vars_files:
|
||||
- ansible/vars.yml
|
||||
roles:
|
||||
- geerlingguy.postgresql
|
||||
- ansible
|
||||
|
||||
Optional: If you also want to use a reverse proxy on the target system, install and add (for example) the `geerlingguy.nginx <https://github.com/geerlingguy/ansible-role-nginx>`_ role:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible-galaxy install geerlingguy.nginx
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
- hosts: YourAnsibleTargetHostGoesHere
|
||||
become: yes
|
||||
vars_files:
|
||||
- ansible/vars.yml
|
||||
roles:
|
||||
- geerlingguy.postgresql
|
||||
- ansible
|
||||
- geerlingguy.nginx
|
||||
|
||||
4. Create ``ansible/vars.yml`` to configure your ansible deployment:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
paperlessng_secret_key: PleaseGenerateAStrongKeyForThis
|
||||
|
||||
paperlessng_superuser_name: YourUserName
|
||||
paperlessng_superuser_email: name@domain.tld
|
||||
paperlessng_superuser_password: YourDesiredPasswordUsedForFirstLogin
|
||||
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
- deu
|
||||
|
||||
For all of the available options, please check ``ansible/README.md`` and :ref:`configuration`.
|
||||
|
||||
Optional configurations for the above-mentioned PostgreSQL and nginx roles would also go here.
|
||||
|
||||
5. Run the ansible playbook from the management node:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible-playbook playbook.yml
|
||||
|
||||
When this step completes successfully, paperless-ng will be available on the target host at ``http://127.0.0.1:8000`` (or the address you configured).
|
||||
|
||||
Migration to paperless-ng
|
||||
#########################
|
||||
|
||||
At its core, paperless-ng is still paperless and fully compatible. However, some
|
||||
At its core, paperless-ngx is still paperless and fully compatible. However, some
|
||||
things have changed under the hood, so you need to adapt your setup depending on
|
||||
how you installed paperless.
|
||||
|
||||
This setup describes how to update an existing paperless Docker installation.
|
||||
The important things to keep in mind are as follows:
|
||||
|
||||
* Read the :ref:`changelog <paperless_changelog>` and take note of breaking changes.
|
||||
* Read the :doc:`changelog </changelog>` and take note of breaking changes.
|
||||
* You should decide if you want to stick with SQLite or want to migrate your database
|
||||
to PostgreSQL. See :ref:`setup-sqlite_to_psql` for details on how to move your data from
|
||||
SQLite to PostgreSQL. Both work fine with paperless. However, if you already have a
|
||||
@@ -576,10 +522,10 @@ The important things to keep in mind are as follows:
|
||||
such as email checking and maintenance, requires a `redis`_ message broker
|
||||
instance. The docker-compose route takes care of that.
|
||||
* The layout of the folder structure for your documents and data remains the
|
||||
same, so you can just plug your old docker volumes into paperless-ng and
|
||||
same, so you can just plug your old docker volumes into paperless-ngx and
|
||||
expect it to find everything where it should be.
|
||||
|
||||
Migration to paperless-ng is then performed in a few simple steps:
|
||||
Migration to paperless-ngx is then performed in a few simple steps:
|
||||
|
||||
1. Stop paperless.
|
||||
|
||||
@@ -589,20 +535,20 @@ Migration to paperless-ng is then performed in a few simple steps:
|
||||
$ docker-compose down
|
||||
|
||||
2. Do a backup for two purposes: If something goes wrong, you still have your
|
||||
data. Second, if you don't like paperless-ng, you can switch back to
|
||||
data. Second, if you don't like paperless-ngx, you can switch back to
|
||||
paperless.
|
||||
|
||||
3. Download the latest release of paperless-ng. You can either go with the
|
||||
docker-compose files from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`__
|
||||
3. Download the latest release of paperless-ngx. You can either go with the
|
||||
docker-compose files from `here <https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose>`__
|
||||
or clone the repository to build the image yourself (see :ref:`above <setup-docker_build>`).
|
||||
You can either replace your current paperless folder or put paperless-ng
|
||||
You can either replace your current paperless folder or put paperless-ngx
|
||||
in a different location.
|
||||
|
||||
.. caution::
|
||||
|
||||
Paperless-ng includes a ``.env`` file. This will set the
|
||||
Paperless-ngx includes a ``.env`` file. This will set the
|
||||
project name for docker compose to ``paperless``, which will also define the name
|
||||
of the volumes by paperless-ng. However, if you experience that paperless-ng
|
||||
of the volumes by paperless-ngx. However, if you experience that paperless-ngx
|
||||
is not using your old paperless volumes, verify the names of your volumes with
|
||||
|
||||
.. code:: shell-session
|
||||
@@ -632,7 +578,7 @@ Migration to paperless-ng is then performed in a few simple steps:
|
||||
This will migrate your database and create the search index. After that,
|
||||
paperless will take care of maintaining the index by itself.
|
||||
|
||||
8. Start paperless-ng.
|
||||
8. Start paperless-ngx.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
@@ -723,14 +669,14 @@ management commands as below.
|
||||
7. Start paperless.
|
||||
|
||||
|
||||
Moving back to paperless
|
||||
Moving back to Paperless
|
||||
========================
|
||||
|
||||
Lets say you migrated to Paperless-ng and used it for a while, but decided that
|
||||
Lets say you migrated to Paperless-ngx and used it for a while, but decided that
|
||||
you don't like it and want to move back (If you do, send me a mail about what
|
||||
part you didn't like!), you can totally do that with a few simple steps.
|
||||
|
||||
Paperless-ng modified the database schema slightly, however, these changes can
|
||||
Paperless-ngx modified the database schema slightly, however, these changes can
|
||||
be reverted while keeping your current data, so that your current data will
|
||||
be compatible with original Paperless.
|
||||
|
||||
@@ -748,7 +694,7 @@ Or without docker:
|
||||
$ cd /path/to/paperless/src
|
||||
$ python3 manage.py migrate documents 0023
|
||||
|
||||
After that, you need to clear your cookies (Paperless-ng comes with updated
|
||||
After that, you need to clear your cookies (Paperless-ngx comes with updated
|
||||
dependencies that do cookie-processing differently) and probably your cache
|
||||
as well.
|
||||
|
||||
@@ -763,7 +709,8 @@ configuring some options in paperless can help improve performance immensely:
|
||||
|
||||
* Stick with SQLite to save some resources.
|
||||
* Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR
|
||||
the first page of your documents.
|
||||
the first page of your documents. In most cases, this page contains enough
|
||||
information to be able to find it.
|
||||
* ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured
|
||||
to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that
|
||||
paperless will use 2 workers and 2 threads per worker. This may result in
|
||||
@@ -774,8 +721,13 @@ configuring some options in paperless can help improve performance immensely:
|
||||
your documents before feeding them into paperless. Some scanners are able to
|
||||
do this! You might want to even specify ``skip_noarchive`` to skip archive
|
||||
file generation for already ocr'ed documents entirely.
|
||||
* If you want to perform OCR on the device, consider using ``PAPERLESS_OCR_CLEAN=none``.
|
||||
This will speed up OCR times and use less memory at the expense of slightly worse
|
||||
OCR results.
|
||||
* Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption
|
||||
times. Thumbnails will be about 20% larger.
|
||||
* If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to
|
||||
1. This will save some memory.
|
||||
|
||||
For details, refer to :ref:`configuration`.
|
||||
|
||||
@@ -800,7 +752,7 @@ Using nginx as a reverse proxy
|
||||
##############################
|
||||
|
||||
If you want to expose paperless to the internet, you should hide it behind a
|
||||
reverse proxy with SSL enabled.
|
||||
reverse proxy with SSL enabled.
|
||||
|
||||
In addition to the usual configuration for SSL,
|
||||
the following configuration is required for paperless to operate:
|
||||
@@ -834,4 +786,6 @@ the following configuration is required for paperless to operate:
|
||||
}
|
||||
}
|
||||
|
||||
The ``PAPERLESS_URL`` configuration variable is also required when using a reverse proxy. Please refer to the :ref:`hosting-and-security` docs.
|
||||
|
||||
Also read `this <https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu>`__, towards the end of the section.
|
||||
|
@@ -25,6 +25,19 @@ Check for the following issues:
|
||||
* Go to the admin interface, and check if there are failed tasks. If so, the
|
||||
tasks will contain an error message.
|
||||
|
||||
Consumer warns ``OCR for XX failed``
|
||||
####################################
|
||||
|
||||
If you find the OCR accuracy to be too low, and/or the document consumer warns
|
||||
that ``OCR for XX failed, but we're going to stick with what we've got since
|
||||
FORGIVING_OCR is enabled``, then you might need to install the
|
||||
`Tesseract language files <http://packages.ubuntu.com/search?keywords=tesseract-ocr>`_
|
||||
marching your document's languages.
|
||||
|
||||
As an example, if you are running Paperless-ngx from any Ubuntu or Debian
|
||||
box, and your documents are written in Spanish you may need to run::
|
||||
|
||||
apt-get install -y tesseract-ocr-spa
|
||||
|
||||
Consumer fails to pickup any new files
|
||||
######################################
|
||||
@@ -81,7 +94,7 @@ UserWarning in sklearn on every single document
|
||||
You may encounter warnings like this:
|
||||
|
||||
.. code::
|
||||
|
||||
|
||||
/usr/local/lib/python3.7/site-packages/sklearn/base.py:315:
|
||||
UserWarning: Trying to unpickle estimator CountVectorizer from version 0.23.2 when using version 0.24.0.
|
||||
This might lead to breaking code or invalid results. Use at your own risk.
|
||||
@@ -94,6 +107,31 @@ If you want to get rid of the warning or actually experience issues with automat
|
||||
the file ``classification_model.pickle`` in the data directory and let paperless recreate it.
|
||||
|
||||
|
||||
504 Server Error: Gateway Timeout when adding Office documents
|
||||
##############################################################
|
||||
|
||||
You may experience these errors when using the optional TIKA integration:
|
||||
|
||||
.. code::
|
||||
|
||||
requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: http://gotenberg:3000/forms/libreoffice/convert
|
||||
|
||||
Gotenberg is a server that converts Office documents into PDF documents and has a default timeout of 30 seconds.
|
||||
When conversion takes longer, Gotenberg raises this error.
|
||||
|
||||
You can increase the timeout by configuring a command flag for Gotenberg (see also `here <https://gotenberg.dev/docs/modules/api#properties>`__).
|
||||
If using docker-compose, this is achieved by the following configuration change in the ``docker-compose.yml`` file:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
gotenberg:
|
||||
image: gotenberg/gotenberg:7.4
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- "gotenberg"
|
||||
- "--chromium-disable-routes=true"
|
||||
- "--api-timeout=60"
|
||||
|
||||
Permission denied errors in the consumption directory
|
||||
#####################################################
|
||||
|
||||
@@ -176,11 +214,24 @@ This might have multiple reasons.
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 386, in sendfile
|
||||
sent += os.sendfile(sockno, fileno, offset + sent, count)
|
||||
OSError: [Errno 22] Invalid argument
|
||||
|
||||
|
||||
To fix this issue, add
|
||||
|
||||
.. code::
|
||||
|
||||
SENDFILE=0
|
||||
|
||||
|
||||
to your `docker-compose.env` file.
|
||||
|
||||
Error while reading metadata
|
||||
############################
|
||||
|
||||
You might find messages like these in your log files:
|
||||
|
||||
.. code::
|
||||
|
||||
[WARNING] [paperless.parsing.tesseract] Error while reading metadata
|
||||
|
||||
This indicates that paperless failed to read PDF metadata from one of your documents. This happens when you
|
||||
open the affected documents in paperless for editing. Paperless will continue to work, and will simply not
|
||||
show the invalid metadata.
|
||||
|