Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
31487af
dependencies update and import
El1an3 May 20, 2024
f15207a
added basic code from codealong
El1an3 May 20, 2024
01929c5
created endpoints and schema
El1an3 May 22, 2024
d655441
updated dependencies in backend
El1an3 May 22, 2024
abcbc52
updated frontend dependencies & read me
El1an3 May 22, 2024
e6e10c2
created components and reusables
El1an3 May 22, 2024
e9faff7
started form
El1an3 May 22, 2024
cedccd9
changed readme
El1an3 May 22, 2024
09ccceb
move netlify to frontend folder
El1an3 May 22, 2024
3828d70
delete netlify file
El1an3 May 22, 2024
79794dd
started on form and header
El1an3 May 22, 2024
22374ce
styling and routes set up
El1an3 May 23, 2024
f9bf5f8
update routes
El1an3 May 23, 2024
e382850
fixed styling on deskopt
El1an3 May 23, 2024
c990fdb
fixed styling
El1an3 May 23, 2024
60d2647
minor styling fix
El1an3 May 23, 2024
d659724
added homepage, changed routing
El1an3 May 24, 2024
5af67bc
styled homepage
El1an3 May 24, 2024
b8331f1
registration form
El1an3 May 24, 2024
ad7aaa7
created Login and deleted Sign in
El1an3 May 24, 2024
c39c24b
finished frontend styling
El1an3 May 24, 2024
96431a0
styling fix dashboard on desktop
El1an3 May 24, 2024
ca710ed
styling changes, completing read me
El1an3 May 25, 2024
e6c6dc9
cleaning up code
El1an3 May 25, 2024
d55f1b9
added more error handling, fixed styling homepage
El1an3 May 25, 2024
fbfaf00
update read me
El1an3 May 26, 2024
5a6aeea
fixed env file added netlify.toml
El1an3 May 30, 2024
4447102
fixes in post route
El1an3 May 30, 2024
b2668a3
fixed error messages, added loading component
El1an3 May 30, 2024
eccfdaf
styling fix
El1an3 May 30, 2024
ecc6e87
another styling fix
El1an3 May 30, 2024
5b42e7e
.
El1an3 May 30, 2024
172b0f5
undo last commit
El1an3 May 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Project Auth API

Replace this readme with your own information about your project.

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
This fullstack project requires developing a backend to support user registration, login and an authenticated endpoint accessible only to loggen-in users.
The frontend features a startpage, a registration and login form and a page displaying authenticated content. Also sign-out button that removes the access token from local storage and brings the user back to the startpage.

## The problem

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
I started by using the code from the codealong in the backend, trying to figure out how to do an authentication process and use bycript properly.
Being frustrated with backend I started creating the frontend and for a bit my project was all over the place.
In the end I went back to finish and test backend and then finish a frontend that could also be used for our final project.

If I had more time, I would add a loading animation page, because it takes forever on login and registration.

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
Backend: https://project-auth-ziup.onrender.com
Frontend: https://project-auth-eliane.netlify.app
8 changes: 6 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"express": "^4.17.3",
"mongoose": "^8.0.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-list-endpoints": "^7.1.0",
"mongodb": "^4.17.2",
"mongoose": "^8.4.0",
"nodemon": "^3.0.1"
}
}
115 changes: 107 additions & 8 deletions backend/server.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,126 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
import dotenv from "dotenv";
import expressListEndpoints from "express-list-endpoints";
import bcrypt from "bcrypt";
import crypto from "crypto";

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo";
dotenv.config();

const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth";
mongoose.connect(mongoUrl);
mongoose.Promise = Promise;

// Defines the port the app will run on. Defaults to 8080, but can be overridden
// when starting the server. Example command to overwrite PORT env variable value:
// PORT=9000 npm start
//create schema and model
const { Schema, model } = mongoose;
const userSchema = new Schema({
name: {
type: String,
unique: true,
},
email: {
type: String,
unique: true,
},
password: {
type: String,
required: true,
},
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString("hex"),
},
});
const User = model("User", userSchema);

//defines the port the app will run on
const port = process.env.PORT || 8080;
const app = express();

// Add middlewares to enable cors and json body parsing
//add middlewares to enable cors and json body parsing
app.use(cors());
app.use(express.json());

// Start defining your routes here
//middleware to check if database is available
app.use((req, res, next) => {
if (mongoose.connection.readyState === 1) {
next();
} else {
res.status(503).json({ error: "service unavailable" });
}
});

//middleware to authenticate user
const authenticateUser = async (req, res, next) => {
const user = await User.findOne({ accessToken: req.header("Authorization") });
if (user) {
req.user = user;
next();
} else {
res.status(401).json({
loggedOut: true,
message: "You have to log in to get access",
});
}
};

//registration endpoint
app.post("/register", async (req, res) => {
try {
const { name, email, password } = req.body;
const salt = bcrypt.genSaltSync();
const user = new User({
name,
email,
password: bcrypt.hashSync(password, salt),
});
await user.save();
res.status(201).json({
success: true,
message: "User created",
id: user._id,
accessToken: user.accessToken,
});
} catch (error) {
res.status(400).json({
success: false,
message: "Could not create user",
errors: error,
});
}
});

//authenticated endpoint *super secret endpoint*
app.get("/dashboard", authenticateUser, (req, res) => {
res.json({
secret: "This is the secret dashboard, only visible to logged-in users!",
});
});

//login endpoint
app.post("/login", async (req, res) => {
//find user by name
const user = await User.findOne({ name: req.body.name });
//check if password is correct
if (user && bcrypt.compareSync(req.body.password, user.password)) {
//success
res.status(200).json({ userId: user._id, accessToken: user.accessToken });
} else {
//failure
res
.status(401)
.json({ notFound: true, message: "Invalid name or password" });
}
});

//route to list all endpoints
app.get("/", (req, res) => {
res.send("Hello Technigo!");
const endpoints = expressListEndpoints(app);
res.json(endpoints);
});

// Start the server
//start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
8 changes: 7 additions & 1 deletion netlify.toml → frontend/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
# how it should build the JavaScript assets to deploy from.
[build]
base = "frontend/"
publish = "build/"
publish = "dist"
command = "npm run build"

[[redirects]]
from = "/*"
to = "/index.html"
status = 200

7 changes: 5 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.23.1",
"styled-components": "^6.1.11"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"babel-plugin-styled-components": "^2.1.4",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
Expand Down
Binary file added frontend/public/globe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/header.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 16 additions & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Login } from "./components/Login";
import { Registration } from "./components/Registration";
import { Dashboard } from "./components/Dashboard";
import { Homepage } from "./components/Homepage";

export const App = () => {
return <div>Find me in src/app.jsx!</div>;
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/login" element={<Login />} />
<Route path="/registration" element={<Registration />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</BrowserRouter>
);
};
90 changes: 90 additions & 0 deletions frontend/src/components/Dashboard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//imports
import styled from "styled-components";
import { Button } from "../reusables/Button";
import Globe from "../../public/globe.png";
import HeaderPicture from "../../public/header.jpg";
import { useNavigate } from "react-router-dom";

//styling
const DashboardSection = styled.section`
display: flex;
flex-direction: column;

@media all and (min-width: 1024px) {
flex-direction: row;
}
`;

const DashboardHeader = styled.div`
display: flex;
background-image: url(${HeaderPicture});
background-size: cover;
background-position: center;
justify-content: space-between;
align-items: center;

@media all and (min-width: 1024px) {
flex-direction: column-reverse;
justify-content: flex-end;
width: 30%;
height: 100vh;
}
`;

const DashboardGlobe = styled.img`
height: 70px;
width: 70px;
margin: 20px;
@media all and (min-width: 1024px) {
height: 250px;
width: 250px;
}
`;

const ContentWrapper = styled.div`
background: var(--grey);
border-radius: 20px;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
margin: 50px;
padding: 20px;

@media all and (min-width: 1024px) {
width: 70%;
}
`;

const StyledButton = styled(Button)`
width: 250px;
`;

//component
export const Dashboard = () => {
const navigate = useNavigate();

const handleLogout = () => {
localStorage.removeItem("accessToken");
localStorage.removeItem("username");
//navigate to homepage
navigate("/");
};

return (
<DashboardSection>
<DashboardHeader>
<DashboardGlobe src={Globe} alt="globe with green background" />
<StyledButton onClick={handleLogout}>Log out</StyledButton>
</DashboardHeader>
<ContentWrapper>
<h1>Welcome</h1>
<p>🤫 This is only shown to logged in users 🤫 </p>
<p>👇</p>
<p>👇</p>
<p>👇</p>
<p>
Here we will show the dashboard with all your travel data, points and
distances.
</p>
</ContentWrapper>
</DashboardSection>
);
};
78 changes: 78 additions & 0 deletions frontend/src/components/Homepage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//import
import { Link } from "react-router-dom";
import styled from "styled-components";
import HeaderPicture from "../../public/header.jpg";
import Globe from "../../public/globe.png";
import { Button } from "../reusables/Button";

//styling
const StyledSection = styled.section`
display: flex;
flex-direction: column;
align-items: center;
background-image: url(${HeaderPicture});
background-size: cover;
background-position: center;
height: 100vh;
padding: 20px 0;
`;

const HeaderGlobeContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin: 20px 0;
`;

const HeaderGlobe = styled.img`
width: 280px;
height: 280px;

@media all and (min-width: 744px) {
width: 400px;
height: 400px;
}
`;

const BottomForm = styled.div`
background: var(--yellow);
position: fixed;
z-index: 0;
bottom: 0;
width: 280px;
height: 140px;
border-radius: 140px 140px 0 0;
display: flex;
justify-content: center;
align-items: center;
text-align: center;

@media all and (min-width: 744px) {
width: 400px;
height: 200px;
border-radius: 200px 200px 0 0;
}
`;

const ActionButton = styled(Button)`
position: relative;
z-index: 1;
`;

//component
export const Homepage = () => {
return (
<StyledSection>
<Link to={`/login`}>
<Button>Log in</Button>
</Link>
<HeaderGlobeContainer>
<HeaderGlobe src={Globe} alt="globe with green background" />
</HeaderGlobeContainer>
<Link to={`/registration`}>
<ActionButton>Take action</ActionButton>
</Link>
<BottomForm />
</StyledSection>
);
};
Loading