From e283fe0654ee4b4d0f9ebf69caa52a57dae7dc88 Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Sat, 6 Apr 2024 17:27:17 +0200 Subject: [PATCH] feat: move away from clerk --- bun.lockb | Bin 196305 -> 245675 bytes next.config.mjs | 7 +- package.json | 13 ++- .../20240406132011_add_lucia/migration.sql | 18 ++++ .../migration.sql | 14 +++ prisma/schema.prisma | 15 +++ src/app/api/verifyAuth/route.ts | 8 ++ src/app/auth/signIn/page.tsx | 101 ++++++++++++++++++ src/app/auth/signUp/page.tsx | 90 ++++++++++++++++ src/app/history/page.tsx | 5 +- src/app/layout.tsx | 11 +- src/app/page.tsx | 13 ++- src/app/remove/page.tsx | 5 +- src/components/app/Navbar/Navbar.tsx | 33 +++++- .../{Desktop/Desktop.tsx => Points.tsx} | 12 ++- src/components/app/Protected/Protected.tsx | 14 +++ .../app/RemovePoints/RemovePoints.tsx | 12 ++- src/components/ui/button.tsx | 1 + src/lib/auth/index.ts | 62 +++++++++++ src/lib/db/index.ts | 11 +- src/middleware.ts | 15 ++- 21 files changed, 422 insertions(+), 38 deletions(-) create mode 100644 prisma/migrations/20240406132011_add_lucia/migration.sql create mode 100644 prisma/migrations/20240406140422_add_username_pw/migration.sql create mode 100644 src/app/api/verifyAuth/route.ts create mode 100644 src/app/auth/signIn/page.tsx create mode 100644 src/app/auth/signUp/page.tsx rename src/components/app/Points/{Desktop/Desktop.tsx => Points.tsx} (88%) create mode 100644 src/components/app/Protected/Protected.tsx create mode 100644 src/lib/auth/index.ts diff --git a/bun.lockb b/bun.lockb index 8b8efbffcb6b4e7d5523d7808f4f8541c7b55717..8ac374424a2ad198f950d5548b925a870713ca23 100755 GIT binary patch delta 68831 zcmeEvcU%-rmu}B61B?g?q9lom7(hiO2snsD1w{-b0~tig**F*oDk#$8NKz5Ogo+VN zV9p?BKtRBRiUAc9D)*f39-TMrx4U=0z4xEDfAl={JXLk7>QwB8(OW-c_IAr<3?JIc z(XhJFr}neFw{+0veXpa^teX`)_S`C0N}4_9%&6TKYLyZMUUS$R+%<*;yRs=p5D~Fq zG2zh!k;o#5J^)idmjR59^^5X}4j=>@3X}wMIkXrvFCZ*9a3NvGa2G&XNE`;XwJpt{3C;?gkVy(j=9&sdmpaB6Ore_TOhjv8y%fKjBNMxszixrX~5^KR4 z8m=PGvXlf~kOu2N3O=xy2%ngF1`7ff27n)+28mMuv093C7W^Mhl8 zDZ|kQ)Zpoccueob*vD~-_Cf=qf&xYn1f|=8PzC-3FD3^1M8`yeg{XkoXsqPA{&WkS z)#(wE3y58Hnn~{;5{KmxPHZwqLW6fpgRU1zo|RA%SU_oLGzumI*0xTIMqO>%XfWtl zo2UREzZfub4s;xbo;tK%4IJYmg0KjJ7&w3#A3-<^2qHQnBsd0I5fNk%voInc+IAp4 zZxUhx!k~8qgeeHvZ$kmG0{t0;48dVBri2O@M7INv2oDb-xWHj3B!)+agkyo(aARxN z4yFg($RV`7(16fDD4#eA94pul5E~j39TMydx)ktU*sooX5nHqXPz%rr5J#pVW3Ue( zxGeFV9zj6ai8X+ju9U$IfS5i85Impg#^6{$%%=_rJ(MT`2wk1H1`5XtI~qcT1rQhp z0W{bX5Dj&Vq+4 zQw|){3GPAw`??4amq`Ukfa~-RC={_75Ixk(na=1nl}`5^5c}BIg`T$UzVm?zqsDL2x#}tB40AVE*fP*X;5C@|cB*0PH0}xyK5p={SGwA|v z0AhR@AQo5vi19o(x*`F9Xmp(%v_NG;d@talh3Efj{lNMEKUzQ_|Nmq#3jR9_NCdPPyT%U?Jt103 zj|I{m+XIO69kyd!>xq2em@Ya#3UUwxe;z#sqyTxKe+>fvqpMRPA@>wm%4y9Bw(-Pk1_Cr!)RDI1w;*OWvH)&cx>S!CcR&H_=4a7 z%DdFG3IZJ~uq>7?I2I7?`b38r&IcP2aj`MMA<=|4=;(2QBNK>&8YzI_0k*K9h^XM` zP#=Q0A5Xh%c>+EELm6}hRDyICkRI~|z_uC&etfiq&VP@=bxY~#7!nW%J|T!zz|p=B zje^7l5Wsow9}o~>5FAZhf{fTle?BVz)X~l-3QinR(E%x- z%YnWG5W6%uC@ee*JQNKaJ%MK~AMgO}@fmQO3J&u(h>aj#ub?xuG8kbH67J^{1GNeX z4uv7ox{{9f14F&RQ1mLgLQ%n?Q9j^3(UZX7B+`>NNT8lXCxTLz-ut!5vnV$3uk3vrxfsTIIxSH0r zf#b^VAMO_u6&yx(^HkVx*O00`1_{osrK5HM;{5FaNwM$R7t;rYTY$JUK2E1sy34@P z)n$O#(p&53DqIA_A_ePdi~_{@Et^4CVGE?ku1o~PgNq`~1&OtbXajYbbb?PLZD7Ji zy07*CM?+02Kl<1bbo9YfNRP9mPKX@V z_?sZ$<`Bn3JlsqdbPhNcoC}DZkphSnkj|qG_yeLI7#?K+2jF@4a_D#sK(x0qpZ4h2 zTsqyPE%dZ{2#6I|0G)?x{B;n}l_vnPW(S!BmVj8`b4Z9SQ3J%$TLXw)bezGROnf>Z zc3~`_GN3mg&SMuqtY~nwK~z9cK*D(7SWa-XPn00aXCW~f1Z?RLKc@Kiwb%wk5j+e`QL9>6}Jzt}*>yF)zk zEhTim?0}p)B2C&Z%L_4I%7Tp8HmR|rwmogJ9 zU_2Cr4H^Wf3fKz}(|v}Dpnh%Z0KAqby@00WrZ z0j7*T91ttG9}MBB9&?mVe;SaQ74WJdG&nkDesp-45sdzWpko&fV$$^i#Bv1w0lu+8 zkRS>SVOM-QLZ9iAR zu|X>;=`k4(h?8Y5AnL2m&>n{O0X`u<{sHm8WkL4?M2Be+bbBC->8JcUm{^~qyAq61 zp8WN2dY<;M=ppsb&!*wNv9Qn+ggK<^aX6q?p@iV6b!)}U6a|Gn$G$rs;1B!pc*NR~ zik72)UN9cc?p(XGWKe$T?wv1(&X1aKJ&}l>yLe3P`le5J>~x<_d9(ZWmKI`SO@H3( z#rKz8NWEL_`LRYt(%v#Ou-bdydZV}f-bcSrQ`1jPk{-WqqhC%*+xT$*W`4%7Ek<@i zo0v<-mIvvy2`+saFSplqPLx54-04+am03*zhm-dgrQbhyf86<&$Bq+LTK5jMXl`4n zX}7g;&vvDgUxEv_%atoA*fw=z>WuS)FAd7=C)>q3E<*&mSKw zxzM`Ms>f;lgYQ2a^_pw%w>kW2&F7?<=j~O}ZIyOh@VD`Pt+)Q$inQAB??+7z#8^kZ z9+9``)6jzvjWK%ZjgvURi?qFlIo`~3XimL1DYk3+@SwKGJG35Jsma(B4c)ysH?XXAx~X@@c%8eZd{S)l1J#y= zpTCSA)xQoOeudAn*mn0jhqd~CVI9^|8YyLUQZ{#p+m%*p(c~Ugx$}s_jX}RA`JX4Z zb5&XMg>3yT>uRPLP0;n<$rAdwvnmTV;nQdN@=l82@x>j{~y zX~|aQko6!1lPX%4tiz-Se7+#FwJh0sT(Vw^iqN)XohLoCE!jGfWR5nUwTi3*=@hA= zW6Aq0Nf2hR67t9vZ4>r*DKbZg&)q0R5H_TGvIVI!z>+;en)DpN=S51>DW%C49TT1r z>@S#4iu7?d;VlAY0ZfuCfVeXZ<04~yC$k4y^5()m^;LcWPz3m zcO0y_6UclWbJl9IPS=ukkyIIE$?FCcOOPV92bu89VX^*I9xI-#8$_kkv*by@@@@m^ z2vS?egl#WN=IHTxNgz5gJHZXLCB&j{Yl&mwbIgp13l2rt#}XvEEgKm69C``*xD^=IflU?+GL?o?jV+K~=uvJ8 zFv>LVJcMA2#zbJ?1F)%-0q7c@HSFWq3v^AF1H+n947xnq(2~~!P83)?oAiO)Ho)kX zwLtC_!06$CmAL^7b5jG3r=lu8G^aC+jdf#fCOt-5@~(nr4Cy(f&tMZC4^BL23!Dq( z*aE|PN#a0Ytt36hSn?V{!?IbV_6QTMe1AwmHV!gpJNGA5jrgnr(!aMu17- zZYf&}ngJw)MT3wbecXqU8IC;Z0lx+=czOvi+N;ohUMa(1075xWfMLU-S-@Ne(#-;= z0NVx(rz!^A#`_3 zKw2)`{K1f65S3viL}44KD3~3T8D0gbIO1VZ0FQhDhFt_p*>V&1*r8;-C0};IPa4${b=D_{fWN3mK>cfJ zeP9s$QSCj5*Av7)R-wj^5QY;=AV07g}VVtOz!D7R274cKohul~R$!La{r zb0;vWiBvwqNSt{9`(45jU{njKlrk`=sg_Y}0Whd|q8!!9aEVmC_fO^DlM~7_O+W)WAGfHm7GCLAFdX zWto$nd_FtfoXi2ZVNTZb`P@E`cnqnoWzL%pq4AItCL8R{qy?E{$>&vr2rXx-C;|OO zO&!X(5iojo`b;(L2?Tza0k5q?PtNTiVZG^Tz^J^Qun0^96>0_B@l0U!s(>{nA&9FF zEah4zve$wA9>OB4LoLO}ut-e-^-pENzy$I4XcZ+jfp-3$MItp1)V~(i^|z`gtPXSj z+%VZPP(A*j)|l~nPWZE!5k!f9t=9!eN)0jsR}E3U)U3s)kw^^#Rpf9unK2IE3#!No zzg1k3MJ@ZSdVwlx!*BHzsDHH}Z~NPizte_*D)QrR^%kgqEp4RT->K_C{c9cCK^6It zwmWq+uc!T=MO^;3fGTq2?<#x+RpiLu>Qp$0QnMD^`%C2&f{MeK-p20$a|Z@v6IQ2T zFihz<+|6bI!z~{6FF3ozLU0PjaBq78%=;I!f$8b`i{${DPct~MIT6I%Ur{T7O{Ex^ zgu1^OPrx8wdjiQMfrlQ73>wCo$|NFsL(BAGc&XsF4Gi57W5_Lf8;Ek3d5P zzzP9Qr5QVFDp_yC=M9AA1|}HJHJFF#z-Y5D|9Q87p;>6_7!#h#G=hL1%nnDRr4You z5^>Z{KXIY4`$sWcEQ zhIVizu-^`rg+WO>AB=F_kdckX=Dhb1!phLT(T8$==UD`dX~bX?_BmHFXCj}i;zrg_ zF-lY=pf&W6Je^x8CFaID>6&dq?3D;0VM zA#W;VHycI_71{wII`$icW>cCgOrJn1bP7Urj{Y#f=+F`f(Ru43=LlwC1`_rYU)!d6M#`uo?_R5;cSEB ztG6jDkW_Wzb87=(Pb1Ywm~(psL7$NMdgkmYL1euXJeY!D49V$-unJ)2RAu2wgx7l> zeWa&P$2>ncWonc(k2cCtwAp~JfHd>nVTp;wT zD(oF0q^Ap?-4a6PxbS((p}!u9CYo?VfYC?zQV5xXWmp2kO#CcJuJw!mOLg2|v6L3+;Mv!_RpIRIB8$a;WY zk)-NOcy@`TmmhGifeE`HlFR|I9YlK2!Lr335=E-I^4S}sNKaQj?^P7t1F#CsfQPDR z`lJKdVO8-3hLxsS4lt(YAg-QCNk78(ixDj(xR`naqumHjW#`0@p6+~hbqtx~&gV(R z(hi4)LC>YdlBynjUJZztfo^S&IMJgqJT`Foz>INKV#mahs-ArI={V98fE!Qdc=EY! z@z}9*T+O)!5Hg~cgr^X)q(WHJQA3$m1tM$)^jVGVxR|V;&F8&XLVJmRDWkiT^qj-zZCNVXGjMNs25cncqXsCi z-!f5#0y7idG={Ti;HH&tX3m=dA$qXEe!^ZSAalI< zyhk8fP>IpqYKinA6K0?#Y^cDXPr;WM_YfGGrCttk<(ET;;=3r`LWSW78G)=fOfbsD$o1K|V<^=M2RVYv; zfjPEJ3hAlE=UJuD^BSfCyn9FihHZdXIpDq141*ok-Gui87*-oz-e|#NZYt?HkI#)u zh24y7)Uc3-0Cp$%Vhp^jTTQA4^LaB?(;W!sCs>5?fzh)E4DcQRqnB57v)&rI>ePEB zw$~ccb3UK90YrVOHZVkZr-6+E2BQJk4`A5Ocr-KNS*@kBQ@zAqxR%UWz~|iu5j_h- z1lHglX(Cs_qQ{#63`?hnaVD_Y!01!m8({d11d|p_k4qPAQ0TSHbkZ}F&+7&e&OEfA z*i+Y0vnOdC-CF1j7;H~~;XV(Jh4MzNr{iD=2ey`B^q{&9Yywptl)&qkA(|I3aM)8b z$eeIKFB?QW>%jUA^GtFB?8y|mTi=A6 z1#AL!$ZCcV4h8z`q>)7zNinWFGCUP>GwG0qIj;jk^q~bhlDl9Nwg2mwbI(J_iF%dO zOGvK^)a>M10-H)U>YDS`Lue9|1g?eQjWSg%4)1ZBMTe&XnBE(JVL~tfW91nzx`N<6 z?!au(yQ4q|*-?YKB%92Mg#%h1Ss%;iM(3f9hvDN8!YKhqH4_tV2QWA+%`oSV$j74! zzJJPx&}_89{R$x}7uRbGIGCPN5W-PPAL%r$KzxZQex)C{ zjVcf7;R_)rx;?OL?gvJ9gbz&355VAPhRaof=qvzz&)o{liPB#|2uCD%W1&|n4`nQ#qvz$oCS5v*_y?q=Q2Jowpdg_ z?9r>hY^cQe&P;YE?Hy{N;d%g@M)k;EDwGdLXb4~}=<#H`i#nMuv5dL`NR_#Jz!x~)`VNcVUPX)3)> zmTsq}$g1RhK#A0X?K_}pzE z(nIJIgs{KC4lJbOOGV2ZEFatiU~p20C!ZP!(E|zxt^PsLu*21Q#X&MBh0m@(NY(?y z93oXy;aGEs^i1XR^bU(=J`TiKVAzS&NtAaS7*k6aDX)Ry#DmcV177zCof4)YFd zZKd~)q9dg08a_MZDCxO|&%1P#?kH;M=Mu+o9IG3db4?*MiW*PzkCC2h`Mez<&?5*3 zW*e|6RI}l&E6=#>*UP3EChU1-q-PqRdkI8eGC#$fH@KXBsD?7;nXvblld9=_?oSXM zsI|oIIGK~q=dC|3nk+bAF9O362!m_132*ZWx<6^w4h-i3%|@N1pVBGD%>y=`da7=K z5c&u9C|LA+pQ76dsi2=G0W*d;dJ9No;^6HsH0=a1BZ!ls&JN#z;l4-tho^s<_A-o5 zcmxK5W1Kd!6Bw?U^e*-km@6=$(&5sZ7b;uf-8)LveVs;MRe=AKcIo zLJDq6xbYH43E)r;mEM{`s1kMk9daRNBHV1a9jR1*Kmlb4RtgFyGYJqegEPYsCE=b4 zH&(zMZmh5e+;}0PJ{t*K;)vPQ;ZoCjNy$?SAVuUZ;cp+j$01~+V4zXfER4k2{ zem>l2X91%lvPkt}B~rgwT7W6S;l`Zsltx{DhnOygi5Ew-91AyM921X-_7WH_j;Jq! z8|^K{Z=>J^0qPP*4k*hRT^!NCN=8S-bSZFS1ybS0>t7+}Uqhurgz_=tTDTF@7)%Gm z>+cZLt)pUTM7|zw98DV;9TD{`xUoPuPf>g`AYO>5=V1FWVJ_Tw{VT-TS4gFz5%cYU z8?lJd5z#R0%G4!}m~I!`h_KC4mjDCU7qBN%5&IZ~jg7hxF~b44rQkjWH(rRS!_rM% zi0A=W1S$PAr4qz{f`UIJK!X)bMnr7M8Mx)(u7(@?`ZnB{?hb=@0r5ga{T>py5Ru` z_`q~t3>SDq01FBMR03QHi1U06AQrq15HCcmzy?5^=bHdAN{Al}V)|T0&jUn{?g2!7 zKOp8m2*?9G0SM&?h_evD8eInL1$ZA2euxL~ff*kHVtflA8h8STgXkF`UWgcc4j;He zz6Hc|?*V%Neg{N5m`N5u77!#t4p0Kue|bhw0K`iiu?1R8yfzd6Z$%t`e`gR29>Ekm zk}3Fq1B0O)LfkNxH3~k^b@)wHyu=Y*H;&N}(Y029C|Sb?#@itI6Y|KV`ARsy2^S_- z93{xcd?g-6;CFE)NcDZ1)Xbd4q(#KMvl)(vC3^#6XZrzSQ~-Yb2Z-r|nRrADE`SfT z8v=;&5h&6~eNU2D2m%(cgo!}J;4%gi8C(H~hE`(ae}XuDQXw5;I+Jf5AeOU%$-jXO z?MH)|OhgtS)-an%fQa!qjJ}28TLIC~c0jxkWdQd94g$Qx=!iJ2>KKlQ!H4jHb{hmt zM8m&8OxOtVnB)#I5#zrx91(-x8T|*NBjV0T z{A41!nFvG-vcM`DVxx5iacoF3x)h`TR}ho-VA6{teuswW!$crr!F?Hyh!yC^aB;+R z%1pcpU?1T6fVjz+0%Aoi0P#Y^SzyI*air!y3YfqS5c|#v5DT8nApRQ&$ejVr^*1qeTcEqq{w?2-Hj2zCX810=u#CNemQK}SF=U@9Q|5HlEb2gHitKV%{g7!HW} zBLMLdN7N%3{qK;P|51=Y0TQkP#I&hQ2603~s~H^;U6%%kdOCya7<~gER!|6tXRvKd zd;uU{i0GL@k6Fd)Z+N;zmUOJ!VVSjZk zd#=C4zV}MqF(D?BLyMYZYxXYLqVvXh)nv_QIn_V&#x|KGep%|5_;~!VSznj-)UoO|1PN)CJ)a^7?9y9pDX`rguLiNBsNJD>}H z`^+Nf3db|=8Y%M}mn#hepYGUl*k7sex>wc9h-kCH34I6G^?em+u^@M|?5rPGZ)La< zQX}`I#B6T5H{*V>`=wifL!?a?@M`3|m-X=!Gn^n^;oT#NO$YUb*GyOOspw~^HL2}_6Fz;0Ut2Oa?{SQI4tqzY*`_PAUtZQ?tsivAY;52Aqqp^%+^CDLPXf@cfN=%>VV3CP=oc&A*HY4r)nYCD;iBAn@^QZuT_ug{ zk9Ho$Mgcj8 znxCZ|ow~A8Z{)s>LF*D$u_{aj+zNGxx|f0qr<%K7tslQV)g7U)SEw*aZtd}V*%ehI z3rmK?81G;Gak!7=!)rgp3@;HkeA(c|=93w_dwXyr?v5CKEp*pD-|Y>#%G=J*?fvY^ z_wjulcR497ZO_#za$U1cX^Pv3xKAIuROF5rKN>&x{MObPB>u&LXizK_H|)A;!L*{W z5(nm5>mG~FNW5?ExbRGgNq}VtKWmNWMcMhsyRxL^r=QF(x!q{kU;RgP`6s!b*OE^= z-hVaE;zi%Oa&ljjvcP?qXXx;KjW-oCGtN$P?aUlq@@DRdOK0T>Qu~V#a)FTE{K~CCx-HIL(uM!q0v=y96 zVx3t)|rku{CYgj2^Wtd{w zx-xrO*Zv2~>}L!Ji%8->xttqWo-9_o<>IcZe71Pj503e1^Lckv3Hw&n$hDttM4kwd z5e8j7HPB#`!1mG={>E?4DjOb*;tuN`>^?;0{y@X8KYOQ7Ip8C;ZnKOuDch_pFk9~S zvb5dke8vpo({iHw=Hxf69RoZvKMZOZm?L#dsdcxN?YlUZl0s?MjoPR}UdoyHt(K2( zeEe?Iw0TJX3jsI9YPV9nb|?Lc2J}7lD*2*I*Ta6>K75Ru)XzfWoy)~z%kKPe9^^Z^ zuH9&J(NUJ5>$Cr?k6Jz-6qc%MtaIA$vCV3+{oq-ZUHC^b@I&Cbw@TdbHP_@Ef$Msi z?q1oEM@tOK)GxnqJmxXx)|;6{Hj!joVC6{lnd?^;It2847d~3scA;D2idj#~ zmftPXX($t`a1!;Gw2X1iz>N(*6XMs%jx~2+aRU9E22@|y^K|ZGd%LApvaJ7oR#o2H zHCj<43hj&(x2-<7b9>v=qIbg&F6}XB?%s;1_9p|#&=zIERGaAGH=;MWX$L=i$glR? z?$YuqQ(?y*r>~b~LRy#n?0Ui3o#@eBw4lsQY50u;PMPXu_9e5vX825)8rjt5V?+Om z550J%h`0LvV>L;$ma4FClT$fvVYN@3yJrP;XBh@wuI%+tc4^;@2j`bNbXP7>Av!Xy zwX8T7EPJV^q@C*ZWu1rg)vAs?E?!Kkw<_*xz zu^8xa>_~Q<59{8UIs5ZhC3;8nZ&?v|Tblo(yXWbj5<}RPt&0Q$?eXt~M6-A`xv<5J ztZkJQII9(}T|B3K3(>83=iDs)p?WDp42zoh#Qj=bDUDC_pCxpzIGOOtY)(#V6kF?4 zaGi6S^O9a0Ep*klD2DZ(dc8}`DE_Ue=;HM3EQ}mqlcBe2bW+eJLpk}2jRgP2&KtJ_ zugIMkYpL%$k{6phJG)u1Mv!Kn_E0nZw9sTlV_>cRybHCLHk$W*ah);@FH!*0L{>eo*7k{o0o-1MIYbq^0Hz`y1vzt6mv=ckFV0la}WIV zcz$fbtaDC%Zb{f|ZeM$fvMWs_g01vFY;c(+o*Rd?9A55k;=IixsJ>Y#s`f6i;me-A z?^X$Rr_VNsx7A(rHgZI#m%iPMQN|f5(sdehK9HX_59Zzu9Cb6WoH9&E=W{rbik8?v8pE-IrdwhV-i7#K>e9^7|e)oljGsF$Q z-=oz0g*9eeo|}x?d^5r`z3S|Ps}{D+JBF!PWy#-6-!;nk(lzC2J|{+Sh7DAie1UU< z-O<}v%Kf6}8Vw(huk>Fvawr#X5Vu>eX_h#*MoD|Lsrkx&!2-E_CkH`}%6f%_>jOV2 zlzlOF(odCh+LNZ3(wrwZre^BZxCe{BCY}#Cc57L$ z)GgY1i*~%bG`Uc&t!jPGm?r5^wv=%|&B=<+)BRsFc5x8nUp|X2&exnP?AmRv^J)c| z#ZxDn+PlvF_|@=4ua=eS^JWhH7$>Lq_}KQCwJ&l%Nw;;iP5#=s%IaRan!<|I-hSse zUtlh^QHBX=?7A$GRX|QJ{eqqm?fPo$VdFxB+#OHbO^cp-t!Cz!%KXA%+gaACj~^zL zMCy*K5}sWB_TDG^K!L>aA9yxBpyD1hg1LS0NqzByc8NHOhZS3we zpR;c))E4n812n8Ir`sKSu5fQZQFl^TH8q8)FdD``9v5Al){WOxN|fVI$|{&le64xw z)ydNFOJ`O})ZH4maa2&c`YG$Dya=LG(AaZn?B3A*RWsX9-ST)DnIYLrwvgPYnzso5 zP*k*BW{a!>a;iFIqTjyGzLEgagm3sLIIigs_rZPX@T4h+ z)6!(^wmkc`OzZN2*!|8$d$yg?>Xz3pQKQc=^pctPtGZx#VBprXfxW*k@@R}HwM@>p zE-JsjF+AzATSeX#&)WyQUN7#FS7N>1xj7*1W!1?d53T#Uae?*92ECt*og}$Cn;iue z#b1|DOJ=^fVeRinJ{&MvCD(gq++dxsX)b=7pNziZ{4Pyue1>zY`b9$PyVstb%Z$Ix z9GD2QZ&X6smrzO6Q^wiz!WwW5{yW6ZyA;Ocl>((DSdH(vl0#|!upWxYEgDJ!G zin&$XaJ%XGAoqnVt20OY`1SH~$(P(0rttGbyA^BL{@ICPkH4Gs($sssvg7RNFNX2$ z6;Hf}KJJ(LtlOpXvOsU4(hS{OnUrCA$=oJxc#n+Y>v3b62W;}(n0UKBSZ-TK$ouA% zXFaDo`E*XI9`#Ip6guYX`I9HIFQtyTFg8^C&0F%^JjO1r69wXS zIYpW8b!Tr+5ZE-CJ^VE9TiLXwmE57@zeV$o_PF@*lI~}I#_g}~=1iL%F>i5@gm>`e z5*Mk->BQ>~ck^WBy?7uhM`lWh3V;Dzqp}670 zwcTn<4Tm@_@t6|ho@#sRz3yFmAI|(Ewz6g$9Xx909htvXH@Wyit;*LF!KK=9{xufM zu9;-bsN2`wCqrAl>>Xv79uqr$+Z7NTg?U5$UwAd=?j^fM&vGXZPE)uXHfyM3Uzyx} zM>6hiTGd=CEv=%oqR3Y6o_hHX<*<)7s~43YovK-@ zn(zPI+}X9{!o~~Ff2`eM)m~~5Su0yc%!(@6Zs<2`;MSybPVrM84>93O$4Garo^mno z_ViIFH>W3PlIpLO$(auwS-Z)Chiubszmp{|HaWt-Eh_)WjdbXcC1W2svW}8xfqem{+w90HBO{yL$X)Mc z$$P+#liF~Mb^jnsHnuoUJ}GJlAjemxdCQ)Y>ieJixMs6Pzsq>{>9D`$QZfNO~wQ1IFSIdt`c6O*vmI-zEnHB$`ZtMUH!=~0!LE-jD zn}Z#al&9(Efz#rZQ!E<%Nh?#Qwj!%xu$ik-bwg^uevt~CuJo;Udp!CPCc9v`-<(j6 zgrGJvb>!-mJ$93xtvCIWZCm7=t~{1rGBUq|?m}u05q&KP?Q<-rE-J8Uf%}1e>->V({FBP8_XxxsRJV~$@mOMZLR zKy}KE`rh8jdjfmT7~`q>MLMAT(nPNt>H~|1b1cJe+p5b&=4NdONLe;V%$;Y&-MQ#Z zkF@v)E2H=>Hj7UWD-BrWQkY_qvMMw@^G)sCy5^kK{k)o1*Y6jY-eRp*Gpo?OR^&aQ zq2Fq$;ioS6#;xD7ojzC6JLkDS7_O|TcZew3{iEo{+sS5|zmKwMu{C@4y7OYA?1pT6 zmr;kL%KYs5ZmnLv1ZfdHxU28f^#f8y3D7 z>ZcS8U-Bw$$OVEuLsPk9Q4V|XY!pGi4fB#aemX{8~U{+ zDUKjRyZmJ6{--w13*r_2QTxE{w2S$!u_HWE>$Adh@;jO~bg8XfGa!1U+BAWc_M*{` zmJJ?zCM_jhU&?XMZTFiUKB{r{d+&HAID200^GLIpF^tRlMRCKfBX`~rSnMpk!JDb9 zAvs7r=bBOV{WG^+4g{gHc8j3?!6K?>5cNy$iebTbMBum8~gc)?Md~o zLkAwzjwPD>LTe4pv*xT{czM;40hy6ITJUQ-xDO?Q&CB9;2Mo2e-r8g$dvc~y{N_^c z6xSc&0^xJ#7^%s#4J%`|SALKuQm)PxG&ns!`hG{LPtU3pJKut^QHy+>y>&AKhqvEg z45KHmh#MZ)Z#uEL`b>djX=dC$?Y zt}_ZwAHCMfuS@gQ>U~rYn|dWxOLP)O!&k)(8@AbX4IDFtotC&Gw&aa=T*njF3pYMb z-6gn|`f);lbY_UO1OMUSLHqS`S1&&krD|qVP%34{R2VnTYvOjt z+NmCCk$m>)x*%lk=e_>tIwtMy`PQh)#xTFY`g=3$zS5w!t@{r+U41ttK6;s;=b9Z; zwp^Wm_{P3%<@?vJu5XilOc|zk&gIwOCkfV{D@wJZul!gVYtAcovheV~pb1LzdesYHMz3F@_Vt=nZkhr?*bO zdp^f^LiNWP?}g#YUN)Nt#c%f+mEKRnVw~gm{Jj>JN`JPuw`Is`D%4Hhx!^Vg$N4NZ$UP6;{M$xE;MVX^`WrJI_ zS0sI|Nl9Dd{u$^EKm3FX@vIwRv9@7!OVmv3JFrQk!PAUVZzev8#P@2P3m%So^6Bh9>@ zD)*JnvCa;oYD}-YH^mKqS++Ym+UQi$58ue+ok=H)54I%dc7E(uzOLe?vG8J*X|Z5_ zMaH&^Z+td|sa16je5t*D{afq$V@rF#EdHilA!wyNOzjgje|Xl2(|zhX%EQkm)$Nk? z*-1ivzr49;U2adboAlGVvs<{jq2KKN!{ZvxCtF*#Ke`cK9^0Rrb8$2NiUiBg;vqUDV*iCr&8+*`&@OqRe{vo z6cr8cOQqdb-&GeZ&YV$uWdiGzRoH8-uX~5NncwMn)0FUVV%><&Lr*V1T^Yr?U%eQ= z9{B571HU0Fx;Qp3xm`(~(h4)uA1rWc^jY^ZQ+PADNNK>#sF273SJOl8WSzRW*=N_O z+$&Y%1AA0f?_HAiU{uQb!d{mi_7q^ex&tnW{!ry zrfSP1L21uDwspbBE3b8t&Tm*vNtGjSC^{%g6>3FD-s<`=-m3qHqAB@vb`<#b4eFa% z$+#HT3H-{g=;BB#E`L{e<=v5!%}S#i@2f5df08ueO&NcLgZV`dEBP#)2jBb}pofY)kPD&y4}8=6*xpNmMM3PHCIaPOi%`w#zJ_AB)L+Ont$6Vi!b^x<#A1gFwr zVPkCnH+{T4b+_N%*)wACYkOD_6@fYtGyk)ni_j)W+_frDyxZ z@|JggU{FQU9F>bjEYljZ-0ETK7HjH<)P1=fc6E>S&9^=BuQ3%yyN!Q%*2wA_t1j60 zJpGW%=Mim^l}0Tq@5i#w50qUIeL>~Qjh8P3uxA3N7iGq1?Ux|woe>c zPst5W+=K~ipq)UUlU7gNgo7nma%Az-N$|fJbO5cDfY_PO99gf(f@f~RR1Q$?b4S)| z(&f3E(3A`GDA2bgtIbW=hBTnfk<~#S1iD!gXx|r(@Q?EQyl@jbNCCYFw3C#7=_dSw zH1?$<>oa*4=q_oXy008rU&+Wz+RN$rlTALK2ddT z<*(g@^JIWNeeK9*vB*(xT!qR#foHvOWJ|EfR^V5V+rD*Vb6I5OTUTL%Ebva?QY>=9 zJ6GXgIp9U_9N9b;`5t&J@|hiuY#A20y~9s`oKdmruv~4O?@dWav7{AbIuUf_eK2 zvo?Nv6Oa-5^MvfD)Viq63HI|fB10a{rG^x}SN38Fze%xNg>AiAvVs?%GL!9XPe@Fa zJ~cdQbc0`pd_m~6tmCf&$0iFW4BNl6wD@~?lFsEd-dWW<_fIX&RkN?DEPYMRUKtYF zQF(MO{a%i?i@%~3U7Qdv!|VGNDUR=2ZAmCSs6MQ-L1R=*@4a`AANu6^Y}Se4Pm;a7 z2VS@@=vt8PCVyKqMb}c_)oxj8aO9*AGtFgBIwsO{fZBv{o{28be5Y}dlP6608G5bO zuVTYoBbT&G31jt#yxFh3TG6SG!57c-F)@166R)g)66{u<^RD)>cel=nlIx3(Hwcb2 z^sHJ)*`=Ra`cPksVk~nUJIt%+PD;NXT5UYw+NZJJeJ{+s$*CQ`_~Z_SoEWRRu3-nS zzt<}2xn^LF%BgC_V0NP3g6nfOUDi%|7&Rex;kQD{FueyWidT4fVO-m}b-rZTsKUpe z=k35`=zh%{G%Fs zx;{{b>Gyr`PaOQZI8C~0U4sr?kLkVk+~hT5rS3(Yy?D2{|A1?|Q0{m)OXvR zMmerG<-QgBun#C&t8!8X?iq3OKsz^Zh2Byb6*AdZ+WM%XqWU4#8U zZkZ&dLsgi5-=`#Q_v3uC`9$vW<+|2q60Uq0nRs4zn82k$tK!Phg)iktRWH}GFY3_{ z8TzQZ%0T)@`{2D>pA9qFyU_V_vGb5qx^BV7y(q)(w!X^7f98SLKR&zWy?A6e@avG3HF{eq!}OTwFK+m!6@STM=fMKP*zxMonq?db zLd|^G-tn2=;@_V>R(`qUo!(5ZpKrFeOf=r(wNGpN5VM)2{Q;%LMq{5_-^eI7p(d98nLS3v9If`fy2XdJY|!#Z*c@McC)A1hF2)Onj4sMDmQfGXOGk}TbTd&6=$RoNq+Gj`HkYQY*<1fi_PY6LL0-bcuLrjomaWm;*qR_&8#vct1o{R8G+3|qEi{sTS7f)&+YgO>-i*O*T`TB|eJPU6GH z<2#;ioBJbv@-Y(`wGVqf#q~?|sNF@`rB_TXmT-PQmW&`~NWg)uMPK(Nd%W8^Env-5 z#f&hb`AOsQZ5qCFU!PvSc$z_8cJ)hi^`DveGaj3OLf4{%qlQtH>&sUq&nF_R-M4qHY&E3@x)`{p)* zh4%bA-%2H37wwQ3c=JLdks9HePJEZ!Rzn%47fYPgqKi}MdEEHez-7%#5<7AyWyOA0 zPgeC>O-#7Ft@n~PvvL>h*)!%1zCW+*U_g&&CkEd7=Kk@L+J$Y~cd%!aq`cG?8Xu$V z(re{_KP*9Z$^NV7#w%3u6ECmb(|MH#0|f2ogrMhKc1IS;^p7a@1r`eXYSx3OFsl@ z9bG23>cp&a$DYMyHl@b1${!6^d6uW3UYWOF)cSP}B zBg+*WnFDqackf$jubsGHxgfw@$0SK{z15S+)iZ1LPB?FG^Xi+U*KFANtTy8u|GMkD z=tZ8Rn%^`?PO{lZ|HhtP)9^PtqKmU_#HtNWqd)ZyH8Q(%tuFJU*+j$jv8F?F@BJ*d zxGFbxl>C9?g27vsOnk6Tw)~AA`&I6!ACsSjB}^O{Ay`ZZboO)670fS3@E^_*U7V+G z4m-QNYBue(d&*+zB-SXG43GAncRaUy-B`_8Qv?z@&gEr&%j|dUs}BhZ+IoydzU$n} zc6~JAk;H3k=J=Sy4G!v<0@k*!}MA?T+A@1$U}8a5c|{}A8AQf?GvoZW>qD1z0#p?Qv+hyWT5Kd(vVY) z*Uk!3Z2Afu^W*dO%vsZ1_i=Ld@%$IdW7m}C4REIn(~IQ@al?)JukDmOZCDS(6fAFT zTkJhNT3EbjzN2%p?e4AKDal$f4VsbN&HT}O<_~H%xc^G=-S;Qy?nTS{w3K`=Hr(^A zP0a2{mhhz-ONN~D-jQv{BKN#^6>jbi2YJ~Kj_lDa(({9>&_NyeY2ZdIvd2eP;TPng zA062yEb;{KT^hjEJ000$S>*gqSE0Km@G9WuEK>E8t58}C_|i{~>~Spe8t`)D2A>_- zmMn77XIJ4oZQzZ-tytvHFYsWZ13dkUBiouq)&swSeB4(@wk?ZX^A#RU1^|Bz+@3|6 zeS-&+fxz>>IkG3R$d|xtkx%Y&WIM9RoGy4U(FOhk_+%D2>AS1YbP(`;-yPY`Eb<%h zHso`DII>-!{Xd}ndcb9WICZQb&UF1C>yga1uTYnHqw!PYRT9mT;OhC~c@5l?Ek}C4@&laLXuA0(hWA zvxJ9GaTpCs-yWdEvV^`pK>32oMO5NhLIoL6c8vigR)*!sS|~g#!-8j4BM@|Zg0NT^ z*%Jh5V-W74uvDlm3qm;xNwOdagtt(bX99we90<#W%jG~&HU;4+3M++%y+F8vLRK#j zl7uZNB#Z^Ywl@eV!VSGa7;FYYCkm^DR`MX!qEI9c!dhVm3aREG%v1m&U09$1f~f@v z+&&<^z92mme8j!2wzaRh=Nck-w%Xc zmLSCT10h>@76te5Am}QAkSmN-0zujegnKCD3$>L&C`Tbl8HBCETPVz%0D_SU2nE9B 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 B^-2H$ delta 38236 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

eyej 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 WAwr{6C+D2-G^bOoM9B}Fmi#}9GVYoH diff --git a/next.config.mjs b/next.config.mjs index 4678774..27ca96c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,9 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + webpack: (config) => { + config.externals.push("@node-rs/argon2", "@node-rs/bcrypt"); + return config; + } +}; export default nextConfig; diff --git a/package.json b/package.json index 0548c7a..589e43a 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,15 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "db:generate": "prisma generate", + "db:migrate": "prisma migrate dev --name" }, "dependencies": { - "@clerk/nextjs": "^4.29.11", + "@lucia-auth/adapter-prisma": "^4.0.1", + "@node-rs/argon2": "^1.8.0", + "@node-rs/bcrypt": "^1.10.1", + "@prisma/adapter-pg": "^5.12.1", "@prisma/client": "^5.12.1", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.6", @@ -19,7 +24,10 @@ "@radix-ui/react-switch": "^1.0.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "lucia": "^3.1.1", "next": "14.1.4", + "oslo": "^1.2.0", + "pg": "^8.11.5", "react": "^18", "react-dom": "^18", "tailwind-merge": "^2.2.2", @@ -27,6 +35,7 @@ }, "devDependencies": { "@types/node": "^20", + "@types/pg": "^8.11.4", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.0.1", diff --git a/prisma/migrations/20240406132011_add_lucia/migration.sql b/prisma/migrations/20240406132011_add_lucia/migration.sql new file mode 100644 index 0000000..2fb700e --- /dev/null +++ b/prisma/migrations/20240406132011_add_lucia/migration.sql @@ -0,0 +1,18 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Session_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20240406140422_add_username_pw/migration.sql b/prisma/migrations/20240406140422_add_username_pw/migration.sql new file mode 100644 index 0000000..e7d9f9a --- /dev/null +++ b/prisma/migrations/20240406140422_add_username_pw/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - A unique constraint covering the columns `[username]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - Added the required column `hashed_password` to the `User` table without a default value. This is not possible if the table is not empty. + - Added the required column `username` to the `User` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "hashed_password" TEXT NOT NULL, +ADD COLUMN "username" TEXT NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a19de92..915156c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -6,6 +6,7 @@ generator client { provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] } datasource db { @@ -13,6 +14,20 @@ datasource db { url = env("DATABASE_URL") } +model User { + id String @id + sessions Session[] + username String @unique + hashed_password String +} + +model Session { + id String @id + userId String + expiresAt DateTime + user User @relation(references: [id], fields: [userId], onDelete: Cascade) +} + model Point { id Int @id @default(autoincrement()) userId String diff --git a/src/app/api/verifyAuth/route.ts b/src/app/api/verifyAuth/route.ts new file mode 100644 index 0000000..bdd69f2 --- /dev/null +++ b/src/app/api/verifyAuth/route.ts @@ -0,0 +1,8 @@ +import { validateRequest } from "@/lib/auth"; + +export default async function GET(request: Request) { + const { user } = await validateRequest(); + if (!user) { + return Response.redirect("/auth/login"); + } +} \ No newline at end of file diff --git a/src/app/auth/signIn/page.tsx b/src/app/auth/signIn/page.tsx new file mode 100644 index 0000000..19da62a --- /dev/null +++ b/src/app/auth/signIn/page.tsx @@ -0,0 +1,101 @@ +/** + * @see https://v0.dev/t/ZOQ6u9Lf2bO +*/ +import { Label } from "@/components/ui/label" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import Link from "next/link" +import { Argon2id } from "oslo/password"; +import { cookies } from "next/headers"; +import { lucia } from "@/lib/auth"; +import { redirect } from "next/navigation"; +import prisma from "@/lib/db"; + +export default function Component() { + return ( +

+
+
+

Inicia sesión

+

haha yes

+
+
+
+
+ + +
+
+ + +
+ +
+ ¿No tienes una cuenta? + + Crear una cuenta + +
+
+
+
+
+ ) +} + +async function login(formData: FormData): Promise { + "use server"; + const username = formData.get("username"); + if ( + typeof username !== "string" || + username.length < 3 || + username.length > 31 || + !/^[a-z0-9_-]+$/.test(username) + ) { + return { + error: "Invalid username" + }; + } + const password = formData.get("password"); + if (typeof password !== "string" || password.length < 6 || password.length > 255) { + return { + error: "Invalid password" + }; + } + + const existingUser = await prisma.user.findUnique({ + where: { + username: username + } + }) + if (!existingUser) { + // NOTE: + // Returning immediately allows malicious actors to figure out valid usernames from response times, + // allowing them to only focus on guessing passwords in brute-force attacks. + // As a preventive measure, you may want to hash passwords even for invalid usernames. + // However, valid usernames can be already be revealed with the signup page among other methods. + // It will also be much more resource intensive. + // Since protecting against this is non-trivial, + // it is crucial your implementation is protected against brute-force attacks with login throttling etc. + // If usernames are public, you may outright tell the user that the username is invalid. + return { + error: "Incorrect username or password" + }; + } + + const validPassword = await new Argon2id().verify(existingUser.hashed_password, password); + if (!validPassword) { + return { + error: "Incorrect username or password" + }; + } + + const session = await lucia.createSession(existingUser.id, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes); + return redirect("/"); +} + +interface ActionResult { + error: string; +} \ No newline at end of file diff --git a/src/app/auth/signUp/page.tsx b/src/app/auth/signUp/page.tsx new file mode 100644 index 0000000..d32c242 --- /dev/null +++ b/src/app/auth/signUp/page.tsx @@ -0,0 +1,90 @@ +/** + * @see https://v0.dev/t/5ENQEFtiZm9 +*/ +import { Label } from "@/components/ui/label" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import Link from "next/link" +import prisma from "@/lib/db"; +import { Argon2id } from "oslo/password"; +import { cookies } from "next/headers"; +import { lucia } from "@/lib/auth"; +import { redirect } from "next/navigation"; +import { generateId } from "lucia"; + +export default function Component() { + return ( +
+
+
+

Crea una cuenta

+

venga ya tio

+
+
+
+
+ + +
+
+ + +
+ +
+ ¿Ya tienes una cuenta? + + Login + +
+
+
+
+
+ ) +} + +async function signup(formData: FormData): Promise { + "use server"; + const username = formData.get("username"); + console.log(username) + // username must be between 4 ~ 31 characters, and only consists of lowercase letters, 0-9, -, and _ + // keep in mind some database (e.g. mysql) are case insensitive + if ( + typeof username !== "string" || + username.length < 3 || + username.length > 31 || + !/^[a-z0-9_-]+$/.test(username) + ) { + return { + error: "Invalid username" + }; + } + const password = formData.get("password"); + if (typeof password !== "string" || password.length < 6 || password.length > 255) { + return { + error: "Invalid password" + }; + } + + const hashedPassword = await new Argon2id().hash(password); + const userId = generateId(15); + + // TODO: check if username is already used + await prisma.user.create({ + data: { + id: userId, + username: username, + hashed_password: hashedPassword + } + }); + + const session = await lucia.createSession(userId, {}); + const sessionCookie = lucia.createSessionCookie(session.id); + cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes); + return redirect("/"); +} + +interface ActionResult { + error: string; +} diff --git a/src/app/history/page.tsx b/src/app/history/page.tsx index 2ee6523..affe457 100644 --- a/src/app/history/page.tsx +++ b/src/app/history/page.tsx @@ -1,11 +1,12 @@ import History from '@/components/app/History/History' import prisma from '@/lib/db' -import { currentUser } from '@clerk/nextjs' +import { validateRequest } from '@/lib/auth'; export default async function Page() { + const { user } = await validateRequest(); const pointHistory = (await prisma.point.findMany({ where: { - userId: (await currentUser())!.id + userId: user!.id } })).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).map(p => { return { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e513cf7..d2cb4af 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,7 @@ import localFont from 'next/font/local' import "./globals.css"; import { ClerkProvider } from "@clerk/nextjs"; import Navbar from "@/components/app/Navbar/Navbar"; +import Protected from "@/components/app/Protected/Protected"; const satoshi = localFont({ src: './fonts/Satoshi-Medium.woff2' }) @@ -22,15 +23,15 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - -
- {children} -
+ +
+ {children} +
+
-
); } diff --git a/src/app/page.tsx b/src/app/page.tsx index dfd6231..dc10954 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,18 +1,21 @@ -import DesktopPoints from "@/components/app/Points/Desktop/Desktop"; +import Points from "@/components/app/Points/Points"; import prisma from "@/lib/db"; -import { currentUser } from "@clerk/nextjs"; +import { validateRequest } from "@/lib/auth"; +import { redirect } from "next/navigation"; export default async function Home() { + const { user } = await validateRequest(); + const pointCount = (await prisma.pointCount.findFirst({ where: { - userId: (await currentUser())!.id, + userId: user!.id, } - }))!.balance + }) || { balance: 0 }).balance return ( <>

tienes {pointCount} puntos

- +
); diff --git a/src/app/remove/page.tsx b/src/app/remove/page.tsx index 3d5de9c..abe1d66 100644 --- a/src/app/remove/page.tsx +++ b/src/app/remove/page.tsx @@ -1,11 +1,12 @@ import RemovePoints from "@/components/app/RemovePoints/RemovePoints" +import { validateRequest } from "@/lib/auth"; import prisma from "@/lib/db" -import { currentUser } from "@clerk/nextjs" export default async function Page() { + const { user } = await validateRequest(); const pointCount = (await prisma.pointCount.findFirst({ where: { - userId: (await currentUser())!.id, + userId: user!.id, } }))!.balance return ( diff --git a/src/components/app/Navbar/Navbar.tsx b/src/components/app/Navbar/Navbar.tsx index 2443699..4b37df6 100644 --- a/src/components/app/Navbar/Navbar.tsx +++ b/src/components/app/Navbar/Navbar.tsx @@ -4,12 +4,14 @@ * Documentation: https://v0.dev/docs#integrating-generated-code-into-your-nextjs-app */ import { Button } from "@/components/ui/button" -import { DropdownMenuShortcut } from "@/components/ui/dropdown-menu" -import { UserButton } from "@clerk/nextjs" import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from "@/components/ui/dropdown-menu" +import { lucia, validateRequest } from "@/lib/auth" +import { cookies } from "next/headers" import Link from "next/link" +import { redirect } from "next/navigation" -export default function Navbar() { +export default async function Navbar() { + const { user } = await validateRequest(); return ( ) } +async function logout(): Promise { + "use server"; + const { session } = await validateRequest(); + await lucia.invalidateSession(session!.id); + const sessionCookie = lucia.createBlankSessionCookie(); + cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes); + return redirect("/auth/signUp"); +} + +interface ActionResult { + error: string; +} \ No newline at end of file diff --git a/src/components/app/Points/Desktop/Desktop.tsx b/src/components/app/Points/Points.tsx similarity index 88% rename from src/components/app/Points/Desktop/Desktop.tsx rename to src/components/app/Points/Points.tsx index e48f580..653a4ef 100644 --- a/src/components/app/Points/Desktop/Desktop.tsx +++ b/src/components/app/Points/Points.tsx @@ -11,9 +11,11 @@ import { import { Input } from "@/components/ui/input" import prisma from "@/lib/db" import { redirect } from "next/navigation" -import { currentUser } from "@clerk/nextjs" +import { validateRequest } from "@/lib/auth" -export default function DesktopPoints() { +export default async function Points() { + const { user } = await validateRequest(); + async function createPoints(formData: FormData) { 'use server' @@ -24,14 +26,14 @@ export default function DesktopPoints() { await prisma.point.create({ data: { - userId: (await currentUser())!.id, + userId: user!.id, number: Number(rawFormData.points), reason: rawFormData.reason as string, } }) await prisma.pointCount.upsert({ where: { - userId: (await currentUser())!.id, + userId: user!.id, }, update: { balance: { @@ -39,7 +41,7 @@ export default function DesktopPoints() { } }, create: { - userId: (await currentUser())!.id, + userId: user!.id, balance: Number(rawFormData.points), } }) diff --git a/src/components/app/Protected/Protected.tsx b/src/components/app/Protected/Protected.tsx new file mode 100644 index 0000000..ef794df --- /dev/null +++ b/src/components/app/Protected/Protected.tsx @@ -0,0 +1,14 @@ +import { validateRequest } from "@/lib/auth"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export default async function Protected({ children }: { children: React.ReactNode }) { + const publicRoutes = ['/auth/signIn', '/auth/signUp']; + + if (publicRoutes.includes(headers().get('x-url')!)) return <>{children}; + + const { user } = await validateRequest(); + if (!user) redirect('/auth/signIn'); + + return <>{children}; +} \ No newline at end of file diff --git a/src/components/app/RemovePoints/RemovePoints.tsx b/src/components/app/RemovePoints/RemovePoints.tsx index 2b89961..0f7430f 100644 --- a/src/components/app/RemovePoints/RemovePoints.tsx +++ b/src/components/app/RemovePoints/RemovePoints.tsx @@ -11,9 +11,11 @@ import { import { Input } from "@/components/ui/input" import prisma from "@/lib/db" import { redirect } from "next/navigation" -import { currentUser } from "@clerk/nextjs" +import { validateRequest } from "@/lib/auth" + +export default async function RemovePoints() { + const { user } = await validateRequest(); -export default function RemovePoints() { async function createPoints(formData: FormData) { 'use server' @@ -24,14 +26,14 @@ export default function RemovePoints() { await prisma.point.create({ data: { - userId: (await currentUser())!.id, + userId: user!.id, number: Number(rawFormData.points), reason: rawFormData.reason as string, } }) await prisma.pointCount.upsert({ where: { - userId: (await currentUser())!.id, + userId: user!.id, }, update: { balance: { @@ -39,7 +41,7 @@ export default function RemovePoints() { } }, create: { - userId: (await currentUser())!.id, + userId: user!.id, balance: Number(rawFormData.points), } }) diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 0270f64..74a7bb6 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -25,6 +25,7 @@ const buttonVariants = cva( sm: "h-8 rounded-md px-3 text-xs", lg: "h-10 rounded-md px-8", icon: "h-9 w-9", + text: "h-9" }, }, defaultVariants: { diff --git a/src/lib/auth/index.ts b/src/lib/auth/index.ts new file mode 100644 index 0000000..b5c0153 --- /dev/null +++ b/src/lib/auth/index.ts @@ -0,0 +1,62 @@ +// src/auth.ts +import { Lucia } from "lucia"; +import { adapter } from "../db"; +import { cache } from "react"; +import { cookies } from "next/headers"; +import type { Session, User } from "lucia"; + +export const lucia = new Lucia(adapter, { + sessionCookie: { + // this sets cookies with super long expiration + // since Next.js doesn't allow Lucia to extend cookie expiration when rendering pages + expires: false, + attributes: { + // set to `true` when using HTTPS + secure: process.env.NODE_ENV === "production" + } + }, + getUserAttributes: (attributes) => { + return { + // attributes has the type of DatabaseUserAttributes + username: attributes.username + }; + } +}); + +export const validateRequest = cache( + async (): Promise<{ user: User; session: Session } | { user: null; session: null }> => { + const sessionId = cookies().get(lucia.sessionCookieName)?.value ?? null; + if (!sessionId) { + return { + user: null, + session: null + }; + } + + const result = await lucia.validateSession(sessionId); + // next.js throws when you attempt to set cookie when rendering page + try { + if (result.session && result.session.fresh) { + const sessionCookie = lucia.createSessionCookie(result.session.id); + cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes); + } + if (!result.session) { + const sessionCookie = lucia.createBlankSessionCookie(); + cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes); + } + } catch {} + return result; + } +); + +// IMPORTANT! +declare module "lucia" { + interface Register { + Lucia: typeof lucia; + DatabaseUserAttributes: DatabaseUserAttributes; + } +} + +interface DatabaseUserAttributes { + username: string; +} diff --git a/src/lib/db/index.ts b/src/lib/db/index.ts index f4ba39b..7ae27f6 100644 --- a/src/lib/db/index.ts +++ b/src/lib/db/index.ts @@ -1,15 +1,22 @@ import { PrismaClient } from '@prisma/client' +import { PrismaAdapter } from '@lucia-auth/adapter-prisma' +import { Pool } from 'pg' +import { PrismaPg } from '@prisma/adapter-pg' +const pool = new Pool({ connectionString: process.env.DATABASE_URL }) +const pgAdapter = new PrismaPg(pool) const prismaClientSingleton = () => { - return new PrismaClient() + return new PrismaClient({ adapter: pgAdapter }) } declare global { var prismaGlobal: undefined | ReturnType } + const prisma = globalThis.prismaGlobal ?? prismaClientSingleton() +export const adapter = new PrismaAdapter(prisma.session, prisma.user); export default prisma -if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma +if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma \ No newline at end of file diff --git a/src/middleware.ts b/src/middleware.ts index 3e006d2..f3433b7 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,7 +1,12 @@ -import { authMiddleware } from "@clerk/nextjs"; +import { NextResponse } from 'next/server'; -export default authMiddleware({}); +export function middleware(request: Request) { + const requestHeaders = new Headers(request.headers); + requestHeaders.set('x-url', new URL(request.url).pathname); -export const config = { - matcher: ["/((?!.+.[w]+$|_next).*)", "/", "/(api|trpc)(.*)"], -}; \ No newline at end of file + return NextResponse.next({ + request: { + headers: requestHeaders + } + }); +} \ No newline at end of file