Add lessons domain Phase 1: instructor and lesson type entities
Foundation tables for the lessons module with full CRUD, pagination, search, and sorting. Includes migration, Drizzle schema, Zod validation, services, routes, and 23 integration tests.
This commit is contained in:
95
packages/backend/src/routes/v1/lessons.ts
Normal file
95
packages/backend/src/routes/v1/lessons.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { FastifyPluginAsync } from 'fastify'
|
||||
import {
|
||||
PaginationSchema,
|
||||
InstructorCreateSchema,
|
||||
InstructorUpdateSchema,
|
||||
LessonTypeCreateSchema,
|
||||
LessonTypeUpdateSchema,
|
||||
} from '@lunarfront/shared/schemas'
|
||||
import { InstructorService, LessonTypeService } from '../../services/lesson.service.js'
|
||||
|
||||
export const lessonRoutes: FastifyPluginAsync = async (app) => {
|
||||
// --- Instructors ---
|
||||
|
||||
app.post('/instructors', { preHandler: [app.authenticate, app.requirePermission('lessons.admin')] }, async (request, reply) => {
|
||||
const parsed = InstructorCreateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const instructor = await InstructorService.create(app.db, parsed.data)
|
||||
return reply.status(201).send(instructor)
|
||||
})
|
||||
|
||||
app.get('/instructors', { preHandler: [app.authenticate, app.requirePermission('lessons.view')] }, async (request, reply) => {
|
||||
const params = PaginationSchema.parse(request.query)
|
||||
const result = await InstructorService.list(app.db, params)
|
||||
return reply.send(result)
|
||||
})
|
||||
|
||||
app.get('/instructors/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.view')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const instructor = await InstructorService.getById(app.db, id)
|
||||
if (!instructor) return reply.status(404).send({ error: { message: 'Instructor not found', statusCode: 404 } })
|
||||
return reply.send(instructor)
|
||||
})
|
||||
|
||||
app.patch('/instructors/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.admin')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const parsed = InstructorUpdateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const instructor = await InstructorService.update(app.db, id, parsed.data)
|
||||
if (!instructor) return reply.status(404).send({ error: { message: 'Instructor not found', statusCode: 404 } })
|
||||
return reply.send(instructor)
|
||||
})
|
||||
|
||||
app.delete('/instructors/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.admin')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const instructor = await InstructorService.delete(app.db, id)
|
||||
if (!instructor) return reply.status(404).send({ error: { message: 'Instructor not found', statusCode: 404 } })
|
||||
return reply.send(instructor)
|
||||
})
|
||||
|
||||
// --- Lesson Types ---
|
||||
|
||||
app.post('/lesson-types', { preHandler: [app.authenticate, app.requirePermission('lessons.admin')] }, async (request, reply) => {
|
||||
const parsed = LessonTypeCreateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const lessonType = await LessonTypeService.create(app.db, parsed.data)
|
||||
return reply.status(201).send(lessonType)
|
||||
})
|
||||
|
||||
app.get('/lesson-types', { preHandler: [app.authenticate, app.requirePermission('lessons.view')] }, async (request, reply) => {
|
||||
const params = PaginationSchema.parse(request.query)
|
||||
const result = await LessonTypeService.list(app.db, params)
|
||||
return reply.send(result)
|
||||
})
|
||||
|
||||
app.get('/lesson-types/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.view')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const lessonType = await LessonTypeService.getById(app.db, id)
|
||||
if (!lessonType) return reply.status(404).send({ error: { message: 'Lesson type not found', statusCode: 404 } })
|
||||
return reply.send(lessonType)
|
||||
})
|
||||
|
||||
app.patch('/lesson-types/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.admin')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const parsed = LessonTypeUpdateSchema.safeParse(request.body)
|
||||
if (!parsed.success) {
|
||||
return reply.status(400).send({ error: { message: 'Validation failed', details: parsed.error.flatten(), statusCode: 400 } })
|
||||
}
|
||||
const lessonType = await LessonTypeService.update(app.db, id, parsed.data)
|
||||
if (!lessonType) return reply.status(404).send({ error: { message: 'Lesson type not found', statusCode: 404 } })
|
||||
return reply.send(lessonType)
|
||||
})
|
||||
|
||||
app.delete('/lesson-types/:id', { preHandler: [app.authenticate, app.requirePermission('lessons.admin')] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const lessonType = await LessonTypeService.delete(app.db, id)
|
||||
if (!lessonType) return reply.status(404).send({ error: { message: 'Lesson type not found', statusCode: 404 } })
|
||||
return reply.send(lessonType)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user