From c573052450896e91bb64811e5e130188cb93c9af Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 12 Sep 2023 10:48:10 -1000 Subject: [PATCH] Implement WebFinger, rework TS --- bun.lockb | Bin 72184 -> 79257 bytes database/entities/Application.ts | 35 +++++++ database/entities/DBStatus.ts | 73 -------------- database/entities/Emoji.ts | 30 ++++++ database/entities/Favourite.ts | 23 +++++ database/entities/Instance.ts | 66 +++++++++++++ database/entities/RawObject.ts | 18 ++++ database/entities/Renote.ts | 29 ++++++ database/entities/Status.ts | 114 ++++++++++++++++++++++ database/entities/{DBUser.ts => User.ts} | 19 +++- index.ts | 1 + package.json | 37 +++---- server/api/.well-known/webfinger/index.ts | 39 ++++++++ server/api/object/[uuid]/index.ts | 19 ++++ server/api/v1/accounts/[id]/index.ts | 16 ++- server/api/v1/accounts/[id]/statuses.ts | 20 ++-- tsconfig.json | 10 +- types/entities/account.ts | 20 ++-- types/entities/activity.ts | 2 +- types/entities/announcement.ts | 10 +- types/entities/application.ts | 2 +- types/entities/async_attachment.ts | 6 +- types/entities/attachment.ts | 16 +-- types/entities/card.ts | 2 +- types/entities/context.ts | 8 +- types/entities/conversation.ts | 10 +- types/entities/emoji.ts | 2 +- types/entities/featured_tag.ts | 2 +- types/entities/field.ts | 2 +- types/entities/filter.ts | 2 +- types/entities/history.ts | 2 +- types/entities/identity_proof.ts | 2 +- types/entities/instance.ts | 18 ++-- types/entities/list.ts | 6 +- types/entities/marker.ts | 2 +- types/entities/mention.ts | 2 +- types/entities/notification.ts | 14 +-- types/entities/poll.ts | 6 +- types/entities/poll_option.ts | 2 +- types/entities/preferences.ts | 2 +- types/entities/push_subscription.ts | 6 +- types/entities/relationship.ts | 2 +- types/entities/report.ts | 10 +- types/entities/results.ts | 14 +-- types/entities/role.ts | 2 +- types/entities/scheduled_status.ts | 10 +- types/entities/source.ts | 6 +- types/entities/stats.ts | 2 +- types/entities/status.ts | 38 ++++---- types/entities/status_params.ts | 2 +- types/entities/status_source.ts | 2 +- types/entities/tag.ts | 6 +- types/entities/token.ts | 2 +- types/entities/urls.ts | 2 +- types/entity.ts | 0 utils/response.ts | 6 ++ 56 files changed, 560 insertions(+), 239 deletions(-) create mode 100644 database/entities/Application.ts delete mode 100644 database/entities/DBStatus.ts create mode 100644 database/entities/Emoji.ts create mode 100644 database/entities/Favourite.ts create mode 100644 database/entities/Instance.ts create mode 100644 database/entities/RawObject.ts create mode 100644 database/entities/Renote.ts create mode 100644 database/entities/Status.ts rename database/entities/{DBUser.ts => User.ts} (77%) create mode 100644 server/api/.well-known/webfinger/index.ts create mode 100644 server/api/object/[uuid]/index.ts create mode 100644 types/entity.ts diff --git a/bun.lockb b/bun.lockb index 86abf0f9510f9621e6857dbd45762f43c83d7bf1..d4ae0e9b89d7e76175f2657bd6779de0db1ad06e 100755 GIT binary patch delta 18518 zcmeHPcR&=^)}L8HSp@+_-GxO!EQl^u0U?6M&Z44;Vu@ve1((jUD2TdijmGYcB9lx;FG;7dCdEk^=UQ;J|Q=X%AVrq<=%;DJ7 z9CzMtYJr=neFctlWWE@uoa_|}H=_#Be zcqi~ypw#XOh9R1(H6&)CzabSl&X#!pkTe=U!@~Ej#Br55uB*9F%$Qq))=LA5>oVu1B`i$mo@OOi6WB5fmR2t`v*2js(7=WGBGqOq@Ahr^`_5v$*FfaZcWyBe}cS@jaB2B}Jh1K$Ei6x!Tke z4uO`THDu|wLN3f@mO3#dJ(hdoA+}o$xeyA8Y3T+X$1MR*9;8ZIQ%V-+;VBB{SSlvy z)8f_H8QgWqsbPtQW~fur*0I8M+4UUI50 zmM80S2ZH71fTw;FQnW^sQ5Vk*MF(PtXB+iUNSBqFtuy3u#SO#`by*n(9W+luJ&KKX zUZTRRNoQpYJ>jj z!g2U-mS*&lFUO(C{1b%as7s(!-^yR;&t!h;Cw90N0y1b3C@q~90it1h(Ewp-UIa=5 z^oLS3m7PHyL4!c4yk8TJa|87RMcA6dKrvb7kxj*ZvvjEgbp~;UVzD5yQnXnE5Q|)@ z&XB0XVA1G_I(`O9nnnbRe5^qmr^B|k0X)fHfK~nJ90AerN_N zKoNJ)noyKz=TK3>GoW^4=^kWgwFQK(i>;U-GcCTgYC!Ff)TiBlNWXsSQhLVBCEw(A z9q4@JmwiJs=A1uz`a%BcbIwCN9+bMye$Il6$~qma*1_KSep zX^(p@y>qa2;Zcu4hRDtL8edSa2;Mz@!;DsC$Lq-Igicv&wbkLYms@Jxm}=_|*({aW z)GoR7IQ!xG0W716A6s2zX-fxLrRB_uRMkUj=9>0@vf`{rDJgy zYKhVs%mZT-v7U|*{4-YI7$Mi;e)SPbDl)BouzZDuvlh6U;JTx%0@KzHv9aN}a4RF!#7rd9!BZ42vN^srElr`USvw=2zdwW2!UwlAk5|vRsh*{$jAkiSXqr= z`On}Iz=LGgnKoqY$Is00$dBBMp?~Z>nFsqK6YX9U~CfP@QO5q zJQEygFAOMO0*=~XY8->*H^FrjdMI=cwrhbqJ@z>>BA8R4Z6kIfKyC$L6E@dAOuimD zu_+X?dkl`^8g;oULgi>X8@qS~$Eht+59Z<2h(Ri!3MoZ8!m?4Y-E(jna8}fmX&Z@k z+F<*&)TtU`114H%0Ds7@fuo2Q0z%#xJ3$mRXIhsK8!+vKie&0Za8yxPL$U@gte0E3 zEW?G(atoKc;;Di9=V%sWJzbfzd${}*gwz~sNf9h_uFYma7>OH%SQBoQkFjvV)X4VK zX1zSZ<+iv-kWbN>Q?T{N;M%gwHN)g%kt5H;P)z(DaP7cBZ|7inb$6*-ZL<&?FfCAo zNpT3ao(#^w`oPEb*!IN9f{*2Gz)`FS%TqoSTx+rO+QIVOZ)$pgip^(qKMNlV%7I;>Z{aGAF!n^iAduERqLRmL*FVqFN16u>foLw~lEiLs-u zE7hd3WN@UMSaTsbY9~ai{JNzk6o46Z>WN+!7n%{AIMGzL8XQG5Had)c9h@IHEEUIK z>$>&Pj@%+ksn1Hi!sSaKAXQNrYv)FN)~jK-%%uUF)i7MHZ6GQD57Z8pA+KItoD|iAfLg)-P66+v5aCH-0 zKi-%1@(-6kg+PpK+BX6)NnOOI=zXLgtuX6N5Vl~h$}o8?e~#;fV!X2{+>Q;HV7G87Lb8?ro6oveZP3LEXxj+198j-uGj`Q9I!# zEuRXGA`9V*?c_K({D&^o&H)dIR8#b0Bsi*x@vx}Ig6jrObjam@;)bHpTZfc^3qwsT zIJiI+#+<{#WwBwbS6Ha23MV{za_*oAQhKZ58;)vpIDD=~bixV^S34zl4s0{KhLg~Mz zc}lghyoE%Rcm%IN`wCKgfCH@v!~^7>WPlP;GA9k7 zcIg16@|4OA94CVsb6eqC1hyT@P~|~X6NxA(mrD%NzoJ$s9|}M$3QCrX8?}bwP?T^x3Cdr@f=Bzh*FCZVvvYZf2G7A5vB1i0VKZ+ zP$EkF6@c28S@>(9l!#LK_rxHTqb6bYZbC>EZc!=H`zQ_kBS7`;0F;Q5{4PN9djO^P zQBv$aK=mE~lpaU{M*`Zkej_OoQR?_5F-SzI;VWX0%2TQ*6uv{r*A>x-%5A8zh0@}) z2Tvjg@;_x9aiIqE6&x>=~Zg?f+>i6~|3Sg5B(UY=5arg|u_ zLnZ*!1~k-Cu{*?<=4Ve!tI)9lmdSr!h!QF8x#6yaO8icb^xQA^Cg+K$AAoLjSwZlQ;gm&%+6{ zwEnx#|8MtskAJ(*uj|?FBWKh4&krw(efDGGw7MRro_tbvy3DDQV@b1lF5W-f^6S+6 z$-dD~m-$SaJ9(7n<<^aM-Md&Zdil$mkCq-9l~nh)u`_G&n}l`y%KjC?wj=UVvKrcM z#-z){ZqD zuIVW4*`%-43C~YHty40>s_^c{X5HI7>lt@0>}G&&U7yoyE7jST?^AzgPURh|il2{> zhyPM-X|>ufzd!AoeC%xWt#1ypx!FCQUu6^H6egLnY4Dw}Et}mZuV1yjRjV7XD~{9J z4~=SKKT&J!&}-=?pNAE@W=wWBPV|1hX!Z5WeZHHMr>NVm^mg-EzF!`j97T^(JSVD( zkEm~wRmif>m_uC6K3@H5c|PhEI^WIBw&H>|kvr@U-Kn8n-0VSwdBQ^Wpte%8LlYRH zXxR2Ew}3O5IrWD2iRR`cW2t#-9OCO(RroUGTAr3xc%ll@#2$Zd%Mc3mKbba z^cZ^oV}CcFbB70hva;j)q{M|mZi!Bz9#My8JXY@1ttUuND`OL+y`?0w?g(=%1@4?~XSAFL%OJ0=Z)^$~_ne%GRH4fi+x@^+YRWC!k zo;|cFSoQ4AZSP4vn@;-d>x?RiWBNB#-aOrLvzKq=mj3u${zki=q?RRFrQKZZ7U*vOnllZ~e4q zxmDJ0+gN(^C(moFV}io;v%ACB&A;@Ub7Xwmpr{>KxN3JQZ&Ut2&quCeP>8V*(T{(X0meUJTOP_hH31*h^^lDxhHg;fxdgDz=m7yu5ZY1!*k zRXm<`H0K{dVbAv0S$LvS*4e;wGiSFPbiRKrdQg?L>r-C4lWwt7Yd*iTd-q-Le&^oT z7vJDFENOOmf_c}`in%Q#+m5~P;_MgO&ka*PZmVu{fAN@wz1y}v)t$M__KNvpYMY&j zE-%9Pv+inRjelWOFCKlFgvj?mzOcPoOTq0QnDKVGC$3@?$u9S8R!$MN+m@YxXr?WNSZh@yc^4Mki_bJa!VC zjRAXYvW6eZh9}3cP2g^TGco^^7&aiwj!jR|@WWXdIE4}RrE2i%Xlg3#1NRu*Xx1_f z_GQDqG!6ebyARH5AnZ%m@Z(rvI_v{youT2!vo0C1ZxHMQH<9s~urCMpWoq!+X$`nr z;A$8&{8Xkjz`k7A2aYkvEZ8>~_GM}K>1;Q+r{L-tHGDqHG{U|-*axnFd1S-BA+Rr7 z!_Q_X!PyLjeFHW8TsC|l>;rcL+&tz#2=)zweS-sec-I~H2ezIB@gzEfPLV;Vf+x-Hxl*@(eSI- z8gRG3)flS5YcTCl*f$FHfm_EMhrzzluy2^A<9aDRL9Kpn^|0^Klxgd)q%^Rppjy@N zI@hjf?Y_-jYfb$k%hPnDL-j?0r>EPl8?3r~?d<;HwF|0W|9PGI^;z8)?H`?Jp0|G& zyE|IJiic_VjZA5RpT^j+p(YK#nH7QS@wpvSn>Ba|n2UR^%~(4&akz%Zi?iV|{0`~V)!F$8Lmee|9K2w%zEN_jIF`-IFpZ!cB))gcwaorVmQ4YwpurQtVzbqW1Xgr zx2-XwviMs7s*s(nk0&~Vd5n)US=n;J&mu^Ynv7TP8F1R)Co`G8@}o?e{lD+mXoTRq+V(LNWXK7b9w9UzgQ;jEPwGJX4v)z#+k%l_WfCl z$y?Ho^LP*O2U0Z1pO%^aFO5GyTVZGU(^`Lkfi2$h6@K6NpSy6~t0^^k(*rMYqo+=! zUkLtHGMef!-r{fJrhwurQ!!}Y5t>Pvzek2i!;^y9qRrnpuw`76=!U<4V9U5d(EdHH zey^uZ|G)0{hW&`b5AJ@7bm?cK$MK$Ay5;^(M*jbu@Nblv=!dQUQp3Mi8PgxT=C2*t zGA{G9SFh9Lzw%^$_U?Z-=u40u^fDL8_BiW*&50#$Rhzh1uf;_20{{;&Vvr=BA`Fi} zLOO^m)uHz;)ddO19kR%*Kw~YkB8!aP0>%N<$6*VlSC*dw73ekHQDmsYN&Ab=nr*lB&=fMeM8`eOp~0XaINt4FPYU5zrV=13rK+zymU1 zGE;7MG3BH6On^S*%?9QGbAhjbdBA+25Lf^#1il6q0ZV~pz;a*(@DJb{U?s2$SOcsD z)&cbE2Kurm4;TW_yU@mfntqkwgN!fW2lxX40KGW11!(HX{n$L{7ubT^oj_{>wEzX6 z1Oj1B6QDQpRY0GBJ_qgrw}9Kgb>If@17HNQfh+VP?K@<(oyBiSAGIvxrwLq>V@G(Gdz^efiIW>Tqz;BSh0PX-k0Y3safsVjH z;2KZ{P&gg|jsgdO9l%at6fhbX0fYkSKn9Qr^dakdA=3e91+)g}b-4@h68XD8C*TY~ zYh@BJ78nP70gMC;0KJ-b1x_P>2$%p&1n3n#y`FamegPi`deQpvOh(as(7b#LXo3Df6d(f{tXsBoCvPIpl7~A3a7MYk_|ND}bfI5@0?s8z7fWCqD)t^CiH5sQ~$k zd@vp$Uy;v-19`w;AP1m$p-IdJvH$}>lS7lF1ISOwKoUT{s|myciGUs$03-l9ic)eY z6_9{v3g82z0yNu3AQzy4h61!~DWK7mGXoSMG`V8{n%Gf*AK(kr2S^cbfTHPhfF^kg zFd3Lcoe(n(paKF}Jqwr#%mDI%0)RX*5BLh01Iz^$0)@Z=U@`DDun1TVkY|?xWDvQW zc=9ZHki54NFp=9x!*77qz$#!3K+`}Wa~RkJYz8(0)CtW$$*2xdNxltv^3qmd3qbWc zgBAe?ft>&iOlxdA0EJCLKpe0LcY*E!b_08XeZYQzI;Hg?4Yn3}QtUWz6p$1cgggz9 z1e8~T3|R*p1B!vSMtO-9sRCI@osos4J;_d-#|0_qDGS;+q?RLQTsli)BNL0Ts@ST1 ztv&d@O??7<&{#SeC!LLv@qI&l0)711^L?sliarPQGXJ-vd!MxJH3fx!KK?%bft)cn zLziwy+%+%};E&4M}U%w3mFV?OyQ+xi7c{4rEh`l%0#+8@Q^l*0bmGMpg{ zI$&1&^Wq4b#`~`HJd!ZciSHZW;}6Z6c0oy1jD7!c!!24UX+l5LzFah%k7oT2D_x`$nAb0NIjFzu z+81ttp)^VcyML@ETXxvhMLOQuJn=;Qkk6CqQXNs1W~}V6l5fp^!?icNb6SZn*nN*E zd1@<3vf0ohN*Cz_XD8o13zUcVI-!pyqMjLS4O;YOM^H;T-x-@(w|R>y_922cm|D7D zy*?7?BAxj3xaixrL0^xXC~t~hP@cy+9_`4FVY82Rbde5z_S|{Tdd`)*UT=FD!73Cd z@-b0PXo|Y|?RWd1N*5=$cTS-^4B+SK|H5Pz|hEhMDz(AisPC9=o9Y7OG{GkXZomiEQtf2%89SiWy8CK~Kn=lqMe0!=@ zI@TtX`1_bpB%M>04yvIjP&CSN0GRIFb=&nF^L07M`@+QbizPrf`>zecu-L~Zbyx1F8sJZXoKxeI*2Qs#6ug) zGJaQWSv}F#Ks-D;ivvl=^w5Y_1N^hdh4nq52-@o+9+fN`d(pV2(_tGylOT!;=~Sj} z*zG}vo@Gl=LdzbS{N%#^aYCVzj&(Ngl#%N?de29w028q4{T)^8>IsF+SH+&6h>}I9 zSo@PvD(N)pq{6k`UJgCpO&Cc~rLT&uKBFb=ibY@bwT3CHRqPf7TUAVTIzUzg`RM=`>6mcZvt~~_CUtcYh6^I~ z$1snJ%BIpW;~EZYwho@0HWTHT8uE;Ea@l=g`7|R1g#Ak;opA0}Qr4)N zn`W?39zZRpyE6AP0V?rGGmfOUn0HLWZF?+q9Y7sya%E|#u*X%rb>wV*9BduhflNeu zpCC&Q3YBz7I%J0WOOL(Pim);JiOLB>D0nsNQmRyqz~)aknwTzUI`(-`R3;20xK!@W z!p{b%q_fYJ=a!X@s`P!JFp#iydw8-5X9Hvw;f>7P>LX>QfHyZ|mqsT`uDE=rI5T0D)X(bAo zb7SUQ5+xh0W&=v1RMNrY@$p}-*;ZQgos1{9;{MHb_hBbW6siS2;tAz2xAuJpPm9zD zItvdImwcI3X#nmHj^~{!3T?>6vt>{H*gZjxn2}levyUz+RMK(e6&^hnJRkjKfQiFMf)r2 zA;4+d*bvNKT~Vl{6VX;Z0zNISxO_h3eLRi3e0V?T8^vX+HfAYv`?2HPQyS>F& z!90cPy&DuBjPQkq;5=18C_dZ4M^iWM-K($TKUT&E`3N@$u5&2+rA+dtxL*Xwo~ziX zs{!P(O;^3c6VbRP8cQ!%Hh*f)Tjj9ysRJ*$UOHo*wXNHVl`{MLf}X-Yu_u(dmnl@j znQe6O)xd3P{V8h$Dg^t$R!Ko=y2ROK^UE5)nO4h$3wjDF)xG9jO&ZwzRDAAre}?am z`uo!-BiuF2D?8*B>ePJ}PX%8K{Nt04Loa>Tc>hxH)xp2K$A&MudN+M5yMHah*#%K% z3+bqkrbqdls;{D-iJjs;9r=B>w>HkGAE?I@adxblo{O_$v(v+-Ki;7qtV=OutK+nB_?sN@3CK{vVEi`bwTpjw0lF{Gs_8+-j|R!o z8TF~Dx_CYQ3InxrEiXW;@~qlFf2A$&nV(dh&u}fT1L~D$)&4B{N6+SetUv0Pvqn(H zpOi_?RcEK^@plBWwE1OKcvW@rK?c>uj2cfdjDK0jGgkOh9FU6{wLgWaV2;tC)23$W zw1)iEPQ0sY)*C)<0IHW~p=IZ%o}ZK(GX*uuvufcUEcC05O*a_Tap`GBLwZVz&XATK zuTv-JjBx`7>0;HEZt>?^QgRdYDT3~{$+=>8^t6`mXw7?ceA@^mZyDH6Pe!nxqC=f+ z(5sE<>e%dr1f4+`LLH00tC4^YT0(jGK^na)B@=!pQ`21gvz9-&`@ijvDw6&}=TL#R zcc>t!6`!7}%Zbxv81?C_%Wo>*_t;1Sy-!R%u@-kc>b=`hnq+gi&ePcHoAnz0-fC3+ zy-?kRU47&k{_aem?zHVyjjdPflYt z4@U&W1ylqP*&+i942$q2=*$3aD53(6fC7R7f{ysUQ&kO&4;}nHmw)E!*QdU7w{y-t z_f}I~dHhSCE44lg5?WpCQn@PT?DpA*&Met7arpYSNADfE{p6)*Px(7M;iq2c*f-BC zBre-)vZCbltuQEqEJNeYrA&)9NTVGinBgB!sukO7bZx-1?ZpI=y>CWXk7 z)Bt+$;-aDgNdgZ6{|3Wzzt449=`0;yhVe%FN>W4UE0fX+dHx|f&!{g+{*rXRXMuL% zX%88WhH;QQK`%69!|afaAYEnI&H`tSE4v`SA|C_gD3XN#JuC6S?axE90d}euQNgXC zVr_n5o~ty!yev@AYobL=1x(>i7PRC+OCZ_H{KDLV{NZ`!BS^?ClMJY0?fsCAA?y8& zPhL~ZG*FI8IuC1k%JMu{VSa985(YN z0+Q#1p&TE$U4XbtQZvMcxmlNoATgk)z%^Qp>poq-LYM0x&8W|Dxr*cS%Ornr_V_0e zaC^)V+Q8SsH1j@yY>tK_%Hk`XqY5OfW3jWe%=ISpYN*QMhZhtLlj>WvemkL8BR#yZ zsMIA%o59&3ZsaT|laiWfe2v~NH@`3^zM@#V2z>*|EqP9>7s#mif}(6^Im|7{AC+G& zoxVdG_(^nRPsc@R6PD(WDs|>cl4=E_6dSG8k91Xz29?%;vq8B9&hm0sjx-nj*>hJ} zMR`69a+Q@-xJoOfqcIo{2BV_5)P;+2Xvm?@YN}c6%q}YB*cL+1DLko}rXK{(DW6l6 zU0#}B=;{s56OPHt&(1^p{4&>A3@6Qq)g~U)oH7h8+^t(^-2x#IbB`U}*sok?S$PSX zm%1v-IByoU)Xd0@)27`B$@ce$htPO@AtNEZD|)7tBw9A5~whPC_C0*Y+ zSxdILkX-Lgo}BzrHT&*quj!{^yauSRgGtel2#G4^rD!HLPSso#kin8v&r_08`<-vD zywQJ^OzRuA3p~GPV)2vjzxDRli`TywF(Kw_`n2JyZtF#TX*SNi<`|8Omt)n`(q>Y~ z$Pb6;1Y*D59!H^C)=-_%E?y-iz%JTSIz9`j8lTTnU4UJA56#kGt4s$2tjc|uCj(4S zP;44iH?oU8RM*I^`1(jv4^>}dPxI4>OhJKGF^{SP)n@pVNeQxxG)fP$8)h0LsXG}1 zJ1hJ7N}Yn+HzXz4u8hGs+6}r;>KkAcTc|qNE>2M$Y97Ri+XXegR1;`bp3s>=W$%IY zM_oOhS!wGhNj5M)m5l&n(`6n}c?IkqmDPZK2gXj;Q|pqkhFD4T;~7SQbporedb5$z zL+#2B&}kOebWEcE=oxWw~N)3j!$2z z#%Cw0!{;PYOm<~0vK@(l*w!?%D#yWiS~wPNRoWo)b#q~o8%&ROBdd6w(#>|m_t4om zoT4MunRyMB2)kHD>G*t$s`1&F>hS3zCDJbTQ+lLbNyCOiYI*{BZN*5cgKiCU>^$7X zydQ(*f@#6-5u{n@O9x}q{B&YaS7;3>P3-dbL9{>6rreF>#WqAZ51OsYcracDL48B5 z%Bx@;Iv?&Pe;-U4ci5CJILLTXC?l-O1Tc25zM677!FX+aII{AU#*`6dQ&MrF@eG0v zhN+fE*_9R0@yM7p+$v5|dbC}(hSIF&HW_KOzqw6`$6bX}N8*9y{BX)>VN;Gnc^{O> z3mDrPM+L8=L_tlh${;YULy*a;Jf<^sQu&aHX0@~_7MwL~3C0Yy8twlG4qk7`EXbmbt|fXV7)M{VPo8Zd|28(m4k$aZnetT&&1z$lJ2j#GZEQ*f zZf@Kd%Z(JQ1!DsY>i+pruk)dr#%X?ccpG9-l<{C}o7S)vjJv7ppj^~jVt#m$j1!rI zt>x2HFph)Pawiz)e?4{bi(tGENP=LiAtf69I4bglX!1|ADVw3NX^oK;m!fG_qD^iS zL;F!zf?FjUfB-~U<&`m%(axrvgp%h%x*`jkHPzK*l0P94Vp`DdoxatGFnwCmXtoH!nc%*}|arM>JSr5j^jg{kl zeVuX35FFatzGgNYtUFq2$JdKs998vDl8@oGoC0%NXoh22Y7)M=D9A$K{DW!3mr$}!ciMzn=L3glVt5Uy} zB-z!$Y67f=d%*gstFg2d&9d4Qzt-MluR-JPV12c&VOE)1^WHP;gR(1SMs!wM;zr&J z)!JcO0mktKgA0GxnHp6?0B-f&)x9tfSG+W8rpVNm{5!!5DDQ_7jZId=eYmN1QX5ak z6>l~iQwZvVxbHu3ovj4B`#L)V_Q-YC58njX6l_xD&9jM=(Z!~G0wvGvt8M}_Qj$|k zTlL;xxK(=^sJXoajMrIPz5QU?Y0e9E4NSKmhfH^TZ{S?SRzjp_fN>q}u&CPwc4HU( zsJBG+b?j{DfmYg$?lDZnebk$hasZ6;4Y`f8CZN4HZw|(#`GIgl&ERSuY&NUG)6NbZlcl2~Q{ zJdT6s=Zl^Ims?30dL+DBeo1m<9sqc}2i4Y^WR8_l_5F1{OCEQC=07g<3YOeqpw3w` zAEe8NbUjO7zzJ}~@&GO@*~5_l_bULn+$_1i2;lnW#rj1FB$r!BnI_&9?q+*Ot8H#i zHb>i~dV7{^?sA>8sfM)&H}9e3g80iRrNRV-C13grWC33^&?|11 zJfIPJBV+`mA7oR#-OZ8>;KKEcXdn)_t&VAA?~F~T0Rl}je&oY9AXUhodo~DHi1==aKpc` z@XzfK<7op}TqXSTIK)J9df*?}UNAQWRl&b;@UO}ts%RHj+IaXk*&(J-$z=Ek_Ab~o zikSlcCcwWb4l#ocg55U}{!Mj=YO0zF|G@qUHk*>B!M{oHZ<<5QrDI?N-0*L@L(Hd| z>G013|G;Xf+YI;zwqk}u5M2PvtAc+s9byqJp9%ja!@p{WSWJDZ;UCy0u*XTvf`3!s z-zZ+T4gSq>h~;FS3;(7YY2sXmc#i78PJzYG zbBGmGIS>BLFw#-5m9!xr{>{XgBOKyIYBeAJfz6%o5HHaOV5_Q)bmszxc$umfz`t2W zIt#XrI@Z9y*+zP*#vwM)X|OF|nF}4FmL6XS|K=F!2e3_)PVjH8kzOW;*i2W!_JKX} zr~}W7Uwjn)%`=j5kwa{yfs5eZd?RgN)FVWRS06|}!P^OiA3*!m_I)f%raq5#p;b#7 zhEDU>o^*3{MMZuN%V|`%B-{N>14;c^gEgZ@;t4wLa(|%C?fH`?*5`HqXOH=CdZm%} zi^6tDd|gyK|0X*3Z$03z9FM=A{(j#5XJg#z4>-R?Sbg9Kf6HU(9gmmJ`x9wd=#8bj zp@aY4^}M0{19cq#KV@QlUN`@G>3HEEi`9N~T?QG5e>Zw~;-4u0 zKphqa_biWY%^x_iKCfE~!yiAfJ}(~(e`5IY+~!XWe=Ph*7XH7&&spt#>tIphjYIvm zbR7TNYWO>~bMw0rzuM~G&KURg`{JJyh2Z}ie*7YoSX=bMwPyYcw!%@A9^0tvl@Ln! zd%XMFk6K}MEx-?8#xX(ha!!bbs^-SyU48(BL{ket>g9euzz|g zAP@)wf`P_B2oMT{0pY+Y3~?IZ2mE6x=s;ulL|jb*ICHCj$-op~DliS04$J^%0@c7Q zU^XxZm=7!fYJi0R0gnQUfX9F(z~cb_6C)021+)g*0BwPIbV)p*s6S5eV^N$PYNYwm zD!v4&5e|aHs`9@!_ys8m-^ZjdAQIkK0Di8=&xgMTJ_e2f?*jh-4ghZgZvlS;N`O*e zFYrIW8^A7heK#()1Ahh911|wh zC~fy}VmIMS-kMt=mjlZH4p2`(uHAcRhj2fG%clX(vc*>H$U@|ZTmT6&d)ezB+ zmVa0!dQr0@oyA}pa>R@WkCjK-h|#p-NIx;1zCU7)d>*|*(d(VbkHkvjb@c8#8$zqZ|SZk^}Y8F;ndbCQ|smaIY zNbg(SpB?S{M*i6*gApM3$s-P-z8|-b^u8{B=kNnL6K0Hv<$7((IC=*Cy>E%HDN1^! z;9U08sBf`1haGl4^Leu0>=TFR$-nYbiCGLLKy#MtO^h?qD@w_3v zFc_yWh6uX@7v6Wthn2+M*|kBVG}X#>Y^9yHf6`vGq6?U71Z_W)iAS;nKh2EvzED1J z$2r53AD@l8(W8WReL6hS`$~D(wWfFPSoiik)LRqaSv%=|y6aebVWcs~GKHD;9vgle zk#Zitb)?!QAY%|oulHd-a7GOP{p)yyI85#nW^sa&PMGEMLDcg^Yo%kb_U^7)b?Ydp zp(&)=6YcRb>&X-CJ9ys@F5A}crL}US3u-KrIe^|bh>y)YQofOj~6xbKJ4fiFyvKi|MYj`L>xJh1Civuuu_v@et8`W7nq(j+G}q3K^H zTfA@5_wV~=FK6H@Z(!^WT9RLELhpRp)8c)lK5?aO=jYRgO+*94mc1AkMP1H#uBSA+ z9d$NJj*X_SXHAwh(b{X)qgvfDZ1tnVddq@09c;!+^i+N}+2Va=e#xU<_q?-ZfC&Sq z;@s2LVfWeAv8S49FTH!;p?B_%*?aF2^CdJ)K|@%8kpj<|WKB@_b1CwSSQ>k-r)5xc z?GW-moz=v?Y|eM+qHlS1aZMKQ3-wd&??h%C*mo})ARBqi)-5RHtDf?rmNX2ae;lp< z%GAO8Uj2;vt)9+&ZsSTc=M$zQ?ksWIoA#Zf?i)OAVTMDk;T=m1EO;H3 z%6@I=)Ok&%rHKBXy{Grf2gjU!-<+!2sUB^c+EAYhrVhK?XnXCik!N2*g-irpQTH4J`rr=A-7VlpHto>-~nV7P+5on+X^k#odmRlZ6xEvFp0tUdzg+C?H z{#xhN2R-B01+KahD7;H>%R^H=Kt$@oZ%r2OUn)F3dRu({AwwyAu_nM>uZ{Ye%OZjD zzU>j~9rou_K(+k3k(MjL!QMX0jw3@*x}Lzlt%oM@7eW(kUz+D zywvHVPulL~FN=-Ae>g`~D?UA2^aLHeVvlHpZx;=rotl>VmKYmwt@s3*p$s)s>#G&Y zu!_QX{xVcXbyv3pmb!8aT-oLEqg>_A9A~++w(7eEA)7~rh*Hzyzom%An>)pe7IM9U c(u&1@y+gz-ezuV)UVOkJd^WoiMTGDF0(ZNzA^-pY diff --git a/database/entities/Application.ts b/database/entities/Application.ts new file mode 100644 index 00000000..10eb7fef --- /dev/null +++ b/database/entities/Application.ts @@ -0,0 +1,35 @@ +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; +import { APIApplication } from "~types/entities/application"; + +/** + * Applications from clients + */ +@Entity({ + name: "applications", +}) +export class Application extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @Column("varchar") + name!: string; + + @Column("varchar", { + nullable: true, + }) + website!: string | null; + + @Column("varchar", { + nullable: true, + }) + vapid_key!: string | null; + + // eslint-disable-next-line @typescript-eslint/require-await + async toAPI(): Promise { + return { + name: "", + website: null, + vapid_key: null, + } + } +} \ No newline at end of file diff --git a/database/entities/DBStatus.ts b/database/entities/DBStatus.ts deleted file mode 100644 index e28df093..00000000 --- a/database/entities/DBStatus.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { getConfig } from "@config"; -import { - BaseEntity, - Column, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "typeorm"; -import { Status } from "~types/entities/status"; -import { DBUser } from "./DBUser"; - -const config = getConfig(); - -@Entity({ - name: "statuses", -}) -export class DBStatus extends BaseEntity { - @PrimaryGeneratedColumn("uuid") - id!: string; - - @ManyToOne(() => DBUser, (user) => user.id) - account!: DBUser; - - @CreateDateColumn() - created_at!: Date; - - @UpdateDateColumn() - updated_at!: Date; - - @ManyToOne(() => DBStatus, (status) => status.id, { - nullable: true, - }) - reblog?: DBStatus; - - @Column("boolean") - isReblog!: boolean; - - toAPI(): Status { - return { - account: this.account.toAPI(), - application: null, - bookmarked: false, - created_at: this.created_at.toISOString(), - emojis: [], - favourited: false, - favourites_count: 0, - id: this.id, - in_reply_to_account_id: null, - in_reply_to_id: null, - language: null, - media_attachments: [], - mentions: [], - muted: false, - pinned: false, - poll: null, - reblog: this.isReblog ? this.reblog?.toAPI() ?? null : null, - reblogged: false, - reblogs_count: 0, - replies_count: 0, - sensitive: false, - spoiler_text: "", - tags: [], - card: null, - content: "", - uri: `${config.http.base_url}/@${this.account.username}/${this.id}`, - url: `${config.http.base_url}/@${this.account.username}/${this.id}`, - visibility: "public", - quote: null, - }; - } -} diff --git a/database/entities/Emoji.ts b/database/entities/Emoji.ts new file mode 100644 index 00000000..53b59691 --- /dev/null +++ b/database/entities/Emoji.ts @@ -0,0 +1,30 @@ +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; +import { APIEmoji } from "~types/entities/emoji"; + +@Entity({ + name: "emojis", +}) +export class Emoji extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @Column("varchar") + shortcode!: string; + + @Column("varchar") + url!: string; + + @Column("boolean") + visible_in_picker!: boolean; + + // eslint-disable-next-line @typescript-eslint/require-await + async toAPI(): Promise { + return { + shortcode: this.shortcode, + static_url: "", + url: "", + visible_in_picker: false, + category: undefined, + } + } +} \ No newline at end of file diff --git a/database/entities/Favourite.ts b/database/entities/Favourite.ts new file mode 100644 index 00000000..2c61fe9e --- /dev/null +++ b/database/entities/Favourite.ts @@ -0,0 +1,23 @@ +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { User } from "./User"; +import { Status } from "./Status"; + +/** + * Stores an ActivityPub Like event + */ +@Entity({ + name: "favourites", +}) +export class Favourite extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @ManyToOne(() => User, (user) => user.id) + actor!: User; + + @ManyToOne(() => Status, (status) => status.id) + object!: Status; + + @Column("datetime") + published!: Date; +} \ No newline at end of file diff --git a/database/entities/Instance.ts b/database/entities/Instance.ts new file mode 100644 index 00000000..c54aff58 --- /dev/null +++ b/database/entities/Instance.ts @@ -0,0 +1,66 @@ +import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; +import { APIInstance } from "~types/entities/instance"; +import { User } from "./User"; + +@Entity({ + name: "instances", +}) +export class Instance extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @ManyToOne(() => User, (user) => user.id) + contact_account!: User; + + @Column("jsonb", { + default: { + media_attachments: { + image_matrix_limit: 0, + image_size_limit: 0, + supported_mime_types: [], + video_frame_limit: 0, + video_matrix_limit: 0, + video_size_limit: 0, + }, + polls: { + max_options: 0, + max_characters_per_option: 0, + max_expiration: 0, + min_expiration: 0, + }, + statuses: { + characters_reserved_per_url: 0, + max_characters: 0, + max_media_attachments: 0, + }, + }, + }) + configuration!: APIInstance["configuration"]; + + async toAPI(): Promise { + return { + uri: "", + approval_required: false, + email: "", + thumbnail: "", + title: "", + version: "", + configuration: this.configuration, + contact_account: await this.contact_account.toAPI(), + description: "", + invites_enabled: false, + languages: [], + registrations: false, + rules: [], + stats: { + domain_count: 0, + status_count: 0, + user_count: 0, + }, + urls: { + streaming_api: "", + }, + max_toot_chars: 0, + }; + } +} \ No newline at end of file diff --git a/database/entities/RawObject.ts b/database/entities/RawObject.ts new file mode 100644 index 00000000..983b100f --- /dev/null +++ b/database/entities/RawObject.ts @@ -0,0 +1,18 @@ +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; +import { + APObject, +} from "activitypub-types"; + +/** + * Stores an ActivityPub object as raw JSON-LD data + */ +@Entity({ + name: "objects", +}) +export class RawObject extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @Column("json") + data!: APObject; +} \ No newline at end of file diff --git a/database/entities/Renote.ts b/database/entities/Renote.ts new file mode 100644 index 00000000..45f789b0 --- /dev/null +++ b/database/entities/Renote.ts @@ -0,0 +1,29 @@ +import { + BaseEntity, + Column, + Entity, + ManyToOne, + PrimaryGeneratedColumn, +} from "typeorm"; +import { User } from "./User"; +import { Status } from "./Status"; + +/** + * Stores an ActivityPub Renote event + */ +@Entity({ + name: "renotes", +}) +export class Renote extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @ManyToOne(() => User, (user) => user.id) + actor!: User; + + @ManyToOne(() => Status, (status) => status.id) + object!: Status; + + @Column("datetime") + published!: Date; +} diff --git a/database/entities/Status.ts b/database/entities/Status.ts new file mode 100644 index 00000000..ee6a2a68 --- /dev/null +++ b/database/entities/Status.ts @@ -0,0 +1,114 @@ +import { getConfig } from "@config"; +import { + BaseEntity, + Column, + CreateDateColumn, + Entity, + ManyToMany, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from "typeorm"; +import { APIStatus } from "~types/entities/status"; +import { User } from "./User"; +import { Application } from "./Application"; +import { Emoji } from "./Emoji"; +import { Favourite } from "./Favourite"; + +const config = getConfig(); + +/** + * Stores ActivityPub notes + */ +@Entity({ + name: "statuses", +}) +export class Status extends BaseEntity { + @PrimaryGeneratedColumn("uuid") + id!: string; + + @ManyToOne(() => User, (user) => user.id) + account!: User; + + @CreateDateColumn() + created_at!: Date; + + @UpdateDateColumn() + updated_at!: Date; + + @ManyToOne(() => Status, (status) => status.id, { + nullable: true, + }) + reblog?: Status; + + @Column("boolean") + isReblog!: boolean; + + @Column("varchar", { + default: "", + }) + content!: string; + + @Column("varchar") + visibility!: APIStatus["visibility"]; + + @Column("boolean") + sensitive!: boolean; + + @Column("varchar", { + default: "", + }) + spoiler_text!: string; + + @ManyToOne(() => Application, (app) => app.id, { + nullable: true + }) + application!: Application | null; + + @ManyToMany(() => Emoji, (emoji) => emoji.id) + emojis!: Emoji[]; + + async getFavourites(): Promise { + return Favourite.find({ + where: { + object: { + id: this.id + }, + }, + }); + } + + async toAPI(): Promise { + return { + account: await this.account.toAPI(), + application: await this.application?.toAPI() ?? null, + bookmarked: false, + created_at: this.created_at.toISOString(), + emojis: await Promise.all(this.emojis.map(async (emoji) => await emoji.toAPI())), + favourited: false, + favourites_count: (await this.getFavourites()).length, + id: this.id, + in_reply_to_account_id: null, + in_reply_to_id: null, + language: null, + media_attachments: [], + mentions: [], + muted: false, + pinned: false, + poll: null, + reblog: this.isReblog ? await this.reblog?.toAPI() ?? null : null, + reblogged: false, + reblogs_count: 0, + replies_count: 0, + sensitive: false, + spoiler_text: "", + tags: [], + card: null, + content: "", + uri: `${config.http.base_url}/@${this.account.username}/${this.id}`, + url: `${config.http.base_url}/@${this.account.username}/${this.id}`, + visibility: "public", + quote: null, + }; + } +} diff --git a/database/entities/DBUser.ts b/database/entities/User.ts similarity index 77% rename from database/entities/DBUser.ts rename to database/entities/User.ts index 0c4ca44f..6a8c3e4b 100644 --- a/database/entities/DBUser.ts +++ b/database/entities/User.ts @@ -1,13 +1,16 @@ import { getConfig, getHost } from "@config"; import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm"; -import { Account } from "~types/entities/account"; +import { APIAccount } from "~types/entities/account"; const config = getConfig(); +/** + * Stores local and remote users + */ @Entity({ name: "users", }) -export class DBUser extends BaseEntity { +export class User extends BaseEntity { @PrimaryGeneratedColumn("uuid") id!: string; @@ -16,8 +19,10 @@ export class DBUser extends BaseEntity { }) username!: string; - @Column("varchar") - password!: string; + @Column("varchar", { + nullable: true, + }) + password!: string | null; @Column("varchar", { unique: true, @@ -35,7 +40,11 @@ export class DBUser extends BaseEntity { @UpdateDateColumn() updated_at!: Date; - toAPI(): Account { + @Column("boolean") + isRemote!: boolean; + + // eslint-disable-next-line @typescript-eslint/require-await + async toAPI(): Promise { return { acct: `@${this.username}@${getHost()}`, avatar: "", diff --git a/index.ts b/index.ts index bc6bb8ef..f44d8fde 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,5 @@ import { getConfig } from "@config"; +import "reflect-metadata"; const router = new Bun.FileSystemRouter({ style: "nextjs", diff --git a/package.json b/package.json index 75e85fd0..459dcd8d 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,22 @@ { - "name": "fedi-project", - "module": "index.ts", - "type": "module", - "devDependencies": { - "@typescript-eslint/eslint-plugin": "^6.6.0", - "@typescript-eslint/parser": "^6.6.0", - "bun-types": "latest", - "eslint": "^8.49.0", - "typescript": "^5.2.2" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "dependencies": { - "pg": "^8.11.3", - "typeorm": "^0.3.17" - } + "name": "fedi-project", + "module": "index.ts", + "type": "module", + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.6.0", + "@typescript-eslint/parser": "^6.6.0", + "activitypub-types": "^1.0.3", + "bun-types": "latest", + "eslint": "^8.49.0", + "typescript": "^5.2.2" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "jsonld": "^8.3.1", + "pg": "^8.11.3", + "reflect-metadata": "^0.1.13", + "typeorm": "^0.3.17" + } } diff --git a/server/api/.well-known/webfinger/index.ts b/server/api/.well-known/webfinger/index.ts new file mode 100644 index 00000000..415990d9 --- /dev/null +++ b/server/api/.well-known/webfinger/index.ts @@ -0,0 +1,39 @@ +import { errorResponse, jsonResponse } from "@response"; +import { MatchedRoute } from "bun"; +import { User } from "~database/entities/User"; +import { getHost } from "@config"; + +/** + * ActivityPub WebFinger endpoint + */ +export default async ( + req: Request, + matchedRoute: MatchedRoute +): Promise => { + // In the format acct:name@example.com + const resource = matchedRoute.query.resource; + const requestedUser = resource.split("acct:")[1]; + + // Check if user is a local user + if (requestedUser.split("@")[1] !== getHost()) { + return errorResponse("User is a remote user", 404); + } + + const user = await User.findOneBy({ username: requestedUser.split("@")[0] }); + + if (!user) { + return errorResponse("User not found", 404); + } + + return jsonResponse({ + subject: `acct:${user.username}@${getHost()}`, + + links: [ + { + rel: "self", + type: "application/activity+json", + href: `${getHost()}/@${user.username}/actor` + }, + ] + }) +}; diff --git a/server/api/object/[uuid]/index.ts b/server/api/object/[uuid]/index.ts new file mode 100644 index 00000000..30a68e3e --- /dev/null +++ b/server/api/object/[uuid]/index.ts @@ -0,0 +1,19 @@ +import { errorResponse, jsonResponse } from "@response"; +import { MatchedRoute } from "bun"; +import { RawObject } from "~database/entities/RawObject"; + +/** + * Fetch a user + */ +export default async ( + req: Request, + matchedRoute: MatchedRoute +): Promise => { + const object = await RawObject.findOneBy({ + id: matchedRoute.params.id + }); + + if (!object) return errorResponse("Object not found", 404) + + return jsonResponse(object); +}; diff --git a/server/api/v1/accounts/[id]/index.ts b/server/api/v1/accounts/[id]/index.ts index c7253c64..a4102bb8 100644 --- a/server/api/v1/accounts/[id]/index.ts +++ b/server/api/v1/accounts/[id]/index.ts @@ -1,24 +1,22 @@ -import { jsonResponse } from "@response"; +import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { DBUser } from "~database/entities/DBUser"; +import { User } from "~database/entities/User"; +/** + * Fetch a user + */ export default async ( req: Request, matchedRoute: MatchedRoute ): Promise => { const id = matchedRoute.params.id; - const user = await DBUser.findOneBy({ + const user = await User.findOneBy({ id, }); if (!user) - return jsonResponse( - { - error: "User not found", - }, - 404 - ); + return errorResponse("User not found", 404) return jsonResponse(user.toAPI()); }; diff --git a/server/api/v1/accounts/[id]/statuses.ts b/server/api/v1/accounts/[id]/statuses.ts index cdc08534..1e5db16e 100644 --- a/server/api/v1/accounts/[id]/statuses.ts +++ b/server/api/v1/accounts/[id]/statuses.ts @@ -1,8 +1,11 @@ -import { jsonResponse } from "@response"; +import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { DBStatus } from "~database/entities/DBStatus"; -import { DBUser } from "~database/entities/DBUser"; +import { Status } from "~database/entities/Status"; +import { User } from "~database/entities/User"; +/** + * Fetch all statuses for a user + */ export default async ( req: Request, matchedRoute: MatchedRoute @@ -24,19 +27,14 @@ export default async ( tagged?: string; } = matchedRoute.query; - const user = await DBUser.findOneBy({ + const user = await User.findOneBy({ id, }); if (!user) - return jsonResponse( - { - error: "User not found", - }, - 404 - ); + return errorResponse("User not found", 404) - const statuses = await DBStatus.find({ + const statuses = await Status.find({ where: { account: { id: user.id, diff --git a/tsconfig.json b/tsconfig.json index efa87a01..a26f7689 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "strictFunctionTypes": true, "forceConsistentCasingInFileNames": true, "allowJs": true, + "emitDecoratorMetadata": true, "experimentalDecorators": true, "types": [ "bun-types" // add Bun global @@ -25,6 +26,11 @@ "paths": { "@*": ["./utils/*"], "~*": ["./*"] - } - } + }, + }, + "include": [ + "*.ts", + "**/*.ts", + "server/api/.well-known/**/*.ts" + ] } diff --git a/types/entities/account.ts b/types/entities/account.ts index 52a5e532..311342c2 100644 --- a/types/entities/account.ts +++ b/types/entities/account.ts @@ -1,9 +1,9 @@ -import { Emoji } from "./emoji"; -import { Field } from "./field"; -import { Role } from "./role"; -import { Source } from "./source"; +import { APIEmoji } from "./emoji"; +import { APIField } from "./field"; +import { APIRole } from "./role"; +import { APISource } from "./source"; -export interface Account { +export interface APIAccount { id: string; username: string; acct: string; @@ -24,11 +24,11 @@ export interface Account { avatar_static: string; header: string; header_static: string; - emojis: Emoji[]; - moved: Account | null; - fields: Field[]; + emojis: APIEmoji[]; + moved: APIAccount | null; + fields: APIField[]; bot: boolean; - source?: Source; - role?: Role; + source?: APISource; + role?: APIRole; mute_expires_at?: string; } diff --git a/types/entities/activity.ts b/types/entities/activity.ts index 5e1b4d10..5b21fdc2 100644 --- a/types/entities/activity.ts +++ b/types/entities/activity.ts @@ -1,4 +1,4 @@ -export interface Activity { +export interface APIActivity { week: string; statuses: string; logins: string; diff --git a/types/entities/announcement.ts b/types/entities/announcement.ts index 953c3eb7..73955b0b 100644 --- a/types/entities/announcement.ts +++ b/types/entities/announcement.ts @@ -1,7 +1,7 @@ -import { Emoji } from "./emoji"; -import { StatusTag } from "./status"; +import { APIEmoji } from "./emoji"; +import { APIStatusTag } from "./status"; -export interface Announcement { +export interface APIAnnouncement { id: string; content: string; starts_at: string | null; @@ -13,8 +13,8 @@ export interface Announcement { read: boolean | null; mentions: AnnouncementAccount[]; statuses: AnnouncementStatus[]; - tags: StatusTag[]; - emojis: Emoji[]; + tags: APIStatusTag[]; + emojis: APIEmoji[]; reactions: AnnouncementReaction[]; } diff --git a/types/entities/application.ts b/types/entities/application.ts index f95d6630..3439df77 100644 --- a/types/entities/application.ts +++ b/types/entities/application.ts @@ -1,4 +1,4 @@ -export interface Application { +export interface APIApplication { name: string; website?: string | null; vapid_key?: string | null; diff --git a/types/entities/async_attachment.ts b/types/entities/async_attachment.ts index 71817c1b..e46cd2c9 100644 --- a/types/entities/async_attachment.ts +++ b/types/entities/async_attachment.ts @@ -1,13 +1,13 @@ -import { Meta } from "./attachment"; +import { APIMeta } from "./attachment"; -export interface AsyncAttachment { +export interface APIAsyncAttachment { id: string; type: "unknown" | "image" | "gifv" | "video" | "audio"; url: string | null; remote_url: string | null; preview_url: string; text_url: string | null; - meta: Meta | null; + meta: APIMeta | null; description: string | null; blurhash: string | null; } diff --git a/types/entities/attachment.ts b/types/entities/attachment.ts index 2933fd6c..b494fdd0 100644 --- a/types/entities/attachment.ts +++ b/types/entities/attachment.ts @@ -1,4 +1,4 @@ -export interface Sub { +export interface APISub { // For Image, Gifv, and Video width?: number; height?: number; @@ -13,15 +13,15 @@ export interface Sub { bitrate?: number; } -export interface Focus { +export interface APIFocus { x: number; y: number; } -export interface Meta { - original?: Sub; - small?: Sub; - focus?: Focus; +export interface APIMeta { + original?: APISub; + small?: APISub; + focus?: APIFocus; length?: string; duration?: number; fps?: number; @@ -34,14 +34,14 @@ export interface Meta { audio_channel?: string; } -export interface Attachment { +export interface APIAttachment { id: string; type: "unknown" | "image" | "gifv" | "video" | "audio"; url: string; remote_url: string | null; preview_url: string | null; text_url: string | null; - meta: Meta | null; + meta: APIMeta | null; description: string | null; blurhash: string | null; } diff --git a/types/entities/card.ts b/types/entities/card.ts index bd496cd9..1e139ad0 100644 --- a/types/entities/card.ts +++ b/types/entities/card.ts @@ -1,4 +1,4 @@ -export interface Card { +export interface APICard { url: string; title: string; description: string; diff --git a/types/entities/context.ts b/types/entities/context.ts index e73a0180..79dfeac3 100644 --- a/types/entities/context.ts +++ b/types/entities/context.ts @@ -1,6 +1,6 @@ -import { Status } from "./status"; +import { APIStatus } from "./status"; -export interface Context { - ancestors: Status[]; - descendants: Status[]; +export interface APIContext { + ancestors: APIStatus[]; + descendants: APIStatus[]; } diff --git a/types/entities/conversation.ts b/types/entities/conversation.ts index d8eeeda3..ef05c835 100644 --- a/types/entities/conversation.ts +++ b/types/entities/conversation.ts @@ -1,9 +1,9 @@ -import { Account } from "./account"; -import { Status } from "./status"; +import { APIAccount } from "./account"; +import { APIStatus } from "./status"; -export interface Conversation { +export interface APIConversation { id: string; - accounts: Account[]; - last_status: Status | null; + accounts: APIAccount[]; + last_status: APIStatus | null; unread: boolean; } diff --git a/types/entities/emoji.ts b/types/entities/emoji.ts index 32e4437f..ea725c8e 100644 --- a/types/entities/emoji.ts +++ b/types/entities/emoji.ts @@ -1,4 +1,4 @@ -export interface Emoji { +export interface APIEmoji { shortcode: string; static_url: string; url: string; diff --git a/types/entities/featured_tag.ts b/types/entities/featured_tag.ts index 765acb0b..e8a04d2d 100644 --- a/types/entities/featured_tag.ts +++ b/types/entities/featured_tag.ts @@ -1,4 +1,4 @@ -export interface FeaturedTag { +export interface APIFeaturedTag { id: string; name: string; statuses_count: number; diff --git a/types/entities/field.ts b/types/entities/field.ts index f5e5cb83..2030e80d 100644 --- a/types/entities/field.ts +++ b/types/entities/field.ts @@ -1,4 +1,4 @@ -export interface Field { +export interface APIField { name: string; value: string; verified_at: string | null; diff --git a/types/entities/filter.ts b/types/entities/filter.ts index 496dfe4e..e3fd6d43 100644 --- a/types/entities/filter.ts +++ b/types/entities/filter.ts @@ -1,4 +1,4 @@ -export interface Filter { +export interface APIFilter { id: string; phrase: string; context: FilterContext[]; diff --git a/types/entities/history.ts b/types/entities/history.ts index 4c9d55d3..985b52f0 100644 --- a/types/entities/history.ts +++ b/types/entities/history.ts @@ -1,4 +1,4 @@ -export interface History { +export interface APIHistory { day: string; uses: number; accounts: number; diff --git a/types/entities/identity_proof.ts b/types/entities/identity_proof.ts index a7aff6e1..513925ff 100644 --- a/types/entities/identity_proof.ts +++ b/types/entities/identity_proof.ts @@ -1,4 +1,4 @@ -export interface IdentityProof { +export interface APIIdentityProof { provider: string; provider_username: string; updated_at: string; diff --git a/types/entities/instance.ts b/types/entities/instance.ts index f111c4c5..ba3c270e 100644 --- a/types/entities/instance.ts +++ b/types/entities/instance.ts @@ -1,16 +1,16 @@ -import { Account } from "./account"; -import { Stats } from "./stats"; -import { URLs } from "./urls"; +import { APIAccount } from "./account"; +import { APIStats } from "./stats"; +import { APIURLs } from "./urls"; -export interface Instance { +export interface APIInstance { uri: string; title: string; description: string; email: string; version: string; thumbnail: string | null; - urls: URLs; - stats: Stats; + urls: APIURLs; + stats: APIStats; languages: string[]; registrations: boolean; approval_required: boolean; @@ -37,11 +37,11 @@ export interface Instance { max_expiration: number; }; }; - contact_account: Account; - rules: InstanceRule[]; + contact_account: APIAccount; + rules: APIInstanceRule[]; } -export interface InstanceRule { +export interface APIInstanceRule { id: string; text: string; } diff --git a/types/entities/list.ts b/types/entities/list.ts index d61ccb53..85ccbf52 100644 --- a/types/entities/list.ts +++ b/types/entities/list.ts @@ -1,7 +1,7 @@ -export interface List { +export interface APIList { id: string; title: string; - replies_policy: RepliesPolicy; + replies_policy: APIRepliesPolicy; } -export type RepliesPolicy = "followed" | "list" | "none"; +export type APIRepliesPolicy = "followed" | "list" | "none"; diff --git a/types/entities/marker.ts b/types/entities/marker.ts index 8668487c..d81b47aa 100644 --- a/types/entities/marker.ts +++ b/types/entities/marker.ts @@ -1,4 +1,4 @@ -export interface Marker { +export interface APIMarker { home: { last_read_id: string; version: number; diff --git a/types/entities/mention.ts b/types/entities/mention.ts index b323e149..f229af90 100644 --- a/types/entities/mention.ts +++ b/types/entities/mention.ts @@ -1,4 +1,4 @@ -export interface Mention { +export interface APIMention { id: string; username: string; url: string; diff --git a/types/entities/notification.ts b/types/entities/notification.ts index cca7fbba..6e8b4288 100644 --- a/types/entities/notification.ts +++ b/types/entities/notification.ts @@ -1,12 +1,12 @@ -import { Account } from "./account"; -import { Status } from "./status"; +import { APIAccount } from "./account"; +import { APIStatus } from "./status"; -export interface Notification { - account: Account; +export interface APINotification { + account: APIAccount; created_at: string; id: string; - status?: Status; - type: NotificationType; + status?: APIStatus; + type: APINotificationType; } -export type NotificationType = string; +export type APINotificationType = string; diff --git a/types/entities/poll.ts b/types/entities/poll.ts index efa3e357..1de2bf1d 100644 --- a/types/entities/poll.ts +++ b/types/entities/poll.ts @@ -1,11 +1,11 @@ -import { PollOption } from "./poll_option"; +import { APIPollOption } from "./poll_option"; -export interface Poll { +export interface APIPoll { id: string; expires_at: string | null; expired: boolean; multiple: boolean; votes_count: number; - options: PollOption[]; + options: APIPollOption[]; voted: boolean; } diff --git a/types/entities/poll_option.ts b/types/entities/poll_option.ts index 89375a56..86017868 100644 --- a/types/entities/poll_option.ts +++ b/types/entities/poll_option.ts @@ -1,4 +1,4 @@ -export interface PollOption { +export interface APIPollOption { title: string; votes_count: number | null; } diff --git a/types/entities/preferences.ts b/types/entities/preferences.ts index eb144ab6..1ad5b14f 100644 --- a/types/entities/preferences.ts +++ b/types/entities/preferences.ts @@ -1,4 +1,4 @@ -export interface Preferences { +export interface APIPreferences { "posting:default:visibility": "public" | "unlisted" | "private" | "direct"; "posting:default:sensitive": boolean; "posting:default:language": string | null; diff --git a/types/entities/push_subscription.ts b/types/entities/push_subscription.ts index 907a996e..ab48c6dc 100644 --- a/types/entities/push_subscription.ts +++ b/types/entities/push_subscription.ts @@ -1,4 +1,4 @@ -export interface Alerts { +export interface APIAlerts { follow: boolean; favourite: boolean; mention: boolean; @@ -6,9 +6,9 @@ export interface Alerts { poll: boolean; } -export interface PushSubscription { +export interface APIPushSubscription { id: string; endpoint: string; server_key: string; - alerts: Alerts; + alerts: APIAlerts; } diff --git a/types/entities/relationship.ts b/types/entities/relationship.ts index 330a7f17..5dffd082 100644 --- a/types/entities/relationship.ts +++ b/types/entities/relationship.ts @@ -1,4 +1,4 @@ -export interface Relationship { +export interface APIRelationship { id: string; following: boolean; followed_by: boolean; diff --git a/types/entities/report.ts b/types/entities/report.ts index 487fd7b0..efc7d3ff 100644 --- a/types/entities/report.ts +++ b/types/entities/report.ts @@ -1,15 +1,15 @@ -import { Account } from "./account"; +import { APIAccount } from "./account"; -export interface Report { +export interface APIReport { id: string; action_taken: boolean; action_taken_at: string | null; - category: Category; + category: APICategory; comment: string; forwarded: boolean; status_ids: string[] | null; rule_ids: string[] | null; - target_account: Account; + target_account: APIAccount; } -export type Category = "spam" | "violation" | "other"; +export type APICategory = "spam" | "violation" | "other"; diff --git a/types/entities/results.ts b/types/entities/results.ts index ba10872d..54c1d2bd 100644 --- a/types/entities/results.ts +++ b/types/entities/results.ts @@ -1,9 +1,9 @@ -import { Account } from "./account"; -import { Status } from "./status"; -import { Tag } from "./tag"; +import { APIAccount } from "./account"; +import { APIStatus } from "./status"; +import { APITag } from "./tag"; -export interface Results { - accounts: Account[]; - statuses: Status[]; - hashtags: Tag[]; +export interface APIResults { + accounts: APIAccount[]; + statuses: APIStatus[]; + hashtags: APITag[]; } diff --git a/types/entities/role.ts b/types/entities/role.ts index 4566feac..6bb0f4b6 100644 --- a/types/entities/role.ts +++ b/types/entities/role.ts @@ -1,3 +1,3 @@ -export interface Role { +export interface APIRole { name: string; } diff --git a/types/entities/scheduled_status.ts b/types/entities/scheduled_status.ts index 58330967..10b9f505 100644 --- a/types/entities/scheduled_status.ts +++ b/types/entities/scheduled_status.ts @@ -1,9 +1,9 @@ -import { Attachment } from "./attachment"; -import { StatusParams } from "./status_params"; +import { APIAttachment } from "./attachment"; +import { APIStatusParams } from "./status_params"; -export interface ScheduledStatus { +export interface APIScheduledStatus { id: string; scheduled_at: string; - params: StatusParams; - media_attachments: Attachment[]; + params: APIStatusParams; + media_attachments: APIAttachment[]; } diff --git a/types/entities/source.ts b/types/entities/source.ts index b11c0b33..bd18094a 100644 --- a/types/entities/source.ts +++ b/types/entities/source.ts @@ -1,9 +1,9 @@ -import { Field } from "./field"; +import { APIField } from "./field"; -export interface Source { +export interface APISource { privacy: string | null; sensitive: boolean | null; language: string | null; note: string; - fields: Field[]; + fields: APIField[]; } diff --git a/types/entities/stats.ts b/types/entities/stats.ts index 4f3e825d..3effffe9 100644 --- a/types/entities/stats.ts +++ b/types/entities/stats.ts @@ -1,4 +1,4 @@ -export interface Stats { +export interface APIStats { user_count: number; status_count: number; domain_count: number; diff --git a/types/entities/status.ts b/types/entities/status.ts index 4a0eb2a9..6fb21a5d 100644 --- a/types/entities/status.ts +++ b/types/entities/status.ts @@ -1,22 +1,22 @@ -import { Account } from "./account"; -import { Application } from "./application"; -import { Attachment } from "./attachment"; -import { Card } from "./card"; -import { Emoji } from "./emoji"; -import { Mention } from "./mention"; -import { Poll } from "./poll"; +import { APIAccount } from "./account"; +import { APIApplication } from "./application"; +import { APIAttachment } from "./attachment"; +import { APICard } from "./card"; +import { APIEmoji } from "./emoji"; +import { APIMention } from "./mention"; +import { APIPoll } from "./poll"; -export interface Status { +export interface APIStatus { id: string; uri: string; url: string; - account: Account; + account: APIAccount; in_reply_to_id: string | null; in_reply_to_account_id: string | null; - reblog: Status | null; + reblog: APIStatus | null; content: string; created_at: string; - emojis: Emoji[]; + emojis: APIEmoji[]; replies_count: number; reblogs_count: number; favourites_count: number; @@ -26,21 +26,21 @@ export interface Status { sensitive: boolean; spoiler_text: string; visibility: "public" | "unlisted" | "private" | "direct"; - media_attachments: Attachment[]; - mentions: Mention[]; - tags: StatusTag[]; - card: Card | null; - poll: Poll | null; - application: Application | null; + media_attachments: APIAttachment[]; + mentions: APIMention[]; + tags: APIStatusTag[]; + card: APICard | null; + poll: APIPoll | null; + application: APIApplication | null; language: string | null; pinned: boolean | null; bookmarked?: boolean; // These parameters are unique parameters in fedibird.com for quote. quote_id?: string; - quote?: Status | null; + quote?: APIStatus | null; } -export interface StatusTag { +export interface APIStatusTag { name: string; url: string; } diff --git a/types/entities/status_params.ts b/types/entities/status_params.ts index 3f67a6c0..7c74bb56 100644 --- a/types/entities/status_params.ts +++ b/types/entities/status_params.ts @@ -1,4 +1,4 @@ -export interface StatusParams { +export interface APIStatusParams { text: string; in_reply_to_id: string | null; media_ids: string[] | null; diff --git a/types/entities/status_source.ts b/types/entities/status_source.ts index 095d4682..3f82d1b8 100644 --- a/types/entities/status_source.ts +++ b/types/entities/status_source.ts @@ -1,4 +1,4 @@ -export interface StatusSource { +export interface APIStatusSource { id: string; text: string; spoiler_text: string; diff --git a/types/entities/tag.ts b/types/entities/tag.ts index 7022eccb..a519dca2 100644 --- a/types/entities/tag.ts +++ b/types/entities/tag.ts @@ -1,8 +1,8 @@ -import { History } from "./history"; +import { APIHistory } from "./history"; -export interface Tag { +export interface APITag { name: string; url: string; - history: History[]; + history: APIHistory[]; following?: boolean; } diff --git a/types/entities/token.ts b/types/entities/token.ts index 90b94280..54450272 100644 --- a/types/entities/token.ts +++ b/types/entities/token.ts @@ -1,4 +1,4 @@ -export interface Token { +export interface APIToken { access_token: string; token_type: string; scope: string; diff --git a/types/entities/urls.ts b/types/entities/urls.ts index 1f877fe2..8a7134f1 100644 --- a/types/entities/urls.ts +++ b/types/entities/urls.ts @@ -1,3 +1,3 @@ -export interface URLs { +export interface APIURLs { streaming_api: string; } diff --git a/types/entity.ts b/types/entity.ts new file mode 100644 index 00000000..e69de29b diff --git a/utils/response.ts b/utils/response.ts index eb820d94..f6148dbd 100644 --- a/utils/response.ts +++ b/utils/response.ts @@ -5,4 +5,10 @@ export const jsonResponse = (data: object, status = 200) => { }, status, }); +} + +export const errorResponse = (error: string, status = 500) => { + return jsonResponse({ + error: error + }, status); } \ No newline at end of file