Compare commits
676 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceec4dcfe6 | ||
|
|
7177184678 | ||
|
|
0523b31b96 | ||
|
|
80030dee32 | ||
|
|
cb43770025 | ||
|
|
eaba7e5255 | ||
|
|
49dbacdccd | ||
|
|
d94acdd89a | ||
|
|
06d9c03e92 | ||
|
|
9d54046140 | ||
|
|
dec3d629d4 | ||
|
|
8118afd0ac | ||
|
|
577c0efa9c | ||
|
|
dcf0bc65bf | ||
|
|
c211b96719 | ||
|
|
1e7d3b25c2 | ||
|
|
b16982df0f | ||
|
|
540ddf47a7 | ||
|
|
02730bb9bf | ||
|
|
d1d8e3e340 | ||
|
|
42a39b3f1d | ||
|
|
dd503695a1 | ||
|
|
1d66bbe40a | ||
|
|
5da9d74da6 | ||
|
|
b14b9114f8 | ||
|
|
8a43413f88 | ||
|
|
c3bd1188aa | ||
|
|
fc209f64de | ||
|
|
96afaca0ad | ||
|
|
f663237a16 | ||
|
|
ac3ead8dce | ||
|
|
7c9a75e725 | ||
|
|
596c73288f | ||
|
|
d1d7b23da6 | ||
|
|
e677c78471 | ||
|
|
9734f707f0 | ||
|
|
e5fa96b666 | ||
|
|
bcef7d3f73 | ||
|
|
aed3af5838 | ||
|
|
6bd34c7632 | ||
|
|
040584c865 | ||
|
|
ecb2d1d81b | ||
|
|
a74c72db45 | ||
|
|
f5b1e10618 | ||
|
|
e7fed5a45b | ||
|
|
f8dfbf7eee | ||
|
|
fca5fc5b87 | ||
|
|
4ee19be63d | ||
|
|
b2ad3f7368 | ||
|
|
b73d278ded | ||
|
|
6366cf0b18 | ||
|
|
f73518029c | ||
|
|
c782f21b0f | ||
|
|
0942fc7042 | ||
|
|
c1987237d0 | ||
|
|
cf85404dd2 | ||
|
|
6f226fa549 | ||
|
|
228ebea66c | ||
|
|
bb19834042 | ||
|
|
7870e89bc0 | ||
|
|
8888b9f446 | ||
|
|
f6e5c6f0de | ||
|
|
e7659ea36b | ||
|
|
7730ccd611 | ||
|
|
80890075e8 | ||
|
|
9b04004120 | ||
|
|
a73d7f14b7 | ||
|
|
ffe960a8c2 | ||
|
|
73c80732d9 | ||
|
|
8e2663bf7b | ||
|
|
e697e58164 | ||
|
|
c01496624a | ||
|
|
8906408a8f | ||
|
|
3ec7951380 | ||
|
|
b30aefa522 | ||
|
|
bc8a750dfe | ||
|
|
f1f7f17ade | ||
|
|
9182d33e1c | ||
|
|
7d836a3728 | ||
|
|
010d16fc1d | ||
|
|
fa89ba4665 | ||
|
|
a0752904c1 | ||
|
|
371718634b | ||
|
|
0f4f8751f2 | ||
|
|
ec45ee471f | ||
|
|
6fffcbac4e | ||
|
|
2948589fcd | ||
|
|
ecd0b2ee0d | ||
|
|
205f11d677 | ||
|
|
949f0f277f | ||
|
|
665e45889c | ||
|
|
8d87e0d5f9 | ||
|
|
46d80464d2 | ||
|
|
829ed9fb6d | ||
|
|
988d3e5bdd | ||
|
|
6eb3ab0635 | ||
|
|
c2e03bbfab | ||
|
|
608a0015ee | ||
|
|
f81857acce | ||
|
|
b1e0d5b39f | ||
|
|
68cf7a2173 | ||
|
|
89d1c06441 | ||
|
|
2bebb5f0f8 | ||
|
|
683b11d265 | ||
|
|
4d1b9dd211 | ||
|
|
b8f35ce932 | ||
|
|
a078f0b787 | ||
|
|
7401d16e45 | ||
|
|
958a44f95e | ||
|
|
e08239781f | ||
|
|
c29698dffa | ||
|
|
81de95632a | ||
|
|
7f2d221083 | ||
|
|
74b7cd8e81 | ||
|
|
6cb51b4eb4 | ||
|
|
f09bf3e1d0 | ||
|
|
6f345be3e4 | ||
|
|
ddd4ffa4ca | ||
|
|
deabc80fd7 | ||
|
|
b6a51bed51 | ||
|
|
0426629a59 | ||
|
|
0358e42d2c | ||
|
|
3768e3345f | ||
|
|
16e434be66 | ||
|
|
bf303c536a | ||
|
|
43a460993c | ||
|
|
7f0673ee70 | ||
|
|
4c3099a086 | ||
|
|
f0bc9167b1 | ||
|
|
23d646c456 | ||
|
|
76add9e527 | ||
|
|
c63cc5a2d2 | ||
|
|
25c8788390 | ||
|
|
aa52b07bb1 | ||
|
|
76b466f649 | ||
|
|
8ecc2da947 | ||
|
|
8650d2ffe7 | ||
|
|
34d7d2c8c4 | ||
|
|
201329abce | ||
|
|
f2b5dd3787 | ||
|
|
5072bbb2cb | ||
|
|
6b19ab6613 | ||
|
|
730be5ef6b | ||
|
|
46ee595389 | ||
|
|
dee465ab86 | ||
|
|
209f9fa77f | ||
|
|
ba8c09f454 | ||
|
|
16a34defc0 | ||
|
|
7d1e03075d | ||
|
|
1c25f6ee69 | ||
|
|
aa172b8bb5 | ||
|
|
4711e7bcd5 | ||
|
|
8a47aee137 | ||
|
|
190cb99a79 | ||
|
|
603203848a | ||
|
|
5e6f14b5dc | ||
|
|
976eb5583d | ||
|
|
b92152693f | ||
|
|
7ec24d9d77 | ||
|
|
20ebbf6611 | ||
|
|
ba7e71a7c3 | ||
|
|
8973c4598f | ||
|
|
18889ad725 | ||
|
|
73ccbe912f | ||
|
|
84e3a98303 | ||
|
|
7dd5b34d42 | ||
|
|
4470d0a704 | ||
|
|
a76e01d2b7 | ||
|
|
2697093ac1 | ||
|
|
59f9964e80 | ||
|
|
1516d9932b | ||
|
|
fcb115f42d | ||
|
|
e410272e6b | ||
|
|
87f1881b42 | ||
|
|
c0d85f3d85 | ||
|
|
98d79b8ed9 | ||
|
|
fe80730bb1 | ||
|
|
6c8ee96e6a | ||
|
|
b521dec8f9 | ||
|
|
e9baf0c4b6 | ||
|
|
e1a6f593e1 | ||
|
|
4b068b3058 | ||
|
|
da54bd6c21 | ||
|
|
0d179eca4d | ||
|
|
dacd511d24 | ||
|
|
c44b37c50c | ||
|
|
a721dc1f31 | ||
|
|
d2e6d23741 | ||
|
|
5f4a0317ab | ||
|
|
22f4be8f54 | ||
|
|
eeadc532fe | ||
|
|
93a35ad251 | ||
|
|
99787287bb | ||
|
|
bdd523190e | ||
|
|
4c1dd5c097 | ||
|
|
e1f658633d | ||
|
|
9c79105c02 | ||
|
|
6d5ceae8b4 | ||
|
|
381f09087a | ||
|
|
426b38bb33 | ||
|
|
488d98045e | ||
|
|
7955e0720b | ||
|
|
e017a19985 | ||
|
|
f8df76f526 | ||
|
|
11ebaec5f0 | ||
|
|
326b35a7ac | ||
|
|
5bf15548d0 | ||
|
|
6a734c0139 | ||
|
|
81b6f4d6f6 | ||
|
|
0b92d94570 | ||
|
|
fc5506179a | ||
|
|
0fe34ad224 | ||
|
|
54f35701a2 | ||
|
|
a809404ce1 | ||
|
|
fb32e44b47 | ||
|
|
e9c0369062 | ||
|
|
7358b3fe31 | ||
|
|
2a1f759e9e | ||
|
|
2fccb8c367 | ||
|
|
e039d95192 | ||
|
|
0f96031d6f | ||
|
|
6214fc84fa | ||
|
|
47578e02e3 | ||
|
|
35a4379b67 | ||
|
|
23f84642e6 | ||
|
|
edb9e85efd | ||
|
|
2d2c598fa6 | ||
|
|
cf4836dc75 | ||
|
|
d8306559fd | ||
|
|
e8c9d1c539 | ||
|
|
d8f415f8ab | ||
|
|
7b6579ac8a | ||
|
|
057307181e | ||
|
|
4fb832c042 | ||
|
|
e503cb69f2 | ||
|
|
95811e99bc | ||
|
|
62fff5ca60 | ||
|
|
5b28aa0848 | ||
|
|
db5aad8eb6 | ||
|
|
977ec33918 | ||
|
|
1819377897 | ||
|
|
f1b7bd59f6 | ||
|
|
f3afd5cb79 | ||
|
|
e6a5bf116e | ||
|
|
21b5a76fa7 | ||
|
|
b6263eb607 | ||
|
|
c8257e848e | ||
|
|
05bb7c8553 | ||
|
|
b600b11415 | ||
|
|
019ce80fc5 | ||
|
|
8cea2f75b3 | ||
|
|
6914063853 | ||
|
|
43e0d4a856 | ||
|
|
066d8e8d6c | ||
|
|
948e05c083 | ||
|
|
fb5b28d9cb | ||
|
|
677bce376b | ||
|
|
8faa96f5e6 | ||
|
|
f62806f6c9 | ||
|
|
58835b7e53 | ||
|
|
7a5298a755 | ||
|
|
bc4a6462ce | ||
|
|
ac3673e111 | ||
|
|
c746c1931d | ||
|
|
586d198d47 | ||
|
|
9515ceeb42 | ||
|
|
e8b4e9af46 | ||
|
|
10e399b3c3 | ||
|
|
dcbc3286e2 | ||
|
|
b185f9b56e | ||
|
|
7096b3dab9 | ||
|
|
36cacdf598 | ||
|
|
4e48ffc14d | ||
|
|
e119bc55ea | ||
|
|
1ce3068a99 | ||
|
|
d562d1a60d | ||
|
|
9f858398ab | ||
|
|
0ac80e8387 | ||
|
|
0dca0b92d1 | ||
|
|
c9b36ba32e | ||
|
|
f2c4e78381 | ||
|
|
05bff54b71 | ||
|
|
2bd163d92a | ||
|
|
5e27ba5c8c | ||
|
|
5aaeb3b76d | ||
|
|
36fb9f562a | ||
|
|
ad99bf1801 | ||
|
|
4c2a094255 | ||
|
|
97693cc611 | ||
|
|
c6d4fcd08f | ||
|
|
dd7b9ddd85 | ||
|
|
26d62e4117 | ||
|
|
babd7783af | ||
|
|
1529e796df | ||
|
|
d4b904b92b | ||
|
|
12d4177823 | ||
|
|
8142b32f38 | ||
|
|
c5abbb4e1c | ||
|
|
65ac73414f | ||
|
|
ede4213c8e | ||
|
|
b60d291490 | ||
|
|
b9ede79888 | ||
|
|
3d2cb838d1 | ||
|
|
778734419d | ||
|
|
be8683f556 | ||
|
|
c3450f4614 | ||
|
|
5881bc9ab0 | ||
|
|
a2fb499a20 | ||
|
|
411a928fea | ||
|
|
f5d02cdde9 | ||
|
|
c9340af8d0 | ||
|
|
a722bcc13f | ||
|
|
77fe3cfc60 | ||
|
|
470f93cefc | ||
|
|
92fde4dd12 | ||
|
|
95bc92955f | ||
|
|
f2f914221c | ||
|
|
c2d8038c63 | ||
|
|
cb8ac5ebf1 | ||
|
|
aa78e3ab1f | ||
|
|
bc00165094 | ||
|
|
94ef59602f | ||
|
|
14e2f84ceb | ||
|
|
f228fa5540 | ||
|
|
f2d2c1cbf8 | ||
|
|
d9be370e24 | ||
|
|
727c63b98e | ||
|
|
34dfb49b71 | ||
|
|
0b0a704d44 | ||
|
|
2d99d0bf13 | ||
|
|
1790df2090 | ||
|
|
10570ade44 | ||
|
|
43526d9d1a | ||
|
|
2636f876ab | ||
|
|
eed9da1471 | ||
|
|
9a2ebbabe2 | ||
|
|
716396a726 | ||
|
|
0727496601 | ||
|
|
194030fcfc | ||
|
|
b3b644527d | ||
|
|
7e5beeff46 | ||
|
|
a47b69bcec | ||
|
|
6ec6a23861 | ||
|
|
c9cc0d3d5d | ||
|
|
28d2b35718 | ||
|
|
b4f131be50 | ||
|
|
d0b359561f | ||
|
|
453636dfe2 | ||
|
|
b1605aa6d3 | ||
|
|
23503b80a4 | ||
|
|
0d69fbd9a3 | ||
|
|
0d665e528f | ||
|
|
de0b8bb7b2 | ||
|
|
84da110085 | ||
|
|
6b0d49b1fc | ||
|
|
4c20772e11 | ||
|
|
68f8348dde | ||
|
|
5023e77296 | ||
|
|
95316cbe8c | ||
|
|
cd454bae51 | ||
|
|
241201657c | ||
|
|
9eefaddd9b | ||
|
|
d6d47bbd6b | ||
|
|
82c883f95e | ||
|
|
dd40b0d9b9 | ||
|
|
963837ef1d | ||
|
|
66863b72f7 | ||
|
|
89773447a5 | ||
|
|
6d899a6335 | ||
|
|
28672c0114 | ||
|
|
b8300b7121 | ||
|
|
584ef4d4bd | ||
|
|
e8295a944a | ||
|
|
f8f5698ad0 | ||
|
|
700f32718e | ||
|
|
54d92a2708 | ||
|
|
ba47e3b2fe | ||
|
|
6e5405eeed | ||
|
|
45326e664f | ||
|
|
6ce44f7092 | ||
|
|
b320419088 | ||
|
|
ca183a4fb8 | ||
|
|
895bb755cd | ||
|
|
a9e715dc50 | ||
|
|
7cb046c542 | ||
|
|
cd03faf0fc | ||
|
|
87ba03b224 | ||
|
|
6458f91e1c | ||
|
|
312ebbbcc0 | ||
|
|
060a7ad80c | ||
|
|
ae893abc5f | ||
|
|
12d6415f7f | ||
|
|
897ac75281 | ||
|
|
cec551c3de | ||
|
|
cb98c913d4 | ||
|
|
55a9d945cc | ||
|
|
cc7ec4f0c5 | ||
|
|
265b81a52b | ||
|
|
b42b09ccbe | ||
|
|
118071ba4b | ||
|
|
73b85eced4 | ||
|
|
997a0a433f | ||
|
|
0d7e344ca3 | ||
|
|
1884d50c3b | ||
|
|
f5fad7a01d | ||
|
|
5c2ed2b2f9 | ||
|
|
05475eb4fc | ||
|
|
9e6cc302c0 | ||
|
|
d422421cf9 | ||
|
|
23a3ef069e | ||
|
|
2a81ea90db | ||
|
|
5fb7207d65 | ||
|
|
d79d864825 | ||
|
|
d249b8b202 | ||
|
|
e9bd68f3b0 | ||
|
|
506e088236 | ||
|
|
c906d296be | ||
|
|
3b7f6ccf8e | ||
|
|
f1a7d2f8d0 | ||
|
|
fb13ded8e8 | ||
|
|
85e4ff67e4 | ||
|
|
6250efa208 | ||
|
|
f1e1a27408 | ||
|
|
076358ab79 | ||
|
|
d1efc14bb9 | ||
|
|
508b7b326f | ||
|
|
d1284972a3 | ||
|
|
cdba1d0c52 | ||
|
|
ec28375208 | ||
|
|
01068a9217 | ||
|
|
7f01753bc5 | ||
|
|
0e223a056e | ||
|
|
9d08f9bed1 | ||
|
|
2cabeb8f68 | ||
|
|
7aaebab348 | ||
|
|
928cdfe2ae | ||
|
|
edb7b4dc17 | ||
|
|
85ee63af43 | ||
|
|
74b23a0bc5 | ||
|
|
be6c0bb850 | ||
|
|
ddb670ae1e | ||
|
|
4752758cf7 | ||
|
|
bd8aab4cba | ||
|
|
c61ede4153 | ||
|
|
efd46d6bd3 | ||
|
|
f2a8abb264 | ||
|
|
978aadc9b1 | ||
|
|
0626f07270 | ||
|
|
0afc8c9e5c | ||
|
|
28480c6acd | ||
|
|
826cdddca7 | ||
|
|
ec92ad9f47 | ||
|
|
ce97b9b9fd | ||
|
|
bbd93e111d | ||
|
|
f7c7d50e54 | ||
|
|
854d8bb705 | ||
|
|
7c0f261a97 | ||
|
|
7b9861b2c6 | ||
|
|
3a8fcbf4bc | ||
|
|
83d9247df6 | ||
|
|
d32286a13d | ||
|
|
621936f461 | ||
|
|
802e715fae | ||
|
|
adcafff384 | ||
|
|
a5ce1cf1e1 | ||
|
|
87d18a3089 | ||
|
|
e7fc0e97d6 | ||
|
|
34bdb8fcfc | ||
|
|
13b04f7672 | ||
|
|
76b9b2fa37 | ||
|
|
896d7cfbed | ||
|
|
0fb1b0840f | ||
|
|
1c0250075b | ||
|
|
cf2af817b9 | ||
|
|
ec24f79204 | ||
|
|
be902be453 | ||
|
|
888e08792e | ||
|
|
adc6ef22d9 | ||
|
|
0318d39112 | ||
|
|
abcfa0a05b | ||
|
|
a4b5c99ebb | ||
|
|
546e61a403 | ||
|
|
dcb68bd7a8 | ||
|
|
d411720234 | ||
|
|
1ae887d77c | ||
|
|
30465a771e | ||
|
|
f004b48b99 | ||
|
|
fc5e2247f6 | ||
|
|
5956647bd0 | ||
|
|
87eaf3ce5c | ||
|
|
73eba60210 | ||
|
|
4597ceb3a6 | ||
|
|
c0c25344c8 | ||
|
|
5efb36103f | ||
|
|
c0575a68ee | ||
|
|
ffd8a3a637 | ||
|
|
425ec275e9 | ||
|
|
3b9f336634 | ||
|
|
f792c31046 | ||
|
|
55a54ff89e | ||
|
|
78a40c9b14 | ||
|
|
17f32d16cc | ||
|
|
d57a0f2ae1 | ||
|
|
d6fdfef243 | ||
|
|
62d28dc89e | ||
|
|
57c65156f7 | ||
|
|
6e54dff40d | ||
|
|
0e722c8df1 | ||
|
|
f05479865c | ||
|
|
4e4055e7a8 | ||
|
|
7414ca10b3 | ||
|
|
1e17dfa6cb | ||
|
|
64d6d9e93b | ||
|
|
68902312cc | ||
|
|
a52b50b706 | ||
|
|
2527bdbfe1 | ||
|
|
473aaf13be | ||
|
|
0844b597f8 | ||
|
|
d45d7f92fb | ||
|
|
b3a822b4e8 | ||
|
|
788fadbd5e | ||
|
|
40f29e1e9b | ||
|
|
a036a25e1d | ||
|
|
abed362dc5 | ||
|
|
ce78299464 | ||
|
|
030f6607f0 | ||
|
|
c3a4e33245 | ||
|
|
aabf0843ab | ||
|
|
748e4acfb6 | ||
|
|
6e48a6b512 | ||
|
|
88500ab219 | ||
|
|
d0f8c141e1 | ||
|
|
34a1bf1380 | ||
|
|
b87ba12a7d | ||
|
|
bb0d048235 | ||
|
|
b991c65d8b | ||
|
|
b3b5db351f | ||
|
|
9562e06b92 | ||
|
|
7fc4899507 | ||
|
|
d649ae6ff7 | ||
|
|
633579e738 | ||
|
|
a65cb32d70 | ||
|
|
ead7fb4233 | ||
|
|
31b70a7736 | ||
|
|
bbeadee98e | ||
|
|
df42e352f7 | ||
|
|
1f985fe72f | ||
|
|
575296d7fc | ||
|
|
b93dc9f200 | ||
|
|
6255f737ba | ||
|
|
3ed2144a0e | ||
|
|
a437761d03 | ||
|
|
b432e1bf46 | ||
|
|
8dd59e3e67 | ||
|
|
fd5543407a | ||
|
|
48d012ff92 | ||
|
|
2c4eae5ca2 | ||
|
|
629646122f | ||
|
|
d4f284f1a3 | ||
|
|
ff3b5b39a5 | ||
|
|
9667980f2d | ||
|
|
188a34f835 | ||
|
|
f9cd5f11d9 | ||
|
|
adedf0178b | ||
|
|
4e15b82896 | ||
|
|
ed0ea34161 | ||
|
|
e2ffd36073 | ||
|
|
6bd2a1852f | ||
|
|
371236e364 | ||
|
|
6cbdc9d7c5 | ||
|
|
a94125f3f2 | ||
|
|
2f5f5d75a7 | ||
|
|
02f2284f3b | ||
|
|
2b1305a315 | ||
|
|
ec78f67abd | ||
|
|
25c04af500 | ||
|
|
c0391d866e | ||
|
|
dcb97be587 | ||
|
|
c6eb98aef2 | ||
|
|
1c6e15c064 | ||
|
|
711a3a30b0 | ||
|
|
e203ca14f4 | ||
|
|
0f1b69b625 | ||
|
|
176eaad70b | ||
|
|
486dfe4e63 | ||
|
|
81cf4bab99 | ||
|
|
9c3f563f83 | ||
|
|
6b42781c21 | ||
|
|
891a0d1bd0 | ||
|
|
3b9063dc63 | ||
|
|
2bfdffb9c4 | ||
|
|
9f8685bf10 | ||
|
|
b58bc414bf | ||
|
|
0b81723118 | ||
|
|
66418ec064 | ||
|
|
d87640a4f1 | ||
|
|
e5580ac0c4 | ||
|
|
b92c800e00 | ||
|
|
e370fbe500 | ||
|
|
89d4d828b9 | ||
|
|
d004015f03 | ||
|
|
ba5b5fbfe3 | ||
|
|
dd29a87107 | ||
|
|
b394540f53 | ||
|
|
3ed5f8c0bd | ||
|
|
5d5cef2a87 | ||
|
|
9264e344d7 | ||
|
|
0a46ac3e1b | ||
|
|
a438fc746f | ||
|
|
0c8ffaf73e | ||
|
|
6c1bbb3248 | ||
|
|
e663c60a89 | ||
|
|
e1e8979e0b | ||
|
|
de53b24536 | ||
|
|
bc518a0e82 | ||
|
|
2ed87febcb | ||
|
|
ee169b3a46 | ||
|
|
90d690c187 | ||
|
|
0e7d4ef110 | ||
|
|
7a6397af22 | ||
|
|
ac512612e7 | ||
|
|
95fc3dfdfb | ||
|
|
39be89780e | ||
|
|
2642333928 | ||
|
|
7e1d745435 | ||
|
|
218e638f88 | ||
|
|
cad2a989c1 | ||
|
|
f844aeb2b4 | ||
|
|
7847763a31 | ||
|
|
3f49bc382e | ||
|
|
3ae9e518a3 | ||
|
|
1e5ced6737 | ||
|
|
fa67652ba4 | ||
|
|
62106cc0a4 | ||
|
|
99740e3eab | ||
|
|
c12adbb594 | ||
|
|
157b2da133 | ||
|
|
208f21728f | ||
|
|
0377080da6 | ||
|
|
f5c48c9679 | ||
|
|
99ef1308ea | ||
|
|
9f075c16c5 | ||
|
|
2f17f19425 | ||
|
|
06f00e9664 | ||
|
|
c681174adf | ||
|
|
4a12ce1888 | ||
|
|
4e39f2387a | ||
|
|
e354098b96 | ||
|
|
9086720c3c | ||
|
|
2bb9171e32 | ||
|
|
294efef38c | ||
|
|
4f1d25fba7 | ||
|
|
d3e363a4d5 | ||
|
|
a10f286f0f | ||
|
|
390c53097f | ||
|
|
4c30b2c665 | ||
|
|
325e6e0904 | ||
|
|
56ec440272 | ||
|
|
0af5e07eed | ||
|
|
8a764ceb67 | ||
|
|
fe829aa850 | ||
|
|
2ca22656d6 | ||
|
|
cb7fa99fd3 | ||
|
|
441639a8d5 | ||
|
|
a78e1d504b | ||
|
|
02f6b0ec61 | ||
|
|
8c60cc7084 | ||
|
|
a7e4596e97 | ||
|
|
97d53ceb2e | ||
|
|
a49fb20885 | ||
|
|
2d5e97e140 | ||
|
|
ebdf5a0601 | ||
|
|
9ca02c90ed | ||
|
|
0595638228 | ||
|
|
1f4d0cc3cd | ||
|
|
e6c0d1c28a |
@@ -1,108 +0,0 @@
|
|||||||
version: 2
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
docker:
|
|
||||||
- image: golangci/golangci-lint:v1.16
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: golangci-lint run -v -D errcheck
|
|
||||||
build-node:
|
|
||||||
docker:
|
|
||||||
- image: circleci/node
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run:
|
|
||||||
name: "Pull Submodules"
|
|
||||||
command: |
|
|
||||||
git submodule init
|
|
||||||
git submodule update --remote
|
|
||||||
- run:
|
|
||||||
name: "Build"
|
|
||||||
command: ./wizard.sh -a
|
|
||||||
- run:
|
|
||||||
name: "Cleanup"
|
|
||||||
command: rm -rf frontend/node_modules
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: .
|
|
||||||
paths:
|
|
||||||
- '*'
|
|
||||||
build-go:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.12
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: '~/project'
|
|
||||||
- run:
|
|
||||||
name: "Compile"
|
|
||||||
command: ./wizard.sh -c
|
|
||||||
- run:
|
|
||||||
name: "Cleanup"
|
|
||||||
command: |
|
|
||||||
rm -rf frontend/build
|
|
||||||
git checkout -- go.sum # TODO: why is it being changed?
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: .
|
|
||||||
paths:
|
|
||||||
- '*'
|
|
||||||
docker-latest:
|
|
||||||
docker:
|
|
||||||
- image: docker
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: '~/project'
|
|
||||||
- setup_remote_docker
|
|
||||||
- run: docker build -t filebrowser/filebrowser .
|
|
||||||
- run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
|
||||||
- run: docker push filebrowser/filebrowser
|
|
||||||
- run: docker logout
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: .
|
|
||||||
paths:
|
|
||||||
- '*'
|
|
||||||
release:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang:1.12
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: '~/project'
|
|
||||||
- setup_remote_docker
|
|
||||||
- run: docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
|
||||||
- run: curl -sL https://git.io/goreleaser | bash
|
|
||||||
- run: docker logout
|
|
||||||
workflows:
|
|
||||||
version: 2
|
|
||||||
build-workflow:
|
|
||||||
jobs:
|
|
||||||
- lint:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
- build-node:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
- build-go:
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
requires:
|
|
||||||
- build-node
|
|
||||||
- lint
|
|
||||||
- release:
|
|
||||||
context: deploy
|
|
||||||
requires:
|
|
||||||
- build-go
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^v.*/
|
|
||||||
branches:
|
|
||||||
ignore: /.*/
|
|
||||||
- docker-latest:
|
|
||||||
context: deploy
|
|
||||||
requires:
|
|
||||||
- build-go
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: master
|
|
||||||
tags:
|
|
||||||
ignore: /.*/
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
testdata/
|
*
|
||||||
.github/
|
!docker/*
|
||||||
**.git
|
!filebrowser
|
||||||
5
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# These owners will be the default owners for everything in the repo.
|
||||||
|
# Unless a later match takes precedence, @o1egl will be requested for
|
||||||
|
# review when someone opens a pull request.
|
||||||
|
|
||||||
|
* @o1egl
|
||||||
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -4,19 +4,19 @@ about: Create a report to help us improve
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Description**
|
**Description**
|
||||||
A clear and concise description of what the issue is about. What are you trying to do?
|
<!-- A clear and concise description of what the issue is about. What are you trying to do? -->
|
||||||
|
|
||||||
**Expected behaviour**
|
**Expected behaviour**
|
||||||
What did you expect to happen?
|
<!-- What did you expect to happen? -->
|
||||||
|
|
||||||
**What is happening instead?**
|
**What is happening instead?**
|
||||||
Please, give full error messages and/or log.
|
<!-- Please, give full error messages and/or log. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here. If applicable, add screenshots to help explain your problem.
|
<!-- Add any other context about the problem here. If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
**How to reproduce?**
|
**How to reproduce?**
|
||||||
Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behaviour as minimally as possible?
|
<!-- Tell us how to reproduce this issue. How can someone who is starting from scratch reproduce this behaviour as minimally as possible? -->
|
||||||
|
|
||||||
**Files**
|
**Files**
|
||||||
A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile.
|
<!-- A list of relevant files for this issue. Large files can be uploaded one-by-one or in a tarball/zipfile. -->
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -4,13 +4,13 @@ about: Suggest an idea for this project
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a problem? Please describe.**
|
||||||
Add a clear and concise description of what the problem is. E.g. *I'm always frustrated when [...]*
|
<!-- Add a clear and concise description of what the problem is. E.g. *I'm always frustrated when [...]* -->
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
Add a clear and concise description of what you want to happen.
|
<!-- Add a clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
Add a clear and concise description of any alternative solutions or features you've considered.
|
<!-- Add a clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|||||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,6 +1,8 @@
|
|||||||
**Description**
|
**Description**
|
||||||
|
<!--
|
||||||
Please explain the changes you made here.
|
Please explain the changes you made here.
|
||||||
If the feature changes current behaviour, explain why your solution is better.
|
If the feature changes current behaviour, explain why your solution is better.
|
||||||
|
-->
|
||||||
|
|
||||||
:rotating_light: Before submitting your PR, please read [community](https://github.com/filebrowser/community), and indicate which issues (in any of the repos) are either fixed or closed by this PR. See [GitHub Help: Closing issues using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/).
|
:rotating_light: Before submitting your PR, please read [community](https://github.com/filebrowser/community), and indicate which issues (in any of the repos) are either fixed or closed by this PR. See [GitHub Help: Closing issues using keywords](https://help.github.com/articles/closing-issues-via-commit-messages/).
|
||||||
|
|
||||||
@@ -11,6 +13,8 @@ If the feature changes current behaviour, explain why your solution is better.
|
|||||||
- [ ] AVOID breaking the continuous integration build.
|
- [ ] AVOID breaking the continuous integration build.
|
||||||
|
|
||||||
**Further comments**
|
**Further comments**
|
||||||
|
<!--
|
||||||
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did, what alternatives you considered, etc.
|
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did, what alternatives you considered, etc.
|
||||||
|
|
||||||
:heart: Thank you!
|
:heart: Thank you!
|
||||||
|
-->
|
||||||
|
|||||||
100
.github/workflows/main.yaml
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
name: main
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# linters
|
||||||
|
lint-frontend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: make lint-frontend
|
||||||
|
lint-backend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.18.3
|
||||||
|
- run: make lint-backend
|
||||||
|
lint-commits:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: make lint-commits
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [lint-frontend, lint-backend, lint-commits]
|
||||||
|
steps:
|
||||||
|
- run: echo "done"
|
||||||
|
|
||||||
|
# tests
|
||||||
|
test-frontend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: make test-frontend
|
||||||
|
test-backend:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.18.3
|
||||||
|
- run: make test-backend
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [test-frontend, test-backend]
|
||||||
|
steps:
|
||||||
|
- run: echo "done"
|
||||||
|
|
||||||
|
# release
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [lint, test]
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags/v')
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: 1.18.3
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Build frontend
|
||||||
|
run: make build-frontend
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: release --rm-dist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||||
28
.gitignore
vendored
@@ -1,7 +1,31 @@
|
|||||||
*.db
|
*.db
|
||||||
*.lock
|
|
||||||
*.bak
|
*.bak
|
||||||
_old
|
_old
|
||||||
rice-box.go
|
rice-box.go
|
||||||
.idea/
|
.idea/
|
||||||
filebrowser
|
/filebrowser
|
||||||
|
/filebrowser.exe
|
||||||
|
/dist
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw*
|
||||||
|
bin/
|
||||||
|
build/
|
||||||
|
|||||||
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "frontend"]
|
|
||||||
path = frontend
|
|
||||||
url = https://github.com/filebrowser/frontend
|
|
||||||
121
.golangci.yml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
linters-settings:
|
||||||
|
dupl:
|
||||||
|
threshold: 100
|
||||||
|
exhaustive:
|
||||||
|
default-signifies-exhaustive: false
|
||||||
|
funlen:
|
||||||
|
lines: 100
|
||||||
|
statements: 50
|
||||||
|
gci:
|
||||||
|
local-prefixes: github.com/filebrowser/filebrowser
|
||||||
|
goconst:
|
||||||
|
min-len: 2
|
||||||
|
min-occurrences: 2
|
||||||
|
gocritic:
|
||||||
|
enabled-tags:
|
||||||
|
- diagnostic
|
||||||
|
- experimental
|
||||||
|
- opinionated
|
||||||
|
- performance
|
||||||
|
- style
|
||||||
|
disabled-checks:
|
||||||
|
- dupImport # https://github.com/go-critic/go-critic/issues/845
|
||||||
|
- ifElseChain
|
||||||
|
- octalLiteral
|
||||||
|
- whyNoLint
|
||||||
|
- wrapperFunc
|
||||||
|
gocyclo:
|
||||||
|
min-complexity: 15
|
||||||
|
goimports:
|
||||||
|
local-prefixes: github.com/filebrowser/filebrowser
|
||||||
|
gomnd:
|
||||||
|
settings:
|
||||||
|
mnd:
|
||||||
|
# don't include the "operation" and "assign"
|
||||||
|
checks: argument,case,condition,return
|
||||||
|
govet:
|
||||||
|
check-shadowing: true
|
||||||
|
lll:
|
||||||
|
line-length: 140
|
||||||
|
maligned:
|
||||||
|
suggest-new: true
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
nolintlint:
|
||||||
|
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
|
||||||
|
allow-unused: false # report any unused nolint directives
|
||||||
|
require-explanation: false # don't require an explanation for nolint directives
|
||||||
|
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
|
||||||
|
|
||||||
|
linters:
|
||||||
|
# please, do not use `enable-all`: it's deprecated and will be removed soon.
|
||||||
|
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- bodyclose
|
||||||
|
- deadcode
|
||||||
|
- depguard
|
||||||
|
- dogsled
|
||||||
|
- dupl
|
||||||
|
- errcheck
|
||||||
|
- exportloopref
|
||||||
|
- exhaustive
|
||||||
|
- funlen
|
||||||
|
- gochecknoinits
|
||||||
|
- goconst
|
||||||
|
- gocritic
|
||||||
|
- gocyclo
|
||||||
|
- goimports
|
||||||
|
- gomnd
|
||||||
|
- goprintffuncname
|
||||||
|
- gosec
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- lll
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- nolintlint
|
||||||
|
- rowserrcheck
|
||||||
|
- staticcheck
|
||||||
|
- structcheck
|
||||||
|
- stylecheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
- varcheck
|
||||||
|
- whitespace
|
||||||
|
- prealloc
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- path: cmd/.*.go
|
||||||
|
linters:
|
||||||
|
- gochecknoinits
|
||||||
|
- path: .*_test.go
|
||||||
|
linters:
|
||||||
|
- lll
|
||||||
|
- gochecknoinits
|
||||||
|
- gocyclo
|
||||||
|
- funlen
|
||||||
|
- dupl
|
||||||
|
- scopelint
|
||||||
|
- text: "Auther"
|
||||||
|
linters:
|
||||||
|
- misspell
|
||||||
|
- text: "strconv.Parse"
|
||||||
|
linters:
|
||||||
|
- gomnd
|
||||||
|
|
||||||
|
run:
|
||||||
|
go: '1.18'
|
||||||
|
skip-dirs:
|
||||||
|
- frontend/
|
||||||
|
skip-files:
|
||||||
|
- http/rice-box.go
|
||||||
|
|
||||||
|
# golangci.com configuration
|
||||||
|
# https://github.com/golangci/golangci/wiki/Configuration
|
||||||
|
service:
|
||||||
|
golangci-lint-version: 1.27.x # use the fixed version to not introduce new linters unexpectedly
|
||||||
214
.goreleaser.yml
@@ -1,8 +1,13 @@
|
|||||||
project_name: filebrowser
|
project_name: filebrowser
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
build:
|
build:
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
|
ldflags:
|
||||||
|
- -s -w -X github.com/filebrowser/filebrowser/v2/version.Version={{ .Version }} -X github.com/filebrowser/filebrowser/v2/version.CommitSHA={{ .ShortCommit }}
|
||||||
main: main.go
|
main: main.go
|
||||||
binary: filebrowser
|
binary: filebrowser
|
||||||
goos:
|
goos:
|
||||||
@@ -10,10 +15,6 @@ build:
|
|||||||
- linux
|
- linux
|
||||||
- windows
|
- windows
|
||||||
- freebsd
|
- freebsd
|
||||||
- netbsd
|
|
||||||
- openbsd
|
|
||||||
- dragonfly
|
|
||||||
- solaris
|
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- 386
|
- 386
|
||||||
@@ -26,14 +27,8 @@ build:
|
|||||||
ignore:
|
ignore:
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: 386
|
goarch: 386
|
||||||
- goos: openbsd
|
|
||||||
goarch: arm
|
|
||||||
- goos: freebsd
|
- goos: freebsd
|
||||||
goarch: arm
|
goarch: arm
|
||||||
- goos: netbsd
|
|
||||||
goarch: arm
|
|
||||||
- goos: solaris
|
|
||||||
goarch: arm
|
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
-
|
-
|
||||||
@@ -45,12 +40,201 @@ archives:
|
|||||||
|
|
||||||
dockers:
|
dockers:
|
||||||
-
|
-
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/amd64"
|
||||||
goos: linux
|
goos: linux
|
||||||
goarch: amd64
|
goarch: amd64
|
||||||
goarm: ''
|
|
||||||
image_templates:
|
image_templates:
|
||||||
- "filebrowser/filebrowser:latest"
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64"
|
||||||
- "filebrowser/filebrowser:{{ .Tag }}"
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64"
|
||||||
- "filebrowser/filebrowser:v{{ .Major }}"
|
|
||||||
extra_files:
|
extra_files:
|
||||||
- .docker.json
|
- docker_config.json
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm64"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64"
|
||||||
|
extra_files:
|
||||||
|
- docker_config.json
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm/v6"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: '6'
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6"
|
||||||
|
extra_files:
|
||||||
|
- docker_config.json
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm/v7"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: '7'
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7"
|
||||||
|
extra_files:
|
||||||
|
- docker_config.json
|
||||||
|
## s6 based docker images
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/amd64"
|
||||||
|
goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6.aarch64
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm64"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6.armhf
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm/v6"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: '6'
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
-
|
||||||
|
dockerfile: Dockerfile.s6.armhf
|
||||||
|
use: buildx
|
||||||
|
build_flag_templates:
|
||||||
|
- "--pull"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.source={{.GitURL}}"
|
||||||
|
- "--platform=linux/arm/v7"
|
||||||
|
goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: '7'
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7-s6"
|
||||||
|
extra_files:
|
||||||
|
- docker/root
|
||||||
|
docker_manifests:
|
||||||
|
- name_template: "filebrowser/filebrowser:latest"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7"
|
||||||
|
- name_template: "filebrowser/filebrowser:{{ .Tag }}"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7"
|
||||||
|
- name_template: "filebrowser/filebrowser:v{{ .Major }}"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7"
|
||||||
|
## s6 image manifests
|
||||||
|
- name_template: "filebrowser/filebrowser:s6"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7-s6"
|
||||||
|
- name_template: "filebrowser/filebrowser:{{ .Tag }}-s6"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:{{ .Tag }}-armv7-s6"
|
||||||
|
- name_template: "filebrowser/filebrowser:v{{ .Major }}-s6"
|
||||||
|
image_templates:
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-amd64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-arm64-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv6-s6"
|
||||||
|
- "filebrowser/filebrowser:v{{ .Major }}-armv7-s6"
|
||||||
|
brews:
|
||||||
|
- name: filebrowser
|
||||||
|
tap:
|
||||||
|
owner: filebrowser
|
||||||
|
name: homebrew-tap
|
||||||
|
folder: Formula
|
||||||
|
homepage: https://filebrowser.org
|
||||||
|
commit_author:
|
||||||
|
name: FileBrowser Robot
|
||||||
|
email: robot@filebrowser.org
|
||||||
|
description: File Browser is a create-your-own-cloud-kind of software where you can install it on a server, direct it to a path and then access your files through a nice web interface
|
||||||
|
license: "MIT"
|
||||||
10
.tx/config
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[main]
|
||||||
|
host = https://www.transifex.com
|
||||||
|
lang_map = pt_BR: pt-br, zh_CN: zh-cn, zh_HK: zh-hk, zh_TW: zh-tw, nl_BE: nl-be, sv_SE: sv-se
|
||||||
|
|
||||||
|
[file-browser.file-browser]
|
||||||
|
file_filter = frontend/src/i18n/<lang>.json
|
||||||
|
minimum_perc = 50
|
||||||
|
source_file = frontend/src/i18n/en.json
|
||||||
|
source_lang = en
|
||||||
|
type = KEYVALUEJSON
|
||||||
14
.versionrc
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"types": [
|
||||||
|
{ "type": "feat", "section": "Features" },
|
||||||
|
{ "type": "fix", "section": "Bug Fixes" },
|
||||||
|
{ "type": "perf", "section": "Performance improvements" },
|
||||||
|
{ "type": "revert", "section": "Reverts" },
|
||||||
|
{ "type": "refactor", "section": "Refactorings" },
|
||||||
|
{ "type": "build", "section": "Build" },
|
||||||
|
{ "type": "ci", "hidden": true },
|
||||||
|
{ "type": "test", "hidden": true },
|
||||||
|
{ "type": "chore", "hidden": true },
|
||||||
|
{ "type": "docs", "hidden": true }
|
||||||
|
]
|
||||||
|
}
|
||||||
529
CHANGELOG.md
Normal file
@@ -0,0 +1,529 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [2.22.4](https://github.com/filebrowser/filebrowser/compare/v2.22.3...v2.22.4) (2022-07-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* disable cookie auth for non GET requests ([80030de](https://github.com/filebrowser/filebrowser/commit/80030dee32d161043766d57ba4e0ad0b0d99290b))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **deps:** bump moment from 2.29.2 to 2.29.4 in /frontend ([#2036](https://github.com/filebrowser/filebrowser/issues/2036)) ([cb43770](https://github.com/filebrowser/filebrowser/commit/cb437700255e41ff559b9f5a99ab4290b2f8df87))
|
||||||
|
* **deps:** bump shell-quote from 1.7.2 to 1.7.3 in /frontend ([#2025](https://github.com/filebrowser/filebrowser/issues/2025)) ([eaba7e5](https://github.com/filebrowser/filebrowser/commit/eaba7e5255f960141e0fc1557f87073df9f6d66a))
|
||||||
|
|
||||||
|
### [2.22.3](https://github.com/filebrowser/filebrowser/compare/v2.22.2...v2.22.3) (2022-07-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use correct field name in user put api ([#2026](https://github.com/filebrowser/filebrowser/issues/2026)) ([d94acdd](https://github.com/filebrowser/filebrowser/commit/d94acdd89a0069fe87107024fd332a0d59a112fc))
|
||||||
|
|
||||||
|
### [2.22.2](https://github.com/filebrowser/filebrowser/compare/v2.22.1...v2.22.2) (2022-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* display disk capacity in a correct format ([#2013](https://github.com/filebrowser/filebrowser/issues/2013)) ([dec3d62](https://github.com/filebrowser/filebrowser/commit/dec3d629d42de567aa708154ebc4e03b5223608c))
|
||||||
|
* don't calculate usage for files ([#1973](https://github.com/filebrowser/filebrowser/issues/1973)) ([577c0ef](https://github.com/filebrowser/filebrowser/commit/577c0efa9cff13628d5e3bac710ef568a00949e0)), closes [#1972](https://github.com/filebrowser/filebrowser/issues/1972) [#1967](https://github.com/filebrowser/filebrowser/issues/1967)
|
||||||
|
* preview url building fix ([#1976](https://github.com/filebrowser/filebrowser/issues/1976)) ([dcf0bc6](https://github.com/filebrowser/filebrowser/commit/dcf0bc65bfcfc7df3804d7392598a92019468cf7))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **backend:** upgrade golangci-lint to 1.46.2 ([#1991](https://github.com/filebrowser/filebrowser/issues/1991)) ([8118afd](https://github.com/filebrowser/filebrowser/commit/8118afd0ac0d25f4503c98879369764c35e7408e))
|
||||||
|
|
||||||
|
### [2.22.1](https://github.com/filebrowser/filebrowser/compare/v2.22.0...v2.22.1) (2022-06-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* use correct basepath prefix for preview urls ([#1971](https://github.com/filebrowser/filebrowser/issues/1971)) ([1e7d3b2](https://github.com/filebrowser/filebrowser/commit/1e7d3b25c283c556d98c65f1c2f46db4e4178995))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **backend:** bump go version to 1.8.3 ([b16982d](https://github.com/filebrowser/filebrowser/commit/b16982df0f7da9eedb678455298b42ac55c86666))
|
||||||
|
|
||||||
|
## [2.22.0](https://github.com/filebrowser/filebrowser/compare/v2.21.1...v2.22.0) (2022-06-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add branding to the window title ([#1850](https://github.com/filebrowser/filebrowser/issues/1850)) ([f8dfbf7](https://github.com/filebrowser/filebrowser/commit/f8dfbf7eeecf3ee99ce906276777676f44e81e34))
|
||||||
|
* add disk usage information to the sidebar ([d1d8e3e](https://github.com/filebrowser/filebrowser/commit/d1d8e3e3405381b01317fe07ae729d70219415a7))
|
||||||
|
* automatically focus username field on login page ([596c732](https://github.com/filebrowser/filebrowser/commit/596c73288f5b53bd7e79ab8046136dc75ff078b9))
|
||||||
|
* invalid symlink icon ([b14b911](https://github.com/filebrowser/filebrowser/commit/b14b9114f837cacf9f7788e88c503142a81585be))
|
||||||
|
* page title localization ([8a43413](https://github.com/filebrowser/filebrowser/commit/8a43413f888440dc11b11c509abff45f706033d8))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow CSP inline styling ([5da9d74](https://github.com/filebrowser/filebrowser/commit/5da9d74da62c69c431361bcaf0c07dc1da237ea8))
|
||||||
|
* disable autocapitalize of login input (closes [#1910](https://github.com/filebrowser/filebrowser/issues/1910)) ([aed3af5](https://github.com/filebrowser/filebrowser/commit/aed3af58384697dc3de30f1450b837b0b74e4fa6))
|
||||||
|
* drag-and-drop folder upload ([e677c78](https://github.com/filebrowser/filebrowser/commit/e677c78471f09f8d2c21d63d7388e908924aa6d9))
|
||||||
|
* expired token error ([c3bd118](https://github.com/filebrowser/filebrowser/commit/c3bd1188aa396cbf00c593d259a9da0eddeeea3b))
|
||||||
|
* folder info on upload list ([d1d7b23](https://github.com/filebrowser/filebrowser/commit/d1d7b23da6cc0c9a2f2f3e17021ec4f13ea557dd))
|
||||||
|
* network error object message ([fc209f6](https://github.com/filebrowser/filebrowser/commit/fc209f64deff7a2793980d11ee738f7140c444cf))
|
||||||
|
* set correct scope when user home creation is enabled ([02730bb](https://github.com/filebrowser/filebrowser/commit/02730bb9bfa3bfbfa251bb4736fc4c08d33609ab))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **backend:** bump dependency versions ([7c9a75e](https://github.com/filebrowser/filebrowser/commit/7c9a75e72588f92d58fb58d32cdac352bce73b20))
|
||||||
|
* **deps:** bump async from 2.6.3 to 2.6.4 in /frontend ([#1933](https://github.com/filebrowser/filebrowser/issues/1933)) ([e5fa96b](https://github.com/filebrowser/filebrowser/commit/e5fa96b666eac2e46a02bde832488baca5f2cd6d))
|
||||||
|
* **deps:** bump eventsource from 1.1.0 to 1.1.1 in /frontend ([dd50369](https://github.com/filebrowser/filebrowser/commit/dd503695a1a8119a631643414d3a9070890f3f3c))
|
||||||
|
* **deps:** bump minimist from 1.2.5 to 1.2.6 in /frontend ([#1889](https://github.com/filebrowser/filebrowser/issues/1889)) ([a74c72d](https://github.com/filebrowser/filebrowser/commit/a74c72db451207e1275988f3d208fa6d6f0468a9))
|
||||||
|
* **deps:** bump minimist from 1.2.5 to 1.2.6 in /tools ([#1891](https://github.com/filebrowser/filebrowser/issues/1891)) ([f5b1e10](https://github.com/filebrowser/filebrowser/commit/f5b1e106183fb2192063a72fd195fc8c181ba8f9))
|
||||||
|
* **deps:** bump moment from 2.29.1 to 2.29.2 in /frontend ([#1900](https://github.com/filebrowser/filebrowser/issues/1900)) ([040584c](https://github.com/filebrowser/filebrowser/commit/040584c86563d869c7a05887ef1f781bce653033))
|
||||||
|
* **deps:** bump url-parse from 1.5.7 to 1.5.10 in /frontend ([#1841](https://github.com/filebrowser/filebrowser/issues/1841)) ([b2ad3f7](https://github.com/filebrowser/filebrowser/commit/b2ad3f73686a2abaa4fc62963fba6f83c9da9b5e))
|
||||||
|
* **frontend:** bump node version from 14 to 16 ([ac3ead8](https://github.com/filebrowser/filebrowser/commit/ac3ead8dcef9c64c6be8b5cbbceee143b2cc77a8))
|
||||||
|
* upgrade go version to 1.18.1 ([6bd34c7](https://github.com/filebrowser/filebrowser/commit/6bd34c76324780c1edd8625d5b22f5a84990852b))
|
||||||
|
|
||||||
|
### [2.21.1](https://github.com/filebrowser/filebrowser/compare/v2.21.0...v2.21.1) (2022-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* display user scope for admin users ([#1834](https://github.com/filebrowser/filebrowser/issues/1834)) ([6366cf0](https://github.com/filebrowser/filebrowser/commit/6366cf0b181f13eac38f69f1760d6f6f0586a5d1))
|
||||||
|
|
||||||
|
## [2.21.0](https://github.com/filebrowser/filebrowser/compare/v2.20.1...v2.21.0) (2022-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add colorized file type icons ([2948589](https://github.com/filebrowser/filebrowser/commit/2948589fcde6d1dca7f3ea52a621d8213fa3300c))
|
||||||
|
* add gallery view mode ([8888b9f](https://github.com/filebrowser/filebrowser/commit/8888b9f44640394df9e3583db4392472d7027a4b))
|
||||||
|
* add Ukrainian translation / update Russian translation ([#1753](https://github.com/filebrowser/filebrowser/issues/1753)) ([665e458](https://github.com/filebrowser/filebrowser/commit/665e45889cd333f1e3500e4bf38d15d229c9fe2a))
|
||||||
|
* add upload file list with progress ([#1825](https://github.com/filebrowser/filebrowser/issues/1825)) ([cf85404](https://github.com/filebrowser/filebrowser/commit/cf85404dd25cd7fdd73aa32878b4dc5f85ee3e96))
|
||||||
|
* smaller column width to fit 2 columns in landscape mobiles ([7870e89](https://github.com/filebrowser/filebrowser/commit/7870e89bc04f1494f2705795476b5f1c9d621e38))
|
||||||
|
* use real image path to calculate cache key ([c198723](https://github.com/filebrowser/filebrowser/commit/c1987237d05adcce77c614e5247a181ae5cdfacd))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* correctly handle non-ascii passwords for shared resources ([c782f21](https://github.com/filebrowser/filebrowser/commit/c782f21b0fa4511a15e7015117d075eaf5ea332c))
|
||||||
|
* don't expose scope for non-admin users ([0942fc7](https://github.com/filebrowser/filebrowser/commit/0942fc7042fd949cce91855169d0bcf16eb75771))
|
||||||
|
* open all the pdf files correctly ([#1742](https://github.com/filebrowser/filebrowser/issues/1742)) ([949f0f2](https://github.com/filebrowser/filebrowser/commit/949f0f277f6004904b3edfa716a8365ec93fa0fa))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* **deps:** bump browserslist from 4.16.3 to 4.19.1 in /frontend ([8089007](https://github.com/filebrowser/filebrowser/commit/80890075e802e2a4217edbb01d6417122d702f5e))
|
||||||
|
* **deps:** bump dns-packet from 1.3.1 to 1.3.4 in /frontend ([a73d7f1](https://github.com/filebrowser/filebrowser/commit/a73d7f14b787935c6ebe525dba64b65f8ed733e2))
|
||||||
|
* **deps:** bump follow-redirects from 1.13.3 to 1.14.8 in /frontend ([f1f7f17](https://github.com/filebrowser/filebrowser/commit/f1f7f17ade8d40fc6cfb22c79960bce299876b56))
|
||||||
|
* **deps:** bump hosted-git-info from 2.8.8 to 2.8.9 in /frontend ([e7659ea](https://github.com/filebrowser/filebrowser/commit/e7659ea36bdf780ce17005f7170a2fef02a2d5e5))
|
||||||
|
* **deps:** bump path-parse from 1.0.6 to 1.0.7 in /frontend ([c014966](https://github.com/filebrowser/filebrowser/commit/c01496624a7ebfc8a7c256bd919a400367281cbb))
|
||||||
|
* **deps:** bump postcss from 7.0.35 to 7.0.39 in /frontend ([9182d33](https://github.com/filebrowser/filebrowser/commit/9182d33e1cc375473fb18989a92d20252884f096))
|
||||||
|
* **deps:** bump ssri from 6.0.1 to 6.0.2 in /frontend ([3717186](https://github.com/filebrowser/filebrowser/commit/371718634b11f32e68165f31c51b6b1139c829ec))
|
||||||
|
* **deps:** bump tar from 6.1.0 to 6.1.11 in /frontend ([010d16f](https://github.com/filebrowser/filebrowser/commit/010d16fc1d8f0200e5662943aef17ee89c5877b7))
|
||||||
|
* **deps:** bump url-parse from 1.5.1 to 1.5.4 in /frontend ([8906408](https://github.com/filebrowser/filebrowser/commit/8906408a8f0ed86d1e11ea90fc573b36815c9c0d))
|
||||||
|
* **deps:** bump url-parse from 1.5.4 to 1.5.7 in /frontend ([228ebea](https://github.com/filebrowser/filebrowser/commit/228ebea66cc871b33459406590a80ef906298e7d))
|
||||||
|
* **deps:** bump ws from 6.2.1 to 6.2.2 in /frontend ([73c8073](https://github.com/filebrowser/filebrowser/commit/73c80732d934bc8802a6d7c7a559cad37df405f0))
|
||||||
|
|
||||||
|
### [2.20.1](https://github.com/filebrowser/filebrowser/compare/v2.20.0...v2.20.1) (2021-12-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* revert to using the default alpine based docker image ([46d8046](https://github.com/filebrowser/filebrowser/commit/46d80464d2a67927b06a11b83fb137ad364a90ed))
|
||||||
|
|
||||||
|
## [2.20.0](https://github.com/filebrowser/filebrowser/compare/v2.19.0...v2.20.0) (2021-12-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* detect multiple subtitle languages ([#1723](https://github.com/filebrowser/filebrowser/issues/1723)) ([c2e03bb](https://github.com/filebrowser/filebrowser/commit/c2e03bbfab97fc6716bcdd59158e9d5129bf0ea7))
|
||||||
|
* use linuxserver based docker image ([b8f35ce](https://github.com/filebrowser/filebrowser/commit/b8f35ce9322c2b0dbf954cfd3ff584bc9f742fdd))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* set correct default database path in the config ([988d3e5](https://github.com/filebrowser/filebrowser/commit/988d3e5bdd224509ddc2f08444560e3087e9c67d))
|
||||||
|
* upgrade vulnerable versions of the library ([6eb3ab0](https://github.com/filebrowser/filebrowser/commit/6eb3ab063509a015ad630ab704ae3791461d0982))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* refactor makefile ([f81857a](https://github.com/filebrowser/filebrowser/commit/f81857acce25936a700945db5ef4af545eaeb1cf))
|
||||||
|
* remove deprecated goreleaser use_buildx param ([4d1b9dd](https://github.com/filebrowser/filebrowser/commit/4d1b9dd2112002a93bb26cece07dcfd81c31dc2c))
|
||||||
|
|
||||||
|
## [2.19.0](https://github.com/filebrowser/filebrowser/compare/v2.18.0...v2.19.0) (2021-11-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* prefetch previous and next images in preview. ([#1627](https://github.com/filebrowser/filebrowser/issues/1627)) ([7401d16](https://github.com/filebrowser/filebrowser/commit/7401d16e457bb232fd7dd7ef427e8960d465705c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* empty file listing on share ([e082397](https://github.com/filebrowser/filebrowser/commit/e08239781f61e7bb25d9b8c5c6cce90f34621a76))
|
||||||
|
* relative font sizes ([c29698d](https://github.com/filebrowser/filebrowser/commit/c29698dffac769077ab7c7869569a902979ee3d7))
|
||||||
|
|
||||||
|
## [2.18.0](https://github.com/filebrowser/filebrowser/compare/v2.17.2...v2.18.0) (2021-10-31)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability to select file modified time format ([#1536](https://github.com/filebrowser/filebrowser/issues/1536)) ([0426629](https://github.com/filebrowser/filebrowser/commit/0426629a59c712849570d3e29956948ae7725a4a))
|
||||||
|
* add manifest theme color param ([#1542](https://github.com/filebrowser/filebrowser/issues/1542)) ([0358e42](https://github.com/filebrowser/filebrowser/commit/0358e42d2c206732fffa77714f5a66f4fe50a69d))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* back button behaviour in preview ([#1573](https://github.com/filebrowser/filebrowser/issues/1573)) ([deabc80](https://github.com/filebrowser/filebrowser/commit/deabc80fd7670983039dfcd29531b45002ca5d9e))
|
||||||
|
* fix sidebar navigation on mobile devices ([#1618](https://github.com/filebrowser/filebrowser/issues/1618)) ([f09bf3e](https://github.com/filebrowser/filebrowser/commit/f09bf3e1d076b27d29ba8a91cf448a99993bc444))
|
||||||
|
* search box is misaligned when the browser preferred font size is other than 16px ([#1613](https://github.com/filebrowser/filebrowser/issues/1613)) ([6f345be](https://github.com/filebrowser/filebrowser/commit/6f345be3e47ba57ecc1eb9a62587ab949078c125))
|
||||||
|
* security issue in command runner (closes [#1621](https://github.com/filebrowser/filebrowser/issues/1621)) ([74b7cd8](https://github.com/filebrowser/filebrowser/commit/74b7cd8e81840537a8206317344f118093153e8d))
|
||||||
|
* set correct editor height regardless of preferred font size ([#1614](https://github.com/filebrowser/filebrowser/issues/1614)) ([ddd4ffa](https://github.com/filebrowser/filebrowser/commit/ddd4ffa4caa6b292a3a644ecd897aba1237c7503))
|
||||||
|
* zoom pics when dlclick at first time ([#1561](https://github.com/filebrowser/filebrowser/issues/1561)) ([b6a51be](https://github.com/filebrowser/filebrowser/commit/b6a51bed516814944f8aa41440652242d57824c5))
|
||||||
|
|
||||||
|
### [2.17.2](https://github.com/filebrowser/filebrowser/compare/v2.17.1...v2.17.2) (2021-08-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bug with inlineLink not creating url properly ([#1515](https://github.com/filebrowser/filebrowser/issues/1515)) ([43a4609](https://github.com/filebrowser/filebrowser/commit/43a460993c3f0d158b876db4b20caa7963e9f361))
|
||||||
|
|
||||||
|
### [2.17.1](https://github.com/filebrowser/filebrowser/compare/v2.17.0...v2.17.1) (2021-08-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* internal server error if --disable-preview-resize flag is set (closes [#1510](https://github.com/filebrowser/filebrowser/issues/1510)) ([4c3099a](https://github.com/filebrowser/filebrowser/commit/4c3099a086c206dcb3bc70ee8c8da02eee61c30b))
|
||||||
|
|
||||||
|
## [2.17.0](https://github.com/filebrowser/filebrowser/compare/v2.16.1...v2.17.0) (2021-08-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* open file option on preview ([76add9e](https://github.com/filebrowser/filebrowser/commit/76add9e5274b0373c6b983e3b20e387a14ea6c9e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 401 error in share view open file button ([#1495](https://github.com/filebrowser/filebrowser/issues/1495)) ([25c8788](https://github.com/filebrowser/filebrowser/commit/25c87883908babde073390a2e2320a8e5880a87c))
|
||||||
|
* escape quote on index template ([23d646c](https://github.com/filebrowser/filebrowser/commit/23d646c456876d06cf48e71c1e57b69de99511f0)), closes [#1501](https://github.com/filebrowser/filebrowser/issues/1501)
|
||||||
|
* file caching directive ([c63cc5a](https://github.com/filebrowser/filebrowser/commit/c63cc5a2d25909cc4e2f2e7235f276ec66c32bf2))
|
||||||
|
|
||||||
|
### [2.16.1](https://github.com/filebrowser/filebrowser/compare/v2.16.0...v2.16.1) (2021-08-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check symlink target type (closes [#1488](https://github.com/filebrowser/filebrowser/issues/1488)) ([76b466f](https://github.com/filebrowser/filebrowser/commit/76b466f6492e74cf13e66a33e7e5f597ac92b240))
|
||||||
|
|
||||||
|
## [2.16.0](https://github.com/filebrowser/filebrowser/compare/v2.15.0...v2.16.0) (2021-07-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* browser cache directives ([190cb99](https://github.com/filebrowser/filebrowser/commit/190cb99a79a0d438eca2da13539f8c6449ad73ac))
|
||||||
|
* display error messages on settings ([6032038](https://github.com/filebrowser/filebrowser/commit/603203848a8b2221158088b6d849609db4c0c46c))
|
||||||
|
* file name on page title ([16a34de](https://github.com/filebrowser/filebrowser/commit/16a34defc02554a77c6ac47b9e17e69d098a09fe))
|
||||||
|
* gzip encoding for static js files ([aa172b8](https://github.com/filebrowser/filebrowser/commit/aa172b8bb5f17d5f5cb9666bfb5ee650d8091fb5))
|
||||||
|
* loading spinner on views navigation ([976eb55](https://github.com/filebrowser/filebrowser/commit/976eb5583dae474125fd7ddec5dc19b6c291f98f))
|
||||||
|
* message for connection error ([5e6f14b](https://github.com/filebrowser/filebrowser/commit/5e6f14b5dcb9c5efdf526f1346e09c2d0b2f6974))
|
||||||
|
* mod time title on file info ([7d1e030](https://github.com/filebrowser/filebrowser/commit/7d1e03075d2c27148f60813defa0f68403d1d3c2))
|
||||||
|
* open file option on share ([1c25f6e](https://github.com/filebrowser/filebrowser/commit/1c25f6ee69bd71eed82af7020006d0e27537a967))
|
||||||
|
* show more button on share ([ba8c09f](https://github.com/filebrowser/filebrowser/commit/ba8c09f454feeadf4a1e97547a34151a81b389d5))
|
||||||
|
* support for IE11 browser ([7ec24d9](https://github.com/filebrowser/filebrowser/commit/7ec24d9d7794fa37825f64ca2d1575f568fb1362))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* break resource create/update handlers on error (closes [#1464](https://github.com/filebrowser/filebrowser/issues/1464)) ([5072bbb](https://github.com/filebrowser/filebrowser/commit/5072bbb2cbf5b29d041629faa8367f15e4d145a2))
|
||||||
|
* copying files with special characters ([20ebbf6](https://github.com/filebrowser/filebrowser/commit/20ebbf6611b734371426fb1b9cb5e388be90bf7e))
|
||||||
|
* delete image cache when moving ([8973c45](https://github.com/filebrowser/filebrowser/commit/8973c4598ff817647f1f1ad6ee36480054cd2776))
|
||||||
|
* don't remove files on unsuccessful updates (closes [#1456](https://github.com/filebrowser/filebrowser/issues/1456)) ([6b19ab6](https://github.com/filebrowser/filebrowser/commit/6b19ab6613b12be7f075299cd98f4b41d43827c7))
|
||||||
|
* failure on broken symlink deletion ([8650d2f](https://github.com/filebrowser/filebrowser/commit/8650d2ffe7a29cbafa800efcecbf6a61598a9f0c))
|
||||||
|
* inconsistent double click on listing item ([ba7e71a](https://github.com/filebrowser/filebrowser/commit/ba7e71a7c3b0cc71012e5adf94b1c642e554972e))
|
||||||
|
* no items displayed on file listing ([18889ad](https://github.com/filebrowser/filebrowser/commit/18889ad725f7f7e5a7e3f7abcf156487556dbeaf))
|
||||||
|
* omit file content ([209f9fa](https://github.com/filebrowser/filebrowser/commit/209f9fa77f751054512355f2b74b9b7258465d0b))
|
||||||
|
* short commit sha and typo fix in Makefile ([#1411](https://github.com/filebrowser/filebrowser/issues/1411)) ([46ee595](https://github.com/filebrowser/filebrowser/commit/46ee59538914dc2859f0da6b32e2d062d0a01b10))
|
||||||
|
|
||||||
|
## [2.15.0](https://github.com/filebrowser/filebrowser/compare/v2.14.1...v2.15.0) (2021-04-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add EXIF thumbnail support for JPEG files ([#1234](https://github.com/filebrowser/filebrowser/issues/1234)) ([7dd5b34](https://github.com/filebrowser/filebrowser/commit/7dd5b34d425dfbc2782152310483cbecf85c800a))
|
||||||
|
* dynamic autoplay on previewer ([a76e01d](https://github.com/filebrowser/filebrowser/commit/a76e01d2b78a785f3665a8b3532c7cc566bfabce))
|
||||||
|
* dynamic item count on file listing ([6c8ee96](https://github.com/filebrowser/filebrowser/commit/6c8ee96e6a21fae5d4608bdc7a5c5a161d7dafd3))
|
||||||
|
* dynamic zoom limit on previewer ([e410272](https://github.com/filebrowser/filebrowser/commit/e410272e6be6a0b660efe8d4eee6c6e9dd834cc5))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* buttons without permission on header ([1516d99](https://github.com/filebrowser/filebrowser/commit/1516d9932bf9926ac8b4cb3e738a5f51e80d5b1d))
|
||||||
|
* check modify permission on file overwrite ([59f9964](https://github.com/filebrowser/filebrowser/commit/59f9964e80c8233775f27be33a4c16a31bfe848a))
|
||||||
|
* empty archive name on directory download ([2697093](https://github.com/filebrowser/filebrowser/commit/2697093ac151f74eea3022951d128acfe04d1dcf))
|
||||||
|
* empty text file on editor ([e9baf0c](https://github.com/filebrowser/filebrowser/commit/e9baf0c4b688fab291cdc842ec464c7a7a816499))
|
||||||
|
* error causes panic on upload ([e1a6f59](https://github.com/filebrowser/filebrowser/commit/e1a6f593e1824e7fa4345a61dff5b1bb8cd22d05))
|
||||||
|
* hidden editor header on Safari ([b521dec](https://github.com/filebrowser/filebrowser/commit/b521dec8f9b14dd92248c429e902ebc639046389))
|
||||||
|
* image quality switch on previewer ([c0d85f3](https://github.com/filebrowser/filebrowser/commit/c0d85f3d85926c8790757bf142140d19455ae8ca))
|
||||||
|
* list item interactions on share ([87f1881](https://github.com/filebrowser/filebrowser/commit/87f1881b429877a740ea84a8e783ad4103248289))
|
||||||
|
* missing bold variation for Roboto font ([98d79b8](https://github.com/filebrowser/filebrowser/commit/98d79b8ed955df5691a306d709b4ab60d91da408))
|
||||||
|
* mouse wheel zoom on previewer ([fcb115f](https://github.com/filebrowser/filebrowser/commit/fcb115f42d33db2be7a4d428ec53d65d6050320b))
|
||||||
|
* no header button animations on file listing ([fe80730](https://github.com/filebrowser/filebrowser/commit/fe80730bb135b38e4d9de470c75cbe10b1aec201))
|
||||||
|
|
||||||
|
### [2.14.1](https://github.com/filebrowser/filebrowser/compare/v2.14.0...v2.14.1) (2021-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* display public routes with header proxy auth ([da54bd6](https://github.com/filebrowser/filebrowser/commit/da54bd6c214d7ee39b71d710ddfe6dd25fc4e5d6))
|
||||||
|
|
||||||
|
## [2.14.0](https://github.com/filebrowser/filebrowser/compare/v2.13.0...v2.14.0) (2021-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add health check handler ([a721dc1](https://github.com/filebrowser/filebrowser/commit/a721dc1f314732e60d331a1a7da97d06e0e8b613))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* hide dotfile error on share ([5f4a031](https://github.com/filebrowser/filebrowser/commit/5f4a0317ab5685fe4a558df74e604c12e04a1c10))
|
||||||
|
* prefix handling on http router ([93a35ad](https://github.com/filebrowser/filebrowser/commit/93a35ad2516accdcb9735db509550979d01de2c3))
|
||||||
|
* qr code url on share ([22f4be8](https://github.com/filebrowser/filebrowser/commit/22f4be8f54162b7cf494177705ffb8b09117bd01))
|
||||||
|
* text file detection on editor ([eeadc53](https://github.com/filebrowser/filebrowser/commit/eeadc532fe6057969b3c1a4726f236851b154cfa))
|
||||||
|
|
||||||
|
## [2.13.0](https://github.com/filebrowser/filebrowser/compare/v2.12.1...v2.13.0) (2021-03-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* dual pane settings view ([db5aad8](https://github.com/filebrowser/filebrowser/commit/db5aad8eb679cfe1b1ace5142cf342951217f0f7))
|
||||||
|
* improved settings navbar ([5b28aa0](https://github.com/filebrowser/filebrowser/commit/5b28aa0848710b9d3ee02a2aa912856395f48bd2))
|
||||||
|
* improved sharing prompt ([1819377](https://github.com/filebrowser/filebrowser/commit/18193778971e27d18b5a35df8c2d0e2953b48111))
|
||||||
|
* increased header button counter size ([4fb832c](https://github.com/filebrowser/filebrowser/commit/4fb832c0422107e16f22b7aa928224f36de4978f))
|
||||||
|
* larger previewer content ([62fff5c](https://github.com/filebrowser/filebrowser/commit/62fff5ca60da1f887c1f95fa4808d3753596dab2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* archive contains parent path on Windows ([54f3570](https://github.com/filebrowser/filebrowser/commit/54f35701a2bd5cb7ec0628ca9789047072c073db))
|
||||||
|
* check rules on http resource handlers ([5bf1554](https://github.com/filebrowser/filebrowser/commit/5bf15548d0ad147acfad5000277531be2671f7ce))
|
||||||
|
* download current dir on file listing ([488d980](https://github.com/filebrowser/filebrowser/commit/488d98045e7476ed11e53c13d9498a9db3165bbc))
|
||||||
|
* encoded file path on share ([7955e07](https://github.com/filebrowser/filebrowser/commit/7955e0720baef3710106c7e69bbbf078d5489220))
|
||||||
|
* full file path on share ([e017a19](https://github.com/filebrowser/filebrowser/commit/e017a199850e19dd51b960ba59402c215fd8f1af))
|
||||||
|
* header dropdown icon color on previewer ([f8df76f](https://github.com/filebrowser/filebrowser/commit/f8df76f52684f10722ce123fec2c90e321ddf103))
|
||||||
|
* item dragging on file listing ([326b35a](https://github.com/filebrowser/filebrowser/commit/326b35a7ac7871afcdf892ca150349665b7f6379))
|
||||||
|
* modified time on info prompt ([11ebaec](https://github.com/filebrowser/filebrowser/commit/11ebaec5f0671ec02ebe55d4a73a514bce3a6713))
|
||||||
|
* root path name on archive ([426b38b](https://github.com/filebrowser/filebrowser/commit/426b38bb3362d2d477d0d8aa27d880664d537431))
|
||||||
|
* stuck icon on header button ([6a734c0](https://github.com/filebrowser/filebrowser/commit/6a734c01391b437c2842f5d97fb63f29a0017510))
|
||||||
|
* update image cache when replacing ([81b6f4d](https://github.com/filebrowser/filebrowser/commit/81b6f4d6f6a01886583016f61f4f1951a59f244d))
|
||||||
|
* wait for async command exit ([#1326](https://github.com/filebrowser/filebrowser/issues/1326)) ([6d5ceae](https://github.com/filebrowser/filebrowser/commit/6d5ceae8b454edd749b3b65c88aacc0a31ce9215))
|
||||||
|
|
||||||
|
|
||||||
|
### Refactorings
|
||||||
|
|
||||||
|
* migrate from rice to embed.FS ([fc55061](https://github.com/filebrowser/filebrowser/commit/fc5506179a64e9e2f57f7b6d6cce4b95f5ebc235))
|
||||||
|
|
||||||
|
### [2.12.1](https://github.com/filebrowser/filebrowser/compare/v2.12.0...v2.12.1) (2021-03-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing default config into the docker image ([7358b3f](https://github.com/filebrowser/filebrowser/commit/7358b3fe3178c20007b4b5ef5c03705badd538c4))
|
||||||
|
|
||||||
|
## [2.12.0](https://github.com/filebrowser/filebrowser/compare/v2.11.0...v2.12.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add homebrew tap ([2d2c598](https://github.com/filebrowser/filebrowser/commit/2d2c598fa6bd1ecaf39c542182890c8dd9b1cad0))
|
||||||
|
* added tiff files preview support ([#1222](https://github.com/filebrowser/filebrowser/issues/1222)) ([e8c9d1c](https://github.com/filebrowser/filebrowser/commit/e8c9d1c53989b4b52f6fba2a8ac41ae612c03a7c))
|
||||||
|
* allow disabling file detections by reading header ([#1175](https://github.com/filebrowser/filebrowser/issues/1175)) ([6914063](https://github.com/filebrowser/filebrowser/commit/6914063853a8a3f3cecfa4b21f223820c2a0b7df))
|
||||||
|
* allow to password protect shares ([#1252](https://github.com/filebrowser/filebrowser/issues/1252)) ([d8f415f](https://github.com/filebrowser/filebrowser/commit/d8f415f8abd0c4301803bd968c54429dd3fe4b59))
|
||||||
|
* build multi-arch docker images ([cf4836d](https://github.com/filebrowser/filebrowser/commit/cf4836dc757ef79ad615179bb7a6c7bbd3b09c2c))
|
||||||
|
* share management delete confirm ([#1212](https://github.com/filebrowser/filebrowser/issues/1212)) ([b600b11](https://github.com/filebrowser/filebrowser/commit/b600b11415fd1fb90ff2f5136be95a9c737ae1cb))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't allow to remove root user ([019ce80](https://github.com/filebrowser/filebrowser/commit/019ce80fc529a0437984fdc3d1ab6916f34dd594))
|
||||||
|
* double click to zoom pics in phone's browser ([#1274](https://github.com/filebrowser/filebrowser/issues/1274)) ([f1b7bd5](https://github.com/filebrowser/filebrowser/commit/f1b7bd59f67e719b7bfd203b0d7ec016fd21ab49))
|
||||||
|
* environmental variables not expanded in command ([#1241](https://github.com/filebrowser/filebrowser/issues/1241)) ([f3afd5c](https://github.com/filebrowser/filebrowser/commit/f3afd5cb79d6ad8b9cc8d54cb8fc2344b7c07d3d))
|
||||||
|
* fetch resource api once when sorting (closes [#1172](https://github.com/filebrowser/filebrowser/issues/1172)) ([#1202](https://github.com/filebrowser/filebrowser/issues/1202)) ([05bb7c8](https://github.com/filebrowser/filebrowser/commit/05bb7c85531349f3e9d1d8a523bb1243587b2ebc))
|
||||||
|
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
* use make for building the project ([#1304](https://github.com/filebrowser/filebrowser/issues/1304)) ([23f8464](https://github.com/filebrowser/filebrowser/commit/23f84642e6c1e07f89f98d2c1bb6fc9da36cc71c))
|
||||||
|
|
||||||
|
## [2.11.0](https://github.com/filebrowser/filebrowser/compare/v2.10.0...v2.11.0) (2020-12-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add sharing management ([#1178](https://github.com/filebrowser/filebrowser/issues/1178)) (closes [#1000](https://github.com/filebrowser/filebrowser/issues/1000)) ([677bce3](https://github.com/filebrowser/filebrowser/commit/677bce376b024d9ff38f34e74243034fe5a1ec3c))
|
||||||
|
* download shared subdirectory ([#1184](https://github.com/filebrowser/filebrowser/issues/1184)) ([fb5b28d](https://github.com/filebrowser/filebrowser/commit/fb5b28d9cbdee10d38fcd719b9fd832121be58ef))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* check user input to prevent permission elevation ([#1196](https://github.com/filebrowser/filebrowser/issues/1196)) (closes [#1195](https://github.com/filebrowser/filebrowser/issues/1195)) ([f62806f](https://github.com/filebrowser/filebrowser/commit/f62806f6c9e9c7f392d1b747d65b8fe40b313e89))
|
||||||
|
* delete extra remove prefix ([#1186](https://github.com/filebrowser/filebrowser/issues/1186)) ([7a5298a](https://github.com/filebrowser/filebrowser/commit/7a5298a7556f7dcc52f59b8ea76d040d3ddc3d12))
|
||||||
|
* move files between different volumes (closes [#1177](https://github.com/filebrowser/filebrowser/issues/1177)) ([58835b7](https://github.com/filebrowser/filebrowser/commit/58835b7e535cc96e1c8a5d85821c1545743ca757))
|
||||||
|
* recaptcha race condition ([#1176](https://github.com/filebrowser/filebrowser/issues/1176)) ([ac3673e](https://github.com/filebrowser/filebrowser/commit/ac3673e111afac6616af9650ca07028b6c27e6cd))
|
||||||
|
|
||||||
|
## [2.10.0](https://github.com/filebrowser/filebrowser/compare/v2.9.0...v2.10.0) (2020-11-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add hide dotfiles param ([#1148](https://github.com/filebrowser/filebrowser/issues/1148)) ([10e399b](https://github.com/filebrowser/filebrowser/commit/10e399b3c3dbdcfb4465a9d4138e1da6bae0873d))
|
||||||
|
* add single click mode ([#1139](https://github.com/filebrowser/filebrowser/issues/1139)) ([e8b4e9a](https://github.com/filebrowser/filebrowser/commit/e8b4e9af46d6e99dbeb965dd9727d9ed017d52a2))
|
||||||
|
* automatically jump to the next photo when deleting while previewing ([#1143](https://github.com/filebrowser/filebrowser/issues/1143)) ([9515cee](https://github.com/filebrowser/filebrowser/commit/9515ceeb42e5ef5267400220a2082dec775e843d))
|
||||||
|
* shared folder file listing ([e119bc5](https://github.com/filebrowser/filebrowser/commit/e119bc55ea82cefcbcc0571650107dfd5d73f570))
|
||||||
|
* shared item information ([36cacdf](https://github.com/filebrowser/filebrowser/commit/36cacdf598e4e09f064c8ace0ca7a6c24b23028e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* empty folder in archive ([7096b3d](https://github.com/filebrowser/filebrowser/commit/7096b3dab92441981c9964e4a6175af0a255d2be))
|
||||||
|
* fix hanging when reading a named pipe file (closes [#1155](https://github.com/filebrowser/filebrowser/issues/1155)) ([586d198](https://github.com/filebrowser/filebrowser/commit/586d198d47b525eeccc6fe587573a3ad83adb4f6))
|
||||||
|
* previewer title overflow ([4e48ffc](https://github.com/filebrowser/filebrowser/commit/4e48ffc14d09dabeea12dc495144277db62b5b7d))
|
||||||
|
* resource rename action invalid path ([1ce3068](https://github.com/filebrowser/filebrowser/commit/1ce3068a99c80c153fd41359255d173bce6e79e8))
|
||||||
|
|
||||||
|
## [2.9.0](https://github.com/filebrowser/filebrowser/compare/v2.8.0...v2.9.0) (2020-10-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* support WKWebview custom protocol ([#1113](https://github.com/filebrowser/filebrowser/issues/1113)) ([0ac80e8](https://github.com/filebrowser/filebrowser/commit/0ac80e8387a69924284259bde448af2813d84ed1))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow start from Windows explorer ([f2c4e78](https://github.com/filebrowser/filebrowser/commit/f2c4e78381610879eda5316d38a999c89df6c14a))
|
||||||
|
* file upload missing path slash ([5e27ba5](https://github.com/filebrowser/filebrowser/commit/5e27ba5c8c1be603c6ae7fec8de48e3532dea1f7))
|
||||||
|
* preview case sensitive file extension ([05bff54](https://github.com/filebrowser/filebrowser/commit/05bff54b71543fd232f1089c40504d0cbfd106be))
|
||||||
|
* search missing path slash ([2bd163d](https://github.com/filebrowser/filebrowser/commit/2bd163d92a856d65c8d4615e37898470c1edf2f4))
|
||||||
|
|
||||||
|
## [2.8.0](https://github.com/filebrowser/filebrowser/compare/v2.7.0...v2.8.0) (2020-10-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add disable exec flag ([#1090](https://github.com/filebrowser/filebrowser/issues/1090)) ([97693cc](https://github.com/filebrowser/filebrowser/commit/97693cc6117ce1c956baede91de5dd48b904e175))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* empty commands setting ([c6d4fcd](https://github.com/filebrowser/filebrowser/commit/c6d4fcd08f5f1531c2cef514dc86019e23e7289f))
|
||||||
|
* file upload path encoding ([babd778](https://github.com/filebrowser/filebrowser/commit/babd7783afe85b790e1c558375d7b5013b2d366f))
|
||||||
|
* fix empty command name ([#1106](https://github.com/filebrowser/filebrowser/issues/1106)) ([36fb9f5](https://github.com/filebrowser/filebrowser/commit/36fb9f562a2c005ca4390fdebde0b4690201dff9))
|
||||||
|
* fix panic when accessing nonexistent .js file in static path ([#1105](https://github.com/filebrowser/filebrowser/issues/1105)) ([ad99bf1](https://github.com/filebrowser/filebrowser/commit/ad99bf180197e0e6d82231a86457585de16366a8))
|
||||||
|
* preview key shortcut conflict ([dd7b9dd](https://github.com/filebrowser/filebrowser/commit/dd7b9ddd8546361060ef99e838a691b2fc6c495a))
|
||||||
|
* search results absolute url ([26d62e4](https://github.com/filebrowser/filebrowser/commit/26d62e411716a5eb9a5a703e47484cfb3fbf3bd0))
|
||||||
|
|
||||||
|
## [2.7.0](https://github.com/filebrowser/filebrowser/compare/v2.6.2...v2.7.0) (2020-09-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add --socket-perm flag to control unix socket file permissions (closes [#1060](https://github.com/filebrowser/filebrowser/issues/1060)) ([65ac734](https://github.com/filebrowser/filebrowser/commit/65ac73414fadc4686c94803a93ff319e8f7ce9d1))
|
||||||
|
* preview mobile dropdown ([7787344](https://github.com/filebrowser/filebrowser/commit/778734419de314d4cb64d07109bbab73f8e2e42a))
|
||||||
|
* preview size button ([3d2cb83](https://github.com/filebrowser/filebrowser/commit/3d2cb838d111ee61047599f49e76de80c821f341))
|
||||||
|
* put selected files in the root of the archive (closes [#1065](https://github.com/filebrowser/filebrowser/issues/1065)) ([8142b32](https://github.com/filebrowser/filebrowser/commit/8142b32f3865eccd3331328e0d087f805d186ed5))
|
||||||
|
|
||||||
|
### [2.6.2](https://github.com/filebrowser/filebrowser/compare/v2.6.1...v2.6.2) (2020-08-05)
|
||||||
|
|
||||||
|
### [2.6.1](https://github.com/filebrowser/filebrowser/compare/v2.6.0...v2.6.1) (2020-07-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* delete cached previews when deleting file ([f5d02cd](https://github.com/filebrowser/filebrowser/commit/f5d02cdde97923b963878abf5a300393b9feb348))
|
||||||
|
* escape special characters in preview url (closes [#1002](https://github.com/filebrowser/filebrowser/issues/1002)) ([c9340af](https://github.com/filebrowser/filebrowser/commit/c9340af8d045671ad3338c5d2d887c335ab92de4))
|
||||||
|
|
||||||
|
## [2.6.0](https://github.com/filebrowser/filebrowser/compare/v2.5.0...v2.6.0) (2020-07-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add lazy load of image thumbnails ([bc00165](https://github.com/filebrowser/filebrowser/commit/bc001650944ae963b12b5b2538a68de7cd0d8f82))
|
||||||
|
* add param to disable img resizing ([aa78e3a](https://github.com/filebrowser/filebrowser/commit/aa78e3ab1fcae6f618e811ba4e315a7a209f9df2))
|
||||||
|
* cache resized images ([95bc929](https://github.com/filebrowser/filebrowser/commit/95bc92955f391ece22c40d9592f2a3e6e26907b9))
|
||||||
|
* limit image resize workers ([94ef596](https://github.com/filebrowser/filebrowser/commit/94ef59602fb50fc21b1164feda90a3b9aeb5e972))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* conflict handling on upload button ([f228fa5](https://github.com/filebrowser/filebrowser/commit/f228fa55408824618e9f0879da67c86d22b0d324))
|
||||||
|
* drop feedback ([f2d2c1c](https://github.com/filebrowser/filebrowser/commit/f2d2c1cbf85fba3edffb7b079f121ed3f0bc1e02))
|
||||||
|
* missing error message ([d9be370](https://github.com/filebrowser/filebrowser/commit/d9be370e2474b8070fa58db920c9481270cc4a48))
|
||||||
|
* parent verification on copy ([727c63b](https://github.com/filebrowser/filebrowser/commit/727c63b98e2964d0960d25914c296570f6c79478))
|
||||||
|
* path separator inconsistency on rename ([34dfb49](https://github.com/filebrowser/filebrowser/commit/34dfb49b719c948e709a4639b4af2c5cb73b3887))
|
||||||
|
|
||||||
|
## [2.5.0](https://github.com/filebrowser/filebrowser/compare/v2.4.0...v2.5.0) (2020-07-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add previewer title and loading indicator ([716396a](https://github.com/filebrowser/filebrowser/commit/716396a726329f0ba42fc34167dd07497c5bf47c))
|
||||||
|
* duplicate files in the same directory ([43526d9](https://github.com/filebrowser/filebrowser/commit/43526d9d1a8c837245e3f5059e0b4737583eeaeb))
|
||||||
|
* file copy, move and paste conflict checking ([eed9da1](https://github.com/filebrowser/filebrowser/commit/eed9da1471723ed3fbe6c00b1d6362b1c5fd8b04))
|
||||||
|
* rename option on replace prompt ([2636f87](https://github.com/filebrowser/filebrowser/commit/2636f876ab8f88eea6d9548de524ca2339eb0843))
|
||||||
|
* upload queue ([6ec6a23](https://github.com/filebrowser/filebrowser/commit/6ec6a2386173410f5cab9941dbf1bacb6b70ddd2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* blinking previewer ([9a2ebba](https://github.com/filebrowser/filebrowser/commit/9a2ebbabe2e9f0c292701d33f36f9b7a457b1164))
|
||||||
|
* dark theme colors ([b3b6445](https://github.com/filebrowser/filebrowser/commit/b3b644527d5673e16e61d404ff58a3c7bd6b6637))
|
||||||
|
* directory conflict checking ([7e5beef](https://github.com/filebrowser/filebrowser/commit/7e5beeff464e75ab185c430cd96e7cc67209ccc1))
|
||||||
|
* prompt before closing window ([194030f](https://github.com/filebrowser/filebrowser/commit/194030fcfcf54a2cf5e2f8ececcbb4754474d8f8))
|
||||||
|
* remove incomplete uploaded files ([0727496](https://github.com/filebrowser/filebrowser/commit/0727496601a9918c8131c56f62419bfac7ac589a))
|
||||||
|
* reset clipboard after pasting cutted files ([10570ad](https://github.com/filebrowser/filebrowser/commit/10570ade442b573ebe00af08369e28b1b0688df6))
|
||||||
|
|
||||||
|
## [2.4.0](https://github.com/filebrowser/filebrowser/compare/v2.3.0...v2.4.0) (2020-07-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* full screen editor ([0d665e5](https://github.com/filebrowser/filebrowser/commit/0d665e528f880ceda0976ceed66070ac34de7969))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add preview bypass for .gif files ([#1012](https://github.com/filebrowser/filebrowser/issues/1012)) ([453636d](https://github.com/filebrowser/filebrowser/commit/453636dfe2bbf177c74617862eb763485d4774bf))
|
||||||
|
* prompt key shortcut conflict ([0d69fbd](https://github.com/filebrowser/filebrowser/commit/0d69fbd9a342aa2695859021df0c423e3ae4a4fa))
|
||||||
|
|
||||||
|
## [2.3.0](https://github.com/filebrowser/filebrowser/compare/v2.2.0...v2.3.0) (2020-06-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add image thumbnails support ([#980](https://github.com/filebrowser/filebrowser/issues/980)) ([6b0d49b](https://github.com/filebrowser/filebrowser/commit/6b0d49b1fc8bdce89576ba91cc0b8ec594fcd625))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* typo in image_templates (apline -> alpine) ([#1005](https://github.com/filebrowser/filebrowser/issues/1005)) ([84da110](https://github.com/filebrowser/filebrowser/commit/84da11008516a371fc0446d97863dc14d337aa25))
|
||||||
|
|
||||||
|
## [2.2.0](https://github.com/filebrowser/filebrowser/compare/v2.1.2...v2.2.0) (2020-06-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add alpine and debian docker images ([66863b7](https://github.com/filebrowser/filebrowser/commit/66863b72f7664e6cb9417f7da542a92fa77ca635))
|
||||||
|
* add folder upload ([#981](https://github.com/filebrowser/filebrowser/issues/981)) ([8977344](https://github.com/filebrowser/filebrowser/commit/89773447a56675b298394149d7a05c5df4039f14)), closes [filebrowser/filebrowser#741](https://github.com/filebrowser/filebrowser/issues/741)
|
||||||
|
* add key shortcuts ([95316cb](https://github.com/filebrowser/filebrowser/commit/95316cbe8c8ac3dbb28310bc11ec347c0caf699b))
|
||||||
|
* upload progress based on total size ([#993](https://github.com/filebrowser/filebrowser/issues/993)) ([cd454ba](https://github.com/filebrowser/filebrowser/commit/cd454bae51f40b1249e6fa6133c2949970eb3018))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add a workaround to fix window freezing when viewing a large file [#992](https://github.com/filebrowser/filebrowser/issues/992) ([2412016](https://github.com/filebrowser/filebrowser/commit/241201657c2bf01806d02a297eb846b26102a479))
|
||||||
|
* apply all fs user rulles ([68f8348](https://github.com/filebrowser/filebrowser/commit/68f8348ddeecba570a361e7aba4546052cc3e356))
|
||||||
|
* frontend token validation ([dd40b0d](https://github.com/filebrowser/filebrowser/commit/dd40b0d9b9cc6268a611306ac4684a1af852b79d)), closes [filebrowser/filebrowser#638](https://github.com/filebrowser/filebrowser/issues/638)
|
||||||
|
* multiple selection count ([963837e](https://github.com/filebrowser/filebrowser/commit/963837ef1dc6e2e84fcf924606ce388ac30f3891))
|
||||||
|
* save event hook ([82c883f](https://github.com/filebrowser/filebrowser/commit/82c883f95eead9eebe215e230f74773c945f864a)), closes [filebrowser/filebrowser#696](https://github.com/filebrowser/filebrowser/issues/696)
|
||||||
14
Dockerfile
@@ -1,13 +1,15 @@
|
|||||||
FROM alpine:latest as certs
|
FROM alpine:latest
|
||||||
RUN apk --update add ca-certificates
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
FROM scratch
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
VOLUME /srv
|
VOLUME /srv
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
COPY .docker.json /.filebrowser.json
|
COPY docker_config.json /.filebrowser.json
|
||||||
COPY filebrowser /filebrowser
|
COPY filebrowser /filebrowser
|
||||||
|
|
||||||
ENTRYPOINT [ "/filebrowser" ]
|
ENTRYPOINT [ "/filebrowser" ]
|
||||||
16
Dockerfile.s6
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM ghcr.io/linuxserver/baseimage-alpine:3.14
|
||||||
|
|
||||||
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
|
# copy local files
|
||||||
|
COPY docker/root/ /
|
||||||
|
COPY filebrowser /usr/bin/filebrowser
|
||||||
|
|
||||||
|
# ports and volumes
|
||||||
|
VOLUME /srv /config /database
|
||||||
|
EXPOSE 80
|
||||||
16
Dockerfile.s6.aarch64
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM ghcr.io/linuxserver/baseimage-alpine:arm64v8-3.14
|
||||||
|
|
||||||
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
|
# copy local files
|
||||||
|
COPY docker/root/ /
|
||||||
|
COPY filebrowser /usr/bin/filebrowser
|
||||||
|
|
||||||
|
# ports and volumes
|
||||||
|
VOLUME /srv /config /database
|
||||||
|
EXPOSE 80
|
||||||
16
Dockerfile.s6.armhf
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM ghcr.io/linuxserver/baseimage-alpine:arm32v7-3.14
|
||||||
|
|
||||||
|
RUN apk --update add ca-certificates \
|
||||||
|
mailcap \
|
||||||
|
curl
|
||||||
|
|
||||||
|
HEALTHCHECK --start-period=2s --interval=5s --timeout=3s \
|
||||||
|
CMD curl -f http://localhost/health || exit 1
|
||||||
|
|
||||||
|
# copy local files
|
||||||
|
COPY docker/root/ /
|
||||||
|
COPY filebrowser /usr/bin/filebrowser
|
||||||
|
|
||||||
|
# ports and volumes
|
||||||
|
VOLUME /srv /config /database
|
||||||
|
EXPOSE 80
|
||||||
68
Makefile
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
include common.mk
|
||||||
|
include tools.mk
|
||||||
|
|
||||||
|
LDFLAGS += -X "$(MODULE)/version.Version=$(VERSION)" -X "$(MODULE)/version.CommitSHA=$(VERSION_HASH)"
|
||||||
|
|
||||||
|
## Build:
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: | build-frontend build-backend ## Build binary
|
||||||
|
|
||||||
|
.PHONY: build-frontend
|
||||||
|
build-frontend: ## Build frontend
|
||||||
|
$Q cd frontend && npm ci && npm run build
|
||||||
|
|
||||||
|
.PHONY: build-backend
|
||||||
|
build-backend: ## Build backend
|
||||||
|
$Q $(go) build -ldflags '$(LDFLAGS)' -o .
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: | test-frontend test-backend ## Run all tests
|
||||||
|
|
||||||
|
.PHONY: test-frontend
|
||||||
|
test-frontend: ## Run frontend tests
|
||||||
|
|
||||||
|
.PHONY: test-backend
|
||||||
|
test-backend: ## Run backend tests
|
||||||
|
$Q $(go) test -v ./...
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: lint-frontend lint-backend lint-commits ## Run all linters
|
||||||
|
|
||||||
|
.PHONY: lint-frontend
|
||||||
|
lint-frontend: ## Run frontend linters
|
||||||
|
$Q cd frontend && npm ci && npm run lint
|
||||||
|
|
||||||
|
.PHONY: lint-backend
|
||||||
|
lint-backend: | $(golangci-lint) ## Run backend linters
|
||||||
|
$Q $(golangci-lint) run -v
|
||||||
|
|
||||||
|
.PHONY: lint-commits
|
||||||
|
lint-commits: $(commitlint) ## Run commit linters
|
||||||
|
$Q ./scripts/commitlint.sh
|
||||||
|
|
||||||
|
fmt: $(goimports) ## Format source files
|
||||||
|
$Q $(goimports) -local $(MODULE) -w $$(find . -type f -name '*.go' -not -path "./vendor/*")
|
||||||
|
|
||||||
|
clean: clean-tools ## Clean
|
||||||
|
|
||||||
|
## Release:
|
||||||
|
|
||||||
|
.PHONY: bump-version
|
||||||
|
bump-version: $(standard-version) ## Bump app version
|
||||||
|
$Q ./scripts/bump_version.sh
|
||||||
|
|
||||||
|
## Help:
|
||||||
|
help: ## Show this help
|
||||||
|
@echo ''
|
||||||
|
@echo 'Usage:'
|
||||||
|
@echo ' ${YELLOW}make${RESET} ${GREEN}<target> [options]${RESET}'
|
||||||
|
@echo ''
|
||||||
|
@echo 'Options:'
|
||||||
|
@$(call global_option, "V [0|1]", "enable verbose mode (default:0)")
|
||||||
|
@echo ''
|
||||||
|
@echo 'Targets:'
|
||||||
|
@awk 'BEGIN {FS = ":.*?## "} { \
|
||||||
|
if (/^[a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \
|
||||||
|
else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \
|
||||||
|
}' $(MAKEFILE_LIST)
|
||||||
20
README.md
@@ -4,28 +4,30 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
[](https://travis-ci.com/filebrowser/filebrowser)
|
[](https://github.com/filebrowser/filebrowser/actions/workflows/main.yaml)
|
||||||
[](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
|
[](https://goreportcard.com/report/github.com/filebrowser/filebrowser)
|
||||||
[](http://godoc.org/github.com/filebrowser/filebrowser)
|
[](http://godoc.org/github.com/filebrowser/filebrowser)
|
||||||
[](https://github.com/filebrowser/filebrowser/releases/latest)
|
[](https://github.com/filebrowser/filebrowser/releases/latest)
|
||||||
[](http://webchat.freenode.net/?channels=%23filebrowser)
|
[](http://webchat.freenode.net/?channels=%23filebrowser)
|
||||||
|
|
||||||
> ℹ INFO: **This project is not under active development ATM. A small group of developers keeps the project alive, but due to lack of time, we can't continue adding new features or doing deep changes. Please read [#532](https://github.com/filebrowser/filebrowser/issues/532) for more info!**
|
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app.
|
||||||
|
|
||||||
filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Please refer to our docs at [docs.filebrowser.xyz/features](https://docs.filebrowser.xyz/features)
|
Please refer to our docs at [https://filebrowser.org/features](https://filebrowser.org/features)
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Please refer to our docs at [docs.filebrowser.xyz](https://docs.filebrowser.xyz/).
|
For installation instructions please refer to our docs at [https://filebrowser.org/installation](https://filebrowser.org/installation).
|
||||||
|
|
||||||
## Usage
|
## Configuration
|
||||||
|
|
||||||
Please refer to our docs at [docs.filebrowser.xyz/usage](https://docs.filebrowser.xyz/usage).
|
[Authentication Method](https://filebrowser.org/configuration/authentication-method) - You can change the way the user authenticates with the filebrowser server
|
||||||
|
|
||||||
|
[Command Runner](https://filebrowser.org/configuration/command-runner) - The command runner is a feature that enables you to execute any shell command you want before or after a certain event.
|
||||||
|
|
||||||
|
[Custom Branding](https://filebrowser.org/configuration/custom-branding) - You can customize your File Browser installation by change its name to any other you want, by adding a global custom style sheet and by using your own logotype if you want.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Please refer to our docs at [docs.filebrowser.xyz/contributing](https://docs.filebrowser.xyz/contributing).
|
If you're interested in contributing to this project, our docs are best places to start [https://filebrowser.org/contributing](https://filebrowser.org/contributing).
|
||||||
|
|||||||
26
SECURITY.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 2.x | :white_check_mark: |
|
||||||
|
| < 2.0 | :x: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Vulnerabilities should be reported to filebrowser@googlegroups.com - which is a private, maintainer-only group. Maintainers will attempt to respond to/confirm reports within 2-3 days, but if you believe your report to be "critical" to user safety and security, please note as such in the subject. We have tens of thousands of users using our software, and take security vulnerabilities seriously.
|
||||||
|
|
||||||
|
When reporting an issue, where possible, please provide at least:
|
||||||
|
|
||||||
|
* The commit version the issue was identified at
|
||||||
|
* A proof of concept (plaintext; no binaries)
|
||||||
|
* Steps to reproduce
|
||||||
|
* Your recommended remediation(s), if any.
|
||||||
|
|
||||||
|
The FileBrowser team is a volunteer-only effort, and may reach back out for clarification.
|
||||||
|
|
||||||
|
> Note: Please do not open public issues for security issues, as GitHub does not provide facility for private issues, and deleting the issue makes it hard to triage/respond back to the reporter.
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
// Auther is the authentication interface.
|
// Auther is the authentication interface.
|
||||||
type Auther interface {
|
type Auther interface {
|
||||||
// Auth is called to authenticate a request.
|
// Auth is called to authenticate a request.
|
||||||
Auth(r *http.Request, s *users.Storage, root string) (*users.User, error)
|
Auth(r *http.Request, s users.Store, root string) (*users.User, error)
|
||||||
// LoginPage indicates if this auther needs a login page.
|
// LoginPage indicates if this auther needs a login page.
|
||||||
LoginPage() bool
|
LoginPage() bool
|
||||||
}
|
}
|
||||||
|
|||||||
11
auth/json.go
@@ -20,13 +20,13 @@ type jsonCred struct {
|
|||||||
ReCaptcha string `json:"recaptcha"`
|
ReCaptcha string `json:"recaptcha"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONAuth is a json implementaion of an Auther.
|
// JSONAuth is a json implementation of an Auther.
|
||||||
type JSONAuth struct {
|
type JSONAuth struct {
|
||||||
ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"`
|
ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth authenticates the user via a json in content body.
|
// Auth authenticates the user via a json in content body.
|
||||||
func (a JSONAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users.User, error) {
|
func (a JSONAuth) Auth(r *http.Request, sto users.Store, root string) (*users.User, error) {
|
||||||
var cred jsonCred
|
var cred jsonCred
|
||||||
|
|
||||||
if r.Body == nil {
|
if r.Body == nil {
|
||||||
@@ -40,7 +40,7 @@ func (a JSONAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users
|
|||||||
|
|
||||||
// If ReCaptcha is enabled, check the code.
|
// If ReCaptcha is enabled, check the code.
|
||||||
if a.ReCaptcha != nil && len(a.ReCaptcha.Secret) > 0 {
|
if a.ReCaptcha != nil && len(a.ReCaptcha.Secret) > 0 {
|
||||||
ok, err := a.ReCaptcha.Ok(cred.ReCaptcha)
|
ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) //nolint:govet
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -66,7 +66,7 @@ func (a JSONAuth) LoginPage() bool {
|
|||||||
|
|
||||||
const reCaptchaAPI = "/recaptcha/api/siteverify"
|
const reCaptchaAPI = "/recaptcha/api/siteverify"
|
||||||
|
|
||||||
// ReCaptcha identifies a recaptcha conenction.
|
// ReCaptcha identifies a recaptcha connection.
|
||||||
type ReCaptcha struct {
|
type ReCaptcha struct {
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
@@ -76,7 +76,7 @@ type ReCaptcha struct {
|
|||||||
// Ok checks if a reCaptcha responde is correct.
|
// Ok checks if a reCaptcha responde is correct.
|
||||||
func (r *ReCaptcha) Ok(response string) (bool, error) {
|
func (r *ReCaptcha) Ok(response string) (bool, error) {
|
||||||
body := url.Values{}
|
body := url.Values{}
|
||||||
body.Set("secret", r.Key)
|
body.Set("secret", r.Secret)
|
||||||
body.Add("response", response)
|
body.Add("response", response)
|
||||||
|
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
@@ -89,6 +89,7 @@ func (r *ReCaptcha) Ok(response string) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const MethodNoAuth settings.AuthMethod = "noauth"
|
|||||||
type NoAuth struct{}
|
type NoAuth struct{}
|
||||||
|
|
||||||
// Auth uses authenticates user 1.
|
// Auth uses authenticates user 1.
|
||||||
func (a NoAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users.User, error) {
|
func (a NoAuth) Auth(r *http.Request, sto users.Store, root string) (*users.User, error) {
|
||||||
return sto.Get(root, uint(1))
|
return sto.Get(root, uint(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type ProxyAuth struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auth authenticates the user via an HTTP header.
|
// Auth authenticates the user via an HTTP header.
|
||||||
func (a ProxyAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users.User, error) {
|
func (a ProxyAuth) Auth(r *http.Request, sto users.Store, root string) (*users.User, error) {
|
||||||
username := r.Header.Get(a.Header)
|
username := r.Header.Get(a.Header)
|
||||||
user, err := sto.Get(root, username)
|
user, err := sto.Get(root, username)
|
||||||
if err == errors.ErrNotExist {
|
if err == errors.ErrNotExist {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ type Storage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewStorage creates a auth storage from a backend.
|
// NewStorage creates a auth storage from a backend.
|
||||||
func NewStorage(back StorageBackend, users *users.Storage) *Storage {
|
func NewStorage(back StorageBackend, userStore *users.Storage) *Storage {
|
||||||
return &Storage{back: back, users: users}
|
return &Storage{back: back, users: userStore}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get wraps a StorageBackend.Get.
|
// Get wraps a StorageBackend.Get.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ var cmdsAddCmd = &cobra.Command{
|
|||||||
Use: "add <event> <command>",
|
Use: "add <event> <command>",
|
||||||
Short: "Add a command to run on a specific event",
|
Short: "Add a command to run on a specific event",
|
||||||
Long: `Add a command to run on a specific event.`,
|
Long: `Add a command to run on a specific event.`,
|
||||||
Args: cobra.MinimumNArgs(2),
|
Args: cobra.MinimumNArgs(2), //nolint:gomnd
|
||||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||||
s, err := d.store.Settings.Get()
|
s, err := d.store.Settings.Get()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ You can also specify an optional parameter (index_end) so
|
|||||||
you can remove all commands from 'index' to 'index_end',
|
you can remove all commands from 'index' to 'index_end',
|
||||||
including 'index_end'.`,
|
including 'index_end'.`,
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil {
|
if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { //nolint:gomnd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ including 'index_end'.`,
|
|||||||
i, err := strconv.Atoi(args[1])
|
i, err := strconv.Atoi(args[1])
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
f := i
|
f := i
|
||||||
if len(args) == 3 {
|
if len(args) == 3 { //nolint:gomnd
|
||||||
f, err = strconv.Atoi(args[2])
|
f, err = strconv.Atoi(args[2])
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/auth"
|
"github.com/filebrowser/filebrowser/v2/auth"
|
||||||
"github.com/filebrowser/filebrowser/v2/errors"
|
"github.com/filebrowser/filebrowser/v2/errors"
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -40,19 +41,44 @@ func addConfigFlags(flags *pflag.FlagSet) {
|
|||||||
flags.String("recaptcha.secret", "", "ReCaptcha secret")
|
flags.String("recaptcha.secret", "", "ReCaptcha secret")
|
||||||
|
|
||||||
flags.String("branding.name", "", "replace 'File Browser' by this name")
|
flags.String("branding.name", "", "replace 'File Browser' by this name")
|
||||||
|
flags.String("branding.color", "", "set the theme color")
|
||||||
flags.String("branding.files", "", "path to directory with images and custom styles")
|
flags.String("branding.files", "", "path to directory with images and custom styles")
|
||||||
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
|
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAuthentication(flags *pflag.FlagSet) (settings.AuthMethod, auth.Auther) {
|
//nolint:gocyclo
|
||||||
|
func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.AuthMethod, auth.Auther) {
|
||||||
method := settings.AuthMethod(mustGetString(flags, "auth.method"))
|
method := settings.AuthMethod(mustGetString(flags, "auth.method"))
|
||||||
|
|
||||||
|
var defaultAuther map[string]interface{}
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
if hasAuth := defaults[0]; hasAuth != true {
|
||||||
|
for _, arg := range defaults {
|
||||||
|
switch def := arg.(type) {
|
||||||
|
case *settings.Settings:
|
||||||
|
method = def.AuthMethod
|
||||||
|
case auth.Auther:
|
||||||
|
ms, err := json.Marshal(def)
|
||||||
|
checkErr(err)
|
||||||
|
err = json.Unmarshal(ms, &defaultAuther)
|
||||||
|
checkErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var auther auth.Auther
|
var auther auth.Auther
|
||||||
if method == auth.MethodProxyAuth {
|
if method == auth.MethodProxyAuth {
|
||||||
header := mustGetString(flags, "auth.header")
|
header := mustGetString(flags, "auth.header")
|
||||||
|
|
||||||
if header == "" {
|
if header == "" {
|
||||||
panic(nerrors.New("you must set the flag 'auth.header' for method 'proxy'"))
|
header = defaultAuther["header"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if header == "" {
|
||||||
|
checkErr(nerrors.New("you must set the flag 'auth.header' for method 'proxy'"))
|
||||||
|
}
|
||||||
|
|
||||||
auther = &auth.ProxyAuth{Header: header}
|
auther = &auth.ProxyAuth{Header: header}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,11 +88,22 @@ func getAuthentication(flags *pflag.FlagSet) (settings.AuthMethod, auth.Auther)
|
|||||||
|
|
||||||
if method == auth.MethodJSONAuth {
|
if method == auth.MethodJSONAuth {
|
||||||
jsonAuth := &auth.JSONAuth{}
|
jsonAuth := &auth.JSONAuth{}
|
||||||
|
|
||||||
host := mustGetString(flags, "recaptcha.host")
|
host := mustGetString(flags, "recaptcha.host")
|
||||||
key := mustGetString(flags, "recaptcha.key")
|
key := mustGetString(flags, "recaptcha.key")
|
||||||
secret := mustGetString(flags, "recaptcha.secret")
|
secret := mustGetString(flags, "recaptcha.secret")
|
||||||
|
|
||||||
|
if key == "" {
|
||||||
|
if kmap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok {
|
||||||
|
key = kmap["key"].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if secret == "" {
|
||||||
|
if smap, ok := defaultAuther["recaptcha"].(map[string]interface{}); ok {
|
||||||
|
secret = smap["secret"].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if key != "" && secret != "" {
|
if key != "" && secret != "" {
|
||||||
jsonAuth.ReCaptcha = &auth.ReCaptcha{
|
jsonAuth.ReCaptcha = &auth.ReCaptcha{
|
||||||
Host: host,
|
Host: host,
|
||||||
@@ -74,7 +111,6 @@ func getAuthentication(flags *pflag.FlagSet) (settings.AuthMethod, auth.Auther)
|
|||||||
Secret: secret,
|
Secret: secret,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auther = jsonAuth
|
auther = jsonAuth
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +122,7 @@ func getAuthentication(flags *pflag.FlagSet) (settings.AuthMethod, auth.Auther)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) {
|
func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Auther) {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
|
||||||
|
|
||||||
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
||||||
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
||||||
@@ -96,18 +132,22 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
|
|||||||
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
|
fmt.Fprintf(w, "\tName:\t%s\n", set.Branding.Name)
|
||||||
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
|
fmt.Fprintf(w, "\tFiles override:\t%s\n", set.Branding.Files)
|
||||||
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal)
|
fmt.Fprintf(w, "\tDisable external links:\t%t\n", set.Branding.DisableExternal)
|
||||||
|
fmt.Fprintf(w, "\tColor:\t%s\n", set.Branding.Color)
|
||||||
fmt.Fprintln(w, "\nServer:")
|
fmt.Fprintln(w, "\nServer:")
|
||||||
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
|
fmt.Fprintf(w, "\tLog:\t%s\n", ser.Log)
|
||||||
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
|
fmt.Fprintf(w, "\tPort:\t%s\n", ser.Port)
|
||||||
fmt.Fprintf(w, "\tBase URL:\t%s\n", ser.BaseURL)
|
fmt.Fprintf(w, "\tBase URL:\t%s\n", ser.BaseURL)
|
||||||
fmt.Fprintf(w, "\tRoot:\t%s\n", ser.Root)
|
fmt.Fprintf(w, "\tRoot:\t%s\n", ser.Root)
|
||||||
|
fmt.Fprintf(w, "\tSocket:\t%s\n", ser.Socket)
|
||||||
fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address)
|
fmt.Fprintf(w, "\tAddress:\t%s\n", ser.Address)
|
||||||
fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert)
|
fmt.Fprintf(w, "\tTLS Cert:\t%s\n", ser.TLSCert)
|
||||||
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
|
fmt.Fprintf(w, "\tTLS Key:\t%s\n", ser.TLSKey)
|
||||||
|
fmt.Fprintf(w, "\tExec Enabled:\t%t\n", ser.EnableExec)
|
||||||
fmt.Fprintln(w, "\nDefaults:")
|
fmt.Fprintln(w, "\nDefaults:")
|
||||||
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
|
fmt.Fprintf(w, "\tScope:\t%s\n", set.Defaults.Scope)
|
||||||
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
|
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
|
||||||
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
|
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
|
||||||
|
fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick)
|
||||||
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))
|
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))
|
||||||
fmt.Fprintf(w, "\tSorting:\n")
|
fmt.Fprintf(w, "\tSorting:\n")
|
||||||
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
|
fmt.Fprintf(w, "\t\tBy:\t%s\n", set.Defaults.Sorting.By)
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/auth"
|
"github.com/filebrowser/filebrowser/v2/auth"
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -55,7 +56,7 @@ The path must be for a json or yaml file.`,
|
|||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
var rawAuther interface{}
|
var rawAuther interface{}
|
||||||
if filepath.Ext(args[0]) != ".json" {
|
if filepath.Ext(args[0]) != ".json" { //nolint:goconst
|
||||||
rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{}))
|
rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{}))
|
||||||
} else {
|
} else {
|
||||||
rawAuther = file.Auther
|
rawAuther = file.Auther
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -31,7 +31,7 @@ override the options.`,
|
|||||||
s := &settings.Settings{
|
s := &settings.Settings{
|
||||||
Key: generateKey(),
|
Key: generateKey(),
|
||||||
Signup: mustGetBool(flags, "signup"),
|
Signup: mustGetBool(flags, "signup"),
|
||||||
Shell: strings.Split(strings.TrimSpace(mustGetString(flags, "shell")), " "),
|
Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")),
|
||||||
AuthMethod: authMethod,
|
AuthMethod: authMethod,
|
||||||
Defaults: defaults,
|
Defaults: defaults,
|
||||||
Branding: settings.Branding{
|
Branding: settings.Branding{
|
||||||
@@ -43,6 +43,7 @@ override the options.`,
|
|||||||
|
|
||||||
ser := &settings.Server{
|
ser := &settings.Server{
|
||||||
Address: mustGetString(flags, "address"),
|
Address: mustGetString(flags, "address"),
|
||||||
|
Socket: mustGetString(flags, "socket"),
|
||||||
Root: mustGetString(flags, "root"),
|
Root: mustGetString(flags, "root"),
|
||||||
BaseURL: mustGetString(flags, "baseurl"),
|
BaseURL: mustGetString(flags, "baseurl"),
|
||||||
TLSKey: mustGetString(flags, "key"),
|
TLSKey: mustGetString(flags, "key"),
|
||||||
@@ -60,7 +61,7 @@ override the options.`,
|
|||||||
|
|
||||||
fmt.Printf(`
|
fmt.Printf(`
|
||||||
Congratulations! You've set up your database to use with File Browser.
|
Congratulations! You've set up your database to use with File Browser.
|
||||||
Now add your first user via 'filebrowser users new' and then you just
|
Now add your first user via 'filebrowser users add' and then you just
|
||||||
need to call the main command to boot up the server.
|
need to call the main command to boot up the server.
|
||||||
`)
|
`)
|
||||||
printSettings(ser, s, auther)
|
printSettings(ser, s, auther)
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/auth"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
@@ -34,6 +31,8 @@ you want to change. Other options will remain unchanged.`,
|
|||||||
ser.BaseURL = mustGetString(flags, flag.Name)
|
ser.BaseURL = mustGetString(flags, flag.Name)
|
||||||
case "root":
|
case "root":
|
||||||
ser.Root = mustGetString(flags, flag.Name)
|
ser.Root = mustGetString(flags, flag.Name)
|
||||||
|
case "socket":
|
||||||
|
ser.Socket = mustGetString(flags, flag.Name)
|
||||||
case "cert":
|
case "cert":
|
||||||
ser.TLSCert = mustGetString(flags, flag.Name)
|
ser.TLSCert = mustGetString(flags, flag.Name)
|
||||||
case "key":
|
case "key":
|
||||||
@@ -49,9 +48,11 @@ you want to change. Other options will remain unchanged.`,
|
|||||||
case "auth.method":
|
case "auth.method":
|
||||||
hasAuth = true
|
hasAuth = true
|
||||||
case "shell":
|
case "shell":
|
||||||
set.Shell = strings.Split(strings.TrimSpace(mustGetString(flags, flag.Name)), " ")
|
set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name))
|
||||||
case "branding.name":
|
case "branding.name":
|
||||||
set.Branding.Name = mustGetString(flags, flag.Name)
|
set.Branding.Name = mustGetString(flags, flag.Name)
|
||||||
|
case "branding.color":
|
||||||
|
set.Branding.Color = mustGetString(flags, flag.Name)
|
||||||
case "branding.disableExternal":
|
case "branding.disableExternal":
|
||||||
set.Branding.DisableExternal = mustGetBool(flags, flag.Name)
|
set.Branding.DisableExternal = mustGetBool(flags, flag.Name)
|
||||||
case "branding.files":
|
case "branding.files":
|
||||||
@@ -61,16 +62,15 @@ you want to change. Other options will remain unchanged.`,
|
|||||||
|
|
||||||
getUserDefaults(flags, &set.Defaults, false)
|
getUserDefaults(flags, &set.Defaults, false)
|
||||||
|
|
||||||
var auther auth.Auther
|
// read the defaults
|
||||||
if hasAuth {
|
auther, err := d.store.Auth.Get(set.AuthMethod)
|
||||||
set.AuthMethod, auther = getAuthentication(flags)
|
checkErr(err)
|
||||||
err = d.store.Auth.Save(auther)
|
|
||||||
checkErr(err)
|
|
||||||
} else {
|
|
||||||
auther, err = d.store.Auth.Get(set.AuthMethod)
|
|
||||||
checkErr(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// check if there are new flags for existing auth method
|
||||||
|
set.AuthMethod, auther = getAuthentication(flags, hasAuth, set, auther)
|
||||||
|
|
||||||
|
err = d.store.Auth.Save(auther)
|
||||||
|
checkErr(err)
|
||||||
err = d.store.Settings.Save(set)
|
err = d.store.Settings.Save(set)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
err = d.store.Settings.SaveServer(ser)
|
err = d.store.Settings.SaveServer(ser)
|
||||||
|
|||||||
12
cmd/docs.go
@@ -88,7 +88,7 @@ func generateMarkdown(cmd *cobra.Command, w io.Writer) {
|
|||||||
|
|
||||||
short := cmd.Short
|
short := cmd.Short
|
||||||
long := cmd.Long
|
long := cmd.Long
|
||||||
if len(long) == 0 {
|
if long == "" {
|
||||||
long = short
|
long = short
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,21 +106,21 @@ func generateMarkdown(cmd *cobra.Command, w io.Writer) {
|
|||||||
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example))
|
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example))
|
||||||
}
|
}
|
||||||
|
|
||||||
printOptions(buf, cmd, name)
|
printOptions(buf, cmd)
|
||||||
_, err := buf.WriteTo(w)
|
_, err := buf.WriteTo(w)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFlagsTable(fs *pflag.FlagSet, buf io.StringWriter) {
|
func generateFlagsTable(fs *pflag.FlagSet, buf io.StringWriter) {
|
||||||
buf.WriteString("| Name | Shorthand | Usage |\n")
|
_, _ = buf.WriteString("| Name | Shorthand | Usage |\n")
|
||||||
buf.WriteString("|------|-----------|-------|\n")
|
_, _ = buf.WriteString("|------|-----------|-------|\n")
|
||||||
|
|
||||||
fs.VisitAll(func(f *pflag.Flag) {
|
fs.VisitAll(func(f *pflag.Flag) {
|
||||||
buf.WriteString("|" + f.Name + "|" + f.Shorthand + "|" + f.Usage + "|\n")
|
_, _ = buf.WriteString("|" + f.Name + "|" + f.Shorthand + "|" + f.Usage + "|\n")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) {
|
func printOptions(buf *bytes.Buffer, cmd *cobra.Command) {
|
||||||
flags := cmd.NonInheritedFlags()
|
flags := cmd.NonInheritedFlags()
|
||||||
flags.SetOutput(buf)
|
flags.SetOutput(buf)
|
||||||
if flags.HasAvailableFlags() {
|
if flags.HasAvailableFlags() {
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
157
cmd/root.go
@@ -2,24 +2,33 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"io/ioutil"
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/auth"
|
|
||||||
fbhttp "github.com/filebrowser/filebrowser/v2/http"
|
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
|
||||||
"github.com/filebrowser/filebrowser/v2/storage"
|
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
|
||||||
homedir "github.com/mitchellh/go-homedir"
|
homedir "github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
v "github.com/spf13/viper"
|
v "github.com/spf13/viper"
|
||||||
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/auth"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/diskcache"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/frontend"
|
||||||
|
fbhttp "github.com/filebrowser/filebrowser/v2/http"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/img"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/storage"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -28,6 +37,7 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
|
cobra.MousetrapHelpText = ""
|
||||||
|
|
||||||
rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n")
|
rootCmd.SetVersionTemplate("File Browser version {{printf \"%s\" .Version}}\n")
|
||||||
|
|
||||||
@@ -50,7 +60,15 @@ func addServerFlags(flags *pflag.FlagSet) {
|
|||||||
flags.StringP("cert", "t", "", "tls certificate")
|
flags.StringP("cert", "t", "", "tls certificate")
|
||||||
flags.StringP("key", "k", "", "tls key")
|
flags.StringP("key", "k", "", "tls key")
|
||||||
flags.StringP("root", "r", ".", "root to prepend to relative paths")
|
flags.StringP("root", "r", ".", "root to prepend to relative paths")
|
||||||
|
flags.String("socket", "", "socket to listen to (cannot be used with address, port, cert nor key flags)")
|
||||||
|
flags.Uint32("socket-perm", 0666, "unix socket file permissions") //nolint:gomnd
|
||||||
flags.StringP("baseurl", "b", "", "base url")
|
flags.StringP("baseurl", "b", "", "base url")
|
||||||
|
flags.String("cache-dir", "", "file cache directory (disabled if empty)")
|
||||||
|
flags.Int("img-processors", 4, "image processors count") //nolint:gomnd
|
||||||
|
flags.Bool("disable-thumbnails", false, "disable image thumbnails")
|
||||||
|
flags.Bool("disable-preview-resize", false, "disable resize of image previews")
|
||||||
|
flags.Bool("disable-exec", false, "disables Command Runner feature")
|
||||||
|
flags.Bool("disable-type-detection-by-header", false, "disables type detection by reading file headers")
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
@@ -98,6 +116,24 @@ user created with the credentials from options "username" and "password".`,
|
|||||||
quickSetup(cmd.Flags(), d)
|
quickSetup(cmd.Flags(), d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build img service
|
||||||
|
workersCount, err := cmd.Flags().GetInt("img-processors")
|
||||||
|
checkErr(err)
|
||||||
|
if workersCount < 1 {
|
||||||
|
log.Fatal("Image resize workers count could not be < 1")
|
||||||
|
}
|
||||||
|
imgSvc := img.New(workersCount)
|
||||||
|
|
||||||
|
var fileCache diskcache.Interface = diskcache.NewNoOp()
|
||||||
|
cacheDir, err := cmd.Flags().GetString("cache-dir")
|
||||||
|
checkErr(err)
|
||||||
|
if cacheDir != "" {
|
||||||
|
if err := os.MkdirAll(cacheDir, 0700); err != nil { //nolint:govet,gomnd
|
||||||
|
log.Fatalf("can't make directory %s: %s", cacheDir, err)
|
||||||
|
}
|
||||||
|
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
||||||
|
}
|
||||||
|
|
||||||
server := getRunParams(cmd.Flags(), d.store)
|
server := getRunParams(cmd.Flags(), d.store)
|
||||||
setupLog(server.Log)
|
setupLog(server.Log)
|
||||||
|
|
||||||
@@ -109,19 +145,41 @@ user created with the credentials from options "username" and "password".`,
|
|||||||
|
|
||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
|
|
||||||
if server.TLSKey != "" && server.TLSCert != "" {
|
switch {
|
||||||
cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey)
|
case server.Socket != "":
|
||||||
|
listener, err = net.Listen("unix", server.Socket)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
listener, err = tls.Listen("tcp", adr, &tls.Config{Certificates: []tls.Certificate{cer}})
|
socketPerm, err := cmd.Flags().GetUint32("socket-perm") //nolint:govet
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
} else {
|
err = os.Chmod(server.Socket, os.FileMode(socketPerm))
|
||||||
|
checkErr(err)
|
||||||
|
case server.TLSKey != "" && server.TLSCert != "":
|
||||||
|
cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:govet
|
||||||
|
checkErr(err)
|
||||||
|
listener, err = tls.Listen("tcp", adr, &tls.Config{
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
Certificates: []tls.Certificate{cer}},
|
||||||
|
)
|
||||||
|
checkErr(err)
|
||||||
|
default:
|
||||||
listener, err = net.Listen("tcp", adr)
|
listener, err = net.Listen("tcp", adr)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
handler, err := fbhttp.NewHandler(d.store, server)
|
sigc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
|
||||||
|
go cleanupHandler(listener, sigc)
|
||||||
|
|
||||||
|
assetsFs, err := fs.Sub(frontend.Assets(), "dist")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, server, assetsFs)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
log.Println("Listening on", listener.Addr().String())
|
log.Println("Listening on", listener.Addr().String())
|
||||||
if err := http.Serve(listener, handler); err != nil {
|
if err := http.Serve(listener, handler); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -129,6 +187,14 @@ user created with the credentials from options "username" and "password".`,
|
|||||||
}, pythonConfig{allowNoDB: true}),
|
}, pythonConfig{allowNoDB: true}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer
|
||||||
|
sig := <-c
|
||||||
|
log.Printf("Caught signal %s: shutting down.", sig)
|
||||||
|
listener.Close()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
|
func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
|
||||||
server, err := st.Settings.GetServer()
|
server, err := st.Settings.GetServer()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
@@ -141,26 +207,59 @@ func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
|
|||||||
server.BaseURL = val
|
server.BaseURL = val
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, set := getParamB(flags, "address"); set {
|
|
||||||
server.Address = val
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "port"); set {
|
|
||||||
server.Port = val
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, set := getParamB(flags, "log"); set {
|
if val, set := getParamB(flags, "log"); set {
|
||||||
server.Log = val
|
server.Log = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isSocketSet := false
|
||||||
|
isAddrSet := false
|
||||||
|
|
||||||
|
if val, set := getParamB(flags, "address"); set {
|
||||||
|
server.Address = val
|
||||||
|
isAddrSet = isAddrSet || set
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, set := getParamB(flags, "port"); set {
|
||||||
|
server.Port = val
|
||||||
|
isAddrSet = isAddrSet || set
|
||||||
|
}
|
||||||
|
|
||||||
if val, set := getParamB(flags, "key"); set {
|
if val, set := getParamB(flags, "key"); set {
|
||||||
server.TLSKey = val
|
server.TLSKey = val
|
||||||
|
isAddrSet = isAddrSet || set
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, set := getParamB(flags, "cert"); set {
|
if val, set := getParamB(flags, "cert"); set {
|
||||||
server.TLSCert = val
|
server.TLSCert = val
|
||||||
|
isAddrSet = isAddrSet || set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, set := getParamB(flags, "socket"); set {
|
||||||
|
server.Socket = val
|
||||||
|
isSocketSet = isSocketSet || set
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAddrSet && isSocketSet {
|
||||||
|
checkErr(errors.New("--socket flag cannot be used with --address, --port, --key nor --cert"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not use saved Socket if address was manually set.
|
||||||
|
if isAddrSet && server.Socket != "" {
|
||||||
|
server.Socket = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_, disableThumbnails := getParamB(flags, "disable-thumbnails")
|
||||||
|
server.EnableThumbnails = !disableThumbnails
|
||||||
|
|
||||||
|
_, disablePreviewResize := getParamB(flags, "disable-preview-resize")
|
||||||
|
server.ResizePreview = !disablePreviewResize
|
||||||
|
|
||||||
|
_, disableTypeDetectionByHeader := getParamB(flags, "disable-type-detection-by-header")
|
||||||
|
server.TypeDetectionByHeader = !disableTypeDetectionByHeader
|
||||||
|
|
||||||
|
_, disableExec := getParamB(flags, "disable-exec")
|
||||||
|
server.EnableExec = !disableExec
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +299,7 @@ func setupLog(logMethod string) {
|
|||||||
case "stderr":
|
case "stderr":
|
||||||
log.SetOutput(os.Stderr)
|
log.SetOutput(os.Stderr)
|
||||||
case "":
|
case "":
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(io.Discard)
|
||||||
default:
|
default:
|
||||||
log.SetOutput(&lumberjack.Logger{
|
log.SetOutput(&lumberjack.Logger{
|
||||||
Filename: logMethod,
|
Filename: logMethod,
|
||||||
@@ -213,12 +312,14 @@ func setupLog(logMethod string) {
|
|||||||
|
|
||||||
func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
||||||
set := &settings.Settings{
|
set := &settings.Settings{
|
||||||
Key: generateKey(),
|
Key: generateKey(),
|
||||||
Signup: false,
|
Signup: false,
|
||||||
CreateUserDir: false,
|
CreateUserDir: false,
|
||||||
|
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
|
||||||
Defaults: settings.UserDefaults{
|
Defaults: settings.UserDefaults{
|
||||||
Scope: ".",
|
Scope: ".",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
|
SingleClick: false,
|
||||||
Perm: users.Permissions{
|
Perm: users.Permissions{
|
||||||
Admin: false,
|
Admin: false,
|
||||||
Execute: true,
|
Execute: true,
|
||||||
@@ -230,6 +331,11 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
|||||||
Download: true,
|
Download: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
AuthMethod: "",
|
||||||
|
Branding: settings.Branding{},
|
||||||
|
Commands: nil,
|
||||||
|
Shell: nil,
|
||||||
|
Rules: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -307,5 +413,4 @@ func initConfig() {
|
|||||||
} else {
|
} else {
|
||||||
cfgFile = "Using config file: " + v.ConfigFileUsed()
|
cfgFile = "Using config file: " + v.ConfigFileUsed()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rulesCmd.AddCommand(rulesRmCommand)
|
rulesCmd.AddCommand(rulesRmCommand)
|
||||||
rulesRmCommand.Flags().Uint("index", 0, "index of rule to remove")
|
rulesRmCommand.Flags().Uint("index", 0, "index of rule to remove")
|
||||||
rulesRmCommand.MarkFlagRequired("index")
|
_ = rulesRmCommand.MarkFlagRequired("index")
|
||||||
}
|
}
|
||||||
|
|
||||||
var rulesRmCommand = &cobra.Command{
|
var rulesRmCommand = &cobra.Command{
|
||||||
@@ -27,7 +28,7 @@ You can also specify an optional parameter (index_end) so
|
|||||||
you can remove all commands from 'index' to 'index_end',
|
you can remove all commands from 'index' to 'index_end',
|
||||||
including 'index_end'.`,
|
including 'index_end'.`,
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if err := cobra.RangeArgs(1, 2)(cmd, args); err != nil {
|
if err := cobra.RangeArgs(1, 2)(cmd, args); err != nil { //nolint:gomnd
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ including 'index_end'.`,
|
|||||||
i, err := strconv.Atoi(args[0])
|
i, err := strconv.Atoi(args[0])
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
f := i
|
f := i
|
||||||
if len(args) == 2 {
|
if len(args) == 2 { //nolint:gomnd
|
||||||
f, err = strconv.Atoi(args[1])
|
f, err = strconv.Atoi(args[1])
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
}
|
}
|
||||||
|
|||||||
23
cmd/rules.go
@@ -3,12 +3,13 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/rules"
|
"github.com/filebrowser/filebrowser/v2/rules"
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/filebrowser/filebrowser/v2/storage"
|
"github.com/filebrowser/filebrowser/v2/storage"
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -18,8 +19,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rulesCmd = &cobra.Command{
|
var rulesCmd = &cobra.Command{
|
||||||
Use: "rules",
|
Use: "rules",
|
||||||
Short: "Rules management utility",
|
Short: "Rules management utility",
|
||||||
Long: `On each subcommand you'll have available at least two flags:
|
Long: `On each subcommand you'll have available at least two flags:
|
||||||
"username" and "id". You must either set only one of them
|
"username" and "id". You must either set only one of them
|
||||||
or none. If you set one of them, the command will apply to
|
or none. If you set one of them, the command will apply to
|
||||||
@@ -28,14 +29,14 @@ rules.`,
|
|||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User), global func(*settings.Settings)) {
|
func runRules(st *storage.Storage, cmd *cobra.Command, usersFn func(*users.User), globalFn func(*settings.Settings)) {
|
||||||
id := getUserIdentifier(cmd.Flags())
|
id := getUserIdentifier(cmd.Flags())
|
||||||
if id != nil {
|
if id != nil {
|
||||||
user, err := st.Users.Get("", id)
|
user, err := st.Users.Get("", id)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
if users != nil {
|
if usersFn != nil {
|
||||||
users(user)
|
usersFn(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
printRules(user.Rules, id)
|
printRules(user.Rules, id)
|
||||||
@@ -45,8 +46,8 @@ func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User),
|
|||||||
s, err := st.Settings.Get()
|
s, err := st.Settings.Get()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
if global != nil {
|
if globalFn != nil {
|
||||||
global(s)
|
globalFn(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
printRules(s.Rules, id)
|
printRules(s.Rules, id)
|
||||||
@@ -65,14 +66,14 @@ func getUserIdentifier(flags *pflag.FlagSet) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printRules(rules []rules.Rule, id interface{}) {
|
func printRules(rulez []rules.Rule, id interface{}) {
|
||||||
if id == nil {
|
if id == nil {
|
||||||
fmt.Printf("Global Rules:\n\n")
|
fmt.Printf("Global Rules:\n\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Rules for user %v:\n\n", id)
|
fmt.Printf("Rules for user %v:\n\n", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
for id, rule := range rules {
|
for id, rule := range rulez {
|
||||||
fmt.Printf("(%d) ", id)
|
fmt.Printf("(%d) ", id)
|
||||||
if rule.Regex {
|
if rule.Regex {
|
||||||
if rule.Allow {
|
if rule.Allow {
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/rules"
|
"github.com/filebrowser/filebrowser/v2/rules"
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/filebrowser/filebrowser/v2/storage/bolt/importer"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/storage/bolt/importer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -10,7 +11,7 @@ func init() {
|
|||||||
|
|
||||||
upgradeCmd.Flags().String("old.database", "", "")
|
upgradeCmd.Flags().String("old.database", "", "")
|
||||||
upgradeCmd.Flags().String("old.config", "", "")
|
upgradeCmd.Flags().String("old.config", "", "")
|
||||||
upgradeCmd.MarkFlagRequired("old.database")
|
_ = upgradeCmd.MarkFlagRequired("old.database")
|
||||||
}
|
}
|
||||||
|
|
||||||
var upgradeCmd = &cobra.Command{
|
var upgradeCmd = &cobra.Command{
|
||||||
|
|||||||
54
cmd/users.go
@@ -7,10 +7,11 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -24,38 +25,39 @@ var usersCmd = &cobra.Command{
|
|||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
func printUsers(users []*users.User) {
|
func printUsers(usrs []*users.User) {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
|
||||||
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
|
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
|
||||||
|
|
||||||
for _, user := range users {
|
for _, u := range usrs {
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
|
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
|
||||||
user.ID,
|
u.ID,
|
||||||
user.Username,
|
u.Username,
|
||||||
user.Scope,
|
u.Scope,
|
||||||
user.Locale,
|
u.Locale,
|
||||||
user.ViewMode,
|
u.ViewMode,
|
||||||
user.Perm.Admin,
|
u.SingleClick,
|
||||||
user.Perm.Execute,
|
u.Perm.Admin,
|
||||||
user.Perm.Create,
|
u.Perm.Execute,
|
||||||
user.Perm.Rename,
|
u.Perm.Create,
|
||||||
user.Perm.Modify,
|
u.Perm.Rename,
|
||||||
user.Perm.Delete,
|
u.Perm.Modify,
|
||||||
user.Perm.Share,
|
u.Perm.Delete,
|
||||||
user.Perm.Download,
|
u.Perm.Share,
|
||||||
user.LockPassword,
|
u.Perm.Download,
|
||||||
|
u.LockPassword,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUsernameOrID(arg string) (string, uint) {
|
func parseUsernameOrID(arg string) (username string, id uint) {
|
||||||
id, err := strconv.ParseUint(arg, 10, 0)
|
id64, err := strconv.ParseUint(arg, 10, 64) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return arg, 0
|
return arg, 0
|
||||||
}
|
}
|
||||||
return "", uint(id)
|
return "", uint(id64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addUserFlags(flags *pflag.FlagSet) {
|
func addUserFlags(flags *pflag.FlagSet) {
|
||||||
@@ -74,6 +76,7 @@ func addUserFlags(flags *pflag.FlagSet) {
|
|||||||
flags.String("scope", ".", "scope for users")
|
flags.String("scope", ".", "scope for users")
|
||||||
flags.String("locale", "en", "locale for users")
|
flags.String("locale", "en", "locale for users")
|
||||||
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
|
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
|
||||||
|
flags.Bool("singleClick", false, "use single clicks only")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getViewMode(flags *pflag.FlagSet) users.ViewMode {
|
func getViewMode(flags *pflag.FlagSet) users.ViewMode {
|
||||||
@@ -84,6 +87,7 @@ func getViewMode(flags *pflag.FlagSet) users.ViewMode {
|
|||||||
return viewMode
|
return viewMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all bool) {
|
func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all bool) {
|
||||||
visit := func(flag *pflag.Flag) {
|
visit := func(flag *pflag.Flag) {
|
||||||
switch flag.Name {
|
switch flag.Name {
|
||||||
@@ -93,6 +97,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
|
|||||||
defaults.Locale = mustGetString(flags, flag.Name)
|
defaults.Locale = mustGetString(flags, flag.Name)
|
||||||
case "viewMode":
|
case "viewMode":
|
||||||
defaults.ViewMode = getViewMode(flags)
|
defaults.ViewMode = getViewMode(flags)
|
||||||
|
case "singleClick":
|
||||||
|
defaults.SingleClick = mustGetBool(flags, flag.Name)
|
||||||
case "perm.admin":
|
case "perm.admin":
|
||||||
defaults.Perm.Admin = mustGetBool(flags, flag.Name)
|
defaults.Perm.Admin = mustGetBool(flags, flag.Name)
|
||||||
case "perm.execute":
|
case "perm.execute":
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -14,7 +15,7 @@ var usersAddCmd = &cobra.Command{
|
|||||||
Use: "add <username> <password>",
|
Use: "add <username> <password>",
|
||||||
Short: "Create a new user",
|
Short: "Create a new user",
|
||||||
Long: `Create a new user and add it to the database.`,
|
Long: `Create a new user and add it to the database.`,
|
||||||
Args: cobra.ExactArgs(2),
|
Args: cobra.ExactArgs(2), //nolint:gomnd
|
||||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||||
s, err := d.store.Settings.Get()
|
s, err := d.store.Settings.Get()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
@@ -33,9 +34,9 @@ var usersAddCmd = &cobra.Command{
|
|||||||
|
|
||||||
servSettings, err := d.store.Settings.GetServer()
|
servSettings, err := d.store.Settings.GetServer()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
//since getUserDefaults() polluted s.Defaults.Scope
|
// since getUserDefaults() polluted s.Defaults.Scope
|
||||||
//which makes the Scope not the one saved in the db
|
// which makes the Scope not the one saved in the db
|
||||||
//we need the right s.Defaults.Scope here
|
// we need the right s.Defaults.Scope here
|
||||||
s2, err := d.store.Settings.Get()
|
s2, err := d.store.Settings.Get()
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -65,8 +67,7 @@ list or set it to 0.`,
|
|||||||
// with the new username. If there is, print an error and cancel the
|
// with the new username. If there is, print an error and cancel the
|
||||||
// operation
|
// operation
|
||||||
if user.Username != onDB.Username {
|
if user.Username != onDB.Username {
|
||||||
conflictuous, err := d.store.Users.Get("", user.Username)
|
if conflictuous, err := d.store.Users.Get("", user.Username); err == nil { //nolint:govet
|
||||||
if err == nil {
|
|
||||||
checkErr(usernameConflictError(user.Username, conflictuous.ID, user.ID))
|
checkErr(usernameConflictError(user.Username, conflictuous.ID, user.ID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,6 +83,7 @@ list or set it to 0.`,
|
|||||||
}, pythonConfig{}),
|
}, pythonConfig{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
func usernameConflictError(username string, original, new uint) error {
|
func usernameConflictError(username string, originalID, newID uint) error {
|
||||||
return errors.New("can't import user with ID " + strconv.Itoa(int(new)) + " and username \"" + username + "\" because the username is already registred with the user " + strconv.Itoa(int(original)))
|
return fmt.Errorf(`can't import user with ID %d and username "%s" because the username is already registred with the user %d`,
|
||||||
|
newID, username, originalID)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
"github.com/filebrowser/filebrowser/v2/users"
|
"github.com/filebrowser/filebrowser/v2/users"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -40,17 +41,19 @@ options you want to change.`,
|
|||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
defaults := settings.UserDefaults{
|
defaults := settings.UserDefaults{
|
||||||
Scope: user.Scope,
|
Scope: user.Scope,
|
||||||
Locale: user.Locale,
|
Locale: user.Locale,
|
||||||
ViewMode: user.ViewMode,
|
ViewMode: user.ViewMode,
|
||||||
Perm: user.Perm,
|
SingleClick: user.SingleClick,
|
||||||
Sorting: user.Sorting,
|
Perm: user.Perm,
|
||||||
Commands: user.Commands,
|
Sorting: user.Sorting,
|
||||||
|
Commands: user.Commands,
|
||||||
}
|
}
|
||||||
getUserDefaults(flags, &defaults, false)
|
getUserDefaults(flags, &defaults, false)
|
||||||
user.Scope = defaults.Scope
|
user.Scope = defaults.Scope
|
||||||
user.Locale = defaults.Locale
|
user.Locale = defaults.Locale
|
||||||
user.ViewMode = defaults.ViewMode
|
user.ViewMode = defaults.ViewMode
|
||||||
|
user.SingleClick = defaults.SingleClick
|
||||||
user.Perm = defaults.Perm
|
user.Perm = defaults.Perm
|
||||||
user.Commands = defaults.Commands
|
user.Commands = defaults.Commands
|
||||||
user.Sorting = defaults.Sorting
|
user.Sorting = defaults.Sorting
|
||||||
|
|||||||
28
cmd/utils.go
@@ -7,14 +7,16 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/asdine/storm"
|
"github.com/asdine/storm/v3"
|
||||||
"github.com/filebrowser/filebrowser/v2/settings"
|
|
||||||
"github.com/filebrowser/filebrowser/v2/storage"
|
|
||||||
"github.com/filebrowser/filebrowser/v2/storage/bolt"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/settings"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/storage"
|
||||||
|
"github.com/filebrowser/filebrowser/v2/storage/bolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkErr(err error) {
|
func checkErr(err error) {
|
||||||
@@ -70,7 +72,9 @@ func dbExists(path string) (bool, error) {
|
|||||||
d := filepath.Dir(path)
|
d := filepath.Dir(path)
|
||||||
_, err = os.Stat(d)
|
_, err = os.Stat(d)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
os.MkdirAll(d, 0700)
|
if err := os.MkdirAll(d, 0700); err != nil { //nolint:govet,gomnd
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +117,7 @@ func marshal(filename string, data interface{}) error {
|
|||||||
encoder := json.NewEncoder(fd)
|
encoder := json.NewEncoder(fd)
|
||||||
encoder.SetIndent("", " ")
|
encoder.SetIndent("", " ")
|
||||||
return encoder.Encode(data)
|
return encoder.Encode(data)
|
||||||
case ".yml", ".yaml":
|
case ".yml", ".yaml": //nolint:goconst
|
||||||
encoder := yaml.NewEncoder(fd)
|
encoder := yaml.NewEncoder(fd)
|
||||||
return encoder.Encode(data)
|
return encoder.Encode(data)
|
||||||
default:
|
default:
|
||||||
@@ -175,3 +179,15 @@ func cleanUpMapValue(v interface{}) interface{} {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertCmdStrToCmdArray checks if cmd string is blank (whitespace included)
|
||||||
|
// then returns empty string array, else returns the splitted word array of cmd.
|
||||||
|
// This is to ensure the result will never be []string{""}
|
||||||
|
func convertCmdStrToCmdArray(cmd string) []string {
|
||||||
|
var cmdArray []string
|
||||||
|
trimmedCmdStr := strings.TrimSpace(cmd)
|
||||||
|
if trimmedCmdStr != "" {
|
||||||
|
cmdArray = strings.Split(trimmedCmdStr, " ")
|
||||||
|
}
|
||||||
|
return cmdArray
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/version"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -15,6 +16,6 @@ var versionCmd = &cobra.Command{
|
|||||||
Use: "version",
|
Use: "version",
|
||||||
Short: "Print the version number",
|
Short: "Print the version number",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println("File Browser Version " + version.Version)
|
fmt.Println("File Browser v" + version.Version + "/" + version.CommitSHA)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
34
commitlint.config.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
module.exports = {
|
||||||
|
rules: {
|
||||||
|
'body-leading-blank': [1, 'always'],
|
||||||
|
'body-max-line-length': [2, 'always', 100],
|
||||||
|
'footer-leading-blank': [1, 'always'],
|
||||||
|
'footer-max-line-length': [2, 'always', 100],
|
||||||
|
'header-max-length': [2, 'always', 100],
|
||||||
|
'scope-case': [2, 'always', 'lower-case'],
|
||||||
|
'subject-case': [
|
||||||
|
2,
|
||||||
|
'never',
|
||||||
|
['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
|
||||||
|
],
|
||||||
|
'subject-full-stop': [2, 'never', '.'],
|
||||||
|
'type-case': [2, 'always', 'lower-case'],
|
||||||
|
'type-empty': [2, 'never'],
|
||||||
|
'type-enum': [
|
||||||
|
2,
|
||||||
|
'always',
|
||||||
|
[
|
||||||
|
'feat',
|
||||||
|
'fix',
|
||||||
|
'perf',
|
||||||
|
'revert',
|
||||||
|
'refactor',
|
||||||
|
'build',
|
||||||
|
'ci',
|
||||||
|
'test',
|
||||||
|
'chore',
|
||||||
|
'docs',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
28
common.mk
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
DATE ?= $(shell date +%FT%T%z)
|
||||||
|
BASE_PATH := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
VERSION ?= $(shell git describe --tags --always --match=v* 2> /dev/null || \
|
||||||
|
cat $(CURDIR)/.version 2> /dev/null || echo v0)
|
||||||
|
VERSION_HASH = $(shell git rev-parse HEAD)
|
||||||
|
BRANCH = $(shell git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
|
go = GOGC=off go
|
||||||
|
MODULE = $(shell env GO111MODULE=on go list -m)
|
||||||
|
|
||||||
|
# printing
|
||||||
|
# $Q (quiet) is used in the targets as a replacer for @.
|
||||||
|
# This macro helps to print the command for debugging by setting V to 1. Example `make test-unit V=1`
|
||||||
|
V = 0
|
||||||
|
Q = $(if $(filter 1,$V),,@)
|
||||||
|
# $M is a macro to print a colored ▶ character. Example `$(info $(M) running coverage tests…)` will print "▶ running coverage tests…"
|
||||||
|
M = $(shell printf "\033[34;1m▶\033[0m")
|
||||||
|
|
||||||
|
GREEN := $(shell tput -Txterm setaf 2)
|
||||||
|
YELLOW := $(shell tput -Txterm setaf 3)
|
||||||
|
WHITE := $(shell tput -Txterm setaf 7)
|
||||||
|
CYAN := $(shell tput -Txterm setaf 6)
|
||||||
|
RESET := $(shell tput -Txterm sgr0)
|
||||||
|
|
||||||
|
define global_option
|
||||||
|
printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n" $(1) $(2)
|
||||||
|
endef
|
||||||
11
diskcache/cache.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package diskcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interface interface {
|
||||||
|
Store(ctx context.Context, key string, value []byte) error
|
||||||
|
Load(ctx context.Context, key string) (value []byte, exist bool, err error)
|
||||||
|
Delete(ctx context.Context, key string) error
|
||||||
|
}
|
||||||
110
diskcache/file_cache.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package diskcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha1" //nolint:gosec
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileCache struct {
|
||||||
|
fs afero.Fs
|
||||||
|
|
||||||
|
// granular locks
|
||||||
|
scopedLocks struct {
|
||||||
|
sync.Mutex
|
||||||
|
sync.Once
|
||||||
|
locks map[string]sync.Locker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(fs afero.Fs, root string) *FileCache {
|
||||||
|
return &FileCache{
|
||||||
|
fs: afero.NewBasePathFs(fs, root),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileCache) Store(ctx context.Context, key string, value []byte) error {
|
||||||
|
mu := f.getScopedLocks(key)
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
fileName := f.getFileName(key)
|
||||||
|
if err := f.fs.MkdirAll(filepath.Dir(fileName), 0700); err != nil { //nolint:gomnd
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := afero.WriteFile(f.fs, fileName, value, 0700); err != nil { //nolint:gomnd
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileCache) Load(ctx context.Context, key string) (value []byte, exist bool, err error) {
|
||||||
|
r, ok, err := f.open(key)
|
||||||
|
if err != nil || !ok {
|
||||||
|
return nil, ok, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
value, err = io.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return value, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileCache) Delete(ctx context.Context, key string) error {
|
||||||
|
mu := f.getScopedLocks(key)
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
fileName := f.getFileName(key)
|
||||||
|
if err := f.fs.Remove(fileName); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileCache) open(key string) (afero.File, bool, error) {
|
||||||
|
fileName := f.getFileName(key)
|
||||||
|
file, err := f.fs.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getScopedLocks pull lock from the map if found or create a new one
|
||||||
|
func (f *FileCache) getScopedLocks(key string) (lock sync.Locker) {
|
||||||
|
f.scopedLocks.Do(func() { f.scopedLocks.locks = map[string]sync.Locker{} })
|
||||||
|
|
||||||
|
f.scopedLocks.Lock()
|
||||||
|
lock, ok := f.scopedLocks.locks[key]
|
||||||
|
if !ok {
|
||||||
|
lock = &sync.Mutex{}
|
||||||
|
f.scopedLocks.locks[key] = lock
|
||||||
|
}
|
||||||
|
f.scopedLocks.Unlock()
|
||||||
|
|
||||||
|
return lock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileCache) getFileName(key string) string {
|
||||||
|
hasher := sha1.New() //nolint:gosec
|
||||||
|
_, _ = hasher.Write([]byte(key))
|
||||||
|
hash := hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
return fmt.Sprintf("%s/%s/%s", hash[:1], hash[1:3], hash)
|
||||||
|
}
|
||||||
55
diskcache/file_cache_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package diskcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileCache(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
const (
|
||||||
|
key = "key"
|
||||||
|
value = "some text"
|
||||||
|
newValue = "new text"
|
||||||
|
cacheRoot = "/cache"
|
||||||
|
cachedFilePath = "a/62/a62f2225bf70bfaccbc7f1ef2a397836717377de"
|
||||||
|
)
|
||||||
|
|
||||||
|
fs := afero.NewMemMapFs()
|
||||||
|
cache := New(fs, "/cache")
|
||||||
|
|
||||||
|
// store new key
|
||||||
|
err := cache.Store(ctx, key, []byte(value))
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkValue(t, ctx, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, value)
|
||||||
|
|
||||||
|
// update existing key
|
||||||
|
err = cache.Store(ctx, key, []byte(newValue))
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkValue(t, ctx, fs, filepath.Join(cacheRoot, cachedFilePath), cache, key, newValue)
|
||||||
|
|
||||||
|
// delete key
|
||||||
|
err = cache.Delete(ctx, key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
exists, err := afero.Exists(fs, filepath.Join(cacheRoot, cachedFilePath))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkValue(t *testing.T, ctx context.Context, fs afero.Fs, fileFullPath string, cache *FileCache, key, wantValue string) { //nolint:golint
|
||||||
|
t.Helper()
|
||||||
|
// check actual file content
|
||||||
|
b, err := afero.ReadFile(fs, fileFullPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, wantValue, string(b))
|
||||||
|
|
||||||
|
// check cache content
|
||||||
|
b, ok, err := cache.Load(ctx, key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, wantValue, string(b))
|
||||||
|
}
|
||||||
24
diskcache/noop_cache.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package diskcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NoOp struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoOp() *NoOp {
|
||||||
|
return &NoOp{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoOp) Store(ctx context.Context, key string, value []byte) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoOp) Load(ctx context.Context, key string) (value []byte, exist bool, err error) {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoOp) Delete(ctx context.Context, key string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
docker/root/defaults/settings.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"port": 80,
|
||||||
|
"baseURL": "",
|
||||||
|
"address": "",
|
||||||
|
"log": "stdout",
|
||||||
|
"database": "/database/filebrowser.db",
|
||||||
|
"root": "/srv"
|
||||||
|
}
|
||||||
15
docker/root/etc/cont-init.d/20-config
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/with-contenv bash
|
||||||
|
|
||||||
|
# make folders
|
||||||
|
mkdir -p /database
|
||||||
|
|
||||||
|
# copy config
|
||||||
|
if [ ! -f "/config/settings.json" ]; then
|
||||||
|
cp -a /defaults/settings.json /config/settings.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
# permissions
|
||||||
|
chown abc:abc \
|
||||||
|
/config/settings.json \
|
||||||
|
/database \
|
||||||
|
/srv
|
||||||
3
docker/root/etc/services.d/filebrowser/run
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/with-contenv bash
|
||||||
|
|
||||||
|
exec s6-setuidgid abc filebrowser -c /config/settings.json -d /database/filebrowser.db;
|
||||||
@@ -5,4 +5,4 @@
|
|||||||
"log": "stdout",
|
"log": "stdout",
|
||||||
"database": "/database.db",
|
"database": "/database.db",
|
||||||
"root": "/srv"
|
"root": "/srv"
|
||||||
}
|
}
|
||||||
@@ -3,15 +3,19 @@ package errors
|
|||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrEmptyKey = errors.New("empty key")
|
ErrEmptyKey = errors.New("empty key")
|
||||||
ErrExist = errors.New("the resource already exists")
|
ErrExist = errors.New("the resource already exists")
|
||||||
ErrNotExist = errors.New("the resource does not exist")
|
ErrNotExist = errors.New("the resource does not exist")
|
||||||
ErrEmptyPassword = errors.New("password is empty")
|
ErrEmptyPassword = errors.New("password is empty")
|
||||||
ErrEmptyUsername = errors.New("username is empty")
|
ErrEmptyUsername = errors.New("username is empty")
|
||||||
ErrEmptyRequest = errors.New("empty request")
|
ErrEmptyRequest = errors.New("empty request")
|
||||||
ErrScopeIsRelative = errors.New("scope is a relative path")
|
ErrScopeIsRelative = errors.New("scope is a relative path")
|
||||||
ErrInvalidDataType = errors.New("invalid data type")
|
ErrInvalidDataType = errors.New("invalid data type")
|
||||||
ErrIsDirectory = errors.New("file is directory")
|
ErrIsDirectory = errors.New("file is directory")
|
||||||
ErrInvalidOption = errors.New("invalid option")
|
ErrInvalidOption = errors.New("invalid option")
|
||||||
ErrInvalidAuthMethod = errors.New("invalid auth method")
|
ErrInvalidAuthMethod = errors.New("invalid auth method")
|
||||||
|
ErrPermissionDenied = errors.New("permission denied")
|
||||||
|
ErrInvalidRequestParams = errors.New("invalid request params")
|
||||||
|
ErrSourceIsParent = errors.New("source is parent")
|
||||||
|
ErrRootUserDeletion = errors.New("user with id 1 can't be deleted")
|
||||||
)
|
)
|
||||||
|
|||||||
222
files/file.go
@@ -1,8 +1,8 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5" //nolint:gosec
|
||||||
"crypto/sha1"
|
"crypto/sha1" //nolint:gosec
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@@ -17,9 +17,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/errors"
|
"github.com/filebrowser/filebrowser/v2/errors"
|
||||||
"github.com/filebrowser/filebrowser/v2/rules"
|
"github.com/filebrowser/filebrowser/v2/rules"
|
||||||
"github.com/spf13/afero"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileInfo describes a file.
|
// FileInfo describes a file.
|
||||||
@@ -33,19 +34,24 @@ type FileInfo struct {
|
|||||||
ModTime time.Time `json:"modified"`
|
ModTime time.Time `json:"modified"`
|
||||||
Mode os.FileMode `json:"mode"`
|
Mode os.FileMode `json:"mode"`
|
||||||
IsDir bool `json:"isDir"`
|
IsDir bool `json:"isDir"`
|
||||||
|
IsSymlink bool `json:"isSymlink"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Subtitles []string `json:"subtitles,omitempty"`
|
Subtitles []string `json:"subtitles,omitempty"`
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
Checksums map[string]string `json:"checksums,omitempty"`
|
Checksums map[string]string `json:"checksums,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileOptions are the options when getting a file info.
|
// FileOptions are the options when getting a file info.
|
||||||
type FileOptions struct {
|
type FileOptions struct {
|
||||||
Fs afero.Fs
|
Fs afero.Fs
|
||||||
Path string
|
Path string
|
||||||
Modify bool
|
Modify bool
|
||||||
Expand bool
|
Expand bool
|
||||||
Checker rules.Checker
|
ReadHeader bool
|
||||||
|
Token string
|
||||||
|
Checker rules.Checker
|
||||||
|
Content bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileInfo creates a File object from a path and a given user. This File
|
// NewFileInfo creates a File object from a path and a given user. This File
|
||||||
@@ -56,12 +62,73 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
|
|||||||
return nil, os.ErrPermission
|
return nil, os.ErrPermission
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := opts.Fs.Stat(opts.Path)
|
file, err := stat(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
file := &FileInfo{
|
if opts.Expand {
|
||||||
|
if file.IsDir {
|
||||||
|
if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:govet
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.detectType(opts.Modify, opts.Content, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func stat(opts FileOptions) (*FileInfo, error) {
|
||||||
|
var file *FileInfo
|
||||||
|
|
||||||
|
if lstaterFs, ok := opts.Fs.(afero.Lstater); ok {
|
||||||
|
info, _, err := lstaterFs.LstatIfPossible(opts.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
file = &FileInfo{
|
||||||
|
Fs: opts.Fs,
|
||||||
|
Path: opts.Path,
|
||||||
|
Name: info.Name(),
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
Mode: info.Mode(),
|
||||||
|
IsDir: info.IsDir(),
|
||||||
|
IsSymlink: IsSymlink(info.Mode()),
|
||||||
|
Size: info.Size(),
|
||||||
|
Extension: filepath.Ext(info.Name()),
|
||||||
|
Token: opts.Token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// regular file
|
||||||
|
if file != nil && !file.IsSymlink {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fs doesn't support afero.Lstater interface or the file is a symlink
|
||||||
|
info, err := opts.Fs.Stat(opts.Path)
|
||||||
|
if err != nil {
|
||||||
|
// can't follow symlink
|
||||||
|
if file != nil && file.IsSymlink {
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set correct file size in case of symlink
|
||||||
|
if file != nil && file.IsSymlink {
|
||||||
|
file.Size = info.Size()
|
||||||
|
file.IsDir = info.IsDir()
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file = &FileInfo{
|
||||||
Fs: opts.Fs,
|
Fs: opts.Fs,
|
||||||
Path: opts.Path,
|
Path: opts.Path,
|
||||||
Name: info.Name(),
|
Name: info.Name(),
|
||||||
@@ -70,20 +137,10 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
|
|||||||
IsDir: info.IsDir(),
|
IsDir: info.IsDir(),
|
||||||
Size: info.Size(),
|
Size: info.Size(),
|
||||||
Extension: filepath.Ext(info.Name()),
|
Extension: filepath.Ext(info.Name()),
|
||||||
|
Token: opts.Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Expand {
|
return file, nil
|
||||||
if file.IsDir {
|
|
||||||
return file, file.readListing(opts.Checker)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = file.detectType(opts.Modify, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return file, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checksum checksums a given File for a given User, using a specific
|
// Checksum checksums a given File for a given User, using a specific
|
||||||
@@ -105,6 +162,7 @@ func (i *FileInfo) Checksum(algo string) error {
|
|||||||
|
|
||||||
var h hash.Hash
|
var h hash.Hash
|
||||||
|
|
||||||
|
//nolint:gosec
|
||||||
switch algo {
|
switch algo {
|
||||||
case "md5":
|
case "md5":
|
||||||
h = md5.New()
|
h = md5.New()
|
||||||
@@ -127,30 +185,40 @@ func (i *FileInfo) Checksum(algo string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *FileInfo) detectType(modify, saveContent bool) error {
|
func (i *FileInfo) RealPath() string {
|
||||||
|
if realPathFs, ok := i.Fs.(interface {
|
||||||
|
RealPath(name string) (fPath string, err error)
|
||||||
|
}); ok {
|
||||||
|
realPath, err := realPathFs.RealPath(i.Path)
|
||||||
|
if err == nil {
|
||||||
|
return realPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:goconst
|
||||||
|
//TODO: use constants
|
||||||
|
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||||
|
if IsNamedPipe(i.Mode) {
|
||||||
|
i.Type = "blob"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// failing to detect the type should not return error.
|
// failing to detect the type should not return error.
|
||||||
// imagine the situation where a file in a dir with thousands
|
// imagine the situation where a file in a dir with thousands
|
||||||
// of files couldn't be opened: we'd have immediately
|
// of files couldn't be opened: we'd have immediately
|
||||||
// a 500 even though it doesn't matter. So we just log it.
|
// a 500 even though it doesn't matter. So we just log it.
|
||||||
reader, err := i.Fs.Open(i.Path)
|
|
||||||
if err != nil {
|
|
||||||
log.Print(err)
|
|
||||||
i.Type = "blob"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer reader.Close()
|
|
||||||
|
|
||||||
buffer := make([]byte, 512)
|
|
||||||
n, err := reader.Read(buffer)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
log.Print(err)
|
|
||||||
i.Type = "blob"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mimetype := mime.TypeByExtension(i.Extension)
|
mimetype := mime.TypeByExtension(i.Extension)
|
||||||
if mimetype == "" {
|
|
||||||
mimetype = http.DetectContentType(buffer[:n])
|
var buffer []byte
|
||||||
|
if readHeader {
|
||||||
|
buffer = i.readFirstBytes()
|
||||||
|
|
||||||
|
if mimetype == "" {
|
||||||
|
mimetype = http.DetectContentType(buffer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -164,10 +232,10 @@ func (i *FileInfo) detectType(modify, saveContent bool) error {
|
|||||||
case strings.HasPrefix(mimetype, "image"):
|
case strings.HasPrefix(mimetype, "image"):
|
||||||
i.Type = "image"
|
i.Type = "image"
|
||||||
return nil
|
return nil
|
||||||
case isBinary(buffer[:n], n) || i.Size > 10*1024*1024: // 10 MB
|
case strings.HasSuffix(mimetype, "pdf"):
|
||||||
i.Type = "blob"
|
i.Type = "pdf"
|
||||||
return nil
|
return nil
|
||||||
default:
|
case (strings.HasPrefix(mimetype, "text") || !isBinary(buffer)) && i.Size <= 10*1024*1024: // 10 MB
|
||||||
i.Type = "text"
|
i.Type = "text"
|
||||||
|
|
||||||
if !modify {
|
if !modify {
|
||||||
@@ -183,11 +251,34 @@ func (i *FileInfo) detectType(modify, saveContent bool) error {
|
|||||||
|
|
||||||
i.Content = string(content)
|
i.Content = string(content)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
i.Type = "blob"
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *FileInfo) readFirstBytes() []byte {
|
||||||
|
reader, err := i.Fs.Open(i.Path)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
i.Type = "blob"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer reader.Close()
|
||||||
|
|
||||||
|
buffer := make([]byte, 512) //nolint:gomnd
|
||||||
|
n, err := reader.Read(buffer)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
log.Print(err)
|
||||||
|
i.Type = "blob"
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer[:n]
|
||||||
|
}
|
||||||
|
|
||||||
func (i *FileInfo) detectSubtitles() {
|
func (i *FileInfo) detectSubtitles() {
|
||||||
if i.Type != "video" {
|
if i.Type != "video" {
|
||||||
return
|
return
|
||||||
@@ -196,15 +287,21 @@ func (i *FileInfo) detectSubtitles() {
|
|||||||
i.Subtitles = []string{}
|
i.Subtitles = []string{}
|
||||||
ext := filepath.Ext(i.Path)
|
ext := filepath.Ext(i.Path)
|
||||||
|
|
||||||
// TODO: detect multiple languages. Base.Lang.vtt
|
// detect multiple languages. Base*.vtt
|
||||||
|
// TODO: give subtitles descriptive names (lang) and track attributes
|
||||||
path := strings.TrimSuffix(i.Path, ext) + ".vtt"
|
parentDir := strings.TrimRight(i.Path, i.Name)
|
||||||
if _, err := i.Fs.Stat(path); err == nil {
|
dir, err := afero.ReadDir(i.Fs, parentDir)
|
||||||
i.Subtitles = append(i.Subtitles, path)
|
if err == nil {
|
||||||
|
base := strings.TrimSuffix(i.Name, ext)
|
||||||
|
for _, f := range dir {
|
||||||
|
if !f.IsDir() && strings.HasPrefix(f.Name(), base) && strings.HasSuffix(f.Name(), ".vtt") {
|
||||||
|
i.Subtitles = append(i.Subtitles, path.Join(parentDir, f.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *FileInfo) readListing(checker rules.Checker) error {
|
func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||||
afs := &afero.Afero{Fs: i.Fs}
|
afs := &afero.Afero{Fs: i.Fs}
|
||||||
dir, err := afs.ReadDir(i.Path)
|
dir, err := afs.ReadDir(i.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -219,18 +316,22 @@ func (i *FileInfo) readListing(checker rules.Checker) error {
|
|||||||
|
|
||||||
for _, f := range dir {
|
for _, f := range dir {
|
||||||
name := f.Name()
|
name := f.Name()
|
||||||
path := path.Join(i.Path, name)
|
fPath := path.Join(i.Path, name)
|
||||||
|
|
||||||
if !checker.Check(path) {
|
if !checker.Check(fPath) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(f.Mode().String(), "L") {
|
isSymlink, isInvalidLink := false, false
|
||||||
|
if IsSymlink(f.Mode()) {
|
||||||
|
isSymlink = true
|
||||||
// It's a symbolic link. We try to follow it. If it doesn't work,
|
// It's a symbolic link. We try to follow it. If it doesn't work,
|
||||||
// we stay with the link information instead if the target's.
|
// we stay with the link information instead of the target's.
|
||||||
info, err := i.Fs.Stat(path)
|
info, err := i.Fs.Stat(fPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
f = info
|
f = info
|
||||||
|
} else {
|
||||||
|
isInvalidLink = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,8 +342,9 @@ func (i *FileInfo) readListing(checker rules.Checker) error {
|
|||||||
ModTime: f.ModTime(),
|
ModTime: f.ModTime(),
|
||||||
Mode: f.Mode(),
|
Mode: f.Mode(),
|
||||||
IsDir: f.IsDir(),
|
IsDir: f.IsDir(),
|
||||||
|
IsSymlink: isSymlink,
|
||||||
Extension: filepath.Ext(name),
|
Extension: filepath.Ext(name),
|
||||||
Path: path,
|
Path: fPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.IsDir {
|
if file.IsDir {
|
||||||
@@ -250,9 +352,13 @@ func (i *FileInfo) readListing(checker rules.Checker) error {
|
|||||||
} else {
|
} else {
|
||||||
listing.NumFiles++
|
listing.NumFiles++
|
||||||
|
|
||||||
err := file.detectType(true, false)
|
if isInvalidLink {
|
||||||
if err != nil {
|
file.Type = "invalid_link"
|
||||||
return err
|
} else {
|
||||||
|
err := file.detectType(true, false, readHeader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ type Listing struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ApplySort applies the sort order using .Order and .Sort
|
// ApplySort applies the sort order using .Order and .Sort
|
||||||
|
//nolint:goconst
|
||||||
func (l Listing) ApplySort() {
|
func (l Listing) ApplySort() {
|
||||||
// Check '.Order' to know how to sort
|
// Check '.Order' to know how to sort
|
||||||
|
// TODO: use enum
|
||||||
if !l.Sorting.Asc {
|
if !l.Sorting.Asc {
|
||||||
switch l.Sorting.By {
|
switch l.Sorting.By {
|
||||||
case "name":
|
case "name":
|
||||||
@@ -62,11 +64,11 @@ func (l byName) Swap(i, j int) {
|
|||||||
// Treat upper and lower case equally
|
// Treat upper and lower case equally
|
||||||
func (l byName) Less(i, j int) bool {
|
func (l byName) Less(i, j int) bool {
|
||||||
if l.Items[i].IsDir && !l.Items[j].IsDir {
|
if l.Items[i].IsDir && !l.Items[j].IsDir {
|
||||||
return true
|
return l.Sorting.Asc
|
||||||
}
|
}
|
||||||
|
|
||||||
if !l.Items[i].IsDir && l.Items[j].IsDir {
|
if !l.Items[i].IsDir && l.Items[j].IsDir {
|
||||||
return false
|
return !l.Sorting.Asc
|
||||||
}
|
}
|
||||||
|
|
||||||
return natural.Less(strings.ToLower(l.Items[j].Name), strings.ToLower(l.Items[i].Name))
|
return natural.Less(strings.ToLower(l.Items[j].Name), strings.ToLower(l.Items[i].Name))
|
||||||
|
|||||||
@@ -1,46 +1,59 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isBinary(content []byte, n int) bool {
|
func isBinary(content []byte) bool {
|
||||||
maybeStr := string(content)
|
maybeStr := string(content)
|
||||||
runeCnt := utf8.RuneCount(content)
|
runeCnt := utf8.RuneCount(content)
|
||||||
runeIndex := 0
|
runeIndex := 0
|
||||||
gotRuneErrCnt := 0
|
gotRuneErrCnt := 0
|
||||||
firstRuneErrIndex := -1
|
firstRuneErrIndex := -1
|
||||||
|
|
||||||
for _, b := range maybeStr {
|
const (
|
||||||
// 8 and below are control chars (e.g. backspace, null, eof, etc)
|
// 8 and below are control chars (e.g. backspace, null, eof, etc)
|
||||||
if b <= 8 {
|
maxControlCharsCode = 8
|
||||||
|
// 0xFFFD(65533) is the "error" Rune or "Unicode replacement character"
|
||||||
|
// see https://golang.org/pkg/unicode/utf8/#pkg-constants
|
||||||
|
unicodeReplacementChar = 0xFFFD
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, b := range maybeStr {
|
||||||
|
if b <= maxControlCharsCode {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0xFFFD(65533) is the "error" Rune or "Unicode replacement character"
|
if b == unicodeReplacementChar {
|
||||||
// see https://golang.org/pkg/unicode/utf8/#pkg-constants
|
// if it is not the last (utf8.UTFMax - x) rune
|
||||||
if b == 0xFFFD {
|
|
||||||
//if it is not the last (utf8.UTFMax - x) rune
|
|
||||||
if runeCnt > utf8.UTFMax && runeIndex < runeCnt-utf8.UTFMax {
|
if runeCnt > utf8.UTFMax && runeIndex < runeCnt-utf8.UTFMax {
|
||||||
return true
|
return true
|
||||||
} else {
|
}
|
||||||
//else it is the last (utf8.UTFMax - x) rune
|
// else it is the last (utf8.UTFMax - x) rune
|
||||||
//there maybe Vxxx, VVxx, VVVx, thus, we may got max 3 0xFFFD rune (asume V is the byte we got)
|
// there maybe Vxxx, VVxx, VVVx, thus, we may got max 3 0xFFFD rune (assume V is the byte we got)
|
||||||
//for Chinese, it can only be Vxx, VVx, we may got max 2 0xFFFD rune
|
// for Chinese, it can only be Vxx, VVx, we may got max 2 0xFFFD rune
|
||||||
gotRuneErrCnt++
|
gotRuneErrCnt++
|
||||||
|
|
||||||
//mark the first time
|
// mark the first time
|
||||||
if firstRuneErrIndex == -1 {
|
if firstRuneErrIndex == -1 {
|
||||||
firstRuneErrIndex = runeIndex
|
firstRuneErrIndex = runeIndex
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runeIndex++
|
runeIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
//if last (utf8.UTFMax - x ) rune has the "error" Rune, but not all
|
// if last (utf8.UTFMax - x ) rune has the "error" Rune, but not all
|
||||||
if firstRuneErrIndex != -1 && gotRuneErrCnt != runeCnt-firstRuneErrIndex {
|
if firstRuneErrIndex != -1 && gotRuneErrCnt != runeCnt-firstRuneErrIndex {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsNamedPipe(mode os.FileMode) bool {
|
||||||
|
return mode&os.ModeNamedPipe != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSymlink(mode os.FileMode) bool {
|
||||||
|
return mode&os.ModeSymlink != 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
// CopyDir copies a directory from source to dest and all
|
// CopyDir copies a directory from source to dest and all
|
||||||
// of its sub-directories. It doesn't stop if it finds an error
|
// of its sub-directories. It doesn't stop if it finds an error
|
||||||
// during the copy. Returns an error if any.
|
// during the copy. Returns an error if any.
|
||||||
func CopyDir(fs afero.Fs, source string, dest string) error {
|
func CopyDir(fs afero.Fs, source, dest string) error {
|
||||||
// Get properties of source.
|
// Get properties of source.
|
||||||
srcinfo, err := fs.Stat(source)
|
srcinfo, err := fs.Stat(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,14 +2,35 @@ package fileutils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MoveFile moves file from src to dst.
|
||||||
|
// By default the rename filesystem system call is used. If src and dst point to different volumes
|
||||||
|
// the file copy is used as a fallback
|
||||||
|
func MoveFile(fs afero.Fs, src, dst string) error {
|
||||||
|
if fs.Rename(src, dst) == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// fallback
|
||||||
|
err := CopyFile(fs, src, dst)
|
||||||
|
if err != nil {
|
||||||
|
_ = fs.Remove(dst)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fs.Remove(src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CopyFile copies a file from source to dest and returns
|
// CopyFile copies a file from source to dest and returns
|
||||||
// an error if any.
|
// an error if any.
|
||||||
func CopyFile(fs afero.Fs, source string, dest string) error {
|
func CopyFile(fs afero.Fs, source, dest string) error {
|
||||||
// Open the source file.
|
// Open the source file.
|
||||||
src, err := fs.Open(source)
|
src, err := fs.Open(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -19,13 +40,13 @@ func CopyFile(fs afero.Fs, source string, dest string) error {
|
|||||||
|
|
||||||
// Makes the directory needed to create the dst
|
// Makes the directory needed to create the dst
|
||||||
// file.
|
// file.
|
||||||
err = fs.MkdirAll(filepath.Dir(dest), 0666)
|
err = fs.MkdirAll(filepath.Dir(dest), 0666) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the destination file.
|
// Create the destination file.
|
||||||
dst, err := fs.Create(dest)
|
dst, err := fs.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) //nolint:gomnd
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -37,15 +58,71 @@ func CopyFile(fs afero.Fs, source string, dest string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the mode if the user can't
|
// Copy the mode
|
||||||
// open the file.
|
|
||||||
info, err := fs.Stat(source)
|
info, err := fs.Stat(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fs.Chmod(dest, info.Mode())
|
return err
|
||||||
if err != nil {
|
}
|
||||||
return err
|
err = fs.Chmod(dest, info.Mode())
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommonPrefix returns common directory path of provided files
|
||||||
|
func CommonPrefix(sep byte, paths ...string) string {
|
||||||
|
// Handle special cases.
|
||||||
|
switch len(paths) {
|
||||||
|
case 0:
|
||||||
|
return ""
|
||||||
|
case 1:
|
||||||
|
return path.Clean(paths[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note, we treat string as []byte, not []rune as is often
|
||||||
|
// done in Go. (And sep as byte, not rune). This is because
|
||||||
|
// most/all supported OS' treat paths as string of non-zero
|
||||||
|
// bytes. A filename may be displayed as a sequence of Unicode
|
||||||
|
// runes (typically encoded as UTF-8) but paths are
|
||||||
|
// not required to be valid UTF-8 or in any normalized form
|
||||||
|
// (e.g. "é" (U+00C9) and "é" (U+0065,U+0301) are different
|
||||||
|
// file names.
|
||||||
|
c := []byte(path.Clean(paths[0]))
|
||||||
|
|
||||||
|
// We add a trailing sep to handle the case where the
|
||||||
|
// common prefix directory is included in the path list
|
||||||
|
// (e.g. /home/user1, /home/user1/foo, /home/user1/bar).
|
||||||
|
// path.Clean will have cleaned off trailing / separators with
|
||||||
|
// the exception of the root directory, "/" (in which case we
|
||||||
|
// make it "//", but this will get fixed up to "/" bellow).
|
||||||
|
c = append(c, sep)
|
||||||
|
|
||||||
|
// Ignore the first path since it's already in c
|
||||||
|
for _, v := range paths[1:] {
|
||||||
|
// Clean up each path before testing it
|
||||||
|
v = path.Clean(v) + string(sep)
|
||||||
|
|
||||||
|
// Find the first non-common byte and truncate c
|
||||||
|
if len(v) < len(c) {
|
||||||
|
c = c[:len(v)]
|
||||||
|
}
|
||||||
|
for i := 0; i < len(c); i++ {
|
||||||
|
if v[i] != c[i] {
|
||||||
|
c = c[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trailing non-separator characters and the final separator
|
||||||
|
for i := len(c) - 1; i >= 0; i-- {
|
||||||
|
if c[i] == sep {
|
||||||
|
c = c[:i]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(c)
|
||||||
|
}
|
||||||
|
|||||||
46
fileutils/file_test.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package fileutils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCommonPrefix(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
paths []string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
"same lvl": {
|
||||||
|
paths: []string{
|
||||||
|
"/home/user/file1",
|
||||||
|
"/home/user/file2",
|
||||||
|
},
|
||||||
|
want: "/home/user",
|
||||||
|
},
|
||||||
|
"sub folder": {
|
||||||
|
paths: []string{
|
||||||
|
"/home/user/folder",
|
||||||
|
"/home/user/folder/file",
|
||||||
|
},
|
||||||
|
want: "/home/user/folder",
|
||||||
|
},
|
||||||
|
"relative path": {
|
||||||
|
paths: []string{
|
||||||
|
"/home/user/folder",
|
||||||
|
"/home/user/folder/../folder2",
|
||||||
|
},
|
||||||
|
want: "/home/user",
|
||||||
|
},
|
||||||
|
"no common path": {
|
||||||
|
paths: []string{
|
||||||
|
"/home/user/folder",
|
||||||
|
"/etc/file",
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, tt := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
if got := CommonPrefix('/', tt.paths...); got != tt.want {
|
||||||
|
t.Errorf("CommonPrefix() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
1
frontend
13
frontend/assets.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build !dev
|
||||||
|
// +build !dev
|
||||||
|
|
||||||
|
package frontend
|
||||||
|
|
||||||
|
import "embed"
|
||||||
|
|
||||||
|
//go:embed dist/*
|
||||||
|
var assets embed.FS
|
||||||
|
|
||||||
|
func Assets() embed.FS {
|
||||||
|
return assets
|
||||||
|
}
|
||||||
15
frontend/assets_dev.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build dev
|
||||||
|
// +build dev
|
||||||
|
|
||||||
|
package frontend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var assets fs.FS = os.DirFS("frontend")
|
||||||
|
|
||||||
|
func Assets() fs.FS {
|
||||||
|
return assets
|
||||||
|
}
|
||||||
3
frontend/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ["@vue/app"],
|
||||||
|
};
|
||||||
4
frontend/dist/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
||||||
27951
frontend/package-lock.json
generated
Normal file
75
frontend/package.json
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"name": "filebrowser-frontend",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --no-clean",
|
||||||
|
"lint": "npx vue-cli-service lint --no-fix --max-warnings=0",
|
||||||
|
"fix": "npx vue-cli-service lint",
|
||||||
|
"watch": "find ./dist -maxdepth 1 -mindepth 1 ! -name '.gitignore' -exec rm -r {} + && vue-cli-service build --watch --no-clean"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ace-builds": "^1.4.7",
|
||||||
|
"clipboard": "^2.0.4",
|
||||||
|
"core-js": "^3.9.1",
|
||||||
|
"css-vars-ponyfill": "^2.4.3",
|
||||||
|
"js-base64": "^2.5.1",
|
||||||
|
"lodash.clonedeep": "^4.5.0",
|
||||||
|
"lodash.throttle": "^4.1.1",
|
||||||
|
"material-icons": "^1.10.5",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"normalize.css": "^8.0.1",
|
||||||
|
"noty": "^3.2.0-beta",
|
||||||
|
"pretty-bytes": "^6.0.0",
|
||||||
|
"qrcode.vue": "^1.7.0",
|
||||||
|
"utif": "^3.1.0",
|
||||||
|
"vue": "^2.6.10",
|
||||||
|
"vue-async-computed": "^3.9.0",
|
||||||
|
"vue-i18n": "^8.15.3",
|
||||||
|
"vue-lazyload": "^1.3.3",
|
||||||
|
"vue-router": "^3.1.3",
|
||||||
|
"vue-simple-progress": "^1.1.1",
|
||||||
|
"vuex": "^3.1.2",
|
||||||
|
"vuex-router-sync": "^5.0.0",
|
||||||
|
"whatwg-fetch": "^3.6.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "^4.1.2",
|
||||||
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
|
"@vue/cli-service": "^4.1.2",
|
||||||
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"compression-webpack-plugin": "^6.0.3",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-prettier": "^3.3.1",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"prettier": "^2.2.1",
|
||||||
|
"vue-template-compiler": "^2.6.10"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended",
|
||||||
|
"@vue/prettier"
|
||||||
|
],
|
||||||
|
"rules": {},
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postcss": {
|
||||||
|
"plugins": {
|
||||||
|
"autoprefixer": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie < 11"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
frontend/public/img/icons/android-chrome-192x192.png
Normal file
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
BIN
frontend/public/img/icons/android-chrome-512x512.png
Normal file
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
BIN
frontend/public/img/icons/apple-touch-icon.png
Normal file
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
9
frontend/public/img/icons/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square150x150logo src="/mstile-150x150.png"/>
|
||||||
|
<TileColor>#455a64</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
||||||
BIN
frontend/public/img/icons/favicon-16x16.png
Normal file
|
Before Width: | Height: | Size: 843 B After Width: | Height: | Size: 843 B |
BIN
frontend/public/img/icons/favicon-32x32.png
Normal file
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
frontend/public/img/icons/favicon.ico
Normal file
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
frontend/public/img/icons/mstile-144x144.png
Normal file
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
BIN
frontend/public/img/icons/mstile-150x150.png
Normal file
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
BIN
frontend/public/img/icons/mstile-310x150.png
Normal file
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
BIN
frontend/public/img/icons/mstile-310x310.png
Normal file
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
frontend/public/img/icons/mstile-70x70.png
Normal file
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
42
frontend/public/img/icons/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M3245 6989 c-522 -39 -1042 -197 -1480 -449 -849 -488 -1459 -1308
|
||||||
|
-1673 -2250 -177 -776 -89 -1582 250 -2301 368 -778 1052 -1418 1857 -1739
|
||||||
|
903 -359 1927 -325 2812 92 778 368 1418 1052 1739 1857 359 903 325 1927 -92
|
||||||
|
2812 -296 627 -806 1175 -1423 1529 -587 338 -1308 500 -1990 449z m555 -580
|
||||||
|
c519 -51 1018 -245 1446 -565 788 -588 1229 -1526 1174 -2496 -16 -277 -58
|
||||||
|
-500 -145 -763 -144 -440 -378 -819 -710 -1150 -452 -452 -1005 -730 -1655
|
||||||
|
-832 -91 -14 -175 -18 -405 -18 -304 0 -369 6 -595 51 -1105 223 -1999 1092
|
||||||
|
-2259 2197 -52 221 -73 412 -73 667 0 397 64 732 204 1080 304 752 886 1334
|
||||||
|
1638 1638 431 174 895 238 1380 191z"/>
|
||||||
|
<path d="M2670 5215 c0 -13 -44 -15 -335 -15 -352 0 -383 -3 -399 -45 -3 -9
|
||||||
|
-6 -758 -6 -1663 0 -1168 -3 -1643 -11 -1632 -8 11 -9 8 -4 -15 3 -16 17 -41
|
||||||
|
31 -55 l24 -25 1530 0 1530 0 24 25 c14 14 26 36 27 50 1 14 1 711 1 1550 l-2
|
||||||
|
1526 -228 142 -229 142 -136 0 -137 0 0 -600 0 -600 -705 0 -705 0 0 615 0
|
||||||
|
615 -135 0 c-113 0 -135 -2 -135 -15z m-264 -190 c57 -29 89 -71 103 -137 35
|
||||||
|
-154 -98 -282 -258 -247 -55 12 -122 62 -148 113 -36 69 -12 186 49 243 62 58
|
||||||
|
170 70 254 28z m2316 -1702 c17 -15 18 -49 18 -670 l0 -653 -1245 0 -1245 0 0
|
||||||
|
654 c0 582 2 656 16 670 14 14 139 16 1226 16 1113 0 1213 -1 1230 -17z
|
||||||
|
m-2602 -1363 c40 -40 13 -100 -43 -100 -60 0 -88 59 -47 100 11 11 31 20 45
|
||||||
|
20 14 0 34 -9 45 -20z m2840 0 c41 -41 11 -100 -52 -100 -35 0 -58 24 -58 60
|
||||||
|
0 54 71 79 110 40z"/>
|
||||||
|
<path d="M2431 3091 c-7 -13 -7 -23 2 -35 11 -15 97 -16 1067 -14 l1055 3 0
|
||||||
|
30 0 30 -1057 3 c-1023 2 -1058 1 -1067 -17z"/>
|
||||||
|
<path d="M2436 2675 c-19 -19 -11 -41 17 -49 41 -11 2067 -7 2088 4 23 13 25
|
||||||
|
46 3 54 -9 3 -483 6 -1054 6 -919 0 -1040 -2 -1054 -15z"/>
|
||||||
|
<path d="M2447 2273 c-14 -4 -17 -13 -15 -36 l3 -32 1049 -3 c767 -1 1052 1
|
||||||
|
1062 9 20 16 17 47 -5 59 -20 10 -2055 13 -2094 3z"/>
|
||||||
|
<path d="M3822 5027 c-21 -23 -22 -30 -22 -293 0 -258 1 -271 20 -292 27 -29
|
||||||
|
56 -35 140 -30 56 3 75 8 93 26 22 22 22 26 22 298 l0 276 -24 19 c-19 16 -40
|
||||||
|
19 -115 19 -84 0 -95 -2 -114 -23z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
147
frontend/public/img/logo.svg
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xml:space="preserve"
|
||||||
|
width="560"
|
||||||
|
height="560"
|
||||||
|
version="1.1"
|
||||||
|
style="clip-rule:evenodd;fill-rule:evenodd;image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
|
||||||
|
viewBox="0 0 560 560"
|
||||||
|
id="svg44"
|
||||||
|
sodipodi:docname="icon_raw.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||||
|
inkscape:export-filename="/home/umarcor/filebrowser/logo/icon_raw.svg.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"><metadata
|
||||||
|
id="metadata48"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:window-height="711"
|
||||||
|
id="namedview46"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.33714286"
|
||||||
|
inkscape:cx="-172.33051"
|
||||||
|
inkscape:cy="280"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="20"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg44" />
|
||||||
|
<defs
|
||||||
|
id="defs4">
|
||||||
|
<style
|
||||||
|
type="text/css"
|
||||||
|
id="style2">
|
||||||
|
<![CDATA[
|
||||||
|
.fil1 {fill:#FEFEFE}
|
||||||
|
.fil6 {fill:#006498}
|
||||||
|
.fil7 {fill:#0EA5EB}
|
||||||
|
.fil8 {fill:#2979FF}
|
||||||
|
.fil3 {fill:#2BBCFF}
|
||||||
|
.fil0 {fill:#455A64}
|
||||||
|
.fil4 {fill:#53C6FC}
|
||||||
|
.fil5 {fill:#BDEAFF}
|
||||||
|
.fil2 {fill:#332C2B;fill-opacity:0.149020}
|
||||||
|
]]>
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
id="g85"
|
||||||
|
transform="translate(-70,-70)"><path
|
||||||
|
class="fil1"
|
||||||
|
d="M 350,71 C 504,71 629,196 629,350 629,504 504,629 350,629 196,629 71,504 71,350 71,196 196,71 350,71 Z"
|
||||||
|
id="path9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#fefefe" /><path
|
||||||
|
class="fil2"
|
||||||
|
d="M 475,236 593,387 C 596,503 444,639 301,585 L 225,486 339,330 c 0,0 138,-95 136,-94 z"
|
||||||
|
id="path11"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#332c2b;fill-opacity:0.14902003" /><path
|
||||||
|
class="fil3"
|
||||||
|
d="m 231,211 h 208 l 38,24 v 246 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 V 219 c 0,-5 3,-8 8,-8 z"
|
||||||
|
id="path13"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#2bbcff" /><path
|
||||||
|
class="fil4"
|
||||||
|
d="m 231,211 h 208 l 38,24 v 2 L 440,214 H 231 c -4,0 -7,3 -7,7 v 263 c -1,-1 -1,-2 -1,-3 V 219 c 0,-5 3,-8 8,-8 z"
|
||||||
|
id="path15"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#53c6fc" /><polygon
|
||||||
|
class="fil5"
|
||||||
|
points="305,212 418,212 418,310 305,310 "
|
||||||
|
id="polygon17"
|
||||||
|
style="fill:#bdeaff" /><path
|
||||||
|
class="fil5"
|
||||||
|
d="m 255,363 h 189 c 3,0 5,2 5,4 V 483 H 250 V 367 c 0,-2 2,-4 5,-4 z"
|
||||||
|
id="path19"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#bdeaff" /><polygon
|
||||||
|
class="fil6"
|
||||||
|
points="250,470 449,470 449,483 250,483 "
|
||||||
|
id="polygon21"
|
||||||
|
style="fill:#006498" /><path
|
||||||
|
class="fil6"
|
||||||
|
d="m 380,226 h 10 c 3,0 6,2 6,5 v 40 c 0,3 -3,6 -6,6 h -10 c -3,0 -6,-3 -6,-6 v -40 c 0,-3 3,-5 6,-5 z"
|
||||||
|
id="path23"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#006498" /><path
|
||||||
|
class="fil1"
|
||||||
|
d="m 254,226 c 10,0 17,7 17,17 0,9 -7,16 -17,16 -9,0 -17,-7 -17,-16 0,-10 8,-17 17,-17 z"
|
||||||
|
id="path25"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#fefefe" /><path
|
||||||
|
class="fil6"
|
||||||
|
d="m 267,448 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,3 -3,3 H 267 c -2,0 -3,-2 -3,-3 v 0 c 0,-2 1,-3 3,-3 z"
|
||||||
|
id="path27"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#006498" /><path
|
||||||
|
class="fil6"
|
||||||
|
d="m 267,415 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,2 -3,2 H 267 c -2,0 -3,-1 -3,-2 v 0 c 0,-2 1,-3 3,-3 z"
|
||||||
|
id="path29"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#006498" /><path
|
||||||
|
class="fil6"
|
||||||
|
d="m 267,381 h 165 c 2,0 3,2 3,3 v 0 c 0,2 -1,3 -3,3 H 267 c -2,0 -3,-1 -3,-3 v 0 c 0,-1 1,-3 3,-3 z"
|
||||||
|
id="path31"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#006498" /><path
|
||||||
|
class="fil1"
|
||||||
|
d="m 236,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
|
||||||
|
id="path33"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#fefefe" /><path
|
||||||
|
class="fil1"
|
||||||
|
d="m 463,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
|
||||||
|
id="path35"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#fefefe" /><polygon
|
||||||
|
class="fil6"
|
||||||
|
points="305,212 284,212 284,310 305,310 "
|
||||||
|
id="polygon37"
|
||||||
|
style="fill:#006498" /><path
|
||||||
|
class="fil7"
|
||||||
|
d="m 477,479 v 2 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 v -2 c 0,4 3,8 8,8 h 238 c 5,0 8,-4 8,-8 z"
|
||||||
|
id="path39"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#0ea5eb" /><path
|
||||||
|
class="fil8"
|
||||||
|
d="M 350,70 C 505,70 630,195 630,350 630,505 505,630 350,630 195,630 70,505 70,350 70,195 195,70 350,70 Z m 0,46 C 479,116 584,221 584,350 584,479 479,584 350,584 221,584 116,479 116,350 116,221 221,116 350,116 Z"
|
||||||
|
id="path41"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#2979ff" /></g>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
144
frontend/public/index.html
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||||
|
|
||||||
|
[{[ if .ReCaptcha -]}]
|
||||||
|
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
|
||||||
|
[{[ end ]}]
|
||||||
|
|
||||||
|
<title>[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]</title>
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png">
|
||||||
|
|
||||||
|
<!-- Add to home screen for Android and modern mobile browsers -->
|
||||||
|
<link rel="manifest" id="manifestPlaceholder" crossorigin="use-credentials">
|
||||||
|
<meta name="theme-color" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
||||||
|
|
||||||
|
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="assets">
|
||||||
|
<link rel="apple-touch-icon" href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png">
|
||||||
|
|
||||||
|
<!-- Add to home screen for Windows -->
|
||||||
|
<meta name="msapplication-TileImage" content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png">
|
||||||
|
<meta name="msapplication-TileColor" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]">
|
||||||
|
|
||||||
|
<!-- Inject Some Variables and generate the manifest json -->
|
||||||
|
<script>
|
||||||
|
window.FileBrowser = JSON.parse('[{[ .Json ]}]');
|
||||||
|
|
||||||
|
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
||||||
|
var dynamicManifest = {
|
||||||
|
"name": window.FileBrowser.Name || 'File Browser',
|
||||||
|
"short_name": window.FileBrowser.Name || 'File Browser',
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": fullStaticURL + "/img/icons/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": fullStaticURL + "/img/icons/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": window.location.origin + window.FileBrowser.BaseURL,
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"theme_color": window.FileBrowser.Color || "#455a64"
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringManifest = JSON.stringify(dynamicManifest);
|
||||||
|
const blob = new Blob([stringManifest], {type: 'application/json'});
|
||||||
|
const manifestURL = URL.createObjectURL(blob);
|
||||||
|
document.querySelector('#manifestPlaceholder').setAttribute('href', manifestURL);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#loading {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: .1s ease opacity;
|
||||||
|
-webkit-transition: .1s ease opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading.done {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner {
|
||||||
|
width: 70px;
|
||||||
|
text-align: center;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner > div {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner .bounce1 {
|
||||||
|
-webkit-animation-delay: -0.32s;
|
||||||
|
animation-delay: -0.32s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading .spinner .bounce2 {
|
||||||
|
-webkit-animation-delay: -0.16s;
|
||||||
|
animation-delay: -0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes sk-bouncedelay {
|
||||||
|
0%, 80%, 100% { -webkit-transform: scale(0) }
|
||||||
|
40% { -webkit-transform: scale(1.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sk-bouncedelay {
|
||||||
|
0%, 80%, 100% {
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
} 40% {
|
||||||
|
-webkit-transform: scale(1.0);
|
||||||
|
transform: scale(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<div id="loading">
|
||||||
|
<div class="spinner">
|
||||||
|
<div class="bounce1"></div>
|
||||||
|
<div class="bounce2"></div>
|
||||||
|
<div class="bounce3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
[{[ if .Theme -]}]
|
||||||
|
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css" />
|
||||||
|
[{[ end ]}]
|
||||||
|
[{[ if .CSS -]}]
|
||||||
|
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
||||||
|
[{[ end ]}]
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
frontend/public/manifest.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "File Browser",
|
||||||
|
"short_name": "File Browser",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "./img/icons/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "./static/img/icons/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"theme_color": "#455a64"
|
||||||
|
}
|
||||||
211
frontend/public/themes/dark.css
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
:root {
|
||||||
|
--background: #141D24;
|
||||||
|
--surfacePrimary: #20292F;
|
||||||
|
--surfaceSecondary: #3A4147;
|
||||||
|
--divider: rgba(255, 255, 255, 0.12);
|
||||||
|
--icon: #ffffff;
|
||||||
|
--textPrimary: rgba(255, 255, 255, 0.87);
|
||||||
|
--textSecondary: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading {
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
#loading .spinner div, main .spinner div {
|
||||||
|
background: var(--icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
#login {
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background: var(--surfacePrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#search #input {
|
||||||
|
background: var(--surfaceSecondary);
|
||||||
|
border-color: var(--surfacePrimary);
|
||||||
|
}
|
||||||
|
#search #input input::placeholder {
|
||||||
|
color: var(--textSecondary);
|
||||||
|
}
|
||||||
|
#search.active #input {
|
||||||
|
background: var(--surfacePrimary);
|
||||||
|
}
|
||||||
|
#search.active input {
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
#search #result {
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
#search .boxes {
|
||||||
|
background: var(--surfaceSecondary);
|
||||||
|
}
|
||||||
|
#search .boxes h3 {
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
color: var(--textPrimary) !important;
|
||||||
|
}
|
||||||
|
.action:hover {
|
||||||
|
background-color: rgba(255, 255, 255, .1);
|
||||||
|
}
|
||||||
|
.action i {
|
||||||
|
color: var(--icon) !important;
|
||||||
|
}
|
||||||
|
.action .counter {
|
||||||
|
border-color: var(--surfacePrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > div {
|
||||||
|
border-color: var(--divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs {
|
||||||
|
border-color: var(--divider);
|
||||||
|
color: var(--textPrimary) !important;
|
||||||
|
}
|
||||||
|
.breadcrumbs span {
|
||||||
|
color: var(--textPrimary) !important;
|
||||||
|
}
|
||||||
|
.breadcrumbs a:hover {
|
||||||
|
background-color: rgba(255, 255, 255, .1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing .item {
|
||||||
|
background: var(--surfacePrimary);
|
||||||
|
color: var(--textPrimary);
|
||||||
|
border-color: var(--divider) !important;
|
||||||
|
}
|
||||||
|
#listing .item i {
|
||||||
|
color: var(--icon);
|
||||||
|
}
|
||||||
|
#listing .item .modified {
|
||||||
|
color: var(--textSecondary);
|
||||||
|
}
|
||||||
|
#listing h2,
|
||||||
|
#listing.list .header span {
|
||||||
|
color: var(--textPrimary) !important;
|
||||||
|
}
|
||||||
|
#listing.list .header span {
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
#listing.list .header i {
|
||||||
|
color: var(--icon);
|
||||||
|
}
|
||||||
|
#listing.list .item.header {
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--surfacePrimary);
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
.button--flat:hover {
|
||||||
|
background: var(--surfaceSecondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard #nav ul li {
|
||||||
|
color: var(--textSecondary);
|
||||||
|
}
|
||||||
|
.dashboard #nav ul li:hover {
|
||||||
|
background: var(--surfaceSecondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3,
|
||||||
|
.dashboard #nav,
|
||||||
|
.dashboard p label {
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
.card#share input,
|
||||||
|
.card#share select,
|
||||||
|
.input {
|
||||||
|
background: var(--surfaceSecondary);
|
||||||
|
color: var(--textPrimary);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
.input:hover,
|
||||||
|
.input:focus {
|
||||||
|
border-color: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
.input--red {
|
||||||
|
background: #73302D;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input--green {
|
||||||
|
background: #147A41;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard #nav .wrapper,
|
||||||
|
.collapsible {
|
||||||
|
border-color: var(--divider);
|
||||||
|
}
|
||||||
|
.collapsible > label * {
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
color: var(--textSecondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-list li:hover {
|
||||||
|
background: var(--surfaceSecondary);
|
||||||
|
}
|
||||||
|
.file-list li:before {
|
||||||
|
color: var(--textSecondary);
|
||||||
|
}
|
||||||
|
.file-list li[aria-selected=true]:before {
|
||||||
|
color: var(--icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shell {
|
||||||
|
background: var(--surfacePrimary);
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
.shell__result {
|
||||||
|
border-top: 1px solid var(--divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor-container {
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor-container .bar {
|
||||||
|
background: var(--surfacePrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 736px) {
|
||||||
|
#file-selection {
|
||||||
|
background: var(--surfaceSecondary) !important;
|
||||||
|
}
|
||||||
|
#file-selection span {
|
||||||
|
color: var(--textPrimary) !important;
|
||||||
|
}
|
||||||
|
nav {
|
||||||
|
background: var(--surfaceSecondary) !important;
|
||||||
|
}
|
||||||
|
#dropdown {
|
||||||
|
background: var(--surfaceSecondary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.share__box {
|
||||||
|
background: var(--surfacePrimary) !important;
|
||||||
|
color: var(--textPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.share__box__element {
|
||||||
|
border-top-color: var(--divider);
|
||||||
|
}
|
||||||
26
frontend/src/App.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
__webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "app",
|
||||||
|
mounted() {
|
||||||
|
const loading = document.getElementById("loading");
|
||||||
|
loading.classList.add("done");
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
loading.parentNode.removeChild(loading);
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import "./css/styles.css";
|
||||||
|
</style>
|
||||||
16
frontend/src/api/commands.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { removePrefix } from "./utils";
|
||||||
|
import { baseURL } from "@/utils/constants";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
const ssl = window.location.protocol === "https:";
|
||||||
|
const protocol = ssl ? "wss:" : "ws:";
|
||||||
|
|
||||||
|
export default function command(url, command, onmessage, onclose) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`;
|
||||||
|
|
||||||
|
let conn = new window.WebSocket(url);
|
||||||
|
conn.onopen = () => conn.send(command);
|
||||||
|
conn.onmessage = onmessage;
|
||||||
|
conn.onclose = onclose;
|
||||||
|
}
|
||||||
186
frontend/src/api/files.js
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
import { createURL, fetchURL, removePrefix } from "./utils";
|
||||||
|
import { baseURL } from "@/utils/constants";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export async function fetch(url) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
const res = await fetchURL(`/api/resources${url}`, {});
|
||||||
|
|
||||||
|
let data = await res.json();
|
||||||
|
data.url = `/files${url}`;
|
||||||
|
|
||||||
|
if (data.isDir) {
|
||||||
|
if (!data.url.endsWith("/")) data.url += "/";
|
||||||
|
data.items = data.items.map((item, index) => {
|
||||||
|
item.index = index;
|
||||||
|
item.url = `${data.url}${encodeURIComponent(item.name)}`;
|
||||||
|
|
||||||
|
if (item.isDir) {
|
||||||
|
item.url += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resourceAction(url, method, content) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
let opts = { method };
|
||||||
|
|
||||||
|
if (content) {
|
||||||
|
opts.body = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetchURL(`/api/resources${url}`, opts);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(url) {
|
||||||
|
return resourceAction(url, "DELETE");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function put(url, content = "") {
|
||||||
|
return resourceAction(url, "PUT", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function download(format, ...files) {
|
||||||
|
let url = `${baseURL}/api/raw`;
|
||||||
|
|
||||||
|
if (files.length === 1) {
|
||||||
|
url += removePrefix(files[0]) + "?";
|
||||||
|
} else {
|
||||||
|
let arg = "";
|
||||||
|
|
||||||
|
for (let file of files) {
|
||||||
|
arg += removePrefix(file) + ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
arg = arg.substring(0, arg.length - 1);
|
||||||
|
arg = encodeURIComponent(arg);
|
||||||
|
url += `/?files=${arg}&`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
url += `algo=${format}&`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store.state.jwt) {
|
||||||
|
url += `auth=${store.state.jwt}&`;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function post(url, content = "", overwrite = false, onupload) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
let bufferContent;
|
||||||
|
if (
|
||||||
|
content instanceof Blob &&
|
||||||
|
!["http:", "https:"].includes(window.location.protocol)
|
||||||
|
) {
|
||||||
|
bufferContent = await new Response(content).arrayBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.open(
|
||||||
|
"POST",
|
||||||
|
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
request.setRequestHeader("X-Auth", store.state.jwt);
|
||||||
|
|
||||||
|
if (typeof onupload === "function") {
|
||||||
|
request.upload.onprogress = onupload;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onload = () => {
|
||||||
|
if (request.status === 200) {
|
||||||
|
resolve(request.responseText);
|
||||||
|
} else if (request.status === 409) {
|
||||||
|
reject(request.status);
|
||||||
|
} else {
|
||||||
|
reject(request.responseText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onerror = () => {
|
||||||
|
reject(new Error("001 Connection aborted"));
|
||||||
|
};
|
||||||
|
|
||||||
|
request.send(bufferContent || content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveCopy(items, copy = false, overwrite = false, rename = false) {
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
for (let item of items) {
|
||||||
|
const from = item.from;
|
||||||
|
const to = encodeURIComponent(removePrefix(item.to));
|
||||||
|
const url = `${from}?action=${
|
||||||
|
copy ? "copy" : "rename"
|
||||||
|
}&destination=${to}&override=${overwrite}&rename=${rename}`;
|
||||||
|
promises.push(resourceAction(url, "PATCH"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function move(items, overwrite = false, rename = false) {
|
||||||
|
return moveCopy(items, false, overwrite, rename);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function copy(items, overwrite = false, rename = false) {
|
||||||
|
return moveCopy(items, true, overwrite, rename);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checksum(url, algo) {
|
||||||
|
const data = await resourceAction(`${url}?checksum=${algo}`, "GET");
|
||||||
|
return (await data.json()).checksums[algo];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDownloadURL(file, inline) {
|
||||||
|
const params = {
|
||||||
|
...(inline && { inline: "true" }),
|
||||||
|
};
|
||||||
|
|
||||||
|
return createURL("api/raw" + file.path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPreviewURL(file, size) {
|
||||||
|
const params = {
|
||||||
|
inline: "true",
|
||||||
|
key: Date.parse(file.modified),
|
||||||
|
};
|
||||||
|
|
||||||
|
return createURL("api/preview/" + size + file.path, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubtitlesURL(file) {
|
||||||
|
const params = {
|
||||||
|
inline: "true",
|
||||||
|
};
|
||||||
|
|
||||||
|
const subtitles = [];
|
||||||
|
for (const sub of file.subtitles) {
|
||||||
|
subtitles.push(createURL("api/raw" + sub, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtitles;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function usage(url) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
const res = await fetchURL(`/api/usage${url}`, {});
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
9
frontend/src/api/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import * as files from "./files";
|
||||||
|
import * as share from "./share";
|
||||||
|
import * as users from "./users";
|
||||||
|
import * as settings from "./settings";
|
||||||
|
import * as pub from "./pub";
|
||||||
|
import search from "./search";
|
||||||
|
import commands from "./commands";
|
||||||
|
|
||||||
|
export { files, share, users, settings, pub, commands, search };
|
||||||
70
frontend/src/api/pub.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { fetchURL, removePrefix, createURL } from "./utils";
|
||||||
|
import { baseURL } from "@/utils/constants";
|
||||||
|
|
||||||
|
export async function fetch(url, password = "") {
|
||||||
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
const res = await fetchURL(
|
||||||
|
`/api/public/share${url}`,
|
||||||
|
{
|
||||||
|
headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) },
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = await res.json();
|
||||||
|
data.url = `/share${url}`;
|
||||||
|
|
||||||
|
if (data.isDir) {
|
||||||
|
if (!data.url.endsWith("/")) data.url += "/";
|
||||||
|
data.items = data.items.map((item, index) => {
|
||||||
|
item.index = index;
|
||||||
|
item.url = `${data.url}${encodeURIComponent(item.name)}`;
|
||||||
|
|
||||||
|
if (item.isDir) {
|
||||||
|
item.url += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function download(format, hash, token, ...files) {
|
||||||
|
let url = `${baseURL}/api/public/dl/${hash}`;
|
||||||
|
|
||||||
|
if (files.length === 1) {
|
||||||
|
url += encodeURIComponent(files[0]) + "?";
|
||||||
|
} else {
|
||||||
|
let arg = "";
|
||||||
|
|
||||||
|
for (let file of files) {
|
||||||
|
arg += encodeURIComponent(file) + ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
arg = arg.substring(0, arg.length - 1);
|
||||||
|
arg = encodeURIComponent(arg);
|
||||||
|
url += `/?files=${arg}&`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
url += `algo=${format}&`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
url += `token=${token}&`;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDownloadURL(share, inline = false) {
|
||||||
|
const params = {
|
||||||
|
...(inline && { inline: "true" }),
|
||||||
|
...(share.token && { token: share.token }),
|
||||||
|
};
|
||||||
|
|
||||||
|
return createURL("api/public/dl/" + share.hash + share.path, params, false);
|
||||||
|
}
|
||||||
27
frontend/src/api/search.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { fetchURL, removePrefix } from "./utils";
|
||||||
|
import url from "../utils/url";
|
||||||
|
|
||||||
|
export default async function search(base, query) {
|
||||||
|
base = removePrefix(base);
|
||||||
|
query = encodeURIComponent(query);
|
||||||
|
|
||||||
|
if (!base.endsWith("/")) {
|
||||||
|
base += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = await fetchURL(`/api/search${base}?query=${query}`, {});
|
||||||
|
|
||||||
|
let data = await res.json();
|
||||||
|
|
||||||
|
data = data.map((item) => {
|
||||||
|
item.url = `/files${base}` + url.encodePath(item.path);
|
||||||
|
|
||||||
|
if (item.dir) {
|
||||||
|
item.url += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
12
frontend/src/api/settings.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { fetchURL, fetchJSON } from "./utils";
|
||||||
|
|
||||||
|
export function get() {
|
||||||
|
return fetchJSON(`/api/settings`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(settings) {
|
||||||
|
await fetchURL(`/api/settings`, {
|
||||||
|
method: "PUT",
|
||||||
|
body: JSON.stringify(settings),
|
||||||
|
});
|
||||||
|
}
|
||||||
36
frontend/src/api/share.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { fetchURL, fetchJSON, removePrefix, createURL } from "./utils";
|
||||||
|
|
||||||
|
export async function list() {
|
||||||
|
return fetchJSON("/api/shares");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(url) {
|
||||||
|
url = removePrefix(url);
|
||||||
|
return fetchJSON(`/api/share${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(hash) {
|
||||||
|
await fetchURL(`/api/share/${hash}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(url, password = "", expires = "", unit = "hours") {
|
||||||
|
url = removePrefix(url);
|
||||||
|
url = `/api/share${url}`;
|
||||||
|
if (expires !== "") {
|
||||||
|
url += `?expires=${expires}&unit=${unit}`;
|
||||||
|
}
|
||||||
|
let body = "{}";
|
||||||
|
if (password != "" || expires !== "" || unit !== "hours") {
|
||||||
|
body = JSON.stringify({ password: password, expires: expires, unit: unit });
|
||||||
|
}
|
||||||
|
return fetchJSON(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShareURL(share) {
|
||||||
|
return createURL("share/" + share.hash, {}, false);
|
||||||
|
}
|
||||||
41
frontend/src/api/users.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { fetchURL, fetchJSON } from "./utils";
|
||||||
|
|
||||||
|
export async function getAll() {
|
||||||
|
return fetchJSON(`/api/users`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(id) {
|
||||||
|
return fetchJSON(`/api/users/${id}`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(user) {
|
||||||
|
const res = await fetchURL(`/api/users`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
what: "user",
|
||||||
|
which: [],
|
||||||
|
data: user,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 201) {
|
||||||
|
return res.headers.get("Location");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(user, which = ["all"]) {
|
||||||
|
await fetchURL(`/api/users/${user.id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
body: JSON.stringify({
|
||||||
|
what: "user",
|
||||||
|
which: which,
|
||||||
|
data: user,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(id) {
|
||||||
|
await fetchURL(`/api/users/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
});
|
||||||
|
}
|
||||||
81
frontend/src/api/utils.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import store from "@/store";
|
||||||
|
import { renew, logout } from "@/utils/auth";
|
||||||
|
import { baseURL } from "@/utils/constants";
|
||||||
|
import { encodePath } from "@/utils/url";
|
||||||
|
|
||||||
|
export async function fetchURL(url, opts, auth = true) {
|
||||||
|
opts = opts || {};
|
||||||
|
opts.headers = opts.headers || {};
|
||||||
|
|
||||||
|
let { headers, ...rest } = opts;
|
||||||
|
|
||||||
|
let res;
|
||||||
|
try {
|
||||||
|
res = await fetch(`${baseURL}${url}`, {
|
||||||
|
headers: {
|
||||||
|
"X-Auth": store.state.jwt,
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
const error = new Error("000 No connection");
|
||||||
|
error.status = 0;
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth && res.headers.get("X-Renew-Token") === "true") {
|
||||||
|
await renew(store.state.jwt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.status < 200 || res.status > 299) {
|
||||||
|
const error = new Error(await res.text());
|
||||||
|
error.status = res.status;
|
||||||
|
|
||||||
|
if (auth && res.status == 401) {
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchJSON(url, opts) {
|
||||||
|
const res = await fetchURL(url, opts);
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
throw new Error(res.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removePrefix(url) {
|
||||||
|
url = url.split("/").splice(2).join("/");
|
||||||
|
|
||||||
|
if (url === "") url = "/";
|
||||||
|
if (url[0] !== "/") url = "/" + url;
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createURL(endpoint, params = {}, auth = true) {
|
||||||
|
let prefix = baseURL;
|
||||||
|
if (!prefix.endsWith("/")) {
|
||||||
|
prefix = prefix + "/";
|
||||||
|
}
|
||||||
|
const url = new URL(prefix + encodePath(endpoint), origin);
|
||||||
|
|
||||||
|
const searchParams = {
|
||||||
|
...(auth && { auth: store.state.jwt }),
|
||||||
|
...params,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const key in searchParams) {
|
||||||
|
url.searchParams.set(key, searchParams[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||