******VIDEOCONFERENCE PAGE*****

 VIDEOCONFERENCE.JSX

import React, { useEffect, useRef, useState } from "react";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import { Badge, IconButton } from "@mui/material";
import ScreenShareTwoToneIcon from "@mui/icons-material/ScreenShareTwoTone";
import StopScreenShareTwoToneIcon from "@mui/icons-material/StopScreenShareTwoTone";
import SettingsVoiceSharpIcon from "@mui/icons-material/SettingsVoiceSharp";
import MicOffOutlinedIcon from "@mui/icons-material/MicOffOutlined";
import VideocamOffOutlinedIcon from "@mui/icons-material/VideocamOffOutlined";
import ForumOutlinedIcon from "@mui/icons-material/ForumOutlined";
import CallEndOutlinedIcon from "@mui/icons-material/CallEndOutlined";
import VideocamOutlinedIcon from "@mui/icons-material/VideocamOutlined";
import ForumIcon from "@mui/icons-material/Forum";
import SendRoundedIcon from "@mui/icons-material/SendRounded";
import EmojiPicker from "emoji-picker-react";
import MoodOutlinedIcon from "@mui/icons-material/MoodOutlined";
import { io } from "socket.io-client";
import styles from "../styles/VC.module.css";
import { useParams } from "react-router-dom";
import { useLocation } from "react-router-dom";
import { Typography } from "@mui/material";

const server_Url = "http://localhost:8080";
let connections = {};
let makingOffer = {};
let ignoreOffer = {};
let isPolite = {};

const peerConfigConnections = {
  iceServers: [{ urls: "stun:stun1.l.google.com:19302" }],
};

export default function VideoConference() {
  const socketRef = useRef();
  const socketIdRef = useRef();
  const localVideoRef = useRef();
  const bottomRef = useRef();
  const [videoAvailable, setVideoAvailable] = useState(true);
  const [audioAvailable, setAudioAvailable] = useState(true);
  const [screenAvailable, setScreenAvailable] = useState();
  const [video, setVideo] = useState([]);
  const [audio, setAudio] = useState();
  const [screen, setScreen] = useState();
  const [showChat, setShowChat] = useState();
  const [askForUsername, setAskForUsername] = useState(true);
  const [username, setUsername] = useState("");
  const [videos, setVideos] = useState([]);
  const [newMessages, setNewMessages] = useState([]);
  const [message, setMessage] = useState("");
  const [messages, setMessages] = useState([]);
  const [showEmoji, setShowEmoji] = useState(false);
  const { meetingCode } = useParams();
  const room = meetingCode;
  const location = useLocation();
  const count = videos.length + 1;
  useEffect(() => {
    getPermissions();
  }, []);
  const isHost = location.state?.isHost ?? false;
  const isGuest = location.state?.isGuest ?? false;
  const getPermissions = async () => {
    try {
      const videoStream = await navigator.mediaDevices.getUserMedia({
        video: true,
      });
      setVideoAvailable(true);
      console.log("video permission granted!");

      const audioStream = await navigator.mediaDevices.getUserMedia({
        audio: true,
      });
      setAudioAvailable(true);
      console.log("audio permission granted!");

      const screenSharePermission =
        typeof navigator.mediaDevices.getDisplayMedia === "function";
      setScreenAvailable(screenSharePermission);

      const userMediaStream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true,
      });

      window.localStream = userMediaStream;
      if (localVideoRef.current) {
        localVideoRef.current.srcObject = userMediaStream;
      }
    } catch (e) {
      console.log("Permission error:", e);
      setVideoAvailable(false);
      setAudioAvailable(false);
    }
  };

  useEffect(() => {
    if (video !== undefined || audio !== undefined) {
      getUserMedia();
    }
  }, [audio, video]);

  const silence = () => {
    let ctx = new AudioContext();
    let oscillator = ctx.createOscillator();
    let dst = oscillator.connect(ctx.createMediaStreamDestination());
    oscillator.start();
    ctx.resume();
    return Object.assign(dst.stream.getAudioTracks()[0], { enabled: false });
  };

  const black = ({ width = 333, height = 600 } = {}) => {
    let canvas = Object.assign(document.createElement("canvas"), {
      width,
      height,
    });
    canvas.getContext("2d").fillRect(0, 0, width, height);
    let stream = canvas.captureStream();
    return Object.assign(stream.getVideoTracks()[0], { enabled: false });
  };

  const connect = () => {
    localStorage.setItem("username", username);
    setAskForUsername(false);
    setVideo(videoAvailable);
    setAudio(audioAvailable);
    getUserMedia();

    connectToSocketServer();
  };

  const connectToSocketServer = () => {
    socketRef.current = io(server_Url);

    socketRef.current.on("connect", () => {
      socketIdRef.current = socketRef.current.id;
      socketRef.current.emit("join-call", { room, username, isHost });
    });

    socketRef.current.on("signal", gotMessageFromServer);
    socketRef.current.on("chat-message", addMessage);

    socketRef.current.on("user-left", (id) => {
      setVideos((videos) => videos.filter((video) => video.socketId !== id));
    });

    socketRef.current.on("user-joined", ({ id, clients }) => {
      clients.forEach((client) => {
        const socketListId = client.id;
        const username = client.username;
        if (connections[socketListId]) return;

        connections[socketListId] = new RTCPeerConnection(
          peerConfigConnections
        );
        makingOffer[socketListId] = false;
        ignoreOffer[socketListId] = false;
        isPolite[socketListId] = socketIdRef.current < socketListId;

        connections[socketListId].onicecandidate = (event) => {
          if (event.candidate) {
            socketRef.current.emit(
              "signal",
              socketListId,
              JSON.stringify({ ice: event.candidate })
            );
          }
        };

        connections[socketListId].onnegotiationneeded = async () => {
          try {
            makingOffer[socketListId] = true;
            const offer = await connections[socketListId].createOffer();
            await connections[socketListId].setLocalDescription(offer);
            socketRef.current.emit(
              "signal",
              socketListId,
              JSON.stringify({
                sdp: connections[socketListId].localDescription,
              })
            );
          } catch (err) {
            console.error(err);
          } finally {
            makingOffer[socketListId] = false;
          }
        };

        connections[socketListId].ontrack = (event) => {
          console.log("ontrack fired from", socketListId, event.streams);

          if (!event.streams || !event.streams[0]) {
            console.warn("No remote stream received");
            return;
          }

          setVideos((prev) => {
            const updated = prev.filter((v) => v.socketId !== socketListId);
            return [
              ...updated,
              {
                socketId: socketListId,
                stream: event.streams[0],
                username,
                role: client.role,
              },
            ];
          });
        };

        const localStream = window.localStream;
        const senders = connections[socketListId].getSenders();

        localStream.getTracks().forEach((track) => {
          connections[socketListId].addTrack(track, localStream);
        });
      });
    });
  };

  const gotMessageFromServer = async (fromId, message) => {
    const signal = JSON.parse(message);
    const pc = connections[fromId];
    const polite = isPolite[fromId];

    try {
      if (signal.sdp) {
        const desc = new RTCSessionDescription(signal.sdp);
        const offerCollision =
          signal.sdp.type === "offer" &&
          (makingOffer[fromId] || pc.signalingState !== "stable");

        ignoreOffer[fromId] = !polite && offerCollision;
        if (ignoreOffer[fromId]) return;

        await pc.setRemoteDescription(desc);

        if (signal.sdp.type === "offer") {
          const answer = await pc.createAnswer();
          await pc.setLocalDescription(answer);
          socketRef.current.emit(
            "signal",
            fromId,
            JSON.stringify({ sdp: pc.localDescription })
          );
        }
      }

      if (signal.ice) {
        try {
          await pc.addIceCandidate(new RTCIceCandidate(signal.ice));
        } catch (err) {
          if (!ignoreOffer[fromId]) console.error("Error adding ICE", err);
        }
      }
    } catch (err) {
      console.error("Signal error from", fromId, err);
    }
  };

  const getUserMediaSuccess = (stream) => {
    try {
      window.localStream?.getTracks().forEach((track) => track.stop());
    } catch (e) {
      console.log(e);
    }

    window.localStream = stream;
    if (localVideoRef.current) localVideoRef.current.srcObject = stream;

    Object.entries(connections).forEach(([id, conn]) => {
      if (id !== socketIdRef.current) {
        const senders = conn.getSenders();
        stream.getTracks().forEach((track) => {
          const sender = senders.find((s) => s.track?.kind === track.kind);
          if (sender) {
            sender.replaceTrack(track);
          } else {
            conn.addTrack(track, stream);
          }
        });
      }
    });
    stream.getTracks().forEach(
      (track) =>
        (track.onended = () => {
          setVideo(false);
          setAudio(false);

          try {
            let tracks = localVideoRef.current.srcObject.getTracks();
            tracks.forEach((track) => track.stop());
          } catch (e) {
            console.log(e);
          }

          let blackSilence = (...args) =>
            new MediaStream([black(...args), silence()]);
          window.localStream = blackSilence();
          localVideoRef.current.srcObject = window.localStream;

          for (let id in connections) {
            const conn = connections[id];

            window.localStream.getTracks().forEach((track) => {
              const sender = conn
                .getSenders()
                .find((s) => s.track?.kind === track.kind);

              if (sender) {
                sender.replaceTrack(track);
              } else {
                conn.addTrack(track, stream);
              }
            });
            conn
              .createOffer()
              .then((description) =>
                conn
                  .setLocalDescription(description)
                  .then(() =>
                    socketRef.current.emit(
                      "signal",
                      id,
                      JSON.stringify({ sdp: conn.localDescription })
                    )
                  )
                  .catch((e) => console.log(e))
              )
              .catch((e) => console.log(e));
          }
        })
    );
  };

  const getUserMedia = () => {
    if ((video && videoAvailable) || (audio && audioAvailable)) {
      navigator.mediaDevices
        .getUserMedia({ video, audio })
        .then(getUserMediaSuccess)
        .catch((e) => console.log("getUserMedia error", e));
    } else {
      try {
        localVideoRef.current?.srcObject
          ?.getTracks()
          .forEach((track) => track.stop());
      } catch {}
    }
  };
  let addMessage = (data, sender, socketIdSender) => {
    setMessages((prevMsg) => [...prevMsg, { sender: sender, data: data }]);
    if (socketIdSender !== socketIdRef.current) {
      setNewMessages((prevMsg) => prevMsg + 1);
    }
  };
  let sendMessage = () => {
    socketRef.current.emit("chat-message", message, username);

    setMessage("");
  };
  const handleMic = () => {
    setAudio(!audio);
  };

  const handleVideo = () => {
    setVideo(!video);
  };
  let handleCallEnd = () => {
    try {
      let tracks = localVideoRef.current.srcObject.getTracks();
      tracks.forEach((track) => track.stop());
    } catch (e) {
      console.log(e);
    }
    isGuest ? (window.location.href = "/") : (window.location.href = "/home");
  };
  const handleScreenShare = () => {
    console.log(screen);
    if (screen) {
      StopScreenShare();
    } else {
      if (navigator.mediaDevices.getDisplayMedia) {
        navigator.mediaDevices
          .getDisplayMedia({ video: true, audio: true })
          .then(getDisplayMediaSuccess)
          .catch((e) => console.log(e));
      }
      setScreen(true);
    }
  };
  let StopScreenShare = () => {
    setScreen(false);
    try {
      //STOPPING THE STREAM (SCREEN SHARING)
      let tracks = localVideoRef.current.srcObject.getTracks();
      tracks.forEach((track) => track.stop());
    } catch (e) {
      console.log(e);
    }

    // ASSIGNING BACK THE CAMERA STREAM
    navigator.mediaDevices
      .getUserMedia({ video: true, audio: true })
      .then((camStream) => {
        window.localStream = camStream;
        localVideoRef.current.srcObject = camStream;

        Object.entries(connections).forEach(([id, conn]) => {
          if (id === socketIdRef.current) return;
          const senders = conn.getSenders();

          camStream.getTracks().forEach((track) => {
            const sender = senders.find((s) => s.track?.kind === track.kind);
            if (sender) {
              sender.replaceTrack(track);
            } else {
              conn.addTrack(track, camStream);
            }
          });

          conn
            .createOffer()
            .then((desc) => conn.setLocalDescription(desc))
            .then(() => {
              socketRef.current.emit(
                "signal",
                id,
                JSON.stringify({
                  sdp: conn.localDescription,
                })
              );
            });
        });
      })
      .catch((e) => console.log("Webcam error:", e));
  };
  let getDisplayMediaSuccess = (stream) => {

    //TO STOP EXISTING TRACKS
    try {
      window.localStream.getTracks().forEach((track) => track.stop());
    } catch (e) {
      console.log(e);
    }

    //ASSIGNING THE CURRENT STREAM
    window.localStream = stream;
    localVideoRef.current.srcObject = stream;


    Object.entries(connections).forEach(([id, conn]) => {
      if (id !== socketIdRef.current) {
        const senders = conn.getSenders();
        stream.getTracks().forEach((track) => {
          const sender = senders.find((s) => s.track?.kind === track.kind);

          // IF SENDER EXISTS WE REPLACE THE TRACK
          if (sender) {
            sender.replaceTrack(track);
          } else {
            // WE ADD A NEW TRACK
            conn.addTrack(track, stream);
          }
          // CREATNG OFFER
          conn.createOffer().then((description) => {
            // SETTING LOCAL DESCRIPTION
            conn
              .setLocalDescription(description)

              // EMITTING SIGNAL
              .then(() => {
                socketRef.current.emit(
                  "signal",
                  id,
                  JSON.stringify({ sdp: conn.localDescription })
                );
              })
              .catch((e) => console.log(e));
          });
        });
      }
    });

    // ON ENDING , WE STOP SCREEN SHARING
    stream.getTracks().forEach((track) => {
      track.onended = () => {
        console.log("stopped");
        StopScreenShare();
      };
    });
  };

  const getCount = (count) => {
    if (count === 3 || count === 4) return styles.grid2;
    if (count > 4 || count >= 9) return styles.grid3;
    if (count > 9) return styles.gird4;
    return styles.grid1;
  };

  useEffect(() => {
    bottomRef.current?.scrollIntoView();
  }, [messages]);
  // useEffect(() => {
  //   if (screen !== undefined) {
  //     getDisplayMedia();
  //   }
  // }, [screen]);
  return (
    <>
      <div className={styles.vcContainer}>
        {askForUsername ? (
          <>
            <h2
              style={{
                fontFamily: "Josefin Sans",
                color: "#4035351",
                fontSize: "3rem",
              }}
            >
              Enter into lobby
            </h2>
            {isHost ? (
              <p style={{fontSize:"2rem",color:"whitesmoke"}}>
                Share this Meeting Code with your Troop: <b style={{color:"gold"}}>{room}</b>
              </p>
            ) : (
              <></>
            )}
            <div className={styles.MeetContainer}>
              <TextField
                className={styles.inputsRequired}
                label="Username"
                value={username}
                variant="outlined"
                onChange={(e) => setUsername(e.target.value)}
              />
              <Button variant="contained" onClick={connect}>
                Connect
              </Button>
            </div>
            <video
              className={styles.videoInMeet}
              ref={localVideoRef}
              autoPlay
              muted
            ></video>
          </>
        ) : (
          <div className={styles.displayPage}>
            <div className={`${styles.videosContainer} ${getCount(count)}`}>
              <div className={styles.userVideo}>
                <video ref={localVideoRef} autoPlay muted></video>
                <Typography className={styles.youLabel}>you</Typography>
              </div>
              {videos.map(({ socketId, username, stream, role }) => (
                <div key={socketId} className={styles.userDisplay}>
                  <video
                    ref={(ref) => ref && (ref.srcObject = stream)}
                    autoPlay
                    playsInline
                  />
                  <div>
                    <Typography className={styles.userLabel}>
                      {username}
                    </Typography>
                    {role === "host" && (
                      <span style={{ color: "gold", position: "absolute" }}>
                        ⭐ Host
                      </span>
                    )}
                  </div>
                </div>
              ))}
            </div>
            {showChat ? (
              <div className={styles.chatBox}>
                <h2
                  style={{
                    fontFamily: "Josefin Sans",
                    color: "#4035351",
                    fontSize: "2rem",
                    marginTop: "1rem",
                    marginBottom: "0",
                  }}
                >
                  Chat
                </h2>
                <div className={styles.showChats}>
                  {messages.length > 0 ? (
                    messages.map((msg, index) => {
                      return (
                        <>
                          {msg.sender === username ? (
                            <div key={index} className={styles.sentChat}>
                              <p style={{ fontWeight: "bolder" }}>YOU</p>
                              <p>{msg.data}</p>
                            </div>
                          ) : (
                            <div key={index} className={styles.receivedChat}>
                              <p style={{ fontWeight: "bolder" }}>
                                {msg.sender.toUpperCase()}
                              </p>
                              <p>{msg.data}</p>
                            </div>
                          )}
                          <div ref={bottomRef}></div>
                        </>
                      );
                    })
                  ) : (
                    <>
                      <p>No messages yet!</p>
                    </>
                  )}
                </div>
                <div className={styles.chatMessage}>
                  <TextField
                    sx={{
                      backgroundColor: "#1e1e1e",
                      borderRadius: "8px",
                      input: {
                        color: "#ffffff",
                      },
                      label: {
                        color: "#ffffff",
                      },
                      "&:hover": {
                        backgroundColor: "#121212",
                      },
                      "& label.Mui-focused": {
                        color: "#ffffff",
                      },
                      "& .MuiOutlinedInput-root": {
                        "& fieldset": {
                          borderColor: "#ffffff",
                        },
                        "&:hover fieldset": {
                          borderColor: "#ffffff",
                        },
                        "&.Mui-focused fieldset": {
                          borderColor: "#ffffff",
                        },
                      },
                    }}
                    fullWidth
                    value={message}
                    label="Enter Message"
                    variant="outlined"
                    onChange={(e) => setMessage(e.target.value)}
                  />
                  <div className={styles.chatIcons}>
                    <IconButton onClick={() => setShowEmoji(!showEmoji)}>
                      <MoodOutlinedIcon
                        style={{ color: "white", fontSize: "2rem" }}
                      />
                    </IconButton>
                    {showEmoji && (
                      <div className={styles.emojiContainer}>
                        <EmojiPicker
                          style={{ height: "50vh" }}
                          onEmojiClick={(emojiData) =>
                            setMessage((prev) => prev + emojiData.emoji)
                          }
                        />
                      </div>
                    )}
                    <SendRoundedIcon
                      sx={{
                        fontSize: "2rem",
                        "&:hover": {
                          opacity: "0.5",
                          cursor: "pointer",
                        },
                      }}
                      onClick={sendMessage}
                    />
                  </div>
                </div>
              </div>
            ) : (
              <></>
            )}
            <div className={styles.Icons}>
              <IconButton onClick={handleMic} style={{ color: "white" }}>
                {audio === true ? (
                  <SettingsVoiceSharpIcon />
                ) : (
                  <MicOffOutlinedIcon />
                )}
              </IconButton>
              <IconButton onClick={handleVideo} style={{ color: "white" }}>
                {video === true ? (
                  <VideocamOutlinedIcon />
                ) : (
                  <VideocamOffOutlinedIcon />
                )}
              </IconButton>
              <IconButton
                onClick={handleScreenShare}
                style={{ color: "white" }}
              >
                {screen === true ? (
                  <StopScreenShareTwoToneIcon />
                ) : (
                  <ScreenShareTwoToneIcon />
                )}
              </IconButton>
              <IconButton onClick={handleCallEnd} style={{ color: "red" }}>
                <CallEndOutlinedIcon />{" "}
              </IconButton>
              <Badge
                color="secondary"
                onClick={() => setShowChat(!showChat)}
                badgeContent={newMessages}
                max={999}
                sx={{
                  "&:hover": {
                    opacity: "0.5",
                    cursor: "pointer",
                  },
                }}
              >
                {" "}
                {showChat ? (
                  <ForumOutlinedIcon style={{ color: "white" }} />
                ) : (
                  <ForumIcon style={{ color: "white" }} />
                )}
              </Badge>
            </div>
          </div>
        )}
      </div>
    </>
  );
}

VC.MODULE.CSS

.vcContainer {
  background: linear-gradient(135deg, #1e1e2f, #4a4a75);
  width: 100vw;
  height: 100vh;
  font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.MuiInputBase-root input {
  color: white;
}

.inputsRequired .MuiFormLabel-root {
  color: gray;
  padding: 1rem;
}
.inputsRequired .MuiOutlinedInput-notchedOutline {
  border-color: white;
}
.inputsRequired .MuiOutlinedInput-root:hover .MuiOutlinedInput-notchedOutline {
  border-color: gray;
}
.inputsRequired .MuiInputBase-root {
  margin: 1rem;
}
.MeetContainer {
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 20px;
  width: 50vh;
}

.videoInMeet {
  height: 50vh;
  width: 50vh;
  min-height: 20vh;
  min-width: 30vh;
  border-radius: 25px;
}
.displayPage {
  display: flex;
  flex-direction: column;
  width: 100vw;
  position: relative;
  height: 100vh;
}
.videosContainer {
  flex: 1;
  height: 100vh;

  display: grid;
  position: relative;
  gap: 1rem;
  align-items: center;
  overflow: auto;

  justify-items: center;
}
.grid1 {
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  grid-auto-rows: 1fr;
}
.grid2 {
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: minmax(200px, 1fr);
}
.grid3 {
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(200px, 1fr);
}
.grid4 {
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: minmax(200px, 1fr);
}
.userVideo,
.userDisplay {
  position: relative;
  border-radius: 8px;
  aspect-ratio: 16 / 9;
  background-color: #1e1e2f;
  display: flex;
  align-content: flex-end;
  flex-direction: column;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 500px;
  align-items: center;
}
.userVideo video,
.userDisplay video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 12px;
}

.youLabel,
.userLabel {
  position: absolute;
  bottom: 4px;
  left: 4px;
  color: white;
  background: rgba(0, 0, 0, 0.6);
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 14px;
}
.youLabel {
  display: none;
}
.userVideo:hover .youLabel {
  display: block;
}
.userLabel {
  display: none;
}
.userDisplay:hover .userLabel {
  display: block;
}

.Icons {
  bottom: 0;
  position: relative;
  width: 100%;
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  z-index: 10;
  background-color: rgb(30, 29, 29);
}

.chatBox {
  color: whitesmoke;
  position: absolute;
  height: 60vh;
  width: 60vh;
  background-color: #1d1c1c;
  right: 0;
  bottom: 0;
  padding: 1rem;
  margin-bottom: 2.7rem;
  border-radius: 10px;
  margin-right: 1.2rem;
}
.chatMessage {
  position: absolute;
  display: flex;
  justify-content: center;
  align-items: center;
  bottom: 0;
  width: 93%;
  margin-bottom: 10px;
  color: white;
}

.chatIcons .MuiSvgIcon-root {
  position: relative;
  right: 0;
}
.chatIcons {
  display: flex;
  justify-content: space-evenly;
  align-items: center;
}
.epr-main {
  height: 12px;
  width: 23px;
}
.emojiContainer {
  position: absolute;
  left: 0;
  bottom: 0;
  margin-bottom: 5rem;
}
.showChats {
  height: 46vh;
  bottom: 0;
  right: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 5px;
  scrollbar-width: thin;
  scroll-behavior: smooth;
  overflow-x: hidden;
  position: relative;
  scrollbar-gutter: stable;
}

.chatBox p {
  margin: auto;
}
.sentChat {
  align-self: flex-end;
  background: #848181;
  max-width: 150px;
  border-radius: 15px;
  padding: 10px;
  margin-bottom: 5px;
  margin-right: 10px;
  word-break: break-word;
  color: black;
}
.receivedChat {
  align-self: flex-start;
  background-color: rgb(218 217 226);
  max-width: 150px;
  border-radius: 15px;
  padding: 10px;
  margin-bottom: 5px;
  word-break: break-word;
  color: black;
}



Comments

Popular posts from this blog

MIDDLEWARE.JS

MODELS

AUTHENTICATION PAGE