From 31b1ae566eb7f636d6d71ca6822e8ae1b58639ae Mon Sep 17 00:00:00 2001 From: Avior Date: Thu, 25 Jul 2024 12:29:06 +0200 Subject: [PATCH] feat: Add standard Error reporting to user through RFC9457 (#519) --- server/bun.lockb | Bin 88897 -> 86398 bytes server/package.json | 55 +++++++------ server/src/V2/endpoints/jsonEndpoints.ts | 28 ++++--- server/src/index.ts | 30 +++---- server/src/libs/Errors.ts | 46 +++++++++++ server/src/libs/RFCs/RFC7807.ts | 52 ++++++++++++ server/src/util.ts | 99 ++++++----------------- 7 files changed, 180 insertions(+), 130 deletions(-) create mode 100644 server/src/libs/Errors.ts create mode 100644 server/src/libs/RFCs/RFC7807.ts diff --git a/server/bun.lockb b/server/bun.lockb index 8ec573aa12b66f347c3093d6a7c196dffff786d8..2a5bc0dd6997ea6d5c2b25d971b59e7c1ecb3a83 100755 GIT binary patch delta 16415 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 H+Z_H66!qBK delta 17951 zcmeHvcUV+c_wJk{qYO$@ap*;CfDBa!B@WmjB8e#~#(*Ot4AP5ARKVDyLCscAEJ3jb zVk1UPV%M116Ju;K8cQtLu~%~6bxP#?zT~_2d+z<`KF@a^-&tp^cdxzI-h1sm$2o56 zmTGrx(#|sYUFkcisdekcYt|h5aiF`$zGe~Y2T!?S=j?v>%3LpJ?{05b58^a*-JCtZ zi{Dx^d$f1STv(cDG^Ir$5m`h{ll1~#qp^YgTUovjsfYd+WMjzDDCY=ifwYHA$xk+C zn1`h#XIKg?sHeoV45w%`;R=4dtg~j!emeVl6dLWX{O2A%XvW8Jb`(=G4B$ZD| z2^?<8%P+{xCsw1O3Q2w>^XnW~I^RZ@FRSTnKHs+FxUA*MP6f@RMovIdqx&JL_oJIj zjbK!$PR$7DWZwy%=4uD(pjnq45;%Y3Ck&q7iFrI0igqabO@ z(jZG{S-7>92wPbyIibj>t)w1W^38d9<|554=u}|>Dj@$pa>Y58EOF|(LZ@6v$;->m z3rrb(wXGCjIP5evzL1myPQIEFF`_0gP(&|UN;yyqNjQQFMFwQLYtadZRS1Zh?}67LtZ2FEBkn zI}1xJkS)-9mRvJP^=D;g=TI&TOBp_#*7EcAQv2ACl2foIvxlY!g41N1^HOOWC=8aW zyNq%zP;KWBjRsjzx*HPfqckE^io*|*267YZ)L-lu*%>2KG`V3K4U)sRG!-}0(F90} z&kY61AywK^G&jQ8poVQq`XD$OH_DJ4Gxi*LpX&xYjmKc4zy)pU~WuSZC6bb+J^ zQa0fKxsfXyyt0vpb(ZRpH|mm7KU7Eq`%a`3NUSVJrCE|O{u)bu%4k|0XJDuJ*S;n- zm}4$T3mlPBg!vLXfTbh`SkpzC=>8_jK3|qSASn^fgQt$|g!I<5!U#r54r^hc0{x?< ziqKoJ!3)uBX`>U~PO+E!+kVpO(%tXI=@RwER zh6KzDdwb>({d(&Y+TX)_Pe>bnG14hE`)cTC#h#nTUN5#gRuIFdj=gm7!Svx)2a|%o z={b&>mUp!5*i&k^?7-(|CNyX_TG+VMHK#K7?zqMK22^YaZd}ScPjVQXkui~5l;3dH zxa3bek5+cht`0FI2yVB%+E1OHwU)YVdGh~ zy^%Y|DjlP^R;&ihU@MIV*;{HY`U=pH6Qzwr-``ke(2%0&YOo=>>AFKh29{chT9M2(prCadpi!V!EVpxnH59B#^rseXjz}Sig2wmLF@I;1 zZZ3>i4pLqGVI8~fWa6!DSUN;s8&(BTX2blQOx%yG(m6`!kA;WiE7h>-=0?uK{9K~A zjm+W_#ouhquDh6YTC6PW2k0TS*#R1o7@9OHm9A0z4qJBJ)x^KBW&Um^T^MqfN}$4~ zMqMs63WpcRxe{6r(L2Q1$ltPK{_ZBuz%1@jToS8vkKz~Gv+M3A-B~O$^5VoHaKh4* zdL80q)OCd>#Z%n^^+Ti9cyZ{qK*P$BqT`?A!NC&&IZ6&<5iXh4ct+{w$~H96+{o{B zWa(Zet^up`iqdt#`lHFfj5>?+?j5CD0h=5FM*3(|R^^R3Y$i3WW!3IRt~c}ZiPDwA zCI{eY2g~rq0-c>f=0niL7Tp$pM#G~pr_g2j=- z{8~rp*2p$Y6bAAFG^ucXnpnz&A^rfrn2&{dh#+O|f1jVnuUXVN`{kwzP-kI@cbHSMCf z@yyRJO7{bvXQb7QkabS(8ciI^H4yX242^O?!*achwzHwp(BQ#VlgBRD1u$=WYg-Qw z$zN?v_*?5-SZV0gWv|0ZO9M~q8XKivou4Nr4_0Y;4uM8(W7lbIv|S);>Uw^GHTHkj z&U<*VDt{9{)QhG2n{+?o`%)TDtSi2QH}elL@#WquJ;0>>*&7_Y)h?26>cg%Fm~?~j z+h)`WH=?s`#obFjRQ1-FxWW+m<*dC;V#LAkUQniMdFq4AS?hyAUcQP&w-3~CWqzwSe5 zRHrmPJE2MALnSr-k{=diGx$N1^0V5@sGScjp2m*9>d&geO}Y>~A<#gJUsc+CXyI&) zdnCU;fTedZ@ecx6RR@#KfIUWDrI@Or(4=9flvvcBrFS&(%>r2!L>~w*6F(`C`5R6A zuYoMxXwuqaVqa&0#z;QFz^=nS)xi8CO#DUzONV%9U{w(&ZA1{QRVtMp#QZy%bcTJ^W$J5;qsfedh zcM{rLl7{eOLRosGNjDqcSG{1w7c#yaPC}yr6_=8>C8{^FK$l3}EZhx;T^jaB&?r8n zWpkr06v6crYY6c%@}t6;e;1Q>8;k~a%RZ8S9L}n`m~;^xG@4Fg&ACQ=NkF5d7r##U zFFLSvlS%g&MzmjQ!)j5aKQ(>|2Ydy{ zc0QA}MzoRYtk5J431$ac$GZI|F3i6t9um5+bO^JFRY5E_vFkle+A|2KC#`JWJBp?EGV$+3v8rAs?S9NiZv`Hv-%My6Q_j5^6jF+SRe? zir0T4$-gB)68o80PaFIWAl_gP60iD_3SvJHuYV_XD1lTL>p?z?5;>~Tu)eW+Oue{U zYIsPDwaAJ_+}D@XElj_7k>uFi*tQ$Ki?!xUv{1hw6R;Yfi=-8>9w2+QEWd=LizGGq z6+j)?4A4cA>e~vCeFs4G>;dQ^N%egPkbNKi&JH2o{G+6X4ge0ouK-nW2B7QjBu()} zk(DHsza;DbDy8;S1*pNx06AWf9Z8aYRhHLe`~Quk`f8|sy16b_NRsp$vb+gN0o|76 zT}Zk}Qn^}EaFL{3c?OXEIY1Xl@_$JRE|SF8$+F~6+)x1yRw8&=HXsETNxHSd2bF6m z+ey-GBiXK#?IdY#ZDqT?EFEROzC?W;HO)XYfI|zpKz&Ijy=*5*6}v(5Y+Jk+{er4# zMH*xqxm0~grndMX*-qw3Qh9$_CrO(_FeF7021!&neaNx_>+*(I33+vrtt6@RYmj8> zBHKyQtx4AFOEN{_gZj`NlKgu?QoS)m{)43Qah$wRa3eazLz2%MvffYD-y)-Y{lDG& zO<n!?Fep;56)e@zZl4-H#MMkBFab639|4ex49N4TH!sS^PXFes+A1EM?;7 z74g$Fm@MulLDPE`B}|KjRb0vQqr4OVr2xxh~+nD%;_>^ni@K5~;dYpZW>s}eNXPL+9u{Bc{?(bXkcH=`m*PZ*UZA0&q(u1Nj zLM|-VjPDe-Hc8Kpo1>*oR-)XP&rqTghv->rHRZvIM9R*rXJ?11yu6rSGI?zW^>MO_ z@?ob%Dr=~o6%J8RzU-SM^72g9GY>V@nwdrF2&gW@R9}8QF#ThuEU8s2WrJ|6&1*8MXGGLp1qr@qJmgODtVbw^-PU+Nh?& z7-u1>JWbD1EGn-KtXibvEqdmZuA+?0oK9YKp!TY%PRwxxQESsNh9gv7k!+VpC62&+ zWvHkwEH{I^>@qORYATBPWD>O<)TB(6S66maq_Q>V5QH>dwYzk(Xzdo?TW`Jy}RL zQAa?{%~pB!W*0=NI9tzp=BTJxHamyB3^|xDHPwf8%_Zs_s1>;?ufD8Sq{?zJUwJC( z4OWpyUZy*3WzGt$9xs2yxwKiA{AeN`6^UV1DLsxyy`&h zRZ|JfaU@Y|3o&0KRbKD0T_Tk@67w}mMZM2*N0FD^D9o3dN@6~viP{co(rA^JnH?3W ztkIaSA{CX)#ut&7XA$O0O{K7q4~RMfYVHRruT*wHq>4Yle2r027B+hfc^SrFzQ#n? z-*iVPJ1%Xyd0$+7a^d(dqn}+I+R$@yrlCo|ls9`_A2YaD@WHun$5mO1*8FnRzTl2q zX3?Fane!?x?^^KQ^27sIU4n%lzG=6u!VS|tMql!KQs2kj-TN=B?Eb~w4Kvq_ZNKAK zj>o3G;i1>}HJdr_^1+4fyTb2|8R}?BvK-vGx8%#OE6m%@*qvX?HF}(Ub?RpyAQI&( zA+vsTn+_~zEgz3wXxWr}>y6lL?>+j_&@<9|Q)KxIzv%-9O|(m1eWYgPq;Oa7?`I9P zZf1YkxGtpkrEVUBf9jdNYgOgJUoT-VAFD61`h-i!{b=;~2h$#p`EW*jpX?+ndu~>Y z(>rY{Hmr{6l#`tFmSdk~SCZmR{ZKpeOwmt$pTvH2<<9B+`xO&bq|Bf2eZBCq>xXx8 z<`2hLJf9NWbwS#g^~qK{>e_H}^8&kJFd*@-=$}G%s`A(``(Wx$UX*dplk@pnw0U-30so zF1x36xDY(}l;re5%%^nq2TtxfhTatcs z-FSK5oM~m}KW=^01xpa&4IR0vf5O^%G3ST3PI#a{^(cPovBRsL1WnIe(CO57cYeqn zSmW4fdb^pS^U9{Iy*9X4zl84{94y;2s&4mgdOT`*N=Ci#^6Q6JaAVzAHpcDNsn5T8 zCwct7nXRW-n!VVzVTu0I>Sl{N3Y&T?9(0IX;pS=@6Wx2K_Q8s>Ys{I%(_AGWx5 z+`o5_*X!5@iuLT}SXJT|viV~v@#9Od<&0BNqgcf_qUu0ti&az+>sL(F+EQ#eYHAGQ zN{C7vk1eM}4POycR9*3QFxsi`fDn@d!p zfccuM^4i9#Mau3|%-1{>wS$@Gk=J%md)3q~<|q)AH6QaOsJ!;DT_WYV0Q2>!>RWMd z{jKT3Q%%*KQG4&iv2PL|y$n4&=tsMy2Ry0{PahFss{8$n@48~A#gmR)=ux}-#iFXv zl`k6Iz4h|KCl`zFu||)_-e`Rf)4o7o(*Ec%OG21s@^sg?xBqc!}LTG`iss2^Rw z@^jshD`W>6Q5Uqs56WlD~cbYuB4@``Uy!ISnW? zt{OAvNay#*^&Z?dtDV)>caVAukq(PgW3!*tETUmEEYh>SpQ)&WZ2o6Nom-@jGmibR z&;7&?_l#fK`!(lAakahM6z^Ud8|41cr-}AYFYf64{8-nz@;!Fnefm@R3%lGFPG2W@ zc*Qk*>*1z^jo&ZW)Qi5}l};UIvE@{4*=KsTrd-wHkL;;PnacI-{lzNkD63pdUbjKn zE>Tg(Si%yb$`|X|HZ^se=_-hdU!rG$^Hp9aSOh@UQ>U5V0;1Md;4e!HR9`7BqJxW%bYF8ZN$i$*Qx7O~!?_#zaLN;de8_uu2 zX~oX3wc_V^Z`i)JvsSCe>y^eVWaAg?^RK+Qs~ZA0P7ob!u18A$PVGVD2E)d3j+{2U z*!*+Am+RVcI{EkdjGOG#rPuJ(R`;iLQRB*63jC`W@ltg9rT0zxJq8#3h)1{dTPXc1 zOxJG!-O?|<(X#EVY@^@0yUDh5Z2TY1N`99a`pM58pzAz9tylw?F!8#GPr9XFQ~Sua zOR~)dQu%S{58PABje+d|UG#c`Txb@RUuo!2XjsX1}PeI+0ZA~EqWSjEJqZw?093D%lAieh>XKKJeH@NNr z)QA%hBs)KlZ7m?_o~}m#x#$6JfC7CCkd1=#04UHW0NGpsvQbb^rN?58#uZ8kSPI}7 zKn2}^RsaP+zeSSG9XJ8d^#ULpRdx%Y>m@)odJB0Tpi6m4^C=A*t~oRWxNKf43}dsd z#Py^ppp2pniUzs@lqK}u!3*#Pe1MjKFF-HoRsySl&w)yyirHSB5=#?D{ijKw{?Q;) z2E7hY*3cUZBM<>l#`ppBu7%#e(CfZ!0Ac7Pv1gGGZw zvq7UzqfMhrgGuj1=>1A(AQET~(9F`C9GY#K-TMH|;zNLDk!Fo%=_!Czv=QGt?u4?7 zZM^1H(h+w?faZnffnL+ls~dXBLoa;j9hV+(23!Gphh+ma0qFe}z5CJvG?z5Tl-UP> zgTNu+FmMF;5%>u>3j7Ql1Ad`*oX2rPlYbI81)K)<0oA~AUcW(QDSPy*4KyaHSX&H!hDbHI7v0_`!svVJwqO6X@)9&SwmdM`)m zbQN#_=nYa|fL=xQ05V_;01QAp^xgoiPFjtD04=QD;5q{xfH1%t@Buu44&X^rKGVxJ ztP!Ke7YZ$m#xM|}fC|$+?P+%a+Ou|2bhxY^vB_6jcA_p4eGb?QdQRbOs-ad*8+P`3b_oqAHQo~N0w^-d1WGzeciJu}`6xpu5Lyb}fDb?clP@JO)occa z0Pg{VfCOM5FaYQdbOE{o(LfaNF3qh8H#Ai50B-}7l0AX9fH#2N01aa=fM%VBGZY8` zXcf_FqUi_*Xi9 zJn4x5^?oQY97q9DWtj#^-@)mS79bzU1G0dUOngvAlO&^}XX8Ex$gO9q&yR#%{$>{i zW!rM#Ghh@@3Q*^YfYHDQKrt{5pv61}fUQLA{X!@Ufce0ufB?(`<^pp72FwD=fRBMm zz(imIFdq02m;umKO$R;#rU6rc$-q>Ae93PnFdO(p)Dhw$+>`^0fu+C_Syn(U1J(h1 zfStfbU<0ro_#9XTtN>O5l>phQfG+^@TMeuM*2?!^LRJIBeFc0$L$m|f0?;sR0=@+{ z1K$8&%l9O=1KWVD03{nWvR$iy{?5beL?{yK02h!PD?I>cJq zo{g^^?`DBUrPf)#IDV+v_7?CDH3S>5Rg7jH_guJX%y=)1o6APrb8%CSP23zd=-sog z?d*ZF5hxps#mp9?Yyr!@-@+43D+lKWaf{CMdb~IX1v{W1m}%^mQqT%&8S}aC!X0I^ zA2w%i+}FBILp0R)ml@ppoR&R)g=4rOgj%4wT{4xUE>Jr`9K0=NbH7RH7jt;E) zDfYqQ%RE;c0?%;l?QGk97dPc--O}nmZJvK{q7OV#FV!5uY7xVHcIH8VE-@5cHyAaQ zY}W%9X7eDN+r&P`UETv%W_l3LYphw1hjC)SE_`<#Tm3MU?`p#w9y$w69XMCcmHqxO zfU9DSANg>;Ebx(wn{w={{ej~**ZkD1C(mJef(@Z~&|&6BE?hht^C*lP#MV9P%cZlI zu;jA}{B!Bx$F4Zt(C2X&_YoUk=YkoS|2T|a*@PW=9O|wdN^3kawdDBkbpbqw!A00u zaZQ-}lTdzt6ZYm4Xa0B-mJaQF6IKrGW)n8+c__R7#Fc;6gf)B`>aLvB8=A0c{Q4_1 zF`U7MFd7pa>SOOb4Xqc^!g}{EEazFc6c^k6EL@7pS#FfId7dUW#pA@@ch5sRDrfR+ zTRa(bcgFc~^4Mc;l+$-niT6f6>%MsPt6ACKjJ1E^?4}&!>si!o!IoV;ZN&neD8vyg z=|z~Ea;&e>qS=RhCtO)7R}d!gE*zC|;iby>RZi^13uk`46KnCZe&1ZE2gNUk(|BD0 z6VF`gQl`2{*uk0CrhQ6ts-g|1>a-&o9lDj+?=pv=Cax5~4V?yFV z^8+L#mZc#q{I3&zdD;_?G*h zn_d{}_d0rrDWVZj4&*7PZ+OmV2nh)Xr5xE)&gZ;hl*4|?$(~n?avV@O`y(?Ys7^UC zs2mUa-#Y!<5dNW|e`u#f>XC9dQ8|eu&T|CqD9Uj~Fyo4qkq`kKg&2i4|Uf$wgbxVV~R_%M@ z8+1bKuDB2Sc?$(7>0XEu&9GChPjmls>wuq6%V~jrPxTgdb6gy6@(~<($XFjCk%xzJ z8q()XP-5FepR4eYH{}B@1*|-0cjcI3UUATZAJ0d7qXg|MSf`g;3g=OxqjIp(cgL2U z)mgvl#JI#{RE|E5+}&^bt%|Lbrt<#U#8)shfGJtmUGf#-beWgbrede9Lhk)d)Lf4Zyb6RigM<3&C}MybVY&7UNL8Vg*YqB z*sWH=a4Rm(T{%>GFtS&qFlNOY;`$bc;&>C`2ugNTj-zh*`O47G^S_!ReZ7YWSU6`5-YrCEs^fat_@fIBn2pXM!Vn!gsvP_LAUfb;Sl~O2UyZ$TP_*;9 zxc9I2?OO1P@e38!!k3Q-6<$EP|8q&DgmQLtS^CJrqPq7yP*BM&@gobML#8V}D{*mCpQj16wp?@J4O>p@b}K@f+9%J(r0pI!X9drl zfJKkC3xsT2&c)KSleCJ5CG6QId52+!=n5T3GqR@m%1F-(;Pk>rFAU^g-1%URky!+pzJ7hJWPnP=KA-DIz$Z%=)n=lt<~ zL|080;Wzm5JxszQJ8a6z!Qe-+oj>n(IC93TlJiYM4||03&vk8z5?0$|>HKpY{iB8R z_E_*2qJz;zS$IdH+Qi?K1%YMB_(;Se|4t}ez!DpWa0XzIvK zZjAq~p&+j)C@XtdiXb?0sa%Ngrz6*fGj49vlv~g+z-TB){UDnDH;DWoOIATjUQQnV z9gU<*{}sSrR>ADb9T&Q}a$Z7g4Cf{UwBqbG_i*EEIl-YX*HHMyh3g|Ex^N=}TNf@; zSnbYPZ%%OMnsb|%dvGfqvg?Hs6eva%C_nOuF-UZv*j$!5Bd8$HoNUQTjh5Y#v-47v zdn$wf;G)2iot3ZH#E=!Gu)vZbuX(8VO5g zX38*&xj?A!unij!$`i@sc%)&BxMqxlp7 diff --git a/server/package.json b/server/package.json index fd8020ee5..424a4621a 100644 --- a/server/package.json +++ b/server/package.json @@ -1,30 +1,29 @@ { - "name": "@tcgdex/server", - "private": true, - "main": "dist/index.js", - "scripts": { - "compile": "bun compiler/index.ts", - "dev": "bun --watch src/index.ts", - "validate": "tsc --noEmit --project ./tsconfig.json", - "start": "bun src/index.ts" - }, - "license": "MIT", - "dependencies": { - "@dzeio/config": "^1", - "@dzeio/object-util": "^1", - "@sentry/node": "^7", - "@tcgdex/sdk": "^2", - "apicache": "^1", - "express": "^4", - "graphql": "^15", - "graphql-http": "^1.22.1", - "ruru": "^2.0.0-beta.11" - }, - "devDependencies": { - "@types/apicache": "^1", - "@types/express": "^4", - "@types/node": "^20", - "glob": "^10", - "typescript": "^4" - } + "name": "@tcgdex/server", + "private": true, + "main": "dist/index.js", + "scripts": { + "compile": "bun compiler/index.ts", + "dev": "bun --watch src/index.ts", + "validate": "tsc --noEmit --project ./tsconfig.json", + "start": "bun src/index.ts" + }, + "license": "MIT", + "dependencies": { + "@dzeio/config": "^1", + "@dzeio/object-util": "^1", + "@tcgdex/sdk": "^2", + "apicache": "^1", + "express": "^4", + "graphql": "^15", + "graphql-http": "^1.22.1", + "ruru": "^2.0.0-beta.14" + }, + "devDependencies": { + "@types/apicache": "^1", + "@types/express": "^4", + "@types/node": "^20", + "glob": "^10", + "typescript": "^4" + } } diff --git a/server/src/V2/endpoints/jsonEndpoints.ts b/server/src/V2/endpoints/jsonEndpoints.ts index 1594ff63b..7adb3998b 100644 --- a/server/src/V2/endpoints/jsonEndpoints.ts +++ b/server/src/V2/endpoints/jsonEndpoints.ts @@ -3,7 +3,8 @@ import { Card as SDKCard } from '@tcgdex/sdk' import apicache from 'apicache' import express, { Request } from 'express' import { Query } from '../../interfaces' -import { betterSorter, checkLanguage, sendError, unique } from '../../util' +import { Errors, sendError } from '../../libs/Errors' +import { betterSorter, checkLanguage, unique } from '../../util' import Card from '../Components/Card' import Serie from '../Components/Serie' import Set from '../Components/Set' @@ -97,7 +98,8 @@ server const { lang, what } = req.params if (!checkLanguage(lang)) { - return sendError('LanguageNotFoundError', res, lang) + sendError(Errors.LANGUAGE_INVALID, res, { lang }) + return } const query: Query = req.advQuery! @@ -114,7 +116,8 @@ server data = Serie.find(lang, query) break default: - return sendError('EndpointNotFoundError', res, what) + sendError(Errors.NOT_FOUND, res, { details: `You can only run random requests on "card", "set" or "serie" while you did on "${what}"` }) + return } const item = Math.min(data.length - 1, Math.max(0, Math.round(Math.random() * data.length))) req.DO_NOT_CACHE = true @@ -136,7 +139,8 @@ server } if (!checkLanguage(lang)) { - return sendError('LanguageNotFoundError', res, lang) + sendError(Errors.LANGUAGE_INVALID, res, { lang }) + return } let result: any @@ -193,12 +197,12 @@ server ).sort() break default: - sendError('EndpointNotFoundError', res, endpoint) + sendError(Errors.NOT_FOUND, res, { endpoint }) return } if (!result) { - sendError('NotFoundError', res) + sendError(Errors.NOT_FOUND, res) } res.json(result) }) @@ -217,7 +221,7 @@ server id = id.toLowerCase() if (!checkLanguage(lang)) { - return sendError('LanguageNotFoundError', res, lang) + return sendError(Errors.LANGUAGE_INVALID, res, { lang }) } let result: any | undefined @@ -243,6 +247,9 @@ server } break default: + if (!endpointToField[endpoint]) { + break + } result = { name: id, cards: Card.find(lang, {[endpointToField[endpoint]]: id}) @@ -250,7 +257,8 @@ server } } if (!result) { - return res.status(404).send({error: "Endpoint or id not found"}) + sendError(Errors.NOT_FOUND, res) + return } return res.send(result) @@ -271,7 +279,7 @@ server subid = subid.toLowerCase() if (!checkLanguage(lang)) { - return sendError('LanguageNotFoundError', res, lang) + return sendError(Errors.LANGUAGE_INVALID, res, { lang }) } let result: any | undefined @@ -283,7 +291,7 @@ server break } if (!result) { - return sendError('NotFoundError', res) + return sendError(Errors.NOT_FOUND, res) } return res.send(result) }) diff --git a/server/src/index.ts b/server/src/index.ts index 40d2b3a20..39329a100 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,7 +1,7 @@ -import Sentry from '@sentry/node' -import express, { NextFunction } from 'express' +import express, { type Response } from 'express' import jsonEndpoints from './V2/endpoints/jsonEndpoints' import graphql from './V2/graphql' +import { Errors, sendError } from './libs/Errors' import status from './status' // Current API version @@ -10,20 +10,11 @@ const VERSION = 2 // Init Express server const server = express() -// allow to catch servers errors -const sentryDSN = process.env.SENTRY_DSN - -if (sentryDSN) { - Sentry.init({ dsn: sentryDSN}) - - server.use(Sentry.Handlers.requestHandler()) -} - // Route logging / Error logging for debugging server.use((req, res, next) => { const now = new Date() // Date of request User-Agent 32 first chars request Method - let prefix = `\x1b[2m${now.toISOString()}\x1b[22m ${req.headers['user-agent']?.slice(0, 32).padEnd(32)} ${req.method.toUpperCase().padEnd(7)}` + const prefix = `\x1b[2m${now.toISOString()}\x1b[22m ${req.headers['user-agent']?.slice(0, 32).padEnd(32)} ${req.method.toUpperCase().padEnd(7)}` const url = new URL(req.url, `http://${req.headers.host}`) const fullURL = url.toString() @@ -82,12 +73,13 @@ server.use(`/v${VERSION}`, jsonEndpoints) // Status page server.use('/status', status) -if (sentryDSN) { - server.use(Sentry.Handlers.errorHandler()) -} +// handle 404 errors +server.use((_, res) => { + sendError(Errors.NOT_FOUND, res) +}) -// error logging to the backend -server.use((err: Error, _1: any, _2: any, next: NextFunction) => { +// General error handler +server.use((err: Error, _1: unknown, res: Response, _2: unknown) => { // add a full line dash to not miss it const columns = (process?.stdout?.columns ?? 32) - 7 const dashes = ''.padEnd(columns / 2, '-') @@ -97,9 +89,9 @@ server.use((err: Error, _1: any, _2: any, next: NextFunction) => { console.error(err) console.error(`\x1b[91m${dashes} ERROR ${dashes}\x1b[0m`) - next(err) + sendError(Errors.GENERAL, res, { err }) }) // Start server server.listen(3000) -console.log(`🚀 Server ready at localhost:3000`); +console.log('🚀 Server ready at localhost:3000'); diff --git a/server/src/libs/Errors.ts b/server/src/libs/Errors.ts new file mode 100644 index 000000000..89e8613e9 --- /dev/null +++ b/server/src/libs/Errors.ts @@ -0,0 +1,46 @@ +import type { Response } from 'express' +import { languages } from '../util' +import type RFC7807 from './RFCs/RFC7807' + +export enum Errors { + LANGUAGE_INVALID = 'language-invalid', + NOT_FOUND = 'not-found', + + GENERAL = 'general' +} + +const titles: Record = { + [Errors.LANGUAGE_INVALID]: 'The chosen language is not available in the database', + [Errors.NOT_FOUND]: 'The resource you are trying to reach does not exists', + + [Errors.GENERAL]: 'An unknown error occured, please contact a developper with this message' +} + +const status: Record = { + [Errors.LANGUAGE_INVALID]: 404, + [Errors.NOT_FOUND]: 404, + + [Errors.GENERAL]: 500 +} + +const details: Partial) => string>> = { + [Errors.LANGUAGE_INVALID]: (meta) => `You must use one of the following languages (${languages.join(', ')}) while you used "${meta?.lang}"`, +} + +export function sendError(error: Errors, res: Response, metadata?: Record) { + const json: RFC7807 & Record = { + type: `https://tcgdex.dev/errors/${error}`, + title: titles[error], + status: status[error], + endpoint: res.req.url, + method: res.req.method, + ...metadata + } + + const dt = details[error] + if (dt) { + json.details = dt(metadata) + } + + res.status(json.status ?? 500).json(json).end() +} diff --git a/server/src/libs/RFCs/RFC7807.ts b/server/src/libs/RFCs/RFC7807.ts new file mode 100644 index 000000000..407ebbcda --- /dev/null +++ b/server/src/libs/RFCs/RFC7807.ts @@ -0,0 +1,52 @@ +/** + * Add headers: + * Content-Type: application/problem+json + * + * following https://www.rfc-editor.org/rfc/rfc7807.html + */ +export default interface RFC7807 { + /** + * A URI reference [RFC3986] that identifies the + * problem type. + * + * This specification encourages that, when + * dereferenced, it provide human-readable documentation for the + * problem type (e.g., using HTML [W3C.REC-html5-20141028]). + * + * When + * this member is not present, its value is assumed to be + * "about:blank" + */ + type?: string + + /** + * A short, human-readable summary of the problem + * type. + * + * It SHOULD NOT change from occurrence to occurrence of the + * problem, except for purposes of localization (e.g., using + * proactive content negotiation; see [RFC7231], Section 3.4). + */ + title?: string + + /** + * The HTTP status code ([RFC7231], Section 6) + * generated by the origin server for this occurrence of the problem. + */ + status?: number + + /** + * A human-readable explanation specific to this + * occurrence of the problem. + */ + details?: string + + /** + * A URI reference that identifies the specific + * occurrence of the problem. + * + * It may or may not yield further + * information if dereferenced. + */ + instance?: string +} diff --git a/server/src/util.ts b/server/src/util.ts index 1db8fce0b..4700c30af 100644 --- a/server/src/util.ts +++ b/server/src/util.ts @@ -1,58 +1,35 @@ -import { mustBeObject, objectLoop } from '@dzeio/object-util' -import { SupportedLanguages } from '@tcgdex/sdk' -import { Response } from 'express' -import { Query } from './interfaces' +import { objectGet, objectLoop } from '@dzeio/object-util' +import type { SupportedLanguages } from '@tcgdex/sdk' +import type { Query } from './interfaces' + +export const languages = [ + 'en', + 'fr', + 'es', + 'it', + 'pt', + 'pt-br', + 'pt-pt', + 'de', + 'nl', + 'pl', + 'ru', + 'ja', + 'ko', + 'zh-tw', + 'id', + 'th', + 'zh-cn' +] as const export function checkLanguage(str: string): str is SupportedLanguages { - return [ - 'en', - 'fr', - 'es', - 'it', - 'pt', - 'pt-br', - 'pt-pt', - 'de', - 'nl', - 'pl', - 'ru', - 'ja', - 'ko', - 'zh-tw', - 'id', - 'th', - 'zh-cn' - ].includes(str) + return languages.includes(str as 'en') } export function unique(arr: Array): Array { return arr.reduce((p, c) => p.includes(c) ? p : [...p, c], [] as Array) } -export function sendError(error: 'UnknownError' | 'NotFoundError' | 'LanguageNotFoundError' | 'EndpointNotFoundError', res: Response, v?: any) { - let message = '' - let status = 404 - switch (error) { - case 'LanguageNotFoundError': - message = `Language not found (${v})` - break - case 'EndpointNotFoundError': - message = `Endpoint not found (${v})` - break - case 'NotFoundError': - message = 'The resource you are searching does not exists' - break - case 'UnknownError': - default: - message = `an unknown error occured (${v})` - status = 500 - break - } - res.status(status).json({ - message - }).end() -} - export function betterSorter(a: string, b: string) { const ra = parseInt(a, 10) const rb = parseInt(b, 10) @@ -242,7 +219,7 @@ export function handleValidation(data: Array, query: Query) { } return data.filter((v) => objectLoop(filters, (valueToValidate, key: string) => { - let value: any + let value: unknown // handle subfields if (key.includes('.')) { value = objectGet(v, key.split('.')) @@ -253,35 +230,11 @@ export function handleValidation(data: Array, query: Query) { })) } -/** - * go through an object to get a specific value - * @param obj the object to go through - * @param path the path to follow - * @returns the value or undefined - */ -function objectGet(obj: object, path: Array): any | undefined { - mustBeObject(obj) - let pointer: object = obj; - for (let index = 0; index < path.length; index++) { - const key = path[index]; - const nextIndex = index + 1; - if (!Object.prototype.hasOwnProperty.call(pointer, key) && nextIndex < path.length) { - return undefined - } - // if last index - if (nextIndex === path.length) { - return (pointer as any)[key] - } - // move pointer to new key - pointer = (pointer as any)[key] - } -} - /** * validate that the value is null or undefined * @param value the value the check * @returns if the value is undefined or null or not */ -function isNull(value: any): value is (undefined | null) { +export function isNull(value: unknown): value is (undefined | null) { return typeof value === 'undefined' || value === null }