Compare commits
1005 Commits
v1.13.0
...
archive/v1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f216b322c2 | ||
![]() |
d54c7ca27c | ||
![]() |
077de8dcaa | ||
![]() |
6f50e5671a | ||
![]() |
bab9bdc832 | ||
![]() |
8e0adbb0fb | ||
![]() |
6b175ae7e3 | ||
![]() |
9494633da3 | ||
![]() |
c754a5f391 | ||
![]() |
2b692c6fc8 | ||
![]() |
35b6fb1a6d | ||
![]() |
61566a34d1 | ||
![]() |
e14f4c94c2 | ||
![]() |
8d35f22daf | ||
![]() |
6f8ef9bbdc | ||
![]() |
e7ddf6ba8f | ||
![]() |
407a119b9a | ||
![]() |
96aa2451bf | ||
![]() |
7680aab04d | ||
![]() |
3aef26b229 | ||
![]() |
935141adc0 | ||
![]() |
4300733d0c | ||
![]() |
c284a091c0 | ||
![]() |
7e768bfe23 | ||
![]() |
938f388a0b | ||
![]() |
d22a6fb8ea | ||
![]() |
11142e2f05 | ||
![]() |
fe7fb488c0 | ||
![]() |
0929c5086a | ||
![]() |
eb3ed7719b | ||
![]() |
6c19a0f8c7 | ||
![]() |
a0ece589b0 | ||
![]() |
8165071edf | ||
![]() |
a667974378 | ||
![]() |
fe1f88ce5d | ||
![]() |
8f2715e437 | ||
![]() |
57a3223c77 | ||
![]() |
df82ac8ac4 | ||
![]() |
16adddc803 | ||
![]() |
d1ae82c5c2 | ||
![]() |
0098936347 | ||
![]() |
17b85f6400 | ||
![]() |
48be9c0fd1 | ||
![]() |
ce36c2d0ea | ||
![]() |
06c63ef4a4 | ||
![]() |
03d93a7d6e | ||
![]() |
a0005c8b3e | ||
![]() |
2220343c50 | ||
![]() |
68fbed996f | ||
![]() |
20a4d8949d | ||
![]() |
1029ecfd49 | ||
![]() |
4de2a3b09e | ||
![]() |
651407c88e | ||
![]() |
3e2e485f66 | ||
![]() |
10efaad224 | ||
![]() |
3dda02660c | ||
![]() |
0b4e8141b0 | ||
![]() |
16ab0efa59 | ||
![]() |
20ca1ec547 | ||
![]() |
a1c9ab237f | ||
![]() |
a65239f7f1 | ||
![]() |
a0622675fd | ||
![]() |
1e0b778097 | ||
![]() |
3b666fef77 | ||
![]() |
9291c98189 | ||
![]() |
f6dadd8c82 | ||
![]() |
022bb272e6 | ||
![]() |
edad1a41e8 | ||
![]() |
421e78a748 | ||
![]() |
b961df90a7 | ||
![]() |
0b26b7098a | ||
![]() |
b09566a9a9 | ||
![]() |
b869deab66 | ||
![]() |
3d552c3112 | ||
![]() |
2a2bf3bf55 | ||
![]() |
26863b8cdc | ||
![]() |
b0f7d07214 | ||
![]() |
30c6557a32 | ||
![]() |
e18e173089 | ||
![]() |
6a8bdbd4f6 | ||
![]() |
b5dec87a62 | ||
![]() |
e50d30876a | ||
![]() |
cbcd9ed67d | ||
![]() |
6bcc26b487 | ||
![]() |
56fcb3fee1 | ||
![]() |
557e1790dd | ||
![]() |
2e67697d36 | ||
![]() |
ca4500692f | ||
![]() |
5aba6bff09 | ||
![]() |
9ed74068b9 | ||
![]() |
be4685742c | ||
![]() |
86b0a38811 | ||
![]() |
3e528f0a9a | ||
![]() |
170d7b6922 | ||
![]() |
f05249a9ad | ||
![]() |
5957ed7af3 | ||
![]() |
8ed63893eb | ||
![]() |
ea9dd926bc | ||
![]() |
f31d3b531f | ||
![]() |
d851448c32 | ||
![]() |
65327d52a6 | ||
![]() |
2ea5ae59b2 | ||
![]() |
a04d09028b | ||
![]() |
dd9255cb81 | ||
![]() |
536d7ecd3e | ||
![]() |
f50aac08df | ||
![]() |
db0f1d2159 | ||
![]() |
d97b565d6c | ||
![]() |
d3071f13d8 | ||
![]() |
768407c1d7 | ||
![]() |
c5d18b03cd | ||
![]() |
5f05b44cde | ||
![]() |
beaa09e9b3 | ||
![]() |
d6960f537b | ||
![]() |
0918eab004 | ||
![]() |
9b16789a17 | ||
![]() |
157240351f | ||
![]() |
6ad3d45d60 | ||
![]() |
b715e4d426 | ||
![]() |
aad5e9e99f | ||
![]() |
2a104ad33f | ||
![]() |
851290ee89 | ||
![]() |
a8c6c55e3b | ||
![]() |
992a647424 | ||
![]() |
c22461a1b6 | ||
![]() |
23fefc3ab7 | ||
![]() |
0beb9f0b5f | ||
![]() |
d376f9e7a3 | ||
![]() |
07e7bcd30b | ||
![]() |
95e86cb649 | ||
![]() |
802e5591ce | ||
![]() |
26d5730ad2 | ||
![]() |
8c7554e081 | ||
![]() |
9f5d47c320 | ||
![]() |
4aa452ce63 | ||
![]() |
7ef81ae10f | ||
![]() |
3628292afa | ||
![]() |
8aa5ecde62 | ||
![]() |
2f149eac9d | ||
![]() |
fcfc705b87 | ||
![]() |
7bd5c010a1 | ||
![]() |
52168d8e61 | ||
![]() |
a3842d9228 | ||
![]() |
452c51bd16 | ||
![]() |
22bedd9957 | ||
![]() |
cb318c723d | ||
![]() |
9a81d3c28e | ||
![]() |
3ca59e3b7a | ||
![]() |
07a12bdf15 | ||
![]() |
62e81d8bf0 | ||
![]() |
e295a41caa | ||
![]() |
c545a80aa3 | ||
![]() |
56f1a0cb51 | ||
![]() |
7218b6da97 | ||
![]() |
83f9f2d387 | ||
![]() |
13a2e38385 | ||
![]() |
996d942387 | ||
![]() |
cc42eb9fab | ||
![]() |
44125be979 | ||
![]() |
c2e9cc9a51 | ||
![]() |
fcd10f2adc | ||
![]() |
93009c1eed | ||
![]() |
d875be60d4 | ||
![]() |
db48d4c576 | ||
![]() |
3293231ad2 | ||
![]() |
aa1f2d3b59 | ||
![]() |
f492b679e3 | ||
![]() |
7ca84322bd | ||
![]() |
e3257b8fa3 | ||
![]() |
0bcda5ded8 | ||
![]() |
7ec82c0891 | ||
![]() |
3241ac7dc2 | ||
![]() |
e974605fc8 | ||
![]() |
ce13380533 | ||
![]() |
e23e3acda3 | ||
![]() |
63ab9972da | ||
![]() |
feb4901620 | ||
![]() |
67788a1b1b | ||
![]() |
001faf9ed7 | ||
![]() |
7a464d8a6e | ||
![]() |
5acd1c7c1b | ||
![]() |
de14540374 | ||
![]() |
37e0c2667b | ||
![]() |
252abb41c3 | ||
![]() |
fb2af341d8 | ||
![]() |
931f5f9c27 | ||
![]() |
b5d04e575e | ||
![]() |
3d395601fe | ||
![]() |
6630ce646c | ||
![]() |
f5508eea1c | ||
![]() |
c8c460432f | ||
![]() |
18299dafd2 | ||
![]() |
817d09026e | ||
![]() |
d76b009390 | ||
![]() |
9effed3ce1 | ||
![]() |
c1bbfc5dcf | ||
![]() |
b6c9cfb76f | ||
![]() |
59ca7bbcf2 | ||
![]() |
52c8d5e999 | ||
![]() |
5851e7f1b7 | ||
![]() |
e05b3441de | ||
![]() |
0d6e79cb93 | ||
![]() |
76a102d901 | ||
![]() |
bbd4659fbf | ||
![]() |
0880420ef6 | ||
![]() |
2351c79282 | ||
![]() |
d6016fc798 | ||
![]() |
bc17291006 | ||
![]() |
ed6cb14c4d | ||
![]() |
08de8a04b8 | ||
![]() |
5c67de8b47 | ||
![]() |
38b0408b1a | ||
![]() |
9ccad7ea86 | ||
![]() |
4a4e810a14 | ||
![]() |
76d2df3bde | ||
![]() |
c02563d894 | ||
![]() |
574ec6780b | ||
![]() |
9e0f56982b | ||
![]() |
1c66daf12b | ||
![]() |
59d683849e | ||
![]() |
9946acb1a0 | ||
![]() |
83a760644d | ||
![]() |
25ccff8640 | ||
![]() |
5c4c5a7794 | ||
![]() |
cb6af97595 | ||
![]() |
c4407dccf6 | ||
![]() |
ecdea4c3c8 | ||
![]() |
26d6f302cf | ||
![]() |
ecf10622ef | ||
![]() |
0fb553675b | ||
![]() |
2080fde4f9 | ||
![]() |
d10e67ce09 | ||
![]() |
74fe7c586b | ||
![]() |
05188aed6d | ||
![]() |
865efb7752 | ||
![]() |
4782b4da07 | ||
![]() |
4693632c7d | ||
![]() |
4c4b571a88 | ||
![]() |
328c87995b | ||
![]() |
a1d10e7d4a | ||
![]() |
77d9a7e9d3 | ||
![]() |
981b090088 | ||
![]() |
6d1c788ee0 | ||
![]() |
02de773d5b | ||
![]() |
2a240d83fd | ||
![]() |
25cdf7916d | ||
![]() |
11b5983a0d | ||
![]() |
4964987245 | ||
![]() |
ed129d6074 | ||
![]() |
37e928d869 | ||
![]() |
06def8c11e | ||
![]() |
10571676a4 | ||
![]() |
af5160237d | ||
![]() |
b86842ba73 | ||
![]() |
bfc271e743 | ||
![]() |
ee88140fdd | ||
![]() |
51249a1dce | ||
![]() |
57ec9e6b13 | ||
![]() |
1324d17d87 | ||
![]() |
26b438a888 | ||
![]() |
70f3f98363 | ||
![]() |
71e4be2d5e | ||
![]() |
5740806a28 | ||
![]() |
9b50a1b7a6 | ||
![]() |
19caad832e | ||
![]() |
dd6ae13281 | ||
![]() |
077abbe961 | ||
![]() |
3d85dc1127 | ||
![]() |
e3ea5dd13c | ||
![]() |
714b2ecd9c | ||
![]() |
883937bfd7 | ||
![]() |
0ebe08d796 | ||
![]() |
36b4fff5c7 | ||
![]() |
0684c8c388 | ||
![]() |
67744c877d | ||
![]() |
45d8c945e2 | ||
![]() |
ee19307ea2 | ||
![]() |
2c1cd25be4 | ||
![]() |
6e65558ea4 | ||
![]() |
304324ebd0 | ||
![]() |
97cd06d2ba | ||
![]() |
df948065a3 | ||
![]() |
f92126b44f | ||
![]() |
e329f6cdf1 | ||
![]() |
2c96438d61 | ||
![]() |
41a9aac75d | ||
![]() |
8768168536 | ||
![]() |
325809fbbf | ||
![]() |
3dd47a9f5b | ||
![]() |
00f16ef8f0 | ||
![]() |
5e67aae83b | ||
![]() |
ae5c603c98 | ||
![]() |
c62aa3cb55 | ||
![]() |
7073cb6d5c | ||
![]() |
a495ad58d0 | ||
![]() |
569165371c | ||
![]() |
ea14fa5251 | ||
![]() |
4a02865697 | ||
![]() |
3a2a20cefd | ||
![]() |
f2f42de701 | ||
![]() |
6d60d4897c | ||
![]() |
9f71ce8083 | ||
![]() |
50f8f7da93 | ||
![]() |
d475344b51 | ||
![]() |
2630863409 | ||
![]() |
e120f4a3f7 | ||
![]() |
6aff4c986c | ||
![]() |
2e891b1634 | ||
![]() |
35a0c5d36f | ||
![]() |
82d786b94c | ||
![]() |
de49d602a1 | ||
![]() |
3cb6511b66 | ||
![]() |
1d3ae777d5 | ||
![]() |
dbb2ea39d2 | ||
![]() |
fb607332b9 | ||
![]() |
b956f627b0 | ||
![]() |
2ac64ab573 | ||
![]() |
482e00970c | ||
![]() |
d62340efb5 | ||
![]() |
eb0f35219c | ||
![]() |
243598ae50 | ||
![]() |
74c965d21d | ||
![]() |
30316179a0 | ||
![]() |
f16a1101e6 | ||
![]() |
0466f7a18a | ||
![]() |
97cf3b2079 | ||
![]() |
6542d75a6a | ||
![]() |
c6900c5d51 | ||
![]() |
0e9642ea3e | ||
![]() |
3ab2892066 | ||
![]() |
69b69aca6a | ||
![]() |
a05dbd2e5a | ||
![]() |
c1641f6fb8 | ||
![]() |
452c79f9a1 | ||
![]() |
37959fe31c | ||
![]() |
30f73f39a0 | ||
![]() |
fa613cd5fb | ||
![]() |
b8afb22902 | ||
![]() |
07e07fc7e8 | ||
![]() |
58f95c1891 | ||
![]() |
7ad8e3b3da | ||
![]() |
b9a548758a | ||
![]() |
a436caf2ad | ||
![]() |
5461f8a225 | ||
![]() |
78b747571c | ||
![]() |
0c6a9a189b | ||
![]() |
45d666ff2d | ||
![]() |
4143925322 | ||
![]() |
9be3d2ccaf | ||
![]() |
8be8a310d7 | ||
![]() |
b81c339922 | ||
![]() |
d3d103f141 | ||
![]() |
dd575ccb88 | ||
![]() |
a83c7c64b5 | ||
![]() |
c04ded6fd8 | ||
![]() |
f24c779737 | ||
![]() |
3cdd358fc8 | ||
![]() |
e11939b149 | ||
![]() |
a4ef0702c9 | ||
![]() |
548b6352ca | ||
![]() |
2658c16073 | ||
![]() |
49d0b6aa00 | ||
![]() |
e65f584197 | ||
![]() |
ce1bbda188 | ||
![]() |
846897fb4c | ||
![]() |
457e134261 | ||
![]() |
3e129763c7 | ||
![]() |
c49d086965 | ||
![]() |
df7bfc4efd | ||
![]() |
7fba1f9ed2 | ||
![]() |
3205d52331 | ||
![]() |
111960c530 | ||
![]() |
e1bc1a0129 | ||
![]() |
8b543a5fa9 | ||
![]() |
dc7a67a1d7 | ||
![]() |
350c20d6ab | ||
![]() |
b5f0cd7c70 | ||
![]() |
90488cd77a | ||
![]() |
5bbc59e87c | ||
![]() |
c02758213b | ||
![]() |
09c62d67c1 | ||
![]() |
3f3fa3044c | ||
![]() |
62673145fb | ||
![]() |
0baf73de5e | ||
![]() |
66a0783e7b | ||
![]() |
17144c45e5 | ||
![]() |
311c0ba4f1 | ||
![]() |
e293d23ae3 | ||
![]() |
93769d2608 | ||
![]() |
e7540563d0 | ||
![]() |
fc1047550e | ||
![]() |
dadc618719 | ||
![]() |
36f3bd2869 | ||
![]() |
fdcea983a4 | ||
![]() |
081534457c | ||
![]() |
94a6272a1d | ||
![]() |
d389e0ecf8 | ||
![]() |
17eb1c604f | ||
![]() |
99474aab06 | ||
![]() |
f3d3bf20de | ||
![]() |
3c999e9847 | ||
![]() |
692fa5f606 | ||
![]() |
752b8e79ff | ||
![]() |
3f82cf4ab3 | ||
![]() |
1549b9df74 | ||
![]() |
78ef87a952 | ||
![]() |
29ede48e0f | ||
![]() |
6349d25219 | ||
![]() |
830a450f00 | ||
![]() |
18f9ce9c0b | ||
![]() |
2471be0c78 | ||
![]() |
60cfd687dc | ||
![]() |
e06c61b95d | ||
![]() |
471eee0872 | ||
![]() |
20abd8a9f8 | ||
![]() |
88e5c471de | ||
![]() |
09086e574d | ||
![]() |
8d95c13e31 | ||
![]() |
c922cc4351 | ||
![]() |
a42f28c502 | ||
![]() |
b802f3a71f | ||
![]() |
f78f212a77 | ||
![]() |
22cbfd473b | ||
![]() |
e5973ef713 | ||
![]() |
5364a29b5f | ||
![]() |
49754d33fa | ||
![]() |
d7d95037be | ||
![]() |
515146d4a2 | ||
![]() |
b7540fab58 | ||
![]() |
88e6f8abf6 | ||
![]() |
3c4dadd905 | ||
![]() |
f18f997796 | ||
![]() |
3a1daf46ae | ||
![]() |
8dffea4a42 | ||
![]() |
3852a6c5cf | ||
![]() |
6493f51a29 | ||
![]() |
028f42e775 | ||
![]() |
eb1cc55f94 | ||
![]() |
fb864f1132 | ||
![]() |
8b8d988c07 | ||
![]() |
c2b5451fe4 | ||
![]() |
487d3a6262 | ||
![]() |
fe990b4cd2 | ||
![]() |
019c7e2f78 | ||
![]() |
1c64a4f145 | ||
![]() |
fc869aa203 | ||
![]() |
3a0ada9f46 | ||
![]() |
cc9980fc19 | ||
![]() |
7515d8af64 | ||
![]() |
5e7579c1fd | ||
![]() |
38af53f281 | ||
![]() |
a26bec5b00 | ||
![]() |
feb943b6df | ||
![]() |
059e37a41f | ||
![]() |
7ce67fd465 | ||
![]() |
6b8b8209f3 | ||
![]() |
fd1d12859d | ||
![]() |
efb00b2387 | ||
![]() |
9b2ca57038 | ||
![]() |
9694face16 | ||
![]() |
7ef14832d0 | ||
![]() |
33f7b58e6e | ||
![]() |
9e992da863 | ||
![]() |
c986a218c7 | ||
![]() |
8ee6312402 | ||
![]() |
3c86b12ef9 | ||
![]() |
02d09edd49 | ||
![]() |
55af3c3dd1 | ||
![]() |
f8f5a77744 | ||
![]() |
5b6956ff24 | ||
![]() |
f1c138eaed | ||
![]() |
caf43638de | ||
![]() |
b783d2e210 | ||
![]() |
9a40a5f019 | ||
![]() |
81a7b34101 | ||
![]() |
f124e2a889 | ||
![]() |
02b2bcafc5 | ||
![]() |
81a5fd377e | ||
![]() |
f875ae4abf | ||
![]() |
01fd400ec7 | ||
![]() |
c59420581c | ||
![]() |
0aa9462cea | ||
![]() |
bf2f6f84e5 | ||
![]() |
5126f01b57 | ||
![]() |
ec4814a76e | ||
![]() |
69b53d70c5 | ||
![]() |
02875f5a34 | ||
![]() |
29d8c4e08d | ||
![]() |
df203311fe | ||
![]() |
10f9b91c44 | ||
![]() |
cd861364a2 | ||
![]() |
90b52abc04 | ||
![]() |
093b726c52 | ||
![]() |
fd84fc9dbe | ||
![]() |
4353646b3a | ||
![]() |
7545e5312c | ||
![]() |
bd494ce9ec | ||
![]() |
ee3cf8e6d1 | ||
![]() |
df524fdc1f | ||
![]() |
f0c0cfee1d | ||
![]() |
cf1bf3c163 | ||
![]() |
b9d703fe25 | ||
![]() |
46f7e685b6 | ||
![]() |
8023331fca | ||
![]() |
597db7d4bd | ||
![]() |
773bd32cd0 | ||
![]() |
c7e3756de1 | ||
![]() |
64bf122c95 | ||
![]() |
a92b0411fd | ||
![]() |
728d61762a | ||
![]() |
d9783e2a4d | ||
![]() |
b6303d2c16 | ||
![]() |
dd673a62b5 | ||
![]() |
b7577038a0 | ||
![]() |
613b71d23b | ||
![]() |
0284100c2d | ||
![]() |
26cd470d31 | ||
![]() |
ebaf509a42 | ||
![]() |
646db73061 | ||
![]() |
e6df581909 | ||
![]() |
a8e12409b5 | ||
![]() |
b7c7e293f7 | ||
![]() |
fe85aff052 | ||
![]() |
12d8bcad6e | ||
![]() |
bbfc244f16 | ||
![]() |
e275a2736a | ||
![]() |
d2a8076596 | ||
![]() |
83344f748f | ||
![]() |
1d5dbc454d | ||
![]() |
16e2dc60aa | ||
![]() |
a6fd4a8472 | ||
![]() |
c25698dfa7 | ||
![]() |
2ab2064a72 | ||
![]() |
356c26ce84 | ||
![]() |
bc56dfbcb5 | ||
![]() |
daaeb36363 | ||
![]() |
a46a9cf0bf | ||
![]() |
cbf435169a | ||
![]() |
7d05f6c54a | ||
![]() |
8a8667d1f4 | ||
![]() |
b9b8b764db | ||
![]() |
4978af351d | ||
![]() |
b5d639652d | ||
![]() |
c4ebfaf7f6 | ||
![]() |
256266280d | ||
![]() |
f886b58529 | ||
![]() |
14fe93b9ab | ||
![]() |
a2fb0ceb7d | ||
![]() |
bc284ecf6d | ||
![]() |
6113f586c9 | ||
![]() |
6c0862248c | ||
![]() |
0e8f2a7c6c | ||
![]() |
a808f8bbd5 | ||
![]() |
9428d5638e | ||
![]() |
e00cd5e304 | ||
![]() |
3c04bf2742 | ||
![]() |
2ef6d450bc | ||
![]() |
628b0bffeb | ||
![]() |
27eaa566a5 | ||
![]() |
fb36646bd3 | ||
![]() |
304cc37618 | ||
![]() |
8239e8a581 | ||
![]() |
69e117d898 | ||
![]() |
c773ec8a30 | ||
![]() |
b4b49ee096 | ||
![]() |
cf5ab87db9 | ||
![]() |
deaff293d2 | ||
![]() |
dccdebd2c0 | ||
![]() |
2674d4f034 | ||
![]() |
cb529561e1 | ||
![]() |
1a1cf49c67 | ||
![]() |
757b61a010 | ||
![]() |
448dcbab46 | ||
![]() |
30fc5bbb09 | ||
![]() |
d3e14818df | ||
![]() |
864e242ed9 | ||
![]() |
8f18baea8f | ||
![]() |
130489a1a9 | ||
![]() |
88a5a2049b | ||
![]() |
15fb3e5328 | ||
![]() |
90b800b030 | ||
![]() |
be88ad2676 | ||
![]() |
dfadfc0f13 | ||
![]() |
5ae48c8012 | ||
![]() |
6f163111ce | ||
![]() |
3bcbd05252 | ||
![]() |
e0d2697618 | ||
![]() |
7340535b9a | ||
![]() |
c385355c2b | ||
![]() |
a119790697 | ||
![]() |
e392098e35 | ||
![]() |
a2d4d16867 | ||
![]() |
7f74a85400 | ||
![]() |
1fc9eaf360 | ||
![]() |
1898f9b183 | ||
![]() |
1fb03a755f | ||
![]() |
8a505e3b66 | ||
![]() |
45ecec5623 | ||
![]() |
b34dfcd72f | ||
![]() |
319aa39925 | ||
![]() |
08ac40dd48 | ||
![]() |
0a0dc25e15 | ||
![]() |
0557a15fa8 | ||
![]() |
c5fafdda11 | ||
![]() |
434d1fe225 | ||
![]() |
405769dc97 | ||
![]() |
ffa116bf44 | ||
![]() |
6b1d8cabf4 | ||
![]() |
20c21e9e65 | ||
![]() |
088743a155 | ||
![]() |
d1fba28936 | ||
![]() |
65064a6934 | ||
![]() |
c7f5b7ae82 | ||
![]() |
2b244165e2 | ||
![]() |
47682bc143 | ||
![]() |
0dcfb97824 | ||
![]() |
50af671e02 | ||
![]() |
1ef273c35d | ||
![]() |
91b9831548 | ||
![]() |
3241968626 | ||
![]() |
5dee65afcb | ||
![]() |
4108eabd0d | ||
![]() |
829a693128 | ||
![]() |
bf1e49fc4c | ||
![]() |
d1984c0dda | ||
![]() |
22bb28db62 | ||
![]() |
721b52a45b | ||
![]() |
edf4f98d41 | ||
![]() |
7321ea1603 | ||
![]() |
b80c2126a3 | ||
![]() |
70afed9122 | ||
![]() |
87479c32de | ||
![]() |
35f75563a7 | ||
![]() |
f5d6a9f428 | ||
![]() |
2d314efb98 | ||
![]() |
930bac3c8b | ||
![]() |
d457f66e8b | ||
![]() |
98ef1ba579 | ||
![]() |
dd8514a84d | ||
![]() |
7bcfeab85c | ||
![]() |
d1cd03302c | ||
![]() |
36669652bf | ||
![]() |
48234896a4 | ||
![]() |
d4c310433e | ||
![]() |
a9d82a64a8 | ||
![]() |
74b6b8cb62 | ||
![]() |
52e8a1aba3 | ||
![]() |
0f92523d28 | ||
![]() |
6934fc6510 | ||
![]() |
ad746be010 | ||
![]() |
97119f729a | ||
![]() |
e576f1b0c4 | ||
![]() |
1771293fcf | ||
![]() |
d872423a76 | ||
![]() |
6c12f65b2d | ||
![]() |
566f50ec66 | ||
![]() |
e702f9c317 | ||
![]() |
680f8086e7 | ||
![]() |
9216f000ad | ||
![]() |
cea6ef7a66 | ||
![]() |
3287daf4e4 | ||
![]() |
376b40b25f | ||
![]() |
b00ca90d15 | ||
![]() |
06ec83701c | ||
![]() |
2b61ec32d0 | ||
![]() |
08299dd85d | ||
![]() |
a2e7199ff5 | ||
![]() |
28d68c8a77 | ||
![]() |
999abb7016 | ||
![]() |
5af2658d88 | ||
![]() |
46fd55d100 | ||
![]() |
fd4d747927 | ||
![]() |
ec3ea965d0 | ||
![]() |
41e4438a05 | ||
![]() |
254e0ea132 | ||
![]() |
190e24b25d | ||
![]() |
3f22aa9638 | ||
![]() |
0e679841a4 | ||
![]() |
e512e658e6 | ||
![]() |
f7cea2f92e | ||
![]() |
de5689f5b2 | ||
![]() |
e6cf5a5984 | ||
![]() |
23e7ccb543 | ||
![]() |
b4d97d4a2b | ||
![]() |
8f90fe79c8 | ||
![]() |
eb0df5d5e9 | ||
![]() |
e75510309d | ||
![]() |
3425d01853 | ||
![]() |
606737f3b2 | ||
![]() |
bf3b5fbf8e | ||
![]() |
f2fb06e6f3 | ||
![]() |
e58ba44e3d | ||
![]() |
1e19ec6b9a | ||
![]() |
6ed637cfdd | ||
![]() |
1d17e24c6e | ||
![]() |
de155a753d | ||
![]() |
1b4020b3d7 | ||
![]() |
b948750d55 | ||
![]() |
ce41ac9158 | ||
![]() |
5869467db3 | ||
![]() |
80472af53c | ||
![]() |
eaa6039082 | ||
![]() |
03b84e7c43 | ||
![]() |
0ee8f5c498 | ||
![]() |
41d15e8731 | ||
![]() |
bef081d353 | ||
![]() |
a79045c064 | ||
![]() |
24ae8249f9 | ||
![]() |
81341df635 | ||
![]() |
3c2bbf244d | ||
![]() |
fa60251c18 | ||
![]() |
5bd06494d5 | ||
![]() |
2fd217ef1f | ||
![]() |
d7068ca42b | ||
![]() |
f7b19cddbf | ||
![]() |
62e756a11e | ||
![]() |
c992661a17 | ||
![]() |
dde3205425 | ||
![]() |
15a264de3d | ||
![]() |
66929a9088 | ||
![]() |
45c9518a6d | ||
![]() |
95d32dc0da | ||
![]() |
d5ab1119d3 | ||
![]() |
d9110f4ef7 | ||
![]() |
5468394ef2 | ||
![]() |
7ad21e0e45 | ||
![]() |
5012c0c97c | ||
![]() |
698208fcd5 | ||
![]() |
e0d5fd9290 | ||
![]() |
4bd1974911 | ||
![]() |
d246e4090a | ||
![]() |
ff172f5ea1 | ||
![]() |
09b1413748 | ||
![]() |
14b997fe2c | ||
![]() |
908b412a9a | ||
![]() |
cbd80615be | ||
![]() |
a9707f0ab0 | ||
![]() |
4637e33326 | ||
![]() |
4a5f21dd87 | ||
![]() |
0778c2808b | ||
![]() |
567a1bb770 | ||
![]() |
743ee886be | ||
![]() |
20c6abae63 | ||
![]() |
ae0c585918 | ||
![]() |
4cfc416cdc | ||
![]() |
9902c4745d | ||
![]() |
e373ca7bdc | ||
![]() |
6a34a35585 | ||
![]() |
ee935a2988 | ||
![]() |
0a977a9d0a | ||
![]() |
4d26a3d2c6 | ||
![]() |
276d11e4e8 | ||
![]() |
e89c0f15dd | ||
![]() |
2bdf0aae14 | ||
![]() |
2bc7f0b8e0 | ||
![]() |
bf8ae22f3f | ||
![]() |
a5c6dab7c3 | ||
![]() |
f3eedec402 | ||
![]() |
741152dd50 | ||
![]() |
89c639f850 | ||
![]() |
727fb38baf | ||
![]() |
e19dd2d527 | ||
![]() |
9aa41b3524 | ||
![]() |
3911740360 | ||
![]() |
f161722b34 | ||
![]() |
adb956467b | ||
![]() |
00e17f4d69 | ||
![]() |
dbe49b24df | ||
![]() |
7e75193f4a | ||
![]() |
96e8cfb765 | ||
![]() |
d47ca6109a | ||
![]() |
8ac7d56fc5 | ||
![]() |
bfaede26c4 | ||
![]() |
477fd360f8 | ||
![]() |
5b50870f21 | ||
![]() |
8161316c01 | ||
![]() |
3cb26722f1 | ||
![]() |
7912f4a22a | ||
![]() |
851b23fb09 | ||
![]() |
849a108520 | ||
![]() |
97ff2e126c | ||
![]() |
ef6c4e6789 | ||
![]() |
3e467c517d | ||
![]() |
70ac696f17 | ||
![]() |
39c80ded58 | ||
![]() |
98782ca69d | ||
![]() |
c332eea354 | ||
![]() |
41a3f039b5 | ||
![]() |
2d3cf43bc5 | ||
![]() |
7c90702ff7 | ||
![]() |
952cf11b9e | ||
![]() |
4383550d98 | ||
![]() |
fcba2cca77 | ||
![]() |
2042b85056 | ||
![]() |
283a2ab648 | ||
![]() |
5e7b93d153 | ||
![]() |
c4ac35164b | ||
![]() |
22a13981f3 | ||
![]() |
29251b6e38 | ||
![]() |
b382f1412a | ||
![]() |
320537a054 | ||
![]() |
2fe7f8be46 | ||
![]() |
f100198a8a | ||
![]() |
b470fc0140 | ||
![]() |
db02d5eff0 | ||
![]() |
9564a9c28d | ||
![]() |
55295922c8 | ||
![]() |
c5b701f99d | ||
![]() |
3c606efc46 | ||
![]() |
cbab1a51f1 | ||
![]() |
41bcfcaffe | ||
![]() |
7cb14374cf | ||
![]() |
19b9fd0578 | ||
![]() |
d668c475de | ||
![]() |
248f3f2181 | ||
![]() |
bcd10f63ea | ||
![]() |
db9733f0d5 | ||
![]() |
e6aa213aa1 | ||
![]() |
f0fa726e71 | ||
![]() |
ae46ef7add | ||
![]() |
c87ca25f22 | ||
![]() |
ef627d53e5 | ||
![]() |
d0fcf3607d | ||
![]() |
3dbb7e5781 | ||
![]() |
9597358cb0 | ||
![]() |
c5a21a3b0e | ||
![]() |
f56ccec77f | ||
![]() |
489340a338 | ||
![]() |
e9e3d75383 | ||
![]() |
74a1e5ed86 | ||
![]() |
3d961a3dbb | ||
![]() |
604d56d0b8 | ||
![]() |
a82e259c1d | ||
![]() |
34ef99a8ab | ||
![]() |
fa389d19d0 | ||
![]() |
ebe70d996c | ||
![]() |
e83e13fd57 | ||
![]() |
02362ae5e1 | ||
![]() |
e0f324f61c | ||
![]() |
c422a081bf | ||
![]() |
72efb24b73 | ||
![]() |
ee7653a8cd | ||
![]() |
020abaf7d6 | ||
![]() |
6e2f6350e6 | ||
![]() |
6b939f7567 | ||
![]() |
c958a7c593 | ||
![]() |
8709ea4df0 | ||
![]() |
16d3041d7a | ||
![]() |
64b2037eda | ||
![]() |
4133001c73 | ||
![]() |
d5f46eedab | ||
![]() |
7a2a3e048e | ||
![]() |
20891a370b | ||
![]() |
ca412e0184 | ||
![]() |
8a89f5ae27 | ||
![]() |
dbea2acc8f | ||
![]() |
65167625c4 | ||
![]() |
77b23d3acb | ||
![]() |
5d8aa27831 | ||
![]() |
b15eda9466 | ||
![]() |
5eb4b975ae | ||
![]() |
3ce1e01d96 | ||
![]() |
afcefe3d04 | ||
![]() |
4726fe8b6f | ||
![]() |
201a4a7ef9 | ||
![]() |
93a6391f96 | ||
![]() |
782db3f324 | ||
![]() |
7610a0459e | ||
![]() |
927616decb | ||
![]() |
8b2b7bbe6d | ||
![]() |
ca30dbc832 | ||
![]() |
3c3c847db5 | ||
![]() |
b7a2601724 | ||
![]() |
1189df1fe6 | ||
![]() |
a37177703c | ||
![]() |
8df1324afd | ||
![]() |
a6e2708605 | ||
![]() |
0df91c31f1 | ||
![]() |
1718cf6504 | ||
![]() |
bec8d00232 | ||
![]() |
1471dd72a6 | ||
![]() |
588a786d73 | ||
![]() |
7e1c1da424 | ||
![]() |
94ebe3b61c | ||
![]() |
75c5ccccec | ||
![]() |
eb4c8e1b1e | ||
![]() |
73b1b942a9 | ||
![]() |
8c5ef111d8 | ||
![]() |
b6266ad18f | ||
![]() |
2635c3a1a0 | ||
![]() |
13ece25de0 | ||
![]() |
c69ece1d0e | ||
![]() |
07ec6ff7ab | ||
![]() |
66e23bd356 | ||
![]() |
abc58000b4 | ||
![]() |
5e3ef94697 | ||
![]() |
2daee375d0 | ||
![]() |
857944aabe | ||
![]() |
72f58d54a3 | ||
![]() |
668b068bb5 | ||
![]() |
9893ae9880 | ||
![]() |
9cdf2f046f | ||
![]() |
f7f841ce6d | ||
![]() |
e8a52d48cf | ||
![]() |
21eb253c57 | ||
![]() |
754286cb9a | ||
![]() |
08f5d9a92f | ||
![]() |
a1a61000ab | ||
![]() |
dd91d4264a | ||
![]() |
9d87ac5244 | ||
![]() |
3559e27cdd | ||
![]() |
4b9c79fa07 | ||
![]() |
ee197bf89f | ||
![]() |
31dac60d04 | ||
![]() |
75a7dd38a1 | ||
![]() |
d7cb7c78af | ||
![]() |
148f394679 | ||
![]() |
f57873fb1b | ||
![]() |
32754defef | ||
![]() |
0e83c94832 | ||
![]() |
d2a6f79612 | ||
![]() |
94db39e055 | ||
![]() |
d15773f282 | ||
![]() |
118e3703dc | ||
![]() |
9b1b620a9c | ||
![]() |
6d3feaebfd | ||
![]() |
781929e9a8 | ||
![]() |
5fc7c15039 | ||
![]() |
44f860d9b0 | ||
![]() |
0cfa5211e9 | ||
![]() |
d689a707a4 | ||
![]() |
55e1745889 | ||
![]() |
43ec058593 | ||
![]() |
a4d96061de | ||
![]() |
f1eecd146d | ||
![]() |
1663450c1f | ||
![]() |
d840308392 | ||
![]() |
a08467342c | ||
![]() |
d71d388c08 | ||
![]() |
e2093436ac | ||
![]() |
b7e2013589 | ||
![]() |
f31cee75f3 | ||
![]() |
ec27f3c053 | ||
![]() |
737f00df3a | ||
![]() |
e6804dad2f | ||
![]() |
1875e9852e | ||
![]() |
f021e7fcc3 | ||
![]() |
4cf9ed9d26 | ||
![]() |
f8b77d7ef7 | ||
![]() |
31850c3351 | ||
![]() |
446842ecfc | ||
![]() |
8159b7574c | ||
![]() |
7050f29cff | ||
![]() |
2f32565476 | ||
![]() |
ceeb2da3fe | ||
![]() |
6dc5c1de32 | ||
![]() |
a5ab6f2558 | ||
![]() |
f846c2934c | ||
![]() |
1adb5e724d | ||
![]() |
8fad13b500 | ||
![]() |
6b2ee0e301 | ||
![]() |
7a241950d4 | ||
![]() |
c1a1f6d74e | ||
![]() |
b99422da12 | ||
![]() |
2ec695fba7 | ||
![]() |
109c07b23b | ||
![]() |
bf34c955ff | ||
![]() |
6ece5240a5 | ||
![]() |
211fbf0cf6 | ||
![]() |
f2d635671d | ||
![]() |
8b204cac99 | ||
![]() |
d15c701510 | ||
![]() |
c73688d167 | ||
![]() |
32da039d5f | ||
![]() |
79da613cb6 | ||
![]() |
2973e4672a | ||
![]() |
18e0012a59 | ||
![]() |
2554ced198 | ||
![]() |
fad13c148e | ||
![]() |
dbaa606a9f | ||
![]() |
c0bccc6a95 | ||
![]() |
b1a4eec7be | ||
![]() |
bb8a0d26e2 | ||
![]() |
629a5dd61e | ||
![]() |
b21970fd53 | ||
![]() |
4279ba13e9 | ||
![]() |
28d70438ec | ||
![]() |
ca6454f9fd | ||
![]() |
0ffc9955b2 | ||
![]() |
cf53d0866a | ||
![]() |
3f19c0ed03 | ||
![]() |
355efadf87 | ||
![]() |
927a9781ad | ||
![]() |
70eb22df42 | ||
![]() |
f461485aa0 | ||
![]() |
b6f1ced455 | ||
![]() |
10f36870e6 | ||
![]() |
fdaf9e9b46 | ||
![]() |
57b709824f | ||
![]() |
c7b46ac861 | ||
![]() |
bf28a512c6 | ||
![]() |
4333bd58cf | ||
![]() |
96a29883cd | ||
![]() |
59e359ff98 | ||
![]() |
4603813896 |
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"qpdf": {
|
|
||||||
"version": "11.2.0"
|
|
||||||
},
|
|
||||||
"jbig2enc": {
|
|
||||||
"version": "0.29",
|
|
||||||
"git_tag": "0.29"
|
|
||||||
}
|
|
||||||
}
|
|
14
.codecov.yml
@@ -1,3 +1,17 @@
|
|||||||
|
codecov:
|
||||||
|
require_ci_to_pass: true
|
||||||
|
# https://docs.codecov.com/docs/flags#recommended-automatic-flag-management
|
||||||
|
# Require each flag to have 1 upload before notification
|
||||||
|
flag_management:
|
||||||
|
default_rules:
|
||||||
|
after_n_builds: 1
|
||||||
|
individual_flags:
|
||||||
|
- name: backend
|
||||||
|
paths:
|
||||||
|
- src/
|
||||||
|
- name: frontend
|
||||||
|
paths:
|
||||||
|
- src-ui/
|
||||||
# https://docs.codecov.com/docs/pull-request-comments
|
# https://docs.codecov.com/docs/pull-request-comments
|
||||||
# codecov will only comment if coverage changes
|
# codecov will only comment if coverage changes
|
||||||
comment:
|
comment:
|
||||||
|
14
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
title: "[Feature Request] "
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of what you would like to see.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: other
|
||||||
|
attributes:
|
||||||
|
label: Other
|
||||||
|
description: Add any other context or information about the feature request here.
|
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -20,11 +20,16 @@ NOTE: Please check only one box!
|
|||||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
- [ ] New feature (non-breaking change which adds functionality)
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
- [ ] Other (please explain)
|
- [ ] Other (please explain):
|
||||||
|
|
||||||
## Checklist:
|
## Checklist:
|
||||||
|
|
||||||
|
<!--
|
||||||
|
NOTE: PRs that do not address the following will not be merged, please do not skip any relevant items.
|
||||||
|
-->
|
||||||
|
|
||||||
- [ ] I have read & agree with the [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md).
|
- [ ] I have read & agree with the [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md).
|
||||||
|
- [ ] If applicable, I have included testing coverage for new code in this PR, for [backend](https://docs.paperless-ngx.com/development/#testing) and / or [front-end](https://docs.paperless-ngx.com/development/#testing-and-code-style) changes.
|
||||||
- [ ] 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 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://docs.paperless-ngx.com/development/#back-end-development).
|
- [ ] If applicable, I have checked that all tests pass, see [documentation](https://docs.paperless-ngx.com/development/#back-end-development).
|
||||||
- [ ] I have run all `pre-commit` hooks, see [documentation](https://docs.paperless-ngx.com/development/#code-formatting-with-pre-commit-hooks).
|
- [ ] I have run all `pre-commit` hooks, see [documentation](https://docs.paperless-ngx.com/development/#code-formatting-with-pre-commit-hooks).
|
||||||
|
15
.github/dependabot.yml
vendored
@@ -17,6 +17,21 @@ updates:
|
|||||||
# Add reviewers
|
# Add reviewers
|
||||||
reviewers:
|
reviewers:
|
||||||
- "paperless-ngx/frontend"
|
- "paperless-ngx/frontend"
|
||||||
|
groups:
|
||||||
|
frontend-angular-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "@angular*"
|
||||||
|
- "@ng-*"
|
||||||
|
- "ngx-*"
|
||||||
|
- "ng2-pdf-viewer"
|
||||||
|
frontend-jest-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "@types/jest"
|
||||||
|
- "jest*"
|
||||||
|
frontend-eslint-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "@typescript-eslint*"
|
||||||
|
- "eslint"
|
||||||
|
|
||||||
# Enable version updates for Python
|
# Enable version updates for Python
|
||||||
- package-ecosystem: "pip"
|
- package-ecosystem: "pip"
|
||||||
|
4
.github/release-drafter.yml
vendored
@@ -40,7 +40,7 @@ categories:
|
|||||||
labels:
|
labels:
|
||||||
- 'frontend'
|
- 'frontend'
|
||||||
- 'backend'
|
- 'backend'
|
||||||
collapse-after: 0
|
collapse-after: 1
|
||||||
include-labels:
|
include-labels:
|
||||||
- 'enhancement'
|
- 'enhancement'
|
||||||
- 'bug'
|
- 'bug'
|
||||||
@@ -54,6 +54,8 @@ include-labels:
|
|||||||
- 'ci-cd'
|
- 'ci-cd'
|
||||||
- 'breaking-change'
|
- 'breaking-change'
|
||||||
- 'notable'
|
- 'notable'
|
||||||
|
exclude-labels:
|
||||||
|
- 'skip-changelog'
|
||||||
category-template: '### $TITLE'
|
category-template: '### $TITLE'
|
||||||
change-template: '- $TITLE @$AUTHOR ([#$NUMBER]($URL))'
|
change-template: '- $TITLE @$AUTHOR ([#$NUMBER]($URL))'
|
||||||
change-title-escapes: '\<*_&#@'
|
change-title-escapes: '\<*_&#@'
|
||||||
|
491
.github/scripts/cleanup-tags.py
vendored
@@ -1,491 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
from typing import Dict
|
|
||||||
from typing import Final
|
|
||||||
from typing import Iterator
|
|
||||||
from typing import List
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from common import get_log_level
|
|
||||||
from github import ContainerPackage
|
|
||||||
from github import GithubBranchApi
|
|
||||||
from github import GithubContainerRegistryApi
|
|
||||||
|
|
||||||
logger = logging.getLogger("cleanup-tags")
|
|
||||||
|
|
||||||
|
|
||||||
class ImageProperties:
|
|
||||||
"""
|
|
||||||
Data class wrapping the properties of an entry in the image index
|
|
||||||
manifests list. It is NOT an actual image with layers, etc
|
|
||||||
|
|
||||||
https://docs.docker.com/registry/spec/manifest-v2-2/
|
|
||||||
https://github.com/opencontainers/image-spec/blob/main/manifest.md
|
|
||||||
https://github.com/opencontainers/image-spec/blob/main/descriptor.md
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data: Dict) -> None:
|
|
||||||
self._data = data
|
|
||||||
# This is the sha256: digest string. Corresponds to GitHub API name
|
|
||||||
# if the package is an untagged package
|
|
||||||
self.digest = self._data["digest"]
|
|
||||||
platform_data_os = self._data["platform"]["os"]
|
|
||||||
platform_arch = self._data["platform"]["architecture"]
|
|
||||||
platform_variant = self._data["platform"].get(
|
|
||||||
"variant",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
self.platform = f"{platform_data_os}/{platform_arch}{platform_variant}"
|
|
||||||
|
|
||||||
|
|
||||||
class ImageIndex:
|
|
||||||
"""
|
|
||||||
Data class wrapping up logic for an OCI Image Index
|
|
||||||
JSON data. Primary use is to access the manifests listing
|
|
||||||
|
|
||||||
See https://github.com/opencontainers/image-spec/blob/main/image-index.md
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, package_url: str, tag: str) -> None:
|
|
||||||
self.qualified_name = f"{package_url}:{tag}"
|
|
||||||
logger.info(f"Getting image index for {self.qualified_name}")
|
|
||||||
try:
|
|
||||||
proc = subprocess.run(
|
|
||||||
[
|
|
||||||
shutil.which("docker"),
|
|
||||||
"buildx",
|
|
||||||
"imagetools",
|
|
||||||
"inspect",
|
|
||||||
"--raw",
|
|
||||||
self.qualified_name,
|
|
||||||
],
|
|
||||||
capture_output=True,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._data = json.loads(proc.stdout)
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
logger.error(
|
|
||||||
f"Failed to get image index for {self.qualified_name}: {e.stderr}",
|
|
||||||
)
|
|
||||||
raise e
|
|
||||||
|
|
||||||
@property
|
|
||||||
def image_pointers(self) -> Iterator[ImageProperties]:
|
|
||||||
for manifest_data in self._data["manifests"]:
|
|
||||||
yield ImageProperties(manifest_data)
|
|
||||||
|
|
||||||
|
|
||||||
class RegistryTagsCleaner:
|
|
||||||
"""
|
|
||||||
This is the base class for the image registry cleaning. Given a package
|
|
||||||
name, it will keep all images which are tagged and all untagged images
|
|
||||||
referred to by a manifest. This results in only images which have been untagged
|
|
||||||
and cannot be referenced except by their SHA in being removed. None of these
|
|
||||||
images should be referenced, so it is fine to delete them.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
package_name: str,
|
|
||||||
repo_owner: str,
|
|
||||||
repo_name: str,
|
|
||||||
package_api: GithubContainerRegistryApi,
|
|
||||||
branch_api: Optional[GithubBranchApi],
|
|
||||||
):
|
|
||||||
self.actually_delete = False
|
|
||||||
self.package_api = package_api
|
|
||||||
self.branch_api = branch_api
|
|
||||||
self.package_name = package_name
|
|
||||||
self.repo_owner = repo_owner
|
|
||||||
self.repo_name = repo_name
|
|
||||||
self.tags_to_delete: List[str] = []
|
|
||||||
self.tags_to_keep: List[str] = []
|
|
||||||
|
|
||||||
# Get the information about all versions of the given package
|
|
||||||
# These are active, not deleted, the default returned from the API
|
|
||||||
self.all_package_versions = self.package_api.get_active_package_versions(
|
|
||||||
self.package_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get a mapping from a tag like "1.7.0" or "feature-xyz" to the ContainerPackage
|
|
||||||
# tagged with it. It makes certain lookups easy
|
|
||||||
self.all_pkgs_tags_to_version: Dict[str, ContainerPackage] = {}
|
|
||||||
for pkg in self.all_package_versions:
|
|
||||||
for tag in pkg.tags:
|
|
||||||
self.all_pkgs_tags_to_version[tag] = pkg
|
|
||||||
logger.info(
|
|
||||||
f"Located {len(self.all_package_versions)} versions of package {self.package_name}",
|
|
||||||
)
|
|
||||||
|
|
||||||
self.decide_what_tags_to_keep()
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
"""
|
|
||||||
This method will delete image versions, based on the selected tags to delete.
|
|
||||||
It behaves more like an unlinking than actual deletion. Removing the tag
|
|
||||||
simply removes a pointer to an image, but the actual image data remains accessible
|
|
||||||
if one has the sha256 digest of it.
|
|
||||||
"""
|
|
||||||
for tag_to_delete in self.tags_to_delete:
|
|
||||||
package_version_info = self.all_pkgs_tags_to_version[tag_to_delete]
|
|
||||||
|
|
||||||
if self.actually_delete:
|
|
||||||
logger.info(
|
|
||||||
f"Deleting {tag_to_delete} (id {package_version_info.id})",
|
|
||||||
)
|
|
||||||
self.package_api.delete_package_version(
|
|
||||||
package_version_info,
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
logger.info(
|
|
||||||
f"Would delete {tag_to_delete} (id {package_version_info.id})",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.info("No tags to delete")
|
|
||||||
|
|
||||||
def clean_untagged(self, is_manifest_image: bool):
|
|
||||||
"""
|
|
||||||
This method will delete untagged images, that is those which are not named. It
|
|
||||||
handles if the image tag is actually a manifest, which points to images that look otherwise
|
|
||||||
untagged.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _clean_untagged_manifest():
|
|
||||||
"""
|
|
||||||
|
|
||||||
Handles the deletion of untagged images, but where the package is a manifest, ie a multi
|
|
||||||
arch image, which means some "untagged" images need to exist still.
|
|
||||||
|
|
||||||
Ok, bear with me, these are annoying.
|
|
||||||
|
|
||||||
Our images are multi-arch, so the manifest is more like a pointer to a sha256 digest.
|
|
||||||
These images are untagged, but pointed to, and so should not be removed (or every pull fails).
|
|
||||||
|
|
||||||
So for each image getting kept, parse the manifest to find the digest(s) it points to. Then
|
|
||||||
remove those from the list of untagged images. The final result is the untagged, not pointed to
|
|
||||||
version which should be safe to remove.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
Tag: ghcr.io/paperless-ngx/paperless-ngx:1.7.1 refers to
|
|
||||||
amd64: sha256:b9ed4f8753bbf5146547671052d7e91f68cdfc9ef049d06690b2bc866fec2690
|
|
||||||
armv7: sha256:81605222df4ba4605a2ba4893276e5d08c511231ead1d5da061410e1bbec05c3
|
|
||||||
arm64: sha256:374cd68db40734b844705bfc38faae84cc4182371de4bebd533a9a365d5e8f3b
|
|
||||||
each of which appears as untagged image, but isn't really.
|
|
||||||
|
|
||||||
So from the list of untagged packages, remove those digests. Once all tags which
|
|
||||||
are being kept are checked, the remaining untagged packages are actually untagged
|
|
||||||
with no referrals in a manifest to them.
|
|
||||||
"""
|
|
||||||
# Simplify the untagged data, mapping name (which is a digest) to the version
|
|
||||||
# At the moment, these are the images which APPEAR untagged.
|
|
||||||
untagged_versions = {}
|
|
||||||
for x in self.all_package_versions:
|
|
||||||
if x.untagged:
|
|
||||||
untagged_versions[x.name] = x
|
|
||||||
|
|
||||||
skips = 0
|
|
||||||
|
|
||||||
# Parse manifests to locate digests pointed to
|
|
||||||
for tag in sorted(self.tags_to_keep):
|
|
||||||
try:
|
|
||||||
image_index = ImageIndex(
|
|
||||||
f"ghcr.io/{self.repo_owner}/{self.package_name}",
|
|
||||||
tag,
|
|
||||||
)
|
|
||||||
for manifest in image_index.image_pointers:
|
|
||||||
|
|
||||||
if manifest.digest in untagged_versions:
|
|
||||||
logger.info(
|
|
||||||
f"Skipping deletion of {manifest.digest},"
|
|
||||||
f" referred to by {image_index.qualified_name}"
|
|
||||||
f" for {manifest.platform}",
|
|
||||||
)
|
|
||||||
del untagged_versions[manifest.digest]
|
|
||||||
skips += 1
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
self.actually_delete = False
|
|
||||||
logger.exception(err)
|
|
||||||
return
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f"Skipping deletion of {skips} packages referred to by a manifest",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delete the untagged and not pointed at packages
|
|
||||||
logger.info(f"Deleting untagged packages of {self.package_name}")
|
|
||||||
for to_delete_name in untagged_versions:
|
|
||||||
to_delete_version = untagged_versions[to_delete_name]
|
|
||||||
|
|
||||||
if self.actually_delete:
|
|
||||||
logger.info(
|
|
||||||
f"Deleting id {to_delete_version.id} named {to_delete_version.name}",
|
|
||||||
)
|
|
||||||
self.package_api.delete_package_version(
|
|
||||||
to_delete_version,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.info(
|
|
||||||
f"Would delete {to_delete_name} (id {to_delete_version.id})",
|
|
||||||
)
|
|
||||||
|
|
||||||
def _clean_untagged_non_manifest():
|
|
||||||
"""
|
|
||||||
If the package is not a multi-arch manifest, images without tags are safe to delete.
|
|
||||||
"""
|
|
||||||
|
|
||||||
for package in self.all_package_versions:
|
|
||||||
if package.untagged:
|
|
||||||
if self.actually_delete:
|
|
||||||
logger.info(
|
|
||||||
f"Deleting id {package.id} named {package.name}",
|
|
||||||
)
|
|
||||||
self.package_api.delete_package_version(
|
|
||||||
package,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.info(
|
|
||||||
f"Would delete {package.name} (id {package.id})",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.info(
|
|
||||||
f"Not deleting tag {package.tags[0]} of package {self.package_name}",
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("Beginning untagged image cleaning")
|
|
||||||
|
|
||||||
if is_manifest_image:
|
|
||||||
_clean_untagged_manifest()
|
|
||||||
else:
|
|
||||||
_clean_untagged_non_manifest()
|
|
||||||
|
|
||||||
def decide_what_tags_to_keep(self):
|
|
||||||
"""
|
|
||||||
This method holds the logic to delete what tags to keep and there fore
|
|
||||||
what tags to delete.
|
|
||||||
|
|
||||||
By default, any image with at least 1 tag will be kept
|
|
||||||
"""
|
|
||||||
# By default, keep anything which is tagged
|
|
||||||
self.tags_to_keep = list(set(self.all_pkgs_tags_to_version.keys()))
|
|
||||||
|
|
||||||
def check_remaining_tags_valid(self):
|
|
||||||
"""
|
|
||||||
Checks the non-deleted tags are still valid. The assumption is if the
|
|
||||||
manifest is can be inspected and each image manifest if points to can be
|
|
||||||
inspected, the image will still pull.
|
|
||||||
|
|
||||||
https://github.com/opencontainers/image-spec/blob/main/image-index.md
|
|
||||||
"""
|
|
||||||
logger.info("Beginning confirmation step")
|
|
||||||
a_tag_failed = False
|
|
||||||
for tag in sorted(self.tags_to_keep):
|
|
||||||
|
|
||||||
try:
|
|
||||||
image_index = ImageIndex(
|
|
||||||
f"ghcr.io/{self.repo_owner}/{self.package_name}",
|
|
||||||
tag,
|
|
||||||
)
|
|
||||||
for manifest in image_index.image_pointers:
|
|
||||||
logger.info(f"Checking {manifest.digest} for {manifest.platform}")
|
|
||||||
|
|
||||||
# This follows the pointer from the index to an actual image, layers and all
|
|
||||||
# Note the format is @
|
|
||||||
digest_name = f"ghcr.io/{self.repo_owner}/{self.package_name}@{manifest.digest}"
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
shutil.which("docker"),
|
|
||||||
"buildx",
|
|
||||||
"imagetools",
|
|
||||||
"inspect",
|
|
||||||
"--raw",
|
|
||||||
digest_name,
|
|
||||||
],
|
|
||||||
capture_output=True,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
logger.error(f"Failed to inspect digest: {e.stderr}")
|
|
||||||
a_tag_failed = True
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
a_tag_failed = True
|
|
||||||
logger.error(f"Failed to inspect: {e.stderr}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
if a_tag_failed:
|
|
||||||
raise Exception("At least one image tag failed to inspect")
|
|
||||||
|
|
||||||
|
|
||||||
class MainImageTagsCleaner(RegistryTagsCleaner):
|
|
||||||
def decide_what_tags_to_keep(self):
|
|
||||||
"""
|
|
||||||
Overrides the default logic for deciding what images to keep. Images tagged as "feature-"
|
|
||||||
will be removed, if the corresponding branch no longer exists.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Default to everything gets kept still
|
|
||||||
super().decide_what_tags_to_keep()
|
|
||||||
|
|
||||||
# Locate the feature branches
|
|
||||||
feature_branches = {}
|
|
||||||
for branch in self.branch_api.get_branches(
|
|
||||||
repo=self.repo_name,
|
|
||||||
):
|
|
||||||
if branch.name.startswith("feature-"):
|
|
||||||
logger.debug(f"Found feature branch {branch.name}")
|
|
||||||
feature_branches[branch.name] = branch
|
|
||||||
|
|
||||||
logger.info(f"Located {len(feature_branches)} feature branches")
|
|
||||||
|
|
||||||
if not len(feature_branches):
|
|
||||||
# Our work here is done, delete nothing
|
|
||||||
return
|
|
||||||
|
|
||||||
# Filter to packages which are tagged with feature-*
|
|
||||||
packages_tagged_feature: List[ContainerPackage] = []
|
|
||||||
for package in self.all_package_versions:
|
|
||||||
if package.tag_matches("feature-"):
|
|
||||||
packages_tagged_feature.append(package)
|
|
||||||
|
|
||||||
# Map tags like "feature-xyz" to a ContainerPackage
|
|
||||||
feature_pkgs_tags_to_versions: Dict[str, ContainerPackage] = {}
|
|
||||||
for pkg in packages_tagged_feature:
|
|
||||||
for tag in pkg.tags:
|
|
||||||
feature_pkgs_tags_to_versions[tag] = pkg
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
f'Located {len(feature_pkgs_tags_to_versions)} versions of package {self.package_name} tagged "feature-"',
|
|
||||||
)
|
|
||||||
|
|
||||||
# All the feature tags minus all the feature branches leaves us feature tags
|
|
||||||
# with no corresponding branch
|
|
||||||
self.tags_to_delete = list(
|
|
||||||
set(feature_pkgs_tags_to_versions.keys()) - set(feature_branches.keys()),
|
|
||||||
)
|
|
||||||
|
|
||||||
# All the tags minus the set of going to be deleted tags leaves us the
|
|
||||||
# tags which will be kept around
|
|
||||||
self.tags_to_keep = list(
|
|
||||||
set(self.all_pkgs_tags_to_version.keys()) - set(self.tags_to_delete),
|
|
||||||
)
|
|
||||||
logger.info(
|
|
||||||
f"Located {len(self.tags_to_delete)} versions of package {self.package_name} to delete",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LibraryTagsCleaner(RegistryTagsCleaner):
|
|
||||||
"""
|
|
||||||
Exists for the off chance that someday, the installer library images
|
|
||||||
will need their own logic
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
|
||||||
parser = ArgumentParser(
|
|
||||||
description="Using the GitHub API locate and optionally delete container"
|
|
||||||
" tags which no longer have an associated feature branch",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Requires an affirmative command to actually do a delete
|
|
||||||
parser.add_argument(
|
|
||||||
"--delete",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="If provided, actually delete the container tags",
|
|
||||||
)
|
|
||||||
|
|
||||||
# When a tagged image is updated, the previous version remains, but it no longer tagged
|
|
||||||
# Add this option to remove them as well
|
|
||||||
parser.add_argument(
|
|
||||||
"--untagged",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="If provided, delete untagged containers as well",
|
|
||||||
)
|
|
||||||
|
|
||||||
# If given, the package is assumed to be a multi-arch manifest. Cache packages are
|
|
||||||
# not multi-arch, all other types are
|
|
||||||
parser.add_argument(
|
|
||||||
"--is-manifest",
|
|
||||||
action="store_true",
|
|
||||||
default=False,
|
|
||||||
help="If provided, the package is assumed to be a multi-arch manifest following schema v2",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Allows configuration of log level for debugging
|
|
||||||
parser.add_argument(
|
|
||||||
"--loglevel",
|
|
||||||
default="info",
|
|
||||||
help="Configures the logging level",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get the name of the package being processed this round
|
|
||||||
parser.add_argument(
|
|
||||||
"package",
|
|
||||||
help="The package to process",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=get_log_level(args),
|
|
||||||
datefmt="%Y-%m-%d %H:%M:%S",
|
|
||||||
format="%(asctime)s %(levelname)-8s %(message)s",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Must be provided in the environment
|
|
||||||
repo_owner: Final[str] = os.environ["GITHUB_REPOSITORY_OWNER"]
|
|
||||||
repo: Final[str] = os.environ["GITHUB_REPOSITORY"]
|
|
||||||
gh_token: Final[str] = os.environ["TOKEN"]
|
|
||||||
|
|
||||||
# Find all branches named feature-*
|
|
||||||
# Note: Only relevant to the main application, but simpler to
|
|
||||||
# leave in for all packages
|
|
||||||
with GithubBranchApi(gh_token) as branch_api:
|
|
||||||
with GithubContainerRegistryApi(gh_token, repo_owner) as container_api:
|
|
||||||
if args.package in {"paperless-ngx", "paperless-ngx/builder/cache/app"}:
|
|
||||||
cleaner = MainImageTagsCleaner(
|
|
||||||
args.package,
|
|
||||||
repo_owner,
|
|
||||||
repo,
|
|
||||||
container_api,
|
|
||||||
branch_api,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
cleaner = LibraryTagsCleaner(
|
|
||||||
args.package,
|
|
||||||
repo_owner,
|
|
||||||
repo,
|
|
||||||
container_api,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Set if actually doing a delete vs dry run
|
|
||||||
cleaner.actually_delete = args.delete
|
|
||||||
|
|
||||||
# Clean images with tags
|
|
||||||
cleaner.clean()
|
|
||||||
|
|
||||||
# Clean images which are untagged
|
|
||||||
cleaner.clean_untagged(args.is_manifest)
|
|
||||||
|
|
||||||
# Verify remaining tags still pull
|
|
||||||
if args.is_manifest:
|
|
||||||
cleaner.check_remaining_tags_valid()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
_main()
|
|
48
.github/scripts/common.py
vendored
@@ -1,48 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
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.lower()}/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.lower()}/builder/cache/{pkg_name}:{pkg_version}"
|
|
||||||
|
|
||||||
|
|
||||||
def get_log_level(args) -> int:
|
|
||||||
"""
|
|
||||||
Returns a logging level, based
|
|
||||||
:param args:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
levels = {
|
|
||||||
"critical": logging.CRITICAL,
|
|
||||||
"error": logging.ERROR,
|
|
||||||
"warn": logging.WARNING,
|
|
||||||
"warning": logging.WARNING,
|
|
||||||
"info": logging.INFO,
|
|
||||||
"debug": logging.DEBUG,
|
|
||||||
}
|
|
||||||
level = levels.get(args.loglevel.lower())
|
|
||||||
if level is None:
|
|
||||||
level = logging.INFO
|
|
||||||
return level
|
|
92
.github/scripts/get-build-json.py
vendored
@@ -1,92 +0,0 @@
|
|||||||
#!/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
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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"]
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise NotImplementedError(args.package)
|
|
||||||
|
|
||||||
# The JSON object we'll output
|
|
||||||
output = {
|
|
||||||
"name": args.package,
|
|
||||||
"version": version,
|
|
||||||
"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()
|
|
274
.github/scripts/github.py
vendored
@@ -1,274 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
This module contains some useful classes for interacting with the Github API.
|
|
||||||
The full documentation for the API can be found here: https://docs.github.com/en/rest
|
|
||||||
|
|
||||||
Mostly, this focusses on two areas, repo branches and repo packages, as the use case
|
|
||||||
is cleaning up container images which are no longer referred to.
|
|
||||||
|
|
||||||
"""
|
|
||||||
import functools
|
|
||||||
import logging
|
|
||||||
import re
|
|
||||||
import urllib.parse
|
|
||||||
from typing import Dict
|
|
||||||
from typing import List
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
logger = logging.getLogger("github-api")
|
|
||||||
|
|
||||||
|
|
||||||
class _GithubApiBase:
|
|
||||||
"""
|
|
||||||
A base class for interacting with the Github API. It
|
|
||||||
will handle the session and setting authorization headers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, token: str) -> None:
|
|
||||||
self._token = token
|
|
||||||
self._client: Optional[httpx.Client] = None
|
|
||||||
|
|
||||||
def __enter__(self) -> "_GithubApiBase":
|
|
||||||
"""
|
|
||||||
Sets up the required headers for auth and response
|
|
||||||
type from the API
|
|
||||||
"""
|
|
||||||
self._client = httpx.Client()
|
|
||||||
self._client.headers.update(
|
|
||||||
{
|
|
||||||
"Accept": "application/vnd.github.v3+json",
|
|
||||||
"Authorization": f"token {self._token}",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
"""
|
|
||||||
Ensures the authorization token is cleaned up no matter
|
|
||||||
the reason for the exit
|
|
||||||
"""
|
|
||||||
if "Accept" in self._client.headers:
|
|
||||||
del self._client.headers["Accept"]
|
|
||||||
if "Authorization" in self._client.headers:
|
|
||||||
del self._client.headers["Authorization"]
|
|
||||||
|
|
||||||
# Close the session as well
|
|
||||||
self._client.close()
|
|
||||||
self._client = None
|
|
||||||
|
|
||||||
def _read_all_pages(self, endpoint):
|
|
||||||
"""
|
|
||||||
Helper function to read all pages of an endpoint, utilizing the
|
|
||||||
next.url until exhausted. Assumes the endpoint returns a list
|
|
||||||
"""
|
|
||||||
internal_data = []
|
|
||||||
|
|
||||||
while True:
|
|
||||||
resp = self._client.get(endpoint)
|
|
||||||
if resp.status_code == 200:
|
|
||||||
internal_data += resp.json()
|
|
||||||
if "next" in resp.links:
|
|
||||||
endpoint = resp.links["next"]["url"]
|
|
||||||
else:
|
|
||||||
logger.debug("Exiting pagination loop")
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
logger.warning(f"Request to {endpoint} return HTTP {resp.status_code}")
|
|
||||||
resp.raise_for_status()
|
|
||||||
|
|
||||||
return internal_data
|
|
||||||
|
|
||||||
|
|
||||||
class _EndpointResponse:
|
|
||||||
"""
|
|
||||||
For all endpoint JSON responses, store the full
|
|
||||||
response data, for ease of extending later, if need be.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data: Dict) -> None:
|
|
||||||
self._data = data
|
|
||||||
|
|
||||||
|
|
||||||
class GithubBranch(_EndpointResponse):
|
|
||||||
"""
|
|
||||||
Simple wrapper for a repository branch, only extracts name information
|
|
||||||
for now.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data: Dict) -> None:
|
|
||||||
super().__init__(data)
|
|
||||||
self.name = self._data["name"]
|
|
||||||
|
|
||||||
|
|
||||||
class GithubBranchApi(_GithubApiBase):
|
|
||||||
"""
|
|
||||||
Wrapper around branch API.
|
|
||||||
|
|
||||||
See https://docs.github.com/en/rest/branches/branches
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, token: str) -> None:
|
|
||||||
super().__init__(token)
|
|
||||||
|
|
||||||
self._ENDPOINT = "https://api.github.com/repos/{REPO}/branches"
|
|
||||||
|
|
||||||
def get_branches(self, repo: str) -> List[GithubBranch]:
|
|
||||||
"""
|
|
||||||
Returns all current branches of the given repository owned by the given
|
|
||||||
owner or organization.
|
|
||||||
"""
|
|
||||||
# The environment GITHUB_REPOSITORY already contains the owner in the correct location
|
|
||||||
endpoint = self._ENDPOINT.format(REPO=repo)
|
|
||||||
internal_data = self._read_all_pages(endpoint)
|
|
||||||
return [GithubBranch(branch) for branch in internal_data]
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerPackage(_EndpointResponse):
|
|
||||||
"""
|
|
||||||
Data class wrapping the JSON response from the package related
|
|
||||||
endpoints
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data: Dict):
|
|
||||||
super().__init__(data)
|
|
||||||
# This is a numerical ID, required for interactions with this
|
|
||||||
# specific package, including deletion of it or restoration
|
|
||||||
self.id: int = self._data["id"]
|
|
||||||
|
|
||||||
# A string name. This might be an actual name or it could be a
|
|
||||||
# digest string like "sha256:"
|
|
||||||
self.name: str = self._data["name"]
|
|
||||||
|
|
||||||
# URL to the package, including its ID, can be used for deletion
|
|
||||||
# or restoration without needing to build up a URL ourselves
|
|
||||||
self.url: str = self._data["url"]
|
|
||||||
|
|
||||||
# The list of tags applied to this image. Maybe an empty list
|
|
||||||
self.tags: List[str] = self._data["metadata"]["container"]["tags"]
|
|
||||||
|
|
||||||
@functools.cached_property
|
|
||||||
def untagged(self) -> bool:
|
|
||||||
"""
|
|
||||||
Returns True if the image has no tags applied to it, False otherwise
|
|
||||||
"""
|
|
||||||
return len(self.tags) == 0
|
|
||||||
|
|
||||||
@functools.cache
|
|
||||||
def tag_matches(self, pattern: str) -> bool:
|
|
||||||
"""
|
|
||||||
Returns True if the image has at least one tag which matches the given regex,
|
|
||||||
False otherwise
|
|
||||||
"""
|
|
||||||
for tag in self.tags:
|
|
||||||
if re.match(pattern, tag) is not None:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"Package {self.name}"
|
|
||||||
|
|
||||||
|
|
||||||
class GithubContainerRegistryApi(_GithubApiBase):
|
|
||||||
"""
|
|
||||||
Class wrapper to deal with the Github packages API. This class only deals with
|
|
||||||
container type packages, the only type published by paperless-ngx.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, token: str, owner_or_org: str) -> None:
|
|
||||||
super().__init__(token)
|
|
||||||
self._owner_or_org = owner_or_org
|
|
||||||
if self._owner_or_org == "paperless-ngx":
|
|
||||||
# https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-an-organization
|
|
||||||
self._PACKAGES_VERSIONS_ENDPOINT = "https://api.github.com/orgs/{ORG}/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions"
|
|
||||||
# https://docs.github.com/en/rest/packages#delete-package-version-for-an-organization
|
|
||||||
self._PACKAGE_VERSION_DELETE_ENDPOINT = "https://api.github.com/orgs/{ORG}/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions/{PACKAGE_VERSION_ID}"
|
|
||||||
else:
|
|
||||||
# https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-the-authenticated-user
|
|
||||||
self._PACKAGES_VERSIONS_ENDPOINT = "https://api.github.com/user/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions"
|
|
||||||
# https://docs.github.com/en/rest/packages#delete-a-package-version-for-the-authenticated-user
|
|
||||||
self._PACKAGE_VERSION_DELETE_ENDPOINT = "https://api.github.com/user/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions/{PACKAGE_VERSION_ID}"
|
|
||||||
self._PACKAGE_VERSION_RESTORE_ENDPOINT = (
|
|
||||||
f"{self._PACKAGE_VERSION_DELETE_ENDPOINT}/restore"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_active_package_versions(
|
|
||||||
self,
|
|
||||||
package_name: str,
|
|
||||||
) -> List[ContainerPackage]:
|
|
||||||
"""
|
|
||||||
Returns all the versions of a given package (container images) from
|
|
||||||
the API
|
|
||||||
"""
|
|
||||||
|
|
||||||
package_type: str = "container"
|
|
||||||
# Need to quote this for slashes in the name
|
|
||||||
package_name = urllib.parse.quote(package_name, safe="")
|
|
||||||
|
|
||||||
endpoint = self._PACKAGES_VERSIONS_ENDPOINT.format(
|
|
||||||
ORG=self._owner_or_org,
|
|
||||||
PACKAGE_TYPE=package_type,
|
|
||||||
PACKAGE_NAME=package_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
pkgs = []
|
|
||||||
|
|
||||||
for data in self._read_all_pages(endpoint):
|
|
||||||
pkgs.append(ContainerPackage(data))
|
|
||||||
|
|
||||||
return pkgs
|
|
||||||
|
|
||||||
def get_deleted_package_versions(
|
|
||||||
self,
|
|
||||||
package_name: str,
|
|
||||||
) -> List[ContainerPackage]:
|
|
||||||
package_type: str = "container"
|
|
||||||
# Need to quote this for slashes in the name
|
|
||||||
package_name = urllib.parse.quote(package_name, safe="")
|
|
||||||
|
|
||||||
endpoint = (
|
|
||||||
self._PACKAGES_VERSIONS_ENDPOINT.format(
|
|
||||||
ORG=self._owner_or_org,
|
|
||||||
PACKAGE_TYPE=package_type,
|
|
||||||
PACKAGE_NAME=package_name,
|
|
||||||
)
|
|
||||||
+ "?state=deleted"
|
|
||||||
)
|
|
||||||
|
|
||||||
pkgs = []
|
|
||||||
|
|
||||||
for data in self._read_all_pages(endpoint):
|
|
||||||
pkgs.append(ContainerPackage(data))
|
|
||||||
|
|
||||||
return pkgs
|
|
||||||
|
|
||||||
def delete_package_version(self, package_data: ContainerPackage):
|
|
||||||
"""
|
|
||||||
Deletes the given package version from the GHCR
|
|
||||||
"""
|
|
||||||
resp = self._client.delete(package_data.url)
|
|
||||||
if resp.status_code != 204:
|
|
||||||
logger.warning(
|
|
||||||
f"Request to delete {package_data.url} returned HTTP {resp.status_code}",
|
|
||||||
)
|
|
||||||
|
|
||||||
def restore_package_version(
|
|
||||||
self,
|
|
||||||
package_name: str,
|
|
||||||
package_data: ContainerPackage,
|
|
||||||
):
|
|
||||||
package_type: str = "container"
|
|
||||||
endpoint = self._PACKAGE_VERSION_RESTORE_ENDPOINT.format(
|
|
||||||
ORG=self._owner_or_org,
|
|
||||||
PACKAGE_TYPE=package_type,
|
|
||||||
PACKAGE_NAME=package_name,
|
|
||||||
PACKAGE_VERSION_ID=package_data.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
resp = self._client.post(endpoint)
|
|
||||||
if resp.status_code != 204:
|
|
||||||
logger.warning(
|
|
||||||
f"Request to delete {endpoint} returned HTTP {resp.status_code}",
|
|
||||||
)
|
|
23
.github/stale.yml
vendored
@@ -1,23 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
|
||||||
onlyLabels: [cant-reproduce]
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# See https://github.com/marketplace/stale for more info on the app
|
|
||||||
# and https://github.com/probot/stale for the configuration docs
|
|
209
.github/workflows/ci.yml
vendored
@@ -16,7 +16,7 @@ on:
|
|||||||
env:
|
env:
|
||||||
# This is the version of pipenv all the steps will use
|
# This is the version of pipenv all the steps will use
|
||||||
# If changing this, change Dockerfile
|
# If changing this, change Dockerfile
|
||||||
DEFAULT_PIP_ENV_VERSION: "2022.11.30"
|
DEFAULT_PIP_ENV_VERSION: "2023.7.23"
|
||||||
# This is the default version of Python to use in most steps
|
# This is the default version of Python to use in most steps
|
||||||
# If changing this, change Dockerfile
|
# If changing this, change Dockerfile
|
||||||
DEFAULT_PYTHON_VERSION: "3.9"
|
DEFAULT_PYTHON_VERSION: "3.9"
|
||||||
@@ -77,6 +77,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
path: site/
|
path: site/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
documentation-deploy:
|
documentation-deploy:
|
||||||
name: "Deploy Documentation"
|
name: "Deploy Documentation"
|
||||||
@@ -106,15 +107,6 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
python-version: ['3.8', '3.9', '3.10']
|
python-version: ['3.8', '3.9', '3.10']
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
env:
|
|
||||||
# Enable Tika end to end testing
|
|
||||||
TIKA_LIVE: 1
|
|
||||||
# Enable paperless_mail testing against real server
|
|
||||||
PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }}
|
|
||||||
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
|
||||||
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
|
||||||
# Enable Gotenberg end to end testing
|
|
||||||
GOTENBERG_LIVE: 1
|
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
@@ -156,18 +148,24 @@ jobs:
|
|||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
||||||
-
|
-
|
||||||
name: Tests
|
name: Tests
|
||||||
|
env:
|
||||||
|
PAPERLESS_CI_TEST: 1
|
||||||
|
# Enable paperless_mail testing against real server
|
||||||
|
PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }}
|
||||||
|
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
||||||
|
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
|
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
|
||||||
-
|
-
|
||||||
name: Upload coverage to Codecov
|
name: Upload coverage
|
||||||
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
||||||
uses: codecov/codecov-action@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
# not required for public repos, but intermittently fails otherwise
|
name: backend-coverage-report
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
path: src/coverage.xml
|
||||||
# future expansion
|
retention-days: 7
|
||||||
flags: backend
|
if-no-files-found: warn
|
||||||
-
|
-
|
||||||
name: Stop containers
|
name: Stop containers
|
||||||
if: always()
|
if: always()
|
||||||
@@ -190,95 +188,94 @@ jobs:
|
|||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: cd src-ui && npm ci
|
cache: 'npm'
|
||||||
- run: cd src-ui && npm run lint
|
cache-dependency-path: 'src-ui/package-lock.json'
|
||||||
- run: cd src-ui && npm run test
|
-
|
||||||
- run: cd src-ui && npm run e2e:ci
|
name: Install dependencies
|
||||||
|
run: cd src-ui && npm ci
|
||||||
|
-
|
||||||
|
name: Install Playwright
|
||||||
|
run: npx playwright install --with-deps
|
||||||
|
-
|
||||||
|
name: Linting checks
|
||||||
|
run: cd src-ui && npm run lint
|
||||||
|
-
|
||||||
|
name: Run Jest unit tests
|
||||||
|
run: cd src-ui && npm run test
|
||||||
|
-
|
||||||
|
name: Upload Jest coverage
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: jest-coverage-report
|
||||||
|
path: src-ui/coverage
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: warn
|
||||||
|
-
|
||||||
|
name: Run Playwright e2e tests
|
||||||
|
run: cd src-ui && npx playwright test
|
||||||
|
-
|
||||||
|
name: Upload Playwright test results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: src-ui/playwright-report
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
prepare-docker-build:
|
tests-coverage-upload:
|
||||||
name: Prepare Docker Pipeline Data
|
name: "Upload coverage"
|
||||||
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-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs:
|
needs:
|
||||||
- documentation
|
|
||||||
- tests-backend
|
- tests-backend
|
||||||
- tests-frontend
|
- tests-frontend
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Set ghcr repository name
|
|
||||||
id: set-ghcr-repository
|
|
||||||
run: |
|
|
||||||
ghcr_name=$(echo "${GITHUB_REPOSITORY}" | awk '{ print tolower($0) }')
|
|
||||||
echo "repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
-
|
-
|
||||||
name: Set up Python
|
name: Download frontend coverage
|
||||||
uses: actions/setup-python@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
name: jest-coverage-report
|
||||||
|
path: src-ui/
|
||||||
-
|
-
|
||||||
name: Setup qpdf image
|
name: Upload frontend coverage to Codecov
|
||||||
id: qpdf-setup
|
uses: codecov/codecov-action@v3
|
||||||
run: |
|
with:
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py qpdf)
|
# not required for public repos, but intermittently fails otherwise
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
echo ${build_json}
|
flags: frontend
|
||||||
|
directory: src-ui/
|
||||||
echo "qpdf-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
-
|
||||||
name: Setup psycopg2 image
|
name: Download backend coverage
|
||||||
id: psycopg2-setup
|
uses: actions/download-artifact@v3
|
||||||
run: |
|
with:
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py psycopg2)
|
name: backend-coverage-report
|
||||||
|
path: src/
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "psycopg2-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
-
|
||||||
name: Setup pikepdf image
|
name: Upload coverage to Codecov
|
||||||
id: pikepdf-setup
|
uses: codecov/codecov-action@v3
|
||||||
run: |
|
with:
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py pikepdf)
|
# not required for public repos, but intermittently fails otherwise
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
# future expansion
|
||||||
|
flags: backend
|
||||||
|
directory: src/
|
||||||
|
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "pikepdf-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Setup jbig2enc image
|
|
||||||
id: jbig2enc-setup
|
|
||||||
run: |
|
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py jbig2enc)
|
|
||||||
|
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "jbig2enc-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
|
|
||||||
ghcr-repository: ${{ steps.set-ghcr-repository.outputs.repository }}
|
|
||||||
|
|
||||||
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 and push image to docker hub.
|
|
||||||
build-docker-image:
|
build-docker-image:
|
||||||
|
name: Build Docker image for ${{ github.ref_name }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
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'))
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-build-docker-image-${{ github.ref_name }}
|
group: ${{ github.workflow }}-build-docker-image-${{ github.ref_name }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
needs:
|
needs:
|
||||||
- prepare-docker-build
|
- tests-backend
|
||||||
|
- tests-frontend
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Check pushing to Docker Hub
|
name: Check pushing to Docker Hub
|
||||||
id: docker-hub
|
id: push-other-places
|
||||||
# Only push to Dockerhub from the main repo AND the ref is either:
|
# Only push to Dockerhub from the main repo AND the ref is either:
|
||||||
# main
|
# main
|
||||||
# dev
|
# dev
|
||||||
@@ -286,21 +283,29 @@ jobs:
|
|||||||
# a tag
|
# a tag
|
||||||
# Otherwise forks would require a Docker Hub account and secrets setup
|
# Otherwise forks would require a Docker Hub account and secrets setup
|
||||||
run: |
|
run: |
|
||||||
if [[ ${{ needs.prepare-docker-build.outputs.ghcr-repository }} == "paperless-ngx/paperless-ngx" && ( ${{ github.ref_name }} == "main" || ${{ github.ref_name }} == "dev" || ${{ github.ref_name }} == "beta" || ${{ startsWith(github.ref, 'refs/tags/v') }} == "true" ) ]] ; then
|
if [[ ${{ github.repository_owner }} == "paperless-ngx" && ( ${{ github.ref_name }} == "main" || ${{ github.ref_name }} == "dev" || ${{ github.ref_name }} == "beta" || ${{ startsWith(github.ref, 'refs/tags/v') }} == "true" ) ]] ; then
|
||||||
echo "Enabling DockerHub image push"
|
echo "Enabling DockerHub image push"
|
||||||
echo "enable=true" >> $GITHUB_OUTPUT
|
echo "enable=true" >> $GITHUB_OUTPUT
|
||||||
else
|
else
|
||||||
echo "Not pushing to DockerHub"
|
echo "Not pushing to DockerHub"
|
||||||
echo "enable=false" >> $GITHUB_OUTPUT
|
echo "enable=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
-
|
||||||
|
name: Set ghcr repository name
|
||||||
|
id: set-ghcr-repository
|
||||||
|
run: |
|
||||||
|
ghcr_name=$(echo "${{ github.repository }}" | awk '{ print tolower($0) }')
|
||||||
|
echo "Name is ${ghcr_name}"
|
||||||
|
echo "ghcr-repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
||||||
-
|
-
|
||||||
name: Gather Docker metadata
|
name: Gather Docker metadata
|
||||||
id: docker-meta
|
id: docker-meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/${{ needs.prepare-docker-build.outputs.ghcr-repository }}
|
ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}
|
||||||
name=paperlessngx/paperless-ngx,enable=${{ steps.docker-hub.outputs.enable }}
|
name=paperlessngx/paperless-ngx,enable=${{ steps.push-other-places.outputs.enable }}
|
||||||
|
name=quay.io/paperlessngx/paperless-ngx,enable=${{ steps.push-other-places.outputs.enable }}
|
||||||
tags: |
|
tags: |
|
||||||
# Tag branches with branch name
|
# Tag branches with branch name
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
@@ -311,6 +316,9 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
# If https://github.com/docker/buildx/issues/1044 is resolved,
|
||||||
|
# the append input with a native arm64 arch could be used to
|
||||||
|
# significantly speed up building
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
@@ -328,10 +336,19 @@ jobs:
|
|||||||
name: Login to Docker Hub
|
name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
# Don't attempt to login is not pushing to Docker Hub
|
# Don't attempt to login is not pushing to Docker Hub
|
||||||
if: steps.docker-hub.outputs.enable == 'true'
|
if: steps.push-other-places.outputs.enable == 'true'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
-
|
||||||
|
name: Login to Quay.io
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
# Don't attempt to login is not pushing to Quay.io
|
||||||
|
if: steps.push-other-places.outputs.enable == 'true'
|
||||||
|
with:
|
||||||
|
registry: quay.io
|
||||||
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
|
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Build and push
|
name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
@@ -342,19 +359,13 @@ jobs:
|
|||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.docker-meta.outputs.tags }}
|
tags: ${{ steps.docker-meta.outputs.tags }}
|
||||||
labels: ${{ steps.docker-meta.outputs.labels }}
|
labels: ${{ steps.docker-meta.outputs.labels }}
|
||||||
build-args: |
|
# Get cache layers from this branch, then dev
|
||||||
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
|
# This allows new branches to get at least some cache benefits, generally from dev
|
||||||
cache-from: |
|
cache-from: |
|
||||||
type=registry,ref=ghcr.io/${{ needs.prepare-docker-build.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||||
type=registry,ref=ghcr.io/${{ needs.prepare-docker-build.outputs.ghcr-repository }}/builder/cache/app:dev
|
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:dev
|
||||||
type=registry,ref=ghcr.io/${{ needs.prepare-docker-build.outputs.ghcr-repository }}/builder/cache/app:main
|
|
||||||
cache-to: |
|
cache-to: |
|
||||||
type=registry,mode=max,ref=ghcr.io/${{ needs.prepare-docker-build.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
type=registry,mode=max,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||||
-
|
-
|
||||||
name: Inspect image
|
name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
@@ -370,6 +381,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: frontend-compiled
|
name: frontend-compiled
|
||||||
path: src/documents/static/frontend/
|
path: src/documents/static/frontend/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
needs:
|
needs:
|
||||||
@@ -451,7 +463,7 @@ jobs:
|
|||||||
do
|
do
|
||||||
cp --verbose ${file_name} dist/paperless-ngx/
|
cp --verbose ${file_name} dist/paperless-ngx/
|
||||||
done
|
done
|
||||||
mv --verbose dist/paperless-ngx/paperless.conf.example paperless.conf
|
mv --verbose dist/paperless-ngx/paperless.conf.example dist/paperless-ngx/paperless.conf
|
||||||
|
|
||||||
echo "Copying Docker related files"
|
echo "Copying Docker related files"
|
||||||
cp --recursive docker/ dist/paperless-ngx/docker
|
cp --recursive docker/ dist/paperless-ngx/docker
|
||||||
@@ -478,6 +490,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: release
|
name: release
|
||||||
path: dist/paperless-ngx.tar.xz
|
path: dist/paperless-ngx.tar.xz
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
publish-release:
|
publish-release:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -586,5 +599,5 @@ jobs:
|
|||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
issue_number: result.data.number,
|
issue_number: result.data.number,
|
||||||
labels: ['documentation']
|
labels: ['documentation', 'skip-changelog']
|
||||||
});
|
});
|
||||||
|
98
.github/workflows/cleanup-tags.yml
vendored
@@ -12,9 +12,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/cleanup-tags.yml"
|
- ".github/workflows/cleanup-tags.yml"
|
||||||
- ".github/scripts/cleanup-tags.py"
|
|
||||||
- ".github/scripts/github.py"
|
|
||||||
- ".github/scripts/common.py"
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: registry-tags-cleanup
|
group: registry-tags-cleanup
|
||||||
@@ -22,62 +19,59 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup-images:
|
cleanup-images:
|
||||||
name: Cleanup Image Tags for ${{ matrix.primary-name }}
|
name: Cleanup Image Tags for paperless-ngx
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- primary-name: "paperless-ngx"
|
|
||||||
cache-name: "paperless-ngx/builder/cache/app"
|
|
||||||
|
|
||||||
- primary-name: "paperless-ngx/builder/qpdf"
|
|
||||||
cache-name: "paperless-ngx/builder/cache/qpdf"
|
|
||||||
|
|
||||||
- primary-name: "paperless-ngx/builder/pikepdf"
|
|
||||||
cache-name: "paperless-ngx/builder/cache/pikepdf"
|
|
||||||
|
|
||||||
- primary-name: "paperless-ngx/builder/jbig2enc"
|
|
||||||
cache-name: "paperless-ngx/builder/cache/jbig2enc"
|
|
||||||
|
|
||||||
- primary-name: "paperless-ngx/builder/psycopg2"
|
|
||||||
cache-name: "paperless-ngx/builder/cache/psycopg2"
|
|
||||||
env:
|
env:
|
||||||
# Requires a personal access token with the OAuth scope delete:packages
|
# Requires a personal access token with the OAuth scope delete:packages
|
||||||
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Clean temporary images
|
||||||
uses: actions/checkout@v3
|
|
||||||
-
|
|
||||||
name: Login to Github Container Registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.10"
|
|
||||||
-
|
|
||||||
name: Install Python libraries
|
|
||||||
run: |
|
|
||||||
python -m pip install httpx docker
|
|
||||||
#
|
|
||||||
# Clean up primary package
|
|
||||||
#
|
|
||||||
-
|
|
||||||
name: Cleanup for package "${{ matrix.primary-name }}"
|
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
run: |
|
uses: stumpylog/image-cleaner-action/ephemeral@v0.2.0
|
||||||
python ${GITHUB_WORKSPACE}/.github/scripts/cleanup-tags.py --untagged --is-manifest --delete "${{ matrix.primary-name }}"
|
with:
|
||||||
#
|
token: "${{ env.TOKEN }}"
|
||||||
# Clean up registry cache package
|
owner: "${{ github.repository_owner }}"
|
||||||
#
|
is_org: "true"
|
||||||
|
package_name: "paperless-ngx"
|
||||||
|
scheme: "branch"
|
||||||
|
repo_name: "paperless-ngx"
|
||||||
|
match_regex: "feature-"
|
||||||
|
do_delete: "true"
|
||||||
|
|
||||||
|
cleanup-untagged-images:
|
||||||
|
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
||||||
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
needs:
|
||||||
|
- cleanup-images
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- primary-name: "paperless-ngx"
|
||||||
|
- primary-name: "paperless-ngx/builder/cache/app"
|
||||||
|
# TODO: Remove the above and replace with the below
|
||||||
|
# - primary-name: "builder/qpdf"
|
||||||
|
# - primary-name: "builder/cache/qpdf"
|
||||||
|
# - primary-name: "builder/pikepdf"
|
||||||
|
# - primary-name: "builder/cache/pikepdf"
|
||||||
|
# - primary-name: "builder/jbig2enc"
|
||||||
|
# - primary-name: "builder/cache/jbig2enc"
|
||||||
|
# - primary-name: "builder/psycopg2"
|
||||||
|
# - primary-name: "builder/cache/psycopg2"
|
||||||
|
env:
|
||||||
|
# Requires a personal access token with the OAuth scope delete:packages
|
||||||
|
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
||||||
|
steps:
|
||||||
-
|
-
|
||||||
name: Cleanup for package "${{ matrix.cache-name }}"
|
name: Clean untagged images
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
run: |
|
uses: stumpylog/image-cleaner-action/untagged@v0.2.0
|
||||||
python ${GITHUB_WORKSPACE}/.github/scripts/cleanup-tags.py --untagged --delete "${{ matrix.cache-name }}"
|
with:
|
||||||
|
token: "${{ env.TOKEN }}"
|
||||||
|
owner: "${{ github.repository_owner }}"
|
||||||
|
is_org: "true"
|
||||||
|
package_name: "${{ matrix.primary-name }}"
|
||||||
|
do_delete: "true"
|
||||||
|
310
.github/workflows/installer-library.yml
vendored
@@ -1,310 +0,0 @@
|
|||||||
# This workflow will run to update the installer library of
|
|
||||||
# Docker images. These are the images which provide updated wheels
|
|
||||||
# .deb installation packages or maybe just some compiled library
|
|
||||||
|
|
||||||
name: Build Image Library
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
# Must match one of these branches AND one of the paths
|
|
||||||
# to be triggered
|
|
||||||
branches:
|
|
||||||
- "main"
|
|
||||||
- "dev"
|
|
||||||
- "library-*"
|
|
||||||
- "feature-*"
|
|
||||||
paths:
|
|
||||||
# Trigger the workflow if a Dockerfile changed
|
|
||||||
- "docker-builders/**"
|
|
||||||
# Trigger if a package was updated
|
|
||||||
- ".build-config.json"
|
|
||||||
- "Pipfile.lock"
|
|
||||||
# Also trigger on workflow changes related to the library
|
|
||||||
- ".github/workflows/installer-library.yml"
|
|
||||||
- ".github/workflows/reusable-workflow-builder.yml"
|
|
||||||
- ".github/scripts/**"
|
|
||||||
|
|
||||||
# Set a workflow level concurrency group so primary workflow
|
|
||||||
# can wait for this to complete if needed
|
|
||||||
# DO NOT CHANGE without updating main workflow group
|
|
||||||
concurrency:
|
|
||||||
group: build-installer-library
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
prepare-docker-build:
|
|
||||||
name: Prepare Docker Image Version Data
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Set ghcr repository name
|
|
||||||
id: set-ghcr-repository
|
|
||||||
run: |
|
|
||||||
ghcr_name=$(echo "${GITHUB_REPOSITORY}" | awk '{ print tolower($0) }')
|
|
||||||
echo "repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
-
|
|
||||||
name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.9"
|
|
||||||
-
|
|
||||||
name: Install jq
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install jq
|
|
||||||
-
|
|
||||||
name: Setup qpdf image
|
|
||||||
id: qpdf-setup
|
|
||||||
run: |
|
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py qpdf)
|
|
||||||
|
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "qpdf-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Setup psycopg2 image
|
|
||||||
id: psycopg2-setup
|
|
||||||
run: |
|
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py psycopg2)
|
|
||||||
|
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "psycopg2-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Setup pikepdf image
|
|
||||||
id: pikepdf-setup
|
|
||||||
run: |
|
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py pikepdf)
|
|
||||||
|
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "pikepdf-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Setup jbig2enc image
|
|
||||||
id: jbig2enc-setup
|
|
||||||
run: |
|
|
||||||
build_json=$(python ${GITHUB_WORKSPACE}/.github/scripts/get-build-json.py jbig2enc)
|
|
||||||
|
|
||||||
echo ${build_json}
|
|
||||||
|
|
||||||
echo "jbig2enc-json=${build_json}" >> $GITHUB_OUTPUT
|
|
||||||
-
|
|
||||||
name: Setup other versions
|
|
||||||
id: cache-bust-setup
|
|
||||||
run: |
|
|
||||||
pillow_version=$(jq -r '.default.pillow.version | gsub("=";"")' Pipfile.lock)
|
|
||||||
lxml_version=$(jq -r '.default.lxml.version | gsub("=";"")' Pipfile.lock)
|
|
||||||
|
|
||||||
echo "Pillow is ${pillow_version}"
|
|
||||||
echo "lxml is ${lxml_version}"
|
|
||||||
|
|
||||||
echo "pillow-version=${pillow_version}" >> $GITHUB_OUTPUT
|
|
||||||
echo "lxml-version=${lxml_version}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
|
|
||||||
ghcr-repository: ${{ steps.set-ghcr-repository.outputs.repository }}
|
|
||||||
|
|
||||||
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 }}
|
|
||||||
|
|
||||||
pillow-version: ${{ steps.cache-bust-setup.outputs.pillow-version }}
|
|
||||||
|
|
||||||
lxml-version: ${{ steps.cache-bust-setup.outputs.lxml-version }}
|
|
||||||
|
|
||||||
build-qpdf-debs:
|
|
||||||
name: qpdf
|
|
||||||
needs:
|
|
||||||
- prepare-docker-build
|
|
||||||
uses: ./.github/workflows/reusable-workflow-builder.yml
|
|
||||||
with:
|
|
||||||
dockerfile: ./docker-builders/Dockerfile.qpdf
|
|
||||||
build-platforms: linux/amd64
|
|
||||||
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_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=${{ needs.prepare-docker-build.outputs.ghcr-repository }}
|
|
||||||
QPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }}
|
|
||||||
PIKEPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).version }}
|
|
||||||
PILLOW_VERSION=${{ needs.prepare-docker-build.outputs.pillow-version }}
|
|
||||||
LXML_VERSION=${{ needs.prepare-docker-build.outputs.lxml-version }}
|
|
||||||
|
|
||||||
commit-binary-files:
|
|
||||||
name: Store installers
|
|
||||||
needs:
|
|
||||||
- prepare-docker-build
|
|
||||||
- build-qpdf-debs
|
|
||||||
- build-jbig2enc
|
|
||||||
- build-psycopg2-wheel
|
|
||||||
- build-pikepdf-wheel
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
ref: binary-library
|
|
||||||
-
|
|
||||||
name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.9"
|
|
||||||
-
|
|
||||||
name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -qq --no-install-recommends tree
|
|
||||||
-
|
|
||||||
name: Extract qpdf files
|
|
||||||
run: |
|
|
||||||
version=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }}
|
|
||||||
tag=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).image_tag }}
|
|
||||||
|
|
||||||
docker pull --quiet ${tag}
|
|
||||||
docker create --name qpdf-extract ${tag}
|
|
||||||
|
|
||||||
mkdir --parents qpdf/${version}/amd64
|
|
||||||
docker cp qpdf-extract:/usr/src/qpdf/${version}/amd64 qpdf/${version}
|
|
||||||
|
|
||||||
mkdir --parents qpdf/${version}/arm64
|
|
||||||
docker cp qpdf-extract:/usr/src/qpdf/${version}/arm64 qpdf/${version}
|
|
||||||
|
|
||||||
mkdir --parents qpdf/${version}/armv7
|
|
||||||
docker cp qpdf-extract:/usr/src/qpdf/${version}/armv7 qpdf/${version}
|
|
||||||
-
|
|
||||||
name: Extract psycopg2 files
|
|
||||||
run: |
|
|
||||||
version=${{ fromJSON(needs.prepare-docker-build.outputs.psycopg2-json).version }}
|
|
||||||
tag=${{ fromJSON(needs.prepare-docker-build.outputs.psycopg2-json).image_tag }}
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/amd64 ${tag}
|
|
||||||
docker create --platform linux/amd64 --name psycopg2-extract ${tag}
|
|
||||||
mkdir --parents psycopg2/${version}/amd64
|
|
||||||
docker cp psycopg2-extract:/usr/src/wheels/ psycopg2/${version}/amd64
|
|
||||||
mv psycopg2/${version}/amd64/wheels/* psycopg2/${version}/amd64
|
|
||||||
rm -r psycopg2/${version}/amd64/wheels/
|
|
||||||
docker rm psycopg2-extract
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/arm64 ${tag}
|
|
||||||
docker create --platform linux/arm64 --name psycopg2-extract ${tag}
|
|
||||||
mkdir --parents psycopg2/${version}/arm64
|
|
||||||
docker cp psycopg2-extract:/usr/src/wheels/ psycopg2/${version}/arm64
|
|
||||||
mv psycopg2/${version}/arm64/wheels/* psycopg2/${version}/arm64
|
|
||||||
rm -r psycopg2/${version}/arm64/wheels/
|
|
||||||
docker rm psycopg2-extract
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/arm/v7 ${tag}
|
|
||||||
docker create --platform linux/arm/v7 --name psycopg2-extract ${tag}
|
|
||||||
mkdir --parents psycopg2/${version}/armv7
|
|
||||||
docker cp psycopg2-extract:/usr/src/wheels/ psycopg2/${version}/armv7
|
|
||||||
mv psycopg2/${version}/armv7/wheels/* psycopg2/${version}/armv7
|
|
||||||
rm -r psycopg2/${version}/armv7/wheels/
|
|
||||||
docker rm psycopg2-extract
|
|
||||||
-
|
|
||||||
name: Extract pikepdf files
|
|
||||||
run: |
|
|
||||||
version=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).version }}
|
|
||||||
tag=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).image_tag }}
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/amd64 ${tag}
|
|
||||||
docker create --platform linux/amd64 --name pikepdf-extract ${tag}
|
|
||||||
mkdir --parents pikepdf/${version}/amd64
|
|
||||||
docker cp pikepdf-extract:/usr/src/wheels/ pikepdf/${version}/amd64
|
|
||||||
mv pikepdf/${version}/amd64/wheels/* pikepdf/${version}/amd64
|
|
||||||
rm -r pikepdf/${version}/amd64/wheels/
|
|
||||||
docker rm pikepdf-extract
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/arm64 ${tag}
|
|
||||||
docker create --platform linux/arm64 --name pikepdf-extract ${tag}
|
|
||||||
mkdir --parents pikepdf/${version}/arm64
|
|
||||||
docker cp pikepdf-extract:/usr/src/wheels/ pikepdf/${version}/arm64
|
|
||||||
mv pikepdf/${version}/arm64/wheels/* pikepdf/${version}/arm64
|
|
||||||
rm -r pikepdf/${version}/arm64/wheels/
|
|
||||||
docker rm pikepdf-extract
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/arm/v7 ${tag}
|
|
||||||
docker create --platform linux/arm/v7 --name pikepdf-extract ${tag}
|
|
||||||
mkdir --parents pikepdf/${version}/armv7
|
|
||||||
docker cp pikepdf-extract:/usr/src/wheels/ pikepdf/${version}/armv7
|
|
||||||
mv pikepdf/${version}/armv7/wheels/* pikepdf/${version}/armv7
|
|
||||||
rm -r pikepdf/${version}/armv7/wheels/
|
|
||||||
docker rm pikepdf-extract
|
|
||||||
-
|
|
||||||
name: Extract jbig2enc files
|
|
||||||
run: |
|
|
||||||
version=${{ fromJSON(needs.prepare-docker-build.outputs.jbig2enc-json).version }}
|
|
||||||
tag=${{ fromJSON(needs.prepare-docker-build.outputs.jbig2enc-json).image_tag }}
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/amd64 ${tag}
|
|
||||||
docker create --platform linux/amd64 --name jbig2enc-extract ${tag}
|
|
||||||
mkdir --parents jbig2enc/${version}/amd64
|
|
||||||
docker cp jbig2enc-extract:/usr/src/jbig2enc/build jbig2enc/${version}/amd64/
|
|
||||||
mv jbig2enc/${version}/amd64/build/* jbig2enc/${version}/amd64/
|
|
||||||
docker rm jbig2enc-extract
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/arm64 ${tag}
|
|
||||||
docker create --platform linux/arm64 --name jbig2enc-extract ${tag}
|
|
||||||
mkdir --parents jbig2enc/${version}/arm64
|
|
||||||
docker cp jbig2enc-extract:/usr/src/jbig2enc/build jbig2enc/${version}/arm64
|
|
||||||
mv jbig2enc/${version}/arm64/build/* jbig2enc/${version}/arm64/
|
|
||||||
docker rm jbig2enc-extract
|
|
||||||
|
|
||||||
docker pull --quiet --platform linux/arm/v7 ${tag}
|
|
||||||
docker create --platform linux/arm/v7 --name jbig2enc-extract ${tag}
|
|
||||||
mkdir --parents jbig2enc/${version}/armv7
|
|
||||||
docker cp jbig2enc-extract:/usr/src/jbig2enc/build jbig2enc/${version}/armv7
|
|
||||||
mv jbig2enc/${version}/armv7/build/* jbig2enc/${version}/armv7/
|
|
||||||
docker rm jbig2enc-extract
|
|
||||||
-
|
|
||||||
name: Show file structure
|
|
||||||
run: |
|
|
||||||
tree .
|
|
||||||
-
|
|
||||||
name: Commit files
|
|
||||||
run: |
|
|
||||||
git config --global user.name "github-actions"
|
|
||||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add pikepdf/ qpdf/ psycopg2/ jbig2enc/
|
|
||||||
git commit -m "Updating installer packages" || true
|
|
||||||
git push origin || true
|
|
4
.github/workflows/project-actions.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
if: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened')
|
if: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened')
|
||||||
steps:
|
steps:
|
||||||
- name: Add issue to project and set status to ${{ env.todo }}
|
- name: Add issue to project and set status to ${{ env.todo }}
|
||||||
uses: leonsteinhaeuser/project-beta-automations@v2.0.1
|
uses: leonsteinhaeuser/project-beta-automations@v2.1.0
|
||||||
with:
|
with:
|
||||||
gh_token: ${{ secrets.GH_TOKEN }}
|
gh_token: ${{ secrets.GH_TOKEN }}
|
||||||
organization: paperless-ngx
|
organization: paperless-ngx
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request.user.login != 'dependabot'
|
if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request.user.login != 'dependabot'
|
||||||
steps:
|
steps:
|
||||||
- name: Add PR to project and set status to "Needs Review"
|
- name: Add PR to project and set status to "Needs Review"
|
||||||
uses: leonsteinhaeuser/project-beta-automations@v2.0.1
|
uses: leonsteinhaeuser/project-beta-automations@v2.1.0
|
||||||
with:
|
with:
|
||||||
gh_token: ${{ secrets.GH_TOKEN }}
|
gh_token: ${{ secrets.GH_TOKEN }}
|
||||||
organization: paperless-ngx
|
organization: paperless-ngx
|
||||||
|
47
.github/workflows/repo-maintenance.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
name: 'Repository Maintenance'
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 3 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lock
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
name: 'Stale'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v8
|
||||||
|
with:
|
||||||
|
days-before-stale: 7
|
||||||
|
days-before-close: 14
|
||||||
|
any-of-labels: 'cant-reproduce,not a bug'
|
||||||
|
stale-issue-label: stale
|
||||||
|
stale-pr-label: stale
|
||||||
|
stale-issue-message: >
|
||||||
|
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.
|
||||||
|
lock-threads:
|
||||||
|
name: 'Lock Old Threads'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v4
|
||||||
|
with:
|
||||||
|
issue-inactive-days: '30'
|
||||||
|
pr-inactive-days: '30'
|
||||||
|
log-output: true
|
||||||
|
issue-comment: >
|
||||||
|
This issue has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
pr-comment: >
|
||||||
|
This pull request has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
57
.github/workflows/reusable-workflow-builder.yml
vendored
@@ -1,57 +0,0 @@
|
|||||||
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
|
|
||||||
build-platforms:
|
|
||||||
required: false
|
|
||||||
default: linux/amd64,linux/arm64,linux/arm/v7
|
|
||||||
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-22.04
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
-
|
|
||||||
name: Login to Github Container Registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
-
|
|
||||||
name: Build ${{ fromJSON(inputs.build-json).name }}
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ${{ inputs.dockerfile }}
|
|
||||||
tags: ${{ fromJSON(inputs.build-json).image_tag }}
|
|
||||||
platforms: ${{ inputs.build-platforms }}
|
|
||||||
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 }}
|
|
1
.gitignore
vendored
@@ -73,6 +73,7 @@ virtualenv
|
|||||||
.venv/
|
.venv/
|
||||||
/docker-compose.env
|
/docker-compose.env
|
||||||
/docker-compose.yml
|
/docker-compose.yml
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
# Used for development
|
# Used for development
|
||||||
scripts/import-for-development
|
scripts/import-for-development
|
||||||
|
@@ -27,7 +27,7 @@ repos:
|
|||||||
- id: check-case-conflict
|
- id: check-case-conflict
|
||||||
- id: detect-private-key
|
- id: detect-private-key
|
||||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
rev: "v2.7.1"
|
rev: 'v3.0.0'
|
||||||
hooks:
|
hooks:
|
||||||
- id: prettier
|
- id: prettier
|
||||||
types_or:
|
types_or:
|
||||||
@@ -36,42 +36,17 @@ repos:
|
|||||||
- markdown
|
- markdown
|
||||||
exclude: "(^Pipfile\\.lock$)"
|
exclude: "(^Pipfile\\.lock$)"
|
||||||
# Python hooks
|
# Python hooks
|
||||||
- repo: https://github.com/asottile/reorder_python_imports
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
rev: v3.9.0
|
rev: 'v0.0.280'
|
||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: ruff
|
||||||
exclude: "(migrations)"
|
|
||||||
- repo: https://github.com/asottile/yesqa
|
|
||||||
rev: "v1.4.0"
|
|
||||||
hooks:
|
|
||||||
- id: yesqa
|
|
||||||
exclude: "(migrations)"
|
|
||||||
- repo: https://github.com/asottile/add-trailing-comma
|
|
||||||
rev: "v2.4.0"
|
|
||||||
hooks:
|
|
||||||
- id: add-trailing-comma
|
|
||||||
exclude: "(migrations)"
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
|
||||||
rev: 6.0.0
|
|
||||||
hooks:
|
|
||||||
- id: flake8
|
|
||||||
files: ^src/
|
|
||||||
args:
|
|
||||||
- "--config=./src/setup.cfg"
|
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/psf/black
|
||||||
rev: 22.12.0
|
rev: 23.7.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
|
||||||
rev: v3.3.1
|
|
||||||
hooks:
|
|
||||||
- id: pyupgrade
|
|
||||||
exclude: "(migrations)"
|
|
||||||
args:
|
|
||||||
- "--py38-plus"
|
|
||||||
# Dockerfile hooks
|
# Dockerfile hooks
|
||||||
- repo: https://github.com/AleksaC/hadolint-py
|
- repo: https://github.com/AleksaC/hadolint-py
|
||||||
rev: v2.10.0
|
rev: v2.12.0.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: hadolint
|
- id: hadolint
|
||||||
# Shell script hooks
|
# Shell script hooks
|
||||||
@@ -82,6 +57,6 @@ repos:
|
|||||||
args:
|
args:
|
||||||
- "--tab"
|
- "--tab"
|
||||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||||
rev: "v0.9.0.2"
|
rev: "v0.9.0.5"
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
|
@@ -2,3 +2,5 @@
|
|||||||
semi: false
|
semi: false
|
||||||
# https://prettier.io/docs/en/options.html#quotes
|
# https://prettier.io/docs/en/options.html#quotes
|
||||||
singleQuote: true
|
singleQuote: true
|
||||||
|
# https://prettier.io/docs/en/options.html#trailing-commas
|
||||||
|
trailingComma: "es5"
|
||||||
|
23
.ruff.toml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# https://beta.ruff.rs/docs/settings/
|
||||||
|
# https://beta.ruff.rs/docs/rules/
|
||||||
|
extend-select = ["I", "W", "UP", "COM", "DJ", "EXE", "ISC", "ICN", "G201", "INP", "PIE", "RSE", "SIM", "TID", "PLC", "PLE", "RUF"]
|
||||||
|
# TODO PTH
|
||||||
|
ignore = ["DJ001", "SIM105", "RUF012"]
|
||||||
|
fix = true
|
||||||
|
line-length = 88
|
||||||
|
respect-gitignore = true
|
||||||
|
src = ["src"]
|
||||||
|
target-version = "py38"
|
||||||
|
format = "grouped"
|
||||||
|
show-fixes = true
|
||||||
|
|
||||||
|
[per-file-ignores]
|
||||||
|
".github/scripts/*.py" = ["E501", "INP001", "SIM117"]
|
||||||
|
"docker/wait-for-redis.py" = ["INP001"]
|
||||||
|
"*/tests/*.py" = ["E501", "SIM117"]
|
||||||
|
"*/migrations/*.py" = ["E501", "SIM"]
|
||||||
|
"src/paperless_tesseract/tests/test_parser.py" = ["RUF001"]
|
||||||
|
"src/documents/models.py" = ["SIM115"]
|
||||||
|
|
||||||
|
[isort]
|
||||||
|
force-single-line = true
|
@@ -45,7 +45,7 @@ Examples of `non-trivial` PRs might include:
|
|||||||
|
|
||||||
- Additional features
|
- Additional features
|
||||||
- Large changes to many distinct files
|
- Large changes to many distinct files
|
||||||
- Breaking or depreciation of existing features
|
- Breaking or deprecation of existing features
|
||||||
|
|
||||||
Our community review process for `non-trivial` PRs is the following:
|
Our community review process for `non-trivial` PRs is the following:
|
||||||
|
|
||||||
|
49
Dockerfile
@@ -1,11 +1,11 @@
|
|||||||
# syntax=docker/dockerfile:1.4
|
# syntax=docker/dockerfile:1
|
||||||
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md
|
# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md
|
||||||
|
|
||||||
# Stage: compile-frontend
|
# Stage: compile-frontend
|
||||||
# Purpose: Compiles the frontend
|
# Purpose: Compiles the frontend
|
||||||
# Notes:
|
# Notes:
|
||||||
# - Does NPM stuff with Typescript and such
|
# - Does NPM stuff with Typescript and such
|
||||||
FROM --platform=$BUILDPLATFORM node:16-bullseye-slim AS compile-frontend
|
FROM --platform=$BUILDPLATFORM docker.io/node:16-bookworm-slim AS compile-frontend
|
||||||
|
|
||||||
COPY ./src-ui /src/src-ui
|
COPY ./src-ui /src/src-ui
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ RUN set -eux \
|
|||||||
# Comments:
|
# Comments:
|
||||||
# - pipenv dependencies are not left in the final image
|
# - pipenv dependencies are not left in the final image
|
||||||
# - pipenv can't touch the final image somehow
|
# - pipenv can't touch the final image somehow
|
||||||
FROM --platform=$BUILDPLATFORM python:3.9-slim-bullseye as pipenv-base
|
FROM --platform=$BUILDPLATFORM docker.io/python:3.9-alpine as pipenv-base
|
||||||
|
|
||||||
WORKDIR /usr/src/pipenv
|
WORKDIR /usr/src/pipenv
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ COPY Pipfile* ./
|
|||||||
|
|
||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
&& echo "Installing pipenv" \
|
&& echo "Installing pipenv" \
|
||||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2022.11.30 \
|
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.7.23 \
|
||||||
&& echo "Generating requirement.txt" \
|
&& echo "Generating requirement.txt" \
|
||||||
&& pipenv requirements > requirements.txt
|
&& pipenv requirements > requirements.txt
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ RUN set -eux \
|
|||||||
# Purpose: The final image
|
# Purpose: The final image
|
||||||
# Comments:
|
# Comments:
|
||||||
# - Don't leave anything extra in here
|
# - Don't leave anything extra in here
|
||||||
FROM python:3.9-slim-bullseye as main-app
|
FROM docker.io/python:3.9-slim-bookworm as main-app
|
||||||
|
|
||||||
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
||||||
LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/"
|
LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/"
|
||||||
@@ -46,15 +46,6 @@ LABEL org.opencontainers.image.url="https://github.com/paperless-ngx/paperless-n
|
|||||||
LABEL org.opencontainers.image.licenses="GPL-3.0-only"
|
LABEL org.opencontainers.image.licenses="GPL-3.0-only"
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
# Buildx provided, must be defined to use though
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
|
|
||||||
# Workflow provided
|
|
||||||
ARG JBIG2ENC_VERSION
|
|
||||||
ARG QPDF_VERSION
|
|
||||||
ARG PIKEPDF_VERSION
|
|
||||||
ARG PSYCOPG2_VERSION
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Begin installation and configuration
|
# Begin installation and configuration
|
||||||
@@ -79,9 +70,9 @@ ARG RUNTIME_PACKAGES="\
|
|||||||
# Image processing
|
# Image processing
|
||||||
liblept5 \
|
liblept5 \
|
||||||
liblcms2-2 \
|
liblcms2-2 \
|
||||||
libtiff5 \
|
libtiff6 \
|
||||||
libfreetype6 \
|
libfreetype6 \
|
||||||
libwebp6 \
|
libwebp7 \
|
||||||
libopenjp2-7 \
|
libopenjp2-7 \
|
||||||
libimagequant0 \
|
libimagequant0 \
|
||||||
libraqm0 \
|
libraqm0 \
|
||||||
@@ -107,6 +98,8 @@ ARG RUNTIME_PACKAGES="\
|
|||||||
libxml2 \
|
libxml2 \
|
||||||
libxslt1.1 \
|
libxslt1.1 \
|
||||||
libgnutls30 \
|
libgnutls30 \
|
||||||
|
libqpdf29 \
|
||||||
|
qpdf \
|
||||||
# Mime type detection
|
# Mime type detection
|
||||||
file \
|
file \
|
||||||
libmagic1 \
|
libmagic1 \
|
||||||
@@ -175,12 +168,22 @@ RUN set -eux \
|
|||||||
&& chmod +x install_management_commands.sh \
|
&& chmod +x install_management_commands.sh \
|
||||||
&& ./install_management_commands.sh
|
&& ./install_management_commands.sh
|
||||||
|
|
||||||
|
# Buildx provided, must be defined to use though
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETVARIANT
|
||||||
|
|
||||||
|
# Can be workflow provided, defaults set for manual building
|
||||||
|
ARG JBIG2ENC_VERSION=0.29
|
||||||
|
ARG QPDF_VERSION=11.3.0
|
||||||
|
ARG PIKEPDF_VERSION=7.2.0
|
||||||
|
ARG PSYCOPG2_VERSION=2.9.6
|
||||||
|
|
||||||
# Install the built packages from the installer library images
|
# Install the built packages from the installer library images
|
||||||
# These change sometimes
|
# These change sometimes
|
||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
&& echo "Getting binaries" \
|
&& echo "Getting binaries" \
|
||||||
&& mkdir paperless-ngx \
|
&& mkdir paperless-ngx \
|
||||||
&& curl --fail --silent --show-error --output paperless-ngx.tar.gz --location https://github.com/paperless-ngx/paperless-ngx/archive/41d6e7e407af09a0882736d50c89b6e015997bff.tar.gz \
|
&& curl --fail --silent --show-error --output paperless-ngx.tar.gz --location https://github.com/paperless-ngx/builder/archive/58bb061b9b3b63009852d6d875f9a305d9ae6ac9.tar.gz \
|
||||||
&& tar -xf paperless-ngx.tar.gz --directory paperless-ngx --strip-components=1 \
|
&& tar -xf paperless-ngx.tar.gz --directory paperless-ngx --strip-components=1 \
|
||||||
&& cd paperless-ngx \
|
&& cd paperless-ngx \
|
||||||
# Setting a specific revision ensures we know what this installed
|
# Setting a specific revision ensures we know what this installed
|
||||||
@@ -188,9 +191,7 @@ RUN set -eux \
|
|||||||
&& echo "Installing jbig2enc" \
|
&& echo "Installing jbig2enc" \
|
||||||
&& cp ./jbig2enc/${JBIG2ENC_VERSION}/${TARGETARCH}${TARGETVARIANT}/jbig2 /usr/local/bin/ \
|
&& cp ./jbig2enc/${JBIG2ENC_VERSION}/${TARGETARCH}${TARGETVARIANT}/jbig2 /usr/local/bin/ \
|
||||||
&& cp ./jbig2enc/${JBIG2ENC_VERSION}/${TARGETARCH}${TARGETVARIANT}/libjbig2enc* /usr/local/lib/ \
|
&& cp ./jbig2enc/${JBIG2ENC_VERSION}/${TARGETARCH}${TARGETVARIANT}/libjbig2enc* /usr/local/lib/ \
|
||||||
&& echo "Installing qpdf" \
|
&& chmod a+x /usr/local/bin/jbig2 \
|
||||||
&& apt-get install --yes --no-install-recommends ./qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/libqpdf29_*.deb \
|
|
||||||
&& apt-get install --yes --no-install-recommends ./qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/qpdf_*.deb \
|
|
||||||
&& echo "Installing pikepdf and dependencies" \
|
&& echo "Installing pikepdf and dependencies" \
|
||||||
&& python3 -m pip install --no-cache-dir ./pikepdf/${PIKEPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/*.whl \
|
&& python3 -m pip install --no-cache-dir ./pikepdf/${PIKEPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/*.whl \
|
||||||
&& python3 -m pip list \
|
&& python3 -m pip list \
|
||||||
@@ -214,15 +215,17 @@ ARG BUILD_PACKAGES="\
|
|||||||
build-essential \
|
build-essential \
|
||||||
git \
|
git \
|
||||||
default-libmysqlclient-dev \
|
default-libmysqlclient-dev \
|
||||||
python3-dev"
|
pkg-config"
|
||||||
|
|
||||||
RUN set -eux \
|
# hadolint ignore=DL3042
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
||||||
|
set -eux \
|
||||||
&& echo "Installing build system packages" \
|
&& echo "Installing build system packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
||||||
&& python3 -m pip install --no-cache-dir --upgrade wheel \
|
&& python3 -m pip install --no-cache-dir --upgrade wheel \
|
||||||
&& echo "Installing Python requirements" \
|
&& echo "Installing Python requirements" \
|
||||||
&& python3 -m pip install --default-timeout=1000 --no-cache-dir --requirement requirements.txt \
|
&& python3 -m pip install --default-timeout=1000 --requirement requirements.txt \
|
||||||
&& echo "Installing NLTK data" \
|
&& echo "Installing NLTK data" \
|
||||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
||||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
||||||
|
74
Pipfile
@@ -10,72 +10,100 @@ name = "piwheels"
|
|||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
dateparser = "~=1.1"
|
dateparser = "~=1.1"
|
||||||
django = "~=4.1"
|
# WARNING: django does not use semver.
|
||||||
|
# Only patch versions are guaranteed to not introduce breaking changes.
|
||||||
|
django = "~=4.1.9"
|
||||||
django-cors-headers = "*"
|
django-cors-headers = "*"
|
||||||
|
django-celery-results = "*"
|
||||||
django-compression-middleware = "*"
|
django-compression-middleware = "*"
|
||||||
|
django-guardian = "*"
|
||||||
django-extensions = "*"
|
django-extensions = "*"
|
||||||
django-filter = "~=22.1"
|
django-filter = "~=23.1"
|
||||||
djangorestframework = "~=3.14"
|
djangorestframework = "~=3.14"
|
||||||
|
djangorestframework-guardian = "*"
|
||||||
filelock = "*"
|
filelock = "*"
|
||||||
gunicorn = "*"
|
gunicorn = "*"
|
||||||
imap-tools = "*"
|
imap-tools = "*"
|
||||||
langdetect = "*"
|
langdetect = "*"
|
||||||
pathvalidate = "*"
|
pathvalidate = "*"
|
||||||
pillow = "~=9.3"
|
|
||||||
pikepdf = "*"
|
|
||||||
python-gnupg = "*"
|
python-gnupg = "*"
|
||||||
python-dotenv = "*"
|
python-dotenv = "*"
|
||||||
python-dateutil = "*"
|
python-dateutil = "*"
|
||||||
python-magic = "*"
|
python-magic = "*"
|
||||||
|
python-ipware = "*"
|
||||||
psycopg2 = "*"
|
psycopg2 = "*"
|
||||||
rapidfuzz = "*"
|
rapidfuzz = "*"
|
||||||
redis = {extras = ["hiredis"], version = "*"}
|
redis = {extras = ["hiredis"], version = "*"}
|
||||||
scikit-learn = "~=1.1"
|
scikit-learn = "~=1.3"
|
||||||
numpy = "*"
|
whitenoise = "~=6.5"
|
||||||
whitenoise = "~=6.2"
|
watchdog = "~=3.0"
|
||||||
watchdog = "~=2.1"
|
|
||||||
whoosh="~=2.7"
|
whoosh="~=2.7"
|
||||||
inotifyrecursive = "~=0.3"
|
inotifyrecursive = "~=0.3"
|
||||||
ocrmypdf = "~=14.0"
|
ocrmypdf = "~=14.0"
|
||||||
tqdm = "*"
|
tqdm = "*"
|
||||||
tika = "*"
|
tika-client = "*"
|
||||||
# TODO: This will sadly also install daphne+dependencies,
|
channels = "~=4.0"
|
||||||
# which an ASGI server we don't need. Adds about 15MB image size.
|
channels-redis = "*"
|
||||||
channels = "~=3.0"
|
|
||||||
uvicorn = {extras = ["standard"], version = "*"}
|
uvicorn = {extras = ["standard"], version = "*"}
|
||||||
concurrent-log-handler = "*"
|
concurrent-log-handler = "*"
|
||||||
"pdfminer.six" = "*"
|
|
||||||
pyzbar = "*"
|
pyzbar = "*"
|
||||||
mysqlclient = "*"
|
mysqlclient = "*"
|
||||||
celery = {extras = ["redis"], version = "*"}
|
celery = {extras = ["redis"], version = "*"}
|
||||||
django-celery-results = "*"
|
|
||||||
setproctitle = "*"
|
setproctitle = "*"
|
||||||
nltk = "*"
|
nltk = "*"
|
||||||
pdf2image = "*"
|
pdf2image = "*"
|
||||||
flower = "*"
|
flower = "*"
|
||||||
bleach = "*"
|
bleach = "*"
|
||||||
|
zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"}
|
||||||
#
|
#
|
||||||
# Packages locked due to issues (try to check if these are fixed in a release every so often)
|
# Packages locked due to issues (try to check if these are fixed in a release every so often)
|
||||||
#
|
#
|
||||||
# Pin this until piwheels is building 1.9 (see https://www.piwheels.org/project/scipy/)
|
# Pin this until piwheels is building 1.9 (see https://www.piwheels.org/project/scipy/)
|
||||||
scipy = "==1.8.1"
|
scipy = "==1.8.1"
|
||||||
# Newer versions aren't builting yet (see https://www.piwheels.org/project/cryptography/)
|
# v4 brings in extra dependencies for features not used here
|
||||||
cryptography = "==38.0.1"
|
reportlab = "==3.6.12"
|
||||||
# Locked version until https://github.com/django/channels_redis/issues/332
|
# Pin these until piwheels is building a newer version (see https://www.piwheels.org/project/{package}/)
|
||||||
# is resolved
|
cryptography = "==40.0.1"
|
||||||
channels-redis = "==3.4.1"
|
pikepdf = "==7.2.0"
|
||||||
|
pillow = "==9.5.0"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
coveralls = "*"
|
# Linting
|
||||||
|
black = "*"
|
||||||
|
pre-commit = "*"
|
||||||
|
ruff = "*"
|
||||||
|
# Testing
|
||||||
factory-boy = "*"
|
factory-boy = "*"
|
||||||
pytest = "*"
|
pytest = "*"
|
||||||
pytest-cov = "*"
|
pytest-cov = "*"
|
||||||
pytest-django = "*"
|
pytest-django = "*"
|
||||||
|
pytest-httpx = "*"
|
||||||
pytest-env = "*"
|
pytest-env = "*"
|
||||||
pytest-sugar = "*"
|
pytest-sugar = "*"
|
||||||
pytest-xdist = "*"
|
pytest-xdist = "*"
|
||||||
black = "*"
|
pytest-rerunfailures = "*"
|
||||||
pre-commit = "*"
|
"pdfminer.six" = "*"
|
||||||
imagehash = "*"
|
imagehash = "*"
|
||||||
|
daphne = "*"
|
||||||
|
# Documentation
|
||||||
mkdocs-material = "*"
|
mkdocs-material = "*"
|
||||||
|
|
||||||
|
[typing-dev]
|
||||||
|
mypy = "*"
|
||||||
|
types-Pillow = "*"
|
||||||
|
django-filter-stubs = "*"
|
||||||
|
types-python-dateutil = "*"
|
||||||
|
djangorestframework-stubs = {extras= ["compatible-mypy"], version="*"}
|
||||||
|
celery-types = "*"
|
||||||
|
django-stubs = {extras= ["compatible-mypy"], version="*"}
|
||||||
|
types-dateparser = "*"
|
||||||
|
types-bleach = "*"
|
||||||
|
types-humanfriendly = "*"
|
||||||
|
types-redis = "*"
|
||||||
|
types-tqdm = "*"
|
||||||
|
types-Markdown = "*"
|
||||||
|
types-Pygments = "*"
|
||||||
|
types-backports = "*"
|
||||||
|
types-colorama = "*"
|
||||||
|
types-psycopg2 = "*"
|
||||||
|
types-setuptools = "*"
|
||||||
|
4358
Pipfile.lock
generated
10
README.md
@@ -101,13 +101,9 @@ For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/i
|
|||||||
|
|
||||||
# Affiliated Projects
|
# 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:
|
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Affiliated-Projects) for a user-maintained list of affiliated projects and software that is compatible with Paperless-ngx.
|
||||||
|
|
||||||
- [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless-ngx. Also works with the original Paperless and 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.
|
|
||||||
- [Scan to Paperless](https://github.com/sbrunner/scan-to-paperless): Scan and prepare (crop, deskew, OCR, ...) your documents for Paperless.
|
|
||||||
- [Paperless Mobile](https://github.com/astubenbord/paperless-mobile): A modern, feature rich mobile application for Paperless.
|
|
||||||
|
|
||||||
# Important Note
|
# 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. 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 like your social insurance number, tax records, invoices, etc. **Paperless-ngx should never be run on an untrusted host** because information is stored in clear text without encryption. No guarantees are made regarding security (but we do try!) and you use the app at your own risk.
|
||||||
|
> **The safest way to run Paperless-ngx is on a local server in your own home with backups in place**.
|
||||||
|
@@ -1,81 +0,0 @@
|
|||||||
#!/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 -eu
|
|
||||||
|
|
||||||
if ! command -v jq &> /dev/null ; then
|
|
||||||
echo "jq required"
|
|
||||||
exit 1
|
|
||||||
elif [ ! -f "$1" ]; then
|
|
||||||
echo "$1 is not a file, please provide the Dockerfile"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get the branch name (used for caching)
|
|
||||||
branch_name=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
|
|
||||||
# Parse eithe Pipfile.lock or the .build-config.json
|
|
||||||
jbig2enc_version=$(jq -r '.jbig2enc.version' .build-config.json)
|
|
||||||
qpdf_version=$(jq -r '.qpdf.version' .build-config.json)
|
|
||||||
psycopg2_version=$(jq -r '.default.psycopg2.version | gsub("=";"")' Pipfile.lock)
|
|
||||||
pikepdf_version=$(jq -r '.default.pikepdf.version | gsub("=";"")' Pipfile.lock)
|
|
||||||
pillow_version=$(jq -r '.default.pillow.version | gsub("=";"")' Pipfile.lock)
|
|
||||||
lxml_version=$(jq -r '.default.lxml.version | gsub("=";"")' Pipfile.lock)
|
|
||||||
|
|
||||||
base_filename="$(basename -- "${1}")"
|
|
||||||
build_args_str=""
|
|
||||||
cache_from_str=""
|
|
||||||
|
|
||||||
case "${base_filename}" in
|
|
||||||
|
|
||||||
*.jbig2enc)
|
|
||||||
build_args_str="--build-arg JBIG2ENC_VERSION=${jbig2enc_version}"
|
|
||||||
cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/jbig2enc:${jbig2enc_version}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*.psycopg2)
|
|
||||||
build_args_str="--build-arg PSYCOPG2_VERSION=${psycopg2_version}"
|
|
||||||
cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/psycopg2:${psycopg2_version}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*.qpdf)
|
|
||||||
build_args_str="--build-arg QPDF_VERSION=${qpdf_version}"
|
|
||||||
cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/qpdf:${qpdf_version}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*.pikepdf)
|
|
||||||
build_args_str="--build-arg QPDF_VERSION=${qpdf_version} --build-arg PIKEPDF_VERSION=${pikepdf_version} --build-arg PILLOW_VERSION=${pillow_version} --build-arg LXML_VERSION=${lxml_version}"
|
|
||||||
cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/pikepdf:${pikepdf_version}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
Dockerfile)
|
|
||||||
build_args_str="--build-arg QPDF_VERSION=${qpdf_version} --build-arg PIKEPDF_VERSION=${pikepdf_version} --build-arg PSYCOPG2_VERSION=${psycopg2_version} --build-arg JBIG2ENC_VERSION=${jbig2enc_version}"
|
|
||||||
cache_from_str="--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"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Unable to match ${base_filename}"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
read -r -a build_args_arr <<< "${build_args_str}"
|
|
||||||
read -r -a cache_from_arr <<< "${cache_from_str}"
|
|
||||||
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
docker buildx build --file "${1}" \
|
|
||||||
--progress=plain \
|
|
||||||
--output=type=docker \
|
|
||||||
"${cache_from_arr[@]}" \
|
|
||||||
"${build_args_arr[@]}" \
|
|
||||||
"${@:2}" .
|
|
@@ -1,4 +1,8 @@
|
|||||||
commit_message: '[ci skip]'
|
commit_message: '[ci skip]'
|
||||||
|
pull_request_labels: [
|
||||||
|
"skip-changelog",
|
||||||
|
"translation"
|
||||||
|
]
|
||||||
files:
|
files:
|
||||||
- source: /src/locale/en_US/LC_MESSAGES/django.po
|
- source: /src/locale/en_US/LC_MESSAGES/django.po
|
||||||
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
|
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
|
||||||
|
@@ -1,48 +0,0 @@
|
|||||||
# 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 JBIG2ENC_VERSION
|
|
||||||
|
|
||||||
ARG BUILD_PACKAGES="\
|
|
||||||
build-essential \
|
|
||||||
automake \
|
|
||||||
libtool \
|
|
||||||
libleptonica-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
git \
|
|
||||||
ca-certificates"
|
|
||||||
|
|
||||||
WORKDIR /usr/src/jbig2enc
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Installing build tools" \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
|
||||||
&& echo "Building jbig2enc" \
|
|
||||||
&& git clone --quiet --branch $JBIG2ENC_VERSION https://github.com/agl/jbig2enc . \
|
|
||||||
&& ./autogen.sh \
|
|
||||||
&& ./configure \
|
|
||||||
&& make \
|
|
||||||
&& echo "Gathering package data" \
|
|
||||||
&& dpkg-query -f '${Package;-40}${Version}\n' -W > ./pkg-list.txt \
|
|
||||||
&& echo "Cleaning up image" \
|
|
||||||
&& apt-get -y purge ${BUILD_PACKAGES} \
|
|
||||||
&& apt-get -y autoremove --purge \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
|
||||||
&& echo "Moving files around" \
|
|
||||||
&& mkdir build \
|
|
||||||
# Unlink a symlink that causes problems
|
|
||||||
&& unlink ./src/.libs/libjbig2enc.la \
|
|
||||||
# Move what the link pointed to
|
|
||||||
&& mv ./src/libjbig2enc.la ./build/ \
|
|
||||||
# Move the shared library .so files
|
|
||||||
&& mv ./src/.libs/libjbig2enc* ./build/ \
|
|
||||||
# And move the cli binary
|
|
||||||
&& mv ./src/jbig2 ./build/ \
|
|
||||||
&& mv ./pkg-list.txt ./build/
|
|
@@ -1,118 +0,0 @@
|
|||||||
# 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_VERSION - Version of pikepdf to build wheel for
|
|
||||||
|
|
||||||
# Default to pulling from the main repo registry when manually building
|
|
||||||
ARG REPO="paperless-ngx/paperless-ngx"
|
|
||||||
|
|
||||||
# This does nothing, except provide a name for a copy below
|
|
||||||
ARG QPDF_VERSION
|
|
||||||
FROM --platform=$BUILDPLATFORM ghcr.io/${REPO}/builder/qpdf:${QPDF_VERSION} as qpdf-builder
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: builder
|
|
||||||
# Purpose:
|
|
||||||
# - Build the pikepdf wheel
|
|
||||||
# - Build any dependent wheels which can't be found
|
|
||||||
#
|
|
||||||
FROM python:3.9-slim-bullseye as builder
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.description="A intermediate image with pikepdf wheel built"
|
|
||||||
|
|
||||||
# Buildx provided
|
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
|
||||||
# Workflow provided
|
|
||||||
ARG QPDF_VERSION
|
|
||||||
ARG PIKEPDF_VERSION
|
|
||||||
# These are not used, but will still bust the cache if one changes
|
|
||||||
# Otherwise, the main image will try to build thing (and fail)
|
|
||||||
ARG PILLOW_VERSION
|
|
||||||
ARG LXML_VERSION
|
|
||||||
|
|
||||||
ARG BUILD_PACKAGES="\
|
|
||||||
build-essential \
|
|
||||||
python3-dev \
|
|
||||||
python3-pip \
|
|
||||||
# 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/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/*.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 \
|
|
||||||
&& echo "Installing build tools" \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
|
||||||
&& echo "Installing qpdf" \
|
|
||||||
&& dpkg --install libqpdf29_*.deb \
|
|
||||||
&& dpkg --install libqpdf-dev_*.deb \
|
|
||||||
&& echo "Installing Python tools" \
|
|
||||||
&& python3 -m pip install --no-cache-dir --upgrade \
|
|
||||||
pip \
|
|
||||||
wheel \
|
|
||||||
# https://pikepdf.readthedocs.io/en/latest/installation.html#requirements
|
|
||||||
pybind11 \
|
|
||||||
&& echo "Building pikepdf wheel ${PIKEPDF_VERSION}" \
|
|
||||||
&& mkdir wheels \
|
|
||||||
&& python3 -m pip wheel \
|
|
||||||
# Build the package at the required version
|
|
||||||
pikepdf==${PIKEPDF_VERSION} \
|
|
||||||
# Look to piwheels for additional pre-built wheels
|
|
||||||
--extra-index-url https://www.piwheels.org/simple \
|
|
||||||
# Output the *.whl into this directory
|
|
||||||
--wheel-dir wheels \
|
|
||||||
# Do not use a binary packge for the package being built
|
|
||||||
--no-binary=pikepdf \
|
|
||||||
# Do use binary packages for dependencies
|
|
||||||
--prefer-binary \
|
|
||||||
# Don't cache build files
|
|
||||||
--no-cache-dir \
|
|
||||||
&& ls -ahl wheels \
|
|
||||||
&& echo "Gathering package data" \
|
|
||||||
&& dpkg-query -f '${Package;-40}${Version}\n' -W > ./wheels/pkg-list.txt \
|
|
||||||
&& echo "Cleaning up image" \
|
|
||||||
&& apt-get -y purge ${BUILD_PACKAGES} \
|
|
||||||
&& apt-get -y autoremove --purge \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: package
|
|
||||||
# Purpose: Holds the compiled .whl files in a tiny image to pull
|
|
||||||
#
|
|
||||||
FROM alpine:3.17 as package
|
|
||||||
|
|
||||||
WORKDIR /usr/src/wheels/
|
|
||||||
|
|
||||||
COPY --from=builder /usr/src/wheels/*.whl ./
|
|
||||||
COPY --from=builder /usr/src/wheels/pkg-list.txt ./
|
|
@@ -1,66 +0,0 @@
|
|||||||
# This Dockerfile builds the psycopg2 wheel
|
|
||||||
# Inputs:
|
|
||||||
# - PSYCOPG2_VERSION - Version to build
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: builder
|
|
||||||
# Purpose:
|
|
||||||
# - Build the psycopg2 wheel
|
|
||||||
#
|
|
||||||
FROM python:3.9-slim-bullseye as builder
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.description="A intermediate image with psycopg2 wheel built"
|
|
||||||
|
|
||||||
ARG PSYCOPG2_VERSION
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
ARG BUILD_PACKAGES="\
|
|
||||||
build-essential \
|
|
||||||
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 \
|
|
||||||
&& echo "Installing build tools" \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
|
||||||
&& echo "Installing Python tools" \
|
|
||||||
&& python3 -m pip install --no-cache-dir --upgrade pip wheel \
|
|
||||||
&& echo "Building psycopg2 wheel ${PSYCOPG2_VERSION}" \
|
|
||||||
&& cd /usr/src \
|
|
||||||
&& mkdir wheels \
|
|
||||||
&& python3 -m pip wheel \
|
|
||||||
# Build the package at the required version
|
|
||||||
psycopg2==${PSYCOPG2_VERSION} \
|
|
||||||
# Output the *.whl into this directory
|
|
||||||
--wheel-dir wheels \
|
|
||||||
# Do not use a binary packge for the package being built
|
|
||||||
--no-binary=psycopg2 \
|
|
||||||
# Do use binary packages for dependencies
|
|
||||||
--prefer-binary \
|
|
||||||
# Don't cache build files
|
|
||||||
--no-cache-dir \
|
|
||||||
&& ls -ahl wheels/ \
|
|
||||||
&& echo "Gathering package data" \
|
|
||||||
&& dpkg-query -f '${Package;-40}${Version}\n' -W > ./wheels/pkg-list.txt \
|
|
||||||
&& echo "Cleaning up image" \
|
|
||||||
&& apt-get -y purge ${BUILD_PACKAGES} \
|
|
||||||
&& apt-get -y autoremove --purge \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: package
|
|
||||||
# Purpose: Holds the compiled .whl files in a tiny image to pull
|
|
||||||
#
|
|
||||||
FROM alpine:3.17 as package
|
|
||||||
|
|
||||||
WORKDIR /usr/src/wheels/
|
|
||||||
|
|
||||||
COPY --from=builder /usr/src/wheels/*.whl ./
|
|
||||||
COPY --from=builder /usr/src/wheels/pkg-list.txt ./
|
|
@@ -1,156 +0,0 @@
|
|||||||
#
|
|
||||||
# Stage: pre-build
|
|
||||||
# Purpose:
|
|
||||||
# - Installs common packages
|
|
||||||
# - Sets common environment variables related to dpkg
|
|
||||||
# - Aquires the qpdf source from bookwork
|
|
||||||
# Useful Links:
|
|
||||||
# - https://qpdf.readthedocs.io/en/stable/installation.html#system-requirements
|
|
||||||
# - https://wiki.debian.org/Multiarch/HOWTO
|
|
||||||
# - https://wiki.debian.org/CrossCompiling
|
|
||||||
#
|
|
||||||
|
|
||||||
FROM debian:bullseye-slim as pre-build
|
|
||||||
|
|
||||||
ARG QPDF_VERSION
|
|
||||||
|
|
||||||
ARG COMMON_BUILD_PACKAGES="\
|
|
||||||
cmake \
|
|
||||||
debhelper\
|
|
||||||
debian-keyring \
|
|
||||||
devscripts \
|
|
||||||
dpkg-dev \
|
|
||||||
equivs \
|
|
||||||
packaging-dev \
|
|
||||||
libtool"
|
|
||||||
|
|
||||||
ENV DEB_BUILD_OPTIONS="terse nocheck nodoc parallel=2"
|
|
||||||
|
|
||||||
WORKDIR /usr/src
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Installing common packages" \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${COMMON_BUILD_PACKAGES} \
|
|
||||||
&& echo "Getting qpdf source" \
|
|
||||||
&& echo "deb-src http://deb.debian.org/debian/ bookworm main" > /etc/apt/sources.list.d/bookworm-src.list \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get source --yes --quiet qpdf=${QPDF_VERSION}-1/bookworm
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: amd64-builder
|
|
||||||
# Purpose: Builds qpdf for x86_64 (native build)
|
|
||||||
#
|
|
||||||
FROM pre-build as amd64-builder
|
|
||||||
|
|
||||||
ARG AMD64_BUILD_PACKAGES="\
|
|
||||||
build-essential \
|
|
||||||
libjpeg62-turbo-dev:amd64 \
|
|
||||||
libgnutls28-dev:amd64 \
|
|
||||||
zlib1g-dev:amd64"
|
|
||||||
|
|
||||||
WORKDIR /usr/src/qpdf-${QPDF_VERSION}
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Beginning amd64" \
|
|
||||||
&& echo "Install amd64 packages" \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${AMD64_BUILD_PACKAGES} \
|
|
||||||
&& echo "Building amd64" \
|
|
||||||
&& dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean \
|
|
||||||
&& echo "Removing debug files" \
|
|
||||||
&& rm -f ../libqpdf29-dbgsym* \
|
|
||||||
&& rm -f ../qpdf-dbgsym* \
|
|
||||||
&& echo "Gathering package data" \
|
|
||||||
&& dpkg-query -f '${Package;-40}${Version}\n' -W > ../pkg-list.txt
|
|
||||||
#
|
|
||||||
# Stage: armhf-builder
|
|
||||||
# Purpose:
|
|
||||||
# - Sets armhf specific environment
|
|
||||||
# - Builds qpdf for armhf (cross compile)
|
|
||||||
#
|
|
||||||
FROM pre-build as armhf-builder
|
|
||||||
|
|
||||||
ARG ARMHF_PACKAGES="\
|
|
||||||
crossbuild-essential-armhf \
|
|
||||||
libjpeg62-turbo-dev:armhf \
|
|
||||||
libgnutls28-dev:armhf \
|
|
||||||
zlib1g-dev:armhf"
|
|
||||||
|
|
||||||
WORKDIR /usr/src/qpdf-${QPDF_VERSION}
|
|
||||||
|
|
||||||
ENV CXX="/usr/bin/arm-linux-gnueabihf-g++" \
|
|
||||||
CC="/usr/bin/arm-linux-gnueabihf-gcc"
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Beginning armhf" \
|
|
||||||
&& echo "Install armhf packages" \
|
|
||||||
&& dpkg --add-architecture armhf \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${ARMHF_PACKAGES} \
|
|
||||||
&& echo "Building armhf" \
|
|
||||||
&& dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean --host-arch armhf \
|
|
||||||
&& echo "Removing debug files" \
|
|
||||||
&& rm -f ../libqpdf29-dbgsym* \
|
|
||||||
&& rm -f ../qpdf-dbgsym* \
|
|
||||||
&& echo "Gathering package data" \
|
|
||||||
&& dpkg-query -f '${Package;-40}${Version}\n' -W > ../pkg-list.txt
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: aarch64-builder
|
|
||||||
# Purpose:
|
|
||||||
# - Sets aarch64 specific environment
|
|
||||||
# - Builds qpdf for aarch64 (cross compile)
|
|
||||||
#
|
|
||||||
FROM pre-build as aarch64-builder
|
|
||||||
|
|
||||||
ARG ARM64_PACKAGES="\
|
|
||||||
crossbuild-essential-arm64 \
|
|
||||||
libjpeg62-turbo-dev:arm64 \
|
|
||||||
libgnutls28-dev:arm64 \
|
|
||||||
zlib1g-dev:arm64"
|
|
||||||
|
|
||||||
ENV CXX="/usr/bin/aarch64-linux-gnu-g++" \
|
|
||||||
CC="/usr/bin/aarch64-linux-gnu-gcc"
|
|
||||||
|
|
||||||
WORKDIR /usr/src/qpdf-${QPDF_VERSION}
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Beginning arm64" \
|
|
||||||
&& echo "Install arm64 packages" \
|
|
||||||
&& dpkg --add-architecture arm64 \
|
|
||||||
&& apt-get update --quiet \
|
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${ARM64_PACKAGES} \
|
|
||||||
&& echo "Building arm64" \
|
|
||||||
&& dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean --host-arch arm64 \
|
|
||||||
&& echo "Removing debug files" \
|
|
||||||
&& rm -f ../libqpdf29-dbgsym* \
|
|
||||||
&& rm -f ../qpdf-dbgsym* \
|
|
||||||
&& echo "Gathering package data" \
|
|
||||||
&& dpkg-query -f '${Package;-40}${Version}\n' -W > ../pkg-list.txt
|
|
||||||
|
|
||||||
#
|
|
||||||
# Stage: package
|
|
||||||
# Purpose: Holds the compiled .deb files in arch/variant specific folders
|
|
||||||
#
|
|
||||||
FROM alpine:3.17 as package
|
|
||||||
|
|
||||||
LABEL org.opencontainers.image.description="A image with qpdf installers stored in architecture & version specific folders"
|
|
||||||
|
|
||||||
ARG QPDF_VERSION
|
|
||||||
|
|
||||||
WORKDIR /usr/src/qpdf/${QPDF_VERSION}/amd64
|
|
||||||
|
|
||||||
COPY --from=amd64-builder /usr/src/*.deb ./
|
|
||||||
COPY --from=amd64-builder /usr/src/pkg-list.txt ./
|
|
||||||
|
|
||||||
# Note this is ${TARGETARCH}${TARGETVARIANT} for armv7
|
|
||||||
WORKDIR /usr/src/qpdf/${QPDF_VERSION}/armv7
|
|
||||||
|
|
||||||
COPY --from=armhf-builder /usr/src/*.deb ./
|
|
||||||
COPY --from=armhf-builder /usr/src/pkg-list.txt ./
|
|
||||||
|
|
||||||
WORKDIR /usr/src/qpdf/${QPDF_VERSION}/arm64
|
|
||||||
|
|
||||||
COPY --from=aarch64-builder /usr/src/*.deb ./
|
|
||||||
COPY --from=aarch64-builder /usr/src/pkg-list.txt ./
|
|
@@ -1,57 +0,0 @@
|
|||||||
# Installer Library
|
|
||||||
|
|
||||||
This folder contains the Dockerfiles for building certain installers or libraries, which are then pulled into the main image.
|
|
||||||
|
|
||||||
## [jbig2enc](https://github.com/agl/jbig2enc)
|
|
||||||
|
|
||||||
### Why
|
|
||||||
|
|
||||||
JBIG is an image coding which can achieve better compression of images for PDFs.
|
|
||||||
|
|
||||||
### What
|
|
||||||
|
|
||||||
The Docker image builds a shared library file and utility, which is copied into the correct location in the final image.
|
|
||||||
|
|
||||||
### Updating
|
|
||||||
|
|
||||||
1. Ensure the given qpdf version is present in [Debian bookworm](https://packages.debian.org/bookworm/qpdf)
|
|
||||||
2. Update `.build-config.json` to the given version
|
|
||||||
3. If the Debian specific version has incremented, update `Dockerfile.qpdf`
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
|
|
||||||
- [OCRMyPDF Documentation](https://ocrmypdf.readthedocs.io/en/latest/jbig2.html)
|
|
||||||
|
|
||||||
## [psycopg2](https://www.psycopg.org/)
|
|
||||||
|
|
||||||
### Why
|
|
||||||
|
|
||||||
The pre-built wheels of psycopg2 are built on Debian 9, which provides a quite old version of libpq-dev. This causes issue with authentication methods.
|
|
||||||
|
|
||||||
### What
|
|
||||||
|
|
||||||
The image builds psycopg2 wheels on Debian 10 and places the produced wheels into `/usr/src/wheels/`.
|
|
||||||
|
|
||||||
See Also:
|
|
||||||
|
|
||||||
- [Issue 266](https://github.com/paperless-ngx/paperless-ngx/issues/266)
|
|
||||||
|
|
||||||
## [qpdf](https://qpdf.readthedocs.io/en/stable/index.html)
|
|
||||||
|
|
||||||
### Why
|
|
||||||
|
|
||||||
qpdf and it's library provide tools to read, manipulate and fix up PDFs. Version 11 is also required by `pikepdf` 6+ and Debian 9 does not provide above version 10.
|
|
||||||
|
|
||||||
### What
|
|
||||||
|
|
||||||
The Docker image cross compiles .deb installers for each supported architecture of the main image. The installers are placed in `/usr/src/qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/`
|
|
||||||
|
|
||||||
## [pikepdf](https://pikepdf.readthedocs.io/en/latest/)
|
|
||||||
|
|
||||||
### Why
|
|
||||||
|
|
||||||
Required by OCRMyPdf, this is a general purpose library for PDF manipulation in Python via the qpdf libraries.
|
|
||||||
|
|
||||||
### What
|
|
||||||
|
|
||||||
The built wheels are placed into `/usr/src/wheels/`
|
|
@@ -37,7 +37,7 @@ services:
|
|||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:13
|
image: docker.io/library/postgres:15
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
@@ -39,7 +39,7 @@ services:
|
|||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:13
|
image: docker.io/library/postgres:15
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
@@ -35,7 +35,7 @@ services:
|
|||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:13
|
image: docker.io/library/postgres:15
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- pgdata:/var/lib/postgresql/data
|
- pgdata:/var/lib/postgresql/data
|
||||||
|
@@ -80,7 +80,7 @@ django_checks() {
|
|||||||
|
|
||||||
search_index() {
|
search_index() {
|
||||||
|
|
||||||
local -r index_version=3
|
local -r index_version=6
|
||||||
local -r index_version_file=${DATA_DIR}/.index_version
|
local -r index_version_file=${DATA_DIR}/.index_version
|
||||||
|
|
||||||
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then
|
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then
|
||||||
|
@@ -15,6 +15,10 @@ do
|
|||||||
env_name=${line%%=*}
|
env_name=${line%%=*}
|
||||||
# Check if it starts with "PAPERLESS_" and ends in "_FILE"
|
# Check if it starts with "PAPERLESS_" and ends in "_FILE"
|
||||||
if [[ ${env_name} == PAPERLESS_*_FILE ]]; then
|
if [[ ${env_name} == PAPERLESS_*_FILE ]]; then
|
||||||
|
# This should have been named different..
|
||||||
|
if [[ ${env_name} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
# Extract the value of the environment
|
# Extract the value of the environment
|
||||||
env_value=${line#*=}
|
env_value=${line#*=}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ stdout_logfile=/dev/stdout
|
|||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
environment = HOME="/usr/src/paperless",USER="paperless"
|
||||||
|
|
||||||
[program:consumer]
|
[program:consumer]
|
||||||
command=python3 manage.py document_consumer
|
command=python3 manage.py document_consumer
|
||||||
@@ -25,10 +26,11 @@ stdout_logfile=/dev/stdout
|
|||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
environment = HOME="/usr/src/paperless",USER="paperless"
|
||||||
|
|
||||||
[program:celery]
|
[program:celery]
|
||||||
|
|
||||||
command = celery --app paperless worker --loglevel INFO
|
command = celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
|
||||||
user=paperless
|
user=paperless
|
||||||
stopasgroup = true
|
stopasgroup = true
|
||||||
stopwaitsecs = 60
|
stopwaitsecs = 60
|
||||||
@@ -37,6 +39,7 @@ stdout_logfile=/dev/stdout
|
|||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
environment = HOME="/usr/src/paperless",USER="paperless"
|
||||||
|
|
||||||
[program:celery-beat]
|
[program:celery-beat]
|
||||||
|
|
||||||
@@ -48,6 +51,7 @@ stdout_logfile=/dev/stdout
|
|||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
environment = HOME="/usr/src/paperless",USER="paperless"
|
||||||
|
|
||||||
[program:celery-flower]
|
[program:celery-flower]
|
||||||
command = /usr/local/bin/flower-conditional.sh
|
command = /usr/local/bin/flower-conditional.sh
|
||||||
@@ -58,3 +62,4 @@ stdout_logfile=/dev/stdout
|
|||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
environment = HOME="/usr/src/paperless",USER="paperless"
|
||||||
|
@@ -12,13 +12,12 @@ from typing import Final
|
|||||||
from redis import Redis
|
from redis import Redis
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
MAX_RETRY_COUNT: Final[int] = 5
|
MAX_RETRY_COUNT: Final[int] = 5
|
||||||
RETRY_SLEEP_SECONDS: Final[int] = 5
|
RETRY_SLEEP_SECONDS: Final[int] = 5
|
||||||
|
|
||||||
REDIS_URL: Final[str] = os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
|
REDIS_URL: Final[str] = os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
|
||||||
|
|
||||||
print(f"Waiting for Redis...", flush=True)
|
print("Waiting for Redis...", flush=True)
|
||||||
|
|
||||||
attempt = 0
|
attempt = 0
|
||||||
with Redis.from_url(url=REDIS_URL) as client:
|
with Redis.from_url(url=REDIS_URL) as client:
|
||||||
@@ -29,7 +28,7 @@ if __name__ == "__main__":
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(
|
print(
|
||||||
f"Redis ping #{attempt} failed.\n"
|
f"Redis ping #{attempt} failed.\n"
|
||||||
f"Error: {str(e)}.\n"
|
f"Error: {e!s}.\n"
|
||||||
f"Waiting {RETRY_SLEEP_SECONDS}s",
|
f"Waiting {RETRY_SLEEP_SECONDS}s",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
@@ -37,8 +36,8 @@ if __name__ == "__main__":
|
|||||||
attempt += 1
|
attempt += 1
|
||||||
|
|
||||||
if attempt >= MAX_RETRY_COUNT:
|
if attempt >= MAX_RETRY_COUNT:
|
||||||
print(f"Failed to connect to redis using environment variable PAPERLESS_REDIS.")
|
print("Failed to connect to redis using environment variable PAPERLESS_REDIS.")
|
||||||
sys.exit(os.EX_UNAVAILABLE)
|
sys.exit(os.EX_UNAVAILABLE)
|
||||||
else:
|
else:
|
||||||
print(f"Connected to Redis broker.")
|
print("Connected to Redis broker.")
|
||||||
sys.exit(os.EX_OK)
|
sys.exit(os.EX_OK)
|
||||||
|
@@ -78,7 +78,7 @@ After that, [make a backup](#backup).
|
|||||||
The docker-compose files refer to the `latest` version, which is
|
The docker-compose files refer to the `latest` version, which is
|
||||||
always the latest stable release.
|
always the latest stable release.
|
||||||
|
|
||||||
2. If you built the image yourself, do the following:
|
1. If you built the image yourself, do the following:
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ git pull
|
$ git pull
|
||||||
@@ -98,7 +98,7 @@ the background.
|
|||||||
won't automatically update to newer versions. In order to enable
|
won't automatically update to newer versions. In order to enable
|
||||||
updates as described above, either get the new `docker-compose.yml`
|
updates as described above, either get the new `docker-compose.yml`
|
||||||
file from
|
file from
|
||||||
[here](https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose)
|
[here](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
|
||||||
or edit the `docker-compose.yml` file, find the line that says
|
or edit the `docker-compose.yml` file, find the line that says
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -148,6 +148,13 @@ following:
|
|||||||
$ pip install -r requirements.txt
|
$ pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
At times, some dependencies will be removed from requirements.txt.
|
||||||
|
Comparing the versions and removing no longer needed dependencies
|
||||||
|
will keep your system or virtual environment clean and prevent
|
||||||
|
possible conflicts.
|
||||||
|
|
||||||
3. Migrate the database.
|
3. Migrate the database.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
@@ -160,6 +167,16 @@ following:
|
|||||||
This might not actually do anything. Not every new paperless version
|
This might not actually do anything. Not every new paperless version
|
||||||
comes with new database migrations.
|
comes with new database migrations.
|
||||||
|
|
||||||
|
### Database Upgrades
|
||||||
|
|
||||||
|
In general, paperless does not require a specific version of PostgreSQL or MariaDB and it is
|
||||||
|
safe to update them to newer versions. However, you should always take a backup and follow
|
||||||
|
the instructions from your database's documentation for how to upgrade between major versions.
|
||||||
|
|
||||||
|
For PostgreSQL, refer to [Upgrading a PostgreSQL Cluster](https://www.postgresql.org/docs/current/upgrading.html).
|
||||||
|
|
||||||
|
For MariaDB, refer to [Upgrading MariaDB](https://mariadb.com/kb/en/upgrading/)
|
||||||
|
|
||||||
## Downgrading Paperless {#downgrade-paperless}
|
## Downgrading Paperless {#downgrade-paperless}
|
||||||
|
|
||||||
Downgrades are possible. However, some updates also contain database
|
Downgrades are possible. However, some updates also contain database
|
||||||
@@ -453,7 +470,7 @@ The issues detected by the sanity checker are as follows:
|
|||||||
- Inaccessible thumbnails due to improper permissions.
|
- Inaccessible thumbnails due to improper permissions.
|
||||||
- Documents without any content (warning).
|
- Documents without any content (warning).
|
||||||
- Orphaned files in the media directory (warning). These are files
|
- Orphaned files in the media directory (warning). These are files
|
||||||
that are not referenced by any document im paperless.
|
that are not referenced by any document in paperless.
|
||||||
|
|
||||||
```
|
```
|
||||||
document_sanity_checker
|
document_sanity_checker
|
||||||
@@ -475,12 +492,13 @@ mail_fetcher
|
|||||||
The command takes no arguments and processes all your mail accounts and
|
The command takes no arguments and processes all your mail accounts and
|
||||||
rules.
|
rules.
|
||||||
|
|
||||||
!!! note
|
!!! tip
|
||||||
|
|
||||||
As of October 2022 Microsoft no longer supports IMAP authentication
|
To use OAuth access tokens for mail fetching,
|
||||||
for Exchange servers, thus Exchange is no longer supported until a
|
select the box to indicate the password is actually
|
||||||
solution is implemented in the Python IMAP library used by Paperless.
|
a token when creating or editing a mail account. The
|
||||||
See [learn.microsoft.com](https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online)
|
details for creating a token depend on your email
|
||||||
|
provider.
|
||||||
|
|
||||||
### Creating archived documents {#archiver}
|
### Creating archived documents {#archiver}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Advanced Topics
|
# Advanced Topics
|
||||||
|
|
||||||
Paperless offers a couple features that automate certain tasks and make
|
Paperless offers a couple of features that automate certain tasks and make
|
||||||
your life easier.
|
your life easier.
|
||||||
|
|
||||||
## Matching tags, correspondents, document types, and storage paths {#matching}
|
## Matching tags, correspondents, document types, and storage paths {#matching}
|
||||||
@@ -9,7 +9,7 @@ Paperless will compare the matching algorithms defined by every tag,
|
|||||||
correspondent, document type, and storage path in your database to see
|
correspondent, document type, and storage path in your database to see
|
||||||
if they apply to the text in a document. In other words, if you define a
|
if they apply to the text in a document. In other words, if you define a
|
||||||
tag called `Home Utility` that had a `match` property of `bc hydro` and
|
tag called `Home Utility` that had a `match` property of `bc hydro` and
|
||||||
a `matching_algorithm` of `literal`, Paperless will automatically tag
|
a `matching_algorithm` of `Exact`, Paperless will automatically tag
|
||||||
your newly-consumed document with your `Home Utility` tag so long as the
|
your newly-consumed document with your `Home Utility` tag so long as the
|
||||||
text `bc hydro` appears in the body of the document somewhere.
|
text `bc hydro` appears in the body of the document somewhere.
|
||||||
|
|
||||||
@@ -25,18 +25,19 @@ documents.
|
|||||||
|
|
||||||
The following algorithms are available:
|
The following algorithms are available:
|
||||||
|
|
||||||
|
- **None:** No matching will be performed.
|
||||||
- **Any:** Looks for any occurrence of any word provided in match in
|
- **Any:** Looks for any occurrence of any word provided in match in
|
||||||
the PDF. If you define the match as `Bank1 Bank2`, it will match
|
the PDF. If you define the match as `Bank1 Bank2`, it will match
|
||||||
documents containing either of these terms.
|
documents containing either of these terms.
|
||||||
- **All:** Requires that every word provided appears in the PDF,
|
- **All:** Requires that every word provided appears in the PDF,
|
||||||
albeit not in the order provided.
|
albeit not in the order provided.
|
||||||
- **Literal:** Matches only if the match appears exactly as provided
|
- **Exact:** Matches only if the match appears exactly as provided
|
||||||
(i.e. preserve ordering) in the PDF.
|
(i.e. preserve ordering) in the PDF.
|
||||||
- **Regular expression:** Parses the match as a regular expression and
|
- **Regular expression:** Parses the match as a regular expression and
|
||||||
tries to find a match within the document.
|
tries to find a match within the document.
|
||||||
- **Fuzzy match:** I don't know. Look at the source.
|
- **Fuzzy match:** I don't know. Look at [the source](https://github.com/paperless-ngx/paperless-ngx/blob/main/src/documents/matching.py).
|
||||||
- **Auto:** Tries to automatically match new documents. This does not
|
- **Auto:** Tries to automatically match new documents. This does not
|
||||||
require you to set a match. See the notes below.
|
require you to set a match. See the [notes below](#automatic-matching).
|
||||||
|
|
||||||
When using the _any_ or _all_ matching algorithms, you can search for
|
When using the _any_ or _all_ matching algorithms, you can search for
|
||||||
terms that consist of multiple words by enclosing them in double quotes.
|
terms that consist of multiple words by enclosing them in double quotes.
|
||||||
@@ -91,7 +92,7 @@ when using this feature:
|
|||||||
decide when not to assign a certain tag, correspondent, document
|
decide when not to assign a certain tag, correspondent, document
|
||||||
type, or storage path. This will usually be the case as you start
|
type, or storage path. This will usually be the case as you start
|
||||||
filling up paperless with documents. Example: If all your documents
|
filling up paperless with documents. Example: If all your documents
|
||||||
are either from "Webshop" and "Bank", paperless will assign one
|
are either from "Webshop" or "Bank", paperless will assign one
|
||||||
of these correspondents to ANY new document, if both are set to
|
of these correspondents to ANY new document, if both are set to
|
||||||
automatic matching.
|
automatic matching.
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ when using this feature:
|
|||||||
Sometimes you may want to do something arbitrary whenever a document is
|
Sometimes you may want to do something arbitrary whenever a document is
|
||||||
consumed. Rather than try to predict what you may want to do, Paperless
|
consumed. Rather than try to predict what you may want to do, Paperless
|
||||||
lets you execute scripts of your own choosing just before or after a
|
lets you execute scripts of your own choosing just before or after a
|
||||||
document is consumed using a couple simple hooks.
|
document is consumed using a couple of simple hooks.
|
||||||
|
|
||||||
Just write a script, put it somewhere that Paperless can read & execute,
|
Just write a script, put it somewhere that Paperless can read & execute,
|
||||||
and then put the path to that script in `paperless.conf` or
|
and then put the path to that script in `paperless.conf` or
|
||||||
@@ -196,7 +197,7 @@ The script can be in any language, A simple shell script example:
|
|||||||
!!! warning
|
!!! warning
|
||||||
|
|
||||||
The post consumption script should not modify the document files
|
The post consumption script should not modify the document files
|
||||||
directly
|
directly.
|
||||||
|
|
||||||
The script's stdout and stderr will be logged line by line to the
|
The script's stdout and stderr will be logged line by line to the
|
||||||
webserver log, along with the exit code of the script.
|
webserver log, along with the exit code of the script.
|
||||||
@@ -308,6 +309,9 @@ Paperless provides the following placeholders within filenames:
|
|||||||
- `{added_month_name_short}`: Month added abbreviated name, as per
|
- `{added_month_name_short}`: Month added abbreviated name, as per
|
||||||
locale
|
locale
|
||||||
- `{added_day}`: Day added only (number 01-31).
|
- `{added_day}`: Day added only (number 01-31).
|
||||||
|
- `{owner_username}`: Username of document owner, if any, or "none"
|
||||||
|
- `{original_name}`: Document original filename, minus the extension, if any, or "none"
|
||||||
|
- `{doc_pk}`: The paperless identifier (primary key) for the document.
|
||||||
|
|
||||||
Paperless will try to conserve the information from your database as
|
Paperless will try to conserve the information from your database as
|
||||||
much as possible. However, some characters that you can use in document
|
much as possible. However, some characters that you can use in document
|
||||||
@@ -368,7 +372,7 @@ value.
|
|||||||
One of the best things in Paperless is that you can not only access the
|
One of the best things in Paperless is that you can not only access the
|
||||||
documents via the web interface, but also via the file system.
|
documents via the web interface, but also via the file system.
|
||||||
|
|
||||||
When as single storage layout is not sufficient for your use case,
|
When a single storage layout is not sufficient for your use case,
|
||||||
storage paths come to the rescue. Storage paths allow you to configure
|
storage paths come to the rescue. Storage paths allow you to configure
|
||||||
more precisely where each document is stored in the file system.
|
more precisely where each document is stored in the file system.
|
||||||
|
|
||||||
@@ -414,13 +418,6 @@ structure as in the previous example above.
|
|||||||
Defining a storage path is optional. If no storage path is defined for a
|
Defining a storage path is optional. If no storage path is defined for a
|
||||||
document, the global `PAPERLESS_FILENAME_FORMAT` is applied.
|
document, the global `PAPERLESS_FILENAME_FORMAT` is applied.
|
||||||
|
|
||||||
!!! warning
|
|
||||||
|
|
||||||
If you adjust the format of an existing storage path, old documents
|
|
||||||
don't get relocated automatically. You need to run the
|
|
||||||
[document renamer](/administration#renamer) to
|
|
||||||
adjust their paths.
|
|
||||||
|
|
||||||
## Celery Monitoring {#celery-monitoring}
|
## Celery Monitoring {#celery-monitoring}
|
||||||
|
|
||||||
The monitoring tool
|
The monitoring tool
|
||||||
@@ -492,7 +489,7 @@ database to be case sensitive. This would prevent a user from creating a
|
|||||||
tag `Name` and `NAME` as they are considered the same.
|
tag `Name` and `NAME` as they are considered the same.
|
||||||
|
|
||||||
Per Django documentation, to enable this requires manual intervention.
|
Per Django documentation, to enable this requires manual intervention.
|
||||||
To enable case sensetive tables, you can execute the following command
|
To enable case sensitive tables, you can execute the following command
|
||||||
against each table:
|
against each table:
|
||||||
|
|
||||||
`ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`
|
`ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`
|
||||||
@@ -507,3 +504,115 @@ existing tables) with:
|
|||||||
Using mariadb version 10.4+ is recommended. Using the `utf8mb3` character set on
|
Using mariadb version 10.4+ is recommended. Using the `utf8mb3` character set on
|
||||||
an older system may fix issues that can arise while setting up Paperless-ngx but
|
an older system may fix issues that can arise while setting up Paperless-ngx but
|
||||||
`utf8mb3` can cause issues with consumption (where `utf8mb4` does not).
|
`utf8mb3` can cause issues with consumption (where `utf8mb4` does not).
|
||||||
|
|
||||||
|
## Barcodes {#barcodes}
|
||||||
|
|
||||||
|
Paperless is able to utilize barcodes for automatically preforming some tasks.
|
||||||
|
|
||||||
|
At this time, the library utilized for detection of barcodes supports the following types:
|
||||||
|
|
||||||
|
- AN-13/UPC-A
|
||||||
|
- UPC-E
|
||||||
|
- EAN-8
|
||||||
|
- Code 128
|
||||||
|
- Code 93
|
||||||
|
- Code 39
|
||||||
|
- Codabar
|
||||||
|
- Interleaved 2 of 5
|
||||||
|
- QR Code
|
||||||
|
- SQ Code
|
||||||
|
|
||||||
|
You may check for updates on the [zbar library homepage](https://github.com/mchehab/zbar).
|
||||||
|
For usage in Paperless, the type of barcode does not matter, only the contents of it.
|
||||||
|
|
||||||
|
For how to enable barcode usage, see [the configuration](/configuration#barcodes).
|
||||||
|
The two settings may be enabled independently, but do have interactions as explained
|
||||||
|
below.
|
||||||
|
|
||||||
|
### Document Splitting {#document-splitting}
|
||||||
|
|
||||||
|
When enabled, Paperless will look for a barcode with the configured value and create a new document
|
||||||
|
starting from the next page. The page with the barcode on it will _not_ be retained. It
|
||||||
|
is expected to be a page existing only for triggering the split.
|
||||||
|
|
||||||
|
### Archive Serial Number Assignment
|
||||||
|
|
||||||
|
When enabled, the value of the barcode (as an integer) will be used to set the document's
|
||||||
|
archive serial number, allowing quick reference back to the original, paper document.
|
||||||
|
|
||||||
|
If document splitting via barcode is also enabled, documents will be split when an ASN
|
||||||
|
barcode is located. However, differing from the splitting, the page with the
|
||||||
|
barcode _will_ be retained. This allows application of a barcode to any page, including
|
||||||
|
one which holds data to keep in the document.
|
||||||
|
|
||||||
|
## Automatic collation of double-sided documents {#collate}
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
If your scanner supports double-sided scanning natively, you do not need this feature.
|
||||||
|
|
||||||
|
This feature is turned off by default, see [configuration](/configuration#collate) on how to turn it on.
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
If you have a scanner with an automatic document feeder (ADF) that only scans a single side,
|
||||||
|
this feature makes scanning double-sided documents much more convenient by automatically
|
||||||
|
collating two separate scans into one document, reordering the pages as necessary.
|
||||||
|
|
||||||
|
### Usage example
|
||||||
|
|
||||||
|
Suppose you have a double-sided document with 6 pages (3 sheets of paper). First,
|
||||||
|
put the stack into your ADF as normal, ensuring that page 1 is scanned first. Your ADF
|
||||||
|
will now scan pages 1, 3, and 5. Then you (or your the scanner, if it supports it) upload
|
||||||
|
the scan into the correct sub-directory of the consume folder (`double-sided` by default;
|
||||||
|
keep in mind that Paperless will _not_ automatically create the directory for you.)
|
||||||
|
Paperless will then process the scan and move it into an internal staging area.
|
||||||
|
|
||||||
|
The next step is to turn your stack upside down (without reordering the sheets of paper),
|
||||||
|
and scan it once again, your ADF will now scan pages 6, 4, and 2, in that order. Once this
|
||||||
|
scan is copied into the sub-directory, Paperless will collate the previous scan with the
|
||||||
|
new one, reversing the order of the pages on the second, "even numbered" scan. The
|
||||||
|
resulting document will have the pages 1-6 in the correct order, and this new file will
|
||||||
|
then be processed as normal.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
When scanning the even numbered pages, you can omit the last empty pages, if there are
|
||||||
|
any. For example, if page 6 is empty, you only need to scan pages 2 and 4. _Do not_ omit
|
||||||
|
empty pages in the middle of the document.
|
||||||
|
|
||||||
|
### Things that could go wrong
|
||||||
|
|
||||||
|
Paperless will notice when the first, "odd numbered" scan has less pages than the second
|
||||||
|
scan (this can happen when e.g. the ADF skipped a few pages in the first pass). In that
|
||||||
|
case, Paperless will remove the staging copy as well as the scan, and give you an error
|
||||||
|
message asking you to restart the process from scratch, by scanning the odd pages again,
|
||||||
|
followed by the even pages.
|
||||||
|
|
||||||
|
It's important that the scan files get consumed in the correct order, and one at a time.
|
||||||
|
You therefore need to make sure that Paperless is running while you upload the files into
|
||||||
|
the directory; and if you're using [polling](/configuration#polling), make sure that
|
||||||
|
`CONSUMER_POLLING` is set to a value lower than it takes for the second scan to appear,
|
||||||
|
like 5-10 or even lower.
|
||||||
|
|
||||||
|
Another thing that might happen is that you start a double sided scan, but then forget
|
||||||
|
to upload the second file. To avoid collating the wrong documents if you then come back
|
||||||
|
a day later to scan a new double-sided document, Paperless will only keep an "odd numbered
|
||||||
|
pages" file for up to 30 minutes. If more time passes, it will consider the next incoming
|
||||||
|
scan a completely new "odd numbered pages" one. The old staging file will get discarded.
|
||||||
|
|
||||||
|
### Interaction with "subdirs as tags"
|
||||||
|
|
||||||
|
The collation feature can be used together with the [subdirs as tags](/configuration#consume_config)
|
||||||
|
feature (but this is not a requirement). Just create a correctly named double-sided subdir
|
||||||
|
in the hierachy and upload your scans there. For example, both `double-sided/foo/bar` as
|
||||||
|
well as `foo/bar/double-sided` will cause the collated document to be treated as if it
|
||||||
|
were uploaded into `foo/bar` and receive both `foo` and `bar` tags, but not `double-sided`.
|
||||||
|
|
||||||
|
### Interaction with document splitting
|
||||||
|
|
||||||
|
You can use the [document splitting](#document-splitting) feature, but if you use a normal
|
||||||
|
single-sided split marker page, the split document(s) will have an empty page at the front (or
|
||||||
|
whatever else was on the backside of the split marker page.) You can work around that by having
|
||||||
|
a split marker page that has the split barcode on _both_ sides. This way, the extra page will
|
||||||
|
get automatically removed.
|
||||||
|
57
docs/api.md
@@ -14,12 +14,15 @@ The API provides 7 main endpoints:
|
|||||||
- `/api/document_types/`: Full CRUD support.
|
- `/api/document_types/`: Full CRUD support.
|
||||||
- `/api/logs/`: Read-Only.
|
- `/api/logs/`: Read-Only.
|
||||||
- `/api/tags/`: Full CRUD support.
|
- `/api/tags/`: Full CRUD support.
|
||||||
|
- `/api/tasks/`: Read-only.
|
||||||
- `/api/mail_accounts/`: Full CRUD support.
|
- `/api/mail_accounts/`: Full CRUD support.
|
||||||
- `/api/mail_rules/`: Full CRUD support.
|
- `/api/mail_rules/`: Full CRUD support.
|
||||||
|
- `/api/users/`: Full CRUD support.
|
||||||
|
- `/api/groups/`: Full CRUD support.
|
||||||
|
|
||||||
All of these endpoints except for the logging endpoint allow you to
|
All of these endpoints except for the logging endpoint allow you to
|
||||||
fetch, edit and delete individual objects by appending their primary key
|
fetch (and edit and delete where appropriate) individual objects by
|
||||||
to the path, for example `/api/documents/454/`.
|
appending their primary key to the path, e.g. `/api/documents/454/`.
|
||||||
|
|
||||||
The objects served by the document endpoint contain the following
|
The objects served by the document endpoint contain the following
|
||||||
fields:
|
fields:
|
||||||
@@ -44,6 +47,8 @@ fields:
|
|||||||
Read-only.
|
Read-only.
|
||||||
- `archived_file_name`: Verbose filename of the archived document.
|
- `archived_file_name`: Verbose filename of the archived document.
|
||||||
Read-only. Null if no archived document is available.
|
Read-only. Null if no archived document is available.
|
||||||
|
- `set_permissions`: Allows setting document permissions. Optional,
|
||||||
|
write-only. See [below](#permissions).
|
||||||
|
|
||||||
## Downloading documents
|
## Downloading documents
|
||||||
|
|
||||||
@@ -254,11 +259,51 @@ The endpoint supports the following optional form fields:
|
|||||||
- `document_type`: Similar to correspondent.
|
- `document_type`: Similar to correspondent.
|
||||||
- `tags`: Similar to correspondent. Specify this multiple times to
|
- `tags`: Similar to correspondent. Specify this multiple times to
|
||||||
have multiple tags added to the document.
|
have multiple tags added to the document.
|
||||||
|
- `archive_serial_number`: An optional archive serial number to set.
|
||||||
|
|
||||||
The endpoint will immediately return "OK" if the document consumption
|
The endpoint will immediately return HTTP 200 if the document consumption
|
||||||
process was started successfully. No additional status information about
|
process was started successfully, with the UUID of the consumption task
|
||||||
the consumption process itself is available, since that happens in a
|
as the data. No additional status information about the consumption process
|
||||||
different process.
|
itself is available immediately, since that happens in a different process.
|
||||||
|
However, querying the tasks endpoint with the returned UUID e.g.
|
||||||
|
`/api/tasks/?task_id={uuid}` will provide information on the state of the
|
||||||
|
consumption including the ID of a created document if consumption succeeded.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
All objects (documents, tags, etc.) allow setting object-level permissions
|
||||||
|
with an optional `set_permissions` parameter which is of the form:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"owner": user_id,
|
||||||
|
"view": {
|
||||||
|
"users": [...],
|
||||||
|
"groups": [...],
|
||||||
|
},
|
||||||
|
"change": {
|
||||||
|
"users": [...],
|
||||||
|
"groups": [...],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Arrays should contain user or group ID numbers.
|
||||||
|
|
||||||
|
If this parameter is supplied the object's permissions will be overwritten,
|
||||||
|
assuming the authenticated user has permission to do so (the user must be
|
||||||
|
the object owner or a superuser).
|
||||||
|
|
||||||
|
### Retrieving full permissions
|
||||||
|
|
||||||
|
By default, the API will return a truncated version of object-level
|
||||||
|
permissions, returning `user_can_change` indicating whether the current user
|
||||||
|
can edit the object (either because they are the object owner or have permissions
|
||||||
|
granted). You can pass the parameter `full_perms=true` to API calls to view the
|
||||||
|
full permissions of objects in a format that mirrors the `set_permissions`
|
||||||
|
parameter above.
|
||||||
|
|
||||||
## API Versioning
|
## API Versioning
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 740 KiB |
Before Width: | Height: | Size: 457 KiB After Width: | Height: | Size: 383 KiB |
Before Width: | Height: | Size: 436 KiB After Width: | Height: | Size: 704 KiB |
Before Width: | Height: | Size: 462 KiB After Width: | Height: | Size: 474 KiB |
Before Width: | Height: | Size: 608 KiB After Width: | Height: | Size: 616 KiB |
Before Width: | Height: | Size: 698 KiB After Width: | Height: | Size: 708 KiB |
Before Width: | Height: | Size: 706 KiB After Width: | Height: | Size: 705 KiB |
Before Width: | Height: | Size: 480 KiB After Width: | Height: | Size: 480 KiB |
Before Width: | Height: | Size: 680 KiB After Width: | Height: | Size: 689 KiB |
Before Width: | Height: | Size: 686 KiB After Width: | Height: | Size: 685 KiB |
Before Width: | Height: | Size: 848 KiB After Width: | Height: | Size: 859 KiB |
Before Width: | Height: | Size: 703 KiB After Width: | Height: | Size: 706 KiB |
Before Width: | Height: | Size: 388 KiB After Width: | Height: | Size: 393 KiB |
Before Width: | Height: | Size: 517 KiB After Width: | Height: | Size: 516 KiB |
@@ -1,5 +1,830 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## paperless-ngx 1.17.3
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: When PDF/A rendering fails, add a consideration for the user to add args to override [@stumpylog](https://github.com/stumpylog) ([#4083](https://github.com/paperless-ngx/paperless-ngx/pull/4083))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Chore: update frontend PDF viewer (including pdf-js) [@shamoon](https://github.com/shamoon) ([#4065](https://github.com/paperless-ngx/paperless-ngx/pull/4065))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Dev: Upload code coverage in the same job [@stumpylog](https://github.com/stumpylog) ([#4084](https://github.com/paperless-ngx/paperless-ngx/pull/4084))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3 changes</summary>
|
||||||
|
|
||||||
|
- Fix: When PDF/A rendering fails, add a consideration for the user to add args to override [@stumpylog](https://github.com/stumpylog) ([#4083](https://github.com/paperless-ngx/paperless-ngx/pull/4083))
|
||||||
|
- Chore: update frontend PDF viewer (including pdf-js) [@shamoon](https://github.com/shamoon) ([#4065](https://github.com/paperless-ngx/paperless-ngx/pull/4065))
|
||||||
|
- Chore: Prepare for Python 3.11 support [@stumpylog](https://github.com/stumpylog) ([#4066](https://github.com/paperless-ngx/paperless-ngx/pull/4066))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.17.2
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Enhancement: Allow to set a prefix for keys and channels in redis [@amo13](https://github.com/amo13) ([#3993](https://github.com/paperless-ngx/paperless-ngx/pull/3993))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Increase the HTTP timeouts for Tika/Gotenberg to maximum task time [@stumpylog](https://github.com/stumpylog) ([#4061](https://github.com/paperless-ngx/paperless-ngx/pull/4061))
|
||||||
|
- Fix: Allow adding an SSL certificate for IMAP SSL context [@stumpylog](https://github.com/stumpylog) ([#4048](https://github.com/paperless-ngx/paperless-ngx/pull/4048))
|
||||||
|
- Fix: tag creation sometimes retained search text [@shamoon](https://github.com/shamoon) ([#4038](https://github.com/paperless-ngx/paperless-ngx/pull/4038))
|
||||||
|
- Fix: enforce permissions on bulk_edit operations [@shamoon](https://github.com/shamoon) ([#4007](https://github.com/paperless-ngx/paperless-ngx/pull/4007))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>6 changes</summary>
|
||||||
|
|
||||||
|
- Fix: Increase the HTTP timeouts for Tika/Gotenberg to maximum task time [@stumpylog](https://github.com/stumpylog) ([#4061](https://github.com/paperless-ngx/paperless-ngx/pull/4061))
|
||||||
|
- Enhancement: disable / hide some UI buttons / elements if insufficient permissions, show errors [@shamoon](https://github.com/shamoon) ([#4014](https://github.com/paperless-ngx/paperless-ngx/pull/4014))
|
||||||
|
- Fix: Allow adding an SSL certificate for IMAP SSL context [@stumpylog](https://github.com/stumpylog) ([#4048](https://github.com/paperless-ngx/paperless-ngx/pull/4048))
|
||||||
|
- Fix: tag creation sometimes retained search text [@shamoon](https://github.com/shamoon) ([#4038](https://github.com/paperless-ngx/paperless-ngx/pull/4038))
|
||||||
|
- Fix: enforce permissions on bulk_edit operations [@shamoon](https://github.com/shamoon) ([#4007](https://github.com/paperless-ngx/paperless-ngx/pull/4007))
|
||||||
|
- Enhancement: Allow to set a prefix for keys and channels in redis [@amo13](https://github.com/amo13) ([#3993](https://github.com/paperless-ngx/paperless-ngx/pull/3993))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.17.1
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Fix / Enhancement: restrict status messages by owner if set \& improve 404 page [@shamoon](https://github.com/shamoon) ([#3959](https://github.com/paperless-ngx/paperless-ngx/pull/3959))
|
||||||
|
- Feature: Add Ukrainian translation [@shamoon](https://github.com/shamoon) ([#3941](https://github.com/paperless-ngx/paperless-ngx/pull/3941))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: handle ASN = 0 on frontend cards [@shamoon](https://github.com/shamoon) ([#3988](https://github.com/paperless-ngx/paperless-ngx/pull/3988))
|
||||||
|
- Fix: improve light color filled primary button text legibility [@shamoon](https://github.com/shamoon) ([#3980](https://github.com/paperless-ngx/paperless-ngx/pull/3980))
|
||||||
|
- Fix / Enhancement: restrict status messages by owner if set \& improve 404 page [@shamoon](https://github.com/shamoon) ([#3959](https://github.com/paperless-ngx/paperless-ngx/pull/3959))
|
||||||
|
- Fix: handle very old date strings in correspondent list [@shamoon](https://github.com/shamoon) ([#3953](https://github.com/paperless-ngx/paperless-ngx/pull/3953))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- docs(bare-metal): add new dependency [@bin101](https://github.com/bin101) ([#3931](https://github.com/paperless-ngx/paperless-ngx/pull/3931))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Chore: Loosen Pipfile restriction on some packages and update them [@stumpylog](https://github.com/stumpylog) ([#3972](https://github.com/paperless-ngx/paperless-ngx/pull/3972))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>6 changes</summary>
|
||||||
|
|
||||||
|
- Fix: handle ASN = 0 on frontend cards [@shamoon](https://github.com/shamoon) ([#3988](https://github.com/paperless-ngx/paperless-ngx/pull/3988))
|
||||||
|
- Fix: improve light color filled primary button text legibility [@shamoon](https://github.com/shamoon) ([#3980](https://github.com/paperless-ngx/paperless-ngx/pull/3980))
|
||||||
|
- Fix / Enhancement: restrict status messages by owner if set \& improve 404 page [@shamoon](https://github.com/shamoon) ([#3959](https://github.com/paperless-ngx/paperless-ngx/pull/3959))
|
||||||
|
- Fix: handle very old date strings in correspondent list [@shamoon](https://github.com/shamoon) ([#3953](https://github.com/paperless-ngx/paperless-ngx/pull/3953))
|
||||||
|
- Chore: Reduces the 2 mail tests flakiness [@stumpylog](https://github.com/stumpylog) ([#3949](https://github.com/paperless-ngx/paperless-ngx/pull/3949))
|
||||||
|
- Feature: Add Ukrainian translation [@shamoon](https://github.com/shamoon) ([#3941](https://github.com/paperless-ngx/paperless-ngx/pull/3941))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.17.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add support for additional UK date formats [@brainrecursion](https://github.com/brainrecursion) ([#3887](https://github.com/paperless-ngx/paperless-ngx/pull/3887))
|
||||||
|
- Add 'doc_pk' to PAPERLESS_FILENAME_FORMAT handling [@mechanarchy](https://github.com/mechanarchy) ([#3861](https://github.com/paperless-ngx/paperless-ngx/pull/3861))
|
||||||
|
- Feature: hover buttons for saved view widgets [@shamoon](https://github.com/shamoon) ([#3875](https://github.com/paperless-ngx/paperless-ngx/pull/3875))
|
||||||
|
- Feature: collate two single-sided multipage scans [@brakhane](https://github.com/brakhane) ([#3784](https://github.com/paperless-ngx/paperless-ngx/pull/3784))
|
||||||
|
- Feature: include global and object-level permissions in export / import [@shamoon](https://github.com/shamoon) ([#3672](https://github.com/paperless-ngx/paperless-ngx/pull/3672))
|
||||||
|
- Enhancement / Fix: Migrate encrypted png thumbnails to webp [@shamoon](https://github.com/shamoon) ([#3719](https://github.com/paperless-ngx/paperless-ngx/pull/3719))
|
||||||
|
- Feature: Add Slovak translation [@shamoon](https://github.com/shamoon) ([#3722](https://github.com/paperless-ngx/paperless-ngx/pull/3722))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: cancel possibly slow queries on doc details [@shamoon](https://github.com/shamoon) ([#3925](https://github.com/paperless-ngx/paperless-ngx/pull/3925))
|
||||||
|
- Fix: note creation / deletion should respect doc permissions [@shamoon](https://github.com/shamoon) ([#3903](https://github.com/paperless-ngx/paperless-ngx/pull/3903))
|
||||||
|
- Fix: notes show persistent scrollbars [@shamoon](https://github.com/shamoon) ([#3904](https://github.com/paperless-ngx/paperless-ngx/pull/3904))
|
||||||
|
- Fix: Provide SSL context to IMAP client [@stumpylog](https://github.com/stumpylog) ([#3886](https://github.com/paperless-ngx/paperless-ngx/pull/3886))
|
||||||
|
- Fix/enhancement: permissions for mail rules \& accounts [@shamoon](https://github.com/shamoon) ([#3869](https://github.com/paperless-ngx/paperless-ngx/pull/3869))
|
||||||
|
- Fix: Classifier special case when no items are set to automatic matching [@stumpylog](https://github.com/stumpylog) ([#3858](https://github.com/paperless-ngx/paperless-ngx/pull/3858))
|
||||||
|
- Fix: issues with copy2 or copystat and SELinux permissions [@stumpylog](https://github.com/stumpylog) ([#3847](https://github.com/paperless-ngx/paperless-ngx/pull/3847))
|
||||||
|
- Fix: Parsing office document timestamps [@stumpylog](https://github.com/stumpylog) ([#3836](https://github.com/paperless-ngx/paperless-ngx/pull/3836))
|
||||||
|
- Fix: Add warning to install script need for permissions [@shamoon](https://github.com/shamoon) ([#3835](https://github.com/paperless-ngx/paperless-ngx/pull/3835))
|
||||||
|
- Fix interaction between API and barcode archive serial number [@stumpylog](https://github.com/stumpylog) ([#3834](https://github.com/paperless-ngx/paperless-ngx/pull/3834))
|
||||||
|
- Enhancement / Fix: Migrate encrypted png thumbnails to webp [@shamoon](https://github.com/shamoon) ([#3719](https://github.com/paperless-ngx/paperless-ngx/pull/3719))
|
||||||
|
- Fix: add UI tour step padding [@hakimio](https://github.com/hakimio) ([#3791](https://github.com/paperless-ngx/paperless-ngx/pull/3791))
|
||||||
|
- Fix: translate file tasks types in footer [@shamoon](https://github.com/shamoon) ([#3749](https://github.com/paperless-ngx/paperless-ngx/pull/3749))
|
||||||
|
- Fix: limit ng-select size for addition of filter button [@shamoon](https://github.com/shamoon) ([#3731](https://github.com/paperless-ngx/paperless-ngx/pull/3731))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Documentation: improvements to grammar, spelling, indentation [@mechanarchy](https://github.com/mechanarchy) ([#3844](https://github.com/paperless-ngx/paperless-ngx/pull/3844))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Bump stumpylog/image-cleaner-action from 0.1.0 to 0.2.0 [@dependabot](https://github.com/dependabot) ([#3910](https://github.com/paperless-ngx/paperless-ngx/pull/3910))
|
||||||
|
- Chore: group frontend angular dependabot updates [@shamoon](https://github.com/shamoon) ([#3750](https://github.com/paperless-ngx/paperless-ngx/pull/3750))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>17 changes</summary>
|
||||||
|
|
||||||
|
- Chore: Bump the frontend-angular-dependencies group in /src-ui with 11 updates [@shamoon](https://github.com/shamoon) ([#3918](https://github.com/paperless-ngx/paperless-ngx/pull/3918))
|
||||||
|
- Bump stumpylog/image-cleaner-action from 0.1.0 to 0.2.0 [@dependabot](https://github.com/dependabot) ([#3910](https://github.com/paperless-ngx/paperless-ngx/pull/3910))
|
||||||
|
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#3911](https://github.com/paperless-ngx/paperless-ngx/pull/3911))
|
||||||
|
- Bump tslib from 2.6.0 to 2.6.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#3909](https://github.com/paperless-ngx/paperless-ngx/pull/3909))
|
||||||
|
- Bump jest-environment-jsdom from 29.5.0 to 29.6.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3916](https://github.com/paperless-ngx/paperless-ngx/pull/3916))
|
||||||
|
- Bump [@<!---->types/node from 20.3.3 to 20.4.5 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.3.3 to 20.4.5 in /src-ui @dependabot) ([#3915](https://github.com/paperless-ngx/paperless-ngx/pull/3915))
|
||||||
|
- Bump bootstrap from 5.3.0 to 5.3.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#3914](https://github.com/paperless-ngx/paperless-ngx/pull/3914))
|
||||||
|
- Bump [@<!---->playwright/test from 1.36.1 to 1.36.2 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.36.1 to 1.36.2 in /src-ui @dependabot) ([#3912](https://github.com/paperless-ngx/paperless-ngx/pull/3912))
|
||||||
|
- Bump the frontend-jest-dependencies group in /src-ui with 1 update [@dependabot](https://github.com/dependabot) ([#3906](https://github.com/paperless-ngx/paperless-ngx/pull/3906))
|
||||||
|
- Chore: Update dependencies [@stumpylog](https://github.com/stumpylog) ([#3883](https://github.com/paperless-ngx/paperless-ngx/pull/3883))
|
||||||
|
- Chore: Update Python dependencies [@stumpylog](https://github.com/stumpylog) ([#3842](https://github.com/paperless-ngx/paperless-ngx/pull/3842))
|
||||||
|
- Bump the frontend-angular-dependencies group in /src-ui with 16 updates [@dependabot](https://github.com/dependabot) ([#3826](https://github.com/paperless-ngx/paperless-ngx/pull/3826))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.60.1 to 6.1.0 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.60.1 to 6.1.0 in /src-ui @dependabot) ([#3829](https://github.com/paperless-ngx/paperless-ngx/pull/3829))
|
||||||
|
- Bump jest and [@<!---->types/jest in /src-ui @dependabot](https://github.com/<!---->types/jest in /src-ui @dependabot) ([#3828](https://github.com/paperless-ngx/paperless-ngx/pull/3828))
|
||||||
|
- Bump [@<!---->playwright/test from 1.36.0 to 1.36.1 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.36.0 to 1.36.1 in /src-ui @dependabot) ([#3827](https://github.com/paperless-ngx/paperless-ngx/pull/3827))
|
||||||
|
- Bump semver from 5.7.1 to 5.7.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3793](https://github.com/paperless-ngx/paperless-ngx/pull/3793))
|
||||||
|
- Chore: Bump Angular to v16 and other frontend packages [@dependabot](https://github.com/dependabot) ([#3727](https://github.com/paperless-ngx/paperless-ngx/pull/3727))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>35 changes</summary>
|
||||||
|
|
||||||
|
- Fix: cancel possibly slow queries on doc details [@shamoon](https://github.com/shamoon) ([#3925](https://github.com/paperless-ngx/paperless-ngx/pull/3925))
|
||||||
|
- [BUG] Set office document creation date with timezone, if it is naive [@a17t](https://github.com/a17t) ([#3760](https://github.com/paperless-ngx/paperless-ngx/pull/3760))
|
||||||
|
- Fix: note creation / deletion should respect doc permissions [@shamoon](https://github.com/shamoon) ([#3903](https://github.com/paperless-ngx/paperless-ngx/pull/3903))
|
||||||
|
- Chore: Bump the frontend-angular-dependencies group in /src-ui with 11 updates [@shamoon](https://github.com/shamoon) ([#3918](https://github.com/paperless-ngx/paperless-ngx/pull/3918))
|
||||||
|
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#3911](https://github.com/paperless-ngx/paperless-ngx/pull/3911))
|
||||||
|
- Bump tslib from 2.6.0 to 2.6.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#3909](https://github.com/paperless-ngx/paperless-ngx/pull/3909))
|
||||||
|
- Bump jest-environment-jsdom from 29.5.0 to 29.6.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3916](https://github.com/paperless-ngx/paperless-ngx/pull/3916))
|
||||||
|
- Bump [@<!---->types/node from 20.3.3 to 20.4.5 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.3.3 to 20.4.5 in /src-ui @dependabot) ([#3915](https://github.com/paperless-ngx/paperless-ngx/pull/3915))
|
||||||
|
- Bump bootstrap from 5.3.0 to 5.3.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#3914](https://github.com/paperless-ngx/paperless-ngx/pull/3914))
|
||||||
|
- Bump [@<!---->playwright/test from 1.36.1 to 1.36.2 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.36.1 to 1.36.2 in /src-ui @dependabot) ([#3912](https://github.com/paperless-ngx/paperless-ngx/pull/3912))
|
||||||
|
- Bump the frontend-jest-dependencies group in /src-ui with 1 update [@dependabot](https://github.com/dependabot) ([#3906](https://github.com/paperless-ngx/paperless-ngx/pull/3906))
|
||||||
|
- Fix: notes show persistent scrollbars [@shamoon](https://github.com/shamoon) ([#3904](https://github.com/paperless-ngx/paperless-ngx/pull/3904))
|
||||||
|
- Add support for additional UK date formats [@brainrecursion](https://github.com/brainrecursion) ([#3887](https://github.com/paperless-ngx/paperless-ngx/pull/3887))
|
||||||
|
- Add 'doc_pk' to PAPERLESS_FILENAME_FORMAT handling [@mechanarchy](https://github.com/mechanarchy) ([#3861](https://github.com/paperless-ngx/paperless-ngx/pull/3861))
|
||||||
|
- Fix: Provide SSL context to IMAP client [@stumpylog](https://github.com/stumpylog) ([#3886](https://github.com/paperless-ngx/paperless-ngx/pull/3886))
|
||||||
|
- Feature: hover buttons for saved view widgets [@shamoon](https://github.com/shamoon) ([#3875](https://github.com/paperless-ngx/paperless-ngx/pull/3875))
|
||||||
|
- Fix/enhancement: permissions for mail rules \& accounts [@shamoon](https://github.com/shamoon) ([#3869](https://github.com/paperless-ngx/paperless-ngx/pull/3869))
|
||||||
|
- Chore: typing improvements [@stumpylog](https://github.com/stumpylog) ([#3860](https://github.com/paperless-ngx/paperless-ngx/pull/3860))
|
||||||
|
- Fix: Classifier special case when no items are set to automatic matching [@stumpylog](https://github.com/stumpylog) ([#3858](https://github.com/paperless-ngx/paperless-ngx/pull/3858))
|
||||||
|
- Fix: issues with copy2 or copystat and SELinux permissions [@stumpylog](https://github.com/stumpylog) ([#3847](https://github.com/paperless-ngx/paperless-ngx/pull/3847))
|
||||||
|
- Chore: Update Python dependencies [@stumpylog](https://github.com/stumpylog) ([#3842](https://github.com/paperless-ngx/paperless-ngx/pull/3842))
|
||||||
|
- Feature: include global and object-level permissions in export / import [@shamoon](https://github.com/shamoon) ([#3672](https://github.com/paperless-ngx/paperless-ngx/pull/3672))
|
||||||
|
- Fix: Parsing office document timestamps [@stumpylog](https://github.com/stumpylog) ([#3836](https://github.com/paperless-ngx/paperless-ngx/pull/3836))
|
||||||
|
- Fix interaction between API and barcode archive serial number [@stumpylog](https://github.com/stumpylog) ([#3834](https://github.com/paperless-ngx/paperless-ngx/pull/3834))
|
||||||
|
- Bump the frontend-angular-dependencies group in /src-ui with 16 updates [@dependabot](https://github.com/dependabot) ([#3826](https://github.com/paperless-ngx/paperless-ngx/pull/3826))
|
||||||
|
- Enhancement / Fix: Migrate encrypted png thumbnails to webp [@shamoon](https://github.com/shamoon) ([#3719](https://github.com/paperless-ngx/paperless-ngx/pull/3719))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.60.1 to 6.1.0 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.60.1 to 6.1.0 in /src-ui @dependabot) ([#3829](https://github.com/paperless-ngx/paperless-ngx/pull/3829))
|
||||||
|
- Bump jest and [@<!---->types/jest in /src-ui @dependabot](https://github.com/<!---->types/jest in /src-ui @dependabot) ([#3828](https://github.com/paperless-ngx/paperless-ngx/pull/3828))
|
||||||
|
- Bump [@<!---->playwright/test from 1.36.0 to 1.36.1 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.36.0 to 1.36.1 in /src-ui @dependabot) ([#3827](https://github.com/paperless-ngx/paperless-ngx/pull/3827))
|
||||||
|
- Bump semver from 5.7.1 to 5.7.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3793](https://github.com/paperless-ngx/paperless-ngx/pull/3793))
|
||||||
|
- Fix: add UI tour step padding [@hakimio](https://github.com/hakimio) ([#3791](https://github.com/paperless-ngx/paperless-ngx/pull/3791))
|
||||||
|
- Fix: translate file tasks types in footer [@shamoon](https://github.com/shamoon) ([#3749](https://github.com/paperless-ngx/paperless-ngx/pull/3749))
|
||||||
|
- Feature: Add Slovak translation [@shamoon](https://github.com/shamoon) ([#3722](https://github.com/paperless-ngx/paperless-ngx/pull/3722))
|
||||||
|
- Fix: limit ng-select size for addition of filter button [@shamoon](https://github.com/shamoon) ([#3731](https://github.com/paperless-ngx/paperless-ngx/pull/3731))
|
||||||
|
- Chore: Bump Angular to v16 and other frontend packages [@dependabot](https://github.com/dependabot) ([#3727](https://github.com/paperless-ngx/paperless-ngx/pull/3727))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.16.5
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: support barcode upscaling for better detection of small barcodes [@bmachek](https://github.com/bmachek) ([#3655](https://github.com/paperless-ngx/paperless-ngx/pull/3655))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: owner removed when set_permissions passed on object create [@shamoon](https://github.com/shamoon) ([#3702](https://github.com/paperless-ngx/paperless-ngx/pull/3702))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>2 changes</summary>
|
||||||
|
|
||||||
|
- Feature: support barcode upscaling for better detection of small barcodes [@bmachek](https://github.com/bmachek) ([#3655](https://github.com/paperless-ngx/paperless-ngx/pull/3655))
|
||||||
|
- Fix: owner removed when set_permissions passed on object create [@shamoon](https://github.com/shamoon) ([#3702](https://github.com/paperless-ngx/paperless-ngx/pull/3702))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.16.4
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: prevent button wrapping when sidebar narrows in MS Edge [@shamoon](https://github.com/shamoon) ([#3682](https://github.com/paperless-ngx/paperless-ngx/pull/3682))
|
||||||
|
- Fix: Handling for filenames with non-ascii and no content attribute [@stumpylog](https://github.com/stumpylog) ([#3695](https://github.com/paperless-ngx/paperless-ngx/pull/3695))
|
||||||
|
- Fix: Generation of thumbnails for existing stored emails [@stumpylog](https://github.com/stumpylog) ([#3696](https://github.com/paperless-ngx/paperless-ngx/pull/3696))
|
||||||
|
- Fix: Use row gap for filter editor [@kleinweby](https://github.com/kleinweby) ([#3662](https://github.com/paperless-ngx/paperless-ngx/pull/3662))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Documentation: update API docs re permissions [@shamoon](https://github.com/shamoon) ([#3697](https://github.com/paperless-ngx/paperless-ngx/pull/3697))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Updates codecov configuration for the flag settings and notification delay [@stumpylog](https://github.com/stumpylog) ([#3656](https://github.com/paperless-ngx/paperless-ngx/pull/3656))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4 changes</summary>
|
||||||
|
|
||||||
|
- Fix: prevent button wrapping when sidebar narrows in MS Edge [@shamoon](https://github.com/shamoon) ([#3682](https://github.com/paperless-ngx/paperless-ngx/pull/3682))
|
||||||
|
- Fix: Handling for filenames with non-ascii and no content attribute [@stumpylog](https://github.com/stumpylog) ([#3695](https://github.com/paperless-ngx/paperless-ngx/pull/3695))
|
||||||
|
- Fix: Generation of thumbnails for existing stored emails [@stumpylog](https://github.com/stumpylog) ([#3696](https://github.com/paperless-ngx/paperless-ngx/pull/3696))
|
||||||
|
- Fix: Use row gap for filter editor [@kleinweby](https://github.com/kleinweby) ([#3662](https://github.com/paperless-ngx/paperless-ngx/pull/3662))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.16.3
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Set user and home environment through supervisord [@stumpylog](https://github.com/stumpylog) ([#3638](https://github.com/paperless-ngx/paperless-ngx/pull/3638))
|
||||||
|
- Fix: Ignore errors when trying to copy the original file's stats [@stumpylog](https://github.com/stumpylog) ([#3652](https://github.com/paperless-ngx/paperless-ngx/pull/3652))
|
||||||
|
- Fix: Copy default thumbnail if thumbnail generation fails [@plu](https://github.com/plu) ([#3632](https://github.com/paperless-ngx/paperless-ngx/pull/3632))
|
||||||
|
- Fix: Set user and home environment through supervisord [@stumpylog](https://github.com/stumpylog) ([#3638](https://github.com/paperless-ngx/paperless-ngx/pull/3638))
|
||||||
|
- Fix: Fix quick install with external database not being fully ready [@stumpylog](https://github.com/stumpylog) ([#3637](https://github.com/paperless-ngx/paperless-ngx/pull/3637))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Update default Postgres version for new installs [@stumpylog](https://github.com/stumpylog) ([#3640](https://github.com/paperless-ngx/paperless-ngx/pull/3640))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>2 changes</summary>
|
||||||
|
|
||||||
|
- Fix: Ignore errors when trying to copy the original file's stats [@stumpylog](https://github.com/stumpylog) ([#3652](https://github.com/paperless-ngx/paperless-ngx/pull/3652))
|
||||||
|
- Fix: Copy default thumbnail if thumbnail generation fails [@plu](https://github.com/plu) ([#3632](https://github.com/paperless-ngx/paperless-ngx/pull/3632))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.16.2
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Increase httpx operation timeouts to 30s [@stumpylog](https://github.com/stumpylog) ([#3627](https://github.com/paperless-ngx/paperless-ngx/pull/3627))
|
||||||
|
- Fix: Better error handling and checking when parsing documents via Tika [@stumpylog](https://github.com/stumpylog) ([#3617](https://github.com/paperless-ngx/paperless-ngx/pull/3617))
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
- Development: frontend unit testing [@shamoon](https://github.com/shamoon) ([#3597](https://github.com/paperless-ngx/paperless-ngx/pull/3597))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Bumps the CI/Docker pipenv version [@stumpylog](https://github.com/stumpylog) ([#3622](https://github.com/paperless-ngx/paperless-ngx/pull/3622))
|
||||||
|
- Chore: Set CI artifact retention days [@stumpylog](https://github.com/stumpylog) ([#3621](https://github.com/paperless-ngx/paperless-ngx/pull/3621))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3 changes</summary>
|
||||||
|
|
||||||
|
- Fix: Increase httpx operation timeouts to 30s [@stumpylog](https://github.com/stumpylog) ([#3627](https://github.com/paperless-ngx/paperless-ngx/pull/3627))
|
||||||
|
- Fix: Better error handling and checking when parsing documents via Tika [@stumpylog](https://github.com/stumpylog) ([#3617](https://github.com/paperless-ngx/paperless-ngx/pull/3617))
|
||||||
|
- Development: frontend unit testing [@shamoon](https://github.com/shamoon) ([#3597](https://github.com/paperless-ngx/paperless-ngx/pull/3597))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.16.1
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: PIL ImportError on ARM devices with Docker [@stumpylog](https://github.com/stumpylog) ([#3605](https://github.com/paperless-ngx/paperless-ngx/pull/3605))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Enable the image cleanup action [@stumpylog](https://github.com/stumpylog) ([#3606](https://github.com/paperless-ngx/paperless-ngx/pull/3606))
|
||||||
|
|
||||||
|
## paperless-ngx 1.16.0
|
||||||
|
|
||||||
|
### Notable Changes
|
||||||
|
|
||||||
|
- Chore: Update base image to Debian bookworm [@stumpylog](https://github.com/stumpylog) ([#3469](https://github.com/paperless-ngx/paperless-ngx/pull/3469))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: Update to a simpler Tika library [@stumpylog](https://github.com/stumpylog) ([#3517](https://github.com/paperless-ngx/paperless-ngx/pull/3517))
|
||||||
|
- Feature: Allow to filter documents by original filename and checksum [@jayme-github](https://github.com/jayme-github) ([#3485](https://github.com/paperless-ngx/paperless-ngx/pull/3485))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: return user first / last name from backend [@shamoon](https://github.com/shamoon) ([#3579](https://github.com/paperless-ngx/paperless-ngx/pull/3579))
|
||||||
|
- Fix use of `PAPERLESS_DB_TIMEOUT` for all db types [@shamoon](https://github.com/shamoon) ([#3576](https://github.com/paperless-ngx/paperless-ngx/pull/3576))
|
||||||
|
- Fix: handle mail rules with no filters on some imap servers [@shamoon](https://github.com/shamoon) ([#3554](https://github.com/paperless-ngx/paperless-ngx/pull/3554))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Chore: Python dependency updates (celery 5.3.0 in particular) [@stumpylog](https://github.com/stumpylog) ([#3584](https://github.com/paperless-ngx/paperless-ngx/pull/3584))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>8 changes</summary>
|
||||||
|
|
||||||
|
- Chore: Python dependency updates (celery 5.3.0 in particular) [@stumpylog](https://github.com/stumpylog) ([#3584](https://github.com/paperless-ngx/paperless-ngx/pull/3584))
|
||||||
|
- Fix: return user first / last name from backend [@shamoon](https://github.com/shamoon) ([#3579](https://github.com/paperless-ngx/paperless-ngx/pull/3579))
|
||||||
|
- Fix use of `PAPERLESS_DB_TIMEOUT` for all db types [@shamoon](https://github.com/shamoon) ([#3576](https://github.com/paperless-ngx/paperless-ngx/pull/3576))
|
||||||
|
- Fix: handle mail rules with no filters on some imap servers [@shamoon](https://github.com/shamoon) ([#3554](https://github.com/paperless-ngx/paperless-ngx/pull/3554))
|
||||||
|
- Chore: Copy file stats from original file [@stumpylog](https://github.com/stumpylog) ([#3551](https://github.com/paperless-ngx/paperless-ngx/pull/3551))
|
||||||
|
- Chore: Adds test for barcode ASN when it already exists [@stumpylog](https://github.com/stumpylog) ([#3550](https://github.com/paperless-ngx/paperless-ngx/pull/3550))
|
||||||
|
- Feature: Update to a simpler Tika library [@stumpylog](https://github.com/stumpylog) ([#3517](https://github.com/paperless-ngx/paperless-ngx/pull/3517))
|
||||||
|
- Feature: Allow to filter documents by original filename and checksum [@jayme-github](https://github.com/jayme-github) ([#3485](https://github.com/paperless-ngx/paperless-ngx/pull/3485))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.15.1
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix incorrect colors in v1.15.0 [@shamoon](https://github.com/shamoon) ([#3523](https://github.com/paperless-ngx/paperless-ngx/pull/3523))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
- Fix incorrect colors in v1.15.0 [@shamoon](https://github.com/shamoon) ([#3523](https://github.com/paperless-ngx/paperless-ngx/pull/3523))
|
||||||
|
|
||||||
|
## paperless-ngx 1.15.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: quick filters from document detail [@shamoon](https://github.com/shamoon) ([#3476](https://github.com/paperless-ngx/paperless-ngx/pull/3476))
|
||||||
|
- Feature: Add explanations to relative dates [@shamoon](https://github.com/shamoon) ([#3471](https://github.com/paperless-ngx/paperless-ngx/pull/3471))
|
||||||
|
- Enhancement: paginate frontend tasks [@shamoon](https://github.com/shamoon) ([#3445](https://github.com/paperless-ngx/paperless-ngx/pull/3445))
|
||||||
|
- Feature: Better encapsulation of barcode logic [@stumpylog](https://github.com/stumpylog) ([#3425](https://github.com/paperless-ngx/paperless-ngx/pull/3425))
|
||||||
|
- Enhancement: Improve frontend error handling [@shamoon](https://github.com/shamoon) ([#3413](https://github.com/paperless-ngx/paperless-ngx/pull/3413))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: KeyError error on unauthenticated API calls \& persist authentication when enabled [@ajgon](https://github.com/ajgon) ([#3516](https://github.com/paperless-ngx/paperless-ngx/pull/3516))
|
||||||
|
- Fix: exclude consumer \& AnonymousUser users from export manifest [@shamoon](https://github.com/shamoon) ([#3487](https://github.com/paperless-ngx/paperless-ngx/pull/3487))
|
||||||
|
- Fix: prevent date suggestion search if disabled [@shamoon](https://github.com/shamoon) ([#3472](https://github.com/paperless-ngx/paperless-ngx/pull/3472))
|
||||||
|
- Sync Pipfile.lock based on latest Pipfile [@adamantike](https://github.com/adamantike) ([#3475](https://github.com/paperless-ngx/paperless-ngx/pull/3475))
|
||||||
|
- Fix: DocumentSerializer should return correct original filename [@jayme-github](https://github.com/jayme-github) ([#3473](https://github.com/paperless-ngx/paperless-ngx/pull/3473))
|
||||||
|
- consumer.py: read from original file (instead of temp copy) [@chrisblech](https://github.com/chrisblech) ([#3466](https://github.com/paperless-ngx/paperless-ngx/pull/3466))
|
||||||
|
- Bugfix: Catch an nltk AttributeError and handle it [@stumpylog](https://github.com/stumpylog) ([#3453](https://github.com/paperless-ngx/paperless-ngx/pull/3453))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Adding doc on how to setup Fail2ban [@GuillaumeHullin](https://github.com/GuillaumeHullin) ([#3414](https://github.com/paperless-ngx/paperless-ngx/pull/3414))
|
||||||
|
- Docs: Fix typo [@MarcelBochtler](https://github.com/MarcelBochtler) ([#3437](https://github.com/paperless-ngx/paperless-ngx/pull/3437))
|
||||||
|
- [Documentation] Move nginx [@shamoon](https://github.com/shamoon) ([#3420](https://github.com/paperless-ngx/paperless-ngx/pull/3420))
|
||||||
|
- Documentation: Note possible dependency removal for bare metal [@stumpylog](https://github.com/stumpylog) ([#3408](https://github.com/paperless-ngx/paperless-ngx/pull/3408))
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
- Development: migrate frontend tests to playwright [@shamoon](https://github.com/shamoon) ([#3401](https://github.com/paperless-ngx/paperless-ngx/pull/3401))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>10 changes</summary>
|
||||||
|
|
||||||
|
- Bump eslint from 8.39.0 to 8.41.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3513](https://github.com/paperless-ngx/paperless-ngx/pull/3513))
|
||||||
|
- Bump concurrently from 8.0.1 to 8.1.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3510](https://github.com/paperless-ngx/paperless-ngx/pull/3510))
|
||||||
|
- Bump [@<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot](https://github.com/<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot) ([#3507](https://github.com/paperless-ngx/paperless-ngx/pull/3507))
|
||||||
|
- Bump [@<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot](https://github.com/<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot) ([#3508](https://github.com/paperless-ngx/paperless-ngx/pull/3508))
|
||||||
|
- Bump [@<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3505](https://github.com/paperless-ngx/paperless-ngx/pull/3505))
|
||||||
|
- Bump bootstrap from 5.2.3 to 5.3.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3497](https://github.com/paperless-ngx/paperless-ngx/pull/3497))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3500](https://github.com/paperless-ngx/paperless-ngx/pull/3500))
|
||||||
|
- Bump tslib from 2.5.0 to 2.5.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3501](https://github.com/paperless-ngx/paperless-ngx/pull/3501))
|
||||||
|
- Bump [@<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot) ([#3498](https://github.com/paperless-ngx/paperless-ngx/pull/3498))
|
||||||
|
- Bump [@<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot) ([#3499](https://github.com/paperless-ngx/paperless-ngx/pull/3499))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>22 changes</summary>
|
||||||
|
|
||||||
|
- Fix: KeyError error on unauthenticated API calls \& persist authentication when enabled [@ajgon](https://github.com/ajgon) ([#3516](https://github.com/paperless-ngx/paperless-ngx/pull/3516))
|
||||||
|
- Bump eslint from 8.39.0 to 8.41.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3513](https://github.com/paperless-ngx/paperless-ngx/pull/3513))
|
||||||
|
- Bump concurrently from 8.0.1 to 8.1.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3510](https://github.com/paperless-ngx/paperless-ngx/pull/3510))
|
||||||
|
- Bump [@<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot](https://github.com/<!---->ng-bootstrap/ng-bootstrap from 14.1.0 to 14.2.0 in /src-ui @dependabot) ([#3507](https://github.com/paperless-ngx/paperless-ngx/pull/3507))
|
||||||
|
- Bump [@<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot](https://github.com/<!---->popperjs/core from 2.11.7 to 2.11.8 in /src-ui @dependabot) ([#3508](https://github.com/paperless-ngx/paperless-ngx/pull/3508))
|
||||||
|
- Bump [@<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3505](https://github.com/paperless-ngx/paperless-ngx/pull/3505))
|
||||||
|
- Bump bootstrap from 5.2.3 to 5.3.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3497](https://github.com/paperless-ngx/paperless-ngx/pull/3497))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.59.2 to 5.59.8 in /src-ui @dependabot) ([#3500](https://github.com/paperless-ngx/paperless-ngx/pull/3500))
|
||||||
|
- Bump tslib from 2.5.0 to 2.5.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#3501](https://github.com/paperless-ngx/paperless-ngx/pull/3501))
|
||||||
|
- Bump [@<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.16.3 to 20.2.5 in /src-ui @dependabot) ([#3498](https://github.com/paperless-ngx/paperless-ngx/pull/3498))
|
||||||
|
- Bump [@<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.33.0 to 1.34.3 in /src-ui @dependabot) ([#3499](https://github.com/paperless-ngx/paperless-ngx/pull/3499))
|
||||||
|
- Feature: quick filters from document detail [@shamoon](https://github.com/shamoon) ([#3476](https://github.com/paperless-ngx/paperless-ngx/pull/3476))
|
||||||
|
- Fix: exclude consumer \& AnonymousUser users from export manifest [@shamoon](https://github.com/shamoon) ([#3487](https://github.com/paperless-ngx/paperless-ngx/pull/3487))
|
||||||
|
- Fix: prevent date suggestion search if disabled [@shamoon](https://github.com/shamoon) ([#3472](https://github.com/paperless-ngx/paperless-ngx/pull/3472))
|
||||||
|
- Feature: Add explanations to relative dates [@shamoon](https://github.com/shamoon) ([#3471](https://github.com/paperless-ngx/paperless-ngx/pull/3471))
|
||||||
|
- Fix: DocumentSerializer should return correct original filename [@jayme-github](https://github.com/jayme-github) ([#3473](https://github.com/paperless-ngx/paperless-ngx/pull/3473))
|
||||||
|
- consumer.py: read from original file (instead of temp copy) [@chrisblech](https://github.com/chrisblech) ([#3466](https://github.com/paperless-ngx/paperless-ngx/pull/3466))
|
||||||
|
- Bugfix: Catch an nltk AttributeError and handle it [@stumpylog](https://github.com/stumpylog) ([#3453](https://github.com/paperless-ngx/paperless-ngx/pull/3453))
|
||||||
|
- Chore: Improves the logging mixin and allows it to be typed better [@stumpylog](https://github.com/stumpylog) ([#3451](https://github.com/paperless-ngx/paperless-ngx/pull/3451))
|
||||||
|
- Enhancement: paginate frontend tasks [@shamoon](https://github.com/shamoon) ([#3445](https://github.com/paperless-ngx/paperless-ngx/pull/3445))
|
||||||
|
- Add SSL Support for MariaDB [@kimdre](https://github.com/kimdre) ([#3444](https://github.com/paperless-ngx/paperless-ngx/pull/3444))
|
||||||
|
- Enhancement: Improve frontend error handling [@shamoon](https://github.com/shamoon) ([#3413](https://github.com/paperless-ngx/paperless-ngx/pull/3413))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.14.5
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: owner filtering [@shamoon](https://github.com/shamoon) ([#3309](https://github.com/paperless-ngx/paperless-ngx/pull/3309))
|
||||||
|
- Enhancement: dynamic counts include all pages, hide for Any [@shamoon](https://github.com/shamoon) ([#3329](https://github.com/paperless-ngx/paperless-ngx/pull/3329))
|
||||||
|
- Enhancement: save tour completion, hide welcome widget [@shamoon](https://github.com/shamoon) ([#3321](https://github.com/paperless-ngx/paperless-ngx/pull/3321))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Adds better handling for files with invalid utf8 content [@stumpylog](https://github.com/stumpylog) ([#3387](https://github.com/paperless-ngx/paperless-ngx/pull/3387))
|
||||||
|
- Fix: respect permissions for autocomplete suggestions [@shamoon](https://github.com/shamoon) ([#3359](https://github.com/paperless-ngx/paperless-ngx/pull/3359))
|
||||||
|
- Fix: Transition to new library for finding IPs for failed logins [@stumpylog](https://github.com/stumpylog) ([#3382](https://github.com/paperless-ngx/paperless-ngx/pull/3382))
|
||||||
|
- [Security] Render frontend text as plain text [@shamoon](https://github.com/shamoon) ([#3366](https://github.com/paperless-ngx/paperless-ngx/pull/3366))
|
||||||
|
- Fix: default frontend to current owner, allow setting no owner on create [@shamoon](https://github.com/shamoon) ([#3347](https://github.com/paperless-ngx/paperless-ngx/pull/3347))
|
||||||
|
- Fix: dont perform mail actions when rule filename filter not met [@shamoon](https://github.com/shamoon) ([#3336](https://github.com/paperless-ngx/paperless-ngx/pull/3336))
|
||||||
|
- Fix: permission-aware bulk editing in 1.14.1+ [@shamoon](https://github.com/shamoon) ([#3345](https://github.com/paperless-ngx/paperless-ngx/pull/3345))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Rework workflows [@stumpylog](https://github.com/stumpylog) ([#3242](https://github.com/paperless-ngx/paperless-ngx/pull/3242))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- Chore: Upgrade channels to v4 [@stumpylog](https://github.com/stumpylog) ([#3383](https://github.com/paperless-ngx/paperless-ngx/pull/3383))
|
||||||
|
- Chore: Upgrades Python dependencies to their latest allowed versions [@stumpylog](https://github.com/stumpylog) ([#3365](https://github.com/paperless-ngx/paperless-ngx/pull/3365))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>13 changes</summary>
|
||||||
|
|
||||||
|
- Fix: Adds better handling for files with invalid utf8 content [@stumpylog](https://github.com/stumpylog) ([#3387](https://github.com/paperless-ngx/paperless-ngx/pull/3387))
|
||||||
|
- Fix: respect permissions for autocomplete suggestions [@shamoon](https://github.com/shamoon) ([#3359](https://github.com/paperless-ngx/paperless-ngx/pull/3359))
|
||||||
|
- Chore: Upgrade channels to v4 [@stumpylog](https://github.com/stumpylog) ([#3383](https://github.com/paperless-ngx/paperless-ngx/pull/3383))
|
||||||
|
- Fix: Transition to new library for finding IPs for failed logins [@stumpylog](https://github.com/stumpylog) ([#3382](https://github.com/paperless-ngx/paperless-ngx/pull/3382))
|
||||||
|
- Feature: owner filtering [@shamoon](https://github.com/shamoon) ([#3309](https://github.com/paperless-ngx/paperless-ngx/pull/3309))
|
||||||
|
- [Security] Render frontend text as plain text [@shamoon](https://github.com/shamoon) ([#3366](https://github.com/paperless-ngx/paperless-ngx/pull/3366))
|
||||||
|
- Enhancement: dynamic counts include all pages, hide for Any [@shamoon](https://github.com/shamoon) ([#3329](https://github.com/paperless-ngx/paperless-ngx/pull/3329))
|
||||||
|
- Fix: default frontend to current owner, allow setting no owner on create [@shamoon](https://github.com/shamoon) ([#3347](https://github.com/paperless-ngx/paperless-ngx/pull/3347))
|
||||||
|
- [Fix] Position:fixed for .global-dropzone-overlay [@denilsonsa](https://github.com/denilsonsa) ([#3367](https://github.com/paperless-ngx/paperless-ngx/pull/3367))
|
||||||
|
- Fix: dont perform mail actions when rule filename filter not met [@shamoon](https://github.com/shamoon) ([#3336](https://github.com/paperless-ngx/paperless-ngx/pull/3336))
|
||||||
|
- Enhancement: save tour completion, hide welcome widget [@shamoon](https://github.com/shamoon) ([#3321](https://github.com/paperless-ngx/paperless-ngx/pull/3321))
|
||||||
|
- Fix: permission-aware bulk editing in 1.14.1+ [@shamoon](https://github.com/shamoon) ([#3345](https://github.com/paperless-ngx/paperless-ngx/pull/3345))
|
||||||
|
- Fix: Add proper testing for \*\_\_id\_\_in testing [@shamoon](https://github.com/shamoon) ([#3315](https://github.com/paperless-ngx/paperless-ngx/pull/3315))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.14.4
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Inversion in tagged mail searching [@stumpylog](https://github.com/stumpylog) ([#3305](https://github.com/paperless-ngx/paperless-ngx/pull/3305))
|
||||||
|
- Fix dynamic count labels hidden in light mode [@shamoon](https://github.com/shamoon) ([#3303](https://github.com/paperless-ngx/paperless-ngx/pull/3303))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>3 changes</summary>
|
||||||
|
|
||||||
|
- New Crowdin updates [@paperlessngx-bot](https://github.com/paperlessngx-bot) ([#3298](https://github.com/paperless-ngx/paperless-ngx/pull/3298))
|
||||||
|
- Fix: Inversion in tagged mail searching [@stumpylog](https://github.com/stumpylog) ([#3305](https://github.com/paperless-ngx/paperless-ngx/pull/3305))
|
||||||
|
- Fix dynamic count labels hidden in light mode [@shamoon](https://github.com/shamoon) ([#3303](https://github.com/paperless-ngx/paperless-ngx/pull/3303))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.14.3
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Enhancement: better keyboard nav for filter/edit dropdowns [@shamoon](https://github.com/shamoon) ([#3227](https://github.com/paperless-ngx/paperless-ngx/pull/3227))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Bump filelock from 3.10.2 to 3.12.0 to fix permissions bug [@rbrownwsws](https://github.com/rbrownwsws) ([#3282](https://github.com/paperless-ngx/paperless-ngx/pull/3282))
|
||||||
|
- Fix: Handle cases where media files aren't all in the same filesystem [@stumpylog](https://github.com/stumpylog) ([#3261](https://github.com/paperless-ngx/paperless-ngx/pull/3261))
|
||||||
|
- Fix: Prevent erroneous warning when starting container [@stumpylog](https://github.com/stumpylog) ([#3262](https://github.com/paperless-ngx/paperless-ngx/pull/3262))
|
||||||
|
- Retain doc changes on tab switch after refresh doc [@shamoon](https://github.com/shamoon) ([#3243](https://github.com/paperless-ngx/paperless-ngx/pull/3243))
|
||||||
|
- Fix: Don't send Gmail related setting if the server doesn't support it [@stumpylog](https://github.com/stumpylog) ([#3240](https://github.com/paperless-ngx/paperless-ngx/pull/3240))
|
||||||
|
- Fix: close all docs on logout [@shamoon](https://github.com/shamoon) ([#3232](https://github.com/paperless-ngx/paperless-ngx/pull/3232))
|
||||||
|
- Fix: Respect superuser for advanced queries, test coverage for object perms [@shamoon](https://github.com/shamoon) ([#3222](https://github.com/paperless-ngx/paperless-ngx/pull/3222))
|
||||||
|
- Fix: ALLOWED_HOSTS logic being overwritten when \* is set [@ikaruswill](https://github.com/ikaruswill) ([#3218](https://github.com/paperless-ngx/paperless-ngx/pull/3218))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>7 changes</summary>
|
||||||
|
|
||||||
|
- Bump eslint from 8.38.0 to 8.39.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3276](https://github.com/paperless-ngx/paperless-ngx/pull/3276))
|
||||||
|
- Bump [@<!---->typescript-eslint/parser from 5.58.0 to 5.59.2 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.58.0 to 5.59.2 in /src-ui @dependabot) ([#3278](https://github.com/paperless-ngx/paperless-ngx/pull/3278))
|
||||||
|
- Bump [@<!---->types/node from 18.15.11 to 18.16.3 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.15.11 to 18.16.3 in /src-ui @dependabot) ([#3275](https://github.com/paperless-ngx/paperless-ngx/pull/3275))
|
||||||
|
- Bump rxjs from 7.8.0 to 7.8.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#3277](https://github.com/paperless-ngx/paperless-ngx/pull/3277))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.58.0 to 5.59.2 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.58.0 to 5.59.2 in /src-ui @dependabot) ([#3274](https://github.com/paperless-ngx/paperless-ngx/pull/3274))
|
||||||
|
- Bump cypress from 12.9.0 to 12.11.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3268](https://github.com/paperless-ngx/paperless-ngx/pull/3268))
|
||||||
|
- Bulk bump angular packages to 15.2.8 in /src-ui [@dependabot](https://github.com/dependabot) ([#3270](https://github.com/paperless-ngx/paperless-ngx/pull/3270))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>14 changes</summary>
|
||||||
|
|
||||||
|
- Bump eslint from 8.38.0 to 8.39.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3276](https://github.com/paperless-ngx/paperless-ngx/pull/3276))
|
||||||
|
- Bump [@<!---->typescript-eslint/parser from 5.58.0 to 5.59.2 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.58.0 to 5.59.2 in /src-ui @dependabot) ([#3278](https://github.com/paperless-ngx/paperless-ngx/pull/3278))
|
||||||
|
- Bump [@<!---->types/node from 18.15.11 to 18.16.3 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.15.11 to 18.16.3 in /src-ui @dependabot) ([#3275](https://github.com/paperless-ngx/paperless-ngx/pull/3275))
|
||||||
|
- Bump rxjs from 7.8.0 to 7.8.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#3277](https://github.com/paperless-ngx/paperless-ngx/pull/3277))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.58.0 to 5.59.2 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.58.0 to 5.59.2 in /src-ui @dependabot) ([#3274](https://github.com/paperless-ngx/paperless-ngx/pull/3274))
|
||||||
|
- Bump cypress from 12.9.0 to 12.11.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#3268](https://github.com/paperless-ngx/paperless-ngx/pull/3268))
|
||||||
|
- Bulk bump angular packages to 15.2.8 in /src-ui [@dependabot](https://github.com/dependabot) ([#3270](https://github.com/paperless-ngx/paperless-ngx/pull/3270))
|
||||||
|
- Fix: Handle cases where media files aren't all in the same filesystem [@stumpylog](https://github.com/stumpylog) ([#3261](https://github.com/paperless-ngx/paperless-ngx/pull/3261))
|
||||||
|
- Retain doc changes on tab switch after refresh doc [@shamoon](https://github.com/shamoon) ([#3243](https://github.com/paperless-ngx/paperless-ngx/pull/3243))
|
||||||
|
- Fix: Don't send Gmail related setting if the server doesn't support it [@stumpylog](https://github.com/stumpylog) ([#3240](https://github.com/paperless-ngx/paperless-ngx/pull/3240))
|
||||||
|
- Fix: close all docs on logout [@shamoon](https://github.com/shamoon) ([#3232](https://github.com/paperless-ngx/paperless-ngx/pull/3232))
|
||||||
|
- Enhancement: better keyboard nav for filter/edit dropdowns [@shamoon](https://github.com/shamoon) ([#3227](https://github.com/paperless-ngx/paperless-ngx/pull/3227))
|
||||||
|
- Fix: Respect superuser for advanced queries, test coverage for object perms [@shamoon](https://github.com/shamoon) ([#3222](https://github.com/paperless-ngx/paperless-ngx/pull/3222))
|
||||||
|
- Fix: ALLOWED_HOSTS logic being overwritten when \* is set [@ikaruswill](https://github.com/ikaruswill) ([#3218](https://github.com/paperless-ngx/paperless-ngx/pull/3218))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.14.2
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: Finnish translation [@shamoon](https://github.com/shamoon) ([#3215](https://github.com/paperless-ngx/paperless-ngx/pull/3215))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Load saved views from app frame, not dashboard [@shamoon](https://github.com/shamoon) ([#3211](https://github.com/paperless-ngx/paperless-ngx/pull/3211))
|
||||||
|
- Fix: advanced search or date searching + doc type/correspondent/storage path broken [@shamoon](https://github.com/shamoon) ([#3209](https://github.com/paperless-ngx/paperless-ngx/pull/3209))
|
||||||
|
- Fix MixedContentTypeError in add_inbox_tags handler [@e1mo](https://github.com/e1mo) ([#3212](https://github.com/paperless-ngx/paperless-ngx/pull/3212))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>4 changes</summary>
|
||||||
|
|
||||||
|
- Feature: Finnish translation [@shamoon](https://github.com/shamoon) ([#3215](https://github.com/paperless-ngx/paperless-ngx/pull/3215))
|
||||||
|
- Fix: Load saved views from app frame, not dashboard [@shamoon](https://github.com/shamoon) ([#3211](https://github.com/paperless-ngx/paperless-ngx/pull/3211))
|
||||||
|
- Fix: advanced search or date searching + doc type/correspondent/storage path broken [@shamoon](https://github.com/shamoon) ([#3209](https://github.com/paperless-ngx/paperless-ngx/pull/3209))
|
||||||
|
- Fix MixedContentTypeError in add_inbox_tags handler [@e1mo](https://github.com/e1mo) ([#3212](https://github.com/paperless-ngx/paperless-ngx/pull/3212))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.14.1
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: reduce frequency of permissions queries to speed up v1.14.0 [@shamoon](https://github.com/shamoon) ([#3201](https://github.com/paperless-ngx/paperless-ngx/pull/3201))
|
||||||
|
- Fix: permissions-aware statistics [@shamoon](https://github.com/shamoon) ([#3199](https://github.com/paperless-ngx/paperless-ngx/pull/3199))
|
||||||
|
- Fix: Use document owner for matching if set [@shamoon](https://github.com/shamoon) ([#3198](https://github.com/paperless-ngx/paperless-ngx/pull/3198))
|
||||||
|
- Fix: respect permissions on document view actions [@shamoon](https://github.com/shamoon) ([#3174](https://github.com/paperless-ngx/paperless-ngx/pull/3174))
|
||||||
|
- Increment API version for 1.14.1+ [@shamoon](https://github.com/shamoon) ([#3191](https://github.com/paperless-ngx/paperless-ngx/pull/3191))
|
||||||
|
- Fix: dropdown Private items with empty set [@shamoon](https://github.com/shamoon) ([#3189](https://github.com/paperless-ngx/paperless-ngx/pull/3189))
|
||||||
|
- Documentation: add note for macOS [@shamoon](https://github.com/shamoon) ([#3190](https://github.com/paperless-ngx/paperless-ngx/pull/3190))
|
||||||
|
- Fix: make the importer a little more robust against some errors [@stumpylog](https://github.com/stumpylog) ([#3188](https://github.com/paperless-ngx/paperless-ngx/pull/3188))
|
||||||
|
- Fix: Specify backend for auto-login [@shamoon](https://github.com/shamoon) ([#3163](https://github.com/paperless-ngx/paperless-ngx/pull/3163))
|
||||||
|
- Fix: StoragePath missing the owned or granted filter [@stumpylog](https://github.com/stumpylog) ([#3180](https://github.com/paperless-ngx/paperless-ngx/pull/3180))
|
||||||
|
- Fix: Redis socket connections fail due to redis-py [@stumpylog](https://github.com/stumpylog) ([#3176](https://github.com/paperless-ngx/paperless-ngx/pull/3176))
|
||||||
|
- Fix: Handle delete mail action with no filters [@shamoon](https://github.com/shamoon) ([#3161](https://github.com/paperless-ngx/paperless-ngx/pull/3161))
|
||||||
|
- Fix typos and wrong version number in doc [@FizzyMUC](https://github.com/FizzyMUC) ([#3171](https://github.com/paperless-ngx/paperless-ngx/pull/3171))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Documentation: add note for macOS [@shamoon](https://github.com/shamoon) ([#3190](https://github.com/paperless-ngx/paperless-ngx/pull/3190))
|
||||||
|
- Fix typos and wrong version number in doc [@FizzyMUC](https://github.com/FizzyMUC) ([#3171](https://github.com/paperless-ngx/paperless-ngx/pull/3171))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Fix isort not running, upgrade to the latest black [@stumpylog](https://github.com/stumpylog) ([#3177](https://github.com/paperless-ngx/paperless-ngx/pull/3177))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>11 changes</summary>
|
||||||
|
|
||||||
|
- Fix: reduce frequency of permissions queries to speed up v1.14.0 [@shamoon](https://github.com/shamoon) ([#3201](https://github.com/paperless-ngx/paperless-ngx/pull/3201))
|
||||||
|
- Fix: permissions-aware statistics [@shamoon](https://github.com/shamoon) ([#3199](https://github.com/paperless-ngx/paperless-ngx/pull/3199))
|
||||||
|
- Fix: Use document owner for matching if set [@shamoon](https://github.com/shamoon) ([#3198](https://github.com/paperless-ngx/paperless-ngx/pull/3198))
|
||||||
|
- Chore: Fix isort not running, upgrade to the latest black [@stumpylog](https://github.com/stumpylog) ([#3177](https://github.com/paperless-ngx/paperless-ngx/pull/3177))
|
||||||
|
- Fix: respect permissions on document view actions [@shamoon](https://github.com/shamoon) ([#3174](https://github.com/paperless-ngx/paperless-ngx/pull/3174))
|
||||||
|
- Increment API version for 1.14.1+ [@shamoon](https://github.com/shamoon) ([#3191](https://github.com/paperless-ngx/paperless-ngx/pull/3191))
|
||||||
|
- Fix: dropdown Private items with empty set [@shamoon](https://github.com/shamoon) ([#3189](https://github.com/paperless-ngx/paperless-ngx/pull/3189))
|
||||||
|
- Fix: make the importer a little more robust against some errors [@stumpylog](https://github.com/stumpylog) ([#3188](https://github.com/paperless-ngx/paperless-ngx/pull/3188))
|
||||||
|
- Fix: Specify backend for auto-login [@shamoon](https://github.com/shamoon) ([#3163](https://github.com/paperless-ngx/paperless-ngx/pull/3163))
|
||||||
|
- Fix: StoragePath missing the owned or granted filter [@stumpylog](https://github.com/stumpylog) ([#3180](https://github.com/paperless-ngx/paperless-ngx/pull/3180))
|
||||||
|
- Fix: Handle delete mail action with no filters [@shamoon](https://github.com/shamoon) ([#3161](https://github.com/paperless-ngx/paperless-ngx/pull/3161))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.14.0
|
||||||
|
|
||||||
|
### Notable Changes
|
||||||
|
|
||||||
|
- Feature: multi-user permissions [@shamoon](https://github.com/shamoon) ([#2147](https://github.com/paperless-ngx/paperless-ngx/pull/2147))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: Stronger typing for file consumption [@stumpylog](https://github.com/stumpylog) ([#2744](https://github.com/paperless-ngx/paperless-ngx/pull/2744))
|
||||||
|
- Feature: double-click docs [@shamoon](https://github.com/shamoon) ([#2966](https://github.com/paperless-ngx/paperless-ngx/pull/2966))
|
||||||
|
- feature: Add support for zxing as barcode scanning lib [@margau](https://github.com/margau) ([#2907](https://github.com/paperless-ngx/paperless-ngx/pull/2907))
|
||||||
|
- Feature: Enable images to be released on Quay.io [@stumpylog](https://github.com/stumpylog) ([#2972](https://github.com/paperless-ngx/paperless-ngx/pull/2972))
|
||||||
|
- Feature: test mail account [@shamoon](https://github.com/shamoon) ([#2949](https://github.com/paperless-ngx/paperless-ngx/pull/2949))
|
||||||
|
- Feature: Capture celery and kombu logs to a file [@stumpylog](https://github.com/stumpylog) ([#2954](https://github.com/paperless-ngx/paperless-ngx/pull/2954))
|
||||||
|
- Fix: Resolve Redis connection issues with ACLs [@stumpylog](https://github.com/stumpylog) ([#2939](https://github.com/paperless-ngx/paperless-ngx/pull/2939))
|
||||||
|
- Feature: Allow mail account to use access tokens [@stumpylog](https://github.com/stumpylog) ([#2930](https://github.com/paperless-ngx/paperless-ngx/pull/2930))
|
||||||
|
- Fix: Consumer polling could overwhelm database [@stumpylog](https://github.com/stumpylog) ([#2922](https://github.com/paperless-ngx/paperless-ngx/pull/2922))
|
||||||
|
- Feature: Improved statistics widget [@shamoon](https://github.com/shamoon) ([#2910](https://github.com/paperless-ngx/paperless-ngx/pull/2910))
|
||||||
|
- Enhancement: rename comments to notes and improve notes UI [@shamoon](https://github.com/shamoon) ([#2904](https://github.com/paperless-ngx/paperless-ngx/pull/2904))
|
||||||
|
- Allow psql client certificate authentication [@Ongy](https://github.com/Ongy) ([#2899](https://github.com/paperless-ngx/paperless-ngx/pull/2899))
|
||||||
|
- Enhancement: support filtering multiple correspondents, doctypes \& storage paths [@shamoon](https://github.com/shamoon) ([#2893](https://github.com/paperless-ngx/paperless-ngx/pull/2893))
|
||||||
|
- Feature: Change celery serializer to pickle [@stumpylog](https://github.com/stumpylog) ([#2861](https://github.com/paperless-ngx/paperless-ngx/pull/2861))
|
||||||
|
- Feature: Allow naming to include owner and original name [@stumpylog](https://github.com/stumpylog) ([#2873](https://github.com/paperless-ngx/paperless-ngx/pull/2873))
|
||||||
|
- Feature: Allows filtering email by the TO value(s) as well [@stumpylog](https://github.com/stumpylog) ([#2871](https://github.com/paperless-ngx/paperless-ngx/pull/2871))
|
||||||
|
- Feature: owner-aware unique model name constraint [@shamoon](https://github.com/shamoon) ([#2827](https://github.com/paperless-ngx/paperless-ngx/pull/2827))
|
||||||
|
- Feature/2396 better mail actions [@jonaswinkler](https://github.com/jonaswinkler) ([#2718](https://github.com/paperless-ngx/paperless-ngx/pull/2718))
|
||||||
|
- Feature: Reduce classifier memory usage somewhat during training [@stumpylog](https://github.com/stumpylog) ([#2733](https://github.com/paperless-ngx/paperless-ngx/pull/2733))
|
||||||
|
- Feature: Add PAPERLESS_OCR_SKIP_ARCHIVE_FILE config setting [@bdr99](https://github.com/bdr99) ([#2743](https://github.com/paperless-ngx/paperless-ngx/pull/2743))
|
||||||
|
- Feature: dynamic document counts in dropdowns [@shamoon](https://github.com/shamoon) ([#2704](https://github.com/paperless-ngx/paperless-ngx/pull/2704))
|
||||||
|
- Allow setting the ASN on document upload [@stumpylog](https://github.com/stumpylog) ([#2713](https://github.com/paperless-ngx/paperless-ngx/pull/2713))
|
||||||
|
- Feature: Log failed login attempts [@shamoon](https://github.com/shamoon) ([#2359](https://github.com/paperless-ngx/paperless-ngx/pull/2359))
|
||||||
|
- Feature: Rename documents when storage path format changes [@stumpylog](https://github.com/stumpylog) ([#2696](https://github.com/paperless-ngx/paperless-ngx/pull/2696))
|
||||||
|
- Feature: update error message colors \& show on document failures [@shamoon](https://github.com/shamoon) ([#2689](https://github.com/paperless-ngx/paperless-ngx/pull/2689))
|
||||||
|
- Feature: multi-user permissions [@shamoon](https://github.com/shamoon) ([#2147](https://github.com/paperless-ngx/paperless-ngx/pull/2147))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Allow setting additional Django settings for proxies [@stumpylog](https://github.com/stumpylog) ([#3135](https://github.com/paperless-ngx/paperless-ngx/pull/3135))
|
||||||
|
- Fix: Use exclude instead of difference for mariadb [@shamoon](https://github.com/shamoon) ([#2983](https://github.com/paperless-ngx/paperless-ngx/pull/2983))
|
||||||
|
- Fix: permissions display should not show users with inherited permissions \& unable to change owner [@shamoon](https://github.com/shamoon) ([#2818](https://github.com/paperless-ngx/paperless-ngx/pull/2818))
|
||||||
|
- Fix: Resolve Redis connection issues with ACLs [@stumpylog](https://github.com/stumpylog) ([#2939](https://github.com/paperless-ngx/paperless-ngx/pull/2939))
|
||||||
|
- Fix: unable to edit correspondents (in ) [@shamoon](https://github.com/shamoon) ([#2938](https://github.com/paperless-ngx/paperless-ngx/pull/2938))
|
||||||
|
- Fix: Consumer polling could overwhelm database [@stumpylog](https://github.com/stumpylog) ([#2922](https://github.com/paperless-ngx/paperless-ngx/pull/2922))
|
||||||
|
- Fix: Chrome struggles with commas [@stumpylog](https://github.com/stumpylog) ([#2892](https://github.com/paperless-ngx/paperless-ngx/pull/2892))
|
||||||
|
- Fix formatting in Setup documentation page [@igrybkov](https://github.com/igrybkov) ([#2880](https://github.com/paperless-ngx/paperless-ngx/pull/2880))
|
||||||
|
- Fix: logout on change password via frontend [@shamoon](https://github.com/shamoon) ([#2863](https://github.com/paperless-ngx/paperless-ngx/pull/2863))
|
||||||
|
- Fix: give superuser full doc perms [@shamoon](https://github.com/shamoon) ([#2820](https://github.com/paperless-ngx/paperless-ngx/pull/2820))
|
||||||
|
- Fix: Append Gmail labels instead of replacing [@stumpylog](https://github.com/stumpylog) ([#2860](https://github.com/paperless-ngx/paperless-ngx/pull/2860))
|
||||||
|
- Fix: Ensure email date is made aware during action processing [@stumpylog](https://github.com/stumpylog) ([#2837](https://github.com/paperless-ngx/paperless-ngx/pull/2837))
|
||||||
|
- Fix: disable bulk edit dialog buttons during operation [@shamoon](https://github.com/shamoon) ([#2819](https://github.com/paperless-ngx/paperless-ngx/pull/2819))
|
||||||
|
- fix database locked error [@jonaswinkler](https://github.com/jonaswinkler) ([#2808](https://github.com/paperless-ngx/paperless-ngx/pull/2808))
|
||||||
|
- Fix: Disable suggestions for read-only docs [@shamoon](https://github.com/shamoon) ([#2813](https://github.com/paperless-ngx/paperless-ngx/pull/2813))
|
||||||
|
- Update processed mail migration [@shamoon](https://github.com/shamoon) ([#2804](https://github.com/paperless-ngx/paperless-ngx/pull/2804))
|
||||||
|
- Fix: Ensure scratch directory exists before using [@stumpylog](https://github.com/stumpylog) ([#2775](https://github.com/paperless-ngx/paperless-ngx/pull/2775))
|
||||||
|
- Don't submit owner via API on document upload [@jonaswinkler](https://github.com/jonaswinkler) ([#2777](https://github.com/paperless-ngx/paperless-ngx/pull/2777))
|
||||||
|
- Fix: only offer log files that exist [@shamoon](https://github.com/shamoon) ([#2739](https://github.com/paperless-ngx/paperless-ngx/pull/2739))
|
||||||
|
- Fix: permissions editing and initial view issues [@shamoon](https://github.com/shamoon) ([#2717](https://github.com/paperless-ngx/paperless-ngx/pull/2717))
|
||||||
|
- Fix: reset saved view ID on quickFilter [@shamoon](https://github.com/shamoon) ([#2703](https://github.com/paperless-ngx/paperless-ngx/pull/2703))
|
||||||
|
- Fix: bulk edit reset apply button state [@shamoon](https://github.com/shamoon) ([#2701](https://github.com/paperless-ngx/paperless-ngx/pull/2701))
|
||||||
|
- Fix: add missing i18n for mobile preview tab title [@nathanaelhoun](https://github.com/nathanaelhoun) ([#2692](https://github.com/paperless-ngx/paperless-ngx/pull/2692))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Whitespace changes, making sure the example is correcly aligned [@denilsonsa](https://github.com/denilsonsa) ([#3089](https://github.com/paperless-ngx/paperless-ngx/pull/3089))
|
||||||
|
- Docs: Include additional information about barcodes [@stumpylog](https://github.com/stumpylog) ([#2889](https://github.com/paperless-ngx/paperless-ngx/pull/2889))
|
||||||
|
- Fix formatting in Setup documentation page [@igrybkov](https://github.com/igrybkov) ([#2880](https://github.com/paperless-ngx/paperless-ngx/pull/2880))
|
||||||
|
- [Documentation] Update docker-compose steps to support podman [@white-gecko](https://github.com/white-gecko) ([#2855](https://github.com/paperless-ngx/paperless-ngx/pull/2855))
|
||||||
|
- docs: better language code help [@tooomm](https://github.com/tooomm) ([#2830](https://github.com/paperless-ngx/paperless-ngx/pull/2830))
|
||||||
|
- Feature: Add an option to disable matching [@bdr99](https://github.com/bdr99) ([#2727](https://github.com/paperless-ngx/paperless-ngx/pull/2727))
|
||||||
|
- Docs: Remove outdated PAPERLESS_WORKER_RETRY [@shamoon](https://github.com/shamoon) ([#2694](https://github.com/paperless-ngx/paperless-ngx/pull/2694))
|
||||||
|
- Fix: add missing i18n for mobile preview tab title [@nathanaelhoun](https://github.com/nathanaelhoun) ([#2692](https://github.com/paperless-ngx/paperless-ngx/pull/2692))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Configure ruff as the primary linter for Python [@stumpylog](https://github.com/stumpylog) ([#2988](https://github.com/paperless-ngx/paperless-ngx/pull/2988))
|
||||||
|
- Feature: Enable images to be released on Quay.io [@stumpylog](https://github.com/stumpylog) ([#2972](https://github.com/paperless-ngx/paperless-ngx/pull/2972))
|
||||||
|
- Chore: Updates locked pipenv to latest version [@stumpylog](https://github.com/stumpylog) ([#2943](https://github.com/paperless-ngx/paperless-ngx/pull/2943))
|
||||||
|
- Chore: Properly collapse section in releases [@tooomm](https://github.com/tooomm) ([#2838](https://github.com/paperless-ngx/paperless-ngx/pull/2838))
|
||||||
|
- Chore: Don't include changelog PR for different releases [@tooomm](https://github.com/tooomm) ([#2832](https://github.com/paperless-ngx/paperless-ngx/pull/2832))
|
||||||
|
- Chore: Speed up frontend CI testing [@stumpylog](https://github.com/stumpylog) ([#2796](https://github.com/paperless-ngx/paperless-ngx/pull/2796))
|
||||||
|
- Bump leonsteinhaeuser/project-beta-automations from 2.0.1 to 2.1.0 [@dependabot](https://github.com/dependabot) ([#2789](https://github.com/paperless-ngx/paperless-ngx/pull/2789))
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>15 changes</summary>
|
||||||
|
|
||||||
|
- Bump ng2-pdf-viewer from 9.1.4 to 9.1.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#3109](https://github.com/paperless-ngx/paperless-ngx/pull/3109))
|
||||||
|
- Grouped bump angular packages from 15.2.6 to 15.2.7 in /src-ui [@dependabot](https://github.com/dependabot) ([#3108](https://github.com/paperless-ngx/paperless-ngx/pull/3108))
|
||||||
|
- Bump typescript from 4.8.4 to 4.9.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#3071](https://github.com/paperless-ngx/paperless-ngx/pull/3071))
|
||||||
|
- Bulk Bump npm packages 04.23 [@dependabot](https://github.com/dependabot) ([#3068](https://github.com/paperless-ngx/paperless-ngx/pull/3068))
|
||||||
|
- Bump wait-on from 6.0.1 to 7.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#2990](https://github.com/paperless-ngx/paperless-ngx/pull/2990))
|
||||||
|
- Bulk bump angular packages to 15.2.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#2991](https://github.com/paperless-ngx/paperless-ngx/pull/2991))
|
||||||
|
- Bump [@<!---->types/node from 18.11.18 to 18.15.11 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.11.18 to 18.15.11 in /src-ui @dependabot) ([#2993](https://github.com/paperless-ngx/paperless-ngx/pull/2993))
|
||||||
|
- Bump [@<!---->ng-select/ng-select from 10.0.3 to 10.0.4 in /src-ui @dependabot](https://github.com/<!---->ng-select/ng-select from 10.0.3 to 10.0.4 in /src-ui @dependabot) ([#2992](https://github.com/paperless-ngx/paperless-ngx/pull/2992))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.50.0 to 5.57.0 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.50.0 to 5.57.0 in /src-ui @dependabot) ([#2989](https://github.com/paperless-ngx/paperless-ngx/pull/2989))
|
||||||
|
- Chore: Update cryptography to latest version [@stumpylog](https://github.com/stumpylog) ([#2891](https://github.com/paperless-ngx/paperless-ngx/pull/2891))
|
||||||
|
- Chore: Update to qpdf 11.3.0 in Docker image [@stumpylog](https://github.com/stumpylog) ([#2862](https://github.com/paperless-ngx/paperless-ngx/pull/2862))
|
||||||
|
- Bump leonsteinhaeuser/project-beta-automations from 2.0.1 to 2.1.0 [@dependabot](https://github.com/dependabot) ([#2789](https://github.com/paperless-ngx/paperless-ngx/pull/2789))
|
||||||
|
- Bump zone.js from 0.11.8 to 0.12.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#2793](https://github.com/paperless-ngx/paperless-ngx/pull/2793))
|
||||||
|
- Bump [@<!---->typescript-eslint/parser from 5.50.0 to 5.54.0 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.50.0 to 5.54.0 in /src-ui @dependabot) ([#2792](https://github.com/paperless-ngx/paperless-ngx/pull/2792))
|
||||||
|
- Bulk Bump angular packages to 15.2.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#2788](https://github.com/paperless-ngx/paperless-ngx/pull/2788))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>72 changes</summary>
|
||||||
|
|
||||||
|
- Feature: Catalan translation [@shamoon](https://github.com/shamoon) ([#3146](https://github.com/paperless-ngx/paperless-ngx/pull/3146))
|
||||||
|
- Fix: Allow setting additional Django settings for proxies [@stumpylog](https://github.com/stumpylog) ([#3135](https://github.com/paperless-ngx/paperless-ngx/pull/3135))
|
||||||
|
- Fix: Increase mail account password field length [@stumpylog](https://github.com/stumpylog) ([#3134](https://github.com/paperless-ngx/paperless-ngx/pull/3134))
|
||||||
|
- Fix: respect permissions for matching suggestions [@shamoon](https://github.com/shamoon) ([#3103](https://github.com/paperless-ngx/paperless-ngx/pull/3103))
|
||||||
|
- Bump ng2-pdf-viewer from 9.1.4 to 9.1.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#3109](https://github.com/paperless-ngx/paperless-ngx/pull/3109))
|
||||||
|
- Grouped bump angular packages from 15.2.6 to 15.2.7 in /src-ui [@dependabot](https://github.com/dependabot) ([#3108](https://github.com/paperless-ngx/paperless-ngx/pull/3108))
|
||||||
|
- Fix: update PaperlessTask on hard failures [@shamoon](https://github.com/shamoon) ([#3062](https://github.com/paperless-ngx/paperless-ngx/pull/3062))
|
||||||
|
- Bump typescript from 4.8.4 to 4.9.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#3071](https://github.com/paperless-ngx/paperless-ngx/pull/3071))
|
||||||
|
- Bulk Bump npm packages 04.23 [@dependabot](https://github.com/dependabot) ([#3068](https://github.com/paperless-ngx/paperless-ngx/pull/3068))
|
||||||
|
- Fix: Hide UI tour steps if user doesnt have permissions [@shamoon](https://github.com/shamoon) ([#3060](https://github.com/paperless-ngx/paperless-ngx/pull/3060))
|
||||||
|
- Fix: Hide Permissions tab if user cannot view users [@shamoon](https://github.com/shamoon) ([#3061](https://github.com/paperless-ngx/paperless-ngx/pull/3061))
|
||||||
|
- v1.14.0 delete document fixes [@shamoon](https://github.com/shamoon) ([#3020](https://github.com/paperless-ngx/paperless-ngx/pull/3020))
|
||||||
|
- Bump wait-on from 6.0.1 to 7.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#2990](https://github.com/paperless-ngx/paperless-ngx/pull/2990))
|
||||||
|
- Fix: inline plaintext docs to enforce styling [@shamoon](https://github.com/shamoon) ([#3013](https://github.com/paperless-ngx/paperless-ngx/pull/3013))
|
||||||
|
- Chore: Configure ruff as the primary linter for Python [@stumpylog](https://github.com/stumpylog) ([#2988](https://github.com/paperless-ngx/paperless-ngx/pull/2988))
|
||||||
|
- Bulk bump angular packages to 15.2.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#2991](https://github.com/paperless-ngx/paperless-ngx/pull/2991))
|
||||||
|
- Bump [@<!---->types/node from 18.11.18 to 18.15.11 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.11.18 to 18.15.11 in /src-ui @dependabot) ([#2993](https://github.com/paperless-ngx/paperless-ngx/pull/2993))
|
||||||
|
- Bump [@<!---->ng-select/ng-select from 10.0.3 to 10.0.4 in /src-ui @dependabot](https://github.com/<!---->ng-select/ng-select from 10.0.3 to 10.0.4 in /src-ui @dependabot) ([#2992](https://github.com/paperless-ngx/paperless-ngx/pull/2992))
|
||||||
|
- Bump [@<!---->typescript-eslint/eslint-plugin from 5.50.0 to 5.57.0 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/eslint-plugin from 5.50.0 to 5.57.0 in /src-ui @dependabot) ([#2989](https://github.com/paperless-ngx/paperless-ngx/pull/2989))
|
||||||
|
- Feature: Stronger typing for file consumption [@stumpylog](https://github.com/stumpylog) ([#2744](https://github.com/paperless-ngx/paperless-ngx/pull/2744))
|
||||||
|
- Fix: Use exclude instead of difference for mariadb [@shamoon](https://github.com/shamoon) ([#2983](https://github.com/paperless-ngx/paperless-ngx/pull/2983))
|
||||||
|
- Fix: permissions display should not show users with inherited permissions \& unable to change owner [@shamoon](https://github.com/shamoon) ([#2818](https://github.com/paperless-ngx/paperless-ngx/pull/2818))
|
||||||
|
- Feature: double-click docs [@shamoon](https://github.com/shamoon) ([#2966](https://github.com/paperless-ngx/paperless-ngx/pull/2966))
|
||||||
|
- feature: Add support for zxing as barcode scanning lib [@margau](https://github.com/margau) ([#2907](https://github.com/paperless-ngx/paperless-ngx/pull/2907))
|
||||||
|
- Feature: test mail account [@shamoon](https://github.com/shamoon) ([#2949](https://github.com/paperless-ngx/paperless-ngx/pull/2949))
|
||||||
|
- Feature: Capture celery and kombu logs to a file [@stumpylog](https://github.com/stumpylog) ([#2954](https://github.com/paperless-ngx/paperless-ngx/pull/2954))
|
||||||
|
- Fix: Resolve Redis connection issues with ACLs [@stumpylog](https://github.com/stumpylog) ([#2939](https://github.com/paperless-ngx/paperless-ngx/pull/2939))
|
||||||
|
- Feature: Allow mail account to use access tokens [@stumpylog](https://github.com/stumpylog) ([#2930](https://github.com/paperless-ngx/paperless-ngx/pull/2930))
|
||||||
|
- Fix: Consumer polling could overwhelm database [@stumpylog](https://github.com/stumpylog) ([#2922](https://github.com/paperless-ngx/paperless-ngx/pull/2922))
|
||||||
|
- Feature: Improved statistics widget [@shamoon](https://github.com/shamoon) ([#2910](https://github.com/paperless-ngx/paperless-ngx/pull/2910))
|
||||||
|
- Enhancement: rename comments to notes and improve notes UI [@shamoon](https://github.com/shamoon) ([#2904](https://github.com/paperless-ngx/paperless-ngx/pull/2904))
|
||||||
|
- Allow psql client certificate authentication [@Ongy](https://github.com/Ongy) ([#2899](https://github.com/paperless-ngx/paperless-ngx/pull/2899))
|
||||||
|
- Enhancement: support filtering multiple correspondents, doctypes \& storage paths [@shamoon](https://github.com/shamoon) ([#2893](https://github.com/paperless-ngx/paperless-ngx/pull/2893))
|
||||||
|
- Fix: frontend handle private tags, doctypes, correspondents [@shamoon](https://github.com/shamoon) ([#2839](https://github.com/paperless-ngx/paperless-ngx/pull/2839))
|
||||||
|
- Fix: Chrome struggles with commas [@stumpylog](https://github.com/stumpylog) ([#2892](https://github.com/paperless-ngx/paperless-ngx/pull/2892))
|
||||||
|
- Feature: Change celery serializer to pickle [@stumpylog](https://github.com/stumpylog) ([#2861](https://github.com/paperless-ngx/paperless-ngx/pull/2861))
|
||||||
|
- Feature: Allow naming to include owner and original name [@stumpylog](https://github.com/stumpylog) ([#2873](https://github.com/paperless-ngx/paperless-ngx/pull/2873))
|
||||||
|
- Feature: Allows filtering email by the TO value(s) as well [@stumpylog](https://github.com/stumpylog) ([#2871](https://github.com/paperless-ngx/paperless-ngx/pull/2871))
|
||||||
|
- Fix: logout on change password via frontend [@shamoon](https://github.com/shamoon) ([#2863](https://github.com/paperless-ngx/paperless-ngx/pull/2863))
|
||||||
|
- Fix: give superuser full doc perms [@shamoon](https://github.com/shamoon) ([#2820](https://github.com/paperless-ngx/paperless-ngx/pull/2820))
|
||||||
|
- Fix: Append Gmail labels instead of replacing [@stumpylog](https://github.com/stumpylog) ([#2860](https://github.com/paperless-ngx/paperless-ngx/pull/2860))
|
||||||
|
- Feature: owner-aware unique model name constraint [@shamoon](https://github.com/shamoon) ([#2827](https://github.com/paperless-ngx/paperless-ngx/pull/2827))
|
||||||
|
- Chore: Create list parsing utility for settings [@stumpylog](https://github.com/stumpylog) ([#2816](https://github.com/paperless-ngx/paperless-ngx/pull/2816))
|
||||||
|
- Fix: Ensure email date is made aware during action processing [@stumpylog](https://github.com/stumpylog) ([#2837](https://github.com/paperless-ngx/paperless-ngx/pull/2837))
|
||||||
|
- Chore: Convert more code to pathlib [@stumpylog](https://github.com/stumpylog) ([#2817](https://github.com/paperless-ngx/paperless-ngx/pull/2817))
|
||||||
|
- Fix: disable bulk edit dialog buttons during operation [@shamoon](https://github.com/shamoon) ([#2819](https://github.com/paperless-ngx/paperless-ngx/pull/2819))
|
||||||
|
- fix database locked error [@jonaswinkler](https://github.com/jonaswinkler) ([#2808](https://github.com/paperless-ngx/paperless-ngx/pull/2808))
|
||||||
|
- Fix: Disable suggestions for read-only docs [@shamoon](https://github.com/shamoon) ([#2813](https://github.com/paperless-ngx/paperless-ngx/pull/2813))
|
||||||
|
- update django.po messages [@jonaswinkler](https://github.com/jonaswinkler) ([#2806](https://github.com/paperless-ngx/paperless-ngx/pull/2806))
|
||||||
|
- Update processed mail migration [@shamoon](https://github.com/shamoon) ([#2804](https://github.com/paperless-ngx/paperless-ngx/pull/2804))
|
||||||
|
- Feature/2396 better mail actions [@jonaswinkler](https://github.com/jonaswinkler) ([#2718](https://github.com/paperless-ngx/paperless-ngx/pull/2718))
|
||||||
|
- Bump zone.js from 0.11.8 to 0.12.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#2793](https://github.com/paperless-ngx/paperless-ngx/pull/2793))
|
||||||
|
- Bump [@<!---->typescript-eslint/parser from 5.50.0 to 5.54.0 in /src-ui @dependabot](https://github.com/<!---->typescript-eslint/parser from 5.50.0 to 5.54.0 in /src-ui @dependabot) ([#2792](https://github.com/paperless-ngx/paperless-ngx/pull/2792))
|
||||||
|
- Bulk Bump angular packages to 15.2.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#2788](https://github.com/paperless-ngx/paperless-ngx/pull/2788))
|
||||||
|
- Fix: Ensure scratch directory exists before using [@stumpylog](https://github.com/stumpylog) ([#2775](https://github.com/paperless-ngx/paperless-ngx/pull/2775))
|
||||||
|
- Don't submit owner via API on document upload [@jonaswinkler](https://github.com/jonaswinkler) ([#2777](https://github.com/paperless-ngx/paperless-ngx/pull/2777))
|
||||||
|
- Feature: Reduce classifier memory usage somewhat during training [@stumpylog](https://github.com/stumpylog) ([#2733](https://github.com/paperless-ngx/paperless-ngx/pull/2733))
|
||||||
|
- Chore: Setup for mypy typing checks [@stumpylog](https://github.com/stumpylog) ([#2742](https://github.com/paperless-ngx/paperless-ngx/pull/2742))
|
||||||
|
- Feature: Add PAPERLESS_OCR_SKIP_ARCHIVE_FILE config setting [@bdr99](https://github.com/bdr99) ([#2743](https://github.com/paperless-ngx/paperless-ngx/pull/2743))
|
||||||
|
- Fix: only offer log files that exist [@shamoon](https://github.com/shamoon) ([#2739](https://github.com/paperless-ngx/paperless-ngx/pull/2739))
|
||||||
|
- Feature: dynamic document counts in dropdowns [@shamoon](https://github.com/shamoon) ([#2704](https://github.com/paperless-ngx/paperless-ngx/pull/2704))
|
||||||
|
- Fix: permissions editing and initial view issues [@shamoon](https://github.com/shamoon) ([#2717](https://github.com/paperless-ngx/paperless-ngx/pull/2717))
|
||||||
|
- Fix: reset saved view ID on quickFilter [@shamoon](https://github.com/shamoon) ([#2703](https://github.com/paperless-ngx/paperless-ngx/pull/2703))
|
||||||
|
- Feature: Add an option to disable matching [@bdr99](https://github.com/bdr99) ([#2727](https://github.com/paperless-ngx/paperless-ngx/pull/2727))
|
||||||
|
- Chore: Improve clarity of some test asserting [@stumpylog](https://github.com/stumpylog) ([#2714](https://github.com/paperless-ngx/paperless-ngx/pull/2714))
|
||||||
|
- Allow setting the ASN on document upload [@stumpylog](https://github.com/stumpylog) ([#2713](https://github.com/paperless-ngx/paperless-ngx/pull/2713))
|
||||||
|
- Fix: bulk edit reset apply button state [@shamoon](https://github.com/shamoon) ([#2701](https://github.com/paperless-ngx/paperless-ngx/pull/2701))
|
||||||
|
- Feature: Log failed login attempts [@shamoon](https://github.com/shamoon) ([#2359](https://github.com/paperless-ngx/paperless-ngx/pull/2359))
|
||||||
|
- Feature: Rename documents when storage path format changes [@stumpylog](https://github.com/stumpylog) ([#2696](https://github.com/paperless-ngx/paperless-ngx/pull/2696))
|
||||||
|
- Feature: update error message colors \& show on document failures [@shamoon](https://github.com/shamoon) ([#2689](https://github.com/paperless-ngx/paperless-ngx/pull/2689))
|
||||||
|
- Feature: multi-user permissions [@shamoon](https://github.com/shamoon) ([#2147](https://github.com/paperless-ngx/paperless-ngx/pull/2147))
|
||||||
|
- Fix: add missing i18n for mobile preview tab title [@nathanaelhoun](https://github.com/nathanaelhoun) ([#2692](https://github.com/paperless-ngx/paperless-ngx/pull/2692))
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## paperless-ngx 1.13.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Feature: allow disable warn on close saved view with changes [@shamoon](https://github.com/shamoon) ([#2681](https://github.com/paperless-ngx/paperless-ngx/pull/2681))
|
||||||
|
- Feature: Add option to enable response compression [@stumpylog](https://github.com/stumpylog) ([#2621](https://github.com/paperless-ngx/paperless-ngx/pull/2621))
|
||||||
|
- Feature: split documents on ASN barcode [@muued](https://github.com/muued) ([#2554](https://github.com/paperless-ngx/paperless-ngx/pull/2554))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: Ignore path filtering didn't handle sub directories [@stumpylog](https://github.com/stumpylog) ([#2674](https://github.com/paperless-ngx/paperless-ngx/pull/2674))
|
||||||
|
- Bugfix: Generation of secret key hangs during install script [@stumpylog](https://github.com/stumpylog) ([#2657](https://github.com/paperless-ngx/paperless-ngx/pull/2657))
|
||||||
|
- Fix: Remove files produced by barcode splitting when completed [@stumpylog](https://github.com/stumpylog) ([#2648](https://github.com/paperless-ngx/paperless-ngx/pull/2648))
|
||||||
|
- Fix: add missing storage path placeholders [@shamoon](https://github.com/shamoon) ([#2651](https://github.com/paperless-ngx/paperless-ngx/pull/2651))
|
||||||
|
- Fix long dropdown contents break document detail column view [@shamoon](https://github.com/shamoon) ([#2638](https://github.com/paperless-ngx/paperless-ngx/pull/2638))
|
||||||
|
- Fix: tags dropdown should stay closed when removing [@shamoon](https://github.com/shamoon) ([#2625](https://github.com/paperless-ngx/paperless-ngx/pull/2625))
|
||||||
|
- Bugfix: Configure scheduled tasks to expire after some time [@stumpylog](https://github.com/stumpylog) ([#2614](https://github.com/paperless-ngx/paperless-ngx/pull/2614))
|
||||||
|
- Bugfix: Limit management list pagination maxSize to 5 [@Kaaybi](https://github.com/Kaaybi) ([#2618](https://github.com/paperless-ngx/paperless-ngx/pull/2618))
|
||||||
|
- Fix: Don't crash on bad ASNs during indexing [@stumpylog](https://github.com/stumpylog) ([#2586](https://github.com/paperless-ngx/paperless-ngx/pull/2586))
|
||||||
|
- Fix: Prevent mktime OverflowError except in even more rare caes [@stumpylog](https://github.com/stumpylog) ([#2574](https://github.com/paperless-ngx/paperless-ngx/pull/2574))
|
||||||
|
- Bugfix: Whoosh relative date queries weren't handling timezones [@stumpylog](https://github.com/stumpylog) ([#2566](https://github.com/paperless-ngx/paperless-ngx/pull/2566))
|
||||||
|
- Fix importing files with non-ascii names [@Kexogg](https://github.com/Kexogg) ([#2555](https://github.com/paperless-ngx/paperless-ngx/pull/2555))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Chore: update recommended Gotenberg to 7.8, docs note possible incompatibility [@shamoon](https://github.com/shamoon) ([#2608](https://github.com/paperless-ngx/paperless-ngx/pull/2608))
|
||||||
|
- [Documentation] Add v1.12.2 changelog [@github-actions](https://github.com/github-actions) ([#2553](https://github.com/paperless-ngx/paperless-ngx/pull/2553))
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- Chore: Faster Docker image cleanup [@stumpylog](https://github.com/stumpylog) ([#2687](https://github.com/paperless-ngx/paperless-ngx/pull/2687))
|
||||||
|
- Chore: Remove duplicated folder [@stumpylog](https://github.com/stumpylog) ([#2561](https://github.com/paperless-ngx/paperless-ngx/pull/2561))
|
||||||
|
- Chore: Switch test coverage to Codecov [@stumpylog](https://github.com/stumpylog) ([#2582](https://github.com/paperless-ngx/paperless-ngx/pull/2582))
|
||||||
|
- Bump docker/build-push-action from 3 to 4 [@dependabot](https://github.com/dependabot) ([#2576](https://github.com/paperless-ngx/paperless-ngx/pull/2576))
|
||||||
|
- Chore: Run tests which require convert in the CI [@stumpylog](https://github.com/stumpylog) ([#2570](https://github.com/paperless-ngx/paperless-ngx/pull/2570))
|
||||||
|
|
||||||
|
- Feature: split documents on ASN barcode [@muued](https://github.com/muued) ([#2554](https://github.com/paperless-ngx/paperless-ngx/pull/2554))
|
||||||
|
- Bugfix: Whoosh relative date queries weren't handling timezones [@stumpylog](https://github.com/stumpylog) ([#2566](https://github.com/paperless-ngx/paperless-ngx/pull/2566))
|
||||||
|
- Fix importing files with non-ascii names [@Kexogg](https://github.com/Kexogg) ([#2555](https://github.com/paperless-ngx/paperless-ngx/pull/2555))
|
||||||
|
|
||||||
## paperless-ngx 1.12.2
|
## paperless-ngx 1.12.2
|
||||||
|
|
||||||
_Note: Version 1.12.x introduced searching of comments which will work for comments added after the upgrade but a reindex of the search index is required in order to be able to search
|
_Note: Version 1.12.x introduced searching of comments which will work for comments added after the upgrade but a reindex of the search index is required in order to be able to search
|
||||||
|
@@ -17,6 +17,8 @@ run paperless, these settings have to be defined in different places.
|
|||||||
|
|
||||||
## Required services
|
## Required services
|
||||||
|
|
||||||
|
### Redis Broker
|
||||||
|
|
||||||
`PAPERLESS_REDIS=<url>`
|
`PAPERLESS_REDIS=<url>`
|
||||||
|
|
||||||
: This is required for processing scheduled tasks such as email
|
: This is required for processing scheduled tasks such as email
|
||||||
@@ -33,6 +35,14 @@ matcher.
|
|||||||
|
|
||||||
Defaults to `redis://localhost:6379`.
|
Defaults to `redis://localhost:6379`.
|
||||||
|
|
||||||
|
`PAPERLESS_REDIS_PREFIX=<prefix>`
|
||||||
|
|
||||||
|
: Prefix to be used in Redis for keys and channels. Useful for sharing one Redis server among multiple Paperless instances.
|
||||||
|
|
||||||
|
Defaults to no prefix.
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
`PAPERLESS_DBENGINE=<engine_name>`
|
`PAPERLESS_DBENGINE=<engine_name>`
|
||||||
|
|
||||||
: Optional, gives the ability to choose Postgres or MariaDB for
|
: Optional, gives the ability to choose Postgres or MariaDB for
|
||||||
@@ -79,21 +89,108 @@ changed here.
|
|||||||
|
|
||||||
`PAPERLESS_DBSSLMODE=<mode>`
|
`PAPERLESS_DBSSLMODE=<mode>`
|
||||||
|
|
||||||
: SSL mode to use when connecting to PostgreSQL.
|
: SSL mode to use when connecting to PostgreSQL or MariaDB.
|
||||||
|
|
||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
|
|
||||||
Default is `prefer`.
|
See [the official documentation about
|
||||||
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-mode).
|
||||||
|
|
||||||
`PAPERLESS_DB_TIMEOUT=<float>`
|
*Note*: SSL mode values differ between PostgreSQL and MariaDB.
|
||||||
|
|
||||||
|
Default is `prefer` for PostgreSQL and `PREFERRED` for MariaDB.
|
||||||
|
|
||||||
|
`PAPERLESS_DBSSLROOTCERT=<ca-path>`
|
||||||
|
|
||||||
|
: SSL root certificate path
|
||||||
|
|
||||||
|
See [the official documentation about
|
||||||
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
|
Changes path of `root.crt`.
|
||||||
|
|
||||||
|
See [the official documentation about
|
||||||
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-ca).
|
||||||
|
|
||||||
|
Defaults to unset, using the documented path in the home directory.
|
||||||
|
|
||||||
|
`PAPERLESS_DBSSLCERT=<client-cert-path>`
|
||||||
|
|
||||||
|
: SSL client certificate path
|
||||||
|
|
||||||
|
See [the official documentation about
|
||||||
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
|
|
||||||
|
See [the official documentation about
|
||||||
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-cert).
|
||||||
|
|
||||||
|
Changes path of `postgresql.crt`.
|
||||||
|
|
||||||
|
Defaults to unset, using the documented path in the home directory.
|
||||||
|
|
||||||
|
`PAPERLESS_DBSSLKEY=<client-cert-key>`
|
||||||
|
|
||||||
|
: SSL client key path
|
||||||
|
|
||||||
|
See [the official documentation about
|
||||||
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
|
|
||||||
|
See [the official documentation about
|
||||||
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-key).
|
||||||
|
|
||||||
|
Changes path of `postgresql.key`.
|
||||||
|
|
||||||
|
Defaults to unset, using the documented path in the home directory.
|
||||||
|
|
||||||
|
`PAPERLESS_DB_TIMEOUT=<int>`
|
||||||
|
|
||||||
: Amount of time for a database connection to wait for the database to
|
: Amount of time for a database connection to wait for the database to
|
||||||
unlock. Mostly applicable for an sqlite based installation, consider
|
unlock. Mostly applicable for sqlite based installation. Consider changing
|
||||||
changing to postgresql if you need to increase this.
|
to postgresql if you are having concurrency problems with sqlite.
|
||||||
|
|
||||||
Defaults to unset, keeping the Django defaults.
|
Defaults to unset, keeping the Django defaults.
|
||||||
|
|
||||||
|
## Optional Services
|
||||||
|
|
||||||
|
### Tika {#tika}
|
||||||
|
|
||||||
|
Paperless can make use of [Tika](https://tika.apache.org/) and
|
||||||
|
[Gotenberg](https://gotenberg.dev/) for parsing and converting
|
||||||
|
"Office" documents (such as ".doc", ".xlsx" and ".odt").
|
||||||
|
Tika and Gotenberg are also needed to allow parsing of E-Mails (.eml).
|
||||||
|
|
||||||
|
If you wish to use this, you must provide a Tika server and a Gotenberg server,
|
||||||
|
configure their endpoints, and enable the feature.
|
||||||
|
|
||||||
|
`PAPERLESS_TIKA_ENABLED=<bool>`
|
||||||
|
|
||||||
|
: Enable (or disable) the Tika parser.
|
||||||
|
|
||||||
|
Defaults to false.
|
||||||
|
|
||||||
|
`PAPERLESS_TIKA_ENDPOINT=<url>`
|
||||||
|
|
||||||
|
: Set the endpoint URL were Paperless can reach your Tika server.
|
||||||
|
|
||||||
|
Defaults to "<http://localhost:9998>".
|
||||||
|
|
||||||
|
`PAPERLESS_TIKA_GOTENBERG_ENDPOINT=<url>`
|
||||||
|
|
||||||
|
: Set the endpoint URL were Paperless can reach your Gotenberg server.
|
||||||
|
|
||||||
|
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.sqlite-tika.yml`](https://github.com/paperless-ngx/paperless-ngx/blob/main/docker/compose/docker-compose.sqlite-tika.yml)
|
||||||
|
file for reference).
|
||||||
|
|
||||||
|
Add all three configuration parameters to your configuration. If using
|
||||||
|
Docker, this may be the `environment` key of the webserver or a
|
||||||
|
`docker-compose.env` file. Bare metal installations may have a `.conf` file
|
||||||
|
containing the configuration parameters. Be sure to use the correct format
|
||||||
|
and watch out for indentation if editing the YAML file.
|
||||||
|
|
||||||
## Paths and folders
|
## Paths and folders
|
||||||
|
|
||||||
`PAPERLESS_CONSUMPTION_DIR=<path>`
|
`PAPERLESS_CONSUMPTION_DIR=<path>`
|
||||||
@@ -227,8 +324,7 @@ not include a trailing slash. E.g. <https://paperless.domain.com>
|
|||||||
|
|
||||||
: A list of trusted origins for unsafe requests (e.g. POST). As of
|
: 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.
|
Django 4.0 this is required to access the Django admin via the web.
|
||||||
See
|
See the [Django project documentation on the settings](https://docs.djangoproject.com/en/4.1/ref/settings/#csrf-trusted-origins)
|
||||||
<https://docs.djangoproject.com/en/4.0/ref/settings/#csrf-trusted-origins>
|
|
||||||
|
|
||||||
Can also be set using PAPERLESS_URL (see above).
|
Can also be set using PAPERLESS_URL (see above).
|
||||||
|
|
||||||
@@ -239,8 +335,8 @@ See
|
|||||||
|
|
||||||
: If you're planning on putting Paperless on the open internet, then
|
: 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.
|
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:
|
Failing to do so leaves you open to HTTP host header attacks.
|
||||||
<https://docs.djangoproject.com/en/3.1/topics/security/#host-header-validation>
|
You can read more about this in [the Django project's documentation](https://docs.djangoproject.com/en/4.1/topics/security/#host-header-validation)
|
||||||
|
|
||||||
Just remember that this is a comma-separated list, so
|
Just remember that this is a comma-separated list, so
|
||||||
"example.com" is fine, as is "example.com,www.example.com", but
|
"example.com" is fine, as is "example.com,www.example.com", but
|
||||||
@@ -248,8 +344,7 @@ Failing to do so leaves you open to HTTP host header attacks:
|
|||||||
|
|
||||||
Can also be set using PAPERLESS_URL (see above).
|
Can also be set using PAPERLESS_URL (see above).
|
||||||
|
|
||||||
If manually set, please remember to include "localhost". Otherwise
|
"localhost" is always allowed for docker healthcheck
|
||||||
docker healthcheck will fail.
|
|
||||||
|
|
||||||
Defaults to "\*", which is all hosts.
|
Defaults to "\*", which is all hosts.
|
||||||
|
|
||||||
@@ -262,6 +357,14 @@ do CORS calls. Set this to your public domain name.
|
|||||||
|
|
||||||
Defaults to "<http://localhost:8000>".
|
Defaults to "<http://localhost:8000>".
|
||||||
|
|
||||||
|
`PAPERLESS_TRUSTED_PROXIES=<comma-separated-list>`
|
||||||
|
|
||||||
|
: This may be needed to prevent IP address spoofing if you are using e.g.
|
||||||
|
fail2ban with log entries for failed authorization attempts. Value should be
|
||||||
|
IP address(es).
|
||||||
|
|
||||||
|
Defaults to empty string.
|
||||||
|
|
||||||
`PAPERLESS_FORCE_SCRIPT_NAME=<path>`
|
`PAPERLESS_FORCE_SCRIPT_NAME=<path>`
|
||||||
|
|
||||||
: To host paperless under a subpath url like example.com/paperless you
|
: To host paperless under a subpath url like example.com/paperless you
|
||||||
@@ -348,16 +451,16 @@ applications.
|
|||||||
If you're exposing paperless to the internet directly, do not use
|
If you're exposing paperless to the internet directly, do not use
|
||||||
this.
|
this.
|
||||||
|
|
||||||
Also see the warning [in the official documentation](https://docs.djangoproject.com/en/3.1/howto/auth-remote-user/#configuration).
|
Also see the warning [in the official documentation](https://docs.djangoproject.com/en/4.1/howto/auth-remote-user/#configuration).
|
||||||
|
|
||||||
Defaults to "false" which disables this feature.
|
Defaults to "false" which disables this feature.
|
||||||
|
|
||||||
`PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=<str>`
|
`PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=<str>`
|
||||||
|
|
||||||
: If "PAPERLESS*ENABLE_HTTP_REMOTE_USER" is enabled, this
|
: If "PAPERLESS_ENABLE_HTTP_REMOTE_USER" is enabled, this
|
||||||
property allows to customize the name of the HTTP header from which
|
property allows to customize the name of the HTTP header from which
|
||||||
the authenticated username is extracted. Values are in terms of
|
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).
|
[HttpRequest.META](https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest.META).
|
||||||
Thus, the configured value must start with `HTTP*`
|
Thus, the configured value must start with `HTTP*`
|
||||||
followed by the normalized actual header name.
|
followed by the normalized actual header name.
|
||||||
|
|
||||||
@@ -371,6 +474,46 @@ redirect the user back to the SSO application's logout page.
|
|||||||
|
|
||||||
Defaults to None, which disables this feature.
|
Defaults to None, which disables this feature.
|
||||||
|
|
||||||
|
`PAPERLESS_USE_X_FORWARD_HOST=<bool>`
|
||||||
|
|
||||||
|
: Configures the Django setting [USE_X_FORWARDED_HOST](https://docs.djangoproject.com/en/4.2/ref/settings/#use-x-forwarded-host)
|
||||||
|
which may be needed for hosting behind a proxy.
|
||||||
|
|
||||||
|
Defaults to False
|
||||||
|
|
||||||
|
`PAPERLESS_USE_X_FORWARD_PORT=<bool>`
|
||||||
|
|
||||||
|
: Configures the Django setting [USE_X_FORWARDED_PORT](https://docs.djangoproject.com/en/4.2/ref/settings/#use-x-forwarded-port)
|
||||||
|
which may be needed for hosting behind a proxy.
|
||||||
|
|
||||||
|
Defaults to False
|
||||||
|
|
||||||
|
`PAPERLESS_PROXY_SSL_HEADER=<json-list>`
|
||||||
|
|
||||||
|
: Configures the Django setting [SECURE_PROXY_SSL_HEADER](https://docs.djangoproject.com/en/4.2/ref/settings/#secure-proxy-ssl-header)
|
||||||
|
which may be needed for hosting behind a proxy. The two values in the list will form the tuple of
|
||||||
|
HTTP header/value expected by Django, eg `'["HTTP_X_FORWARDED_PROTO", "https"]'`.
|
||||||
|
|
||||||
|
Defaults to None
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
Settings this value has security implications. Read the Django documentation
|
||||||
|
and be sure you understand its usage before setting it.
|
||||||
|
|
||||||
|
`PAPERLESS_EMAIL_CERTIFICATE_FILE=<path>`
|
||||||
|
|
||||||
|
: Configures an additional SSL certificate file containing a [certificate](https://docs.python.org/3/library/ssl.html#certificates)
|
||||||
|
or certificate chain which should be trusted for validating SSL connections against mail providers.
|
||||||
|
This is for use with self-signed certificates against local IMAP servers.
|
||||||
|
|
||||||
|
Defaults to None.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
Settings this value has security implications for the security of your email.
|
||||||
|
Understand what it does and be sure you need to before setting.
|
||||||
|
|
||||||
## OCR settings {#ocr}
|
## OCR settings {#ocr}
|
||||||
|
|
||||||
Paperless uses [OCRmyPDF](https://ocrmypdf.readthedocs.io/en/latest/)
|
Paperless uses [OCRmyPDF](https://ocrmypdf.readthedocs.io/en/latest/)
|
||||||
@@ -383,36 +526,29 @@ needs.
|
|||||||
: Customize the language that paperless will attempt to use when
|
: Customize the language that paperless will attempt to use when
|
||||||
parsing documents.
|
parsing documents.
|
||||||
|
|
||||||
It should be a 3-letter language code consistent with ISO 639:
|
It should be a 3-letter code, see the list of [languages Tesseract supports](https://tesseract-ocr.github.io/tessdoc/Data-Files-in-different-versions.html).
|
||||||
https://www.loc.gov/standards/iso639-2/php/code_list.php
|
|
||||||
|
|
||||||
Set this to the language most of your documents are written in.
|
Set this to the language most of your documents are written in.
|
||||||
|
|
||||||
This can be a combination of multiple languages such as `deu+eng`,
|
This can be a combination of multiple languages such as `deu+eng`,
|
||||||
in which case tesseract will use whatever language matches best.
|
in which case Tesseract will use whatever language matches best.
|
||||||
Keep in mind that tesseract uses much more cpu time with multiple
|
Keep in mind that Tesseract uses much more CPU time with multiple
|
||||||
languages enabled.
|
languages enabled.
|
||||||
|
|
||||||
Defaults to "eng".
|
Defaults to "eng".
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
If your language contains a '-' such as chi-sim, you must use chi_sim
|
If your language contains a '-' such as chi-sim, you must use `chi_sim`.
|
||||||
|
|
||||||
`PAPERLESS_OCR_MODE=<mode>`
|
`PAPERLESS_OCR_MODE=<mode>`
|
||||||
|
|
||||||
: Tell paperless when and how to perform ocr on your documents. Four
|
: Tell paperless when and how to perform ocr on your documents. Three
|
||||||
modes are available:
|
modes are available:
|
||||||
|
|
||||||
- `skip`: Paperless skips all pages and will perform ocr only on
|
- `skip`: Paperless skips all pages and will perform ocr only on
|
||||||
pages where no text is present. This is the safest option.
|
pages where no text is present. This is the safest option.
|
||||||
|
|
||||||
- `skip_noarchive`: In addition to skip, paperless won't create
|
|
||||||
an archived version of your documents when it finds any text in
|
|
||||||
them. This is useful if you don't want to have two
|
|
||||||
almost-identical versions of your digital documents in the media
|
|
||||||
folder. This is the fastest option.
|
|
||||||
|
|
||||||
- `redo`: Paperless will OCR all pages of your documents and
|
- `redo`: Paperless will OCR all pages of your documents and
|
||||||
attempt to replace any existing text layers with new text. This
|
attempt to replace any existing text layers with new text. This
|
||||||
will be useful for documents from scanners that already
|
will be useful for documents from scanners that already
|
||||||
@@ -435,6 +571,19 @@ modes are available:
|
|||||||
Read more about this in the [OCRmyPDF
|
Read more about this in the [OCRmyPDF
|
||||||
documentation](https://ocrmypdf.readthedocs.io/en/latest/advanced.html#when-ocr-is-skipped).
|
documentation](https://ocrmypdf.readthedocs.io/en/latest/advanced.html#when-ocr-is-skipped).
|
||||||
|
|
||||||
|
`PAPERLESS_OCR_SKIP_ARCHIVE_FILE=<mode>`
|
||||||
|
|
||||||
|
: Specify when you would like paperless to skip creating an archived
|
||||||
|
version of your documents. This is useful if you don't want to have two
|
||||||
|
almost-identical versions of your documents in the media folder.
|
||||||
|
|
||||||
|
- `never`: Never skip creating an archived version.
|
||||||
|
- `with_text`: Skip creating an archived version for documents
|
||||||
|
that already have embedded text.
|
||||||
|
- `always`: Always skip creating an archived version.
|
||||||
|
|
||||||
|
The default is `never`.
|
||||||
|
|
||||||
`PAPERLESS_OCR_CLEAN=<mode>`
|
`PAPERLESS_OCR_CLEAN=<mode>`
|
||||||
|
|
||||||
: Tells paperless to use `unpaper` to clean any input document before
|
: Tells paperless to use `unpaper` to clean any input document before
|
||||||
@@ -577,76 +726,6 @@ they use underscores instead of dashes.
|
|||||||
{"deskew": true, "optimize": 3, "unpaper_args": "--pre-rotate 90"}
|
{"deskew": true, "optimize": 3, "unpaper_args": "--pre-rotate 90"}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tika settings {#tika}
|
|
||||||
|
|
||||||
Paperless can make use of [Tika](https://tika.apache.org/) and
|
|
||||||
[Gotenberg](https://gotenberg.dev/) for parsing and converting
|
|
||||||
"Office" documents (such as ".doc", ".xlsx" and ".odt").
|
|
||||||
Tika and Gotenberg are also needed to allow parsing of E-Mails (.eml).
|
|
||||||
|
|
||||||
If you wish to use this, you must provide a Tika server and a Gotenberg server,
|
|
||||||
configure their endpoints, and enable the feature.
|
|
||||||
|
|
||||||
`PAPERLESS_TIKA_ENABLED=<bool>`
|
|
||||||
|
|
||||||
: Enable (or disable) the Tika parser.
|
|
||||||
|
|
||||||
Defaults to false.
|
|
||||||
|
|
||||||
`PAPERLESS_TIKA_ENDPOINT=<url>`
|
|
||||||
|
|
||||||
: Set the endpoint URL were Paperless can reach your Tika server.
|
|
||||||
|
|
||||||
Defaults to "<http://localhost:9998>".
|
|
||||||
|
|
||||||
`PAPERLESS_TIKA_GOTENBERG_ENDPOINT=<url>`
|
|
||||||
|
|
||||||
: Set the endpoint URL were Paperless can reach your Gotenberg server.
|
|
||||||
|
|
||||||
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.sqlite-tika.yml`
|
|
||||||
file for reference). The changes requires are as follows:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
services:
|
|
||||||
# ...
|
|
||||||
|
|
||||||
webserver:
|
|
||||||
# ...
|
|
||||||
|
|
||||||
environment:
|
|
||||||
# ...
|
|
||||||
|
|
||||||
PAPERLESS_TIKA_ENABLED: 1
|
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
|
||||||
|
|
||||||
# ...
|
|
||||||
|
|
||||||
gotenberg:
|
|
||||||
image: gotenberg/gotenberg:7.8
|
|
||||||
restart: unless-stopped
|
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
|
||||||
command:
|
|
||||||
- 'gotenberg'
|
|
||||||
- '--chromium-disable-javascript=true'
|
|
||||||
- '--chromium-allow-list=file:///tmp/.*'
|
|
||||||
|
|
||||||
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 {#software_tweaks}
|
## Software tweaks {#software_tweaks}
|
||||||
|
|
||||||
`PAPERLESS_TASK_WORKERS=<num>`
|
`PAPERLESS_TASK_WORKERS=<num>`
|
||||||
@@ -698,17 +777,10 @@ paperless will process in parallel on a single document.
|
|||||||
on large documents within the default 1800 seconds. So extending
|
on large documents within the default 1800 seconds. So extending
|
||||||
this timeout may prove to be useful on weak hardware setups.
|
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>`
|
`PAPERLESS_TIME_ZONE=<timezone>`
|
||||||
|
|
||||||
: Set the time zone here. See
|
: Set the time zone here. See more details on
|
||||||
<https://docs.djangoproject.com/en/3.1/ref/settings/#std:setting-TIME_ZONE>
|
why and how to set it [in the Django project documentation](https://docs.djangoproject.com/en/4.1/ref/settings/#std:setting-TIME_ZONE)
|
||||||
for details on how to set it.
|
for details on how to set it.
|
||||||
|
|
||||||
Defaults to UTC.
|
Defaults to UTC.
|
||||||
@@ -770,46 +842,33 @@ should be a valid crontab(5) expression describing when to run.
|
|||||||
to enable compression in your proxy configuration rather than
|
to enable compression in your proxy configuration rather than
|
||||||
the webserver
|
the webserver
|
||||||
|
|
||||||
## Polling {#polling}
|
`PAPERLESS_CONVERT_MEMORY_LIMIT=<num>`
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_POLLING=<num>`
|
: On smaller systems, or even in the case of Very Large Documents, the
|
||||||
|
consumer may explode, complaining about how it's "unable to extend
|
||||||
|
pixel cache". In such cases, try setting this to a reasonably low
|
||||||
|
value, like 32. The default is to use whatever is necessary to do
|
||||||
|
everything without writing to disk, and units are in megabytes.
|
||||||
|
|
||||||
: If paperless won't find documents added to your consume folder, it
|
For more information on how to use this value, you should search the
|
||||||
might not be able to automatically detect filesystem changes. In
|
web for "MAGICK_MEMORY_LIMIT".
|
||||||
that case, specify a polling interval in seconds here, which will
|
|
||||||
then cause paperless to periodically check your consumption
|
|
||||||
directory for changes. This will also disable listening for file
|
|
||||||
system changes with `inotify`.
|
|
||||||
|
|
||||||
Defaults to 0, which disables polling and uses filesystem
|
Defaults to 0, which disables the limit.
|
||||||
notifications.
|
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_POLLING_RETRY_COUNT=<num>`
|
`PAPERLESS_CONVERT_TMPDIR=<path>`
|
||||||
|
|
||||||
: If consumer polling is enabled, sets the number of times paperless
|
: Similar to the memory limit, if you've got a small system and your
|
||||||
will check for a file to remain unmodified.
|
OS mounts /tmp as tmpfs, you should set this to a path that's on a
|
||||||
|
physical disk, like /home/your_user/tmp or something. ImageMagick
|
||||||
|
will use this as scratch space when crunching through very large
|
||||||
|
documents.
|
||||||
|
|
||||||
Defaults to 5.
|
For more information on how to use this value, you should search the
|
||||||
|
web for "MAGICK_TMPDIR".
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_POLLING_DELAY=<num>`
|
Default is none, which disables the temporary directory.
|
||||||
|
|
||||||
: If consumer polling is enabled, sets the delay in seconds between
|
## Document Consumption {#consume_config}
|
||||||
each check (above) paperless will do while waiting for a file to
|
|
||||||
remain unmodified.
|
|
||||||
|
|
||||||
Defaults to 5.
|
|
||||||
|
|
||||||
## iNotify {#inotify}
|
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_INOTIFY_DELAY=<num>`
|
|
||||||
|
|
||||||
: Sets the time in seconds the consumer will wait for additional
|
|
||||||
events from inotify before the consumer will consider a file ready
|
|
||||||
and begin consumption. Certain scanners or network setups may
|
|
||||||
generate multiple events for a single file, leading to multiple
|
|
||||||
consumers working on the same file. Configure this to prevent that.
|
|
||||||
|
|
||||||
Defaults to 0.5 seconds.
|
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`
|
`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`
|
||||||
|
|
||||||
@@ -840,96 +899,51 @@ don't exist yet.
|
|||||||
|
|
||||||
Defaults to false.
|
Defaults to false.
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>`
|
`PAPERLESS_CONSUMER_IGNORE_PATTERNS=<json>`
|
||||||
|
|
||||||
: Enables the scanning and page separation based on detected barcodes.
|
: By default, paperless ignores certain files and folders in the
|
||||||
This allows for scanning and adding multiple documents per uploaded
|
consumption directory, such as system files created by the Mac OS
|
||||||
file, which are separated by one or multiple barcode pages.
|
or hidden folders some tools use to store data.
|
||||||
|
|
||||||
For ease of use, it is suggested to use a standardized separation
|
This can be adjusted by configuring a custom json array with
|
||||||
page, e.g. [here](https://www.alliancegroup.co.uk/patch-codes.htm).
|
patterns to exclude.
|
||||||
|
|
||||||
If no barcodes are detected in the uploaded file, no page separation
|
For example, `.DS_STORE/*` will ignore any files found in a folder
|
||||||
will happen.
|
named `.DS_STORE`, including `.DS_STORE/bar.pdf` and `foo/.DS_STORE/bar.pdf`
|
||||||
|
|
||||||
The original document will be removed and the separated pages will
|
A pattern like `._*` will ignore anything starting with `._`, including:
|
||||||
be saved as pdf.
|
`._foo.pdf` and `._bar/foo.pdf`
|
||||||
|
|
||||||
Defaults to false.
|
Defaults to
|
||||||
|
`[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini", "@eaDir/*"]`.
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT=<bool>`
|
`PAPERLESS_CONSUMER_BARCODE_SCANNER=<string>`
|
||||||
|
|
||||||
: Whether TIFF image files should be scanned for barcodes. This will
|
: Sets the barcode scanner used for barcode functionality.
|
||||||
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.
|
Currently, "PYZBAR" (the default) or "ZXING" might be selected.
|
||||||
|
If you have problems that your Barcodes/QR-Codes are not detected
|
||||||
|
(especially with bad scan quality and/or small codes), try the other one.
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT`
|
zxing is not available on all platforms.
|
||||||
|
|
||||||
: Defines the string to be detected as a separator barcode. If
|
`PAPERLESS_PRE_CONSUME_SCRIPT=<filename>`
|
||||||
paperless is used with the PATCH-T separator pages, users shouldn't
|
|
||||||
change this.
|
|
||||||
|
|
||||||
Defaults to "PATCHT"
|
: After some initial validation, Paperless can trigger an arbitrary
|
||||||
|
script if you like before beginning consumption. This script will be provided
|
||||||
|
data for it to work with via the environment.
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=<bool>`
|
For more information, take a look at [pre-consumption script](/advanced_usage#pre-consume-script).
|
||||||
|
|
||||||
: Enables the detection of barcodes in the scanned document and
|
The default is blank, which means nothing will be executed.
|
||||||
setting the ASN (archive serial number) if a properly formatted
|
|
||||||
barcode is detected.
|
|
||||||
|
|
||||||
The barcode must consist of a (configurable) prefix and the ASN
|
|
||||||
to be set, for instance `ASN00123`.
|
|
||||||
|
|
||||||
This option is compatible with barcode page separation, since
|
|
||||||
pages will be split up before reading the ASN.
|
|
||||||
|
|
||||||
If no ASN barcodes are detected in the uploaded file, no ASN will
|
|
||||||
be set. If a barcode with an already existing ASN is detected, no ASN
|
|
||||||
will be set either and a warning will be logged.
|
|
||||||
|
|
||||||
Defaults to false.
|
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_ASN_BARCODE_PREFIX=ASN`
|
|
||||||
|
|
||||||
: Defines the prefix that is used to identify a barcode as an ASN
|
|
||||||
barcode.
|
|
||||||
|
|
||||||
Defaults to "ASN"
|
|
||||||
|
|
||||||
`PAPERLESS_CONVERT_MEMORY_LIMIT=<num>`
|
|
||||||
|
|
||||||
: On smaller systems, or even in the case of Very Large Documents, the
|
|
||||||
consumer may explode, complaining about how it's "unable to extend
|
|
||||||
pixel cache". In such cases, try setting this to a reasonably low
|
|
||||||
value, like 32. The default is to use whatever is necessary to do
|
|
||||||
everything without writing to disk, and units are in megabytes.
|
|
||||||
|
|
||||||
For more information on how to use this value, you should search the
|
|
||||||
web for "MAGICK_MEMORY_LIMIT".
|
|
||||||
|
|
||||||
Defaults to 0, which disables the limit.
|
|
||||||
|
|
||||||
`PAPERLESS_CONVERT_TMPDIR=<path>`
|
|
||||||
|
|
||||||
: Similar to the memory limit, if you've got a small system and your
|
|
||||||
OS mounts /tmp as tmpfs, you should set this to a path that's on a
|
|
||||||
physical disk, like /home/your_user/tmp or something. ImageMagick
|
|
||||||
will use this as scratch space when crunching through very large
|
|
||||||
documents.
|
|
||||||
|
|
||||||
For more information on how to use this value, you should search the
|
|
||||||
web for "MAGICK_TMPDIR".
|
|
||||||
|
|
||||||
Default is none, which disables the temporary directory.
|
|
||||||
|
|
||||||
`PAPERLESS_POST_CONSUME_SCRIPT=<filename>`
|
`PAPERLESS_POST_CONSUME_SCRIPT=<filename>`
|
||||||
|
|
||||||
: After a document is consumed, Paperless can trigger an arbitrary
|
: After a document is consumed, Paperless can trigger an arbitrary
|
||||||
script if you like. This script will be passed a number of arguments
|
script if you like. This script will be provided
|
||||||
for you to work with. For more information, take a look at [Post-consumption script](/advanced_usage#post-consume-script).
|
data for it to work with via the environment.
|
||||||
|
|
||||||
|
For more information, take a look at [Post-consumption script](/advanced_usage#post-consume-script).
|
||||||
|
|
||||||
The default is blank, which means nothing will be executed.
|
The default is blank, which means nothing will be executed.
|
||||||
|
|
||||||
@@ -996,23 +1010,167 @@ within your documents.
|
|||||||
second, and year last order. Characters D, M, or Y can be shuffled
|
second, and year last order. Characters D, M, or Y can be shuffled
|
||||||
to meet the required order.
|
to meet the required order.
|
||||||
|
|
||||||
`PAPERLESS_CONSUMER_IGNORE_PATTERNS=<json>`
|
### Polling {#polling}
|
||||||
|
|
||||||
: By default, paperless ignores certain files and folders in the
|
`PAPERLESS_CONSUMER_POLLING=<num>`
|
||||||
consumption directory, such as system files created by the Mac OS
|
|
||||||
or hidden folders some tools use to store data.
|
|
||||||
|
|
||||||
This can be adjusted by configuring a custom json array with
|
: If paperless won't find documents added to your consume folder, it
|
||||||
patterns to exclude.
|
might not be able to automatically detect filesystem changes. In
|
||||||
|
that case, specify a polling interval in seconds here, which will
|
||||||
|
then cause paperless to periodically check your consumption
|
||||||
|
directory for changes. This will also disable listening for file
|
||||||
|
system changes with `inotify`.
|
||||||
|
|
||||||
For example, `.DS_STORE/*` will ignore any files found in a folder
|
Defaults to 0, which disables polling and uses filesystem
|
||||||
named `.DS_STORE`, including `.DS_STORE/bar.pdf` and `foo/.DS_STORE/bar.pdf`
|
notifications.
|
||||||
|
|
||||||
A pattern like `._*` will ignore anything starting with `._`, including:
|
`PAPERLESS_CONSUMER_POLLING_RETRY_COUNT=<num>`
|
||||||
`._foo.pdf` and `._bar/foo.pdf`
|
|
||||||
|
|
||||||
Defaults to
|
: If consumer polling is enabled, sets the number of times paperless
|
||||||
`[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini", "@eaDir/*"]`.
|
will check for a file to remain unmodified.
|
||||||
|
|
||||||
|
Defaults to 5.
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_POLLING_DELAY=<num>`
|
||||||
|
|
||||||
|
: If consumer polling is enabled, sets the delay in seconds between
|
||||||
|
each check (above) paperless will do while waiting for a file to
|
||||||
|
remain unmodified.
|
||||||
|
|
||||||
|
Defaults to 5.
|
||||||
|
|
||||||
|
### iNotify {#inotify}
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_INOTIFY_DELAY=<num>`
|
||||||
|
|
||||||
|
: Sets the time in seconds the consumer will wait for additional
|
||||||
|
events from inotify before the consumer will consider a file ready
|
||||||
|
and begin consumption. Certain scanners or network setups may
|
||||||
|
generate multiple events for a single file, leading to multiple
|
||||||
|
consumers working on the same file. Configure this to prevent that.
|
||||||
|
|
||||||
|
Defaults to 0.5 seconds.
|
||||||
|
|
||||||
|
## Barcodes {#barcodes}
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
See additional information in the [advanced usage documentation](/advanced_usage#barcodes)
|
||||||
|
|
||||||
|
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=<string>`
|
||||||
|
|
||||||
|
: 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_CONSUMER_ENABLE_ASN_BARCODE=<bool>`
|
||||||
|
|
||||||
|
: Enables the detection of barcodes in the scanned document and
|
||||||
|
setting the ASN (archive serial number) if a properly formatted
|
||||||
|
barcode is detected.
|
||||||
|
|
||||||
|
The barcode must consist of a (configurable) prefix and the ASN
|
||||||
|
to be set, for instance `ASN00123`.
|
||||||
|
|
||||||
|
This option is compatible with barcode page separation, since
|
||||||
|
pages will be split up before reading the ASN.
|
||||||
|
|
||||||
|
If no ASN barcodes are detected in the uploaded file, no ASN will
|
||||||
|
be set. If a barcode with an existing ASN is detected, the
|
||||||
|
document will not be consumed and an error logged.
|
||||||
|
|
||||||
|
Defaults to false.
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_ASN_BARCODE_PREFIX=<string>`
|
||||||
|
|
||||||
|
: Defines the prefix that is used to identify a barcode as an ASN
|
||||||
|
barcode.
|
||||||
|
|
||||||
|
Defaults to "ASN"
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_BARCODE_UPSCALE=<float>`
|
||||||
|
|
||||||
|
: Defines the upscale factor used in barcode detection.
|
||||||
|
Improves the detection of small barcodes, i.e. with a value of 1.5 by
|
||||||
|
upscaling the document beforce the detection process. Upscaling will
|
||||||
|
only take place if value is bigger than 1.0. Otherwise upscaling will
|
||||||
|
not be performed to save resources. Try using in combination with
|
||||||
|
PAPERLESS_CONSUMER_BARCODE_DPI set to a value higher than default.
|
||||||
|
|
||||||
|
Defaults to 0.0
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_BARCODE_DPI=<int>`
|
||||||
|
|
||||||
|
: During barcode detection every page from a PDF document needs
|
||||||
|
to be converted to an image. A dpi value can be specified in the
|
||||||
|
conversion process. Default is 300. If the detection of small barcodes
|
||||||
|
fails a bigger dpi value i.e. 600 can fix the issue. Try using in
|
||||||
|
combination with PAPERLESS_CONSUMER_BARCODE_UPSCALE bigger than 1.0.
|
||||||
|
|
||||||
|
Defaults to "300"
|
||||||
|
|
||||||
|
## Collate Double-Sided Documents {#collate}
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED=<bool>`
|
||||||
|
|
||||||
|
: Enables automatic collation of two single-sided scans into a double-sided
|
||||||
|
document.
|
||||||
|
|
||||||
|
This is useful if you have an automatic document feeder that only supports
|
||||||
|
single-sided scans, but you need to scan a double-sided document. If your
|
||||||
|
ADF supports double-sided scans natively, you do not need this feature.
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_RECURSIVE` must be enabled for this to work.
|
||||||
|
|
||||||
|
For more information, read the [corresponding section in the advanced
|
||||||
|
documentation](/advanced_usage#collate).
|
||||||
|
|
||||||
|
Defaults to false.
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_SUBDIR_NAME=<str>`
|
||||||
|
|
||||||
|
: The name of the subdirectory that the collate feature expects documents to
|
||||||
|
arrive.
|
||||||
|
|
||||||
|
This only has an effect if `PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED`
|
||||||
|
has been enabled. Note that Paperless will not automatically create the
|
||||||
|
directory.
|
||||||
|
|
||||||
|
Defaults to "double-sided".
|
||||||
|
|
||||||
|
`PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_TIFF_SUPPORT=<bool>`
|
||||||
|
: Whether TIFF image files should be supported when collating documents.
|
||||||
|
This will automatically convert any TIFF image(s) to pdfs for later
|
||||||
|
processing. This only has an effect if
|
||||||
|
`PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED` has been enabled.
|
||||||
|
|
||||||
|
Defaults to false.
|
||||||
|
|
||||||
## Binaries
|
## Binaries
|
||||||
|
|
||||||
@@ -1021,7 +1179,7 @@ find on your system when it starts up. Unless you've done something
|
|||||||
creative with their installation, you probably won't need to edit any
|
creative with their installation, you probably won't need to edit any
|
||||||
of these. However, if you've installed these programs somewhere where
|
of these. However, if you've installed these programs somewhere where
|
||||||
simply typing the name of the program doesn't automatically execute it
|
simply typing the name of the program doesn't automatically execute it
|
||||||
(ie. the program isn't in your \$PATH), then you'll need to specify
|
(ie. the program isn't in your $PATH), then you'll need to specify
|
||||||
the literal path for that program.
|
the literal path for that program.
|
||||||
|
|
||||||
`PAPERLESS_CONVERT_BINARY=<path>`
|
`PAPERLESS_CONVERT_BINARY=<path>`
|
||||||
@@ -1104,12 +1262,17 @@ actual group ID on the host system, which you can get by executing
|
|||||||
: Additional OCR languages to install. By default, paperless comes
|
: Additional OCR languages to install. By default, paperless comes
|
||||||
with English, German, Italian, Spanish and French. If your language
|
with English, German, Italian, Spanish and French. If your language
|
||||||
is not in this list, install additional languages with this
|
is not in this list, install additional languages with this
|
||||||
configuration option:
|
configuration option. You will need to [find the right LangCodes](https://tesseract-ocr.github.io/tessdoc/Data-Files-in-different-versions.html)
|
||||||
|
but note that [tesseract-ocr-\* package names](https://packages.debian.org/bullseye/graphics/)
|
||||||
|
do not always correspond with the language codes e.g. "chi_tra" should be
|
||||||
|
specified as "chi-tra".
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
PAPERLESS_OCR_LANGUAGES=tur ces
|
PAPERLESS_OCR_LANGUAGES=tur ces chi-tra
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Make sure it's a space separated list when using several values.
|
||||||
|
|
||||||
To actually use these languages, also set the default OCR language
|
To actually use these languages, also set the default OCR language
|
||||||
of paperless:
|
of paperless:
|
||||||
|
|
||||||
|
@@ -119,7 +119,9 @@ first-time setup.
|
|||||||
|
|
||||||
## Back end development
|
## Back end development
|
||||||
|
|
||||||
The back end is a [Django](https://www.djangoproject.com/) application. [PyCharm](https://www.jetbrains.com/de-de/pycharm/) as well as [Visual Studio Code](https://code.visualstudio.com) work well for development, but you can use whatever you want.
|
The back end is a [Django](https://www.djangoproject.com/) application.
|
||||||
|
[PyCharm](https://www.jetbrains.com/de-de/pycharm/) as well as [Visual Studio Code](https://code.visualstudio.com)
|
||||||
|
work well for development, but you can use whatever you want.
|
||||||
|
|
||||||
Configure the IDE to use the `src/`-folder as the base source folder.
|
Configure the IDE to use the `src/`-folder as the base source folder.
|
||||||
Configure the following launch configurations in your IDE:
|
Configure the following launch configurations in your IDE:
|
||||||
@@ -138,7 +140,10 @@ $ python3 manage.py runserver & \
|
|||||||
celery --app paperless worker -l DEBUG
|
celery --app paperless worker -l DEBUG
|
||||||
```
|
```
|
||||||
|
|
||||||
You might need the front end to test your back end code. This assumes that you have AngularJS installed on your system. Go to the [Front end development](#front-end-development) section for further details. To build the front end once use this commmand:
|
You might need the front end to test your back end code.
|
||||||
|
This assumes that you have AngularJS installed on your system.
|
||||||
|
Go to the [Front end development](#front-end-development) section for further details.
|
||||||
|
To build the front end once use this command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# src-ui/
|
# src-ui/
|
||||||
@@ -172,8 +177,7 @@ The front end is built using AngularJS. In order to get started, you need Node.j
|
|||||||
|
|
||||||
The following commands are all performed in the `src-ui`-directory. You will need a running back end (including an active session) to connect to the back end API. To spin it up refer to the commands under the section [above](#back-end-development).
|
The following commands are all performed in the `src-ui`-directory. You will need a running back end (including an active session) to connect to the back end API. To spin it up refer to the commands under the section [above](#back-end-development).
|
||||||
|
|
||||||
1. Install the Angular CLI. You might need sudo privileges
|
1. Install the Angular CLI. You might need sudo privileges to perform this command:
|
||||||
to perform this command:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install -g @angular/cli
|
$ npm install -g @angular/cli
|
||||||
@@ -181,7 +185,7 @@ The front end is built using AngularJS. In order to get started, you need Node.j
|
|||||||
|
|
||||||
2. Make sure that it's on your path.
|
2. Make sure that it's on your path.
|
||||||
|
|
||||||
3. Install all neccessary modules:
|
3. Install all necessary modules:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install
|
$ npm install
|
||||||
@@ -202,7 +206,7 @@ The front end is built using AngularJS. In order to get started, you need Node.j
|
|||||||
|
|
||||||
### Testing and code style
|
### Testing and code style
|
||||||
|
|
||||||
- The front end code (.ts, .html, .scss) use `prettier` for code
|
The front end code (.ts, .html, .scss) use `prettier` for code
|
||||||
formatting via the Git `pre-commit` hooks which run automatically on
|
formatting via the Git `pre-commit` hooks which run automatically on
|
||||||
commit. See [above](#code-formatting-with-pre-commit-hooks) for installation instructions. You can also run this via the CLI with a
|
commit. See [above](#code-formatting-with-pre-commit-hooks) for installation instructions. You can also run this via the CLI with a
|
||||||
command such as
|
command such as
|
||||||
@@ -211,22 +215,23 @@ The front end is built using AngularJS. In order to get started, you need Node.j
|
|||||||
$ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
|
$ git ls-files -- '*.ts' | xargs pre-commit run prettier --files
|
||||||
```
|
```
|
||||||
|
|
||||||
- Front end testing uses jest and cypress. There is currently a need
|
Front end testing uses Jest and Playwright. Unit tests and e2e tests,
|
||||||
for significantly more front end tests. Unit tests and e2e tests,
|
|
||||||
respectively, can be run non-interactively with:
|
respectively, can be run non-interactively with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ng test
|
$ ng test
|
||||||
$ npm run e2e:ci
|
$ npx playwright test
|
||||||
```
|
```
|
||||||
|
|
||||||
- Cypress also includes a UI which can be run with:
|
Playwright also includes a UI which can be run with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ./node_modules/.bin/cypress open
|
$ npx playwright test --ui
|
||||||
```
|
```
|
||||||
|
|
||||||
- In order to build the front end and serve it as part of Django, execute:
|
### Building the frontend
|
||||||
|
|
||||||
|
In order to build the front end and serve it as part of Django, execute:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ ng build --configuration production
|
$ ng build --configuration production
|
||||||
@@ -251,7 +256,7 @@ these parts have to be translated separately.
|
|||||||
- The translated strings need to be placed in the
|
- The translated strings need to be placed in the
|
||||||
`src-ui/src/locale/` folder.
|
`src-ui/src/locale/` folder.
|
||||||
- In order to extract added or changed strings from the source files,
|
- In order to extract added or changed strings from the source files,
|
||||||
call `ng xi18n --ivy`.
|
call `ng extract-i18n`.
|
||||||
|
|
||||||
Adding new languages requires adding the translated files in the
|
Adding new languages requires adding the translated files in the
|
||||||
`src-ui/src/locale/` folder and adjusting a couple files.
|
`src-ui/src/locale/` folder and adjusting a couple files.
|
||||||
@@ -369,13 +374,10 @@ If you want to build the documentation locally, this is how you do it:
|
|||||||
The docker image is primarily built by the GitHub actions workflow, but
|
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.
|
it can be faster when developing to build and tag an image locally.
|
||||||
|
|
||||||
To provide the build arguments automatically, build the image using the
|
Building the image works as with any image:
|
||||||
helper script `build-docker-image.sh`.
|
|
||||||
|
|
||||||
Building the docker image from source:
|
```
|
||||||
|
docker build --file Dockerfile --tag paperless:local --progress simple .
|
||||||
```bash
|
|
||||||
./build-docker-image.sh Dockerfile -t <your-tag>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending Paperless-ngx
|
## Extending Paperless-ngx
|
||||||
@@ -393,7 +395,7 @@ responsible for:
|
|||||||
- Retrieving the content from the original
|
- Retrieving the content from the original
|
||||||
- Creating a thumbnail
|
- Creating a thumbnail
|
||||||
- _optional:_ Retrieving a created date from the original
|
- _optional:_ Retrieving a created date from the original
|
||||||
- _optional:_ Creainge an archived document from the original
|
- _optional:_ Creating an archived document from the original
|
||||||
|
|
||||||
Custom parsers can be added to Paperless-ngx to support more file types. In
|
Custom parsers can be added to Paperless-ngx to support more file types. In
|
||||||
order to do that, you need to write the parser itself and announce its
|
order to do that, you need to write the parser itself and announce its
|
||||||
|
21
docs/faq.md
@@ -3,10 +3,11 @@
|
|||||||
## _What's the general plan for Paperless-ngx?_
|
## _What's the general plan for Paperless-ngx?_
|
||||||
|
|
||||||
**A:** While Paperless-ngx is already considered largely
|
**A:** While Paperless-ngx is already considered largely
|
||||||
"feature-complete" it is a community-driven project and development
|
"feature-complete", it is a community-driven project and development
|
||||||
will be guided in this way. New features can be submitted via GitHub
|
will be guided in this way. New features can be submitted via
|
||||||
discussions and "up-voted" by the community but this is not a
|
[GitHub discussions](https://github.com/paperless-ngx/paperless-ngx/discussions)
|
||||||
guarantee the feature will be implemented. This project will always be
|
and "up-voted" by the community, but this is not a
|
||||||
|
guarantee that the feature will be implemented. This project will always be
|
||||||
open to collaboration in the form of PRs, ideas etc.
|
open to collaboration in the form of PRs, ideas etc.
|
||||||
|
|
||||||
## _I'm using docker. Where are my documents?_
|
## _I'm using docker. Where are my documents?_
|
||||||
@@ -27,6 +28,12 @@ system. On Linux, chances are high that this location is
|
|||||||
files around manually. This folder is meant to be entirely managed by
|
files around manually. This folder is meant to be entirely managed by
|
||||||
docker and paperless.
|
docker and paperless.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Files consumed from the consumption directory are re-created inside
|
||||||
|
this media directory and are removed from the consumption directory
|
||||||
|
itself.
|
||||||
|
|
||||||
## Let's say I want to switch tools in a year. Can I easily move to other systems?
|
## 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.
|
**A:** Your documents are stored as plain files inside the media folder.
|
||||||
@@ -52,7 +59,7 @@ elsewhere. Here are a couple notes about that.
|
|||||||
WebP images are processed with OCR and converted into PDF documents.
|
WebP images are processed with OCR and converted into PDF documents.
|
||||||
- Plain text documents are supported as well and are added verbatim to
|
- Plain text documents are supported as well and are added verbatim to
|
||||||
paperless.
|
paperless.
|
||||||
- With the optional Tika integration enabled (see [Tika configuration](/configuration#tika),
|
- With the optional Tika integration enabled (see [Tika configuration](https://docs.paperless-ngx.com/configuration#tika)),
|
||||||
Paperless also supports various Office documents (.docx, .doc, odt,
|
Paperless also supports various Office documents (.docx, .doc, odt,
|
||||||
.ppt, .pptx, .odp, .xls, .xlsx, .ods).
|
.ppt, .pptx, .odp, .xls, .xlsx, .ods).
|
||||||
|
|
||||||
@@ -76,7 +83,7 @@ has to do much less work to serve the data.
|
|||||||
## _How do I install paperless-ngx on Raspberry Pi?_
|
## _How do I install paperless-ngx on Raspberry Pi?_
|
||||||
|
|
||||||
**A:** Docker images are available for armv7 and arm64 hardware, so just
|
**A:** Docker images are available for armv7 and arm64 hardware, so just
|
||||||
follow the docker-compose instructions. Apart from more required disk
|
follow the [docker-compose instructions](https://docs.paperless-ngx.com/setup/#installation). Apart from more required disk
|
||||||
space compared to a bare metal installation, docker comes with close to
|
space compared to a bare metal installation, docker comes with close to
|
||||||
zero overhead, even on Raspberry Pi.
|
zero overhead, even on Raspberry Pi.
|
||||||
|
|
||||||
@@ -103,7 +110,7 @@ see if it works.
|
|||||||
|
|
||||||
## _How do I proxy this with NGINX?_
|
## _How do I proxy this with NGINX?_
|
||||||
|
|
||||||
**A:** See [here](/setup#nginx).
|
**A:** See [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx).
|
||||||
|
|
||||||
## _How do I get WebSocket support with Apache mod_wsgi_?
|
## _How do I get WebSocket support with Apache mod_wsgi_?
|
||||||
|
|
||||||
|
@@ -33,6 +33,11 @@ steps described in [Docker setup](#docker_hub) automatically.
|
|||||||
$ bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
|
$ bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
macOS users will need to install e.g. [gnu-sed](https://formulae.brew.sh/formula/gnu-sed) with support
|
||||||
|
for running as `sed`.
|
||||||
|
|
||||||
### From GHCR / Docker Hub {#docker_hub}
|
### From GHCR / Docker Hub {#docker_hub}
|
||||||
|
|
||||||
1. Login with your user and create a folder in your home-directory to have a place for your
|
1. Login with your user and create a folder in your home-directory to have a place for your
|
||||||
@@ -43,7 +48,7 @@ steps described in [Docker setup](#docker_hub) automatically.
|
|||||||
```
|
```
|
||||||
|
|
||||||
2. Go to the [/docker/compose directory on the project
|
2. Go to the [/docker/compose directory on the project
|
||||||
page](https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose)
|
page](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
|
||||||
and download one of the `docker-compose.*.yml` files,
|
and download one of the `docker-compose.*.yml` files,
|
||||||
depending on which database backend you want to use. Rename this
|
depending on which database backend you want to use. Rename this
|
||||||
file to `docker-compose.yml`. If you want to enable
|
file to `docker-compose.yml`. If you want to enable
|
||||||
@@ -160,8 +165,7 @@ steps described in [Docker setup](#docker_hub) automatically.
|
|||||||
`PAPERLESS_CONSUMER_POLLING`, which will disable inotify. See
|
`PAPERLESS_CONSUMER_POLLING`, which will disable inotify. See
|
||||||
[here](/configuration#polling).
|
[here](/configuration#polling).
|
||||||
|
|
||||||
6. Run `docker-compose pull`, followed by `docker-compose up -d`. This
|
6. Run `docker-compose pull`. This will pull the image.
|
||||||
will pull the image, create and start the necessary containers.
|
|
||||||
|
|
||||||
7. To be able to login, you will need a super user. To create it,
|
7. To be able to login, you will need a super user. To create it,
|
||||||
execute the following command:
|
execute the following command:
|
||||||
@@ -170,10 +174,18 @@ steps described in [Docker setup](#docker_hub) automatically.
|
|||||||
$ docker-compose run --rm webserver createsuperuser
|
$ docker-compose run --rm webserver createsuperuser
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or using docker exec from within the container:
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ python3 manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
This will prompt you to set a username, an optional e-mail address
|
This will prompt you to set a username, an optional e-mail address
|
||||||
and finally a password (at least 8 characters).
|
and finally a password (at least 8 characters).
|
||||||
|
|
||||||
8. The default `docker-compose.yml` exports the webserver on your local
|
8. Run `docker-compose up -d`. This will create and start the necessary containers.
|
||||||
|
|
||||||
|
9. The default `docker-compose.yml` exports the webserver on your local
|
||||||
port
|
port
|
||||||
|
|
||||||
8000\. If you did not change this, you should now be able to visit
|
8000\. If you did not change this, you should now be able to visit
|
||||||
@@ -189,7 +201,7 @@ steps described in [Docker setup](#docker_hub) automatically.
|
|||||||
git clone https://github.com/paperless-ngx/paperless-ngx
|
git clone https://github.com/paperless-ngx/paperless-ngx
|
||||||
```
|
```
|
||||||
|
|
||||||
The master branch always reflects the latest stable version.
|
The main branch always reflects the latest stable version.
|
||||||
|
|
||||||
2. Copy one of the `docker/compose/docker-compose.*.yml` to
|
2. Copy one of the `docker/compose/docker-compose.*.yml` to
|
||||||
`docker-compose.yml` in the root folder, depending on which database
|
`docker-compose.yml` in the root folder, depending on which database
|
||||||
@@ -247,6 +259,7 @@ supported.
|
|||||||
- `python3-pip`
|
- `python3-pip`
|
||||||
- `python3-dev`
|
- `python3-dev`
|
||||||
- `default-libmysqlclient-dev` for MariaDB
|
- `default-libmysqlclient-dev` for MariaDB
|
||||||
|
- `pkg-config` for mysqlclient (python dependency)
|
||||||
- `fonts-liberation` for generating thumbnails for plain text
|
- `fonts-liberation` for generating thumbnails for plain text
|
||||||
files
|
files
|
||||||
- `imagemagick` >= 6 for PDF conversion
|
- `imagemagick` >= 6 for PDF conversion
|
||||||
@@ -261,7 +274,7 @@ supported.
|
|||||||
Use this list for your preferred package management:
|
Use this list for your preferred package management:
|
||||||
|
|
||||||
```
|
```
|
||||||
python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev libmagic-dev mime-support libzbar0 poppler-utils
|
python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev pkg-config libmagic-dev mime-support libzbar0 poppler-utils
|
||||||
```
|
```
|
||||||
|
|
||||||
These dependencies are required for OCRmyPDF, which is used for text
|
These dependencies are required for OCRmyPDF, which is used for text
|
||||||
@@ -365,6 +378,10 @@ supported.
|
|||||||
documents are written in.
|
documents are written in.
|
||||||
- Set `PAPERLESS_TIME_ZONE` to your local time zone.
|
- Set `PAPERLESS_TIME_ZONE` to your local time zone.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
Ensure your Redis instance [is secured](https://redis.io/docs/getting-started/#securing-redis).
|
||||||
|
|
||||||
7. Create the following directories if they are missing:
|
7. Create the following directories if they are missing:
|
||||||
|
|
||||||
- `/opt/paperless/media`
|
- `/opt/paperless/media`
|
||||||
@@ -467,7 +484,7 @@ supported.
|
|||||||
in front of gunicorn instead.
|
in front of gunicorn instead.
|
||||||
|
|
||||||
For instructions on how to use nginx for that,
|
For instructions on how to use nginx for that,
|
||||||
[see the instructions below](/setup#nginx).
|
[see the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx).
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
|
|
||||||
@@ -543,7 +560,7 @@ Users who installed with the bare-metal route should also update their
|
|||||||
Git clone to point to `https://github.com/paperless-ngx/paperless-ngx`,
|
Git clone to point to `https://github.com/paperless-ngx/paperless-ngx`,
|
||||||
e.g. using the command
|
e.g. using the command
|
||||||
`git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`
|
`git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`
|
||||||
and then pull the lastest version.
|
and then pull the latest version.
|
||||||
|
|
||||||
## Migrating from Paperless
|
## Migrating from Paperless
|
||||||
|
|
||||||
@@ -585,7 +602,7 @@ Migration to paperless-ngx is then performed in a few simple steps:
|
|||||||
|
|
||||||
3. Download the latest release of paperless-ngx. You can either go with
|
3. Download the latest release of paperless-ngx. You can either go with
|
||||||
the docker-compose files from
|
the docker-compose files from
|
||||||
[here](https://github.com/paperless-ngx/paperless-ngx/tree/master/docker/compose)
|
[here](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
|
||||||
or clone the repository to build the image yourself (see
|
or clone the repository to build the image yourself (see
|
||||||
[above](#docker_build)). You can
|
[above](#docker_build)). You can
|
||||||
either replace your current paperless folder or put paperless-ngx in
|
either replace your current paperless folder or put paperless-ngx in
|
||||||
@@ -818,14 +835,14 @@ performance immensely:
|
|||||||
other tasks).
|
other tasks).
|
||||||
- Keep `PAPERLESS_OCR_MODE` at its default value `skip` and consider
|
- Keep `PAPERLESS_OCR_MODE` at its default value `skip` and consider
|
||||||
OCR'ing your documents before feeding them into paperless. Some
|
OCR'ing your documents before feeding them into paperless. Some
|
||||||
scanners are able to do this! You might want to even specify
|
scanners are able to do this!
|
||||||
`skip_noarchive` to skip archive file generation for already ocr'ed
|
- Set `PAPERLESS_OCR_SKIP_ARCHIVE_FILE` to `with_text` to skip archive
|
||||||
documents entirely.
|
file generation for already ocr'ed documents, or `always` to skip it
|
||||||
|
for all documents.
|
||||||
- If you want to perform OCR on the device, consider using
|
- If you want to perform OCR on the device, consider using
|
||||||
`PAPERLESS_OCR_CLEAN=none`. This will speed up OCR times and use
|
`PAPERLESS_OCR_CLEAN=none`. This will speed up OCR times and use
|
||||||
less memory at the expense of slightly worse OCR results.
|
less memory at the expense of slightly worse OCR results.
|
||||||
- If using docker, consider setting `PAPERLESS_WEBSERVER_WORKERS` to
|
- If using docker, consider setting `PAPERLESS_WEBSERVER_WORKERS` to 1. This will save some memory.
|
||||||
1. This will save some memory.
|
|
||||||
- Consider setting `PAPERLESS_ENABLE_NLTK` to false, to disable the
|
- Consider setting `PAPERLESS_ENABLE_NLTK` to false, to disable the
|
||||||
more advanced language processing, which can take more memory and
|
more advanced language processing, which can take more memory and
|
||||||
processing time.
|
processing time.
|
||||||
@@ -846,45 +863,8 @@ For details, refer to [configuration](/configuration).
|
|||||||
|
|
||||||
# Using nginx as a reverse proxy {#nginx}
|
# Using nginx as a reverse proxy {#nginx}
|
||||||
|
|
||||||
If you want to expose paperless to the internet, you should hide it
|
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx) for user-maintained documentation of using nginx with Paperless-ngx.
|
||||||
behind a reverse proxy with SSL enabled.
|
|
||||||
|
|
||||||
In addition to the usual configuration for SSL, the following
|
# Enhancing security {#security}
|
||||||
configuration is required for paperless to operate:
|
|
||||||
|
|
||||||
```nginx
|
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-Security-Tools-with-Paperless-ngx) for user-maintained documentation of how to configure security tools like Fail2ban with Paperless-ngx.
|
||||||
http {
|
|
||||||
|
|
||||||
# Adjust as required. This is the maximum size for file uploads.
|
|
||||||
# The default value 1M might be a little too small.
|
|
||||||
client_max_body_size 10M;
|
|
||||||
|
|
||||||
server {
|
|
||||||
|
|
||||||
location / {
|
|
||||||
|
|
||||||
# Adjust host and port as required.
|
|
||||||
proxy_pass http://localhost:8000/;
|
|
||||||
|
|
||||||
# These configuration options are required for WebSockets to work.
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
|
|
||||||
proxy_redirect off;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Host $server_name;
|
|
||||||
add_header P3P 'CP=""'; # may not be required in all setups
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `PAPERLESS_URL` configuration variable is also required when using a
|
|
||||||
reverse proxy. Please refer to the [hosting and security](/configuration#hosting-and-security) docs.
|
|
||||||
|
|
||||||
Also read
|
|
||||||
[this](https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu),
|
|
||||||
towards the end of the section.
|
|
||||||
|
@@ -332,3 +332,24 @@ change the port gunicorn listens on.
|
|||||||
|
|
||||||
To fix this, set `PAPERLESS_PORT` again to your desired port, or the
|
To fix this, set `PAPERLESS_PORT` again to your desired port, or the
|
||||||
default of 8000.
|
default of 8000.
|
||||||
|
|
||||||
|
## Database Warns about unique constraint "documents_tag_name_uniq
|
||||||
|
|
||||||
|
You may see database log lines like:
|
||||||
|
|
||||||
|
```
|
||||||
|
ERROR: duplicate key value violates unique constraint "documents_tag_name_uniq"
|
||||||
|
DETAIL: Key (name)=(NameF) already exists.
|
||||||
|
STATEMENT: INSERT INTO "documents_tag" ("owner_id", "name", "match", "matching_algorithm", "is_insensitive", "color", "is_inbox_tag") VALUES (NULL, 'NameF', '', 1, true, '#a6cee3', false) RETURNING "documents_tag"."id"
|
||||||
|
```
|
||||||
|
|
||||||
|
This can happen during heavy consumption when using polling. Paperless will handle it correctly and the file
|
||||||
|
will still be consumed
|
||||||
|
|
||||||
|
## Consumption fails with "Ghostscript PDF/A rendering failed"
|
||||||
|
|
||||||
|
Newer versions of OCRmyPDF will fail if it encounters errors during processing.
|
||||||
|
This is intentional as the output archive file may differ in unexpected or undesired
|
||||||
|
ways from the original. As the logs indicate, if you encounter this error you can set
|
||||||
|
`PAPERLESS_OCR_USER_ARGS: '{"continue_on_soft_render_error": true}'` to try to 'force'
|
||||||
|
processing documents with this issue.
|
||||||
|
@@ -60,8 +60,8 @@ following operations on your documents:
|
|||||||
|
|
||||||
This process can be configured to fit your needs. If you don't want
|
This process can be configured to fit your needs. If you don't want
|
||||||
paperless to create archived versions for digital documents, you can
|
paperless to create archived versions for digital documents, you can
|
||||||
configure that by configuring `PAPERLESS_OCR_MODE=skip_noarchive`.
|
configure that by configuring
|
||||||
Please read the
|
`PAPERLESS_OCR_SKIP_ARCHIVE_FILE=with_text`. Please read the
|
||||||
[relevant section in the documentation](/configuration#ocr).
|
[relevant section in the documentation](/configuration#ocr).
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
@@ -69,7 +69,9 @@ following operations on your documents:
|
|||||||
No matter which options you choose, Paperless will always store the
|
No matter which options you choose, Paperless will always store the
|
||||||
original document that it found in the consumption directory or in the
|
original document that it found in the consumption directory or in the
|
||||||
mail and will never overwrite that document. Archived versions are
|
mail and will never overwrite that document. Archived versions are
|
||||||
stored alongside the original versions.
|
stored alongside the original versions. Any files found in the
|
||||||
|
consumption directory will stored inside the Paperless-ngx file
|
||||||
|
structure and will not be retained in the consumption directory.
|
||||||
|
|
||||||
### The consumption directory
|
### The consumption directory
|
||||||
|
|
||||||
@@ -77,7 +79,9 @@ The primary method of getting documents into your database is by putting
|
|||||||
them in the consumption directory. The consumer waits patiently, looking
|
them in the consumption directory. The consumer waits patiently, looking
|
||||||
for new additions to this directory. When it finds them,
|
for new additions to this directory. When it finds them,
|
||||||
the consumer goes about the process of parsing them with the OCR,
|
the consumer goes about the process of parsing them with the OCR,
|
||||||
indexing what it finds, and storing it in the media directory.
|
indexing what it finds, and storing it in the media directory. You should
|
||||||
|
think of this folder as a temporary location, as files will be re-created
|
||||||
|
inside Paperless-ngx and removed from the consumption folder.
|
||||||
|
|
||||||
Getting stuff into this directory is up to you. If you're running
|
Getting stuff into this directory is up to you. If you're running
|
||||||
Paperless on your local computer, you might just want to drag and drop
|
Paperless on your local computer, you might just want to drag and drop
|
||||||
@@ -88,6 +92,15 @@ Typically, you're looking at an FTP server like
|
|||||||
[Proftpd](http://www.proftpd.org/) or a Windows folder share with
|
[Proftpd](http://www.proftpd.org/) or a Windows folder share with
|
||||||
[Samba](https://www.samba.org/).
|
[Samba](https://www.samba.org/).
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
|
||||||
|
Files found in the consumption directory that are consumed will be
|
||||||
|
removed from the consumption directory and stored inside the
|
||||||
|
Paperless-ngx file structure using any settings / storage paths
|
||||||
|
you have specified. This action is performed as safely as possible
|
||||||
|
but this means it is expected that files in the consumption
|
||||||
|
directory will no longer exist (there) after being consumed.
|
||||||
|
|
||||||
### Web UI Upload
|
### Web UI Upload
|
||||||
|
|
||||||
The dashboard has a file drop field to upload documents to paperless.
|
The dashboard has a file drop field to upload documents to paperless.
|
||||||
@@ -202,6 +215,45 @@ configured via `PAPERLESS_EMAIL_TASK_CRON` (see [software tweaks](/configuration
|
|||||||
You can also submit a document using the REST API, see [POSTing documents](/api#file-uploads)
|
You can also submit a document using the REST API, see [POSTing documents](/api#file-uploads)
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
As of version 1.14.0 Paperless-ngx added core support for user / group permissions. Permissions is
|
||||||
|
based around 'global' permissions as well as 'object-level' permissions. Global permissions designate
|
||||||
|
which parts of the application a user can access (e.g. Documents, Tags, Settings) and object-level
|
||||||
|
determine which objects are visible or editable. All objects have an 'owner' and 'view' and 'edit'
|
||||||
|
permissions which can be granted to other users or groups. The paperless-ngx permissions system uses
|
||||||
|
the built-in user model of the backend framework, Django.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Object-level permissions only apply to the object itself. In other words, setting permissions
|
||||||
|
for a Tag will _not_ affect the permissions of documents that have the Tag.
|
||||||
|
|
||||||
|
Permissions can be set using the new "Permissions" tab when editing documents, or bulk-applied
|
||||||
|
in the UI by selecting documents and choosing the "Permissions" button. Owner can also optionally
|
||||||
|
be set for documents uploaded via the API. Documents consumed via the consumption dir currently
|
||||||
|
do not have an owner set.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
After migration to version 1.14.0 all existing documents, tags etc. will have no explicit owner
|
||||||
|
set which means they will be visible / editable by all users. Once an object has an owner set,
|
||||||
|
only the owner can explicitly grant / revoke permissions.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
When first migrating to permissions it is recommended to use a 'superuser' account (which
|
||||||
|
would usually have been setup during installation) to ensure you have full permissions.
|
||||||
|
|
||||||
|
Note that superusers have access to all objects.
|
||||||
|
|
||||||
|
### Users and Groups
|
||||||
|
|
||||||
|
Paperless-ngx versions after 1.14.0 allow creating and editing users and groups via the 'frontend' UI.
|
||||||
|
These can be found under Settings > Users & Groups, assuming the user has access. If a user is designated
|
||||||
|
as a member of a group those permissions will be inherited and this is reflected in the UI. Explicit
|
||||||
|
permissions can be granted to limit access to certain parts of the UI (and corresponding API endpoints).
|
||||||
|
|
||||||
## Best practices {#basic-searching}
|
## Best practices {#basic-searching}
|
||||||
|
|
||||||
Paperless offers a couple tools that help you organize your document
|
Paperless offers a couple tools that help you organize your document
|
||||||
|
@@ -30,7 +30,9 @@ def worker_int(worker):
|
|||||||
worker.log.info("worker received INT or QUIT signal")
|
worker.log.info("worker received INT or QUIT signal")
|
||||||
|
|
||||||
## get traceback info
|
## get traceback info
|
||||||
import threading, sys, traceback
|
import sys
|
||||||
|
import threading
|
||||||
|
import traceback
|
||||||
|
|
||||||
id2name = {th.ident: th.name for th in threading.enumerate()}
|
id2name = {th.ident: th.name for th in threading.enumerate()}
|
||||||
code = []
|
code = []
|
||||||
|
@@ -72,7 +72,7 @@ fi
|
|||||||
if ! docker stats --no-stream &> /dev/null ; then
|
if ! docker stats --no-stream &> /dev/null ; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "WARN: It look like the current user does not have Docker permissions."
|
echo "WARN: It look like the current user does not have Docker permissions."
|
||||||
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user."
|
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting shell)."
|
||||||
echo ""
|
echo ""
|
||||||
sleep 3
|
sleep 3
|
||||||
fi
|
fi
|
||||||
@@ -384,6 +384,14 @@ fi
|
|||||||
|
|
||||||
${DOCKER_COMPOSE_CMD} pull
|
${DOCKER_COMPOSE_CMD} pull
|
||||||
|
|
||||||
|
if [ "$DATABASE_BACKEND" == "postgres" ] || [ "$DATABASE_BACKEND" == "mariadb" ] ; then
|
||||||
|
echo "Starting DB first for initilzation"
|
||||||
|
${DOCKER_COMPOSE_CMD} up --detach db
|
||||||
|
# hopefully enough time for even the slower systems
|
||||||
|
sleep 15
|
||||||
|
${DOCKER_COMPOSE_CMD} stop
|
||||||
|
fi
|
||||||
|
|
||||||
${DOCKER_COMPOSE_CMD} run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
|
${DOCKER_COMPOSE_CMD} run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
|
||||||
|
|
||||||
${DOCKER_COMPOSE_CMD} up --detach
|
${DOCKER_COMPOSE_CMD} up --detach
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#PAPERLESS_OCR_LANGUAGE=eng
|
#PAPERLESS_OCR_LANGUAGE=eng
|
||||||
#PAPERLESS_OCR_MODE=skip
|
#PAPERLESS_OCR_MODE=skip
|
||||||
|
#PAPERLESS_OCR_SKIP_ARCHIVE_FILE=never
|
||||||
#PAPERLESS_OCR_OUTPUT_TYPE=pdfa
|
#PAPERLESS_OCR_OUTPUT_TYPE=pdfa
|
||||||
#PAPERLESS_OCR_PAGES=1
|
#PAPERLESS_OCR_PAGES=1
|
||||||
#PAPERLESS_OCR_IMAGE_DPI=300
|
#PAPERLESS_OCR_IMAGE_DPI=300
|
||||||
@@ -65,6 +66,11 @@
|
|||||||
#PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=false
|
#PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=false
|
||||||
#PAPERLESS_CONSUMER_ENABLE_BARCODES=false
|
#PAPERLESS_CONSUMER_ENABLE_BARCODES=false
|
||||||
#PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT
|
#PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT
|
||||||
|
#PAPERLESS_CONSUMER_BARCODE_UPSCALE=0.0
|
||||||
|
#PAPERLESS_CONSUMER_BARCODE_DPI=300
|
||||||
|
#PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED=false
|
||||||
|
#PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_SUBDIR_NAME=double-sided
|
||||||
|
#PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_TIFF_SUPPORT=false
|
||||||
#PAPERLESS_PRE_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
|
#PAPERLESS_PRE_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
|
||||||
#PAPERLESS_POST_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
|
#PAPERLESS_POST_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
|
||||||
#PAPERLESS_FILENAME_DATE_ORDER=YMD
|
#PAPERLESS_FILENAME_DATE_ORDER=YMD
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
|
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:15
|
||||||
docker run -d -p 6379:6379 redis:latest
|
docker run -d -p 6379:6379 redis:latest
|
||||||
docker run -p 3000:3000 -d gotenberg/gotenberg:7.8 gotenberg --chromium-disable-javascript=true --chromium-allow-list="file:///tmp/.*"
|
docker run -p 3000:3000 -d gotenberg/gotenberg:7.8 gotenberg --chromium-disable-javascript=true --chromium-allow-list="file:///tmp/.*"
|
||||||
docker run -p 9998:9998 -d ghcr.io/paperless-ngx/tika:latest
|
docker run -p 9998:9998 -d ghcr.io/paperless-ngx/tika:latest
|
||||||
|
3
src-ui/.gitignore
vendored
@@ -49,3 +49,6 @@ Thumbs.db
|
|||||||
# Cypress
|
# Cypress
|
||||||
cypress/videos/**/*
|
cypress/videos/**/*
|
||||||
cypress/screenshots/**/*
|
cypress/screenshots/**/*
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
@@ -18,11 +18,13 @@
|
|||||||
"locales": {
|
"locales": {
|
||||||
"ar-AR": "src/locale/messages.ar_AR.xlf",
|
"ar-AR": "src/locale/messages.ar_AR.xlf",
|
||||||
"be-BY": "src/locale/messages.be_BY.xlf",
|
"be-BY": "src/locale/messages.be_BY.xlf",
|
||||||
|
"ca-ES": "src/locale/messages.ca_ES.xlf",
|
||||||
"cs-CZ": "src/locale/messages.cs_CZ.xlf",
|
"cs-CZ": "src/locale/messages.cs_CZ.xlf",
|
||||||
"da-DK": "src/locale/messages.da_DK.xlf",
|
"da-DK": "src/locale/messages.da_DK.xlf",
|
||||||
"de-DE": "src/locale/messages.de_DE.xlf",
|
"de-DE": "src/locale/messages.de_DE.xlf",
|
||||||
"en-GB": "src/locale/messages.en_GB.xlf",
|
"en-GB": "src/locale/messages.en_GB.xlf",
|
||||||
"es-ES": "src/locale/messages.es_ES.xlf",
|
"es-ES": "src/locale/messages.es_ES.xlf",
|
||||||
|
"fi-FI": "src/locale/messages.fi_FI.xlf",
|
||||||
"fr-FR": "src/locale/messages.fr_FR.xlf",
|
"fr-FR": "src/locale/messages.fr_FR.xlf",
|
||||||
"it-IT": "src/locale/messages.it_IT.xlf",
|
"it-IT": "src/locale/messages.it_IT.xlf",
|
||||||
"lb-LU": "src/locale/messages.lb_LU.xlf",
|
"lb-LU": "src/locale/messages.lb_LU.xlf",
|
||||||
@@ -32,10 +34,12 @@
|
|||||||
"pt-PT": "src/locale/messages.pt_PT.xlf",
|
"pt-PT": "src/locale/messages.pt_PT.xlf",
|
||||||
"ro-RO": "src/locale/messages.ro_RO.xlf",
|
"ro-RO": "src/locale/messages.ro_RO.xlf",
|
||||||
"ru-RU": "src/locale/messages.ru_RU.xlf",
|
"ru-RU": "src/locale/messages.ru_RU.xlf",
|
||||||
|
"sk-SK": "src/locale/messages.sk_SK.xlf",
|
||||||
"sl-SI": "src/locale/messages.sl_SI.xlf",
|
"sl-SI": "src/locale/messages.sl_SI.xlf",
|
||||||
"sr-CS": "src/locale/messages.sr_CS.xlf",
|
"sr-CS": "src/locale/messages.sr_CS.xlf",
|
||||||
"sv-SE": "src/locale/messages.sv_SE.xlf",
|
"sv-SE": "src/locale/messages.sv_SE.xlf",
|
||||||
"tr-TR": "src/locale/messages.tr_TR.xlf",
|
"tr-TR": "src/locale/messages.tr_TR.xlf",
|
||||||
|
"uk-UA": "src/locale/messages.uk_UA.xlf",
|
||||||
"zh-CN": "src/locale/messages.zh_CN.xlf"
|
"zh-CN": "src/locale/messages.zh_CN.xlf"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -145,37 +149,6 @@
|
|||||||
"scripts": []
|
"scripts": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"e2e": {
|
|
||||||
"builder": "@cypress/schematic:cypress",
|
|
||||||
"options": {
|
|
||||||
"devServerTarget": "paperless-ui:serve",
|
|
||||||
"watch": true,
|
|
||||||
"headless": false
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "paperless-ui:serve:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cypress-run": {
|
|
||||||
"builder": "@cypress/schematic:cypress",
|
|
||||||
"options": {
|
|
||||||
"devServerTarget": "paperless-ui:serve"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "paperless-ui:serve:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cypress-open": {
|
|
||||||
"builder": "@cypress/schematic:cypress",
|
|
||||||
"options": {
|
|
||||||
"watch": true,
|
|
||||||
"headless": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
"lint": {
|
||||||
"builder": "@angular-eslint/builder:lint",
|
"builder": "@angular-eslint/builder:lint",
|
||||||
"options": {
|
"options": {
|
||||||
@@ -188,7 +161,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultProject": "paperless-ui",
|
|
||||||
"cli": {
|
"cli": {
|
||||||
"schematicCollections": [
|
"schematicCollections": [
|
||||||
"@angular-eslint/schematics"
|
"@angular-eslint/schematics"
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
import { defineConfig } from 'cypress'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
videosFolder: 'cypress/videos',
|
|
||||||
screenshotsFolder: 'cypress/screenshots',
|
|
||||||
fixturesFolder: 'cypress/fixtures',
|
|
||||||
e2e: {
|
|
||||||
setupNodeEvents(on, config) {
|
|
||||||
return require('./cypress/plugins/index.ts')(on, config)
|
|
||||||
},
|
|
||||||
baseUrl: 'http://localhost:4200',
|
|
||||||
},
|
|
||||||
})
|
|
@@ -1,107 +0,0 @@
|
|||||||
describe('document-detail', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
// also uses global fixtures from cypress/support/e2e.ts
|
|
||||||
|
|
||||||
this.modifiedDocuments = []
|
|
||||||
|
|
||||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
|
||||||
cy.intercept('GET', 'http://localhost:8000/api/documents/1/', (req) => {
|
|
||||||
let response = { ...documentsJson }
|
|
||||||
response = response.results.find((d) => d.id == 1)
|
|
||||||
req.reply(response)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('PUT', 'http://localhost:8000/api/documents/1/', (req) => {
|
|
||||||
this.modifiedDocuments.push(req.body) // store this for later
|
|
||||||
req.reply({ result: 'OK' })
|
|
||||||
}).as('saveDoc')
|
|
||||||
|
|
||||||
cy.fixture('documents/1/comments.json').then((commentsJson) => {
|
|
||||||
cy.intercept(
|
|
||||||
'GET',
|
|
||||||
'http://localhost:8000/api/documents/1/comments/',
|
|
||||||
(req) => {
|
|
||||||
req.reply(commentsJson.filter((c) => c.id != 10)) // 3
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'DELETE',
|
|
||||||
'http://localhost:8000/api/documents/1/comments/?id=9',
|
|
||||||
(req) => {
|
|
||||||
req.reply(commentsJson.filter((c) => c.id != 9 && c.id != 10)) // 2
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'POST',
|
|
||||||
'http://localhost:8000/api/documents/1/comments/',
|
|
||||||
(req) => {
|
|
||||||
req.reply(commentsJson) // 4
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.viewport(1024, 1024)
|
|
||||||
cy.visit('/documents/1/').wait('@ui-settings')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should activate / deactivate save button when changes are saved', () => {
|
|
||||||
cy.contains('button', 'Save').should('be.disabled')
|
|
||||||
cy.get('app-input-text[formcontrolname="title"]')
|
|
||||||
.type(' additional')
|
|
||||||
.wait(1500) // this delay is for frontend debounce
|
|
||||||
cy.contains('button', 'Save').should('not.be.disabled')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should warn on unsaved changes', () => {
|
|
||||||
cy.get('app-input-text[formcontrolname="title"]')
|
|
||||||
.type(' additional')
|
|
||||||
.wait(1500) // this delay is for frontend debounce
|
|
||||||
cy.get('button[title="Close"]').click()
|
|
||||||
cy.contains('You have unsaved changes')
|
|
||||||
cy.contains('button', 'Cancel').click().wait(150)
|
|
||||||
cy.contains('button', 'Save').click().wait('@saveDoc').wait(2000) // navigates away after saving
|
|
||||||
cy.contains('You have unsaved changes').should('not.exist')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a mobile preview', () => {
|
|
||||||
cy.viewport(440, 1000)
|
|
||||||
cy.get('a')
|
|
||||||
.contains('Preview')
|
|
||||||
.scrollIntoView({ offset: { top: 150, left: 0 } })
|
|
||||||
.click()
|
|
||||||
cy.get('pdf-viewer').should('be.visible')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of comments', () => {
|
|
||||||
cy.wait(1000)
|
|
||||||
.get('a')
|
|
||||||
.contains('Comments')
|
|
||||||
.click({ force: true })
|
|
||||||
.wait(1000)
|
|
||||||
cy.get('app-document-comments').find('.card').its('length').should('eq', 3)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support comment deletion', () => {
|
|
||||||
cy.wait(1000).get('a').contains('Comments').click().wait(1000)
|
|
||||||
cy.get('app-document-comments')
|
|
||||||
.find('.card')
|
|
||||||
.first()
|
|
||||||
.find('button')
|
|
||||||
.click({ force: true })
|
|
||||||
.wait(500)
|
|
||||||
cy.get('app-document-comments').find('.card').its('length').should('eq', 2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support comment insertion', () => {
|
|
||||||
cy.wait(1000).get('a').contains('Comments').click().wait(1000)
|
|
||||||
cy.get('app-document-comments')
|
|
||||||
.find('form textarea')
|
|
||||||
.type('Testing new comment')
|
|
||||||
.wait(500)
|
|
||||||
cy.get('app-document-comments').find('form button').click().wait(1500)
|
|
||||||
cy.get('app-document-comments').find('.card').its('length').should('eq', 4)
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,155 +0,0 @@
|
|||||||
describe('documents-list', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
// also uses global fixtures from cypress/support/e2e.ts
|
|
||||||
|
|
||||||
this.bulkEdits = {}
|
|
||||||
|
|
||||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
|
||||||
// bulk edit
|
|
||||||
cy.intercept(
|
|
||||||
'POST',
|
|
||||||
'http://localhost:8000/api/documents/bulk_edit/',
|
|
||||||
(req) => {
|
|
||||||
this.bulkEdits = req.body // store this for later
|
|
||||||
req.reply({ result: 'OK' })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
cy.intercept('GET', 'http://localhost:8000/api/documents/*', (req) => {
|
|
||||||
let response = { ...documentsJson }
|
|
||||||
|
|
||||||
// bulkEdits was set earlier by bulk_edit intercept
|
|
||||||
if (this.bulkEdits.hasOwnProperty('documents')) {
|
|
||||||
response.results = response.results.map((d) => {
|
|
||||||
if ((this.bulkEdits['documents'] as Array<number>).includes(d.id)) {
|
|
||||||
switch (this.bulkEdits['method']) {
|
|
||||||
case 'modify_tags':
|
|
||||||
d.tags = (d.tags as Array<number>).concat([
|
|
||||||
this.bulkEdits['parameters']['add_tags'],
|
|
||||||
])
|
|
||||||
break
|
|
||||||
case 'set_correspondent':
|
|
||||||
d.correspondent =
|
|
||||||
this.bulkEdits['parameters']['correspondent']
|
|
||||||
break
|
|
||||||
case 'set_document_type':
|
|
||||||
d.document_type =
|
|
||||||
this.bulkEdits['parameters']['document_type']
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return d
|
|
||||||
})
|
|
||||||
} else if (req.query.hasOwnProperty('tags__id__all')) {
|
|
||||||
// filtering e.g. http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&tags__id__all=2
|
|
||||||
const tag_id = +req.query['tags__id__all']
|
|
||||||
response.results = (documentsJson.results as Array<any>).filter((d) =>
|
|
||||||
(d.tags as Array<number>).includes(tag_id)
|
|
||||||
)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
req.reply(response)
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/documents/selection_data/', {
|
|
||||||
fixture: 'documents/selection_data.json',
|
|
||||||
}).as('selection-data')
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.viewport(1280, 1024)
|
|
||||||
cy.visit('/documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents rendered as cards with thumbnails', () => {
|
|
||||||
cy.contains('3 documents')
|
|
||||||
cy.contains('lorem ipsum')
|
|
||||||
cy.get('app-document-card-small:first-of-type img')
|
|
||||||
.invoke('attr', 'src')
|
|
||||||
.should('eq', 'http://localhost:8000/api/documents/1/thumb/')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should change to table "details" view', () => {
|
|
||||||
cy.get('div.btn-group input[value="details"]').next().click()
|
|
||||||
cy.get('table')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should change to large cards view', () => {
|
|
||||||
cy.get('div.btn-group input[value="largeCards"]').next().click()
|
|
||||||
cy.get('app-document-card-large')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show partial tag selection', () => {
|
|
||||||
cy.get('app-document-card-small:nth-child(1)').click()
|
|
||||||
cy.get('app-document-card-small:nth-child(4)').click()
|
|
||||||
cy.get('app-bulk-editor button')
|
|
||||||
.contains('Tags')
|
|
||||||
.click()
|
|
||||||
.wait('@selection-data')
|
|
||||||
cy.get('svg.bi-dash').should('be.visible')
|
|
||||||
cy.get('svg.bi-check').should('be.visible')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow bulk removal', () => {
|
|
||||||
cy.get('app-document-card-small:nth-child(1)').click()
|
|
||||||
cy.get('app-document-card-small:nth-child(4)').click()
|
|
||||||
cy.get('app-bulk-editor').within(() => {
|
|
||||||
cy.get('button').contains('Tags').click().wait('@selection-data')
|
|
||||||
cy.get('button').contains('Another Sample Tag').click()
|
|
||||||
cy.get('button').contains('Apply').click()
|
|
||||||
})
|
|
||||||
cy.contains('operation will remove the tag')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should filter tags', () => {
|
|
||||||
cy.get('app-filter-editor app-filterable-dropdown[title="Tags"]').within(
|
|
||||||
() => {
|
|
||||||
cy.contains('button', 'Tags').click()
|
|
||||||
cy.contains('button', 'Tag 2').click()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should apply tags', () => {
|
|
||||||
cy.get('app-document-card-small:first-of-type').click()
|
|
||||||
cy.get('app-bulk-editor app-filterable-dropdown[title="Tags"]').within(
|
|
||||||
() => {
|
|
||||||
cy.contains('button', 'Tags').click()
|
|
||||||
cy.contains('button', 'Test Tag').click()
|
|
||||||
cy.contains('button', 'Apply').click()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
cy.contains('button', 'Confirm').click()
|
|
||||||
cy.get('app-document-card-small:first-of-type').contains('Test Tag')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should apply correspondent', () => {
|
|
||||||
cy.get('app-document-card-small:first-of-type').click()
|
|
||||||
cy.get(
|
|
||||||
'app-bulk-editor app-filterable-dropdown[title="Correspondent"]'
|
|
||||||
).within(() => {
|
|
||||||
cy.contains('button', 'Correspondent').click()
|
|
||||||
cy.contains('button', 'ABC Test Correspondent').click()
|
|
||||||
cy.contains('button', 'Apply').click()
|
|
||||||
})
|
|
||||||
cy.contains('button', 'Confirm').click()
|
|
||||||
cy.get('app-document-card-small:first-of-type').contains(
|
|
||||||
'ABC Test Correspondent'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should apply document type', () => {
|
|
||||||
cy.get('app-document-card-small:first-of-type').click()
|
|
||||||
cy.get(
|
|
||||||
'app-bulk-editor app-filterable-dropdown[title="Document type"]'
|
|
||||||
).within(() => {
|
|
||||||
cy.contains('button', 'Document type').click()
|
|
||||||
cy.contains('button', 'Test Doc Type').click()
|
|
||||||
cy.contains('button', 'Apply').click()
|
|
||||||
})
|
|
||||||
cy.contains('button', 'Confirm').click()
|
|
||||||
cy.get('app-document-card-small:first-of-type').contains('Test Doc Type')
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,331 +0,0 @@
|
|||||||
import { PaperlessDocument } from 'src/app/data/paperless-document'
|
|
||||||
|
|
||||||
describe('documents query params', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
// also uses global fixtures from cypress/support/e2e.ts
|
|
||||||
|
|
||||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
|
||||||
// mock api filtering
|
|
||||||
cy.intercept('GET', 'http://localhost:8000/api/documents/*', (req) => {
|
|
||||||
let response = { ...documentsJson }
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('ordering')) {
|
|
||||||
const sort_field = req.query['ordering'].toString().replace('-', '')
|
|
||||||
const reverse = req.query['ordering'].toString().indexOf('-') !== -1
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).sort((docA, docB) => {
|
|
||||||
let result = 0
|
|
||||||
switch (sort_field) {
|
|
||||||
case 'created':
|
|
||||||
case 'added':
|
|
||||||
result =
|
|
||||||
new Date(docA[sort_field]) < new Date(docB[sort_field])
|
|
||||||
? -1
|
|
||||||
: 1
|
|
||||||
break
|
|
||||||
case 'archive_serial_number':
|
|
||||||
result = docA[sort_field] < docB[sort_field] ? -1 : 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (reverse) result = -result
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('tags__id__in')) {
|
|
||||||
const tag_ids: Array<number> = req.query['tags__id__in']
|
|
||||||
.toString()
|
|
||||||
.split(',')
|
|
||||||
.map((v) => +v)
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter(
|
|
||||||
(d) =>
|
|
||||||
d.tags.length > 0 &&
|
|
||||||
d.tags.filter((t) => tag_ids.includes(t)).length > 0
|
|
||||||
)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (req.query.hasOwnProperty('tags__id__none')) {
|
|
||||||
const tag_ids: Array<number> = req.query['tags__id__none']
|
|
||||||
.toString()
|
|
||||||
.split(',')
|
|
||||||
.map((v) => +v)
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.tags.filter((t) => tag_ids.includes(t)).length == 0)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (
|
|
||||||
req.query.hasOwnProperty('is_tagged') &&
|
|
||||||
req.query['is_tagged'] == '0'
|
|
||||||
) {
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.tags.length == 0)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('document_type__id')) {
|
|
||||||
const doctype_id = +req.query['document_type__id']
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.document_type == doctype_id)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (
|
|
||||||
req.query.hasOwnProperty('document_type__isnull') &&
|
|
||||||
req.query['document_type__isnull'] == '1'
|
|
||||||
) {
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.document_type == undefined)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('correspondent__id')) {
|
|
||||||
const correspondent_id = +req.query['correspondent__id']
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.correspondent == correspondent_id)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (
|
|
||||||
req.query.hasOwnProperty('correspondent__isnull') &&
|
|
||||||
req.query['correspondent__isnull'] == '1'
|
|
||||||
) {
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.correspondent == undefined)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('storage_path__id')) {
|
|
||||||
const storage_path_id = +req.query['storage_path__id']
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.storage_path == storage_path_id)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (
|
|
||||||
req.query.hasOwnProperty('storage_path__isnull') &&
|
|
||||||
req.query['storage_path__isnull'] == '1'
|
|
||||||
) {
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.storage_path == undefined)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('created__date__gt')) {
|
|
||||||
const date = new Date(req.query['created__date__gt'])
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => new Date(d.created) > date)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (req.query.hasOwnProperty('created__date__lt')) {
|
|
||||||
const date = new Date(req.query['created__date__lt'])
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => new Date(d.created) < date)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('added__date__gt')) {
|
|
||||||
const date = new Date(req.query['added__date__gt'])
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => new Date(d.added) > date)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (req.query.hasOwnProperty('added__date__lt')) {
|
|
||||||
const date = new Date(req.query['added__date__lt'])
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => new Date(d.added) < date)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('title_content')) {
|
|
||||||
const title_content_regexp = new RegExp(
|
|
||||||
req.query['title_content'].toString(),
|
|
||||||
'i'
|
|
||||||
)
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter(
|
|
||||||
(d) =>
|
|
||||||
title_content_regexp.test(d.title) ||
|
|
||||||
title_content_regexp.test(d.content)
|
|
||||||
)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.hasOwnProperty('archive_serial_number')) {
|
|
||||||
const asn = +req.query['archive_serial_number']
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) => d.archive_serial_number == asn)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (req.query.hasOwnProperty('archive_serial_number__isnull')) {
|
|
||||||
const isnull = req.query['storage_path__isnull'] == '1'
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter((d) =>
|
|
||||||
isnull
|
|
||||||
? d.archive_serial_number == undefined
|
|
||||||
: d.archive_serial_number != undefined
|
|
||||||
)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (req.query.hasOwnProperty('archive_serial_number__gt')) {
|
|
||||||
const asn = +req.query['archive_serial_number__gt']
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter(
|
|
||||||
(d) => d.archive_serial_number > 0 && d.archive_serial_number > asn
|
|
||||||
)
|
|
||||||
response.count = response.results.length
|
|
||||||
} else if (req.query.hasOwnProperty('archive_serial_number__lt')) {
|
|
||||||
const asn = +req.query['archive_serial_number__lt']
|
|
||||||
response.results = (
|
|
||||||
documentsJson.results as Array<PaperlessDocument>
|
|
||||||
).filter(
|
|
||||||
(d) => d.archive_serial_number > 0 && d.archive_serial_number < asn
|
|
||||||
)
|
|
||||||
response.count = response.results.length
|
|
||||||
}
|
|
||||||
|
|
||||||
req.reply(response)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents sorted by created', () => {
|
|
||||||
cy.visit('/documents?sort=created')
|
|
||||||
cy.get('app-document-card-small').first().contains('No latin title')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents reverse sorted by created', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true')
|
|
||||||
cy.get('app-document-card-small').first().contains('sit amet')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents sorted by added', () => {
|
|
||||||
cy.visit('/documents?sort=added')
|
|
||||||
cy.get('app-document-card-small').first().contains('No latin title')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents reverse sorted by added', () => {
|
|
||||||
cy.visit('/documents?sort=added&reverse=true')
|
|
||||||
cy.get('app-document-card-small').first().contains('sit amet')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by any tags', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&tags__id__in=2,4,5')
|
|
||||||
cy.contains('3 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by excluded tags', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&tags__id__none=2,4')
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by no tags', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&is_tagged=0')
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by document type', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&document_type__id=1')
|
|
||||||
cy.contains('3 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by no document type', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&document_type__isnull=1')
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by correspondent', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&correspondent__id=9')
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by no correspondent', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&correspondent__isnull=1')
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by storage path', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&storage_path__id=2')
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by no storage path', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&storage_path__isnull=1')
|
|
||||||
cy.contains('3 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by title or content', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&title_content=lorem')
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by asn', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&archive_serial_number=12345')
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by empty asn', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&archive_serial_number__isnull=1'
|
|
||||||
)
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by non-empty asn', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&archive_serial_number__isnull=0'
|
|
||||||
)
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by asn greater than', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&archive_serial_number__gt=12346'
|
|
||||||
)
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by asn less than', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&archive_serial_number__lt=12346'
|
|
||||||
)
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by created date greater than', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&created__date__gt=2022-03-23'
|
|
||||||
)
|
|
||||||
cy.contains('3 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by created date less than', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&created__date__lt=2022-03-23'
|
|
||||||
)
|
|
||||||
cy.contains('One document')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by added date greater than', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&added__date__gt=2022-03-24')
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by added date less than', () => {
|
|
||||||
cy.visit('/documents?sort=created&reverse=true&added__date__lt=2022-03-24')
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by multiple filters', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/documents?sort=created&reverse=true&document_type__id=1&correspondent__id=9&tags__id__in=4,5'
|
|
||||||
)
|
|
||||||
cy.contains('2 documents')
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,25 +0,0 @@
|
|||||||
describe('manage', () => {
|
|
||||||
// also uses global fixtures from cypress/support/e2e.ts
|
|
||||||
|
|
||||||
it('should show a list of correspondents with bottom pagination as well', () => {
|
|
||||||
cy.visit('/correspondents')
|
|
||||||
cy.get('tbody').find('tr').its('length').should('eq', 25)
|
|
||||||
cy.get('ngb-pagination').its('length').should('eq', 2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of tags without bottom pagination', () => {
|
|
||||||
cy.visit('/tags')
|
|
||||||
cy.get('tbody').find('tr').its('length').should('eq', 8)
|
|
||||||
cy.get('ngb-pagination').its('length').should('eq', 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of documents filtered by tag', () => {
|
|
||||||
cy.intercept('http://localhost:8000/api/documents/*', (req) => {
|
|
||||||
if (req.url.indexOf('tags__id__all=4'))
|
|
||||||
req.reply({ count: 3, next: null, previous: null, results: [] })
|
|
||||||
})
|
|
||||||
cy.visit('/tags')
|
|
||||||
cy.get('tbody').find('button:visible').contains('Documents').first().click() // id = 4
|
|
||||||
cy.contains('3 documents')
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,182 +0,0 @@
|
|||||||
describe('settings', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
// also uses global fixtures from cypress/support/e2e.ts
|
|
||||||
|
|
||||||
this.modifiedViews = []
|
|
||||||
|
|
||||||
// mock API methods
|
|
||||||
cy.intercept('http://localhost:8000/api/ui_settings/', {
|
|
||||||
fixture: 'ui_settings/settings.json',
|
|
||||||
}).then(() => {
|
|
||||||
cy.fixture('saved_views/savedviews.json').then((savedViewsJson) => {
|
|
||||||
// saved views PATCH
|
|
||||||
cy.intercept(
|
|
||||||
'PATCH',
|
|
||||||
'http://localhost:8000/api/saved_views/*',
|
|
||||||
(req) => {
|
|
||||||
this.modifiedViews.push(req.body) // store this for later
|
|
||||||
req.reply({ result: 'OK' })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'GET',
|
|
||||||
'http://localhost:8000/api/saved_views/*',
|
|
||||||
(req) => {
|
|
||||||
let response = { ...savedViewsJson }
|
|
||||||
if (this.modifiedViews.length) {
|
|
||||||
response.results = response.results.map((v) => {
|
|
||||||
if (this.modifiedViews.find((mv) => mv.id == v.id))
|
|
||||||
v = this.modifiedViews.find((mv) => mv.id == v.id)
|
|
||||||
return v
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
req.reply(response)
|
|
||||||
}
|
|
||||||
).as('savedViews')
|
|
||||||
})
|
|
||||||
|
|
||||||
this.newMailAccounts = []
|
|
||||||
|
|
||||||
cy.intercept(
|
|
||||||
'POST',
|
|
||||||
'http://localhost:8000/api/mail_accounts/',
|
|
||||||
(req) => {
|
|
||||||
const newRule = req.body
|
|
||||||
newRule.id = 3
|
|
||||||
this.newMailAccounts.push(newRule) // store this for later
|
|
||||||
req.reply({ result: 'OK' })
|
|
||||||
}
|
|
||||||
).as('saveAccount')
|
|
||||||
|
|
||||||
cy.fixture('mail_accounts/mail_accounts.json').then(
|
|
||||||
(mailAccountsJson) => {
|
|
||||||
cy.intercept(
|
|
||||||
'GET',
|
|
||||||
'http://localhost:8000/api/mail_accounts/*',
|
|
||||||
(req) => {
|
|
||||||
console.log(req, this.newMailAccounts)
|
|
||||||
|
|
||||||
let response = { ...mailAccountsJson }
|
|
||||||
if (this.newMailAccounts.length) {
|
|
||||||
response.results = response.results.concat(this.newMailAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.reply(response)
|
|
||||||
}
|
|
||||||
).as('getAccounts')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
this.newMailRules = []
|
|
||||||
|
|
||||||
cy.intercept('POST', 'http://localhost:8000/api/mail_rules/', (req) => {
|
|
||||||
const newRule = req.body
|
|
||||||
newRule.id = 2
|
|
||||||
this.newMailRules.push(newRule) // store this for later
|
|
||||||
req.reply({ result: 'OK' })
|
|
||||||
}).as('saveRule')
|
|
||||||
|
|
||||||
cy.fixture('mail_rules/mail_rules.json').then((mailRulesJson) => {
|
|
||||||
cy.intercept('GET', 'http://localhost:8000/api/mail_rules/*', (req) => {
|
|
||||||
let response = { ...mailRulesJson }
|
|
||||||
if (this.newMailRules.length) {
|
|
||||||
response.results = response.results.concat(this.newMailRules)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.reply(response)
|
|
||||||
}).as('getRules')
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.fixture('documents/documents.json').then((documentsJson) => {
|
|
||||||
cy.intercept('GET', 'http://localhost:8000/api/documents/1/', (req) => {
|
|
||||||
let response = { ...documentsJson }
|
|
||||||
response = response.results.find((d) => d.id == 1)
|
|
||||||
req.reply(response)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.viewport(1024, 1600)
|
|
||||||
cy.visit('/settings')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should activate / deactivate save button when settings change and are saved', () => {
|
|
||||||
cy.contains('button', 'Save').should('be.disabled')
|
|
||||||
cy.contains('Use system settings').click()
|
|
||||||
cy.contains('button', 'Save').should('not.be.disabled')
|
|
||||||
cy.contains('button', 'Save').click()
|
|
||||||
cy.contains('button', 'Save').should('be.disabled')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should warn on unsaved changes', () => {
|
|
||||||
cy.contains('Use system settings').click()
|
|
||||||
cy.contains('a', 'Dashboard').click()
|
|
||||||
cy.contains('You have unsaved changes')
|
|
||||||
cy.contains('button', 'Cancel').click()
|
|
||||||
cy.contains('button', 'Save').click().wait('@savedViews').wait(2000)
|
|
||||||
cy.contains('a', 'Dashboard').click()
|
|
||||||
cy.contains('You have unsaved changes').should('not.exist')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should apply appearance changes when set', () => {
|
|
||||||
cy.contains('Use system settings').click()
|
|
||||||
cy.get('body').should('not.have.class', 'color-scheme-system')
|
|
||||||
cy.contains('Enable dark mode').click()
|
|
||||||
cy.get('body').should('have.class', 'color-scheme-dark')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should remove saved view from sidebar when unset', () => {
|
|
||||||
cy.contains('a', 'Saved views').click().wait(2000)
|
|
||||||
cy.get('#show_in_sidebar_1').click()
|
|
||||||
cy.contains('button', 'Save').click().wait('@savedViews').wait(2000)
|
|
||||||
cy.contains('li', 'Inbox').should('not.exist')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should remove saved view from dashboard when unset', () => {
|
|
||||||
cy.contains('a', 'Saved views').click()
|
|
||||||
cy.get('#show_on_dashboard_1').click()
|
|
||||||
cy.contains('button', 'Save').click().wait('@savedViews').wait(2000)
|
|
||||||
cy.visit('/dashboard')
|
|
||||||
cy.get('app-saved-view-widget').contains('Inbox').should('not.exist')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of mail accounts & rules & support creation', () => {
|
|
||||||
cy.contains('a', 'Mail').click()
|
|
||||||
cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule
|
|
||||||
cy.contains('button', 'Add Account').click()
|
|
||||||
cy.contains('Create new mail account')
|
|
||||||
cy.get('app-input-text[formcontrolname="name"]').type(
|
|
||||||
'Example Mail Account'
|
|
||||||
)
|
|
||||||
cy.get('app-input-text[formcontrolname="imap_server"]').type(
|
|
||||||
'mail.example.com'
|
|
||||||
)
|
|
||||||
cy.get('app-input-text[formcontrolname="imap_port"]').type('993')
|
|
||||||
cy.get('app-input-text[formcontrolname="username"]').type('username')
|
|
||||||
cy.get('app-input-password[formcontrolname="password"]').type('pass')
|
|
||||||
cy.contains('app-mail-account-edit-dialog button', 'Save')
|
|
||||||
.click()
|
|
||||||
.wait('@saveAccount')
|
|
||||||
.wait('@getAccounts')
|
|
||||||
cy.contains('Saved account')
|
|
||||||
|
|
||||||
cy.wait(1000)
|
|
||||||
cy.contains('button', 'Add Rule').click()
|
|
||||||
cy.contains('Create new mail rule')
|
|
||||||
cy.get('app-input-text[formcontrolname="name"]').type('Example Rule')
|
|
||||||
cy.get('app-input-select[formcontrolname="account"]').type('Example{enter}')
|
|
||||||
cy.get('app-input-number[formcontrolname="maximum_age"]').type('30')
|
|
||||||
cy.get('app-input-text[formcontrolname="filter_subject"]').type(
|
|
||||||
'[paperless]'
|
|
||||||
)
|
|
||||||
cy.contains('app-mail-rule-edit-dialog button', 'Save')
|
|
||||||
.click()
|
|
||||||
.wait('@saveRule')
|
|
||||||
.wait('@getRules')
|
|
||||||
cy.contains('Saved rule').wait(1000)
|
|
||||||
|
|
||||||
cy.get('app-settings .tab-content ul li').its('length').should('eq', 7)
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,93 +0,0 @@
|
|||||||
describe('tasks', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
this.dismissedTasks = new Set<number>()
|
|
||||||
|
|
||||||
cy.fixture('tasks/tasks.json').then((tasksViewsJson) => {
|
|
||||||
// acknowledge tasks POST
|
|
||||||
cy.intercept(
|
|
||||||
'POST',
|
|
||||||
'http://localhost:8000/api/acknowledge_tasks/',
|
|
||||||
(req) => {
|
|
||||||
req.body['tasks'].forEach((t) => this.dismissedTasks.add(t)) // store this for later
|
|
||||||
req.reply({ result: 'OK' })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
cy.intercept('GET', 'http://localhost:8000/api/tasks/', (req) => {
|
|
||||||
let response = [...tasksViewsJson]
|
|
||||||
if (this.dismissedTasks.size) {
|
|
||||||
response = response.filter((t) => {
|
|
||||||
return !this.dismissedTasks.has(t.id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
req.reply(response)
|
|
||||||
}).as('tasks')
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.visit('/tasks')
|
|
||||||
cy.wait('@tasks')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should show a list of dismissable tasks in tabs', () => {
|
|
||||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 10) // double because collapsible result tr
|
|
||||||
cy.wait(500) // stabilizes the test, for some reason...
|
|
||||||
cy.get('tbody')
|
|
||||||
.find('button:visible')
|
|
||||||
.contains('Dismiss')
|
|
||||||
.first()
|
|
||||||
.click()
|
|
||||||
.wait('@tasks')
|
|
||||||
.wait(2000)
|
|
||||||
.then(() => {
|
|
||||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 8) // double because collapsible result tr
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly switch between task tabs', () => {
|
|
||||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 10) // double because collapsible result tr
|
|
||||||
cy.wait(500) // stabilizes the test, for some reason...
|
|
||||||
cy.get('app-tasks')
|
|
||||||
.find('a:visible')
|
|
||||||
.contains('Queued')
|
|
||||||
.first()
|
|
||||||
.click()
|
|
||||||
.wait(2000)
|
|
||||||
.then(() => {
|
|
||||||
cy.get('tbody').find('tr:visible').should('not.exist')
|
|
||||||
})
|
|
||||||
cy.get('app-tasks')
|
|
||||||
.find('a:visible')
|
|
||||||
.contains('Started')
|
|
||||||
.first()
|
|
||||||
.click()
|
|
||||||
.wait(2000)
|
|
||||||
.then(() => {
|
|
||||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 2) // double because collapsible result tr
|
|
||||||
})
|
|
||||||
cy.get('app-tasks')
|
|
||||||
.find('a:visible')
|
|
||||||
.contains('Complete')
|
|
||||||
.first()
|
|
||||||
.click()
|
|
||||||
.wait('@tasks')
|
|
||||||
.wait(2000)
|
|
||||||
.then(() => {
|
|
||||||
cy.get('tbody').find('tr:visible').its('length').should('eq', 12) // double because collapsible result tr
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should allow toggling all tasks in list and warn on dismiss', () => {
|
|
||||||
cy.get('thead').find('input[type="checkbox"]').first().click()
|
|
||||||
cy.get('body').find('button').contains('Dismiss selected').first().click()
|
|
||||||
cy.contains('Confirm')
|
|
||||||
cy.get('.modal')
|
|
||||||
.contains('button', 'Dismiss')
|
|
||||||
.click()
|
|
||||||
.wait('@tasks')
|
|
||||||
.wait(2000)
|
|
||||||
.then(() => {
|
|
||||||
cy.get('tbody').find('tr:visible').should('not.exist')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1 +0,0 @@
|
|||||||
{"count":27,"next":"http://localhost:8000/api/correspondents/?page=2","previous":null,"results":[{"id":9,"slug":"abc-test-correspondent","name":"ABC Test Correspondent","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":13,"slug":"corresp-10","name":"Corresp 10","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":14,"slug":"corresp-11","name":"Corresp 11","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":15,"slug":"corresp-12","name":"Corresp 12","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":16,"slug":"corresp-13","name":"Corresp 13","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":18,"slug":"corresp-15","name":"Corresp 15","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":19,"slug":"corresp-16","name":"Corresp 16","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":20,"slug":"corresp-17","name":"Corresp 17","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":21,"slug":"corresp-18","name":"Corresp 18","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":22,"slug":"corresp-19","name":"Corresp 19","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":23,"slug":"corresp-20","name":"Corresp 20","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":24,"slug":"corresp-21","name":"Corresp 21","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":25,"slug":"corresp-22","name":"Corresp 22","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":26,"slug":"corresp-23","name":"Corresp 23","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":5,"slug":"corresp-3","name":"Corresp 3","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":6,"slug":"corresp-4","name":"Corresp 4","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":7,"slug":"corresp-5","name":"Corresp 5","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":8,"slug":"corresp-6","name":"Corresp 6","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":10,"slug":"corresp-7","name":"Corresp 7","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":11,"slug":"corresp-8","name":"Corresp 8","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":12,"slug":"corresp-9","name":"Corresp 9","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":17,"slug":"correspondent-14","name":"Correspondent 14","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0,"last_correspondence":null},{"id":2,"slug":"correspondent-2","name":"Correspondent 2","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":7,"last_correspondence":"2021-01-20T23:37:58.204614Z"},{"id":27,"slug":"michael-shamoon","name":"Michael Shamoon","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":1,"last_correspondence":"2022-03-16T03:48:50.089624Z"},{"id":4,"slug":"newest-correspondent","name":"Newest Correspondent","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":1,"last_correspondence":"2021-02-07T08:00:00Z"}]}
|
|
@@ -1 +0,0 @@
|
|||||||
{"count":1,"next":null,"previous":null,"results":[{"id":1,"slug":"test","name":"Test Doc Type","match":"","matching_algorithm":1,"is_insensitive":true,"document_count":0}]}
|
|
@@ -1,46 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"comment": "Testing new comment",
|
|
||||||
"created": "2022-08-08T04:24:55.176008Z",
|
|
||||||
"user": {
|
|
||||||
"id": 1,
|
|
||||||
"username": "user2",
|
|
||||||
"firstname": "",
|
|
||||||
"lastname": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"comment": "Testing one more time",
|
|
||||||
"created": "2022-02-18T04:24:55.176008Z",
|
|
||||||
"user": {
|
|
||||||
"id": 2,
|
|
||||||
"username": "user1",
|
|
||||||
"firstname": "",
|
|
||||||
"lastname": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"comment": "Another comment",
|
|
||||||
"created": "2021-11-08T04:24:47.925042Z",
|
|
||||||
"user": {
|
|
||||||
"id": 2,
|
|
||||||
"username": "user33",
|
|
||||||
"firstname": "",
|
|
||||||
"lastname": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 7,
|
|
||||||
"comment": "Cupcake ipsum dolor sit amet cheesecake candy cookie tiramisu. Donut chocolate chupa chups macaroon brownie halvah pie cheesecake gummies. Sweet chocolate bar candy donut gummi bears bear claw liquorice bonbon shortbread.\n\nDonut chocolate bar candy wafer wafer tiramisu. Gummies chocolate cake muffin toffee carrot cake macaroon. Toffee toffee jelly beans danish lollipop cake.",
|
|
||||||
"created": "2021-02-08T02:37:49.724132Z",
|
|
||||||
"user": {
|
|
||||||
"id": 3,
|
|
||||||
"username": "admin",
|
|
||||||
"firstname": "",
|
|
||||||
"lastname": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@@ -1 +0,0 @@
|
|||||||
{"original_checksum":"e959bc7d593245d92685213264e962ba","original_size":963754,"original_mime_type":"application/pdf","media_filename":"2022/lorem-ipsum.pdf","has_archive_version":true,"original_metadata":[],"archive_checksum":"5a1f46a9150bcade978c764b039ce4d0","archive_media_filename":"2022/lorem-ipsum.pdf","archive_size":351160,"archive_metadata":[{"namespace":"http://ns.adobe.com/pdf/1.3/","prefix":"pdf","key":"Producer","value":"pikepdf5.0.1"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"ModifyDate","value":"2022-03-22T04:53:18+00:00"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"CreateDate","value":"2022-03-22T18:05:43+00:00"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"CreatorTool","value":"ocrmypdf13.4.0/TesseractOCR-PDF4.1.1"},{"namespace":"http://ns.adobe.com/xap/1.0/mm/","prefix":"xmpMM","key":"DocumentID","value":"uuid:df27edcf-e34a-11f7-0000-8fa6067a3c04"},{"namespace":"http://purl.org/dc/elements/1.1/","prefix":"dc","key":"format","value":"application/pdf"},{"namespace":"http://purl.org/dc/elements/1.1/","prefix":"dc","key":"title","value":"ScannedDocument"},{"namespace":"http://www.aiim.org/pdfa/ns/id/","prefix":"pdfaid","key":"part","value":"2"},{"namespace":"http://www.aiim.org/pdfa/ns/id/","prefix":"pdfaid","key":"conformance","value":"B"},{"namespace":"http://purl.org/dc/elements/1.1/","prefix":"dc","key":"creator","value":"None"},{"namespace":"http://ns.adobe.com/xap/1.0/","prefix":"xmp","key":"MetadataDate","value":"2022-03-22T21:53:18.882551-07:00"}]}
|
|
@@ -1 +0,0 @@
|
|||||||
{"correspondents":[],"tags":[3],"document_types":[1]}
|
|
@@ -1,73 +0,0 @@
|
|||||||
{
|
|
||||||
"count": 3,
|
|
||||||
"next": null,
|
|
||||||
"previous": null,
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"correspondent": 9,
|
|
||||||
"document_type": 1,
|
|
||||||
"storage_path": null,
|
|
||||||
"title": "No latin title",
|
|
||||||
"content": "Test document PDF \n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla est purus, ultrices in porttitor \nin, accumsan non quam. Nam consectetur porttitor rhoncus. Curabitur eu est et leo feugiat \nauctor vel quis lorem. Ut et ligula dolor, sit amet consequat lorem. Aliquam porta eros sed \nvelit imperdiet egestas. Maecenas tempus eros ut diam ullamcorper id dictum libero \ntempor. Donec quis augue quis magna condimentum lobortis. Quisque imperdiet ipsum vel \nmagna viverra rutrum. Cras viverra molestie urna, vitae vestibulum turpis varius id. \nVestibulum mollis, arcu iaculis bibendum varius, velit sapien blandit metus, ac posuere lorem \nnulla ac dolor. Maecenas urna elit, tincidunt in dapibus nec, vehicula eu dui. Duis lacinia \nfringilla massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur \nridiculus mus. Ut consequat ultricies est, non rhoncus mauris congue porta. Vivamus viverra \nsuscipit felis eget condimentum. Cum sociis natoque penatibus et magnis dis parturient \nmontes, nascetur ridiculus mus. Integer bibendum sagittis ligula, non faucibus nulla volutpat \nvitae. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. \nIn aliquet quam et velit bibendum accumsan. Cum sociis natoque penatibus et magnis dis \nparturient montes, nascetur ridiculus mus. Vestibulum vitae ipsum nec arcu semper \nadipiscing at ac lacus. Praesent id pellentesque orci. Morbi congue viverra nisl nec rhoncus. \nInteger mattis, ipsum a tincidunt commodo, lacus arcu elementum elit, at mollis eros ante ac \nrisus. In volutpat, ante at pretium ultricies, velit magna suscipit enim, aliquet blandit massa \norci nec lorem. Nulla facilisi. Duis eu vehicula arcu. Nulla facilisi. Maecenas pellentesque \nvolutpat felis, quis tristique ligula luctus vel. Sed nec mi eros. Integer augue enim, sollicitudin \nullamcorper mattis eget, aliquam in est. Morbi sollicitudin libero nec augue dignissim ut \nconsectetur dui volutpat. Nulla facilisi. Mauris egestas vestibulum neque cursus tincidunt. \nDonec sit amet pulvinar orci. \nQuisque volutpat pharetra tincidunt. Fusce sapien arcu, molestie eget varius egestas, \nfaucibus ac urna. Sed at nisi in velit egestas aliquam ut a felis. Aenean malesuada iaculis nisl, \nut tempor lacus egestas consequat. Nam nibh lectus, gravida sed egestas ut, feugiat quis \ndolor. Donec eu leo enim, non laoreet ante. Morbi dictum tempor vulputate. Phasellus \nultricies risus vel augue sagittis euismod. Vivamus tincidunt placerat nisi in aliquam. Cras \nquis mi ac nunc pretium aliquam. Aenean elementum erat ac metus commodo rhoncus. \nAliquam nulla augue, porta non sagittis quis, accumsan vitae sem. Phasellus id lectus tortor, \neget pulvinar augue. Etiam eget velit ac purus fringilla blandit. Donec odio odio, sagittis sed \niaculis sed, consectetur eget sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. \nMaecenas accumsan velit vel turpis rutrum in sodales diam placerat. \nQuisque luctus ullamcorper velit sit amet lobortis. Etiam ligula felis, vulputate quis rhoncus \nnec, fermentum eget odio. Vivamus vel ipsum ac augue sodales mollis euismod nec tellus. \nFusce et augue rutrum nunc semper vehicula vel semper nisl. Nam laoreet euismod quam at \nvarius. Sed aliquet auctor nibh. Curabitur malesuada fermentum lacus vel accumsan. Duis \nornare scelerisque nulla, ac pulvinar ligula tempus sit amet. In placerat nulla ac ante \nscelerisque posuere. Phasellus at ante felis. Sed hendrerit risus a metus posuere rutrum. \nPhasellus eu augue dui. Proin in vestibulum ipsum. Aenean accumsan mollis sapien, ut \neleifend sem blandit at. Vivamus luctus mi eget lorem lobortis pharetra. Phasellus at tortor \nquam, a volutpat purus. Etiam sollicitudin arcu vel elit bibendum et imperdiet risus tincidunt. \nEtiam elit velit, posuere ut pulvinar ac, condimentum eget justo. Fusce a erat velit. Vivamus \nimperdiet ultrices orci in hendrerit.",
|
|
||||||
"tags": [
|
|
||||||
4
|
|
||||||
],
|
|
||||||
"created": "2022-03-22T07:24:18Z",
|
|
||||||
"modified": "2022-03-22T07:24:23.264859Z",
|
|
||||||
"added": "2022-03-22T07:24:22.922631Z",
|
|
||||||
"archive_serial_number": null,
|
|
||||||
"original_file_name": "2022-03-22 no latin title.pdf",
|
|
||||||
"archived_file_name": "2022-03-22 no latin title.pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"correspondent": null,
|
|
||||||
"document_type": null,
|
|
||||||
"storage_path": 2,
|
|
||||||
"title": "lorem ipsum dolor sit amet",
|
|
||||||
"content": "Test document PDF",
|
|
||||||
"tags": [],
|
|
||||||
"created": "2022-03-23T07:24:18Z",
|
|
||||||
"modified": "2022-03-23T07:24:23.264859Z",
|
|
||||||
"added": "2022-03-23T07:24:22.922631Z",
|
|
||||||
"archive_serial_number": 12345,
|
|
||||||
"original_file_name": "2022-03-23 lorem ipsum dolor sit amet.pdf",
|
|
||||||
"archived_file_name": "2022-03-23 llorem ipsum dolor sit amet.pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"correspondent": null,
|
|
||||||
"document_type": 1,
|
|
||||||
"storage_path": null,
|
|
||||||
"title": "dolor",
|
|
||||||
"content": "Test document PDF",
|
|
||||||
"tags": [
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"created": "2022-03-24T07:24:18Z",
|
|
||||||
"modified": "2022-03-24T07:24:23.264859Z",
|
|
||||||
"added": "2022-03-24T07:24:22.922631Z",
|
|
||||||
"archive_serial_number": null,
|
|
||||||
"original_file_name": "2022-03-24 dolor.pdf",
|
|
||||||
"archived_file_name": "2022-03-24 dolor.pdf"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"correspondent": 9,
|
|
||||||
"document_type": 1,
|
|
||||||
"storage_path": null,
|
|
||||||
"title": "sit amet",
|
|
||||||
"content": "Test document PDF",
|
|
||||||
"tags": [
|
|
||||||
4, 5
|
|
||||||
],
|
|
||||||
"created": "2022-06-01T07:24:18Z",
|
|
||||||
"modified": "2022-06-01T07:24:23.264859Z",
|
|
||||||
"added": "2022-06-01T07:24:22.922631Z",
|
|
||||||
"archive_serial_number": 12347,
|
|
||||||
"original_file_name": "2022-06-01 sit amet.pdf",
|
|
||||||
"archived_file_name": "2022-06-01 sit amet.pdf"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
Before Width: | Height: | Size: 156 KiB |
@@ -1,293 +0,0 @@
|
|||||||
{
|
|
||||||
"selected_correspondents": [
|
|
||||||
{
|
|
||||||
"id": 62,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 75,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 55,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 56,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 73,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 58,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 44,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 42,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 74,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 54,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 29,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 71,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 68,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 82,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 34,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 41,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 51,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 46,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 40,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 43,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 80,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 70,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 52,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 67,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 53,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 32,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 63,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 35,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 45,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 38,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 79,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 48,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 72,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 78,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 39,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 57,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 61,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 81,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 77,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 69,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 36,
|
|
||||||
"document_count": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 31,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 30,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 50,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 49,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 60,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 47,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 66,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 37,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 28,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 59,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 33,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 76,
|
|
||||||
"document_count": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"selected_tags": [
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"document_count": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 7,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"document_count": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"document_count": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"document_count": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"selected_document_types": [
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 11,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 7,
|
|
||||||
"document_count": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"document_count": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"selected_storage_paths": []
|
|
||||||
}
|
|
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"count": 2,
|
|
||||||
"next": null,
|
|
||||||
"previous": null,
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "IMAP Server",
|
|
||||||
"imap_server": "imap.example.com",
|
|
||||||
"imap_port": 993,
|
|
||||||
"imap_security": 2,
|
|
||||||
"username": "inbox@example.com",
|
|
||||||
"password": "pass",
|
|
||||||
"character_set": "UTF-8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Gmail",
|
|
||||||
"imap_server": "imap.gmail.com",
|
|
||||||
"imap_port": 993,
|
|
||||||
"imap_security": 2,
|
|
||||||
"username": "user@gmail.com",
|
|
||||||
"password": "pass",
|
|
||||||
"character_set": "UTF-8"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"count": 1,
|
|
||||||
"next": null,
|
|
||||||
"previous": null,
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "Gmail",
|
|
||||||
"account": 2,
|
|
||||||
"folder": "INBOX",
|
|
||||||
"filter_from": null,
|
|
||||||
"filter_subject": "[paperless]",
|
|
||||||
"filter_body": null,
|
|
||||||
"filter_attachment_filename": null,
|
|
||||||
"maximum_age": 30,
|
|
||||||
"action": 3,
|
|
||||||
"action_parameter": null,
|
|
||||||
"assign_title_from": 1,
|
|
||||||
"assign_tags": [
|
|
||||||
9
|
|
||||||
],
|
|
||||||
"assign_correspondent_from": 1,
|
|
||||||
"assign_correspondent": 2,
|
|
||||||
"assign_document_type": null,
|
|
||||||
"order": 0,
|
|
||||||
"attachment_type": 2,
|
|
||||||
"consumption_scope": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1 +0,0 @@
|
|||||||
{"version":"v1.7.1","update_available":false,"feature_is_set":true}
|
|
@@ -1,44 +0,0 @@
|
|||||||
{
|
|
||||||
"count": 3,
|
|
||||||
"next": null,
|
|
||||||
"previous": null,
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "Inbox",
|
|
||||||
"show_on_dashboard": true,
|
|
||||||
"show_in_sidebar": true,
|
|
||||||
"sort_field": "created",
|
|
||||||
"sort_reverse": true,
|
|
||||||
"filter_rules": [
|
|
||||||
{
|
|
||||||
"rule_type": 6,
|
|
||||||
"value": "18"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "Recently Added",
|
|
||||||
"show_on_dashboard": true,
|
|
||||||
"show_in_sidebar": false,
|
|
||||||
"sort_field": "created",
|
|
||||||
"sort_reverse": true,
|
|
||||||
"filter_rules": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 11,
|
|
||||||
"name": "Taxes",
|
|
||||||
"show_on_dashboard": false,
|
|
||||||
"show_in_sidebar": true,
|
|
||||||
"sort_field": "created",
|
|
||||||
"sort_reverse": true,
|
|
||||||
"filter_rules": [
|
|
||||||
{
|
|
||||||
"rule_type": 6,
|
|
||||||
"value": "39"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"count": 1,
|
|
||||||
"next": null,
|
|
||||||
"previous": null,
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"slug": "year-title",
|
|
||||||
"name": "Year - Title",
|
|
||||||
"path": "{created_year}/{title}",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 6,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"document_count": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"count": 8,
|
|
||||||
"next": null,
|
|
||||||
"previous": null,
|
|
||||||
"results": [
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"slug": "another-sample-tag",
|
|
||||||
"name": "Another Sample Tag",
|
|
||||||
"color": "#a6cee3",
|
|
||||||
"text_color": "#000000",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 6,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 7,
|
|
||||||
"slug": "newone",
|
|
||||||
"name": "NewOne",
|
|
||||||
"color": "#9e4ad1",
|
|
||||||
"text_color": "#ffffff",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 1,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"slug": "partial-tag",
|
|
||||||
"name": "Partial Tag",
|
|
||||||
"color": "#72dba7",
|
|
||||||
"text_color": "#000000",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 1,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"slug": "tag-2",
|
|
||||||
"name": "Tag 2",
|
|
||||||
"color": "#612db7",
|
|
||||||
"text_color": "#ffffff",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 1,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"slug": "tag-3",
|
|
||||||
"name": "Tag 3",
|
|
||||||
"color": "#b2df8a",
|
|
||||||
"text_color": "#000000",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 1,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"slug": "tagwithpartial",
|
|
||||||
"name": "TagWithPartial",
|
|
||||||
"color": "#3b2db4",
|
|
||||||
"text_color": "#ffffff",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 6,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"slug": "test-another",
|
|
||||||
"name": "Test Another",
|
|
||||||
"color": "#3ccea5",
|
|
||||||
"text_color": "#000000",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 4,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"slug": "test-tag",
|
|
||||||
"name": "Test Tag",
|
|
||||||
"color": "#fb9a99",
|
|
||||||
"text_color": "#000000",
|
|
||||||
"match": "",
|
|
||||||
"matching_algorithm": 1,
|
|
||||||
"is_insensitive": true,
|
|
||||||
"is_inbox_tag": false,
|
|
||||||
"document_count": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@@ -1,142 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 141,
|
|
||||||
"type": "file",
|
|
||||||
"result": "sample 2.pdf: Not consuming sample 2.pdf: It is a duplicate. : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ngx.nosync-udqDZzaE/lib/python3.8/site-packages/django_q/cluster.py\", line 432, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/paperless-ngx/src/documents/tasks.py\", line 316, in consume_file\n document = Consumer().try_consume_file(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 218, in try_consume_file\n self.pre_check_duplicate()\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 113, in pre_check_duplicate\n self._fail(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 84, in _fail\n raise ConsumerError(f\"{self.filename}: {log_message or message}\")\ndocuments.consumer.ConsumerError: sample 2.pdf: Not consuming sample 2.pdf: It is a duplicate.\n",
|
|
||||||
"status": "FAILURE",
|
|
||||||
"task_id": "d8ddbe298a42427d82553206ddf0bc94",
|
|
||||||
"task_file_name": "sample 2.pdf",
|
|
||||||
"date_created": "2022-05-26T23:17:38.333474-07:00",
|
|
||||||
"date_done": null,
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 132,
|
|
||||||
"type": "file",
|
|
||||||
"result": " : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 131, in get_version\n env=env,\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 68, in run\n proc = subprocess_run(args, env=env, **kwargs)\n File \"/Users/admin/opt/anaconda3/envs/paperless-ng/lib/python3.6/subprocess.py\", line 423, in run\n with Popen(*popenargs, **kwargs) as process:\n File \"/Users/admin/opt/anaconda3/envs/paperless-ng/lib/python3.6/subprocess.py\", line 729, in __init__\n restore_signals, start_new_session)\n File \"/Users/admin/opt/anaconda3/envs/paperless-ng/lib/python3.6/subprocess.py\", line 1364, in _execute_child\n raise child_exception_type(errno_num, err_msg, err_filename)\nFileNotFoundError: [Errno 2] No such file or directory: 'unpaper': 'unpaper'\n\nThe above exception was the direct cause of the following exception:\n\nTraceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 287, in check_external_program\n found_version = version_checker()\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_exec/unpaper.py\", line 34, in version\n return get_version('unpaper')\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 137, in get_version\n ) from e\nocrmypdf.exceptions.MissingDependencyError: Could not find program 'unpaper' on the PATH\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/paperless_tesseract/parsers.py\", line 176, in parse\n ocrmypdf.ocr(**ocr_args)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/api.py\", line 315, in ocr\n check_options(options, plugin_manager)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_validation.py\", line 260, in check_options\n _check_options(options, plugin_manager, ocr_engine_languages)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_validation.py\", line 250, in _check_options\n check_options_preprocessing(options)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/_validation.py\", line 128, in check_options_preprocessing\n required_for=['--clean, --clean-final'],\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/ocrmypdf/subprocess.py\", line 293, in check_external_program\n raise MissingDependencyError()\nocrmypdf.exceptions.MissingDependencyError\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 179, in try_consume_file\n document_parser.parse(self.path, mime_type, self.filename)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/paperless_tesseract/parsers.py\", line 197, in parse\n raise ParseError(e)\ndocuments.parsers.ParseError\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/django_q/cluster.py\", line 436, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/tasks.py\", line 73, in consume_file\n override_tag_ids=override_tag_ids)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 196, in try_consume_file\n raise ConsumerError(e)\ndocuments.consumer.ConsumerError\n",
|
|
||||||
"status": "FAILURE",
|
|
||||||
"task_id": "4c554075552c4cc985abd76e6f274c90",
|
|
||||||
"task_file_name": "pdf-sample 10.24.48 PM.pdf",
|
|
||||||
"date_created": "2022-05-26T14:26:07.846365-07:00",
|
|
||||||
"date_done": null,
|
|
||||||
"acknowledged": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 115,
|
|
||||||
"type": "file",
|
|
||||||
"result": "2021-01-24 2021-01-20 sample_wide_orange.pdf: Document is a duplicate : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/django_q/cluster.py\", line 436, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/tasks.py\", line 75, in consume_file\n task_id=task_id\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 168, in try_consume_file\n self.pre_check_duplicate()\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 85, in pre_check_duplicate\n self._fail(\"Document is a duplicate\")\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 53, in _fail\n raise ConsumerError(f\"{self.filename}: {message}\")\ndocuments.consumer.ConsumerError: 2021-01-24 2021-01-20 sample_wide_orange.pdf: Document is a duplicate\n",
|
|
||||||
"status": "FAILURE",
|
|
||||||
"task_id": "86494713646a4364b01da17aadca071d",
|
|
||||||
"task_file_name": "2021-01-24 2021-01-20 sample_wide_orange.pdf",
|
|
||||||
"date_created": "2022-05-26T14:26:07.817608-07:00",
|
|
||||||
"date_done": null,
|
|
||||||
"acknowledged": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 85,
|
|
||||||
"type": "file",
|
|
||||||
"result": "cannot open resource : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/django_q/cluster.py\", line 436, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/tasks.py\", line 81, in consume_file\n task_id=task_id\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/consumer.py\", line 244, in try_consume_file\n self.path, mime_type, self.filename)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/documents/parsers.py\", line 302, in get_optimised_thumbnail\n thumbnail = self.get_thumbnail(document_path, mime_type, file_name)\n File \"/Users/admin/Documents/Work/Contributions/paperless-ng/src/paperless_text/parsers.py\", line 29, in get_thumbnail\n layout_engine=ImageFont.LAYOUT_BASIC)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/PIL/ImageFont.py\", line 852, in truetype\n return freetype(font)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/PIL/ImageFont.py\", line 849, in freetype\n return FreeTypeFont(font, size, index, encoding, layout_engine)\n File \"/Users/admin/.local/share/virtualenvs/paperless-ng/lib/python3.6/site-packages/PIL/ImageFont.py\", line 210, in __init__\n font, size, index, encoding, layout_engine=layout_engine\nOSError: cannot open resource\n",
|
|
||||||
"status": "FAILURE",
|
|
||||||
"task_id": "abca803fa46342e1ac81f3d3f2080e79",
|
|
||||||
"task_file_name": "simple.txt",
|
|
||||||
"date_created": "2022-05-26T14:26:07.771541-07:00",
|
|
||||||
"date_done": null,
|
|
||||||
"acknowledged": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 41,
|
|
||||||
"type": "file",
|
|
||||||
"result": "commands.txt: Not consuming commands.txt: It is a duplicate. : Traceback (most recent call last):\n File \"/Users/admin/.local/share/virtualenvs/paperless-ngx.nosync-udqDZzaE/lib/python3.8/site-packages/django_q/cluster.py\", line 432, in worker\n res = f(*task[\"args\"], **task[\"kwargs\"])\n File \"/Users/admin/Documents/paperless-ngx/src/documents/tasks.py\", line 70, in consume_file\n document = Consumer().try_consume_file(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 199, in try_consume_file\n self.pre_check_duplicate()\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 97, in pre_check_duplicate\n self._fail(\n File \"/Users/admin/Documents/paperless-ngx/src/documents/consumer.py\", line 69, in _fail\n raise ConsumerError(f\"{self.filename}: {log_message or message}\")\ndocuments.consumer.ConsumerError: commands.txt: Not consuming commands.txt: It is a duplicate.\n",
|
|
||||||
"status": "FAILURE",
|
|
||||||
"task_id": "0af67672e8e14404b060d4cf8f69313d",
|
|
||||||
"task_file_name": "commands.txt",
|
|
||||||
"date_created": "2022-05-26T14:26:07.704247-07:00",
|
|
||||||
"date_done": null,
|
|
||||||
"acknowledged": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 10,
|
|
||||||
"type": "file",
|
|
||||||
"result": "Success. New document id 260 created",
|
|
||||||
"status": "SUCCESS",
|
|
||||||
"task_id": "b7629a0f41bd40c7a3ea4680341321b5",
|
|
||||||
"task_file_name": "2022-03-24+Sonstige+ScanPC2022-03-24_081058.pdf",
|
|
||||||
"date_created": "2022-05-26T14:26:07.670577-07:00",
|
|
||||||
"date_done": "2022-05-26T14:26:07.670577-07:00",
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": 260
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 9,
|
|
||||||
"type": "file",
|
|
||||||
"result": "Success. New document id 261 created",
|
|
||||||
"status": "SUCCESS",
|
|
||||||
"task_id": "02e276a86a424ccfb83309df5d8594be",
|
|
||||||
"task_file_name": "2sample-pdf-with-images.pdf",
|
|
||||||
"date_created": "2022-05-26T14:26:07.668987-07:00",
|
|
||||||
"date_done": "2022-05-26T14:26:07.668987-07:00",
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": 261
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 8,
|
|
||||||
"type": "file",
|
|
||||||
"result": "Success. New document id 262 created",
|
|
||||||
"status": "SUCCESS",
|
|
||||||
"task_id": "41229b8be9b445c0a523697d0f58f13e",
|
|
||||||
"task_file_name": "2sample-pdf-with-images_pw.pdf",
|
|
||||||
"date_created": "2022-05-26T14:26:07.667993-07:00",
|
|
||||||
"date_done": "2022-05-26T14:26:07.667993-07:00",
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": 262
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 6,
|
|
||||||
"type": "file",
|
|
||||||
"result": "Success. New document id 264 created",
|
|
||||||
"status": "SUCCESS",
|
|
||||||
"task_id": "bbbca32d408c4619bd0b512a8327c773",
|
|
||||||
"task_file_name": "homebridge.log",
|
|
||||||
"date_created": "2022-05-26T14:26:07.665560-07:00",
|
|
||||||
"date_done": "2022-05-26T14:26:07.665560-07:00",
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": 264
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"type": "file",
|
|
||||||
"result": "Success. New document id 265 created",
|
|
||||||
"status": "SUCCESS",
|
|
||||||
"task_id": "00ab285ab4bf482ab30c7d580b252ecb",
|
|
||||||
"task_file_name": "IMG_7459.PNG",
|
|
||||||
"date_created": "2022-05-26T14:26:07.664506-07:00",
|
|
||||||
"date_done": "2022-05-26T14:26:07.664506-07:00",
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": 265
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"type": "file",
|
|
||||||
"result": "Success. New document id 267 created",
|
|
||||||
"status": "SUCCESS",
|
|
||||||
"task_id": "289c5163cfec410db42948a0cacbeb9c",
|
|
||||||
"task_file_name": "IMG_7459.PNG",
|
|
||||||
"date_created": "2022-05-26T14:26:07.659661-07:00",
|
|
||||||
"date_done": "2022-05-26T14:26:07.659661-07:00",
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": 267
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"type": "file",
|
|
||||||
"result": null,
|
|
||||||
"status": "STARTED",
|
|
||||||
"task_id": "7a4ebdb2bde04311935284027ef8ca65",
|
|
||||||
"task_file_name": "2019-08-04 DSA Questionnaire - 5-8-19.pdf",
|
|
||||||
"date_created": "2022-05-26T14:26:07.655276-07:00",
|
|
||||||
"date_done": null,
|
|
||||||
"acknowledged": false,
|
|
||||||
"related_document": null
|
|
||||||
}
|
|
||||||
]
|
|
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"user_id": 1,
|
|
||||||
"username": "admin",
|
|
||||||
"display_name": "Admin",
|
|
||||||
"settings": {
|
|
||||||
"language": "",
|
|
||||||
"bulk_edit": {
|
|
||||||
"confirmation_dialogs": true,
|
|
||||||
"apply_on_close": false
|
|
||||||
},
|
|
||||||
"documentListSize": 50,
|
|
||||||
"dark_mode": {
|
|
||||||
"use_system": true,
|
|
||||||
"enabled": "false",
|
|
||||||
"thumb_inverted": "true"
|
|
||||||
},
|
|
||||||
"theme": {
|
|
||||||
"color": "#b198e5"
|
|
||||||
},
|
|
||||||
"document_details": {
|
|
||||||
"native_pdf_viewer": false
|
|
||||||
},
|
|
||||||
"date_display": {
|
|
||||||
"date_locale": "",
|
|
||||||
"date_format": "mediumDate"
|
|
||||||
},
|
|
||||||
"notifications": {
|
|
||||||
"consumer_new_documents": true,
|
|
||||||
"consumer_success": true,
|
|
||||||
"consumer_failed": true,
|
|
||||||
"consumer_suppress_on_dashboard": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
// Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
|
|
||||||
// For more info, visit https://on.cypress.io/plugins-api
|
|
||||||
module.exports = (on, config) => {}
|
|
@@ -1,43 +0,0 @@
|
|||||||
// ***********************************************
|
|
||||||
// This example namespace declaration will help
|
|
||||||
// with Intellisense and code completion in your
|
|
||||||
// IDE or Text Editor.
|
|
||||||
// ***********************************************
|
|
||||||
// declare namespace Cypress {
|
|
||||||
// interface Chainable<Subject = any> {
|
|
||||||
// customCommand(param: any): typeof customCommand;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// function customCommand(param: any): void {
|
|
||||||
// console.warn(param);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// NOTE: You can use it like so:
|
|
||||||
// Cypress.Commands.add('customCommand', customCommand);
|
|
||||||
//
|
|
||||||
// ***********************************************
|
|
||||||
// This example commands.js shows you how to
|
|
||||||
// create various custom commands and overwrite
|
|
||||||
// existing commands.
|
|
||||||
//
|
|
||||||
// For more comprehensive examples of custom
|
|
||||||
// commands please read more here:
|
|
||||||
// https://on.cypress.io/custom-commands
|
|
||||||
// ***********************************************
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a parent command --
|
|
||||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a child command --
|
|
||||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This is a dual command --
|
|
||||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// -- This will overwrite an existing command --
|
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
@@ -1,47 +0,0 @@
|
|||||||
// mock API methods
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.intercept('http://localhost:8000/api/ui_settings/', {
|
|
||||||
fixture: 'ui_settings/settings.json',
|
|
||||||
}).as('ui-settings')
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/remote_version/', {
|
|
||||||
fixture: 'remote_version/remote_version.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/saved_views/*', {
|
|
||||||
fixture: 'saved_views/savedviews.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/tags/*', {
|
|
||||||
fixture: 'tags/tags.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/correspondents/*', {
|
|
||||||
fixture: 'correspondents/correspondents.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/document_types/*', {
|
|
||||||
fixture: 'document_types/doctypes.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/storage_paths/*', {
|
|
||||||
fixture: 'storage_paths/storage_paths.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/tasks/', {
|
|
||||||
fixture: 'tasks/tasks.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/documents/1/metadata/', {
|
|
||||||
fixture: 'documents/1/metadata.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/documents/1/suggestions/', {
|
|
||||||
fixture: 'documents/1/suggestions.json',
|
|
||||||
})
|
|
||||||
|
|
||||||
cy.intercept('http://localhost:8000/api/documents/1/thumb/', {
|
|
||||||
fixture: 'documents/lorem-ipsum.png',
|
|
||||||
})
|
|
||||||
})
|
|
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../tsconfig.json",
|
|
||||||
"include": ["**/*.ts"],
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": false,
|
|
||||||
"types": ["cypress"]
|
|
||||||
}
|
|
||||||
}
|
|
61
src-ui/e2e/dashboard/dashboard.spec.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
const REQUESTS_HAR1 = 'e2e/dashboard/requests/api-dashboard1.har'
|
||||||
|
const REQUESTS_HAR2 = 'e2e/dashboard/requests/api-dashboard2.har'
|
||||||
|
const REQUESTS_HAR3 = 'e2e/dashboard/requests/api-dashboard3.har'
|
||||||
|
const REQUESTS_HAR4 = 'e2e/dashboard/requests/api-dashboard4.har'
|
||||||
|
|
||||||
|
test('dashboard inbox link', async ({ page }) => {
|
||||||
|
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||||
|
await page.goto('/dashboard')
|
||||||
|
await page.getByRole('link', { name: 'Documents in inbox' }).click()
|
||||||
|
await expect(page).toHaveURL(/tags__id__all=9/)
|
||||||
|
await expect(page.locator('app-document-list')).toHaveText(/8 documents/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dashboard total documents link', async ({ page }) => {
|
||||||
|
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
|
||||||
|
await page.goto('/dashboard')
|
||||||
|
await page.getByRole('link').filter({ hasText: 'Total documents' }).click()
|
||||||
|
await expect(page).toHaveURL(/documents/)
|
||||||
|
await expect(page.locator('app-document-list')).toHaveText(/61 documents/)
|
||||||
|
await page.getByRole('button', { name: 'Reset filters' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dashboard saved view show all', async ({ page }) => {
|
||||||
|
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
|
||||||
|
await page.goto('/dashboard')
|
||||||
|
await page
|
||||||
|
.locator('app-widget-frame')
|
||||||
|
.filter({ hasText: 'Inbox' })
|
||||||
|
.getByRole('link', { name: 'Show all' })
|
||||||
|
.click()
|
||||||
|
await expect(page).toHaveURL(/view\/7/)
|
||||||
|
await expect(page.locator('app-document-list')).toHaveText(/8 documents/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dashboard saved view document links', async ({ page }) => {
|
||||||
|
await page.routeFromHAR(REQUESTS_HAR4, { notFound: 'fallback' })
|
||||||
|
await page.goto('/dashboard')
|
||||||
|
await page
|
||||||
|
.locator('app-widget-frame')
|
||||||
|
.filter({ hasText: 'Inbox' })
|
||||||
|
.locator('table')
|
||||||
|
.getByRole('link', { name: /test/ })
|
||||||
|
.first()
|
||||||
|
.click({ position: { x: 0, y: 0 } })
|
||||||
|
await expect(page).toHaveURL(/documents\/310\/details/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('test slim sidebar', async ({ page }) => {
|
||||||
|
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
|
||||||
|
await page.goto('/dashboard')
|
||||||
|
await page.locator('#sidebarMenu').getByRole('button').click()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('link', { name: 'Dashboard' }).getByText('Dashboard')
|
||||||
|
).toBeHidden()
|
||||||
|
await page.locator('#sidebarMenu').getByRole('button').click()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('link', { name: 'Dashboard' }).getByText('Dashboard')
|
||||||
|
).toBeVisible()
|
||||||
|
})
|