feat: move community supabase request to api layer (#612)
* feat: move community supabase request to api layer * chore: pr review checkpoint * chore: PR fixes * feat: implement role-based authorization middleware and align the community features with the developer guide. - Added role-based authorization middleware for organization and course membership checks, including `orgMemberMiddleware`, `orgTeamMemberMiddleware`, and `courseMemberMiddleware`. - Introduced route-specific middleware for comment and question authorization, ensuring only authors or team members can modify content. - Updated community routes to utilize the new middleware for enhanced security and access control. - Refactored community services to streamline upvote functionality and improve error handling. - Enhanced UI components for community interactions, including new comment and question pages, and improved state management for user actions. - Updated documentation to reflect new middleware usage and best practices for authorization in the API layer. * feat: enhance course and organization routes with middleware for improved access control - Added `courseMemberMiddleware` to course download and clone routes to enforce membership checks. - Updated lesson route to include `courseMemberMiddleware` for PDF downloads. - Integrated `orgMemberMiddleware` into organization routes for team and audience access, ensuring only authorized members can retrieve data. - Refactored lesson and organization routes for consistency and clarity in middleware usage. * chore: streamline dashboard and API development scripts in package.json - Consolidated dashboard and API development commands to include all relevant filters in a single line for improved clarity and ease of use. - Removed redundant full command variants to simplify the script section. --------- Co-authored-by: Rotimi Best <[email protected]>
This commit is contained in:
co-authored by
Rotimi Best
parent
9fb6c7cbaa
commit
bc9e3b41ae
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
ZCommunityComment,
|
||||
ZCommunityCommentDelete,
|
||||
ZCommunityQuestion,
|
||||
ZCommunityQuestionUpdate,
|
||||
ZCommunityQuestions,
|
||||
ZGetCommunity,
|
||||
ZGetCommunityCommentQuestionId,
|
||||
ZNewCommunityQuestion,
|
||||
ZUpvotePost,
|
||||
ZUpvotePostParam
|
||||
} from '@cio/utils/validation/community';
|
||||
import {
|
||||
createComment,
|
||||
createQuestion,
|
||||
deleteComment,
|
||||
deleteCommentsByQuestionId,
|
||||
deleteQuestion,
|
||||
editQuestion,
|
||||
fetchCommunityQuestion,
|
||||
fetchCommunityQuestions,
|
||||
upvote
|
||||
} from '@api/services/community';
|
||||
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import { authMiddleware } from '@api/middlewares/auth';
|
||||
import { commentAuthorOrTeamMiddleware } from './middlewares/comment-author-or-team';
|
||||
import { handleError } from '@api/utils/errors';
|
||||
import { orgMemberMiddleware } from '@api/middlewares/org-member';
|
||||
import { orgTeamMemberMiddleware } from '@api/middlewares/org-team-member';
|
||||
import { questionAuthorOrTeamMiddleware } from './middlewares/question-author-or-team';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
|
||||
export const communityRouter = new Hono()
|
||||
.get('/', authMiddleware, orgMemberMiddleware, zValidator('query', ZCommunityQuestions), async (c) => {
|
||||
try {
|
||||
const { orgId } = c.req.valid('query');
|
||||
|
||||
const result = await fetchCommunityQuestions(orgId);
|
||||
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to load community posts');
|
||||
}
|
||||
})
|
||||
.get('/:slug', authMiddleware, orgMemberMiddleware, zValidator('param', ZCommunityQuestion), async (c) => {
|
||||
try {
|
||||
const { slug } = c.req.valid('param');
|
||||
|
||||
const result = await fetchCommunityQuestion({ slug });
|
||||
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to load community post');
|
||||
}
|
||||
})
|
||||
.post('/', authMiddleware, orgMemberMiddleware, zValidator('json', ZNewCommunityQuestion), async (c) => {
|
||||
try {
|
||||
const { title, body, courseId, organizationId, authorProfileId, votes, slug } = c.req.valid('json');
|
||||
|
||||
const result = await createQuestion({
|
||||
title,
|
||||
body,
|
||||
courseId,
|
||||
organizationId,
|
||||
authorProfileId,
|
||||
votes,
|
||||
slug
|
||||
});
|
||||
|
||||
return c.json({ success: true, data: result }, 201);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to create community post');
|
||||
}
|
||||
})
|
||||
.put(
|
||||
'/:id',
|
||||
authMiddleware,
|
||||
questionAuthorOrTeamMiddleware,
|
||||
zValidator('param', ZGetCommunity),
|
||||
zValidator('json', ZCommunityQuestionUpdate),
|
||||
async (c) => {
|
||||
try {
|
||||
const { id } = c.req.valid('param');
|
||||
const { title, body, courseId } = c.req.valid('json');
|
||||
|
||||
const result = await editQuestion({ id, title, body, courseId });
|
||||
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to update community post');
|
||||
}
|
||||
}
|
||||
)
|
||||
.delete('/:id', authMiddleware, questionAuthorOrTeamMiddleware, zValidator('param', ZGetCommunity), async (c) => {
|
||||
try {
|
||||
const { id } = c.req.valid('param');
|
||||
|
||||
const result = await deleteQuestion({ id });
|
||||
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to delete community post');
|
||||
}
|
||||
})
|
||||
|
||||
.post(
|
||||
'/:id/comment',
|
||||
authMiddleware,
|
||||
orgMemberMiddleware,
|
||||
zValidator('param', ZGetCommunity),
|
||||
zValidator('json', ZCommunityComment),
|
||||
async (c) => {
|
||||
try {
|
||||
const { id } = c.req.valid('param');
|
||||
console.log('question id', id);
|
||||
const { body, authorProfileId, votes } = c.req.valid('json');
|
||||
|
||||
const result = await createComment({
|
||||
body,
|
||||
questionId: id,
|
||||
authorProfileId,
|
||||
votes
|
||||
});
|
||||
|
||||
return c.json({ success: true, data: result }, 201);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to submit comment');
|
||||
}
|
||||
}
|
||||
)
|
||||
.post(
|
||||
'/:id/upvote',
|
||||
authMiddleware,
|
||||
orgMemberMiddleware,
|
||||
zValidator('param', ZUpvotePostParam),
|
||||
zValidator('json', ZUpvotePost),
|
||||
async (c) => {
|
||||
try {
|
||||
const { id } = c.req.valid('param');
|
||||
const { isQuestion } = c.req.valid('json');
|
||||
|
||||
const result = await upvote({
|
||||
id,
|
||||
isQuestion
|
||||
});
|
||||
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
} catch (error) {
|
||||
return handleError(c, error, 'Failed to upvote post');
|
||||
}
|
||||
}
|
||||
)
|
||||
.delete(
|
||||
'/:id/comment',
|
||||
authMiddleware,
|
||||
commentAuthorOrTeamMiddleware,
|
||||
zValidator('param', ZCommunityCommentDelete),
|
||||
async (c) => {
|
||||
const { id } = c.req.valid('param');
|
||||
const result = await deleteComment({ id });
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
}
|
||||
)
|
||||
.delete(
|
||||
'/:questionId/comments',
|
||||
authMiddleware,
|
||||
orgTeamMemberMiddleware,
|
||||
zValidator('param', ZGetCommunityCommentQuestionId),
|
||||
async (c) => {
|
||||
const { questionId } = c.req.valid('param');
|
||||
const result = await deleteCommentsByQuestionId({ questionId });
|
||||
return c.json({ success: true, data: result }, 200);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user