eduverse-backend / src /modules /community /controllers /communities.controller.ts
tarekdx3's picture
feat: restructure community system with global + department communities
8aa5b53
Raw
History Blame Contribute Delete
7.74 kB
import {
Controller,
Get,
Post,
Put,
Body,
Param,
Query,
Req,
UseGuards,
ParseIntPipe,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiParam,
ApiBody,
ApiResponse,
ApiBearerAuth,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../../auth/guards/roles.guard';
import { Roles } from '../../auth/roles.decorator';
import { RoleName } from '../../auth/entities/role.entity';
import { CommunitiesService } from '../services/communities.service';
import { CommunityPostsService } from '../services/community-posts.service';
import { CommunityTagsService } from '../services/community-tags.service';
import {
CreateCommunityDto,
UpdateCommunityDto,
CommunityQueryDto,
CreatePostDto,
PostQueryDto,
CreateTagDto,
} from '../dto';
@ApiTags('🏘️ Communities')
@ApiBearerAuth('JWT-auth')
@Controller('api/community')
@UseGuards(JwtAuthGuard, RolesGuard)
export class CommunitiesController {
constructor(
private readonly communitiesService: CommunitiesService,
private readonly postsService: CommunityPostsService,
private readonly tagsService: CommunityTagsService,
) {}
// ============== COMMUNITY CRUD ==============
@Get('communities')
@ApiOperation({
summary: 'List communities',
description: 'List all communities the user has access to. Global communities are visible to all. Department communities are filtered by enrollment.',
})
@ApiResponse({ status: 200, description: 'Paginated list of communities' })
async findAllCommunities(@Query() query: CommunityQueryDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.communitiesService.findAll(query, userId, roles);
}
@Post('communities')
@HttpCode(HttpStatus.CREATED)
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.DEPARTMENT_HEAD, RoleName.INSTRUCTOR)
@ApiOperation({
summary: 'Create a community',
description: 'Create a new global or department community. Admin/Instructor/Department Head only.',
})
@ApiBody({ type: CreateCommunityDto })
@ApiResponse({ status: 201, description: 'Community created' })
async createCommunity(@Body() dto: CreateCommunityDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
return this.communitiesService.create(dto, userId);
}
@Get('communities/:communityId')
@ApiOperation({ summary: 'Get community details' })
@ApiParam({ name: 'communityId', description: 'Community ID' })
@ApiResponse({ status: 200, description: 'Community details' })
async getCommunity(@Param('communityId', ParseIntPipe) communityId: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.communitiesService.checkAccess(communityId, userId, roles);
}
@Put('communities/:communityId')
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.DEPARTMENT_HEAD, RoleName.INSTRUCTOR)
@ApiOperation({ summary: 'Update a community' })
@ApiParam({ name: 'communityId', description: 'Community ID' })
@ApiBody({ type: UpdateCommunityDto })
async updateCommunity(
@Param('communityId', ParseIntPipe) communityId: number,
@Body() dto: UpdateCommunityDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.communitiesService.update(communityId, dto, userId, roles);
}
// ============== COMMUNITY POSTS ==============
@Get('communities/:communityId/posts')
@ApiOperation({
summary: 'List posts in a community',
description: 'Get paginated posts from a specific community. Access is validated.',
})
@ApiParam({ name: 'communityId', description: 'Community ID' })
async getCommunityPosts(
@Param('communityId', ParseIntPipe) communityId: number,
@Query() query: PostQueryDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
// Validate access
await this.communitiesService.checkAccess(communityId, userId, roles);
// Override communityId in query
query.communityId = communityId;
return this.postsService.findAll(query);
}
@Post('communities/:communityId/posts')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({
summary: 'Create a post in a community',
description: 'Create a new post in a specific community. Access is validated.',
})
@ApiParam({ name: 'communityId', description: 'Community ID' })
@ApiBody({ type: CreatePostDto })
async createCommunityPost(
@Param('communityId', ParseIntPipe) communityId: number,
@Body() dto: CreatePostDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
// Validate access
await this.communitiesService.checkAccess(communityId, userId, roles);
// Override communityId
dto.communityId = communityId;
return this.postsService.create(dto, userId);
}
// ============== CONVENIENCE ROUTES ==============
@Get('global')
@ApiOperation({
summary: 'Get global community posts',
description: 'Shortcut to list posts from the global community (community_id = 1).',
})
async getGlobalPosts(@Query() query: PostQueryDto) {
query.communityId = 1; // Global community is always ID 1
return this.postsService.findAll(query);
}
@Post('global/posts')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({
summary: 'Create a post in global community',
description: 'Shortcut to create a post in the global community.',
})
@ApiBody({ type: CreatePostDto })
async createGlobalPost(@Body() dto: CreatePostDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
dto.communityId = 1;
return this.postsService.create(dto, userId);
}
@Get('department/:departmentId')
@ApiOperation({
summary: 'List department communities',
description: 'List all communities for a specific department. Access is validated by enrollment.',
})
@ApiParam({ name: 'departmentId', description: 'Department ID' })
async getDepartmentCommunities(
@Param('departmentId', ParseIntPipe) departmentId: number,
@Query() query: CommunityQueryDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
query.departmentId = departmentId;
return this.communitiesService.findAll(query, userId, roles);
}
@Get('my-departments')
@ApiOperation({
summary: 'Get my accessible departments',
description: 'Returns department IDs the user has access to based on enrollment.',
})
async getMyDepartments(@Req() req: any) {
const userId = req.user.userId || req.user.id;
const departmentIds = await this.communitiesService.getUserDepartmentIds(userId);
return { departmentIds };
}
// ============== TAGS ==============
@Get('tags')
@ApiOperation({ summary: 'List all tags' })
async getAllTags() {
return this.tagsService.findAll();
}
@Post('tags')
@HttpCode(HttpStatus.CREATED)
@Roles(RoleName.INSTRUCTOR, RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.TA)
@ApiOperation({
summary: 'Create a tag',
description: 'Create a new tag. Instructor/Admin/TA only.',
})
@ApiBody({ type: CreateTagDto })
async createTag(@Body() dto: CreateTagDto) {
return this.tagsService.create(dto);
}
// ============== HELPER ==============
private extractRoles(user: any): string[] {
if (!user.roles) return [];
return user.roles.map((r: any) => r.roleName || r.name || r);
}
}