322 أسطر
12 KiB
TypeScript
322 أسطر
12 KiB
TypeScript
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);
|
|
}
|
|
}
|