feat: social media
This commit is contained in:
parent
78a9dea3f5
commit
e0599b48c3
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ export class AuthMiddleware implements NestMiddleware {
|
|||
} catch (err) {
|
||||
throw new Error('Unauthorized');
|
||||
}
|
||||
console.log('Request...');
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue