aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/web/app/(canvas)/canvas/[id]/page.tsx4
-rw-r--r--apps/web/app/(canvas)/canvas/layout.tsx13
-rw-r--r--apps/web/app/(canvas)/canvas/page.tsx22
-rw-r--r--apps/web/app/(canvas)/canvas/search&create.tsx35
-rw-r--r--apps/web/app/(canvas)/canvas/thinkPad.tsx36
-rw-r--r--apps/web/app/(canvas)/canvas/thinkPads.tsx30
-rw-r--r--apps/web/app/(canvas)/layout.tsx19
-rw-r--r--apps/web/app/actions/doers.ts35
-rw-r--r--apps/web/app/actions/fetchers.ts24
-rw-r--r--apps/web/cf-env.d.ts6
-rw-r--r--apps/web/migrations/0000_bitter_electro.sql (renamed from apps/web/migrations/000_setup.sql)18
-rw-r--r--apps/web/migrations/0001_remarkable_avengers.sql4
-rw-r--r--apps/web/migrations/meta/0000_snapshot.json824
-rw-r--r--apps/web/migrations/meta/_journal.json13
-rw-r--r--apps/web/server/db/schema.ts19
-rw-r--r--package.json2
16 files changed, 1076 insertions, 28 deletions
diff --git a/apps/web/app/(canvas)/canvas/[id]/page.tsx b/apps/web/app/(canvas)/canvas/[id]/page.tsx
index 6efb6cf4..e58ab35e 100644
--- a/apps/web/app/(canvas)/canvas/[id]/page.tsx
+++ b/apps/web/app/(canvas)/canvas/[id]/page.tsx
@@ -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`}
diff --git a/apps/web/app/(canvas)/canvas/layout.tsx b/apps/web/app/(canvas)/canvas/layout.tsx
deleted file mode 100644
index 9bc3b6d7..00000000
--- a/apps/web/app/(canvas)/canvas/layout.tsx
+++ /dev/null
@@ -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>
- );
-}
diff --git a/apps/web/app/(canvas)/canvas/page.tsx b/apps/web/app/(canvas)/canvas/page.tsx
index 8b5252af..815d0b93 100644
--- a/apps/web/app/(canvas)/canvas/page.tsx
+++ b/apps/web/app/(canvas)/canvas/page.tsx
@@ -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;
diff --git a/apps/web/app/(canvas)/canvas/search&create.tsx b/apps/web/app/(canvas)/canvas/search&create.tsx
new file mode 100644
index 00000000..3998dde7
--- /dev/null
+++ b/apps/web/app/(canvas)/canvas/search&create.tsx
@@ -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>
+ );
+} \ No newline at end of file
diff --git a/apps/web/app/(canvas)/canvas/thinkPad.tsx b/apps/web/app/(canvas)/canvas/thinkPad.tsx
new file mode 100644
index 00000000..dad2de48
--- /dev/null
+++ b/apps/web/app/(canvas)/canvas/thinkPad.tsx
@@ -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>
+ );
+} \ No newline at end of file
diff --git a/apps/web/app/(canvas)/canvas/thinkPads.tsx b/apps/web/app/(canvas)/canvas/thinkPads.tsx
new file mode 100644
index 00000000..b83d4142
--- /dev/null
+++ b/apps/web/app/(canvas)/canvas/thinkPads.tsx
@@ -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>
+ );
+}
diff --git a/apps/web/app/(canvas)/layout.tsx b/apps/web/app/(canvas)/layout.tsx
new file mode 100644
index 00000000..5c925573
--- /dev/null
+++ b/apps/web/app/(canvas)/layout.tsx
@@ -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>
+ );
+}
diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts
index 99a1b719..30ddbc8e 100644
--- a/apps/web/app/actions/doers.ts
+++ b/apps/web/app/actions/doers.ts
@@ -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,
+ // };
+ // }
+ // }
+}; \ No newline at end of file
diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts
index 664c20ac..f485c28d 100644
--- a/apps/web/app/actions/fetchers.ts
+++ b/apps/web/app/actions/fetchers.ts
@@ -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,
+ };
+ }
+};
diff --git a/apps/web/cf-env.d.ts b/apps/web/cf-env.d.ts
index be5c991a..e645f7b1 100644
--- a/apps/web/cf-env.d.ts
+++ b/apps/web/cf-env.d.ts
@@ -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,
}
}
}
diff --git a/apps/web/migrations/000_setup.sql b/apps/web/migrations/0000_bitter_electro.sql
index a4855ec9..3ce840e7 100644
--- a/apps/web/migrations/000_setup.sql
+++ b/apps/web/migrations/0000_bitter_electro.sql
@@ -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`); \ No newline at end of file
diff --git a/apps/web/migrations/0001_remarkable_avengers.sql b/apps/web/migrations/0001_remarkable_avengers.sql
deleted file mode 100644
index cb2fa422..00000000
--- a/apps/web/migrations/0001_remarkable_avengers.sql
+++ /dev/null
@@ -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`); \ No newline at end of file
diff --git a/apps/web/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json
new file mode 100644
index 00000000..30920063
--- /dev/null
+++ b/apps/web/migrations/meta/0000_snapshot.json
@@ -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": {}
+ }
+} \ No newline at end of file
diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json
new file mode 100644
index 00000000..68c578aa
--- /dev/null
+++ b/apps/web/migrations/meta/_journal.json
@@ -0,0 +1,13 @@
+{
+ "version": "7",
+ "dialect": "sqlite",
+ "entries": [
+ {
+ "idx": 0,
+ "version": "6",
+ "when": 1719619409551,
+ "tag": "0000_bitter_electro",
+ "breakpoints": true
+ }
+ ]
+} \ No newline at end of file
diff --git a/apps/web/server/db/schema.ts b/apps/web/server/db/schema.ts
index 69372a35..c40e50f1 100644
--- a/apps/web/server/db/schema.ts
+++ b/apps/web/server/db/schema.ts
@@ -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;
diff --git a/package.json b/package.json
index c94610a6..e95e630c 100644
--- a/package.json
+++ b/package.json
@@ -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",