diff --git a/apps/api/config/db.ts b/apps/api/config/db.ts index 83e1a11..d4b7240 100644 --- a/apps/api/config/db.ts +++ b/apps/api/config/db.ts @@ -4,7 +4,7 @@ import { log } from '@repo/logger' export const connect_db = async () => { try { const conn = await mongoose.connect(process.env.MONGO_URI as string) - log(`MongoDB Connected here: ${conn.connection.name}`) + log(`MongoDB Connected: ${conn.connection.name}`) } catch (error) { log(`Error: ${error}`) process.exit(1) diff --git a/apps/api/package.json b/apps/api/package.json index b671e85..b5672d4 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -20,7 +20,8 @@ "cors": "^2.8.5", "express": "^4.18.2", "mongoose": "^8.1.1", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "zod": "^3.22.4" }, "devDependencies": { "@repo/eslint-config": "*", diff --git a/apps/api/src/controllers/job_controller.ts b/apps/api/src/controllers/job_controller.ts index a67a928..6b93224 100644 --- a/apps/api/src/controllers/job_controller.ts +++ b/apps/api/src/controllers/job_controller.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express' -import { JOB_SCHEMA } from '@models/job_model' +import { JOB_SCHEMA } from '@/models/job_model' import { isValidObjectId } from 'mongoose' +import { log } from 'console' //* @desc Get all jobs //* route GET /api/jobs @@ -11,7 +12,7 @@ export async function get_jobs(_: Request, res: Response): Promise { const jobs = await JOB_SCHEMA.find({}).sort({ createdAt: -1 }) res.status(200).json({ jobs }) } catch (error) { - console.error('Error fetching jobs:', error) + log('Error fetching jobs:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -35,7 +36,7 @@ export async function get_job(req: Request, res: Response): Promise { } res.status(200).json(job) } catch (error) { - console.error('Error fetching job:', error) + log('Error fetching job:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -45,12 +46,13 @@ export async function get_job(req: Request, res: Response): Promise { //! @access Private export async function post_job(req: Request, res: Response): Promise { try { + // validate job data + const job_data = req.body //* post job - const jobData = req.body - const newJob = await JOB_SCHEMA.create(jobData) - res.status(201).json(newJob) + const new_job = await JOB_SCHEMA.create(job_data) + res.status(201).json(new_job) } catch (error) { - console.error('Error posting job:', error) + log('Error posting job:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -73,10 +75,10 @@ export async function delete_job(req: Request, res: Response): Promise { return } //* delete job - const deleted_job = await JOB_SCHEMA.findByIdAndDelete(id) + await JOB_SCHEMA.findByIdAndDelete(id) res.status(200).json({ message: 'Job deleted successfully' }) } catch (error) { - console.error('Error deleting job:', error) + log('Error deleting job:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -104,7 +106,7 @@ export async function update_job(req: Request, res: Response): Promise { }) res.status(200).json(updated_job) } catch (error) { - console.error('Error updating job:', error) + log('Error updating job:', error) res.status(500).json({ error: 'Internal server error' }) } } diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 95ae3af..a7cf113 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,6 +1,6 @@ import { log } from '@repo/logger' import { createServer } from './server' -import job_route from '@routes/job_route' +import job_route from '@/routes/job_route' const port = process.env.PORT || 5001 const server = createServer() diff --git a/apps/api/src/middleware/job_validation_middleware.ts b/apps/api/src/middleware/job_validation_middleware.ts new file mode 100644 index 0000000..c97bec7 --- /dev/null +++ b/apps/api/src/middleware/job_validation_middleware.ts @@ -0,0 +1,18 @@ +import { Request, Response, NextFunction } from 'express' +import { z } from 'zod' + +export function validate_job_data(schema: z.AnyZodObject) { + return (req: Request, res: Response, next: NextFunction) => { + try { + schema.parse(req.body) + next() + } catch (error) { + if (error instanceof z.ZodError) { + const error_messages = error.errors.map((issue) => ({ + message: `${issue.path.join('.')} - ${issue.message}`, + })) + res.status(400).json({ error: 'Invalid data', error_messages }) + } + } + } +} diff --git a/apps/api/src/routes/job_route.ts b/apps/api/src/routes/job_route.ts index 7295271..942f972 100644 --- a/apps/api/src/routes/job_route.ts +++ b/apps/api/src/routes/job_route.ts @@ -5,7 +5,9 @@ import { get_jobs, post_job, update_job, -} from '@controllers/job_controller' +} from '@/controllers/job_controller' +import { validate_job_data } from '@/middleware/job_validation_middleware' +import { JOB_VALIDATION_SCHEMA } from '@/validations/job_validation' const router = express.Router() @@ -19,7 +21,7 @@ router.get('/:id', get_job) //* @desc Post job //! @access Private -router.post('/', post_job) +router.post('/', validate_job_data(JOB_VALIDATION_SCHEMA), post_job) //* @desc Delete job //! @access Private @@ -27,6 +29,6 @@ router.delete('/:id', delete_job) //* @desc Update job //! @access Private -router.patch('/:id', update_job) +router.patch('/:id', validate_job_data(JOB_VALIDATION_SCHEMA), update_job) export default router diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 62bd43b..d805a66 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -3,7 +3,7 @@ import express, { type Express } from 'express' import morgan from 'morgan' import cors from 'cors' import { config } from 'dotenv' -import { connect_db } from '@config/db' +import { connect_db } from 'config/db' config() connect_db() diff --git a/apps/api/src/validations/job_validation.ts b/apps/api/src/validations/job_validation.ts new file mode 100644 index 0000000..a5b12cb --- /dev/null +++ b/apps/api/src/validations/job_validation.ts @@ -0,0 +1,13 @@ +import { z } from 'zod' + +export const JOB_VALIDATION_SCHEMA = z.object({ + company: z.string(), + logo: z.string().optional(), + position: z.string(), + role: z.string(), + level: z.string(), + contract: z.string(), + location: z.string(), + languages: z.array(z.string()), + tools: z.array(z.string()), +}) diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 84e4f60..ef3422b 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -7,11 +7,6 @@ "baseUrl": ".", "paths": { "@/*": ["src/*"], - "@lib/*": ["src/lib/*"], - "@controllers/*": ["src/controllers/*"], - "@models/*": ["src/models/*"], - "@config/*": ["config/*"], - "@routes/*": ["src/routes/*"], }, }, "exclude": ["node_modules"], diff --git a/package-lock.json b/package-lock.json index dce13f8..efbf6a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,8 @@ "cors": "^2.8.5", "express": "^4.18.2", "mongoose": "^8.1.1", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "zod": "^3.22.4" }, "devDependencies": { "@repo/eslint-config": "*", @@ -14249,6 +14250,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",