From a8aa5105abba298ee577e8ffc6c88b36492edf2f Mon Sep 17 00:00:00 2001 From: SouthFox Date: Wed, 12 Jul 2023 00:10:58 +0800 Subject: [PATCH] [feat] return real actor activitypub+json --- app.py | 14 ++++++++++- demo/activitypub.py | 55 +++++++++++++++++++++++++++++++++++++++++ demo/config.py.simple | 7 ++++-- demo/static/avatar.png | Bin 0 -> 4929 bytes demo/utils/key.py | 14 ----------- 5 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 demo/static/avatar.png diff --git a/app.py b/app.py index 7a374b5..cd28cad 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,14 @@ #!/usr/bin/env python3 """Creatr App""" from flask import Flask, Response, request, Request, abort, jsonify + from demo.utils.checker import inbox_prechecker +from demo.activitypub import ME from demo import config -app = Flask(__name__) +app = Flask(__name__, + static_folder="demo/static",) def is_ap_requested(ap_request: Request) -> bool: """Check request accept headers.""" @@ -100,3 +103,12 @@ def wellknown_webfinger() -> Response: resp.headers["Content-Type"] = "application/jrd+json; charset=utf-8" return resp + + +@app.route(f"/meow/{config.USERNAME}") +def locate_user() -> Response: + """Return user ActivityPub response.""" + resp = jsonify(ME) + resp.headers["Content-Type"] = "application/activity+json" + + return resp diff --git a/demo/activitypub.py b/demo/activitypub.py index 3f54226..85db5d9 100644 --- a/demo/activitypub.py +++ b/demo/activitypub.py @@ -2,6 +2,61 @@ """ActivityPub settings, fetch & post""" import httpx from demo import config +from demo.utils.key import get_pubkey_as_pem + + +AS_CTX = "https://www.w3.org/ns/activitystreams" +AS_PUBLIC = "https://www.w3.org/ns/activitystreams#Public" + +AS_EXTENDED_CTX = [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + # AS ext + "Hashtag": "as:Hashtag", + "sensitive": "as:sensitive", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "alsoKnownAs": {"@id": "as:alsoKnownAs", "@type": "@id"}, + "movedTo": {"@id": "as:movedTo", "@type": "@id"}, + # toot + "toot": "http://joinmastodon.org/ns#", + "featured": {"@id": "toot:featured", "@type": "@id"}, + "Emoji": "toot:Emoji", + "blurhash": "toot:blurhash", + "votersCount": "toot:votersCount", + }, +] + +ME = { + "@context": AS_EXTENDED_CTX, + "type": "Person", + "id": config.ID, + "following": config.BASE_URL + "/following", + "followers": config.BASE_URL + "/followers", + "featured": config.BASE_URL + "/featured", + "inbox": config.BASE_URL + "/inbox", + "outbox": config.BASE_URL + "/outbox", + "preferredUsername": config.USERNAME, + "name": config.NICKNAME, + "summary": config.ACTOR_SUMMARY, + "endpoints": { + "sharedInbox": config.BASE_URL + "/inbox", + }, + "url": config.ID + "/", # the path is important for Mastodon compat + "manuallyApprovesFollowers": False, + "attachment": [], + "icon": { + "mediaType": "image/png", + "type": "Image", + "url": config.AVATAR_URL, + }, + "publicKey": { + "id": f"{config.ID}#main-key", + "owner": config.ID, + "publicKeyPem": get_pubkey_as_pem(config.KEY_PATH), + }, + "tag": [] # TODO tag support +} def fetch( diff --git a/demo/config.py.simple b/demo/config.py.simple index 9316321..3119314 100644 --- a/demo/config.py.simple +++ b/demo/config.py.simple @@ -7,8 +7,11 @@ KEY_PATH = _ROOT_DIR / "data" / "key.pem" USER_AGENT = "COSCUP-demo" AP_CONTENT_TYPE = "application/activity+json" +ACTOR_SUMMARY = "I'm banana" USERNAME = "foo" -DOMAIN = "localhost" +NICKNAME = "foo" SCHEME = "http" +DOMAIN = "localhost" +BASE_URL = f"{SCHEME}://{DOMAIN}" ID = f"{SCHEME}://{DOMAIN}/user/{USERNAME}" -BASE_URL = f"{SCHEME}://{DOMAIN}/" +AVATAR_URL = f"{BASE_URL}/static/avatar.png" diff --git a/demo/static/avatar.png b/demo/static/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..0e833db7ceff65a4b82fcfcb1b943a497e14cf8a GIT binary patch literal 4929 zcmeHL`8U*$_a6H$yD>aBxFOsi5Qxvl+S~~Q zV(J5dm_eK@e>-xAb|HVs52PJn@t6Lu{-?nI9|ivW`Gameqw@FQU(rtX&LAdc7FITP z4o)s^9$r5F69R%l!Xlz#;$R7gq?GhYnNzZI@(NHzC1n+u>S;B&x`yT%E$y><`UZwZ z=PwwWn3|beSXx=zAZ+a}Iv^dLoBUwlcY}}3bo9KkZTS>_&nAEiNjN6%6*>`es^X}#s6c!cVE5ViCFDtL8tio5<)Dr3* z)Ds&>jStC9kD6N^KcTcfZENr7?CS36ebz_q9~h(!Js*DYa^%(P(XsIf`sCE~%$wP_ z@8;$g-hWtJ`nddQWp!4m5I@7l+ynqiY*XWWXBZV6U-x(73k6tZ{c3sTxxvNpaiUVtRzPyB$eM}EEX$t} z<)Yn=$ey;m`iR3-eTIhAUNdf~!OIDlL-Wm9gUqXy2Lw5_#uEO-ECvRyNjw|dKaB73 zy?<|`wd@xJ>qganjL2;CRoe}VZPsMOhxdHw%0$T?rJJoKO&mtx+AB3F;R(#ZryGxT&kUU(`DSX7 zQRFeCH=DnmlASELl2b&t#oyohEH^Y!7)@NYRv}*Y`WgH*Jq#AS>z32CAS_K(++Zkr z`^C!;hir$9j7#*2t5~P4&0em{UEwg($xwP@<;L|1T@88o{7mO)SFouL(iQ4W+zcaT zgy`IbGp1CK4E-&$PS<#K;!Q7ylf|`y^qDE(e=?dLyUqUS-cVAM$Yh& zERUZ$rHyeIPx~JDaJO7zow6A^P|3I^GR38MTiN<6gt$B}P@}x`2cvI!l2*#ks6{J} z%iw$Clq@=>H$Bff5OA(NZLJUQS4_{xisP4HP@ezR7X9T{O#ATP>oC1a9?yF+d*J>YQ`Zn zXSH}D;))dRGYqoB4Sv;?GITi1ay_?}&64W=-XSCuHz0%{Ood;e7ix_i7YHSP=jWxXnQx`pr=APPr$_nb zxrJ1V`{fXhDdW8gIJs2UPR<7hJmlyLb}f~zzXUSY?F`ZlsJw1;r;-8QKNoF{Kw8}n zw&XNH)US|+%|P5DGioaV=L;jpXxV9$%-rNwRIYEG=>uh*M>Xm*?N(UbsWa0r5D4BKMqM&4~<5bAH%ye(t#*1mulOe61 zvJ0sc6cYiBaXmC3pi5oRf^;%r`kq-QgYr(_+Rdc#Dkgd^wDoF!_*wE(VfuF6G)#6u z7PWnfz(Wr+u@)yxWAeWUkYfe@9fhN=DiP-F8?UBDi&EYxTDMiYdU9-Jhqeaghcl7) zmCAp zzpCtqCzM&k;W%|QU6$S42eikhK6FsrFAbLj%|QT)FHt%4TjdE8-AW7I=ku zYg=!z3sZ;>>PSU?X)cs0Zrsus1z>s=!KQ}g(u%IB=$H1(yc<$?2KUhfwc(&ai$HCd z;^abQ)Br4JK(`%F59qpGb-Ln!RQ>Ic3Zxr2kd6FZ*Dgw!eBH>74z(U(HJ~cnhL+Ma zMnq7n5M14{A8G{&Cq3N0$*nMG?Z>{(c#Fi(J>Qxy9xmLl!E@`vu z&0ln`PTMPsrIxRL=@!#Kk7;cWXp%vCKuri9xN(B+yH8E1|3YYqZB1cQq`NQsvIKMS zF`lxh5~T43{CgPaZV18C_fDD+gs9Cl1O8kenZV*(U=UJ1=PNumr~T@T`a}A*BqYe; zg!^TM+%w!W@!9P0B6~JIW&y=d)6#F=x@eYA#IAZQXDy#*+2=GFnl|UBI>roHjOUmM z>&X5{4rjX)GjF(Ii_`iYxt_EVpQ{*kRZKDk+)97RQj%m-p4A18Gk8*c|DnrozaebWWl7?yUSnn?rCA)f9`w+!{Ne9&fI{HI zAgP4n732R##wd6$e>*)}YG-;Sr<$&LlE^99sqt!7c{Cbdga7l7Ozr6YR6KH`@cxrL z$5zcp!T|6$r~3ZQDY(ws>r?XZ_D}Cbh~Utw9*sUuZqc>al+XZ!QM%3^REsR+w@F3$q!)g=Hu%8Hu2#eY+mkoHJ6|Ea<-GmIF#h{bHN2O?Qn};4 zVNVptR!hK2w}e|o5opEfMPiapWEeF-UG0*a<2RR;TTw|XZla=F_n!Tjt$JV&o;WLk zP^l|+`aeQY1L0&UTq1G#hlj&6AE#YM3t9;l4R%4po;@TU5Ca)o4rBLkEw9_MGpsRT zg;TN~@#(fOOitU&!Wv9@j+QVUvYzwGpmI(CKf96n}M)B(+hk8r$@J0w+MmiKPJG`kMnuW!wqjA{R#np=#M1(-Ih&13?-OZNND)X zmA7ioW_0gqyv^@t(e{dQ%wW-i1|=G+7OyD`7+#HPn}l@j&QVW?vY|t<$LT$Oc_x3v z2{FuLe902@JN*==8%xH+C9YsXtEokgtmjxih%5%v=ZT6@`7wF3*Gk{y!QecMB+vh; zE!nu#li1sKj)(rk+dL+x?Uz~M-d)y)JmCXRgUkX3blN61*eqkxhd`RRaI^2YygU(T z)x)i&k5u#&llWC9mo9U~9TGQz#Cq|j>FeiQdR!S8+aMPw9ph=ecE_>~fDb6^XifyEx&Z$;nDLhTn216>?aiiQyc|SzBn{+WI?W(09ODhlUCQdUxpUsN0=z!eW^*O6*PKc`;gSPX+|JzU z%!OTk8f%gUZNg_)#}x^9oN%U1oi?9xCxOe7IUy1Gup#~vmV*RWByB!;TTr(04e_II zi+I5|4pRGpk(_@>J`8RuF30>Xye_-QVGjSZvO2r$T*%XZNP3Xe%ZSFB?Ao4Syoccq zk~Yn)et$Wfq}Rprdc5sEY?L%P5m@O@YWG|*aXwbU^4OUOGiI$;m|Ygewbh+F`Wr(E zTL5@Ly5jm*DAx9~oyGF8I`+yd(^>`(vcq!GOMAv2CLS-G`$Gn3+P$KUpew@M-qM+K zHFIAEg1NyLzY?;A7L_O%rYFTZmK5CYoVH^g2sO1)Xf*5IW&F3c#kSn*FXF>=~>?wIWr znc9wpxpj%gAm;5?!m5<2;lf0(aH-A3V&@Jol}(MDmyBB(?_O6&^(Z+u7N-JDamsA1 z9a)HE72-t|ENF#Dd*+m-0oFn1AkwUmwKIoA^Vv5L9SH~MAb9GN3}AZNn9nH@Fa_FZ zFPf@_`z=so zn76EFKs0~5wCJANkhXC*V8I4zcQUUPgTU44St61V;3UtZKn1Lx+oIA5$E@#eM}cZs zJ()!-G7IpL6}8ME0Wf>#$k^2jt;3IeHL=Ebz8dZLz*0!v2cyQYg3*Dvrg|Y2jt}u^ z2*T~J75=b-drO}7qF8IRloZwy3dBjpoWmkZmWW-%v`%R&BP-~XmfmInta@fCGYy1A zo>x!ShP8CEwBryD{XmM2Sy(<_cC7m()2nKsEgO(3aS+ArYk+n4Od$wh-Kpk3@IntI zGmu=m0wsSzDuBo;nO;U@?PB@~U>0(36Il;sUeyczb5hKi>nZ1sBi9~$#)-=n!vHXe znXduN)j?et_51)FMvcW6BAOpXCL@}UIgwneR{4l#5Q)e-mpazKdZ_fWfmN0E9l#jf zMmMmQr8NFslV$G*Yw4FxVF3PECS|{@X3T+@HcRKxS^vt8<-GH**0R!o1!xk-t>SfAvKzd@Et00F63a=DELR Y6fy(OMEsUL`tK9O#=_pb$<{9 literal 0 HcmV?d00001 diff --git a/demo/utils/key.py b/demo/utils/key.py index c04124b..e637bb2 100644 --- a/demo/utils/key.py +++ b/demo/utils/key.py @@ -1,8 +1,5 @@ #!/usr/bin/env python3 """process Key.""" - -import sys - from pathlib import Path from Crypto.PublicKey import RSA from demo.config import KEY_PATH @@ -12,14 +9,3 @@ def get_pubkey_as_pem(key_path: Path) -> str: """Exporting public key from private key file.""" text = key_path.read_text() return RSA.import_key(text).public_key().export_key("PEM").decode("utf-8") - - -def gen_key(): - """Generate key.""" - if KEY_PATH.exists(): - print("is existing!") - sys.exit(2) - else: - k = RSA.generate(2048) - privkey_pem = k.exportKey("PEM").decode("utf-8") - KEY_PATH.write_text(privkey_pem)