From 1e5385b76def36609e10d162707254e9a7760030 Mon Sep 17 00:00:00 2001 From: Santthosh Selvadurai Date: Mon, 24 Jun 2024 23:11:06 -0700 Subject: [PATCH] #93 Preparing for the marketplace --- .../migration.sql | 2 + prisma/schema.prisma | 41 +++++------ .../assistants/[id]/authentication/route.ts | 62 ++++++++++++++++ src/app/api/assistants/[id]/route.ts | 3 + src/app/api/assistants/route.ts | 4 ++ src/app/assistants/ListAssistants.tsx | 22 ++++-- src/app/assistants/[id]/chat/ChatPage.tsx | 5 +- src/app/assistants/[id]/client.ts | 15 ++++ src/app/assistants/[id]/settings/page.tsx | 72 ++++++++++++++++--- src/app/types/assistant.ts | 1 + 10 files changed, 191 insertions(+), 36 deletions(-) create mode 100644 prisma/migrations/20240625053303_adding_flag_for_authenticated_users_only_flag/migration.sql create mode 100644 src/app/api/assistants/[id]/authentication/route.ts diff --git a/prisma/migrations/20240625053303_adding_flag_for_authenticated_users_only_flag/migration.sql b/prisma/migrations/20240625053303_adding_flag_for_authenticated_users_only_flag/migration.sql new file mode 100644 index 0000000..1e31259 --- /dev/null +++ b/prisma/migrations/20240625053303_adding_flag_for_authenticated_users_only_flag/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Assistant" ADD COLUMN "authenticatedUsersOnly" BOOLEAN DEFAULT true; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index da7a907..523a6f5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -56,26 +56,27 @@ model ModelProviderKey { } model Assistant { - id String @id - modelId String? - model Model? @relation(fields: [modelId], references: [id]) - modelProviderId String? - modelProvider ModelProvider? @relation(fields: [modelProviderId], references: [id]) - object Json? - organizationOwner String? - organizationOwnerType String? - organization Organization? @relation(fields: [organizationOwner, organizationOwnerType], references: [owner, ownerType]) - avatar String? - profile String? - theme Json? - created_at DateTime @default(now()) - updated_at DateTime @updatedAt - Thread Thread[] - Folder Folder[] - File File[] - published Boolean? @default(false) - modelProviderKeyId String? - modelProviderKey ModelProviderKey? @relation(fields: [modelProviderKeyId], references: [id]) + id String @id + modelId String? + model Model? @relation(fields: [modelId], references: [id]) + modelProviderId String? + modelProvider ModelProvider? @relation(fields: [modelProviderId], references: [id]) + object Json? + organizationOwner String? + organizationOwnerType String? + organization Organization? @relation(fields: [organizationOwner, organizationOwnerType], references: [owner, ownerType]) + avatar String? + profile String? + theme Json? + created_at DateTime @default(now()) + updated_at DateTime @updatedAt + Thread Thread[] + Folder Folder[] + File File[] + published Boolean? @default(false) + authenticatedUsersOnly Boolean? @default(true) + modelProviderKeyId String? + modelProviderKey ModelProviderKey? @relation(fields: [modelProviderKeyId], references: [id]) } model Thread { diff --git a/src/app/api/assistants/[id]/authentication/route.ts b/src/app/api/assistants/[id]/authentication/route.ts new file mode 100644 index 0000000..5a585b2 --- /dev/null +++ b/src/app/api/assistants/[id]/authentication/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, + authenticatedUsersOnly: 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 + authenticatedUsersOnly: !assistant.authenticatedUsersOnly, + }, + }); + 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/[id]/route.ts b/src/app/api/assistants/[id]/route.ts index 144a4bb..dc7f187 100644 --- a/src/app/api/assistants/[id]/route.ts +++ b/src/app/api/assistants/[id]/route.ts @@ -26,6 +26,7 @@ export async function GET(req: NextRequest, res: NextResponse) { profile: true, published: true, theme: true, + authenticatedUsersOnly: true, }, }); @@ -64,6 +65,8 @@ export async function GET(req: NextRequest, res: NextResponse) { : null; // @ts-ignore assistant.object.published = assistant.published; + // @ts-ignore + assistant.object.authenticatedUsersOnly = assistant.authenticatedUsersOnly; } return Response.json(assistant.object, { status: 200 }); diff --git a/src/app/api/assistants/route.ts b/src/app/api/assistants/route.ts index 4f1c2bc..224dd91 100644 --- a/src/app/api/assistants/route.ts +++ b/src/app/api/assistants/route.ts @@ -19,6 +19,7 @@ export async function GET(req: NextRequest, res: NextResponse) { profile: true, modelId: true, published: true, + authenticatedUsersOnly: true, }, }); let assistantsCollection = assistants.map((assistant) => { @@ -29,6 +30,9 @@ export async function GET(req: NextRequest, res: NextResponse) { assistant.object.modelId = assistant.modelId; // @ts-ignore assistant.object.published = assistant.published; + // @ts-ignore + assistant.object.authenticatedUsersOnly = + assistant.authenticatedUsersOnly; } return assistant.object; }); diff --git a/src/app/assistants/ListAssistants.tsx b/src/app/assistants/ListAssistants.tsx index e64b9e1..8147727 100644 --- a/src/app/assistants/ListAssistants.tsx +++ b/src/app/assistants/ListAssistants.tsx @@ -53,7 +53,7 @@ export default function ListAssistants() { Create Assistant -
+
- +
+ -
+
{assistant.modelId} {assistant.published ? 'Public' : 'Private'} @@ -98,9 +104,13 @@ export default function ListAssistants() { {assistant.description} -
+
- diff --git a/src/app/assistants/[id]/chat/ChatPage.tsx b/src/app/assistants/[id]/chat/ChatPage.tsx index 844aaf5..c6f5ed6 100644 --- a/src/app/assistants/[id]/chat/ChatPage.tsx +++ b/src/app/assistants/[id]/chat/ChatPage.tsx @@ -42,7 +42,10 @@ export default function ChatPage() { }, [streamText]); return ( -
+
{ + let shouldAuth = !authenticationRequired; + if (assistant && assistant.id) { + let [status, response] = await updateAuthenticationRequirement( + assistant.id + ); + if (status === 200) { + let message = + 'Assistant ' + + assistant.name + + ' now requires users to be authenticated.'; + + if (!shouldAuth) { + message = + 'Assistant ' + assistant.name + ' now supports anonymous users.'; + } + // @ts-ignore + setAuthenticationRequired(shouldAuth); + + toast.success(message, { + duration: 4000, + }); + } else { + toast.error('Assistant ' + assistant.name + ' could not be updated.', { + duration: 4000, + }); + } + } + }; + 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.'; + 'Assistant ' + assistant.name + ' now available in marketplace.'; if (!visibility) { - message = 'Assistant ' + assistant.name + ' removed from store.'; + message = + 'Assistant ' + assistant.name + ' removed from marketplace.'; } setPublish(visibility); @@ -39,7 +75,7 @@ export default function Settings() { duration: 4000, }); } else { - toast.error('Assistant ' + assistant.name + ' could not be deleted.', { + toast.error('Assistant ' + assistant.name + ' could not be updated.', { duration: 4000, }); } @@ -85,17 +121,35 @@ export default function Settings() {
-

Store

+

Access Controls

- Public (Available for Everyone) + Requires Authentication +

+

+ When enabled this assistant will require users to + authenticate. +

+
+
+ +
+
+ +
+

+ Publish in Marketplace

-

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

+ When enabled this assistant will be visible on the + marketplace.

@@ -114,7 +168,7 @@ export default function Settings() {

Delete this assistant

-

+

Once you delete this assistant, there is no going back. Please be certain.

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