My Cars Club Backend Reminders
-
I've updated
users
' table's columnrefresh_tokens
array to instead be a separate tablerefresh_tokens
which has a one to many relationship withusers
table (multiple 'refresh tokens' can belong to a single user) referencing aFOREIGN KEY
ofuser_id
withON DELETE CASCADE
constraint ensuring if a user deletes their account then all of their "refresh_token"'s rows would be removed with that action. -
NOTES about
refreshTokenController.ts
(or rather a warning):- On the frontend, in my React app, I must use a state of
flag
(update: or rather the'loading'
Redux Toolkit state) in-between the/refreshtoken
endpoint HTTP (Axios interceptors) requests, because it serves a purpose for the slow-internet users, whereas: without it if they made a rapid fast requests before response is received they'll have a failed response on the 2nd (or 3rd, etc.) request because thisrefreshTokenController
will read the samerefreshToken
cookie twice -> and will 'think' the 2nd request was an attempt to re-use token, since I don't like the #1st Fix: a queue and flag on the backend (or use a caching library likenode-cache
): which would mean I keep therefreshToken
in a cache for a little while & allow re-use for a brief period of time for the sake of slow-internet users & thus breaking my rules of 1 refreshToken = 1 request; and further making my server a little bit less secure: by openning a window for a fast enough hacker to re-use the token on behalf of the victimized user without getting caught in my "detect refreshToken reuse" logic. I don't wanted to make such a sacrifice so instead leave the backend'srefreshTokenController
logic as-is: if slow-internet conneciton user rapidly fires 2 requests very fast the 2nd would fail -> HOWEVER that's where the fix #2 comes with theflag
state on the frontend side in React: which would be set totrue
(boolean) initially & just before the request is made then thesetFlag(false)
would be called & once response has arrived ONLY then I'llsetFlag(true)
again & thus allowing for further request ONLY AFTER the previous response has arrived, in similar fashion to whatuseEffect
does with its cleanup function when making GET HTTP requests.
- On the frontend, in my React app, I must use a state of
-
Server URL updated: from
https://cars-clubs-backend.onrender.com
tohttps://cars-clubs-server.onrender.com
=> here it is on the frontend's netlify.toml file-> Update2: Re-reading the following I find it confusing as the server from my second Render account [email protected] (password hint: one) still auto-deploys from GitHub I will have to check back this again later. Original: BUT I must manual deploy it from my second account (where my Cars Club Database is deployed at! - Which database is deleted after90 days(UPDATE: now it is 30 days as per https://docs.render.com/free#30-day-limit) and must be re-deployed! => That is the email: [email protected] (password hint: one) instead of the [email protected]). EXPLANATIONS: that "-server
" subdomain is deployed on my second Render account which is where my Database Cars Club is deployed as well, I had to do it because on the first Render account (with-backend
subdomain deployed) there's multiple servers and by using Cron Jobs (to keep my Cars Club server running 24/7) I'd run out of free instances on the 31th day (well Render.com offers 750 free hours and Cars Club backend taking up 744, BUT I have 5 more servers running on my first Render account: ~1.5 hours each (imagine if they run even longer per month/gets more visits -> they'll go down at like 25th day each month)). That's why I instead re-deployed Cars-Clubs-server (named itserver
on the 2nd acc. instead ofbackend
as was on my 1st acc.) on my second Render account BUT: I must click "manual deploy" button whenever I update my MAIN branch because I can't connect multiple Render's account to a single GitHub account (altough I do have an option to log it out and re-connect on the 2nd Render account however I'll lost the deploy for the rest of my 5 instances on the 1st Render account & any updates would require 5x manual deploys clicks) which would have allowed me auto-deploys have I had been connected to my own server. Again that's why I must do "Manual deploy from last commit" in Render.com on my server in my second account.- To tickle waking up the server or pinging that wakes up the server a feature being used is called cron-job.
- Cron-job being used can be https://cron-job.org/en or the render's own cron jobs: https://dashboard.render.com/cron/new
- When re-creating a free PostgreSQL Database on Render (because it gets removed after 90 days (UPDATE: now it is 30 days as per https://docs.render.com/free#30-day-limit)), a reminder or rather an important step to follow is to set the Database Optional name of the database as
cars_club
as for it to match the name that is in server'sdatabase.sql
file, but leave the "Username" empty. Then connect to the database with CLI command ofpsql external_link_paste_here
and run all of theCREATE
SQL commands from the server'sdatabase.sql
including theCREATE VIEW
- I have to scroll down to find it or use CTRL+F - which is inside "-- TESTS" part which I should perhaps modify this file to move that command above the "-- TESTS" comment. - Test user must be created manually from the Register page at my website https://cars-club.netlify.app. Just insert the informations: username: test; e-mail: [email protected]; password: test.
CREATE VIEW posts_view_except_post_image AS SELECT
is used for the "Edit Post" functionality. Without thisVIEW
the server returns errors when a user tries to modify a post.- Sometimes Render.com adds their own letters on the end of my DB name even though I named it "cars_club", like today (created on 25 October 2023) they added "_ncwm" on the end of my DB name and now its name is "cars_club_ncwm" but it's all working fine. Their default Username of choice was "cars_club_ncwm_user".
- UPDATE2: for #1: it seems like there is no more 90 days expiration limit -> UPDATE: now it is 30 days as per https://docs.render.com/free#30-day-limit, #2: as mentioned below, important steps on how to connect Render's Server with Database is to add or rather edit the server's (reminder: from my second Render account [email protected] and password hint: one) environment variable
CONNECTION_STRING_POSTGRESQL
with the value of the "External Database URL" link from the earlier created databasecars club database
" or whatever name I give it. And then just click "Save, rebuild and deploy
" button. - Bugs that happened is I forgot to add Environment Variables (see
.env#examples
for deployed Apps: andACCESS_TOKEN_SECRET
withREFRESH_TOKEN_SECRET
andCONNECTION_STRING_POSTGRESQL
(the external database URL links)) and UPDATE: for Render.com the server crashes if I haveNODE_ENV=production
and deployed with cache removed option I found on on their forum I shouldn't be using it since they add it for me & removing it fixed the "bash tsc not found" even thoughtnpm i
was success on their part this error occurs when NODE_ENV is set by me.- Oh and talking about External Database URL Links bugs caused by
pg
library returning "SSL/TLS required" error, a fix was to add?ssl=true
at the end of the External Database URL. Read more at this Render forum's self-answered question which has hyperlink source that leads to this StackOverflow comment/answer -> I googled it thinking it's a Render issue but turns out it waspg
library issue, now fixed.
- Oh and talking about External Database URL Links bugs caused by
- To tickle waking up the server or pinging that wakes up the server a feature being used is called cron-job.
-
I thought UI/UX would be much better if I had "Posted by" username in the
Marketplace
component, but my logic was not implemented in the JWT token: and instead I had modified on the backend bothloginController
andverifyRefreshTokenController
to incldueuser_name
in the response & for theverifyRefreshTokenController
I must query to my Database instead of including private info likeuser_email
/user_name
in the JWT Token. Then on the Frontend also use Redux Toolkit whenuseVerifyRefreshToken
is successful & has received thatuser_name
,user_role
,user_email
,user_id
-> For Posts UPDATE & DELETE Buttons to be rendered conditionally based onpost.user_id === user_id
. -
IMPORTANT FACT is that the console response of 403 (Forbidden) is an "OKAY" response if there's updated data on the screen it means the Axios Interceptor did its job well & the 403 status means the
authorizeJWT
did a good job at protecting the Source & if the data is displayed on the Frontend thenrefreshTokenController
did successfully refreshed the accessToken by using User's "session"/'refreshToken' TOKEN & delivering a fresh NEW short-lived "accessToken" (as well as new 'refreshToken'). :) -
VERY IMPORTANT decision & plans I've made for NOT using Async Thunk with /editpost & /createpost ALIKE, in my setup = accessToken iS to be refreshed & used ONLY once, because it is shortly-lived so I MUST use
useAxiosInterceptor
Hook call which is IMPOSSIBLE inside Redux Files. YES I do can have "editPost" & "createPost" separate Async Thunks in a single REDUX file, but it's #1 NOT that useful since there's no reusability usage; and #2 the MOST important issue is the fact that I'll have to make sure I "just" calluseAxiosInterceptor
() WITHOUT using its returned Axios (if I don't need to) in the Component where I'd be calling(/dispatching) that AsyncThunk & all the while using the SAME intercepted Axios in the Async Thunk Redux file BUT imported from Axios.ts (the EXACT 'reference Axios instance object' that's being intercepted in myuseAxiosInterceptor
custom Hook) just to make sure my POST/PUT Requests won't fail by using a non-intercepted Axios instance. A lot of workaround indeed. Overall forgetting to calluseAxiosInterceptor
would lead to Async Thunk failing on me and causing bugs to my App.- And I also wouldn't be able to call a DISPATCH function in my Redux file to fill my
ModalPost.tsx
input fields with the VALUES matching thepost_id
that the user has clicked on, becauseuseDispatch
is a Hook that also can NOT be called inside Redux files.
- And I also wouldn't be able to call a DISPATCH function in my Redux file to fill my
-
I have not added IMAGE Size limits just for easier testing. In a real App I would at least limit the size and even possibly provide a link to my Users of a free service to resize their images or add my own functionality that will resize theirs images either on the Frontend Notice or without their Notice I'd resize them on the backend and store them on the server or database & then frontend will retrieve the resized pictures.
-
(Decided not to use
sessionStorage
and keep reading for more info about my new decision.) Next what I am planning to do a UI/UX modifications on the Frontend: is to retain the User's destination in the website by something like storing thewindow.scrollY
value in asessionStorage
so whenever User refreshes -> they will be scrolled down to their pre-refresh destination: it would be useful for theEdit Post
button. -> Unless I decide a different workaround: to modify myeditPostController
on the server to make it return the Buffer if the User has provided a new image - meaningreq.file !== undefined
in theeditPostController
wherereq.file
comes from my Multer Middleware -> or actually: I would modify my SQL toRETURN _
no matter if user updatedimage
or didn't -> just so that on the Frontend I will update thegetSortedPosts
by calling the reducereditSortedPostAction
with those values received aseditedPostData
inside theeditPost
function call insideModalPost_Create_or_Edit_Button
component.-
make my
The plan is to
/editpost/:post_id/:user_id
editPostController
to return all of the (RETURNING *
) theeditedPostData
including thepost_image_buffer
and then insideMOdalPost_Create_or_Edit_Button.tsx
call thegetSortedPost
's 'editSortedPostAction' reducer. - ->->->->-> NEW DECISION MADE: I decided a different workaround: to modify my
editPostController
on the server to make it return theEditedPost
no matter if the User has provided a new image or not -> meaning in theeditPostController
: I have modified my SQL toRETURN _
no matter the cases if the User updatedimage
or not -> just so that on the Frontend I now am mutating thePosts
Array state from thegetSortedPosts
Redux file based on theedittedPostData.post_id
(meaning I'm only mutating the editted Post element of thePosts
Array state using.map
method) by calling the reducereditSortedPostAction
action function with 1 Argument of this Object received (from calling/editpost/:post_id/:user_id
) asdata?.edittedPostData
(Object) inside theeditPost
function insideModalPost_Create_or_Edit_Button
component.
-
-
PaginationMarketplace.tsx
may need aflag
state as well, to add upon all of thedisabled
logic an extra... || flag
-> so that they can't trigger another call while one is incoming -> but then I would need to use thegetSortedPosts
's "status states" like thepostsStatus
because I don't have ANY responses inside of myPaginationMarketplace
and thus I can't have a local stateflag
but instead I must just use a condition:'loading'
(or'idle'
or'failed'
) value ofpostsState
state -> then setflag = true
otherwise ifpostsState
='succeeded'
-> then setflag = false
as that willactive
my buttons. -> AGAIN All of this is needed so that slow internet Users won't re-trigger 2 or 3 or more subsequental requests with the same refresh_token at which point the Server will reject the 2nd or 3rd Request and remova ALLrefresh_token
's Rows matching thatuser_id
from therefresh_tokens
table and logs the User out as a suspicios activity of where a Hacker might be trying to re-use therefreshToken
cookie; because the backend Code "doesn't know" if the User is legit or a Hacker & on top of it all: my server-side code-wise rules are 1 Request per 1 Refresh Token -> these rules are set by myrefreshTokenController
code. -
Future plans about my current LIMITLESS size of the images: I can either block responses if images are larger than let's say 5mb or otherwise I implement a functionality to resize the incoming images before storing them to my Database or if in future I decide to store them on the server's folder of
/images
- still, resize the image first before storing them. - DevTools NETWORK tests: That's an idea that came to my mind because I noticed in a "Slow 3G" running: thegetSortedPostsAsyncThunk
AKA/getsortedposts
request even though there's 5 small pictures of size in my local folder of around 100-200kB (that's what instagram image sizes are) so that's an average of 0.2mb per 1 image making up for 1mb total size & it's quite slow on "Slow 3G". But, thinking about it it's because it's the Slow 3G being close to unusable, nothing is wrong on my part, since 1mb is such a small Data. Another results on "Slow 3G": whenever I make any other CRUD Operations: Delete a Post / Create a Post / Edit a Post -> the response is received quite fast!:) -> That's because thecreatepost
response had a size of 380B (less than half a 1kB) took 4.4s (because it's a protected route withauthorizeJWT
middleware) &refreshtoken
response had just 1kb took 2s, but my images had 2.2MB (2200kB) and took 45s. -> Note by "my images" I refer to the response bygetsortedposts?limit=5&offset=0&carNameTitle=
(carNameTitle
can be empty as it's optional value in my getSortedPosts logic). - By those results I decided not to make myModalPostSuccessText
be showingLoading
component ifpostsStatus
if"loading"
, since myModalPostSuccessText
's Loading state is unclickable outside meaning it's unclosable for as long as it takes for response (a quick non-data of ~ 300B - 1kB) to be received: however on "Slow 3G" or even "Fast 3G" I stop "Loading" theModalPostSuccessText
and show the response message & an "Okay" button that will clsoe the Modal (or clicks outside or the 'X' button) -> However for slow internet User's myPost.tsx
's "Loading" would still be there: but my slow internet Users have the freedom of navigating around the page if they wanna do some other actions or they can await thePosts
data at thePost.tsx
's "Loading" screen coming from a condition ofPostsStatus
Redux State of the/getsortedposts?limit=5&offset=0&carNameTitle=
request's response. - That gave me a new idea that aLoading
component can be shown on TOP above the rest of the Posts which will remain at the Bottom. That would be a great UI/UX improvements or rather a decision if I want to keep showing the rest of the posts or not. -> Thinking about it: the current rules of 1 Request per 1 Refresh Token would cause trouble in such a scenario: where if the User tried to remove / "DELETE" some of their (others) Posts just below theLoading...
or "Edit" one of their post while the/getsortedposts
is being processed: there's a chance that my Server will request that 2nd request (and log out the User thinking it's a Hacker) -> Re-thinking about it and seeing my tests in DevTools Network tab: therefreshtoken
being quite a small & fast response of around 1kB & 100ms (in 'No Throttling' mode; and 500ms in "Fast 3G" and 2s in "Slow 3G") it means the until the User decides to either "Delete" or "Edit" one of their Posts it will take up some time and ifrefreshtoken
is done: then Server won't reject the 2nd request. -> Except that the Logic of "EDIT A POST" is that it requires yet another GET request so that theModalPost
can be filled with the Data matching thepost_id
and allow the User to edit it: meaning slow internet User's will still be logged out by my server thinking it's a Hacker making the 2nd request with the same unchangedrefreshToken
cookie before it has been refreshed after the "CREATE A POST" response has been received. -> BUT, re-thinking again: it's the fact that myaccessToken
lasts 15s: so 1. "CREATE A POST" is made: hopefullyrefreshtoken
is triggered: andaccessToken
is used inauthorizeJWT
middleware and allows thecreatepost
request, and (if I decided to go with theLoading
modification as mentioned above, but I'm not doing it yet!) if Slow Internet user clicks the "EDIT A POST" button real quick after having "CREATED A POST" (and my plan ofLoading
abovePosts.tsx
) it means even Slow User will be fine sinceaccessToken
will last for 14s more: and request won't need to be intercepted & recall to/refreshtoken
won't happen. That seem good on paper, but in practice maybe some issues will happen and complications are unavoidable until I implement restricttions server-side as well, at which point my Website will look slow and laggy (at least laggish): since if I implement DDoS protection that would still allow at least 15 Requests per 1 Minute to not introduce any slowness or laggy-feeling in my Website. - Anyways, for now I don't show the rest of thePosts
Array state while/getsortedposts
request is ongoing instead there's Loading over ALL of theposts.map(...
data inside myPost.tsx
; however if I do decide to show the rest of myPosts
and a Loading on top of the very firstPost
element: this Logic will have to be implemented by creating a newLoadingPosts
component and render it insideMarketplace.tsx
or maybe..., I can even render it inside myPost.tsx
just abovediv
element that rendersposts.map(...
data, instead ofMarketplace
. - I'm still thinking to create some guards on the server-side to check if/refreshtoken
is ongoing or in processs or if such a response like/refreshtoken
has been sent recently to that IP address or not -> and based on it block the request before it's even reached by my 'is User Hacked' code-guards where I remove allrefresh_token
's Rows (based on thatuser_id
stored inside the JWT Token) fromrefresh_tokens
table & technically logs out the User from all devices and if they try to access a protected route they'll get a "Please login"ModalText
message, it's still somewhat bugging me as it's not a perfect UI/UX & can happen for fast internet Users as well if I forget (to implement those Workarounds) to add someflag
guards todisable
Buttons or show upLoading
Component as technically removing the Button from the DOM as to not allow them to make subsequent request real fast (before 1st request's response has had the time to return or rather to be processed, once the Server process has started, the 2nd Request will succeed as well even if the 1st response hasn't arrived yet), in like a sub-milliseconds: the 2nd Request would fail & logs out the User from all devices (again, removes allrefresh_token
's Rows matching thatuser_id
) as if they were Hacked -> it makes up for a bad UX which I wanna fix in the future however the code-rules being 1 Reuqest per 1 Refresh Token is too strict and is too good, at which point I must keep usingflag
's on the Frontend or implementcache
on the server-side to keep therefreshToken
's for 1 minute and after 1 minute remove them from Database to ease up the rules, which reduces the User's protection. A Catch-22! - -> Re-thinking: it might even be a matter of Fast Internet Users where they can send 2 fast requests rather than the slower internet User who can't send the 2nd request faster than the Fast Internet User. Still, overall my tests are Random and I can't predict what the User will do, however the most highest chance for this issue that happens is when the Request is being intercepted & both the 1st-in-the-process-of-interception-request and 2nd request non-intercepted but still being sent with the samerefreshToken
cookie which the server has already removed it from the Database is being processed and that 2nd Request gets rejected by my server logic:authorizeJWT
checks ifaccessToken
is expired -> if true then sends403
response which gets intercepted on my Frontend'suseAxiosInterceptor
which calls/refreshtoken
refreshTokenController
& removes the currentrefreshToken
from the Database from the 1st request & then the 2nd request coming in with the sameaccessToken
Redux state and of course samerefreshToken
cookie: gets intercepted as well, but whenrefreshTokenController
receives the response and thatrefreshToken
's didn't found suchrefresh_token
inside therefresh_tokens
table -> then removes all of therefresh_token
's Rows matching thatuser_id
& technically logs my User out of the App. I'm thinking of re-testing mysetTimeout
workaround to allow for at least 2nd Request by setting up a removal of the "receivedrefreshToken
's from the Database indelay
of 2seconds or otherwise to usenode-cache
library. -----> And a research of why mysetTimeout
test failed and the ChatGPT answer quote: "Scenario 2: Response Sent Before Timeout Completes: If the response is sent before the specified delay insetTimeout
elapses (i.e., before 2000 milliseconds), the callback function scheduled bysetTimeout
will not have a chance to execute. The response will be sent to the client, and thetimeout
will be effectively canceled.".- So I'm not sure if even such a workaround exists besides some cron job but then again each Request is unique and doesn't know about preivous Request's data -> and I don't know how ChatGPT's website manages for such a smooth checking: whenever I change my IP Adress using VPN: they immediately refresh my website as if they're pinging each User's in a sub-second with empty responses only to checkif their IP Adress is the same or otherwise they use some
sessionStorage
checking orsetInterval
that checks against my IP Adress every 1 second & refreshes my website if I use VPN, but I don't see how I can keep some kind of backuprefreshToken
inside ofsessionStorage
or something similar. Every other workaround would mean to just weaken my current security setup rules of 1 Request per 1 Refresh Token & I don't like to do that. - For now it will be as-is, for real App I can use something like
Redis
to storerefreshToken
for some time (no longer than 2seconds) otherwise google's suggestions is a message queue system such asRabbitMQ
orApache Kafka
, none of which I have ever used yet.
- So I'm not sure if even such a workaround exists besides some cron job but then again each Request is unique and doesn't know about preivous Request's data -> and I don't know how ChatGPT's website manages for such a smooth checking: whenever I change my IP Adress using VPN: they immediately refresh my website as if they're pinging each User's in a sub-second with empty responses only to checkif their IP Adress is the same or otherwise they use some
-
UI/UX improved/fixed for the Slow Internet Users (and even affected Fast Internet Users as well) logging out the spam-requests/spam-button-clicks. The fix: disabling the Buttons (
disable={flag}
) until the response has arrived seems like a bulletproof way to not accidently log out the User by my too strict code-rules of: 1 Request per 1 Refresh Token -> I've tested myFormSearchCars
component in React (I spammed 100s of button clicks to callhandleSearchFN
): and without theflag
where I spam the search button -> keeps logging me out the ~50th Request -> that's on the no limits / internet speed no throttling mode test's result: when it's time for interceptor to work -> meanig theaccessToken
has expired -> the/refreshtoken
endpoint is being called several times with at least 2 Requests having the samerefreshToken
cookie's value & my backend logs the User out -> but withflag
logic: I tried 100s of clicks: I was never logged out soflag
improved the UX at 100%. Again, the how & why it all works: as in my point above (#10 at the end) I've mentioned that whenaccessToken
expires and it's time to intercept the request: then the next request fails -> However with theflag
at work: it won'tactivate
the Button (will keep itdisabled={flag}
until response has arrived: so no "next request" can happen until arrival & thus avoiding any potential errors where the Server logs out the User thinking that they got hacked because of how my code acts against a Request to therefreshTokenController
where therefreshToken
cookie's value - a JWTrefresh_token
token - doesn't exist inside the Database: logs them out. But, with theflag
implementations -> bad UX fixed. / solved. -> By all of this I even solved a potential issue where the User tries to spam a Request until a previous Request's Response has not yet arrived.- However I can not neglect the fact that mini-issue remains: if the
accessToken
has expired & if the User really quickly clicks the rest / any of the UNDISABLED or still ACTIVE buttons; example: if the User clicks thehandleSearchFN
then clicks thedelete
a post button oredit
a post Real Quick - this will make the 2nd Request fail. -> I do know that by the time the Fast Internet User clicks those buttons: therefreshToken
cookie would have already been updated with a fresh newrefresh_token
coming from the 1st Request with the Axios interceptor call to/refreshtoken
Controller -> however the Slow Internet Users or laggy server would remain affected in such a scenario. -> Overall the rule of 1 Request Per 1 Refresh Token is too good and too protective: but anything too good in life is a Catch-22: "too good security" hurts the UX VS "medium security": smoothens up the UX but hurts the overal User's security. - The best workaround to avoid my backend security logic logging out my Users in a rapid fast Requests where 2 Requests use the same
refresh_token
meaning the 2nd request was sent with the previousrefreshToken
cookie -> would be to have a file that checks against all states: either localloading
states or localflag
states orRedux Async Thunk
"pending" response being of state"loading"
as per thePostsStatus
state -> so grab all those state and make a globaldisable
Button states that each and every component will receive and check against thisglobalDisable
state (which will be of value"boolean"
) on top of each Component's local "disable" condition so something like:disable={flag || globalDisable}
. -> Or since this "global disable" state will take in consideration of all of the localloading
& Redux /status
states (no matter how deeply nested children components & grandchildren components are), from every and each component: I then would use this yet-to-be-made "disable all buttons" 'while any Request is ongoing & Response hasn't arrived yet'-feature by having it inside of a Redux Slice or the outmost Parent likeMarketplace.tsx
to hold theglobalDisable
state and those Marketplace children Components's Buttons will be passed an attributedisable={globalDisable}
. This very strict rule and very protective is a big Catch-22!
- However I can not neglect the fact that mini-issue remains: if the
-
Image-wise I could have used on the backend
const buffer = Buffer.from(valueBYTEA, 'hex')
and then unwrap thebuffer
like so:const arrayBuffer = buffer.buffer
and pass it in place ofpost_image_buffer
so that I wouldn't need to useUint8Array
on frontend & only to useBlob
directly on the receivedpost_image_buffer
. But as-is currently works well. -
Explanation of how Multer works in my case: I'm using Multer middleware to process the
FormData
received from amultipart/form-data
request with the storage optionmemoryStorage()
, then usingupload.single
instance middleware to populate thereq.body
with text fields &req.file
with metadata information about the image which is then handled by my controller (createPostController.ts
) to store only thebuffer
data to my PostgreSQL as aBYTEA
column type.- (Again, if I
console.log
thereq.body
inside Multer middleware jsut before theupload.single
middleware instance: then it's an empty object ({}
) and ifimage
property of what'supload.single('image')
looks for - isundefined
: then only thereq.body.title
is processed (console.log
ged): but luckily myif
conditional guards are returning a response with status500
error ifimage
field is not found inside thatmultipart/form-data
request andnext
function call is never reached - guards serves as a "don't trust the frontend relationship".) - => In short Multer middleware's
upload.single
instance middleware populates thereq.body
exactly the same as whatexpress.json
middleware AKAbodyParser
middleware under the hood does.
- (Again, if I
-
My
refresh_tokens
andposts
tables both have a MANY-TO-ONE relationship with myusers
table. -
My
users
table has a ONE-TO-MANY relationship with myrefresh_tokens
andposts
tables alike. (both sayings work.) -
To avoid the error
POST https://cars-club.netlify.app/api/api/v1/post/createpost 502 (Bad Gateway)
I have to make sure the images areat 1080px or smallerof JPG format. An easy way to resize/convert an image is using https://redketchup.io/image-resizer & export as JPG (by selecting "JPG - JPEG image" option it'll be converted to JPG).
- Because there is bug in Windows 11 (and Windows 10) where both .WEBP & .JPEG formats are saved as .JPG format but under the hood they are different and thus creating such error as above. Luckily I have a note that if a potential employer wants to make a post they can right click to download one of the images I've used in my own posts and then they themself can create a post using that car image.
- Have an "edit username" & "edit e-mail" features, but that will require me to also run SQL Query against my PostgreSQL database
posts
table to runUPDATE posts SET post_created_by_username=$1 WHERE user_id=$2
& respectively if I've choosen to show e-mail.- For
post_created_by_email
I might even implement a conditional checking inModalPost.tsx
(& "edits Posts") to make User decide whether to show "contact number" or "contact e-mail" or both. A toggler.
- For
- Have a comments section which
comments
table that will relate with eachpost
Row of theposts
table andFOREIGN KEY
"user_id" column toREFERENCE
users
table. - Have a replies sections: such
replies
table will have to connect with "comment_id" fromcomments
table & the "user_id" fromusers
table. - On the Frontend part of UI/UX I may implement some alert/modal that will be shown on successful EDITs of the POST.
- Planning for having a user-profile section (will write more info in the future).
- A technical plan from now for in the future is: to have me fetch information from a user like: 1. Name; 2. E-mail.
- A good possibility would be to have a User Profile Picture that they may change it from this section.
- Edit password may be cool feature to have: but I must think about the security of such an action. (May write a list of options sometimes soon.)
- A confirmation code may be sent to e-mail or phone number;
- Slight issue may be the speed of which the server & the free services will deliver those codes.
- And then, on top of it all - will employers really "waste"/invest time into openning their e-mails just to test out this future?
- Wrapping up all these thoughts from my mind, I think it's to for now the 'reset password' feature to be just a simple button which is allowing an 'immediate reset'.
- And then, on top of it all - will employers really "waste"/invest time into openning their e-mails just to test out this future?
- Edit password may be cool feature to have: but I must think about the security of such an action. (May write a list of options sometimes soon.)