aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-05-25 23:38:48 -0500
committerDhravya <[email protected]>2024-05-25 23:38:48 -0500
commitc12ecfc4316a6f37d2d07c57e4dfefa231783c0d (patch)
treec04612a8db2b06060e1e1087ef298fa124aa71e4 /apps
parentfix global file stuff (diff)
downloadsupermemory-c12ecfc4316a6f37d2d07c57e4dfefa231783c0d.tar.xz
supermemory-c12ecfc4316a6f37d2d07c57e4dfefa231783c0d.zip
brought all the APIs back
Diffstat (limited to 'apps')
-rw-r--r--apps/web/app/api/chat/route.ts73
-rw-r--r--apps/web/app/api/getCount/route.ts47
-rw-r--r--apps/web/app/api/me/route.ts47
-rw-r--r--apps/web/app/api/spaces/route.ts29
-rw-r--r--apps/web/app/api/store/route.ts121
-rw-r--r--apps/web/app/helpers/lib/get-metadata.ts40
-rw-r--r--apps/web/app/upload/uploaded-files-card.tsx8
-rw-r--r--apps/web/env.d.ts3
-rw-r--r--apps/web/package.json2
-rw-r--r--apps/web/tsconfig.json5
10 files changed, 368 insertions, 7 deletions
diff --git a/apps/web/app/api/chat/route.ts b/apps/web/app/api/chat/route.ts
new file mode 100644
index 00000000..34099848
--- /dev/null
+++ b/apps/web/app/api/chat/route.ts
@@ -0,0 +1,73 @@
+import { type NextRequest } from "next/server";
+import { ChatHistory } from "@repo/shared-types";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+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 query = new URL(req.url).searchParams.get("q");
+ const spaces = new URL(req.url).searchParams.get("spaces");
+
+ const sourcesOnly =
+ new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
+
+ const chatHistory = (await req.json()) as {
+ chatHistory: ChatHistory[];
+ };
+
+ console.log("CHathistory", chatHistory);
+
+ if (!query) {
+ return new Response(JSON.stringify({ message: "Invalid query" }), {
+ status: 400,
+ });
+ }
+
+ try {
+ const resp = await fetch(
+ `https://cf-ai-backend.dhravya.workers.dev/chat?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`,
+ {
+ headers: {
+ "X-Custom-Auth-Key": process.env.BACKEND_SECURITY_KEY!,
+ },
+ method: "POST",
+ body: JSON.stringify({
+ chatHistory: chatHistory.chatHistory ?? [],
+ }),
+ },
+ );
+
+ console.log("sourcesOnly", sourcesOnly);
+
+ if (sourcesOnly == "true") {
+ const data = await resp.json();
+ console.log("data", data);
+ return new Response(JSON.stringify(data), { status: 200 });
+ }
+
+ if (resp.status !== 200 || !resp.ok) {
+ const errorData = await resp.json();
+ console.log(errorData);
+ return new Response(
+ JSON.stringify({ message: "Error in CF function", error: errorData }),
+ { status: resp.status },
+ );
+ }
+
+ // Stream the response back to the client
+ const { readable, writable } = new TransformStream();
+ resp && resp.body!.pipeTo(writable);
+
+ return new Response(readable, { status: 200 });
+ } catch {}
+}
diff --git a/apps/web/app/api/getCount/route.ts b/apps/web/app/api/getCount/route.ts
new file mode 100644
index 00000000..f760c145
--- /dev/null
+++ b/apps/web/app/api/getCount/route.ts
@@ -0,0 +1,47 @@
+import { db } from "@/app/helpers/server/db";
+import { and, eq, ne, sql } from "drizzle-orm";
+import { sessions, storedContent, users } from "@/app/helpers/server/db/schema";
+import { type NextRequest, NextResponse } from "next/server";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+export async function GET(req: NextRequest) {
+ const session = await ensureAuth(req);
+
+ if (!session) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+
+ const tweetsCount = await db
+ .select({
+ count: sql<number>`count(*)`.mapWith(Number),
+ })
+ .from(storedContent)
+ .where(
+ and(
+ eq(storedContent.user, session.user.id),
+ eq(storedContent.type, "twitter-bookmark"),
+ ),
+ );
+
+ const pageCount = await db
+ .select({
+ count: sql<number>`count(*)`.mapWith(Number),
+ })
+ .from(storedContent)
+ .where(
+ and(
+ eq(storedContent.user, session.user.id),
+ ne(storedContent.type, "twitter-bookmark"),
+ ),
+ );
+
+ return NextResponse.json({
+ tweetsCount: tweetsCount[0]!.count,
+ tweetsLimit: 1000,
+ pageCount: pageCount[0]!.count,
+ pageLimit: 100,
+ user: session.user.email,
+ });
+}
diff --git a/apps/web/app/api/me/route.ts b/apps/web/app/api/me/route.ts
new file mode 100644
index 00000000..20b6aece
--- /dev/null
+++ b/apps/web/app/api/me/route.ts
@@ -0,0 +1,47 @@
+import { db } from "@/app/helpers/server/db";
+import { eq } from "drizzle-orm";
+import { sessions, users } from "@/app/helpers/server/db/schema";
+import { type NextRequest, NextResponse } from "next/server";
+
+export const runtime = "edge";
+
+export async function GET(req: NextRequest) {
+ const token =
+ req.cookies.get("next-auth.session-token")?.value ??
+ req.cookies.get("__Secure-authjs.session-token")?.value ??
+ req.cookies.get("authjs.session-token")?.value ??
+ req.headers.get("Authorization")?.replace("Bearer ", "");
+
+ const session = await db
+ .select()
+ .from(sessions)
+ .where(eq(sessions.sessionToken, token!));
+
+ if (!session || session.length === 0) {
+ return new Response(
+ JSON.stringify({ message: "Invalid Key, session not found." }),
+ { status: 404 },
+ );
+ }
+
+ const user = await db
+ .select()
+ .from(users)
+ .where(eq(users.id, session[0]!.userId))
+ .limit(1);
+
+ if (!user || user.length === 0) {
+ return NextResponse.json(
+ { message: "Invalid Key, session not found." },
+ { status: 404 },
+ );
+ }
+
+ return new Response(
+ JSON.stringify({
+ message: "OK",
+ data: { session: session[0], user: user[0] },
+ }),
+ { status: 200 },
+ );
+}
diff --git a/apps/web/app/api/spaces/route.ts b/apps/web/app/api/spaces/route.ts
new file mode 100644
index 00000000..c46b02fc
--- /dev/null
+++ b/apps/web/app/api/spaces/route.ts
@@ -0,0 +1,29 @@
+import { db } from "@/app/helpers/server/db";
+import { sessions, space, users } from "@/app/helpers/server/db/schema";
+import { eq } from "drizzle-orm";
+import { NextRequest, NextResponse } from "next/server";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+export async function GET(req: NextRequest) {
+ const session = await ensureAuth(req);
+
+ if (!session) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+
+ const spaces = await db
+ .select()
+ .from(space)
+ .where(eq(space.user, session.user.id))
+ .all();
+
+ return NextResponse.json(
+ {
+ message: "OK",
+ data: spaces,
+ },
+ { status: 200 },
+ );
+}
diff --git a/apps/web/app/api/store/route.ts b/apps/web/app/api/store/route.ts
new file mode 100644
index 00000000..f96f90cf
--- /dev/null
+++ b/apps/web/app/api/store/route.ts
@@ -0,0 +1,121 @@
+import { db } from "@/app/helpers/server/db";
+import { and, eq, sql, inArray } from "drizzle-orm";
+import {
+ contentToSpace,
+ sessions,
+ storedContent,
+ users,
+ space,
+} from "@/app/helpers/server/db/schema";
+import { type NextRequest, NextResponse } from "next/server";
+import { getMetaData } from "@/app/helpers/lib/get-metadata";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+export async function POST(req: NextRequest) {
+ const session = await ensureAuth(req);
+
+ if (!session) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+
+ const data = (await req.json()) as {
+ pageContent: string;
+ url: string;
+ spaces?: string[];
+ };
+
+ const metadata = await getMetaData(data.url);
+ let storeToSpaces = data.spaces;
+
+ if (!storeToSpaces) {
+ storeToSpaces = [];
+ }
+
+ const count = await db
+ .select({
+ count: sql<number>`count(*)`.mapWith(Number),
+ })
+ .from(storedContent)
+ .where(
+ and(
+ eq(storedContent.user, session.user.id),
+ eq(storedContent.type, "page"),
+ ),
+ );
+
+ if (count[0]!.count > 100) {
+ return NextResponse.json(
+ { message: "Error", error: "Limit exceeded" },
+ { status: 499 },
+ );
+ }
+
+ const rep = await db
+ .insert(storedContent)
+ .values({
+ content: data.pageContent,
+ title: metadata.title,
+ description: metadata.description,
+ url: data.url,
+ baseUrl: metadata.baseUrl,
+ image: metadata.image,
+ savedAt: new Date(),
+ user: session.user.id,
+ })
+ .returning({ id: storedContent.id });
+
+ const id = rep[0]?.id;
+
+ if (!id) {
+ return NextResponse.json(
+ { message: "Error", error: "Error in CF function" },
+ { status: 500 },
+ );
+ }
+
+ if (storeToSpaces.length > 0) {
+ const spaceData = await db
+ .select()
+ .from(space)
+ .where(
+ and(
+ inArray(space.name, storeToSpaces ?? []),
+ eq(space.user, session.user.id),
+ ),
+ )
+ .all();
+
+ await Promise.all([
+ spaceData.forEach(async (space) => {
+ await db
+ .insert(contentToSpace)
+ .values({ contentId: id, spaceId: space.id });
+ }),
+ ]);
+ }
+
+ const res = (await Promise.race([
+ fetch("https://cf-ai-backend.dhravya.workers.dev/add", {
+ method: "POST",
+ headers: {
+ "X-Custom-Auth-Key": process.env.BACKEND_SECURITY_KEY,
+ },
+ body: JSON.stringify({ ...data, user: session.user.email }),
+ }),
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error("Request timed out")), 40000),
+ ),
+ ])) as Response;
+
+ if (res.status !== 200) {
+ console.log(res.status, res.statusText);
+ return NextResponse.json(
+ { message: "Error", error: "Error in CF function" },
+ { status: 500 },
+ );
+ }
+
+ return NextResponse.json({ message: "OK", data: "Success" }, { status: 200 });
+}
diff --git a/apps/web/app/helpers/lib/get-metadata.ts b/apps/web/app/helpers/lib/get-metadata.ts
new file mode 100644
index 00000000..4609e49b
--- /dev/null
+++ b/apps/web/app/helpers/lib/get-metadata.ts
@@ -0,0 +1,40 @@
+"use server";
+import * as cheerio from "cheerio";
+
+// TODO: THIS SHOULD PROBABLY ALSO FETCH THE OG-IMAGE
+export async function getMetaData(url: string) {
+ const response = await fetch(url);
+ const html = await response.text();
+
+ const $ = cheerio.load(html);
+
+ // Extract the base URL
+ const baseUrl = new URL(url).origin;
+
+ // Extract title
+ const title = $("title").text().trim();
+
+ const description = $("meta[name=description]").attr("content") ?? "";
+
+ const _favicon =
+ $("link[rel=icon]").attr("href") ?? "https://supermemory.dhr.wtf/web.svg";
+
+ let favicon =
+ _favicon.trim().length > 0
+ ? _favicon.trim()
+ : "https://supermemory.dhr.wtf/web.svg";
+ if (favicon.startsWith("/")) {
+ favicon = baseUrl + favicon;
+ } else if (favicon.startsWith("./")) {
+ favicon = baseUrl + favicon.slice(1);
+ }
+
+ // Prepare the metadata object
+ const metadata = {
+ title,
+ description,
+ image: favicon,
+ baseUrl,
+ };
+ return metadata;
+}
diff --git a/apps/web/app/upload/uploaded-files-card.tsx b/apps/web/app/upload/uploaded-files-card.tsx
index fc9b7d5b..869663b5 100644
--- a/apps/web/app/upload/uploaded-files-card.tsx
+++ b/apps/web/app/upload/uploaded-files-card.tsx
@@ -1,5 +1,5 @@
import Image from "next/image";
-import type { UploadedFile } from "@repo/shared-types";
+// import type { UploadedFile } from '@repo/shared-types';
import {
Card,
@@ -7,11 +7,11 @@ import {
CardDescription,
CardHeader,
CardTitle,
-} from "@repo/ui/src/card";
-import { ScrollArea, ScrollBar } from "@repo/ui/src/scroll-area";
+} from "@repo/ui/src/shadcn/card";
+import { ScrollArea, ScrollBar } from "@repo/ui/src/shadcn/scroll-area";
interface UploadedFilesCardProps {
- uploadedFiles: UploadedFile[];
+ uploadedFiles: any[];
}
import { ImageIcon } from "@radix-ui/react-icons";
diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts
index d74dcdf3..161177a1 100644
--- a/apps/web/env.d.ts
+++ b/apps/web/env.d.ts
@@ -1,4 +1,4 @@
-// Generated by Wrangler on Sat May 25 2024 17:07:13 GMT-0500 (Central Daylight Time)
+// Generated by Wrangler on Sat May 25 2024 23:32:58 GMT-0500 (Central Daylight Time)
// by running `wrangler types --env-interface CloudflareEnv env.d.ts`
interface CloudflareEnv {
@@ -9,6 +9,7 @@ interface CloudflareEnv {
R2_ACCESS_ID: string;
R2_SECRET_KEY: string;
R2_BUCKET_NAME: string;
+ BACKEND_SECURITY_KEY: string;
STORAGE: R2Bucket;
DATABASE: D1Database;
}
diff --git a/apps/web/package.json b/apps/web/package.json
index 70ad3b46..adca3031 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,5 +1,5 @@
{
- "name": "web",
+ "name": "@repo/web",
"version": "1.0.0",
"private": true,
"scripts": {
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index e5590d6b..ee2fb650 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -5,7 +5,10 @@
{
"name": "next"
}
- ]
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
},
"include": [
"cf-env.d.ts",