diff options
| author | Dhravya Shah <[email protected]> | 2025-03-03 10:59:34 -0600 |
|---|---|---|
| committer | Dhravya Shah <[email protected]> | 2025-03-03 10:59:34 -0600 |
| commit | ff14d77c11153b4679967b6aa26600a2b7a0e2a0 (patch) | |
| tree | 3936616ec2341117ebbfc237d4c1fcef2369694e /apps/backend/src | |
| parent | Merge pull request #338 from supermemoryai/Dhravya-patch-2 (diff) | |
| download | supermemory-ff14d77c11153b4679967b6aa26600a2b7a0e2a0.tar.xz supermemory-ff14d77c11153b4679967b6aa26600a2b7a0e2a0.zip | |
auto generated openapi schema
Diffstat (limited to 'apps/backend/src')
| -rw-r--r-- | apps/backend/src/index.tsx | 377 | ||||
| -rw-r--r-- | apps/backend/src/minimal.ts | 18 | ||||
| -rw-r--r-- | apps/backend/src/routes/actions.ts | 6 | ||||
| -rw-r--r-- | apps/backend/src/routes/integrations.ts | 321 | ||||
| -rw-r--r-- | apps/backend/src/routes/memories.ts | 5 | ||||
| -rw-r--r-- | apps/backend/src/routes/spaces.ts | 10 | ||||
| -rw-r--r-- | apps/backend/src/routes/user.ts | 42 |
7 files changed, 414 insertions, 365 deletions
diff --git a/apps/backend/src/index.tsx b/apps/backend/src/index.tsx index 48790e90..1e61304d 100644 --- a/apps/backend/src/index.tsx +++ b/apps/backend/src/index.tsx @@ -17,6 +17,7 @@ import spacesRoute from "./routes/spaces"; import actions from "./routes/actions"; import memories from "./routes/memories"; import integrations from "./routes/integrations"; +import { fromHono, OpenAPIRoute } from "chanfana"; import { cloudflareRateLimiter, DurableObjectRateLimiter, @@ -24,190 +25,203 @@ import { } from "@hono-rate-limiter/cloudflare"; import { ConfigType, GeneralConfigType, rateLimiter } from "hono-rate-limiter"; -export const app = new Hono<{ Variables: Variables; Bindings: Env }>() - .use("*", timing()) - .use("*", logger()) - .use( - "*", - cors({ - origin: [ - "http://localhost:3000", - "https://supermemory.ai", - "https://*.supermemory.ai", - "https://*.supermemory.com", - "https://supermemory.com", - "chrome-extension://*", - ], - allowHeaders: ["*"], - allowMethods: ["*"], - credentials: true, - exposeHeaders: ["*"], - }) - ) - .use("/v1/*", auth) - .use("/v1/*", (c, next) => { - const user = c.get("user"); - - if (c.env.NODE_ENV === "development") { - return next(); - } +// Create base Hono app first +const honoApp = new Hono<{ Variables: Variables; Bindings: Env }>(); + +const app = fromHono(honoApp, { + base: "", +}); + +// Add all middleware and routes +app.use("*", timing()); +app.use("*", logger()); +app.use( + "*", + cors({ + origin: [ + "http://localhost:3000", + "https://supermemory.ai", + "https://*.supermemory.ai", + "https://*.supermemory.com", + "https://supermemory.com", + "chrome-extension://*", + ], + allowHeaders: ["*"], + allowMethods: ["*"], + credentials: true, + exposeHeaders: ["*"], + }) +); + +app.use("/v1/*", auth); +app.use("/v1/*", (c, next) => { + const user = c.get("user"); - // RATELIMITS - const rateLimitConfig = { - // Endpoints that bypass rate limiting - excludedPaths: [ - "/v1/add", - "/v1/chat", - "/v1/suggested-learnings", - "/v1/recommended-questions", - ] as (string | RegExp)[], - - // Custom rate limits for specific endpoints - customLimits: { - notionImport: { - paths: ["/v1/integrations/notion/import", "/v1/integrations/notion"], - windowMs: 10 * 60 * 1000, // 10 minutes - limit: 5, // 5 requests per 10 minutes - }, - inviteSpace: { - paths: [/^\v1\/spaces\/[^/]+\/invite$/], - windowMs: 60 * 1000, // 1 minute - limit: 5, // 5 requests per minute - }, - } as Record< - string, - { paths: (string | RegExp)[]; windowMs: number; limit: number } - >, - - default: { + if (c.env.NODE_ENV === "development") { + return next(); + } + + // RATELIMITS + const rateLimitConfig = { + // Endpoints that bypass rate limiting + excludedPaths: [ + "/v1/add", + "/v1/chat", + "/v1/suggested-learnings", + "/v1/recommended-questions", + ] as (string | RegExp)[], + + // Custom rate limits for specific endpoints + customLimits: { + notionImport: { + paths: ["/v1/integrations/notion/import", "/v1/integrations/notion"], + windowMs: 10 * 60 * 1000, // 10 minutes + limit: 5, // 5 requests per 10 minutes + }, + inviteSpace: { + paths: [/^\v1\/spaces\/[^/]+\/invite$/], windowMs: 60 * 1000, // 1 minute - limit: 100, // 100 requests per minute + limit: 5, // 5 requests per minute }, + } as Record< + string, + { paths: (string | RegExp)[]; windowMs: number; limit: number } + >, + + default: { + windowMs: 60 * 1000, // 1 minute + limit: 100, // 100 requests per minute + }, - common: { - standardHeaders: "draft-6", - keyGenerator: (c: Context) => - (user?.uuid ?? c.req.header("cf-connecting-ip")) + - "-" + - new Date().getDate(), // day so that limit gets reset every day - store: new DurableObjectStore({ namespace: c.env.RATE_LIMITER }), - } as GeneralConfigType<ConfigType>, - }; + common: { + standardHeaders: "draft-6", + keyGenerator: (c: Context) => + (user?.uuid ?? c.req.header("cf-connecting-ip")) + + "-" + + new Date().getDate(), // day so that limit gets reset every day + store: new DurableObjectStore({ namespace: c.env.RATE_LIMITER }), + } as GeneralConfigType<ConfigType>, + }; + if ( + c.req.path && + rateLimitConfig.excludedPaths.some((path) => + typeof path === "string" ? c.req.path === path : path.test(c.req.path) + ) + ) { + return next(); + } + + // Check for custom rate limits + for (const [_, config] of Object.entries(rateLimitConfig.customLimits)) { if ( - c.req.path && - rateLimitConfig.excludedPaths.some((path) => + config.paths.some((path) => typeof path === "string" ? c.req.path === path : path.test(c.req.path) ) ) { - return next(); + return rateLimiter({ + windowMs: config.windowMs, + limit: config.limit, + ...rateLimitConfig.common, + })(c as any, next); } + } - // Check for custom rate limits - for (const [_, config] of Object.entries(rateLimitConfig.customLimits)) { - if ( - config.paths.some((path) => - typeof path === "string" ? c.req.path === path : path.test(c.req.path) - ) - ) { - return rateLimiter({ - windowMs: config.windowMs, - limit: config.limit, - ...rateLimitConfig.common, - })(c as any, next); - } - } + // Apply default rate limit + return rateLimiter({ + windowMs: rateLimitConfig.default.windowMs, + limit: rateLimitConfig.default.limit, + ...rateLimitConfig.common, + })(c as any, next); +}); - // Apply default rate limit - return rateLimiter({ - windowMs: rateLimitConfig.default.windowMs, - limit: rateLimitConfig.default.limit, - ...rateLimitConfig.common, - })(c as any, next); - }) - .get("/", (c) => { - return c.html(<LandingPage />); - }) - // TEMPORARY REDIRECT - .all("/api/*", async (c) => { - // Get the full URL and path - const url = new URL(c.req.url); - const path = url.pathname; - const newPath = path.replace("/api", "/v1"); - - // Preserve query parameters and build target URL - const redirectUrl = "https://api.supermemory.ai" + newPath + url.search; - - // Use c.redirect() for a proper redirect - return c.redirect(redirectUrl); - }) - .route("/v1/user", user) - .route("/v1/spaces", spacesRoute) - .route("/v1", actions) - .route("/v1/integrations", integrations) - .route("/v1/memories", memories) - .get("/v1/session", (c) => { - const user = c.get("user"); - - if (!user) { - return c.json({ error: "Unauthorized" }, 401); - } +app.get("/", (c) => { + return c.html(<LandingPage />); +}); + +// TEMPORARY REDIRECT +app.all("/api/*", async (c) => { + // Get the full URL and path + const url = new URL(c.req.url); + const path = url.pathname; + const newPath = path.replace("/api", "/v1"); + + // Preserve query parameters and build target URL + const redirectUrl = "https://api.supermemory.ai" + newPath + url.search; - return c.json({ - user, + // Use c.redirect() for a proper redirect + return c.redirect(redirectUrl); +}); + +app.route("/v1/user", user); +app.route("/v1/spaces", spacesRoute); +app.route("/v1", actions); +app.route("/v1/integrations", integrations); +app.route("/v1/memories", memories); + +app.get("/v1/session", (c) => { + const user = c.get("user"); + + if (!user) { + return c.json({ error: "Unauthorized" }, 401); + } + + return c.json({ + user, + }); +}); + +app.post( + "/waitlist", + zValidator( + "json", + z.object({ email: z.string().email(), token: z.string() }) + ), + async (c) => { + const { email, token } = c.req.valid("json"); + + const address = c.req.raw.headers.get("CF-Connecting-IP"); + + const idempotencyKey = crypto.randomUUID(); + const url = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; + const firstResult = await fetch(url, { + body: JSON.stringify({ + secret: c.env.TURNSTILE_SECRET_KEY, + response: token, + remoteip: address, + idempotency_key: idempotencyKey, + }), + method: "POST", + headers: { + "Content-Type": "application/json", + }, }); - }) - .post( - "/waitlist", - zValidator( - "json", - z.object({ email: z.string().email(), token: z.string() }) - ), - async (c) => { - const { email, token } = c.req.valid("json"); - - const address = c.req.raw.headers.get("CF-Connecting-IP"); - - const idempotencyKey = crypto.randomUUID(); - const url = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; - const firstResult = await fetch(url, { - body: JSON.stringify({ - secret: c.env.TURNSTILE_SECRET_KEY, - response: token, - remoteip: address, - idempotency_key: idempotencyKey, - }), - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }); - const firstOutcome = (await firstResult.json()) as { success: boolean }; + const firstOutcome = (await firstResult.json()) as { success: boolean }; - if (!firstOutcome.success) { - console.info("Turnstile verification failed", firstOutcome); - return c.json( - { error: "Turnstile verification failed" }, - 439 as StatusCode - ); - } + if (!firstOutcome.success) { + console.info("Turnstile verification failed", firstOutcome); + return c.json( + { error: "Turnstile verification failed" }, + 439 as StatusCode + ); + } - const resend = new Resend(c.env.RESEND_API_KEY); + const resend = new Resend(c.env.RESEND_API_KEY); - const db = database(c.env.HYPERDRIVE.connectionString); + const db = database(c.env.HYPERDRIVE.connectionString); - const ip = - c.req.header("cf-connecting-ip") || - `${c.req.raw.cf?.asn}-${c.req.raw.cf?.country}-${c.req.raw.cf?.city}-${c.req.raw.cf?.region}-${c.req.raw.cf?.postalCode}`; + const ip = + c.req.header("cf-connecting-ip") || + `${c.req.raw.cf?.asn}-${c.req.raw.cf?.country}-${c.req.raw.cf?.city}-${c.req.raw.cf?.region}-${c.req.raw.cf?.postalCode}`; - const { success } = await c.env.EMAIL_LIMITER.limit({ key: ip }); + const { success } = await c.env.EMAIL_LIMITER.limit({ key: ip }); - if (!success) { - return c.json({ error: "Rate limit exceeded" }, 429); - } + if (!success) { + return c.json({ error: "Rate limit exceeded" }, 429); + } - const message = `Supermemory started as a side project a few months ago when I built it as a hackathon project. + const message = `Supermemory started as a side project a few months ago when I built it as a hackathon project. <br></br> you guys loved it too much. like wayy too much. it was embarrassing, because this was not it - it was nothing but a hackathon project. <br></br> @@ -216,26 +230,27 @@ export const app = new Hono<{ Variables: Variables; Bindings: Env }>() So, it's time to make this good. My vision is to make supermemory the best memory tool on the internet. `; - try { - await db.insert(waitlist).values({ email }); - await resend.emails.send({ - from: "Dhravya From Supermemory <[email protected]>", - to: email, - subject: "You're in the waitlist - A personal note from Dhravya", - html: `<p>Hi. I'm Dhravya. I'm building Supermemory to help people remember everything.<br></br> ${message} <br></br><br></br>I'll be in touch when we launch! Till then, just reply to this email if you wanna talk :)<br></br>If you want to follow me on X, here's my handle: <a href='https://x.com/dhravyashah'>@dhravyashah</a><br></br><br></br>- Dhravya</p>`, - }); - } catch (e) { - console.error(e); - return c.json({ error: "Failed to add to waitlist" }, 400); - } - - return c.json({ success: true }); + try { + await db.insert(waitlist).values({ email }); + await resend.emails.send({ + from: "Dhravya From Supermemory <[email protected]>", + to: email, + subject: "You're in the waitlist - A personal note from Dhravya", + html: `<p>Hi. I'm Dhravya. I'm building Supermemory to help people remember everything.<br></br> ${message} <br></br><br></br>I'll be in touch when we launch! Till then, just reply to this email if you wanna talk :)<br></br>If you want to follow me on X, here's my handle: <a href='https://x.com/dhravyashah'>@dhravyashah</a><br></br><br></br>- Dhravya</p>`, + }); + } catch (e) { + console.error(e); + return c.json({ error: "Failed to add to waitlist" }, 400); } - ) - .onError((err, c) => { - console.error(err); - return c.json({ error: "Internal server error" }, 500); - }); + + return c.json({ success: true }); + } +); + +app.onError((err, c) => { + console.error(err); + return c.json({ error: "Internal server error" }, 500); +}); export default { fetch: app.fetch, diff --git a/apps/backend/src/minimal.ts b/apps/backend/src/minimal.ts new file mode 100644 index 00000000..4647dd33 --- /dev/null +++ b/apps/backend/src/minimal.ts @@ -0,0 +1,18 @@ +import { fromHono } from "chanfana"; +import { Hono } from "hono"; +import { serve as serveHono } from "@hono/node-server"; + +const app = fromHono(new Hono()); + +app.get("/", (c) => { + return c.json({ message: "Hello, world!" }); +}); + +app.get("/entry/:id", (c) => { + const id = c.req.param("id"); + return c.json({ message: `Hello, ${id}!` }); +}); + +serveHono(app, (info) => { + console.log(info); +}); diff --git a/apps/backend/src/routes/actions.ts b/apps/backend/src/routes/actions.ts index 37cc18e4..f26b46a5 100644 --- a/apps/backend/src/routes/actions.ts +++ b/apps/backend/src/routes/actions.ts @@ -38,8 +38,11 @@ import { } from "@supermemory/db"; import { typeDecider } from "../utils/typeDecider"; import { isErr, Ok } from "../errors/results"; +import { fromHono } from "chanfana"; -const actions = new Hono<{ Variables: Variables; Bindings: Env }>() +const actions = fromHono(new Hono<{ Variables: Variables; Bindings: Env }>(), { + base: "", +}) .post( "/chat", zValidator( @@ -1195,3 +1198,4 @@ const actions = new Hono<{ Variables: Variables; Bindings: Env }>() ); export default actions; +import { Context } from 'hono/jsx' diff --git a/apps/backend/src/routes/integrations.ts b/apps/backend/src/routes/integrations.ts index a4ba98f7..a76563d7 100644 --- a/apps/backend/src/routes/integrations.ts +++ b/apps/backend/src/routes/integrations.ts @@ -5,173 +5,180 @@ import { getAllNotionPageContents } from "../utils/notion"; import { and, eq, or } from "@supermemory/db"; import { documents } from "@supermemory/db/schema"; import { database } from "@supermemory/db"; +import { fromHono } from "chanfana"; + +const integrations = fromHono( + new Hono<{ Variables: Variables; Bindings: Env }>(), + { + base: "", + } +).get("/notion/import", async (c) => { + const user = c.get("user"); + if (!user) { + return c.json({ error: "Unauthorized" }, 401); + } + + // Create SSE stream + const stream = new TransformStream(); + const writer = stream.writable.getWriter(); + const encoder = new TextEncoder(); + + // Create response first so client gets headers immediately + const response = new Response(stream.readable, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + // Required CORS headers for SSE + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Credentials": "true", + }, + }); -const integrations = new Hono<{ Variables: Variables; Bindings: Env }>().get("/notion/import", async (c) => { - const user = c.get("user"); - if (!user) { - return c.json({ error: "Unauthorized" }, 401); - } - - // Create SSE stream - const stream = new TransformStream(); - const writer = stream.writable.getWriter(); - const encoder = new TextEncoder(); - - // Create response first so client gets headers immediately - const response = new Response(stream.readable, { - headers: { - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - Connection: "keep-alive", - // Required CORS headers for SSE - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Credentials": "true", - }, - }); - - const sendMessage = async (data: Record<string, any>) => { - // Proper SSE format requires "data: " prefix and double newline - const formattedData = `data: ${JSON.stringify(data)}\n\n`; - await writer.write(encoder.encode(formattedData)); - }; - - // Start processing in background - c.executionCtx.waitUntil( - (async () => { - try { - // Send initial heartbeat - await sendMessage({ type: "connected" }); - - const token = await getDecryptedKV( - c.env.ENCRYPTED_TOKENS, - `${user.uuid}-notion`, - `${c.env.WORKOS_COOKIE_PASSWORD}-${user.uuid}` - ); - - const stringToken = new TextDecoder().decode(token); - if (!stringToken) { - await sendMessage({ type: "error", error: "No token found" }); - await writer.close(); - return; - } + const sendMessage = async (data: Record<string, any>) => { + // Proper SSE format requires "data: " prefix and double newline + const formattedData = `data: ${JSON.stringify(data)}\n\n`; + await writer.write(encoder.encode(formattedData)); + }; + + // Start processing in background + c.executionCtx.waitUntil( + (async () => { + try { + // Send initial heartbeat + await sendMessage({ type: "connected" }); + + const token = await getDecryptedKV( + c.env.ENCRYPTED_TOKENS, + `${user.uuid}-notion`, + `${c.env.WORKOS_COOKIE_PASSWORD}-${user.uuid}` + ); + + const stringToken = new TextDecoder().decode(token); + if (!stringToken) { + await sendMessage({ type: "error", error: "No token found" }); + await writer.close(); + return; + } - await sendMessage({ type: "progress", progress: 5 }); - - // Fetch pages with progress updates - const pages = await getAllNotionPageContents( - stringToken, - async (progress) => { - // Map progress from 0-100 to 5-40 range - const scaledProgress = Math.floor(5 + (progress * 35) / 100); - await sendMessage({ type: "progress", progress: scaledProgress }); - } - ); - - await sendMessage({ type: "progress", progress: 40 }); - - let processed = 0; - const totalPages = pages.length; - - const db = database(c.env.HYPERDRIVE.connectionString); - - for (const page of pages) { - // Calculate document hash for duplicate detection - const encoder = new TextEncoder(); - const data = encoder.encode(page.content); - const hashBuffer = await crypto.subtle.digest("SHA-256", data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const documentHash = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); - - // Check for duplicates using hash - const existingDocs = await db - .select() - .from(documents) - .where( - and( - eq(documents.userId, user.id), - or( - eq(documents.contentHash, documentHash), - and( - eq(documents.type, "notion"), - or( - eq(documents.url, page.url), - eq(documents.raw, page.content) - ) + await sendMessage({ type: "progress", progress: 5 }); + + // Fetch pages with progress updates + const pages = await getAllNotionPageContents( + stringToken, + async (progress) => { + // Map progress from 0-100 to 5-40 range + const scaledProgress = Math.floor(5 + (progress * 35) / 100); + await sendMessage({ type: "progress", progress: scaledProgress }); + } + ); + + await sendMessage({ type: "progress", progress: 40 }); + + let processed = 0; + const totalPages = pages.length; + + const db = database(c.env.HYPERDRIVE.connectionString); + + for (const page of pages) { + // Calculate document hash for duplicate detection + const encoder = new TextEncoder(); + const data = encoder.encode(page.content); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const documentHash = hashArray + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + + // Check for duplicates using hash + const existingDocs = await db + .select() + .from(documents) + .where( + and( + eq(documents.userId, user.id), + or( + eq(documents.contentHash, documentHash), + and( + eq(documents.type, "notion"), + or( + eq(documents.url, page.url), + eq(documents.raw, page.content) ) ) ) - ); - - if (existingDocs.length > 0) { - await sendMessage({ - type: "warning", - message: `Skipping duplicate page: ${page.title}` - }); - processed++; - continue; - } - - // Insert into documents table first - try { - await db.insert(documents).values({ - uuid: page.id, + ) + ); + + if (existingDocs.length > 0) { + await sendMessage({ + type: "warning", + message: `Skipping duplicate page: ${page.title}`, + }); + processed++; + continue; + } + + // Insert into documents table first + try { + await db.insert(documents).values({ + uuid: page.id, + userId: user.id, + type: "notion", + url: page.url, + title: page.title, + contentHash: documentHash, + raw: page.content, + }); + + await c.env.CONTENT_WORKFLOW.create({ + params: { userId: user.id, + content: page.url, + spaces: [], type: "notion", + uuid: page.id, url: page.url, - title: page.title, - contentHash: documentHash, - raw: page.content, - }); - - await c.env.CONTENT_WORKFLOW.create({ - params: { - userId: user.id, - content: page.url, - spaces: [], + prefetched: { + contentToVectorize: page.content, + contentToSave: page.content, + title: page.title, type: "notion", - uuid: page.id, - url: page.url, - prefetched: { - contentToVectorize: page.content, - contentToSave: page.content, - title: page.title, - type: "notion", - }, - createdAt: page.createdAt, }, - id: `${user.id}-${page.id}-${new Date().getTime()}`, - }); - - processed++; - const progress = 50 + Math.floor((processed / totalPages) * 50); - await sendMessage({ type: "progress", progress, page: page.title }); - - } catch (error) { - console.error(`Failed to process page ${page.title}:`, error); - await sendMessage({ - type: "warning", - message: `Failed to process page: ${page.title}`, - error: error instanceof Error ? error.message : "Unknown error" - }); - processed++; - continue; - } + createdAt: page.createdAt, + }, + id: `${user.id}-${page.id}-${new Date().getTime()}`, + }); + + processed++; + const progress = 50 + Math.floor((processed / totalPages) * 50); + await sendMessage({ type: "progress", progress, page: page.title }); + } catch (error) { + console.error(`Failed to process page ${page.title}:`, error); + await sendMessage({ + type: "warning", + message: `Failed to process page: ${page.title}`, + error: error instanceof Error ? error.message : "Unknown error", + }); + processed++; + continue; } - - await sendMessage({ type: "complete", progress: 100 }); - await writer.close(); - } catch (error) { - console.error("Import error:", error); - await sendMessage({ - type: "error", - error: error instanceof Error ? error.message : "Import failed", - }); - await writer.close(); } - })() - ); - - return response; - }); -export default integrations;
\ No newline at end of file + await sendMessage({ type: "complete", progress: 100 }); + await writer.close(); + } catch (error) { + console.error("Import error:", error); + await sendMessage({ + type: "error", + error: error instanceof Error ? error.message : "Import failed", + }); + await writer.close(); + } + })() + ); + + return response; +}); + +export default integrations; diff --git a/apps/backend/src/routes/memories.ts b/apps/backend/src/routes/memories.ts index 48d16f74..f895bcbf 100644 --- a/apps/backend/src/routes/memories.ts +++ b/apps/backend/src/routes/memories.ts @@ -9,8 +9,11 @@ import { contentToSpace, } from "@supermemory/db/schema"; import { and, database, desc, eq, or, sql, isNull } from "@supermemory/db"; +import { fromHono } from "chanfana"; -const memories = new Hono<{ Variables: Variables; Bindings: Env }>() +const memories = fromHono(new Hono<{ Variables: Variables; Bindings: Env }>(), { + base: "", +}) .get( "/", zValidator( diff --git a/apps/backend/src/routes/spaces.ts b/apps/backend/src/routes/spaces.ts index cb0cf417..c051217a 100644 --- a/apps/backend/src/routes/spaces.ts +++ b/apps/backend/src/routes/spaces.ts @@ -12,8 +12,14 @@ import { import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; import { randomId } from "@supermemory/shared"; - -const spacesRoute = new Hono<{ Variables: Variables; Bindings: Env }>() +import { fromHono } from "chanfana"; + +const spacesRoute = fromHono( + new Hono<{ Variables: Variables; Bindings: Env }>(), + { + base: "", + } +) .get("/", async (c) => { const user = c.get("user"); if (!user) { diff --git a/apps/backend/src/routes/user.ts b/apps/backend/src/routes/user.ts index 0d45737f..761b2fa4 100644 --- a/apps/backend/src/routes/user.ts +++ b/apps/backend/src/routes/user.ts @@ -11,8 +11,11 @@ import { import { decryptApiKey, getApiKey } from "../auth"; import { DurableObjectStore } from "@hono-rate-limiter/cloudflare"; import { rateLimiter } from "hono-rate-limiter"; +import { fromHono } from "chanfana"; -const user = new Hono<{ Variables: Variables; Bindings: Env }>() +const user = fromHono(new Hono<{ Variables: Variables; Bindings: Env }>(), { + base: "", +}) .get("/", (c) => { return c.json(c.get("user")); }) @@ -132,30 +135,23 @@ const user = new Hono<{ Variables: Variables; Bindings: Env }>() return c.json({ invitations }); }) - .get( - "/key", - async (c) => { - const user = c.get("user"); - if (!user) { - return c.json({ error: "Unauthorized" }, 401); - } + .get("/key", async (c) => { + const user = c.get("user"); + if (!user) { + return c.json({ error: "Unauthorized" }, 401); + } - // we need user.id and user.lastApiKeyGeneratedAt - const lastApiKeyGeneratedAt = user.lastApiKeyGeneratedAt?.getTime(); - if (!lastApiKeyGeneratedAt) { - return c.json({ error: "No API key generated" }, 400); - } + // we need user.id and user.lastApiKeyGeneratedAt + const lastApiKeyGeneratedAt = user.lastApiKeyGeneratedAt?.getTime(); + if (!lastApiKeyGeneratedAt) { + return c.json({ error: "No API key generated" }, 400); + } - const key = await getApiKey( - user.uuid, - lastApiKeyGeneratedAt.toString(), - c - ); + const key = await getApiKey(user.uuid, lastApiKeyGeneratedAt.toString(), c); - const decrypted = await decryptApiKey(key, c); - return c.json({ key, decrypted }); - } - ) + const decrypted = await decryptApiKey(key, c); + return c.json({ key, decrypted }); + }) .post("/update", async (c) => { const user = c.get("user"); if (!user) { @@ -194,6 +190,6 @@ const user = new Hono<{ Variables: Variables; Bindings: Env }>() .where(eq(users.id, user.id)); return c.json({ success: true }); - }) + }); export default user; |