Skip to content

Commit 090b1f7

Browse files
committed
fix: resolve merge conflict in App.tsx
2 parents 7ab9596 + 8d17610 commit 090b1f7

24 files changed

Lines changed: 914 additions & 322 deletions

Dockerfile.prod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ WORKDIR /app
77
# Copy package.json
88
COPY package.json .
99

10-
# Install production dependencies using Yarn
11-
RUN npm install --production
10+
# Install dependencies (dev deps needed for the build step)
11+
RUN npm install
1212
# Copy the rest of the application files
1313
COPY . .
1414

15-
# Build the frontend using Yarn
15+
# Build the frontend
1616
RUN npm run build
1717

1818
# Stage 2: Serve the application with Nginx

backend/models/User.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,12 @@ const UserSchema = new mongoose.Schema({
1818
},
1919
});
2020

21-
UserSchema.pre('save', async function (next) {
22-
21+
UserSchema.pre('save', async function () {
2322
if (!this.isModified('password'))
24-
return next();
23+
return;
2524

26-
try {
27-
const salt = await bcrypt.genSalt(10);
28-
this.password = await bcrypt.hash(this.password, salt);
29-
next();
30-
} catch (err) {
31-
return next(err);
32-
}
25+
const salt = await bcrypt.genSalt(10);
26+
this.password = await bcrypt.hash(this.password, salt);
3327
});
3428

3529
// Compare passwords during login

backend/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"scripts": {
66
"dev": "nodemon server.js",
77
"start": "node server.js",
8-
"test": "echo \"Error: no test specified\" && exit 1"
8+
"test": "jasmine spec/**/*.spec.cjs"
9+
910
},
1011
"keywords": [],
1112
"author": "",
@@ -20,7 +21,8 @@
2021
"express-session": "^1.18.1",
2122
"mongoose": "^8.8.2",
2223
"passport": "^0.7.0",
23-
"passport-local": "^1.0.0"
24+
"passport-local": "^1.0.0",
25+
"zod": "^4.4.3"
2426
},
2527
"devDependencies": {
2628
"nodemon": "^3.1.9"

backend/routes/auth.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
11
const express = require("express");
22
const passport = require("passport");
33
const User = require("../models/User");
4+
const { signupSchema, loginSchema } = require("../validators/authValidator");
5+
const { validateRequest } = require("../validators/validationRequest");
46
const router = express.Router();
57

68
// Signup route
7-
router.post("/signup", async (req, res) => {
9+
router.post("/signup", validateRequest(signupSchema), async (req, res) => {
810

911
const { username, email, password } = req.body;
1012

1113
try {
12-
const existingUser = await User.findOne( {email} );
14+
const existingUser = await User.findOne({
15+
$or: [{ email }, { username }],
16+
});
1317

1418
if (existingUser)
15-
return res.status(400).json( {message: 'User already exists'} );
19+
return res.status(400).json({ message: 'User already exists' });
1620

17-
const newUser = new User( {username, email, password} );
21+
const newUser = new User({ username, email, password });
1822
await newUser.save();
19-
res.status(201).json( {message: 'User created successfully'} );
23+
res.status(201).json({ message: 'User created successfully' });
2024
} catch (err) {
25+
if (err && err.code === 11000) {
26+
return res.status(400).json({ message: 'User already exists' });
27+
}
28+
2129
res.status(500).json({ message: 'Error creating user', error: err.message });
2230
}
2331
});
2432

2533
// Login route
26-
router.post("/login", passport.authenticate('local'), (req, res) => {
34+
router.post("/login", validateRequest(loginSchema), passport.authenticate('local'), (req, res) => {
2735
res.status(200).json( { message: 'Login successful', user: req.user } );
2836
});
2937

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const { z } = require("zod");
2+
3+
const signupSchema = z.object({
4+
username: z.string()
5+
.trim()
6+
.min(3, "Username must be at least 3 characters long")
7+
.max(30, "Username must be at most 30 characters long")
8+
.regex(/^[a-zA-Z0-9_]+$/, "Username can only contain letters, numbers, and underscores")
9+
,
10+
11+
email: z.string()
12+
.trim()
13+
.toLowerCase()
14+
.email("Invalid email address"),
15+
16+
17+
password: z.string()
18+
.min(8, "Password must be at least 8 characters long")
19+
.max(100, "Password must be at most 100 characters long")
20+
.regex(
21+
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/,
22+
'Password must contain uppercase, lowercase, number, and special character'
23+
),
24+
});
25+
26+
27+
const loginSchema = z.object({
28+
email: z.string()
29+
.trim()
30+
.toLowerCase()
31+
.email("Invalid email address"),
32+
password: z.string()
33+
.min(8, "Password must be at least 8 characters long")
34+
.max(100, "Password must be at most 100 characters long")
35+
});
36+
37+
38+
module.exports = { signupSchema, loginSchema };
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const validateRequest = (schema) => (req, res, next) => {
2+
const result = schema.safeParse(req.body);
3+
4+
if(!result.success) {
5+
return res.status(400).json({
6+
success: false,
7+
message: 'Validation failed',
8+
errors: result.error.issues.map((err) => ({
9+
field: err.path.join('.'),
10+
message: err.message,
11+
})),
12+
});
13+
}
14+
15+
req.validated = result.data;
16+
req.body = result.data;
17+
next();
18+
}
19+
20+
module.exports = { validateRequest };

package.json

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"dev": "vite --host",
88
"build": "vite build",
99
"lint": "eslint .",
10+
"test": "vitest",
11+
"test:backend": "jasmine spec/**/*.spec.cjs",
1012
"preview": "vite preview",
1113
"docker:dev": "docker compose --profile dev up --build",
1214
"docker:prod": "docker compose --profile prod up -d --build"
@@ -16,7 +18,7 @@
1618
"@emotion/styled": "^11.11.0",
1719
"@mui/icons-material": "^5.15.6",
1820
"@mui/material": "^5.15.6",
19-
"@primer/octicons-react": "^19.15.5",
21+
"@primer/octicons-react": "^19.25.0",
2022
"@vitejs/plugin-react": "^4.3.3",
2123
"axios": "^1.7.7",
2224
"framer-motion": "^12.23.12",
@@ -32,6 +34,9 @@
3234
},
3335
"devDependencies": {
3436
"@eslint/js": "^9.13.0",
37+
"@testing-library/jest-dom": "^6.9.1",
38+
"@testing-library/react": "^16.3.2",
39+
"@testing-library/user-event": "^14.6.1",
3540
"@types/jasmine": "^5.1.8",
3641
"@types/node": "^22.10.1",
3742
"@types/react": "^18.3.23",
@@ -47,10 +52,13 @@
4752
"eslint-plugin-react-refresh": "^0.4.14",
4853
"express-session": "^1.18.2",
4954
"globals": "^15.11.0",
50-
"jasmine": "^5.9.0",
55+
"jasmine": "^5.13.0",
56+
"jasmine-spec-reporter": "^7.0.0",
57+
"jsdom": "^29.1.1",
5158
"passport": "^0.7.0",
5259
"passport-local": "^1.0.0",
53-
"supertest": "^7.1.4",
54-
"vite": "^5.4.10"
60+
"supertest": "^7.2.2",
61+
"vite": "^5.4.10",
62+
"vitest": "^4.1.6"
5563
}
5664
}

spec/auth.routes.spec.cjs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ describe('Auth Routes', () => {
2222
let app;
2323

2424
beforeAll(async () => {
25-
await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test', {
26-
useNewUrlParser: true,
27-
useUnifiedTopology: true,
28-
});
25+
await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test');
2926
app = createTestApp();
3027
});
3128

@@ -57,6 +54,15 @@ describe('Auth Routes', () => {
5754
expect(res.body.message).toBe('User already exists');
5855
});
5956

57+
it('should not sign up a user with existing username', async () => {
58+
await new User({ username: 'testuser', email: 'test@example.com', password: 'password123' }).save();
59+
const res = await request(app)
60+
.post('/auth/signup')
61+
.send({ username: 'testuser', email: 'test2@example.com', password: 'password456' });
62+
expect(res.status).toBe(400);
63+
expect(res.body.message).toBe('User already exists');
64+
});
65+
6066
it('should login a user with correct credentials', async () => {
6167
await request(app)
6268
.post('/auth/signup')

spec/user.model.spec.cjs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ const User = require('../backend/models/User');
44

55
describe('User Model', () => {
66
beforeAll(async () => {
7-
await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test', {
8-
useNewUrlParser: true,
9-
useUnifiedTopology: true,
10-
});
7+
await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test');
118
});
129

1310
afterAll(async () => {

src/App.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useLocation } from "react-router-dom";
12
import Navbar from "./components/Navbar";
23
import Footer from "./components/Footer";
34
import ScrollProgressBar from "./components/ScrollProgressBar";
@@ -6,19 +7,23 @@ import Router from "./Routes/Router";
67
import ThemeWrapper from "./context/ThemeContext";
78
import ChatbotWidget from "./components/Chatbot/ChatbotWidget";
89

10+
const FULLSCREEN_ROUTES = ["/signup", "/login"];
11+
912
function App() {
13+
const location = useLocation();
14+
const isFullscreen = FULLSCREEN_ROUTES.includes(location.pathname);
15+
1016
return (
11-
<ThemeWrapper>
1217
<div className="relative flex flex-col min-h-screen">
13-
<ScrollProgressBar />
18+
{!isFullscreen && <ScrollProgressBar />}
1419

15-
<Navbar />
20+
{!isFullscreen && <Navbar />}
1621

17-
<main className="flex-grow bg-gray-50 dark:bg-gray-800 flex justify-center items-center">
22+
<main className={`flex justify-center items-center ${isFullscreen ? "flex-1" : "flex-grow bg-gray-50 dark:bg-gray-800"}`}>
1823
<Router />
1924
</main>
2025

21-
<Footer />
26+
{!isFullscreen && <Footer />}
2227

2328
<ChatbotWidget />
2429

@@ -40,8 +45,7 @@ function App() {
4045
}}
4146
/>
4247
</div>
43-
</ThemeWrapper>
4448
);
4549
}
4650

47-
export default App;
51+
export default App;

0 commit comments

Comments
 (0)