From 1d03afd397f4c3fdf79af321c43e6170c7c85e40 Mon Sep 17 00:00:00 2001 From: Nicklas Knell Date: Mon, 3 Jun 2024 18:25:46 +0200 Subject: [PATCH 1/2] feat: add netlify integration --- .gitignore | 3 ++ README.md | 33 +++++++++++++++++- bun.lockb | Bin 252294 -> 261487 bytes netlify.toml | 9 +++++ netlify/functions/exchange-token.ts | 51 +++++++++++++++++++++++++++ package.json | 2 ++ src/App.tsx | 32 +++++++++++++++-- src/AuthCallback.tsx | 52 ++++++++++++++++++++++++++++ src/api/api.ts | 10 ++++-- src/api/fetchAllOpenPrs.ts | 3 +- src/utils/authHelper.ts | 37 ++++++++++++++++++++ src/utils/index.ts | 1 + 12 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 netlify.toml create mode 100644 netlify/functions/exchange-token.ts create mode 100644 src/AuthCallback.tsx create mode 100644 src/utils/authHelper.ts diff --git a/.gitignore b/.gitignore index cbfc9c0..6ff5fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ dist-ssr *.njsproj *.sln *.sw? + +# Local Netlify folder +.netlify diff --git a/README.md b/README.md index b0c369c..1ae9ccd 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -# github-viewer \ No newline at end of file +# github-viewer + +## github app setup + +Follow [this guide](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app) on how to create a GitHub App and add the homepage URL and callback URL according to your netlify app. + +**Homepage URL**: https://your-netlify-prefix.netlify.app/ + +**Callback URL**: https://your-netlify-prefix.app/callback + + +## netlify setup + +For this you can follow [this getting started page](https://docs.netlify.com/cli/get-started). + +But these three commands should be enough: + +```bash +npm install netlify-cli -g +netlify login +netlify init +``` + +After this add these env vars to netlify: + +**Any env var without the prefix VITE will not be available on the client side** + +``` +GITHUB_CLIENT_SECRET= +VITE_GITHUB_CLIENT_ID= +VITE_CODERS_INITIAL= // username1,displayName1|username2,displayName2|... +``` diff --git a/bun.lockb b/bun.lockb index 28072e0027bc98e9bf89bef366c8534601520eca..cc46bc4eef6a785bc9bfc058891c45c1f96054ce 100755 GIT binary patch delta 58809 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

Ml7 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 delta 49052 zcmeF4d3Y36+V;B(8q$HtB1^(1AYfP$**hW7>{}o}R8~a@5FkLvc{s8DZx6km`*ZTIpKqM|*k=hx6FY|jbz}W6HyHF&`|~?Knl`!EKaLjj z=^DGR|8-W|5esL;XJlJGUumB&{{S3>7Z5Fbcuvlg+=-dK0i-C4-4!ke|KRueD#Opg z(w~BrE-B#hx5Zt*6R`4m#BSohE^lv1pDz|`4IC#Gt_0r!E4NW_1-LI<9_|FIw8u;M zd@=BxXrHeXJjUaxuu2^Y^DloIk>Z>Akbf7CTf)>bFTb7}$R9pBW87Gpm@#ftrg~y> zW_IR?44x*7@04*_jC=hWQR* z^DqBNN>zRtIl1G9kIl=P5|66>noEDFh6~6{{S%@T^+W=!j3#Gh44;yamDBQC*Y7)Y zwR{t`P=+aV6H!Qx!f0s|O}9p7I|Et0BL^^1ro;TTUu$e_E_gUj1r5 zUn3$a!>T|$)g_Pos<3**kFE^9C3jV1bq%*io`6+Q0jvtlfR#QIc6%ac!nkRfzG*dm zzU$D(!RpD|VO4B&2CZWb_{t%u2XZsA$4Mzu+s!xtE5TV<6?zv|S7uJ0JYjM|=8P|{ zchl|m;v;itXPqkrB#a!FF)C8QP3S7;NL{yKb?PbqJYW7qBGfhO5y&h50y9l>;O-k- zo(8KY%Eh}CJ59VQ)SZ^nIr%@L%m3#3ZVx2E8j2+5lv>)QfxDWfWleD=_sE>gX_@1v zD0ZroIRcA!+=YYp8Jw!>=j_>A!rvPSrPnK{FzW{n%+n~~`CEv&vxht=n8J-wl4 z$9nN4JpIQ6*Y8tUW4Pb5H^R)H{Ks6qNB%ql%6N+BFbvjU_VVl`SQ*rXte#Fr*HmrW#nnel7(Qik*7(d3nZu@zVngU170gS-M|GG!I&1i7 z(q!di&LB%)zpifC^}D%~bz;Vp(Fs)a->thpUT^HZW1Ob>rcTaE7?b1kO`JMDcld-6 znZr|^xs;dRodCO2ehn(3J{y^lGi4GLn4CE^Cu?M`@9iFL<5u)^8}kjUPI(BHzmIe* z&HTT%bk|{I*3|9guGde{HEZ62<=?)yo8KnvIBf`z5>N(TP^fN?tNXaU^?|KCGQ-BCKiK-{ZEh(#OM<;R>);%dg}o{YO|8`3lyk9H6{7R#tvSKX=cp3(K($ z%qEpTx4-L{lbJm&b23YGfNe$B%X@2pTfZp--TYpLHG79m&KRCa4>uX)>eFFonZSyV z%;phUlbz+ThkP{h0g9^$ucbcX4Y#@VUICZGz7JNlyxEkOADMn{4R)u-4p`gA7#!6x zOUbA#%oOvn$>vYV8J9JT@HIo-iku$e>bo=Ck(@=O{3pZepwS)=fmQKaU{$meTvj92 z%5zA7V~D8faYa~;!?f}a_$@5R}>XEA1j%>GpECf{~w@DHqGUHS6#XVj*!OdU^tOC!I zPVuK;?db2p8smMi8WP#*7PPesO4rNV%B)lNGreTla18b!So+9eTGCX7S}WVjSmKG3 zCuFmc_%d=bCMy0My3&0Gs~WSXy4Bz3ad4VDgQiSw-ctU<$)5pin4j(y)CE?;VL4g| zKHnYF-3(fwtNxeA*@I$gjyv;`V9nI(uCU|TrDSvs5i{ZI;E(Ti6AZ+zgJjwkNW zv5uwj9D(`>-R8TS*8|u}P^rM@YYGo_9JwXHs?gFI?&4m5zw1{Ky#e~SGu?a&;o8`% z;5u;22i;xu7_7Cu9&QBBAwFJ9qbC6s)CjH#mxSxWXL8*F_QIOl`4k)nKaQ>VNpM5> zCRja};OW2Nmxz57=46(?(c@cTrE3bS!KL6lIW#eD3l0qNPNEY=WaO|en=Wyu&QtnXI#GSS$9gkdEdL4PGpF!8<>R3$Yk0!Q@%!*obt8wP&Eyxv zUJXn4@@<$uoqAo1(0GkIr=6#}tc-DEvZknczO3<+re;mfoSe1R?oh5?-dAhgazBQZ z&#-ZF^7%SF@3vz#ta>f=xXn7Ze#54Y9I4G9D>E}Q)S|iXWpvf&cJfjEM`n$hI+^=n zZ*1)aQ?i|{$XD+L*YE6lryuh2HzBBCPBxQAu>W=;filXX9V$3-peR?<&A17A9Qwl> z-A&?dSRFl`%}v)#Y^A@*I#NYa@z+{yy4mfQs<1W>-%F~Po=ChS(GNsu*EvFj3K%vu zXV?Um!K^KA1&@0fjG};RiND3;2e-PLz@``7iSXG5w}6$~+>w3&R)LMTyYUmTt6?`I zeXTqKk(!r7sExgAhnw(cGLmC<=49u&JL6@yr4}tx1x`KTF0rZOvu5b1_TnpUkNID9 z8?cvn<#!lcyHw;6>o(G>$2f;h$eTKTcxI%FTI_Ob`1`Jkw7fR)D){tnx1i5qb#+G0 z_$JQba+-TkE<{&>TRg6~!fi;+*W9^~GkmHZxO}BvqyObNCTGHUwP4Hy?pjgMxR zW|S^FaLqSwc6#dI^s)oqpINbD`DZpyykUUf|FV4`zOBEc9jxEh-^%V@zilAb^7-1? zhi0^nBG8I_i`xqmLjKZrut8h@XuCU~PuTPMJYgSb&^B17I1d7N`R(-jA-}P^H*6cY zgqC8b)lc!aw+}RI>z`!@8?_C*8sN2zoz^fVSiXeM*M&4C?ZXKnYi$WTH8E_RC}9^s zWlGwGiQ&Mpl0IL$eJC*{&>+g^yV(iNCY0uc3JLXgLUD|3cPBKAP-iE!noviDtgnN1 zVdHRcKATz+<@r=cYj3oj+9YiK7Hto7P)r#+ojP~C)-EJ=HfyJYeW+p&5pBEJA{MVSz=wC3LXxHtTI*XLgKQk*wG!s*19ToDs-lbUC=Qc zxW1~-*TczSd{sNTQ#i05p{*U$E+v@3`kn675>Zd~I~a35ALtYVrH z)(ti7)UM&cqM97_HSE?uYubff!&a!49o;Q#&8lUmb_)ldtHl7?hnl2Vv9;~OZsB0} z+C^qk@NO*4c*d8ha=Er$&^;XN#)dKsk#TGn3O4MV}q`fjJX({y8fJGyt+3O2A)dxryqxfl1d7c@!<>>||IPHUZF1sdAX zeZp4HhIT4+XG6Q7PdNBa!z*wg zb#+3OZq#&_HiVEH`^**XEFssgIZMfn%_Zcfc#Tk+leSEgh&F&wZ%12A$c_Dpkef>i z+moC29zt$@2d-%4n?+I#A>^i5MW~09-#J2V+Ey$_H|P5ZIa=^jLhjUIF$LpWL`J(D z+uTsB+nrf*I6f5Ig*C_-lXT|okd}7pfN;>yoKkXUI}Wz-EO+P4^{jxi4{y2RS*~Zu zN=&v328OL^$#&tuaBx*}Bp2gf$w%B8qd!WUSyixSc)pSG@om=bKp-Rd?s zBi8FfJ?w(9Ve4cMyKrncP=VVKJ>4zEO6_T9Bj`G&0CD?lcF%v-MsKP*vn2G&qmSPEx_&UTd?p_rwvYu z!k}x^OPxc(ud!}&tc6U8{@g|TI6L_ULM`o>iLL$VcIw2o#qk+nADWO7SVyRjA|&1J zNP}+H{;$XlS3)Ijan4F;g9zQ^CMDF<37wC`27A)D5aqL@jSB_u#$q3l#T?m%#o)85 zG!Gu`=kt-hXPv@g_IXx(f1hvg6>A}u+q;@upJ6p8j(IRV6s$VH?K(HlzF1li?&`lE zs|8jmXH9>A)fCH_#K95+uPjU^K`ShG0CcmTgQZzRE{xbdFU~y(T=LR6t3Q}B$n|uN z@4>lPnxXEF{T`O`Ea|ki^sVmt;8;LUq+%sGbzv3+@ABf9vuvh&u;?hSCn_=ruULVP zu^7+#JPXi0shWbpDTJ=}+)*s`6?I^PjlG@vIqTvULMcvd4tHQW-)^VQ3T!p13x!rpLOOEU^SxNp3J9-wkkYVm*bQk^y zSn5dEDvPr$_rA~*OL@6*_hM=LaQ4sO>sXr7Zd~aRMRVzf)!|B9!M|7sv6PFuVemS} zD^I)aS}eEAWu3&*L}4Grsys5X3CS9WrJ6Z$0UL|MT7#6}VM1>GCLsR3z)hPDhW?3p-&olQD zQcljDB=A~950n}c@#s&e)0KQ4!*aKs^f94e!dOl$Zh=hjX;`Xbv~%Yqf>!1reRd>n;%Mmo%bi68Ht@yk2+A^rW#t(&&y7qHYgZY`8`8jAy) zrW0|o6C(SVCjJ1=Vm`4^zKq2#K+kD=C_B;Z-zdlGg{5ij-ZUP>Vz=_F6Ig89nl0Qk z8cd4ZUdpXWio$UB1Ffu8SehR0{r^iWO$Z*ah^sc)_2gE{L+njh+AA1bDt(V1;bKloC9o5YcXXs!nrH^v-!9I0{Q8 zGQ((r!SW_g(~$N4G&}mia4=@N+a2yI>w%>NZZFQoQUagzz`hqtJxuk{ zeOS$jqjMN-|LjP|F``zp*>?29;ow9>Zi(s(2KH4f4W*OZx?_%Ah|B&tZh3B=#q)eV zrVTFXT|xn6l zc8^L<>y;Axj*vU+m2Ldo$o_(5-9FbYToev|kEp$z+-HV@Y4=7pBqf-O)sZ-6+{}=* z|6aRraoDPIpB-(&fsFfnz9A|ku!j&2C_EaLoEMqq>6{YM=GoCp!hsJEr>Lg_!{+N2 zuF!Tuw>zP31wP-sPUvkyGhOX|ul|9z2;Jmp)gOqYxP#C%M>|Kz%{g;{o=#PngM`L7 zu{S-aR}EAv@Ef7=(t_C!QAVV@0)NBe!J${{;t#753m$g@!<^LZ7y5kToX~ngZbM5y zB7fC?4xw9|9N!}}?n;hf+vl6%cx)w<;fz7$M~iygjWx&d@Gs&|=?Yns7TKvQc*C~H zE`ZV&+lA2H#dh?Q;lQnicN$JU?-9~KakK6gvf3=M3!e-JbC$TX!MXWa8<*IrPcfv3 zU7cam)NZ+yCqyi^l&+!R;j0#h;2O*L$^voDwh_D^OB0O;3^t#`SR|@HZIl=kJ*KYgC2Kx;h^&#;<3l=)Mvth9}t-%oDo9HX@;}0 zA0?!l0jC10^5zPkvz3>#)1L_iFJkfZl3&r;K2x4_w*i{oG87z()zm4q+`yzLj8+)# z-Qgq8kwwiLk&;ijy8zR-Z77(6rFH7w&221gyG;8;+Ui;EwtM~49B7DhAG>m}w3j#s zFYCRh?bJ2lVAM*tMyx`TCwZ3N*{4TisebN(WG$9Dl^u{yE%}VwV{Tk`ELFnY(eJ}* zeI?zgf3X@qTl7{l7E7LPo?EawU9pO{+VmqZS8?f48Q5T-J zepqFvt_xcYSK9^aIB%_vlqb(sSZ;YtsK6Ikz3iCwDS<9)v<%W(rUaG|N>a!=zQ&Gz zAsoDBty>g#%0Z#vWGsym1#_QXhn475O4d(Usv_euJ*oI}PCnh(EC@B#;U(}WA@}v^ zQ9`OQ{l!y7?DOuk9)tE=QWQoPqG)!jP+$udNnUL&S$*S z`w-po5d#;nc)o1Gv(|dona2bs-6Sk|af0G*vl`1Qjpwjm*4xpW!@(P0a2x5~7_(lm z3lMi9dR1ogzKo?@$df7G{6#l`d$t&lrK)mrrG%;*+&Bh@Q%GMdO_V4*J)Kw1SUns| z>-))#?kS2Ub_xa0V0CbEAu1gQo&AN5wbqnWEUcK-s~Qz zg1Qe{_GUYETR2eWCE91Fv8DGVq$xwA7^?+X-5g7|qVKQ7#()GfUXS8Fj#8NMC3MB4tSW4$qE)c(s((M>t6s;$u3CY2z zJ#EI)e&;^ucG~Xd;GT8oVWm4cXp=dH#gmMt120vZ@30GB2?sNFxX*Q7Rk7OO!mjiR z2fmjh&%H`M9!o3RolQ?-b;K&=RP_{=b~E>ASo;;9uYbe}W&o0=+a)pS)KH(@vqy3uXAuiY=aAn zhe;cFBT^%#RA3s`K>JWACGayL=U5OJ{ujMiMGL+_$Q`%CeUhTy)B)RV#WN33)=0Oi*Z$UhNCzY*vXTcC-)(p!=MQ^>iBvh=o| z{y*VTq)i1Xm>tWxin8>cl``6=kJoYB^U?Ru6D^axSs#0YLr( z?Q;jNw@VjB<(0!43luvZlmJsadpfMED69W+fpVD%bo~)4{tlpg=K$q<50E|=lm-hU zr5toZjpDR3O<`cJG1odDufK-Vez@LSh9bKy%Ym2^fjS5a0C&pPVAvTE?1 zr;9Zv4S%^yC|!`nVv3I@D9&* zSo8m11kaYwEYDG_3Pk3LSW)?2yjTU@<>_~Oe2>Ta?T#+7@|ox9^I>QGDZoEpfj)l6 z%J=~@2CCRjJJD^|C_8iv&$=Zk5}HFMg(-%e-oCr-{S)w7fR$3m#`BK zSI*N$eFRJO|Hz9KD|po7V;+AjkxQ)LCmw(5>0;&c8C(WF>*+;V{rC^h@4T~M5C}L| zQC7t+p)2Y#A8K!L<^DU?23!VRH_bR${#87#>TxxXt9x9-)>%`Pw(p4sd)rcfgZ4~qAV%R)5Y@Z<#BIM z7b}?V*A^(_sGj@@D9h>p!ttaEIsj-(e-0&4(&J$KyzY^WFUT z_9X!oaJR?zx)DCH^!czdD1bG!7kcqUS@Dl}dQp~cqbq8W$Hwz>ImB-9FM?|;;SzN5 zQZHUy4SNe*#yKpo`yyIt^>>ob~MQU|nJr^dqc>&87r;vQAS{=Kd{AzlZwUc8EOQ0lQ?O29 zfAQ=?u&$yk>0Lg=?|bq8iIx8m&rht4{4>X9{1wm`eeOAkWuJhR@C#2b%4*41=!!by z#fz2hYgp;N^>nfHvz{%M{he&hKPTX^68=Dh;{MKu?4M=)8`iC=xN76ES{8$?06 z2-ZH(1il7t3+oaqqjs<=)WNemdhufEojqHud{SYh>*48Q***1_HC;VDAk_NrrLd&sp8iLy{GRsW^CAJUB3645V%h6F-UzFrn_#tk3#>~leXFPM zfUm_q=*1Uh`5p3fkMkTsUGfeQa(EY({hsIeN35Run0WCQp8uDyy7U`ZJ#h|J1ODOh zKVi+KAfg&jT9I0R3MfH&k7GRtMOXJg+W(!`W14@7KrPpMFDbcS z%HaB+u(s+{;5Zx?kuP7^h(br;f!LH(r#UH*Ov(Fy5 z;m>ztf4&?0^WB*5#>o4Q|8Fnbv>dtqQ+{&!^W9h$-SFqTG2Nzof4&>r?k$`DP1aH5 z&v#?)!{ndu#{PUaR`i{ib|KkXhJU^rQ*ZqFZjAq61CMihUi$OhnBI};+4j$OV|r)y z=esfOlYhP&`}5t{|LoluukrrNyRpr=KRh0=>z?o7FK16W-{0Q;j~M$1*0px63;q2S z>|8$i<}{y`?D`k`^F#eR`HZ!X@fl|~`DcHB6?;CPRqc~}R)PqR^|zN?im|s~-LS~- z?{9az9J46X-@{*j(MGKEmt#ytKW0Odq1ZLQ#h5o_Czx`UzyG2E{undW!oJb$#WqoX zgu2BLnwUw&5VlJ=BB7b7RUBcog)p}`LJQ+Bg%Dc|A+r=h$ZV9bUqZ#w2+1a+G{TJH z2yaM8G38nEnrMWdC8U}5*CGrk zh4AdP2))fY2~njH`c^#jTjYSx2_DVP^p>7<)5Hl$bVO}|eBN8%9tttpf z7?7JTs{d z!n|q-M7YSl$Zs*W(XF2ddBu!PeRTGT_BYwoP)@9)3Y924DVn%qEix9f;rbOX`z z%}EL8C3KEQxZf;{M_5x6;b#d8O#Awze9$ZvJ!H;_9yZ+@Knu-NqDRaHk!{i&LXVoY zqD98v2wH3gij3JPT4I6;&{C5jT4r{L9y8?@Xt~)ddcwrr2(2)aL{FLnqNhx) z#?aFySG3X`hRo@BwY3RVeb(IB1bvk`CR%NpG=;~_w-yL(+8`V-^V=YtmvC0XTP8V-u%;!#@-V_7b4J2| z5JFm8gm=u+wg^#42$v)rHr?AHY?rXU9m4zOf`rk@2!q=r95HL#BgD2si0Odvkr~(l zVZVf(5{{W*M}!$E2-zJGJ~2Ba#J5JM-U;Ej$?Ak~RKg(%pPRVO2=m$?%@c0o8T;kbmqn)+Q4mb67!&=uit=9q+T?GV~@^Y^gM_)I}Je?N0x%2_Gj z_)M$rC~MlIEboqT)@Qz!GN1!WS}Mx-KC>(pC8{IJB`N28Cbb93b}8%GQLVrG%ta}q zJE08jiSo10Jl7K?wlhjh8pX=615JZ|2{Qa9+Y$2@Os15QH^q^YS4G3FeH10Xit84Mn)oEFFpvH4x#FgeInY z2Euj;>oX9VnF|s|4?-9`455WtI}9QAR)m=02q80YIKqAjJ0&EW;0S~nw;^PYKu9q= zB*YI!sIKEo8NQ8N}Bg`I&(B2%7kTe7#VH83~lRFCGw1ndlI-B~V z5ta-^STGu)t2rj2TLwa#EQIc6eip)c31=nrFv(*O)(k^fJ_aGpoRKhKI6~T3gx+T9 zScIq%2$v+Jo9^Qfwo6z)4&i2VLBi-vgu&Seea+fzgxHY?G2;>Xn}Or`*e_wHgn=eF z0b#}{gzO0jx0)Ri;zuJ?pNKHnWKBdkD&dfXAtr7T!n`bm*^>}5%mE2WV-ON1BMdjW zlMzl!I4&X6)Xza!G8SP$4#FsNOhUJD2yLbyWSRL>5Y9_DD`Bijo{F$08)5lWgluz0 z!hrDzY10rUn5EMYq9!0*k}%11pN_Cy!ush5Ip%_d(Gw8{&p?=J*3LkPorDmRi!j{` z%thEQVW)P$@KV=_YaOoThk4hiu&2-WXEm~FD|KsYMlkc2!FHw$6j6olEc5biVw zBqU8mNSKXqx5=H2a9YA~33E;TIS5OpAuO1KaGyCQq1$wXHhBp1&HOxs^AgTVxZfn_ zBdnQ$usk1OfjJ{#KrTYsod^$^rFSAk%|y5)VWH`M7s7T4>+eFa%>@ag??4!QH^L&b z_HKmOSqL%rAQ&_79)$f8c1l=kf^!jO%tpwbi}0A)At8PaLiKwQmYb}55speYBw>Y# zyANSr9>VPV5S}szBqZe{B+NrtX>#WwoR)B0!n3CSe1s);A}pAXu-Y7x(CsdSHU$W4 z&HMs{^AgTVc-|!6kFe%$gyr`mtT$&Q47dj&?E!=r&C&-DqUIu8lCaTqUx2V(!ukaW zo6Q9Yqwhr+{2;;>v-UxR*!vJ-9zxh=20nzaU&2laJ52Clgc35O)?HgS(2%qu{c{RqNdb3j7U{Rjy*!akF0Bb=6ST*4cs{-X#>9za;| zD8ifOn1pT%5ZWw4IAG>4LO3tstc15r@?wNF4)L zB;l~>z64>rg!M}h-ZvK{j9!Q^cqzgWvvw&$>>~&<%Md;?1D7G}m#|a9F%x_YVTO&6 z{TRY0W`~6MM-i$&j&R&$J&tfx!XXKto4Dl&^A;h@UXF0m9FUN-7$M;agi|K>353%U zj!XEfslNhYi9uMf0^x7wn1pUi5ZXM+efo^w6g!T-jgV^*U(VEIj}~3^AtkDT7+UIcdhzH!f^=!Q~x>j&C>`Aop7Bf^Ze2-zDEs+t`V;-5pP zz6qha$=ZZ)RKg(%*O|D@2=kstn7tXHmN_6HX&pksO9Q?2q|WVg!nB8)psJaF^H=FLSBaGgOu>N&~zUG32*j)&N-$3YZ z*1o~VehD#uK^SNT{sm#iZiJl@ZZ*L-5#skCWWR|p*zAySR6_Or2t!QPeuQ~@5e`Yn zFmVSEl3qiYeE?y&IUwP*goHwbOp{xPuw);?aS5YL{kIUhz0R@w!MFUWoJEdFIR82x zZ4UZPAe%pTls0?6^Z(7>6JOl4e%l`vxWw1iDw_76_;1`c<86QI5>8Cb4~eN(lfR~J zYk$ITEQ!&P|GCKK2Ilr-ypK~BKODueO+#Lqn~Y=rwvN{Bm_MpgS|j&AQma3(ME)Nm z4>j6G!q5CxYzq_q#NR4#6ThO3*+xR%1E_>C-v3KuSL7sF+i+#$WoQ;+!Nt z=PUR={TFy!P{Chjk*|um*=)G%FK%s~V-nB$9|?r-^osvn=_^J4!|D7-vlI1S{#DzK z&E2-^JO8~U1MjWzdW)*$|J}dc(&xASZDiPETJ*dTa`yRN&oa@uR);*_<=@;(qJCMS zSMIKhYbz`;I?mP7^UrmzN4PkwGTj4Mo|D11vkMXp22>K3!z9D#0PF!8Qw518R@HG7l zM86qwzPMY`6T5ki`l2PjW_Paco~A#?40R=6s;8C3ZtiLN`H1RT4(OK#x_Wtj`uWG- z=^n*Lem0_SndZH(KI78g$yJ96;6pUYH=!w?ia=jWRaf5PY5E0Yb1!W_FKuPCBs8_Y zzvmZAxRK{K(DRE!^S+av=NsgSRS++#dR(`9T2;aydD?BBre9&`tL?f5dz!vws;|Fm zXm0nk8ie(odVPm}h^Jjgc&eujMbpnGd^HiLdEzk7u@>6j=?;zIa8IjE_#>c8lUTiV zJ?JEXE7Q~J5N5UNzgX=X>1lNde+P7p^0az{zt{L{yhbBZ2j{2X`h!#C=Q=7R9_Y)u zy2jzhKmAAdeaAg*yq94Ew1Y~(HNn%^koYmX`;Fp>URpLJ=Z{(%>b%LGV}d7YtaCg~ z->X$+G_F%TP2a73)+>0br!_{?9MGlT7O4(Rz-OQ~nC@v!3HJo*HT~oWzdZeiV|>2= zbx|&&dO( zlwVt*{8WMaBR}nGhtXY4=X$_%Y)|+O!pdZUr*$B#)+_EoPy0bl<7za;%*nGB=jjI^ zPlJ`<8SpGv1+-hP0c(MF$>+g3upVfK)Q-3TYy_LYX7Cc&0<opFqVpPH{w5CWFEZRO00N>F8Lbl055~Jpov*4Q_76SxI0Bz21;IkRL1h#;!U>n#Dv{4TQBS0p&3ET{B0a^!|_nJ|fL7K*zHMM{i z;tfCxTwB{w&-}%YZ%C{=# zeLzq@RQ(WW^M4cU2M54g;2_ZNLrZ`lCa=f4&#o2lBz4K)SsqIqRqjs{|K)-AJ6dVJ(E$SyF`l@1CP!5y_*MbV5A~2ukTcz{#nF3qD zSL*bq!(MlA0L_3UU@_2d-sgjPARCMa6Eq332xNd^;ByLJ2L^%xK))&J1!|Mw^`H)@ z3+jOzKs=}q8i0nN5ztovw*cKkcYv3{Uho?D3wRUk2M53->Q?DQRYc zye}#21UL!4055_KU=dD>!D(;?d<{;4%@nTR4pxFIgKI$rpr7>}Bg3WmECY{$$H8*& z1Xux{1W$pd!AkIqj*8C`SOr#tHDE1x4m=Omf%V`8@FLg%HiAuHGce$OpqutX;9;Qu z`om-}6-)!;&~!+f1ag252d8M@ckuVRAN)Yz9MJCzbz0DmSetmW8@i7bNT>Ll)st{^7 z6SM=JL09lS&;hIy$On;dgeMo#F5;`7bLt0?U&8Nzhk$<5S^ypZ`T^`Ol$G}x&OJd7 z@C(oh@f^^BO+QrJO)K|-L*P9UL<&?~1o|Ok7V)P^qkM|y@e}sn!8gQz3zTLooDDes zhI~5jMx1`e@hoF9C;kVgCN(Ct85mA0%-pd;u2+JUy9B+xBHw~G)^o-NJU2RIWdsqQEYjpL#y zU9xqz(OpMA%0hRX_CQaCQ9zpFWJj{sFetrPahhM6$dM`)tqyK^j-wLjQUcxbBK37u z_fwNA73g-@4Ri(aS36aBq+n@%K%~IlgjHx!ALW>irt+QZW{jIWD1$%h%t$6m5Gl+} z?N+>~raT6N+d$-gqKZfS#kXSj(DWDt6scCLm3crJI1cbYvvGlSZQkt!BaI$ScsLjd zGQkKi3d{jB!B{W`WPyobJje#)zyvS@OafEE6fha&fN5Yl$OVcIfLY)U&8^u4^1)p| z4tIh`Lp5qe8>$hy2dJUR@B?5TxDVV5%)*DPYt54nS;d`?yx%0=4+_A1@HBV|JPB5S zC%|&>ICu;!153dYV8CLq2s{dG@CaB49tIDA2f+fM3h65Dcb`lAu=CdF68tOp8THi8Y{1)v1$fOOg7m%vuA1-uM)g4ciw`wMsjyb1P$1K=(2A@~3s1c$);;5~2{ zs0pEh}!1q8s_7nI! zI0t^zls`}4AK(}8PjCTT1it|d!DXOUYiML^2-MZ;=9xgM55CcksQXpDKLEU|$KuMrPdJd=vbmp%BRH$@mDnK03^`N1y*K>iM59Akl2+=cz zo-OnMqRQ%FL=Pouq~@BQJ*w;1lzKX;1@wec8$=#Xl<^o4DNs+-s$3JGhm%Mp^surA zKNX^hr3aXz6;h=%>l=d`LD2^0=?O>9aymicM8ayh3Q)@%%FZlK0LBB<+bEn?q8cp@{bAZ;g@|g_gfJtB?xDBYF@$l^+ z3ycP%z(|k@@`mw|0fquSS`Pto9s!1fwPZd9*2Xdx9tX04(kX5NthFAoRZ&$+l_^eG z2k04IxE1y^!c)N%FkR!X2a-F0YC0R91?~iQfj7Zlz#CvEcmcc)_JP;HUa$x32D`wk z;1%#P*a5bKZD2Fl1U7;VU_Dp|p40fRA+Q>#yH|mU)KcC47+4CH01d%IATk8=2y1oR z2j2_kf=IfD2|oxHfCqrm=Hn~>0xv9E`Q=l-lQ80>h{Z(M;1RG0JPM4*(u-C^g(;6^ zunNm1tcEC`XW?hS6X0>6N-hU0!PDR=khg-5CqV%T~TbsVg;B|1Oh1($(Fnl=65TXu`>FKZsOLg+|JA8m)D!0@cMTRAZz> z+6%O89t3ZJLeLy&yA^A-9Dv^eEkO$qiI=YQ5&L7p?}4H|hq3#CJnceSqss6Yd=z{L zjsPX-59FwQ`~&blkZ*gq9cT-}pbcmZQa~$^43a>kvL6w?wTP{9iOX{`Q(?-yXlo+{ z70uxHJreQJa@Dxzlc`>S=tYQnK`%x0azrml^w+HRpbcmZ^y0OeUInPDx?pHatqOMr zoj@G$0o{gw!{=ApA*)~ifp;V2!95S1BJDFp2#Cm zbo*2#zCybQE`aaBH{fe<2Al>bfC^Ee--7SJS?~+^5&Qrq0OflQ);9ik@H6-c`~#c^ z{{+tdU)1q3;w7NWR2gL$OE2g}VmY9RSQfT`AE;81)~IqSI0`+Iwj|*aK#fw*snx}> zi-Uk(1;r3Jj2QH|6dVmo1HH+-223HoGORb4k++$Z2p1jC8?mba4X1`tLskXW5Jl#Y zdQE4^$hmQ>wy-e>ifD{;G6;bdKp7>#jX-Ts6I=&s0A*MmRyt{7U9||;0oQ|upgy<( z)CEdY4~_>7z)bAMdY_aCR9MkW)tcXH5nBZ{C%$Qsv`q*{S}8v*uV$VWsi^dbUqs83 zsFp=q)snCZh+IWmE#IUf1<6O63Q`ZHfL307qyY7ZeAGxiloYLGRjvQ9mq3}dCEN~l z07`_zzr;)HSj4{v;qE})+zoUEsh(Z5+tbndfHa^A^n_iF@z<-pNQ5F4?(I3sE*h^^ zMkd){!nXm<^e@1zgte^>f(L;9Ko2KR>2 zi+EWBN|;xlwPLGfm2lqt<^Qzyu8(TA^9L>e?Tr(Y8z(mR-BH5)fQR*X3BEaLjjYzG zq+6eZm6z|{_{5}?;(okZBsO8Pl{62mBCk<+RG>o5f9^A+)+1Zqy6SO9NwWu!mXF|3 z36BRqes#g#JHDHK)ng4Yam17@)#2|}jbojz#_TO=u31fA*NZl{t)`Tg(Qf~&oLJ$_ zY4_dVlo~frY@&K}i8c@8(eh?IV#v$x5qe=$QkyDxv`lQQF&Ih=|MIKXK6(9~VC@~m zgh)%wm}v7EX|1Wz=KN}_sWm6sG+Kil(WjL%L$Ry<`y86Zm`B%;(o11Ztg#x^DR~XE zlLF(*xRtJ2yV#2KC!7D3f~j%~N=_Iw2WCI`^mEApzKZM z%%06wY@b6+VojKHwKvZip7G39%fAOj&HWIr3O~7{MDmQMSARnp3=e(N^c*pzh&lb% z@OB@4|8SeDF~62GFKi+A66MVgo8YqL&0k)ivlGgjceh!wrp6|#iWMqvy1i(1veL?% z2VbQNXHmkOY8qqcRl`hxry!*xXA9MzyMPdsM7UfOP4OXl> zUn`VsuEC);`5PiryHarm)0x{|Y|CrSX=2qw)i%N@6-*zv>i?}0kuK%OAm40c0TpR; zBek_B9a1OKq6MtI(p0!g#k%G0y#D5cSKI0hOtr2|Y|Z}ly=T6W(eA-nS3CLEO6FB{ z?!R_+q?51oYjmV6FGp)@CG+uS8st{2scH9;)#!>7U*~Q2GMSdEY#x3IzOJ$9l`8L4J!fbGAlCG|oz`Vh(PlvbR=Q%&d!$SF8WkDIe+GE2HrLPsCU8<-D_~p!1MNFKP2{gvdVls*&JIY zhJ63NO}{Vg-?t#MhWV8FS#1WTsEyy8UbgJ!8~62e+Nh1tS5U*0eFcvv@X!wT<%X_H zHt)Tslh+G)tgc}?y<#=}?@ zD8-sq(_F;UI$g^ozRIXvsO2v3&$d2aX829dK1qsZ?x+N6yEl!|ErzVRvC-K@#86+= zxJ+#`>s9jDUE6GctG$b-?#L+xPk-99)&+i7PpK-@ceb{y1t zF5VrDZ%WsUD|J_0-EL`-+Vo?*Y4@E~p%v9{kyPKk_tbr3)y+3l9a9{yP-0V!$a-o= zx8#4-XJ)aqzDu`7VwyI-xxV?9vj5F!7F{u0*^_j)*s~$OLycQHzfT@bXd^f7e?Q-T zw_>U#ROV5RJZ2;v|E|hQ6{E?cF=Io|cWJQg)!kM#zx7=M^YI=!_F@Bb&0cPT+Z&qO z_EN#u8oD>whDTOTK0J2QZ7SZ~72j)Q9>$~PSB=~yeN*_|hSjQQb|nRzv)Y=ZerY3n99f9K6_++o>}Z6(TBS3Q!5(QX`5xzm{^UYPdg)tFuh zrqOHEY#1I|Oj$cxuW4Q)`>U%SQxeQjJX+54a({a5oH5@;)qU@($75dZ&v(fhJh|-+ zk6(@1m|$K|+WmN_?hVSvH}l_r>Zz+9pL%J>4So8z4;Suee>LXE1oNwERwB`@+20mV z`SGc?|9Iu9M{J^LypP-);-TE_ly9#evtZf0s~&B=w0r-$aLu)qyZ&-DCOy&2A#Jrx zJgy~eRJm}&?fyp(UiFxfXg2M$Iz`8}bpE28|7gp_uT$72OdT&z&iiD_#$JU{3}N#` z=WOv7F=dI#oO5RS7}%P#Y5++HoYe1R-gM?YiH(Y{x#O0x7n=n zTQ%u%OO-o6U*}B>=2VT4Y4-v9^2XP#kd+uRU%$?LifnMnB-2^qv88o(rP-^0A{mQC zMRpFEac^+e9)JfY#{9Vb3xmh!Zg?IKrmEKTsF3;Y4d&?dkg5F__^y!Y_MvrSfW-HQ zOo61uA+t;TOvwBQS3BSpH~vEM`Lr=NRVB0Li7jU->D!}8blnCUn9<8;n`~k~9hldRH zOvIx`irX^-cYhan{Ij$1l%d^@I!#G2CzRJ+DdwW`eyNQ~Jb=xKzS{xp1#QhN*-P4* zC;n=kB&Qt`6_{YVNSvPGg+WuhHIIK49fr``erRLI%(6tBU4Sp;g^_ zzrC^EVw|!&y2-Qa*BJKPqr-Yze(jAk*!M&ybI)6Jer#uV`&{y9yVpP1K7ji&eZq|; zGS;!?>$j}(b=Klmg|2Jc#dXhkt=1b|m&E;ydvo^=LWVUDQhkrjO$V)BR==+1>4Q|R zemC<2k$-ga(6bT!$4#ToA**T2BX!-qY}lkaZQ7M8lSBbJ;Zf!PdK;Oeo^8(aX2?8n zh}^rUx|O`A?FUs;-`(HUOH1yWB@;5!r!+2PAdzt6nW_0M@&t%s~G3Vd5mRT2SnMdDobxqM~?R&c`zW4gW)6aes z*Mgk1BB(cO=GS+u2GOnixXW~PAJh0F)ux~`=d|4TDk0M%n#}2anf3WerDYfb!FuUGoMLfiaE1&yEJi77Kj}LtA%@O+4-4bK9a}D}{6jKJ82jOZj z4|Jz|-=W|BcJbj1D(D=Vn(*FmkooijnlOEkd;2dwpy@4#N<8-l9y);GabS=sdxX5) zgV^tD%X(nz)*$@Lw(lG^y>6;D<~DaQ7H-?OFRONwc~`sVzT3*QF8GBb0=WsRT0>$qF;?|8B7n)7A-kv@KuTIsft z^J?GcU$DxYqdndt-!a&|YwkbOeBIOypM6VANG)yRduy=i`O$xCc#}isrH`ymbt0wy z$E~H>(f>GA=!-r_-8SBH)M{Ym_cI%gS~aZ$L(IBgtvK_|QJ$Yq4>2cxrHa=KH8qcM z3$8rW`C3wb#|$(67`p2mTWwH=^To6LrmJ4PH-GY&W?my3^XJzLQ?^pENDi^q;tcbH zQm)D{eSV{Y8%cQ$9rw-IuTORU=fIAn^bTUXGfep7B4t(_W*+#Mlr@K$Z%Jt-3^RLV zw;HynX0h06orbxyv@oq=?|PvhbqDsIb8i`DuK5JVA;T7p$8j`{x~1J+*fzb#%hnMb zwZn5qoIK2Y_c@NUhM6Z}^WZ1e*Da$nUH3Mn%=-;09NdY!_FwHN#Ar8a)xJZ&Z(n>o za)xAgt(j?F_>{g)$TVNT)snnCHoUd&;|8DeCf6+z&kmWU@o~0?G(7ZxwEfGi%O0wI z2z~*^REo49_%kjlaqjV5Ke_swPNP9D4bK;|)GG?1j7m1TA*cm0J#udEt9{WtrEf6msS z#q;Ps#{77Kq5c?;oAKa1WxJEs?a|xEy3;B$iE_u8I{spvuIQJ?nX+FnV8LuNv{o2P2JWrE9|WX+w)_v+fH{B7qgx}a`4(+m7Khs zJ@LJ4GqEI7_Y@v_;E6rCX7^XSpTExYpnm7GP5zhUS$w?dS&HRWcD#A%OU?$>#=9+Q zGwR(Dv+f&HN44Nh0n;~&7}fl~z$5XWqMT!Fn#_?Y2Fd?IvTa0t?S$`{jFXZWnX&pYIg47Y~+=yxrt(1Zk_B_pzr(N)|ok> z>_Ad*f>me#!SgcXrx=>oE{f)PdFXYYhSa?L74PZ){aARf7M(1<$}#zut=OvPa@={+ z=D?UaD?a|=GR1qdv8XpMA_x49alf2n{!ek&9uQ@f#fR@hR0KifG3qDc11v&>5lGw& zOUyD#Ep4@2L<6+MHzpwA_$Up{C2BpQ38tb6h=7kVMHEFNBP6X|->+OjL&Qwi2jc$j z-22Vo#8}%u>p$Z+_q^^o=bn2W-{{h6?ASh@HsD!#e-^iH+U@K$yE@DBfvrOV9;nkC zm_@%d7c2S%I?He~0KsyW|3v)#df=t*6U`8XSV0FsUlsG~n)ItT z8_X-m6Ugrh&f`Nspy<+fz0|tqa?so5W{6&i6a|Qo*AmfE0z^Yqf^V72=PVuw{?oC7 z5?Vj#;uPJ|GY+hp5i6f3Qa-~i1q6Icxei*EJ0it`t#pl}XrMJzv5lIwq71UDz!Ke>EF>c?}6~QbE71TFi1^pI%gQi!` z-P*zICKfA)lPHDZ)&k;5KpZ&XR_b_;T-3bfvY6YhjjqS&!fdrHwlo`@b& z+BdGg;-pn=5~?X%NCow$7(y22AArk2qo_Wex3A5e>y8Jq)?oPk8s z*fX%f&(*H4e~9To2`%DMV7hvY$$#G~g{~fRibXi2rT0x*WWO#{D+XRfYNehl=&Xdq zQMZDe8>C3dBZ{UqNIFv`p%$_WGPr*h%sdtJ)XKNEz`I!LiTl8!6z-7;NGL}C%~+lz zoh$1;cvS96R(G;I8cSRTUyAaPJDMtIFe-Qet<(SwV-)>F-JakloJJyn!L?+ZU5Hw{ zwOVVZQ(z-XaTLrE$Xg}#9S)wy>Xd=w_zkABo1>Z3|3oYBXPqSkOR%a0Msvk^h`+I8 z0txro{Mq7s;luxTaqdRfFGwCe%vB=q@O4>rtSo)33rmSYkv!pEwyYa@eW|5X8`;m~ z0zw(eUMQ56hm%q(zbRhW?k=0X;Q~WR_ox#MrZ8FdP(A7KQWz_b=6#WqG5H5CWwjw2 zk-ae~o*z1L=qn$8T7u1>j78}d&}@;)R{D5kQobx}LxMATg5tFApV7mMK5PSA;l$RR zbx0;P09Tm|T$b|JKTwKwTcZ7eD~i6QnG|3P>e+x$0C8pXv$KB~b}PyZQHm8d7us($3qUeUclST7e$g$GwEV{}tQnSd@4no9_ z<`vn`lgn0(sviC=FqnGqqRKx&i*+dM{axq2_|ogiSiu~Eo#i!I6lW*vrJY$+XD9nI zt9NWE_p?^BgO;e-S=pP~65N$3RDv4bq6~Ix0r%W7gCbi)_kXzVrLv2XTT8eld}J-v zJ7QBC*YeD)HvWmE&?VllKy4{R_M2W_(@h&`~@513LH8_Q@oZ=x5B14f8Q2| z7!8_^I7wYDNG+t5>nM-Ob$uOIORJ9twmi3PZGS-lwC}B>^91FzuX!d zkl;pT0wVU%SK!^#EXp_)0ZPB3!&!v5B zabPlXIlK)lLvMUPYlwe+k?#iR*ogl1u zZ-JIMK_}NXQx5ymC!fv&J)|g~JHuVaZrj}ktf`l5Hi&P2H$W)X14PjligJeLEyDrg zK)y4!^&{qm%n7iWhNq8vr34+Cl;CWZxcVw&49$Kw6(WVp5gT4m6v6 zO~hD^V>(d*2vOUDwHL6VK_=SNb5`nNYEIpx+^xl9Rl0{Wr@R?LdRW4w@ppb0a<1?+08a-rp zdbPdmD(##^(d}g~X;cwaY5^QuL~GhZ#CyKnsGszH^JAaw{X@;~o_5;GGl-F2-OI^q zo1+I~)}ab~TBF-ab-MvyI9-%Q{-@DtA#{+r=p%ZJ8cBcfdk zvcn*gRPq(Q+d&Rz4p4w+`xd*n!PRvkyUB~j?IN#^2smOJrUb^)QJig_?T_*uf7A^K z)lpqX`2|Z=(HouQDASi)cGJF2a6X%2?jy6`jE)IA<&6KWL~WUTj}_L^-c{1~oenmB zFGx0`F0|t&VJdE4jc|6&<{s9WUom z$CBLKT-F0Zq#nT~6vez6UY-Or@ zgIPwteWm1y$(K}FN`1ZLSiG~IzurFKO!1CQC!6(J+{03Gj&b$t4CgODQbDAuE!Fo*KSLEf<4TQ+;c!y48XQkAR5 zEbe7m`n8+v_pF++H^y-n-FDaWWSTy6r%_b&OhTJ)QR2nLzM3NkDY83Us`)7ngJ?cM zW-DA=bCgg^afJVi(D5?hx!-v{hT7CvE{}}C+-=j&{V2R`I-NgUZ$gR4q z&<0g_%YG)`wVsqWiBTA$T2zvvaJOUCDOwqsK5)@HU-J^^+w+~udX*o=X);C$l&=ou zbkqm74JaqCp4g&V@cy@(gDY$j z+BQy@9@lF^%oI&fSyn*1nvnFRt9f3S?|iLxaH4+hay+wYYh|9F-RRl-qRJ*4w9-F+?P%oscJ8n4XEAO;%nlNMB zw26>vM$GhCF*D;0d;H`vwg%Tea(WxXfzB&YQ!n`*f|Ndx3T2DfTC*PfzuvMc%P { + const client_id = process.env.VITE_GITHUB_CLIENT_ID!; + const client_secret = process.env.GITHUB_CLIENT_SECRET!; + const code = event.body ? JSON.parse(event.body).code : null; + + if (!code) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'Code is required' }) + }; + } + + try { + const response = await fetch('https://github.com/login/oauth/access_token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: JSON.stringify({ + client_id, + client_secret, + code + }) + }); + + const data: any = await response.json(); + + if (data.error) { + return { + statusCode: 400, + body: JSON.stringify({ error: data.error }) + }; + } + + return { + statusCode: 200, + body: JSON.stringify(data) + }; + } catch (error) { + return { + statusCode: 500, + body: JSON.stringify({ error: 'Internal Server Error' }) + }; + } +}; + +export { handler }; diff --git a/package.json b/package.json index 403701f..79ce158 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,12 @@ "@mui/icons-material": "5.14.12", "@mui/joy": "5.0.0-beta.9", "@mui/material": "5.14.12", + "@netlify/functions": "2.7.0", "axios": "1.5.1", "graphql": "16.8.1", "graphql-request": "7.0.1", "immer": "10.0.3", + "node-fetch": "3.3.2", "react": "18.2.0", "react-dom": "18.2.0", "react-router-dom": "6.23.1", diff --git a/src/App.tsx b/src/App.tsx index ea33c9d..e622a3f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,34 @@ import { CssVarsProvider } from "@mui/joy/styles"; -import { Box, CssBaseline } from "@mui/joy"; +import { Box, Button, CssBaseline } from "@mui/joy"; import { PullRequestView } from "./pr"; +import { OpenInNew } from "@mui/icons-material"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { AuthCallback } from "./AuthCallback"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + }, + { + path: "/login", + element: ( + + ), + }, + { + path: "/callback", + element: , + }, +]); export function App() { return ( @@ -21,7 +49,7 @@ export function App() { overflowY: "hidden", }} > - + diff --git a/src/AuthCallback.tsx b/src/AuthCallback.tsx new file mode 100644 index 0000000..8bd5353 --- /dev/null +++ b/src/AuthCallback.tsx @@ -0,0 +1,52 @@ +import { Box, Typography } from "@mui/joy"; +import { useEffect, useState } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { storeToken } from "./utils"; + +export const AuthCallback = () => { + const navigate = useNavigate(); + const [urlSearchParams] = useSearchParams(); + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState([]); + + useEffect(() => { + if (urlSearchParams.get("code") != null) { + setLoading(true); + fetch("/.netlify/functions/exchange-token", { + method: "POST", + body: JSON.stringify({ code: urlSearchParams.get("code") }), + }) + .then((response) => response.json()) + .then((data) => { + setLoading(false); + storeToken(data) + navigate("/"); + }) + .catch((error) => { + setLoading(false); + setErrors([error.message]) + }); + } else { + setErrors(["No code found in URL."]) + } + + }, [urlSearchParams]); + + return ( + + + {loading && errors.length === 0 ? "Loading..." : null} + {!loading && errors.length !== 0 ? errors.map(error => {error}) : null} + + + ); +}; diff --git a/src/api/api.ts b/src/api/api.ts index f1e33dc..8b93e34 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -1,5 +1,11 @@ import { GithubGraphqlClient } from "./GithubGraphqlClient.ts"; +import { getAccessToken } from "../utils"; -const AUTH_TOKEN = import.meta.env.VITE_AUTH_TOKEN; +let api: GithubGraphqlClient; -export const api = new GithubGraphqlClient(AUTH_TOKEN); +export async function getApi() { + if (!api) { + api = new GithubGraphqlClient(await getAccessToken()); + } + return api; +} diff --git a/src/api/fetchAllOpenPrs.ts b/src/api/fetchAllOpenPrs.ts index e32633a..9807109 100644 --- a/src/api/fetchAllOpenPrs.ts +++ b/src/api/fetchAllOpenPrs.ts @@ -1,6 +1,6 @@ +import { getApi } from "./api.ts"; import { mapResult } from "./mapResult.ts"; import { PullRequest } from "./types.ts"; -import { api } from "./api.ts"; export async function fetchAllOpenPrs( usernames: string[], @@ -11,6 +11,7 @@ export async function fetchAllOpenPrs( const openPRs: PullRequest[] = []; while (hasNextPage) { + const api = await getApi(); const response = await api.getPullRequests(usernames, "open", 50, after); const mappedResult = mapResult(response, baseUsername); diff --git a/src/utils/authHelper.ts b/src/utils/authHelper.ts new file mode 100644 index 0000000..1d15502 --- /dev/null +++ b/src/utils/authHelper.ts @@ -0,0 +1,37 @@ +interface TokenResponse { + access_token: string; + refresh_token: string; + expires_in: number; + refresh_token_expires_in: number; +} + +export function storeToken({ + access_token, + refresh_token, + expires_in, + refresh_token_expires_in +}: TokenResponse) { + localStorage.setItem('github_access_token', access_token); + localStorage.setItem('github_refresh_token', refresh_token); + localStorage.setItem('github_token_expiry', (Date.now() + expires_in * 1000).toString()); + localStorage.setItem('github_refresh_token_expiry', (Date.now() + refresh_token_expires_in * 1000).toString()); +} + +// async function refreshToken() { +// throw new Error('Not implemented yet'); +// } + +export async function getAccessToken() { + const token = localStorage.getItem('github_access_token'); + // TODO: Implement refresh token logic + // const expiry = localStorage.getItem('github_token_expiry'); + // if (!token || !expiry) { + // throw new Error('No token found'); + // } + + // if (parseInt(expiry) < Date.now()) { + // await refreshToken(); + // } + + return token ?? ''; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index fd871d4..605b24a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1 +1,2 @@ export * from "./timeAgo.ts"; +export * from "./authHelper.ts"; From ca853ea416a2c50386f5649cfa961b1d191a724e Mon Sep 17 00:00:00 2001 From: Nicklas Knell Date: Tue, 11 Jun 2024 20:31:16 +0200 Subject: [PATCH 2/2] feat: refreshAccessToken function --- netlify/functions/refresh-token.ts | 51 ++++++++++++++++++++++++++++++ src/utils/authHelper.ts | 32 ++++++++++++------- 2 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 netlify/functions/refresh-token.ts diff --git a/netlify/functions/refresh-token.ts b/netlify/functions/refresh-token.ts new file mode 100644 index 0000000..e2059ba --- /dev/null +++ b/netlify/functions/refresh-token.ts @@ -0,0 +1,51 @@ +import fetch from 'node-fetch'; +import { Handler, HandlerEvent } from '@netlify/functions'; + +const handler: Handler = async (event: HandlerEvent) => { + const client_id = process.env.VITE_GITHUB_CLIENT_ID!; + const client_secret = process.env.GITHUB_CLIENT_SECRET!; + const refresh_token = event.body ? JSON.parse(event.body).refreshToken : null; + + if (!refresh_token) { + return { + statusCode: 400, + body: JSON.stringify({ error: 'refresh_token is required' }) + }; + } + + try { + const response = await fetch('https://github.com/login/oauth/access_token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: JSON.stringify({ + client_id, + client_secret, + refresh_token + }) + }); + + const data: any = await response.json(); + + if (data.error) { + return { + statusCode: 400, + body: JSON.stringify({ error: data.error }) + }; + } + + return { + statusCode: 200, + body: JSON.stringify(data) + }; + } catch (error) { + return { + statusCode: 500, + body: JSON.stringify({ error: 'Internal Server Error' }) + }; + } +}; + +export { handler }; diff --git a/src/utils/authHelper.ts b/src/utils/authHelper.ts index 1d15502..fa0fa0a 100644 --- a/src/utils/authHelper.ts +++ b/src/utils/authHelper.ts @@ -17,21 +17,31 @@ export function storeToken({ localStorage.setItem('github_refresh_token_expiry', (Date.now() + refresh_token_expires_in * 1000).toString()); } -// async function refreshToken() { -// throw new Error('Not implemented yet'); -// } +async function refreshAccessToken() { + const refreshToken = localStorage.getItem('github_refresh_token'); + if (!refreshToken) { + throw new Error('No refresh token found'); + } + fetch("/.netlify/functions/refresh-token", { + method: "POST", + body: JSON.stringify({ refreshToken}), + }) + .then((response) => response.json()) + .then((data) => { + storeToken(data) + }) +} export async function getAccessToken() { const token = localStorage.getItem('github_access_token'); - // TODO: Implement refresh token logic - // const expiry = localStorage.getItem('github_token_expiry'); - // if (!token || !expiry) { - // throw new Error('No token found'); - // } + const expiry = localStorage.getItem('github_token_expiry'); + if (!token || !expiry) { + throw new Error('No token found'); + } - // if (parseInt(expiry) < Date.now()) { - // await refreshToken(); - // } + if (parseInt(expiry) < Date.now()) { + await refreshAccessToken(); + } return token ?? ''; }