diff options
| -rw-r--r-- | apps/web/app/(dash)/home/filterSpaces.tsx | 37 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/heading.tsx | 28 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/page.tsx | 56 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/queryinput.tsx | 4 | ||||
| -rw-r--r-- | apps/web/app/actions/fetchers.ts | 30 | ||||
| -rw-r--r-- | apps/web/migrations/0001_nervous_longshot.sql | 2 | ||||
| -rw-r--r-- | apps/web/migrations/meta/0001_snapshot.json | 905 | ||||
| -rw-r--r-- | apps/web/migrations/meta/_journal.json | 31 | ||||
| -rw-r--r-- | apps/web/package.json | 1 | ||||
| -rw-r--r-- | apps/web/server/db/schema.ts | 3 | ||||
| -rwxr-xr-x | bun.lockb | bin | 1158164 -> 1158660 bytes |
11 files changed, 1045 insertions, 52 deletions
diff --git a/apps/web/app/(dash)/home/filterSpaces.tsx b/apps/web/app/(dash)/home/filterSpaces.tsx index d0e4fa1e..061923d8 100644 --- a/apps/web/app/(dash)/home/filterSpaces.tsx +++ b/apps/web/app/(dash)/home/filterSpaces.tsx @@ -76,30 +76,31 @@ export function FilterSpaces({ </div> <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 transition-opacity p-2 rounded-lg max-w-64 bg-[#2C3338]"> <CommandGroup className="pointer-events-none opacity-0 group-focus-within:opacity-100 scale-50 scale-y-50 group-focus-within:scale-y-100 group-focus-within:scale-100 group-focus-within:pointer-events-auto transition-all origin-top"> - {initialSpaces.map((space) => ( - <CommandItem - className="text-[#eaeaea] data-[disabled]:opacity-90" - value={space.name} - key={space.id} - onSelect={() => handleSelect(space)} - > - <Check - className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`} - /> - {space.name} - </CommandItem> - ))} + {initialSpaces.map((space) => { + if (!selectedSpaces.some((v) => v.id === space.id)) { + return ( + <CommandItem + className="text-[#eaeaea] data-[disabled]:opacity-90" + value={space.name} + key={space.id} + onSelect={() => handleSelect(space)} + > + <Check + className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`} + /> + {space.name} + </CommandItem> + ); + } + })} </CommandGroup> </CommandList> </Command> </div> - {/* <button + <button type="submit" - className="h-12 w-12 rounded-[14px] all-center shrink-0 hover:brightness-125 outline-none bg-[#369DFD1A] p-3 active:scale-90" + className="rounded-lg bg-[#369DFD1A] p-3 transition-colors" > - <Image src={ArrowRightIcon} alt="Right arrow icon" /> - </button> */} - <button type="submit" className="rounded-lg bg-[#369DFD1A] p-3 transition-colors"> <Image src={ArrowRightIcon} alt="Enter" /> </button> </div> diff --git a/apps/web/app/(dash)/home/heading.tsx b/apps/web/app/(dash)/home/heading.tsx index 75a83dae..e50f509b 100644 --- a/apps/web/app/(dash)/home/heading.tsx +++ b/apps/web/app/(dash)/home/heading.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { motion } from "framer-motion"; +import { AnimatePresence, motion } from "framer-motion"; const headings = [ "Unlock your digital brain", @@ -13,17 +13,23 @@ export function Heading({ queryPresent }: { queryPresent: boolean }) { const [showHeading, setShowHeading] = useState<number>(0); useEffect(() => { setShowHeading(Math.floor(Math.random() * headings.length)); - }, []); + }, [queryPresent]); return ( - <div className="h-[3.4rem] overflow-hidden text-white text-center"> - <motion.h1 - animate={{ opacity: queryPresent ? 0 : 1, y: queryPresent ? "20%" : 0 }} - className={`text-[2.45rem] font-semibold ${ - queryPresent ? "pointer-events-none" : "pointer-events-auto" - } transition-opacity`} - > - {headings[showHeading]} - </motion.h1> + <div className="h-[7rem] flex items-end justify-center overflow-hidden text-white"> + <AnimatePresence mode="popLayout"> + {!queryPresent && ( + <motion.h1 + initial={{ opacity: 0, y: "20%" }} + animate={{ opacity: 1, y: "0%" }} + exit={{ opacity: 0, y: "20%" }} + className={`text-[2.45rem] font-semibold ${ + queryPresent ? "pointer-events-none" : "pointer-events-auto" + } transition-opacity`} + > + {headings[showHeading]} + </motion.h1> + )} + </AnimatePresence> </div> ); } diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx index 84ecf242..7572056a 100644 --- a/apps/web/app/(dash)/home/page.tsx +++ b/apps/web/app/(dash)/home/page.tsx @@ -1,12 +1,18 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React, { Suspense, memo, use, useEffect, useState } from "react"; import QueryInput from "./queryinput"; -import { getSessionAuthToken, getSpaces } from "@/app/actions/fetchers"; +import { + getRecentChats, + getSessionAuthToken, + getSpaces, +} from "@/app/actions/fetchers"; import { useRouter } from "next/navigation"; import { createChatThread, linkTelegramToUser } from "@/app/actions/doers"; import { toast } from "sonner"; import { Heading } from "./heading"; +import { ArrowLongRightIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; const linkTelegram = async (telegramUser: string) => { const response = await linkTelegramToUser(telegramUser); @@ -23,7 +29,6 @@ function Page({ }: { searchParams: Record<string, string | string[] | undefined>; }) { - const { push } = useRouter(); const [spaces, setSpaces] = useState<{ id: number; name: string }[]>([]); @@ -32,7 +37,7 @@ function Page({ useEffect(() => { // telegram bot - const telegramUser = searchParams.extension as string + const telegramUser = searchParams.extension as string; if (telegramUser) { linkTelegram(telegramUser); } @@ -57,11 +62,11 @@ function Page({ }, []); return ( - <div className="max-w-3xl h-full justify-center flex mx-auto w-full flex-col px-2 md:px-0"> + <div className="max-w-3xl mt-[18vh] mx-auto w-full px-2 md:px-0"> <Heading queryPresent={queryPresent} /> - <div className="w-full pb-20 mt-12"> + <div className="w-full py-12"> <QueryInput - setQueryPresent={(t:boolean)=> setQueryPresent(t)} + setQueryPresent={(t: boolean) => setQueryPresent(t)} handleSubmit={async (q, spaces) => { if (q.length === 0) { toast.error("Query is required"); @@ -82,8 +87,45 @@ function Page({ initialSpaces={spaces} /> </div> + <History /> </div> ); } +const History = memo(() => { + const [chatThreads, setChatThreads] = useState(null); + + useEffect(() => { + (async () => { + const chatThreads = await getRecentChats(); + + setChatThreads(chatThreads); + })(); + }, []); + + if (!chatThreads){ + return <div>Loading</div>; + } + + if (!chatThreads.success || !chatThreads.data) { + return <div>Error fetching chat threads</div>; + } + + return ( + <div className="space-y-5"> + <h3 className="text-lg">Recent Searches</h3> + <ul className="text-base list-none space-y-3 text-[#b9b9b9]"> + {chatThreads.data.map((thread) => ( + <li className="flex items-center gap-2 truncate"> + <ArrowLongRightIcon className="h-5" />{" "} + <Link prefetch={false} href={`/chat/${thread.id}`}> + {thread.firstMessage} + </Link> + </li> + ))} + </ul> + </div> + ); +}); + export default Page; diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx index 6767d5ea..930f34db 100644 --- a/apps/web/app/(dash)/home/queryinput.tsx +++ b/apps/web/app/(dash)/home/queryinput.tsx @@ -25,9 +25,9 @@ function QueryInput({ >([]); return ( - <div className={`${className}`}> + <div className={`w-full`}> <div - className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} + className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} > {/* input and action button */} <form diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts index 1838ee1c..74df0b04 100644 --- a/apps/web/app/actions/fetchers.ts +++ b/apps/web/app/actions/fetchers.ts @@ -1,6 +1,6 @@ "use server"; -import { and, asc, eq, exists, inArray, not, or, sql } from "drizzle-orm"; +import { and, asc, desc, eq, exists, inArray, not, or, sql } from "drizzle-orm"; import { db } from "../../server/db"; import { canvas, @@ -228,6 +228,34 @@ export const getFullChatThread = async ( }; }; +export const getRecentChats = async (): ServerActionReturnType<ChatThread[]> => { + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + try { + const chatHistorys = await db.query.chatThreads.findMany({ + where: eq(chatThreads.userId, data.user.id), + orderBy: desc(chatThreads.createdAt), + limit: 4, + }); + + return { + success: true, + data: chatHistorys, + }; + } catch (e) { + return { + success: false, + error: (e as Error).message, + }; + } +}; + + export const getChatHistory = async (): ServerActionReturnType< ChatThread[] > => { diff --git a/apps/web/migrations/0001_nervous_longshot.sql b/apps/web/migrations/0001_nervous_longshot.sql new file mode 100644 index 00000000..a15917c6 --- /dev/null +++ b/apps/web/migrations/0001_nervous_longshot.sql @@ -0,0 +1,2 @@ +ALTER TABLE `chatThread` ADD `createdAt` integer; +UPDATE `chatThread` SET `createdAt` = strftime('%s', 'now') WHERE `createdAt` IS NULL; diff --git a/apps/web/migrations/meta/0001_snapshot.json b/apps/web/migrations/meta/0001_snapshot.json new file mode 100644 index 00000000..91c50c13 --- /dev/null +++ b/apps/web/migrations/meta/0001_snapshot.json @@ -0,0 +1,905 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "39768c98-95d2-4d42-bae0-43e3e511be7e", + "prevId": "ab91d972-05ff-4916-84b7-1cfaab4c3879", + "tables": { + "account": { + "name": "account", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "columns": [ + "provider", + "providerAccountId" + ], + "name": "account_provider_providerAccountId_pk" + } + }, + "uniqueConstraints": {} + }, + "authenticator": { + "name": "authenticator", + "columns": { + "credentialID": { + "name": "credentialID", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentialPublicKey": { + "name": "credentialPublicKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentialDeviceType": { + "name": "credentialDeviceType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentialBackedUp": { + "name": "credentialBackedUp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "authenticator_credentialID_unique": { + "name": "authenticator_credentialID_unique", + "columns": [ + "credentialID" + ], + "isUnique": true + } + }, + "foreignKeys": { + "authenticator_userId_user_id_fk": { + "name": "authenticator_userId_user_id_fk", + "tableFrom": "authenticator", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "authenticator_userId_credentialID_pk": { + "columns": [ + "credentialID", + "userId" + ], + "name": "authenticator_userId_credentialID_pk" + } + }, + "uniqueConstraints": {} + }, + "canvas": { + "name": "canvas", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Untitled'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Untitled'" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "canvas_user_userId": { + "name": "canvas_user_userId", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "canvas_userId_user_id_fk": { + "name": "canvas_userId_user_id_fk", + "tableFrom": "canvas", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "chatHistory": { + "name": "chatHistory", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "threadId": { + "name": "threadId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "answerParts": { + "name": "answerParts", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "answerSources": { + "name": "answerSources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "answerJustification": { + "name": "answerJustification", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "chatHistory_thread_idx": { + "name": "chatHistory_thread_idx", + "columns": [ + "threadId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "chatHistory_threadId_chatThread_id_fk": { + "name": "chatHistory_threadId_chatThread_id_fk", + "tableFrom": "chatHistory", + "tableTo": "chatThread", + "columnsFrom": [ + "threadId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "chatThread": { + "name": "chatThread", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "firstMessage": { + "name": "firstMessage", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "chatThread_user_idx": { + "name": "chatThread_user_idx", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "chatThread_userId_user_id_fk": { + "name": "chatThread_userId_user_id_fk", + "tableFrom": "chatThread", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "contentToSpace": { + "name": "contentToSpace", + "columns": { + "contentId": { + "name": "contentId", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "spaceId": { + "name": "spaceId", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "contentToSpace_contentId_storedContent_id_fk": { + "name": "contentToSpace_contentId_storedContent_id_fk", + "tableFrom": "contentToSpace", + "tableTo": "storedContent", + "columnsFrom": [ + "contentId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "contentToSpace_spaceId_space_id_fk": { + "name": "contentToSpace_spaceId_space_id_fk", + "tableFrom": "contentToSpace", + "tableTo": "space", + "columnsFrom": [ + "spaceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "contentToSpace_contentId_spaceId_pk": { + "columns": [ + "contentId", + "spaceId" + ], + "name": "contentToSpace_contentId_spaceId_pk" + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "space": { + "name": "space", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'none'" + }, + "user": { + "name": "user", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "numItems": { + "name": "numItems", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + } + }, + "indexes": { + "space_name_unique": { + "name": "space_name_unique", + "columns": [ + "name" + ], + "isUnique": true + }, + "spaces_name_idx": { + "name": "spaces_name_idx", + "columns": [ + "name" + ], + "isUnique": false + }, + "spaces_user_idx": { + "name": "spaces_user_idx", + "columns": [ + "user" + ], + "isUnique": false + } + }, + "foreignKeys": { + "space_user_user_id_fk": { + "name": "space_user_user_id_fk", + "tableFrom": "space", + "tableTo": "user", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "spacesAccess": { + "name": "spacesAccess", + "columns": { + "spaceId": { + "name": "spaceId", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userEmail": { + "name": "userEmail", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "spacesAccess_spaceId_space_id_fk": { + "name": "spacesAccess_spaceId_space_id_fk", + "tableFrom": "spacesAccess", + "tableTo": "space", + "columnsFrom": [ + "spaceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "spacesAccess_spaceId_userEmail_pk": { + "columns": [ + "spaceId", + "userEmail" + ], + "name": "spacesAccess_spaceId_userEmail_pk" + } + }, + "uniqueConstraints": {} + }, + "storedContent": { + "name": "storedContent", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "savedAt": { + "name": "savedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "baseUrl": { + "name": "baseUrl", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ogImage": { + "name": "ogImage", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'page'" + }, + "image": { + "name": "image", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user": { + "name": "user", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "noteId": { + "name": "noteId", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "storedContent_baseUrl_unique": { + "name": "storedContent_baseUrl_unique", + "columns": [ + "baseUrl" + ], + "isUnique": true + }, + "storedContent_url_idx": { + "name": "storedContent_url_idx", + "columns": [ + "url" + ], + "isUnique": false + }, + "storedContent_savedAt_idx": { + "name": "storedContent_savedAt_idx", + "columns": [ + "savedAt" + ], + "isUnique": false + }, + "storedContent_title_idx": { + "name": "storedContent_title_idx", + "columns": [ + "title" + ], + "isUnique": false + }, + "storedContent_user_idx": { + "name": "storedContent_user_idx", + "columns": [ + "user" + ], + "isUnique": false + } + }, + "foreignKeys": { + "storedContent_user_user_id_fk": { + "name": "storedContent_user_user_id_fk", + "tableFrom": "storedContent", + "tableTo": "user", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + "email" + ], + "isUnique": false + }, + "users_telegram_idx": { + "name": "users_telegram_idx", + "columns": [ + "telegramId" + ], + "isUnique": false + }, + "users_id_idx": { + "name": "users_id_idx", + "columns": [ + "id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "columns": [ + "identifier", + "token" + ], + "name": "verificationToken_identifier_token_pk" + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +}
\ No newline at end of file diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json index c7ab51e1..7af2b032 100644 --- a/apps/web/migrations/meta/_journal.json +++ b/apps/web/migrations/meta/_journal.json @@ -1,13 +1,20 @@ { - "version": "6", - "dialect": "sqlite", - "entries": [ - { - "idx": 0, - "version": "6", - "when": 1720360287793, - "tag": "0000_exotic_sway", - "breakpoints": true - } - ] -} + "version": "6", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1720360287793, + "tag": "0000_exotic_sway", + "breakpoints": true + }, + { + "idx": 1, + "version": "6", + "when": 1721329604476, + "tag": "0001_nervous_longshot", + "breakpoints": true + } + ] +}
\ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index ed3eec11..3699d02d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -23,6 +23,7 @@ "@radix-ui/react-slot": "^1.1.0", "@sentry/nextjs": "^8", "cmdk": "^1.0.0", + "drizzle-kit": "^0.23.0", "lowlight": "^3.1.0", "million": "^3.1.6", "next": "^14.1.1", diff --git a/apps/web/server/db/schema.ts b/apps/web/server/db/schema.ts index ae293a91..8d6157e9 100644 --- a/apps/web/server/db/schema.ts +++ b/apps/web/server/db/schema.ts @@ -193,7 +193,8 @@ export const chatThreads = createTable( userId: text("userId") .notNull() .references(() => users.id, { onDelete: "cascade" }), - }, + createdAt: integer("createdAt").notNull().$defaultFn(() => Math.floor(Date.now() / 1000)), // Use Unix timestamp + }, (thread) => ({ userIdx: index("chatThread_user_idx").on(thread.userId), }), Binary files differ |