Fix: authentication and ingest API

This commit is contained in:
Harshith Mullapudi 2025-06-16 21:50:30 +05:30
parent 33eae2619a
commit 941ce8e4fa
11 changed files with 158 additions and 128 deletions

View File

@ -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,10 +26,10 @@ 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
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

View File

@ -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(),

View File

@ -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<any> {
}
}
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() {
<Form method="post">
<div className="flex flex-col items-center justify-center">
{data.magicLinkSent ? (
<>
<Header1 className="pb-6 text-center text-xl leading-7 font-normal md:text-xl lg:text-2xl">
We've sent you a magic link!
</Header1>
<Fieldset className="flex w-full flex-col items-center gap-y-2">
<Inbox className="text-primary mb-4 h-12 w-12" />
<Paragraph className="mb-6 text-center">
<Card className="min-w-[400px] rounded-md p-3">
<CardHeader className="flex flex-col items-start">
<CardTitle className="mb-0 text-lg">
{" "}
We've sent you a magic link!
</CardTitle>
<CardDescription>
We sent you an email which contains a magic link that will log
you in to your account.
</Paragraph>
</CardDescription>
</CardHeader>
<Fieldset className="flex w-full flex-col items-center gap-y-2 px-2">
<FormButtons
cancelButton={
<Button
type="submit"
name="action"
value="reset"
variant="link"
variant="secondary"
data-action="re-enter email"
>
Re-enter email
</Button>
}
confirmButton={
<Button
variant="ghost"
data-action="log in using another option"
>
Log in using another option
</Button>
}
confirmButton={<></>}
/>
</Fieldset>
</>
</Card>
) : (
<>
<Header1 className="pb-4 font-semibold sm:text-2xl md:text-3xl lg:text-4xl">
Welcome
</Header1>
<Paragraph variant="base" className="mb-6 text-center">
Create an account or login using email
</Paragraph>
<Fieldset className="flex w-full flex-col items-center gap-y-2">
<Input
type="email"
name="email"
spellCheck={false}
placeholder="Email Address"
required
autoFocus
/>
<Card className="min-w-[400px] rounded-md p-3">
<CardHeader className="flex flex-col items-start">
<CardTitle className="mb-0 text-lg">Welcome</CardTitle>
<CardDescription>
Create an account or login using email
</CardDescription>
</CardHeader>
<CardContent className="pt-2">
<Fieldset className="flex w-full flex-col items-center gap-y-2">
<Input
type="email"
name="email"
className="h-9"
spellCheck={false}
placeholder="Email Address"
required
autoFocus
/>
<Button
name="action"
value="send"
type="submit"
variant="secondary"
size="lg"
disabled={isLoading}
data-action="send a magic link"
>
{isLoading ? (
<Loader className="mr-2 size-5" color="white" />
) : (
<Mail className="text-text-bright mr-2 size-5" />
)}
{isLoading ? (
<span className="text-text-bright">Sending</span>
) : (
<span className="text-text-bright">Send a magic link</span>
)}
</Button>
{data.magicLinkError && <>{data.magicLinkError}</>}
</Fieldset>
</>
<Button
name="action"
value="send"
type="submit"
variant="secondary"
size="lg"
disabled={isLoading}
data-action="send a magic link"
>
{isLoading ? (
<Loader className="mr-2 size-5" color="white" />
) : (
<Mail className="text-text-bright mr-2 size-5" />
)}
{isLoading ? (
<span className="text-text-bright">Sending</span>
) : (
<span className="text-text-bright">
Send a magic link
</span>
)}
</Button>
{data.magicLinkError && <>{data.magicLinkError}</>}
</Fieldset>
</CardContent>
</Card>
)}
</div>
</Form>

View File

@ -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(),

View File

@ -13,7 +13,9 @@ const authenticator = new Authenticator<AuthUser>();
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(

View File

@ -58,11 +58,6 @@ function buildTransportOptions(): MailTransportOptions {
}
export async function sendMagicLinkEmail(options: any): Promise<void> {
// 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<void> {
logger.error("Error sending magic link email", {
error: JSON.stringify(error),
});
throw error;
}
}

View File

@ -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 });

View File

@ -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",

View File

@ -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

8
pnpm-lock.yaml generated
View File

@ -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':

View File

@ -57,6 +57,7 @@
"NEO4J_USERNAME",
"NEO4J_PASSWORD",
"OPENAI_API_KEY",
"MAGIC_LINK_SECRET"
"MAGIC_LINK_SECRET",
"ENABLE_EMAIL_LOGIN"
]
}