From ac10ae14f6002524cfffe494e01b945218fc506b Mon Sep 17 00:00:00 2001 From: rafiarrafif Date: Sat, 14 Jun 2025 15:05:20 +0700 Subject: [PATCH] fix: fix.env.example --- .dockerignore | 12 +- .env.example | 2 +- .gitignore | 104 +- Dockerfile | 46 +- README.md | 28 +- bun.lockb | Bin 81719 -> 81728 bytes docker-compose.yml | 48 +- prisma/schema.prisma | 1282 ++++++++--------- scripts/create-example-env.ts | 104 +- scripts/sync-routes.ts | 102 +- src/constants/cookie.keys.ts | 8 +- src/helpers/callback/httpResponse.ts | 160 +- src/helpers/error/handler/index.ts | 108 +- src/helpers/error/instances/app.ts | 26 +- src/helpers/error/instances/forwarder.ts | 42 +- src/helpers/http/jwt/decode/index.ts | 28 +- src/helpers/http/jwt/decode/types.ts | 124 +- src/helpers/http/jwt/encode/index.ts | 22 +- .../http/userHeader/cookies/clearCookies.ts | 66 +- .../http/userHeader/cookies/getCookies.ts | 26 +- .../http/userHeader/cookies/setCookies.ts | 68 +- .../getUserHeaderInformation/index.ts | 52 +- .../getUserHeaderInformation/types.ts | 12 +- src/helpers/security/password/hash.ts | 12 +- src/index.ts | 16 +- src/middleware/auth.middleware.ts | 28 +- .../auth/authenticated.middleware.ts | 32 +- .../auth/unauthenticated.middleware.ts | 30 +- src/modules/auth/auth.types.ts | 130 +- .../controller/authVerification.controller.ts | 54 +- .../loginWithPassword.controller.ts | 82 +- src/modules/auth/index.ts | 20 +- src/modules/auth/schemas/loginWithPassword.ts | 12 +- .../auth/services/authVerification.service.ts | 88 +- .../services/loginWithPassword.service.ts | 74 +- src/modules/debug/debug.controller.ts | 30 +- src/modules/debug/debug.service.ts | 22 +- src/modules/debug/debug2.service.ts | 22 +- src/modules/debug/debug3.service.ts | 18 +- src/modules/debug/index.ts | 14 +- .../user/controller/createUser.controller.ts | 100 +- .../user/controller/getAllUser.controller.ts | 42 +- src/modules/user/index.ts | 14 +- .../repositories/createUser.repository.ts | 34 +- .../findUserByEmailOrUsername.repository.ts | 78 +- .../repositories/getAllUser.repository.ts | 30 +- src/modules/user/schemas/createUser.schema.ts | 30 +- .../user/services/createUser.service.ts | 36 +- .../findUserByEmailOrUsername.service.ts | 22 +- .../user/services/getAllUser.service.ts | 20 +- src/modules/user/user.model.ts | 6 +- .../controller/createUserRole.controller.ts | 134 +- src/modules/userRole/index.ts | 18 +- .../repositories/createUserRole.repository.ts | 30 +- .../userRole/schemas/createUserRole.schema.ts | 56 +- .../services/createUserRole.service.ts | 56 +- src/modules/userRole/userRole.model.ts | 6 +- .../createUserSession.controller.ts | 70 +- src/modules/userSession/index.ts | 14 +- .../checkUserSessionInCache.repository.ts | 26 +- .../findUniqueUserSessionInDB.repository.ts | 66 +- .../insertUserSessionToDB.repository.ts | 64 +- .../storeUserSessionToCache.repository.ts | 38 +- .../checkUserSessionInCache.service.ts | 38 +- .../services/createUserSession.service.ts | 54 +- .../services/getUserSessionFromDB.service.ts | 46 +- .../storeUserSessionToCache.service.ts | 34 +- src/modules/userSession/userSession.model.ts | 6 +- src/modules/userSession/userSession.types.ts | 12 +- src/utils/databases/prisma/connection.ts | 6 +- src/utils/databases/prisma/error/codeList.ts | 450 +++--- src/utils/databases/prisma/error/types.ts | 22 +- src/utils/databases/redis/connection.ts | 14 +- structure.example.md | 32 +- tsconfig.json | 206 +-- 75 files changed, 2532 insertions(+), 2532 deletions(-) diff --git a/.dockerignore b/.dockerignore index 84e935f..ac3f324 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,7 @@ -node_modules -dist -.git -.gitignore -Dockerfile* -docker-compose* +node_modules +dist +.git +.gitignore +Dockerfile* +docker-compose* README.md \ No newline at end of file diff --git a/.env.example b/.env.example index 7449640..26f58a0 100644 --- a/.env.example +++ b/.env.example @@ -33,4 +33,4 @@ REDIS_PASSWORD= DATABASE_URL= MONGODB_URI= -# CREATE USER astofo WITH PASSWORD 'Nahidamylover*123'; \ No newline at end of file +# \ No newline at end of file diff --git a/.gitignore b/.gitignore index f8ff2fe..d35a8f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,53 +1,53 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/dist -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.local -.env.development.local -.env.test.local -.env.production.local - -# vercel -.vercel - -**/*.trace -**/*.zip -**/*.tar.gz -**/*.tgz -**/*.log -package-lock.json -**/*.bun - -# local env files -.env -.env.local -.env.development -.env.test -.env.production - -# local docker compose files -docker-compose.override.yml - -# compiled output -server +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/dist +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel + +**/*.trace +**/*.zip +**/*.tar.gz +**/*.tgz +**/*.log +package-lock.json +**/*.bun + +# local env files +.env +.env.local +.env.development +.env.test +.env.production + +# local docker compose files +docker-compose.override.yml + +# compiled output +server server.exe \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a355dee..b66f21c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,24 @@ -# --- Stage 1: Build --- -FROM oven/bun:1.1 AS builder - -WORKDIR /app - -COPY bun.lockb package.json ./ -RUN bun install - -COPY . . -RUN bunx prisma generate -RUN bun run build - - -# --- Stage 2: Production Runner --- -FROM oven/bun:1.1 AS runner - -WORKDIR /app - -COPY --from=builder /app ./ - -RUN bunx prisma migrate deploy -EXPOSE 3000 - +# --- Stage 1: Build --- +FROM oven/bun:1.1 AS builder + +WORKDIR /app + +COPY bun.lockb package.json ./ +RUN bun install + +COPY . . +RUN bunx prisma generate +RUN bun run build + + +# --- Stage 2: Production Runner --- +FROM oven/bun:1.1 AS runner + +WORKDIR /app + +COPY --from=builder /app ./ + +RUN bunx prisma migrate deploy +EXPOSE 3000 + CMD [ "sh", "-c", "PORT=3000 ./dist/server" ] \ No newline at end of file diff --git a/README.md b/README.md index 688c87e..a5e7166 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# Elysia with Bun runtime - -## Getting Started -To get started with this template, simply paste this command into your terminal: -```bash -bun create elysia ./elysia-example -``` - -## Development -To start the development server run: -```bash -bun run dev -``` - +# Elysia with Bun runtime + +## Getting Started +To get started with this template, simply paste this command into your terminal: +```bash +bun create elysia ./elysia-example +``` + +## Development +To start the development server run: +```bash +bun run dev +``` + Open http://localhost:3000/ with your browser to see the result. \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index f73c7b5d1b98a93918fc6f4cbcd585d79fa76588..1066575bd847c1302a60f499b1bb567c428ad859 100644 GIT binary patch delta 7581 zcmaJ`30zgx)<64lFK`@~RPb=Yc~%g(TrP-kFh^8Wa>#m56NP{rPzLqsGp@dTbu&|3 z)|aU{Rce-+m}8b!TG(Lpy)x}n!`U#@%rUk1UuT~a-}n1o`E#AM*Ra=Gd+oLNId`%1 zfqnl2`{E&DYsJu%@&ZLs8YznRHgG8LD4{3~fnNb;eipC_{E$si8URl&oSYYvJ6?$c z9R|7}r=)1c6h+DNQm4V4O1Xby#T%`Vuf3JdOuDt^#HQJApZ%<-k$E-(nsIJ_d$) z#ch!Wh`djf#N^E^EzWrxGzYMju8D}0Y%`n;m}D<;z#L916Nduhzt?8s>sTw(7pa#m zLaRgo?BmH9Ik2NBX$T||bQhXtizr`$WpIS^fZ1lTiN^wSLP)Ek&n zaub*pYJoZI!o0jZk2_IWq)7uMrNyO16J1Is*1*Mc40`PNi6R<=NoQ^*7qNq3~FK0r*_n_JFGGKPR7?>CEO02B<;O>fota^V1&B>ev zc{p&}I9XXCpgD$L;SbyGjf*!D)4*F;5>uR)pEvUgT|!WsNcHbnzS$+Swk%PudIT^V zbpba9&M@iK&|{;yIH8>Fypr){g_ClXnq;~EUjn06y?cOp{sxo3)TEyR<{&2mv)(9R z1Xu3ug8>&%957Fe1ZG8llfL4SfgJ;8{vlvCunm~?R|E6>=S_M#FzZbKX1yW6oP{2y zyqzfzF=#)<`=ByDAR<7_rOY5*sN};lhpO;=gVbPMT%b%m2a^v^A64PGpVSatjmPDO z+<0x29qbXsTZ=%u^k9MM*cP&=8O4O_`y(`VDwegLzJ>3f09RQp0pnL78E? zf$5H z#1m_2s;iISavco0ABBc_)ODaxCSHZIgFNbggUT?diV%<51VuJb%Ah$0)BsB+UMDqN zR~;zFA>cXKmKaXHaNQQrkPe5Zi$_R}&>e)j?MGiA{9_oUQ{#|SHKLK_Bs;)4x z4p==4N(W`^jc7|%&=`-RP67|%!{y5+g^Z(Kz9e5u-BFBk9%iKWLyUN-er(NQL)m8_ z`8@+M7%a_VTVDVr*N2Qd{sk)0m>d|PC<)NQx-q#2r~$MvB-Jqwqh7`yeuPmX7o!+Y znQe6OD*4*zY5=ZtE)&7?)ND|k6Mthtt3V|gvg#<0`VFWwP$)JmB@Ay252yxI0of=} z4}l6WcJ5`$Y^RH3mVoz|aTqRRB&z^>pmxJ`%ZE@N zxN#=s;QeuY4JwlsMyIMCyk$6Iqiod$pn8JxHxB71pg4Y;Q5lXFvYxOZNOv0fI_l~) z@Yn|`0tX!jp_A^o1Kt25o?du2@qA&dzZ?|TGd2YCKP9!Zu6ArC{V7I$O#mg2D_;@o zL2>;`@25-(wjw=h1m0KX+9Hz!LGZbiNuOe$sL0vDS%w%0X3EsmNPu1~}(a)eNS|ta9;N;5X?E}U4 zQmVL5z7$<_qbfXeN$sIKwxh&c#&vW7qg2en;l;sd%SuuDTKwCsa~L|BtLCD#W}T;Hi|d=}h66!JUKU0}j2X)@lF|zpNqaH4U!`6dM$+Ey-+2NFk~6X}k~0V+8NmsRJX=Tmw$L*p|Wv|r{e(H`W$^DRW)=dEO;JS|l z^5%Q}h+_ecV?(OhxQH@>>~!^wK*vy=pa$TqTe2dTnX2_EJhw7*!)HQ_r4t(~#e0<% zZ{~LmOa4}F;#Lzk=t)7}CQzSmhsEqMxA!Qp0N|rZ*=_Q^GkHIl)B4bXZ}Ug`K}`T` zfP2vR2=FoB5a1KQr+^Ot`vC_49|8^n<^sHcVn7LCDqtF53ZRJQ98E1BjL}d4j)F2A zFbsf$qhtec#FRmREC6l}r7r-7gsYEhT|v?CH!A;PAw1sDM6 zPxHTP>1c|Pn>K#;%$Vmf+75UXunMpOuoAEc@GgKiVi$muzXPxnFb6OTFdOhFU^E~C z&>WxvA^{!%?%QMC-K#NL19$`Q7GNXbZNQs=O@Q@)4S=P93IG8X)3sx5o2|p>HNaND zJAiF~SE%!GXXsvx-Um!QjtBllRgOG<#I6oICCSYcbLxFn@OoA3F8y+9UST!}F7Cjz zIr&Sf&Tg}bu_>`}u?dPY29k!5)J~hyYtus&IglhuiEKwGO4p-1+I$rrR0l~sixQOr z)7t9PgnRA(`9&%u2@tqYTXgCN&COdC3Y#x?nf0nRYxVPx$Hm6S#@Dw#K3-Mjj@ld$ zYDy4C9mUjW&fDh91;byQJL$x?kfGjVS)KCYsUQo8k5(MQKB;ZfW$? z=}5QLGFrauaOAYfyBZoM<2g-M^C)0N#piARael4o3`?F*EzgGAp|XOGz#bMU&2J{> z8O`~}7WdK6Gp^?U(Jn%zzn_kBS{7NGaRgAK2@L5#Ad3+Q@Z0Y%rQmX zYDPnEJEY`KLP()CnDZO$J)7mWnpCfKIk})Lv-3dsav_`WWp$ITgiPJKXVADZNO-p) zIYnvbG^f?Aa=za|r@Kp-abtR?M0D?YXV;A+o8Xd;hb^nGHeFvbEB($eyD7nW zG3Gn1j@rWM2mAL;dCmtfDY1x9Q3~iC*e|BfE^1Dz4_Dfxwk)A`bdb3U*k3}wUDT5P zqZK0ye=M;#qal}?<2dDC%5qu_y4j6>OKUN77s5m`P%iuhc;u4iwipDMge27R-YsJ+acZ^!sx|~)UF(~`&f8AKI#fzYmSgI&Dsi;;H zi)df1Mr&&$Oi!?>eot1bEH2X&4WCx7Y$E!^SkC78BAE1Ih6=87#e=!)jFnw0rp zci(Y#e(3+YBR->Y$gL)2=BC(T)3?oOW!N&}@=@g#m(yxy?*01w_zfl7pD}%*E-#a~ z%BRD5wW}K2PB!P#Y3NnWJpdGEq4AiTol0}7$D4C-)~(Lv`aNR8O8>XEK*Bc`tc{_S zS2d^A3cWu`Y@OPs&vD4PNO6`+sT$tqQZ2Ns2JE8s<%v%x%)V&YH)59ltp@7l+{Z?q z=()cSUiD?sG2_R-M9F?#@7RO#OjH@`OWAz4=vdGPea0`t+b%* zTj4E`6swI|wCw4NueI1;Q$Kkz)%>F2S5NEP;S_o!8b3hrW)j9*QG8ExZfNKNneVt} z^v>@jikx40@ z7433b4dJyxFYhi}xHrR$9G0bj`7~_d)fefs8pV5O{+Rt;zx%qWk9av5=1T3z4KFm@ z4!*Hgr`bsM&R#ckV9w^x3(Pnn=iFE?SO43QMy|{Z@ppqkYcgZ>-t9UXDX?%g}Y_WUB7D8LMy0IxA$S`mh> z`;T?4E_g`WwB9oUuJ5#ozuiT!M zWXq_RTTSx9-90xX`cFPzFWE--VcBY`uig06lXZdZzpj@bqxgFoj@O`jn$v2@cM0p{ z5)00(sn?36@_U-wYT=I$zo!PbYq VnGcFoT$b@kDX7=VT@T7L{td^t1lIrn delta 7044 zcmai33tUyj*57mBfP*g(0yjrAk-|cc;Nfu8qi#(zPYH!r%`EjpMI{shq_lh-wQe2F zPifWlW|nF8P|GZQRiCAKt;ilyMm{Pf%U5cGWvQU|zxM16zwi5Xe;+^BtXYp)vu4ej znSGW!uZC~C8on$?yjpQ*?|I`jO>3oT!C!%6fU||BwFIsJX8vGc3EbPEX;Hw_3#R9H zpEN;B1>Fwx)V#9dhl(`qzA#N|4O$0o3tR`xc3N5d9pQ>UrTBqKm`d9W8g}OeU$z8c zP~?ebJJrOq!A}59z&ufVOvr}QBQ>oJa9-K0qKSnCGk%6KcI+bfjQh4w`ZG#1vv{I| z0A9!^z`Vfiz|p`(#S;rP4gXGI)>trVEPNVP;y@n-W`osec;cPFY+xfWJMeJDQdWM-oAIrV>ZOybzYb9?k-0n*|mg3Ctdiw{SsmY5t^w zGHv8_%7*|jd)UvyX~4X~4#4cgr2L|i@_bF3ls}<-3i}T}+rNSZ@O=5xW|bA>Y4>Y` z+N+8B0CPyr0<*%8!0dKGety2so2mU>R|d*vl+GwF^k@UH22P%R(Bp|GPo9=H0~4lz zW`3+?=Q0ukLxbn2BrHLH7SZPrCMBs^W&-mv+tKQ<)SHfWRC9a-%yWMR%sy@Eq!tx- zz0!`Ly09MeKJ2Wr?QLLQS}<8@9RucU`^Lf#0kf4?pvNJbl2<+jj%qVgl&6Ij9uLg& z5jQBi0busR3qJekzfs{4puvv{22>|#(7Nzc(TS?VeKCu%Kr_`JD(hZ0sZjHh?(8Iz zskbwA-nBI4NO^g|B+Rds&nTG2_`2>Y7?X=jyXWUkoca}Lo_HBBPy9GAFQCG$vOc1R zrXjk)de9uq63F9$6Vp{@MS^A@>bt=a_F(qdvQWJ+WBP@$_u7V(hl@e`@aVmxf*;6n8&ZO_=_#N z9GIP)0L*&Xz;JF}(2ou$P%1DFi~(lFMxUaO1M{V_519EofZ4!0VAfv_%;OhY^mJg> z8w1RGw*qq%GAy~vl1G|!m=^rKG9xk}Qp}`48$)OmLOYtO&^}MHts#D(0NOzmLK~zi zwA)BV8?q}dKg1^Jpxm}TQ9vQ^Hh?z-Ji$EK0II)9#YFo=9}2`6^0RPT13YItr>3P- z_Vs;9wll;N6liBSzW}cfWykoPEh02+0JU!CmqXDTW!rVW0BRWX#c2w~8nQpG(ZQy) zBF-lkkc>0LyA(i+)x;Sx2iNN`$ipb6olmX+g+vKzl-tH9KLs_wq$;9)G74#SyOKe3 z1Ss5n(1c8^BpGkW2JmvgbFwXQJB8v6$JHqMG`_#cA(>z}7a?>LRDxlkW4b1ImpKQ%R!ECfKcm0yq$YNWtxMpq1O{iST_b|Bp^bxB--zsh2B7O z2j4|6lT%ULLxE&NJVT*mLtbsKl0@)0ITV*W$0UL(dh}`mA`66|>JC2n8K{1skZD+n z#Djq&7mT6`$g)A*0xHtnwxtwEF~mL!r5G~YrD=m8X+_mZK5-k#8w_U%yfNlUIEJ2; zO4zI}xNZ3is`GZYMLBtUoK>I#v?R$d-FRd;tIeF13qbV+6=9yy_d&7y4l^+tKyfl* zKM?Iq3Z)uyYDYB(5(4KOr=Y9hJP+Pb)1O{=ns~f0_wzweoXpq{jQ@b-O@{1vy_!!m z(`yVUbyoR?cmWj8r)ICQDA-E$$u@Ydn%8yuS{Me$f&q>%> zJd`{RiPY663qi3rW=_gopg2A)%=labrDD%V?baI=rS6jXROL402jHy=&OhhPU15t;T}-8_bVI%fnzPfqDr5~P z6?^QJv&~JQ*qXcxJr#GpDju~cbzZlF;y9Y8(a~@dE$Z1{4!Bvltnqy&=92XA6A|zj z@*E^=3KwXaPjX#XPnE;G>R6b%@@Y^UQAK_6zhv>q5$l4$Oob%- z8RC5k^fR2Nu^=3retxIBH@|Y+n}Oa?v-dyfDJh#)yrJj~Go??Xr$!t??{<@yg2k#4 zQ_)i+Hle3RG&J!B!<`&+#8c=g=?~~BdnrgNHFg?$%HF0X-k<2H5rYvbHDVEZ%7gvr zsj(dlTW>0QmM`d;_QY?MRfF1&mg5oARtQ^RW$kn0!-cwjwP!u>XJ+*QYGo}4CJhL+9wY@@&r0>^N zipt80^;6Q~2h^xd+-~8hzGbBo-4s>RlX}&RaKB@1@wdPN-~_yD@eWwLlh)8-hbZSz zGJRJwxV3<;0}uvipn>1UdUr$lUw}P;j{qM7J^=&)^8rPGV!#7{5ra2FsKfTN-P4S++$w}y5r00%@vo^Xb6s`GC({+#BYT>KTQ#gaG>8;F2s zk$@I}%iw+i_!6)WP!0GBuov(-;8VZ?z(T;IfX4uj144jBH1>eYc^i5|Y0iO8PUN{3 zN2?D!K6VRwTLIeu+W{O(4&7$JO2G4g7XYgOO94*;<^UcBJOUUExCek2osT(xq4RGN z{;8)W(qjjEcwa&9RlsY2b%6DN4S?4HZvb8dtOhItQ~(I@G@Uxw<+``g+XQ$E@DgAx z;5q7i$QAQGdhY>d9zuh^l*;TwUxv%kM-;i0ZaDJ3bSMHA&m38Rue9Q$x|g@0CHC`| zlA?hdZmGy~G%2Z?f~KW?yES=Ve4BI4lJT_TsP5w6V1t7DCjWPlGcoc>hZvXc&T#ic zf>7O2T^yvAwYvD3I-iY;@VV19?N>5tb(dX(S^Dg4Q|n#pdtg|mJDug(lypfK6||(* zBihq03{IuPXnWJ|;Dk}ePrA#lz?^xr-|Dz6BfDZWrgUc^zBKknkGPL2e$+)BZ9qGl zmj12_qS}k`;TcG3(|}!}arfPwe{#{^MwtdO+>ooGo9VV=9vFB8?GAeLnC`V}IB&lh zLHjd?EH+1ArJDAaw#=oNbD0h_O>M0}dFZkWKXEhXx>8;|y3!%$W~eA!q{^SNMPCv> z>*8~A{j9s}TF#}_u^%TDh3$d^>26Oiw^utwcmAw21(LZgH-(qVcD)MF)BVT1_RXnuf!^ySK2GM|shY zYGErtSD^NypL?KYo?Gm|vao#9X3eRjYZv$Pl zBmY@lB+NhS_fve>tB2Jp58Kd{IHT{$tGHI zPIpCIuWB%N=kCuQTVGY%ERUmI=jOZY3RkOfE3ZG^ZB!@7t=)T@#{R0i?26gaSLbCu z+Vhc<=6EyK4^hRhI?5Xxe)WiN=m>P}D%u)PP0p&9FZrywu*d5u_Iz5G8&cH0XjjLQ zl54x{l*#WvFAEt1J%jSj^DaMfUUykFv*7!~3wArMe!UD@d;qZK@$}|--D}t24zynI zA}yZymE{?3S-VO%JXEu>v%Wy2^4){VRXWc#a`3@bT__8>#5{7T}0C2-*lH<=o|g|ef6EE4d36q z$`bkja=XxHjH`d}@}O0(TUPMCE~EK>#*_R#317Z^bMrHxyX-XQHX)vE*g| zXfwFHL%M4XQiTust)(M6y)xRrz#;J4%luRCEw>(2@pAF@E~i4YFVka9oSVsinQz&% z;NL;Tmvxt24(+{h(4c6?lS>_9JR*Xu3o2dJn!2%np-azQ*5z=#*X+~1wBz!vP1Nty z@`~82)KiezV-JXTIP{fA{NC z6UQ`5#?TU2w(Fm*69%7pVdH}zH_I2&t}DFy!_cy;r-v6#9+L5oL%W)_c2L^}JC+%sm*sM>djPiU6g6=m|D>2m*)6;CuvR#Epx z-DOvxKe-s}G<)qxYL@SzyhdFdphwV}X+awsV_V`EML|*7jJ#>n+_batI{K+G;i;?X UP{_ENAXZV}YH{VRtMdl@8zO9}UH||9 diff --git a/docker-compose.yml b/docker-compose.yml index 8dc4d8f..163a91f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,24 +1,24 @@ -services: - app: - build: - context: . - dockerfile: Dockerfile - ports: - - "3003:3000" - depends_on: - - db - - redis - restart: unless-stopped - - db: - image: postgres:16-alpine - volumes: - - pgdata:/var/lib/postgresql/data - restart: unless-stopped - - redis: - image: redis:7.2-alpine - restart: unless-stopped - -volumes: - pgdata: +services: + app: + build: + context: . + dockerfile: Dockerfile + ports: + - "3003:3000" + depends_on: + - db + - redis + restart: unless-stopped + + db: + image: postgres:16-alpine + volumes: + - pgdata:/var/lib/postgresql/data + restart: unless-stopped + + redis: + image: redis:7.2-alpine + restart: unless-stopped + +volumes: + pgdata: diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 192e10e..9adb55a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,642 +1,642 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? -// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init - - -//// Prisma Configuration //// - -generator client { - provider = "prisma-client-js" -} -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - - - - - -//// Prisma Model //// - -model Media { - id String @id @default(uuid()) - title String @db.Text - titleAlternative Json - slug String @db.Text - pictureMedium String @db.Text - pictureLarge String @db.Text - genres Genre[] @relation("MediaGenres") - country Country @relation("MediaCounty", fields: [countryId], references: [id]) - countryId String - isAiring Boolean @default(false) - isTba Boolean @default(false) - startAiring DateTime - endAiring DateTime - synopsis String @db.Text - nfsw Boolean @default(false) - ageRating AgeRating - mediaType MediaType - source Source - studios Studio[] @relation("MediaStudios") - pendingUpload Boolean @default(true) - uploader User @relation("UserUploadedMedias", fields: [uploadedBy], references: [id]) - uploadedBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - logs MediaLog[] @relation("MediaLogs") - episodes Episode[] @relation("MediaEpisodes") - collections Collection[] @relation("MediaCollections") - reviews MediaReview[] @relation("MediaReviews") - @@map("medias") -} - -model MediaLog { - id String @id @default(uuid()) - status MediaOperation - approval Boolean @default(false) - proposer User @relation("UserProposedMedias", fields: [proposedBy], references: [id]) - proposedBy String - approver User @relation("UserApprovedMedias", fields: [approvedBy], references: [id]) - approvedBy String - media Media @relation("MediaLogs", fields: [mediaId], references: [id]) - mediaId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("media_logs") -} - -model Genre { - id String @id @default(uuid()) - name String @db.VarChar(255) - slug String @db.VarChar(255) - malId Int - malUrl String @db.VarChar(255) - creator User @relation("UserCreatedGenres", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - medias Media[] @relation("MediaGenres") - user_favourite_genres UserPreference[] @relation("UserFavoriteGenres") - @@map("genres") -} - -model Studio { - id String @id @default(uuid()) - name String @db.VarChar(255) - slug String @db.VarChar(255) - logoUrl String @db.Text - colorHex String @db.VarChar(10) - creator User @relation("UserCreatedStudios", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - medias Media[] @relation("MediaStudios") - @@map("studios") -} - -model Country { - id String @id @default(uuid()) - name String @db.VarChar(255) - code String @db.VarChar(5) - flag String @db.VarChar(10) - creator User @relation("UserCreatedCountry", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - medias Media[] @relation("MediaCounty") - user_show_countries UserPreference[] @relation("UserShowContries") - @@map("countries") -} - -model Episode { - id String @id @default(uuid()) - media Media @relation("MediaEpisodes", fields: [mediaId], references: [id]) - mediaId String - episode Int - name String @db.VarChar(255) - pictureThumbnail String @db.Text - viewed BigInt @default(0) - likes BigInt @default(0) - dislikes BigInt @default(0) - pendingUpload Boolean @default(true) - uploader User @relation("UserEpisodes", fields: [uploadedBy], references: [id]) - uploadedBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - user_likes EpisodeLike[] @relation("EpisodeLikes") - videos Video[] @relation("EpisodeVideos") - user_histories WatchHistory[] @relation("EpisodeWatchHistories") - comments Comment[] @relation("EpisodeComments") - @@map("episodes") -} - -model EpisodeLike { - id String @id @default(uuid()) - user User @relation("UserEpisodeLikes", fields: [userId], references: [id]) - userId String - session UserSession @relation("SessionEpisodeLikes", fields: [sessionId], references: [id]) - sessionId String - episode Episode @relation("EpisodeLikes", fields: [episodeId], references: [id]) - episodeId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("episode_likes") -} - -model Video { - id String @id @default(uuid()) - episode Episode @relation("EpisodeVideos", fields: [episodeId], references: [id]) - episodeId String - service VideoService @relation("VideoServices", fields: [serviceId], references: [id]) - serviceId String - code String @db.VarChar(255) - pendingUpload Boolean @default(true) - uploader User @relation("UserVideos", fields: [uploadedBy], references: [id]) - uploadedBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("videos") -} - -model VideoService { - id String @id @default(uuid()) - name String @db.VarChar(255) @unique - domain String @db.VarChar(255) - logo String? @db.Text - hexColor String @db.VarChar(10) - endpointVideo String @db.Text - endpointThumbnail String? @db.Text - creator User @relation("UserVideoServices", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - videos Video[] @relation("VideoServices") - user_preferences UserPreference[] @relation("UserServiceDefault") - @@map("video_services") -} - -model User { - id String @id @default(uuid()) - name String @db.VarChar(255) - username String @unique @db.VarChar(255) - email String @unique @db.Text - password String @db.Text - birthDate DateTime? @db.Date - gender UserGender? - phoneCC Int? - phoneNumber Int? - roles UserRole[] @relation("UserRoles") - bioProfile String? @db.Text - profilePicture String? @db.Text - commentPicture String? @db.Text - preference UserPreference? @relation(fields: [preferenceId], references: [id]) - preferenceId String? @unique - verifiedAt DateTime? - disabledAt DateTime? - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - medias Media[] @relation("UserUploadedMedias") - media_proposeds MediaLog[] @relation("UserProposedMedias") - media_approveds MediaLog[] @relation("UserApprovedMedias") - genres Genre[] @relation("UserCreatedGenres") - studios Studio[] @relation("UserCreatedStudios") - countries Country[] @relation("UserCreatedCountry") - episodes Episode[] @relation("UserEpisodes") - episode_likes EpisodeLike[] @relation("UserEpisodeLikes") - videos Video[] @relation("UserVideos") - video_services VideoService[] @relation("UserVideoServices") - create_roles UserRole[] @relation("UserCreateRoles") - notifications UserNotification[] @relation("UserNotifications") - sessions UserSession[] @relation("UserSession") - logs UserLog[] @relation("UserLogs") - collections Collection[] @relation("UserCollections") - allowed_collections Collection[] @relation("UserSelectedSharingCollention") - watch_histories WatchHistory[] @relation("UserWatchHistories") - media_reviews MediaReview[] @relation("UserMediaReviews") - comments Comment[] @relation("UserComments") - liked_comments CommentLike[] @relation("UserCommentLikes") - reported_comments CommentReport[] @relation("UserReportComments") - approved_comments CommentReport[] @relation("ApprovedReportComments") - create_languages Language[] @relation("UserCreateLanguages") - user_create_email EmailSystemAccount[] @relation("UserCreateSystemAccount") - user_emails EmailSystemHistory[] @relation("UserEmails") - sys_notifications SystemNotification[] @relation("UserCreatorSystemNotifications") - sys_logs SystemLog[] @relation("UserSystemLogs") - @@map("users") -} - -model UserPreference { - id String @id @default(uuid()) - userId User? @relation() - lang Language? @relation("UserPreferenceLang", fields: [langPreference], references: [code]) - langPreference String? - adultFiltering AdultFiltering @default(hide) - adultAlert AdultAlert @default(show) - videoQuality VideoQuality @default(Q1080) - serviceDefault VideoService? @relation("UserServiceDefault", fields: [serviceDefaultId], references: [id]) - serviceDefaultId String? - showContries Country[] @relation("UserShowContries") - favoriteGenres Genre[] @relation("UserFavoriteGenres") - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("user_preferences") -} - -model UserRole { - id String @id @default(uuid()) - name String @db.VarChar(255) @unique - primaryColor String? @db.VarChar(10) - secondaryColor String? @db.VarChar(10) - pictureImage String? @db.Text - badgeImage String? @db.Text - isSuperadmin Boolean @default(false) - canEditMedia Boolean @default(false) - canManageMedia Boolean @default(false) - canEditEpisodes Boolean @default(false) - canManageEpisodes Boolean @default(false) - canEditComment Boolean @default(false) - canManageComment Boolean @default(false) - canEditUser Boolean @default(false) - canManageUser Boolean @default(false) - canEditSystem Boolean @default(false) - canManageSystem Boolean @default(false) - creator User @relation("UserCreateRoles", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - users User[] @relation("UserRoles") - @@map("user_roles") -} - -model UserNotification { - id String @id @default(uuid()) - title String @db.VarChar(255) - content String @db.Text - picture String @db.Text - state UserNotificationState - ctaLink String @db.Text - user User @relation("UserNotifications", fields: [userId], references: [id]) - userId String - isReaded Boolean @default(false) - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("user_notifications") -} - -model UserSession { - id String @id @default(uuid()) - isAuthenticated Boolean @default(false) - user User @relation("UserSession", fields: [userId], references: [id]) - userId String - deviceType String @db.VarChar(255) - deviceOs String @db.VarChar(255) - deviceIp String @db.VarChar(255) - isOnline Boolean @default(false) - lastOnline DateTime @default(now()) - validUntil DateTime - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - logs UserLog[] @relation("UserSessionLogs") - episode_likes EpisodeLike[] @relation("SessionEpisodeLikes") - watch_histories WatchHistory[] @relation("SessionWatchHistories") - @@map("user_sessions") -} - -model UserLog { - id String @id @default(uuid()) - title String @db.VarChar(255) - notes String @db.Text - user User @relation("UserLogs", fields: [userId], references: [id]) - userId String - session UserSession @relation("UserSessionLogs", fields: [sessionId], references: [id]) - sessionId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("user_logs") -} - -model Collection { - id String @id @default(uuid()) - name String @db.VarChar(255) - medias Media[] @relation("MediaCollections") - owner User @relation("UserCollections", fields: [ownerId], references: [id]) - ownerId String - accessStatus AccessStatus @default(private) - password String? @db.VarChar(255) - usersAllowed User[] @relation("UserSelectedSharingCollention") - accessScope AccessScope @default(viewer) - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("collections") -} - -model WatchHistory { - id String @id @default(uuid()) - episode Episode @relation("EpisodeWatchHistories", fields: [id], references: [id]) - episodeId String - user User @relation("UserWatchHistories", fields: [userId], references: [id]) - userId String - session UserSession @relation("SessionWatchHistories", fields: [sessionId], references: [id]) - sessionId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("watch_histories") -} - -model MediaReview { - id String @id @default(uuid()) - media Media @relation("MediaReviews", fields: [mediaId], references:[id]) - mediaId String - rating Int - title String @db.VarChar(255) - text String @db.Text - reaction MediaReviewReaction - creator User @relation("UserMediaReviews", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("movie_reviews") -} - -model Comment { - id String @id @default(uuid()) - episode Episode @relation("EpisodeComments", fields: [episodeId], references: [id]) - episodeId String - text String @db.Text - isParent Boolean @default(false) - parent Comment? @relation("ParentReplyComments", fields: [parentId], references: [id]) - parentId String? - user User @relation("UserComments", fields: [userId], references: [id]) - userId String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - replies Comment[] @relation("ParentReplyComments") - likes CommentLike[] @relation("CommentLikes") - @@map("comments") -} - -model CommentLike { - id String @id @default(uuid()) - comment Comment @relation("CommentLikes", fields: [commentId], references: [id]) - commentId String - user User @relation("UserCommentLikes", fields: [userLiked], references: [id]) - userLiked String - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("comment_likes") -} - -model CommentReport { - id String @id @default(uuid()) - reporter User @relation("UserReportComments", fields: [userReporter], references: [id]) - userReporter String - commentReported String - isSupervisorReport Boolean @default(false) - reason ReportReason - status ReportStatus - description String @db.VarChar(255) - approver User? @relation("ApprovedReportComments", fields: [approvedBy], references: [id]) - approvedBy String? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("comment_reports") -} - -model Language { - id String @id @default(uuid()) - name String @db.VarChar(255) - code String @db.VarChar(5) @unique - countryFlag String @db.VarChar(10) - fileLocation String @db.Text - creator User @relation("UserCreateLanguages", fields: [craetedBy], references: [id]) - craetedBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - user_used UserPreference[] @relation("UserPreferenceLang") - @@map("languages") -} - -model EmailSystemAccount { - id String @id @default(uuid()) - name String @unique - host String @db.VarChar(255) - port Int - secure Boolean - email String @unique @db.VarChar(255) - username String @unique @db.VarChar(255) - password String @db.VarChar(255) - purpose EmailPorpose - creator User @relation("UserCreateSystemAccount", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("email_system_accounts") -} - -model EmailSystemHistory { - id String @id @default(uuid()) - purpose EmailPorpose - fromEmail String @db.Text - toEmail String @db.Text - user User @relation("UserEmails", fields: [userRelated], references: [id]) - userRelated String - title String @db.VarChar(255) - htmlContent String @db.Text - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("email_system_histories") -} - -model SystemPreference { - id String @id @default(uuid()) - key String @db.VarChar(225) - value String @db.VarChar(225) - description String @db.Text - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("system_preferences") -} - -model SystemNotification { - id String @id @default(uuid()) - type TypeSystemNotification - componentName String? @db.VarChar(255) - popupImage String? @db.Text - titleToast String? @db.VarChar(255) - contentToast String? @db.Text - creator User @relation("UserCreatorSystemNotifications", fields: [createdBy], references: [id]) - createdBy String - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("system_notifications") -} - -model SystemLog { - id String @id @default(uuid()) - title String @db.VarChar(255) - notes String @db.Text - user User? @relation("UserSystemLogs", fields: [relatedUser], references: [id]) - relatedUser String? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - @@map("system_logs") -} - - - - - -//// Prisma Enum Values //// - -// Media Enum -enum AgeRating { - G // All Ages - PG // Children - PG_13 // Teens 13 or older - R // 17+ (violance & profanity) - R_plus // Mild Nudity - Rx // Hentai -} -enum MediaType { - TV - ONA - OVA - Movie - Special - Music -} -enum Source { - original - manga - light_novel - game -} - -// MediaLog Enum -enum MediaOperation { - create - update - delete -} - -// User Enum -enum UserGender { - male - female -} - -// UserPreference Enum -enum AdultFiltering { - hide - show - explicit -} -enum AdultAlert { - hide - show -} -enum VideoQuality { - Q2160 - Q1440 - Q1080 - Q720 - Q480 - Q360 - Q240 - Q144 -} - -// userNotification Enum -enum UserNotificationState { - info - warning - danger -} - -// CommentReport Enum -enum ReportStatus { - pending - resolved - rejected -} -enum ReportReason { - sexualize - violent - explicit - hateful - political - racist - spam - other -} - -// Collection Enum -enum AccessStatus { - private - selected - protected - public -} -enum AccessScope { - viewer - editor -} - -// MediaReview Enum -enum MediaReviewReaction { - angry - sad - awesome - happy - sleepy - annoyed - disgusting - disappointed -} - -// EmailSystemHistory Enum -enum EmailPorpose { - forgot_password - account_activation - account_notification - subscribtion -} - -// systemNotification Enum -enum TypeSystemNotification { - component - popup - toast +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + + +//// Prisma Configuration //// + +generator client { + provider = "prisma-client-js" +} +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + + + + + +//// Prisma Model //// + +model Media { + id String @id @default(uuid()) + title String @db.Text + titleAlternative Json + slug String @db.Text + pictureMedium String @db.Text + pictureLarge String @db.Text + genres Genre[] @relation("MediaGenres") + country Country @relation("MediaCounty", fields: [countryId], references: [id]) + countryId String + isAiring Boolean @default(false) + isTba Boolean @default(false) + startAiring DateTime + endAiring DateTime + synopsis String @db.Text + nfsw Boolean @default(false) + ageRating AgeRating + mediaType MediaType + source Source + studios Studio[] @relation("MediaStudios") + pendingUpload Boolean @default(true) + uploader User @relation("UserUploadedMedias", fields: [uploadedBy], references: [id]) + uploadedBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + logs MediaLog[] @relation("MediaLogs") + episodes Episode[] @relation("MediaEpisodes") + collections Collection[] @relation("MediaCollections") + reviews MediaReview[] @relation("MediaReviews") + @@map("medias") +} + +model MediaLog { + id String @id @default(uuid()) + status MediaOperation + approval Boolean @default(false) + proposer User @relation("UserProposedMedias", fields: [proposedBy], references: [id]) + proposedBy String + approver User @relation("UserApprovedMedias", fields: [approvedBy], references: [id]) + approvedBy String + media Media @relation("MediaLogs", fields: [mediaId], references: [id]) + mediaId String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("media_logs") +} + +model Genre { + id String @id @default(uuid()) + name String @db.VarChar(255) + slug String @db.VarChar(255) + malId Int + malUrl String @db.VarChar(255) + creator User @relation("UserCreatedGenres", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + medias Media[] @relation("MediaGenres") + user_favourite_genres UserPreference[] @relation("UserFavoriteGenres") + @@map("genres") +} + +model Studio { + id String @id @default(uuid()) + name String @db.VarChar(255) + slug String @db.VarChar(255) + logoUrl String @db.Text + colorHex String @db.VarChar(10) + creator User @relation("UserCreatedStudios", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + medias Media[] @relation("MediaStudios") + @@map("studios") +} + +model Country { + id String @id @default(uuid()) + name String @db.VarChar(255) + code String @db.VarChar(5) + flag String @db.VarChar(10) + creator User @relation("UserCreatedCountry", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + medias Media[] @relation("MediaCounty") + user_show_countries UserPreference[] @relation("UserShowContries") + @@map("countries") +} + +model Episode { + id String @id @default(uuid()) + media Media @relation("MediaEpisodes", fields: [mediaId], references: [id]) + mediaId String + episode Int + name String @db.VarChar(255) + pictureThumbnail String @db.Text + viewed BigInt @default(0) + likes BigInt @default(0) + dislikes BigInt @default(0) + pendingUpload Boolean @default(true) + uploader User @relation("UserEpisodes", fields: [uploadedBy], references: [id]) + uploadedBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + user_likes EpisodeLike[] @relation("EpisodeLikes") + videos Video[] @relation("EpisodeVideos") + user_histories WatchHistory[] @relation("EpisodeWatchHistories") + comments Comment[] @relation("EpisodeComments") + @@map("episodes") +} + +model EpisodeLike { + id String @id @default(uuid()) + user User @relation("UserEpisodeLikes", fields: [userId], references: [id]) + userId String + session UserSession @relation("SessionEpisodeLikes", fields: [sessionId], references: [id]) + sessionId String + episode Episode @relation("EpisodeLikes", fields: [episodeId], references: [id]) + episodeId String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("episode_likes") +} + +model Video { + id String @id @default(uuid()) + episode Episode @relation("EpisodeVideos", fields: [episodeId], references: [id]) + episodeId String + service VideoService @relation("VideoServices", fields: [serviceId], references: [id]) + serviceId String + code String @db.VarChar(255) + pendingUpload Boolean @default(true) + uploader User @relation("UserVideos", fields: [uploadedBy], references: [id]) + uploadedBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("videos") +} + +model VideoService { + id String @id @default(uuid()) + name String @db.VarChar(255) @unique + domain String @db.VarChar(255) + logo String? @db.Text + hexColor String @db.VarChar(10) + endpointVideo String @db.Text + endpointThumbnail String? @db.Text + creator User @relation("UserVideoServices", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + videos Video[] @relation("VideoServices") + user_preferences UserPreference[] @relation("UserServiceDefault") + @@map("video_services") +} + +model User { + id String @id @default(uuid()) + name String @db.VarChar(255) + username String @unique @db.VarChar(255) + email String @unique @db.Text + password String @db.Text + birthDate DateTime? @db.Date + gender UserGender? + phoneCC Int? + phoneNumber Int? + roles UserRole[] @relation("UserRoles") + bioProfile String? @db.Text + profilePicture String? @db.Text + commentPicture String? @db.Text + preference UserPreference? @relation(fields: [preferenceId], references: [id]) + preferenceId String? @unique + verifiedAt DateTime? + disabledAt DateTime? + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + medias Media[] @relation("UserUploadedMedias") + media_proposeds MediaLog[] @relation("UserProposedMedias") + media_approveds MediaLog[] @relation("UserApprovedMedias") + genres Genre[] @relation("UserCreatedGenres") + studios Studio[] @relation("UserCreatedStudios") + countries Country[] @relation("UserCreatedCountry") + episodes Episode[] @relation("UserEpisodes") + episode_likes EpisodeLike[] @relation("UserEpisodeLikes") + videos Video[] @relation("UserVideos") + video_services VideoService[] @relation("UserVideoServices") + create_roles UserRole[] @relation("UserCreateRoles") + notifications UserNotification[] @relation("UserNotifications") + sessions UserSession[] @relation("UserSession") + logs UserLog[] @relation("UserLogs") + collections Collection[] @relation("UserCollections") + allowed_collections Collection[] @relation("UserSelectedSharingCollention") + watch_histories WatchHistory[] @relation("UserWatchHistories") + media_reviews MediaReview[] @relation("UserMediaReviews") + comments Comment[] @relation("UserComments") + liked_comments CommentLike[] @relation("UserCommentLikes") + reported_comments CommentReport[] @relation("UserReportComments") + approved_comments CommentReport[] @relation("ApprovedReportComments") + create_languages Language[] @relation("UserCreateLanguages") + user_create_email EmailSystemAccount[] @relation("UserCreateSystemAccount") + user_emails EmailSystemHistory[] @relation("UserEmails") + sys_notifications SystemNotification[] @relation("UserCreatorSystemNotifications") + sys_logs SystemLog[] @relation("UserSystemLogs") + @@map("users") +} + +model UserPreference { + id String @id @default(uuid()) + userId User? @relation() + lang Language? @relation("UserPreferenceLang", fields: [langPreference], references: [code]) + langPreference String? + adultFiltering AdultFiltering @default(hide) + adultAlert AdultAlert @default(show) + videoQuality VideoQuality @default(Q1080) + serviceDefault VideoService? @relation("UserServiceDefault", fields: [serviceDefaultId], references: [id]) + serviceDefaultId String? + showContries Country[] @relation("UserShowContries") + favoriteGenres Genre[] @relation("UserFavoriteGenres") + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("user_preferences") +} + +model UserRole { + id String @id @default(uuid()) + name String @db.VarChar(255) @unique + primaryColor String? @db.VarChar(10) + secondaryColor String? @db.VarChar(10) + pictureImage String? @db.Text + badgeImage String? @db.Text + isSuperadmin Boolean @default(false) + canEditMedia Boolean @default(false) + canManageMedia Boolean @default(false) + canEditEpisodes Boolean @default(false) + canManageEpisodes Boolean @default(false) + canEditComment Boolean @default(false) + canManageComment Boolean @default(false) + canEditUser Boolean @default(false) + canManageUser Boolean @default(false) + canEditSystem Boolean @default(false) + canManageSystem Boolean @default(false) + creator User @relation("UserCreateRoles", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + users User[] @relation("UserRoles") + @@map("user_roles") +} + +model UserNotification { + id String @id @default(uuid()) + title String @db.VarChar(255) + content String @db.Text + picture String @db.Text + state UserNotificationState + ctaLink String @db.Text + user User @relation("UserNotifications", fields: [userId], references: [id]) + userId String + isReaded Boolean @default(false) + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("user_notifications") +} + +model UserSession { + id String @id @default(uuid()) + isAuthenticated Boolean @default(false) + user User @relation("UserSession", fields: [userId], references: [id]) + userId String + deviceType String @db.VarChar(255) + deviceOs String @db.VarChar(255) + deviceIp String @db.VarChar(255) + isOnline Boolean @default(false) + lastOnline DateTime @default(now()) + validUntil DateTime + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + logs UserLog[] @relation("UserSessionLogs") + episode_likes EpisodeLike[] @relation("SessionEpisodeLikes") + watch_histories WatchHistory[] @relation("SessionWatchHistories") + @@map("user_sessions") +} + +model UserLog { + id String @id @default(uuid()) + title String @db.VarChar(255) + notes String @db.Text + user User @relation("UserLogs", fields: [userId], references: [id]) + userId String + session UserSession @relation("UserSessionLogs", fields: [sessionId], references: [id]) + sessionId String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("user_logs") +} + +model Collection { + id String @id @default(uuid()) + name String @db.VarChar(255) + medias Media[] @relation("MediaCollections") + owner User @relation("UserCollections", fields: [ownerId], references: [id]) + ownerId String + accessStatus AccessStatus @default(private) + password String? @db.VarChar(255) + usersAllowed User[] @relation("UserSelectedSharingCollention") + accessScope AccessScope @default(viewer) + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("collections") +} + +model WatchHistory { + id String @id @default(uuid()) + episode Episode @relation("EpisodeWatchHistories", fields: [id], references: [id]) + episodeId String + user User @relation("UserWatchHistories", fields: [userId], references: [id]) + userId String + session UserSession @relation("SessionWatchHistories", fields: [sessionId], references: [id]) + sessionId String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("watch_histories") +} + +model MediaReview { + id String @id @default(uuid()) + media Media @relation("MediaReviews", fields: [mediaId], references:[id]) + mediaId String + rating Int + title String @db.VarChar(255) + text String @db.Text + reaction MediaReviewReaction + creator User @relation("UserMediaReviews", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("movie_reviews") +} + +model Comment { + id String @id @default(uuid()) + episode Episode @relation("EpisodeComments", fields: [episodeId], references: [id]) + episodeId String + text String @db.Text + isParent Boolean @default(false) + parent Comment? @relation("ParentReplyComments", fields: [parentId], references: [id]) + parentId String? + user User @relation("UserComments", fields: [userId], references: [id]) + userId String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + replies Comment[] @relation("ParentReplyComments") + likes CommentLike[] @relation("CommentLikes") + @@map("comments") +} + +model CommentLike { + id String @id @default(uuid()) + comment Comment @relation("CommentLikes", fields: [commentId], references: [id]) + commentId String + user User @relation("UserCommentLikes", fields: [userLiked], references: [id]) + userLiked String + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("comment_likes") +} + +model CommentReport { + id String @id @default(uuid()) + reporter User @relation("UserReportComments", fields: [userReporter], references: [id]) + userReporter String + commentReported String + isSupervisorReport Boolean @default(false) + reason ReportReason + status ReportStatus + description String @db.VarChar(255) + approver User? @relation("ApprovedReportComments", fields: [approvedBy], references: [id]) + approvedBy String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("comment_reports") +} + +model Language { + id String @id @default(uuid()) + name String @db.VarChar(255) + code String @db.VarChar(5) @unique + countryFlag String @db.VarChar(10) + fileLocation String @db.Text + creator User @relation("UserCreateLanguages", fields: [craetedBy], references: [id]) + craetedBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + + user_used UserPreference[] @relation("UserPreferenceLang") + @@map("languages") +} + +model EmailSystemAccount { + id String @id @default(uuid()) + name String @unique + host String @db.VarChar(255) + port Int + secure Boolean + email String @unique @db.VarChar(255) + username String @unique @db.VarChar(255) + password String @db.VarChar(255) + purpose EmailPorpose + creator User @relation("UserCreateSystemAccount", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("email_system_accounts") +} + +model EmailSystemHistory { + id String @id @default(uuid()) + purpose EmailPorpose + fromEmail String @db.Text + toEmail String @db.Text + user User @relation("UserEmails", fields: [userRelated], references: [id]) + userRelated String + title String @db.VarChar(255) + htmlContent String @db.Text + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("email_system_histories") +} + +model SystemPreference { + id String @id @default(uuid()) + key String @db.VarChar(225) + value String @db.VarChar(225) + description String @db.Text + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("system_preferences") +} + +model SystemNotification { + id String @id @default(uuid()) + type TypeSystemNotification + componentName String? @db.VarChar(255) + popupImage String? @db.Text + titleToast String? @db.VarChar(255) + contentToast String? @db.Text + creator User @relation("UserCreatorSystemNotifications", fields: [createdBy], references: [id]) + createdBy String + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("system_notifications") +} + +model SystemLog { + id String @id @default(uuid()) + title String @db.VarChar(255) + notes String @db.Text + user User? @relation("UserSystemLogs", fields: [relatedUser], references: [id]) + relatedUser String? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + @@map("system_logs") +} + + + + + +//// Prisma Enum Values //// + +// Media Enum +enum AgeRating { + G // All Ages + PG // Children + PG_13 // Teens 13 or older + R // 17+ (violance & profanity) + R_plus // Mild Nudity + Rx // Hentai +} +enum MediaType { + TV + ONA + OVA + Movie + Special + Music +} +enum Source { + original + manga + light_novel + game +} + +// MediaLog Enum +enum MediaOperation { + create + update + delete +} + +// User Enum +enum UserGender { + male + female +} + +// UserPreference Enum +enum AdultFiltering { + hide + show + explicit +} +enum AdultAlert { + hide + show +} +enum VideoQuality { + Q2160 + Q1440 + Q1080 + Q720 + Q480 + Q360 + Q240 + Q144 +} + +// userNotification Enum +enum UserNotificationState { + info + warning + danger +} + +// CommentReport Enum +enum ReportStatus { + pending + resolved + rejected +} +enum ReportReason { + sexualize + violent + explicit + hateful + political + racist + spam + other +} + +// Collection Enum +enum AccessStatus { + private + selected + protected + public +} +enum AccessScope { + viewer + editor +} + +// MediaReview Enum +enum MediaReviewReaction { + angry + sad + awesome + happy + sleepy + annoyed + disgusting + disappointed +} + +// EmailSystemHistory Enum +enum EmailPorpose { + forgot_password + account_activation + account_notification + subscribtion +} + +// systemNotification Enum +enum TypeSystemNotification { + component + popup + toast } \ No newline at end of file diff --git a/scripts/create-example-env.ts b/scripts/create-example-env.ts index 32e7423..463dc78 100644 --- a/scripts/create-example-env.ts +++ b/scripts/create-example-env.ts @@ -1,52 +1,52 @@ -import fs from "fs"; -import path from "path"; - -const PRESERVED_KEYS = [ - "APP_NAME", - "APP_ENV", - "PORT", - "API_KEY", - "ALLOWED_ORIGINS", - "REDIS_HOST", - "REDIS_PORT", -]; - -try { - const envPath = path.join(process.cwd(), ".env"); - const envExamplePath = path.join(process.cwd(), ".env.example"); - - if (!fs.existsSync(envPath)) { - console.error(`.env file not found at ${envPath}`); - process.exit(1); - } - - const envContent = fs.readFileSync(envPath, "utf-8"); - - const lines = envContent.split("\n"); - const processedLines = lines.map((line) => { - const trimmedLine = line.trim(); - - if (trimmedLine.startsWith("#") || trimmedLine === "") { - return line; - } - - const delimeterIndex = line.indexOf("="); - if (delimeterIndex === -1) { - return line; - } - - const key = line.substring(0, delimeterIndex).trim(); - const value = line.substring(delimeterIndex + 1).trim(); - - if (PRESERVED_KEYS.includes(key)) { - return `${key}=${value}`; - } - return `${key}=`; - }); - - fs.writeFileSync(envExamplePath, processedLines.join("\n")); - console.log("File .env.example berhasil diperbarui!"); -} catch (error) { - console.error("Error while creating .env.example:", error); - process.exit(1); -} +import fs from "fs"; +import path from "path"; + +const PRESERVED_KEYS = [ + "APP_NAME", + "APP_ENV", + "PORT", + "API_KEY", + "ALLOWED_ORIGINS", + "REDIS_HOST", + "REDIS_PORT", +]; + +try { + const envPath = path.join(process.cwd(), ".env"); + const envExamplePath = path.join(process.cwd(), ".env.example"); + + if (!fs.existsSync(envPath)) { + console.error(`.env file not found at ${envPath}`); + process.exit(1); + } + + const envContent = fs.readFileSync(envPath, "utf-8"); + + const lines = envContent.split("\n"); + const processedLines = lines.map((line) => { + const trimmedLine = line.trim(); + + if (trimmedLine.startsWith("#") || trimmedLine === "") { + return line; + } + + const delimeterIndex = line.indexOf("="); + if (delimeterIndex === -1) { + return line; + } + + const key = line.substring(0, delimeterIndex).trim(); + const value = line.substring(delimeterIndex + 1).trim(); + + if (PRESERVED_KEYS.includes(key)) { + return `${key}=${value}`; + } + return `${key}=`; + }); + + fs.writeFileSync(envExamplePath, processedLines.join("\n")); + console.log("File .env.example berhasil diperbarui!"); +} catch (error) { + console.error("Error while creating .env.example:", error); + process.exit(1); +} diff --git a/scripts/sync-routes.ts b/scripts/sync-routes.ts index 03aa674..8b061a9 100644 --- a/scripts/sync-routes.ts +++ b/scripts/sync-routes.ts @@ -1,51 +1,51 @@ -/** - * Dynamically aggregates Elysia sub-routes from modular directories into a central registry. - * - * @behavior - * 1. Scans `./src/modules` for valid Elysia modules - * 2. Generates imports and `.use()` calls for each module - * 3. Writes composed routes to `./src/routes.ts` - * - * @requirements - * - Module directories must contain an export named `[folderName]Module` - * (e.g., `userModule` for `/user` folder) - * - Modules must export an Elysia instance - * - * @outputfile ./src/routes.ts - * @examplegenerated - * ```ts - * import Elysia from "elysia"; - * import { userModule } from './modules/user'; - * import { authModule } from './modules/auth'; - * - * const routes = new Elysia() - * .use(userModule) - * .use(authModule); - * - * export { routes }; - * ``` - */ -import { writeFileSync, readdirSync } from "fs"; -import { join } from "path"; - -const modulesPath = "./src/modules"; -const importLines: string[] = []; -const useLines: string[] = []; - -for (const folder of readdirSync(modulesPath, { withFileTypes: true })) { - if (folder.isDirectory()) { - const varName = `${folder.name}Module`; - importLines.push(`import {${varName}} from './modules/${folder.name}';`); - useLines.push(`.use(${varName})`); - } -} - -const content = ` -import Elysia from "elysia"; -${importLines.join("\n")} -const routes = new Elysia() -${useLines.join("\n")}; -export { routes }; -`; - -writeFileSync(join(modulesPath, "../routes.ts"), content); +/** + * Dynamically aggregates Elysia sub-routes from modular directories into a central registry. + * + * @behavior + * 1. Scans `./src/modules` for valid Elysia modules + * 2. Generates imports and `.use()` calls for each module + * 3. Writes composed routes to `./src/routes.ts` + * + * @requirements + * - Module directories must contain an export named `[folderName]Module` + * (e.g., `userModule` for `/user` folder) + * - Modules must export an Elysia instance + * + * @outputfile ./src/routes.ts + * @examplegenerated + * ```ts + * import Elysia from "elysia"; + * import { userModule } from './modules/user'; + * import { authModule } from './modules/auth'; + * + * const routes = new Elysia() + * .use(userModule) + * .use(authModule); + * + * export { routes }; + * ``` + */ +import { writeFileSync, readdirSync } from "fs"; +import { join } from "path"; + +const modulesPath = "./src/modules"; +const importLines: string[] = []; +const useLines: string[] = []; + +for (const folder of readdirSync(modulesPath, { withFileTypes: true })) { + if (folder.isDirectory()) { + const varName = `${folder.name}Module`; + importLines.push(`import {${varName}} from './modules/${folder.name}';`); + useLines.push(`.use(${varName})`); + } +} + +const content = ` +import Elysia from "elysia"; +${importLines.join("\n")} +const routes = new Elysia() +${useLines.join("\n")}; +export { routes }; +`; + +writeFileSync(join(modulesPath, "../routes.ts"), content); diff --git a/src/constants/cookie.keys.ts b/src/constants/cookie.keys.ts index b6d682a..9d16cbb 100644 --- a/src/constants/cookie.keys.ts +++ b/src/constants/cookie.keys.ts @@ -1,4 +1,4 @@ -export const COOKIE_KEYS = { - AUTH: "auth_token", - CSRF: "csrf_token", -}; +export const COOKIE_KEYS = { + AUTH: "auth_token", + CSRF: "csrf_token", +}; diff --git a/src/helpers/callback/httpResponse.ts b/src/helpers/callback/httpResponse.ts index 2a9a2c3..98d687b 100644 --- a/src/helpers/callback/httpResponse.ts +++ b/src/helpers/callback/httpResponse.ts @@ -1,80 +1,80 @@ -/** - * Returns a standardized response for write operations (POST, PUT, DELETE). - * Only includes data in the response during development. - * - * @param set - Function to set HTTP headers. - * @param status - HTTP status code of the response. - * @param message - A message describing the result. - * @param data - Optional data for success responses or error description (only returned in development). - * - * @returns An object with `status`, `message`, and optionally `data` (in development only). - */ -export function returnWriteResponse( - set: any, - status: number, - message?: string, - data?: any -) { - set.status = status; - - const response: Record = { - status, - message, - }; - if (process.env.APP_ENV === "development") response.data = data; - - return response; -} - -/** - * Returns a standardized response for read operations (GET). - * Always includes data in the response regardless of the environment. - * - * @param set - Function to set HTTP headers. - * @param status - HTTP status code of the response. - * @param message - A message describing the result. - * @param data - Data to include in the response. - * - * @returns An object with `status`, `message`, and `data`. - */ -export function returnReadResponse( - set: any, - status: number, - message: string, - data: any -) { - set.status = status; - return { - status, - message, - data, - }; -} - -/** - * Returns a standardized error response for handling errors in catch blocks. - * - * @param set - Function to set HTTP headers. - * @param status - HTTP status code of the error response. - * @param message - A message describing the error. - * @param errorDetails - Optional, detailed information about the error (e.g., stack trace). - * - * @returns An object with `status`, `message`, and optionally `error_details` (in development only). - */ -export function returnErrorResponse( - set: any, - status: number, - message: string, - errorDetails?: any -) { - set.status = status; - - const response: Record = { - status: "error", - message, - }; - if (process.env.APP_ENV === "development" && errorDetails) - response.error_details = errorDetails; - - return response; -} +/** + * Returns a standardized response for write operations (POST, PUT, DELETE). + * Only includes data in the response during development. + * + * @param set - Function to set HTTP headers. + * @param status - HTTP status code of the response. + * @param message - A message describing the result. + * @param data - Optional data for success responses or error description (only returned in development). + * + * @returns An object with `status`, `message`, and optionally `data` (in development only). + */ +export function returnWriteResponse( + set: any, + status: number, + message?: string, + data?: any +) { + set.status = status; + + const response: Record = { + status, + message, + }; + if (process.env.APP_ENV === "development") response.data = data; + + return response; +} + +/** + * Returns a standardized response for read operations (GET). + * Always includes data in the response regardless of the environment. + * + * @param set - Function to set HTTP headers. + * @param status - HTTP status code of the response. + * @param message - A message describing the result. + * @param data - Data to include in the response. + * + * @returns An object with `status`, `message`, and `data`. + */ +export function returnReadResponse( + set: any, + status: number, + message: string, + data: any +) { + set.status = status; + return { + status, + message, + data, + }; +} + +/** + * Returns a standardized error response for handling errors in catch blocks. + * + * @param set - Function to set HTTP headers. + * @param status - HTTP status code of the error response. + * @param message - A message describing the error. + * @param errorDetails - Optional, detailed information about the error (e.g., stack trace). + * + * @returns An object with `status`, `message`, and optionally `error_details` (in development only). + */ +export function returnErrorResponse( + set: any, + status: number, + message: string, + errorDetails?: any +) { + set.status = status; + + const response: Record = { + status: "error", + message, + }; + if (process.env.APP_ENV === "development" && errorDetails) + response.error_details = errorDetails; + + return response; +} diff --git a/src/helpers/error/handler/index.ts b/src/helpers/error/handler/index.ts index 16cccbb..814c654 100644 --- a/src/helpers/error/handler/index.ts +++ b/src/helpers/error/handler/index.ts @@ -1,54 +1,54 @@ -import { Prisma } from "@prisma/client"; -import { returnErrorResponse } from "../../callback/httpResponse"; -import { AppError } from "../instances/app"; -import { PrismaErrorCodeList } from "../../../utils/databases/prisma/error/codeList"; - -export const mainErrorHandler = (set: any, error: unknown) => { - if (error instanceof AppError) { - return returnErrorResponse( - set, - error.statusCode, - error.message, - error.details - ); - } - - if (error instanceof Prisma.PrismaClientKnownRequestError) { - const errorInfo = PrismaErrorCodeList[error.code]; - - if (errorInfo) - return returnErrorResponse( - set, - errorInfo.status, - errorInfo.message, - error.meta ?? {} - ); - } - - if (error instanceof Prisma.PrismaClientUnknownRequestError) { - return returnErrorResponse(set, 500, "Unknown database error"); - } - - if (error instanceof Prisma.PrismaClientRustPanicError) { - return returnErrorResponse( - set, - 500, - "Database engine crashed unexpectedly" - ); - } - - if (error instanceof Prisma.PrismaClientInitializationError) { - return returnErrorResponse(set, 503, `Can't reach database server.`, error); - } - - if (error instanceof Prisma.PrismaClientValidationError) { - return returnErrorResponse( - set, - 400, - "Invalid input to query", - error.message - ); - } - - return returnErrorResponse(set, 500, "Internal server error"); -}; +import { Prisma } from "@prisma/client"; +import { returnErrorResponse } from "../../callback/httpResponse"; +import { AppError } from "../instances/app"; +import { PrismaErrorCodeList } from "../../../utils/databases/prisma/error/codeList"; + +export const mainErrorHandler = (set: any, error: unknown) => { + if (error instanceof AppError) { + return returnErrorResponse( + set, + error.statusCode, + error.message, + error.details + ); + } + + if (error instanceof Prisma.PrismaClientKnownRequestError) { + const errorInfo = PrismaErrorCodeList[error.code]; + + if (errorInfo) + return returnErrorResponse( + set, + errorInfo.status, + errorInfo.message, + error.meta ?? {} + ); + } + + if (error instanceof Prisma.PrismaClientUnknownRequestError) { + return returnErrorResponse(set, 500, "Unknown database error"); + } + + if (error instanceof Prisma.PrismaClientRustPanicError) { + return returnErrorResponse( + set, + 500, + "Database engine crashed unexpectedly" + ); + } + + if (error instanceof Prisma.PrismaClientInitializationError) { + return returnErrorResponse(set, 503, `Can't reach database server.`, error); + } + + if (error instanceof Prisma.PrismaClientValidationError) { + return returnErrorResponse( + set, + 400, + "Invalid input to query", + error.message + ); + } + + return returnErrorResponse(set, 500, "Internal server error"); +}; diff --git a/src/helpers/error/instances/app.ts b/src/helpers/error/instances/app.ts index 0d80636..2476f58 100644 --- a/src/helpers/error/instances/app.ts +++ b/src/helpers/error/instances/app.ts @@ -1,13 +1,13 @@ -export class AppError extends Error { - public readonly statusCode: number; - public readonly details?: any; - - constructor(statusCode = 400, message: string, details?: any) { - super(message); - this.name = "AppError"; - this.statusCode = statusCode; - this.details = details; - - Object.setPrototypeOf(this, AppError.prototype); - } -} +export class AppError extends Error { + public readonly statusCode: number; + public readonly details?: any; + + constructor(statusCode = 400, message: string, details?: any) { + super(message); + this.name = "AppError"; + this.statusCode = statusCode; + this.details = details; + + Object.setPrototypeOf(this, AppError.prototype); + } +} diff --git a/src/helpers/error/instances/forwarder.ts b/src/helpers/error/instances/forwarder.ts index f3d511f..4bde91f 100644 --- a/src/helpers/error/instances/forwarder.ts +++ b/src/helpers/error/instances/forwarder.ts @@ -1,21 +1,21 @@ -import { Prisma } from "@prisma/client"; -import { AppError } from "./app"; - -export function ErrorForwarder( - cause: unknown, - statusCode: number = 500, - message: string = "Unexpected error" -): never { - if ( - cause instanceof AppError || - cause instanceof Prisma.PrismaClientKnownRequestError || - cause instanceof Prisma.PrismaClientUnknownRequestError || - cause instanceof Prisma.PrismaClientRustPanicError || - cause instanceof Prisma.PrismaClientInitializationError || - cause instanceof Prisma.PrismaClientValidationError - ) { - throw cause; - } - - throw new AppError(statusCode, message, cause); -} +import { Prisma } from "@prisma/client"; +import { AppError } from "./app"; + +export function ErrorForwarder( + cause: unknown, + statusCode: number = 500, + message: string = "Unexpected error" +): never { + if ( + cause instanceof AppError || + cause instanceof Prisma.PrismaClientKnownRequestError || + cause instanceof Prisma.PrismaClientUnknownRequestError || + cause instanceof Prisma.PrismaClientRustPanicError || + cause instanceof Prisma.PrismaClientInitializationError || + cause instanceof Prisma.PrismaClientValidationError + ) { + throw cause; + } + + throw new AppError(statusCode, message, cause); +} diff --git a/src/helpers/http/jwt/decode/index.ts b/src/helpers/http/jwt/decode/index.ts index 1034f70..539228c 100644 --- a/src/helpers/http/jwt/decode/index.ts +++ b/src/helpers/http/jwt/decode/index.ts @@ -1,14 +1,14 @@ -import jwt from "jsonwebtoken"; - -export const jwtDecode = (payload: string) => { - // return payload; - if (!payload) throw "JWT decode payload not found"; - const JWTKey = process.env.JWT_SECRET!; - - try { - const decodedPayload = jwt.verify(payload, JWTKey); - return decodedPayload; - } catch (error) { - throw "JWT expired or not valid"; - } -}; +import jwt from "jsonwebtoken"; + +export const jwtDecode = (payload: string) => { + // return payload; + if (!payload) throw "JWT decode payload not found"; + const JWTKey = process.env.JWT_SECRET!; + + try { + const decodedPayload = jwt.verify(payload, JWTKey); + return decodedPayload; + } catch (error) { + throw "JWT expired or not valid"; + } +}; diff --git a/src/helpers/http/jwt/decode/types.ts b/src/helpers/http/jwt/decode/types.ts index 3d094f0..0320410 100644 --- a/src/helpers/http/jwt/decode/types.ts +++ b/src/helpers/http/jwt/decode/types.ts @@ -1,62 +1,62 @@ -export interface JWTAuthToken { - id: string; - isAuthenticated: boolean; - userId: string; - deviceType: string; - deviceOs: string; - deviceIp: string; - isOnline: boolean; - lastOnline: Date; - validUntil: Date; - deletedAt: null; - createdAt: Date; - updatedAt: Date; - user: User; - iat: number; - exp: number; -} - -interface User { - id: string; - name: string; - username: string; - email: string; - birthDate: null; - gender: null; - phoneCC: null; - phoneNumber: null; - bioProfile: null; - profilePicture: null; - commentPicture: null; - preferenceId: null; - verifiedAt: null; - disabledAt: null; - deletedAt: null; - createdAt: Date; - updatedAt: Date; - roles: Role[]; -} - -interface Role { - id: string; - name: string; - primaryColor: string; - secondaryColor: string; - pictureImage: string; - badgeImage: null; - isSuperadmin: boolean; - canEditMedia: boolean; - canManageMedia: boolean; - canEditEpisodes: boolean; - canManageEpisodes: boolean; - canEditComment: boolean; - canManageComment: boolean; - canEditUser: boolean; - canManageUser: boolean; - canEditSystem: boolean; - canManageSystem: boolean; - createdBy: string; - deletedAt: null; - createdAt: Date; - updatedAt: Date; -} +export interface JWTAuthToken { + id: string; + isAuthenticated: boolean; + userId: string; + deviceType: string; + deviceOs: string; + deviceIp: string; + isOnline: boolean; + lastOnline: Date; + validUntil: Date; + deletedAt: null; + createdAt: Date; + updatedAt: Date; + user: User; + iat: number; + exp: number; +} + +interface User { + id: string; + name: string; + username: string; + email: string; + birthDate: null; + gender: null; + phoneCC: null; + phoneNumber: null; + bioProfile: null; + profilePicture: null; + commentPicture: null; + preferenceId: null; + verifiedAt: null; + disabledAt: null; + deletedAt: null; + createdAt: Date; + updatedAt: Date; + roles: Role[]; +} + +interface Role { + id: string; + name: string; + primaryColor: string; + secondaryColor: string; + pictureImage: string; + badgeImage: null; + isSuperadmin: boolean; + canEditMedia: boolean; + canManageMedia: boolean; + canEditEpisodes: boolean; + canManageEpisodes: boolean; + canEditComment: boolean; + canManageComment: boolean; + canEditUser: boolean; + canManageUser: boolean; + canEditSystem: boolean; + canManageSystem: boolean; + createdBy: string; + deletedAt: null; + createdAt: Date; + updatedAt: Date; +} diff --git a/src/helpers/http/jwt/encode/index.ts b/src/helpers/http/jwt/encode/index.ts index 506bab4..ae7c4ce 100644 --- a/src/helpers/http/jwt/encode/index.ts +++ b/src/helpers/http/jwt/encode/index.ts @@ -1,11 +1,11 @@ -import jwt from "jsonwebtoken"; - -export const jwtEncode = (payload: any) => { - const tokenLifetime = Number(process.env.SESSION_EXPIRE!); - const jwtSecret = process.env.JWT_SECRET!; - const jwtToken = jwt.sign(payload, jwtSecret, { - expiresIn: tokenLifetime, - }); - - return jwtToken; -}; +import jwt from "jsonwebtoken"; + +export const jwtEncode = (payload: any) => { + const tokenLifetime = Number(process.env.SESSION_EXPIRE!); + const jwtSecret = process.env.JWT_SECRET!; + const jwtToken = jwt.sign(payload, jwtSecret, { + expiresIn: tokenLifetime, + }); + + return jwtToken; +}; diff --git a/src/helpers/http/userHeader/cookies/clearCookies.ts b/src/helpers/http/userHeader/cookies/clearCookies.ts index 6882057..654ddd2 100644 --- a/src/helpers/http/userHeader/cookies/clearCookies.ts +++ b/src/helpers/http/userHeader/cookies/clearCookies.ts @@ -1,33 +1,33 @@ -import { serialize } from "cookie"; - -export const clearCookies = ( - set: any, - cookieKeys: string[], - options?: Partial<{ - httpOnly: boolean; - secure: boolean; - sameSite: "strict" | "lax" | "none"; - path: string; - }> -) => { - // Define the default configurations for clearing cookies - const defaultOptions = { - httpOnly: true, - secure: true, - sameSite: "strict" as const, - path: "/", - ...options, - }; - - // Create an array of cleared cookies with the specified names - const clearedCookies = cookieKeys.map((name) => { - return serialize(name, "", { - ...defaultOptions, - expires: new Date(0), - }); - }); - - // Set the cleared cookies in the response headers - set.headers["set-cookie"] = clearedCookies; - return clearedCookies; -}; +import { serialize } from "cookie"; + +export const clearCookies = ( + set: any, + cookieKeys: string[], + options?: Partial<{ + httpOnly: boolean; + secure: boolean; + sameSite: "strict" | "lax" | "none"; + path: string; + }> +) => { + // Define the default configurations for clearing cookies + const defaultOptions = { + httpOnly: true, + secure: true, + sameSite: "strict" as const, + path: "/", + ...options, + }; + + // Create an array of cleared cookies with the specified names + const clearedCookies = cookieKeys.map((name) => { + return serialize(name, "", { + ...defaultOptions, + expires: new Date(0), + }); + }); + + // Set the cleared cookies in the response headers + set.headers["set-cookie"] = clearedCookies; + return clearedCookies; +}; diff --git a/src/helpers/http/userHeader/cookies/getCookies.ts b/src/helpers/http/userHeader/cookies/getCookies.ts index 4af8660..2468751 100644 --- a/src/helpers/http/userHeader/cookies/getCookies.ts +++ b/src/helpers/http/userHeader/cookies/getCookies.ts @@ -1,13 +1,13 @@ -import { parse } from "cookie"; -import { Context } from "elysia"; -import { AppError } from "../../../error/instances/app"; - -export const getCookie = (ctx: Context) => { - try { - const cookiePayload = ctx.request.headers.get("Cookie"); - const cookies = parse(cookiePayload!); - return cookies; - } catch (error) { - throw new AppError(401, "Cookie not found"); - } -}; +import { parse } from "cookie"; +import { Context } from "elysia"; +import { AppError } from "../../../error/instances/app"; + +export const getCookie = (ctx: Context) => { + try { + const cookiePayload = ctx.request.headers.get("Cookie"); + const cookies = parse(cookiePayload!); + return cookies; + } catch (error) { + throw new AppError(401, "Cookie not found"); + } +}; diff --git a/src/helpers/http/userHeader/cookies/setCookies.ts b/src/helpers/http/userHeader/cookies/setCookies.ts index 5c807b6..de65534 100644 --- a/src/helpers/http/userHeader/cookies/setCookies.ts +++ b/src/helpers/http/userHeader/cookies/setCookies.ts @@ -1,34 +1,34 @@ -import { serialize } from "cookie"; - -export const setCookie = async ( - set: any, - name: string, - payload: string, - options?: Partial<{ - httpOnly: boolean; - secure: boolean; - sameSite: "strict" | "lax" | "none"; - maxAge: number; - path: string; - }> -) => { - // Define the default configurations for the cookie - const cookieLifetime = Number(process.env.SESSION_EXPIRE!); - const defaultOptions = { - httpOnly: true, - secure: true, - sameSite: "strict" as const, - maxAge: cookieLifetime, - path: "/", - }; - - // Merge the default options with the provided options - const finalOptions = { ...defaultOptions, ...options }; - - // Create the serialized cookie string - const serializedCookie = serialize(name, payload, finalOptions); - - // Set the cookie in the response headers - set.headers["set-cookie"] = serializedCookie; - return serializedCookie; -}; +import { serialize } from "cookie"; + +export const setCookie = async ( + set: any, + name: string, + payload: string, + options?: Partial<{ + httpOnly: boolean; + secure: boolean; + sameSite: "strict" | "lax" | "none"; + maxAge: number; + path: string; + }> +) => { + // Define the default configurations for the cookie + const cookieLifetime = Number(process.env.SESSION_EXPIRE!); + const defaultOptions = { + httpOnly: true, + secure: true, + sameSite: "strict" as const, + maxAge: cookieLifetime, + path: "/", + }; + + // Merge the default options with the provided options + const finalOptions = { ...defaultOptions, ...options }; + + // Create the serialized cookie string + const serializedCookie = serialize(name, payload, finalOptions); + + // Set the cookie in the response headers + set.headers["set-cookie"] = serializedCookie; + return serializedCookie; +}; diff --git a/src/helpers/http/userHeader/getUserHeaderInformation/index.ts b/src/helpers/http/userHeader/getUserHeaderInformation/index.ts index b9dd853..b842a30 100644 --- a/src/helpers/http/userHeader/getUserHeaderInformation/index.ts +++ b/src/helpers/http/userHeader/getUserHeaderInformation/index.ts @@ -1,26 +1,26 @@ -import { Context } from "elysia"; -import { UAParser } from "ua-parser-js"; -import { UserHeaderInformation } from "./types"; - -export const getUserHeaderInformation = ( - ctx: Context -): UserHeaderInformation => { - const headers = ctx.request.headers; - const userAgentHeader = headers.get("user-agent") || "desktop"; - const userAgent = new UAParser(userAgentHeader); - - const userIP = - headers.get("cf-connecting-ip") || - headers.get("x-real-ip") || - headers.get("x-forwarded-for")?.split(",")[0] || - "Unknown IP"; - - const userHeaderInformation = { - ip: userIP, - deviceType: userAgent.getDevice().type || "desktop", - deviceOS: userAgent.getOS().name + " " + userAgent.getOS().version, - browser: userAgent.getBrowser().name + " " + userAgent.getBrowser().version, - }; - - return userHeaderInformation; -}; +import { Context } from "elysia"; +import { UAParser } from "ua-parser-js"; +import { UserHeaderInformation } from "./types"; + +export const getUserHeaderInformation = ( + ctx: Context +): UserHeaderInformation => { + const headers = ctx.request.headers; + const userAgentHeader = headers.get("user-agent") || "desktop"; + const userAgent = new UAParser(userAgentHeader); + + const userIP = + headers.get("cf-connecting-ip") || + headers.get("x-real-ip") || + headers.get("x-forwarded-for")?.split(",")[0] || + "Unknown IP"; + + const userHeaderInformation = { + ip: userIP, + deviceType: userAgent.getDevice().type || "desktop", + deviceOS: userAgent.getOS().name + " " + userAgent.getOS().version, + browser: userAgent.getBrowser().name + " " + userAgent.getBrowser().version, + }; + + return userHeaderInformation; +}; diff --git a/src/helpers/http/userHeader/getUserHeaderInformation/types.ts b/src/helpers/http/userHeader/getUserHeaderInformation/types.ts index f1a54d1..9b4413b 100644 --- a/src/helpers/http/userHeader/getUserHeaderInformation/types.ts +++ b/src/helpers/http/userHeader/getUserHeaderInformation/types.ts @@ -1,6 +1,6 @@ -export interface UserHeaderInformation { - ip: string; - deviceType: string; - deviceOS: string; - browser: string; -} +export interface UserHeaderInformation { + ip: string; + deviceType: string; + deviceOS: string; + browser: string; +} diff --git a/src/helpers/security/password/hash.ts b/src/helpers/security/password/hash.ts index de949d2..9b4a326 100644 --- a/src/helpers/security/password/hash.ts +++ b/src/helpers/security/password/hash.ts @@ -1,6 +1,6 @@ -import bcrypt from "bcrypt"; - -export const hashPassword = async (password: string): Promise => { - const saltRounds = Number(process.env.SALT_ROUNDS); - return await bcrypt.hash(password, saltRounds); -}; +import bcrypt from "bcrypt"; + +export const hashPassword = async (password: string): Promise => { + const saltRounds = Number(process.env.SALT_ROUNDS); + return await bcrypt.hash(password, saltRounds); +}; diff --git a/src/index.ts b/src/index.ts index d25a260..c55cdcc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ -import { Elysia } from "elysia"; -import { routes } from "./routes"; - -const app = new Elysia().use(routes).listen(process.env.PORT || 3000); - -console.log( - `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` -); +import { Elysia } from "elysia"; +import { routes } from "./routes"; + +const app = new Elysia().use(routes).listen(process.env.PORT || 3000); + +console.log( + `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}` +); diff --git a/src/middleware/auth.middleware.ts b/src/middleware/auth.middleware.ts index b7bed40..e7294c0 100644 --- a/src/middleware/auth.middleware.ts +++ b/src/middleware/auth.middleware.ts @@ -1,14 +1,14 @@ -import { Context } from "elysia"; -import { getCookie } from "../helpers/http/userHeader/cookies/getCookies"; -import { mainErrorHandler } from "../helpers/error/handler"; -import { returnErrorResponse } from "../helpers/callback/httpResponse"; - -export const authMiddleware = (ctx: Context) => { - try { - const cookie = getCookie(ctx); - if (!cookie.auth_token) - return returnErrorResponse(ctx.set, 401, "User Unauthorized"); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; +import { Context } from "elysia"; +import { getCookie } from "../helpers/http/userHeader/cookies/getCookies"; +import { mainErrorHandler } from "../helpers/error/handler"; +import { returnErrorResponse } from "../helpers/callback/httpResponse"; + +export const authMiddleware = (ctx: Context) => { + try { + const cookie = getCookie(ctx); + if (!cookie.auth_token) + return returnErrorResponse(ctx.set, 401, "User Unauthorized"); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/middleware/auth/authenticated.middleware.ts b/src/middleware/auth/authenticated.middleware.ts index df5e5c1..05557a1 100644 --- a/src/middleware/auth/authenticated.middleware.ts +++ b/src/middleware/auth/authenticated.middleware.ts @@ -1,16 +1,16 @@ -import { Context } from "elysia"; -import { getCookie } from "../../helpers/http/userHeader/cookies/getCookies"; -import { returnErrorResponse } from "../../helpers/callback/httpResponse"; -import { mainErrorHandler } from "../../helpers/error/handler"; - -export const authMiddleware = (ctx: Context) => { - try { - const cookie = getCookie(ctx); - if (!cookie.auth_token) - return returnErrorResponse(ctx.set, 401, "User Unauthorized"); - - // pass - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; +import { Context } from "elysia"; +import { getCookie } from "../../helpers/http/userHeader/cookies/getCookies"; +import { returnErrorResponse } from "../../helpers/callback/httpResponse"; +import { mainErrorHandler } from "../../helpers/error/handler"; + +export const authMiddleware = (ctx: Context) => { + try { + const cookie = getCookie(ctx); + if (!cookie.auth_token) + return returnErrorResponse(ctx.set, 401, "User Unauthorized"); + + // pass + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/middleware/auth/unauthenticated.middleware.ts b/src/middleware/auth/unauthenticated.middleware.ts index 3a129c7..bfecabe 100644 --- a/src/middleware/auth/unauthenticated.middleware.ts +++ b/src/middleware/auth/unauthenticated.middleware.ts @@ -1,15 +1,15 @@ -import { Context } from "elysia"; -import { getCookie } from "../../helpers/http/userHeader/cookies/getCookies"; -import { returnErrorResponse } from "../../helpers/callback/httpResponse"; - -export const unautenticatedMiddleware = (ctx: Context) => { - try { - const cookies = getCookie(ctx); - if (cookies.auth_token) - return returnErrorResponse(ctx.set, 403, "User already aunthenticated"); - - // pass - } catch (error) { - // pass - } -}; +import { Context } from "elysia"; +import { getCookie } from "../../helpers/http/userHeader/cookies/getCookies"; +import { returnErrorResponse } from "../../helpers/callback/httpResponse"; + +export const unautenticatedMiddleware = (ctx: Context) => { + try { + const cookies = getCookie(ctx); + if (cookies.auth_token) + return returnErrorResponse(ctx.set, 403, "User already aunthenticated"); + + // pass + } catch (error) { + // pass + } +}; diff --git a/src/modules/auth/auth.types.ts b/src/modules/auth/auth.types.ts index f2ec672..f6463fd 100644 --- a/src/modules/auth/auth.types.ts +++ b/src/modules/auth/auth.types.ts @@ -1,65 +1,65 @@ -export interface LoginWithPasswordRequest { - identifier: string; - password: string; -} - -export interface JWTSessionPayload { - id: string; - isAuthenticated: boolean; - userId: string; - deviceType: string; - deviceOs: string; - deviceIp: string; - isOnline: boolean; - lastOnline: Date; - validUntil: Date; - deletedAt: null; - createdAt: Date; - updatedAt: Date; - user: User; - iat: number; - exp: number; -} -interface User { - id: string; - name: string; - username: string; - email: string; - birthDate: null; - gender: null; - phoneCC: null; - phoneNumber: null; - bioProfile: null; - profilePicture: null; - commentPicture: null; - preferenceId: null; - verifiedAt: null; - disabledAt: null; - deletedAt: null; - createdAt: Date; - updatedAt: Date; - roles: Role[]; -} -interface Role { - id: string; - name: string; - primaryColor: string; - secondaryColor: string; - pictureImage: string; - badgeImage: null; - isSuperadmin: boolean; - canEditMedia: boolean; - canManageMedia: boolean; - canEditEpisodes: boolean; - canManageEpisodes: boolean; - canEditComment: boolean; - canManageComment: boolean; - canEditUser: boolean; - canManageUser: boolean; - canEditSystem: boolean; - canManageSystem: boolean; - createdBy: string; - deletedAt: null; - createdAt: Date; - updatedAt: Date; -} +export interface LoginWithPasswordRequest { + identifier: string; + password: string; +} + +export interface JWTSessionPayload { + id: string; + isAuthenticated: boolean; + userId: string; + deviceType: string; + deviceOs: string; + deviceIp: string; + isOnline: boolean; + lastOnline: Date; + validUntil: Date; + deletedAt: null; + createdAt: Date; + updatedAt: Date; + user: User; + iat: number; + exp: number; +} +interface User { + id: string; + name: string; + username: string; + email: string; + birthDate: null; + gender: null; + phoneCC: null; + phoneNumber: null; + bioProfile: null; + profilePicture: null; + commentPicture: null; + preferenceId: null; + verifiedAt: null; + disabledAt: null; + deletedAt: null; + createdAt: Date; + updatedAt: Date; + roles: Role[]; +} +interface Role { + id: string; + name: string; + primaryColor: string; + secondaryColor: string; + pictureImage: string; + badgeImage: null; + isSuperadmin: boolean; + canEditMedia: boolean; + canManageMedia: boolean; + canEditEpisodes: boolean; + canManageEpisodes: boolean; + canEditComment: boolean; + canManageComment: boolean; + canEditUser: boolean; + canManageUser: boolean; + canEditSystem: boolean; + canManageSystem: boolean; + createdBy: string; + deletedAt: null; + createdAt: Date; + updatedAt: Date; +} diff --git a/src/modules/auth/controller/authVerification.controller.ts b/src/modules/auth/controller/authVerification.controller.ts index 631e835..61e3eb7 100644 --- a/src/modules/auth/controller/authVerification.controller.ts +++ b/src/modules/auth/controller/authVerification.controller.ts @@ -1,27 +1,27 @@ -import { - returnErrorResponse, - returnWriteResponse, -} from "../../../helpers/callback/httpResponse"; -import { Context } from "elysia"; -import { getCookie } from "../../../helpers/http/userHeader/cookies/getCookies"; -import { authVerificationService } from "../services/authVerification.service"; -import { mainErrorHandler } from "../../../helpers/error/handler"; -import { clearCookies } from "../../../helpers/http/userHeader/cookies/clearCookies"; -import { COOKIE_KEYS } from "../../../constants/cookie.keys"; - -export const authVerification = async (ctx: Context) => { - try { - // Get the auth token from cookies - const cookie = getCookie(ctx); - if (!cookie.auth_token) - return returnErrorResponse(ctx.set, 401, "Auth token not found"); - - // Verify the auth token and get the user session - const authService = await authVerificationService(cookie.auth_token); - return returnWriteResponse(ctx.set, 200, "User authenticated", authService); - } catch (error) { - // If token is invalid or expired, clear the auth cookie and return an error response - clearCookies(ctx.set, [COOKIE_KEYS.AUTH]); - return mainErrorHandler(ctx.set, error); - } -}; +import { + returnErrorResponse, + returnWriteResponse, +} from "../../../helpers/callback/httpResponse"; +import { Context } from "elysia"; +import { getCookie } from "../../../helpers/http/userHeader/cookies/getCookies"; +import { authVerificationService } from "../services/authVerification.service"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { clearCookies } from "../../../helpers/http/userHeader/cookies/clearCookies"; +import { COOKIE_KEYS } from "../../../constants/cookie.keys"; + +export const authVerification = async (ctx: Context) => { + try { + // Get the auth token from cookies + const cookie = getCookie(ctx); + if (!cookie.auth_token) + return returnErrorResponse(ctx.set, 401, "Auth token not found"); + + // Verify the auth token and get the user session + const authService = await authVerificationService(cookie.auth_token); + return returnWriteResponse(ctx.set, 200, "User authenticated", authService); + } catch (error) { + // If token is invalid or expired, clear the auth cookie and return an error response + clearCookies(ctx.set, [COOKIE_KEYS.AUTH]); + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/auth/controller/loginWithPassword.controller.ts b/src/modules/auth/controller/loginWithPassword.controller.ts index b2b7509..350e421 100644 --- a/src/modules/auth/controller/loginWithPassword.controller.ts +++ b/src/modules/auth/controller/loginWithPassword.controller.ts @@ -1,41 +1,41 @@ -import { - returnErrorResponse, - returnWriteResponse, -} from "../../../helpers/callback/httpResponse"; -import { Context } from "elysia"; -import { loginWithPasswordService } from "../services/loginWithPassword.service"; -import { LoginWithPasswordRequest } from "../auth.types"; -import { mainErrorHandler } from "../../../helpers/error/handler"; -import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation"; -import { setCookie } from "../../../helpers/http/userHeader/cookies/setCookies"; -import { COOKIE_KEYS } from "../../../constants/cookie.keys"; -import { loginWithPasswordSchema } from "../schemas/loginWithPassword"; - -export const loginWithPassword = async ( - ctx: Context & { body: LoginWithPasswordRequest } -) => { - // Validate the request body against the schema - const { error } = loginWithPasswordSchema.validate(ctx.body); - if (error || !ctx.body) - return returnErrorResponse(ctx.set, 400, "Invalid user input", error); - - // Extract user header information - const userHeaderInfo = getUserHeaderInformation(ctx); - - try { - // Call the service to handle login with password - const jwtToken = await loginWithPasswordService(ctx.body, userHeaderInfo); - - // Set the authentication cookie with the JWT token - setCookie(ctx.set, COOKIE_KEYS.AUTH, jwtToken); - return returnWriteResponse( - ctx.set, - 200, - "Authentication Success", - jwtToken - ); - } catch (error) { - // Handle any errors that occur during the login process - return mainErrorHandler(ctx.set, error); - } -}; +import { + returnErrorResponse, + returnWriteResponse, +} from "../../../helpers/callback/httpResponse"; +import { Context } from "elysia"; +import { loginWithPasswordService } from "../services/loginWithPassword.service"; +import { LoginWithPasswordRequest } from "../auth.types"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation"; +import { setCookie } from "../../../helpers/http/userHeader/cookies/setCookies"; +import { COOKIE_KEYS } from "../../../constants/cookie.keys"; +import { loginWithPasswordSchema } from "../schemas/loginWithPassword"; + +export const loginWithPassword = async ( + ctx: Context & { body: LoginWithPasswordRequest } +) => { + // Validate the request body against the schema + const { error } = loginWithPasswordSchema.validate(ctx.body); + if (error || !ctx.body) + return returnErrorResponse(ctx.set, 400, "Invalid user input", error); + + // Extract user header information + const userHeaderInfo = getUserHeaderInformation(ctx); + + try { + // Call the service to handle login with password + const jwtToken = await loginWithPasswordService(ctx.body, userHeaderInfo); + + // Set the authentication cookie with the JWT token + setCookie(ctx.set, COOKIE_KEYS.AUTH, jwtToken); + return returnWriteResponse( + ctx.set, + 200, + "Authentication Success", + jwtToken + ); + } catch (error) { + // Handle any errors that occur during the login process + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/auth/index.ts b/src/modules/auth/index.ts index 9dba3bc..d2140a3 100644 --- a/src/modules/auth/index.ts +++ b/src/modules/auth/index.ts @@ -1,10 +1,10 @@ -import Elysia from "elysia"; -import { loginWithPassword } from "./controller/loginWithPassword.controller"; -import { authMiddleware } from "../../middleware/auth.middleware"; -import { authVerification } from "./controller/authVerification.controller"; - -export const authModule = new Elysia({ prefix: "/auth" }) - .post("/legacy", loginWithPassword) - .post("/verification", authVerification, { - beforeHandle: authMiddleware, - }); +import Elysia from "elysia"; +import { loginWithPassword } from "./controller/loginWithPassword.controller"; +import { authMiddleware } from "../../middleware/auth.middleware"; +import { authVerification } from "./controller/authVerification.controller"; + +export const authModule = new Elysia({ prefix: "/auth" }) + .post("/legacy", loginWithPassword) + .post("/verification", authVerification, { + beforeHandle: authMiddleware, + }); diff --git a/src/modules/auth/schemas/loginWithPassword.ts b/src/modules/auth/schemas/loginWithPassword.ts index 2d87bb7..6fea031 100644 --- a/src/modules/auth/schemas/loginWithPassword.ts +++ b/src/modules/auth/schemas/loginWithPassword.ts @@ -1,6 +1,6 @@ -import Joi from "joi"; - -export const loginWithPasswordSchema = Joi.object({ - identifier: Joi.string().required(), - password: Joi.string().required(), -}); +import Joi from "joi"; + +export const loginWithPasswordSchema = Joi.object({ + identifier: Joi.string().required(), + password: Joi.string().required(), +}); diff --git a/src/modules/auth/services/authVerification.service.ts b/src/modules/auth/services/authVerification.service.ts index fedcdf7..bdcd4ea 100644 --- a/src/modules/auth/services/authVerification.service.ts +++ b/src/modules/auth/services/authVerification.service.ts @@ -1,44 +1,44 @@ -import { AppError } from "../../../helpers/error/instances/app"; -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; -import { jwtDecode } from "../../../helpers/http/jwt/decode"; -import { checkUserSessionInCacheService } from "../../userSession/services/checkUserSessionInCache.service"; -import { getUserSessionFromDBService } from "../../userSession/services/getUserSessionFromDB.service"; -import { storeUserSessionToCacheService } from "../../userSession/services/storeUserSessionToCache.service"; -import { JWTSessionPayload } from "../auth.types"; - -export const authVerificationService = async (cookie: string) => { - try { - // Decode the JWT token to get the session payload - const jwtSession = jwtDecode(cookie) as JWTSessionPayload; - - // Check if the session exists in Redis - const sessionCheckOnRedis = await checkUserSessionInCacheService( - jwtSession.userId, - jwtSession.id - ); - - if (!sessionCheckOnRedis) { - // If not found in Redis, check the database - const sessionCheckOnDB = await getUserSessionFromDBService(jwtSession.id); - - // If the session found in the database, store it in Redis. if not, throw an error - if (!sessionCheckOnDB) { - throw new AppError(401, "Session invalid or expired"); - } else { - // Store the session in Redis with the remaining time until expiration - const timeExpires = Math.floor( - (new Date(sessionCheckOnDB.validUntil).getTime() - - new Date().getTime()) / - 1000 - ); - await storeUserSessionToCacheService(sessionCheckOnDB, timeExpires); - return sessionCheckOnDB; - } - } else { - // If the session is found in Redis, return it - return jwtSession; - } - } catch (error) { - ErrorForwarder(error, 401, "Token is invalid"); - } -}; +import { AppError } from "../../../helpers/error/instances/app"; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; +import { jwtDecode } from "../../../helpers/http/jwt/decode"; +import { checkUserSessionInCacheService } from "../../userSession/services/checkUserSessionInCache.service"; +import { getUserSessionFromDBService } from "../../userSession/services/getUserSessionFromDB.service"; +import { storeUserSessionToCacheService } from "../../userSession/services/storeUserSessionToCache.service"; +import { JWTSessionPayload } from "../auth.types"; + +export const authVerificationService = async (cookie: string) => { + try { + // Decode the JWT token to get the session payload + const jwtSession = jwtDecode(cookie) as JWTSessionPayload; + + // Check if the session exists in Redis + const sessionCheckOnRedis = await checkUserSessionInCacheService( + jwtSession.userId, + jwtSession.id + ); + + if (!sessionCheckOnRedis) { + // If not found in Redis, check the database + const sessionCheckOnDB = await getUserSessionFromDBService(jwtSession.id); + + // If the session found in the database, store it in Redis. if not, throw an error + if (!sessionCheckOnDB) { + throw new AppError(401, "Session invalid or expired"); + } else { + // Store the session in Redis with the remaining time until expiration + const timeExpires = Math.floor( + (new Date(sessionCheckOnDB.validUntil).getTime() - + new Date().getTime()) / + 1000 + ); + await storeUserSessionToCacheService(sessionCheckOnDB, timeExpires); + return sessionCheckOnDB; + } + } else { + // If the session is found in Redis, return it + return jwtSession; + } + } catch (error) { + ErrorForwarder(error, 401, "Token is invalid"); + } +}; diff --git a/src/modules/auth/services/loginWithPassword.service.ts b/src/modules/auth/services/loginWithPassword.service.ts index 12624c1..f652d55 100644 --- a/src/modules/auth/services/loginWithPassword.service.ts +++ b/src/modules/auth/services/loginWithPassword.service.ts @@ -1,37 +1,37 @@ -import bcrypt from "bcrypt"; -import { findUserByEmailOrUsernameService } from "../../user/services/findUserByEmailOrUsername.service"; -import { LoginWithPasswordRequest } from "../auth.types"; -import { AppError } from "../../../helpers/error/instances/app"; -import { UserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation/types"; -import { createUserSessionService } from "../../userSession/services/createUserSession.service"; -import { jwtEncode } from "../../../helpers/http/jwt/encode"; -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; - -export const loginWithPasswordService = async ( - request: LoginWithPasswordRequest, - userHeaderInfo: UserHeaderInformation -) => { - try { - // search for user data using an identifier (username or email) - const userData = await findUserByEmailOrUsernameService(request.identifier); - - // if user data is not found, throw an error - if (!userData) throw new AppError(404, "User not found"); - - // validate the password in the request with the existing one - if (!(await bcrypt.compare(request.password, userData.password))) - throw new AppError(401, "Password incorrect"); - - // create new user session - const userSession = await createUserSessionService({ - userId: userData.id, - userHeaderInformation: userHeaderInfo, - }); - - // create JWT token that contain user session - const jwtToken = jwtEncode(userSession); - return jwtToken; - } catch (error) { - ErrorForwarder(error); - } -}; +import bcrypt from "bcrypt"; +import { findUserByEmailOrUsernameService } from "../../user/services/findUserByEmailOrUsername.service"; +import { LoginWithPasswordRequest } from "../auth.types"; +import { AppError } from "../../../helpers/error/instances/app"; +import { UserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation/types"; +import { createUserSessionService } from "../../userSession/services/createUserSession.service"; +import { jwtEncode } from "../../../helpers/http/jwt/encode"; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; + +export const loginWithPasswordService = async ( + request: LoginWithPasswordRequest, + userHeaderInfo: UserHeaderInformation +) => { + try { + // search for user data using an identifier (username or email) + const userData = await findUserByEmailOrUsernameService(request.identifier); + + // if user data is not found, throw an error + if (!userData) throw new AppError(404, "User not found"); + + // validate the password in the request with the existing one + if (!(await bcrypt.compare(request.password, userData.password))) + throw new AppError(401, "Password incorrect"); + + // create new user session + const userSession = await createUserSessionService({ + userId: userData.id, + userHeaderInformation: userHeaderInfo, + }); + + // create JWT token that contain user session + const jwtToken = jwtEncode(userSession); + return jwtToken; + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/debug/debug.controller.ts b/src/modules/debug/debug.controller.ts index e4dda41..08c39d6 100644 --- a/src/modules/debug/debug.controller.ts +++ b/src/modules/debug/debug.controller.ts @@ -1,15 +1,15 @@ -import { Context } from "elysia"; -import { mainErrorHandler } from "../../helpers/error/handler"; -import { debugService } from "./debug.service"; -import { returnWriteResponse } from "../../helpers/callback/httpResponse"; - -export const debugController = async (ctx: Context) => { - try { - const dataFromService = await debugService(); - return returnWriteResponse(ctx.set, 200, "Message Sent", dataFromService); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; - -// buat debug untuk date to number (second) +import { Context } from "elysia"; +import { mainErrorHandler } from "../../helpers/error/handler"; +import { debugService } from "./debug.service"; +import { returnWriteResponse } from "../../helpers/callback/httpResponse"; + +export const debugController = async (ctx: Context) => { + try { + const dataFromService = await debugService(); + return returnWriteResponse(ctx.set, 200, "Message Sent", dataFromService); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; + +// buat debug untuk date to number (second) diff --git a/src/modules/debug/debug.service.ts b/src/modules/debug/debug.service.ts index 2f659bf..e01d9db 100644 --- a/src/modules/debug/debug.service.ts +++ b/src/modules/debug/debug.service.ts @@ -1,11 +1,11 @@ -import { ErrorForwarder } from "../../helpers/error/instances/forwarder"; -import { debug2Service } from "./debug2.service"; - -export const debugService = async () => { - try { - const dataFromService = await debug2Service(); - return dataFromService; - } catch (error) { - ErrorForwarder(error); - } -}; +import { ErrorForwarder } from "../../helpers/error/instances/forwarder"; +import { debug2Service } from "./debug2.service"; + +export const debugService = async () => { + try { + const dataFromService = await debug2Service(); + return dataFromService; + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/debug/debug2.service.ts b/src/modules/debug/debug2.service.ts index c5e4fe3..025d15f 100644 --- a/src/modules/debug/debug2.service.ts +++ b/src/modules/debug/debug2.service.ts @@ -1,11 +1,11 @@ -import { ErrorForwarder } from "../../helpers/error/instances/forwarder"; -import { debug3Service } from "./debug3.service"; - -export const debug2Service = async () => { - try { - const dataFromService = await debug3Service(); - return dataFromService; - } catch (error) { - ErrorForwarder(error, 502); - } -}; +import { ErrorForwarder } from "../../helpers/error/instances/forwarder"; +import { debug3Service } from "./debug3.service"; + +export const debug2Service = async () => { + try { + const dataFromService = await debug3Service(); + return dataFromService; + } catch (error) { + ErrorForwarder(error, 502); + } +}; diff --git a/src/modules/debug/debug3.service.ts b/src/modules/debug/debug3.service.ts index 5e7a94e..1cd07b6 100644 --- a/src/modules/debug/debug3.service.ts +++ b/src/modules/debug/debug3.service.ts @@ -1,9 +1,9 @@ -import { AppError } from "../../helpers/error/instances/app"; -import { ErrorForwarder } from "../../helpers/error/instances/forwarder"; - -export const debug3Service = async () => { - // throw new AppError(402, "Error from 3"); - const data = "RAWR"; - // return data; - ErrorForwarder(data); -}; +import { AppError } from "../../helpers/error/instances/app"; +import { ErrorForwarder } from "../../helpers/error/instances/forwarder"; + +export const debug3Service = async () => { + // throw new AppError(402, "Error from 3"); + const data = "RAWR"; + // return data; + ErrorForwarder(data); +}; diff --git a/src/modules/debug/index.ts b/src/modules/debug/index.ts index 9e2d148..465b3a8 100644 --- a/src/modules/debug/index.ts +++ b/src/modules/debug/index.ts @@ -1,7 +1,7 @@ -import Elysia from "elysia"; -import { debugController } from "./debug.controller"; - -export const debugModule = new Elysia({ prefix: "/debug" }).get( - "/", - debugController -); +import Elysia from "elysia"; +import { debugController } from "./debug.controller"; + +export const debugModule = new Elysia({ prefix: "/debug" }).get( + "/", + debugController +); diff --git a/src/modules/user/controller/createUser.controller.ts b/src/modules/user/controller/createUser.controller.ts index 8dc93f8..6f197ea 100644 --- a/src/modules/user/controller/createUser.controller.ts +++ b/src/modules/user/controller/createUser.controller.ts @@ -1,50 +1,50 @@ -import { - returnErrorResponse, - returnWriteResponse, -} from "../../../helpers/callback/httpResponse"; -import { Prisma } from "@prisma/client"; -import { Context } from "elysia"; -import { createUserService } from "../services/createUser.service"; -import { mainErrorHandler } from "../../../helpers/error/handler"; -import { createUserSchema } from "../schemas/createUser.schema"; - -/** - * @function createUser - * @description Creates a new user in the database. - * - * @param {Context & { body: Prisma.UserCreateInput }} ctx - The context object containing the request body. - * @param {Prisma.UserCreateInput} ctx.body - The user data to be created. - * - * @returns {Promise} A response object indicating success or failure. - * @throws {Object} An error response object if validation fails or an error occurs during user creation. - * - * @example - * Request route: POST /users - * Request body: - * { - * "username": "john_doe", - * "email": "john@example.com", - * "password": "password123" - * } - */ -export const createUser = async ( - ctx: Context & { body: Prisma.UserCreateInput } -) => { - // Validate the user input using a validation schema - const { error } = createUserSchema.validate(ctx.body); - if (error) - return returnErrorResponse(ctx.set, 400, "Invalid user input", error); - - // Create the user in the database using the service - try { - const newUser = await createUserService(ctx.body); - return returnWriteResponse( - ctx.set, - 201, - "User created successfully", - newUser - ); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; +import { + returnErrorResponse, + returnWriteResponse, +} from "../../../helpers/callback/httpResponse"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; +import { createUserService } from "../services/createUser.service"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { createUserSchema } from "../schemas/createUser.schema"; + +/** + * @function createUser + * @description Creates a new user in the database. + * + * @param {Context & { body: Prisma.UserCreateInput }} ctx - The context object containing the request body. + * @param {Prisma.UserCreateInput} ctx.body - The user data to be created. + * + * @returns {Promise} A response object indicating success or failure. + * @throws {Object} An error response object if validation fails or an error occurs during user creation. + * + * @example + * Request route: POST /users + * Request body: + * { + * "username": "john_doe", + * "email": "john@example.com", + * "password": "password123" + * } + */ +export const createUser = async ( + ctx: Context & { body: Prisma.UserCreateInput } +) => { + // Validate the user input using a validation schema + const { error } = createUserSchema.validate(ctx.body); + if (error) + return returnErrorResponse(ctx.set, 400, "Invalid user input", error); + + // Create the user in the database using the service + try { + const newUser = await createUserService(ctx.body); + return returnWriteResponse( + ctx.set, + 201, + "User created successfully", + newUser + ); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/user/controller/getAllUser.controller.ts b/src/modules/user/controller/getAllUser.controller.ts index 3eca0c8..56be4e5 100644 --- a/src/modules/user/controller/getAllUser.controller.ts +++ b/src/modules/user/controller/getAllUser.controller.ts @@ -1,21 +1,21 @@ -import { - returnErrorResponse, - returnReadResponse, -} from "../../../helpers/callback/httpResponse"; -import { Context } from "elysia"; -import { getAllUsersService } from "../services/getAllUser.service"; -import { mainErrorHandler } from "../../../helpers/error/handler"; - -export const getAllUser = async (ctx: Context) => { - try { - const allUser = await getAllUsersService(); - return returnReadResponse( - ctx.set, - 200, - "All user ranks successfully", - allUser - ); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; +import { + returnErrorResponse, + returnReadResponse, +} from "../../../helpers/callback/httpResponse"; +import { Context } from "elysia"; +import { getAllUsersService } from "../services/getAllUser.service"; +import { mainErrorHandler } from "../../../helpers/error/handler"; + +export const getAllUser = async (ctx: Context) => { + try { + const allUser = await getAllUsersService(); + return returnReadResponse( + ctx.set, + 200, + "All user ranks successfully", + allUser + ); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/user/index.ts b/src/modules/user/index.ts index efb8647..b13d1da 100644 --- a/src/modules/user/index.ts +++ b/src/modules/user/index.ts @@ -1,7 +1,7 @@ -import Elysia from "elysia"; -import { getAllUser } from "./controller/getAllUser.controller"; -import { createUser } from "./controller/createUser.controller"; - -export const userModule = new Elysia({ prefix: "/users" }) - .get("/", getAllUser) - .post("/", createUser); +import Elysia from "elysia"; +import { getAllUser } from "./controller/getAllUser.controller"; +import { createUser } from "./controller/createUser.controller"; + +export const userModule = new Elysia({ prefix: "/users" }) + .get("/", getAllUser) + .post("/", createUser); diff --git a/src/modules/user/repositories/createUser.repository.ts b/src/modules/user/repositories/createUser.repository.ts index d421cd0..bcbe864 100644 --- a/src/modules/user/repositories/createUser.repository.ts +++ b/src/modules/user/repositories/createUser.repository.ts @@ -1,17 +1,17 @@ -import { Prisma } from "@prisma/client"; -import { userModel } from "../user.model"; - -export const createUserRepo = async (data: Prisma.UserCreateInput) => { - try { - const userData = await userModel.create({ - data, - omit: { - password: true, - }, - }); - - return userData; - } catch (error) { - throw error; - } -}; +import { Prisma } from "@prisma/client"; +import { userModel } from "../user.model"; + +export const createUserRepo = async (data: Prisma.UserCreateInput) => { + try { + const userData = await userModel.create({ + data, + omit: { + password: true, + }, + }); + + return userData; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/user/repositories/findUserByEmailOrUsername.repository.ts b/src/modules/user/repositories/findUserByEmailOrUsername.repository.ts index d9d8129..3d58f03 100644 --- a/src/modules/user/repositories/findUserByEmailOrUsername.repository.ts +++ b/src/modules/user/repositories/findUserByEmailOrUsername.repository.ts @@ -1,39 +1,39 @@ -import { AppError } from "../../../helpers/error/instances/app"; -import { userModel } from "../user.model"; - -export const findUserByEmailOrUsernameRepo = async (identifier: string) => { - try { - const userData = - (await userModel.findUnique({ - where: { email: identifier }, - include: { - roles: { - omit: { - createdBy: true, - createdAt: true, - updatedAt: true, - deletedAt: true, - }, - }, - }, - })) || - (await userModel.findUnique({ - where: { username: identifier }, - include: { - roles: { - omit: { - createdBy: true, - createdAt: true, - updatedAt: true, - deletedAt: true, - }, - }, - }, - })); - - if (!userData) return false; - return userData; - } catch (error) { - throw error; - } -}; +import { AppError } from "../../../helpers/error/instances/app"; +import { userModel } from "../user.model"; + +export const findUserByEmailOrUsernameRepo = async (identifier: string) => { + try { + const userData = + (await userModel.findUnique({ + where: { email: identifier }, + include: { + roles: { + omit: { + createdBy: true, + createdAt: true, + updatedAt: true, + deletedAt: true, + }, + }, + }, + })) || + (await userModel.findUnique({ + where: { username: identifier }, + include: { + roles: { + omit: { + createdBy: true, + createdAt: true, + updatedAt: true, + deletedAt: true, + }, + }, + }, + })); + + if (!userData) return false; + return userData; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/user/repositories/getAllUser.repository.ts b/src/modules/user/repositories/getAllUser.repository.ts index 66a2cdd..f26840b 100644 --- a/src/modules/user/repositories/getAllUser.repository.ts +++ b/src/modules/user/repositories/getAllUser.repository.ts @@ -1,15 +1,15 @@ -import { userModel } from "../user.model"; - -export const getAllUserRepo = async () => { - try { - const data = await userModel.findMany({ - where: { - deletedAt: null, - }, - }); - - return data; - } catch (error) { - throw error; - } -}; +import { userModel } from "../user.model"; + +export const getAllUserRepo = async () => { + try { + const data = await userModel.findMany({ + where: { + deletedAt: null, + }, + }); + + return data; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/user/schemas/createUser.schema.ts b/src/modules/user/schemas/createUser.schema.ts index 3021ddf..e0b3de8 100644 --- a/src/modules/user/schemas/createUser.schema.ts +++ b/src/modules/user/schemas/createUser.schema.ts @@ -1,15 +1,15 @@ -import Joi from "joi"; - -export const createUserSchema = Joi.object({ - name: Joi.string().min(4).max(255).required(), - username: Joi.string().min(4).max(255).required(), - email: Joi.string().email().required(), - password: Joi.string().min(8).max(255).required(), - birthdate: Joi.date(), - gender: Joi.string().valid("male", "female"), - phoneCC: Joi.string().min(2).max(2), - phoneNumber: Joi.string().min(7).max(15), - bioProfile: Joi.string().max(300), - profilePicture: Joi.string().uri(), - commentPicture: Joi.string().uri(), -}); +import Joi from "joi"; + +export const createUserSchema = Joi.object({ + name: Joi.string().min(4).max(255).required(), + username: Joi.string().min(4).max(255).required(), + email: Joi.string().email().required(), + password: Joi.string().min(8).max(255).required(), + birthdate: Joi.date(), + gender: Joi.string().valid("male", "female"), + phoneCC: Joi.string().min(2).max(2), + phoneNumber: Joi.string().min(7).max(15), + bioProfile: Joi.string().max(300), + profilePicture: Joi.string().uri(), + commentPicture: Joi.string().uri(), +}); diff --git a/src/modules/user/services/createUser.service.ts b/src/modules/user/services/createUser.service.ts index bbc1245..5bd847c 100644 --- a/src/modules/user/services/createUser.service.ts +++ b/src/modules/user/services/createUser.service.ts @@ -1,18 +1,18 @@ -import { Prisma } from "@prisma/client"; -import { hashPassword } from "../../../helpers/security/password/hash"; -import { createUserRepo } from "../repositories/createUser.repository"; - -export const createUserService = async (userData: Prisma.UserCreateInput) => { - const { password, ...rest } = userData; // Destructure the password and the rest of the user data - const hashedPassword = await hashPassword(password); // Hash the password before saving to the database - - try { - const newUser = await createUserRepo({ - ...rest, - password: hashedPassword, - }); - return newUser; - } catch (error) { - throw error; - } -}; +import { Prisma } from "@prisma/client"; +import { hashPassword } from "../../../helpers/security/password/hash"; +import { createUserRepo } from "../repositories/createUser.repository"; + +export const createUserService = async (userData: Prisma.UserCreateInput) => { + const { password, ...rest } = userData; // Destructure the password and the rest of the user data + const hashedPassword = await hashPassword(password); // Hash the password before saving to the database + + try { + const newUser = await createUserRepo({ + ...rest, + password: hashedPassword, + }); + return newUser; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/user/services/findUserByEmailOrUsername.service.ts b/src/modules/user/services/findUserByEmailOrUsername.service.ts index 86ed437..8739b8c 100644 --- a/src/modules/user/services/findUserByEmailOrUsername.service.ts +++ b/src/modules/user/services/findUserByEmailOrUsername.service.ts @@ -1,11 +1,11 @@ -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; -import { findUserByEmailOrUsernameRepo } from "../repositories/findUserByEmailOrUsername.repository"; - -export const findUserByEmailOrUsernameService = async (identifier: string) => { - try { - const userData = await findUserByEmailOrUsernameRepo(identifier); - return userData; - } catch (error) { - ErrorForwarder(error); - } -}; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; +import { findUserByEmailOrUsernameRepo } from "../repositories/findUserByEmailOrUsername.repository"; + +export const findUserByEmailOrUsernameService = async (identifier: string) => { + try { + const userData = await findUserByEmailOrUsernameRepo(identifier); + return userData; + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/user/services/getAllUser.service.ts b/src/modules/user/services/getAllUser.service.ts index 509522a..d886b8c 100644 --- a/src/modules/user/services/getAllUser.service.ts +++ b/src/modules/user/services/getAllUser.service.ts @@ -1,10 +1,10 @@ -import { getAllUserRepo } from "../repositories/getAllUser.repository"; - -export const getAllUsersService = async () => { - try { - const allUser = await getAllUserRepo(); - return allUser; - } catch (error) { - throw error; - } -}; +import { getAllUserRepo } from "../repositories/getAllUser.repository"; + +export const getAllUsersService = async () => { + try { + const allUser = await getAllUserRepo(); + return allUser; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/user/user.model.ts b/src/modules/user/user.model.ts index 80f0114..7692938 100644 --- a/src/modules/user/user.model.ts +++ b/src/modules/user/user.model.ts @@ -1,3 +1,3 @@ -import { prisma } from "../../utils/databases/prisma/connection"; - -export const userModel = prisma.user; +import { prisma } from "../../utils/databases/prisma/connection"; + +export const userModel = prisma.user; diff --git a/src/modules/userRole/controller/createUserRole.controller.ts b/src/modules/userRole/controller/createUserRole.controller.ts index 57b3479..726522d 100644 --- a/src/modules/userRole/controller/createUserRole.controller.ts +++ b/src/modules/userRole/controller/createUserRole.controller.ts @@ -1,67 +1,67 @@ -import { Prisma } from "@prisma/client"; -import { Context } from "elysia"; -import { - returnErrorResponse, - returnWriteResponse, -} from "../../../helpers/callback/httpResponse"; -import { createUserRoleService } from "../services/createUserRole.service"; -import { mainErrorHandler } from "../../../helpers/error/handler"; -import { createUserRoleSchema } from "../schemas/createUserRole.schema"; - -/** - * @function createUserRole - * @description Creates a new user role in the database. - * - * @param {Context & { body: UserRole }} ctx - The context object containing the request body. - * @param {UserRole} ctx.body - The user role data to be created. - * - * @returns {Promise} A response object indicating success or failure. - * @throws {Object} An error response object if validation fails or an error occurs during role creation. - * - * @example - * Request route: POST /roles - * Request body: - * { - * "userID": "e31668e6-c261-4a7e-9469-ffad734cf2dd", - * "name": "Admin", - * "primaryColor": "#D9D9D9", - * "secondaryColor": "#FFFFFF", - * "pictureImage": "https://example.com/picture.jpg", - * "badgeImage": "https://example.com/badge.jpg", - * "isSuperadmin": false, - * "canEditMedia": false, - * "canManageMedia": false, - * "canEditEpisodes": false, - * "canManageEpisodes": false, - * "canEditComment": false, - * "canManageComment": false, - * "canEditUser": false, - * "canManageUser": false, - * "canEditSystem": false, - * "canManageSystem": false - * } - */ -export const createUserRole = async ( - ctx: Context & { body: Prisma.UserRoleUncheckedCreateInput } -) => { - const { error } = createUserRoleSchema.validate(ctx.body); - if (error) - return returnErrorResponse(ctx.set, 400, "Invalid user input", error); - - const formData: Prisma.UserRoleUncheckedCreateInput = { - ...ctx.body, - createdBy: "daw", - }; - - try { - const newUserRole = await createUserRoleService(formData); - return returnWriteResponse( - ctx.set, - 201, - "User role created successfully", - newUserRole - ); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; +import { + returnErrorResponse, + returnWriteResponse, +} from "../../../helpers/callback/httpResponse"; +import { createUserRoleService } from "../services/createUserRole.service"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { createUserRoleSchema } from "../schemas/createUserRole.schema"; + +/** + * @function createUserRole + * @description Creates a new user role in the database. + * + * @param {Context & { body: UserRole }} ctx - The context object containing the request body. + * @param {UserRole} ctx.body - The user role data to be created. + * + * @returns {Promise} A response object indicating success or failure. + * @throws {Object} An error response object if validation fails or an error occurs during role creation. + * + * @example + * Request route: POST /roles + * Request body: + * { + * "userID": "e31668e6-c261-4a7e-9469-ffad734cf2dd", + * "name": "Admin", + * "primaryColor": "#D9D9D9", + * "secondaryColor": "#FFFFFF", + * "pictureImage": "https://example.com/picture.jpg", + * "badgeImage": "https://example.com/badge.jpg", + * "isSuperadmin": false, + * "canEditMedia": false, + * "canManageMedia": false, + * "canEditEpisodes": false, + * "canManageEpisodes": false, + * "canEditComment": false, + * "canManageComment": false, + * "canEditUser": false, + * "canManageUser": false, + * "canEditSystem": false, + * "canManageSystem": false + * } + */ +export const createUserRole = async ( + ctx: Context & { body: Prisma.UserRoleUncheckedCreateInput } +) => { + const { error } = createUserRoleSchema.validate(ctx.body); + if (error) + return returnErrorResponse(ctx.set, 400, "Invalid user input", error); + + const formData: Prisma.UserRoleUncheckedCreateInput = { + ...ctx.body, + createdBy: "daw", + }; + + try { + const newUserRole = await createUserRoleService(formData); + return returnWriteResponse( + ctx.set, + 201, + "User role created successfully", + newUserRole + ); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/userRole/index.ts b/src/modules/userRole/index.ts index f81244f..606db28 100644 --- a/src/modules/userRole/index.ts +++ b/src/modules/userRole/index.ts @@ -1,9 +1,9 @@ -import Elysia from "elysia"; -import { createUserRole } from "./controller/createUserRole.controller"; -import { unautenticatedMiddleware } from "../../middleware/auth/unauthenticated.middleware"; - -export const userRoleModule = new Elysia({ prefix: "/roles" }) - .get("/", () => "Hello User Role Module", { - beforeHandle: unautenticatedMiddleware, - }) - .post("/", createUserRole); +import Elysia from "elysia"; +import { createUserRole } from "./controller/createUserRole.controller"; +import { unautenticatedMiddleware } from "../../middleware/auth/unauthenticated.middleware"; + +export const userRoleModule = new Elysia({ prefix: "/roles" }) + .get("/", () => "Hello User Role Module", { + beforeHandle: unautenticatedMiddleware, + }) + .post("/", createUserRole); diff --git a/src/modules/userRole/repositories/createUserRole.repository.ts b/src/modules/userRole/repositories/createUserRole.repository.ts index 4c2566a..7186a3f 100644 --- a/src/modules/userRole/repositories/createUserRole.repository.ts +++ b/src/modules/userRole/repositories/createUserRole.repository.ts @@ -1,15 +1,15 @@ -import { Prisma } from "@prisma/client"; -import { userRoleModel } from "../userRole.model"; - -export const createUserRoleRepo = async ( - data: Prisma.UserRoleUncheckedCreateInput -) => { - try { - const newUserRole = await userRoleModel.create({ - data, - }); - return newUserRole; - } catch (error) { - throw error; - } -}; +import { Prisma } from "@prisma/client"; +import { userRoleModel } from "../userRole.model"; + +export const createUserRoleRepo = async ( + data: Prisma.UserRoleUncheckedCreateInput +) => { + try { + const newUserRole = await userRoleModel.create({ + data, + }); + return newUserRole; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/userRole/schemas/createUserRole.schema.ts b/src/modules/userRole/schemas/createUserRole.schema.ts index 1a9308d..0ce7de6 100644 --- a/src/modules/userRole/schemas/createUserRole.schema.ts +++ b/src/modules/userRole/schemas/createUserRole.schema.ts @@ -1,28 +1,28 @@ -import Joi from "joi"; - -export const createUserRoleSchema = Joi.object({ - name: Joi.string().min(4).max(255).required(), - primaryColor: Joi.string() - .pattern(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/) - .optional(), - secondaryColor: Joi.string() - .pattern(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/) - .optional(), - pictureImage: Joi.string() - .uri({ scheme: ["http", "https"] }) - .optional(), - badgeImage: Joi.string() - .uri({ scheme: ["http", "https"] }) - .optional(), - isSuperadmin: Joi.boolean().required(), - canEditMedia: Joi.boolean().required(), - canManageMedia: Joi.boolean().required(), - canEditEpisodes: Joi.boolean().required(), - canManageEpisodes: Joi.boolean().required(), - canEditComment: Joi.boolean().required(), - canManageComment: Joi.boolean().required(), - canEditUser: Joi.boolean().required(), - canManageUser: Joi.boolean().required(), - canEditSystem: Joi.boolean().required(), - canManageSystem: Joi.boolean().required(), -}); +import Joi from "joi"; + +export const createUserRoleSchema = Joi.object({ + name: Joi.string().min(4).max(255).required(), + primaryColor: Joi.string() + .pattern(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/) + .optional(), + secondaryColor: Joi.string() + .pattern(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/) + .optional(), + pictureImage: Joi.string() + .uri({ scheme: ["http", "https"] }) + .optional(), + badgeImage: Joi.string() + .uri({ scheme: ["http", "https"] }) + .optional(), + isSuperadmin: Joi.boolean().required(), + canEditMedia: Joi.boolean().required(), + canManageMedia: Joi.boolean().required(), + canEditEpisodes: Joi.boolean().required(), + canManageEpisodes: Joi.boolean().required(), + canEditComment: Joi.boolean().required(), + canManageComment: Joi.boolean().required(), + canEditUser: Joi.boolean().required(), + canManageUser: Joi.boolean().required(), + canEditSystem: Joi.boolean().required(), + canManageSystem: Joi.boolean().required(), +}); diff --git a/src/modules/userRole/services/createUserRole.service.ts b/src/modules/userRole/services/createUserRole.service.ts index 5b6902b..edfccd8 100644 --- a/src/modules/userRole/services/createUserRole.service.ts +++ b/src/modules/userRole/services/createUserRole.service.ts @@ -1,28 +1,28 @@ -import { Prisma } from "@prisma/client"; -import { createUserRoleRepo } from "../repositories/createUserRole.repository"; - -export const createUserRoleService = async ( - userRoleData: Prisma.UserRoleUncheckedCreateInput -) => { - try { - const dataPayload = { - ...userRoleData, - isSuperadmin: Boolean(userRoleData.isSuperadmin), - canEditMedia: Boolean(userRoleData.canEditMedia), - canManageMedia: Boolean(userRoleData.canManageMedia), - canEditEpisodes: Boolean(userRoleData.canEditEpisodes), - canManageEpisodes: Boolean(userRoleData.canManageEpisodes), - canEditComment: Boolean(userRoleData.canEditComment), - canManageComment: Boolean(userRoleData.canManageComment), - canEditUser: Boolean(userRoleData.canEditUser), - canManageUser: Boolean(userRoleData.canManageUser), - canEditSystem: Boolean(userRoleData.canEditSystem), - canManageSystem: Boolean(userRoleData.canManageSystem), - deletedAt: null, - }; - const newUserRole = await createUserRoleRepo(dataPayload); - return newUserRole; - } catch (error) { - throw error; - } -}; +import { Prisma } from "@prisma/client"; +import { createUserRoleRepo } from "../repositories/createUserRole.repository"; + +export const createUserRoleService = async ( + userRoleData: Prisma.UserRoleUncheckedCreateInput +) => { + try { + const dataPayload = { + ...userRoleData, + isSuperadmin: Boolean(userRoleData.isSuperadmin), + canEditMedia: Boolean(userRoleData.canEditMedia), + canManageMedia: Boolean(userRoleData.canManageMedia), + canEditEpisodes: Boolean(userRoleData.canEditEpisodes), + canManageEpisodes: Boolean(userRoleData.canManageEpisodes), + canEditComment: Boolean(userRoleData.canEditComment), + canManageComment: Boolean(userRoleData.canManageComment), + canEditUser: Boolean(userRoleData.canEditUser), + canManageUser: Boolean(userRoleData.canManageUser), + canEditSystem: Boolean(userRoleData.canEditSystem), + canManageSystem: Boolean(userRoleData.canManageSystem), + deletedAt: null, + }; + const newUserRole = await createUserRoleRepo(dataPayload); + return newUserRole; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/userRole/userRole.model.ts b/src/modules/userRole/userRole.model.ts index 8b2d986..d2822f7 100644 --- a/src/modules/userRole/userRole.model.ts +++ b/src/modules/userRole/userRole.model.ts @@ -1,3 +1,3 @@ -import { prisma } from "../../utils/databases/prisma/connection"; - -export const userRoleModel = prisma.userRole; +import { prisma } from "../../utils/databases/prisma/connection"; + +export const userRoleModel = prisma.userRole; diff --git a/src/modules/userSession/controllers/createUserSession.controller.ts b/src/modules/userSession/controllers/createUserSession.controller.ts index 9d15ee2..85f3a0c 100644 --- a/src/modules/userSession/controllers/createUserSession.controller.ts +++ b/src/modules/userSession/controllers/createUserSession.controller.ts @@ -1,35 +1,35 @@ -import { Context } from "elysia"; -import { createUserSessionService } from "../services/createUserSession.service"; -import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation"; -import { mainErrorHandler } from "../../../helpers/error/handler"; -import { - returnErrorResponse, - returnWriteResponse, -} from "../../../helpers/callback/httpResponse"; - -export const createUserSessionRole = async ( - ctx: Context & { body: { userId?: string } } -) => { - // Validate request body - if (!ctx.body?.userId) { - return returnErrorResponse(ctx.set, 400, "User ID is required"); - } - - // Get user device and browser information - const userHeaderData = getUserHeaderInformation(ctx); - - try { - const newUserSession = await createUserSessionService({ - userId: ctx.body.userId, - userHeaderInformation: userHeaderData, - }); - return returnWriteResponse( - ctx.set, - 201, - "User session created", - newUserSession - ); - } catch (error) { - return mainErrorHandler(ctx.set, error); - } -}; +import { Context } from "elysia"; +import { createUserSessionService } from "../services/createUserSession.service"; +import { getUserHeaderInformation } from "../../../helpers/http/userHeader/getUserHeaderInformation"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { + returnErrorResponse, + returnWriteResponse, +} from "../../../helpers/callback/httpResponse"; + +export const createUserSessionRole = async ( + ctx: Context & { body: { userId?: string } } +) => { + // Validate request body + if (!ctx.body?.userId) { + return returnErrorResponse(ctx.set, 400, "User ID is required"); + } + + // Get user device and browser information + const userHeaderData = getUserHeaderInformation(ctx); + + try { + const newUserSession = await createUserSessionService({ + userId: ctx.body.userId, + userHeaderInformation: userHeaderData, + }); + return returnWriteResponse( + ctx.set, + 201, + "User session created", + newUserSession + ); + } catch (error) { + return mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/userSession/index.ts b/src/modules/userSession/index.ts index d0302ac..b2647e8 100644 --- a/src/modules/userSession/index.ts +++ b/src/modules/userSession/index.ts @@ -1,7 +1,7 @@ -import Elysia from "elysia"; -import { createUserSessionRole } from "./controllers/createUserSession.controller"; - -export const userSessionModule = new Elysia({ prefix: "/user-sessions" }).post( - "/", - createUserSessionRole -); +import Elysia from "elysia"; +import { createUserSessionRole } from "./controllers/createUserSession.controller"; + +export const userSessionModule = new Elysia({ prefix: "/user-sessions" }).post( + "/", + createUserSessionRole +); diff --git a/src/modules/userSession/repositories/checkUserSessionInCache.repository.ts b/src/modules/userSession/repositories/checkUserSessionInCache.repository.ts index d407f8a..74837af 100644 --- a/src/modules/userSession/repositories/checkUserSessionInCache.repository.ts +++ b/src/modules/userSession/repositories/checkUserSessionInCache.repository.ts @@ -1,13 +1,13 @@ -import { AppError } from "../../../helpers/error/instances/app"; -import { redis } from "../../../utils/databases/redis/connection"; - -export const checkUserSessionInCacheRepo = async (redisKeyName: string) => { - try { - const userSessionInRedis = await redis.exists(redisKeyName); - if (!userSessionInRedis) return false; - - return userSessionInRedis; - } catch (error) { - throw new AppError(500, "Server cache error", error); - } -}; +import { AppError } from "../../../helpers/error/instances/app"; +import { redis } from "../../../utils/databases/redis/connection"; + +export const checkUserSessionInCacheRepo = async (redisKeyName: string) => { + try { + const userSessionInRedis = await redis.exists(redisKeyName); + if (!userSessionInRedis) return false; + + return userSessionInRedis; + } catch (error) { + throw new AppError(500, "Server cache error", error); + } +}; diff --git a/src/modules/userSession/repositories/findUniqueUserSessionInDB.repository.ts b/src/modules/userSession/repositories/findUniqueUserSessionInDB.repository.ts index a6cb1f3..fe29363 100644 --- a/src/modules/userSession/repositories/findUniqueUserSessionInDB.repository.ts +++ b/src/modules/userSession/repositories/findUniqueUserSessionInDB.repository.ts @@ -1,33 +1,33 @@ -import { AppError } from "../../../helpers/error/instances/app"; -import { prisma } from "../../../utils/databases/prisma/connection"; - -export const findUniqueUserSessionInDBRepo = async (identifier: string) => { - try { - const userSession = await prisma.userSession.findUnique({ - where: { - id: identifier, - }, - include: { - user: { - omit: { - password: true, - updatedAt: true, - }, - include: { - roles: true, - }, - }, - }, - omit: { - deletedAt: true, - updatedAt: true, - }, - }); - - if (!userSession) return false; - - return userSession; - } catch (error) { - throw new AppError(500, "Database Error", error); - } -}; +import { AppError } from "../../../helpers/error/instances/app"; +import { prisma } from "../../../utils/databases/prisma/connection"; + +export const findUniqueUserSessionInDBRepo = async (identifier: string) => { + try { + const userSession = await prisma.userSession.findUnique({ + where: { + id: identifier, + }, + include: { + user: { + omit: { + password: true, + updatedAt: true, + }, + include: { + roles: true, + }, + }, + }, + omit: { + deletedAt: true, + updatedAt: true, + }, + }); + + if (!userSession) return false; + + return userSession; + } catch (error) { + throw new AppError(500, "Database Error", error); + } +}; diff --git a/src/modules/userSession/repositories/insertUserSessionToDB.repository.ts b/src/modules/userSession/repositories/insertUserSessionToDB.repository.ts index acd659d..0f042bc 100644 --- a/src/modules/userSession/repositories/insertUserSessionToDB.repository.ts +++ b/src/modules/userSession/repositories/insertUserSessionToDB.repository.ts @@ -1,32 +1,32 @@ -import { Prisma } from "@prisma/client"; -import { userSessionModel } from "../userSession.model"; -import { AppError } from "../../../helpers/error/instances/app"; - -export const createUserSessionRepo = async ( - data: Prisma.UserSessionUncheckedCreateInput -) => { - try { - const newUserSession = await userSessionModel.create({ - data: data, - include: { - user: { - omit: { - password: true, - }, - include: { - roles: true, - }, - }, - }, - omit: { - lastOnline: true, - deletedAt: true, - createdAt: true, - updatedAt: true, - }, - }); - return newUserSession; - } catch (error) { - throw error; - } -}; +import { Prisma } from "@prisma/client"; +import { userSessionModel } from "../userSession.model"; +import { AppError } from "../../../helpers/error/instances/app"; + +export const createUserSessionRepo = async ( + data: Prisma.UserSessionUncheckedCreateInput +) => { + try { + const newUserSession = await userSessionModel.create({ + data: data, + include: { + user: { + omit: { + password: true, + }, + include: { + roles: true, + }, + }, + }, + omit: { + lastOnline: true, + deletedAt: true, + createdAt: true, + updatedAt: true, + }, + }); + return newUserSession; + } catch (error) { + throw error; + } +}; diff --git a/src/modules/userSession/repositories/storeUserSessionToCache.repository.ts b/src/modules/userSession/repositories/storeUserSessionToCache.repository.ts index 4d480c9..753889a 100644 --- a/src/modules/userSession/repositories/storeUserSessionToCache.repository.ts +++ b/src/modules/userSession/repositories/storeUserSessionToCache.repository.ts @@ -1,19 +1,19 @@ -import { Prisma } from "@prisma/client"; -import { AppError } from "../../../helpers/error/instances/app"; -import { redis } from "../../../utils/databases/redis/connection"; - -export const storeUserSessionToCacheRepo = async ( - userSession: Prisma.UserSessionUncheckedCreateInput, - timeExpires: number -) => { - try { - await redis.set( - `${process.env.app_name}:users:${userSession.userId}:sessions:${userSession.id}`, - String(userSession.validUntil), - "EX", - timeExpires - ); - } catch (error) { - throw new AppError(401, "Failed to store user session to cache"); - } -}; +import { Prisma } from "@prisma/client"; +import { AppError } from "../../../helpers/error/instances/app"; +import { redis } from "../../../utils/databases/redis/connection"; + +export const storeUserSessionToCacheRepo = async ( + userSession: Prisma.UserSessionUncheckedCreateInput, + timeExpires: number +) => { + try { + await redis.set( + `${process.env.app_name}:users:${userSession.userId}:sessions:${userSession.id}`, + String(userSession.validUntil), + "EX", + timeExpires + ); + } catch (error) { + throw new AppError(401, "Failed to store user session to cache"); + } +}; diff --git a/src/modules/userSession/services/checkUserSessionInCache.service.ts b/src/modules/userSession/services/checkUserSessionInCache.service.ts index c8abee9..d005b5b 100644 --- a/src/modules/userSession/services/checkUserSessionInCache.service.ts +++ b/src/modules/userSession/services/checkUserSessionInCache.service.ts @@ -1,19 +1,19 @@ -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; -import { checkUserSessionInCacheRepo } from "../repositories/checkUserSessionInCache.repository"; - -export const checkUserSessionInCacheService = async ( - userId: string, - sessionId: string -) => { - try { - // Construct the Redis key name using the userId and sessionId - const redisKeyName = `${process.env.app_name}:users:${userId}:sessions:${sessionId}`; - - // Check if the user session exists in Redis - const userSessionInRedis = await checkUserSessionInCacheRepo(redisKeyName); - return userSessionInRedis; - } catch (error) { - // Forward the error with a 400 status code and a message - ErrorForwarder(error, 400, "Bad Request"); - } -}; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; +import { checkUserSessionInCacheRepo } from "../repositories/checkUserSessionInCache.repository"; + +export const checkUserSessionInCacheService = async ( + userId: string, + sessionId: string +) => { + try { + // Construct the Redis key name using the userId and sessionId + const redisKeyName = `${process.env.app_name}:users:${userId}:sessions:${sessionId}`; + + // Check if the user session exists in Redis + const userSessionInRedis = await checkUserSessionInCacheRepo(redisKeyName); + return userSessionInRedis; + } catch (error) { + // Forward the error with a 400 status code and a message + ErrorForwarder(error, 400, "Bad Request"); + } +}; diff --git a/src/modules/userSession/services/createUserSession.service.ts b/src/modules/userSession/services/createUserSession.service.ts index 754981b..3c769a5 100644 --- a/src/modules/userSession/services/createUserSession.service.ts +++ b/src/modules/userSession/services/createUserSession.service.ts @@ -1,27 +1,27 @@ -import { createUserSessionServiceParams } from "../userSession.types"; -import { createUserSessionRepo } from "../repositories/insertUserSessionToDB.repository"; -import { storeUserSessionToCacheRepo } from "../repositories/storeUserSessionToCache.repository"; -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; - -export const createUserSessionService = async ( - data: createUserSessionServiceParams -) => { - const sessionLifetime = Number(process.env.SESSION_EXPIRE!); - try { - const newUserSession = await createUserSessionRepo({ - userId: data.userId, - isAuthenticated: true, - deviceType: data.userHeaderInformation.deviceType, - deviceOs: data.userHeaderInformation.deviceOS, - deviceIp: data.userHeaderInformation.ip, - validUntil: new Date(new Date().getTime() + sessionLifetime * 1000), - }); - - const timeExpires = Number(process.env.SESSION_EXPIRE!); - await storeUserSessionToCacheRepo(newUserSession, timeExpires); - - return newUserSession; - } catch (error) { - ErrorForwarder(error); - } -}; +import { createUserSessionServiceParams } from "../userSession.types"; +import { createUserSessionRepo } from "../repositories/insertUserSessionToDB.repository"; +import { storeUserSessionToCacheRepo } from "../repositories/storeUserSessionToCache.repository"; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; + +export const createUserSessionService = async ( + data: createUserSessionServiceParams +) => { + const sessionLifetime = Number(process.env.SESSION_EXPIRE!); + try { + const newUserSession = await createUserSessionRepo({ + userId: data.userId, + isAuthenticated: true, + deviceType: data.userHeaderInformation.deviceType, + deviceOs: data.userHeaderInformation.deviceOS, + deviceIp: data.userHeaderInformation.ip, + validUntil: new Date(new Date().getTime() + sessionLifetime * 1000), + }); + + const timeExpires = Number(process.env.SESSION_EXPIRE!); + await storeUserSessionToCacheRepo(newUserSession, timeExpires); + + return newUserSession; + } catch (error) { + ErrorForwarder(error); + } +}; diff --git a/src/modules/userSession/services/getUserSessionFromDB.service.ts b/src/modules/userSession/services/getUserSessionFromDB.service.ts index f9a910b..1c4837e 100644 --- a/src/modules/userSession/services/getUserSessionFromDB.service.ts +++ b/src/modules/userSession/services/getUserSessionFromDB.service.ts @@ -1,23 +1,23 @@ -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; -import { findUniqueUserSessionInDBRepo } from "../repositories/findUniqueUserSessionInDB.repository"; - -export const getUserSessionFromDBService = async (identifier: string) => { - try { - // Check is session exists in DB - const userSession = await findUniqueUserSessionInDBRepo(identifier); - - // If session not found, return false - if ( - !userSession || - !userSession.isAuthenticated || - new Date(userSession.validUntil) < new Date() - ) - return false; - - // If session found, return it - return userSession; - } catch (error) { - // If any DB error occurs, throw an AppError - ErrorForwarder(error, 401, "Unable to get user session"); - } -}; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; +import { findUniqueUserSessionInDBRepo } from "../repositories/findUniqueUserSessionInDB.repository"; + +export const getUserSessionFromDBService = async (identifier: string) => { + try { + // Check is session exists in DB + const userSession = await findUniqueUserSessionInDBRepo(identifier); + + // If session not found, return false + if ( + !userSession || + !userSession.isAuthenticated || + new Date(userSession.validUntil) < new Date() + ) + return false; + + // If session found, return it + return userSession; + } catch (error) { + // If any DB error occurs, throw an AppError + ErrorForwarder(error, 401, "Unable to get user session"); + } +}; diff --git a/src/modules/userSession/services/storeUserSessionToCache.service.ts b/src/modules/userSession/services/storeUserSessionToCache.service.ts index adf0255..53175df 100644 --- a/src/modules/userSession/services/storeUserSessionToCache.service.ts +++ b/src/modules/userSession/services/storeUserSessionToCache.service.ts @@ -1,17 +1,17 @@ -import { Prisma } from "@prisma/client"; -import { storeUserSessionToCacheRepo } from "../repositories/storeUserSessionToCache.repository"; -import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; - -export const storeUserSessionToCacheService = async ( - userSession: Prisma.UserSessionUncheckedCreateInput, - timeExpires: number -) => { - try { - // Store user session in cache with expiration time - await storeUserSessionToCacheRepo(userSession, timeExpires); - return; - } catch (error) { - // If any error occurs while storing session in cache, throw an AppError - ErrorForwarder(error, 401, "Failed to store user session to cache"); - } -}; +import { Prisma } from "@prisma/client"; +import { storeUserSessionToCacheRepo } from "../repositories/storeUserSessionToCache.repository"; +import { ErrorForwarder } from "../../../helpers/error/instances/forwarder"; + +export const storeUserSessionToCacheService = async ( + userSession: Prisma.UserSessionUncheckedCreateInput, + timeExpires: number +) => { + try { + // Store user session in cache with expiration time + await storeUserSessionToCacheRepo(userSession, timeExpires); + return; + } catch (error) { + // If any error occurs while storing session in cache, throw an AppError + ErrorForwarder(error, 401, "Failed to store user session to cache"); + } +}; diff --git a/src/modules/userSession/userSession.model.ts b/src/modules/userSession/userSession.model.ts index 6c993a4..f58d6da 100644 --- a/src/modules/userSession/userSession.model.ts +++ b/src/modules/userSession/userSession.model.ts @@ -1,3 +1,3 @@ -import { prisma } from "../../utils/databases/prisma/connection"; - -export const userSessionModel = prisma.userSession; +import { prisma } from "../../utils/databases/prisma/connection"; + +export const userSessionModel = prisma.userSession; diff --git a/src/modules/userSession/userSession.types.ts b/src/modules/userSession/userSession.types.ts index f451af3..3c19e21 100644 --- a/src/modules/userSession/userSession.types.ts +++ b/src/modules/userSession/userSession.types.ts @@ -1,6 +1,6 @@ -import { UserHeaderInformation } from "../../helpers/http/userHeader/getUserHeaderInformation/types"; - -export interface createUserSessionServiceParams { - userId: string; - userHeaderInformation: UserHeaderInformation; -} +import { UserHeaderInformation } from "../../helpers/http/userHeader/getUserHeaderInformation/types"; + +export interface createUserSessionServiceParams { + userId: string; + userHeaderInformation: UserHeaderInformation; +} diff --git a/src/utils/databases/prisma/connection.ts b/src/utils/databases/prisma/connection.ts index 901f3a0..c230bec 100644 --- a/src/utils/databases/prisma/connection.ts +++ b/src/utils/databases/prisma/connection.ts @@ -1,3 +1,3 @@ -import { PrismaClient } from "@prisma/client"; - -export const prisma = new PrismaClient(); +import { PrismaClient } from "@prisma/client"; + +export const prisma = new PrismaClient(); diff --git a/src/utils/databases/prisma/error/codeList.ts b/src/utils/databases/prisma/error/codeList.ts index 855ec8c..e15d100 100644 --- a/src/utils/databases/prisma/error/codeList.ts +++ b/src/utils/databases/prisma/error/codeList.ts @@ -1,225 +1,225 @@ -/** - * Map of known Prisma error codes to HTTP status codes and messages. - * Extend this map to handle additional error codes if needed. - */ -export const PrismaErrorCodeList: Record< - string, - { status: number; message: string } -> = { - P1000: { - status: 500, - message: `Authentication failed against the database server.`, - }, - P1001: { - status: 503, - message: `Can't reach database server at ${process.env.APP_NAME}.`, - }, - P1002: { - status: 503, - message: `The database server was reached but timed out.`, - }, - P1003: { - status: 500, - message: `Database does not exist.`, - }, - P1008: { - status: 504, - message: `Operations timed out.`, - }, - P1009: { - status: 500, - message: `Database requires SSL connection.`, - }, - P1010: { - status: 403, - message: `User was denied access to the database.`, - }, - P1011: { - status: 500, - message: `Error opening a TLS connection.`, - }, - P1012: { - status: 500, - message: `Schema validation error.`, - }, - P1013: { - status: 500, - message: `Invalid Prisma schema.`, - }, - P1014: { - status: 500, - message: `The underlying engine for the datasource could not be found.`, - }, - P1015: { - status: 500, - message: `Your Prisma schema is using features that are not supported for the database.`, - }, - P1016: { - status: 500, - message: `Your Prisma schema is using features that are not supported for the database version.`, - }, - P1017: { - status: 500, - message: `The Prisma engine has crashed.`, - }, - P1018: { - status: 500, - message: `The current Prisma engine version is not compatible with the database.`, - }, - P1019: { - status: 500, - message: `The Prisma engine could not be started.`, - }, - P1020: { - status: 500, - message: `The Prisma engine exited with an error.`, - }, - P1021: { - status: 500, - message: `The Prisma engine could not be started due to missing dependencies.`, - }, - P1022: { - status: 500, - message: `The Prisma engine could not be started due to an unknown error.`, - }, - P2000: { - status: 400, - message: `The provided value for the column is too long for the column's type.`, - }, - P2001: { - status: 404, - message: `The record searched for in the where condition does not exist.`, - }, - P2002: { - status: 400, - message: `Unique constraint failed on the fields.`, - }, - P2003: { - status: 400, - message: `Foreign key constraint failed on the field.`, - }, - P2004: { - status: 400, - message: `A constraint failed on the database.`, - }, - P2005: { - status: 400, - message: `The value stored in the database for the field is invalid for the field's type.`, - }, - P2006: { - status: 400, - message: `The provided value for the field is not valid.`, - }, - P2007: { - status: 400, - message: `Data validation error.`, - }, - P2008: { - status: 500, - message: `Failed to parse the query.`, - }, - P2009: { - status: 400, - message: `Failed to validate the query.`, - }, - P2010: { - status: 400, - message: `Raw query failed.`, - }, - P2011: { - status: 400, - message: `Null constraint violation on the field.`, - }, - P2012: { - status: 400, - message: `Missing a required value.`, - }, - P2013: { - status: 400, - message: `Missing the required argument.`, - }, - P2014: { - status: 400, - message: `The change you are trying to make would violate the required relation.`, - }, - P2015: { - status: 404, - message: `A related record could not be found.`, - }, - P2016: { - status: 400, - message: `Query interpretation error.`, - }, - P2017: { - status: 400, - message: `The records for the relation between the parent and child models were not connected.`, - }, - P2018: { - status: 400, - message: `The required connected records were not found.`, - }, - P2019: { - status: 400, - message: `Input error.`, - }, - P2020: { - status: 400, - message: `Value out of range for the type.`, - }, - P2021: { - status: 400, - message: `The table does not exist in the current database.`, - }, - P2022: { - status: 400, - message: `The column does not exist in the current database.`, - }, - P2023: { - status: 400, - message: `Inconsistent column data.`, - }, - P2024: { - status: 400, - message: `Timed out fetching a new connection from the connection pool.`, - }, - P2025: { - status: 404, - message: `An operation failed because it depends on one or more records that were required but not found.`, - }, - P2026: { - status: 400, - message: `The current database provider doesn't support a feature that the query uses.`, - }, - P2027: { - status: 400, - message: `Multiple errors occurred on the database during query execution.`, - }, - P2028: { - status: 400, - message: `Transaction API error.`, - }, - P2029: { - status: 400, - message: `Query parameter limit exceeded.`, - }, - P2030: { - status: 400, - message: `Cannot find a fulltext index to use for the search.`, - }, - P2031: { - status: 400, - message: `Prisma needs to perform a transaction, but the database does not support transactions.`, - }, - P2033: { - status: 400, - message: `A number used in the query does not fit into a 64-bit signed integer.`, - }, - P2034: { - status: 503, - message: `Too many connections are open to the database.`, - }, - P2035: { - status: 400, - message: `A constraint failed on the database.`, - }, -}; +/** + * Map of known Prisma error codes to HTTP status codes and messages. + * Extend this map to handle additional error codes if needed. + */ +export const PrismaErrorCodeList: Record< + string, + { status: number; message: string } +> = { + P1000: { + status: 500, + message: `Authentication failed against the database server.`, + }, + P1001: { + status: 503, + message: `Can't reach database server at ${process.env.APP_NAME}.`, + }, + P1002: { + status: 503, + message: `The database server was reached but timed out.`, + }, + P1003: { + status: 500, + message: `Database does not exist.`, + }, + P1008: { + status: 504, + message: `Operations timed out.`, + }, + P1009: { + status: 500, + message: `Database requires SSL connection.`, + }, + P1010: { + status: 403, + message: `User was denied access to the database.`, + }, + P1011: { + status: 500, + message: `Error opening a TLS connection.`, + }, + P1012: { + status: 500, + message: `Schema validation error.`, + }, + P1013: { + status: 500, + message: `Invalid Prisma schema.`, + }, + P1014: { + status: 500, + message: `The underlying engine for the datasource could not be found.`, + }, + P1015: { + status: 500, + message: `Your Prisma schema is using features that are not supported for the database.`, + }, + P1016: { + status: 500, + message: `Your Prisma schema is using features that are not supported for the database version.`, + }, + P1017: { + status: 500, + message: `The Prisma engine has crashed.`, + }, + P1018: { + status: 500, + message: `The current Prisma engine version is not compatible with the database.`, + }, + P1019: { + status: 500, + message: `The Prisma engine could not be started.`, + }, + P1020: { + status: 500, + message: `The Prisma engine exited with an error.`, + }, + P1021: { + status: 500, + message: `The Prisma engine could not be started due to missing dependencies.`, + }, + P1022: { + status: 500, + message: `The Prisma engine could not be started due to an unknown error.`, + }, + P2000: { + status: 400, + message: `The provided value for the column is too long for the column's type.`, + }, + P2001: { + status: 404, + message: `The record searched for in the where condition does not exist.`, + }, + P2002: { + status: 400, + message: `Unique constraint failed on the fields.`, + }, + P2003: { + status: 400, + message: `Foreign key constraint failed on the field.`, + }, + P2004: { + status: 400, + message: `A constraint failed on the database.`, + }, + P2005: { + status: 400, + message: `The value stored in the database for the field is invalid for the field's type.`, + }, + P2006: { + status: 400, + message: `The provided value for the field is not valid.`, + }, + P2007: { + status: 400, + message: `Data validation error.`, + }, + P2008: { + status: 500, + message: `Failed to parse the query.`, + }, + P2009: { + status: 400, + message: `Failed to validate the query.`, + }, + P2010: { + status: 400, + message: `Raw query failed.`, + }, + P2011: { + status: 400, + message: `Null constraint violation on the field.`, + }, + P2012: { + status: 400, + message: `Missing a required value.`, + }, + P2013: { + status: 400, + message: `Missing the required argument.`, + }, + P2014: { + status: 400, + message: `The change you are trying to make would violate the required relation.`, + }, + P2015: { + status: 404, + message: `A related record could not be found.`, + }, + P2016: { + status: 400, + message: `Query interpretation error.`, + }, + P2017: { + status: 400, + message: `The records for the relation between the parent and child models were not connected.`, + }, + P2018: { + status: 400, + message: `The required connected records were not found.`, + }, + P2019: { + status: 400, + message: `Input error.`, + }, + P2020: { + status: 400, + message: `Value out of range for the type.`, + }, + P2021: { + status: 400, + message: `The table does not exist in the current database.`, + }, + P2022: { + status: 400, + message: `The column does not exist in the current database.`, + }, + P2023: { + status: 400, + message: `Inconsistent column data.`, + }, + P2024: { + status: 400, + message: `Timed out fetching a new connection from the connection pool.`, + }, + P2025: { + status: 404, + message: `An operation failed because it depends on one or more records that were required but not found.`, + }, + P2026: { + status: 400, + message: `The current database provider doesn't support a feature that the query uses.`, + }, + P2027: { + status: 400, + message: `Multiple errors occurred on the database during query execution.`, + }, + P2028: { + status: 400, + message: `Transaction API error.`, + }, + P2029: { + status: 400, + message: `Query parameter limit exceeded.`, + }, + P2030: { + status: 400, + message: `Cannot find a fulltext index to use for the search.`, + }, + P2031: { + status: 400, + message: `Prisma needs to perform a transaction, but the database does not support transactions.`, + }, + P2033: { + status: 400, + message: `A number used in the query does not fit into a 64-bit signed integer.`, + }, + P2034: { + status: 503, + message: `Too many connections are open to the database.`, + }, + P2035: { + status: 400, + message: `A constraint failed on the database.`, + }, +}; diff --git a/src/utils/databases/prisma/error/types.ts b/src/utils/databases/prisma/error/types.ts index 97f0e74..271c366 100644 --- a/src/utils/databases/prisma/error/types.ts +++ b/src/utils/databases/prisma/error/types.ts @@ -1,11 +1,11 @@ -/** - * @typedef {Object} ErrorResponse - * @property {number} status - The HTTP status code corresponding to the error. - * @property {string} message - A human-readable error message. - * @property {any} [details] - Additional details about the error, if available. - */ -export interface PrismaErrorTypes { - status: number; - message: string; - details?: any; -} +/** + * @typedef {Object} ErrorResponse + * @property {number} status - The HTTP status code corresponding to the error. + * @property {string} message - A human-readable error message. + * @property {any} [details] - Additional details about the error, if available. + */ +export interface PrismaErrorTypes { + status: number; + message: string; + details?: any; +} diff --git a/src/utils/databases/redis/connection.ts b/src/utils/databases/redis/connection.ts index 5325864..9b5a5b3 100644 --- a/src/utils/databases/redis/connection.ts +++ b/src/utils/databases/redis/connection.ts @@ -1,7 +1,7 @@ -import Redis from "ioredis"; - -export const redis = new Redis({ - host: process.env.REDIS_HOST, - port: Number(process.env.REDIS_PORT), - password: process.env.REDIS_PASSWORD, -}); +import Redis from "ioredis"; + +export const redis = new Redis({ + host: process.env.REDIS_HOST, + port: Number(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, +}); diff --git a/structure.example.md b/structure.example.md index a015194..b2f7c0a 100644 --- a/structure.example.md +++ b/structure.example.md @@ -1,16 +1,16 @@ -src/ -└── modules/ -└── movie/ -├── controller/ -│ ├── createMovie.controller.ts -│ ├── getAllMovies.controller.ts -│ ├── getSimilarByGenre.controller.ts -├── services/ -│ ├── createMovie.service.ts -│ ├── getAllMovies.service.ts -│ ├── getSimilarByGenre.service.ts -├── movie.model.ts -├── movie.repository.ts -├── movie.schema.ts -├── movie.types.ts -└── index.ts +src/ +└── modules/ +└── movie/ +├── controller/ +│ ├── createMovie.controller.ts +│ ├── getAllMovies.controller.ts +│ ├── getSimilarByGenre.controller.ts +├── services/ +│ ├── createMovie.service.ts +│ ├── getAllMovies.service.ts +│ ├── getSimilarByGenre.service.ts +├── movie.model.ts +├── movie.repository.ts +├── movie.schema.ts +├── movie.types.ts +└── index.ts diff --git a/tsconfig.json b/tsconfig.json index 1ca2350..f5e9895 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,103 +1,103 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "ES2022", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - "types": ["bun-types"], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ES2021", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "ES2022", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + "types": ["bun-types"], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}