feat: ai image
This commit is contained in:
parent
90b7d214eb
commit
7d4351adea
|
|
@ -61,12 +61,14 @@ export class BillingController {
|
|||
@Post('/cancel')
|
||||
async cancel(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@GetUserFromRequest() user: User,
|
||||
@Body() body: { feedback: string }
|
||||
) {
|
||||
await this._notificationService.sendEmail(
|
||||
process.env.EMAIL_FROM_ADDRESS,
|
||||
'Subscription Cancelled',
|
||||
`Organization ${org.name} has cancelled their subscription because: ${body.feedback}`
|
||||
`${user.name} from Organization ${org.name} has cancelled their subscription because: ${body.feedback}`,
|
||||
user.email
|
||||
);
|
||||
|
||||
return this._stripeService.setToCancel(org.id);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
import {
|
||||
Body, Controller, Get, Param, Post, Query, Req, Res, UploadedFile, UseInterceptors, UsePipes
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
Res,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
|
||||
|
|
@ -25,14 +35,35 @@ export class MediaController {
|
|||
async generateImage(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Req() req: Request,
|
||||
@Body('prompt') prompt: string
|
||||
@Body('prompt') prompt: string,
|
||||
isPicturePrompt = false
|
||||
) {
|
||||
const total = await this._subscriptionService.checkCredits(org);
|
||||
if (total.credits <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {output: 'data:image/png;base64,' + await this._mediaService.generateImage(prompt, org)};
|
||||
return {
|
||||
output:
|
||||
(isPicturePrompt ? '' : 'data:image/png;base64,') +
|
||||
(await this._mediaService.generateImage(prompt, org, isPicturePrompt)),
|
||||
};
|
||||
}
|
||||
|
||||
@Post('/generate-image-with-prompt')
|
||||
async generateImageFromText(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Req() req: Request,
|
||||
@Body('prompt') prompt: string
|
||||
) {
|
||||
const image = await this.generateImage(org, req, prompt, true);
|
||||
if (!image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const file = await this.storage.uploadSimple(image.output);
|
||||
|
||||
return this._mediaService.saveFile(org.id, file.split('/').pop(), file);
|
||||
}
|
||||
|
||||
@Post('/upload-server')
|
||||
|
|
@ -43,7 +74,11 @@ export class MediaController {
|
|||
@UploadedFile() file: Express.Multer.File
|
||||
) {
|
||||
const uploadedFile = await this.storage.uploadFile(file);
|
||||
return this._mediaService.saveFile(org.id, uploadedFile.originalname, uploadedFile.path);
|
||||
return this._mediaService.saveFile(
|
||||
org.id,
|
||||
uploadedFile.originalname,
|
||||
uploadedFile.path
|
||||
);
|
||||
}
|
||||
|
||||
@Post('/upload-simple')
|
||||
|
|
@ -53,7 +88,11 @@ export class MediaController {
|
|||
@UploadedFile('file') file: Express.Multer.File
|
||||
) {
|
||||
const getFile = await this.storage.uploadFile(file);
|
||||
return this._mediaService.saveFile(org.id, getFile.originalname, getFile.path);
|
||||
return this._mediaService.saveFile(
|
||||
org.id,
|
||||
getFile.originalname,
|
||||
getFile.path
|
||||
);
|
||||
}
|
||||
|
||||
@Post('/:endpoint')
|
||||
|
|
@ -75,10 +114,14 @@ export class MediaController {
|
|||
// @ts-ignore
|
||||
const name = upload.Location.split('/').pop();
|
||||
|
||||
// @ts-ignore
|
||||
const saveFile = await this._mediaService.saveFile(org.id, name, upload.Location);
|
||||
const saveFile = await this._mediaService.saveFile(
|
||||
org.id,
|
||||
name,
|
||||
// @ts-ignore
|
||||
upload.Location
|
||||
);
|
||||
|
||||
res.status(200).json({...upload, saved: saveFile});
|
||||
res.status(200).json({ ...upload, saved: saveFile });
|
||||
// const filePath =
|
||||
// file.path.indexOf('http') === 0
|
||||
// ? file.path
|
||||
|
|
|
|||
|
|
@ -45,6 +45,12 @@ export class PostsController {
|
|||
return this._messagesService.getMarketplaceAvailableOffers(org.id, id);
|
||||
}
|
||||
|
||||
@Post('/posts/generate-image')
|
||||
@CheckPolicies([AuthorizationActions.Create, Sections.POSTS_PER_MONTH])
|
||||
generateImage(@Body() body: { text: string; type: string }) {
|
||||
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async getPosts(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
|
|
|
|||
|
|
@ -655,6 +655,7 @@ export const AddEditModal: FC<{
|
|||
<div className="flex">
|
||||
<div className="flex-1">
|
||||
<MultiMediaComponent
|
||||
text={p.content}
|
||||
label="Attachments"
|
||||
description=""
|
||||
value={p.image}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
import { Button } from '@gitroom/react/form/button';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Loading from 'react-loading';
|
||||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
|
||||
const list = [
|
||||
'Realistic',
|
||||
'Cartoon',
|
||||
'Anime',
|
||||
'Fantasy',
|
||||
'Abstract',
|
||||
'Pixel Art',
|
||||
'Sketch',
|
||||
'Watercolor',
|
||||
'Minimalist',
|
||||
'Cyberpunk',
|
||||
'Monochromatic',
|
||||
'Surreal',
|
||||
'Pop Art',
|
||||
'Fantasy Realism',
|
||||
];
|
||||
|
||||
export const AiImage: FC<{
|
||||
value: string;
|
||||
onChange: (params: { id: string; path: string }) => void;
|
||||
}> = (props) => {
|
||||
const { value, onChange } = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const fetch = useFetch();
|
||||
|
||||
const generateImage = useCallback(
|
||||
(type: string) => async () => {
|
||||
setLoading(true);
|
||||
const image = await (
|
||||
await fetch('/media/generate-image-with-prompt', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
prompt: `
|
||||
<!-- description -->
|
||||
${value}
|
||||
<!-- /description -->
|
||||
|
||||
<!-- style -->
|
||||
${type}
|
||||
<!-- /style -->
|
||||
|
||||
`,
|
||||
}),
|
||||
})
|
||||
).json();
|
||||
setLoading(false);
|
||||
onChange(image);
|
||||
},
|
||||
[value, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative group">
|
||||
<Button
|
||||
{...(value.length < 30
|
||||
? {
|
||||
'data-tooltip-id': 'tooltip',
|
||||
'data-tooltip-content':
|
||||
'Please add at least 30 characters to generate AI image',
|
||||
}
|
||||
: {})}
|
||||
className={clsx(
|
||||
'relative ml-[10px] rounded-[4px] mb-[10px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-customColor21 bg-input',
|
||||
value.length < 30 && 'opacity-25'
|
||||
)}
|
||||
>
|
||||
{loading && (
|
||||
<div className="absolute left-[50%] -translate-x-[50%]">
|
||||
<Loading height={30} width={30} type="spin" color="#fff" />
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={clsx(
|
||||
'flex gap-[5px] items-center',
|
||||
loading && 'invisible'
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] !text-white">AI</div>
|
||||
</div>
|
||||
</Button>
|
||||
{value.length >= 30 && !loading && (
|
||||
<div className="text-[12px] ml-[10px] -mt-[10px] w-[200px] absolute top-[100%] z-[500] left-0 hidden group-hover:block">
|
||||
<ul className="cursor-pointer rounded-[4px] border border-dashed border-customColor21 mt-[3px] p-[5px] bg-customColor2">
|
||||
{list.map((p) => (
|
||||
<li onClick={generateImage(p)} key={p} className="hover:bg-sixth">
|
||||
{p}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -474,6 +474,7 @@ export const withProvider = function <T extends object>(
|
|||
<div className="flex">
|
||||
<div className="flex-1">
|
||||
<MultiMediaComponent
|
||||
text={val.content}
|
||||
label="Attachments"
|
||||
description=""
|
||||
name="image"
|
||||
|
|
@ -558,7 +559,11 @@ export const withProvider = function <T extends object>(
|
|||
? undefined
|
||||
: typeof maximumCharacters === 'number'
|
||||
? maximumCharacters
|
||||
: maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
|
||||
: maximumCharacters(
|
||||
JSON.parse(
|
||||
integration?.additionalSettings || '[]'
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -568,7 +573,11 @@ export const withProvider = function <T extends object>(
|
|||
? undefined
|
||||
: typeof maximumCharacters === 'number'
|
||||
? maximumCharacters
|
||||
: maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
|
||||
: maximumCharacters(
|
||||
JSON.parse(
|
||||
integration?.additionalSettings || '[]'
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
|
|||
import { MultipartFileUploader } from '@gitroom/frontend/components/media/new.uploader';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useUser } from '@gitroom/frontend/components/layout/user.context';
|
||||
import { AiImage } from '@gitroom/frontend/components/launches/ai.image';
|
||||
const Polonto = dynamic(
|
||||
() => import('@gitroom/frontend/components/launches/polonto')
|
||||
);
|
||||
|
|
@ -152,7 +153,7 @@ export const MediaBox: FC<{
|
|||
)}
|
||||
>
|
||||
{!mediaList.length && (
|
||||
<div className="flex flex-col text-center">
|
||||
<div className="flex flex-col text-center items-center justify-center mx-auto">
|
||||
<div>You don{"'"}t have any assets yet.</div>
|
||||
<div>Click the button below to upload one</div>
|
||||
<div className="mt-[10px] justify-center items-center flex flex-col-reverse gap-[10px]">
|
||||
|
|
@ -212,13 +213,14 @@ export const MultiMediaComponent: FC<{
|
|||
label: string;
|
||||
description: string;
|
||||
value?: Array<{ path: string; id: string }>;
|
||||
text: string;
|
||||
name: string;
|
||||
error?: any;
|
||||
onChange: (event: {
|
||||
target: { name: string; value?: Array<{ id: string; path: string }> };
|
||||
}) => void;
|
||||
}> = (props) => {
|
||||
const { name, label, error, description, onChange, value } = props;
|
||||
const { name, label, error, text, description, onChange, value } = props;
|
||||
const user = useUser();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -276,48 +278,59 @@ export const MultiMediaComponent: FC<{
|
|||
onClick={showModal}
|
||||
className="ml-[10px] rounded-[4px] mb-[10px] gap-[8px] !text-primary justify-center items-center w-[127px] flex border border-dashed border-customColor21 bg-input"
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
className="!text-primary"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] text-primary">
|
||||
Insert Media
|
||||
<div className="flex gap-[5px] items-center">
|
||||
<div>
|
||||
<svg
|
||||
className="!text-primary"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] text-primary">
|
||||
Insert Media
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={designMedia}
|
||||
className="ml-[10px] rounded-[4px] mb-[10px] gap-[8px] justify-center items-center w-[127px] flex border border-dashed border-customColor21 !bg-customColor45"
|
||||
className="ml-[10px] rounded-[4px] mb-[10px] gap-[8px] !text-primary justify-center items-center w-[127px] flex border border-dashed border-customColor21 bg-input"
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] !text-white">
|
||||
Design Media
|
||||
<div className="flex gap-[5px] items-center">
|
||||
<div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-[12px] font-[500] !text-white">
|
||||
Design Media
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{!!user?.tier?.ai && (
|
||||
<AiImage
|
||||
value={text}
|
||||
onChange={changeMedia}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!!currentMedia &&
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ export class MediaService {
|
|||
private _subscriptionService: SubscriptionService
|
||||
){}
|
||||
|
||||
async generateImage(prompt: string, org: Organization) {
|
||||
const image = await this._openAi.generateImage(prompt);
|
||||
async generateImage(prompt: string, org: Organization, generatePromptFirst?: boolean) {
|
||||
if (generatePromptFirst) {
|
||||
prompt = await this._openAi.generatePromptForPicture(prompt);
|
||||
console.log('Prompt:', prompt);
|
||||
}
|
||||
const image = await this._openAi.generateImage(prompt, !!generatePromptFirst);
|
||||
await this._subscriptionService.useCredit(org);
|
||||
return image;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ export class NotificationService {
|
|||
}
|
||||
}
|
||||
|
||||
async sendEmail(to: string, subject: string, html: string) {
|
||||
await this._emailService.sendEmail(to, subject, html);
|
||||
async sendEmail(to: string, subject: string, html: string, replyTo?: string) {
|
||||
await this._emailService.sendEmail(to, subject, html, replyTo);
|
||||
}
|
||||
|
||||
hasEmailProvider() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
export interface EmailInterface {
|
||||
name: string;
|
||||
validateEnvKeys: string[];
|
||||
sendEmail(to: string, subject: string, html: string, emailFromName: string, emailFromAddress: string): Promise<any>;
|
||||
}
|
||||
sendEmail(
|
||||
to: string,
|
||||
subject: string,
|
||||
html: string,
|
||||
emailFromName: string,
|
||||
emailFromAddress: string,
|
||||
replyTo?: string
|
||||
): Promise<any>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,20 @@ const resend = new Resend(process.env.RESEND_API_KEY || 're_132');
|
|||
export class ResendProvider implements EmailInterface {
|
||||
name = 'resend';
|
||||
validateEnvKeys = ['RESEND_API_KEY'];
|
||||
async sendEmail(to: string, subject: string, html: string, emailFromName: string, emailFromAddress: string) {
|
||||
async sendEmail(
|
||||
to: string,
|
||||
subject: string,
|
||||
html: string,
|
||||
emailFromName: string,
|
||||
emailFromAddress: string,
|
||||
replyTo?: string
|
||||
) {
|
||||
const sends = await resend.emails.send({
|
||||
from: `${emailFromName} <${emailFromAddress}>`,
|
||||
to,
|
||||
subject,
|
||||
html,
|
||||
...(replyTo && { reply_to: replyTo }),
|
||||
});
|
||||
|
||||
return sends;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,50 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import OpenAI from 'openai';
|
||||
import { shuffle } from 'lodash';
|
||||
import { zodResponseFormat } from 'openai/helpers/zod';
|
||||
import { z } from 'zod';
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: process.env.OPENAI_API_KEY || 'sk-proj-',
|
||||
});
|
||||
|
||||
const PicturePrompt = z.object({
|
||||
prompt: z.string(),
|
||||
});
|
||||
|
||||
@Injectable()
|
||||
export class OpenaiService {
|
||||
async generateImage(prompt: string) {
|
||||
return (await openai.images.generate({
|
||||
prompt,
|
||||
response_format: 'b64_json',
|
||||
model: 'dall-e-3',
|
||||
})).data[0].b64_json;
|
||||
async generateImage(prompt: string, isUrl: boolean) {
|
||||
const generate = (
|
||||
await openai.images.generate({
|
||||
prompt,
|
||||
response_format: isUrl ? 'url' : 'b64_json',
|
||||
model: 'dall-e-3',
|
||||
})
|
||||
).data[0];
|
||||
|
||||
return isUrl ? generate.url : generate.b64_json;
|
||||
}
|
||||
|
||||
async generatePromptForPicture(prompt: string) {
|
||||
return (
|
||||
(
|
||||
await openai.beta.chat.completions.parse({
|
||||
model: 'gpt-4o-2024-08-06',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are an assistant that take a description and style and generate a prompt that will be used later to generate images, make it a very long and descriptive explanation, and write a lot of things for the renderer like, if it${"'"}s realistic describe the camera`,
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `prompt: ${prompt}`,
|
||||
},
|
||||
],
|
||||
response_format: zodResponseFormat(PicturePrompt, 'picturePrompt'),
|
||||
})
|
||||
).choices[0].message.parsed?.prompt || ''
|
||||
);
|
||||
}
|
||||
|
||||
async generatePosts(content: string) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export class EmailService {
|
|||
}
|
||||
}
|
||||
|
||||
async sendEmail(to: string, subject: string, html: string) {
|
||||
async sendEmail(to: string, subject: string, html: string, replyTo?: string) {
|
||||
if (!process.env.EMAIL_FROM_ADDRESS || !process.env.EMAIL_FROM_NAME) {
|
||||
console.log(
|
||||
'Email sender information not found in environment variables'
|
||||
|
|
@ -96,7 +96,8 @@ export class EmailService {
|
|||
subject,
|
||||
modifiedHtml,
|
||||
process.env.EMAIL_FROM_NAME,
|
||||
process.env.EMAIL_FROM_ADDRESS
|
||||
process.env.EMAIL_FROM_ADDRESS,
|
||||
replyTo
|
||||
);
|
||||
console.log(sends);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue