Skip to content

360ProjectOrganization/360Project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

417 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jobly

Our project is a Job Board and Career Portal that allows employers to post available jobs and for applicants to search and apply for jobs/positions. The platform supports multiple user roles including unregistered visitors, registered users (both employers and applicants), and administrators. Unregistered users can browse and search job postings without creating an account. Registered users must create a profile by signing up, and they must declare themselves to be either an employer or an applicant. Employers can post, edit, and manage their own job listings. Applicants can apply to jobs, upload their resume, and manage their personal profiles. Administrators oversee the platform by moderating content, managing user accounts, and viewing data analytics (e.g., user activity, number of job postings, number of users, number of employers). The goal of the system is to provide a structured, searchable job platform that demonstrates full-stack development using the MERN stack including authentication, role-based permissions, database-driven content, and responsive frontend design.

Backend System Architecture

We use a 3 layered architecture to keep our software and data secure while improving performance and eliminating the possibility of bugs.

  • Controller: Handles the HTTP routing, data validation and constucting response objects. This is where we define routes (GET or POST). It reads from the HTTP request sent by the frontend, calls the service and returns the results as a JSON response to the frontend.
  • Service: Handles the business logic. No HTTP or direct databse access. Once the controller calls a service method the service then calls the method in the repository that gets the relevant data. Once the data or a response is returned to the service it will be transferred to the controller to be returned to the frontend. This layer is also be responsible for operations of data such as hashing passwords when doing authentication.
  • Repository: Handles the database operations, models and mock data.

Flow Example (get one applicant): The controller receives GET /api/applicants/123 from the frontend, reads req.params.id and calls applicantService.getApplicantById('123'). Service receives the request by the controller and passes the id into applicantRepository.findById(id). The repository receives this request and runs the MongoDB query which returns the document to the service. The service sends the document to the controller which will transform it to JSON and sends it to frontend.

Schema

Image of the Schema

Setup

  1. Add you env

    Create a .env file inside /server/ and paste the reguired text shared with you.

Run the Application

  1. Start Docker

    Ensure Docker is installed and running on your machine before continuing.

  2. Build containers

    From the root directory /, run:

    docker-compose up --build
  3. Subsequent Runs/Regular Docker Startup

    After initial build, you can start app with

    docker-compose up
  4. Stopping the Applicantion

    docker-compose down

Seed the database (no need to do this unless you want to reset the database):

  1. Start Up Containers

    In a terminal, activate the containers from /, as we will need server running to execute seed.

    docker-compose up
  2. Seed Database

    In a seperate terminal, from / as well, execute this command

    docker-compose exec server node scripts/seed.js 

Run Tests

Frontend tests (from the client directory):

cd client
npm test

Tests use Jest with React Testing Library. The project is configured with:

  • jest.config.cjs (Jest config)
  • babel.config.cjs (Babel config for JSX transformation)
  • src/setupTests.js (Test environment setup)

API reference list

Base URL for API: /api.

Health

Method Endpoint Description Response
GET /api/health Health check { status, timestamp }

Auth

Method Endpoint Description Request Response
POST /api/auth/register Register a new user Body: role, email, password, name { user, token }
POST /api/auth/login Log in and get a user + JWT Body: email, password, role { user, token }
PUT /api/auth/changepassword Change user's password Header: Authorization: Bearer <token>. Body: currentPassword, newPassword { _id, email, name?, role, ... }
PUT /api/auth/changeemail Change user's email Header: Authorization: Bearer <token>. Body: newEmail, password { _id, email, name?, role, ... }

Applicants

Method Endpoint Description Request Response
GET /api/applicants List all applicants Array of applicants (no password, pfp, or resume)
GET /api/applicants/:id Get one applicant by ID Single applicant (no password, pfp, or resume)
GET /api/applicants/:id/pfp Get applicant's profile picture (or default) Image body; Content-Type set
GET /api/applicants/:id/resume Get applicant's resume (download or view) Optional: ?inline=1 to view in browser PDF body; Content-Disposition attachment or inline
POST /api/applicants/:id/delete Delete an applicant account { deleted: true }
PUT /api/applicants/:id/pfp Upload or replace applicant's profile picture multipart/form-data with file Updated applicant object
POST /api/applicants/:id/resume Upload or replace applicant's resume multipart/form-data with file Updated applicant object

Companies

Method Endpoint Description Request Response
GET /api/companies List all companies Array of companies (no password or pfp)
GET /api/companies/:id Get one company by id Single company (no password or pfp)
GET /api/companies/:id/job-postings Job postings for company Array of job postings
GET /api/companies/:id/analytics Company hiring analytics { totalJobs, closedJobs, avgPostingDurationDays, fillRate }
GET /api/companies/:id/pfp Get company's profile picture (or default) Image body; Content-Type set
PUT /api/companies/:id/pfp Upload or replace company's profile picture multipart/form-data with file Updated company object
POST /api/companies/:id/delete Delete a company account { deleted: true }
POST /api/companies/:id/create-job Create a job posting for this company Body: title, optional location, description, tags Created job object

Job postings

Method Endpoint Description Request Response
GET /api/job-postings List all job postings Query: optional status, companyId, limit, skip Array of job postings
GET /api/job-postings/:id Get one job posting Single job posting
POST /api/job-postings/:id/apply Apply to job (appliscant only) Header: Authorization: Bearer <token>. Applicant must have a resume on their profile. Job must be ACTIVE. { applied: true, job }
PUT /api/job-postings/:id Update a job posting Header: Authorization: Bearer <token> (company owner or admin). Body: title, tags, location, description, status (all optional) Updated job object
PATCH /api/job-postings/:id/status Change job status Header: Authorization: Bearer <token> (company owner or admin). Body: { "status": "ACTIVE" | "UNPUBLISHED" | "CLOSED" } Updated job object
DELETE /api/job-postings/:id Delete a job posting Header: Authorization: Bearer <token> (company owner or admin) { deleted: true }
GET /api/job-postings/:id/applications Get recent applications for a job Header: Authorization: Bearer <token> (company owner). Query: optional limit (default 50, max 100) Array of applicants

Admin

Method Endpoint Description Request Response
GET /api/admin/:id/pfp Get administrator's profile picture (or default) Image body; Content-Type set
PUT /api/admin/:id/pfp Upload or replace administrator's profile picture multipart/form-data with file Updated administrator object

Authentication

  • JWT: Login and register returns a token, the client sends it as Authorization: Bearer <token> on protected requests.
  • Protected routes require a valid JWT. The user’s id and role are taken from the token, not the request body for security.
  • Change password/email require the current password and a valid JWT.

Working with files (image or pdf):

Uploading a file: When a file is uploaded on the frontend it is sent to the backend as a multipart/form-data. On the backend it gets parsed by Multer to be held by in memory as a Buffer. The controller passes that Buffer down to service, then to the repository where it gets stored in the DB. Downloading a file: The repository loads the file from the DB. The service returns the file and content type. The controller sends the buffer to the browser.

Developing on the backend

When you add a new feature, say "job postings":

  1. server/repository/models/jobPosting.model.js - Mongoose schema
  2. server/repository/jobPosting.repository.js - Database operations
  3. server/service/jobPosting.service.js - Business logic
  4. server/controller/jobPosting.controller.js - Routes & handlers
  5. Register routes in server/server.js

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors