From 941ce8e4fa06fecda5b3b4ca7496002eff3d4143 Mon Sep 17 00:00:00 2001 From: Harshith Mullapudi Date: Mon, 16 Jun 2025 21:50:30 +0530 Subject: [PATCH] Fix: authentication and ingest API --- README.md | 6 +- apps/webapp/app/lib/ingest.server.ts | 3 +- apps/webapp/app/routes/login.magic.tsx | 148 ++++++++++-------- apps/webapp/app/routes/search.tsx | 4 +- apps/webapp/app/services/auth.server.ts | 4 +- apps/webapp/app/services/email.server.ts | 6 +- apps/webapp/app/services/emailAuth.server.tsx | 3 + apps/webapp/package.json | 1 + docker-compose.yaml | 100 ++++++------ pnpm-lock.yaml | 8 + turbo.json | 3 +- 11 files changed, 158 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 5f8ac70..8c34821 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ C.O.R.E lets you create a private, portable, open-source memory space for LLMs, all stored locally for full data control. You decide what to share or connect with other tools, managing exactly what gets recalled and where. C.O.R.E is built for two reasons: + 1. To give you complete ownership of your memory, stored locally and accessible across any app needing LLM context. 2. To help SOL (your AI assistant) access your context, facts, and preferences for more relevant and personalized responses. @@ -25,11 +26,11 @@ C.O.R.E supports **temporal reasoning**, **relational memory**, and **traceabili ## Getting Started ### Prerequisites + 1. Docker 2. OpenAI API Key - -### Run C.O.R.E locally by +### Run C.O.R.E locally by 1. **Copy Environment Variables** @@ -97,7 +98,6 @@ You can also interact with C.O.R.E. programmatically via its APIs. { "episodeBody": "I love playing badminton", "referenceTime": "2024-06-01T12:00:00Z", - "type": "Conversation", // or "Text" "source": "user", // Which tool or user is ingesting "spaceId": "your-space-id", // optional, for multiple spaces "sessionId": "your-session-id" // optional diff --git a/apps/webapp/app/lib/ingest.server.ts b/apps/webapp/app/lib/ingest.server.ts index 6a40f6b..88eba52 100644 --- a/apps/webapp/app/lib/ingest.server.ts +++ b/apps/webapp/app/lib/ingest.server.ts @@ -76,9 +76,10 @@ export function getUserQueue(userId: string) { } export const IngestBodyRequest = z.object({ + name: z.string(), episodeBody: z.string(), referenceTime: z.string(), - type: z.enum([EpisodeType.Conversation, EpisodeType.Text]), // Assuming these are the EpisodeType values + metadata: z.record(z.union([z.string(), z.number()])), source: z.string(), spaceId: z.string().optional(), sessionId: z.string().optional(), diff --git a/apps/webapp/app/routes/login.magic.tsx b/apps/webapp/app/routes/login.magic.tsx index 95fb994..5dfda75 100644 --- a/apps/webapp/app/routes/login.magic.tsx +++ b/apps/webapp/app/routes/login.magic.tsx @@ -1,10 +1,17 @@ import { + createCookie, redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction, } from "@remix-run/node"; - +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; import { Form, useNavigation } from "@remix-run/react"; import { Inbox, Loader, Mail } from "lucide-react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; @@ -16,8 +23,7 @@ import { FormButtons } from "~/components/ui/FormButtons"; import { Header1 } from "~/components/ui/Headers"; import { Input } from "~/components/ui/input"; import { Paragraph } from "~/components/ui/Paragraph"; -import { TextLink } from "~/components/ui/TextLink"; - +import { Cookie } from "@mjackson/headers"; import { authenticator } from "~/services/auth.server"; import { getUserId } from "~/services/session.server"; import { @@ -65,10 +71,14 @@ export async function loader({ request }: LoaderFunctionArgs): Promise { } } + const magicLinkSent = new Cookie(request.headers.get("cookie") ?? "").has( + "core:magiclink", + ); + return typedjson( { emailLoginEnabled: true, - magicLinkSent: session.has("core:magiclink"), + magicLinkSent, magicLinkError, }, { @@ -93,14 +103,19 @@ export async function action({ request }: ActionFunctionArgs) { .parse(payload); if (action === "send") { - return await authenticator.authenticate("email-link", request); + const headers = await authenticator + .authenticate("email-link", request) + .catch((headers) => headers); + throw redirect("/login/magic", { headers }); } else { - const session = await getUserSession(request); - session.unset("core:magiclink"); + const myCookie = createCookie("core:magiclink"); - return redirect("/magic", { + return redirect("/login/magic", { headers: { - "Set-Cookie": await commitSession(session), + "Set-Cookie": await myCookie.serialize("", { + maxAge: 0, + path: "/", + }), }, }); } @@ -130,80 +145,81 @@ export default function LoginMagicLinkPage() {
{data.magicLinkSent ? ( - <> - - We've sent you a magic link! - -
- - + + + + {" "} + We've sent you a magic link! + + We sent you an email which contains a magic link that will log you in to your account. - + + + +
Re-enter email } - confirmButton={ - - } + confirmButton={<>} />
- + ) : ( - <> - - Welcome - - - Create an account or login using email - -
- + + + Welcome + + Create an account or login using email + + + +
+ - - {data.magicLinkError && <>{data.magicLinkError}} -
- + + {data.magicLinkError && <>{data.magicLinkError}} +
+ + )}
diff --git a/apps/webapp/app/routes/search.tsx b/apps/webapp/app/routes/search.tsx index 758fd9c..342a703 100644 --- a/apps/webapp/app/routes/search.tsx +++ b/apps/webapp/app/routes/search.tsx @@ -5,9 +5,11 @@ import { json } from "@remix-run/node"; export const SearchBodyRequest = z.object({ query: z.string(), - spaceId: z.string().optional(), startTime: z.string().optional(), endTime: z.string().optional(), + + // These are not supported yet, but need to support these + spaceId: z.string().optional(), limit: z.number().optional(), maxBfsDepth: z.number().optional(), includeInvalidated: z.boolean().optional(), diff --git a/apps/webapp/app/services/auth.server.ts b/apps/webapp/app/services/auth.server.ts index d4223f4..b5ba168 100644 --- a/apps/webapp/app/services/auth.server.ts +++ b/apps/webapp/app/services/auth.server.ts @@ -13,7 +13,9 @@ const authenticator = new Authenticator(); const isGoogleAuthSupported = typeof env.AUTH_GOOGLE_CLIENT_ID === "string" && - typeof env.AUTH_GOOGLE_CLIENT_SECRET === "string"; + env.AUTH_GOOGLE_CLIENT_ID.length > 0 && + typeof env.AUTH_GOOGLE_CLIENT_SECRET === "string" && + env.AUTH_GOOGLE_CLIENT_SECRET.length > 0; if (env.AUTH_GOOGLE_CLIENT_ID && env.AUTH_GOOGLE_CLIENT_SECRET) { addGoogleStrategy( diff --git a/apps/webapp/app/services/email.server.ts b/apps/webapp/app/services/email.server.ts index 5791010..1da6751 100644 --- a/apps/webapp/app/services/email.server.ts +++ b/apps/webapp/app/services/email.server.ts @@ -58,11 +58,6 @@ function buildTransportOptions(): MailTransportOptions { } export async function sendMagicLinkEmail(options: any): Promise { - // Auto redirect when in development mode - if (env.NODE_ENV === "development") { - throw redirect(options.magicLink); - } - logger.debug("Sending magic link email", { emailAddress: options.emailAddress, }); @@ -77,6 +72,7 @@ export async function sendMagicLinkEmail(options: any): Promise { logger.error("Error sending magic link email", { error: JSON.stringify(error), }); + throw error; } } diff --git a/apps/webapp/app/services/emailAuth.server.tsx b/apps/webapp/app/services/emailAuth.server.tsx index 7a3a305..3ba2371 100644 --- a/apps/webapp/app/services/emailAuth.server.tsx +++ b/apps/webapp/app/services/emailAuth.server.tsx @@ -16,6 +16,9 @@ const emailStrategy = new EmailLinkStrategy( sendEmail: sendMagicLinkEmail, secret, magicEndpoint: `${APP_ORIGIN}/magic`, + cookie: { + name: "core:magiclink", + }, }, async ({ email }: { email: string }) => { logger.info("Magic link user authenticated", { email }); diff --git a/apps/webapp/package.json b/apps/webapp/package.json index a60985c..dd0e278 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -18,6 +18,7 @@ "@core/database": "workspace:*", "@core/types": "workspace:*", "@opentelemetry/api": "1.9.0", + "@mjackson/headers": "0.11.1", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", diff --git a/docker-compose.yaml b/docker-compose.yaml index 164dbeb..ab9b395 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,58 +1,58 @@ version: "3.8" services: - core: - container_name: core-app - image: redplanethq/core:${VERSION} - environment: - - NODE_ENV=${NODE_ENV} - - DATABASE_URL=${DATABASE_URL} - - DIRECT_URL=${DIRECT_URL} - - SESSION_SECRET=${SESSION_SECRET} - - ENCRYPTION_KEY=${ENCRYPTION_KEY} - - MAGIC_LINK_SECRET=${MAGIC_LINK_SECRET} - - LOGIN_ORIGIN=${LOGIN_ORIGIN} - - APP_ORIGIN=${APP_ORIGIN} - - REDIS_HOST=${REDIS_HOST} - - REDIS_PORT=${REDIS_PORT} - - REDIS_TLS_DISABLED=${REDIS_TLS_DISABLED} - - NEO4J_URI=${NEO4J_URI} - - NEO4J_USERNAME=${NEO4J_USERNAME} - - NEO4J_PASSWORD=${NEO4J_PASSWORD} - - OPENAI_API_KEY=${OPENAI_API_KEY} - - AUTH_GOOGLE_CLIENT_ID=${AUTH_GOOGLE_CLIENT_ID} - - AUTH_GOOGLE_CLIENT_SECRET=${AUTH_GOOGLE_CLIENT_SECRET} - - ENABLE_EMAIL_LOGIN=${ENABLE_EMAIL_LOGIN} - ports: - - "3000:3000" - depends_on: - - postgres - - redis - - neo4j - networks: - - core + # core: + # container_name: core-app + # image: redplanethq/core:${VERSION} + # environment: + # - NODE_ENV=${NODE_ENV} + # - DATABASE_URL=${DATABASE_URL} + # - DIRECT_URL=${DIRECT_URL} + # - SESSION_SECRET=${SESSION_SECRET} + # - ENCRYPTION_KEY=${ENCRYPTION_KEY} + # - MAGIC_LINK_SECRET=${MAGIC_LINK_SECRET} + # - LOGIN_ORIGIN=${LOGIN_ORIGIN} + # - APP_ORIGIN=${APP_ORIGIN} + # - REDIS_HOST=${REDIS_HOST} + # - REDIS_PORT=${REDIS_PORT} + # - REDIS_TLS_DISABLED=${REDIS_TLS_DISABLED} + # - NEO4J_URI=${NEO4J_URI} + # - NEO4J_USERNAME=${NEO4J_USERNAME} + # - NEO4J_PASSWORD=${NEO4J_PASSWORD} + # - OPENAI_API_KEY=${OPENAI_API_KEY} + # - AUTH_GOOGLE_CLIENT_ID=${AUTH_GOOGLE_CLIENT_ID} + # - AUTH_GOOGLE_CLIENT_SECRET=${AUTH_GOOGLE_CLIENT_SECRET} + # - ENABLE_EMAIL_LOGIN=${ENABLE_EMAIL_LOGIN} + # ports: + # - "3033:3000" + # depends_on: + # - postgres + # - redis + # - neo4j + # networks: + # - core - postgres: - container_name: core-postgres - image: postgres:15 - environment: - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - - POSTGRES_DB=${POSTGRES_DB} - ports: - - "5432:5432" - volumes: - - postgres_data:/var/lib/postgresql/data - networks: - - core + # postgres: + # container_name: core-postgres + # image: postgres:15 + # environment: + # - POSTGRES_USER=${POSTGRES_USER} + # - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + # - POSTGRES_DB=${POSTGRES_DB} + # ports: + # - "5432:5432" + # volumes: + # - postgres_data:/var/lib/postgresql/data + # networks: + # - core - redis: - container_name: core-redis - image: redis:7 - ports: - - "6379:6379" - networks: - - core + # redis: + # container_name: core-redis + # image: redis:7 + # ports: + # - "6379:6379" + # networks: + # - core neo4j: container_name: core-neo4j diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f14905..857cb39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,9 @@ importers: '@core/types': specifier: workspace:* version: link:../../packages/types + '@mjackson/headers': + specifier: 0.11.1 + version: 0.11.1 '@nichtsam/remix-auth-email-link': specifier: 3.0.0 version: 3.0.0(remix-auth@4.2.0) @@ -1520,6 +1523,9 @@ packages: '@mjackson/headers@0.10.0': resolution: {integrity: sha512-U1Eu1gF979k7ZoIBsJyD+T5l9MjtPONsZfoXfktsQHPJD0s7SokBGx+tLKDLsOY+gzVYAWS0yRFDNY8cgbQzWQ==} + '@mjackson/headers@0.11.1': + resolution: {integrity: sha512-uXXhd4rtDdDwkqAuGef1nuafkCa1NlTmEc1Jzc0NL4YiA1yON1NFXuqJ3hOuKvNKQwkiDwdD+JJlKVyz4dunFA==} + '@mjackson/headers@0.9.0': resolution: {integrity: sha512-1WFCu2iRaqbez9hcYYI611vcH1V25R+fDfOge/CyKc8sdbzniGfy/FRhNd3DgvFF4ZEEX2ayBrvFHLtOpfvadw==} @@ -9776,6 +9782,8 @@ snapshots: '@mjackson/headers@0.10.0': {} + '@mjackson/headers@0.11.1': {} + '@mjackson/headers@0.9.0': {} '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': diff --git a/turbo.json b/turbo.json index c56c02e..1a52430 100644 --- a/turbo.json +++ b/turbo.json @@ -57,6 +57,7 @@ "NEO4J_USERNAME", "NEO4J_PASSWORD", "OPENAI_API_KEY", - "MAGIC_LINK_SECRET" + "MAGIC_LINK_SECRET", + "ENABLE_EMAIL_LOGIN" ] }