feat: enhance API route naming conventions and structure
- Added comprehensive guidelines for API route naming and structure in `ROUTE_NAMING_BEST_PRACTICES.md`. - Updated `ARCHITECHTURE.md` to reference the new best practices document. - Refactored course-related routes to follow new naming conventions, including changes to endpoint paths and validation schemas. - Removed the deprecated course clone route and integrated cloning functionality directly into the course routes. - Adjusted various API methods to ensure consistent usage of HTTP methods and parameter structures across the application.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import * as z from 'zod';
|
||||
|
||||
const envSchema = z.object({
|
||||
CLOUDFLARE_ACCESS_KEY: z.string().optional(),
|
||||
|
||||
@@ -28,7 +28,7 @@ export const accountRouter = new Hono()
|
||||
return handleError(c, error, 'Failed to fetch account data');
|
||||
}
|
||||
})
|
||||
.put('/user', authMiddleware, zValidator('json', ZUpdateProfile), async (c) => {
|
||||
.put('/profile', authMiddleware, zValidator('json', ZUpdateProfile), async (c) => {
|
||||
const user = c.get('user');
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { CloneCourseErrorResponse, CloneCourseResponse } from '@api/types/database';
|
||||
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import { ZCourseClone } from '@cio/utils/validation/course';
|
||||
import { authMiddleware } from '@api/middlewares/auth';
|
||||
import { cloneCourse } from '@api/services/course/clone';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
|
||||
export const cloneRouter = new Hono().post('/', authMiddleware, zValidator('json', ZCourseClone), async (c) => {
|
||||
try {
|
||||
const validatedData = c.req.valid('json');
|
||||
const { id, title, description, slug, organizationId } = validatedData;
|
||||
|
||||
const user = c.get('user');
|
||||
|
||||
// Clone the course
|
||||
const newCourse = await cloneCourse(id, title, user.id, description, slug, organizationId);
|
||||
|
||||
const successResponse: CloneCourseResponse = {
|
||||
success: true,
|
||||
course: newCourse
|
||||
};
|
||||
return c.json(successResponse, 200);
|
||||
} catch (error) {
|
||||
console.error('Error cloning course:', error);
|
||||
const errorResponse: CloneCourseErrorResponse = {
|
||||
error: 'Failed to clone course',
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
};
|
||||
return c.json(errorResponse, 500);
|
||||
}
|
||||
});
|
||||
@@ -1,49 +1,94 @@
|
||||
import { ZCertificateDownload, ZCourseDownloadContent } from '@cio/utils/validation/course';
|
||||
import {
|
||||
ZCertificateDownload,
|
||||
ZCourseClone,
|
||||
ZCourseCloneParam,
|
||||
ZCourseDownloadContent,
|
||||
ZCourseDownloadParam
|
||||
} from '@cio/utils/validation/course';
|
||||
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import { cloneRouter } from '@api/routes/course/clone';
|
||||
import { authMiddleware } from '@api/middlewares/auth';
|
||||
import { cloneCourse } from '@api/services/course/clone';
|
||||
import { generateCertificate } from '@api/utils/certificate';
|
||||
import { generateCoursePdf } from '@api/utils/course';
|
||||
import { handleError } from '@api/utils/errors';
|
||||
import { katexRouter } from '@api/routes/course/katex';
|
||||
import { lessonRouter } from '@api/routes/course/lesson';
|
||||
import { presignRouter } from '@api/routes/course/presign';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
|
||||
export const courseRouter = new Hono()
|
||||
.post('/download/certificate', zValidator('json', ZCertificateDownload), async (c) => {
|
||||
const validatedData = c.req.valid('json');
|
||||
.post(
|
||||
'/:courseId/download/certificate',
|
||||
zValidator('param', ZCourseDownloadParam),
|
||||
zValidator('json', ZCertificateDownload),
|
||||
async (c) => {
|
||||
const validatedData = c.req.valid('json');
|
||||
|
||||
const result = await generateCertificate(validatedData);
|
||||
const result = await generateCertificate(validatedData);
|
||||
|
||||
c.header('Content-Type', 'application/pdf');
|
||||
c.header('Content-Disposition', `attachment; filename="certificate-${validatedData.courseName}.pdf"`);
|
||||
c.header('Content-Type', 'application/pdf');
|
||||
c.header('Content-Disposition', `attachment; filename="certificate-${validatedData.courseName}.pdf"`);
|
||||
|
||||
return c.body(
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(result);
|
||||
controller.close();
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
.post('/download/content', zValidator('json', ZCourseDownloadContent), async (c) => {
|
||||
const validatedData = c.req.valid('json');
|
||||
return c.body(
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(result);
|
||||
controller.close();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
)
|
||||
.post(
|
||||
'/:courseId/download/content',
|
||||
zValidator('param', ZCourseDownloadParam),
|
||||
zValidator('json', ZCourseDownloadContent),
|
||||
async (c) => {
|
||||
const validatedData = c.req.valid('json');
|
||||
|
||||
const pdfBuffer = await generateCoursePdf(validatedData);
|
||||
const pdfBuffer = await generateCoursePdf(validatedData);
|
||||
|
||||
c.header('Content-Type', 'application/pdf');
|
||||
c.header('Content-Type', 'application/pdf');
|
||||
|
||||
return c.body(
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(pdfBuffer);
|
||||
controller.close();
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
return c.body(
|
||||
new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(pdfBuffer);
|
||||
controller.close();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
)
|
||||
.post(
|
||||
'/:courseId/clone',
|
||||
authMiddleware,
|
||||
zValidator('param', ZCourseCloneParam),
|
||||
zValidator('json', ZCourseClone),
|
||||
async (c) => {
|
||||
try {
|
||||
const { courseId } = c.req.valid('param');
|
||||
const validatedData = c.req.valid('json');
|
||||
const { title, description, slug, organizationId } = validatedData;
|
||||
|
||||
const user = c.get('user');
|
||||
|
||||
// Clone the course
|
||||
const newCourse = await cloneCourse(courseId, title, user.id, description, slug, organizationId);
|
||||
|
||||
return c.json(
|
||||
{
|
||||
success: true,
|
||||
course: newCourse
|
||||
},
|
||||
200
|
||||
);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to clone course');
|
||||
}
|
||||
}
|
||||
)
|
||||
.route('/katex', katexRouter)
|
||||
.route('/lesson', lessonRouter)
|
||||
.route('/presign', presignRouter)
|
||||
.route('/clone', cloneRouter);
|
||||
.route('/presign', presignRouter);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import * as z from 'zod';
|
||||
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import katex from 'katex';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const katexRouter = new Hono().get('/', async (c) => {
|
||||
try {
|
||||
|
||||
@@ -5,17 +5,17 @@ import { getOrganisationAnalytics } from '@api/services/dash';
|
||||
import { handleError } from '@api/utils/errors';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
|
||||
export const dashAnalyticsRouter = new Hono().post(
|
||||
export const dashAnalyticsRouter = new Hono().get(
|
||||
'/stats',
|
||||
authMiddleware,
|
||||
zValidator('json', ZDashStats),
|
||||
zValidator('query', ZDashStats),
|
||||
async (c) => {
|
||||
try {
|
||||
const { orgId, siteName } = c.req.valid('json');
|
||||
const { orgId, siteName } = c.req.valid('query');
|
||||
|
||||
const result = await getOrganisationAnalytics(orgId, siteName);
|
||||
|
||||
return c.json({ success: true, data: result }, 201);
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to load organisation analytics');
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { deliverEmail } from '@cio/email';
|
||||
import { handleError } from '@api/utils/errors';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
|
||||
export const mailRouter = new Hono().post('/send', zValidator('json', ZDeliverEmail), async (c) => {
|
||||
export const mailRouter = new Hono().post('/', zValidator('json', ZDeliverEmail), async (c) => {
|
||||
const validatedData = c.req.valid('json');
|
||||
|
||||
try {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { authMiddleware } from '@api/middlewares/auth';
|
||||
import { handleError } from '@api/utils/errors';
|
||||
import { uploadImage } from '@api/services/media';
|
||||
|
||||
export const mediaRouter = new Hono().post('/image/upload', authMiddleware, async (c) => {
|
||||
export const mediaRouter = new Hono().post('/image', authMiddleware, async (c) => {
|
||||
try {
|
||||
const body = await c.req.parseBody();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ZOnboardingStep1, ZOnboardingStep2 } from '@cio/utils/validation/onboarding';
|
||||
import { ZOnboardingCreateOrg, ZOnboardingUpdateMetadata } from '@cio/utils/validation/onboarding';
|
||||
import { completeOnboarding, createOrganizationWithOwner, updateUserOnboarding } from '@api/services/onboarding';
|
||||
|
||||
import { Hono } from '@api/utils/hono';
|
||||
@@ -7,7 +7,7 @@ import { handleError } from '@api/utils/errors';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
|
||||
export const onboardingRouter = new Hono()
|
||||
.post('/step1', authMiddleware, zValidator('json', ZOnboardingStep1), async (c) => {
|
||||
.post('/create-org', authMiddleware, zValidator('json', ZOnboardingCreateOrg), async (c) => {
|
||||
try {
|
||||
const user = c.get('user');
|
||||
const { fullname, orgName, siteName } = c.req.valid('json');
|
||||
@@ -19,7 +19,7 @@ export const onboardingRouter = new Hono()
|
||||
return handleError(c, error, 'Failed to create organization');
|
||||
}
|
||||
})
|
||||
.post('/step2', authMiddleware, zValidator('json', ZOnboardingStep2), async (c) => {
|
||||
.post('/update-metadata', authMiddleware, zValidator('json', ZOnboardingUpdateMetadata), async (c) => {
|
||||
try {
|
||||
const user = c.get('user');
|
||||
const { goal, source, fullname } = c.req.valid('json');
|
||||
|
||||
Reference in New Issue
Block a user