A full-stack job board platform featuring role-based access control, file uploads, email notifications, and a dual-dashboard interface for Employers and Candidates.

- Authentication: JWT-based auth with Role-Based Access Control (Employer/Candidate).
- Job Management: Create, update, delete, and search jobs (Full-text search & filters).
- Application Tracking:
- Candidates: Apply via PDF resume (stored on Cloudinary) and track status on a Kanban board.
- Employers: View applicants, download secure resumes, and update status (Screening → Offer).
- Notifications: Automated emails via Nodemailer when applications are received or status changes.
- Security: Password hashing, CORS, and protected API routes.
- Backend: Node.js, Express.js, TypeScript
- Database: MongoDB (Mongoose)
- Frontend: React, Vite, TailwindCSS, Shadcn/UI
- Storage: Cloudinary (Resumes)
- Deploy: Vercel (Frontend) and Render (Backend)
- Node.js (v18+)
- MongoDB Connection String
- Cloudinary Account
Backend:
cd server
npm install
# Create .env file (see below)
npm run devFrontend:
# from the root
npm install
npm run devCreate a .env file in the server folder:
PORT=5000
MONGO_URI=mongodb-atlas-url
JWT_SECRET=your_secret_key
CLOUDINARY_CLOUD_NAME=cloud_name
CLOUDINARY_API_KEY=key
CLOUDINARY_API_SECRET=secret
SMTP_EMAIL=audrey29@ethereal.email
SMTP_PASSWORD=passwordNote: When running frontend locally use
baseURL: 'http://localhost:5000/api',in src/lib/axios.tsx file.
A complete Postman Collection is included in the root directory: aniket_postman_collection.json.
Import Instructions:
- Open Postman → Import → Upload
postman_collection.json. - The collection uses a variable
{{url}}which defaults tohttp://localhost:5000/api. - After logging in, copy the token from the response and paste it into the Collection Variables "current value" for
token.
Note: You can either use these to test or you can Register and Login your own Candidate and Employer.
| Role | Email | Password |
|------------|-----------------------|-------------|
| Employer | emp-1@test.com | 123 |
| Candidate | john@test.com | 987 |
├──/ # React Frontend (root)
│ ├── src/components/ # Reusable UI components
│ ├── src/context/ # Auth State Management
│ ├── src/lib/ # axios, utils
│ └── src/pages/ # Dashboard, Jobs, Login pages
├── server/ # Node.js Backend
│ ├── src/models/ # Mongoose Schemas (User, Job, Application)
│ ├── src/controllers/# Business Logic
│ ├── src/routes/ # API Endpoints
│ └── src/middleware/ # Auth & Upload Middleware
│ └── src/utils/ # email service
├── aniket_postman_collection.json # postman collection
└── README.md- POST
/api/auth/register- Register a new user (Employer or Candidate) - POST
/api/auth/login- Login and receive a JWT token - GET
/api/auth/me- Get current logged-in user details
- GET
/api/jobs- Get all jobs (supports search, filters, pagination) - GET
/api/jobs/:id- Get details of a specific job - POST
/api/jobs- Create a new job posting (Employer only) - PUT
/api/jobs/:id- Update a job posting (Employer only) - DELETE
/api/jobs/:id- Delete a job posting (Employer only) - GET
/api/jobs/employer/jobs- Get all jobs posted by the current employer - GET
/api/jobs/:id/applications- Get all applications for a specific job (Employer only)
- POST
/api/applications- Apply to a job with a resume (Candidate only) - GET
/api/applications/my- Get all applications for the logged-in candidate - GET
/api/applications/employer- Get all applications received by the logged-in employer - PUT
/api/applications/:id/status- Update application status (e.g., Interview, Offer) (Employer only) - PUT
/api/applications/:id/withdraw- Withdraw an application (Candidate only)
While this application meets all core functional requirements, the following limitations exist due to time constraints and the nature of a hackathon/demo environment:
- The application currently uses Ethereal Email (fake SMTP service) for testing purposes.
- Emails are not delivered to real inboxes. You must check the server console logs for the "Preview URL" to view sent emails.
- Future Fix: Integrate SendGrid or AWS SES for production email delivery.
- The backend is hosted on Render's free tier, which spins down after 15 minutes of inactivity.
- The first request might take 30–50 seconds to load while the server wakes up. Please be patient on the initial login.
- The salary filter relies on parsing numeric values from the input string (e.g., "$100k+").
- If an employer updates a status, the candidate must refresh the page to see the change on their Kanban board.
- Withdrawing an application permanently deletes the record from the database rather than marking it as withdrawn.