diff --git a/code/backend/package.json b/code/backend/package.json index 61fc594..049d92c 100644 --- a/code/backend/package.json +++ b/code/backend/package.json @@ -4,10 +4,6 @@ "main": "index.js", "license": "MIT", "dependencies": { - "@prisma/client": "^6.9.0", - "@types/bcryptjs": "^3.0.0", - "@types/multer": "^1.4.12", - "@types/readline-sync": "^1.4.8", "bcryptjs": "^3.0.2", "cors": "^2.8.5", "dotenv": "^16.5.0", @@ -23,9 +19,14 @@ "zod": "^3.24.4" }, "devDependencies": { + "@prisma/client": "^6.9.0", + "@types/bcryptjs": "^3.0.0", + "@types/cors": "^2.8.19", "@types/express": "^5.0.1", "@types/jsonwebtoken": "^9.0.9", + "@types/multer": "^1.4.12", "@types/node": "^22.15.2", + "@types/readline-sync": "^1.4.8", "@types/swagger-jsdoc": "^6.0.4", "@types/swagger-ui-express": "^4.1.8", "nodemon": "^3.1.10", diff --git a/code/backend/src/middleware/validationMiddleware.ts b/code/backend/src/middleware/validationMiddleware.ts index cb7aa19..c45df4f 100644 --- a/code/backend/src/middleware/validationMiddleware.ts +++ b/code/backend/src/middleware/validationMiddleware.ts @@ -11,8 +11,10 @@ export function validateData(schema: z.ZodObject) { } catch (error) { if (error instanceof ZodError) { const errorMessages = error.errors.map((issue: any) => ({ - message: `${issue.path.join(".")} is ${issue.message}`, - })); + path: issue.path.join("."), + message: issue.message, +})); + res .status(StatusCodes.BAD_REQUEST) .json({ error: "Invalid data", details: errorMessages }); diff --git a/code/backend/src/server.ts b/code/backend/src/server.ts index 91d4504..072e847 100644 --- a/code/backend/src/server.ts +++ b/code/backend/src/server.ts @@ -5,12 +5,21 @@ import userRouter from "./routes/userRoutes"; import postRouter from "./routes/postRoutes"; import { authenticateToken } from "./middleware/authenticateToken"; import bodyParser from "body-parser"; - +import cors from "cors"; dotenv.config(); const app = express(); -const port = 3000; - +const port = 3001; +app.use( + cors({ + origin: "http://localhost:3000", + credentials: true, + }) +); +app.use((req, res, next) => { + res.header("Access-Control-Expose-Headers", "Authorization"); + next(); +}); // minIO config export const minioClient = new Client({ endPoint: "localhost", // Replace with your MinIO server URL @@ -34,7 +43,7 @@ const options = { }, servers: [ { - url: "http://localhost:3000", + url: `http://localhost:${port}`, }, ], components: { @@ -56,7 +65,6 @@ const options = { }; const specs = swaggerJSDoc(options); app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(specs)); - app.use(bodyParser.json()); app.use("/api/user", userRouter); app.use("/api/posts", authenticateToken(), postRouter); diff --git a/code/backend/yarn.lock b/code/backend/yarn.lock index 4e9e305..7f117bc 100644 --- a/code/backend/yarn.lock +++ b/code/backend/yarn.lock @@ -159,6 +159,13 @@ dependencies: "@types/node" "*" +"@types/cors@^2.8.19": + version "2.8.19" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342" + integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg== + dependencies: + "@types/node" "*" + "@types/express-serve-static-core@^5.0.0": version "5.0.6" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz#41fec4ea20e9c7b22f024ab88a95c6bb288f51b8" diff --git a/code/frontend/package.json b/code/frontend/package.json index 00c82b1..6435e64 100644 --- a/code/frontend/package.json +++ b/code/frontend/package.json @@ -11,6 +11,7 @@ "@types/node": "^16.7.13", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "axios": "^1.10.0", "react": "^19.1.0", "react-dom": "^19.1.0", "react-scripts": "5.0.1", diff --git a/code/frontend/src/pages/LoginAndSignUpPage.tsx b/code/frontend/src/pages/LoginAndSignUpPage.tsx index 3df5f03..6d85888 100644 --- a/code/frontend/src/pages/LoginAndSignUpPage.tsx +++ b/code/frontend/src/pages/LoginAndSignUpPage.tsx @@ -1,23 +1,73 @@ import "./loginAndSignUpPage.css"; import { useState } from "react"; +import axios from "axios"; + +type FormData = { + username: string; + email: string; + password: string; +}; function LoginAndSignUpPage() { const toggleLogin = (event: React.MouseEvent) => { event.preventDefault(); + setErrorMessages(undefined); setSignup(!signup); }; - const [signup, setSignup] = useState(false); + const [signup, setSignup] = useState(false); + const [errorMessages, setErrorMessages] = useState<{ + error: String; + details: { message: string }[]; + }>(); + const [formData, setFormData] = useState({ + username: "", + email: "", + password: "", + }); + + const onSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + setErrorMessages(undefined); + try { + const response = signup + ? await axios.post("http://localhost:3001/api/user/register", { + email: formData.email, + username: formData.username, + password: formData.password, + }) + : await axios.post("http://localhost:3001/api/user/login", { + username: formData.username, + password: formData.password, + }); + const authHeader = response.headers["authorization"]; + if (authHeader && authHeader.startsWith("Bearer ")) { + const token = authHeader.substring(7); + console.log(token, "Hello"); + localStorage.setItem("token", token); + } + } catch (err: any) { + setErrorMessages(err.response.data); + console.error("error:", err.response.data); + } + }; + + const handleChange = (event: React.ChangeEvent) => { + const { name, value } = event.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; return (
-
+
{signup ? "Sign Up" : "Login"}
-
+ value={formData.username} + onChange={handleChange} + required + />
{signup ? (
@@ -41,8 +95,12 @@ function LoginAndSignUpPage() { + value={formData.email} + onChange={handleChange} + required + />
) : ( "" @@ -57,22 +115,35 @@ function LoginAndSignUpPage() { + value={formData.password} + onChange={handleChange} + required + minLength={signup ? 8 : undefined} + />
+ {errorMessages && ( +
+ {errorMessages.details.map((detial, index) => ( +

{detial.message}

+ ))} +
+ )}
-
- {signup ? "Already have an account?" : "Don't have an account yet?"}{" "} - Click here{" "} - {signup ? "to login." : "to sign up."} + {signup + ? "Already have an account? " + : "Don't have an account yet? "} + Click here + {signup ? " to login." : " to sign up."}
-
+
); diff --git a/code/frontend/src/pages/loginAndSignUpPage.css b/code/frontend/src/pages/loginAndSignUpPage.css index b7c93c6..d50d79e 100644 --- a/code/frontend/src/pages/loginAndSignUpPage.css +++ b/code/frontend/src/pages/loginAndSignUpPage.css @@ -1,185 +1,188 @@ -.background{ - display: flex; - flex-direction: column; - height: 100vh; /* Full viewport height */ - width: 100vw; /* Full viewport width */ - background: var(--gradient-blue-backround-mobile); - align-items: center; - justify-content: center; - background-attachment: fixed; +.background { + display: flex; + flex-direction: column; + height: 100vh; /* Full viewport height */ + width: 100vw; /* Full viewport width */ + background: var(--gradient-blue-backround-mobile); + align-items: center; + justify-content: center; + background-attachment: fixed; } -.login-text{ - display: flex; - padding: 40px; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 18px; - color: var(--Rotkehlchen-gray); - text-align: center; +.login-text { + display: flex; + padding: 40px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 18px; + color: var(--Rotkehlchen-gray); + text-align: center; } -.login-icon{ - height: 32px; - width: 32px; - aspect-ratio: 1/1; - align-self: center; +.login-icon { + height: 32px; + width: 32px; + aspect-ratio: 1/1; + align-self: center; } -.login-button{ - display: inline-flex; - padding: auto; - justify-content: center; - align-items: center; - gap: 10px; - width: 150px; - height: 32px; - cursor: pointer; +.login-button { + display: inline-flex; + padding: auto; + justify-content: center; + align-items: center; + gap: 10px; + width: 150px; + height: 32px; + cursor: pointer; - border-radius: 10px; - border: 2px solid var(--Rotkehlchen-orange-default); - background: var(--Rotkehlchen-yellow-default); + border-radius: 10px; + border: 2px solid var(--Rotkehlchen-orange-default); + background: var(--Rotkehlchen-yellow-default); } -.login-button:hover{ - border-radius: 10px; - border: 2px solid var(--Rotkehlchen-orange-default); - background: var(--Rotkehlchen-yellow-hover); +.login-button:hover { + border-radius: 10px; + border: 2px solid var(--Rotkehlchen-orange-default); + background: var(--Rotkehlchen-yellow-hover); } -.login-button:active{ - border-radius: 10px; - border: 2px solid var(--Rotkehlchen-orange-default); - background: var(--Rotkehlchen-orange-default); +.login-button:active { + border-radius: 10px; + border: 2px solid var(--Rotkehlchen-orange-default); + background: var(--Rotkehlchen-orange-default); } -.login-div-input{ - display: flex; - flex-direction: row; - border-radius: 4px; - border: 3px solid var(--Rotkehlchen-brown-dark); - background: #FFF; - width:100%; - height: 44px; - align-items: center; - justify-content: flex-start; +.login-div-input { + display: flex; + flex-direction: row; + border-radius: 4px; + border: 3px solid var(--Rotkehlchen-brown-dark); + background: #fff; + width: 100%; + height: 44px; + align-items: center; + justify-content: flex-start; } -.login-div-input:hover{ - border: 3px solid var(--Rotkehlchen-brown-middle); +.login-div-input:hover { + border: 3px solid var(--Rotkehlchen-brown-middle); } -.login-div-input:focus-within{ - border: 3px solid var(--Rotkehlchen-orange-default) +.login-div-input:focus-within { + border: 3px solid var(--Rotkehlchen-orange-default); } -.login-input{ - width: 100%; - height: 40px; - border: none; - color: var(--Rotkehlchen-brown-middle); - outline: 0 !important; - +.login-input { + width: 100%; + height: 40px; + border: none; + color: var(--Rotkehlchen-brown-middle); + outline: 0 !important; } -.login-here{ - color: var(--Rotkehlchen-orange-default); - background-color: transparent; - border: none; - text-decoration: underline; - cursor: pointer; +.login-here { + color: var(--Rotkehlchen-orange-default); + background-color: transparent; + border: none; + text-decoration: underline; + cursor: pointer; } -.login-signup{ - color: #FFF; - text-align: center; - cursor: pointer; +.login-signup { + color: #fff; + text-align: center; + cursor: pointer; } -.login-part{ - display: flex; - padding: 8px 40px 40px 40px; - flex-direction: column; - justify-content: space-between; - align-items: center; - gap: 24px; +.login-part { + display: flex; + padding: 8px 40px 40px 40px; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 24px; } .input-fields { - display: flex; - width: 100%; - flex-direction: column; - justify-content: space-between; - gap: 16px; + display: flex; + width: 100%; + flex-direction: column; + justify-content: space-between; + gap: 16px; } /* Desktop view*/ @media only screen and (min-width: 768px) { - .login-display{ - min-width: 850px; - min-height: 800px; - } - .login-login{ - display: flex; - flex-direction: row; - border-radius: 4px; - background: rgba(13, 10, 56, 0.71); - box-shadow: 0px 5px 8.9px 15px rgba(115, 116, 206, 0.25); - align-items: center; - justify-content: start; - width: 50vw; - height: 60vh; - min-height: 400px; - min-width: 500px; - } - - .login-image{ - width: 100%; - height: 100%; + .login-display { + min-width: 850px; + min-height: 800px; + } + .login-login { + display: flex; + flex-direction: row; + border-radius: 4px; + background: rgba(13, 10, 56, 0.71); + box-shadow: 0px 5px 8.9px 15px rgba(115, 116, 206, 0.25); + align-items: center; + justify-content: start; + width: 50vw; + height: 60vh; + min-height: 400px; + min-width: 500px; + backdrop-filter: blur(8px); + } - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; + .login-image { + width: 100%; + height: 100%; - border-radius: 8px; - border: 5px solid var(--Rotkehlchen-gray); - background-image: url("../../public/assets/images/IceBirdLogin.jpg"); - background-size: cover; - background-position: 75%; - } + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; - .signup-image{ - width: 100%; - height: 100%; + border-radius: 8px; + border: 5px solid var(--Rotkehlchen-gray); + background-image: url("../../public/assets/images/IceBirdLogin.jpg"); + background-size: cover; + background-position: 75%; + } - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; + .signup-image { + width: 100%; + height: 100%; - border-radius: 8px; - border: 5px solid var(--Rotkehlchen-gray); - background-image: url("../../public/assets/images/SummerOwlSignup.jpg"); - background-size: cover; - /*background-position: 75%;*/ - } + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; - .login-part{ - width: 45%; - height: 90%; - padding:3%; - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - gap: 24px; - } - - .input-fields { - display: flex; - width: 100%; - flex-direction: column; - justify-content: center; - gap: 16px; - } -} \ No newline at end of file + border-radius: 8px; + border: 5px solid var(--Rotkehlchen-gray); + background-image: url("../../public/assets/images/SummerOwlSignup.jpg"); + background-size: cover; + /*background-position: 75%;*/ + } + + .login-part { + width: 45%; + height: 90%; + padding: 3%; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 24px; + } + + .input-fields { + display: flex; + width: 100%; + flex-direction: column; + justify-content: center; + gap: 16px; + } +} +.error-messages { + color: red; +} diff --git a/code/frontend/yarn.lock b/code/frontend/yarn.lock index 90dd84c..df342f7 100644 --- a/code/frontend/yarn.lock +++ b/code/frontend/yarn.lock @@ -2751,6 +2751,15 @@ axe-core@^4.10.0: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.3.tgz#04145965ac7894faddbac30861e5d8f11bfd14fc" integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg== +axios@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.10.0.tgz#af320aee8632eaf2a400b6a1979fa75856f38d54" + integrity sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" @@ -4655,7 +4664,7 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== -follow-redirects@^1.0.0: +follow-redirects@^1.0.0, follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== @@ -4704,6 +4713,17 @@ form-data@^3.0.0: es-set-tostringtag "^2.1.0" mime-types "^2.1.35" +form-data@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.3.tgz#608b1b3f3e28be0fccf5901fc85fb3641e5cf0ae" + integrity sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -6499,7 +6519,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -7677,6 +7697,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.33: version "1.15.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6"