diff --git a/prisma/migrations/20240622051714_adding_the_published_flag/migration.sql b/prisma/migrations/20240622051714_adding_the_published_flag/migration.sql new file mode 100644 index 0000000..19c8efe --- /dev/null +++ b/prisma/migrations/20240622051714_adding_the_published_flag/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Assistant" ADD COLUMN "published" BOOLEAN DEFAULT false; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e432e69..da7a907 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -73,6 +73,7 @@ model Assistant { Thread Thread[] Folder Folder[] File File[] + published Boolean? @default(false) modelProviderKeyId String? modelProviderKey ModelProviderKey? @relation(fields: [modelProviderKeyId], references: [id]) } diff --git a/src/app/api/assistants/[id]/route.ts b/src/app/api/assistants/[id]/route.ts index f8e518c..144a4bb 100644 --- a/src/app/api/assistants/[id]/route.ts +++ b/src/app/api/assistants/[id]/route.ts @@ -24,6 +24,7 @@ export async function GET(req: NextRequest, res: NextResponse) { modelProviderKey: true, avatar: true, profile: true, + published: true, theme: true, }, }); @@ -35,8 +36,6 @@ export async function GET(req: NextRequest, res: NextResponse) { ); } - console.log(assistant.modelProviderKey); - // Inject customization properties into the assistant object if (assistant.object) { // @ts-ignore @@ -63,6 +62,8 @@ export async function GET(req: NextRequest, res: NextResponse) { name: assistant.modelProviderKey.name, } : null; + // @ts-ignore + assistant.object.published = assistant.published; } return Response.json(assistant.object, { status: 200 }); diff --git a/src/app/api/assistants/[id]/visibility/route.ts b/src/app/api/assistants/[id]/visibility/route.ts new file mode 100644 index 0000000..3d7ae28 --- /dev/null +++ b/src/app/api/assistants/[id]/visibility/route.ts @@ -0,0 +1,62 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/app/api/utils/prisma'; +import { getSession } from '@auth0/nextjs-auth0'; + +const getId = (req: Request) => { + const url = new URL(req.url); + return url.pathname.split('/').splice(-2, 1)[0]; +}; + +export async function PUT(req: NextRequest, res: NextResponse) { + const session = await getSession(); + const id = getId(req); + + try { + if (session?.user) { + let organization = await prisma.organization.findFirst({ + where: { + owner: session?.user.sub, + ownerType: 'personal', + }, + }); + + if (organization) { + let assistant = await prisma.assistant.findFirst({ + where: { + id: id, + }, + select: { + id: true, + organization: true, + published: true, + }, + }); + + // @ts-ignore + if (!assistant || assistant.organization.id !== organization.id) { + return NextResponse.json({ message: 'Unauthorized' }, { + status: 401, + } as any); + } else { + await prisma.assistant.update({ + where: { + id: assistant.id, + }, + data: { + // @ts-ignore + published: !assistant.published, + }, + }); + return NextResponse.json( + { message: 'Update successful' }, + { status: 200 } + ); + } + } + } + } catch (error) { + return NextResponse.json({ message: (error as Error).message }, { + status: 400, + } as any); + } +} diff --git a/src/app/api/assistants/route.ts b/src/app/api/assistants/route.ts index 1ef2d68..c33b2d9 100644 --- a/src/app/api/assistants/route.ts +++ b/src/app/api/assistants/route.ts @@ -18,6 +18,7 @@ export async function GET(req: NextRequest, res: NextResponse) { object: true, profile: true, modelId: true, + published: true, }, }); let assistantsCollection = assistants.map((assistant) => { @@ -94,6 +95,7 @@ export async function POST(req: NextRequest, res: NextResponse) { organizationOwner: session?.user.sub, organizationOwnerType: 'personal', object: createResponse as any, + published: true, }, }); diff --git a/src/app/api/auth/[auth0]/route.ts b/src/app/api/auth/[auth0]/route.ts index 303bdaf..31e8488 100644 --- a/src/app/api/auth/[auth0]/route.ts +++ b/src/app/api/auth/[auth0]/route.ts @@ -2,6 +2,6 @@ import { handleAuth, handleLogin } from '@auth0/nextjs-auth0'; export const GET = handleAuth({ login: handleLogin({ - returnTo:'/assistants' - }) + returnTo: '/assistants', + }), }); diff --git a/src/app/assistants/ListAssistants.tsx b/src/app/assistants/ListAssistants.tsx index e1d4de1..e31da82 100644 --- a/src/app/assistants/ListAssistants.tsx +++ b/src/app/assistants/ListAssistants.tsx @@ -79,7 +79,7 @@ export default function ListAssistants() { {assistant.modelId} - + {assistant.description}
diff --git a/src/app/assistants/[id]/SideNavigation.tsx b/src/app/assistants/[id]/SideNavigation.tsx index d4c3c1c..c13f686 100644 --- a/src/app/assistants/[id]/SideNavigation.tsx +++ b/src/app/assistants/[id]/SideNavigation.tsx @@ -8,6 +8,7 @@ import { HiCog, HiChartBar, HiPuzzle, + HiShoppingBag, } from 'react-icons/hi'; import { Assistant } from '@/app/types/assistant'; import Image from 'next/image'; diff --git a/src/app/assistants/[id]/client.ts b/src/app/assistants/[id]/client.ts index b504074..6ba60ca 100644 --- a/src/app/assistants/[id]/client.ts +++ b/src/app/assistants/[id]/client.ts @@ -279,6 +279,21 @@ export async function getMessageMetrics(request: MetricsRequest) { return [response.status, await response.json()]; } +export async function updateVisibilityStatus(id: string | undefined) { + if (!id) { + return [400, { error: 'Assistant ID is required' }]; + } + + let response = await fetch('/api/assistants/' + id + '/visibility', { + method: 'PUT', + headers: { + accept: 'application.json', + }, + }); + + return [response.status, await response.json()]; +} + export async function uploadFile(assistantId: string, file: File) { const formData = new FormData(); formData.append('file', file); diff --git a/src/app/assistants/[id]/settings/page.tsx b/src/app/assistants/[id]/settings/page.tsx index 252ffed..83d078d 100644 --- a/src/app/assistants/[id]/settings/page.tsx +++ b/src/app/assistants/[id]/settings/page.tsx @@ -1,9 +1,12 @@ 'use client'; -import { Button, Modal, Table } from 'flowbite-react'; -import React, { useContext, useState } from 'react'; +import { Button, Modal, Table, ToggleSwitch } from 'flowbite-react'; +import React, { useCallback, useContext, useState } from 'react'; import { HiOutlineExclamationCircle } from 'react-icons/hi'; -import { deleteAssistant } from '@/app/assistants/[id]/client'; +import { + deleteAssistant, + updateVisibilityStatus, +} from '@/app/assistants/[id]/client'; import { toast } from 'react-hot-toast'; import { useRouter } from 'next/navigation'; import EditAssistant from '@/app/assistants/[id]/settings/EditAssistant'; @@ -12,9 +15,37 @@ import AssistantContext from '@/app/assistants/[id]/AssistantContext'; export default function Settings() { const [openModal, setOpenModal] = useState(false); const { assistant } = useContext(AssistantContext); + console.log(assistant.published); + const [publish, setPublish] = useState( + assistant.published ? assistant.published : false + ); const { push } = useRouter(); + const toggleVisibility = async () => { + let visibility = !publish; + if (assistant && assistant.id) { + let [status, response] = await updateVisibilityStatus(assistant.id); + if (status === 200) { + let message = + 'Assistant ' + assistant.name + ' now available in store.'; + + if (!visibility) { + message = 'Assistant ' + assistant.name + ' removed from store.'; + } + setPublish(visibility); + + toast.success(message, { + duration: 4000, + }); + } else { + toast.error('Assistant ' + assistant.name + ' could not be deleted.', { + duration: 4000, + }); + } + } + }; + const handleAssistantDelete = async () => { if (assistant && assistant.id) { let [status, response] = await deleteAssistant(assistant.id); @@ -42,20 +73,45 @@ export default function Settings() { Adjust the original configuration of your assistant here

- +
+ +
+

General Settings

+
+
-

Danger Zone

+

Store

+
+
+ + +
+

+ Available for Everyone +

+

+ When enabled this assistant will be available on the store. +

+
+
+ +
+
+
+ +
+

Danger Zone

-

+

Delete this assistant

@@ -63,14 +119,15 @@ export default function Settings() { Please be certain.

- +
+ +
diff --git a/src/app/types/assistant.ts b/src/app/types/assistant.ts index 5dc98be..dcc2431 100644 --- a/src/app/types/assistant.ts +++ b/src/app/types/assistant.ts @@ -30,4 +30,5 @@ export interface Assistant { avatar?: string | null; profile?: string | null; theme?: AssistantTheme | null; + published?: boolean; }