import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query, UploadedFiles, UseGuards, UseInterceptors, } from '@nestjs/common'; import { FileFieldsInterceptor } from '@nestjs/platform-express'; import { ApiBearerAuth, ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger'; import { CurrentUser } from '../../common/decorators/current-user.decorator'; import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { MultipartFormDataGuard } from '../../common/guards/multipart-form-data.guard'; import { SuperAdminPermissionsGuard } from '../../common/guards/superadmin-permissions.guard'; import { SuperAdminJwtAuthGuard } from '../../common/guards/super-admin-jwt-auth.guard'; import { JwtPayload } from '../../common/interfaces/jwt-payload.interface'; import { SuperAdminPermissions } from '../../common/decorators/superadmin-permissions.decorator'; import { AdminPostQueryDto } from './dto/admin-post-query.dto'; import { CreateReelDto } from './dto/create-reel.dto'; import { CreatePostDto } from './dto/create-post.dto'; import { CreateRepostDto } from './dto/create-repost.dto'; import { PostQueryDto } from './dto/post-query.dto'; import { ReelQueryDto } from './dto/reel-query.dto'; import { UpdateCommentSettingsDto } from './dto/update-comment-settings.dto'; import { UpdatePostDto } from './dto/update-post.dto'; import { PostsService } from './posts.service'; import { SUPERADMIN_PERMISSIONS } from '../superadmin/superadmin-permissions'; @ApiTags('Posts') @Controller('posts') export class PostsController { constructor(private readonly postsService: PostsService) {} @ApiBearerAuth() @UseGuards(JwtAuthGuard, MultipartFormDataGuard) @UseInterceptors( FileFieldsInterceptor([ { name: 'imageFiles', maxCount: 10 }, { name: 'videoFile', maxCount: 1 }, { name: 'audioFile', maxCount: 1 }, ]), ) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { content: { type: 'string', example: 'First post #music' }, visibility: { type: 'string', enum: ['public', 'followers', 'private'] }, imageUrls: { type: 'array', items: { type: 'string' } }, imageCaptions: { type: 'array', items: { type: 'string' } }, imageAltTexts: { type: 'array', items: { type: 'string' } }, taggedUserIds: { type: 'array', items: { type: 'string' } }, collaboratorIds: { type: 'array', items: { type: 'string' } }, mentionUsernames: { type: 'array', items: { type: 'string' } }, location: { type: 'string', example: 'Riyadh, Saudi Arabia' }, latitude: { type: 'number', example: 24.7136 }, longitude: { type: 'number', example: 46.6753 }, videoUrl: { type: 'string', example: 'https://cdn.example.com/video.mp4' }, audioUrl: { type: 'string', example: 'https://cdn.example.com/audio.mp3' }, durationSeconds: { type: 'number', example: 54 }, thumbnailUrl: { type: 'string', example: 'https://cdn.example.com/cover.jpg' }, style: { type: 'string', example: 'Sharqi' }, maqam: { type: 'string', example: 'Hijaz' }, rhythmSignature: { type: 'string', example: '6/8' }, waveformPeaks: { type: 'array', items: { type: 'number' } }, commentsDisabled: { type: 'boolean' }, commentsFollowersOnly: { type: 'boolean' }, imageFiles: { type: 'array', items: { type: 'string', format: 'binary' } }, videoFile: { type: 'string', format: 'binary' }, audioFile: { type: 'string', format: 'binary' }, }, }, }) @Post() async create( @CurrentUser() user: JwtPayload, @Body() dto: CreatePostDto, @UploadedFiles() files?: { imageFiles?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; videoFile?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; audioFile?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; }, ) { return this.postsService.create( user.sub, dto, files?.imageFiles ?? [], files?.videoFile?.[0], files?.audioFile?.[0], ); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Get('user/:userId') async findUserPosts(@Param('userId') userId: string, @Query() query: PostQueryDto) { return this.postsService.findUserPosts(userId, query); } @ApiBearerAuth() @UseGuards(SuperAdminJwtAuthGuard, SuperAdminPermissionsGuard) @SuperAdminPermissions(SUPERADMIN_PERMISSIONS.CONTENT_MODERATE) @Get('admin/moderation') async findPlatformPosts(@Query() query: AdminPostQueryDto) { return this.postsService.findPlatformPosts(query); } @ApiBearerAuth() @UseGuards(JwtAuthGuard, MultipartFormDataGuard) @UseInterceptors( FileFieldsInterceptor([ { name: 'videoFile', maxCount: 1 }, ]), ) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { content: { type: 'string', example: 'New reel from oud session #reel' }, visibility: { type: 'string', enum: ['public', 'followers', 'private'] }, videoUrl: { type: 'string', example: 'https://cdn.example.com/reel.mp4' }, durationSeconds: { type: 'number', example: 42 }, thumbnailUrl: { type: 'string', example: 'https://cdn.example.com/reel-cover.jpg' }, style: { type: 'string', example: 'Sharqi' }, maqam: { type: 'string', example: 'Hijaz' }, rhythmSignature: { type: 'string', example: '6/8' }, mentionUsernames: { type: 'array', items: { type: 'string' } }, videoFile: { type: 'string', format: 'binary' }, }, }, }) @Post('reels') async createReel( @CurrentUser() user: JwtPayload, @Body() dto: CreateReelDto, @UploadedFiles() files?: { videoFile?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; }, ) { return this.postsService.createReel(user.sub, dto, files?.videoFile?.[0]); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Get('reels') async findReels(@Query() query: ReelQueryDto) { return this.postsService.findReels(query); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Get(':postId') async findById(@Param('postId') postId: string) { return this.postsService.findById(postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard, MultipartFormDataGuard) @UseInterceptors( FileFieldsInterceptor([ { name: 'imageFiles', maxCount: 10 }, { name: 'videoFile', maxCount: 1 }, { name: 'audioFile', maxCount: 1 }, ]), ) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { content: { type: 'string', example: 'Updated content' }, visibility: { type: 'string', enum: ['public', 'followers', 'private'] }, imageUrls: { type: 'array', items: { type: 'string' } }, imageCaptions: { type: 'array', items: { type: 'string' } }, imageAltTexts: { type: 'array', items: { type: 'string' } }, taggedUserIds: { type: 'array', items: { type: 'string' } }, collaboratorIds: { type: 'array', items: { type: 'string' } }, mentionUsernames: { type: 'array', items: { type: 'string' } }, location: { type: 'string', example: 'Jeddah, Saudi Arabia' }, latitude: { type: 'number', example: 21.5433 }, longitude: { type: 'number', example: 39.1728 }, videoUrl: { type: 'string', example: 'https://cdn.example.com/video.mp4' }, audioUrl: { type: 'string', example: 'https://cdn.example.com/audio.mp3' }, durationSeconds: { type: 'number', example: 54 }, thumbnailUrl: { type: 'string', example: 'https://cdn.example.com/cover.jpg' }, style: { type: 'string', example: 'Sharqi' }, maqam: { type: 'string', example: 'Hijaz' }, rhythmSignature: { type: 'string', example: '6/8' }, waveformPeaks: { type: 'array', items: { type: 'number' } }, commentsDisabled: { type: 'boolean' }, commentsFollowersOnly: { type: 'boolean' }, imageFiles: { type: 'array', items: { type: 'string', format: 'binary' } }, videoFile: { type: 'string', format: 'binary' }, audioFile: { type: 'string', format: 'binary' }, }, }, }) @Patch(':postId') async update( @CurrentUser() user: JwtPayload, @Param('postId') postId: string, @Body() dto: UpdatePostDto, @UploadedFiles() files?: { imageFiles?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; videoFile?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; audioFile?: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }>; }, ) { return this.postsService.update( user.sub, postId, dto, files?.imageFiles ?? [], files?.videoFile?.[0], files?.audioFile?.[0], ); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Post(':postId/repost') async repost( @CurrentUser() user: JwtPayload, @Param('postId') postId: string, @Body() dto: CreateRepostDto, ) { return this.postsService.createRepost(user.sub, postId, dto); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Patch(':postId/comment-settings') async updateCommentSettings( @CurrentUser() user: JwtPayload, @Param('postId') postId: string, @Body() dto: UpdateCommentSettingsDto, ) { return this.postsService.updateCommentSettings(user.sub, postId, dto); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Patch(':postId/pin-profile') async pinToProfile(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.pinToProfile(user.sub, postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Patch(':postId/unpin-profile') async unpinFromProfile(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.unpinFromProfile(user.sub, postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Patch(':postId/archive') async archive(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.archive(user.sub, postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Patch(':postId/restore-archive') async restoreArchive(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.restoreArchived(user.sub, postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Delete(':postId') async remove(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { await this.postsService.remove(user.sub, postId); return { success: true }; } @ApiBearerAuth() @UseGuards(SuperAdminJwtAuthGuard, SuperAdminPermissionsGuard) @SuperAdminPermissions(SUPERADMIN_PERMISSIONS.CONTENT_MODERATE) @Delete('admin/:postId') async removeBySuperAdmin(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { await this.postsService.removeBySuperAdmin(user.email ?? user.sub, postId); return { success: true }; } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post(':postId/view') async registerView(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.registerView(user.sub, postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post(':postId/play') async registerPlay(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.registerPlay(user.sub, postId); } @ApiBearerAuth() @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @Post(':postId/share') async registerShare(@CurrentUser() user: JwtPayload, @Param('postId') postId: string) { return this.postsService.registerShare(user.sub, postId); } }