A recruitment platform for UIT students and IT employers, with instant student verification through Google OAuth, real-time application tracking, and an interview preparation hub.
- Features
- Screenshots
- Tech stack
- Getting started
- Development
- Architecture
- Business rules
- Deployment and security notes
- License
Candidates
- Google OAuth sign-in with instant UIT student verification, or manual registration.
- Job search (MongoDB Atlas Search) with filters for skill, position, location and working form.
- Personalized job recommendations based on profile skills.
- Saved jobs, followed companies and company performance badges.
- Application tracking (pending → viewed → approved/rejected) with CV re-upload and withdrawal.
- Real-time notifications (Socket.IO + email).
- Interview experience sharing and an interview preparation hub with study resources.
- Company reviews (overall + salary, work-life balance, career, culture, management ratings) with optional anonymity, and aggregated salary insights.
Employers
- Rich-text job posting (TinyMCE) with images, expiration dates and applicant caps.
- Applicant inbox with CV viewing, status updates and real-time alerts.
- Recruitment analytics (views, applications, approval rate).
Admins
- Role-based access control across management routes.
- Employer approval, manual candidate verification (for students who can't verify via Google), report moderation and audit logs.
Candidate experience
Employer workspace
- Backend: Express 5 + TypeScript, Socket.IO, Mongoose, Passport (Google OAuth), Nodemailer, Joi
- Frontend: Next.js 16 (App Router), React 19, Tailwind CSS, React Hook Form + Zod, TinyMCE
- Data and services: MongoDB Atlas (Atlas Search), Cloudinary for uploads, Gmail for email
- Deployment: Docker Compose behind an nginx reverse proxy
Exact dependencies and versions live in BE/package.json and FE/package.json.
- Docker, or Node.js and Yarn to run without containers (the images use Node 22 for BE and Node 20 for FE; see the two
Dockerfiles) - A MongoDB database (Atlas or local)
- A Cloudinary account and a Gmail account with an app password
git clone https://github.com/thaihadefi/Innovation-Project.git
cd Innovation-Project
cp BE/.env.example BE/.env
cp FE/.env.example FE/.envEvery variable is documented inline in BE/.env.example and FE/.env.example. The API refuses to start without DATABASE and JWT_SECRET (BE/config/env.ts); uploads and email also need the Cloudinary and Gmail values. Google OAuth is optional and is disabled while its credentials are empty (see Google OAuth).
Search uses the Atlas Search index named by ATLAS_SEARCH_INDEX when it exists, and falls back to a regex match otherwise, so a local MongoDB works without Atlas.
With Docker (the site is served by nginx at http://localhost):
docker compose up --build -ddocker-compose.yaml overrides the URL and callback variables for the proxied setup, so BE/.env can keep its local-dev values.
Without Docker, in two terminals (set NEXT_PUBLIC_API_URL=http://localhost:4001 in FE/.env first):
cd BE && yarn install && yarn dev # API on http://localhost:4001
cd FE && yarn install && yarn dev # site on http://localhost:3069There is no seed script: employer accounts need admin approval and new admins need activation, so the first super admin is inserted directly into the database. From the BE folder, hash a password:
node -e "console.log(require('bcryptjs').hashSync(process.argv[1], 10))" 'YourStrongPassword'Then insert a document into the accounts_admin collection (with mongosh or the Atlas Data Explorer). Field definitions are in account-admin.model.ts.
db.accounts_admin.insertOne({
fullName: "Super Admin",
email: "admin@uitjobs.local",
password: "<your-hashed-password>",
status: "active",
isSuperAdmin: true,
deleted: false,
createdAt: new Date(),
updatedAt: new Date()
})Log in at /admin/login to open the admin panel (/admin-manage), approve employers and verify candidates.
Google sign-in is what lets UIT students verify instantly; without it, admins verify candidates manually.
- In the Google Cloud Console, create an OAuth 2.0 Client ID (Web application) under APIs & Services > Credentials.
- Add the authorized redirect URI for your setup:
- Docker Compose:
http://localhost/api/auth/google/callback - Without Docker:
http://localhost:4001/auth/google/callback
- Docker Compose:
- Put the credentials in
BE/.envasGOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRETandGOOGLE_CALLBACK_URL.
The project has no automated test suite; type-checking and linting are the quality gates. Run them before committing:
cd BE && yarn typecheck && yarn lint:any # lint:any fails on any explicit `any`
cd FE && yarn lint && yarn buildAll scripts are defined in BE/package.json and FE/package.json.
Browser ──> nginx ──┬──> FE (Next.js, port 3069)
└──> BE (Express API + Socket.IO, port 4001) ──> MongoDB Atlas
└──> Cloudinary, Gmail, Google OAuth
nginx is the public entry point: it routes / to FE, strips the /api/ prefix before forwarding to BE, and upgrades /socket.io/ to WebSocket (nginx.conf).
The backend is layered routes → controllers → services → models: controllers only read the request and send the response, and all business logic and database access live in services. Where to start reading:
| Area | Entry point |
|---|---|
| API bootstrap, shutdown | BE/index.ts, BE/app.ts |
| Route map | BE/routes/index.route.ts |
| Auth, RBAC, rate limiting | BE/middlewares/ |
| Request validation (Joi) | BE/validates/ |
| Pages and dashboards | FE/src/app/(pages)/ |
| Shared frontend types | FE/src/types/ |
These rules are enforced in code; the linked file is the source of truth.
- A verified UIT student's Student ID, Cohort, Full Name and Major lock once each is populated; unverified or manually registered candidates can still edit them (ProfileForm.tsx).
- New employer accounts start pending (
initial) and must be approved by an admin before they can post jobs (company.service.ts). - A candidate can edit or re-upload a CV only while it is still pending and the job hasn't expired; withdrawing an application has no such restriction (cv.service.ts).
- A candidate can review a company at most once (review.service.ts).
- Company performance badges (Top Rated, Active Recruiter, Trusted Employer, Hot Jobs) are computed from activity, not assigned manually (company-badges.helper.ts).
- Cookies: login cookies are
httpOnly,sameSite: lax, and markedSecureautomatically when the request arrived over HTTPS (cookie.helper.ts). - Reverse proxy: the API trusts exactly one proxy hop so rate limits see the real client IP behind nginx; adding another proxy layer in front requires changing this (BE/app.ts).
- Redeploys: on
SIGTERM/SIGINTthe API stops accepting requests, lets in-flight ones finish, then closes Socket.IO, the cache and MongoDB before exiting (BE/index.ts). - Uploads are capped at 20 MB by nginx (nginx.conf).