Compare commits
2598 Commits
vshopify
...
fix-array-
Author | SHA1 | Date | |
---|---|---|---|
|
8290a1606f | ||
|
eddd458744 | ||
|
439ea20a89 | ||
|
cec223c8e7 | ||
|
25cb188d00 | ||
|
31007a8d96 | ||
|
a4fa8db69b | ||
|
6193835ea1 | ||
|
0c78e9e4ac | ||
|
58c409e7fa | ||
|
9577eed524 | ||
|
76f32cd064 | ||
|
92d9c17095 | ||
|
b0396df33f | ||
|
c17572c76f | ||
|
5c91e072a6 | ||
|
4991d0f965 | ||
|
45b74e1ce5 | ||
|
ccb4b9a9ba | ||
|
dd635071d6 | ||
|
fe1448dfae | ||
|
56855bc54d | ||
|
b51fa8df5a | ||
|
3aa979cb11 | ||
|
c95f75bc6c | ||
|
b13a636f89 | ||
|
8de55cef31 | ||
|
cb781f42e3 | ||
|
b59292dc24 | ||
|
03b793d7e2 | ||
|
bee18d1cfb | ||
|
d8698181f4 | ||
|
47f5d97eaf | ||
|
cb3c5e56fd | ||
|
39b76c08de | ||
|
c3c8cc21ff | ||
|
6dba1b6d8b | ||
|
381fe70a79 | ||
|
feb927c2e4 | ||
|
e77bd4c188 | ||
|
43436fc49e | ||
|
d738f797ec | ||
|
b5de97f785 | ||
|
b0c1b0895d | ||
|
8e60932f81 | ||
|
7d14cd74f2 | ||
|
717f1610f5 | ||
|
f1abe6497f | ||
|
046129a57d | ||
|
90d300a490 | ||
|
a2d506c0db | ||
|
58748a24da | ||
|
639e8a4a1d | ||
|
48ebaf5c5a | ||
|
1aaccb1e6b | ||
|
f05a7f9f14 | ||
|
98ddb348b0 | ||
|
a4aa85ebab | ||
|
516efe56f4 | ||
|
a4d72d5bbc | ||
|
24b8ec16f1 | ||
|
ac25fef555 | ||
|
8302f082a2 | ||
|
7546ef7a8e | ||
|
f598c70a4f | ||
|
422da21de5 | ||
|
f530fb3241 | ||
|
5d39bb7466 | ||
|
041cba72b6 | ||
|
892b3e273f | ||
|
91faf5756d | ||
|
e239390ebf | ||
|
b24764d679 | ||
|
4d5a568fd7 | ||
|
5ab55e71e0 | ||
|
929d63ecf8 | ||
|
0ef7f3715f | ||
|
2298f3901a | ||
|
3005f1937a | ||
|
f48eec2e93 | ||
|
754d304e54 | ||
|
9b8d08a668 | ||
|
1b672a1ace | ||
|
60d6e98c67 | ||
|
11f05285a1 | ||
|
2bd1842da1 | ||
|
57544068e9 | ||
|
60cfea9f94 | ||
|
eece001376 | ||
|
98d8ef8e1a | ||
|
0e43042217 | ||
|
c8e6714207 | ||
|
824e779eb2 | ||
|
83ea898780 | ||
|
5af3233fd6 | ||
|
08ff2f3173 | ||
|
22657b66d7 | ||
|
8b6c7a6061 | ||
|
1f197f6688 | ||
|
1055e61bb4 | ||
|
7ad0aa82fc | ||
|
d3f5576570 | ||
|
45141d1391 | ||
|
de9ac9fd43 | ||
|
c53d5272d6 | ||
|
18c78192ec | ||
|
632d67eef4 | ||
|
c23aa48688 | ||
|
95f3e429b4 | ||
|
8635fcfe84 | ||
|
d861537d9a | ||
|
631ee99f60 | ||
|
ffa1441ccd | ||
|
2f3e947027 | ||
|
a62aecfdfe | ||
|
5f829c68f2 | ||
|
0290d74aeb | ||
|
f6bc16007d | ||
|
ad5752f09b | ||
|
55565f1718 | ||
|
5f96d17b8c | ||
|
fd22406e0a | ||
|
64fe542c1e | ||
|
fae1dc8dbb | ||
|
6f2b673021 | ||
|
b26679ca14 | ||
|
04ba1430ca | ||
|
53f3758abc | ||
|
c6742f5533 | ||
|
cb44591a47 | ||
|
e02abb509f | ||
|
eff6be9643 | ||
|
348dbd7107 | ||
|
f74ea14d8b | ||
|
a671632fde | ||
|
e344622c9e | ||
|
06d7483ca3 | ||
|
7fe041fc2c | ||
|
3f18e5476a | ||
|
2a31613fe8 | ||
|
ded0c8a3bc | ||
|
f3d9e07c5e | ||
|
eb3ba95114 | ||
|
7951dcada6 | ||
|
06951a39c6 | ||
|
abe29f21f0 | ||
|
f57eab3008 | ||
|
6d4b2348ac | ||
|
397ca6ef0c | ||
|
d6e5ee2851 | ||
|
98d62e826b | ||
|
7b5ce8f70c | ||
|
2010a9a458 | ||
|
f787058c17 | ||
|
87ccae0d90 | ||
|
07d95c6ed7 | ||
|
514823f7d2 | ||
|
fb4feb24f3 | ||
|
5caa0e0722 | ||
|
0406b420c8 | ||
|
9d72b9779e | ||
|
fdc47e4a38 | ||
|
0566e964c0 | ||
|
896fbf9a5c | ||
|
126c8c101e | ||
|
3cb7cc01e4 | ||
|
2b3d15bf45 | ||
|
4049bdadcb | ||
|
2042ba37d8 | ||
|
41a4ba62b0 | ||
|
21558d25b1 | ||
|
06622bfbfd | ||
|
16fd2e3938 | ||
|
040d7670ec | ||
|
23761eacc1 | ||
|
5790bed766 | ||
|
2f88da67e8 | ||
|
21091cbf1a | ||
|
808949a884 | ||
|
06334273dc | ||
|
5399c04dff | ||
|
cd051d4093 | ||
|
0ca6e8ccfb | ||
|
bd075919f3 | ||
|
c229425534 | ||
|
e89b1826ce | ||
|
4ef19e19cc | ||
|
ff58301729 | ||
|
4ae05272c3 | ||
|
d14dafc871 | ||
|
022a077726 | ||
|
d5bd86b07a | ||
|
66e1eee010 | ||
|
ddb125f458 | ||
|
e6a157a101 | ||
|
0a437fba6a | ||
|
39f2e80dc1 | ||
|
13f9eb0d18 | ||
|
575b829799 | ||
|
02e50fadae | ||
|
a02f191034 | ||
|
d73d0f178f | ||
|
d542a61f5a | ||
|
e0486aaa24 | ||
|
02bf76fb3c | ||
|
8a3ece4a70 | ||
|
c553dc02a9 | ||
|
ff71caa47e | ||
|
2bd8227e20 | ||
|
5c61de3ae9 | ||
|
cff46f2d59 | ||
|
bbbaacc350 | ||
|
60f84d5e30 | ||
|
5218aa3c43 | ||
|
4b2ea0c0c3 | ||
|
9b865ef849 | ||
|
9344113ae4 | ||
|
4448ac9d2a | ||
|
9aff143d40 | ||
|
fc14f418cb | ||
|
b99253ff47 | ||
|
9cb844cbbb | ||
|
285aedef2f | ||
|
5121d64022 | ||
|
8b80910d70 | ||
|
a5ff655eed | ||
|
cc9c63c33e | ||
|
87eef72289 | ||
|
8e8ba3d052 | ||
|
fea27b900c | ||
|
7ad91a76cd | ||
|
a62b674722 | ||
|
350f35b08d | ||
|
f405321abc | ||
|
0d077f6ce5 | ||
|
dffa6accb0 | ||
|
b5abcd5ae5 | ||
|
72a9e676c1 | ||
|
3658b396d3 | ||
|
537acab16d | ||
|
8c6fe91c71 | ||
|
3c344331af | ||
|
d14ce2a37f | ||
|
33d272d4b0 | ||
|
57f5c15670 | ||
|
487faa69c6 | ||
|
9148a1e564 | ||
|
4c3f5e1e1a | ||
|
b0bf0824dd | ||
|
dea5991e01 | ||
|
739932a280 | ||
|
1f8bc5b490 | ||
|
753ffd401b | ||
|
0d1bab45a0 | ||
|
17cc439de3 | ||
|
5d03e300fb | ||
|
a20408bed1 | ||
|
ed0ccd6f13 | ||
|
bb1138efb5 | ||
|
82b36aaca7 | ||
|
688044429e | ||
|
7bbfc8e6d4 | ||
|
85513aa5c3 | ||
|
67254cc30c | ||
|
b055844973 | ||
|
9ba03848f2 | ||
|
5b96ab89fd | ||
|
6a4d8f7404 | ||
|
219d03b8dd | ||
|
523654f2eb | ||
|
b9b8cb9f63 | ||
|
94f2cd4257 | ||
|
26248774c2 | ||
|
99299ba06f | ||
|
6e42eaa26c | ||
|
ae7b621e3d | ||
|
f2ced20c42 | ||
|
e4f256d5cd | ||
|
ca1dac4cc3 | ||
|
8fc2729fab | ||
|
24c19efd52 | ||
|
a3edd829a6 | ||
|
9ec475fa40 | ||
|
aad06c583e | ||
|
f821e35cb0 | ||
|
14313291d5 | ||
|
b818352a04 | ||
|
c0c34fbb41 | ||
|
b372dc21d6 | ||
|
3d576cd06b | ||
|
438dcc4c6f | ||
|
de4ac2c830 | ||
|
f46443a5e3 | ||
|
69e90b7ff1 | ||
|
5089ec9826 | ||
|
372df93c18 | ||
|
1d2ddeedde | ||
|
4df2f1f756 | ||
|
f10c1c4730 | ||
|
92b556e54f | ||
|
b46ae7a651 | ||
|
9f3a3c5f51 | ||
|
b5071237fd | ||
|
1d2bebf17a | ||
|
9086822b94 | ||
|
d90d3c5a0f | ||
|
a3203e5775 | ||
|
5f24b41250 | ||
|
b577c0adb7 | ||
|
a9ad0fde9e | ||
|
9974b6070e | ||
|
bd5e4f3d94 | ||
|
e0adb1133d | ||
|
3cdb4f5b2a | ||
|
248401f534 | ||
|
1228a06a90 | ||
|
b5cd215643 | ||
|
7604667b55 | ||
|
3a278d8079 | ||
|
adcc484528 | ||
|
798553e96a | ||
|
068b717a75 | ||
|
785cf597ad | ||
|
ee70fe85c0 | ||
|
2e31816979 | ||
|
e4237c9511 | ||
|
0bc6967dbc | ||
|
2301769419 | ||
|
42c5f732a2 | ||
|
2428b564fd | ||
|
bb0e96a163 | ||
|
bb733c5811 | ||
|
ffeaf55c4e | ||
|
a12bb1d9ce | ||
|
313f2a667e | ||
|
d5d0be5824 | ||
|
3fa28bb46d | ||
|
eb90fab640 | ||
|
099d65032a | ||
|
e96feb36cd | ||
|
eb6d01c21e | ||
|
03d7dc8971 | ||
|
09d5f5a083 | ||
|
1a41b3fb64 | ||
|
f958550061 | ||
|
1e8e7ec4a4 | ||
|
83c4e38fa5 | ||
|
607d2fedb7 | ||
|
627ada56b7 | ||
|
9ce06fdc4e | ||
|
bb63ae6d87 | ||
|
a4182621da | ||
|
0534261759 | ||
|
c7baa66a4d | ||
|
1732606581 | ||
|
68cdd2c2c8 | ||
|
ea03b6c19c | ||
|
b83eb41df3 | ||
|
e6c68dc5bc | ||
|
76a953819e | ||
|
3a2ad48bd6 | ||
|
674d5bae8a | ||
|
5e983641b6 | ||
|
96d4665880 | ||
|
889ddf6a38 | ||
|
158e613e29 | ||
|
255c52db26 | ||
|
072c81177f | ||
|
e5c7fc93e2 | ||
|
5b7b217b9c | ||
|
06cedaef4b | ||
|
6972e8a3db | ||
|
18ba0148ae | ||
|
dea019ebdc | ||
|
e27e93aa9a | ||
|
c9ee7d477d | ||
|
e9deb13ce4 | ||
|
cdac238f6d | ||
|
e2c5e2c7fb | ||
|
0c3f819200 | ||
|
3673230fdf | ||
|
f2cb07ac95 | ||
|
484cf9d8a2 | ||
|
5b20be8cfd | ||
|
4dbe622a4a | ||
|
9a4dec57d1 | ||
|
f5c5178f95 | ||
|
727cf84080 | ||
|
80a257e85f | ||
|
ad3c15df9b | ||
|
c665bd2321 | ||
|
948bae9f95 | ||
|
a1c10b4ea3 | ||
|
f36df81d9a | ||
|
2fd9eb6c68 | ||
|
8894d14130 | ||
|
4039e74a82 | ||
|
0af3faf6ff | ||
|
0520b69c18 | ||
|
e11a775bed | ||
|
b4ed4623e1 | ||
|
9ee9653c7d | ||
|
e55a16d917 | ||
|
3458a0b22c | ||
|
ddcfa735e0 | ||
|
3370240541 | ||
|
c0cec4716e | ||
|
08b239e87a | ||
|
84132e794a | ||
|
425d70f261 | ||
|
420954ed00 | ||
|
45edd330f5 | ||
|
6a0e2bcad3 | ||
|
d67d3e0167 | ||
|
cd4f3d9a66 | ||
|
5c6db35c9b | ||
|
887bea4328 | ||
|
def5095d77 | ||
|
ab66662ff6 | ||
|
2d84433a62 | ||
|
b8e61787d4 | ||
|
669825a35d | ||
|
31b25ca169 | ||
|
a6ee92fbd5 | ||
|
5ff1a59a99 | ||
|
4f65eb4d65 | ||
|
39328c7368 | ||
|
2f5f3e1b51 | ||
|
022285806b | ||
|
bb60c2ac48 | ||
|
a4ee1e9805 | ||
|
bf0a8c1e62 | ||
|
d2b10ef4e6 | ||
|
9f3fca8fd7 | ||
|
9404819dbe | ||
|
d959f5096b | ||
|
bd3710a60f | ||
|
3d43f3a2b3 | ||
|
6194c156bd | ||
|
850c26dc13 | ||
|
eda0f7327e | ||
|
bf597495ff | ||
|
20025f254c | ||
|
ec76acd3a6 | ||
|
1e2acfb296 | ||
|
2bd4a680ad | ||
|
4ce504a1e1 | ||
|
d2f071b8b2 | ||
|
fdbd7b977a | ||
|
d19961b7a0 | ||
|
c156254600 | ||
|
9b5c6ece90 | ||
|
bf91efc756 | ||
|
a253fd5001 | ||
|
52af129c8b | ||
|
3942463ac9 | ||
|
ff572eef7f | ||
|
2740dfea87 | ||
|
324112b73b | ||
|
1d7dee8314 | ||
|
2d23819944 | ||
|
17f3b4125b | ||
|
c8a1024e24 | ||
|
9a2d2e2d89 | ||
|
b7af234427 | ||
|
a374e351e2 | ||
|
562f88555c | ||
|
167c5297fa | ||
|
b281d09694 | ||
|
853a0ac5ea | ||
|
ea948cfc3f | ||
|
fdd13390fb | ||
|
b2f6f8b3c1 | ||
|
cd12162b6f | ||
|
79717d1d64 | ||
|
e56cbf0baa | ||
|
05232414ad | ||
|
4bbc7d9662 | ||
|
3805b7f287 | ||
|
63620409a9 | ||
|
ba423a79e3 | ||
|
8806ba76eb | ||
|
9e73260230 | ||
|
c0125b83d1 | ||
|
1fa297fb73 | ||
|
57557748e2 | ||
|
8b79212a6e | ||
|
f4af4ec4dc | ||
|
2e150f4bf4 | ||
|
4f4aa051c9 | ||
|
da1dd7448e | ||
|
0fd47eeee0 | ||
|
54c9d7283a | ||
|
848db5f7de | ||
|
5fb32fe0e9 | ||
|
adf5b4ca0c | ||
|
16bfb1dbfe | ||
|
e5421b8a9f | ||
|
f9f1a22e3b | ||
|
9533809631 | ||
|
6d7c11f1b1 | ||
|
0286c72256 | ||
|
763aaa2926 | ||
|
ae4af7dd13 | ||
|
4ae2ea32e9 | ||
|
434298cba6 | ||
|
a2fa688cde | ||
|
895462ac7f | ||
|
e883714446 | ||
|
e1a235b4e8 | ||
|
ffa2c59df7 | ||
|
3f19dc55fa | ||
|
66c2148a63 | ||
|
28850f534c | ||
|
c40c11a822 | ||
|
b334e1aa00 | ||
|
b48986bfd6 | ||
|
ced63baed6 | ||
|
880635d615 | ||
|
d9f8c8d3b1 | ||
|
8155841a1d | ||
|
30f83d8f3f | ||
|
96c86160df | ||
|
b7ea128132 | ||
|
4bee8e9bfe | ||
|
bc195e771e | ||
|
0bc3e94052 | ||
|
3eb3523b52 | ||
|
8e2f84a989 | ||
|
143ec7463f | ||
|
4a5fd08e51 | ||
|
0306635a45 | ||
|
0a4d32cdb5 | ||
|
d590992d1d | ||
|
e8766946dd | ||
|
8c35189b37 | ||
|
e6390cde97 | ||
|
db976a6408 | ||
|
031c3ed055 | ||
|
cb391f08b9 | ||
|
5387a6287e | ||
|
0e4544b2da | ||
|
e0cbb7bede | ||
|
ed45b73274 | ||
|
cadcb586a7 | ||
|
9e31270459 | ||
|
dc07f046f2 | ||
|
5032bbafb1 | ||
|
1540bfb3a1 | ||
|
f3f5851118 | ||
|
9810edcd1a | ||
|
e334b9162a | ||
|
836c676057 | ||
|
1abadd9c5d | ||
|
e8bd1d8237 | ||
|
8a7470500a | ||
|
75689c665d | ||
|
d84f4f676b | ||
|
c97b859963 | ||
|
6b8f4ee1d5 | ||
|
3532789c35 | ||
|
267905b5e7 | ||
|
1626bd7a18 | ||
|
7106830be9 | ||
|
3b1946d65c | ||
|
6dedf4d44f | ||
|
fe5e2584b1 | ||
|
8fae38deca | ||
|
7f8e322e9c | ||
|
4d0f76f9e8 | ||
|
5d2b42960b | ||
|
0098dacdff | ||
|
51666fbf0e | ||
|
defb9120fd | ||
|
11ec72ce8c | ||
|
f67bd69ecc | ||
|
db2c29a6e1 | ||
|
e22e522245 | ||
|
7c8f4c0405 | ||
|
01ab21e4c0 | ||
|
534a2912e1 | ||
|
1456f4e227 | ||
|
63e11451ba | ||
|
7f80674cf2 | ||
|
16f4ca5fbf | ||
|
701ba59bd8 | ||
|
8c6705bccb | ||
|
e4542c4ac4 | ||
|
45eea1d6de | ||
|
4aa94d5a13 | ||
|
b0253e11bf | ||
|
1a4a3714c7 | ||
|
999090dbdb | ||
|
13e3b515c9 | ||
|
b6062a94b9 | ||
|
d0b26e9f69 | ||
|
d6ae34929e | ||
|
a51c9d2b2d | ||
|
6c45ccc73d | ||
|
6459c7bfad | ||
|
e6c1b0cf54 | ||
|
c8558810ad | ||
|
b0eb0b1de7 | ||
|
6bdf31efda | ||
|
3cd2971cec | ||
|
58784ebbfc | ||
|
de24a6e71b | ||
|
8c8a5a4f5e | ||
|
7f41a1ef09 | ||
|
15e1169f62 | ||
|
de92677b69 | ||
|
377ed95130 | ||
|
1b1c7ad3b1 | ||
|
e4e0fb0f35 | ||
|
bf0cbf24e4 | ||
|
7d454a4c7b | ||
|
680f1470cf | ||
|
1854fd307f | ||
|
6239f9da75 | ||
|
40e39b82e8 | ||
|
c71e671311 | ||
|
2ea6eb09e6 | ||
|
55c39a12bc | ||
|
14cb65eb6a | ||
|
a753459a6d | ||
|
bbce4451aa | ||
|
1e378dd986 | ||
|
3db2b60b92 | ||
|
2317a7df55 | ||
|
c53fcde12a | ||
|
4be5eb39ff | ||
|
5ef31a96ea | ||
|
0d017c30e2 | ||
|
92d85fa8a7 | ||
|
c2a0daedeb | ||
|
51642bc4cc | ||
|
c821208b5d | ||
|
a3d536bd51 | ||
|
f7262d12e6 | ||
|
bec888da19 | ||
|
2e6246e385 | ||
|
a9a0bf01ad | ||
|
8dd3c76fa4 | ||
|
a7c22399d8 | ||
|
04cb8a2fe0 | ||
|
494739f771 | ||
|
6fbcc9a244 | ||
|
d016f59867 | ||
|
88f8f3938c | ||
|
1322fd97f6 | ||
|
7f617df4e8 | ||
|
b02542f3be | ||
|
5fd620556e | ||
|
218d64d8df | ||
|
e8291eb00e | ||
|
f8619e382b | ||
|
83c35328ed | ||
|
f548d78907 | ||
|
5aca8b41af | ||
|
a7b6f3fa19 | ||
|
afb989d72e | ||
|
12c96c7a74 | ||
|
79bfffd77b | ||
|
58d68e7e31 | ||
|
499352e51c | ||
|
03fbd51fab | ||
|
bd73cac1a1 | ||
|
0c43fda86d | ||
|
a64b8fb310 | ||
|
316d39f24c | ||
|
265e2930f1 | ||
|
9511cf8d8a | ||
|
b86f0a7d9c | ||
|
19be847624 | ||
|
15e45ac4ec | ||
|
86572635df | ||
|
4a5c51d7a3 | ||
|
8530cf3535 | ||
|
eec54831ef | ||
|
a41e98910d | ||
|
be985466a3 | ||
|
98fb46957a | ||
|
2abc35058b | ||
|
35f97a6013 | ||
|
b8d509eb12 | ||
|
d6f13be95f | ||
|
4dad27bb76 | ||
|
efe1686c05 | ||
|
09462e6877 | ||
|
612a0397a7 | ||
|
3576ebd14f | ||
|
2a190d579c | ||
|
67abc107c5 | ||
|
9ec2052428 | ||
|
657423207b | ||
|
19aaff2345 | ||
|
8873c51f2b | ||
|
2ba24ba56b | ||
|
52f5d21480 | ||
|
0405cda9d6 | ||
|
b422e79896 | ||
|
181d4d5ea4 | ||
|
f30ddbf175 | ||
|
2c3b8d8925 | ||
|
2e2c6aef83 | ||
|
e511538ba6 | ||
|
c12e08ef01 | ||
|
0970944ee4 | ||
|
4eabe91cee | ||
|
cf747f1e07 | ||
|
e5a1da7136 | ||
|
27b6cf436b | ||
|
e4866a8265 | ||
|
d90cc02e5a | ||
|
23a1a8e3f5 | ||
|
b8f1c0df09 | ||
|
ed1f249aaf | ||
|
eef7539c2d | ||
|
1bb35bf545 | ||
|
2b9cb4a257 | ||
|
209cff8888 | ||
|
9e253ac7a3 | ||
|
27c5b16957 | ||
|
2f1df3be7d | ||
|
bb4a28ecd8 | ||
|
54c20b26cc | ||
|
4a71b952b6 | ||
|
3d7f628014 | ||
|
c63529ea99 | ||
|
d43bdcc1a2 | ||
|
a4aa6c5ab9 | ||
|
6d3e1bb40a | ||
|
9428347cb6 | ||
|
618666abf1 | ||
|
9423bc4ea7 | ||
|
95b9e4dfd9 | ||
|
61c6a2ab57 | ||
|
c89f7aaaed | ||
|
c2d72e71aa | ||
|
af93cf2adb | ||
|
7a75a8c254 | ||
|
0aa7dacbca | ||
|
9d41a52d3b | ||
|
c943303a45 | ||
|
b2a5b3c3c4 | ||
|
495d4b82cf | ||
|
ea2a200302 | ||
|
c531b26821 | ||
|
cade6c6c38 | ||
|
c56821300a | ||
|
346821f0d6 | ||
|
0da97c5da3 | ||
|
f0e013e1f8 | ||
|
8a144f3c35 | ||
|
f48bb5a40a | ||
|
3abde67671 | ||
|
59b2e2dba1 | ||
|
479f21f4f3 | ||
|
4a0f10ea99 | ||
|
9a24e4ade1 | ||
|
4691e896a1 | ||
|
869411a977 | ||
|
68bd40d2a4 | ||
|
cb8fe24a77 | ||
|
eb65949b69 | ||
|
fdf6f68624 | ||
|
d0e01768ab | ||
|
fd3d389557 | ||
|
3d63e12c9e | ||
|
ac6770bdff | ||
|
2ad2ce6c3b | ||
|
fcbe1dd8eb | ||
|
df618d1aa2 | ||
|
b846f16e6c | ||
|
51ab9746de | ||
|
ae10d0c7fd | ||
|
a443426d83 | ||
|
f2aa4d4484 | ||
|
4579dc9385 | ||
|
8ef8c97072 | ||
|
84fcd1c1b5 | ||
|
d0e6bcd784 | ||
|
1142ff884e | ||
|
29080e9d7d | ||
|
04c3191795 | ||
|
9d75225bd5 | ||
|
ce85bd26df | ||
|
ba0e46b465 | ||
|
5616b7550f | ||
|
fcd6159b42 | ||
|
02e5e1bc1e | ||
|
6d83a00728 | ||
|
d697c2ac9e | ||
|
c917aec401 | ||
|
89c1b94a12 | ||
|
46b9760179 | ||
|
ff66e66f21 | ||
|
b1f62f74cd | ||
|
a0d0f1f98b | ||
|
f08f064bc6 | ||
|
20d653798c | ||
|
a9e08dd587 | ||
|
5dba4a2201 | ||
|
c964870416 | ||
|
cfbf081cac | ||
|
bad429e853 | ||
|
8ce7466dc1 | ||
|
1ffa067d5f | ||
|
b595763446 | ||
|
e7180ac82a | ||
|
1d3c4b6f90 | ||
|
891809a13a | ||
|
67eeb4b69a | ||
|
f6b157167d | ||
|
d768314f03 | ||
|
3ede0a50f0 | ||
|
8828251204 | ||
|
019788670e | ||
|
3285f24fe9 | ||
|
c7d0537bf9 | ||
|
2254a5960e | ||
|
af4a06f91d | ||
|
3e95b59be8 | ||
|
bb24ec4a9f | ||
|
5a4b675791 | ||
|
5328303c32 | ||
|
3f6212e799 | ||
|
1803d3ee2b | ||
|
421a2b0cd9 | ||
|
533ae0ea89 | ||
|
df5add04e2 | ||
|
174e743904 | ||
|
76a6d94bbe | ||
|
6d76771b16 | ||
|
fca066fe52 | ||
|
aa3c0346c8 | ||
|
bafec0d974 | ||
|
f3f605a90f | ||
|
a9a9f26aa0 | ||
|
4eb143c265 | ||
|
e597b2177c | ||
|
2ae4501de6 | ||
|
f1029fceff | ||
|
b67d3a504b | ||
|
12a5998a07 | ||
|
fbe2096098 | ||
|
d7faa0a0fd | ||
|
8bd54493a3 | ||
|
225243ba93 | ||
|
21bd35accd | ||
|
4acca4c6f5 | ||
|
c106ff8290 | ||
|
296ba4606d | ||
|
54dd602be4 | ||
|
9cac8d36ad | ||
|
d407e9ffba | ||
|
26025b564e | ||
|
4cacc2a9e6 | ||
|
16ef405670 | ||
|
642aad28ac | ||
|
e5feda69c8 | ||
|
aa89d1dffe | ||
|
662f269a94 | ||
|
6f80100ee6 | ||
|
38d3154ddf | ||
|
e16a718bde | ||
|
a40429e219 | ||
|
e5602a86a2 | ||
|
7d4d8bff93 | ||
|
d028ebfdf1 | ||
|
a5aeb2a3c1 | ||
|
410bd5ab9c | ||
|
f4823d962a | ||
|
9aa35f3488 | ||
|
d2a7e5dc4d | ||
|
f7cb44c343 | ||
|
ae67cc8a51 | ||
|
454d628f93 | ||
|
e60e8dc9ba | ||
|
382fe5cd47 | ||
|
86e53552c0 | ||
|
6296d63197 | ||
|
ed1a7bb887 | ||
|
273bc78db3 | ||
|
261a3ecee3 | ||
|
8f0ac61634 | ||
|
5a7b275ee3 | ||
|
54e106ec74 | ||
|
51690b47a3 | ||
|
9ab129ba89 | ||
|
b39a67534c | ||
|
2d717cbf01 | ||
|
c4f284aaf1 | ||
|
e40a9089e0 | ||
|
8cf849e590 | ||
|
03e113b063 | ||
|
d9305b1e63 | ||
|
182fa7d803 | ||
|
4dd41cffe3 | ||
|
9bf3df1e87 | ||
|
2e08c29c64 | ||
|
75afd30008 | ||
|
7652d2c7e3 | ||
|
7e6a2d08e2 | ||
|
c1dbe235dc | ||
|
eb2ccf06a5 | ||
|
e202f24592 | ||
|
d11f87f0cc | ||
|
8f54ec4f4a | ||
|
64534efe71 | ||
|
1235ced355 | ||
|
23d383be67 | ||
|
fb90ff2fbb | ||
|
4706aa95e6 | ||
|
8981414705 | ||
|
7ec978fcdb | ||
|
d58803a058 | ||
|
fe6b7dc1e3 | ||
|
c9f0988b95 | ||
|
cd9a52706c | ||
|
bfdb1b4af9 | ||
|
e5174b4a29 | ||
|
8feb60c30d | ||
|
6bd7fb64ab | ||
|
b9602243d3 | ||
|
b7a930ef18 | ||
|
add206ae2d | ||
|
28ce095fb4 | ||
|
13952a4b79 | ||
|
05ec398346 | ||
|
dea2dd52be | ||
|
debe3cda4b | ||
|
5b5aa2c721 | ||
|
e201ddd74c | ||
|
4a1580169d | ||
|
001ca7de60 | ||
|
184be4e27b | ||
|
7652645dda | ||
|
ef6016857b | ||
|
e449ca2c95 | ||
|
b0f00773d6 | ||
|
451eee549b | ||
|
77da261fea | ||
|
e23c9ee608 | ||
|
37cb87a9c6 | ||
|
211db8e0f0 | ||
|
d074d60dad | ||
|
0bff5e2236 | ||
|
dca986eb2e | ||
|
326eb1135b | ||
|
b2f7b4e6b9 | ||
|
5129d6aa6b | ||
|
a8cf334616 | ||
|
3b4d06a1e5 | ||
|
c7969476b0 | ||
|
7bf24df03a | ||
|
5ef41294e4 | ||
|
2eb68655c7 | ||
|
23049439c0 | ||
|
ce6cd40b92 | ||
|
c36b0c16b0 | ||
|
c15f182377 | ||
|
165cb345b4 | ||
|
9b6d2beb4d | ||
|
722c39a6ff | ||
|
e344749d2f | ||
|
fe782bc3b6 | ||
|
d372cbad74 | ||
|
1e1198f4ec | ||
|
5effc96cff | ||
|
b8d4a1be05 | ||
|
36a25e6efa | ||
|
1240e7914d | ||
|
e4683b1ea1 | ||
|
749c22a0c3 | ||
|
6867774627 | ||
|
80944972e9 | ||
|
c1f608c0d8 | ||
|
4dfbb08db3 | ||
|
ead1dffd98 | ||
|
5a16e4d132 | ||
|
a89491e343 | ||
|
b1b00ae886 | ||
|
cde5bd87d8 | ||
|
3231d5d179 | ||
|
03e49ea2bf | ||
|
7d3eef092c | ||
|
30d0410b49 | ||
|
eb2a887f77 | ||
|
e77b8d29cf | ||
|
490ec299c5 | ||
|
e47c2aa24d | ||
|
3eb9fdca6a | ||
|
a4173a93b7 | ||
|
ad762cf239 | ||
|
5a478607dc | ||
|
4abc6eb387 | ||
|
c313bba288 | ||
|
73eaf97afb | ||
|
8d25df5d4e | ||
|
4a05f16050 | ||
|
3ef1423263 | ||
|
898652189b | ||
|
2976edf333 | ||
|
248be11e4d | ||
|
19ec8c36e2 | ||
|
90d989e358 | ||
|
19eea3a615 | ||
|
7b81b9786d | ||
|
292d302a3d | ||
|
557594e34d | ||
|
48393c3765 | ||
|
022cd666eb | ||
|
2d0eedb132 | ||
|
5d3d664ce6 | ||
|
d1c12d8294 | ||
|
947a67fcd2 | ||
|
9b9540b857 | ||
|
a3b748ffe3 | ||
|
9a3a7a3444 | ||
|
5c8ca15ee2 | ||
|
cd3807a3d8 | ||
|
2a884d6f38 | ||
|
6efeb60c41 | ||
|
dcdab5b218 | ||
|
288fbda54f | ||
|
03bc91fd1e | ||
|
1c5cf29540 | ||
|
631ddc0af2 | ||
|
1d593df5af | ||
|
1d3a8bb7bf | ||
|
0dc9c183b5 | ||
|
05ab43f309 | ||
|
f4153ade92 | ||
|
44e84b46b8 | ||
|
3fe71e7bdc | ||
|
5d4d8a3422 | ||
|
f06199230c | ||
|
da9a6b835a | ||
|
0afc2cd2cb | ||
|
9e2f7fb048 | ||
|
3fa694c65f | ||
|
300d84c5d8 | ||
|
efed00f58b | ||
|
99c4ebe046 | ||
|
699231fd92 | ||
|
c18f112f31 | ||
|
605d04580c | ||
|
143211f276 | ||
|
4ca152da7c | ||
|
1c1f69fa50 | ||
|
147ccd6c96 | ||
|
c8b9906ef3 | ||
|
cd94a9fac1 | ||
|
20a9472ee2 | ||
|
c652a2f122 | ||
|
ce174d507d | ||
|
f66b1b644f | ||
|
5b460f0b4e | ||
|
ab8d116f11 | ||
|
0535e9c68f | ||
|
93b52fbdb3 | ||
|
aab1eb0d36 | ||
|
c07fcc171c | ||
|
8e5b7d2578 | ||
|
37b1e9037f | ||
|
7c0999ac9f | ||
|
5ce1c9d77f | ||
|
14dcb58afa | ||
|
04068025c6 | ||
|
2cd0c7a407 | ||
|
f3592c4782 | ||
|
1e5934895d | ||
|
0f5d932726 | ||
|
4125726be9 | ||
|
bcb692caf0 | ||
|
ff9a1a1f55 | ||
|
faef6d82ff | ||
|
d3a123373e | ||
|
7c66f024ed | ||
|
20c7259174 | ||
|
16a4c072a9 | ||
|
254e8d48f2 | ||
|
52fe374aaa | ||
|
f776725096 | ||
|
6e864c2631 | ||
|
4221554dc8 | ||
|
6b50fdae43 | ||
|
7bae6b2e8f | ||
|
5c6a0f22c0 | ||
|
a9b755fa1d | ||
|
72bdf9e0d7 | ||
|
cbf8b23385 | ||
|
4aacd0d23a | ||
|
69202ed752 | ||
|
aa762821ce | ||
|
8a7bb6bc5a | ||
|
35cb4d4cc3 | ||
|
8ceda3eab1 | ||
|
cc1233e8d5 | ||
|
70883c0869 | ||
|
fe9de98dd1 | ||
|
ecde91ff25 | ||
|
240ad49a43 | ||
|
10adb23e71 | ||
|
c2fc099439 | ||
|
723a38da68 | ||
|
74037fd605 | ||
|
e917796fce | ||
|
5661798601 | ||
|
a8adac9c5a | ||
|
c62018f984 | ||
|
06f2d4425c | ||
|
090da6cfb6 | ||
|
28dbf10a31 | ||
|
7a66d0feda | ||
|
e23ddf118e | ||
|
11d6588249 | ||
|
30db0cd4f4 | ||
|
692b57a1ad | ||
|
7505c65401 | ||
|
54a735ffd9 | ||
|
6a20d9036c | ||
|
c338846d63 | ||
|
c419ad68bb | ||
|
51c486c15a | ||
|
306ff3d919 | ||
|
d667b8ae8b | ||
|
72d728eb47 | ||
|
801ab862a3 | ||
|
d7b4dd2d4c | ||
|
1d3f74c8bb | ||
|
c3f73c0de3 | ||
|
1fc2fc7a11 | ||
|
13ea1898b6 | ||
|
586303f47e | ||
|
ce86a838f2 | ||
|
21e69251e3 | ||
|
a42323a527 | ||
|
14da3023d8 | ||
|
7eb5f57478 | ||
|
2cb443df8a | ||
|
961769ebd0 | ||
|
3266cc8e3b | ||
|
3dec14b89d | ||
|
a67f989dda | ||
|
2d05464b17 | ||
|
a39082e565 | ||
|
d1f567eade | ||
|
b9fdd54538 | ||
|
7243aec213 | ||
|
1fb582c35d | ||
|
c6da0409b0 | ||
|
50d4b55f73 | ||
|
c6a7e90c1a | ||
|
5cbc2e96e7 | ||
|
23a96c07ae | ||
|
7a787fc945 | ||
|
ce43de0c57 | ||
|
eaf47d3739 | ||
|
5eebdb6774 | ||
|
51db1593d6 | ||
|
223c71ce8b | ||
|
127afdb5c1 | ||
|
f34706f9f0 | ||
|
f76ce54109 | ||
|
f67fa6a5d6 | ||
|
5ad6d77973 | ||
|
69d1acc797 | ||
|
0c0235a56e | ||
|
7b0558dc8e | ||
|
77be2fa9d5 | ||
|
8f320337e0 | ||
|
2327b97fa3 | ||
|
a411a6a81a | ||
|
d66f1f5513 | ||
|
7503fde48c | ||
|
20c8916610 | ||
|
5a2a933b64 | ||
|
ffc0e996db | ||
|
a02ad104ee | ||
|
625876ff07 | ||
|
b935275fd9 | ||
|
4c032e9e6a | ||
|
1b770c66cd | ||
|
96259dfb49 | ||
|
e4d9e3e22e | ||
|
fbfab88257 | ||
|
708f8b49df | ||
|
323e5e14a3 | ||
|
5bbaa48b49 | ||
|
cd91e48713 | ||
|
7444bd06e4 | ||
|
0017e687db | ||
|
5865fd5022 | ||
|
4b941a5145 | ||
|
3c5d809cf9 | ||
|
ba101015f6 | ||
|
6999abe1ca | ||
|
cb295e20d4 | ||
|
c6df43363f | ||
|
81cec36b68 | ||
|
47c1ffe53f | ||
|
90a9051827 | ||
|
8bea3dd21e | ||
|
e164b1f169 | ||
|
383765f5fe | ||
|
25a59fff4a | ||
|
03af471f65 | ||
|
d189deefb4 | ||
|
e957180618 | ||
|
47d8d3cdb1 | ||
|
f364451a96 | ||
|
2c51b31c62 | ||
|
0dcf240faf | ||
|
88bc1c64d2 | ||
|
84bd8a6e98 | ||
|
0d657fc646 | ||
|
64b85ac1f9 | ||
|
81c1cc6728 | ||
|
098879585a | ||
|
fc91077430 | ||
|
b71a04943b | ||
|
dd4fb68e5b | ||
|
a085a2a557 | ||
|
fa84e34def | ||
|
8c3f377fc0 | ||
|
f9d14c64ce | ||
|
fc687f9fc0 | ||
|
57071d7bc8 | ||
|
324a246189 | ||
|
a706f81760 | ||
|
ab3aab9c22 | ||
|
fd2e55e444 | ||
|
04b8eafacb | ||
|
e2d0b7c5f7 | ||
|
db1a124ffb | ||
|
0dcfbe8581 | ||
|
0e1b872777 | ||
|
ed5b159fb6 | ||
|
9b7ca76b99 | ||
|
eeebb99ca3 | ||
|
e2b18cf9db | ||
|
955ba91770 | ||
|
c0e9f91bdc | ||
|
259f0b5aad | ||
|
02419dcdd1 | ||
|
4a66c91cac | ||
|
48ac996d77 | ||
|
82b4debcac | ||
|
5f5f71bf37 | ||
|
ae33fc3031 | ||
|
f63a9ed1a0 | ||
|
9843f66bb6 | ||
|
b2c1846ebb | ||
|
8e5a9251d6 | ||
|
3a59e2a5c4 | ||
|
38ff3e5e89 | ||
|
1f0653985a | ||
|
a4fa61e5f6 | ||
|
b68eae6f7d | ||
|
2d7c5cabab | ||
|
42e731369e | ||
|
7d2627551b | ||
|
f4bde164bf | ||
|
bb751793c8 | ||
|
c68141119c | ||
|
163d1a195d | ||
|
6de4f6a3ac | ||
|
89a52703f6 | ||
|
4f7eeea14e | ||
|
bbddd72780 | ||
|
ac099aa513 | ||
|
ece5401121 | ||
|
fd7b11518c | ||
|
9a70b4487c | ||
|
19af02e71b | ||
|
619aa05e14 | ||
|
cc60958b30 | ||
|
bd2f35a241 | ||
|
cf78987fab | ||
|
f8e6b51e9d | ||
|
2b1436e303 | ||
|
cf772bb9e0 | ||
|
fd75008499 | ||
|
e113c12768 | ||
|
3acda5b342 | ||
|
bbea9d7a27 | ||
|
b0125adfe9 | ||
|
d3b28a98be | ||
|
c679654ada | ||
|
26f3cffe5c | ||
|
8f0d82d219 | ||
|
6fec929656 | ||
|
ec68d2a0e6 | ||
|
f6afb9a3f0 | ||
|
70bb6d999e | ||
|
e9074a8ec1 | ||
|
9b730e784f | ||
|
57852821f5 | ||
|
87061ecfc0 | ||
|
d555d2f3f6 | ||
|
3b3fac98ad | ||
|
285a30f67a | ||
|
fa83304697 | ||
|
e42ef4b2f8 | ||
|
cc3908cdb3 | ||
|
e3def45c83 | ||
|
4fe4cdb7bf | ||
|
11a9b10a54 | ||
|
5164402e27 | ||
|
20f32e94a8 | ||
|
35aad4ebf9 | ||
|
d60e4f8fe4 | ||
|
e5a5eb3547 | ||
|
fbb4b13898 | ||
|
573f1ddf76 | ||
|
ead0f284f2 | ||
|
6ff659fb99 | ||
|
194c0f14cb | ||
|
7ab78fff3f | ||
|
eabe24bb3e | ||
|
c01fae5fcd | ||
|
c088e0fd55 | ||
|
cb1dbe7662 | ||
|
03f8d6693d | ||
|
6813a6ea63 | ||
|
4cd18a1b28 | ||
|
90907c6306 | ||
|
c67b2be9d2 | ||
|
8d6c5dcd65 | ||
|
e5fa6fa55a | ||
|
748c92436f | ||
|
e98c6e69d4 | ||
|
c389aa0b99 | ||
|
8696710ad1 | ||
|
ce3bf91b29 | ||
|
2f5f397d33 | ||
|
b196bad75f | ||
|
b97dfa841a | ||
|
31cf2671f5 | ||
|
56c8e43ce8 | ||
|
d7dc70c679 | ||
|
ed0d7b75b5 | ||
|
7649f88173 | ||
|
e9b8035eae | ||
|
46486704ab | ||
|
a6cee23591 | ||
|
33b5ade2b1 | ||
|
0299eb1d24 | ||
|
4f9ea39554 | ||
|
a2a49636e0 | ||
|
609fd131eb | ||
|
08a3fb76fb | ||
|
d5c38ef336 | ||
|
03d2b6eabe | ||
|
cbcd59c996 | ||
|
c5dc7475a6 | ||
|
40dff0381b | ||
|
39a1834a75 | ||
|
84694d55ad | ||
|
605920b421 | ||
|
c2a420a291 | ||
|
037a77a573 | ||
|
b366e5dbf7 | ||
|
a277072968 | ||
|
5523b554f1 | ||
|
6ae9dee982 | ||
|
dae4577b43 | ||
|
6ac9c74028 | ||
|
a7ef9c7c27 | ||
|
08f57558da | ||
|
28694859c9 | ||
|
be7cef29d8 | ||
|
e914c84ad3 | ||
|
193b209c92 | ||
|
d5b25dde3d | ||
|
931aa297cd | ||
|
fa10249893 | ||
|
14a708b3df | ||
|
d9f535f98a | ||
|
e51cb43283 | ||
|
bbcdd5ed0d | ||
|
3c13d6c195 | ||
|
61d89d1777 | ||
|
bede4a3a42 | ||
|
befa436087 | ||
|
59cf53e083 | ||
|
76a4e9e520 | ||
|
af635cdc0d | ||
|
cbf3b3e9ea | ||
|
b0be248ca5 | ||
|
2828ec76b4 | ||
|
de1f94a596 | ||
|
d16d4d83ea | ||
|
ff8adbbd37 | ||
|
a47ba2df3c | ||
|
909fc71543 | ||
|
7f13f6f915 | ||
|
f58fc9b531 | ||
|
82649dad39 | ||
|
7e3cda904a | ||
|
c921b2ca7b | ||
|
2afd02152d | ||
|
f3e9e2ec5f | ||
|
d39ae86915 | ||
|
51f0c2a5f8 | ||
|
003927418c | ||
|
da92cabe4c | ||
|
45a58e1a39 | ||
|
0e1a6a3d5f | ||
|
34969519d4 | ||
|
4f235e1d62 | ||
|
7d0dc49435 | ||
|
8fed8f77ad | ||
|
1c2728bbfb | ||
|
fd543bcee1 | ||
|
24b7705094 | ||
|
6b96f9b446 | ||
|
933dc6be31 | ||
|
d6e3fb46ee | ||
|
efc05edca3 | ||
|
aa3d384f47 | ||
|
a5aa5cf059 | ||
|
ccd8859d7f | ||
|
a28399e31d | ||
|
61f63a9996 | ||
|
de93c5c9d6 | ||
|
94865815c7 | ||
|
33754933d5 | ||
|
560b6db480 | ||
|
4c71167535 | ||
|
707484709a | ||
|
274be7c1bc | ||
|
3f176a6b6b | ||
|
fc8a5ff95f | ||
|
25f84d000b | ||
|
31b7826dce | ||
|
2d4aa52fa5 | ||
|
eee8008bb2 | ||
|
a5ae509f9f | ||
|
8f117b5079 | ||
|
0b5d0349d4 | ||
|
8a0660cbd6 | ||
|
f7a0b91ec1 | ||
|
1ecd1c1e54 | ||
|
fccbbb6fb7 | ||
|
d1886b039e | ||
|
79c61f01c8 | ||
|
e50c9266b4 | ||
|
fd27bd94e2 | ||
|
05f99f3855 | ||
|
31ef763c05 | ||
|
c0abcbea24 | ||
|
a73383cd87 | ||
|
951bfeefb1 | ||
|
fc7125b8cd | ||
|
26bcdbc766 | ||
|
fb1fcbe0b9 | ||
|
2c45f803e4 | ||
|
fbdd2fc470 | ||
|
0558631982 | ||
|
63e1c5807e | ||
|
a3cc1f2ef0 | ||
|
5318684e5c | ||
|
b3b9651cd8 | ||
|
86e528e5df | ||
|
c46a69e1bd | ||
|
9b0d1a23dc | ||
|
db038723f4 | ||
|
a193e1cbf3 | ||
|
493f1b98c2 | ||
|
926b60df3d | ||
|
d8a162fb6e | ||
|
4cf3249e0b | ||
|
407f26b1dc | ||
|
0159588eed | ||
|
6f7be7eb09 | ||
|
09a53718bb | ||
|
5655f22397 | ||
|
791d0abb34 | ||
|
13d9930955 | ||
|
c4f40d68e9 | ||
|
9951370321 | ||
|
0119ad452b | ||
|
3d3016fdca | ||
|
262798d577 | ||
|
cf206e64a7 | ||
|
5ac4135a13 | ||
|
5176eaf4ba | ||
|
514417e888 | ||
|
f83e85dc36 | ||
|
9592a77cff | ||
|
33a893ba31 | ||
|
f89cdadde8 | ||
|
7d2aa28e1f | ||
|
9df4429fc2 | ||
|
843a2491ef | ||
|
d64fb15ac2 | ||
|
3671e7f18c | ||
|
899bf98f45 | ||
|
4230ba513f | ||
|
18f1b4d8c1 | ||
|
75776687bc | ||
|
b5ebd14589 | ||
|
67ba64b0a1 | ||
|
b26e8311c1 | ||
|
0033aab03e | ||
|
1037fe0b14 | ||
|
6c688b9684 | ||
|
601e17ed0f | ||
|
ad86c16bc9 | ||
|
791db983c7 | ||
|
d7a7382d00 | ||
|
e842a00402 | ||
|
78a8c8c1be | ||
|
a7c093a0eb | ||
|
86956c1e7b | ||
|
54539001f1 | ||
|
4321cbf41a | ||
|
8d9941bfd2 | ||
|
f9e38deee7 | ||
|
89fd044b00 | ||
|
039f88d14c | ||
|
d3f9eb38a9 | ||
|
768d97ac6c | ||
|
154078d46f | ||
|
d74c6a30c8 | ||
|
1407d5be8d | ||
|
721c06e157 | ||
|
a0265f18d9 | ||
|
8948475cad | ||
|
faed5349fb | ||
|
7bcaf956e7 | ||
|
31c2a80758 | ||
|
150e4b842c | ||
|
b970f64639 | ||
|
08bd13b2cd | ||
|
6e3d6125c2 | ||
|
143d5f69c1 | ||
|
156ddd24fa | ||
|
cf2c147f4f | ||
|
ce1903b2fa | ||
|
ccdfe5ac86 | ||
|
b0ef98dd63 | ||
|
5c8e62bd90 | ||
|
6674d76d6d | ||
|
dea747a9a9 | ||
|
a32ace1dcb | ||
|
4785f0a4dd | ||
|
8249d8a0f5 | ||
|
234bc30369 | ||
|
b7a081b9a4 | ||
|
88c925017d | ||
|
a3c2a9ac61 | ||
|
7cad6302b7 | ||
|
4b34090376 | ||
|
ac554a27b6 | ||
|
e1ea7200cf | ||
|
802fec6bf3 | ||
|
67aee9bdc6 | ||
|
975ad2f8ab | ||
|
b0814166f5 | ||
|
8f397865fd | ||
|
8ffa7525c5 | ||
|
6048a26511 | ||
|
1a8a7fc27f | ||
|
64901dfc22 | ||
|
6a28497d33 | ||
|
ed1ec2300d | ||
|
edfde494fa | ||
|
a3cc573e4d | ||
|
44fe70ae44 | ||
|
d05a9295b3 | ||
|
7aa4cd8104 | ||
|
4a6088b6b9 | ||
|
d949680d5a | ||
|
0cd7380af0 | ||
|
a3afcd2a6e | ||
|
bd6c7a8c3d | ||
|
920955657d | ||
|
d975ea1509 | ||
|
ba5c49a8df | ||
|
9f6c7180b2 | ||
|
ef70f4d547 | ||
|
8c061b1f07 | ||
|
0aed8fdb5b | ||
|
6125a99381 | ||
|
83b54b7be1 | ||
|
047222b3ee | ||
|
2fe1ab83f5 | ||
|
5372058c33 | ||
|
a08261a798 | ||
|
869985f005 | ||
|
97b5c473c1 | ||
|
337db3daab | ||
|
10da82607d | ||
|
7f49824783 | ||
|
5d8bc73063 | ||
|
82675384c4 | ||
|
3dc663f4d8 | ||
|
2b0fcdf95c | ||
|
d0120f1427 | ||
|
ac34109da3 | ||
|
d5c96eee32 | ||
|
7b252369e9 | ||
|
9d48358f2a | ||
|
2dcd7db797 | ||
|
c267cf0e9c | ||
|
403820cf14 | ||
|
6d667e2d78 | ||
|
258a19fdf5 | ||
|
1a593a1ced | ||
|
b8ac57a5b3 | ||
|
b93f3ff445 | ||
|
5984edb7f4 | ||
|
603bd2692e | ||
|
2e61bdf88f | ||
|
e6aa73197a | ||
|
b2f9353be1 | ||
|
5cba59932b | ||
|
809340e629 | ||
|
bb6a188883 | ||
|
24674d354c | ||
|
e93562b1db | ||
|
f7099cf6aa | ||
|
d88f012d82 | ||
|
f96767d3dc | ||
|
6666786b7a | ||
|
eccbe8e018 | ||
|
aac87539ae | ||
|
a8995d2bed | ||
|
7f40698bba | ||
|
c4f4c3138c | ||
|
3b9c7db481 | ||
|
649f650da6 | ||
|
4c83265de4 | ||
|
ad7b62fa3d | ||
|
650df97e50 | ||
|
6f75125cf5 | ||
|
26c05a8d5f | ||
|
fc4e47cec6 | ||
|
c39f1341aa | ||
|
a39d1e0886 | ||
|
435f51a777 | ||
|
de2c12c1df | ||
|
ae43af999b | ||
|
14a10eeef8 | ||
|
180be49824 | ||
|
5faa756f1c | ||
|
06db29dd43 | ||
|
6d317937c7 | ||
|
522c990278 | ||
|
e584ebe7de | ||
|
1422bd8540 | ||
|
fe18e71538 | ||
|
e5699f674b | ||
|
203db44b4e | ||
|
69c5f5b9e5 | ||
|
5a16dfced3 | ||
|
cb46ef9e6c | ||
|
3b810e9b8d | ||
|
663dcecb8e | ||
|
fbb5671f89 | ||
|
47d348359f | ||
|
ab2f460f35 | ||
|
c77e3a9396 | ||
|
014c43f79b | ||
|
21fb3813dd | ||
|
9a039a747a | ||
|
17578a4615 | ||
|
abb68d6f59 | ||
|
3c443344a5 | ||
|
2887017d12 | ||
|
f2286fb1be | ||
|
bdd11a14ff | ||
|
18f6e5af4d | ||
|
83b07e0caf | ||
|
840a5ac4b4 | ||
|
4923311168 | ||
|
748423dfe8 | ||
|
2f4e610900 | ||
|
d39ec97b9a | ||
|
0d08bd3ad1 | ||
|
3343738bb3 | ||
|
373b0b7850 | ||
|
aa24da72b9 | ||
|
aecc5f3c6e | ||
|
3d4ef48ceb | ||
|
9bb74a17d8 | ||
|
b654dfb237 | ||
|
1bca8c81a4 | ||
|
4d35fd4ab3 | ||
|
bf5ea23bc6 | ||
|
b4c1f695a8 | ||
|
de3d966835 | ||
|
bcdb1ab1d8 | ||
|
63944792b0 | ||
|
3895b133a3 | ||
|
4371b81ef3 | ||
|
cd93a5ab6b | ||
|
d9a8443081 | ||
|
918d3b46f0 | ||
|
f53597845e | ||
|
e65a3efc3c | ||
|
d74fcad9f4 | ||
|
19b88fd986 | ||
|
12105ab85c | ||
|
723817e3f8 | ||
|
101fc51787 | ||
|
58a5f88943 | ||
|
2e35d0e178 | ||
|
319efbeb4b | ||
|
04e6833e22 | ||
|
748c882ba1 | ||
|
ab7d2959ea | ||
|
56d3485d49 | ||
|
119ab7b2c0 | ||
|
d1ea4e4fa4 | ||
|
de5ab80038 | ||
|
2d23f3e5d4 | ||
|
77d1580ee3 | ||
|
a20906bc12 | ||
|
ffbf70d72b | ||
|
caa3ff616c | ||
|
7a102ee920 | ||
|
8451f34302 | ||
|
e4bd8e3106 | ||
|
bc4945c584 | ||
|
fa96deb1de | ||
|
a27217dc62 | ||
|
2dfb637e2f | ||
|
9387c2c771 | ||
|
e7e8ed55c2 | ||
|
cf067ca51b | ||
|
1b7517c05c | ||
|
103b3b916b | ||
|
fa91174b1a | ||
|
10e3595a82 | ||
|
965beebc66 | ||
|
83ab1a3b7c | ||
|
7ac83575d4 | ||
|
831f73d715 | ||
|
245b4ebe63 | ||
|
060f30d0bf | ||
|
f84b2c5160 | ||
|
4e56ef636b | ||
|
59d0cf666f | ||
|
54cc574405 | ||
|
97d16523b5 | ||
|
80086d76a8 | ||
|
5bf1161884 | ||
|
f49954223a | ||
|
98d9efc8d6 | ||
|
59f338a0b7 | ||
|
e1eac5c390 | ||
|
4c818d0359 | ||
|
63d4ccc058 | ||
|
1b85ab8b54 | ||
|
906c7eb7ec | ||
|
2207d836f1 | ||
|
a9cd27e012 | ||
|
eb24cf5f84 | ||
|
fae396dc0d | ||
|
c70393c7ef | ||
|
a9da79cc58 | ||
|
4c57405945 | ||
|
ad8f347989 | ||
|
68595be323 | ||
|
69b855a1e7 | ||
|
6ea96efe68 | ||
|
499a231432 | ||
|
0054fe0886 | ||
|
c59798e9c4 | ||
|
80483ba76f | ||
|
5c792c9e09 | ||
|
fdae221ca5 | ||
|
ba165ddd4f | ||
|
14e4d2d675 | ||
|
d505771d96 | ||
|
d8c1c51a21 | ||
|
71cbe716f9 | ||
|
f4df850d25 | ||
|
4d538c61b1 | ||
|
17e6179fec | ||
|
b7b2f16925 | ||
|
a7f6bcf36c | ||
|
590ca4ef56 | ||
|
04726b3ee4 | ||
|
eb2b523800 | ||
|
73b461f8d0 | ||
|
15be593bbd | ||
|
73c89ac28d | ||
|
45679fa29e | ||
|
861e5b1530 | ||
|
d5019f61ce | ||
|
78dd1b0476 | ||
|
72d5c11811 | ||
|
aefb81b7f0 | ||
|
55cc32ce0f | ||
|
060fc46e4f | ||
|
4bb3d60b6c | ||
|
4222d24d51 | ||
|
6c76866f28 | ||
|
50be6595bd | ||
|
91136d2551 | ||
|
2e822c5878 | ||
|
894dca2eef | ||
|
48355129c5 | ||
|
edc9429e84 | ||
|
f1a222fbb3 | ||
|
40bbc5850f | ||
|
70baa4087c | ||
|
8e4f56bd52 | ||
|
3800780ef2 | ||
|
6e154f6cbc | ||
|
673b6d6733 | ||
|
6185b93b59 | ||
|
9bbaae9dea | ||
|
6c856aba48 | ||
|
33de4cccfc | ||
|
6a79c8a27a | ||
|
631deb9ce7 | ||
|
cb2dd464f1 | ||
|
dca376cb46 | ||
|
89d6c83504 | ||
|
2ea3baf36e | ||
|
3338dcac1c | ||
|
0b9a2ee8ec | ||
|
78ac1ee15d | ||
|
886e9ab511 | ||
|
3c80621dac | ||
|
6b4ff4ce2c | ||
|
b22aa778e1 | ||
|
1c440ed36c | ||
|
70f56d5920 | ||
|
8a1d5bbc57 | ||
|
956370592f | ||
|
afd479ac69 | ||
|
39a4be5641 | ||
|
70fcd053cd | ||
|
c9869922f7 | ||
|
8b6a333cd2 | ||
|
ae329e371a | ||
|
cf7c3c2bf7 | ||
|
3d21d2724e | ||
|
df64a93808 | ||
|
6068d384a4 | ||
|
3c0292f074 | ||
|
371acc84a8 | ||
|
cd9feccf6e | ||
|
f1f3dffc97 | ||
|
e4f298acac | ||
|
e93b030bfe | ||
|
64a7abe53a | ||
|
784a40e2ed | ||
|
9ecd27dc85 | ||
|
72ef23786b | ||
|
28da78fc78 | ||
|
c345e81902 | ||
|
1b39742919 | ||
|
bc33b32522 | ||
|
4671f08b64 | ||
|
9f6f74df59 | ||
|
872c99e033 | ||
|
cb6e8a6937 | ||
|
7c16f8f134 | ||
|
ad11b61af2 | ||
|
120b82ae00 | ||
|
7cf6c97d3f | ||
|
2262acf12b | ||
|
ca3f97c42f | ||
|
8731055786 | ||
|
e943c55a91 | ||
|
be05b6aa90 | ||
|
0b4d94faf2 | ||
|
9f8f677125 | ||
|
949d6bf584 | ||
|
9fc2d2b76b | ||
|
d9935ada9d | ||
|
b4076b53e8 | ||
|
53c81918a5 | ||
|
7c11736992 | ||
|
49f168b7b3 | ||
|
104092702a | ||
|
2ed8341403 | ||
|
e5a196918f | ||
|
907ae760e0 | ||
|
37f7c4e0f9 | ||
|
fe107cd23f | ||
|
a830c1e812 | ||
|
8eb85acb88 | ||
|
ea8f510eff | ||
|
cbec44bb73 | ||
|
ac44ed56e0 | ||
|
dbf2f99c4e | ||
|
3b04ca229f | ||
|
5b1a171d27 | ||
|
9138c6c70a | ||
|
f1092f910a | ||
|
ebc9ddfeb1 | ||
|
201b4d6ec0 | ||
|
3b375929c1 | ||
|
ed7031981b | ||
|
0eace936b0 | ||
|
0d70743a47 | ||
|
778a0f7e8d | ||
|
a8cf6ee8a2 | ||
|
06a150a558 | ||
|
c551e5cd0a | ||
|
776ded0b7e | ||
|
9e3d99ec39 | ||
|
6c9c7328bb | ||
|
2961a2ed53 | ||
|
12836b9f60 | ||
|
17cef99ce8 | ||
|
2194fe43d2 | ||
|
06fef563e3 | ||
|
c38eeddc55 | ||
|
84ec33afb3 | ||
|
4b30132d06 | ||
|
38942aef25 | ||
|
200e8330d8 | ||
|
86092fc955 | ||
|
50cd4cb3ce | ||
|
7c857118fa | ||
|
4e1b18e2bb | ||
|
5fe3c1c61f | ||
|
270bd98a10 | ||
|
02bf5afe0b | ||
|
c878f63f99 | ||
|
8083935b47 | ||
|
c7d11132e5 | ||
|
69442a49f1 | ||
|
5f9ef0ff62 | ||
|
0888cc4f97 | ||
|
6821a9a0a4 | ||
|
003e8a979b | ||
|
08e8050ca5 | ||
|
8d5c3c5cdd | ||
|
dcc4214dcb | ||
|
ded55a1440 | ||
|
e0ff03068a | ||
|
60c05cab55 | ||
|
9c480580ba | ||
|
f3c3f397eb | ||
|
f6c7f61ec3 | ||
|
aae5cce175 | ||
|
8a9bea4603 | ||
|
0e3f6acb0a | ||
|
bbe621109f | ||
|
35f47bdfb7 | ||
|
8ca0be5a4d | ||
|
b6bd7cce6d | ||
|
a7cbb3941a | ||
|
8957720c94 | ||
|
452915478b | ||
|
962c80fff8 | ||
|
aef06c7f61 | ||
|
52be5746c6 | ||
|
b2ff64733d | ||
|
85cf36ac4a | ||
|
5fda21373e | ||
|
1ada87ca31 | ||
|
0554565b30 | ||
|
315284d5f5 | ||
|
99b1e391a6 | ||
|
9b50e85d0d | ||
|
e394474dad | ||
|
7d1761de4f | ||
|
dfd4c967b7 | ||
|
71305461d4 | ||
|
04a29ece3b | ||
|
99dd6f3e50 | ||
|
8f6048191d | ||
|
ee0fa71605 | ||
|
475a68924e | ||
|
c0a544351b | ||
|
15201a927e | ||
|
efa567f965 | ||
|
e1df1c255a | ||
|
64e34d0ef5 | ||
|
2e12befb8b | ||
|
98eee27b93 | ||
|
8ed853a3b3 | ||
|
8fd4a816a6 | ||
|
b12c4c5fa0 | ||
|
6473da7114 | ||
|
bb78ae59d4 | ||
|
459f3c4a93 | ||
|
e014b30915 | ||
|
cc5a388106 | ||
|
5de93f8cc4 | ||
|
ad1b708da5 | ||
|
c7ff36b314 | ||
|
f367480857 | ||
|
475809b1a0 | ||
|
fce7fdb3b7 | ||
|
b2c72f1d75 | ||
|
f0ac7d2c16 | ||
|
7099a3b394 | ||
|
3e985d8554 | ||
|
f94e8c9719 | ||
|
bbe1442c28 | ||
|
3a248d7707 | ||
|
0bb1f16d2a | ||
|
0837756152 | ||
|
23236c96cb | ||
|
6ead5c3800 | ||
|
64db865e1e | ||
|
1b2399745d | ||
|
94acf60100 | ||
|
a4298e8c19 | ||
|
76985838c4 | ||
|
d24964e900 | ||
|
f4fde8f5f7 | ||
|
3461dd6464 | ||
|
560671b57f | ||
|
abf3962d91 | ||
|
f53a85fcd1 | ||
|
73730355b8 | ||
|
8827721605 | ||
|
2497413c60 | ||
|
a6537ef282 | ||
|
c92adc36c6 | ||
|
a64f71f186 | ||
|
0c766a2714 | ||
|
6b7c49431a | ||
|
4b37121b75 | ||
|
120c7b9730 | ||
|
1aa233ca47 | ||
|
14e6e492dd | ||
|
17bdf55bcc | ||
|
1ae6508a43 | ||
|
b7b6cef880 | ||
|
52b5c56da9 | ||
|
85ba9e96a0 | ||
|
b4e15cb27f | ||
|
068cfe5e3e | ||
|
c0449a633d | ||
|
7dfce5e306 | ||
|
fd53f7476e | ||
|
ceb541ad8a | ||
|
d92ced6c6b | ||
|
0847088391 | ||
|
a128685b83 | ||
|
749d26fafa | ||
|
485faf0141 | ||
|
af9d896510 | ||
|
1fc114fec7 | ||
|
e6938cef6f | ||
|
ffafd291ee | ||
|
f532759543 | ||
|
db3ba6db3c | ||
|
0a333f8476 | ||
|
738aaeed12 | ||
|
ce6c9c91fc | ||
|
7035b71ccd | ||
|
923a567822 | ||
|
9b24e9378f | ||
|
779f21a1ca | ||
|
73d70aa5e5 | ||
|
fc78eacf8f | ||
|
006af636e6 | ||
|
4575fda10a | ||
|
6c960628c2 | ||
|
00aa7deaae | ||
|
f722956864 | ||
|
8f520bff12 | ||
|
8398534fa0 | ||
|
e1fed90b71 | ||
|
5ba6e53379 | ||
|
d33bdfd50c | ||
|
4f5392eb74 | ||
|
064087a7c0 | ||
|
e13821ba49 | ||
|
c2b85779c3 | ||
|
cdfdad3e3d | ||
|
1f7992e5da | ||
|
4bad7d7c52 | ||
|
1fcf39d4ab | ||
|
a64e304d16 | ||
|
30c7cbba96 | ||
|
a7d324901d | ||
|
15c87dc0b6 | ||
|
0d2de4c421 | ||
|
da95bd6127 | ||
|
e31b5529b0 | ||
|
0836df6974 | ||
|
de4cd55adf | ||
|
6b156f2144 | ||
|
2b1efd9347 | ||
|
97dd10edc0 | ||
|
cc4e46d212 | ||
|
dc0671942d | ||
|
f79c8ab641 | ||
|
314fda7877 | ||
|
aaf77515fc | ||
|
b5f7b1aad4 | ||
|
d36974a47e | ||
|
5bd16f990c | ||
|
28d7924077 | ||
|
89ecba961c | ||
|
3481a5fd19 | ||
|
70a21c5136 | ||
|
2e2c9764f3 | ||
|
bd447b6c79 | ||
|
4f6ec3aa32 | ||
|
e37b3179ba | ||
|
808214f973 | ||
|
7e714f1ef8 | ||
|
e65e46f664 | ||
|
5e7eb6635f | ||
|
49ae62b02e | ||
|
c9cfe5cc6e | ||
|
e8df010449 | ||
|
949136b161 | ||
|
1e00c63146 | ||
|
6d9b93a407 | ||
|
37c39ad587 | ||
|
e3e65878aa | ||
|
6843b0eaab | ||
|
e25f76753a | ||
|
75c2fabd7f | ||
|
35aeb19fcd | ||
|
5a00f6a4fc | ||
|
3a9fc52b8c | ||
|
64a8de938b | ||
|
605cf407a8 | ||
|
9ed5297e91 | ||
|
07da404a23 | ||
|
21467ef65d | ||
|
4d5b2c4033 | ||
|
5e7836b293 | ||
|
a6fe61d508 | ||
|
c0aa320f0a | ||
|
4bcc18fb41 | ||
|
32370545cb | ||
|
2fd8c831c0 | ||
|
5b4877c402 | ||
|
71c11b34f4 | ||
|
3123718166 | ||
|
dfbec71906 | ||
|
757c087afd | ||
|
b30aa968b0 | ||
|
1e902c8dee | ||
|
db5f64432e | ||
|
6c9c463da9 | ||
|
c73878ccca | ||
|
41e453306d | ||
|
626c6007fd | ||
|
deb88032cb | ||
|
eca317b3c4 | ||
|
9300326483 | ||
|
ebc46eb7d2 | ||
|
cb83669802 | ||
|
f6f616a21d | ||
|
62d50c0189 | ||
|
dd5b143c13 | ||
|
2e864c32fa | ||
|
f4fa7c927c | ||
|
3736cbc107 | ||
|
776825cc66 | ||
|
5cb647e57f | ||
|
9c5f826bb4 | ||
|
6b1803629d | ||
|
5cea0571e3 | ||
|
95c8afd5ba | ||
|
ecc9a34359 | ||
|
1fed7fb5af | ||
|
93d1ded4c2 | ||
|
f199775437 | ||
|
40271f420d | ||
|
9e71c02eb9 | ||
|
c9cbfe630d | ||
|
bf9331b147 | ||
|
f223d2e00c | ||
|
5246e7f035 | ||
|
42de0803c9 | ||
|
242f9c6197 | ||
|
5288198474 | ||
|
b21353c4f9 | ||
|
65a0c6a4b3 | ||
|
af5bd89f34 | ||
|
73aeffd13c | ||
|
03d2f6c017 | ||
|
7e1481c43f | ||
|
8a1069bf70 | ||
|
6097ab5d12 | ||
|
d951575f80 | ||
|
d887546e58 | ||
|
ec3b80cec9 | ||
|
2ef442cf83 | ||
|
6e5a4a7546 | ||
|
e0d46002cb | ||
|
739f13b7a3 | ||
|
34af74b288 | ||
|
4ee0cc8145 | ||
|
4984674b1d | ||
|
f5ee67aafb | ||
|
98a1b0da71 | ||
|
074ff76d49 | ||
|
b75409a6bf | ||
|
94abda6e3e | ||
|
68419a9510 | ||
|
db0854f203 | ||
|
994301ea4c | ||
|
5e344b9be7 | ||
|
66d3da8ac9 | ||
|
71bb876c9e | ||
|
1e029f3290 | ||
|
7926b689fd | ||
|
4638f781f9 | ||
|
8bea1505dd | ||
|
e567f7a80c | ||
|
3774e8dc51 | ||
|
dc27ffa6ba | ||
|
ca25eedfbc | ||
|
1627c05224 | ||
|
f65ca04507 | ||
|
a662b6ef6a | ||
|
bd6d38b3b0 | ||
|
a890d5300b | ||
|
eb8411611a | ||
|
9360ddf294 | ||
|
e3c138fa98 | ||
|
7147dadb2a | ||
|
8d03738a50 | ||
|
e5540ee79f | ||
|
2ccbb6d6a7 | ||
|
3dce7e7e9f | ||
|
79e8ce9226 | ||
|
89857ca77c | ||
|
5ce60be78a | ||
|
600cc20423 | ||
|
987d09041d | ||
|
df52d01a1d | ||
|
07de4af581 | ||
|
95c50dcc0d | ||
|
71a192d0ba | ||
|
9acb8c2ba2 | ||
|
307f7b6bd7 | ||
|
83dd80ae86 | ||
|
ccfa85b5e0 | ||
|
e59821caa1 | ||
|
2215e5e069 | ||
|
48b2e682bf | ||
|
5750da574a | ||
|
b40095f603 | ||
|
ec11585223 | ||
|
eedf189d44 | ||
|
f027d36db8 | ||
|
0c79474e0c | ||
|
610f9e2b22 | ||
|
4db4b28369 | ||
|
18464f5cd5 | ||
|
cf24c1307a | ||
|
35e870a8e7 | ||
|
01be74b219 | ||
|
b8da6847b9 | ||
|
e2e37a0db4 | ||
|
56daf347b9 | ||
|
5098f63175 | ||
|
d9b12a6927 | ||
|
fe4ffcfc62 | ||
|
64a68b95b0 | ||
|
58d01738ab | ||
|
a18dae6d04 | ||
|
b2959e583a | ||
|
a38a4d6f69 | ||
|
0a6ea59254 | ||
|
04984a51e6 | ||
|
8947f13dbe | ||
|
c0549d872c | ||
|
adcd5f0737 | ||
|
0929857b12 | ||
|
7d21b39534 | ||
|
aaf85216eb | ||
|
2c87100ffb | ||
|
6c22546577 | ||
|
24177ffc46 | ||
|
8ae4315d43 | ||
|
a588c72e6c | ||
|
a656f86003 | ||
|
c9dc60be7b | ||
|
91cb9129ab | ||
|
ae32fdeea7 | ||
|
5803512820 | ||
|
553b1f139b | ||
|
623347bc48 | ||
|
f64f86fbb6 | ||
|
39b5462809 | ||
|
a6ee64ea63 | ||
|
0dcd834535 | ||
|
f0fc0441bd | ||
|
69eaaef963 | ||
|
9dbfe22171 | ||
|
5ca4e71c34 | ||
|
26b04e70b1 | ||
|
ad2430496d | ||
|
d4ecfa79d6 | ||
|
8f4cd929cd | ||
|
fb213b0e0f | ||
|
e48d18279c | ||
|
5f46b48d45 | ||
|
0cc24c8076 | ||
|
2738c749dc | ||
|
a7b178b844 | ||
|
69c44b19d4 | ||
|
e6abce8022 | ||
|
ab64bbc3b0 | ||
|
5da83e9945 | ||
|
0e6a8cc33e | ||
|
2e2ea657ef | ||
|
e5fb9e2d31 | ||
|
1386f205fd | ||
|
a294ad41cb | ||
|
ed497cab99 | ||
|
282d0abb62 | ||
|
034b732e7c | ||
|
b1e9c005b7 | ||
|
0d144d088e | ||
|
b2855e74ef | ||
|
0652e30c30 | ||
|
1c58fabc30 | ||
|
262ba6ee1e | ||
|
e0534577d1 | ||
|
c12423f28b | ||
|
3dd6577b32 | ||
|
a638b4fb28 | ||
|
798cf66e3f | ||
|
6cf29123f3 | ||
|
e65d42bd61 | ||
|
febf8ac5b3 | ||
|
c8a9b66694 | ||
|
9c185f1081 | ||
|
0a444508f8 | ||
|
1d00ee41c4 | ||
|
266434fe7b | ||
|
7c88333060 | ||
|
e0b561b12a | ||
|
16e5e2d757 | ||
|
8a07c62603 | ||
|
4df847bc10 | ||
|
b5df44ce8e | ||
|
716952cd58 | ||
|
7b9b418e93 | ||
|
dfd7c6d4a6 | ||
|
920caaa524 | ||
|
13f10657b8 | ||
|
dd5fd2e5bb | ||
|
97b00053c4 | ||
|
76e46d2fa7 | ||
|
4c62c5dc22 | ||
|
c7209df7e2 | ||
|
c17b8e4d9e | ||
|
0ab33b704c | ||
|
ba027de3f7 | ||
|
3ac257bfda | ||
|
ec495ea4d1 | ||
|
3b035158a2 | ||
|
36582a2741 | ||
|
450d3cb7d2 | ||
|
8d157ac5ca | ||
|
89dc379761 | ||
|
3e800e7e53 | ||
|
b12c6289d0 | ||
|
7e50267cb0 | ||
|
6714eb9f7f | ||
|
f1d6f0b2a6 | ||
|
445606e2b1 | ||
|
ad05f479a8 | ||
|
a78c1c8278 | ||
|
06d6d15441 | ||
|
87676ece18 | ||
|
acfd3f1002 | ||
|
d5cbe66b0e | ||
|
88aa34747b | ||
|
cc8dcade49 | ||
|
a64dd9af16 | ||
|
8405ce6369 | ||
|
18e68d04f9 | ||
|
f3010f622c | ||
|
ff87319a74 | ||
|
fa517417ed | ||
|
f9b86a6b2e | ||
|
8a21dfa93f | ||
|
c88ae2d7fe | ||
|
25e979687f | ||
|
af866939f4 | ||
|
e72becdfcb | ||
|
4bd97eecc9 | ||
|
8ffe6dcfa0 | ||
|
23002ac70d | ||
|
1262ad3cd4 | ||
|
d8f145c4dc | ||
|
9c5fd1b478 | ||
|
24f3285003 | ||
|
179520a211 | ||
|
ec31a4fe17 | ||
|
ab780485b2 | ||
|
07c5c2972d | ||
|
af593117e4 | ||
|
931505d135 | ||
|
6c7433b2f1 | ||
|
38ca0accde | ||
|
df79c2cf48 | ||
|
94bcbeb604 | ||
|
35e45333b1 | ||
|
f17a6f13a4 | ||
|
1bff7bdc47 | ||
|
13cb2c695f | ||
|
c581b80132 | ||
|
4dd0ae8bdc | ||
|
4db67e5bc5 | ||
|
6aa9122160 | ||
|
f84f2dca64 | ||
|
3c6992e910 | ||
|
7f79d16f02 | ||
|
790c386ba8 | ||
|
ee72badf21 | ||
|
c9c4453660 | ||
|
f3611ac693 | ||
|
cc6fe24e82 | ||
|
5a7730951a | ||
|
eef729b5f9 | ||
|
0bb0a38649 | ||
|
39029adcd8 | ||
|
c967f91abb | ||
|
1ba0685596 | ||
|
f2daa6a150 | ||
|
e021d42551 | ||
|
13509e31ca | ||
|
b9af805ac1 | ||
|
378d2bc8ba | ||
|
b73aa55a75 | ||
|
a729dd1380 | ||
|
09335e2cf1 | ||
|
31738c465d | ||
|
38fb64130d | ||
|
493b10393b | ||
|
b406f52670 | ||
|
ef3f314754 | ||
|
d2910686cd | ||
|
793b1b56d9 | ||
|
d8f9075e2a | ||
|
1199d4ead9 | ||
|
4520e1bb3e | ||
|
afece8193e | ||
|
3b14585d1a | ||
|
9cd32a908f | ||
|
b9ca02088d | ||
|
440ce0221a | ||
|
caff7eda9f | ||
|
aef1cefc18 | ||
|
1385c7cc9b | ||
|
8d0260b644 | ||
|
5d9827fb60 | ||
|
256d711fde | ||
|
1d82c3779b | ||
|
abc9d07977 | ||
|
40d95acfb9 | ||
|
9a92bc05db | ||
|
8962bf00f6 | ||
|
2083954aa5 | ||
|
66af258876 | ||
|
4ba04031ef | ||
|
a30456a92d | ||
|
c8dd13577e | ||
|
fac35b46bb | ||
|
748cb778e0 | ||
|
bbcca24bcc | ||
|
b8c52b1120 | ||
|
b1b3ce48ee | ||
|
da864f4c6c | ||
|
758f627e12 | ||
|
20322c6ab8 | ||
|
7a711f0690 | ||
|
067b977ec8 | ||
|
58f0ca3d8a | ||
|
4176f3659b | ||
|
5979fe5eef | ||
|
362ba21567 | ||
|
71894ba245 | ||
|
2b19e0fbc6 | ||
|
4d0b402e8b | ||
|
933e0c30bf | ||
|
b430afe3e1 | ||
|
2f25f1790e | ||
|
d90fcf15bd | ||
|
be9cc41957 | ||
|
ef99eeb300 | ||
|
1646241dfb | ||
|
543e628a8b | ||
|
36269cbed6 | ||
|
75dcdc72d2 | ||
|
f5c90bebdc | ||
|
15af66de55 | ||
|
17c8ac8248 | ||
|
e5c6b9a979 | ||
|
753849cedd | ||
|
b81e4c01ec | ||
|
2a32b05df1 | ||
|
e3a0fe88c1 | ||
|
ee3aa49eee | ||
|
dd27ad79bd | ||
|
cfd0f556a5 | ||
|
347e70f4ea | ||
|
828aae35ce | ||
|
2f56783b7e | ||
|
dc4ecdaa38 | ||
|
1440e8c55d | ||
|
51a072808f | ||
|
0a8c2926ea | ||
|
5e79f567a7 | ||
|
e2eb26eb93 | ||
|
740a50a26d | ||
|
be3f248a5a | ||
|
f2870caed2 | ||
|
ad22d3fd91 | ||
|
9dcd8b6edf | ||
|
9607e0e55d | ||
|
ad221eaaa2 | ||
|
74a1a5262d | ||
|
32ae82d4da | ||
|
9c6c8d4f8f | ||
|
45fbe6972b | ||
|
c262132a2f | ||
|
911a7ef6b5 | ||
|
8cb3757f5c | ||
|
10bf914d78 | ||
|
684177ee44 | ||
|
b2052ca308 | ||
|
4174fa648d | ||
|
182d67881d | ||
|
9284ac7461 | ||
|
55eec06e77 | ||
|
069acf0297 | ||
|
d9d2c7d213 | ||
|
83b28e0b00 | ||
|
c2d52fd48f | ||
|
32e6643303 | ||
|
5faf6be02d | ||
|
9ff93ac2d5 | ||
|
5131d8d328 | ||
|
2147f8ec8b | ||
|
cfa568a230 | ||
|
ace904cf16 | ||
|
cb337f4a65 | ||
|
9df78946c5 | ||
|
e47359129e | ||
|
4187e0f94c | ||
|
b2899529c3 | ||
|
3d2b4cbfa8 | ||
|
9daa424afd | ||
|
aa04951081 | ||
|
d8da8023c2 | ||
|
60cadb8b6d | ||
|
dcf8783c2e | ||
|
eb9dc95c58 | ||
|
f94fb4f65e | ||
|
e0aff2cf9d | ||
|
d2369f45ce | ||
|
7b718ada63 | ||
|
652604a36f | ||
|
8a4834dd2b | ||
|
6d49093620 | ||
|
489ce0bebc | ||
|
58922c23a6 | ||
|
be3183fc45 | ||
|
38b4812eb3 | ||
|
69fdc2d821 | ||
|
9104b0f974 | ||
|
6ec7373a1a | ||
|
39be605459 | ||
|
4d4459fa4e | ||
|
2a3dbaa7b4 | ||
|
6069e8f083 | ||
|
30ea223d09 | ||
|
0a957d7097 | ||
|
f28a898053 | ||
|
8921d4fac8 | ||
|
a66578c46d | ||
|
e45dcf61c4 | ||
|
40bfc9b368 | ||
|
73684d11c8 | ||
|
d958b1808c | ||
|
e276443d2d | ||
|
f14ad8be58 | ||
|
4463d2408c | ||
|
9365706777 | ||
|
8e8415515d | ||
|
2767fca5d6 | ||
|
b5b32cacfc | ||
|
98a74a15f7 | ||
|
a0d70ea8a4 | ||
|
db29a31de6 | ||
|
e67d2ee498 |
@@ -2,15 +2,15 @@ version: 2
|
||||
jobs:
|
||||
fast_tests:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
cd .circleci && ./run-tests.sh "Fast=Fast" && ./can-build.sh
|
||||
cd .circleci && ./run-tests.sh "Fast=Fast|ThirdParty=ThirdParty" && ./can-build.sh
|
||||
selenium_tests:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@@ -18,85 +18,77 @@ jobs:
|
||||
cd .circleci && ./run-tests.sh "Selenium=Selenium"
|
||||
integration_tests:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
cd .circleci && ./run-tests.sh "Integration=Integration"
|
||||
external_tests:
|
||||
trigger_docs_build:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
if [ "$CIRCLE_PROJECT_USERNAME" == "btcpayserver" ] && [ "$CIRCLE_PROJECT_REPONAME" == "btcpayserver" ]; then
|
||||
cd .circleci && ./run-tests.sh "ExternalIntegration=ExternalIntegration"
|
||||
else
|
||||
echo "Skipping running ExternalIntegration tests outside of context of main user and repository that have access to secrets"
|
||||
fi
|
||||
|
||||
curl -X POST -H "Authorization: token $GH_PAT" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/btcpayserver/btcpayserver-doc/dispatches --data '{"event_type": "build_docs"}'
|
||||
|
||||
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
|
||||
amd64:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
GIT_COMMIT=$(git rev-parse HEAD)
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
|
||||
sudo docker build --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 -f amd64.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 -f amd64.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64
|
||||
|
||||
arm32v7:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
GIT_COMMIT=$(git rev-parse HEAD)
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker build --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7
|
||||
|
||||
arm64v8:
|
||||
machine:
|
||||
enabled: true
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
GIT_COMMIT=$(git rev-parse HEAD)
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
|
||||
sudo docker build --build-arg CONFIGURATION_NAME=Altcoins-Release --pull -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 -f arm64v8.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --build-arg CONFIGURATION_NAME=Altcoins-Release --pull -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 -f arm64v8.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8
|
||||
|
||||
multiarch:
|
||||
machine:
|
||||
enabled: true
|
||||
image: circleci/classic:201808-01
|
||||
image: ubuntu-2004:202201-02
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
# Turn on Experimental features
|
||||
sudo mkdir $HOME/.docker
|
||||
sudo sh -c 'echo "{ \"experimental\": \"enabled\" }" >> $HOME/.docker/config.json'
|
||||
#
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
#
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
@@ -105,8 +97,7 @@ jobs:
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 --os linux --arch arm64 --variant v8
|
||||
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
|
||||
|
||||
|
||||
|
||||
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 --os linux --arch amd64
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 --os linux --arch arm --variant v7
|
||||
@@ -120,12 +111,15 @@ workflows:
|
||||
- fast_tests
|
||||
- selenium_tests
|
||||
- integration_tests
|
||||
- external_tests:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
publish:
|
||||
jobs:
|
||||
- trigger_docs_build:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
# only act on version tags
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- amd64:
|
||||
filters:
|
||||
# ignore any commit on any branch by default
|
||||
@@ -135,7 +129,6 @@ workflows:
|
||||
# OR feature tags like vlndseedbackup
|
||||
# OR features on specific versions like v1.0.0.88-lndseedbackup-1
|
||||
tags:
|
||||
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- arm32v7:
|
||||
filters:
|
||||
|
@@ -4,6 +4,15 @@ set -e
|
||||
cd ../BTCPayServer.Tests
|
||||
docker-compose -v
|
||||
docker-compose -f "docker-compose.altcoins.yml" down --v
|
||||
docker-compose -f "docker-compose.altcoins.yml" pull
|
||||
|
||||
# For some reason, docker-compose pull fails time to time, so we try several times
|
||||
n=0
|
||||
until [ "$n" -ge 10 ]
|
||||
do
|
||||
docker-compose -f "docker-compose.altcoins.yml" pull && break
|
||||
n=$((n+1))
|
||||
sleep 5
|
||||
done
|
||||
|
||||
docker-compose -f "docker-compose.altcoins.yml" build
|
||||
docker-compose -f "docker-compose.altcoins.yml" run -e "TEST_FILTERS=$1" tests
|
||||
|
@@ -11,10 +11,14 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
charset = utf-8
|
||||
space_before_self_closing = true
|
||||
|
||||
[launchSettings.json]
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
[swagger*.json]
|
||||
indent_size = 4
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
# New line preferences
|
||||
@@ -67,7 +71,7 @@ dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
|
||||
|
||||
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
|
||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
||||
|
||||
# Code style defaults
|
||||
dotnet_sort_system_directives_first = true
|
||||
@@ -121,8 +125,11 @@ csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
csharp_style_prefer_null_check_over_type_check = true:warning
|
||||
csharp_prefer_simple_using_statement = true:warning
|
||||
|
||||
# C++ Files
|
||||
|
||||
[*.{cpp,h,in}]
|
||||
curly_bracket_next_line = true
|
||||
indent_brace_style = Allman
|
||||
|
38
.github/ISSUE_TEMPLATE/bug-report.md
vendored
38
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: File a bug report
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Your BTCPay Environment (please complete the following information):**
|
||||
- BTCPay Server Version [available in the right bottom corner of footer]
|
||||
- Deployment Method: [e.g. Docker, Manual, Third-Party-hoist]
|
||||
- Browser [e.g. chrome, safari]
|
||||
|
||||
**Logs (if applicable)**
|
||||
Basic logs can be found in Server Settings > Logs. More logs https://docs.btcpayserver.org/Troubleshooting/#2-looking-through-the-logs
|
||||
|
||||
**Setup Parameters**
|
||||
If you're reporting a deployment issue run `. btcpay-setup.sh -i` and paste your the parameters by obscuring private information.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
68
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
68
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: 🐛 Bug Report
|
||||
description: File a bug report
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Please provide as much information as you can. It helps us better understand the problem and fix it faster.
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
label: What is your BTCPay version?
|
||||
description: You can see the version in the footer's bottom right corner
|
||||
placeholder: I'm running BTCPay v1.X.X.X
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: deployment
|
||||
attributes:
|
||||
label: How did you deploy BTCPay Server?
|
||||
description: Docker, manual, third-party host? Read more on deployment methods [here](https://docs.btcpayserver.org/Deployment/)
|
||||
placeholder: I'm running BTCPay Server on a...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: How did you encounter this bug?
|
||||
description: Step by step describe how did you encounter the bug?
|
||||
placeholder: 1. I clicked X 2. Then I clicked Y 3. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logoutput
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. Logs can be found in Server Settings > Logs. Here's how you can [troubleshoot an issue](https://docs.btcpayserver.org/Troubleshooting/)
|
||||
render: shell
|
||||
- type: textarea
|
||||
id: browser
|
||||
attributes:
|
||||
label: What browser do you use?
|
||||
description: Provide your browser and it's version. If you replicated issues on multiple browsers, let us know which ones.
|
||||
placeholder: For example Safari 15.00, Chrome 10.0, Tor, Edge, etc
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additonal
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Feel free to provide additional information. Screenshots are always helpful.
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Are you sure this is a bug report?
|
||||
description: By submitting this report, you agree that this is not a support or a feature request. For general questions please read our [documentation](https://docs.btcpayserver.org). You can ask questions in [discussions](https://github.com/btcpayserver/btcpayserver/discussions) and [on our community chat](https://chat.btcpayserver.org)
|
||||
options:
|
||||
- label: I confirm this is a bug report
|
||||
required: true
|
18
.github/ISSUE_TEMPLATE/config.yml
vendored
18
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Community Support Chat
|
||||
- name: 💡 Request a feature
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions/categories/ideas-feature-requests
|
||||
about: Submit a feature request or vote on ideas posted by others. Features with most upvotes become roadmap candidates
|
||||
- name: 🧑💻 Ask a technical question
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions/new?category=technical-support
|
||||
about: If you're experiencing a technical problem post it to our community support forum
|
||||
- name: 🔌 Report a problem with a plugin
|
||||
url: https://github.com/btcpayserver/btcpayserver/discussions/new?category=plugins-integrations
|
||||
about: Experiencing a problem with a third-party plugin? Post it here and we will tag their developers to assist
|
||||
- name: 📝 Official Documentation
|
||||
url: https://docs.btcpayserver.org
|
||||
about: Check our documentation for answers to common questions
|
||||
- name: 💬 Community Support Chat
|
||||
url: https://chat.btcpayserver.org/
|
||||
about: Ask general questions and get community support in real-time.
|
||||
about: Ask general questions and get community support in real-time
|
||||
|
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a new feature or enhancement
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Sketch/Image/Wireframe/Mockup**
|
||||
If applicable provide examples, wireframes, sketches or images to better explain your idea.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
9
.gitignore
vendored
9
.gitignore
vendored
@@ -288,13 +288,14 @@ __pycache__/
|
||||
*.xsd.cs
|
||||
/BTCPayServer/Build/dockerfiles
|
||||
|
||||
# Bundling JS/CSS
|
||||
BTCPayServer/wwwroot/bundles/*
|
||||
!BTCPayServer/wwwroot/bundles/.gitignore
|
||||
|
||||
.vscode/*
|
||||
!.vscode/launch.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/extensions.json
|
||||
BTCPayServer/testpwd
|
||||
.DS_Store
|
||||
Packed Plugins
|
||||
Plugins/packed
|
||||
|
||||
BTCPayServer/wwwroot/swagger/v1/openapi.json
|
||||
BTCPayServer/appsettings.dev.json
|
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -10,14 +10,14 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/BTCPayServer/bin/Debug/netcoreapp3.1/BTCPayServer.dll",
|
||||
"program": "${workspaceFolder}/BTCPayServer/bin/Debug/net6.0/BTCPayServer.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/BTCPayServer",
|
||||
"stopAtEntry": false,
|
||||
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bListening on\\s+(https?://\\S+)"
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
|
43
BTCPayServer.Abstractions/BTCPayServer.Abstractions.csproj
Normal file
43
BTCPayServer.Abstractions/BTCPayServer.Abstractions.csproj
Normal file
@@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Company>BTCPay Server</Company>
|
||||
<Copyright>Copyright © BTCPay Server 2020</Copyright>
|
||||
<Description>A library for BTCPay Server plugin development</Description>
|
||||
<PackageIcon>icon.png</PackageIcon>
|
||||
<PackageTags>btcpay,btcpayserver</PackageTags>
|
||||
<PackageProjectUrl>https://github.com/btcpayserver/btcpayserver/tree/master/BTCPayServer.Abstractions</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<RepositoryUrl>https://github.com/btcpayserver/btcpayserver</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>1591;1573;1572;1584;1570;3021</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="icon.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
18
BTCPayServer.Abstractions/CamelCaseSerializerSettings.cs
Normal file
18
BTCPayServer.Abstractions/CamelCaseSerializerSettings.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Abstractions
|
||||
{
|
||||
public class CamelCaseSerializerSettings
|
||||
{
|
||||
static CamelCaseSerializerSettings()
|
||||
{
|
||||
Settings = new JsonSerializerSettings()
|
||||
{
|
||||
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
Serializer = JsonSerializer.Create(Settings);
|
||||
}
|
||||
public static readonly JsonSerializerSettings Settings;
|
||||
public static readonly JsonSerializer Serializer;
|
||||
}
|
||||
}
|
20
BTCPayServer.Abstractions/Configuration/DataDirectories.cs
Normal file
20
BTCPayServer.Abstractions/Configuration/DataDirectories.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BTCPayServer.Configuration
|
||||
{
|
||||
public class DataDirectories
|
||||
{
|
||||
public string DataDir { get; set; }
|
||||
public string PluginDir { get; set; }
|
||||
public string TempStorageDir { get; set; }
|
||||
public string StorageDir { get; set; }
|
||||
public string TempDir { get; set; }
|
||||
|
||||
public string ToDatadirFullPath(string path)
|
||||
{
|
||||
if (Path.IsPathRooted(path))
|
||||
return path;
|
||||
return Path.Combine(DataDir, path);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
namespace BTCPayServer.Security
|
||||
namespace BTCPayServer.Abstractions.Constants
|
||||
{
|
||||
public class AuthenticationSchemes
|
||||
{
|
7
BTCPayServer.Abstractions/Constants/WellKnownTempData.cs
Normal file
7
BTCPayServer.Abstractions/Constants/WellKnownTempData.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace BTCPayServer.Abstractions.Constants;
|
||||
|
||||
public class WellKnownTempData
|
||||
{
|
||||
public const string SuccessMessage = nameof(SuccessMessage);
|
||||
public const string ErrorMessage = nameof(ErrorMessage);
|
||||
}
|
111
BTCPayServer.Abstractions/Contracts/BaseDbContextFactory.cs
Normal file
111
BTCPayServer.Abstractions/Contracts/BaseDbContextFactory.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Operations;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public abstract class BaseDbContextFactory<T> where T : DbContext
|
||||
{
|
||||
private readonly IOptions<DatabaseOptions> _options;
|
||||
private readonly string _schemaPrefix;
|
||||
|
||||
public BaseDbContextFactory(IOptions<DatabaseOptions> options, string schemaPrefix)
|
||||
{
|
||||
_options = options;
|
||||
_schemaPrefix = schemaPrefix;
|
||||
}
|
||||
|
||||
public abstract T CreateContext();
|
||||
|
||||
class CustomNpgsqlMigrationsSqlGenerator : NpgsqlMigrationsSqlGenerator
|
||||
{
|
||||
#pragma warning disable EF1001 // Internal EF Core API usage.
|
||||
public CustomNpgsqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal.INpgsqlSingletonOptions opts) : base(dependencies, opts)
|
||||
#pragma warning restore EF1001 // Internal EF Core API usage.
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Generate(NpgsqlCreateDatabaseOperation operation, IModel model, MigrationCommandListBuilder builder)
|
||||
{
|
||||
builder
|
||||
.Append("CREATE DATABASE ")
|
||||
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name));
|
||||
|
||||
// POSTGRES gotcha: Indexed Text column (even if PK) are not used if we are not using C locale
|
||||
builder
|
||||
.Append(" TEMPLATE ")
|
||||
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier("template0"));
|
||||
|
||||
builder
|
||||
.Append(" LC_CTYPE ")
|
||||
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier("C"));
|
||||
|
||||
builder
|
||||
.Append(" LC_COLLATE ")
|
||||
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier("C"));
|
||||
|
||||
builder
|
||||
.Append(" ENCODING ")
|
||||
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier("UTF8"));
|
||||
|
||||
if (operation.Tablespace != null)
|
||||
{
|
||||
builder
|
||||
.Append(" TABLESPACE ")
|
||||
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Tablespace));
|
||||
}
|
||||
|
||||
builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
|
||||
|
||||
EndStatement(builder, suppressTransaction: true);
|
||||
}
|
||||
}
|
||||
|
||||
public void ConfigureBuilder(DbContextOptionsBuilder builder)
|
||||
{
|
||||
switch (_options.Value.DatabaseType)
|
||||
{
|
||||
case DatabaseType.Sqlite:
|
||||
builder.UseSqlite(_options.Value.ConnectionString, o =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_schemaPrefix))
|
||||
{
|
||||
o.MigrationsHistoryTable(_schemaPrefix);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case DatabaseType.Postgres:
|
||||
builder
|
||||
.UseNpgsql(_options.Value.ConnectionString, o =>
|
||||
{
|
||||
o.EnableRetryOnFailure(10);
|
||||
if (!string.IsNullOrEmpty(_schemaPrefix))
|
||||
{
|
||||
o.MigrationsHistoryTable(_schemaPrefix);
|
||||
}
|
||||
})
|
||||
.ReplaceService<IMigrationsSqlGenerator, CustomNpgsqlMigrationsSqlGenerator>();
|
||||
break;
|
||||
case DatabaseType.MySQL:
|
||||
builder.UseMySql(_options.Value.ConnectionString, ServerVersion.AutoDetect(_options.Value.ConnectionString), o =>
|
||||
{
|
||||
o.EnableRetryOnFailure(10);
|
||||
|
||||
if (!string.IsNullOrEmpty(_schemaPrefix))
|
||||
{
|
||||
o.MigrationsHistoryTable(_schemaPrefix);
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IBTCPayServerClientFactory
|
||||
{
|
||||
Task<BTCPayServerClient> Create(string userId, params string[] storeIds);
|
||||
Task<BTCPayServerClient> Create(string userId, string[] storeIds, HttpContext httpRequest);
|
||||
}
|
||||
}
|
32
BTCPayServer.Abstractions/Contracts/IBTCPayServerPlugin.cs
Normal file
32
BTCPayServer.Abstractions/Contracts/IBTCPayServerPlugin.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using BTCPayServer.Abstractions.Converters;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IBTCPayServerPlugin
|
||||
{
|
||||
public string Identifier { get; }
|
||||
string Name { get; }
|
||||
[JsonConverter(typeof(VersionConverter))]
|
||||
Version Version { get; }
|
||||
string Description { get; }
|
||||
bool SystemPlugin { get; set; }
|
||||
PluginDependency[] Dependencies { get; }
|
||||
void Execute(IApplicationBuilder applicationBuilder, IServiceProvider applicationBuilderApplicationServices);
|
||||
void Execute(IServiceCollection applicationBuilder);
|
||||
|
||||
public class PluginDependency
|
||||
{
|
||||
public string Identifier { get; set; }
|
||||
public string Condition { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Identifier}: {Condition}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
17
BTCPayServer.Abstractions/Contracts/IFileService.cs
Normal file
17
BTCPayServer.Abstractions/Contracts/IFileService.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
public interface IFileService
|
||||
{
|
||||
Task<bool> IsAvailable();
|
||||
Task<IStoredFile> AddFile(IFormFile file, string userId);
|
||||
Task<IStoredFile> AddFile(Uri file, string userId);
|
||||
Task<string?> GetFileUrl(Uri baseUri, string fileId);
|
||||
Task<string?> GetTemporaryFileUrl(Uri baseUri, string fileId, DateTimeOffset expiry,
|
||||
bool isDownload);
|
||||
Task RemoveFile(string fileId, string userId);
|
||||
}
|
29
BTCPayServer.Abstractions/Contracts/INotificationHandler.cs
Normal file
29
BTCPayServer.Abstractions/Contracts/INotificationHandler.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public abstract class BaseNotification
|
||||
{
|
||||
public abstract string Identifier { get; }
|
||||
public abstract string NotificationType { get; }
|
||||
}
|
||||
|
||||
public interface INotificationHandler
|
||||
{
|
||||
string NotificationType { get; }
|
||||
Type NotificationBlobType { get; }
|
||||
public (string identifier, string name)[] Meta { get; }
|
||||
void FillViewModel(object notification, NotificationViewModel vm);
|
||||
}
|
||||
|
||||
public class NotificationViewModel
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public string Type { get; set; }
|
||||
public DateTimeOffset Created { get; set; }
|
||||
public string Body { get; set; }
|
||||
public string ActionLink { get; set; }
|
||||
public bool Seen { get; set; }
|
||||
}
|
||||
}
|
10
BTCPayServer.Abstractions/Contracts/IPluginHookAction.cs
Normal file
10
BTCPayServer.Abstractions/Contracts/IPluginHookAction.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IPluginHookAction
|
||||
{
|
||||
public string Hook { get; }
|
||||
Task Execute(object args);
|
||||
}
|
||||
}
|
11
BTCPayServer.Abstractions/Contracts/IPluginHookFilter.cs
Normal file
11
BTCPayServer.Abstractions/Contracts/IPluginHookFilter.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IPluginHookFilter
|
||||
{
|
||||
public string Hook { get; }
|
||||
|
||||
Task<object> Execute(object args);
|
||||
}
|
||||
}
|
10
BTCPayServer.Abstractions/Contracts/IPluginHookService.cs
Normal file
10
BTCPayServer.Abstractions/Contracts/IPluginHookService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IPluginHookService
|
||||
{
|
||||
Task ApplyAction(string hook, object args);
|
||||
Task<object> ApplyFilter(string hook, object args);
|
||||
}
|
||||
}
|
7
BTCPayServer.Abstractions/Contracts/IScopeProvider.cs
Normal file
7
BTCPayServer.Abstractions/Contracts/IScopeProvider.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
#nullable enable
|
||||
namespace BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
public interface IScopeProvider
|
||||
{
|
||||
string? GetCurrentStoreId();
|
||||
}
|
13
BTCPayServer.Abstractions/Contracts/ISettingsRepository.cs
Normal file
13
BTCPayServer.Abstractions/Contracts/ISettingsRepository.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#nullable enable
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface ISettingsRepository
|
||||
{
|
||||
Task<T?> GetSettingAsync<T>(string? name = null) where T : class;
|
||||
Task UpdateSetting<T>(T obj, string? name = null) where T : class;
|
||||
Task<T> WaitSettingsChanged<T>(CancellationToken cancellationToken = default) where T : class;
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IStartupTask
|
||||
{
|
12
BTCPayServer.Abstractions/Contracts/IStoreRepository.cs
Normal file
12
BTCPayServer.Abstractions/Contracts/IStoreRepository.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
public interface IStoreRepository
|
||||
{
|
||||
Task<T?> GetSettingAsync<T>(string storeId, string name) where T : class;
|
||||
Task<Dictionary<string, T?>> GetSettingsAsync<T>(string name) where T : class;
|
||||
Task UpdateSetting<T>(string storeId, string name, T obj) where T : class;
|
||||
}
|
12
BTCPayServer.Abstractions/Contracts/IStoredFile.cs
Normal file
12
BTCPayServer.Abstractions/Contracts/IStoredFile.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
public interface IStoredFile
|
||||
{
|
||||
string Id { get; set; }
|
||||
string FileName { get; set; }
|
||||
string StorageFileName { get; set; }
|
||||
DateTime Timestamp { get; set; }
|
||||
string ApplicationUserId { get; set; }
|
||||
}
|
9
BTCPayServer.Abstractions/Contracts/ISwaggerProvider.cs
Normal file
9
BTCPayServer.Abstractions/Contracts/ISwaggerProvider.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
public interface ISwaggerProvider
|
||||
{
|
||||
Task<JObject> Fetch();
|
||||
}
|
18
BTCPayServer.Abstractions/Contracts/ISyncSummaryProvider.cs
Normal file
18
BTCPayServer.Abstractions/Contracts/ISyncSummaryProvider.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface ISyncSummaryProvider
|
||||
{
|
||||
bool AllAvailable();
|
||||
|
||||
string Partial { get; }
|
||||
IEnumerable<ISyncStatus> GetStatuses();
|
||||
}
|
||||
|
||||
public interface ISyncStatus
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public bool Available { get; }
|
||||
}
|
||||
}
|
9
BTCPayServer.Abstractions/Contracts/IUIExtension.cs
Normal file
9
BTCPayServer.Abstractions/Contracts/IUIExtension.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace BTCPayServer.Abstractions.Contracts
|
||||
{
|
||||
public interface IUIExtension
|
||||
{
|
||||
string Partial { get; }
|
||||
|
||||
string Location { get; }
|
||||
}
|
||||
}
|
13
BTCPayServer.Abstractions/Contracts/IUTXOLocker.cs
Normal file
13
BTCPayServer.Abstractions/Contracts/IUTXOLocker.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Payments.PayJoin;
|
||||
|
||||
public interface IUTXOLocker
|
||||
{
|
||||
Task<bool> TryLock(OutPoint outpoint);
|
||||
Task<bool> TryUnlock(params OutPoint[] outPoints);
|
||||
Task<bool> TryLockInputs(OutPoint[] outPoints);
|
||||
Task<HashSet<OutPoint>> FindLocks(OutPoint[] outpoints);
|
||||
}
|
19
BTCPayServer.Abstractions/Converters/VersionConverter.cs
Normal file
19
BTCPayServer.Abstractions/Converters/VersionConverter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Converters
|
||||
{
|
||||
public class VersionConverter : JsonConverter<Version>
|
||||
{
|
||||
public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return Version.Parse(reader.GetString());
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
public class AssetQuoteResult
|
||||
{
|
||||
public string FromAsset { get; set; }
|
||||
public string ToAsset { get; set; }
|
||||
public decimal Bid { get; set; }
|
||||
public decimal Ask { get; set; }
|
||||
|
||||
public AssetQuoteResult() { }
|
||||
|
||||
public AssetQuoteResult(string fromAsset, string toAsset, decimal bid, decimal ask)
|
||||
{
|
||||
FromAsset = fromAsset;
|
||||
ToAsset = toAsset;
|
||||
Bid = bid;
|
||||
Ask = ask;
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class AssetBalancesUnavailableException : CustodianApiException
|
||||
{
|
||||
public AssetBalancesUnavailableException(System.Exception e) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {e.Message}", e)
|
||||
{
|
||||
}
|
||||
|
||||
public AssetBalancesUnavailableException(string errorMsg) : base(500, "asset-balances-unavailable", $"Cannot fetch the asset balances: {errorMsg}")
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class AssetQuoteUnavailableException : CustodianApiException
|
||||
{
|
||||
public AssetPairData AssetPair { get; }
|
||||
|
||||
public AssetQuoteUnavailableException(AssetPairData assetPair) : base(400, "asset-price-unavailable", "Cannot find a quote for pair " + assetPair)
|
||||
{
|
||||
this.AssetPair = assetPair;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class BadConfigException : CustodianApiException
|
||||
{
|
||||
public string[] BadConfigKeys { get; set; }
|
||||
|
||||
public BadConfigException(string[] badConfigKeys) : base(500, "bad-custodian-account-config", "Wrong config values: " + String.Join(", ", badConfigKeys))
|
||||
{
|
||||
this.BadConfigKeys = badConfigKeys;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class CannotWithdrawException : CustodianApiException
|
||||
|
||||
{
|
||||
public CannotWithdrawException(ICustodian custodian, string paymentMethod, string message) : base(403, "cannot-withdraw", message)
|
||||
{
|
||||
}
|
||||
|
||||
public CannotWithdrawException(ICustodian custodian, string paymentMethod, string targetAddress, CustodianApiException originalException) : base(403, "cannot-withdraw", $"{custodian.Name} cannot withdraw {paymentMethod} to '{targetAddress}': {originalException.Message}")
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
public class CustodianApiException : Exception
|
||||
{
|
||||
public int HttpStatus { get; }
|
||||
public string Code { get; }
|
||||
|
||||
public CustodianApiException(int httpStatus, string code, string message, System.Exception ex) : base(message, ex)
|
||||
{
|
||||
HttpStatus = httpStatus;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public CustodianApiException(int httpStatus, string code, string message) : this(httpStatus, code, message, null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class CustodianFeatureNotImplementedException : CustodianApiException
|
||||
{
|
||||
public CustodianFeatureNotImplementedException(string message) : base(400, "not-implemented", message)
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class DepositsUnavailableException : CustodianApiException
|
||||
{
|
||||
public DepositsUnavailableException(string message) : base(404, "deposits-unavailable", message)
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class InsufficientFundsException : CustodianApiException
|
||||
{
|
||||
public InsufficientFundsException(string message) : base(400, "insufficient-funds", message)
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class InvalidWithdrawalTargetException : CustodianApiException
|
||||
|
||||
{
|
||||
public InvalidWithdrawalTargetException(ICustodian custodian, string paymentMethod, string targetAddress, CustodianApiException originalException) : base(403, "invalid-withdrawal-target", $"{custodian.Name} cannot withdraw {paymentMethod} to '{targetAddress}': {originalException.Message}")
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class PermissionDeniedCustodianApiException : CustodianApiException
|
||||
|
||||
{
|
||||
public PermissionDeniedCustodianApiException(ICustodian custodian) : base(403, "custodian-api-permission-denied", $"{custodian.Name}'s API reported that you don't have permission.")
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class TradeNotFoundException : CustodianApiException
|
||||
{
|
||||
private string tradeId { get; }
|
||||
|
||||
public TradeNotFoundException(string tradeId) : base(404, "trade-not-found", "Could not find trade ID " + tradeId)
|
||||
{
|
||||
this.tradeId = tradeId;
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class WithdrawalNotFoundException : CustodianApiException
|
||||
{
|
||||
private string WithdrawalId { get; }
|
||||
|
||||
public WithdrawalNotFoundException(string withdrawalId) : base(404, "withdrawal-not-found", $"Could not find withdrawal ID {withdrawalId}.")
|
||||
{
|
||||
WithdrawalId = withdrawalId;
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public class WrongTradingPairException : CustodianApiException
|
||||
{
|
||||
public const int HttpCode = 404;
|
||||
public WrongTradingPairException(string fromAsset, string toAsset) : base(HttpCode, "wrong-trading-pair", $"Cannot find a trading pair for converting {fromAsset} into {toAsset}.")
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
/**
|
||||
* The result of a market trade. Used as a return type for custodians implementing ICanTrade
|
||||
*/
|
||||
public class MarketTradeResult
|
||||
{
|
||||
public string FromAsset { get; }
|
||||
public string ToAsset { get; }
|
||||
/**
|
||||
* The ledger entries that show the balances that were affected by the trade.
|
||||
*/
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
/**
|
||||
* The unique ID of the trade that was executed.
|
||||
*/
|
||||
public string TradeId { get; }
|
||||
|
||||
public MarketTradeResult(string fromAsset, string toAsset, List<LedgerEntryData> ledgerEntries, string tradeId)
|
||||
{
|
||||
this.FromAsset = fromAsset;
|
||||
this.ToAsset = toAsset;
|
||||
this.LedgerEntries = ledgerEntries;
|
||||
this.TradeId = tradeId;
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.JsonConverters;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
public class SimulateWithdrawalResult
|
||||
{
|
||||
public string PaymentMethod { get; }
|
||||
public string Asset { get; }
|
||||
public decimal MinQty { get; }
|
||||
public decimal MaxQty { get; }
|
||||
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
|
||||
// Fee can be NULL if unknown.
|
||||
public decimal? Fee { get; }
|
||||
|
||||
public SimulateWithdrawalResult(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries,
|
||||
decimal minQty, decimal maxQty)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Asset = asset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
MinQty = minQty;
|
||||
MaxQty = maxQty;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians.Client;
|
||||
|
||||
public class WithdrawResult
|
||||
{
|
||||
public string PaymentMethod { get; }
|
||||
public string Asset { get; set; }
|
||||
public List<LedgerEntryData> LedgerEntries { get; }
|
||||
public string WithdrawalId { get; }
|
||||
public WithdrawalResponseData.WithdrawalStatus Status { get; }
|
||||
public DateTimeOffset CreatedTime { get; }
|
||||
public string TargetAddress { get; }
|
||||
public string TransactionId { get; }
|
||||
|
||||
public WithdrawResult(string paymentMethod, string asset, List<LedgerEntryData> ledgerEntries, string withdrawalId, WithdrawalResponseData.WithdrawalStatus status, DateTimeOffset createdTime, string targetAddress, string transactionId)
|
||||
{
|
||||
PaymentMethod = paymentMethod;
|
||||
Asset = asset;
|
||||
LedgerEntries = ledgerEntries;
|
||||
WithdrawalId = withdrawalId;
|
||||
CreatedTime = createdTime;
|
||||
Status = status;
|
||||
TargetAddress = targetAddress;
|
||||
TransactionId = transactionId;
|
||||
}
|
||||
}
|
17
BTCPayServer.Abstractions/Custodians/ICanDeposit.cs
Normal file
17
BTCPayServer.Abstractions/Custodians/ICanDeposit.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public interface ICanDeposit
|
||||
{
|
||||
/**
|
||||
* Get the address where we can deposit for the chosen payment method (crypto code + network).
|
||||
* The result can be a string in different formats like a bitcoin address or even a LN invoice.
|
||||
*/
|
||||
public Task<DepositAddressData> GetDepositAddressAsync(string paymentMethod, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public string[] GetDepositablePaymentMethods();
|
||||
}
|
31
BTCPayServer.Abstractions/Custodians/ICanTrade.cs
Normal file
31
BTCPayServer.Abstractions/Custodians/ICanTrade.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Custodians.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public interface ICanTrade
|
||||
{
|
||||
/**
|
||||
* A list of tradable asset pairs, or NULL if the custodian cannot trade/convert assets. if thr asset pair contains fiat, fiat is always put last. If both assets are a cyrptocode or both are fiat, the pair is written alphabetically. Always in uppercase. Example: ["BTC/EUR","BTC/USD", "EUR/USD", "BTC/ETH",...]
|
||||
*/
|
||||
public List<AssetPairData> GetTradableAssetPairs();
|
||||
|
||||
/**
|
||||
* Execute a market order right now.
|
||||
*/
|
||||
public Task<MarketTradeResult> TradeMarketAsync(string fromAsset, string toAsset, decimal qty, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
/**
|
||||
* Get the details about a previous market trade.
|
||||
*/
|
||||
public Task<MarketTradeResult> GetTradeInfoAsync(string tradeId, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<AssetQuoteResult> GetQuoteForAssetAsync(string fromAsset, string toAsset, JObject config, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
20
BTCPayServer.Abstractions/Custodians/ICanWithdraw.cs
Normal file
20
BTCPayServer.Abstractions/Custodians/ICanWithdraw.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Custodians.Client;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for custodians that can move funds to the store wallet.
|
||||
/// </summary>
|
||||
public interface ICanWithdraw
|
||||
{
|
||||
public Task<WithdrawResult> WithdrawToStoreWalletAsync(string paymentMethod, decimal amount, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<SimulateWithdrawalResult> SimulateWithdrawalAsync(string paymentMethod, decimal qty, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<WithdrawResult> GetWithdrawalInfoAsync(string paymentMethod, string withdrawalId, JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public string[] GetWithdrawablePaymentMethods();
|
||||
}
|
26
BTCPayServer.Abstractions/Custodians/ICustodian.cs
Normal file
26
BTCPayServer.Abstractions/Custodians/ICustodian.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
public interface ICustodian
|
||||
{
|
||||
/**
|
||||
* Get the unique code that identifies this custodian.
|
||||
*/
|
||||
string Code { get; }
|
||||
|
||||
string Name { get; }
|
||||
|
||||
/**
|
||||
* Get a list of assets and their qty in custody.
|
||||
*/
|
||||
Task<Dictionary<string, decimal>> GetAssetBalancesAsync(JObject config, CancellationToken cancellationToken);
|
||||
|
||||
public Task<Form.Form> GetConfigForm(JObject config, CancellationToken cancellationToken = default);
|
||||
|
||||
}
|
14
BTCPayServer.Abstractions/Extensions/CustodianExtensions.cs
Normal file
14
BTCPayServer.Abstractions/Extensions/CustodianExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Abstractions.Custodians;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class CustodianExtensions
|
||||
{
|
||||
public static ICustodian? GetCustodianByCode(this IEnumerable<ICustodian> custodians, string code)
|
||||
{
|
||||
return custodians.FirstOrDefault(custodian => custodian.Code == code);
|
||||
}
|
||||
}
|
47
BTCPayServer.Abstractions/Extensions/GreenfieldExtensions.cs
Normal file
47
BTCPayServer.Abstractions/Extensions/GreenfieldExtensions.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Client.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class GreenfieldExtensions
|
||||
{
|
||||
public static IActionResult UserNotFound(this ControllerBase ctrl)
|
||||
{
|
||||
return ctrl.CreateAPIError(404, "user-not-found", "The user was not found");
|
||||
}
|
||||
public static IActionResult CreateValidationError(this ControllerBase controller, ModelStateDictionary modelState)
|
||||
{
|
||||
return controller.UnprocessableEntity(modelState.ToGreenfieldValidationError());
|
||||
}
|
||||
|
||||
public static List<GreenfieldValidationError> ToGreenfieldValidationError(this ModelStateDictionary modelState)
|
||||
{
|
||||
List<GreenfieldValidationError> errors = new List<GreenfieldValidationError>();
|
||||
foreach (var error in modelState)
|
||||
{
|
||||
foreach (var errorMessage in error.Value.Errors)
|
||||
{
|
||||
errors.Add(new GreenfieldValidationError(error.Key, errorMessage.ErrorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
public static IActionResult CreateAPIError(this ControllerBase controller, string errorCode, string errorMessage)
|
||||
{
|
||||
return controller.BadRequest(new GreenfieldAPIError(errorCode, errorMessage));
|
||||
}
|
||||
|
||||
public static IActionResult CreateAPIError(this ControllerBase controller, int httpCode, string errorCode, string errorMessage)
|
||||
{
|
||||
return controller.StatusCode(httpCode, new GreenfieldAPIError(errorCode, errorMessage));
|
||||
}
|
||||
|
||||
public static IActionResult CreateAPIPermissionError(this ControllerBase controller, string missingPermission, string message = null)
|
||||
{
|
||||
return controller.StatusCode(403, new GreenfieldPermissionAPIError(missingPermission, message));
|
||||
}
|
||||
}
|
120
BTCPayServer.Abstractions/Extensions/HttpRequestExtensions.cs
Normal file
120
BTCPayServer.Abstractions/Extensions/HttpRequestExtensions.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
public static bool IsOnion(this HttpRequest request)
|
||||
{
|
||||
if (request?.Host.Host == null)
|
||||
return false;
|
||||
return request.Host.Host.EndsWith(".onion", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static string GetAbsoluteRoot(this HttpRequest request)
|
||||
{
|
||||
return string.Concat(
|
||||
request.Scheme,
|
||||
"://",
|
||||
request.Host.ToUriComponent(),
|
||||
request.PathBase.ToUriComponent());
|
||||
}
|
||||
|
||||
public static Uri GetAbsoluteRootUri(this HttpRequest request)
|
||||
{
|
||||
return new Uri(request.GetAbsoluteRoot());
|
||||
}
|
||||
|
||||
public static string GetCurrentUrl(this HttpRequest request)
|
||||
{
|
||||
return string.Concat(
|
||||
request.Scheme,
|
||||
"://",
|
||||
request.Host.ToUriComponent(),
|
||||
request.PathBase.ToUriComponent(),
|
||||
request.Path.ToUriComponent());
|
||||
}
|
||||
|
||||
public static string GetCurrentPath(this HttpRequest request)
|
||||
{
|
||||
return string.Concat(
|
||||
request.PathBase.ToUriComponent(),
|
||||
request.Path.ToUriComponent());
|
||||
}
|
||||
|
||||
public static string GetCurrentPathWithQueryString(this HttpRequest request)
|
||||
{
|
||||
return request.PathBase + request.Path + request.QueryString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If 'toto' and RootPath is 'rootpath' returns '/rootpath/toto'
|
||||
/// If 'toto' and RootPath is empty returns '/toto'
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetRelativePath(this HttpRequest request, string path)
|
||||
{
|
||||
if (path.Length > 0 && path[0] != '/')
|
||||
path = $"/{path}";
|
||||
return string.Concat(
|
||||
request.PathBase.ToUriComponent(),
|
||||
path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If 'https://example.com/toto' returns 'https://example.com/toto'
|
||||
/// If 'toto' and RootPath is 'rootpath' returns '/rootpath/toto'
|
||||
/// If 'toto' and RootPath is empty returns '/toto'
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetRelativePathOrAbsolute(this HttpRequest request, string path)
|
||||
{
|
||||
if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uri) ||
|
||||
uri.IsAbsoluteUri)
|
||||
return path;
|
||||
|
||||
if (path.Length > 0 && path[0] != '/')
|
||||
path = $"/{path}";
|
||||
return string.Concat(
|
||||
request.PathBase.ToUriComponent(),
|
||||
path);
|
||||
}
|
||||
|
||||
public static string GetAbsoluteUri(this HttpRequest request, string redirectUrl)
|
||||
{
|
||||
bool isRelative =
|
||||
(redirectUrl.Length > 0 && redirectUrl[0] == '/')
|
||||
|| !new Uri(redirectUrl, UriKind.RelativeOrAbsolute).IsAbsoluteUri;
|
||||
return isRelative ? request.GetAbsoluteRoot() + redirectUrl : redirectUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will return an absolute URL.
|
||||
/// If `relativeOrAsbolute` is absolute, returns it.
|
||||
/// If `relativeOrAsbolute` is relative, send absolute url based on the HOST of this request (without PathBase)
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="relativeOrAbsolte"></param>
|
||||
/// <returns></returns>
|
||||
public static Uri GetAbsoluteUriNoPathBase(this HttpRequest request, Uri relativeOrAbsolute = null)
|
||||
{
|
||||
if (relativeOrAbsolute == null)
|
||||
{
|
||||
return new Uri(string.Concat(
|
||||
request.Scheme,
|
||||
"://",
|
||||
request.Host.ToUriComponent()), UriKind.Absolute);
|
||||
}
|
||||
if (relativeOrAbsolute.IsAbsoluteUri)
|
||||
return relativeOrAbsolute;
|
||||
return new Uri(string.Concat(
|
||||
request.Scheme,
|
||||
"://",
|
||||
request.Host.ToUriComponent()) + relativeOrAbsolute.ToString().WithStartingSlash(), UriKind.Absolute);
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
using System.Text.Json;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class SetStatusMessageModelExtensions
|
||||
{
|
||||
public static void SetStatusMessageModel(this ITempDataDictionary tempData, StatusMessageModel statusMessage)
|
||||
{
|
||||
if (statusMessage == null)
|
||||
{
|
||||
tempData.Remove("StatusMessageModel");
|
||||
return;
|
||||
}
|
||||
|
||||
tempData["StatusMessageModel"] = JsonSerializer.Serialize(statusMessage, new JsonSerializerOptions());
|
||||
}
|
||||
|
||||
public static StatusMessageModel GetStatusMessageModel(this ITempDataDictionary tempData)
|
||||
{
|
||||
tempData.TryGetValue(WellKnownTempData.SuccessMessage, out var successMessage);
|
||||
tempData.TryGetValue(WellKnownTempData.ErrorMessage, out var errorMessage);
|
||||
tempData.TryGetValue("StatusMessageModel", out var model);
|
||||
if (successMessage != null || errorMessage != null)
|
||||
{
|
||||
var parsedModel = new StatusMessageModel();
|
||||
parsedModel.Message = (string)successMessage ?? (string)errorMessage;
|
||||
if (successMessage != null)
|
||||
{
|
||||
parsedModel.Severity = StatusMessageModel.StatusSeverity.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedModel.Severity = StatusMessageModel.StatusSeverity.Error;
|
||||
}
|
||||
return parsedModel;
|
||||
}
|
||||
else if (model != null && model is string str)
|
||||
{
|
||||
return JObject.Parse(str).ToObject<StatusMessageModel>();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool HasStatusMessage(this ITempDataDictionary tempData)
|
||||
{
|
||||
return (tempData.Peek(WellKnownTempData.SuccessMessage) ??
|
||||
tempData.Peek(WellKnownTempData.ErrorMessage) ??
|
||||
tempData.Peek("StatusMessageModel")) != null;
|
||||
}
|
||||
|
||||
public static bool HasErrorMessage(this ITempDataDictionary tempData)
|
||||
{
|
||||
return GetStatusMessageModel(tempData)?.Severity == StatusMessageModel.StatusSeverity.Error;
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
namespace BTCPayServer.Abstractions.Extensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
43
BTCPayServer.Abstractions/Extensions/StringExtensions.cs
Normal file
43
BTCPayServer.Abstractions/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static bool IsValidFileName(this string fileName)
|
||||
{
|
||||
return !fileName.ToCharArray().Any(c => Path.GetInvalidFileNameChars().Contains(c)
|
||||
|| c == Path.AltDirectorySeparatorChar
|
||||
|| c == Path.DirectorySeparatorChar
|
||||
|| c == Path.PathSeparator
|
||||
|| c == '\\');
|
||||
}
|
||||
|
||||
public static string Truncate(this string value, int maxLength)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return value;
|
||||
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
|
||||
}
|
||||
|
||||
public static string WithTrailingSlash(this string str)
|
||||
{
|
||||
if (str.EndsWith("/", StringComparison.InvariantCulture))
|
||||
return str;
|
||||
return str + "/";
|
||||
}
|
||||
public static string WithStartingSlash(this string str)
|
||||
{
|
||||
if (str.StartsWith("/", StringComparison.InvariantCulture))
|
||||
return str;
|
||||
return $"/{str}";
|
||||
}
|
||||
public static string WithoutEndingSlash(this string str)
|
||||
{
|
||||
if (str.EndsWith("/", StringComparison.InvariantCulture))
|
||||
return str.Substring(0, str.Length - 1);
|
||||
return str;
|
||||
}
|
||||
}
|
139
BTCPayServer.Abstractions/Extensions/ViewsRazor.cs
Normal file
139
BTCPayServer.Abstractions/Extensions/ViewsRazor.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Extensions
|
||||
{
|
||||
public static class ViewsRazor
|
||||
{
|
||||
private const string ACTIVE_CATEGORY_KEY = "ActiveCategory";
|
||||
private const string ACTIVE_PAGE_KEY = "ActivePage";
|
||||
private const string ACTIVE_ID_KEY = "ActiveId";
|
||||
private const string ActivePageClass = "active";
|
||||
|
||||
public enum DateDisplayFormat
|
||||
{
|
||||
Localized,
|
||||
Relative
|
||||
}
|
||||
|
||||
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
SetActivePage(viewData, activePage.ToString(), activePage.GetType().ToString(), title, activeId);
|
||||
}
|
||||
|
||||
public static void SetActivePage(this ViewDataDictionary viewData, string activePage, string category, string title = null, string activeId = null)
|
||||
{
|
||||
// Page Title
|
||||
viewData["Title"] = title ?? activePage;
|
||||
// Navigation
|
||||
viewData[ACTIVE_PAGE_KEY] = activePage;
|
||||
viewData[ACTIVE_ID_KEY] = activeId;
|
||||
SetActiveCategory(viewData, category);
|
||||
}
|
||||
|
||||
public static void SetActiveCategory<T>(this ViewDataDictionary viewData, T activeCategory)
|
||||
{
|
||||
SetActiveCategory(viewData, activeCategory.ToString());
|
||||
}
|
||||
|
||||
public static void SetActiveCategory(this ViewDataDictionary viewData, string activeCategory)
|
||||
{
|
||||
viewData[ACTIVE_CATEGORY_KEY] = activeCategory;
|
||||
}
|
||||
|
||||
public static string IsActiveCategory<T>(this ViewDataDictionary viewData, T category, object id = null)
|
||||
{
|
||||
return IsActiveCategory(viewData, category.ToString(), id);
|
||||
}
|
||||
|
||||
public static string IsActiveCategory(this ViewDataDictionary viewData, string category, object id = null)
|
||||
{
|
||||
if (!viewData.ContainsKey(ACTIVE_CATEGORY_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var activeId = viewData[ACTIVE_ID_KEY];
|
||||
var activeCategory = viewData[ACTIVE_CATEGORY_KEY]?.ToString();
|
||||
var categoryMatch = category.Equals(activeCategory, StringComparison.InvariantCultureIgnoreCase);
|
||||
var idMatch = id == null || activeId == null || id.Equals(activeId);
|
||||
return categoryMatch && idMatch ? ActivePageClass : null;
|
||||
}
|
||||
|
||||
public static string IsActivePage<T>(this ViewDataDictionary viewData, T page, object id = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
return IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id);
|
||||
}
|
||||
|
||||
public static string IsActivePage<T>(this ViewDataDictionary viewData, IEnumerable<T> pages, object id = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
return pages.Any(page => IsActivePage(viewData, page.ToString(), page.GetType().ToString(), id) == ActivePageClass)
|
||||
? ActivePageClass
|
||||
: null;
|
||||
}
|
||||
|
||||
public static string IsActivePage(this ViewDataDictionary viewData, string page, string category, object id = null)
|
||||
{
|
||||
if (!viewData.ContainsKey(ACTIVE_PAGE_KEY))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var activeId = viewData[ACTIVE_ID_KEY];
|
||||
var activePage = viewData[ACTIVE_PAGE_KEY]?.ToString();
|
||||
var activeCategory = viewData[ACTIVE_CATEGORY_KEY]?.ToString();
|
||||
var categoryAndPageMatch = (category == null || activeCategory.Equals(category, StringComparison.InvariantCultureIgnoreCase)) && page.Equals(activePage, StringComparison.InvariantCultureIgnoreCase);
|
||||
var idMatch = id == null || activeId == null || id.Equals(activeId);
|
||||
return categoryAndPageMatch && idMatch ? ActivePageClass : null;
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTimeOffset date, DateDisplayFormat format = DateDisplayFormat.Localized)
|
||||
{
|
||||
var relative = date.ToTimeAgo();
|
||||
var initial = format.ToString().ToLower();
|
||||
var dateTime = date.ToString("o", CultureInfo.InvariantCulture);
|
||||
var displayDate = format == DateDisplayFormat.Relative ? relative : date.ToString("g", CultureInfo.InvariantCulture);
|
||||
return new HtmlString($"<time datetime=\"{dateTime}\" data-relative=\"{relative}\" data-initial=\"{initial}\">{displayDate}</time>");
|
||||
}
|
||||
|
||||
public static HtmlString ToBrowserDate(this DateTime date, DateDisplayFormat format = DateDisplayFormat.Localized)
|
||||
{
|
||||
var relative = date.ToTimeAgo();
|
||||
var initial = format.ToString().ToLower();
|
||||
var dateTime = date.ToString("o", CultureInfo.InvariantCulture);
|
||||
var displayDate = format == DateDisplayFormat.Relative ? relative : date.ToString("g", CultureInfo.InvariantCulture);
|
||||
return new HtmlString($"<time datetime=\"{dateTime}\" data-relative=\"{relative}\" data-initial=\"{initial}\">{displayDate}</time>");
|
||||
}
|
||||
|
||||
public static string ToTimeAgo(this DateTimeOffset date) => (DateTimeOffset.UtcNow - date).ToTimeAgo();
|
||||
|
||||
public static string ToTimeAgo(this DateTime date) => (DateTimeOffset.UtcNow - date).ToTimeAgo();
|
||||
|
||||
public static string ToTimeAgo(this TimeSpan diff) => diff.TotalSeconds > 0 ? $"{diff.TimeString()} ago" : $"in {diff.Negate().TimeString()}";
|
||||
|
||||
public static string TimeString(this TimeSpan timeSpan)
|
||||
{
|
||||
if (timeSpan.TotalMinutes < 1)
|
||||
{
|
||||
return $"{(int)timeSpan.TotalSeconds} second{Plural((int)timeSpan.TotalSeconds)}";
|
||||
}
|
||||
if (timeSpan.TotalHours < 1)
|
||||
{
|
||||
return $"{(int)timeSpan.TotalMinutes} minute{Plural((int)timeSpan.TotalMinutes)}";
|
||||
}
|
||||
return timeSpan.Days < 1
|
||||
? $"{(int)timeSpan.TotalHours} hour{Plural((int)timeSpan.TotalHours)}"
|
||||
: $"{(int)timeSpan.TotalDays} day{Plural((int)timeSpan.TotalDays)}";
|
||||
}
|
||||
|
||||
private static string Plural(int value)
|
||||
{
|
||||
return value == 1 ? string.Empty : "s";
|
||||
}
|
||||
}
|
||||
}
|
37
BTCPayServer.Abstractions/Form/AlertMessage.cs
Normal file
37
BTCPayServer.Abstractions/Form/AlertMessage.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Form;
|
||||
|
||||
public class AlertMessage
|
||||
{
|
||||
// Corresponds to the Bootstrap CSS "alert alert-xxx" messages:
|
||||
// Success = green
|
||||
// Warning = orange
|
||||
// Danger = red
|
||||
// Info = blue
|
||||
public enum AlertMessageType
|
||||
{
|
||||
Success,
|
||||
Warning,
|
||||
Danger,
|
||||
Info
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public AlertMessageType Type;
|
||||
|
||||
// The translated message to be shown to the user
|
||||
public string Message;
|
||||
|
||||
public AlertMessage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public AlertMessage(AlertMessageType type, string message)
|
||||
{
|
||||
this.Type = type;
|
||||
this.Message = message;
|
||||
}
|
||||
}
|
64
BTCPayServer.Abstractions/Form/Field.cs
Normal file
64
BTCPayServer.Abstractions/Form/Field.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Form;
|
||||
|
||||
public class Field
|
||||
{
|
||||
public static Field Create(string label, string name, string value, bool required, string helpText, string type = "text")
|
||||
{
|
||||
return new Field
|
||||
{
|
||||
Label = label,
|
||||
Name = name,
|
||||
Value = value,
|
||||
OriginalValue = value,
|
||||
Required = required,
|
||||
HelpText = helpText,
|
||||
Type = type
|
||||
};
|
||||
}
|
||||
// The name of the HTML5 node. Should be used as the key for the posted data.
|
||||
public string Name;
|
||||
|
||||
public bool Constant;
|
||||
|
||||
// HTML5 compatible type string like "text", "textarea", "email", "password", etc.
|
||||
public string Type;
|
||||
|
||||
public static Field CreateFieldset()
|
||||
{
|
||||
return new Field { Type = "fieldset" };
|
||||
}
|
||||
|
||||
// The value field is what is currently in the DB or what the user entered, but possibly not saved yet due to validation errors.
|
||||
// If this is the first the user sees the form, then value and original value are the same. Value changes as the user starts interacting with the form.
|
||||
public string Value;
|
||||
|
||||
public bool Required;
|
||||
|
||||
// The translated label of the field.
|
||||
public string Label;
|
||||
|
||||
// The original value is the value that is currently saved in the backend. A "reset" button can be used to revert back to this. Should only be set from the constructor.
|
||||
public string OriginalValue;
|
||||
|
||||
// A useful note shown below the field or via a tooltip / info icon. Should be translated for the user.
|
||||
public string HelpText;
|
||||
|
||||
[JsonExtensionData] public IDictionary<string, JToken> AdditionalData { get; set; }
|
||||
public List<Field> Fields { get; set; } = new();
|
||||
|
||||
// The field is considered "valid" if there are no validation errors
|
||||
public List<string> ValidationErrors = new();
|
||||
|
||||
public virtual bool IsValid()
|
||||
{
|
||||
return ValidationErrors.Count == 0 && Fields.All(field => field.IsValid());
|
||||
}
|
||||
}
|
141
BTCPayServer.Abstractions/Form/Form.cs
Normal file
141
BTCPayServer.Abstractions/Form/Form.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.Internal.TypeHandlers.GeometricHandlers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Form;
|
||||
|
||||
public class Form
|
||||
{
|
||||
#nullable enable
|
||||
public static Form Parse(string str)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
return JObject.Parse(str).ToObject<Form>(CamelCaseSerializerSettings.Serializer) ?? throw new InvalidOperationException("Impossible to deserialize Form");
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return JObject.FromObject(this, CamelCaseSerializerSettings.Serializer).ToString(Newtonsoft.Json.Formatting.Indented);
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
// Messages to be shown at the top of the form indicating user feedback like "Saved successfully" or "Please change X because of Y." or a warning, etc...
|
||||
public List<AlertMessage> TopMessages { get; set; } = new();
|
||||
|
||||
// Groups of fields in the form
|
||||
public List<Field> Fields { get; set; } = new();
|
||||
|
||||
// Are all the fields valid in the form?
|
||||
public bool IsValid()
|
||||
{
|
||||
if (TopMessages?.Any(t => t.Type == AlertMessage.AlertMessageType.Danger) is true)
|
||||
return false;
|
||||
return Fields.Select(f => f.IsValid()).All(o => o);
|
||||
}
|
||||
|
||||
public Field GetFieldByFullName(string fullName)
|
||||
{
|
||||
foreach (var f in GetAllFields())
|
||||
{
|
||||
if (f.FullName == fullName)
|
||||
return f.Field;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public IEnumerable<(string FullName, List<string> Path, Field Field)> GetAllFields()
|
||||
{
|
||||
HashSet<string> nameReturned = new();
|
||||
foreach (var f in GetAllFieldsCore(new List<string>(), Fields))
|
||||
{
|
||||
var fullName = string.Join('_', f.Path.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (!nameReturned.Add(fullName))
|
||||
continue;
|
||||
yield return (fullName, f.Path, f.Field);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ValidateFieldNames(out List<string> errors)
|
||||
{
|
||||
errors = new List<string>();
|
||||
HashSet<string> nameReturned = new();
|
||||
foreach (var f in GetAllFieldsCore(new List<string>(), Fields))
|
||||
{
|
||||
var fullName = string.Join('_', f.Path.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (!nameReturned.Add(fullName))
|
||||
{
|
||||
errors.Add($"Form contains duplicate field names '{fullName}'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return errors.Count == 0;
|
||||
}
|
||||
|
||||
IEnumerable<(List<string> Path, Field Field)> GetAllFieldsCore(List<string> path, List<Field> fields)
|
||||
{
|
||||
foreach (var field in fields)
|
||||
{
|
||||
List<string> thisPath = new(path.Count + 1);
|
||||
thisPath.AddRange(path);
|
||||
if (!string.IsNullOrEmpty(field.Name))
|
||||
{
|
||||
thisPath.Add(field.Name);
|
||||
yield return (thisPath, field);
|
||||
}
|
||||
|
||||
foreach (var child in field.Fields)
|
||||
{
|
||||
if (field.Constant)
|
||||
child.Constant = true;
|
||||
foreach (var descendant in GetAllFieldsCore(thisPath, field.Fields))
|
||||
{
|
||||
yield return descendant;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyValuesFromForm(IEnumerable<KeyValuePair<string, StringValues>> form)
|
||||
{
|
||||
var values = form.GroupBy(f => f.Key, f => f.Value).ToDictionary(g => g.Key, g => g.First());
|
||||
foreach (var f in GetAllFields())
|
||||
{
|
||||
if (f.Field.Constant || !values.TryGetValue(f.FullName, out var val))
|
||||
continue;
|
||||
|
||||
f.Field.Value = val;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValues(JObject values)
|
||||
{
|
||||
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
|
||||
SetValues(fields, new List<string>(), values);
|
||||
}
|
||||
|
||||
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
|
||||
{
|
||||
foreach (var prop in values.Properties())
|
||||
{
|
||||
List<string> propPath = new List<string>(path.Count + 1);
|
||||
propPath.AddRange(path);
|
||||
propPath.Add(prop.Name);
|
||||
if (prop.Value.Type == JTokenType.Object)
|
||||
{
|
||||
SetValues(fields, propPath, (JObject)prop.Value);
|
||||
}
|
||||
else if (prop.Value.Type == JTokenType.String)
|
||||
{
|
||||
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
|
||||
f.Value = prop.Value.Value<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
69
BTCPayServer.Abstractions/Models/BaseBTCPayServerPlugin.cs
Normal file
69
BTCPayServer.Abstractions/Models/BaseBTCPayServerPlugin.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Models
|
||||
{
|
||||
public abstract class BaseBTCPayServerPlugin : IBTCPayServerPlugin
|
||||
{
|
||||
public virtual string Identifier
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetType().GetTypeInfo().Assembly.GetName().Name;
|
||||
}
|
||||
}
|
||||
public virtual string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetType().GetTypeInfo().Assembly
|
||||
.GetCustomAttribute<AssemblyProductAttribute>()?
|
||||
.Product ?? "???";
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Version Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetVersion(GetType().GetTypeInfo().Assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||
.InformationalVersion) ??
|
||||
Assembly.GetAssembly(GetType())?.GetName()?.Version ??
|
||||
new Version(1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static Version GetVersion(string informationalVersion)
|
||||
{
|
||||
if (informationalVersion is null)
|
||||
return null;
|
||||
Version.TryParse(informationalVersion, out var r);
|
||||
return r;
|
||||
}
|
||||
|
||||
public virtual string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetType().GetTypeInfo().Assembly
|
||||
.GetCustomAttribute<AssemblyDescriptionAttribute>()?
|
||||
.Description ?? string.Empty;
|
||||
}
|
||||
}
|
||||
public bool SystemPlugin { get; set; }
|
||||
public virtual IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = Array.Empty<IBTCPayServerPlugin.PluginDependency>();
|
||||
|
||||
public virtual void Execute(IApplicationBuilder applicationBuilder,
|
||||
IServiceProvider applicationBuilderApplicationServices)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
34
BTCPayServer.Abstractions/Models/ConfirmModel.cs
Normal file
34
BTCPayServer.Abstractions/Models/ConfirmModel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Models
|
||||
{
|
||||
public class ConfirmModel
|
||||
{
|
||||
private const string ButtonClassDefault = "btn-danger";
|
||||
|
||||
public ConfirmModel() { }
|
||||
|
||||
public ConfirmModel(string title, string desc, string action = null, string buttonClass = ButtonClassDefault, string actionName = null, string controllerName = null)
|
||||
{
|
||||
Title = title;
|
||||
Description = desc;
|
||||
Action = action;
|
||||
ActionName = actionName;
|
||||
ControllerName = controllerName;
|
||||
ButtonClass = buttonClass;
|
||||
|
||||
if (Description.Contains("<strong>", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
DescriptionHtml = true;
|
||||
}
|
||||
}
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool DescriptionHtml { get; set; }
|
||||
public string Action { get; set; }
|
||||
public string ActionName { get; set; }
|
||||
public string ControllerName { get; set; }
|
||||
public string ButtonClass { get; set; } = ButtonClassDefault;
|
||||
}
|
||||
}
|
8
BTCPayServer.Abstractions/Models/DatabaseOptions.cs
Normal file
8
BTCPayServer.Abstractions/Models/DatabaseOptions.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace BTCPayServer.Abstractions.Models
|
||||
{
|
||||
public class DatabaseOptions
|
||||
{
|
||||
public DatabaseType DatabaseType { get; set; }
|
||||
public string ConnectionString { get; set; }
|
||||
}
|
||||
}
|
9
BTCPayServer.Abstractions/Models/DatabaseType.cs
Normal file
9
BTCPayServer.Abstractions/Models/DatabaseType.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace BTCPayServer.Abstractions.Models
|
||||
{
|
||||
public enum DatabaseType
|
||||
{
|
||||
Sqlite,
|
||||
Postgres,
|
||||
MySQL,
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Models
|
||||
namespace BTCPayServer.Abstractions.Models
|
||||
{
|
||||
public class StatusMessageModel
|
||||
{
|
7
BTCPayServer.Abstractions/PushNuget.ps1
Normal file
7
BTCPayServer.Abstractions/PushNuget.ps1
Normal file
@@ -0,0 +1,7 @@
|
||||
rm "bin\release\" -Recurse -Force
|
||||
dotnet pack --configuration Release --include-symbols -p:SymbolPackageFormat=snupkg
|
||||
$package=(ls .\bin\Release\*.nupkg).FullName
|
||||
dotnet nuget push $package --source "https://api.nuget.org/v3/index.json"
|
||||
$ver = ((ls .\bin\release\*.nupkg)[0].Name -replace '.*(\d+\.\d+\.\d+)\.nupkg','$1')
|
||||
git tag -a "BTCPayServer.Abstractions/v$ver" -m "BTCPayServer.Abstractions/$ver"
|
||||
git push origin "BTCPayServer.Abstractions/v$ver"
|
@@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Security;
|
||||
|
||||
public class AuthorizationFilterHandle
|
||||
{
|
||||
public AuthorizationHandlerContext Context { get; }
|
||||
public PolicyRequirement Requirement { get; }
|
||||
public HttpContext HttpContext { get; }
|
||||
public bool Success { get; private set; }
|
||||
|
||||
public AuthorizationFilterHandle(
|
||||
AuthorizationHandlerContext context,
|
||||
PolicyRequirement requirement,
|
||||
HttpContext httpContext)
|
||||
{
|
||||
Context = context;
|
||||
Requirement = requirement;
|
||||
HttpContext = httpContext;
|
||||
}
|
||||
|
||||
public void MarkSuccessful()
|
||||
{
|
||||
Success = true;
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NBitcoin.Crypto;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
@@ -9,6 +10,8 @@ namespace BTCPayServer.Security
|
||||
{
|
||||
public ConsentSecurityPolicy(string name, string value)
|
||||
{
|
||||
if (value.Contains(';', StringComparison.OrdinalIgnoreCase))
|
||||
throw new FormatException();
|
||||
_Value = value;
|
||||
_Name = name;
|
||||
}
|
||||
@@ -67,13 +70,55 @@ namespace BTCPayServer.Security
|
||||
}
|
||||
|
||||
readonly HashSet<ConsentSecurityPolicy> _Policies = new HashSet<ConsentSecurityPolicy>();
|
||||
|
||||
/// <summary>
|
||||
/// Allow a specific script as event handler
|
||||
/// </summary>
|
||||
/// <param name="script"></param>
|
||||
public void AllowUnsafeHashes(string script = null)
|
||||
{
|
||||
if (!allowUnsafeHashes)
|
||||
{
|
||||
Add("script-src", $"'unsafe-hashes'");
|
||||
allowUnsafeHashes = true;
|
||||
}
|
||||
if (script != null)
|
||||
{
|
||||
var sha = GetSha256(script);
|
||||
Add("script-src", $"'sha256-{sha}'");
|
||||
}
|
||||
}
|
||||
|
||||
bool allowUnsafeHashes = false;
|
||||
/// <summary>
|
||||
/// Allow the injection of script tag with the following script
|
||||
/// </summary>
|
||||
/// <param name="script"></param>
|
||||
public void AllowInline(string script)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(script);
|
||||
var sha = GetSha256(script);
|
||||
Add("script-src", $"'sha256-{sha}'");
|
||||
}
|
||||
static string GetSha256(string script)
|
||||
{
|
||||
return Convert.ToBase64String(Hashes.SHA256(Encoding.UTF8.GetBytes(script.Replace("\r\n", "\n", StringComparison.Ordinal))));
|
||||
}
|
||||
|
||||
public void Add(string name, string value)
|
||||
{
|
||||
Add(new ConsentSecurityPolicy(name, value));
|
||||
}
|
||||
public void Add(ConsentSecurityPolicy policy)
|
||||
{
|
||||
if (_Policies.Any(p => p.Name == policy.Name && p.Value == policy.Name))
|
||||
return;
|
||||
_Policies.Add(policy);
|
||||
}
|
||||
|
||||
public void UnsafeEval()
|
||||
{
|
||||
Add("script-src", "'unsafe-eval'");
|
||||
}
|
||||
|
||||
public IEnumerable<ConsentSecurityPolicy> Rules => _Policies;
|
||||
public bool HasRules => _Policies.Count != 0;
|
||||
|
||||
@@ -87,34 +132,19 @@ namespace BTCPayServer.Security
|
||||
{
|
||||
value.Append(';');
|
||||
}
|
||||
List<string> values = new List<string>();
|
||||
HashSet<string> values = new HashSet<string>();
|
||||
List<string> valuesList = new List<string>();
|
||||
values.Add(group.Key);
|
||||
valuesList.Add(group.Key);
|
||||
foreach (var v in group)
|
||||
{
|
||||
values.Add(v.Value);
|
||||
if (values.Add(v.Value))
|
||||
valuesList.Add(v.Value);
|
||||
}
|
||||
foreach (var i in authorized)
|
||||
{
|
||||
values.Add(i);
|
||||
}
|
||||
value.Append(String.Join(" ", values.OfType<object>().ToArray()));
|
||||
value.Append(String.Join(" ", valuesList.OfType<object>().ToArray()));
|
||||
firstGroup = false;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
authorized.Clear();
|
||||
_Policies.Clear();
|
||||
}
|
||||
|
||||
readonly HashSet<string> authorized = new HashSet<string>();
|
||||
internal void AddAllAuthorized(string v)
|
||||
{
|
||||
authorized.Add(v);
|
||||
}
|
||||
|
||||
public IEnumerable<string> Authorized => authorized;
|
||||
}
|
||||
}
|
@@ -7,8 +7,7 @@ namespace BTCPayServer.Security
|
||||
{
|
||||
public PolicyRequirement(string policy)
|
||||
{
|
||||
if (policy == null)
|
||||
throw new ArgumentNullException(nameof(policy));
|
||||
ArgumentNullException.ThrowIfNull(policy);
|
||||
Policy = policy;
|
||||
}
|
||||
public string Policy { get; }
|
16
BTCPayServer.Abstractions/Services/PluginAction.cs
Normal file
16
BTCPayServer.Abstractions/Services/PluginAction.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Services
|
||||
{
|
||||
public abstract class PluginAction<T> : IPluginHookAction
|
||||
{
|
||||
public abstract string Hook { get; }
|
||||
public Task Execute(object args)
|
||||
{
|
||||
return Execute(args is T args1 ? args1 : default);
|
||||
}
|
||||
|
||||
public abstract Task Execute(T arg);
|
||||
}
|
||||
}
|
17
BTCPayServer.Abstractions/Services/PluginHookFilter.cs
Normal file
17
BTCPayServer.Abstractions/Services/PluginHookFilter.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Services
|
||||
{
|
||||
public abstract class PluginHookFilter<T> : IPluginHookFilter
|
||||
{
|
||||
public abstract string Hook { get; }
|
||||
|
||||
public Task<object> Execute(object args)
|
||||
{
|
||||
return Execute(args is T args1 ? args1 : default).ContinueWith(task => task.Result as object);
|
||||
}
|
||||
|
||||
public abstract Task<T> Execute(T arg);
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@ using Ganss.XSS;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
namespace BTCPayServer.Abstractions.Services
|
||||
{
|
||||
public class Safe
|
||||
{
|
16
BTCPayServer.Abstractions/Services/UIExtension.cs
Normal file
16
BTCPayServer.Abstractions/Services/UIExtension.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Services
|
||||
{
|
||||
public class UIExtension : IUIExtension
|
||||
{
|
||||
public UIExtension(string partial, string location)
|
||||
{
|
||||
Partial = partial;
|
||||
Location = location;
|
||||
}
|
||||
|
||||
public string Partial { get; }
|
||||
public string Location { get; }
|
||||
}
|
||||
}
|
30
BTCPayServer.Abstractions/TagHelpers/CSPA.cs
Normal file
30
BTCPayServer.Abstractions/TagHelpers/CSPA.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
/// <summary>
|
||||
/// Add sha256- to allow inline event handlers in a:href=javascript:
|
||||
/// </summary>
|
||||
[HtmlTargetElement("a", Attributes = "csp-allow")]
|
||||
public class CSPA : TagHelper
|
||||
{
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
public CSPA(ContentSecurityPolicies csp)
|
||||
{
|
||||
_csp = csp;
|
||||
}
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
output.Attributes.RemoveAll("csp-allow");
|
||||
if (output.Attributes.TryGetAttribute("href", out var attr))
|
||||
{
|
||||
var v = attr.Value.ToString();
|
||||
if (v.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_csp.AllowUnsafeHashes(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
BTCPayServer.Abstractions/TagHelpers/CSPEventTagHelper.cs
Normal file
37
BTCPayServer.Abstractions/TagHelpers/CSPEventTagHelper.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
/// <summary>
|
||||
/// Add 'unsafe-hashes' and sha256- to allow inline event handlers in CSP
|
||||
/// </summary>
|
||||
[HtmlTargetElement(Attributes = "onclick")]
|
||||
[HtmlTargetElement(Attributes = "onkeypress")]
|
||||
[HtmlTargetElement(Attributes = "onchange")]
|
||||
[HtmlTargetElement(Attributes = "onsubmit")]
|
||||
public class CSPEventTagHelper : TagHelper
|
||||
{
|
||||
public const string EventNames = "onclick,onkeypress,onchange,onsubmit";
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
|
||||
readonly static HashSet<string> EventSet = EventNames.Split(',')
|
||||
.ToHashSet();
|
||||
public CSPEventTagHelper(ContentSecurityPolicies csp)
|
||||
{
|
||||
_csp = csp;
|
||||
}
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
foreach (var attr in output.Attributes)
|
||||
{
|
||||
var n = attr.Name.ToLowerInvariant();
|
||||
if (EventSet.Contains(n))
|
||||
{
|
||||
_csp.AllowUnsafeHashes(attr.Value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
/// <summary>
|
||||
/// Add a nonce-* so the inline-script can pass CSP rule when they are rendered server-side
|
||||
/// </summary>
|
||||
[HtmlTargetElement("script")]
|
||||
public class CSPInlineScriptTagHelper : TagHelper
|
||||
{
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
|
||||
public CSPInlineScriptTagHelper(ContentSecurityPolicies csp)
|
||||
{
|
||||
_csp = csp;
|
||||
}
|
||||
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (output.Attributes.ContainsName("src"))
|
||||
return;
|
||||
if (output.Attributes.TryGetAttribute("type", out var attr))
|
||||
{
|
||||
if (attr.Value?.ToString() != "text/javascript")
|
||||
return;
|
||||
}
|
||||
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
||||
output.Attributes.Add(new TagHelperAttribute("nonce", nonce));
|
||||
_csp.Add("script-src", $"'nonce-{nonce}'");
|
||||
}
|
||||
}
|
25
BTCPayServer.Abstractions/TagHelpers/CSPTemplate.cs
Normal file
25
BTCPayServer.Abstractions/TagHelpers/CSPTemplate.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
/// <summary>
|
||||
/// Add sha256- to allow inline event handlers in CSP
|
||||
/// </summary>
|
||||
[HtmlTargetElement("template", Attributes = "csp-allow")]
|
||||
public class CSPTemplate : TagHelper
|
||||
{
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
public CSPTemplate(ContentSecurityPolicies csp)
|
||||
{
|
||||
_csp = csp;
|
||||
}
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
output.Attributes.RemoveAll("csp-allow");
|
||||
var childContent = await output.GetChildContentAsync();
|
||||
var content = childContent.GetContent();
|
||||
_csp.AllowInline(content);
|
||||
}
|
||||
}
|
48
BTCPayServer.Abstractions/TagHelpers/PermissionTagHelper.cs
Normal file
48
BTCPayServer.Abstractions/TagHelpers/PermissionTagHelper.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
[HtmlTargetElement(Attributes = nameof(Permission))]
|
||||
public class PermissionTagHelper : TagHelper
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<PermissionTagHelper> _logger;
|
||||
|
||||
public PermissionTagHelper(IAuthorizationService authorizationService, IHttpContextAccessor httpContextAccessor, ILogger<PermissionTagHelper> logger)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string Permission { get; set; }
|
||||
public string PermissionResource { get; set; }
|
||||
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Permission))
|
||||
return;
|
||||
if (_httpContextAccessor.HttpContext is null)
|
||||
return;
|
||||
|
||||
var key = $"{Permission}_{PermissionResource}";
|
||||
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var o) ||
|
||||
o is not AuthorizationResult res)
|
||||
{
|
||||
res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
|
||||
PermissionResource,
|
||||
Permission);
|
||||
_httpContextAccessor.HttpContext.Items.Add(key, res);
|
||||
}
|
||||
if (!res.Succeeded)
|
||||
{
|
||||
output.SuppressOutput();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
42
BTCPayServer.Abstractions/TagHelpers/SVGUse.cs
Normal file
42
BTCPayServer.Abstractions/TagHelpers/SVGUse.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
// Make sure that <svg><use href=/ are correctly working if rootpath is present
|
||||
[HtmlTargetElement("use", Attributes = "href")]
|
||||
public class SVGUse : UrlResolutionTagHelper2
|
||||
{
|
||||
private readonly IFileVersionProvider _fileVersionProvider;
|
||||
|
||||
public SVGUse(IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, IFileVersionProvider fileVersionProvider) : base(urlHelperFactory, htmlEncoder)
|
||||
{
|
||||
_fileVersionProvider = fileVersionProvider;
|
||||
}
|
||||
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
var attr = output.Attributes["href"].Value.ToString();
|
||||
var symbolIndex = attr!.IndexOf("#", StringComparison.InvariantCulture);
|
||||
var start = attr.IndexOf("~", StringComparison.InvariantCulture) + 1;
|
||||
var length = (symbolIndex != -1 ? symbolIndex : attr.Length) - start;
|
||||
var filePath = attr.Substring(start, length);
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
var versioned = _fileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, filePath);
|
||||
attr = attr.Replace(filePath, versioned);
|
||||
}
|
||||
output.Attributes.SetAttribute("href", attr);
|
||||
base.Process(context, output);
|
||||
}
|
||||
}
|
31
BTCPayServer.Abstractions/TagHelpers/SrvModel.cs
Normal file
31
BTCPayServer.Abstractions/TagHelpers/SrvModel.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using BTCPayServer.Abstractions.Services;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
[HtmlTargetElement("srv-model")]
|
||||
public class SrvModel : TagHelper
|
||||
{
|
||||
private readonly Safe _safe;
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
|
||||
public SrvModel(Safe safe, ContentSecurityPolicies csp)
|
||||
{
|
||||
_safe = safe;
|
||||
_csp = csp;
|
||||
}
|
||||
public string VarName { get; set; } = "srvModel";
|
||||
public object Model { get; set; }
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
output.TagName = "script";
|
||||
output.TagMode = TagMode.StartTagAndEndTag;
|
||||
output.Attributes.Add(new TagHelperAttribute("type", "text/javascript"));
|
||||
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
||||
output.Attributes.Add(new TagHelperAttribute("nonce", nonce));
|
||||
_csp.Add("script-src", $"'nonce-{nonce}'");
|
||||
output.Content.SetHtmlContent($"var {VarName} = {_safe.Json(Model)};");
|
||||
}
|
||||
}
|
314
BTCPayServer.Abstractions/TagHelpers/UrlResolutionTagHelper2.cs
Normal file
314
BTCPayServer.Abstractions/TagHelpers/UrlResolutionTagHelper2.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers
|
||||
{
|
||||
// A copy of https://github.com/dotnet/aspnetcore/blob/39f0e0b8f40b4754418f81aef0de58a9204a1fe5/src/Mvc/Mvc.Razor/src/TagHelpers/UrlResolutionTagHelper.cs
|
||||
// slightly modified to also work on use tag.
|
||||
public class UrlResolutionTagHelper2 : TagHelper
|
||||
{
|
||||
// Valid whitespace characters defined by the HTML5 spec.
|
||||
private static readonly char[] ValidAttributeWhitespaceChars =
|
||||
new[] { '\t', '\n', '\u000C', '\r', ' ' };
|
||||
private static readonly Dictionary<string, string[]> ElementAttributeLookups =
|
||||
new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "use", new[] { "href" } },
|
||||
{ "a", new[] { "href" } },
|
||||
{ "applet", new[] { "archive" } },
|
||||
{ "area", new[] { "href" } },
|
||||
{ "audio", new[] { "src" } },
|
||||
{ "base", new[] { "href" } },
|
||||
{ "blockquote", new[] { "cite" } },
|
||||
{ "button", new[] { "formaction" } },
|
||||
{ "del", new[] { "cite" } },
|
||||
{ "embed", new[] { "src" } },
|
||||
{ "form", new[] { "action" } },
|
||||
{ "html", new[] { "manifest" } },
|
||||
{ "iframe", new[] { "src" } },
|
||||
{ "img", new[] { "src", "srcset" } },
|
||||
{ "input", new[] { "src", "formaction" } },
|
||||
{ "ins", new[] { "cite" } },
|
||||
{ "link", new[] { "href" } },
|
||||
{ "menuitem", new[] { "icon" } },
|
||||
{ "object", new[] { "archive", "data" } },
|
||||
{ "q", new[] { "cite" } },
|
||||
{ "script", new[] { "src" } },
|
||||
{ "source", new[] { "src", "srcset" } },
|
||||
{ "track", new[] { "src" } },
|
||||
{ "video", new[] { "poster", "src" } },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="UrlResolutionTagHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="urlHelperFactory">The <see cref="IUrlHelperFactory"/>.</param>
|
||||
/// <param name="htmlEncoder">The <see cref="HtmlEncoder"/>.</param>
|
||||
public UrlResolutionTagHelper2(IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder)
|
||||
{
|
||||
UrlHelperFactory = urlHelperFactory;
|
||||
HtmlEncoder = htmlEncoder;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Order => -1000 - 999;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IUrlHelperFactory"/>.
|
||||
/// </summary>
|
||||
protected IUrlHelperFactory UrlHelperFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="HtmlEncoder"/>.
|
||||
/// </summary>
|
||||
protected HtmlEncoder HtmlEncoder { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
[HtmlAttributeNotBound]
|
||||
[ViewContext]
|
||||
public ViewContext ViewContext { get; set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
ArgumentNullException.ThrowIfNull(output);
|
||||
|
||||
if (output.TagName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ElementAttributeLookups.TryGetValue(output.TagName, out var attributeNames))
|
||||
{
|
||||
for (var i = 0; i < attributeNames.Length; i++)
|
||||
{
|
||||
ProcessUrlAttribute(attributeNames[i], output);
|
||||
}
|
||||
}
|
||||
|
||||
// itemid can be present on any HTML element.
|
||||
ProcessUrlAttribute("itemid", output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves and updates URL values starting with '~/' (relative to the application's 'webroot' setting) for
|
||||
/// <paramref name="output"/>'s <see cref="TagHelperOutput.Attributes"/> whose
|
||||
/// <see cref="TagHelperAttribute.Name"/> is <paramref name="attributeName"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributeName">The attribute name used to lookup values to resolve.</param>
|
||||
/// <param name="output">The <see cref="TagHelperOutput"/>.</param>
|
||||
protected void ProcessUrlAttribute(string attributeName, TagHelperOutput output)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(attributeName);
|
||||
ArgumentNullException.ThrowIfNull(output);
|
||||
|
||||
var attributes = output.Attributes;
|
||||
// Read interface .Count once rather than per iteration
|
||||
var attributesCount = attributes.Count;
|
||||
for (var i = 0; i < attributesCount; i++)
|
||||
{
|
||||
var attribute = attributes[i];
|
||||
if (!string.Equals(attribute.Name, attributeName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (attribute.Value is string stringValue)
|
||||
{
|
||||
if (TryResolveUrl(stringValue, resolvedUrl: out string? resolvedUrl))
|
||||
{
|
||||
attributes[i] = new TagHelperAttribute(
|
||||
attribute.Name,
|
||||
resolvedUrl,
|
||||
attribute.ValueStyle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (attribute.Value is IHtmlContent htmlContent)
|
||||
{
|
||||
var htmlString = htmlContent as HtmlString;
|
||||
if (htmlString != null)
|
||||
{
|
||||
// No need for a StringWriter in this case.
|
||||
stringValue = htmlString.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
using var writer = new StringWriter();
|
||||
htmlContent.WriteTo(writer, HtmlEncoder);
|
||||
stringValue = writer.ToString();
|
||||
}
|
||||
|
||||
if (TryResolveUrl(stringValue, resolvedUrl: out IHtmlContent? resolvedUrl))
|
||||
{
|
||||
attributes[i] = new TagHelperAttribute(
|
||||
attribute.Name,
|
||||
resolvedUrl,
|
||||
attribute.ValueStyle);
|
||||
}
|
||||
else if (htmlString == null)
|
||||
{
|
||||
// Not a ~/ URL. Just avoid re-encoding the attribute value later.
|
||||
attributes[i] = new TagHelperAttribute(
|
||||
attribute.Name,
|
||||
new HtmlString(stringValue),
|
||||
attribute.ValueStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to resolve the given <paramref name="url"/> value relative to the application's 'webroot' setting.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL to resolve.</param>
|
||||
/// <param name="resolvedUrl">Absolute URL beginning with the application's virtual root. <c>null</c> if
|
||||
/// <paramref name="url"/> could not be resolved.</param>
|
||||
/// <returns><c>true</c> if the <paramref name="url"/> could be resolved; <c>false</c> otherwise.</returns>
|
||||
protected bool TryResolveUrl(string url, out string? resolvedUrl)
|
||||
{
|
||||
resolvedUrl = null;
|
||||
var start = FindRelativeStart(url);
|
||||
if (start == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var trimmedUrl = CreateTrimmedString(url, start);
|
||||
|
||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||
resolvedUrl = urlHelper.Content(trimmedUrl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to resolve the given <paramref name="url"/> value relative to the application's 'webroot' setting.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL to resolve.</param>
|
||||
/// <param name="resolvedUrl">
|
||||
/// Absolute URL beginning with the application's virtual root. <c>null</c> if <paramref name="url"/> could
|
||||
/// not be resolved.
|
||||
/// </param>
|
||||
/// <returns><c>true</c> if the <paramref name="url"/> could be resolved; <c>false</c> otherwise.</returns>
|
||||
protected bool TryResolveUrl(string url, [NotNullWhen(true)] out IHtmlContent? resolvedUrl)
|
||||
{
|
||||
resolvedUrl = null;
|
||||
var start = FindRelativeStart(url);
|
||||
if (start == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var trimmedUrl = CreateTrimmedString(url, start);
|
||||
|
||||
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
|
||||
var appRelativeUrl = urlHelper.Content(trimmedUrl);
|
||||
var postTildeSlashUrlValue = trimmedUrl.Substring(2);
|
||||
|
||||
if (!appRelativeUrl.EndsWith(postTildeSlashUrlValue, StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
resolvedUrl = new EncodeFirstSegmentContent(
|
||||
appRelativeUrl,
|
||||
appRelativeUrl.Length - postTildeSlashUrlValue.Length,
|
||||
postTildeSlashUrlValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int FindRelativeStart(string url)
|
||||
{
|
||||
if (url == null || url.Length < 2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var maxTestLength = url.Length - 2;
|
||||
|
||||
var start = 0;
|
||||
for (; start < url.Length; start++)
|
||||
{
|
||||
if (start > maxTestLength)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsCharWhitespace(url[start]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Before doing more work, ensure that the URL we're looking at is app-relative.
|
||||
if (url[start] != '~' || url[start + 1] != '/')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
private static string CreateTrimmedString(string input, int start)
|
||||
{
|
||||
var end = input.Length - 1;
|
||||
for (; end >= start; end--)
|
||||
{
|
||||
if (!IsCharWhitespace(input[end]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var len = end - start + 1;
|
||||
|
||||
// Substring returns same string if start == 0 && len == Length
|
||||
return input.Substring(start, len);
|
||||
}
|
||||
|
||||
private static bool IsCharWhitespace(char ch)
|
||||
{
|
||||
return ValidAttributeWhitespaceChars.AsSpan().IndexOf(ch) != -1;
|
||||
}
|
||||
|
||||
private sealed class EncodeFirstSegmentContent : IHtmlContent
|
||||
{
|
||||
private readonly string _firstSegment;
|
||||
private readonly int _firstSegmentLength;
|
||||
private readonly string _secondSegment;
|
||||
|
||||
public EncodeFirstSegmentContent(string firstSegment, int firstSegmentLength, string secondSegment)
|
||||
{
|
||||
_firstSegment = firstSegment;
|
||||
_firstSegmentLength = firstSegmentLength;
|
||||
_secondSegment = secondSegment;
|
||||
}
|
||||
|
||||
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
|
||||
{
|
||||
encoder.Encode(writer, _firstSegment, 0, _firstSegmentLength);
|
||||
writer.Write(_secondSegment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
BIN
BTCPayServer.Abstractions/icon.png
Normal file
BIN
BTCPayServer.Abstractions/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<LangVersion>10.0</LangVersion>
|
||||
<Company>BTCPay Server</Company>
|
||||
<Copyright>Copyright © BTCPay Server 2020</Copyright>
|
||||
<Description>A client library for BTCPay Server Greenfield API</Description>
|
||||
@@ -13,7 +14,7 @@
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Version Condition=" '$(Version)' == '' ">1.1.0</Version>
|
||||
<Version Condition=" '$(Version)' == '' ">1.7.2</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
@@ -27,9 +28,9 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBitcoin" Version="5.0.51" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.24" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="icon.png" Pack="true" PackagePath="\" />
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,6 +23,15 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<ApiKeyData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<ApiKeyData> CreateAPIKey(string userId, CreateApiKeyRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys",
|
||||
bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<ApiKeyData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RevokeCurrentAPIKeyInfo(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/api-keys/current", null, HttpMethod.Delete), token);
|
||||
@@ -35,5 +45,14 @@ namespace BTCPayServer.Client
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
public virtual async Task RevokeAPIKey(string userId, string apikey, CancellationToken token = default)
|
||||
{
|
||||
if (apikey == null)
|
||||
throw new ArgumentNullException(nameof(apikey));
|
||||
if (userId is null)
|
||||
throw new ArgumentNullException(nameof(userId));
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/users/{userId}/api-keys/{apikey}", null, HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
103
BTCPayServer.Client/BTCPayServerClient.Apps.cs
Normal file
103
BTCPayServer.Client/BTCPayServerClient.Apps.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
|
||||
public virtual async Task<PointOfSaleAppData> CreatePointOfSaleApp(string storeId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/apps/pos", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<PointOfSaleAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CrowdfundAppData> CreateCrowdfundApp(string storeId,
|
||||
CreateCrowdfundAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/apps/crowdfund", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<CrowdfundAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PointOfSaleAppData> UpdatePointOfSaleApp(string appId,
|
||||
CreatePointOfSaleAppRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/apps/pos/{appId}", bodyPayload: request,
|
||||
method: HttpMethod.Put), token);
|
||||
return await HandleResponse<PointOfSaleAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<AppDataBase> GetApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
if (appId == null)
|
||||
throw new ArgumentNullException(nameof(appId));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/apps/{appId}",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<AppDataBase>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<AppDataBase[]> GetAllApps(string storeId, CancellationToken token = default)
|
||||
{
|
||||
if (storeId == null)
|
||||
throw new ArgumentNullException(nameof(storeId));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/apps",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<AppDataBase[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<AppDataBase[]> GetAllApps(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/apps",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<AppDataBase[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PointOfSaleAppData> GetPosApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
if (appId == null)
|
||||
throw new ArgumentNullException(nameof(appId));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/apps/pos/{appId}",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<PointOfSaleAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CrowdfundAppData> GetCrowdfundApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
if (appId == null)
|
||||
throw new ArgumentNullException(nameof(appId));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/apps/crowdfund/{appId}",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<CrowdfundAppData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteApp(string appId, CancellationToken token = default)
|
||||
{
|
||||
if (appId == null)
|
||||
throw new ArgumentNullException(nameof(appId));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/apps/{appId}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
|
102
BTCPayServer.Client/BTCPayServerClient.CustodianAccounts.cs
Normal file
102
BTCPayServer.Client/BTCPayServerClient.CustodianAccounts.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<CustodianAccountData>> GetCustodianAccounts(string storeId, bool includeAssetBalances = false, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (includeAssetBalances)
|
||||
{
|
||||
queryPayload.Add("assetBalances", "true");
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts", queryPayload), token);
|
||||
return await HandleResponse<IEnumerable<CustodianAccountData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CustodianAccountResponse> GetCustodianAccount(string storeId, string accountId, bool includeAssetBalances = false, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (includeAssetBalances)
|
||||
{
|
||||
queryPayload.Add("assetBalances", "true");
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", queryPayload), token);
|
||||
return await HandleResponse<CustodianAccountResponse>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CustodianAccountData> CreateCustodianAccount(string storeId, CreateCustodianAccountRequest request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts", bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<CustodianAccountData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<CustodianAccountData> UpdateCustodianAccount(string storeId, string accountId, CreateCustodianAccountRequest request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", bodyPayload: request, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<CustodianAccountData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteCustodianAccount(string storeId, string accountId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}", method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<DepositAddressData> GetCustodianAccountDepositAddress(string storeId, string accountId, string paymentMethod, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/addresses/{paymentMethod}"), token);
|
||||
return await HandleResponse<DepositAddressData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<MarketTradeResponseData> MarketTradeCustodianAccountAsset(string storeId, string accountId, TradeRequestData request, CancellationToken token = default)
|
||||
{
|
||||
//var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/users", null, request, HttpMethod.Post), token);
|
||||
//return await HandleResponse<ApplicationUserData>(response);
|
||||
var internalRequest = CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/market", null,
|
||||
request, HttpMethod.Post);
|
||||
var response = await _httpClient.SendAsync(internalRequest, token);
|
||||
return await HandleResponse<MarketTradeResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<MarketTradeResponseData> GetCustodianAccountTradeInfo(string storeId, string accountId, string tradeId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/{tradeId}", method: HttpMethod.Get), token);
|
||||
return await HandleResponse<MarketTradeResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<TradeQuoteResponseData> GetCustodianAccountTradeQuote(string storeId, string accountId, string fromAsset, string toAsset, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
queryPayload.Add("fromAsset", fromAsset);
|
||||
queryPayload.Add("toAsset", toAsset);
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/trades/quote", queryPayload), token);
|
||||
return await HandleResponse<TradeQuoteResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalResponseData> CreateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals", bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<WithdrawalResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalSimulationResponseData> SimulateCustodianAccountWithdrawal(string storeId, string accountId, WithdrawRequestData request, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/simulation", bodyPayload: request, method: HttpMethod.Post), token);
|
||||
return await HandleResponse<WithdrawalSimulationResponseData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<WithdrawalResponseData> GetCustodianAccountWithdrawalInfo(string storeId, string accountId, string paymentMethod, string withdrawalId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/stores/{storeId}/custodian-accounts/{accountId}/withdrawals/{paymentMethod}/{withdrawalId}", method: HttpMethod.Get), token);
|
||||
return await HandleResponse<WithdrawalResponseData>(response);
|
||||
}
|
||||
}
|
||||
}
|
16
BTCPayServer.Client/BTCPayServerClient.Custodians.cs
Normal file
16
BTCPayServer.Client/BTCPayServerClient.Custodians.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<CustodianData>> GetCustodians(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("api/v1/custodians"), token);
|
||||
return await HandleResponse<IEnumerable<CustodianData>>(response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, bool includeArchived = false,
|
||||
public virtual async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, string[] orderId = null,
|
||||
InvoiceStatus[] status = null,
|
||||
DateTimeOffset? startDate = null,
|
||||
DateTimeOffset? endDate = null,
|
||||
string textSearch = null,
|
||||
bool includeArchived = false,
|
||||
int? skip = null,
|
||||
int? take = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
Dictionary<string, object> queryPayload = new Dictionary<string, object>();
|
||||
queryPayload.Add(nameof(includeArchived), includeArchived);
|
||||
|
||||
if (startDate is DateTimeOffset s)
|
||||
queryPayload.Add(nameof(startDate), Utils.DateTimeToUnixTime(s));
|
||||
|
||||
if (endDate is DateTimeOffset e)
|
||||
queryPayload.Add(nameof(endDate), Utils.DateTimeToUnixTime(e));
|
||||
|
||||
if (orderId != null)
|
||||
queryPayload.Add(nameof(orderId), orderId);
|
||||
if (textSearch != null)
|
||||
queryPayload.Add(nameof(textSearch), textSearch);
|
||||
if (status != null)
|
||||
queryPayload.Add(nameof(status), status.Select(s => s.ToString().ToLower()).ToArray());
|
||||
|
||||
if (skip != null)
|
||||
{
|
||||
queryPayload.Add(nameof(skip), skip);
|
||||
}
|
||||
|
||||
if (take != null)
|
||||
{
|
||||
queryPayload.Add(nameof(take), take);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices",
|
||||
new Dictionary<string, object>() {{nameof(includeArchived), includeArchived}}), token);
|
||||
queryPayload), token);
|
||||
return await HandleResponse<IEnumerable<InvoiceData>>(response);
|
||||
}
|
||||
|
||||
@@ -26,6 +61,13 @@ namespace BTCPayServer.Client
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}"), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
public virtual async Task<InvoicePaymentMethodDataModel[]> GetInvoicePaymentMethods(string storeId, string invoiceId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods"), token);
|
||||
return await HandleResponse<InvoicePaymentMethodDataModel[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ArchiveInvoice(string storeId, string invoiceId,
|
||||
CancellationToken token = default)
|
||||
@@ -47,12 +89,23 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<InvoiceData> UpdateInvoice(string storeId, string invoiceId,
|
||||
UpdateInvoiceRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}", bodyPayload: request,
|
||||
method: HttpMethod.Put), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<InvoiceData> MarkInvoiceStatus(string storeId, string invoiceId,
|
||||
MarkInvoiceStatusRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
if (request.Status!= InvoiceStatus.Complete && request.Status!= InvoiceStatus.Invalid)
|
||||
if (request.Status != InvoiceStatus.Settled && request.Status != InvoiceStatus.Invalid)
|
||||
throw new ArgumentOutOfRangeException(nameof(request.Status), "Status can only be Invalid or Complete");
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/status", bodyPayload: request,
|
||||
@@ -63,9 +116,30 @@ namespace BTCPayServer.Client
|
||||
public virtual async Task<InvoiceData> UnarchiveInvoice(string storeId, string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive",
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/unarchive",
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ActivateInvoicePaymentMethod(string storeId, string invoiceId, string paymentMethod, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods/{paymentMethod}/activate",
|
||||
method: HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PullPaymentData> RefundInvoice(
|
||||
string storeId,
|
||||
string invoiceId,
|
||||
RefundInvoiceRequest request,
|
||||
CancellationToken token = default
|
||||
)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/refund", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<PullPaymentData>(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<LNURLPayPaymentMethodData>>
|
||||
GetStoreLNURLPayPaymentMethods(string storeId, bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay",
|
||||
query), token);
|
||||
return await HandleResponse<IEnumerable<LNURLPayPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LNURLPayPaymentMethodData> GetStoreLNURLPayPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}"), token);
|
||||
return await HandleResponse<LNURLPayPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreLNURLPayPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LNURLPayPaymentMethodData> UpdateStoreLNURLPayPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, LNURLPayPaymentMethodData paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LNURLPay/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LNURLPayPaymentMethodData>(response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,7 +9,7 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
|
||||
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@@ -18,7 +18,16 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningNodeInformationData>(response);
|
||||
}
|
||||
|
||||
public async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
|
||||
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/balance",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<LightningNodeBalanceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ConnectToLightningNode(string cryptoCode, ConnectToNodeRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@@ -29,7 +38,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
|
||||
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@@ -38,24 +47,23 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
|
||||
}
|
||||
|
||||
public async Task<string> OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
|
||||
public virtual async Task OpenLightningChannel(string cryptoCode, OpenLightningChannelRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/channels", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<string>(response);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public async Task<string> GetLightningDepositAddress(string cryptoCode, CancellationToken token = default)
|
||||
public virtual async Task<string> GetLightningDepositAddress(string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/address", method: HttpMethod.Post), token);
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
|
||||
public async Task PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
|
||||
public virtual async Task<LightningPaymentData> PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@@ -63,10 +71,21 @@ namespace BTCPayServer.Client
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices/pay", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
return await HandleResponse<LightningPaymentData>(response);
|
||||
}
|
||||
|
||||
public async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode,
|
||||
public virtual async Task<LightningPaymentData> GetLightningPayment(string cryptoCode,
|
||||
string paymentHash, CancellationToken token = default)
|
||||
{
|
||||
if (paymentHash == null)
|
||||
throw new ArgumentNullException(nameof(paymentHash));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/payments/{paymentHash}",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<LightningPaymentData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string cryptoCode,
|
||||
string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
if (invoiceId == null)
|
||||
@@ -77,7 +96,43 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningInvoiceData>(response);
|
||||
}
|
||||
|
||||
public async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
|
||||
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string cryptoCode,
|
||||
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (pendingOnly is bool v)
|
||||
{
|
||||
queryPayload.Add("pendingOnly", v.ToString());
|
||||
}
|
||||
if (offsetIndex is > 0)
|
||||
{
|
||||
queryPayload.Add("offsetIndex", offsetIndex);
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/invoices", queryPayload), token);
|
||||
return await HandleResponse<LightningInvoiceData[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string cryptoCode,
|
||||
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (includePending is bool v)
|
||||
{
|
||||
queryPayload.Add("includePending", v.ToString());
|
||||
}
|
||||
if (offsetIndex is > 0)
|
||||
{
|
||||
queryPayload.Add("offsetIndex", offsetIndex);
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/server/lightning/{cryptoCode}/payments", queryPayload), token);
|
||||
return await HandleResponse<LightningPaymentData[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string cryptoCode, CreateLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
|
@@ -9,7 +9,7 @@ namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
|
||||
public virtual async Task<LightningNodeInformationData> GetLightningNodeInfo(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@@ -18,7 +18,16 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningNodeInformationData>(response);
|
||||
}
|
||||
|
||||
public async Task ConnectToLightningNode(string storeId, string cryptoCode, ConnectToNodeRequest request,
|
||||
public virtual async Task<LightningNodeBalanceData> GetLightningNodeBalance(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/balance",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<LightningNodeBalanceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ConnectToLightningNode(string storeId, string cryptoCode, ConnectToNodeRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@@ -29,7 +38,7 @@ namespace BTCPayServer.Client
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId, string cryptoCode,
|
||||
public virtual async Task<IEnumerable<LightningChannelData>> GetLightningNodeChannels(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@@ -38,16 +47,16 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<IEnumerable<LightningChannelData>>(response);
|
||||
}
|
||||
|
||||
public async Task<string> OpenLightningChannel(string storeId, string cryptoCode, OpenLightningChannelRequest request,
|
||||
public virtual async Task OpenLightningChannel(string storeId, string cryptoCode, OpenLightningChannelRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/channels", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<string>(response);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
|
||||
public virtual async Task<string> GetLightningDepositAddress(string storeId, string cryptoCode,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
@@ -56,7 +65,7 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<string>(response);
|
||||
}
|
||||
|
||||
public async Task PayLightningInvoice(string storeId, string cryptoCode, PayLightningInvoiceRequest request,
|
||||
public virtual async Task<LightningPaymentData> PayLightningInvoice(string storeId, string cryptoCode, PayLightningInvoiceRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
@@ -64,10 +73,21 @@ namespace BTCPayServer.Client
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices/pay", bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
return await HandleResponse<LightningPaymentData>(response);
|
||||
}
|
||||
|
||||
public async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
|
||||
public virtual async Task<LightningPaymentData> GetLightningPayment(string storeId, string cryptoCode,
|
||||
string paymentHash, CancellationToken token = default)
|
||||
{
|
||||
if (paymentHash == null)
|
||||
throw new ArgumentNullException(nameof(paymentHash));
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments/{paymentHash}",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<LightningPaymentData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> GetLightningInvoice(string storeId, string cryptoCode,
|
||||
string invoiceId, CancellationToken token = default)
|
||||
{
|
||||
if (invoiceId == null)
|
||||
@@ -78,7 +98,43 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<LightningInvoiceData>(response);
|
||||
}
|
||||
|
||||
public async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
|
||||
public virtual async Task<LightningInvoiceData[]> GetLightningInvoices(string storeId, string cryptoCode,
|
||||
bool? pendingOnly = null, long? offsetIndex = null, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (pendingOnly is bool v)
|
||||
{
|
||||
queryPayload.Add("pendingOnly", v.ToString());
|
||||
}
|
||||
if (offsetIndex is > 0)
|
||||
{
|
||||
queryPayload.Add("offsetIndex", offsetIndex);
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/invoices", queryPayload), token);
|
||||
return await HandleResponse<LightningInvoiceData[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningPaymentData[]> GetLightningPayments(string storeId, string cryptoCode,
|
||||
bool? includePending = null, long? offsetIndex = null, CancellationToken token = default)
|
||||
{
|
||||
var queryPayload = new Dictionary<string, object>();
|
||||
if (includePending is bool v)
|
||||
{
|
||||
queryPayload.Add("includePending", v.ToString());
|
||||
}
|
||||
if (offsetIndex is > 0)
|
||||
{
|
||||
queryPayload.Add("offsetIndex", offsetIndex);
|
||||
}
|
||||
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning/{cryptoCode}/payments", queryPayload), token);
|
||||
return await HandleResponse<LightningPaymentData[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningInvoiceData> CreateLightningInvoice(string storeId, string cryptoCode,
|
||||
CreateLightningInvoiceRequest request, CancellationToken token = default)
|
||||
{
|
||||
if (request == null)
|
||||
|
48
BTCPayServer.Client/BTCPayServerClient.LightningAddresses.cs
Normal file
48
BTCPayServer.Client/BTCPayServerClient.LightningAddresses.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<LightningAddressData[]> GetStoreLightningAddresses(string storeId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<LightningAddressData[]>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningAddressData> GetStoreLightningAddress(string storeId, string username,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<LightningAddressData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreLightningAddress(string storeId, string username,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningAddressData> AddOrUpdateStoreLightningAddress(string storeId,
|
||||
string username, LightningAddressData data,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/lightning-addresses/{username}",
|
||||
method: HttpMethod.Post, bodyPayload: data), token);
|
||||
|
||||
return await HandleResponse<LightningAddressData>(response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<LightningNetworkPaymentMethodData>>
|
||||
GetStoreLightningNetworkPaymentMethods(string storeId, bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork",
|
||||
query), token);
|
||||
return await HandleResponse<IEnumerable<LightningNetworkPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData> GetStoreLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}"), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreLightningNetworkPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData> UpdateStoreLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, UpdateLightningNetworkPaymentMethodRequest paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
}
|
||||
}
|
23
BTCPayServer.Client/BTCPayServerClient.Misc.cs
Normal file
23
BTCPayServer.Client/BTCPayServerClient.Misc.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<PermissionMetadata[]> GetPermissionMetadata(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("misc/permissions"), token);
|
||||
return await HandleResponse<PermissionMetadata[]>(response);
|
||||
}
|
||||
public virtual async Task<Language[]> GetAvailableLanguages(CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest("misc/lang"), token);
|
||||
return await HandleResponse<Language[]>(response);
|
||||
}
|
||||
}
|
||||
}
|
56
BTCPayServer.Client/BTCPayServerClient.Notifications.cs
Normal file
56
BTCPayServer.Client/BTCPayServerClient.Notifications.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null, int? skip = null,
|
||||
int? take = null, CancellationToken token = default)
|
||||
{
|
||||
Dictionary<string, object> queryPayload = new Dictionary<string, object>();
|
||||
|
||||
if (seen != null)
|
||||
queryPayload.Add(nameof(seen), seen);
|
||||
if (skip != null)
|
||||
queryPayload.Add(nameof(skip), skip);
|
||||
if (take != null)
|
||||
queryPayload.Add(nameof(take), take);
|
||||
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/users/me/notifications",
|
||||
queryPayload), token);
|
||||
|
||||
return await HandleResponse<IEnumerable<NotificationData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<NotificationData> GetNotification(string notificationId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/users/me/notifications/{notificationId}"), token);
|
||||
return await HandleResponse<NotificationData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<NotificationData> UpdateNotification(string notificationId, bool? seen,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/users/me/notifications/{notificationId}",
|
||||
method: HttpMethod.Put, bodyPayload: new UpdateNotification() { Seen = seen }), token);
|
||||
return await HandleResponse<NotificationData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveNotification(string notificationId, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/users/me/notifications/{notificationId}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Client.Models;
|
||||
|
||||
namespace BTCPayServer.Client
|
||||
{
|
||||
public partial class BTCPayServerClient
|
||||
{
|
||||
public virtual async Task<IEnumerable<OnChainPaymentMethodData>> GetStoreOnChainPaymentMethods(string storeId,
|
||||
bool? enabled = null,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var query = new Dictionary<string, object>();
|
||||
if (enabled != null)
|
||||
{
|
||||
query.Add(nameof(enabled), enabled);
|
||||
}
|
||||
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain",
|
||||
query), token);
|
||||
return await HandleResponse<IEnumerable<OnChainPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodData> GetStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}"), token);
|
||||
return await HandleResponse<OnChainPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodData> UpdateStoreOnChainPaymentMethod(string storeId,
|
||||
string cryptoCode, UpdateOnChainPaymentMethodRequest paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<OnChainPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodPreviewResultData>
|
||||
PreviewProposedStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string cryptoCode, UpdateOnChainPaymentMethodRequest paymentMethod, int offset = 0,
|
||||
int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview",
|
||||
bodyPayload: paymentMethod,
|
||||
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodPreviewResultData> PreviewStoreOnChainPaymentMethodAddresses(
|
||||
string storeId, string cryptoCode, int offset = 0, int amount = 10,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview",
|
||||
queryPayload: new Dictionary<string, object>() { { "offset", offset }, { "amount", amount } },
|
||||
method: HttpMethod.Get), token);
|
||||
return await HandleResponse<OnChainPaymentMethodPreviewResultData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId,
|
||||
string cryptoCode, GenerateOnChainWalletRequest request,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/generate",
|
||||
bodyPayload: request,
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<OnChainPaymentMethodDataWithSensitiveData>(response);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user