aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-07-01 20:32:50 -0500
committerDhravya <[email protected]>2024-07-01 20:32:50 -0500
commit46dfc3b4e7ac5958251401a670c1f4cff49706bb (patch)
treef9baefd7dfd4cac46ef09cfc8349131e6235dfe1 /apps
parentspaces function (diff)
downloadsupermemory-46dfc3b4e7ac5958251401a670c1f4cff49706bb.tar.xz
supermemory-46dfc3b4e7ac5958251401a670c1f4cff49706bb.zip
shareable spaces
Diffstat (limited to 'apps')
-rw-r--r--apps/web/app/(dash)/(memories)/content.tsx60
-rw-r--r--apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx9
-rw-r--r--apps/web/app/actions/doers.ts44
-rw-r--r--apps/web/app/actions/fetchers.ts27
-rw-r--r--apps/web/migrations/0004_skinny_stature.sql6
-rw-r--r--apps/web/migrations/meta/0004_snapshot.json806
-rw-r--r--apps/web/migrations/meta/_journal.json7
-rw-r--r--apps/web/server/db/schema.ts15
8 files changed, 957 insertions, 17 deletions
diff --git a/apps/web/app/(dash)/(memories)/content.tsx b/apps/web/app/(dash)/(memories)/content.tsx
index 26aed6a5..23bff46b 100644
--- a/apps/web/app/(dash)/(memories)/content.tsx
+++ b/apps/web/app/(dash)/(memories)/content.tsx
@@ -30,17 +30,20 @@ import {
DropdownMenuTrigger,
} from "@repo/ui/shadcn/dropdown-menu";
import { Button } from "@repo/ui/shadcn/button";
-import { deleteItem, moveItem } from "@/app/actions/doers";
+import { addUserToSpace, deleteItem, moveItem } from "@/app/actions/doers";
import { toast } from "sonner";
+import { Input } from "@repo/ui/shadcn/input";
export function MemoriesPage({
memoriesAndSpaces,
title = "Your Memories",
currentSpace,
+ usersWithAccess,
}: {
memoriesAndSpaces: { memories: Content[]; spaces: StoredSpace[] };
title?: string;
currentSpace?: StoredSpace;
+ usersWithAccess?: string[];
}) {
const [filter, setFilter] = useState("All");
@@ -70,7 +73,6 @@ export function MemoriesPage({
.filter((item) => {
if (filter === "All") return true;
if (filter === "Spaces" && item.item === "space") {
- console.log(item);
return true;
}
if (filter === "Pages")
@@ -105,12 +107,55 @@ export function MemoriesPage({
{title}
</h2>
{currentSpace && (
- <div className="flex gap-4 items-center">
- Space
- <div className="flex items-center gap-2 bg-secondary p-2 rounded-xl">
- <Image src={MemoriesIcon} alt="Spaces icon" className="w-3 h-3" />
- <span className="text-[#fff]">{currentSpace.name}</span>
+ <div className="flex flex-col gap-2">
+ <div className="flex gap-4 items-center">
+ Space
+ <div className="flex items-center gap-2 bg-secondary p-2 rounded-xl">
+ <Image src={MemoriesIcon} alt="Spaces icon" className="w-3 h-3" />
+ <span className="text-[#fff]">{currentSpace.name}</span>
+ </div>
</div>
+
+ {usersWithAccess && usersWithAccess.length > 0 && (
+ <div className="flex gap-4 items-center">
+ Users with access
+ <div className="flex gap-2">
+ {usersWithAccess.map((user) => (
+ <div className="flex items-center gap-2 bg-secondary p-2 rounded-xl">
+ <Image
+ src={UrlIcon}
+ alt="Spaces icon"
+ className="w-3 h-3"
+ />
+ <span className="text-[#fff]">{user}</span>
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+
+ <form
+ action={async (e: FormData) => {
+ const email = e.get("email")?.toString();
+
+ if (!email) {
+ toast.error("Please enter an email");
+ return;
+ }
+
+ const resp = await addUserToSpace(email, currentSpace.id);
+
+ if (resp.success) {
+ toast.success("User added to space");
+ } else {
+ toast.error("Failed to add user to space");
+ }
+ }}
+ className="flex gap-2 max-w-xl mt-2"
+ >
+ <Input name="email" placeholder="Add user by email" />
+ <Button variant="secondary">Add</Button>
+ </form>
</div>
)}
@@ -268,7 +313,6 @@ function LinkComponent({
onClick={async () => {
toast.info("Adding to space...");
- console.log(id, space.id);
const response = await moveItem(id, [space.id]);
if (response.success) {
diff --git a/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx b/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx
index 723fb29e..759519cb 100644
--- a/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx
+++ b/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx
@@ -1,15 +1,24 @@
import { getMemoriesInsideSpace } from "@/app/actions/fetchers";
import { redirect } from "next/navigation";
import MemoriesPage from "../../content";
+import { db } from "@/server/db";
+import { and, eq } from "drizzle-orm";
+import { spacesAccess } from "@/server/db/schema";
async function Page({ params: { spaceid } }: { params: { spaceid: number } }) {
const { success, data } = await getMemoriesInsideSpace(spaceid);
if (!success ?? !data) return redirect("/home");
+
+ const hasAccess = await db.query.spacesAccess.findMany({
+ where: and(eq(spacesAccess.spaceId, spaceid)),
+ });
+
return (
<MemoriesPage
memoriesAndSpaces={{ memories: data.memories, spaces: [] }}
title={data.spaces[0]?.name}
currentSpace={data.spaces[0]}
+ usersWithAccess={hasAccess.map((x) => x.userEmail) ?? []}
/>
);
}
diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts
index 90103092..82263f6e 100644
--- a/apps/web/app/actions/doers.ts
+++ b/apps/web/app/actions/doers.ts
@@ -9,6 +9,7 @@ import {
contentToSpace,
sessions,
space,
+ spacesAccess,
storedContent,
users,
} from "../../server/db/schema";
@@ -95,6 +96,47 @@ export const limit = async (
return items <= remainingLimit;
};
+export const addUserToSpace = async (userEmail: string, spaceId: number) => {
+ const data = await auth();
+
+ if (!data || !data.user || !data.user.id) {
+ redirect("/signin");
+ return { error: "Not authenticated", success: false };
+ }
+
+ // We need to make sure that the user owns the space
+ const spaceData = await db
+ .select()
+ .from(space)
+ .where(and(eq(space.id, spaceId), eq(space.user, data.user.id)))
+ .all();
+
+ if (spaceData.length === 0) {
+ return {
+ success: false,
+ error: "You do not own this space",
+ };
+ }
+
+ try {
+ await db.insert(spacesAccess).values({
+ spaceId: spaceId,
+ userEmail: userEmail,
+ });
+
+ revalidatePath("/space/" + spaceId);
+
+ return {
+ success: true,
+ };
+ } catch (e) {
+ return {
+ success: false,
+ error: (e as Error).message,
+ };
+ }
+};
+
const getTweetData = async (tweetID: string) => {
const url = `https://cdn.syndication.twimg.com/tweet-result?id=${tweetID}&lang=en&features=tfw_timeline_list%3A%3Btfw_follower_count_sunset%3Atrue%3Btfw_tweet_edit_backend%3Aon%3Btfw_refsrc_session%3Aon%3Btfw_fosnr_soft_interventions_enabled%3Aon%3Btfw_show_birdwatch_pivots_enabled%3Aon%3Btfw_show_business_verified_badge%3Aon%3Btfw_duplicate_scribes_to_settings%3Aon%3Btfw_use_profile_image_shape_enabled%3Aon%3Btfw_show_blue_verified_badge%3Aon%3Btfw_legacy_timeline_sunset%3Atrue%3Btfw_show_gov_verified_badge%3Aon%3Btfw_show_business_affiliate_badge%3Aon%3Btfw_tweet_edit_frontend%3Aon&token=4c2mmul6mnh`;
@@ -191,8 +233,6 @@ export const createMemory = async (input: {
storeToSpaces = [];
}
- console.log("SAVING URL: ", metadata.baseUrl);
-
const vectorSaveResponse = await fetch(
`${process.env.BACKEND_BASE_URL}/api/add`,
{
diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts
index c33f90d1..1541e4ee 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, inArray, not, sql } from "drizzle-orm";
+import { and, asc, eq, inArray, not, or, sql } from "drizzle-orm";
import { db } from "../../server/db";
import {
canvas,
@@ -10,6 +10,7 @@ import {
Content,
contentToSpace,
space,
+ spacesAccess,
storedContent,
StoredSpace,
users,
@@ -90,12 +91,24 @@ export const getMemoriesInsideSpace = async (
.select()
.from(storedContent)
.where(
- inArray(
- storedContent.id,
- db
- .select({ contentId: contentToSpace.contentId })
- .from(contentToSpace)
- .where(eq(contentToSpace.spaceId, spaceId)),
+ and(
+ inArray(
+ storedContent.id,
+ db
+ .select({ contentId: contentToSpace.contentId })
+ .from(contentToSpace)
+ .where(eq(contentToSpace.spaceId, spaceId)),
+ ),
+ or(
+ eq(storedContent.userId, data.user.id!),
+ eq(
+ db
+ .select({ userId: spacesAccess.userEmail })
+ .from(spacesAccess)
+ .where(eq(spacesAccess.spaceId, spaceId)),
+ data.user.email,
+ ),
+ ),
),
)
.execute();
diff --git a/apps/web/migrations/0004_skinny_stature.sql b/apps/web/migrations/0004_skinny_stature.sql
new file mode 100644
index 00000000..a0e2d9f6
--- /dev/null
+++ b/apps/web/migrations/0004_skinny_stature.sql
@@ -0,0 +1,6 @@
+CREATE TABLE `spacesAccess` (
+ `spaceId` integer NOT NULL,
+ `userEmail` text NOT NULL,
+ PRIMARY KEY(`spaceId`, `userEmail`),
+ FOREIGN KEY (`spaceId`) REFERENCES `space`(`id`) ON UPDATE no action ON DELETE cascade
+);
diff --git a/apps/web/migrations/meta/0004_snapshot.json b/apps/web/migrations/meta/0004_snapshot.json
new file mode 100644
index 00000000..6ddd66de
--- /dev/null
+++ b/apps/web/migrations/meta/0004_snapshot.json
@@ -0,0 +1,806 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "1c8dc08a-69e1-4d82-b4fc-e6df7b565864",
+ "prevId": "5d27a673-efd9-47a7-a9f5-84d5cc7d851d",
+ "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
+ },
+ "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": {}
+ }
+}
diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json
index 843330f9..c7245478 100644
--- a/apps/web/migrations/meta/_journal.json
+++ b/apps/web/migrations/meta/_journal.json
@@ -29,6 +29,13 @@
"when": 1719798795179,
"tag": "0003_solid_boom_boom",
"breakpoints": true
+ },
+ {
+ "idx": 4,
+ "version": "6",
+ "when": 1719882966298,
+ "tag": "0004_skinny_stature",
+ "breakpoints": true
}
]
}
diff --git a/apps/web/server/db/schema.ts b/apps/web/server/db/schema.ts
index 01cf202d..979b5152 100644
--- a/apps/web/server/db/schema.ts
+++ b/apps/web/server/db/schema.ts
@@ -161,6 +161,21 @@ export const space = createTable(
}),
);
+export const spacesAccess = createTable(
+ "spacesAccess",
+ {
+ spaceId: integer("spaceId")
+ .notNull()
+ .references(() => space.id, { onDelete: "cascade" }),
+ userEmail: text("userEmail").notNull(),
+ },
+ (spaceAccess) => ({
+ compoundKey: primaryKey({
+ columns: [spaceAccess.spaceId, spaceAccess.userEmail],
+ }),
+ }),
+);
+
export type StoredContent = Omit<typeof storedContent.$inferSelect, "user">;
export type StoredSpace = typeof space.$inferSelect;
export type ChachedSpaceContent = StoredContent & {