Skip to content

Commit

Permalink
#93 Preparing for the marketplace
Browse files Browse the repository at this point in the history
  • Loading branch information
santthosh committed Jun 25, 2024
1 parent b70c56a commit 1e5385b
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Assistant" ADD COLUMN "authenticatedUsersOnly" BOOLEAN DEFAULT true;
41 changes: 21 additions & 20 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
62 changes: 62 additions & 0 deletions src/app/api/assistants/[id]/authentication/route.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
3 changes: 3 additions & 0 deletions src/app/api/assistants/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export async function GET(req: NextRequest, res: NextResponse) {
profile: true,
published: true,
theme: true,
authenticatedUsersOnly: true,
},
});

Expand Down Expand Up @@ -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 });
Expand Down
4 changes: 4 additions & 0 deletions src/app/api/assistants/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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;
});
Expand Down
22 changes: 16 additions & 6 deletions src/app/assistants/ListAssistants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function ListAssistants() {
<HiPlus className='mr-2 h-5 w-5' /> Create Assistant
</Button>
</div>
<div className='bg-grey m-5 mt-10 flex grid flex-auto items-start justify-center'>
<div className='bg-grey mb-5 mt-10 mt-5 flex grid flex-auto items-start justify-center'>
<div
className='4xl:grid-cols-4 grid
grid-cols-1
Expand All @@ -67,8 +67,14 @@ export default function ListAssistants() {
assistants.length &&
assistants.map((assistant, index) => {
return (
<div className={'flex max-w-xs'} key={index}>
<Card imgSrc={'/images/backgrounds/1.jpg'}>
<div
className={'max-w-xs bg-gray-100 tracking-tight'}
key={index}
>
<Card
imgSrc={'/images/backgrounds/1.jpg'}
className={'bg-gray-80'}
>
<Avatar
img={
assistant.avatar
Expand All @@ -88,7 +94,7 @@ export default function ListAssistants() {
{assistant.name}
</h5>
<span className='text-sm text-gray-500 dark:text-gray-400'>
<div className='flex self-center'>
<div className='flex gap-1 self-center'>
<Badge color='gray'>{assistant.modelId}</Badge>
<Badge color={assistant.published ? 'green' : 'red'}>
{assistant.published ? 'Public' : 'Private'}
Expand All @@ -98,9 +104,13 @@ export default function ListAssistants() {
<span className='max-h-12 min-h-12 max-w-xs overflow-y-hidden pt-4 text-center text-sm text-xs text-gray-500 dark:text-gray-400'>
{assistant.description}
</span>
<div className='mt-4 grid items-center justify-center gap-2 xs:grid-cols-1 sm:grid-cols-2 lg:mt-6'>
<div className='mt-4 flex items-center justify-center gap-1'>
<Link href={`/link/${assistant.id}`} target='_blank'>
<Button gradientDuoTone='greenToBlue'>
<Button
gradientDuoTone='greenToBlue'
className={'mr-2 w-[150px]'}
>
<HiChatAlt2 className={'h-5 w-5'} />
Try Assistant
</Button>
</Link>
Expand Down
5 changes: 4 additions & 1 deletion src/app/assistants/[id]/chat/ChatPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export default function ChatPage() {
}, [streamText]);

return (
<div key='1' className='flex h-screen flex-col'>
<div
key='1'
className='flex h-[calc(100vh-5rem)] max-h-full max-w-full flex-col'
>
<div
className={'flex space-y-4 pb-2 pt-1 '}
style={{
Expand Down
15 changes: 15 additions & 0 deletions src/app/assistants/[id]/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,21 @@ export async function updateVisibilityStatus(id: string | undefined) {
return [response.status, await response.json()];
}

export async function updateAuthenticationRequirement(id: string | undefined) {
if (!id) {
return [400, { error: 'Assistant ID is required' }];
}

let response = await fetch('/api/assistants/' + id + '/authentication', {
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);
Expand Down
72 changes: 63 additions & 9 deletions src/app/assistants/[id]/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, { useCallback, useContext, useState } from 'react';
import { HiOutlineExclamationCircle } from 'react-icons/hi';
import {
deleteAssistant,
updateAuthenticationRequirement,
updateVisibilityStatus,
} from '@/app/assistants/[id]/client';
import { toast } from 'react-hot-toast';
Expand All @@ -15,31 +16,66 @@ 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 [authenticationRequired, setAuthenticationRequired] = useState(
assistant.authenticatedUsersOnly ? assistant.authenticatedUsersOnly : true
);

const { push } = useRouter();

const toggleAuthenticationRequirement = async () => {
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);

toast.success(message, {
duration: 4000,
});
} else {
toast.error('Assistant ' + assistant.name + ' could not be deleted.', {
toast.error('Assistant ' + assistant.name + ' could not be updated.', {
duration: 4000,
});
}
Expand Down Expand Up @@ -85,17 +121,35 @@ export default function Settings() {
</Table.Row>
<Table.Row className='bg-white'>
<div className='flex max-w-7xl flex-col gap-4'>
<h1 className='p-2 pl-5 text-xl font-bold'>Store</h1>
<h1 className='p-2 pl-5 text-xl font-bold'>Access Controls</h1>
</div>
</Table.Row>
<Table.Row className='bg-white'>
<Table.Cell className='flex max-w-7xl flex-row gap-4 p-5 font-medium text-gray-900 dark:text-white'>
<div className='p2 flex max-w-md flex-col items-start'>
<h2 className='p2 text-lg text-gray-700'>
Public (Available for Everyone)
Requires Authentication
</h2>
<h3 className='p2 text-xs text-gray-400'>
When enabled this assistant will require users to
authenticate.
</h3>
</div>
<div className='p2 ml-auto flex max-w-md items-center justify-center'>
<ToggleSwitch
checked={authenticationRequired}
onChange={toggleAuthenticationRequirement}
/>
</div>
</Table.Cell>
<Table.Cell className='flex max-w-7xl flex-row gap-4 p-5 font-medium text-gray-900 dark:text-white'>
<div className='p2 flex max-w-md flex-col items-start'>
<h2 className='p2 text-lg text-gray-700'>
Publish in Marketplace
</h2>
<h3 className='p2 text-gray-400'>
When enabled this assistant will be available on the store.
<h3 className='p2 text-xs text-gray-400'>
When enabled this assistant will be visible on the
marketplace.
</h3>
</div>
<div className='p2 ml-auto flex max-w-md items-center justify-center'>
Expand All @@ -114,7 +168,7 @@ export default function Settings() {
<h2 className='p2 text-lg text-gray-700'>
Delete this assistant
</h2>
<h3 className='p2 text-gray-400'>
<h3 className='p2 text-xs text-gray-400'>
Once you delete this assistant, there is no going back.
Please be certain.
</h3>
Expand Down
1 change: 1 addition & 0 deletions src/app/types/assistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ export interface Assistant {
profile?: string | null;
theme?: AssistantTheme | null;
published?: boolean;
authenticatedUsersOnly?: boolean;
}

0 comments on commit 1e5385b

Please sign in to comment.