From 37f3645f0274be28cdf428afec9b78646e4536be Mon Sep 17 00:00:00 2001 From: Alex Whiteside Date: Mon, 23 Sep 2024 10:03:07 -0400 Subject: [PATCH] Auth Works --- biome.json | 2 +- packages/chat/package.json | 1 + .../collections/createMessageCollection.ts | 60 +++++-- packages/chat/src/payload-types.ts | 146 ++++++++++++++++++ packages/chat/src/payload.config.ts | 17 ++ packages/chat/src/payload/getUser.ts | 9 ++ packages/chat/src/plugin/ChatPlugin.ts | 12 +- packages/chat/src/ui/chat/Input.client.tsx | 27 ++++ packages/chat/src/ui/chat/Message.tsx | 15 +- packages/chat/src/ui/chat/index.tsx | 23 ++- .../chat/src/ui/chat/insertMessage.action.ts | 42 +++++ packages/chat/src/ui/chat/types.ts | 4 - packages/chat/src/ui/hooks/getConversation.ts | 28 ++++ .../chat/src/ui/hooks/queryConversations.ts | 14 ++ packages/chat/src/ui/page/ChatView.tsx | 47 ++---- .../chat/src/ui/page/ConversationsList.tsx | 21 ++- packages/chat/src/ui/page/parseParam.ts | 14 ++ packages/chat/tsconfig.json | 3 +- playground/db/playground.db | Bin 1036288 -> 0 bytes playground/src/payload-types.ts | 25 ++- playground/src/payload.config.ts | 1 + 21 files changed, 407 insertions(+), 104 deletions(-) create mode 100644 packages/chat/src/payload-types.ts create mode 100644 packages/chat/src/payload.config.ts create mode 100644 packages/chat/src/payload/getUser.ts create mode 100644 packages/chat/src/ui/chat/Input.client.tsx create mode 100644 packages/chat/src/ui/chat/insertMessage.action.ts delete mode 100644 packages/chat/src/ui/chat/types.ts create mode 100644 packages/chat/src/ui/hooks/getConversation.ts create mode 100644 packages/chat/src/ui/hooks/queryConversations.ts create mode 100644 packages/chat/src/ui/page/parseParam.ts delete mode 100644 playground/db/playground.db diff --git a/biome.json b/biome.json index 7dc6079..b6bc16c 100644 --- a/biome.json +++ b/biome.json @@ -1,7 +1,7 @@ { "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", "files": { - "ignore": ["playground/**"], + "ignore": ["playground/**", "**/payload-types.ts"], "ignoreUnknown": true }, "vcs": { diff --git a/packages/chat/package.json b/packages/chat/package.json index a9868a0..a19e664 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -26,6 +26,7 @@ "build:ui": "vite build", "build:plugin": "unbuild --sourcemaps", "build": "pnpm build:plugin && pnpm build:ui", + "generate": "payload generate:types", "tailwind": "npx tailwindcss -i ./src/ui/tailwind.css -o ./dist/ui/tailwind.css" }, "dependencies": { diff --git a/packages/chat/src/collections/createMessageCollection.ts b/packages/chat/src/collections/createMessageCollection.ts index c93196d..187f540 100644 --- a/packages/chat/src/collections/createMessageCollection.ts +++ b/packages/chat/src/collections/createMessageCollection.ts @@ -1,7 +1,14 @@ import type {ChatPluginConfig} from '../plugin/types' -import type {ArrayField, CollectionConfig} from 'payload' +import type {ArrayField, CollectionConfig, FieldHook} from 'payload' import {get} from 'radash' +const setSentDate: FieldHook< + { id: string }, + Array<{ sent: Date; id: string }> +> = ({ data, value, operation }) => { + return (value ?? []).map((m) => ({ ...m, sent: m.sent ?? new Date() })) +} + export const createMessageCollection = ( config: ChatPluginConfig['generatedCollections']['chats'], ): CollectionConfig => { @@ -10,18 +17,33 @@ export const createMessageCollection = ( const messagesField: ArrayField = { name: 'messages', type: 'array', + required: true, + defaultValue: [], + hooks: { + beforeChange: [setSentDate], + }, fields: [ - { name: 'sent', type: 'date', required: true }, - { name: 'text', type: 'text', required: true }, { - name: 'role', - type: 'select', - options: ['assistant', 'user'], - required: true, + type: 'row', + fields: [ + { + name: 'sent', + type: 'date', + admin: { date: { pickerAppearance: 'dayAndTime' }, readOnly: true }, + }, + { + name: 'role', + type: 'select', + options: ['assistant', 'user'], + required: true, + }, + ], }, + { name: 'text', type: 'text', required: true }, { - name: 'context', - type: 'group', + label: 'context', + type: 'collapsible', + admin: { initCollapsed: true }, fields: [ { name: 'collection', type: 'text' }, { name: 'view', type: 'select', options: ['list', 'record'] }, @@ -31,19 +53,29 @@ export const createMessageCollection = ( } return { + access: { + read: ({ req, data }) => { + if (!req.user) return false + if (!data) return true + return { + 'user.email': { equals: req.user.email }, + } + }, + }, fields: [ messagesField, { name: 'user', type: 'relationship', - relationTo: ['users'], + relationTo: 'users', + hasMany: false, required: true, }, { - name:'description', - type:'text', - defaultValue:'A Recent Chat' - } + name: 'description', + type: 'text', + defaultValue: 'A Recent Chat', + }, ], slug, } diff --git a/packages/chat/src/payload-types.ts b/packages/chat/src/payload-types.ts new file mode 100644 index 0000000..73a5963 --- /dev/null +++ b/packages/chat/src/payload-types.ts @@ -0,0 +1,146 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * This file was automatically generated by Payload. + * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, + * and re-run `payload generate:types` to regenerate this file. + */ + +export interface Config { + auth: { + users: UserAuthOperations; + }; + collections: { + chats: Chat; + users: User; + 'payload-locked-documents': PayloadLockedDocument; + 'payload-preferences': PayloadPreference; + 'payload-migrations': PayloadMigration; + }; + db: { + defaultIDType: string; + }; + globals: {}; + locale: null; + user: User & { + collection: 'users'; + }; +} +export interface UserAuthOperations { + forgotPassword: { + email: string; + password: string; + }; + login: { + email: string; + password: string; + }; + registerFirstUser: { + email: string; + password: string; + }; + unlock: { + email: string; + password: string; + }; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "chats". + */ +export interface Chat { + messages: { + sent?: string | null; + role: 'assistant' | 'user'; + text: string; + collection?: string | null; + view?: ('list' | 'record') | null; + id?: string | null; + }[]; + user: number | User; + description?: string | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "users". + */ +export interface User { + updatedAt: string; + createdAt: string; + email: string; + resetPasswordToken?: string | null; + resetPasswordExpiration?: string | null; + salt?: string | null; + hash?: string | null; + loginAttempts?: number | null; + lockUntil?: string | null; + password?: string | null; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "payload-locked-documents". + */ +export interface PayloadLockedDocument { + document?: + | ({ + relationTo: 'chats'; + value: number | Chat; + } | null) + | ({ + relationTo: 'users'; + value: number | User; + } | null); + globalSlug?: string | null; + user: { + relationTo: 'users'; + value: number | User; + }; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "payload-preferences". + */ +export interface PayloadPreference { + user: { + relationTo: 'users'; + value: number | User; + }; + key?: string | null; + value?: + | { + [k: string]: unknown; + } + | unknown[] + | string + | number + | boolean + | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "payload-migrations". + */ +export interface PayloadMigration { + name?: string | null; + batch?: number | null; + updatedAt: string; + createdAt: string; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "auth". + */ +export interface Auth { + [k: string]: unknown; +} + + +declare module 'payload' { + export interface GeneratedTypes extends Config {} +} \ No newline at end of file diff --git a/packages/chat/src/payload.config.ts b/packages/chat/src/payload.config.ts new file mode 100644 index 0000000..69634c6 --- /dev/null +++ b/packages/chat/src/payload.config.ts @@ -0,0 +1,17 @@ +import path from "node:path"; +import {fileURLToPath} from "node:url"; +import type {Config} from 'payload' +import {buildConfig} from 'payload' +import {chatPlugin} from "./plugin/ChatPlugin"; +const filename = fileURLToPath(import.meta.url) +const dirname = path.dirname(filename) + +export default buildConfig({ + collections: [], + db: {} as Config['db'], + secret: 'secret', + typescript: { + outputFile: path.resolve(dirname, 'payload-types.ts'), + }, + plugins:[chatPlugin({})] +}) diff --git a/packages/chat/src/payload/getUser.ts b/packages/chat/src/payload/getUser.ts new file mode 100644 index 0000000..bded8ac --- /dev/null +++ b/packages/chat/src/payload/getUser.ts @@ -0,0 +1,9 @@ +import {cookies, headers} from "next/headers"; +import payload, {extractJWT} from "payload"; + +export const getUser =()=>{ + + const jwt = extractJWT({headers: headers(), payload:payload, isGraphQL:false}) + + +} \ No newline at end of file diff --git a/packages/chat/src/plugin/ChatPlugin.ts b/packages/chat/src/plugin/ChatPlugin.ts index 7a47e67..73076f8 100644 --- a/packages/chat/src/plugin/ChatPlugin.ts +++ b/packages/chat/src/plugin/ChatPlugin.ts @@ -36,21 +36,19 @@ export const chatPlugin = components: { views: { chat: { - exact:true, - sensitive:false, - path:'/chat', + exact: false, + sensitive: false, + path: '/chat/:chat_id', Component: '@payload-llm-plugins/chat/ChatView', }, }, }, }, collections: [ - createMessageCollection( - pluginConfig.generatedCollections.chats, - ), + createMessageCollection(pluginConfig.generatedCollections.chats), ], } satisfies Partial) - console.log(updatedConfig.admin.components.views.chat) + const clientConfig = pluginConfig.modelClientConfig // @ts-ignore diff --git a/packages/chat/src/ui/chat/Input.client.tsx b/packages/chat/src/ui/chat/Input.client.tsx new file mode 100644 index 0000000..c3408b3 --- /dev/null +++ b/packages/chat/src/ui/chat/Input.client.tsx @@ -0,0 +1,27 @@ +'use client' +import * as React from 'react' +import {insertMessageAction} from './insertMessage.action' + +interface ChatId { + chatId?: string | number +} + +export const SendMessageUI = ({ chatId }: ChatId) => { + return ( +
+ + + + +
+ ) +} diff --git a/packages/chat/src/ui/chat/Message.tsx b/packages/chat/src/ui/chat/Message.tsx index a22fc86..cdd42b2 100644 --- a/packages/chat/src/ui/chat/Message.tsx +++ b/packages/chat/src/ui/chat/Message.tsx @@ -1,10 +1,17 @@ import * as React from 'react' -import type {Message} from './types' +import type {Chat} from '../../payload-types' -export const MessageElement = ({ message }: { message: Message }) => { +export const MessageElement = ({ + message, +}: { message: Chat['messages'][number] }) => { return ( -
-
+
+
{message.text}
diff --git a/packages/chat/src/ui/chat/index.tsx b/packages/chat/src/ui/chat/index.tsx index 6e5fe61..e02736e 100644 --- a/packages/chat/src/ui/chat/index.tsx +++ b/packages/chat/src/ui/chat/index.tsx @@ -1,29 +1,26 @@ -import * as React from "react"; +import * as React from 'react' import {MessageElement} from './Message' -import type {Message} from './types' +import type {PayloadRequest} from 'payload' +import {getChat} from '../hooks/getConversation' +import {SendMessageUI} from './Input.client' interface ChatProps { - // biome-ignore lint/suspicious/noConfusingVoidType: - chat:Promise<{messages: Message[]}|void> + req: PayloadRequest + chatId: string | number | undefined } -export const ChatUI = ({ chat }: ChatProps) => { - +export const ChatUI = ({ req, chatId }: ChatProps) => { + const chat = getChat(req, chatId) const messages = React.use(chat) - console.log(messages) return (
{messages?.messages?.map((message) => ( - + ))}
-
- - - -
+
) diff --git a/packages/chat/src/ui/chat/insertMessage.action.ts b/packages/chat/src/ui/chat/insertMessage.action.ts new file mode 100644 index 0000000..6571696 --- /dev/null +++ b/packages/chat/src/ui/chat/insertMessage.action.ts @@ -0,0 +1,42 @@ +import {getPayload} from 'payload' +import type {Chat, User} from '../../payload-types' +import {headers} from 'next/headers' +import config from '@payload-config' +import {get, isString} from 'radash' + +interface Props { + message: string + chatId?: string | number + user: User +} + +export async function insertMessageAction(data: FormData) { + 'use server' + + const instance = await getPayload({ config }) + const auth = await instance.auth({ headers: headers() }) + + const user = auth.user + + const message = data.get('message') + if (!user || !isString(message)) return + const chatId = data.get('chatId')?.toString() + + const chat = + chatId && + (await instance.findByID({ collection: 'chats', id: chatId, depth: 0 })) + if (chat) { + chat.messages.push({ text: message, role: 'user' }) + await instance.update({ + collection: 'chats', + data: chat, + id: chatId, + }) + } else { + const chat = { + messages: [{ role: 'user', text: message }], + user: get(user, 'id'), + } satisfies Partial + await instance.create({ collection: 'chats', data: chat, user }) + } +} diff --git a/packages/chat/src/ui/chat/types.ts b/packages/chat/src/ui/chat/types.ts deleted file mode 100644 index 7c71943..0000000 --- a/packages/chat/src/ui/chat/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Message { - role: 'assistant' | 'user' - text: string -} \ No newline at end of file diff --git a/packages/chat/src/ui/hooks/getConversation.ts b/packages/chat/src/ui/hooks/getConversation.ts new file mode 100644 index 0000000..c87028a --- /dev/null +++ b/packages/chat/src/ui/hooks/getConversation.ts @@ -0,0 +1,28 @@ +import type {PayloadRequest} from 'payload' +import type {Chat} from '../../payload-types' +import {isObject} from 'radash' + +export const getChat = async ( + req: PayloadRequest, + id: string | number | undefined | null, +): Promise> => { + if (!id) { + return Promise.resolve({ messages: [] }) + } + + const conversation = await req.payload.findByID({ + overrideAccess: false, + user: req.user, + id, + depth: 2, + collection: 'chats', + }) + if ( + conversation && + isObject(conversation.user) && + conversation.user.email === req.user?.email + ) { + return conversation + } + return { messages: [] } +} diff --git a/packages/chat/src/ui/hooks/queryConversations.ts b/packages/chat/src/ui/hooks/queryConversations.ts new file mode 100644 index 0000000..9c71b6c --- /dev/null +++ b/packages/chat/src/ui/hooks/queryConversations.ts @@ -0,0 +1,14 @@ +import type {PayloadRequest} from 'payload' +import type {Chat} from "../../payload-types"; + +export const queryChats = async (req: PayloadRequest):Promise => { + const records = await req.payload.find({ + overrideAccess: false, + user: req.user, + collection: 'chats', + where: { "user.email": { equals: req.user?.email } }, + sort: '-sent', + }) + + return records.docs +} diff --git a/packages/chat/src/ui/page/ChatView.tsx b/packages/chat/src/ui/page/ChatView.tsx index ef34573..389ed06 100644 --- a/packages/chat/src/ui/page/ChatView.tsx +++ b/packages/chat/src/ui/page/ChatView.tsx @@ -1,25 +1,21 @@ import '../tailwind.css' - -import type {AdminViewProps} from 'payload' -//@ts-ignore +import type {AdminViewProps} from 'payload' //@ts-ignore // biome-ignore lint/style/useImportType: -import * as React from 'react' -import {ReactNode, Suspense} from 'react' -import {DefaultTemplate} from '@payloadcms/next/templates' +import * as React from 'react' +import {type ReactNode, Suspense} from 'react' import {redirect} from 'next/navigation' import {ChatUI} from '../chat' import {ConversationsList} from './ConversationsList' +import {getParamValue} from './parseParam' +import {get} from "radash"; const ChatView: React.FC = ({ initPageResult, params, - searchParams, }: AdminViewProps): ReactNode => { - const { permissions, req: { - payload, payload: { config: { routes: { admin: adminRoute }, @@ -27,38 +23,15 @@ const ChatView: React.FC = ({ }, user, }, - visibleEntities, } = initPageResult if (!user || (user && !permissions?.canAccessAdmin)) { return redirect(`${adminRoute}/unauthorized`) } - const conversations = payload - .find({ collection: 'chats', where: { description:{exists:true} } }) - .then((chat) => { - return chat.docs.map((c) => ({ - id: c.id.toString(), - description: c.description, - })) - }).catch(console.error) - - const chatId = 1 - const chat = chatId - ? payload - .find({ - collection: 'chats', - where: { id: { equals: chatId } }, - }) - .then((chats) => { - console.log(chats) - const chat = chats.docs[0] - console.log(chat) + const chatId = get(params,'segments[1]', undefined) + console.log(chatId) - if (!chat) return { messages: [] } - return { ...chat, messages: chat.messages } - }).catch(console.error) - : Promise.resolve({ messages: [] }) return ( <> @@ -69,12 +42,12 @@ const ChatView: React.FC = ({
Loading

}> - +
@@ -83,4 +56,4 @@ const ChatView: React.FC = ({ ) } -export default ChatView \ No newline at end of file +export default ChatView diff --git a/packages/chat/src/ui/page/ConversationsList.tsx b/packages/chat/src/ui/page/ConversationsList.tsx index b58d1ac..203e60f 100644 --- a/packages/chat/src/ui/page/ConversationsList.tsx +++ b/packages/chat/src/ui/page/ConversationsList.tsx @@ -1,20 +1,27 @@ import * as React from 'react' -import { use } from "react"; +import {use} from 'react' +import {get} from 'radash' +import type {PayloadRequest} from 'payload' +import {queryChats} from '../hooks/queryConversations' interface ConversationsListProps { - // biome-ignore lint/suspicious/noConfusingVoidType: - conversations: Promise|void> + req: PayloadRequest + chatId: undefined | string | number } -export const ConversationsList = ({ - conversations, -}: ConversationsListProps) => { +export const ConversationsList = ({ req, chatId }: ConversationsListProps) => { + const conversations = queryChats(req) const convos = use(conversations) return (
{convos?.map((convo) => ( - +

{convo.description}

diff --git a/packages/chat/src/ui/page/parseParam.ts b/packages/chat/src/ui/page/parseParam.ts new file mode 100644 index 0000000..0b909a2 --- /dev/null +++ b/packages/chat/src/ui/page/parseParam.ts @@ -0,0 +1,14 @@ +import type {AdminViewProps} from 'payload' +import {first, isArray, isString} from 'radash' + +export const getParamValue = ( + params: AdminViewProps['params'], + key: string, +) => { + if (!params) return undefined + if (!params[key]) return undefined + const value = params[key] + if (isString(value)) return value + if (isArray(value)) return first(value) ?? undefined + return undefined +} diff --git a/packages/chat/tsconfig.json b/packages/chat/tsconfig.json index ca5d5eb..bb83e5d 100644 --- a/packages/chat/tsconfig.json +++ b/packages/chat/tsconfig.json @@ -3,7 +3,8 @@ "files": [], "compilerOptions": { "jsx": "react-jsx", - "target": "ES2022" + "target": "ES2022", + "paths": { "@payload-config": ["./src/payload.config.ts"] } }, "references": [ { "path": "./tsconfig.lib.json" }, diff --git a/playground/db/playground.db b/playground/db/playground.db deleted file mode 100644 index 9d42746a0476a790c92c9c0af2bfd73bb0076fd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1036288 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 = ({ doc }) => { } export default buildConfig({ + i18n:{fallbackLanguage:'en'}, admin: { components: { // The `BeforeLogin` component renders a message that you see while logging into your admin panel.