From a9c6fc6f29158539a592268927e67687a3eae559 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 17 Jul 2025 19:25:20 +0700 Subject: [PATCH] feat: media --- .../public/icons/platforms/wordpress.png | Bin 0 -> 16941 bytes .../providers/show.all.providers.tsx | 5 + .../wordpress/wordpress.post.type.tsx | 57 +++++ .../wordpress/wordpress.provider.tsx | 35 +++ .../all.providers.settings.ts | 3 + .../posts/providers-settings/wordpress.dto.ts | 25 ++ .../src/integrations/integration.manager.ts | 2 + .../src/integrations/social.abstract.ts | 1 + .../integrations/social/wordpress.provider.ts | 237 ++++++++++++++++++ package.json | 1 + pnpm-lock.yaml | 9 + 11 files changed, 375 insertions(+) create mode 100644 apps/frontend/public/icons/platforms/wordpress.png create mode 100644 apps/frontend/src/components/new-launch/providers/wordpress/wordpress.post.type.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/wordpress.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts diff --git a/apps/frontend/public/icons/platforms/wordpress.png b/apps/frontend/public/icons/platforms/wordpress.png new file mode 100644 index 0000000000000000000000000000000000000000..fc5be8336df973a463c5ca710006a739b6d20023 GIT binary patch literal 16941 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 vZr7`vujuGr^Nr!!pSzU1JNo { const { date, current, global, selectedIntegrations, allIntegrations } = diff --git a/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.post.type.tsx b/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.post.type.tsx new file mode 100644 index 00000000..53a18563 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.post.type.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { FC, useEffect, useState } from 'react'; +import { Select } from '@gitroom/react/form/select'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +export const WordpressPostType: FC<{ + name: string; + onChange: (event: { + target: { + value: string; + name: string; + }; + }) => void; +}> = (props) => { + const { onChange, name } = props; + const t = useT(); + const customFunc = useCustomProviderFunction(); + const [orgs, setOrgs] = useState([]); + const { getValues } = useSettings(); + const [currentMedia, setCurrentMedia] = useState(); + const onChangeInner = (event: { + target: { + value: string; + name: string; + }; + }) => { + setCurrentMedia(event.target.value); + onChange(event); + }; + useEffect(() => { + customFunc.get('postTypes').then((data) => setOrgs(data)); + const settings = getValues()[props.name]; + if (settings) { + setCurrentMedia(settings); + } + }, []); + if (!orgs.length) { + return null; + } + return ( + + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx b/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx new file mode 100644 index 00000000..47fb98a7 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { FC } from 'react'; +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { Input } from '@gitroom/react/form/input'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { WordpressPostType } from '@gitroom/frontend/components/new-launch/providers/wordpress/wordpress.post.type'; +import { MediaComponent } from '@gitroom/frontend/components/media/media.component'; + +const WordpressSettings: FC = () => { + const form = useSettings(); + return ( + <> + + + + + ); +}; +export default withProvider({ + postComment: PostComment.COMMENT, + minimumCharacters: [], + SettingsComponent: WordpressSettings, + CustomPreviewComponent: undefined, // WordpressPreview, + dto: undefined, + checkValidity: undefined, + maximumCharacters: 100000, +}); diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 7a2572a7..3caf5e5f 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -13,6 +13,7 @@ import { IsIn } from 'class-validator'; import { MediumSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/medium.settings.dto'; import { DevToSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.settings.dto'; import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/hashnode.settings.dto'; +import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto'; export type ProviderExtension = { __type: T } & M; export type AllProvidersSettings = @@ -32,6 +33,7 @@ export type AllProvidersSettings = | ProviderExtension<'medium', MediumSettingsDto> | ProviderExtension<'devto', DevToSettingsDto> | ProviderExtension<'hashnode', HashnodeSettingsDto> + | ProviderExtension<'wordpress', WordpressDto> | ProviderExtension<'facebook', None> | ProviderExtension<'threads', None> | ProviderExtension<'mastodon', None> @@ -60,6 +62,7 @@ export const allProviders = (setEmpty?: any) => { { value: InstagramDto, name: 'instagram-standalone' }, { value: MediumSettingsDto, name: 'medium' }, { value: DevToSettingsDto, name: 'devto' }, + { value: WordpressDto, name: 'wordpress' }, { value: HashnodeSettingsDto, name: 'hashnode' }, { value: setEmpty, name: 'facebook' }, { value: setEmpty, name: 'threads' }, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/wordpress.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/wordpress.dto.ts new file mode 100644 index 00000000..732b4e35 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/wordpress.dto.ts @@ -0,0 +1,25 @@ +import { + IsDefined, + IsOptional, + IsString, + MinLength, + ValidateNested, +} from 'class-validator'; +import { MediaDto } from '@gitroom/nestjs-libraries/dtos/media/media.dto'; +import { Type } from 'class-transformer'; + +export class WordpressDto { + @IsString() + @MinLength(2) + @IsDefined() + title: string; + + @IsOptional() + @ValidateNested() + @Type(() => MediaDto) + main_image?: MediaDto; + + @IsString() + @IsDefined() + type: string; +} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 50fed756..c1ca5e73 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -26,6 +26,7 @@ import { FarcasterProvider } from '@gitroom/nestjs-libraries/integrations/social import { TelegramProvider } from '@gitroom/nestjs-libraries/integrations/social/telegram.provider'; import { NostrProvider } from '@gitroom/nestjs-libraries/integrations/social/nostr.provider'; import { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.provider'; +import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social/wordpress.provider'; export const socialIntegrationList: SocialProvider[] = [ new XProvider(), @@ -52,6 +53,7 @@ export const socialIntegrationList: SocialProvider[] = [ new MediumProvider(), new DevToProvider(), new HashnodeProvider(), + new WordpressProvider(), // new MastodonCustomProvider(), ]; diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 9d807236..299cbc1c 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -75,6 +75,7 @@ export abstract class SocialAbstract { json = '{}'; } + console.log(json); if (json.includes('rate_limit_exceeded') || json.includes('Rate limit')) { await timer(5000); console.log('rate limit trying again'); diff --git a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts new file mode 100644 index 00000000..1ad8fadb --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts @@ -0,0 +1,237 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto'; +import slugify from 'slugify'; +import FormData from 'form-data'; +import axios from 'axios'; + +export class WordpressProvider + extends SocialAbstract + implements SocialProvider +{ + identifier = 'wordpress'; + name = 'WordPress'; + isBetweenSteps = false; + editor = 'html' as const; + scopes = [] as string[]; + + async generateAuthUrl() { + const state = makeId(6); + return { + url: '', + codeVerifier: makeId(10), + state, + }; + } + + async refreshToken(refreshToken: string): Promise { + return { + refreshToken: '', + expiresIn: 0, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + }; + } + + async customFields() { + return [ + { + key: 'domain', + label: 'Domain URL', + validation: `/^https?:\\/\\/(?:www\\.)?[\\w\\-]+(\\.[\\w\\-]+)+([\\/?#][^\\s]*)?$/`, + type: 'text' as const, + }, + { + key: 'username', + label: 'Username', + validation: `/.+/`, + type: 'text' as const, + }, + { + key: 'password', + label: 'Password', + validation: `/.+/`, + type: 'password' as const, + }, + ]; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const body = JSON.parse(Buffer.from(params.code, 'base64').toString()) as { + domain: string; + username: string; + password: string; + }; + try { + const auth = Buffer.from(`${body.username}:${body.password}`).toString( + 'base64' + ); + const { id, name, avatar_urls } = await ( + await fetch(`${body.domain}/wp-json/wp/v2/users/me`, { + headers: { + Authorization: `Basic ${auth}`, + }, + }) + ).json(); + + const biggestImage = Object.entries(avatar_urls).reduce( + (all, current) => { + if (all > Number(current[0])) { + return all; + } + return Number(current[0]); + }, + 0 + ); + + return { + refreshToken: '', + expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(), + accessToken: params.code, + id: body.domain + '_' + id, + name, + picture: avatar_urls?.[String(biggestImage)] || '', + username: body.username, + }; + } catch (err) { + console.log(err); + return 'Invalid credentials'; + } + } + + async postTypes(token: string) { + const body = JSON.parse(Buffer.from(token, 'base64').toString()) as { + domain: string; + username: string; + password: string; + }; + + const auth = Buffer.from(`${body.username}:${body.password}`).toString( + 'base64' + ); + + const postTypes = await ( + await this.fetch(`${body.domain}/wp-json/wp/v2/types`, { + headers: { + Authorization: `Basic ${auth}`, + }, + }) + ).json(); + + return Object.entries(postTypes).reduce((all, [key, value]) => { + if ( + key.indexOf('wp_') > -1 || + key.indexOf('nav_') > -1 || + key === 'attachment' + ) { + return all; + } + + all.push({ + id: value.rest_base, + name: value.name, + }); + + return all; + }, []); + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const body = JSON.parse(Buffer.from(accessToken, 'base64').toString()) as { + domain: string; + username: string; + password: string; + }; + + const auth = Buffer.from(`${body.username}:${body.password}`).toString( + 'base64' + ); + + let mediaId = ''; + if (postDetails?.[0]?.settings?.main_image?.path) { + console.log('Uploading image to WordPress', postDetails[0].settings.main_image.path); + const imageData = await axios.get(postDetails[0].settings.main_image.path, { + responseType: 'stream', + }); + + const form = new FormData(); + form.append('file', imageData.data, { + filename: postDetails[0].settings.main_image.path.split('/').pop(), // You can customize the filename + contentType: imageData.headers['content-type'], + }); + if (postDetails[0].settings.main_image?.alt) { + form.append('alt_text', postDetails[0].settings.main_image.alt); + } + + const mediaResponse = await axios.post( + `${body.domain}/wp-json/wp/v2/media`, + { + method: 'POST', + body: form, + }, + { + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/json', + }, + } + ); + + console.log('nevo', mediaResponse); + mediaId = mediaResponse.data.id; + } + + const submit = await ( + await this.fetch( + `https://cms.postiz.com/wp-json/wp/v2/${postDetails?.[0]?.settings?.type}`, + { + headers: { + Authorization: `Basic ${auth}`, + 'Content-Type': 'application/json', + }, + method: 'POST', + body: JSON.stringify({ + title: postDetails?.[0]?.settings?.title, + content: postDetails?.[0]?.message, + slug: slugify(postDetails?.[0]?.settings?.title, { + lower: true, + strict: true, + trim: true, + }), + status: 'publish', + ...(mediaId ? { featured_media: mediaId } : {}), + }), + } + ) + ).json(); + + return [ + { + id: postDetails?.[0].id, + status: 'completed', + postId: String(submit.id), + releaseURL: submit.link, + }, + ]; + } +} diff --git a/package.json b/package.json index ff995a76..2ce1b269 100644 --- a/package.json +++ b/package.json @@ -195,6 +195,7 @@ "sha256": "^0.2.0", "sharp": "^0.33.4", "simple-statistics": "^7.8.3", + "slugify": "^1.6.6", "stripe": "^15.5.0", "striptags": "^3.2.0", "subtitle": "4.2.2-alpha.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44c8c053..6041f17b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -462,6 +462,9 @@ importers: simple-statistics: specifier: ^7.8.3 version: 7.8.8 + slugify: + specifier: ^1.6.6 + version: 1.6.6 stripe: specifier: ^15.5.0 version: 15.12.0 @@ -13260,6 +13263,10 @@ packages: slate@0.94.1: resolution: {integrity: sha512-GH/yizXr1ceBoZ9P9uebIaHe3dC/g6Plpf9nlUwnvoyf6V1UOYrRwkabtOCd3ZfIGxomY4P7lfgLr7FPH8/BKA==} + slugify@1.6.6: + resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} + engines: {node: '>=8.0.0'} + smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -31592,6 +31599,8 @@ snapshots: is-plain-object: 5.0.0 tiny-warning: 1.0.3 + slugify@1.6.6: {} + smart-buffer@4.2.0: {} snake-case@3.0.4: