feat: expand backend admin marketplace and scaling
فشلت بعض الفحوصات
/ deploy (push) Failing after 1m22s
فشلت بعض الفحوصات
/ deploy (push) Failing after 1m22s
هذا الالتزام موجود في:
@@ -1,12 +1,34 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
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 { 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 { PostQueryDto } from './dto/post-query.dto';
|
||||
import { ReelQueryDto } from './dto/reel-query.dto';
|
||||
import { UpdatePostDto } from './dto/update-post.dto';
|
||||
import { PostsService } from './posts.service';
|
||||
import { SUPERADMIN_PERMISSIONS } from '../superadmin/superadmin-permissions';
|
||||
|
||||
@ApiTags('Posts')
|
||||
@Controller('posts')
|
||||
@@ -15,9 +37,58 @@ export class PostsController {
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@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' } },
|
||||
taggedUserIds: { 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' } },
|
||||
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) {
|
||||
return this.postsService.create(user.sub, dto);
|
||||
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()
|
||||
@@ -27,6 +98,58 @@ export class PostsController {
|
||||
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)
|
||||
@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')
|
||||
@@ -36,13 +159,60 @@ export class PostsController {
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@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' } },
|
||||
taggedUserIds: { 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' } },
|
||||
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);
|
||||
return this.postsService.update(
|
||||
user.sub,
|
||||
postId,
|
||||
dto,
|
||||
files?.imageFiles ?? [],
|
||||
files?.videoFile?.[0],
|
||||
files?.audioFile?.[0],
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@@ -52,4 +222,37 @@ export class PostsController {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم