User Canvas (1/2)

This commit is contained in:
codetorso 2024-06-29 07:02:27 +05:30
parent 6888cc45f0
commit 3143baac87
16 changed files with 1076 additions and 28 deletions

View file

@ -9,15 +9,11 @@ import { AutocompleteIcon, blockIcon } from "@repo/ui/icons";
import Image from "next/image";
import { Switch } from "@repo/ui/shadcn/switch";
import { Label } from "@repo/ui/shadcn/label";
import { useRouter } from "next/router";
function page() {
const [fullScreen, setFullScreen] = useState(false);
const [visible, setVisible] = useState(true);
const router = useRouter();
router.push("/home");
return (
<div
className={`h-screen w-full ${!fullScreen ? "px-4 py-6" : "bg-[#1F2428]"} transition-all`}

View file

@ -1,13 +0,0 @@
import "../canvasStyles.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div lang="en" className="bg-[#151515]">
<div>{children}</div>
</div>
);
}

View file

@ -1,9 +1,23 @@
import { redirect } from "next/navigation";
import React from "react";
import { getCanvas } from "@/app/actions/fetchers";
import SearchandCreate from "./search&create";
import ThinkPads from "./thinkPads";
function page() {
redirect("/signin");
return <div>page</div>;
async function page() {
const canvas = await getCanvas();
return (
<div className="h-screen w-full bg-[#171B1F] py-32 text-[#FFFFFF] ">
<div className="flex w-full flex-col items-center gap-8">
<h1 className="text-4xl font-medium">Your thinkpads</h1>
<p>{JSON.stringify(canvas)}</p>
<SearchandCreate />
{
// @ts-ignore
canvas.success && <ThinkPads data={canvas.data} />
}
</div>
</div>
);
}
export default page;

View file

@ -0,0 +1,35 @@
"use client"
import { useFormStatus } from "react-dom";
import Image from "next/image";
import { SearchIcon } from "@repo/ui/icons";
import { createCanvas } from "@/app/actions/doers";
export default function SearchandCreate() {
return (
<div className="flex w-[90%] max-w-2xl gap-2">
<div className="flex flex-grow items-center overflow-hidden rounded-xl bg-[#1F2428]">
<input
placeholder="search here..."
className="flex-grow bg-[#1F2428] px-5 py-3 text-xl focus:border-none focus:outline-none"
/>
<button className="h-full border-l-2 border-[#384149] px-2 pl-2">
<Image src={SearchIcon} alt="search" />
</button>
</div>
<form action={createCanvas}>
<Button />
</form>
</div>
);
}
function Button() {
const {pending} = useFormStatus()
return (
<button className="rounded-xl bg-[#1F2428] px-5 py-3 text-xl text-[#B8C4C6]">
{pending? "Creating.." : "Create New"}
</button>
);
}

View file

@ -0,0 +1,36 @@
import {motion} from "framer-motion"
import Link from "next/link";
const childVariants = {
hidden: { opacity: 0, y: 10, filter: "blur(2px)" },
visible: { opacity: 1, y: 0, filter: "blur(0px)" },
};
export default function ThinkPad({
title,
description,
image,
id
}: {
title: string;
description: string;
image: string;
id: string;
}) {
return (
<motion.div
variants={childVariants}
className="flex h-48 gap-4 rounded-2xl bg-[#1F2428] p-2"
>
<Link className="h-full min-w-[40%] rounded-xl bg-[#363f46]" href={`/canvas/${id}`}>
<div></div>
</Link>
<div className="flex flex-col gap-2">
<div>{title}</div>
<div className="overflow-hidden text-ellipsis text-[#B8C4C6]">
{description}
</div>
</div>
</motion.div>
);
}

View file

@ -0,0 +1,30 @@
"use client";
import { motion } from "framer-motion";
import ThinkPad from "./thinkPad";
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
export default function ThinkPads({data}: {
data: {image: string, title:string, description:string, id: string}[]
}) {
return (
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="w-[90%] max-w-2xl space-y-6"
>
{data.map((item) => {
return <ThinkPad {...item} />;
})}
</motion.div>
);
}

View file

@ -0,0 +1,19 @@
import { auth } from "@/server/auth";
import "./canvasStyles.css";
import { redirect } from "next/navigation";
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const info = await auth();
if (!info) {
return redirect("/signin");
}
return (
<div>{children}</div>
);
}

View file

@ -3,6 +3,7 @@
import { revalidatePath } from "next/cache";
import { db } from "../../server/db";
import {
canvas,
chatHistory,
chatThreads,
contentToSpace,
@ -381,3 +382,37 @@ export const linkTelegramToUser = async (
data: true,
};
};
export const createCanvas = async () => {
const data = await auth();
if (!data || !data.user || !data.user.id) {
redirect("/signin");
return { error: "Not authenticated", success: false };
}
const resp = await db
.insert(canvas)
.values({ userId: data.user.id }).returning({id: canvas.id});
redirect(`/canvas/${resp[0]!.id}`);
// TODO INVESTIGATE: NO REDIRECT INSIDE TRY CATCH BLOCK
// try {
// const resp = await db
// .insert(canvas)
// .values({ userId: data.user.id }).returning({id: canvas.id});
// return redirect(`/canvas/${resp[0]!.id}`);
// } catch (e: unknown) {
// const error = e as Error;
// if (
// error.message.includes("D1_ERROR: UNIQUE constraint failed: space.name")
// ) {
// return { success: false, data: 0, error: "Space already exists" };
// } else {
// return {
// success: false,
// data: 0,
// error: "Failed to create space with error: " + error.message,
// };
// }
// }
};

View file

@ -3,6 +3,7 @@
import { and, asc, eq, inArray, not, sql } from "drizzle-orm";
import { db } from "../../server/db";
import {
canvas,
chatHistory,
ChatThread,
chatThreads,
@ -189,3 +190,26 @@ export const getChatHistory = async (): ServerActionReturnType<
};
}
};
export const getCanvas = async () => {
const data = await auth();
if (!data || !data.user || !data.user.id) {
redirect("/signin");
return { error: "Not authenticated", success: false };
}
try {
const canvases = await db.select().from(canvas).where(eq(canvas.userId, data.user.id))
return {
success: true,
data: canvases.map(({ userId, ...rest }) => rest),
};
} catch (e) {
return {
success: false,
error: (e as Error).message,
};
}
};

View file

@ -4,13 +4,19 @@ declare global {
GOOGLE_CLIENT_ID: string;
GOOGLE_CLIENT_SECRET: string;
AUTH_SECRET: string;
R2_ENDPOINT: string;
R2_ACCESS_KEY_ID: string;
R2_SECRET_ACCESS_KEY: string;
R2_PUBLIC_BUCKET_ADDRESS: string;
R2_BUCKET_NAME: string;
BACKEND_SECURITY_KEY: string;
BACKEND_BASE_URL: string;
CLOUDFLARE_ACCOUNT_ID: string,
CLOUDFLARE_DATABASE_ID: string,
CLOUDFLARE_D1_TOKEN: string,
}
}
}

View file

@ -27,6 +27,15 @@ CREATE TABLE `authenticator` (
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `canvas` (
`id` text PRIMARY KEY NOT NULL,
`title` text DEFAULT 'Untitled' NOT NULL,
`description` text DEFAULT 'Untitled' NOT NULL,
`url` text DEFAULT '' NOT NULL,
`userId` text NOT NULL,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `chatHistory` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`threadId` text NOT NULL,
@ -86,7 +95,8 @@ CREATE TABLE `user` (
`name` text,
`email` text NOT NULL,
`emailVerified` integer,
`image` text
`image` text,
`telegramId` text
);
--> statement-breakpoint
CREATE TABLE `verificationToken` (
@ -97,6 +107,7 @@ CREATE TABLE `verificationToken` (
);
--> statement-breakpoint
CREATE UNIQUE INDEX `authenticator_credentialID_unique` ON `authenticator` (`credentialID`);--> statement-breakpoint
CREATE INDEX `canvas_user_userId` ON `canvas` (`userId`);--> statement-breakpoint
CREATE INDEX `chatHistory_thread_idx` ON `chatHistory` (`threadId`);--> statement-breakpoint
CREATE INDEX `chatThread_user_idx` ON `chatThread` (`userId`);--> statement-breakpoint
CREATE UNIQUE INDEX `space_name_unique` ON `space` (`name`);--> statement-breakpoint
@ -105,4 +116,7 @@ CREATE INDEX `spaces_user_idx` ON `space` (`user`);--> statement-breakpoint
CREATE INDEX `storedContent_url_idx` ON `storedContent` (`url`);--> statement-breakpoint
CREATE INDEX `storedContent_savedAt_idx` ON `storedContent` (`savedAt`);--> statement-breakpoint
CREATE INDEX `storedContent_title_idx` ON `storedContent` (`title`);--> statement-breakpoint
CREATE INDEX `storedContent_user_idx` ON `storedContent` (`user`);
CREATE INDEX `storedContent_user_idx` ON `storedContent` (`user`);--> statement-breakpoint
CREATE INDEX `users_email_idx` ON `user` (`email`);--> statement-breakpoint
CREATE INDEX `users_telegram_idx` ON `user` (`telegramId`);--> statement-breakpoint
CREATE INDEX `users_id_idx` ON `user` (`id`);

View file

@ -1,4 +0,0 @@
ALTER TABLE `user` ADD `telegramId` text;--> statement-breakpoint
CREATE INDEX `users_email_idx` ON `user` (`email`);--> statement-breakpoint
CREATE INDEX `users_telegram_idx` ON `user` (`telegramId`);--> statement-breakpoint
CREATE INDEX `users_id_idx` ON `user` (`id`);

View file

@ -0,0 +1,824 @@
{
"version": "6",
"dialect": "sqlite",
"id": "58ec8bd8-5aad-4ade-a718-74eaf6056d36",
"prevId": "00000000-0000-0000-0000-000000000000",
"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
}
},
"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
}
},
"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": {}
},
"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
}
},
"indexes": {
"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": {}
}
}

View file

@ -0,0 +1,13 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1719619409551,
"tag": "0000_bitter_electro",
"breakpoints": true
}
]
}

View file

@ -198,5 +198,24 @@ export const chatHistory = createTable(
}),
);
export const canvas = createTable(
"canvas",
{
id: text("id")
.notNull()
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
title: text("title").default("Untitled").notNull(),
description: text("description").default("Untitled").notNull(),
imageUrl: text("url").default("").notNull(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
},
(canvas) => ({
userIdx: index("canvas_user_userId").on(canvas.userId),
}),
);
export type ChatThread = typeof chatThreads.$inferSelect;
export type ChatHistory = typeof chatHistory.$inferSelect;

View file

@ -20,7 +20,7 @@
"@repo/ui": "*",
"@tailwindcss/typography": "^0.5.13",
"autoprefixer": "^10.4.19",
"drizzle-kit": "^0.21.2",
"drizzle-kit": "^0.22.7",
"eslint-plugin-next-on-pages": "^1.11.3",
"lint-staged": "^15.2.5",
"postcss": "^8.4.38",