aboutsummaryrefslogtreecommitdiff
path: root/apps/web/app/api
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-06-30 14:07:16 -0500
committerDhravya <[email protected]>2024-06-30 14:07:16 -0500
commit7676ed57065eb61d54d500e68c7a7faae16e558b (patch)
tree0d59a7dd9ee1772667abbc03158e268732ff5214 /apps/web/app/api
parenttailwind shadow dom (diff)
downloadsupermemory-7676ed57065eb61d54d500e68c7a7faae16e558b.tar.xz
supermemory-7676ed57065eb61d54d500e68c7a7faae16e558b.zip
added things
Diffstat (limited to 'apps/web/app/api')
-rw-r--r--apps/web/app/api/store/route.ts202
1 files changed, 202 insertions, 0 deletions
diff --git a/apps/web/app/api/store/route.ts b/apps/web/app/api/store/route.ts
new file mode 100644
index 00000000..8a126a56
--- /dev/null
+++ b/apps/web/app/api/store/route.ts
@@ -0,0 +1,202 @@
+import { type NextRequest } from "next/server";
+import { addFromAPIType, AddFromAPIType } from "@repo/shared-types";
+import { ensureAuth } from "../ensureAuth";
+import { z } from "zod";
+import { db } from "@/server/db";
+import { contentToSpace, space, storedContent } from "@/server/db/schema";
+import { and, eq, inArray } from "drizzle-orm";
+import { LIMITS } from "@/lib/constants";
+import { limit } from "@/app/actions/doers";
+
+export const runtime = "edge";
+
+const createMemoryFromAPI = async (input: {
+ data: AddFromAPIType;
+ userId: string;
+}) => {
+ if (!(await limit(input.userId, input.data.type))) {
+ return {
+ success: false,
+ data: 0,
+ error: `You have exceeded the limit of ${LIMITS[input.data.type as keyof typeof LIMITS]} ${input.data.type}s.`,
+ };
+ }
+
+ const vectorSaveResponse = await fetch(
+ `${process.env.BACKEND_BASE_URL}/api/add`,
+ {
+ method: "POST",
+ body: JSON.stringify({
+ pageContent: input.data.pageContent,
+ title: input.data.title,
+ description: input.data.description,
+ url: input.data.url,
+ spaces: input.data.spaces,
+ user: input.userId,
+ type: input.data.type,
+ }),
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: "Bearer " + process.env.BACKEND_SECURITY_KEY,
+ },
+ },
+ );
+
+ if (!vectorSaveResponse.ok) {
+ const errorData = await vectorSaveResponse.text();
+ console.error(errorData);
+ return {
+ success: false,
+ data: 0,
+ error: `Failed to save to vector store. Backend returned error: ${errorData}`,
+ };
+ }
+
+ let contentId: number | undefined;
+
+ // Insert into database
+ try {
+ const insertResponse = await db
+ .insert(storedContent)
+ .values({
+ content: input.data.pageContent,
+ title: input.data.title,
+ description: input.data.description,
+ url: input.data.url,
+ baseUrl: input.data.url,
+ image: input.data.image,
+ savedAt: new Date(),
+ userId: input.userId,
+ type: input.data.type,
+ })
+ .returning({ id: storedContent.id });
+
+ contentId = insertResponse[0]?.id;
+ } catch (e) {
+ const error = e as Error;
+ console.log("Error: ", error.message);
+
+ if (
+ error.message.includes(
+ "D1_ERROR: UNIQUE constraint failed: storedContent.baseUrl",
+ )
+ ) {
+ return {
+ success: false,
+ data: 0,
+ error: "Content already exists",
+ };
+ }
+
+ return {
+ success: false,
+ data: 0,
+ error: "Failed to save to database with error: " + error.message,
+ };
+ }
+
+ if (!contentId) {
+ return {
+ success: false,
+ data: 0,
+ error: "Failed to save to database",
+ };
+ }
+
+ if (input.data.spaces.length > 0) {
+ // Adding the many-to-many relationship between content and spaces
+ const spaceData = await db
+ .select()
+ .from(space)
+ .where(
+ and(
+ inArray(
+ space.id,
+ input.data.spaces.map((s) => parseInt(s)),
+ ),
+ eq(space.user, input.userId),
+ ),
+ )
+ .all();
+
+ await Promise.all(
+ spaceData.map(async (space) => {
+ await db
+ .insert(contentToSpace)
+ .values({ contentId: contentId, spaceId: space.id });
+ }),
+ );
+ }
+
+ try {
+ const response = await vectorSaveResponse.json();
+
+ const expectedResponse = z.object({ status: z.literal("ok") });
+
+ const parsedResponse = expectedResponse.safeParse(response);
+
+ if (!parsedResponse.success) {
+ return {
+ success: false,
+ data: 0,
+ error: `Failed to save to vector store. Backend returned error: ${parsedResponse.error.message}`,
+ };
+ }
+
+ return {
+ success: true,
+ data: 1,
+ };
+ } catch (e) {
+ return {
+ success: false,
+ data: 0,
+ error: `Failed to save to vector store. Backend returned error: ${e}`,
+ };
+ }
+};
+
+export async function POST(req: NextRequest) {
+ const session = await ensureAuth(req);
+
+ if (!session) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+
+ if (!process.env.BACKEND_SECURITY_KEY) {
+ return new Response("Missing BACKEND_SECURITY_KEY", { status: 500 });
+ }
+
+ const body = await req.json();
+
+ const validated = addFromAPIType.safeParse(body);
+
+ if (!validated.success) {
+ return new Response(
+ JSON.stringify({
+ message: "Invalid request",
+ error: validated.error,
+ }),
+ { status: 400 },
+ );
+ }
+
+ const data = validated.data;
+
+ const result = await createMemoryFromAPI({
+ data,
+ userId: session.user.id,
+ });
+
+ if (!result.success) {
+ return new Response(
+ JSON.stringify({
+ message: "Failed to save document",
+ error: result.error,
+ }),
+ { status: 500 },
+ );
+ }
+
+ return new Response("ok", { status: 200 });
+}