Compare commits
229 commits
Author | SHA1 | Date | |
---|---|---|---|
b54a86285a | |||
fb685bf05a | |||
805ca3d4c0 | |||
0231dad173 | |||
f67105611c | |||
7c10ab9fde | |||
37d2d01f8f | |||
|
fe18cf7620 | ||
|
7bfadb46ff | ||
47011db720 | |||
1e563ece83 | |||
7446583a0d | |||
8e6f3fbf46 | |||
a8dea646a0 | |||
dbb1d5ba23 | |||
|
7e1784fd3f | ||
af2419c5d7 | |||
202303e888 | |||
7a214dd212 | |||
c67e4189de | |||
37b913476e | |||
b35490c53d | |||
c118dacde7 | |||
2a013b8fe4 | |||
3a8b8408b4 | |||
6602fd136e | |||
0c49fea641 | |||
6336ba2e83 | |||
db13e1d6a1 | |||
ad62049f0b | |||
fb1f551153 | |||
a2d5202d4e | |||
e9e8a03e08 | |||
3fe44596de | |||
95aad98897 | |||
d76159a2da | |||
06372f9fd0 | |||
c474f683af | |||
f46a4d3c19 | |||
55d9f66cd6 | |||
32ccd908c0 | |||
e5ed91d986 | |||
|
46b344ca3e | ||
|
58401634d8 | ||
|
172c39f781 | ||
db51a8e506 | |||
b62b8bdbf1 | |||
f48115f5f0 | |||
1da4849ad7 | |||
2fe9bacac0 | |||
14cfd8de23 | |||
|
c05bad8d41 | ||
|
f8ad1a832f | ||
bcc81c3093 | |||
f53e46c721 | |||
fcba187a00 | |||
f59cd33db9 | |||
abd0d1f16f | |||
a07cd5ecc0 | |||
a3120079a5 | |||
d06772b376 | |||
faf2855244 | |||
a4403e4e80 | |||
e60158c70b | |||
add8469813 | |||
|
3f2d8b015f | ||
|
900ceb2dfd | ||
|
2706e89138 | ||
08eebffb0e | |||
3522fb1c36 | |||
ea3d61faa1 | |||
d749f8ff4f | |||
9fe057a93e | |||
da1be7167e | |||
22960444db | |||
3e9a69c8d3 | |||
4bcb6a16b2 | |||
6010b9ca94 | |||
9a56cc5a63 | |||
d9e2eb0ef5 | |||
af96558f9c | |||
aa8ba2277f | |||
f88979326c | |||
ed2a6f5348 | |||
d2b4667d89 | |||
718f35aab6 | |||
c347aecef1 | |||
4b41ba20d6 | |||
eb7a0bb5ef | |||
b6b7fe27b6 | |||
798381ee88 | |||
bc96a01c14 | |||
a5b00ec4a5 | |||
d7b81f551d | |||
10396a57ea | |||
d91c61f8fa | |||
baf008e1d6 | |||
89643dd441 | |||
5823d8150a | |||
4d0bb6308a | |||
da59a0ac19 | |||
805edf4555 | |||
78c8fe0b5a | |||
408e69eb39 | |||
191919e7fa | |||
d1f14ff2c7 | |||
76f06cee31 | |||
6074222377 | |||
1a8955dc4f | |||
80e137461a | |||
7658649d61 | |||
75dd8c366a | |||
528c282d68 | |||
cf6aed386d | |||
|
f7da251c5d | ||
051d1002b4 | |||
66b1761738 | |||
7428ff0736 | |||
fc58ab6b25 | |||
d487a76572 | |||
86a3221563 | |||
46cab80ef4 | |||
|
2f0a0b1216 | ||
94fbd80b7b | |||
a8cb4a80bf | |||
57e6b67095 | |||
67e0fcdd76 | |||
1e57f01001 | |||
6ac8e53177 | |||
f3d7f7fc5b | |||
e5d7a28269 | |||
98dad2053c | |||
becdf5e1f1 | |||
ebea73a1fa | |||
00bee2dab8 | |||
501a39fb98 | |||
364838c11b | |||
6f567d0c01 | |||
|
609fcc7514 | ||
|
55c5e930db | ||
|
07150b33ba | ||
1b65add4f7 | |||
696542903e | |||
5dca807463 | |||
69c788566e | |||
1e2f9863e7 | |||
aa962c6ee9 | |||
|
5d61e5955b | ||
|
0d120f8a8d | ||
|
523ca3d89b | ||
702a963308 | |||
6a03890684 | |||
e8987048c1 | |||
b80000ab98 | |||
|
8c14f5336f | ||
|
ec54f698d9 | ||
|
d03097712a | ||
2821ce4a0c | |||
a904349864 | |||
27cd913f40 | |||
9b763c2f0e | |||
1b9a9df5ec | |||
543fbe0655 | |||
|
70aa564632 | ||
|
8f5a3fa43e | ||
90cda5f2a1 | |||
17a2d51952 | |||
2b44be9a17 | |||
40f6ed2dd8 | |||
eed4644409 | |||
3f77a36a2c | |||
c1b8463251 | |||
608e4036e5 | |||
f9b38495fd | |||
d2c5df88eb | |||
2b243acf71 | |||
|
95fdec2739 | ||
9dfe4bc8a6 | |||
15427d5131 | |||
4937aab43d | |||
016d2d410b | |||
6e802b549d | |||
273ffae0e4 | |||
89a539815a | |||
d8c401dead | |||
231ead4d48 | |||
f1ec41b339 | |||
7d819f5e89 | |||
98664a7c84 | |||
9c41737419 | |||
944d0afa9f | |||
6e13733898 | |||
2d9f8e1f9b | |||
764bbdbb70 | |||
d223aa69e3 | |||
8e6b33b3e1 | |||
|
c946b9e3a4 | ||
|
550901e6fb | ||
|
f5a70ebe32 | ||
|
fcbbf6dbe7 | ||
|
eb3aaa2050 | ||
|
49db112ccf | ||
|
66483a244b | ||
7931e210e5 | |||
5139b3dc19 | |||
7ea5e108a5 | |||
fdae736f22 | |||
1da0d49de7 | |||
65016b2664 | |||
d1a53f7d6a | |||
373a84a8e2 | |||
21c56f8e94 | |||
3ed4a71a4c | |||
46973f99fa | |||
ebef769703 | |||
d75809aeee | |||
456ed42d3e | |||
ea4129e888 | |||
f9e33d97cc | |||
b480c52f60 | |||
2a8864fe43 | |||
|
b342241abc | ||
|
97fedf2616 | ||
|
15c1f6218c | ||
|
e8907c3c9e | ||
|
678a56fbf8 | ||
|
4951af4d99 | ||
|
f2db473b0d | ||
|
539e874579 |
|
@ -1,31 +1 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
||||
<g>
|
||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
|
||||
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
|
||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
|
||||
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
|
||||
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
|
||||
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
|
||||
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
|
||||
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
|
||||
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
|
||||
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
|
||||
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
|
||||
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
|
||||
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
|
||||
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
|
||||
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
|
||||
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
|
||||
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
|
||||
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
|
||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="main_outline" x="0" y="0" version="1.1" viewBox="0 0 640 640" xml:space="preserve" style="enable-background:new 0 0 640 640"><g><path id="teabag" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8 c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4 c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z" style="fill:#fff"/><g><g><path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2 c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5 c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5 c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3 c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1 C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4 c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7 S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55 c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8 l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/><path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4 c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1 c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9 c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3 c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3 c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29 c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8 C343.2,346.5,335,363.3,326.8,380.1z"/></g></g></g></svg>
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -2251,6 +2251,23 @@ PATH =
|
|||
;;
|
||||
;; Enable/Disable user statistics for nodeinfo if federation is enabled
|
||||
; SHARE_USER_STATISTICS = true
|
||||
;;
|
||||
;; Maximum federation request and response size (MB)
|
||||
; MAX_SIZE = 4
|
||||
;;
|
||||
;; WARNING: Changing the settings below can break federation.
|
||||
;;
|
||||
;; HTTP signature algorithms
|
||||
; ALGORITHMS = rsa-sha256, rsa-sha512, ed25519
|
||||
;;
|
||||
;; HTTP signature digest algorithm
|
||||
; DIGEST_ALGORITHM = SHA-256
|
||||
;;
|
||||
;; GET headers for federation requests
|
||||
; GET_HEADERS = (request-target), Date
|
||||
;;
|
||||
;; POST headers for federation requests
|
||||
; POST_HEADERS = (request-target), Date, Digest
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -1090,6 +1090,14 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
|
|||
|
||||
- `ENABLED`: **true**: Enable/Disable federation capabilities
|
||||
- `SHARE_USER_STATISTICS`: **true**: Enable/Disable user statistics for nodeinfo if federation is enabled
|
||||
- `MAX_SIZE`: **4**: Maximum federation request and response size (MB)
|
||||
|
||||
WARNING: Changing the settings below can break federation.
|
||||
|
||||
- `ALGORITHMS`: **rsa-sha256, rsa-sha512, ed25519**: HTTP signature algorithms
|
||||
- `DIGEST_ALGORITHM`: **SHA-256**: HTTP signature digest algorithm
|
||||
- `GET_HEADERS`: **(request-target), Date**: GET headers for federation requests
|
||||
- `POST_HEADERS`: **(request-target), Date, Digest**: POST headers for federation requests
|
||||
|
||||
## Packages (`packages`)
|
||||
|
||||
|
|
BIN
docs/static/authorize.png
vendored
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 15 KiB |
54
docs/static/cloudron.svg
vendored
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
7
go.mod
|
@ -28,10 +28,12 @@ require (
|
|||
github.com/ethantkoenig/rupture v1.0.1
|
||||
github.com/felixge/fgprof v0.9.2
|
||||
github.com/gliderlabs/ssh v0.3.4
|
||||
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b
|
||||
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-enry/go-enry/v2 v2.8.2
|
||||
github.com/go-fed/httpsig v1.1.0
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
|
||||
github.com/go-git/go-billy/v5 v5.3.1
|
||||
github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4
|
||||
github.com/go-ldap/ldap/v3 v3.4.3
|
||||
|
@ -83,6 +85,7 @@ require (
|
|||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/unrolled/render v1.4.1
|
||||
github.com/urfave/cli v1.22.9
|
||||
github.com/valyala/fastjson v1.6.3
|
||||
github.com/xanzy/go-gitlab v0.64.0
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
github.com/yuin/goldmark v1.4.12
|
||||
|
@ -107,6 +110,7 @@ require (
|
|||
|
||||
require (
|
||||
cloud.google.com/go v0.99.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
|
||||
|
@ -160,6 +164,7 @@ require (
|
|||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fullstorydev/grpcurl v1.8.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
|
|
14
go.sum
|
@ -69,6 +69,8 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EU
|
|||
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
|
||||
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 h1:2OrsyJYZp7J6nyAsKi2q1SELYRaIc0aQmcQ/EQqPfk8=
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs=
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||
|
@ -460,6 +462,12 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
|
|||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b h1:+RjYfEfoZdM3wHFs752dlOpGaoRhwRRyQxjajg08LcQ=
|
||||
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b/go.mod h1:DE3vvc6Didgfd3k7M1Mos6qMDFNmMrxJmYVMHG9h9Io=
|
||||
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f h1:kJhGo4NApJP0Lt9lkJnfmuTnRWVFbCynY0kiTxpPUR4=
|
||||
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f/go.mod h1:KHkKFKZvc05lr79+RGoq/zG8YjWi3+FK60Bxd+mpCew=
|
||||
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d h1:Z/oRXMlZHjvjIqDma1FrIGL3iE5YL7MUI0bwYEZ6qbA=
|
||||
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
|
@ -472,8 +480,8 @@ github.com/go-enry/go-enry/v2 v2.8.2 h1:uiGmC+3K8sVd/6DOe2AOJEOihJdqda83nPyJNtMR
|
|||
github.com/go-enry/go-enry/v2 v2.8.2/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
|
||||
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
||||
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
|
||||
github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8=
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
|
@ -1507,6 +1515,8 @@ github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
|
|||
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
||||
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
|
||||
|
|
103
integrations/api_activitypub_person_test.go
Normal file
|
@ -0,0 +1,103 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/activitypub"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestActivityPubPerson(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
setting.Federation.Enabled = true
|
||||
defer func() {
|
||||
setting.Federation.Enabled = false
|
||||
}()
|
||||
|
||||
username := "user2"
|
||||
req := NewRequestf(t, "GET", fmt.Sprintf("/api/v1/activitypub/user/%s", username))
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
body := resp.Body.Bytes()
|
||||
assert.Contains(t, string(body), "@context")
|
||||
|
||||
var person ap.Person
|
||||
err := person.UnmarshalJSON(body)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, ap.PersonType, person.Type)
|
||||
assert.Equal(t, username, person.PreferredUsername.String())
|
||||
keyID := person.GetID().String()
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user/%s$", username), keyID)
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user/%s/outbox$", username), person.Outbox.GetID().String())
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user/%s/inbox$", username), person.Inbox.GetID().String())
|
||||
|
||||
pubKey := person.PublicKey
|
||||
assert.NotNil(t, pubKey)
|
||||
publicKeyID := keyID + "#main-key"
|
||||
assert.Equal(t, pubKey.ID.String(), publicKeyID)
|
||||
|
||||
pubKeyPem := pubKey.PublicKeyPem
|
||||
assert.NotNil(t, pubKeyPem)
|
||||
assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem)
|
||||
})
|
||||
}
|
||||
|
||||
func TestActivityPubMissingPerson(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
setting.Federation.Enabled = true
|
||||
defer func() {
|
||||
setting.Federation.Enabled = false
|
||||
}()
|
||||
|
||||
req := NewRequestf(t, "GET", "/api/v1/activitypub/user/nonexistentuser")
|
||||
resp := MakeRequest(t, req, http.StatusNotFound)
|
||||
assert.Contains(t, resp.Body.String(), "user redirect does not exist")
|
||||
})
|
||||
}
|
||||
|
||||
func TestActivityPubPersonInbox(t *testing.T) {
|
||||
srv := httptest.NewServer(c)
|
||||
defer srv.Close()
|
||||
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
appURL := setting.AppURL
|
||||
setting.Federation.Enabled = true
|
||||
setting.AppURL = srv.URL
|
||||
defer func() {
|
||||
setting.Federation.Enabled = false
|
||||
setting.Database.LogSQL = false
|
||||
setting.AppURL = appURL
|
||||
}()
|
||||
username1 := "user1"
|
||||
ctx := context.Background()
|
||||
user1, err := user_model.GetUserByName(ctx, username1)
|
||||
assert.NoError(t, err)
|
||||
user1url := fmt.Sprintf("%s/api/v1/activitypub/user/%s#main-key", srv.URL, username1)
|
||||
c, err := activitypub.NewClient(user1, user1url)
|
||||
assert.NoError(t, err)
|
||||
username2 := "user2"
|
||||
user2inboxurl := fmt.Sprintf("%s/api/v1/activitypub/user/%s/inbox", srv.URL, username2)
|
||||
|
||||
// Signed request succeeds
|
||||
resp, err := c.Post([]byte{}, user2inboxurl)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusNoContent, resp.StatusCode)
|
||||
|
||||
// Unsigned request fails
|
||||
req := NewRequest(t, "POST", user2inboxurl)
|
||||
MakeRequest(t, req, http.StatusInternalServerError)
|
||||
})
|
||||
}
|
|
@ -52,7 +52,7 @@ func TestWebfinger(t *testing.T) {
|
|||
var jrd webfingerJRD
|
||||
DecodeJSON(t, resp, &jrd)
|
||||
assert.Equal(t, "acct:user2@"+appURL.Host, jrd.Subject)
|
||||
assert.ElementsMatch(t, []string{user.HTMLURL()}, jrd.Aliases)
|
||||
assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(user.Name)}, jrd.Aliases)
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", user.LowerName, "unknown.host"))
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
|
|
|
@ -22,14 +22,15 @@ type Type int
|
|||
|
||||
// Note: new type must append to the end of list to maintain compatibility.
|
||||
const (
|
||||
NoType Type = iota
|
||||
Plain // 1
|
||||
LDAP // 2
|
||||
SMTP // 3
|
||||
PAM // 4
|
||||
DLDAP // 5
|
||||
OAuth2 // 6
|
||||
SSPI // 7
|
||||
NoType Type = iota
|
||||
Plain // 1
|
||||
LDAP // 2
|
||||
SMTP // 3
|
||||
PAM // 4
|
||||
DLDAP // 5
|
||||
OAuth2 // 6
|
||||
SSPI // 7
|
||||
Federated // 8
|
||||
)
|
||||
|
||||
// String returns the string name of the LoginType
|
||||
|
@ -178,6 +179,11 @@ func (source *Source) IsSSPI() bool {
|
|||
return source.Type == SSPI
|
||||
}
|
||||
|
||||
// IsFederated returns true of this source is of the Federated type.
|
||||
func (source *Source) IsFederated() bool {
|
||||
return source.Type == Federated
|
||||
}
|
||||
|
||||
// HasTLS returns true of this source supports TLS.
|
||||
func (source *Source) HasTLS() bool {
|
||||
hasTLSer, ok := source.Cfg.(HasTLSer)
|
||||
|
|
|
@ -17,7 +17,7 @@ var (
|
|||
ErrNameEmpty = errors.New("Name is empty")
|
||||
|
||||
// AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-)
|
||||
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.@]`)
|
||||
)
|
||||
|
||||
// ErrNameReserved represents a "reserved name" error.
|
||||
|
|
71
models/forgefed/repository.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package forgefed
|
||||
|
||||
import (
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
const (
|
||||
RepositoryType ap.ActivityVocabularyType = "Repository"
|
||||
)
|
||||
|
||||
type Repository struct {
|
||||
ap.Actor
|
||||
// Team Collection of actors who have management/push access to the repository
|
||||
Team ap.Item `jsonld:"team,omitempty"`
|
||||
// Forks OrderedCollection of repositories that are forks of this repository
|
||||
Forks ap.Item `jsonld:"forks,omitempty"`
|
||||
}
|
||||
|
||||
// GetItemByType instantiates a new Repository object if the type matches
|
||||
// otherwise it defaults to existing activitypub package typer function.
|
||||
func GetItemByType(typ ap.ActivityVocabularyType) (ap.Item, error) {
|
||||
if typ == RepositoryType {
|
||||
return RepositoryNew(""), nil
|
||||
}
|
||||
return ap.GetItemByType(typ)
|
||||
}
|
||||
|
||||
// RepositoryNew initializes a Repository type actor
|
||||
func RepositoryNew(id ap.ID) *Repository {
|
||||
a := ap.ActorNew(id, RepositoryType)
|
||||
o := Repository{Actor: *a}
|
||||
o.Type = RepositoryType
|
||||
return &o
|
||||
}
|
||||
|
||||
func (r Repository) MarshalJSON() ([]byte, error) {
|
||||
b, err := r.Actor.MarshalJSON()
|
||||
if len(b) == 0 || err != nil {
|
||||
return make([]byte, 0), err
|
||||
}
|
||||
|
||||
b = b[:len(b)-1]
|
||||
if r.Team != nil {
|
||||
ap.WriteItemJSONProp(&b, "team", r.Team)
|
||||
}
|
||||
if r.Forks != nil {
|
||||
ap.WriteItemJSONProp(&b, "forks", r.Forks)
|
||||
}
|
||||
ap.Write(&b, '}')
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (r *Repository) UnmarshalJSON(data []byte) error {
|
||||
p := fastjson.Parser{}
|
||||
val, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.Team = ap.JSONGetItem(val, "team")
|
||||
r.Forks = ap.JSONGetItem(val, "forks")
|
||||
|
||||
return ap.OnActor(&r.Actor, func(a *ap.Actor) error {
|
||||
return ap.LoadActor(val, a)
|
||||
})
|
||||
}
|
167
models/forgefed/repository_test.go
Normal file
|
@ -0,0 +1,167 @@
|
|||
package forgefed
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
func Test_GetItemByType(t *testing.T) {
|
||||
type testtt struct {
|
||||
typ ap.ActivityVocabularyType
|
||||
want ap.Item
|
||||
wantErr error
|
||||
}
|
||||
tests := map[string]testtt{
|
||||
"invalid type": {
|
||||
typ: ap.ActivityVocabularyType("invalidtype"),
|
||||
wantErr: fmt.Errorf("empty ActivityStreams type"), // TODO(marius): this error message needs to be improved in go-ap/activitypub
|
||||
},
|
||||
"Repository": {
|
||||
typ: RepositoryType,
|
||||
want: new(Repository),
|
||||
},
|
||||
"Person - fall back": {
|
||||
typ: ap.PersonType,
|
||||
want: new(ap.Person),
|
||||
},
|
||||
"Question - fall back": {
|
||||
typ: ap.QuestionType,
|
||||
want: new(ap.Question),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
maybeRepository, err := GetItemByType(tt.typ)
|
||||
if !reflect.DeepEqual(tt.wantErr, err) {
|
||||
t.Errorf("GetItemByType() error = \"%+v\", wantErr = \"%+v\" when getting Item for type %q", tt.wantErr, err, tt.typ)
|
||||
}
|
||||
if reflect.TypeOf(tt.want) != reflect.TypeOf(maybeRepository) {
|
||||
t.Errorf("Invalid type received %T, expected %T", maybeRepository, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RepositoryMarshalJSON(t *testing.T) {
|
||||
type testPair struct {
|
||||
item Repository
|
||||
want []byte
|
||||
wantErr error
|
||||
}
|
||||
|
||||
tests := map[string]testPair{
|
||||
"empty": {
|
||||
item: Repository{},
|
||||
want: nil,
|
||||
},
|
||||
"with ID": {
|
||||
item: Repository{
|
||||
Actor: ap.Actor{
|
||||
ID: "https://example.com/1",
|
||||
},
|
||||
Team: nil,
|
||||
},
|
||||
want: []byte(`{"id":"https://example.com/1"}`),
|
||||
},
|
||||
"with Team as IRI": {
|
||||
item: Repository{
|
||||
Team: ap.IRI("https://example.com/1"),
|
||||
},
|
||||
want: []byte(`{"team":"https://example.com/1"}`),
|
||||
},
|
||||
"with Team as IRIs": {
|
||||
item: Repository{
|
||||
Team: ap.ItemCollection{
|
||||
ap.IRI("https://example.com/1"),
|
||||
ap.IRI("https://example.com/2"),
|
||||
},
|
||||
},
|
||||
want: []byte(`{"team":["https://example.com/1","https://example.com/2"]}`),
|
||||
},
|
||||
"with Team as Object": {
|
||||
item: Repository{
|
||||
Team: ap.Object{ID: "https://example.com/1"},
|
||||
},
|
||||
want: []byte(`{"team":{"id":"https://example.com/1"}}`),
|
||||
},
|
||||
"with Team as slice of Objects": {
|
||||
item: Repository{
|
||||
Team: ap.ItemCollection{
|
||||
ap.Object{ID: "https://example.com/1"},
|
||||
ap.Object{ID: "https://example.com/2"},
|
||||
},
|
||||
},
|
||||
want: []byte(`{"team":[{"id":"https://example.com/1"},{"id":"https://example.com/2"}]}`),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, err := tt.item.MarshalJSON()
|
||||
if (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error() {
|
||||
t.Errorf("MarshalJSON() error = \"%v\", wantErr \"%v\"", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("MarshalJSON() got = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_RepositoryUnmarshalJSON(t *testing.T) {
|
||||
type testPair struct {
|
||||
data []byte
|
||||
want *Repository
|
||||
wantErr error
|
||||
}
|
||||
|
||||
tests := map[string]testPair{
|
||||
"nil": {
|
||||
data: nil,
|
||||
wantErr: fmt.Errorf("cannot parse JSON: %w", fmt.Errorf("cannot parse empty string; unparsed tail: %q", "")),
|
||||
},
|
||||
"empty": {
|
||||
data: []byte{},
|
||||
wantErr: fmt.Errorf("cannot parse JSON: %w", fmt.Errorf("cannot parse empty string; unparsed tail: %q", "")),
|
||||
},
|
||||
"with Type": {
|
||||
data: []byte(`{"type":"Repository"}`),
|
||||
want: &Repository{
|
||||
Actor: ap.Actor{
|
||||
Type: RepositoryType,
|
||||
},
|
||||
},
|
||||
},
|
||||
"with Type and ID": {
|
||||
data: []byte(`{"id":"https://example.com/1","type":"Repository"}`),
|
||||
want: &Repository{
|
||||
Actor: ap.Actor{
|
||||
ID: "https://example.com/1",
|
||||
Type: RepositoryType,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tt := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := new(Repository)
|
||||
err := got.UnmarshalJSON(tt.data)
|
||||
if (err != nil || tt.wantErr != nil) && tt.wantErr.Error() != err.Error() {
|
||||
t.Errorf("UnmarshalJSON() error = \"%v\", wantErr \"%v\"", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if tt.want != nil && !reflect.DeepEqual(got, tt.want) {
|
||||
jGot, _ := json.Marshal(got)
|
||||
jWant, _ := json.Marshal(tt.want)
|
||||
t.Errorf("UnmarshalJSON() got = %s, want %s", jGot, jWant)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,4 +9,8 @@ const (
|
|||
SettingsKeyHiddenCommentTypes = "issue.hidden_comment_types"
|
||||
// SettingsKeyDiffWhitespaceBehavior is the setting key for whitespace behavior of diff
|
||||
SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour"
|
||||
// UserActivityPubPrivPem is user's private key
|
||||
UserActivityPubPrivPem = "activitypub.priv_pem"
|
||||
// UserActivityPubPubPem is user's public key
|
||||
UserActivityPubPubPem = "activitypub.pub_pem"
|
||||
)
|
||||
|
|
124
modules/activitypub/client.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/proxy"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/go-fed/httpsig"
|
||||
)
|
||||
|
||||
const (
|
||||
// ActivityStreamsContentType const
|
||||
ActivityStreamsContentType = `application/ld+json; profile="https://www.w3.org/ns/activitystreams"`
|
||||
httpsigExpirationTime = 60
|
||||
)
|
||||
|
||||
// Gets the current time as an RFC 2616 formatted string
|
||||
// RFC 2616 requires RFC 1123 dates but with GMT instead of UTC
|
||||
func CurrentTime() string {
|
||||
return strings.ReplaceAll(time.Now().UTC().Format(time.RFC1123), "UTC", "GMT")
|
||||
}
|
||||
|
||||
func containsRequiredHTTPHeaders(method string, headers []string) error {
|
||||
var hasRequestTarget, hasDate, hasDigest bool
|
||||
for _, header := range headers {
|
||||
hasRequestTarget = hasRequestTarget || header == httpsig.RequestTarget
|
||||
hasDate = hasDate || header == "Date"
|
||||
hasDigest = hasDigest || header == "Digest"
|
||||
}
|
||||
if !hasRequestTarget {
|
||||
return fmt.Errorf("missing http header for %s: %s", method, httpsig.RequestTarget)
|
||||
} else if !hasDate {
|
||||
return fmt.Errorf("missing http header for %s: Date", method)
|
||||
} else if !hasDigest && method != http.MethodGet {
|
||||
return fmt.Errorf("missing http header for %s: Digest", method)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Client struct
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
algs []httpsig.Algorithm
|
||||
digestAlg httpsig.DigestAlgorithm
|
||||
getHeaders []string
|
||||
postHeaders []string
|
||||
priv *rsa.PrivateKey
|
||||
pubID string
|
||||
}
|
||||
|
||||
// NewClient function
|
||||
func NewClient(user *user_model.User, pubID string) (c *Client, err error) {
|
||||
if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil {
|
||||
return
|
||||
} else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
priv, err := GetPrivateKey(user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
privPem, _ := pem.Decode([]byte(priv))
|
||||
privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c = &Client{
|
||||
client: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: proxy.Proxy(),
|
||||
},
|
||||
},
|
||||
algs: setting.HttpsigAlgs,
|
||||
digestAlg: httpsig.DigestAlgorithm(setting.Federation.DigestAlgorithm),
|
||||
getHeaders: setting.Federation.GetHeaders,
|
||||
postHeaders: setting.Federation.PostHeaders,
|
||||
priv: privParsed,
|
||||
pubID: pubID,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewRequest function
|
||||
func (c *Client) NewRequest(b []byte, to string) (req *http.Request, err error) {
|
||||
buf := bytes.NewBuffer(b)
|
||||
req, err = http.NewRequest(http.MethodPost, to, buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
req.Header.Add("Content-Type", ActivityStreamsContentType)
|
||||
req.Header.Add("Date", CurrentTime())
|
||||
req.Header.Add("User-Agent", "Gitea/"+setting.AppVer)
|
||||
signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = signer.SignRequest(c.priv, c.pubID, req, b)
|
||||
return
|
||||
}
|
||||
|
||||
// Post function
|
||||
func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
|
||||
var req *http.Request
|
||||
if req, err = c.NewRequest(b, to); err != nil {
|
||||
return
|
||||
}
|
||||
resp, err = c.client.Do(req)
|
||||
return
|
||||
}
|
49
modules/activitypub/client_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
_ "code.gitea.io/gitea/models" // https://discourse.gitea.io/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestActivityPubSignedPost(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
|
||||
pubID := "https://example.com/pubID"
|
||||
c, err := NewClient(user, pubID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := "BODY"
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Regexp(t, regexp.MustCompile("^"+setting.Federation.DigestAlgorithm), r.Header.Get("Digest"))
|
||||
assert.Contains(t, r.Header.Get("Signature"), pubID)
|
||||
assert.Equal(t, r.Header.Get("Content-Type"), ActivityStreamsContentType)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, string(body))
|
||||
fmt.Fprintf(w, expected)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
r, err := c.Post([]byte(expected), srv.URL)
|
||||
assert.NoError(t, err)
|
||||
defer r.Body.Close()
|
||||
body, err := io.ReadAll(r.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, string(body))
|
||||
}
|
46
modules/activitypub/follow.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
func Follow(ctx context.Context, activity ap.Follow) {
|
||||
actorIRI := activity.Actor.GetID()
|
||||
objectIRI := activity.Object.GetID()
|
||||
actorIRISplit := strings.Split(actorIRI.String(), "/")
|
||||
objectIRISplit := strings.Split(objectIRI.String(), "/")
|
||||
actorName := actorIRISplit[len(actorIRISplit)-1] + "@" + actorIRISplit[2]
|
||||
objectName := objectIRISplit[len(objectIRISplit)-1]
|
||||
|
||||
err := FederatedUserNew(actorName, actorIRI)
|
||||
if err != nil {
|
||||
log.Warn("Couldn't create new user", err)
|
||||
}
|
||||
actorUser, err := user_model.GetUserByName(ctx, actorName)
|
||||
if err != nil {
|
||||
log.Warn("Couldn't find actor", err)
|
||||
}
|
||||
objectUser, err := user_model.GetUserByName(ctx, objectName)
|
||||
if err != nil {
|
||||
log.Warn("Couldn't find object", err)
|
||||
}
|
||||
|
||||
user_model.FollowUser(actorUser.ID, objectUser.ID)
|
||||
|
||||
accept := ap.AcceptNew(objectIRI, activity)
|
||||
accept.Actor = ap.Person{ID: objectIRI}
|
||||
accept.To = ap.ItemCollection{ap.IRI(actorIRI.String() + "/inbox")}
|
||||
accept.Object = activity
|
||||
|
||||
Send(objectUser, accept)
|
||||
}
|
18
modules/activitypub/main_test.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: filepath.Join("..", ".."),
|
||||
})
|
||||
}
|
59
modules/activitypub/send.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
func Fetch(iri *url.URL) (b []byte, err error) {
|
||||
req := httplib.NewRequest(iri.String(), http.MethodGet)
|
||||
req.Header("Accept", ActivityStreamsContentType)
|
||||
req.Header("User-Agent", "Gitea/"+setting.AppVer)
|
||||
resp, err := req.Response()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status)
|
||||
return
|
||||
}
|
||||
b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
|
||||
return
|
||||
}
|
||||
|
||||
func Send(user *user_model.User, activity *ap.Activity) {
|
||||
body, err := activity.MarshalJSON()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var jsonmap map[string]interface{}
|
||||
err = json.Unmarshal(body, &jsonmap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
jsonmap["@context"] = "https://www.w3.org/ns/activitystreams"
|
||||
body, _ = json.Marshal(jsonmap)
|
||||
|
||||
for _, to := range activity.To {
|
||||
client, _ := NewClient(user, setting.AppURL+"api/v1/activitypub/user/"+user.Name+"#main-key")
|
||||
resp, _ := client.Post(body, to.GetID().String())
|
||||
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
|
||||
log.Debug(string(respBody))
|
||||
}
|
||||
}
|
22
modules/activitypub/user.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
func FederatedUserNew(name string, IRI ap.IRI) error {
|
||||
user := &user_model.User{
|
||||
Name: name,
|
||||
Email: name,
|
||||
LoginType: auth.Federated,
|
||||
Website: IRI.String(),
|
||||
}
|
||||
return user_model.CreateUser(user)
|
||||
}
|
45
modules/activitypub/user_settings.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
)
|
||||
|
||||
// GetKeyPair function returns a user's private and public keys
|
||||
func GetKeyPair(user *user_model.User) (pub, priv string, err error) {
|
||||
var settings map[string]*user_model.Setting
|
||||
settings, err = user_model.GetUserSettings(user.ID, []string{user_model.UserActivityPubPrivPem, user_model.UserActivityPubPubPem})
|
||||
if err != nil {
|
||||
return
|
||||
} else if len(settings) == 0 {
|
||||
if priv, pub, err = GenerateKeyPair(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPrivPem, priv); err != nil {
|
||||
return
|
||||
}
|
||||
if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPubPem, pub); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
} else {
|
||||
priv = settings[user_model.UserActivityPubPrivPem].SettingValue
|
||||
pub = settings[user_model.UserActivityPubPubPem].SettingValue
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// GetPublicKey function returns a user's public key
|
||||
func GetPublicKey(user *user_model.User) (pub string, err error) {
|
||||
pub, _, err = GetKeyPair(user)
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrivateKey function returns a user's private key
|
||||
func GetPrivateKey(user *user_model.User) (priv string, err error) {
|
||||
_, priv, err = GetKeyPair(user)
|
||||
return
|
||||
}
|
29
modules/activitypub/user_settings_test.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
_ "code.gitea.io/gitea/models" // https://discourse.gitea.io/t/testfixtures-could-not-clean-table-access-no-such-table-access/4137/4
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUserSettings(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
|
||||
pub, priv, err := GetKeyPair(user1)
|
||||
assert.NoError(t, err)
|
||||
pub1, err := GetPublicKey(user1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pub, pub1)
|
||||
priv1, err := GetPrivateKey(user1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, priv, priv1)
|
||||
}
|
BIN
modules/avatar/testdata/avatar.jpeg
vendored
Before Width: | Height: | Size: 521 B After Width: | Height: | Size: 287 B |
|
@ -4,21 +4,49 @@
|
|||
|
||||
package setting
|
||||
|
||||
import "code.gitea.io/gitea/modules/log"
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/go-fed/httpsig"
|
||||
)
|
||||
|
||||
// Federation settings
|
||||
var (
|
||||
Federation = struct {
|
||||
Enabled bool
|
||||
ShareUserStatistics bool
|
||||
MaxSize int64
|
||||
Algorithms []string
|
||||
DigestAlgorithm string
|
||||
GetHeaders []string
|
||||
PostHeaders []string
|
||||
}{
|
||||
Enabled: true,
|
||||
ShareUserStatistics: true,
|
||||
MaxSize: 4,
|
||||
Algorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"},
|
||||
DigestAlgorithm: "SHA-256",
|
||||
GetHeaders: []string{"(request-target)", "Date"},
|
||||
PostHeaders: []string{"(request-target)", "Date", "Digest"},
|
||||
}
|
||||
)
|
||||
|
||||
// Constant slice of httpsig algorithm objects
|
||||
var HttpsigAlgs []httpsig.Algorithm
|
||||
|
||||
func newFederationService() {
|
||||
if err := Cfg.Section("federation").MapTo(&Federation); err != nil {
|
||||
log.Fatal("Failed to map Federation settings: %v", err)
|
||||
} else if !httpsig.IsSupportedDigestAlgorithm(Federation.DigestAlgorithm) {
|
||||
log.Fatal("unsupported digest algorithm: %s", Federation.DigestAlgorithm)
|
||||
return
|
||||
}
|
||||
|
||||
// Get MaxSize in bytes instead of MiB
|
||||
Federation.MaxSize = 1 << 20 * Federation.MaxSize
|
||||
|
||||
HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.Algorithms))
|
||||
for i, alg := range Federation.Algorithms {
|
||||
HttpsigAlgs[i] = httpsig.Algorithm(alg)
|
||||
}
|
||||
}
|
||||
|
|
10
modules/structs/activitypub.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package structs
|
||||
|
||||
// ActivityPub type
|
||||
type ActivityPub struct {
|
||||
Context string `json:"@context"`
|
||||
}
|
|
@ -117,7 +117,7 @@ not_found = The target couldn't be found.
|
|||
network_error = Network error
|
||||
|
||||
[startpage]
|
||||
app_desc = A painless, self-hosted Git service
|
||||
app_desc = The world's first ActivityPub-federated Git server!
|
||||
install = Easy to install
|
||||
install_desc = Simply <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">run the binary</a> for your platform, ship it with <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, or get it <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">packaged</a>.
|
||||
platform = Cross-platform
|
||||
|
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 792 B After Width: | Height: | Size: 791 B |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.2 KiB |
|
@ -1,14 +1 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 520 520" style="enable-background:new 0 0 520 520;" xml:space="preserve">
|
||||
<path d="M13.7,11.9v496.2h35.7V520H0V0h49.4v11.9H13.7z"/>
|
||||
<path d="M166.3,169.2v25.1h0.7c6.7-9.6,14.8-17,24.2-22.2c9.4-5.3,20.3-7.9,32.5-7.9c11.7,0,22.4,2.3,32.1,6.8
|
||||
c9.7,4.5,17,12.6,22.1,24c5.5-8.1,13-15.3,22.4-21.5c9.4-6.2,20.6-9.3,33.5-9.3c9.8,0,18.9,1.2,27.3,3.6c8.4,2.4,15.5,6.2,21.5,11.5
|
||||
c6,5.3,10.6,12.1,14,20.6c3.3,8.5,5,18.7,5,30.7v124.1h-50.9V249.6c0-6.2-0.2-12.1-0.7-17.6c-0.5-5.5-1.8-10.3-3.9-14.3
|
||||
c-2.2-4.1-5.3-7.3-9.5-9.7c-4.2-2.4-9.9-3.6-17-3.6c-7.2,0-13,1.4-17.4,4.1c-4.4,2.8-7.9,6.3-10.4,10.8c-2.5,4.4-4.2,9.4-5,15.1
|
||||
c-0.8,5.6-1.3,11.3-1.3,17v103.3h-50.9v-104c0-5.5-0.1-10.9-0.4-16.3c-0.2-5.4-1.3-10.3-3.1-14.9c-1.8-4.5-4.8-8.2-9-10.9
|
||||
c-4.2-2.7-10.3-4.1-18.5-4.1c-2.4,0-5.6,0.5-9.5,1.6c-3.9,1.1-7.8,3.1-11.5,6.1c-3.7,3-6.9,7.3-9.5,12.9c-2.6,5.6-3.9,13-3.9,22.1
|
||||
v107.6h-50.9V169.2H166.3z"/>
|
||||
<path d="M506.3,508.1V11.9h-35.7V0H520v520h-49.4v-11.9H506.3z"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0" y="0" version="1.1" viewBox="0 0 520 520" xml:space="preserve" style="enable-background:new 0 0 520 520"><path d="M13.7,11.9v496.2h35.7V520H0V0h49.4v11.9H13.7z"/><path d="M166.3,169.2v25.1h0.7c6.7-9.6,14.8-17,24.2-22.2c9.4-5.3,20.3-7.9,32.5-7.9c11.7,0,22.4,2.3,32.1,6.8 c9.7,4.5,17,12.6,22.1,24c5.5-8.1,13-15.3,22.4-21.5c9.4-6.2,20.6-9.3,33.5-9.3c9.8,0,18.9,1.2,27.3,3.6c8.4,2.4,15.5,6.2,21.5,11.5 c6,5.3,10.6,12.1,14,20.6c3.3,8.5,5,18.7,5,30.7v124.1h-50.9V249.6c0-6.2-0.2-12.1-0.7-17.6c-0.5-5.5-1.8-10.3-3.9-14.3 c-2.2-4.1-5.3-7.3-9.5-9.7c-4.2-2.4-9.9-3.6-17-3.6c-7.2,0-13,1.4-17.4,4.1c-4.4,2.8-7.9,6.3-10.4,10.8c-2.5,4.4-4.2,9.4-5,15.1 c-0.8,5.6-1.3,11.3-1.3,17v103.3h-50.9v-104c0-5.5-0.1-10.9-0.4-16.3c-0.2-5.4-1.3-10.3-3.1-14.9c-1.8-4.5-4.8-8.2-9-10.9 c-4.2-2.7-10.3-4.1-18.5-4.1c-2.4,0-5.6,0.5-9.5,1.6c-3.9,1.1-7.8,3.1-11.5,6.1c-3.7,3-6.9,7.3-9.5,12.9c-2.6,5.6-3.9,13-3.9,22.1 v107.6h-50.9V169.2H166.3z"/><path d="M506.3,508.1V11.9h-35.7V0H520v520h-49.4v-11.9H506.3z"/></svg>
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.6 KiB |
|
@ -1,11 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<path
|
||||
d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
|
||||
id="path4"
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path id="path4" d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z" style="fill:#ff0;fill-opacity:1;stroke:#000;stroke-width:1.25402856;stroke-opacity:1"/></svg>
|
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 321 B |
|
@ -1,16 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="40"
|
||||
width="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
width="33.76017"
|
||||
height="33.76017"
|
||||
x="3.119915"
|
||||
y="3.119915" />
|
||||
<path
|
||||
d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect style="fill:#ff0;fill-opacity:1;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="33.76" height="33.76" x="3.12" y="3.12"/><path d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z" style="fill:#fff;fill-opacity:1;stroke:#000;stroke-width:.93031836;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/></svg>
|
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 745 B |
|
@ -1,26 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<g
|
||||
transform="translate(0,-60)"
|
||||
id="layer1">
|
||||
<rect
|
||||
width="36.460953"
|
||||
height="34.805603"
|
||||
x="1.7695236"
|
||||
y="62.597198"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.30826771;stroke-opacity:1" />
|
||||
<g
|
||||
transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)">
|
||||
<path
|
||||
d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.02768445;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g id="layer1" transform="translate(0,-60)"><rect width="36.461" height="34.806" x="1.77" y="62.597" style="fill:#ff0;fill-opacity:1;fill-rule:evenodd;stroke:#000;stroke-width:1.30826771;stroke-opacity:1"/><g><path d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z" transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)" style="fill:#fff;fill-opacity:1;fill-rule:nonzero;stroke:#000;stroke-width:1.02768445;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/><path d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z" transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)" style="fill:#000;fill-opacity:1;stroke:none"/></g></g></svg>
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2 KiB |
|
@ -1,10 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"
|
||||
style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><path d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z" style="fill:#ff0;fill-opacity:.94117647;fill-rule:nonzero;stroke:#000;stroke-width:1.00493038;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/></svg>
|
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 318 B |
|
@ -1,11 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"
|
||||
id="path604"
|
||||
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.72665179;stroke-opacity:1" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><path id="path604" d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z" style="fill:#ff0;fill-opacity:1;stroke:#000;stroke-width:1.72665179;stroke-opacity:1"/></svg>
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -1,11 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
viewBox="0 0 64 64">
|
||||
<path
|
||||
d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"
|
||||
id="path2985"
|
||||
style="fill:#ffff00;fill-opacity:0.94117647;fill-rule:nonzero;stroke:#000000;stroke-width:0.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><path id="path2985" d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z" style="fill:#ff0;fill-opacity:.94117647;fill-rule:nonzero;stroke:#000;stroke-width:.83403099;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/></svg>
|
Before Width: | Height: | Size: 426 B After Width: | Height: | Size: 330 B |
|
@ -1,7 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"/>
|
Before Width: | Height: | Size: 158 B After Width: | Height: | Size: 84 B |
|
@ -1,42 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
width="36.075428"
|
||||
height="31.096582"
|
||||
x="1.962286"
|
||||
y="4.4517088"
|
||||
id="rect4"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.23004246;stroke-opacity:1" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="1.5012145"
|
||||
x="6.0157046"
|
||||
y="10.285"
|
||||
id="rect6"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="6.0157056"
|
||||
y="23.21689"
|
||||
id="rect8"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="5.8130345"
|
||||
y="28.964394"
|
||||
id="rect10"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<rect
|
||||
width="27.96859"
|
||||
height="0.85783684"
|
||||
x="6.0157046"
|
||||
y="17.426493"
|
||||
id="rect12"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect id="rect4" width="36.075" height="31.097" x="1.962" y="4.452" style="fill:#ff0;fill-opacity:1;fill-rule:evenodd;stroke:#000;stroke-width:1.23004246;stroke-opacity:1"/><rect id="rect6" width="27.969" height="1.501" x="6.016" y="10.285" style="fill:#000;fill-opacity:1;stroke:none"/><rect id="rect8" width="27.969" height=".858" x="6.016" y="23.217" style="fill:#000;fill-opacity:1;stroke:none"/><rect id="rect10" width="27.969" height=".858" x="5.813" y="28.964" style="fill:#000;fill-opacity:1;stroke:none"/><rect id="rect12" width="27.969" height=".858" x="6.016" y="17.426" style="fill:#000;fill-opacity:1;stroke:none"/></svg>
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 717 B |
|
@ -1,16 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40">
|
||||
<rect
|
||||
width="33.76017"
|
||||
height="33.76017"
|
||||
x="3.119915"
|
||||
y="3.119915"
|
||||
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z"
|
||||
style="font-size:29.42051125px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.07795751;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect width="33.76" height="33.76" x="3.12" y="3.12" style="fill:#ff0;fill-opacity:1;fill-rule:evenodd;stroke:#000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/><path d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z" style="font-size:29.42051125px;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0;word-spacing:0;writing-mode:lr-tb;text-anchor:start;fill:#fff;fill-opacity:1;stroke:#000;stroke-width:1.07795751;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial"/></svg>
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 998 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
278
routers/api/v1/activitypub/person.go
Normal file
|
@ -0,0 +1,278 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/forgefed"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/activitypub"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
// Person function returns the Person actor for a user
|
||||
func Person(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username} activitypub activitypubPerson
|
||||
// ---
|
||||
// summary: Returns the Person actor for a user
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name
|
||||
person := ap.PersonNew(ap.IRI(link))
|
||||
|
||||
person.Name = ap.NaturalLanguageValuesNew()
|
||||
err := person.Name.Set("en", ap.Content(ctx.ContextUser.FullName))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Set Name", err)
|
||||
return
|
||||
}
|
||||
|
||||
person.PreferredUsername = ap.NaturalLanguageValuesNew()
|
||||
err = person.PreferredUsername.Set("en", ap.Content(ctx.ContextUser.Name))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Set PreferredUsername", err)
|
||||
return
|
||||
}
|
||||
|
||||
person.URL = ap.IRI(ctx.ContextUser.HTMLURL())
|
||||
|
||||
person.Icon = ap.Image{
|
||||
Type: ap.ImageType,
|
||||
MediaType: "image/png",
|
||||
URL: ap.IRI(ctx.ContextUser.AvatarLink()),
|
||||
}
|
||||
|
||||
person.Inbox = ap.IRI(link + "/inbox")
|
||||
person.Outbox = ap.IRI(link + "/outbox")
|
||||
|
||||
person.Following = ap.IRI(link + "/following")
|
||||
person.Followers = ap.IRI(link + "/followers")
|
||||
|
||||
person.Liked = ap.IRI(link + "/liked")
|
||||
|
||||
person.PublicKey.ID = ap.IRI(link + "#main-key")
|
||||
person.PublicKey.Owner = ap.IRI(link)
|
||||
|
||||
publicKeyPem, err := activitypub.GetPublicKey(ctx.ContextUser)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetPublicKey", err)
|
||||
return
|
||||
}
|
||||
person.PublicKey.PublicKeyPem = publicKeyPem
|
||||
|
||||
response(ctx, person)
|
||||
}
|
||||
|
||||
// PersonInbox function handles the incoming data for a user inbox
|
||||
func PersonInbox(ctx *context.APIContext) {
|
||||
// swagger:operation POST /activitypub/user/{username}/inbox activitypub activitypubPersonInbox
|
||||
// ---
|
||||
// summary: Send to the inbox
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(ctx.Req.Body, setting.Federation.MaxSize))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Error reading request body", err)
|
||||
}
|
||||
|
||||
var activity ap.Activity
|
||||
activity.UnmarshalJSON(body)
|
||||
if activity.Type == ap.FollowType {
|
||||
activitypub.Follow(ctx, activity)
|
||||
} else {
|
||||
log.Warn("ActivityStreams type not supported", activity)
|
||||
ctx.PlainText(http.StatusNotImplemented, "ActivityStreams type not supported")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// PersonOutbox function returns the user's Outbox OrderedCollection
|
||||
func PersonOutbox(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/outbox activitypub activitypubPersonOutbox
|
||||
// ---
|
||||
// summary: Returns the Outbox OrderedCollection
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
|
||||
feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
Actor: ctx.ContextUser,
|
||||
IncludePrivate: false,
|
||||
OnlyPerformedBy: true,
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Couldn't fetch outbox", err)
|
||||
}
|
||||
|
||||
outbox := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
for _, action := range feed {
|
||||
/*if action.OpType == ExampleType {
|
||||
activity := ap.ExampleNew()
|
||||
outbox.OrderedItems.Append(activity)
|
||||
}*/
|
||||
log.Debug(action.Content)
|
||||
}
|
||||
outbox.TotalItems = uint(len(outbox.OrderedItems))
|
||||
|
||||
response(ctx, outbox)
|
||||
}
|
||||
|
||||
// PersonFollowing function returns the user's Following Collection
|
||||
func PersonFollowing(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/following activitypub activitypubPersonFollowing
|
||||
// ---
|
||||
// summary: Returns the Following Collection
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
|
||||
users, err := user_model.GetUserFollowing(ctx.ContextUser, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err)
|
||||
return
|
||||
}
|
||||
|
||||
following := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
following.TotalItems = uint(len(users))
|
||||
|
||||
for _, user := range users {
|
||||
// TODO: handle non-Federated users
|
||||
person := ap.PersonNew(ap.IRI(user.Website))
|
||||
following.OrderedItems.Append(person)
|
||||
}
|
||||
|
||||
response(ctx, following)
|
||||
}
|
||||
|
||||
// PersonFollowers function returns the user's Followers Collection
|
||||
func PersonFollowers(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/followers activitypub activitypubPersonFollowers
|
||||
// ---
|
||||
// summary: Returns the Followers Collection
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
|
||||
users, err := user_model.GetUserFollowers(ctx.ContextUser, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
|
||||
return
|
||||
}
|
||||
|
||||
followers := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
followers.TotalItems = uint(len(users))
|
||||
|
||||
for _, user := range users {
|
||||
person := ap.PersonNew(ap.IRI(user.Website))
|
||||
followers.OrderedItems.Append(person)
|
||||
}
|
||||
|
||||
response(ctx, followers)
|
||||
}
|
||||
|
||||
// PersonLiked function returns the user's Liked Collection
|
||||
func PersonLiked(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/followers activitypub activitypubPersonLiked
|
||||
// ---
|
||||
// summary: Returns the Liked Collection
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
|
||||
repos, count, err := repo_model.SearchRepository(&repo_model.SearchRepoOptions{
|
||||
Actor: ctx.Doer,
|
||||
Private: ctx.IsSigned,
|
||||
StarredByID: ctx.ContextUser.ID,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserStarred", err)
|
||||
return
|
||||
}
|
||||
|
||||
liked := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
liked.TotalItems = uint(count)
|
||||
|
||||
for _, repo := range repos {
|
||||
repo := forgefed.RepositoryNew(ap.IRI(strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + repo.OwnerName + "/" + repo.Name))
|
||||
liked.OrderedItems.Append(repo)
|
||||
}
|
||||
|
||||
response(ctx, liked)
|
||||
}
|
198
routers/api/v1/activitypub/repo.go
Normal file
|
@ -0,0 +1,198 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/forgefed"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
// Repo function
|
||||
func Repo(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/{reponame} activitypub activitypubRepo
|
||||
// ---
|
||||
// summary: Returns the repository
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: reponame
|
||||
// in: path
|
||||
// description: name of the repository
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
repo := forgefed.RepositoryNew(ap.IRI(link))
|
||||
|
||||
repo.Name = ap.NaturalLanguageValuesNew()
|
||||
err := repo.Name.Set("en", ap.Content(ctx.Repo.Repository.Name))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Set Name", err)
|
||||
return
|
||||
}
|
||||
|
||||
repo.AttributedTo = ap.IRI(strings.TrimSuffix(link, "/"+ctx.Repo.Repository.Name))
|
||||
|
||||
repo.Summary = ap.NaturalLanguageValuesNew()
|
||||
err = repo.Summary.Set("en", ap.Content(ctx.Repo.Repository.Description))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Set Description", err)
|
||||
return
|
||||
}
|
||||
|
||||
repo.Inbox = ap.IRI(link + "/inbox")
|
||||
repo.Outbox = ap.IRI(link + "/outbox")
|
||||
repo.Followers = ap.IRI(link + "/followers")
|
||||
repo.Team = ap.IRI(link + "/team")
|
||||
|
||||
response(ctx, repo)
|
||||
}
|
||||
|
||||
// RepoInbox function
|
||||
func RepoInbox(ctx *context.APIContext) {
|
||||
// swagger:operation POST /activitypub/user/{username}/{reponame}/inbox activitypub activitypubRepoInbox
|
||||
// ---
|
||||
// summary: Send to the inbox
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: reponame
|
||||
// in: path
|
||||
// description: name of the repository
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
body, err := io.ReadAll(ctx.Req.Body)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Error reading request body", err)
|
||||
}
|
||||
|
||||
var activity ap.Activity
|
||||
activity.UnmarshalJSON(body)
|
||||
if activity.Type == ap.FollowType {
|
||||
// activitypub.Follow(ctx, activity)
|
||||
} else {
|
||||
log.Warn("ActivityStreams type not supported", activity)
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RepoOutbox function
|
||||
func RepoOutbox(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/outbox activitypub activitypubPersonOutbox
|
||||
// ---
|
||||
// summary: Returns the outbox
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: reponame
|
||||
// in: path
|
||||
// description: name of the repository
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
|
||||
feed, err := models.GetFeeds(ctx, models.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
Actor: ctx.ContextUser,
|
||||
IncludePrivate: false,
|
||||
OnlyPerformedBy: true,
|
||||
IncludeDeleted: false,
|
||||
Date: ctx.FormString("date"),
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Couldn't fetch outbox", err)
|
||||
}
|
||||
|
||||
outbox := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
for _, action := range feed {
|
||||
/*if action.OpType == ExampleType {
|
||||
activity := ap.ExampleNew()
|
||||
outbox.OrderedItems.Append(activity)
|
||||
}*/
|
||||
log.Debug(action.Content)
|
||||
}
|
||||
outbox.TotalItems = uint(len(outbox.OrderedItems))
|
||||
|
||||
response(ctx, outbox)
|
||||
}
|
||||
|
||||
// RepoFollowers function
|
||||
func RepoFollowers(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username}/{reponame}/followers activitypub activitypubRepoFollowers
|
||||
// ---
|
||||
// summary: Returns the followers collection
|
||||
// produces:
|
||||
// - application/activity+json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: reponame
|
||||
// in: path
|
||||
// description: name of the repository
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + strings.TrimSuffix(ctx.Req.URL.EscapedPath(), "/")
|
||||
|
||||
users, err := user_model.GetUserFollowers(ctx.ContextUser, utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
|
||||
return
|
||||
}
|
||||
|
||||
followers := ap.OrderedCollectionNew(ap.IRI(link))
|
||||
followers.TotalItems = uint(len(users))
|
||||
|
||||
for _, user := range users {
|
||||
person := ap.PersonNew(ap.IRI(user.Website))
|
||||
followers.OrderedItems.Append(person)
|
||||
}
|
||||
|
||||
response(ctx, followers)
|
||||
}
|
82
routers/api/v1/activitypub/reqsignature.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"code.gitea.io/gitea/modules/activitypub"
|
||||
gitea_context "code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/go-fed/httpsig"
|
||||
)
|
||||
|
||||
func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) {
|
||||
person := ap.PersonNew(ap.IRI(keyID.String()))
|
||||
err = person.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %v", err)
|
||||
return
|
||||
}
|
||||
pubKey := person.PublicKey
|
||||
if pubKey.ID.String() != keyID.String() {
|
||||
err = fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
|
||||
return
|
||||
}
|
||||
pubKeyPem := pubKey.PublicKeyPem
|
||||
block, _ := pem.Decode([]byte(pubKeyPem))
|
||||
if block == nil || block.Type != "PUBLIC KEY" {
|
||||
err = fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
|
||||
return
|
||||
}
|
||||
p, err = x509.ParsePKIXPublicKey(block.Bytes)
|
||||
return
|
||||
}
|
||||
|
||||
func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
|
||||
r := ctx.Req
|
||||
|
||||
// 1. Figure out what key we need to verify
|
||||
v, err := httpsig.NewVerifier(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ID := v.KeyId()
|
||||
idIRI, err := url.Parse(ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 2. Fetch the public key of the other actor
|
||||
b, err := activitypub.Fetch(idIRI)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pubKey, err := getPublicKeyFromResponse(b, idIRI)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 3. Verify the other actor's key
|
||||
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
|
||||
authenticated = v.Verify(pubKey, algo) == nil
|
||||
return
|
||||
}
|
||||
|
||||
// ReqHTTPSignature function
|
||||
func ReqHTTPSignature() func(ctx *gitea_context.APIContext) {
|
||||
return func(ctx *gitea_context.APIContext) {
|
||||
if authenticated, err := verifyHTTPSignatures(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "verifyHttpSignatures", err)
|
||||
} else if !authenticated {
|
||||
ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed")
|
||||
}
|
||||
}
|
||||
}
|
29
routers/api/v1/activitypub/response.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/activitypub"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
ap "github.com/go-ap/activitypub"
|
||||
"github.com/go-ap/jsonld"
|
||||
)
|
||||
|
||||
func response(ctx *context.APIContext, v interface{}) {
|
||||
binary, err := jsonld.WithContext(jsonld.IRI(ap.ActivityBaseURI), jsonld.IRI(ap.SecurityContextURI)).Marshal(v)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "Marshal", err)
|
||||
return
|
||||
}
|
||||
ctx.Resp.Header().Add("Content-Type", activitypub.ActivityStreamsContentType)
|
||||
ctx.Resp.WriteHeader(http.StatusOK)
|
||||
if _, err = ctx.Resp.Write(binary); err != nil {
|
||||
log.Error("write to resp err: %v", err)
|
||||
}
|
||||
}
|
|
@ -81,6 +81,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/activitypub"
|
||||
"code.gitea.io/gitea/routers/api/v1/admin"
|
||||
"code.gitea.io/gitea/routers/api/v1/misc"
|
||||
"code.gitea.io/gitea/routers/api/v1/notify"
|
||||
|
@ -643,6 +644,22 @@ func Routes() *web.Route {
|
|||
m.Get("/version", misc.Version)
|
||||
if setting.Federation.Enabled {
|
||||
m.Get("/nodeinfo", misc.NodeInfo)
|
||||
m.Group("/activitypub", func() {
|
||||
m.Group("/user/{username}", func() {
|
||||
m.Get("", activitypub.Person)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
|
||||
m.Get("/outbox", activitypub.PersonOutbox)
|
||||
m.Get("/following", activitypub.PersonFollowing)
|
||||
m.Get("/followers", activitypub.PersonFollowers)
|
||||
m.Get("/liked", activitypub.PersonLiked)
|
||||
m.Group("/{reponame}", func() {
|
||||
m.Get("", activitypub.Repo)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.RepoInbox)
|
||||
m.Get("/outbox", activitypub.RepoOutbox)
|
||||
m.Get("/followers", activitypub.RepoFollowers)
|
||||
}, repoAssignment())
|
||||
}, context_service.UserAssignmentAPI())
|
||||
})
|
||||
}
|
||||
m.Get("/signing-key.gpg", misc.SigningKey)
|
||||
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
|
||||
|
|
16
routers/api/v1/swagger/activitypub.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package swagger
|
||||
|
||||
import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ActivityPub
|
||||
// swagger:response ActivityPub
|
||||
type swaggerResponseActivityPub struct {
|
||||
// in:body
|
||||
Body api.ActivityPub `json:"body"`
|
||||
}
|
|
@ -36,6 +36,12 @@ func Profile(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if strings.Contains(ctx.ContextUser.Name, "@") {
|
||||
ctx.Resp.Header().Add("Location", ctx.ContextUser.Website)
|
||||
ctx.Resp.WriteHeader(http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.ContextUser.IsOrganization() {
|
||||
org.Home(ctx)
|
||||
return
|
||||
|
|
|
@ -29,6 +29,7 @@ type webfingerLink struct {
|
|||
Rel string `json:"rel,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Href string `json:"href,omitempty"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Titles map[string]string `json:"titles,omitempty"`
|
||||
Properties map[string]interface{} `json:"properties,omitempty"`
|
||||
}
|
||||
|
@ -86,6 +87,7 @@ func WebfingerQuery(ctx *context.Context) {
|
|||
|
||||
aliases := []string{
|
||||
u.HTMLURL(),
|
||||
appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(u.Name),
|
||||
}
|
||||
if !u.KeepEmailPrivate {
|
||||
aliases = append(aliases, fmt.Sprintf("mailto:%s", u.Email))
|
||||
|
@ -101,8 +103,18 @@ func WebfingerQuery(ctx *context.Context) {
|
|||
Rel: "http://webfinger.net/rel/avatar",
|
||||
Href: u.AvatarLink(),
|
||||
},
|
||||
{
|
||||
Rel: "self",
|
||||
Type: "application/activity+json",
|
||||
Href: appURL.String() + "api/v1/activitypub/user/" + url.PathEscape(u.Name),
|
||||
},
|
||||
{
|
||||
Rel: "http://ostatus.org/schema/1.0/subscribe",
|
||||
Template: appURL.String() + "api/v1/authorize_interaction?uri={uri}",
|
||||
},
|
||||
}
|
||||
|
||||
ctx.Resp.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
ctx.JSON(http.StatusOK, &webfingerJRD{
|
||||
Subject: fmt.Sprintf("acct:%s@%s", url.QueryEscape(u.Name), appURL.Host),
|
||||
Aliases: aliases,
|
||||
|
|
|
@ -23,6 +23,242 @@
|
|||
},
|
||||
"basePath": "{{AppSubUrl | JSEscape | Safe}}/api/v1",
|
||||
"paths": {
|
||||
"/activitypub/user/{username}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Returns the Person actor for a user",
|
||||
"operationId": "activitypubPerson",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActivityPub"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/followers": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Returns the Liked Collection",
|
||||
"operationId": "activitypubPersonLiked",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActivityPub"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/following": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Returns the Following Collection",
|
||||
"operationId": "activitypubPersonFollowing",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActivityPub"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/inbox": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Send to the inbox",
|
||||
"operationId": "activitypubPersonInbox",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/outbox": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Returns the outbox",
|
||||
"operationId": "activitypubPersonOutbox",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "name of the repository",
|
||||
"name": "reponame",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActivityPub"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/{reponame}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Returns the repository",
|
||||
"operationId": "activitypubRepo",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "name of the repository",
|
||||
"name": "reponame",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActivityPub"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/{reponame}/followers": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Returns the followers collection",
|
||||
"operationId": "activitypubRepoFollowers",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "name of the repository",
|
||||
"name": "reponame",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/ActivityPub"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/activitypub/user/{username}/{reponame}/inbox": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/activity+json"
|
||||
],
|
||||
"tags": [
|
||||
"activitypub"
|
||||
],
|
||||
"summary": "Send to the inbox",
|
||||
"operationId": "activitypubRepoInbox",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of the user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "name of the repository",
|
||||
"name": "reponame",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/cron": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
@ -13113,6 +13349,17 @@
|
|||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"ActivityPub": {
|
||||
"description": "ActivityPub type",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"@context": {
|
||||
"type": "string",
|
||||
"x-go-name": "Context"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"AddCollaboratorOption": {
|
||||
"description": "AddCollaboratorOption options when adding a user as a collaborator of a repository",
|
||||
"type": "object",
|
||||
|
@ -18794,6 +19041,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ActivityPub": {
|
||||
"description": "ActivityPub",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ActivityPub"
|
||||
}
|
||||
},
|
||||
"AnnotatedTag": {
|
||||
"description": "AnnotatedTag",
|
||||
"schema": {
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="448" height="448" viewBox="0 0 448 448">
|
||||
<path d="M271.5 0v384l-68 32c-115-10.25-203.5-71.5-203.5-145.75 0-71.5 82.5-131 191.75-144.25v43c-71.5 12.5-124 53-124 101.25 0 51 58.5 93.25 135.75 103v0-340zM438.75 145.5l9.25 97.5-131.25-28.5 36.75-20.75c-19.5-11.5-43.5-20-70-24.75v-43c46.25 5.5 87.75 19.5 120.25 39.25z"></path>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="448" height="448" version="1.1" viewBox="0 0 448 448"><path d="M271.5 0v384l-68 32c-115-10.25-203.5-71.5-203.5-145.75 0-71.5 82.5-131 191.75-144.25v43c-71.5 12.5-124 53-124 101.25 0 51 58.5 93.25 135.75 103v0-340zM438.75 145.5l9.25 97.5-131.25-28.5 36.75-20.75c-19.5-11.5-43.5-20-70-24.75v-43c46.25 5.5 87.75 19.5 120.25 39.25z"/></svg>
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 383 B |
|
@ -1,3 +1 @@
|
|||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="416" height="448" viewBox="0 0 416 448">
|
||||
<path d="M170.5 251.5v162.75l-170.5-23.5v-139.25h170.5zM170.5 65.75v164.75h-170.5v-141.25zM416 251.5v196.5l-226.75-31.25v-165.25h226.75zM416 32v198.5h-226.75v-167.25z"></path>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" version="1.1" viewBox="0 0 416 448"><path d="M170.5 251.5v162.75l-170.5-23.5v-139.25h170.5zM170.5 65.75v164.75h-170.5v-141.25zM416 251.5v196.5l-226.75-31.25v-165.25h226.75zM416 32v198.5h-226.75v-167.25z"/></svg>
|
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 276 B |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="64px" height="64px"><path d="M42.2,22.1L25.9,5.8C25.4,5.3,24.7,5,24,5c0,0,0,0,0,0c-0.7,0-1.4,0.3-1.9,0.8l-3.5,3.5l4.1,4.1c0.4-0.2,0.8-0.3,1.3-0.3c1.7,0,3,1.3,3,3c0,0.5-0.1,0.9-0.3,1.3l4,4c0.4-0.2,0.8-0.3,1.3-0.3c1.7,0,3,1.3,3,3s-1.3,3-3,3c-1.7,0-3-1.3-3-3c0-0.5,0.1-0.9,0.3-1.3l-4-4c-0.1,0-0.2,0.1-0.3,0.1v10.4c1.2,0.4,2,1.5,2,2.8c0,1.7-1.3,3-3,3s-3-1.3-3-3c0-1.3,0.8-2.4,2-2.8V18.8c-1.2-0.4-2-1.5-2-2.8c0-0.5,0.1-0.9,0.3-1.3l-4.1-4.1L5.8,22.1C5.3,22.6,5,23.3,5,24c0,0.7,0.3,1.4,0.8,1.9l16.3,16.3c0,0,0,0,0,0c0.5,0.5,1.2,0.8,1.9,0.8s1.4-0.3,1.9-0.8l16.3-16.3c0.5-0.5,0.8-1.2,0.8-1.9C43,23.3,42.7,22.6,42.2,22.1z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 48 48"><path d="M42.2,22.1L25.9,5.8C25.4,5.3,24.7,5,24,5c0,0,0,0,0,0c-0.7,0-1.4,0.3-1.9,0.8l-3.5,3.5l4.1,4.1c0.4-0.2,0.8-0.3,1.3-0.3c1.7,0,3,1.3,3,3c0,0.5-0.1,0.9-0.3,1.3l4,4c0.4-0.2,0.8-0.3,1.3-0.3c1.7,0,3,1.3,3,3s-1.3,3-3,3c-1.7,0-3-1.3-3-3c0-0.5,0.1-0.9,0.3-1.3l-4-4c-0.1,0-0.2,0.1-0.3,0.1v10.4c1.2,0.4,2,1.5,2,2.8c0,1.7-1.3,3-3,3s-3-1.3-3-3c0-1.3,0.8-2.4,2-2.8V18.8c-1.2-0.4-2-1.5-2-2.8c0-0.5,0.1-0.9,0.3-1.3l-4.1-4.1L5.8,22.1C5.3,22.6,5,23.3,5,24c0,0.7,0.3,1.4,0.8,1.9l16.3,16.3c0,0,0,0,0,0c0.5,0.5,1.2,0.8,1.9,0.8s1.4-0.3,1.9-0.8l16.3-16.3c0.5-0.5,0.8-1.2,0.8-1.9C43,23.3,42.7,22.6,42.2,22.1z"/></svg>
|
Before Width: | Height: | Size: 687 B After Width: | Height: | Size: 683 B |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64px" height="64px"><linearGradient id="KpzH_ttTMIjq8dhx1zD2pa" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#KpzH_ttTMIjq8dhx1zD2pa)" d="M25.008,56.007c-0.003-0.368-0.006-1.962-0.009-3.454l-0.003-1.55 c-6.729,0.915-8.358-3.78-8.376-3.83c-0.934-2.368-2.211-3.045-2.266-3.073l-0.124-0.072c-0.463-0.316-1.691-1.157-1.342-2.263 c0.315-0.997,1.536-1.1,2.091-1.082c3.074,0.215,4.63,2.978,4.694,3.095c1.569,2.689,3.964,2.411,5.509,1.844 c0.144-0.688,0.367-1.32,0.659-1.878C20.885,42.865,15.27,40.229,15.27,30.64c0-2.633,0.82-4.96,2.441-6.929 c-0.362-1.206-0.774-3.666,0.446-6.765l0.174-0.442l0.452-0.144c0.416-0.137,2.688-0.624,7.359,2.433 c1.928-0.494,3.969-0.749,6.074-0.759c2.115,0.01,4.158,0.265,6.09,0.759c4.667-3.058,6.934-2.565,7.351-2.433l0.451,0.145 l0.174,0.44c1.225,3.098,0.813,5.559,0.451,6.766c1.618,1.963,2.438,4.291,2.438,6.929c0,9.591-5.621,12.219-10.588,13.087 c0.563,1.065,0.868,2.402,0.868,3.878c0,1.683-0.007,7.204-0.015,8.402l-2-0.014c0.008-1.196,0.015-6.708,0.015-8.389 c0-2.442-0.943-3.522-1.35-3.874l-1.73-1.497l2.274-0.253c5.205-0.578,10.525-2.379,10.525-11.341c0-2.33-0.777-4.361-2.31-6.036 l-0.43-0.469l0.242-0.587c0.166-0.401,0.894-2.442-0.043-5.291c-0.758,0.045-2.568,0.402-5.584,2.447l-0.384,0.259l-0.445-0.123 c-1.863-0.518-3.938-0.796-6.001-0.806c-2.052,0.01-4.124,0.288-5.984,0.806l-0.445,0.123l-0.383-0.259 c-3.019-2.044-4.833-2.404-5.594-2.449c-0.935,2.851-0.206,4.892-0.04,5.293l0.242,0.587l-0.429,0.469 c-1.536,1.681-2.314,3.712-2.314,6.036c0,8.958,5.31,10.77,10.504,11.361l2.252,0.256l-1.708,1.49 c-0.372,0.325-1.03,1.112-1.254,2.727l-0.075,0.549l-0.506,0.227c-1.321,0.592-5.839,2.162-8.548-2.485 c-0.015-0.025-0.544-0.945-1.502-1.557c0.646,0.639,1.433,1.673,2.068,3.287c0.066,0.19,1.357,3.622,7.28,2.339l1.206-0.262 l0.012,3.978c0.003,1.487,0.006,3.076,0.009,3.444L25.008,56.007z"/><linearGradient id="KpzH_ttTMIjq8dhx1zD2pb" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#KpzH_ttTMIjq8dhx1zD2pb)" d="M32,58C17.663,58,6,46.337,6,32S17.663,6,32,6s26,11.663,26,26S46.337,58,32,58z M32,8 C18.767,8,8,18.767,8,32s10.767,24,24,24s24-10.767,24-24S45.233,8,32,8z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><linearGradient id="KpzH_ttTMIjq8dhx1zD2pa" x1="30.999" x2="30.999" y1="16" y2="55.342" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#6dc7ff"/><stop offset="1" stop-color="#e6abff"/></linearGradient><path fill="url(#KpzH_ttTMIjq8dhx1zD2pa)" d="M25.008,56.007c-0.003-0.368-0.006-1.962-0.009-3.454l-0.003-1.55 c-6.729,0.915-8.358-3.78-8.376-3.83c-0.934-2.368-2.211-3.045-2.266-3.073l-0.124-0.072c-0.463-0.316-1.691-1.157-1.342-2.263 c0.315-0.997,1.536-1.1,2.091-1.082c3.074,0.215,4.63,2.978,4.694,3.095c1.569,2.689,3.964,2.411,5.509,1.844 c0.144-0.688,0.367-1.32,0.659-1.878C20.885,42.865,15.27,40.229,15.27,30.64c0-2.633,0.82-4.96,2.441-6.929 c-0.362-1.206-0.774-3.666,0.446-6.765l0.174-0.442l0.452-0.144c0.416-0.137,2.688-0.624,7.359,2.433 c1.928-0.494,3.969-0.749,6.074-0.759c2.115,0.01,4.158,0.265,6.09,0.759c4.667-3.058,6.934-2.565,7.351-2.433l0.451,0.145 l0.174,0.44c1.225,3.098,0.813,5.559,0.451,6.766c1.618,1.963,2.438,4.291,2.438,6.929c0,9.591-5.621,12.219-10.588,13.087 c0.563,1.065,0.868,2.402,0.868,3.878c0,1.683-0.007,7.204-0.015,8.402l-2-0.014c0.008-1.196,0.015-6.708,0.015-8.389 c0-2.442-0.943-3.522-1.35-3.874l-1.73-1.497l2.274-0.253c5.205-0.578,10.525-2.379,10.525-11.341c0-2.33-0.777-4.361-2.31-6.036 l-0.43-0.469l0.242-0.587c0.166-0.401,0.894-2.442-0.043-5.291c-0.758,0.045-2.568,0.402-5.584,2.447l-0.384,0.259l-0.445-0.123 c-1.863-0.518-3.938-0.796-6.001-0.806c-2.052,0.01-4.124,0.288-5.984,0.806l-0.445,0.123l-0.383-0.259 c-3.019-2.044-4.833-2.404-5.594-2.449c-0.935,2.851-0.206,4.892-0.04,5.293l0.242,0.587l-0.429,0.469 c-1.536,1.681-2.314,3.712-2.314,6.036c0,8.958,5.31,10.77,10.504,11.361l2.252,0.256l-1.708,1.49 c-0.372,0.325-1.03,1.112-1.254,2.727l-0.075,0.549l-0.506,0.227c-1.321,0.592-5.839,2.162-8.548-2.485 c-0.015-0.025-0.544-0.945-1.502-1.557c0.646,0.639,1.433,1.673,2.068,3.287c0.066,0.19,1.357,3.622,7.28,2.339l1.206-0.262 l0.012,3.978c0.003,1.487,0.006,3.076,0.009,3.444L25.008,56.007z"/><linearGradient id="KpzH_ttTMIjq8dhx1zD2pb" x1="32" x2="32" y1="5" y2="59.167" gradientUnits="userSpaceOnUse" spreadMethod="reflect"><stop offset="0" stop-color="#1a6dff"/><stop offset="1" stop-color="#c822ff"/></linearGradient><path fill="url(#KpzH_ttTMIjq8dhx1zD2pb)" d="M32,58C17.663,58,6,46.337,6,32S17.663,6,32,6s26,11.663,26,26S46.337,58,32,58z M32,8 C18.767,8,8,18.767,8,32s10.767,24,24,24s24-10.767,24-24S45.233,8,32,8z"/></svg>
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="64px" height="64px"><path fill="#e53935" d="M24 43L16 20 32 20z"/><path fill="#ff7043" d="M24 43L42 20 32 20z"/><path fill="#e53935" d="M37 5L42 20 32 20z"/><path fill="#ffa726" d="M24 43L42 20 45 28z"/><path fill="#ff7043" d="M24 43L6 20 16 20z"/><path fill="#e53935" d="M11 5L6 20 16 20z"/><path fill="#ffa726" d="M24 43L6 20 3 28z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 48 48"><path fill="#e53935" d="M24 43L16 20 32 20z"/><path fill="#ff7043" d="M24 43L42 20 32 20z"/><path fill="#e53935" d="M37 5L42 20 32 20z"/><path fill="#ffa726" d="M24 43L42 20 45 28z"/><path fill="#ff7043" d="M24 43L6 20 16 20z"/><path fill="#e53935" d="M11 5L6 20 16 20z"/><path fill="#ffa726" d="M24 43L6 20 3 28z"/></svg>
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 405 B |
|
@ -1,60 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="67.73333mm"
|
||||
height="67.73333mm"
|
||||
viewBox="0 0 67.73333 67.73333"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
||||
sodipodi:docname="gitea-gogs.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.4352161"
|
||||
inkscape:cx="145.15203"
|
||||
inkscape:cy="129.56614"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer3"
|
||||
inkscape:document-rotation="0"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1006"
|
||||
inkscape:window-x="1200"
|
||||
inkscape:window-y="444"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Layer 3"
|
||||
style="display:inline">
|
||||
<path
|
||||
style="display:inline;fill:#d75547;fill-opacity:1;fill-rule:evenodd;stroke:#428f29;stroke-width:0.0190672;paint-order:markers fill stroke"
|
||||
d="M 28.763808,62.186078 C 28.552224,62.156588 28.3282,62.079645 28.265977,62.01509 28.123377,61.867145 27.941284,61.235718 27.375971,58.928893 27.128273,57.918138 26.870167,56.952588 26.8024,56.783223 26.653428,56.410912 26.321729,56.197407 24.664484,55.407125 22.815394,54.525354 20.800132,53.303558 19.034519,51.99383 l -0.753641,-0.55905 -2.322335,0.678345 c -1.277286,0.37309 -2.657942,0.75099 -3.068125,0.839774 l -0.745787,0.161424 -0.351437,-0.536616 C 10.841488,51.124528 8.2297585,46.620395 7.1670955,44.599636 L 6.6102577,43.540752 7.0934858,42.948695 c 0.2657754,-0.325634 1.2926661,-1.41771 2.2819797,-2.426837 l 1.7987515,-1.834779 -0.180416,-1.038637 c -0.160803,-0.925735 -0.180684,-1.319661 -0.182886,-3.623976 -0.002,-2.082515 0.02593,-2.77882 0.143552,-3.580102 L 11.100491,29.449609 9.0366642,27.414662 C 7.5050421,25.904473 6.9326263,25.287565 6.8169034,25.02237 6.6626002,24.668764 6.6626297,24.660735 6.8197249,24.254845 7.2963868,23.023252 11.239804,16.273894 12.005659,15.378857 l 0.257067,-0.300427 0.484759,0.125942 c 0.266618,0.06927 1.585016,0.43706 2.929773,0.817315 l 2.445013,0.691373 0.429771,-0.260466 c 0.236371,-0.143253 0.844892,-0.564341 1.352266,-0.935741 1.30433,-0.954784 2.690014,-1.762213 4.707543,-2.74306 1.87475,-0.911433 1.838916,-0.883043 2.124645,-1.683256 0.06563,-0.1838 0.336217,-1.1703787 0.601305,-2.1923981 0.26509,-1.0220196 0.573575,-2.1360624 0.685518,-2.4756513 0.17614,-0.5343091 0.23295,-0.623873 0.421984,-0.6652627 0.483669,-0.1058996 3.97054,-0.1502609 7.292057,-0.092772 l 3.518704,0.060902 0.430989,1.7479767 c 0.510047,2.0686117 0.909017,3.4804444 1.127722,3.9906574 0.174557,0.407221 0.0081,0.308631 2.593742,1.53554 1.3556,0.643225 2.654323,1.424602 4.307061,2.591347 0.702523,0.495943 1.33877,0.943783 1.413882,0.995202 0.109699,0.0751 0.502152,-0.01615 1.994813,-0.463793 1.022033,-0.306501 2.381778,-0.689488 3.021651,-0.85108 l 1.163411,-0.293805 0.145052,0.19205 c 0.93221,1.234241 5.324678,8.900157 5.324678,9.292846 0,0.333447 -0.488016,0.914807 -2.540796,3.02678 l -1.893181,1.947777 0.15824,0.960735 c 0.227755,1.382822 0.274355,3.126299 0.15827,5.921847 l -0.101763,2.450639 1.510118,1.495377 c 1.549811,1.534681 2.47101,2.550338 2.683271,2.958414 0.115947,0.22291 0.103768,0.264805 -0.328833,1.13109 -1.289906,2.583039 -4.510781,8.106197 -4.924647,8.444791 -0.229142,0.187467 -0.906252,0.05936 -3.361841,-0.636096 -1.264388,-0.358089 -2.427812,-0.66997 -2.585382,-0.693076 -0.254259,-0.03729 -0.377393,0.02128 -1.094426,0.520539 -2.75021,1.914888 -3.396183,2.301016 -5.615132,3.356412 -0.864328,0.4111 -1.586938,0.808234 -1.713456,0.94169 -0.279925,0.295269 -0.499881,1.010417 -1.141053,3.709893 l -0.508517,2.140988 -1.893589,0.04981 c -2.516342,0.0662 -8.338233,0.06367 -8.822539,-0.0038 z m 7.858077,-12.120535 c 2.091378,-0.388455 5.175143,-1.723813 6.867427,-2.973786 1.796481,-1.326945 4.757949,-4.883235 4.97652,-5.976082 0.04707,-0.23538 0.0195,-0.268126 -0.544626,-0.646577 -0.89966,-0.603557 -2.187787,-1.377201 -5.078243,-3.049968 -4.184205,-2.421491 -4.52345,-2.676285 -4.524118,-3.397889 -0.0011,-1.340016 -1.219035,-3.278179 -2.4241,-3.858035 -1.790528,-0.861573 -3.841611,-0.52172 -5.213619,0.863866 -0.315085,0.318202 -0.680808,0.789483 -0.82133,1.058382 -1.355657,2.59418 0.09164,5.558218 3.005202,6.154597 0.886416,0.181439 1.72976,0.07365 2.913276,-0.372328 l 0.879991,-0.331607 2.02184,1.158352 c 2.672299,1.531012 3.61875,2.12207 4.1007,2.560884 0.340094,0.309652 0.391458,0.394601 0.346398,0.57285 -0.0768,0.303803 -0.673843,1.002125 -1.394132,1.630629 -1.608465,1.403495 -3.352982,2.265892 -5.547142,2.742214 -1.178042,0.255735 -3.523379,0.256058 -4.699639,6.79e-4 -1.211182,-0.262989 -1.987409,-0.533958 -3.06492,-1.06991 -3.757271,-1.868855 -6.310115,-5.470633 -6.829909,-9.636234 -0.156607,-1.255046 -0.06748,-3.197221 0.200967,-4.379595 1.024517,-4.512371 4.409288,-7.971423 8.971948,-9.168835 1.368354,-0.359109 1.98948,-0.43042 3.32699,-0.381966 1.261093,0.04568 1.953923,0.167871 3.540434,0.624377 0.565292,0.162659 1.094549,0.260446 1.454278,0.268701 0.495297,0.01136 0.620752,-0.01798 1.009916,-0.236201 1.079693,-0.605431 1.3594,-1.855128 0.602683,-2.692712 -1.317562,-1.458359 -6.682213,-2.214558 -10.373764,-1.462279 -2.483279,0.506053 -5.053495,1.805105 -6.993517,3.534699 -2.575021,2.295712 -4.259712,4.711906 -5.127071,7.353271 -0.924677,2.815913 -1.025226,6.141317 -0.275431,9.109059 0.774038,3.063694 2.549965,5.963534 4.958139,8.095943 2.441521,2.161939 5.76675,3.672334 8.927653,4.055148 1.089179,0.13191 3.734827,0.04956 4.807199,-0.149613 z"
|
||||
id="path851" />
|
||||
</g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" id="svg8" width="256" height="256" version="1.1" viewBox="0 0 67.733 67.733"><metadata id="metadata5"/><g id="layer3" style="display:inline"><path style="display:inline;fill:#d75547;fill-opacity:1;fill-rule:evenodd;stroke:#428f29;stroke-width:.0190672;paint-order:markers fill stroke" id="path851" d="M 28.763808,62.186078 C 28.552224,62.156588 28.3282,62.079645 28.265977,62.01509 28.123377,61.867145 27.941284,61.235718 27.375971,58.928893 27.128273,57.918138 26.870167,56.952588 26.8024,56.783223 26.653428,56.410912 26.321729,56.197407 24.664484,55.407125 22.815394,54.525354 20.800132,53.303558 19.034519,51.99383 l -0.753641,-0.55905 -2.322335,0.678345 c -1.277286,0.37309 -2.657942,0.75099 -3.068125,0.839774 l -0.745787,0.161424 -0.351437,-0.536616 C 10.841488,51.124528 8.2297585,46.620395 7.1670955,44.599636 L 6.6102577,43.540752 7.0934858,42.948695 c 0.2657754,-0.325634 1.2926661,-1.41771 2.2819797,-2.426837 l 1.7987515,-1.834779 -0.180416,-1.038637 c -0.160803,-0.925735 -0.180684,-1.319661 -0.182886,-3.623976 -0.002,-2.082515 0.02593,-2.77882 0.143552,-3.580102 L 11.100491,29.449609 9.0366642,27.414662 C 7.5050421,25.904473 6.9326263,25.287565 6.8169034,25.02237 6.6626002,24.668764 6.6626297,24.660735 6.8197249,24.254845 7.2963868,23.023252 11.239804,16.273894 12.005659,15.378857 l 0.257067,-0.300427 0.484759,0.125942 c 0.266618,0.06927 1.585016,0.43706 2.929773,0.817315 l 2.445013,0.691373 0.429771,-0.260466 c 0.236371,-0.143253 0.844892,-0.564341 1.352266,-0.935741 1.30433,-0.954784 2.690014,-1.762213 4.707543,-2.74306 1.87475,-0.911433 1.838916,-0.883043 2.124645,-1.683256 0.06563,-0.1838 0.336217,-1.1703787 0.601305,-2.1923981 0.26509,-1.0220196 0.573575,-2.1360624 0.685518,-2.4756513 0.17614,-0.5343091 0.23295,-0.623873 0.421984,-0.6652627 0.483669,-0.1058996 3.97054,-0.1502609 7.292057,-0.092772 l 3.518704,0.060902 0.430989,1.7479767 c 0.510047,2.0686117 0.909017,3.4804444 1.127722,3.9906574 0.174557,0.407221 0.0081,0.308631 2.593742,1.53554 1.3556,0.643225 2.654323,1.424602 4.307061,2.591347 0.702523,0.495943 1.33877,0.943783 1.413882,0.995202 0.109699,0.0751 0.502152,-0.01615 1.994813,-0.463793 1.022033,-0.306501 2.381778,-0.689488 3.021651,-0.85108 l 1.163411,-0.293805 0.145052,0.19205 c 0.93221,1.234241 5.324678,8.900157 5.324678,9.292846 0,0.333447 -0.488016,0.914807 -2.540796,3.02678 l -1.893181,1.947777 0.15824,0.960735 c 0.227755,1.382822 0.274355,3.126299 0.15827,5.921847 l -0.101763,2.450639 1.510118,1.495377 c 1.549811,1.534681 2.47101,2.550338 2.683271,2.958414 0.115947,0.22291 0.103768,0.264805 -0.328833,1.13109 -1.289906,2.583039 -4.510781,8.106197 -4.924647,8.444791 -0.229142,0.187467 -0.906252,0.05936 -3.361841,-0.636096 -1.264388,-0.358089 -2.427812,-0.66997 -2.585382,-0.693076 -0.254259,-0.03729 -0.377393,0.02128 -1.094426,0.520539 -2.75021,1.914888 -3.396183,2.301016 -5.615132,3.356412 -0.864328,0.4111 -1.586938,0.808234 -1.713456,0.94169 -0.279925,0.295269 -0.499881,1.010417 -1.141053,3.709893 l -0.508517,2.140988 -1.893589,0.04981 c -2.516342,0.0662 -8.338233,0.06367 -8.822539,-0.0038 z m 7.858077,-12.120535 c 2.091378,-0.388455 5.175143,-1.723813 6.867427,-2.973786 1.796481,-1.326945 4.757949,-4.883235 4.97652,-5.976082 0.04707,-0.23538 0.0195,-0.268126 -0.544626,-0.646577 -0.89966,-0.603557 -2.187787,-1.377201 -5.078243,-3.049968 -4.184205,-2.421491 -4.52345,-2.676285 -4.524118,-3.397889 -0.0011,-1.340016 -1.219035,-3.278179 -2.4241,-3.858035 -1.790528,-0.861573 -3.841611,-0.52172 -5.213619,0.863866 -0.315085,0.318202 -0.680808,0.789483 -0.82133,1.058382 -1.355657,2.59418 0.09164,5.558218 3.005202,6.154597 0.886416,0.181439 1.72976,0.07365 2.913276,-0.372328 l 0.879991,-0.331607 2.02184,1.158352 c 2.672299,1.531012 3.61875,2.12207 4.1007,2.560884 0.340094,0.309652 0.391458,0.394601 0.346398,0.57285 -0.0768,0.303803 -0.673843,1.002125 -1.394132,1.630629 -1.608465,1.403495 -3.352982,2.265892 -5.547142,2.742214 -1.178042,0.255735 -3.523379,0.256058 -4.699639,6.79e-4 -1.211182,-0.262989 -1.987409,-0.533958 -3.06492,-1.06991 -3.757271,-1.868855 -6.310115,-5.470633 -6.829909,-9.636234 -0.156607,-1.255046 -0.06748,-3.197221 0.200967,-4.379595 1.024517,-4.512371 4.409288,-7.971423 8.971948,-9.168835 1.368354,-0.359109 1.98948,-0.43042 3.32699,-0.381966 1.261093,0.04568 1.953923,0.167871 3.540434,0.624377 0.565292,0.162659 1.094549,0.260446 1.454278,0.268701 0.495297,0.01136 0.620752,-0.01798 1.009916,-0.236201 1.079693,-0.605431 1.3594,-1.855128 0.602683,-2.692712 -1.317562,-1.458359 -6.682213,-2.214558 -10.373764,-1.462279 -2.483279,0.506053 -5.053495,1.805105 -6.993517,3.534699 -2.575021,2.295712 -4.259712,4.711906 -5.127071,7.353271 -0.924677,2.815913 -1.025226,6.141317 -0.275431,9.109059 0.774038,3.063694 2.549965,5.963534 4.958139,8.095943 2.441521,2.161939 5.76675,3.672334 8.927653,4.055148 1.089179,0.13191 3.734827,0.04956 4.807199,-0.149613 z"/></g></svg>
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 4.8 KiB |