diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 3d70248ba..000000000
--- a/.gitignore
+++ /dev/null
@@ -1,15 +0,0 @@
-node_modules
-.DS_Store
-.env
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-build
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-package-lock.json
\ No newline at end of file
diff --git a/Procfile b/Procfile
deleted file mode 100644
index c154cc553..000000000
--- a/Procfile
+++ /dev/null
@@ -1 +0,0 @@
-web: npm start --prefix backend
diff --git a/README.md b/README.md
index dfa05e177..78a608b7f 100644
--- a/README.md
+++ b/README.md
@@ -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 weeks assignment was to build a backend API and a React frontend. The backend handles user authentication, while the frontend includes a registration form, token storage, and access to restricted (secret) content after login.
## 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?
+Backend:
+I followed the codealong and added more specific error messages.
+
+Frontend:
+I started by creating separate components for login and signup functionalities. These components communicate with the backend API for user authentication and account creation. Using styled components for styling and React Router for navigation. Error handling was implemented to provide clear feedback to users. You should get errors if you try and create an account that already exists, if you try to log in without creating a user, if you write the wrong username/password, you should get a success message when you've succesfully created a user which will be displayed on the login page ect. If I had more time, I would refine error messages and enhance user experience with features like form validation. I also intended to create a separate component named AccountPage.jsx to handle account-related functions and content. However, due to issues with the login, I the the code intended for the "Account Page" within LoginPage.jsx. This decision was made to resolve the login issues.
## 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-0pi0.onrender.com/
+
+Frontend: https://cheerful-froyo-159f59.netlify.app/
diff --git a/backend/package.json b/backend/package.json
index 8de5c4ce0..bb1ea689c 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -12,9 +12,11 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
+ "bcrypt-nodejs": "^0.0.3",
"cors": "^2.8.5",
"express": "^4.17.3",
- "mongoose": "^8.0.0",
+ "express-list-endpoints": "^7.1.0",
+ "mongoose": "^8.4.0",
"nodemon": "^3.0.1"
}
}
diff --git a/backend/server.js b/backend/server.js
index dfe86fb8e..9b1571085 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -1,24 +1,104 @@
import cors from "cors";
import express from "express";
import mongoose from "mongoose";
+import crypto from "crypto";
+import bcrypt from "bcrypt-nodejs";
+import expressListEndpoints from "express-list-endpoints";
-const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo";
+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
-const port = process.env.PORT || 8080;
+// Model
+const User = mongoose.model("User", {
+ name: {
+ type: String,
+ unique: true,
+ },
+ email: {
+ type: String,
+ unique: true,
+ },
+ password: {
+ type: String,
+ required: true,
+ },
+ accessToken: {
+ type: String,
+ default: () => crypto.randomBytes(128).toString("hex"),
+ },
+});
+
+// Middleware to authenticate user with access token
+const authenticateUser = async (req, res, next) => {
+ const accessToken = req.header("Authorization");
+ if (!accessToken) {
+ return res.status(401).json({ error: "Unauthorized: Access token is missing" });
+ }
+
+ const user = await User.findOne({ accessToken });
+ if (!user) {
+ return res.status(401).json({ error: "Unauthorized: Access token is invalid" });
+ }
+
+ req.user = user;
+ next();
+};
+
+const port = process.env.PORT || 8082;
const app = express();
-// Add middlewares to enable cors and json body parsing
-app.use(cors());
+app.use(cors({
+ origin: 'https://cheerful-froyo-159f59.netlify.app'
+}));
app.use(express.json());
-// Start defining your routes here
+// Routes
app.get("/", (req, res) => {
- res.send("Hello Technigo!");
+ const endpoints = expressListEndpoints(app);
+ res.json(endpoints);
+});
+
+// Registration endpoint to create a new user
+app.post("/users", async (req, res) => {
+ try {
+ const { name, email, password } = req.body;
+ if (!name) {
+ return res.status(400).json({ error: "Name is required" });
+ }
+ if (!email) {
+ return res.status(400).json({ error: "Email is required" });
+ }
+ if (!password) {
+ return res.status(400).json({ error: "Password is required" });
+ }
+
+ const user = new User({ name, email, password: bcrypt.hashSync(password) });
+ await user.save();
+ res.status(201).json({ id: user._id, accessToken: user.accessToken });
+ } catch (err) {
+ console.error("User creation failed:", err);
+ res.status(400).json({ error: "Could not create user. Something went wrong." });
+ }
+});
+
+// Login endpoint to authenticate a user
+// Find the user by email, if user is found and password is correct, generate and return an access token
+app.post("/login", async (req, res) => {
+ const { email, password } = req.body;
+
+ const user = await User.findOne({ email });
+ if (!user || !bcrypt.compareSync(password, user.password)) {
+ return res.status(401).json({ message: "Invalid email or password" });
+ }
+
+ const accessToken = user.accessToken;
+ res.status(200).json({ accessToken });
+});
+
+// Route to access secrets after authentication
+app.get("/secrets", authenticateUser, (req, res) => {
+ res.json({ secret: "This is a super secret message" });
});
// Start the server
diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs
deleted file mode 100644
index 4dcb43901..000000000
--- a/frontend/.eslintrc.cjs
+++ /dev/null
@@ -1,20 +0,0 @@
-module.exports = {
- root: true,
- env: { browser: true, es2020: true },
- extends: [
- 'eslint:recommended',
- 'plugin:react/recommended',
- 'plugin:react/jsx-runtime',
- 'plugin:react-hooks/recommended',
- ],
- ignorePatterns: ['dist', '.eslintrc.cjs'],
- parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
- settings: { react: { version: '18.2' } },
- plugins: ['react-refresh'],
- rules: {
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
- },
-}
diff --git a/frontend/.gitignore b/frontend/.gitignore
deleted file mode 100644
index 265f50c92..000000000
--- a/frontend/.gitignore
+++ /dev/null
@@ -1,26 +0,0 @@
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-lerna-debug.log*
-
-node_modules
-dist
-dist-ssr
-*.local
-
-# Editor directories and files
-.vscode/*
-!.vscode/extensions.json
-.idea
-.DS_Store
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-
-package-lock.json
\ No newline at end of file
diff --git a/frontend/README.md b/frontend/README.md
deleted file mode 100644
index f768e33fc..000000000
--- a/frontend/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# React + Vite
-
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
-
-Currently, two official plugins are available:
-
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
diff --git a/frontend/package.json b/frontend/package.json
index e9c95b79f..3e4de261d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,8 +10,12 @@
"preview": "vite preview"
},
"dependencies": {
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/react-fontawesome": "^0.2.1",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.23.1",
+ "styled-components": "^6.1.11"
},
"devDependencies": {
"@types/react": "^18.2.15",
diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg
deleted file mode 100644
index e7b8dfb1b..000000000
--- a/frontend/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 1091d4310..edd13986a 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,3 +1,16 @@
+import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
+import { StartPage } from "./components/StartPage.jsx";
+import { LoginPage } from "./components/LoginPage.jsx";
+import { SignUpPage } from "./components/SignUpPage.jsx";
+
export const App = () => {
- return
+ Welcome,
+ Glad to see you!
+
+
+ Login
+
+
+ Sign Up
+
+
+
+ );
+};
diff --git a/frontend/src/components/StyledButton.jsx b/frontend/src/components/StyledButton.jsx
new file mode 100644
index 000000000..db6b271e5
--- /dev/null
+++ b/frontend/src/components/StyledButton.jsx
@@ -0,0 +1,20 @@
+import styled from "styled-components";
+
+export const StyledButton = styled.button`
+ font-size: 1em;
+ padding: 0.75em;
+ margin: 0.25em;
+ width: 350px;
+ color: #000000;
+ background-color: #ffffff;
+ border: none;
+ border-radius: 0.5em;
+ cursor: pointer;
+ box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: scale(1.03);
+ box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.2);
+ }
+`;
diff --git a/frontend/src/components/StyledText.jsx b/frontend/src/components/StyledText.jsx
new file mode 100644
index 000000000..3b7229dec
--- /dev/null
+++ b/frontend/src/components/StyledText.jsx
@@ -0,0 +1,19 @@
+import styled from "styled-components";
+
+export const StyledHeading = styled.div`
+ font-size: ${(props) => props.size || "2.2em"};
+ color: #ffffff;
+ font-weight: ${(props) => (props.fontWeight === "bold" ? "bold" : "normal")};
+ text-align: center;
+`;
+
+export const SmallText = styled.p`
+ font-size: 0.85em;
+ color: #000000;
+`;
+
+export const SecretText = styled.p`
+ font-size: 1.5em;
+ color: #000000;
+ text-align: center;
+`;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 3e560a674..a713e870e 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -1,13 +1,14 @@
-:root {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
- "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap");
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
- monospace;
-}
\ No newline at end of file
+body {
+ background: linear-gradient(
+ to bottom,
+ #b562b3,
+ #ff9a26
+ ); /* Gradient from top to bottom */
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-family: "Inter", sans-serif;
+}
diff --git a/instructions.md b/instructions.md
deleted file mode 100644
index eccc02575..000000000
--- a/instructions.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Instructions
-Your project needs two parts; a backend API, and a React frontend. You'll need to create a `User` model using mongoose, with properties for your registered user, and to store a user's access token.
-
-Then, on the frontend side of things, you'll need to build up a registration form that POSTs to your API. You'll need to store the access token you get back in the browser using local storage, and then use that token when making other requests to your API.
-
-Once a user is logged in, you will need to have one last endpoint which returns some content which only logged-in users should be able to access. You can choose what you want this endpoint to return, and if you want to make multiple endpoints, that's fine too. It could return hard-coded data or something from the database - either is fine. Whatever you choose, it should be displayed in the frontend after you've logged in.
-
-To summarise, your API needs:
-- Registration endpoint, to create a new user.
-- Sign-in endpoint, to authenticate a returning user.
-- An authenticated endpoint which only returns content if the `Authorization` header with the user's token was correct.
-
-Your frontend needs:
-- A registration form.
-- A sign-in form.
-- A page to show the authenticated content from the API.
-- A 'sign out' button that removes the saved access token and redirects the user to the login form.
-
-## Requirements
-- Your API should have routes to register and login, and finally an authenticated endpoint.
-- The authenticated endpoint should return a 401 or 403 (see [401 vs. 403 on SO](https://stackoverflow.com/questions/3297048/403-forbidden-vs-401-unauthorized-http-responses)) with an error message if you try to access it without an `Authentication` access token or with an invalid token.
-- Your frontend should have a registration form which POSTs to the API to create a new user.
-- Your passwords in the database should be encrypted with bcrypt.
-- Your API should validate the user input when creating a new user, and return error messages which could be shown by the frontend (displaying the errors in a nice way in the frontend is a stretch goal - it’s fine to just show 'Something went wrong' on the frontend if you run out of time).
-
-## Stretch goals
-So you’ve completed the requirements? Great job! Make sure you've committed and pushed a version of your project before starting on the stretch goals. Remember that the stretch goals are optional.
-
-### Intermediate Stretch Goals
-- Store data in the database for your authenticated data routes.
-- When registering, display error messages from the API next to the field which has the error. For example, if the email address is invalid, show an error message next to the email input.
-- To challenge yourself, try to implement Google authentication with Firebase. [Here](https://www.freecodecamp.org/news/react-firebase-authentication-and-crud-operations/) you will find detailed tutorial which will guide you through implementation (some of the steps connected to [Material UI](https://mui.com/) components can be replaced with your custom components).
-
-
-### Advanced Stretch Goals
-- Add more routes, perhaps even a `POST` route to create new objects in your database as a logged-in user.
-- Improve validations in the backend to ensure unique email addresses, or validate the email address format using a regular expression.
diff --git a/netlify.toml b/netlify.toml
index 95443a1f3..a0ad77f26 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -2,5 +2,5 @@
# how it should build the JavaScript assets to deploy from.
[build]
base = "frontend/"
- publish = "build/"
+ publish = "dist/"
command = "npm run build"
diff --git a/package.json b/package.json
deleted file mode 100644
index d774b8cc3..000000000
--- a/package.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "project-auth-parent",
- "version": "1.0.0",
- "scripts": {
- "postinstall": "npm install --prefix backend"
- }
-}
diff --git a/pull_request_template.md b/pull_request_template.md
deleted file mode 100644
index d92c89b51..000000000
--- a/pull_request_template.md
+++ /dev/null
@@ -1,7 +0,0 @@
-## Netlify link
-Add your Netlify link here.
-PS. Don't forget to add it in your readme as well.
-
-## Collaborators
-Add your collaborators here. Write their GitHub usernames in square brackets. If there's more than one, separate them with a comma, like this:
-[github-username-1, github-username-2]