From c203e210ac7c086f8351e3e69cd4782e0a3d9d32 Mon Sep 17 00:00:00 2001 From: Mehul Date: Tue, 26 May 2026 23:52:07 +0530 Subject: [PATCH] Restore repository to v1.0.2 state --- .env.example | 3 - README.md | 197 +-------- backend/.env.example | 37 -- backend/config/validateEnv.js | 15 - backend/package.json | 4 +- backend/server.js | 72 +--- index.html | 3 - package.json | 3 +- spec/auth.routes.spec.cjs | 334 +++------------ spec/user.model.spec.cjs | 11 +- src/components/Features.tsx | 156 +------ src/components/Footer.tsx | 1 - src/components/Hero.tsx | 194 ++++----- src/components/HowItWorks.tsx | 111 ++--- src/components/Navbar.tsx | 4 +- src/components/__test__/Navbar.test.tsx | 21 +- src/hooks/useGitHubAuth.ts | 47 +-- src/hooks/useGitHubData.ts | 12 - src/index.css | 215 +--------- src/pages/About/About.tsx | 258 +++--------- src/pages/Activity.tsx | 2 +- src/pages/Contact/Contact.tsx | 504 ++++++++++++----------- src/pages/Contributors/Contributors.tsx | 526 +++--------------------- src/pages/Login/Login.tsx | 122 ++---- src/pages/Signup/Signup.tsx | 285 +++++++------ src/pages/Tracker/Tracker.tsx | 57 +-- 26 files changed, 856 insertions(+), 2338 deletions(-) delete mode 100644 .env.example delete mode 100644 backend/.env.example delete mode 100644 backend/config/validateEnv.js diff --git a/.env.example b/.env.example deleted file mode 100644 index 82180c68..00000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -# URL of the backend API (no trailing slash). -# Must match the origin the backend server listens on. -VITE_BACKEND_URL=http://localhost:5000 diff --git a/README.md b/README.md index b16c9995..a747b53a 100644 --- a/README.md +++ b/README.md @@ -41,206 +41,28 @@ Welcome to **GitHub Tracker**, a web app designed to help you monitor and analyz --- ## ๐Ÿš€ Setup Guide - -### ๐Ÿ“‹ Prerequisites - -Before setting up the project locally, ensure the following tools are installed on your system: - -- Node.js (v18 or later recommended) -- npm -- Docker -- Docker Compose -- MongoDB (required for backend services and testing) - ---- - -## ๐Ÿ“ฅ Clone the Repository - -```bash -git clone https://github.com/GitMetricsLab/github_tracker.git -cd github-tracker -``` - ---- - -# ๐Ÿ’ป Local Development Setup - -This project contains both frontend and backend services. - -## โ–ถ๏ธ Frontend Setup - -Install frontend dependencies: - -```bash -npm install -``` - -Start the frontend development server: - -```bash -npm run dev -``` - -The frontend will run on: - -```txt -http://localhost:5173 -``` - ---- - -## โš™๏ธ Backend Setup - -Move into the backend directory: - +1. Clone the repository to your local machine: ```bash -cd backend +$ git clone https://github.com/yourusername/github-tracker.git ``` -Install backend dependencies: - +2. Navigate to the project directory: ```bash -npm install +$ cd github-tracker ``` -3. Configure environment variables - - Copy the example files and fill in your values: - ```bash - # Frontend (.env in the repo root) - cp .env.example .env - - # Backend (.env inside backend/) - cp backend/.env.example backend/.env - ``` - - Key variables to set: - - | Variable | Where | Description | - |---|---|---| - | `VITE_BACKEND_URL` | root `.env` | URL of the backend (default: `http://localhost:5000`) | - | `MONGO_URI` | `backend/.env` | MongoDB connection string | - | `SESSION_SECRET` | `backend/.env` | Long random string used to sign session cookies | - | `FRONTEND_ORIGIN` | `backend/.env` | URL of the frontend โ€” restricts CORS. **Required in production.** Defaults to `http://localhost:5173` in development. | - -4. Run the frontend +3. Run the frontend ```bash -npm run dev -``` - -The backend server will run on: - -```txt -http://localhost:5000 +$ npm i +$ npm run dev ``` -5. Run the backend +4. Run the backend ```bash -$ cd backend $ npm i $ npm start ``` -This command: - -- Builds frontend and backend containers -- Starts development services -- Enables live file changes using Docker volumes -- Runs frontend and backend simultaneously - -### Development Services - -| Service | Port | -|----------|------| -| Frontend | 5173 | -| Backend | 5000 | - ---- - -## ๐Ÿš€ Production Environment - -Run the production Docker setup: - -```bash -npm run docker:prod -``` - -This command: - -- Creates optimized production builds -- Runs frontend using Nginx -- Starts backend production services - -### Production Services - -| Service | Port | -|----------|------| -| Frontend | 3000 | -| Backend | 5000 | - ---- - -# ๐Ÿ“‚ Docker Configuration Overview - -| File | Purpose | -|------|----------| -| `Dockerfile.dev` | Development container setup | -| `Dockerfile.prod` | Production container setup | -| `docker-compose.yml` | Multi-service container orchestration | - ---- - -# ๐Ÿ”„ Local Development Workflow - -Recommended contributor workflow: - -1. Fork the repository -2. Clone your fork locally -3. Create a new branch -4. Install dependencies -5. Run frontend/backend locally or using Docker -6. Make changes -7. Test your implementation -8. Commit and push changes -9. Open a Pull Request - ---- - -# ๐ŸŒฑ Environment Configuration - -The project uses environment variables for configuration. - -Frontend environment variables: - -```env -VITE_BACKEND_URL=http://localhost:5000 -``` - -Backend environment variables: - -```env -PORT=5000 -MONGO_URI=your_mongodb_connection -SESSION_SECRET=your_secret -``` - -Create corresponding `.env` files before running the application. - ---- - -# ๐Ÿ› ๏ธ Useful Commands - -| Command | Description | -|----------|-------------| -| `npm run dev` | Start frontend locally | -| `npm run build` | Create production build | -| `npm run docker:dev` | Run Docker development environment | -| `npm run docker:prod` | Run Docker production environment | -| `npm run test` | Run frontend tests | -| `npm run test:backend` | Run backend tests | - ---- - ## ๐Ÿงช Backend Unit & Integration Testing with Jasmine This project uses the Jasmine framework for backend unit and integration tests. The tests cover: @@ -318,6 +140,3 @@ spec_files: [ โฌ†๏ธ Back to Top

- - - diff --git a/backend/.env.example b/backend/.env.example deleted file mode 100644 index a46d9911..00000000 --- a/backend/.env.example +++ /dev/null @@ -1,37 +0,0 @@ -# --------------------------------------------------------------- -# Server -# --------------------------------------------------------------- -PORT=5000 -NODE_ENV=development - -# --------------------------------------------------------------- -# MongoDB -# --------------------------------------------------------------- -MONGO_URI=mongodb://127.0.0.1:27017/github_tracker - -# --------------------------------------------------------------- -# Session -# Generate a long random string for production, e.g.: -# node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" -# --------------------------------------------------------------- -SESSION_SECRET=replace-with-a-long-random-string - -# --------------------------------------------------------------- -# CORS โ€” Frontend origin allowlist -# -# Set this to the exact URL of your frontend (no trailing slash). -# REQUIRED in production: the server will refuse to start without it. -# In development, the server defaults to http://localhost:5173 when -# this variable is not set. -# -# Examples: -# Development : FRONTEND_ORIGIN=http://localhost:5173 -# Production : FRONTEND_ORIGIN=https://app.example.com -# --------------------------------------------------------------- -FRONTEND_ORIGIN=http://localhost:5173 - -# --------------------------------------------------------------- -# Logging -# Accepted values: error | warn | info | debug -# --------------------------------------------------------------- -LOG_LEVEL=debug diff --git a/backend/config/validateEnv.js b/backend/config/validateEnv.js deleted file mode 100644 index fde3d9a7..00000000 --- a/backend/config/validateEnv.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Validates required environment variables before the server starts. - * Throws so callers can decide whether to log + exit or handle otherwise, - * which keeps the logic unit-testable without spawning child processes. - */ -function validateEnv() { - if (process.env.NODE_ENV === 'production' && !process.env.FRONTEND_ORIGIN) { - throw new Error( - 'FRONTEND_ORIGIN environment variable is required in production. ' + - 'Set it to the URL of your frontend (e.g., https://app.example.com).' - ); - } -} - -module.exports = { validateEnv }; diff --git a/backend/package.json b/backend/package.json index 747482ec..74ab9dd7 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,8 +25,6 @@ "zod": "^4.4.3" }, "devDependencies": { - "jasmine": "^5.0.0", - "nodemon": "^3.1.9", - "supertest": "^7.0.0" + "nodemon": "^3.1.9" } } diff --git a/backend/server.js b/backend/server.js index c7fa106c..48d6ccfb 100644 --- a/backend/server.js +++ b/backend/server.js @@ -6,62 +6,32 @@ const bodyParser = require('body-parser'); require('dotenv').config(); const cors = require('cors'); -const { validateEnv } = require('./config/validateEnv'); -const logger = require('./logger'); - -// Fail fast in production when required env vars are absent. -try { - validateEnv(); -} catch (err) { - logger.error(`[FATAL] ${err.message}`); - process.exit(1); -} - // Passport configuration require('./config/passportConfig'); -const app = express(); - -// In development, fall back to localhost:5173 if FRONTEND_ORIGIN is not set so -// that contributors can run the stack without a full .env file. -const corsOrigin = process.env.FRONTEND_ORIGIN || 'http://localhost:5173'; +const logger = require('./logger'); -if (!process.env.FRONTEND_ORIGIN) { - logger.warn( - 'FRONTEND_ORIGIN is not set; defaulting to http://localhost:5173. ' + - 'Set this variable in production.' - ); -} +const app = express(); -// CORS โ€” explicit allowlist with credentials support. -// A function-based origin is required so that the header is only set (and -// reflected) for allowed origins; a static string would send the header on -// every response regardless of the requesting origin. +// CORS configuration +const allowedOrigins = ['http://localhost:5173', 'https://github-spy.etlify.app']; app.use(cors({ - origin: (requestOrigin, callback) => { - // Allow same-origin requests (no Origin header) and the configured origin. - if (!requestOrigin || requestOrigin === corsOrigin) { - return callback(null, true); - } - callback(null, false); - }, - credentials: true, - methods: ['GET', 'POST'], - allowedHeaders: ['Content-Type'], + origin: function (origin, callback) { + if (!origin || allowedOrigins.indexOf(origin) !== -1) { + callback(null, true); + } else{ + callback(new Error('Blocked by CORS policy')); + } + }, + credentials: true })); // Middleware app.use(bodyParser.json()); app.use(session({ - secret: process.env.SESSION_SECRET, - resave: false, - saveUninitialized: false, - cookie: { - httpOnly: true, - // Only transmit the cookie over HTTPS in production. - secure: process.env.NODE_ENV === 'production', - sameSite: 'strict', - }, + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false, })); app.use(passport.initialize()); app.use(passport.session()); @@ -72,10 +42,12 @@ app.use('/api/auth', authRoutes); // Connect to MongoDB mongoose.connect(process.env.MONGO_URI, {}).then(() => { - logger.info('Connected to MongoDB'); - app.listen(process.env.PORT, () => { - logger.info(`Server running on port ${process.env.PORT}`); - }); + logger.info('Connected to MongoDB'); + + const PORT = process.env.PORT || 5000; + app.listen(PORT, () => { + logger.info(`Server running on port ${PORT}`); + }); }).catch((err) => { - logger.error('MongoDB connection error', err); + logger.error('MongoDB connection error', err); }); diff --git a/index.html b/index.html index 57ad94cb..b6d940d0 100644 --- a/index.html +++ b/index.html @@ -4,9 +4,6 @@ - - - Github Tracker diff --git a/package.json b/package.json index e29375a1..43ad31cc 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "github-tracker", + "name": "GitHub Tracker", "private": true, "version": "0.0.0", "type": "module", @@ -54,7 +54,6 @@ "autoprefixer": "^10.4.20", "bcryptjs": "^3.0.3", - "cors": "^2.8.5", "eslint": "^9.13.0", "eslint-plugin-react": "^7.37.2", diff --git a/spec/auth.routes.spec.cjs b/spec/auth.routes.spec.cjs index a30d723c..2b29c03a 100644 --- a/spec/auth.routes.spec.cjs +++ b/spec/auth.routes.spec.cjs @@ -1,45 +1,16 @@ +const mongoose = require('mongoose'); const express = require('express'); const request = require('supertest'); -const cors = require('cors'); +const session = require('express-session'); +const passport = require('passport'); -// All backend modules are resolved from the backend directory so the test app -// and the application code share a single instance of each module โ€” avoids -// connection-state mismatches that arise when backend/node_modules is present -// alongside the root node_modules. -const backendPath = [`${__dirname}/../backend`]; - -const mongoose = require(require.resolve('mongoose', { paths: backendPath })); -const session = require(require.resolve('express-session', { paths: backendPath })); -const passport = require(require.resolve('passport', { paths: backendPath })); - -const User = require('../backend/models/User'); +const User = require('../backend/models/User'); const authRoutes = require('../backend/routes/auth'); -const { validateEnv } = require('../backend/config/validateEnv'); - -// A fixed allowed origin used throughout the test suite. -const ALLOWED_ORIGIN = 'http://localhost:5173'; - -// Password satisfying the signup Zod schema (uppercase + lowercase + digit + special). -const VALID_PASSWORD = 'TestPass1!'; +// Create test app function createTestApp() { const app = express(); - // Mirror the production CORS config: function-based origin so the header is - // only reflected for the configured origin, absent for all others. - const allowedOrigin = process.env.FRONTEND_ORIGIN || ALLOWED_ORIGIN; - app.use(cors({ - origin: (requestOrigin, callback) => { - if (!requestOrigin || requestOrigin === allowedOrigin) { - return callback(null, true); - } - callback(null, false); - }, - credentials: true, - methods: ['GET', 'POST'], - allowedHeaders: ['Content-Type'], - })); - app.use(express.json()); app.use( @@ -47,15 +18,13 @@ function createTestApp() { secret: 'test-secret', resave: false, saveUninitialized: false, - // Mirror production cookie options; secure:false is correct for HTTP tests. - cookie: { httpOnly: true, secure: false, sameSite: 'strict' }, }) ); app.use(passport.initialize()); app.use(passport.session()); - // Load passport config AFTER initializing passport. + // Load passport config AFTER initializing passport require('../backend/config/passportConfig'); app.use('/auth', authRoutes); @@ -63,16 +32,10 @@ function createTestApp() { return app; } -// --------------------------------------------------------------------------- -// Integration tests that require a running MongoDB instance. -// All MongoDB-dependent suites live inside one outer describe so they share -// a single connection, unaffected by Jasmine's random suite ordering. -// --------------------------------------------------------------------------- -describe('Backend auth integration', () => { +describe('Auth Routes', () => { let app; beforeAll(async () => { - process.env.FRONTEND_ORIGIN = ALLOWED_ORIGIN; await mongoose.connect('mongodb://127.0.0.1:27017/github_tracker_test'); app = createTestApp(); }); @@ -89,259 +52,96 @@ describe('Backend auth integration', () => { }); // ---------------- SIGNUP ---------------- - describe('Auth Routes', () => { - it('should sign up a new user', async () => { - const res = await request(app) - .post('/auth/signup') - .send({ - username: 'testuser', - email: 'test@example.com', - password: VALID_PASSWORD, - }); - - expect(res.status).toBe(201); - expect(res.body.message).toBe('User created successfully'); - - const user = await User.findOne({ email: 'test@example.com' }); - expect(user).toBeTruthy(); - }); - - it('should not sign up a user with an existing email', async () => { - await User.create({ + it('should sign up a new user', async () => { + const res = await request(app) + .post('/auth/signup') + .send({ username: 'testuser', email: 'test@example.com', - password: VALID_PASSWORD, + password: 'password123', }); - const res = await request(app) - .post('/auth/signup') - .send({ - username: 'testuser2', - email: 'test@example.com', - password: VALID_PASSWORD, - }); + expect(res.status).toBe(201); + expect(res.body.message).toBe('User created successfully'); - expect(res.status).toBe(400); - expect(res.body.message).toBe('User already exists'); - }); - - // ---------------- LOGIN ---------------- - it('should login a user with correct credentials', async () => { - await User.create({ - username: 'testuser', - email: 'test@example.com', - password: VALID_PASSWORD, - }); - - const agent = request.agent(app); - - const res = await agent.post('/auth/login').send({ - email: 'test@example.com', - password: VALID_PASSWORD, - }); - - expect(res.status).toBe(200); - expect(res.body.message).toBe('Login successful'); - expect(res.body.user.email).toBe('test@example.com'); - }); - - it('should reject login with the wrong password', async () => { - await User.create({ - username: 'testuser', - email: 'test@example.com', - password: VALID_PASSWORD, - }); - - const agent = request.agent(app); - - const res = await agent.post('/auth/login').send({ - email: 'test@example.com', - password: 'wrongpassword', - }); + const user = await User.findOne({ email: 'test@example.com' }); + expect(user).toBeTruthy(); + }); - expect(res.status).toBe(401); + it('should not sign up a user with existing email', async () => { + await User.create({ + username: 'testuser', + email: 'test@example.com', + password: 'password123', }); - // ---------------- LOGOUT ---------------- - it('should logout a logged-in user', async () => { - const agent = request.agent(app); - - await agent.post('/auth/signup').send({ - username: 'testuser', - email: 'test@example.com', - password: VALID_PASSWORD, - }); - - await agent.post('/auth/login').send({ + const res = await request(app) + .post('/auth/signup') + .send({ + username: 'testuser2', email: 'test@example.com', - password: VALID_PASSWORD, + password: 'password456', }); - const res = await agent.get('/auth/logout'); - - expect(res.status).toBe(200); - expect(res.body.message).toBe('Logged out successfully'); - }); + expect(res.status).toBe(400); + expect(res.body.message).toBe('User already exists'); }); - // ---------------- CORS BEHAVIOUR ---------------- - describe('CORS behaviour', () => { - it('should include Access-Control-Allow-Origin for the configured origin', async () => { - const res = await request(app) - .get('/auth/logout') - .set('Origin', ALLOWED_ORIGIN); - - expect(res.headers['access-control-allow-origin']).toBe(ALLOWED_ORIGIN); + // ---------------- LOGIN ---------------- + it('should login a user with correct credentials', async () => { + await User.create({ + username: 'testuser', + email: 'test@example.com', + password: 'password123', }); - it('should include Access-Control-Allow-Credentials: true for the allowed origin', async () => { - const res = await request(app) - .get('/auth/logout') - .set('Origin', ALLOWED_ORIGIN); + const agent = request.agent(app); - expect(res.headers['access-control-allow-credentials']).toBe('true'); + const res = await agent.post('/auth/login').send({ + email: 'test@example.com', + password: 'password123', }); - it('should not set Access-Control-Allow-Origin for an unlisted origin', async () => { - const res = await request(app) - .get('/auth/logout') - .set('Origin', 'http://evil.example.com'); + expect(res.status).toBe(200); + expect(res.body.message).toBe('Login successful'); + expect(res.body.user.email).toBe('test@example.com'); + }); - expect(res.headers['access-control-allow-origin']).toBeUndefined(); + it('should not login a user with wrong password', async () => { + await User.create({ + username: 'testuser', + email: 'test@example.com', + password: 'password123', }); - it('should respond to a preflight OPTIONS request from the allowed origin', async () => { - const res = await request(app) - .options('/auth/login') - .set('Origin', ALLOWED_ORIGIN) - .set('Access-Control-Request-Method', 'POST') - .set('Access-Control-Request-Headers', 'Content-Type'); + const agent = request.agent(app); - expect(res.headers['access-control-allow-origin']).toBe(ALLOWED_ORIGIN); - expect(res.headers['access-control-allow-credentials']).toBe('true'); - // 204 is the standard success status for preflight responses. - expect([200, 204]).toContain(res.status); + const res = await agent.post('/auth/login').send({ + email: 'test@example.com', + password: 'wrongpassword', }); - it('should include CORS headers on a credentialed login request from the allowed origin', async () => { - await User.create({ - username: 'corsuser', - email: 'cors@example.com', - password: VALID_PASSWORD, - }); - - const res = await request(app) - .post('/auth/login') - .set('Origin', ALLOWED_ORIGIN) - .send({ email: 'cors@example.com', password: VALID_PASSWORD }); - - expect(res.status).toBe(200); - expect(res.headers['access-control-allow-origin']).toBe(ALLOWED_ORIGIN); - expect(res.headers['access-control-allow-credentials']).toBe('true'); - }); + expect(res.status).toBe(401); }); - // ---------------- SESSION COOKIE FLAGS ---------------- - describe('Session cookie security flags', () => { - it('should set HttpOnly on the session cookie after login', async () => { - await User.create({ - username: 'cookieuser', - email: 'cookie@example.com', - password: VALID_PASSWORD, - }); - - const res = await request(app) - .post('/auth/login') - .set('Origin', ALLOWED_ORIGIN) - .send({ email: 'cookie@example.com', password: VALID_PASSWORD }); + // ---------------- LOGOUT ---------------- + it('should logout a logged-in user', async () => { + const agent = request.agent(app); - expect(res.status).toBe(200); - - const setCookie = res.headers['set-cookie']; - expect(setCookie).toBeTruthy(); - - const cookieStr = Array.isArray(setCookie) ? setCookie[0] : setCookie; - expect(cookieStr).toMatch(/HttpOnly/i); + await agent.post('/auth/signup').send({ + username: 'testuser', + email: 'test@example.com', + password: 'password123', }); - it('should set SameSite=Strict on the session cookie after login', async () => { - await User.create({ - username: 'samesiteuser', - email: 'samesite@example.com', - password: VALID_PASSWORD, - }); - - const res = await request(app) - .post('/auth/login') - .set('Origin', ALLOWED_ORIGIN) - .send({ email: 'samesite@example.com', password: VALID_PASSWORD }); - - expect(res.status).toBe(200); - - const setCookie = res.headers['set-cookie']; - expect(setCookie).toBeTruthy(); - - const cookieStr = Array.isArray(setCookie) ? setCookie[0] : setCookie; - expect(cookieStr).toMatch(/SameSite=Strict/i); + await agent.post('/auth/login').send({ + email: 'test@example.com', + password: 'password123', }); - }); -}); - -// --------------------------------------------------------------------------- -// Environment validation โ€” pure unit tests, no MongoDB required. -// --------------------------------------------------------------------------- -describe('Environment validation (validateEnv)', () => { - let savedNodeEnv; - let savedFrontendOrigin; - let hadFrontendOrigin; - - beforeEach(() => { - savedNodeEnv = process.env.NODE_ENV; - hadFrontendOrigin = Object.prototype.hasOwnProperty.call(process.env, 'FRONTEND_ORIGIN'); - savedFrontendOrigin = process.env.FRONTEND_ORIGIN; - }); - - afterEach(() => { - process.env.NODE_ENV = savedNodeEnv; - if (hadFrontendOrigin) { - process.env.FRONTEND_ORIGIN = savedFrontendOrigin; - } else { - delete process.env.FRONTEND_ORIGIN; - } - }); - - it('should throw when FRONTEND_ORIGIN is absent in production', () => { - process.env.NODE_ENV = 'production'; - delete process.env.FRONTEND_ORIGIN; - - expect(() => validateEnv()).toThrow(); - }); - - it('should include FRONTEND_ORIGIN in the error message when throwing in production', () => { - process.env.NODE_ENV = 'production'; - delete process.env.FRONTEND_ORIGIN; - - expect(() => validateEnv()).toThrowError(/FRONTEND_ORIGIN/); - }); - - it('should not throw when FRONTEND_ORIGIN is set in production', () => { - process.env.NODE_ENV = 'production'; - process.env.FRONTEND_ORIGIN = 'https://app.example.com'; - - expect(() => validateEnv()).not.toThrow(); - }); - - it('should not throw in development without FRONTEND_ORIGIN', () => { - process.env.NODE_ENV = 'development'; - delete process.env.FRONTEND_ORIGIN; - - expect(() => validateEnv()).not.toThrow(); - }); - it('should not throw in test environment without FRONTEND_ORIGIN', () => { - process.env.NODE_ENV = 'test'; - delete process.env.FRONTEND_ORIGIN; + const res = await agent.get('/auth/logout'); - expect(() => validateEnv()).not.toThrow(); + expect(res.status).toBe(200); + expect(res.body.message).toBe('Logged out successfully'); }); -}); +}); \ No newline at end of file diff --git a/spec/user.model.spec.cjs b/spec/user.model.spec.cjs index a6295e3a..e256e638 100644 --- a/spec/user.model.spec.cjs +++ b/spec/user.model.spec.cjs @@ -1,12 +1,5 @@ +const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); - -// Resolve mongoose from the backend directory so this test and the User model -// share one mongoose instance, avoiding connection-state mismatches when -// backend/node_modules is present alongside the root node_modules. -const mongoose = require( - require.resolve('mongoose', { paths: [`${__dirname}/../backend`] }) -); - const User = require('../backend/models/User'); describe('User Model', () => { @@ -72,4 +65,4 @@ describe('User Model', () => { expect(isMatch).toBe(true); expect(isNotMatch).toBe(false); }); -}); +}); \ No newline at end of file diff --git a/src/components/Features.tsx b/src/components/Features.tsx index d8a52028..8125cb42 100644 --- a/src/components/Features.tsx +++ b/src/components/Features.tsx @@ -1,91 +1,4 @@ import { BarChart3, Users, Search, Zap, Shield, Globe } from 'lucide-react'; -import type { CSSProperties, ReactNode } from 'react'; - -type TerminalHeadingProps = { - title: string; - as?: 'h2' | 'h3' | 'h4'; - className?: string; - promptClassName?: string; - titleClassName?: string; - animated?: boolean; -}; - -type TerminalCardProps = { - label: string; - children: ReactNode; - className?: string; - contentClassName?: string; - accent?: 'blue' | 'green'; -}; - -const headingSizes: Record, string> = { - h2: 'text-3xl md:text-4xl', - h3: 'text-xl md:text-2xl', - h4: 'text-lg md:text-xl', -}; - -const TerminalHeading = ({ - title, - as: HeadingTag = 'h2', - className = '', - promptClassName = '', - titleClassName = '', - animated = false, -}: TerminalHeadingProps) => { - const chars = title.length + 2; - - return ( - - - > - - - {title} - - - ); -}; - -const TerminalCard = ({ - label, - children, - className = '', - contentClassName = '', - accent = 'blue', -}: TerminalCardProps) => { - const accentClasses = accent === 'green' ? 'text-[#3fb950] border-[#3fb950]/30' : 'text-[#58a6ff] border-[#58a6ff]/30'; - - return ( -
-
- - -
- {label} - _ -
-
- -
-
- {children} -
-
- ); -}; const Features = () => { const features = [ @@ -146,67 +59,28 @@ const Features = () => { ]; return ( -
-
-
-
-
-
-

- > initialize tracking capabilities -

-

- Powerful Features -

-

- Everything you need to track, analyze, and understand GitHub activity patterns. +

+
+
+

Powerful Features

+

+ Everything you need to track, analyze, and understand GitHub activity patterns

-
+
{features.map((feature, index) => { const IconComponent = feature.icon; - const promptLabel = `feature-${String(index + 1).padStart(2, '0')}`; - const titleTone = index % 2 === 0 ? 'blue' : 'green'; return ( - -
-
-
- -
-
- -
- - cmd - - gh tracker --feature -
-
-
- -

- {feature.description} -

- -
- ready - {feature.title.toLowerCase().replace(/\s+/g, '-')} -
+
+
+
- +

{feature.title}

+

+ {feature.description} +

+
); })}
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 259c3218..3ad55184 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -109,7 +109,6 @@ function Footer() { placeholder="Enter email address" value={email} onChange={(e) => setEmail(e.target.value)} - autoComplete="email" className=" flex-grow text-sm px-4 py-3 bg-zinc-50 dark:bg-zinc-800/40 diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 9a6117a3..a606d777 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -1,179 +1,125 @@ import { Search, GitBranch, GitCommit, GitPullRequest, Layout } from 'lucide-react'; import { Link } from 'react-router-dom'; +// Fixed array declaration for data structure rendering stability const MATRIX_CELLS = Array.from({ length: 21 }, (_, i) => i); const Hero = () => { return ( -
+
- {/* Background Grid */} -
- - {/* Glow Effects */} -
-
+ {/* 1. Cyber Grid Overlay */} +
+ + {/* Ambient Radial Glow Elements */} +
+
-
- -
+
+
- {/* LEFT SIDE */} -
- -

+ {/* LEFT COLUMN: Typography & CTA */} +
+

Track GitHub Activity - + Like Never Before

- -

- Monitor and analyze GitHub user activity with powerful insights. - Perfect for developers, project managers, and teams who want to - understand contribution patterns and repository engagement. + +

+ Monitor and analyze GitHub user activity with powerful insights. Perfect for developers, + project managers, and teams who want to understand contribution patterns and repository engagement.

-
- - - - - Start Tracking - +
+ {/* Interactive Primary Link Button */} + + + + Start Tracking - - - Explore Features - -
- -
-
-
Public API
-
Fast setup
-
-
-
Multi-user
-
Compare activity
-
-
-
Live insights
-
Track trends
-
- {/* RIGHT SIDE */} -
+ {/* RIGHT COLUMN: Dashboard & Floating Git Elements */} +
- {/* Dashboard Card */} -
- - {/* Header */} -
-
+ {/* The Main Dashboard Mockup Card */} +
+
+
- - Dashboard Insights - + Dashboard Insights
- -
- - - +
+ + +
- {/* Stats */} -
- -
-
- Weekly Commits -
- -
- 176 - - +12% - -
- + {/* Mockup Data widgets */} +
+
+
Weekly Commits
+
176 +12%
-
+
- {/* Contribution Grid */} -
-
- Contribution Matrix -
- + {/* Simulated GitHub Contribution Grid */} +
+
Contribution Matrix
{MATRIX_CELLS.map((cellIndex) => { - const intensities = [ - 'bg-slate-200 dark:bg-slate-800', - 'bg-emerald-200 dark:bg-emerald-900/60', - 'bg-emerald-300 dark:bg-emerald-700', - 'bg-emerald-500', - 'bg-emerald-600', - ]; - + // Fully qualified class names to ensure they aren't removed by Tailwind's compilation process + const intensitiesLight = ['bg-slate-200', 'bg-emerald-200', 'bg-emerald-300', 'bg-emerald-500', 'bg-emerald-600']; + const intensitiesDark = ['dark:bg-slate-800', 'dark:bg-emerald-900/60', 'dark:bg-emerald-700', 'dark:bg-emerald-500', 'dark:bg-emerald-400']; + + const lightClass = intensitiesLight[cellIndex % intensitiesLight.length]; + const darkClass = intensitiesDark[cellIndex % intensitiesDark.length]; + return ( -
); })}
- -
-
-
8.4k
-
Views
-
-
-
92%
-
Trend
-
-
-
38
-
PRs
-
-
- {/* Floating Icons */} -
- + {/* Floating Git Icon Nodes using native inline styles for correct staggered delays */} +
+
-
- +
-
- +
+ + {/* Subtle Neon Center Glow */} +
+

diff --git a/src/components/HowItWorks.tsx b/src/components/HowItWorks.tsx index ffe06ecd..86224f9d 100644 --- a/src/components/HowItWorks.tsx +++ b/src/components/HowItWorks.tsx @@ -1,92 +1,107 @@ -import { BarChart3, Activity, Search } from 'lucide-react'; +import { useContext } from 'react'; +import { ArrowDown, ArrowRight, BarChart3, Activity, Search } from 'lucide-react'; +import { ThemeContext } from '../context/ThemeContext'; +import type { ThemeContextType } from '../context/ThemeContext'; const HowItWorks = () => { + const themeContext = useContext(ThemeContext) as ThemeContextType | null; + const mode = themeContext?.mode ?? 'light'; + const steps = [ { number: 1, title: 'Search Users', - label: 'Discover', description: 'Enter GitHub usernames or search for users by name. Add them to your tracking dashboard.', icon: Search, }, { number: 2, title: 'Monitor Activity', - label: 'Observe', description: 'Watch insights of commits, pull requests, issues, and other GitHub activities.', icon: Activity, }, { number: 3, title: 'Analyze Insights', - label: 'Review', description: 'Review detailed analytics, export reports, and gain valuable insights into development patterns.', icon: BarChart3, } ]; - return ( -
-