feat: social media

This commit is contained in:
Nevo David 2024-05-31 14:50:50 +07:00
parent 78a9dea3f5
commit e0599b48c3
12 changed files with 110 additions and 35 deletions

View File

@ -8,6 +8,7 @@ import {
Query,
UploadedFile,
UseInterceptors,
UsePipes,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Express } from 'express';
@ -15,6 +16,7 @@ import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.reque
import { Organization } from '@prisma/client';
import { MediaService } from '@gitroom/nestjs-libraries/database/prisma/media/media.service';
import { ApiTags } from '@nestjs/swagger';
import { CustomFileValidationPipe } from '@gitroom/nestjs-libraries/upload/custom.upload.validation';
@ApiTags('Media')
@Controller('/media')
@ -22,17 +24,10 @@ export class MediaController {
constructor(private _mediaService: MediaService) {}
@Post('/')
@UseInterceptors(FileInterceptor('file'))
@UsePipes(new CustomFileValidationPipe())
async uploadFile(
@GetOrgFromRequest() org: Organization,
@UploadedFile(
'file',
new ParseFilePipe({
validators: [
new MaxFileSizeValidator({ maxSize: 10 * 1024 * 1024 }),
new FileTypeValidator({ fileType: /^(image\/.+|video\/mp4)$/ }),
],
})
)
@UploadedFile('file')
file: Express.Multer.File
) {
const filePath =

View File

@ -65,7 +65,6 @@ export class AuthMiddleware implements NestMiddleware {
} catch (err) {
throw new Error('Unauthorized');
}
console.log('Request...');
next();
}
}

View File

@ -255,7 +255,7 @@ export const AddEditModal: FC<{
for (const key of allKeys) {
if (key.checkValidity) {
const check = await key.checkValidity(key?.value.map((p: any) => p.image || {path: ''}));
const check = await key.checkValidity(key?.value.map((p: any) => p.image || []));
if (typeof check === 'string') {
toaster.show(check, 'warning');
return;

View File

@ -22,11 +22,6 @@ export const DatePicker: FC<{
const changeDate = useCallback(
(type: 'date' | 'time') => (day: Date) => {
console.log(
type === 'time'
? date.format('YYYY-MM-DD') + ' ' + dayjs(day).format('HH:mm:ss')
: dayjs(day).format('YYYY-MM-DD') + ' ' + date.format('HH:mm:ss')
);
onChange(
dayjs(
type === 'time'

View File

@ -181,7 +181,6 @@ export const linkedinCompany = (identifier: string, id: string): ICommand[] => {
const state1 = api.setSelectionRange(newSelectionRange);
const media = await showPostSelector(id);
console.log(media);
executeCommand({
api,
selectedText: state1.selectedText,

View File

@ -131,8 +131,8 @@ export default withProvider(
PinterestPreview,
PinterestSettingsDto,
async ([firstItem, ...otherItems]) => {
const isMp4 = firstItem.find((item) => item.path.indexOf('mp4') > -1);
const isPicture = firstItem.find((item) => item.path.indexOf('mp4') === -1);
const isMp4 = firstItem?.find((item) => item.path.indexOf('mp4') > -1);
const isPicture = firstItem?.find((item) => item.path.indexOf('mp4') === -1);
if (firstItem.length === 0) {
return 'Pinterest requires at least one media';

View File

@ -67,12 +67,16 @@ export const MediaBox: FC<{
const uploadMedia = useCallback(
async (file: ChangeEvent<HTMLInputElement>) => {
const maxFileSize = 10 * 1024 * 1024;
const maxFileSize =
(file?.target?.files?.[0].name.indexOf('mp4') || -1) > -1
? 100 * 1024 * 1024
: 10 * 1024 * 1024;
if (
!file?.target?.files?.length ||
file?.target?.files?.[0]?.size > maxFileSize
) {
toaster.show('Maximum file size 10mb', 'warning');
toaster.show(`Maximum file size ${maxFileSize / 1024 / 1024}mb`, 'warning');
return;
}
const formData = new FormData();

View File

@ -300,7 +300,6 @@ export class PostsService {
integrationId: string
) {
if (!(await this._messagesService.canAddPost(id, order, integrationId))) {
console.log('hello');
throw new Error('You can not add a post to this publication');
}
const getOrgByOrder = await this._messagesService.getOrgByOrder(order);

View File

@ -15,15 +15,43 @@ export class PinterestProvider implements SocialProvider {
name = 'Pinterest';
isBetweenSteps = false;
async refreshToken(refresh_token: string): Promise<AuthTokenDetails> {
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
const { access_token, expires_in } = await (
await fetch('https://api-sandbox.pinterest.com/v5/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(
`${process.env.PINTEREST_CLIENT_ID}:${process.env.PINTEREST_CLIENT_SECRET}`
).toString('base64')}`,
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
scope:
'boards:read,boards:write,pins:read,pins:write,user_accounts:read',
redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/pinterest`,
}),
})
).json();
const { id, profile_image, username } = await (
await fetch('https://api-sandbox.pinterest.com/v5/user_account', {
method: 'GET',
headers: {
Authorization: `Bearer ${access_token}`,
},
})
).json();
return {
refreshToken: '',
expiresIn: 0,
accessToken: '',
id: '',
name: '',
picture: '',
username: '',
id: id,
name: username,
accessToken: access_token,
refreshToken: refreshToken,
expiresIn: expires_in,
picture: profile_image,
username,
};
}
@ -110,7 +138,14 @@ export class PinterestProvider implements SocialProvider {
postDetails: PostDetails<PinterestSettingsDto>[]
): Promise<PostResponse[]> {
let mediaId = '';
if ((postDetails?.[0]?.media?.[0]?.path?.indexOf('mp4') || -1) > -1) {
const findMp4 = postDetails?.[0]?.media?.find(
(p) => (p.path?.indexOf('mp4') || -1) > -1
);
const picture = postDetails?.[0]?.media?.find(
(p) => (p.path?.indexOf('mp4') || -1) === -1
);
if (findMp4) {
const { upload_url, media_id, upload_parameters } = await (
await fetch('https://api-sandbox.pinterest.com/v5/media', {
method: 'POST',
@ -197,6 +232,7 @@ export class PinterestProvider implements SocialProvider {
? {
source_type: 'video_id',
media_id: mediaId,
cover_image_url: picture?.url,
}
: mapImages?.length === 1
? {
@ -213,9 +249,9 @@ export class PinterestProvider implements SocialProvider {
return [
{
id,
id: postDetails?.[0]?.id,
postId: pId,
releaseURL: link,
releaseURL: `https://www.pinterest.com/pin/${pId}`,
status: 'success',
},
];

View File

@ -0,0 +1,46 @@
import {
BadRequestException,
FileTypeValidator,
Injectable,
MaxFileSizeValidator,
ParseFilePipe,
PipeTransform,
} from '@nestjs/common';
@Injectable()
export class CustomFileValidationPipe implements PipeTransform {
async transform(value: any) {
if (!value) {
throw 'No file provided.';
}
if (!value.mimetype) {
return value;
}
// Set the maximum file size based on the MIME type
const maxSize = this.getMaxSize(value.mimetype);
const validation =
(value.mimetype.startsWith('image/') ||
value.mimetype.startsWith('video/mp4')) &&
value.size <= maxSize;
if (validation) {
return value;
}
throw new BadRequestException(
`File size exceeds the maximum allowed size of ${maxSize} bytes.`
);
}
private getMaxSize(mimeType: string): number {
if (mimeType.startsWith('image/')) {
return 10 * 1024 * 1024; // 10 MB
} else if (mimeType.startsWith('video/')) {
return 1024 * 1024 * 1024; // 1 GB
} else {
throw new BadRequestException('Unsupported file type.');
}
}
}

View File

@ -4,6 +4,7 @@ import { diskStorage } from 'multer';
import { mkdirSync } from 'fs';
import { extname } from 'path';
import CloudflareStorage from '@gitroom/nestjs-libraries/upload/cloudflare.storage';
import { CustomFileValidationPipe } from '@gitroom/nestjs-libraries/upload/custom.upload.validation';
const storage =
process.env.CLOUDFLARE_ACCOUNT_ID &&
@ -51,8 +52,9 @@ const storage =
storage,
}),
],
providers: [CustomFileValidationPipe],
get exports() {
return this.imports;
return [...this.imports, ...this.providers];
},
})
export class UploadModule {}

View File

@ -5,5 +5,5 @@ import { FC } from 'react';
export const VideoFrame: FC<{ url: string }> = (props) => {
const { url } = props;
return <video id="videoFrame" src={url + '#t=0.1'} preload="metadata" autoPlay={false}></video>;
return <video className="w-full h-full object-cover" src={url + '#t=0.1'} preload="metadata" autoPlay={false}></video>;
};