aboutsummaryrefslogtreecommitdiff
path: root/apps/web/app/api
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-06-23 20:45:05 -0500
committerDhravya <[email protected]>2024-06-23 20:45:05 -0500
commit75585fd97d7abaf68e0c314f41d0cef414a57b1e (patch)
tree6f98b26f81d3481c857108f1ec60794d30a6421a /apps/web/app/api
parentmade and documented the telegram bot (HYPE) (diff)
downloadsupermemory-75585fd97d7abaf68e0c314f41d0cef414a57b1e.tar.xz
supermemory-75585fd97d7abaf68e0c314f41d0cef414a57b1e.zip
some important housekeeping, crushed all build errors
Diffstat (limited to 'apps/web/app/api')
-rw-r--r--apps/web/app/api/store/route.ts114
-rw-r--r--apps/web/app/api/telegram/route.ts18
-rw-r--r--apps/web/app/api/unfirlsite/route.ts162
3 files changed, 100 insertions, 194 deletions
diff --git a/apps/web/app/api/store/route.ts b/apps/web/app/api/store/route.ts
deleted file mode 100644
index cb10db24..00000000
--- a/apps/web/app/api/store/route.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { db } from "@/server/db";
-import { and, eq, sql, inArray } from "drizzle-orm";
-import {
- contentToSpace,
- sessions,
- storedContent,
- users,
- space,
-} from "@/server/db/schema";
-import { type NextRequest, NextResponse } from "next/server";
-import { getMetaData } from "@/lib/get-metadata";
-import { ensureAuth } from "../ensureAuth";
-import { limit } from "@/app/actions/doers";
-import { LIMITS } from "@/lib/constants";
-
-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 = [];
- }
-
- if (!(await limit(session.user.id))) {
- return NextResponse.json(
- {
- message: "Error: Ratelimit exceeded",
- error: `You have exceeded the limit of ${LIMITS["page"]} pages.`,
- },
- { status: 429 },
- );
- }
-
- 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(),
- userId: 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/api/telegram/route.ts b/apps/web/app/api/telegram/route.ts
index 065a102a..b0d05655 100644
--- a/apps/web/app/api/telegram/route.ts
+++ b/apps/web/app/api/telegram/route.ts
@@ -1,6 +1,6 @@
import { db } from "@/server/db";
import { storedContent, users } from "@/server/db/schema";
-import { cipher, decipher } from "@/server/encrypt";
+import { cipher } from "@/server/encrypt";
import { eq } from "drizzle-orm";
import { Bot, webhookCallback } from "grammy";
import { User } from "grammy/types";
@@ -16,16 +16,9 @@ const token = process.env.TELEGRAM_BOT_TOKEN;
const bot = new Bot(token);
-const getUserByTelegramId = async (telegramId: string) => {
- return await db.query.users
- .findFirst({
- where: eq(users.telegramId, telegramId),
- })
- .execute();
-};
-
bot.command("start", async (ctx) => {
const user: User = (await ctx.getAuthor()).user;
+
const cipherd = cipher(user.id.toString());
await ctx.reply(
`Welcome to Supermemory bot. I am here to help you remember things better. Click here to create and link your accont: http://localhost:3000/signin?telegramUser=${cipherd}`,
@@ -34,9 +27,14 @@ bot.command("start", async (ctx) => {
bot.on("message", async (ctx) => {
const user: User = (await ctx.getAuthor()).user;
+
const cipherd = cipher(user.id.toString());
- const dbUser = await getUserByTelegramId(user.id.toString());
+ const dbUser = await db.query.users
+ .findFirst({
+ where: eq(users.telegramId, user.id.toString()),
+ })
+ .execute();
if (!dbUser) {
await ctx.reply(
diff --git a/apps/web/app/api/unfirlsite/route.ts b/apps/web/app/api/unfirlsite/route.ts
index 4b8b4858..87f5348e 100644
--- a/apps/web/app/api/unfirlsite/route.ts
+++ b/apps/web/app/api/unfirlsite/route.ts
@@ -1,4 +1,4 @@
-import { load } from 'cheerio'
+import { load } from "cheerio";
import { AwsClient } from "aws4fetch";
import type { NextRequest } from "next/server";
@@ -6,14 +6,60 @@ import { ensureAuth } from "../ensureAuth";
export const runtime = "edge";
-const r2 = new AwsClient({
- accessKeyId: process.env.R2_ACCESS_KEY_ID,
- secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
-});
+export async function POST(request: NextRequest) {
+ const r2 = new AwsClient({
+ accessKeyId: process.env.R2_ACCESS_KEY_ID,
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
+ });
+
+ async function unfurl(url: string) {
+ const response = await fetch(url);
+ if (response.status >= 400) {
+ throw new Error(`Error fetching url: ${response.status}`);
+ }
+ const contentType = response.headers.get("content-type");
+ if (!contentType?.includes("text/html")) {
+ throw new Error(`Content-type not right: ${contentType}`);
+ }
+ const content = await response.text();
+ const $ = load(content);
+
+ const og: { [key: string]: string | undefined } = {};
+ const twitter: { [key: string]: string | undefined } = {};
+
+ // @ts-ignore, it just works so why care of type safety if someone has better way go ahead
+ $("meta[property^=og:]").each(
+ (_, el) => (og[$(el).attr("property")!] = $(el).attr("content")),
+ );
+ // @ts-ignore
+ $("meta[name^=twitter:]").each(
+ (_, el) => (twitter[$(el).attr("name")!] = $(el).attr("content")),
+ );
+
+ const title =
+ og["og:title"] ??
+ twitter["twitter:title"] ??
+ $("title").text() ??
+ undefined;
+ const description =
+ og["og:description"] ??
+ twitter["twitter:description"] ??
+ $('meta[name="description"]').attr("content") ??
+ undefined;
+ const image =
+ og["og:image:secure_url"] ??
+ og["og:image"] ??
+ twitter["twitter:image"] ??
+ undefined;
+
+ return {
+ title,
+ description,
+ image,
+ };
+ }
-export async function POST(request: NextRequest) {
-
const d = await ensureAuth(request);
if (!d) {
return new Response("Unauthorized", { status: 401 });
@@ -32,7 +78,7 @@ export async function POST(request: NextRequest) {
}
const website = new URL(request.url).searchParams.get("website");
-
+
if (!website) {
return new Response("Missing website", { status: 400 });
}
@@ -41,27 +87,33 @@ export async function POST(request: NextRequest) {
const encodeWebsite = `${encodeURIComponent(website)}${salt()}`;
try {
- // this returns the og image, description and title of website
+ // this returns the og image, description and title of website
const response = await unfurl(website);
- if (!response.image){
- return new Response(JSON.stringify(response))
+ if (!response.image) {
+ return new Response(JSON.stringify(response));
}
- const imageUrl = await process.env.DEV_IMAGES.get(encodeWebsite)
- if (imageUrl){
- return new Response(JSON.stringify({
- image: imageUrl,
- title: response.title,
- description: response.description,
- }))
+ if (!process.env.DEV_IMAGES) {
+ return new Response("Missing DEV_IMAGES namespace.", { status: 500 });
}
- const res = await fetch(`${response.image}`)
+ const imageUrl = await process.env.DEV_IMAGES!.get(encodeWebsite);
+ if (imageUrl) {
+ return new Response(
+ JSON.stringify({
+ image: imageUrl,
+ title: response.title,
+ description: response.description,
+ }),
+ );
+ }
+
+ const res = await fetch(`${response.image}`);
const image = await res.blob();
const url = new URL(
- `https://${process.env.R2_BUCKET_NAME}.${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`
+ `https://${process.env.R2_BUCKET_NAME}.${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
);
url.pathname = encodeWebsite;
@@ -73,62 +125,32 @@ export async function POST(request: NextRequest) {
}),
{
aws: { signQuery: true },
- }
+ },
);
await fetch(signedPuturl.url, {
- method: 'PUT',
+ method: "PUT",
body: image,
});
- await process.env.DEV_IMAGES.put(encodeWebsite, `${process.env.R2_PUBLIC_BUCKET_ADDRESS}/${encodeWebsite}`)
-
- return new Response(JSON.stringify({
- image: `${process.env.R2_PUBLIC_BUCKET_ADDRESS}/${encodeWebsite}`,
- title: response.title,
- description: response.description,
- }));
+ await process.env.DEV_IMAGES.put(
+ encodeWebsite,
+ `${process.env.R2_PUBLIC_BUCKET_ADDRESS}/${encodeWebsite}`,
+ );
+ return new Response(
+ JSON.stringify({
+ image: `${process.env.R2_PUBLIC_BUCKET_ADDRESS}/${encodeWebsite}`,
+ title: response.title,
+ description: response.description,
+ }),
+ );
} catch (error) {
- console.log(error)
- return new Response(JSON.stringify({
- status: 500,
- error: error,
- }))
- }
+ console.log(error);
+ return new Response(
+ JSON.stringify({
+ status: 500,
+ error: error,
+ }),
+ );
}
-
-export async function unfurl(url: string) {
- const response = await fetch(url)
- if (response.status >= 400) {
- throw new Error(`Error fetching url: ${response.status}`)
- }
- const contentType = response.headers.get('content-type')
- if (!contentType?.includes('text/html')) {
- throw new Error(`Content-type not right: ${contentType}`)
- }
-
- const content = await response.text()
- const $ = load(content)
-
- const og: { [key: string]: string | undefined } = {}
- const twitter: { [key: string]: string | undefined } = {}
-
- // @ts-ignore, it just works so why care of type safety if someone has better way go ahead
- $('meta[property^=og:]').each((_, el) => (og[$(el).attr('property')!] = $(el).attr('content')))
- // @ts-ignore
- $('meta[name^=twitter:]').each((_, el) => (twitter[$(el).attr('name')!] = $(el).attr('content')))
-
- const title = og['og:title'] ?? twitter['twitter:title'] ?? $('title').text() ?? undefined
- const description =
- og['og:description'] ??
- twitter['twitter:description'] ??
- $('meta[name="description"]').attr('content') ??
- undefined
- const image = og['og:image:secure_url'] ?? og['og:image'] ?? twitter['twitter:image'] ?? undefined
-
- return {
- title,
- description,
- image,
- }
}