Front End Project 2

Setup

Notice we have two divs in our section.

One is a left box with some welcome text.
The other is a box on the right that will contain our events.

Add the background

The background image url is SVG image data.

Pay attention to background-color, background-attachment, and background-size. They indicate how the background should be positioned.

Style the Left Box

In your styles, let’s style the left box of the section. The leftBox is a class of a div:

Input the css one at a time to see what’s going. Notice that we give a padding of 50px:

Put a border around the div to see what’s going.

As you can see, we need to style our left box (welcome text):

Right Box

Now we style our right box (place where we will place our future events). We put a border around the div to first see where it is:

Then we give it a width and background color/opacity:

Finally, we give it a height:

Spacing Conflict

As you can see the extra padding (50px) from the leftBox makes it width not 50%. It would add the 50px to its 50% width.

This is why when you float your rightBox right, it gets nudged to the bottom.

In order to resolve this, use

The box-sizing property allows us to include the padding and border in an element’s total width and height.

If you set box-sizing: border-box; on an element, padding and border are included in the width and height:

It will re-adjust the textContent accordingly, and fit our padding inside the 50% width.

sameSite for shared session between domains (next js example)

ref –

  • https://stackoverflow.com/questions/5207160/what-is-a-csrf-token-what-is-its-importance-and-how-does-it-work
  • https://medium.com/swlh/how-to-use-cookies-to-persist-state-in-nextjs-934bed5e6da5

How to Use Cookies to Persist State (User) in NextJS

With LocalStorage

There are a number of ways to persist users in a React or Single Page Application.

A lot of times, devs generally use localStorage to store user data and load the data from there when required.

While this approach works, it’s not the most effective way as it leaves users vulnerable to attacks.

Using cookies is a little safer although it’s still not the safest option.

Personally, I prefer:

  • using cookies
  • JWT’s(JSON Web tokens) with expiry to persist user session
  • force a user to re-login when their session expires.

Using JWT’s is out of the scope of this article, we’ll only talk about cookies.

Getting started using cookies in React/NextJS

To use cookies in NextJS, we need to install 2 packages. For this tutorial, we’ll be using cookie and react-cookie. React-cookie allows us set the cookie from the client side while the cookie package lets us access the set cookie from the server-side. Install both packages by running

Setting a cookie

With both packages installed, It’s time to set a cookie.

Usually, we set a cookie for a user once they’ve successfully signed in or signed up to our application. To set a cookie on Sign in, follow the example below.

Session Token

The session token, also known as a sessionID, is an encrypted, unique string that identifies the specific session instance. If the session token is known to a protected resource such as an application, the application can access the session and all user information contained in it.

sameSite

The sameSite feature merely indicates whether a cookie can be retrieved through a different website with a different origin. Ideally, this should be accurate as it offers just one layer of defense against cross-site attacks.

CSRF tokens

A CSRF token is a secure random token (e.g., synchronizer token or challenge token) that is used to prevent CSRF attacks. The token needs to be unique per user session and should be of large random value to make it difficult to guess. A CSRF secure application assigns a unique CSRF token for every user session.

In a NEXT JS project, a session generates a unique session token, as well as a csrf token.

Say we sign in, and it uses CredentialsProvider. We need to evaluate it, and the credentials object has all the data:

Credential object looks like this in the code:

And in the browser, these values are stored in the browser cookie:

Cross-Site Request Forgery (CSRF) in simple words

Assume you are currently logged into your online banking at www.mybank.com
Assume a money transfer from mybank.com will result in a request of (conceptually) the form http://www.mybank.com/transfer?to=;amount=. (Your account number is not needed, because it is implied by your login.)
You visit www.cute-cat-pictures.org, not knowing that it is a malicious site.
If the owner of that site knows the form of the above request (easy!) and correctly guesses you are logged into mybank.com (requires some luck!), they could include on their page a request like http://www.mybank.com/transfer?to=123456;amount=10000 (where 123456 is the number of their Cayman Islands account and 10000 is an amount that you previously thought you were glad to possess).
You retrieved that www.cute-cat-pictures.org page, so your browser will make that request.
Your bank cannot recognize this origin of the request: Your web browser will send the request along with your www.mybank.com cookie and it will look perfectly legitimate. There goes your money!
This is the world without CSRF tokens.

Now for the better one with CSRF tokens

The transfer request is extended with a third argument: http://www.mybank.com/transfer?to=123456;amount=10000;token=31415926535897932384626433832795028841971.
That token is a huge, impossible-to-guess random number that mybank.com will include on their own web page when they serve it to you. It is different each time they serve any page to anybody.
The attacker is not able to guess the token, is not able to convince your web browser to surrender it (if the browser works correctly…), and so the attacker will not be able to create a valid request, because requests with the wrong token (or no token) will be refused by www.mybank.com.
Result: You keep your 10000 monetary units.

Next JS Pagination

ref – https://codesandbox.io/p/github/Elijah-trillionz/swr-features-app/main?file=/pages/fetching/numbered-pagination-comments.js:1,1-51,1

Next.js Full Tutorial for Beginners | Next.js 13 Full Stack App by Lama Dev

ref – https://www.youtube.com/watch?v=VE8BkImUciY

go t o your dirctory folder

npx create-next-app@latest .

no typescript
yes lint
no tailwind
yes /src
yes app router
yes import alias

Client Side

Next JS has client side and server side. Client side is usually components that will be rendered on the client, such as dashboard components, header, footers, etc.

We signify it by using “use client” at the very top.

Now if you log, you’ll be able to see it on the browser log. We need our footer components to be rendered on the client side. So we must do it for our Footer component.

Similarly, for Register page, Login Page…whatever page that should be rendered on the client side.

src/app/dashboard/(auth)/register/page.jsx

We create a AuthProvider component that uses SessionProvider to keep a session going in its children.
We use this AuthProvider
src/components/AuthProvider/AuthProvider.jsx

we use it in client side layout:

src/app/layout.js

Server Side (API)

On the client side, we have client component dashboard. In our dashboard’s register’s page.jsx, we have a form, and in the submit handler, have this implementation to do a POST request on our server side api:

src/app/dashboard/(auth)/register/page.jsx

We fetch from our server api (/api/auth/register).

There, we have a route file, which has a POST implementation. The parameter is a request object sent from dashboard/(auth)/register.page.jsx

1) We retrieve the body data from via JSON.
2) We’re on the server, so we connect to our mongo db.
3) We then hash the password.
4) Create a new User model, and then save it into the mongo db collections.

src/app/api/auth/register/route.js

Create user

Creating a User starts at the client page dashboard/(auth)/register/page.jsx. We have a form, and after inputting data, we execute the submit handler.

src/app/dashboard/(auth)/register/page.jsx

The request object gets passed into the POST implementation.
We extract it.
We then need to connect to the mongodb because we need to make an INSERT into the mongodb via Models.
But first, let’s has our password before we save it into the db.

When the hashedPassword is successfull, we create a new User model from it and the rest of the request body data.

Then since this is a mongoose model, we simply call save() on it to INSERT the data into the remote database.

src/app/api/auth/register/route.js

After successful insertion, we have our router go to dashboard’s login:

with parameter key success and a value Account has been created.

This means that we have a param object that will be available in Login component.

In it, useEffect will see that params have been updated and then setSuccess to get the value of the key ‘success’, which is ‘Account has been created’.

Therefore, it will set the state success to “Account has been created”.

Finally, make sure your user exists in the db

After you create a User, you Log In

We start off at dashboard’s login component.

We fill in the email/pwd and click the submit handler.

src/app/dashboard/(auth)/login/page.jsx

The submit handler is simply:

src/app/dashboard/(auth)/login/page.jsx

The event object’s target property has all the values stored.
We use signIn with the provider name “credential”, and a param object.

src/app/api/auth/[…nextauth]/route.js

In our Server api’s auth, we use NextAuth to authenticate credentials:

We specify how we want to authenticate credentials. Earlier we saw that we used signIn(“credentail”, {..})
“credential” matches ouCredentialsProvider’s id “credentials”. So this means that we will hit this authorize function.

The credential object looks like this:

Then we connect to the DB. When we connect to the DB, all models’ functionalities will update the mongo db. So we use User model to findOne with the email.

Once its found, we use bcrypt’s compare to see if the incoming password and the db password match.

If the password is correct, we return the user object.
Else, we throw a Wrong Credential msg.

Logged in and ready to Create New Post

Add New Post form is in dashboard’s page.

src/app/dashboard/page.jsx

The submit handler gets all textfield values and then put inside of a body of the fetch.
We do a fetch using POST on api/posts

This is very straightforward because we simply hit the POST function, extract the body, and create a new Post model from that body.
We call save on the model, and this updates the mongo db.

src/app/api/posts/route.js

Delete Post

Simply click on the delete link to delete the Post. We make a DELETE request on /api/posts/:id to trigger the DELETE function in the API.

src/app/dashboard/page.jsx

the id of the post gets passed as params.

When you delete, you’ll see the log:

API – You are trying to (DELETE) post id 64f1d35062e470734364fc72

So the id of the posts matches that of the document in our mongodb.

air bnb clone

Full Stack Airbnb Clone with Next.js 13 App Router: React, Tailwind, Prisma, MongoDB, NextAuth 2023

ref – https://youtu.be/c_-b_isI4vg

Setup

In code:

In terminal:

In code:

In code:

In code:

Nav Bar

Socket IO demo

socket-io-demo

What is Socket.IO? Socket.IO was created in 2010. It was developed to use open connections to facilitate realtime communication, still a relatively new phenomenon at the time. Socket.IO allows bi-directional communication between client and server.

Socket.IO is a library that enables low-latency, bidirectional and event-based communication between a client and a server. It is built on top of the WebSocket protocol and provides additional guarantees like fallback to HTTP long-polling or automatic reconnection

Now, Socket.IO is a library that provides an abstraction layer on top of WebSockets, making it easier to create realtime applications.

Specifically, Socket.IO is a library that enables low-latency, bidirectional and event-based communication between a client and a server.

Thus, we will be using Socket IO library to create real time apps.

Creating a Socket.IO server in Node

ref – https://socket.io/docs/v4/server-initialization/

After we install have installed

in our packpage.json, we can use it in our code. We import it from socket.io and use it to initialize a server to keep track

Now that we have the io Server instance, let’s use it to listen for sockets that connects to it:

We provide an anonymous function (which executes some code) whenever socket connects to our server.

We log to let the user see that a socket has connected. Each socket has a unique id for identification. Each new connection is assigned a random 20-characters identifier

Then we want to provide event actions for when that socket sends this certain event.

Hence, right away, when a socket connects, we say, in the future, if the socket sends an event with string ID
join_room or send_message we want to do some action.

In our case, if the client sends an event join_room, we call join to subscribe the socket to a given channel, where the id is the channel’s unique id.

ref – https://socket.io/docs/v4/broadcasting-events/

You can also broadcast to a room from a given socket:

In the above case, every socket in the room excluding the sender will get the event.

everyone gets the message except the sender

So we run the server and client A.

Client A connects and joins room XMAS, then Client B connects.

Client B joins room XMAS (subscribes to channel XMAS) and then Client A sends message “merry xmas”. Client B will receive it.
Notice Client A will not receive its own message because we have used socket.to(room). We send it to everyone, but excludes the sender.

Everyone including the sender gets the message

Say we simply use emit. It will send the message to everyone AND ALSO the sender.

So let’s open up another browser and join the room XMAS. This time, at Client A’s browser, send a message “HO HO HO”.
You’ll see that everyone, including the sender will get the message.