mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 09:28:40 +00:00
fix: emails are not sent on welcome
This commit is contained in:
parent
26a2d04ca9
commit
d062df14aa
@ -22,7 +22,7 @@ export function SpacePatternCard({ pattern }: SpacePatternCardProps) {
|
||||
actionType,
|
||||
patternId: pattern.id,
|
||||
},
|
||||
{ method: "POST" }
|
||||
{ method: "POST" },
|
||||
);
|
||||
setDialog(false);
|
||||
};
|
||||
@ -77,19 +77,19 @@ export function SpacePatternCard({ pattern }: SpacePatternCardProps) {
|
||||
|
||||
<div className="flex justify-end">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => handleAction("delete")}
|
||||
disabled={fetcher.state === "submitting"}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => handleAction("add")}
|
||||
disabled={fetcher.state === "submitting"}
|
||||
>
|
||||
Add
|
||||
Add to memory
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { type Workspace } from "@core/database";
|
||||
import { prisma } from "~/db.server";
|
||||
import { sendEmail } from "~/services/email.server";
|
||||
import { SpaceService } from "~/services/space.server";
|
||||
|
||||
interface CreateWorkspaceDto {
|
||||
@ -31,7 +32,7 @@ export async function createWorkspace(
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.user.update({
|
||||
const user = await prisma.user.update({
|
||||
where: { id: input.userId },
|
||||
data: {
|
||||
confirmedBasicDetails: true,
|
||||
@ -45,6 +46,8 @@ export async function createWorkspace(
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
await sendEmail({ email: "welcome", to: user.email });
|
||||
|
||||
return workspace;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
import {
|
||||
createActionApiRoute,
|
||||
createHybridActionApiRoute,
|
||||
} from "~/services/routeBuilders/apiBuilder.server";
|
||||
import { SearchService } from "~/services/search.server";
|
||||
import { json } from "@remix-run/node";
|
||||
|
||||
@ -19,7 +22,7 @@ export const SearchBodyRequest = z.object({
|
||||
});
|
||||
|
||||
const searchService = new SearchService();
|
||||
const { action, loader } = createActionApiRoute(
|
||||
const { action, loader } = createHybridActionApiRoute(
|
||||
{
|
||||
body: SearchBodyRequest,
|
||||
allowJWT: true,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { useActionData, useLoaderData } from "@remix-run/react";
|
||||
import { useActionData } from "@remix-run/react";
|
||||
import {
|
||||
type ActionFunctionArgs,
|
||||
json,
|
||||
@ -17,12 +17,7 @@ import {
|
||||
} from "~/components/ui/card";
|
||||
import { Button } from "~/components/ui";
|
||||
import { Input } from "~/components/ui/input";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
requireUser,
|
||||
requireUserId,
|
||||
requireWorkpace,
|
||||
} from "~/services/session.server";
|
||||
import { requireUser, requireUserId } from "~/services/session.server";
|
||||
import { redirectWithSuccessMessage } from "~/models/message.server";
|
||||
import { rootPath } from "~/utils/pathBuilder";
|
||||
import { createWorkspace, getWorkspaceByUser } from "~/models/workspace.server";
|
||||
@ -76,7 +71,6 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
|
||||
export default function ConfirmBasicDetails() {
|
||||
const lastSubmission = useActionData<typeof action>();
|
||||
const { workspace } = useLoaderData<typeof loader>();
|
||||
|
||||
const [form, fields] = useForm({
|
||||
lastSubmission: lastSubmission as any,
|
||||
|
||||
@ -16,8 +16,8 @@ const client = singleton(
|
||||
new EmailClient({
|
||||
transport: buildTransportOptions(),
|
||||
imagesBaseUrl: env.APP_ORIGIN,
|
||||
from: env.FROM_EMAIL ?? "Harshith <harshith@tegon.ai>",
|
||||
replyTo: env.REPLY_TO_EMAIL ?? "harshith@tegon.ai",
|
||||
from: env.FROM_EMAIL ?? "Manik <manik@poozle.dev>",
|
||||
replyTo: env.REPLY_TO_EMAIL ?? "manik@poozle.dev",
|
||||
}),
|
||||
);
|
||||
|
||||
@ -85,5 +85,9 @@ export async function scheduleEmail(
|
||||
) {}
|
||||
|
||||
export async function sendEmail(data: DeliverEmail) {
|
||||
return client.send(data);
|
||||
try {
|
||||
return client.send(data);
|
||||
} catch (e) {
|
||||
logger.error(`Error: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { MCP } from "../utils/mcp";
|
||||
import { type HistoryStep } from "../utils/types";
|
||||
import {
|
||||
createConversationHistoryForAgent,
|
||||
deletePersonalAccessToken,
|
||||
getCreditsForUser,
|
||||
getPreviousExecutionHistory,
|
||||
init,
|
||||
@ -120,9 +121,15 @@ export const chat = task({
|
||||
);
|
||||
|
||||
usageCredits && (await updateUserCredits(usageCredits, 1));
|
||||
|
||||
if (init?.tokenId) {
|
||||
await deletePersonalAccessToken(init.tokenId);
|
||||
}
|
||||
} catch (e) {
|
||||
await updateConversationStatus("failed", payload.conversationId);
|
||||
|
||||
if (init?.tokenId) {
|
||||
await deletePersonalAccessToken(init.tokenId);
|
||||
}
|
||||
throw new Error(e as string);
|
||||
}
|
||||
},
|
||||
|
||||
@ -4,8 +4,12 @@ import { z } from "zod";
|
||||
|
||||
import { openai } from "@ai-sdk/openai";
|
||||
import { logger } from "~/services/logger.service";
|
||||
import { getOrCreatePersonalAccessToken } from "../utils/utils";
|
||||
import {
|
||||
deletePersonalAccessToken,
|
||||
getOrCreatePersonalAccessToken,
|
||||
} from "../utils/utils";
|
||||
import axios from "axios";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export const ExtensionSearchBodyRequest = z.object({
|
||||
userInput: z.string().min(1, "User input is required"),
|
||||
@ -24,8 +28,10 @@ export const extensionSearch = task({
|
||||
const { userInput, userId, context } =
|
||||
ExtensionSearchBodyRequest.parse(body);
|
||||
|
||||
const randomKeyName = `extensionSearch_${nanoid(10)}`;
|
||||
|
||||
const pat = await getOrCreatePersonalAccessToken({
|
||||
name: "extensionSearch",
|
||||
name: randomKeyName,
|
||||
userId: userId as string,
|
||||
});
|
||||
|
||||
@ -106,8 +112,12 @@ If no relevant information is found, provide a brief statement indicating that.`
|
||||
finalText = finalText + chunk;
|
||||
}
|
||||
|
||||
await deletePersonalAccessToken(pat?.id);
|
||||
|
||||
return finalText;
|
||||
} catch (error) {
|
||||
await deletePersonalAccessToken(pat?.id);
|
||||
|
||||
logger.error(`SearchMemoryAgent error: ${error}`);
|
||||
|
||||
return `Context related to: ${userInput}. Looking for relevant background information, previous discussions, and related concepts that would help provide a comprehensive answer.`;
|
||||
|
||||
@ -170,7 +170,7 @@ export const init = async ({ payload }: { payload: InitChatPayload }) => {
|
||||
return { conversation, conversationHistory };
|
||||
}
|
||||
|
||||
const randomKeyName = `chat`;
|
||||
const randomKeyName = `chat_${nanoid(10)}`;
|
||||
const pat = await getOrCreatePersonalAccessToken({
|
||||
name: randomKeyName,
|
||||
userId: workspace.userId as string,
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { Hr, Link, Text } from "@react-email/components";
|
||||
import { Hr, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
import { footer, footerAnchor, hr } from "./styles";
|
||||
import { footer, hr, paragraphLight } from "./styles";
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<>
|
||||
<Hr style={hr} />
|
||||
<Text style={paragraphLight}>happy building your digital brain!</Text>
|
||||
<Text style={footer}>
|
||||
©Sol.ai
|
||||
<Link style={footerAnchor} href="https://core.heysol.ai/">
|
||||
C.O.R.E
|
||||
</Link>
|
||||
the Core team P.S Questions?
|
||||
<br />
|
||||
Just hit reply - we're here to help.
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -8,10 +8,7 @@ export const h1 = {
|
||||
padding: "0",
|
||||
};
|
||||
|
||||
export const main = {
|
||||
backgroundColor: "#15171A",
|
||||
padding: "0 20px",
|
||||
};
|
||||
export const main = {};
|
||||
|
||||
export const container = {
|
||||
backgroundColor: "#15171A",
|
||||
@ -30,29 +27,42 @@ export const hr = {
|
||||
};
|
||||
|
||||
export const paragraph = {
|
||||
color: "#878C99",
|
||||
color: "black",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "16px",
|
||||
fontSize: "14px",
|
||||
lineHeight: "24px",
|
||||
textAlign: "left" as const,
|
||||
};
|
||||
|
||||
export const paragraphLight = {
|
||||
color: "#D7D9DD",
|
||||
color: "black",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "16px",
|
||||
fontSize: "14px",
|
||||
lineHeight: "24px",
|
||||
textAlign: "left" as const,
|
||||
margin: 0,
|
||||
};
|
||||
|
||||
export const heading = {
|
||||
color: "black",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "14px",
|
||||
lineHeight: "24px",
|
||||
textAlign: "left" as const,
|
||||
fontWeight: "bold",
|
||||
margin: 0,
|
||||
marginTop: "20px",
|
||||
};
|
||||
|
||||
export const paragraphTight = {
|
||||
color: "#D7D9DD",
|
||||
color: "black",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "16px",
|
||||
lineHeight: "16px",
|
||||
fontSize: "14px",
|
||||
lineHeight: "14px",
|
||||
textAlign: "left" as const,
|
||||
};
|
||||
|
||||
@ -60,18 +70,19 @@ export const bullets = {
|
||||
color: "#D7D9DD",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "16px",
|
||||
fontSize: "14px",
|
||||
lineHeight: "24px",
|
||||
textAlign: "left" as const,
|
||||
margin: "0",
|
||||
};
|
||||
|
||||
export const anchor = {
|
||||
color: "#826DFF",
|
||||
marginRight: "3px",
|
||||
fontFamily:
|
||||
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
|
||||
fontSize: "16px",
|
||||
fontSize: "14px",
|
||||
textDecoration: "underline",
|
||||
color: "black",
|
||||
};
|
||||
|
||||
export const button = {
|
||||
@ -80,7 +91,7 @@ export const button = {
|
||||
color: "#D7D9DD",
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "16px",
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
textDecoration: "none",
|
||||
textAlign: "center" as const,
|
||||
@ -92,7 +103,7 @@ export const footer = {
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "12px",
|
||||
lineHeight: "16px",
|
||||
lineHeight: "14px",
|
||||
};
|
||||
|
||||
export const footerItalic = {
|
||||
@ -101,7 +112,7 @@ export const footerItalic = {
|
||||
fontFamily:
|
||||
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
||||
fontSize: "12px",
|
||||
lineHeight: "16px",
|
||||
lineHeight: "14px",
|
||||
};
|
||||
|
||||
export const footerAnchor = {
|
||||
|
||||
@ -1,59 +1,86 @@
|
||||
import { Body, Head, Html, Link, Preview, Section, Text } from "@react-email/components";
|
||||
import { Body, Head, Html, Img, Link, Preview, Text } from "@react-email/components";
|
||||
import { Footer } from "./components/Footer";
|
||||
import { anchor, bullets, footerItalic, main, paragraphLight } from "./components/styles";
|
||||
import { anchor, heading, main, paragraphLight } from "./components/styles";
|
||||
import { z } from "zod";
|
||||
|
||||
export const WelcomeEmailSchema = z.object({
|
||||
email: z.literal("welcome"),
|
||||
orgName: z.string(),
|
||||
inviterName: z.string().optional(),
|
||||
inviterEmail: z.string(),
|
||||
inviteLink: z.string().url(),
|
||||
});
|
||||
|
||||
export function WelcomeEmail({ orgName }: { orgName?: string }) {
|
||||
export default function WelcomeEmail() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>Welcome to C.O.R.E. - Your Personal AI Assistant</Preview>
|
||||
<Preview>building your digital brain</Preview>
|
||||
<Body style={main}>
|
||||
<Text style={paragraphLight}>Hey {orgName ?? "there"},</Text>
|
||||
<Text style={paragraphLight}>Welcome to C.O.R.E., your new personal AI assistant!</Text>
|
||||
<Text style={paragraphLight}>
|
||||
I'm excited to help you streamline your daily tasks, boost your productivity, and make
|
||||
your work life easier. C.O.R.E. is designed to be intuitive and powerful, adapting to your
|
||||
unique needs and preferences.
|
||||
</Text>
|
||||
<Text style={paragraphLight}>
|
||||
To get started, you can{" "}
|
||||
<Link style={anchor} href="https://core.heysol.ai/home">
|
||||
visit your dashboard
|
||||
</Link>{" "}
|
||||
where you'll find all the features and capabilities at your disposal. Whether it's
|
||||
managing your schedule, handling communications, or automating repetitive tasks, I'm here
|
||||
to help.
|
||||
</Text>
|
||||
|
||||
<Text style={paragraphLight}>
|
||||
If you have any questions or need assistance, don't hesitate to reach out. You can:{"\n"}•
|
||||
Ask me directly through the chat interface{"\n"}•{" "}
|
||||
<Link style={anchor} href="https://core.heysol.ai/support">
|
||||
Visit our support center
|
||||
<Text style={paragraphLight}>hi there,</Text>
|
||||
<Text
|
||||
style={{
|
||||
...paragraphLight,
|
||||
marginTop: "10px",
|
||||
}}
|
||||
>
|
||||
<Link style={anchor} href="https://x.com/manikagg01">
|
||||
Manik
|
||||
</Link>
|
||||
{"\n"}• Join our{" "}
|
||||
<Link style={anchor} href="https://discord.gg/heysol">
|
||||
Discord community
|
||||
</Link>{" "}
|
||||
to connect with other users and our team
|
||||
from core here. welcome to core. when i first tried core memory, two actions made it click
|
||||
for me. each came down to the same thing: understanding how I can add relevant context
|
||||
about everything that matters to me in core memory and recall it wherever I want.
|
||||
</Text>
|
||||
<Text style={heading}>core mcp</Text>
|
||||
<Text style={paragraphLight}>
|
||||
seamlessly add your code context from cursor/claude-code, project context from linear, or
|
||||
brainstorming sessions from claude desktop via mcp. solve context loss problems across ai
|
||||
tools with persistent, cross-session memory. add this url and get started
|
||||
</Text>
|
||||
<Link
|
||||
style={{
|
||||
...anchor,
|
||||
marginTop: "10px",
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
>
|
||||
https://core.heysol.ai/api/v1/mcp?source='Your Coding Agent'
|
||||
</Link>
|
||||
<Img
|
||||
alt="Claude"
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
width: "100%",
|
||||
borderRadius: "2%",
|
||||
marginTop: "10px",
|
||||
}}
|
||||
src="https://integrations.heysol.ai/core-claude.gif"
|
||||
/>
|
||||
<Text style={heading}>browser extension</Text>
|
||||
<Text style={paragraphLight}>
|
||||
recall relevant context from core memory in chatgpt, grok, and gemini. save conversations
|
||||
and content from chatgpt, grok, gemini, twitter, youtube, blog posts, and any webpage
|
||||
directly into your Core memory with simple text selection.
|
||||
</Text>
|
||||
<Img
|
||||
alt="Claude"
|
||||
style={{
|
||||
marginLeft: "auto",
|
||||
marginRight: "auto",
|
||||
width: "100%",
|
||||
borderRadius: "2%",
|
||||
marginTop: "10px",
|
||||
}}
|
||||
src="https://integrations.heysol.ai/core-extension.gif"
|
||||
/>
|
||||
|
||||
<Text style={paragraphLight}>Looking forward to being your trusted assistant!</Text>
|
||||
|
||||
<Text style={bullets}>Best regards,</Text>
|
||||
<Text style={bullets}>C.O.R.E.</Text>
|
||||
<Text style={paragraphLight}>Your AI Assistant</Text>
|
||||
<Text style={footerItalic}>
|
||||
You can customize your notification preferences anytime in your account settings.
|
||||
<Text style={heading}>need real-time, human help to get started? </Text>
|
||||
<Text style={paragraphLight}>
|
||||
- join our discord community & get direct help from our team + over 100+ enthusiasts using
|
||||
Core memory
|
||||
</Text>
|
||||
<Text style={paragraphLight}>
|
||||
- We are open-source us on our repo -{" "}
|
||||
<Link style={anchor} href="https://github.com/RedPlanetHQ/core">
|
||||
https://github.com/RedPlanetHQ/core
|
||||
</Link>
|
||||
</Text>
|
||||
<Footer />
|
||||
</Body>
|
||||
|
||||
@ -4,7 +4,7 @@ import { z } from "zod";
|
||||
|
||||
import { setGlobalBasePath } from "../emails/components/BasePath";
|
||||
|
||||
import { WelcomeEmail, WelcomeEmailSchema } from "../emails/welcome";
|
||||
import WelcomeEmail, { WelcomeEmailSchema } from "../emails/welcome";
|
||||
import { constructMailTransport, MailTransport, MailTransportOptions } from "./transports";
|
||||
import MagicLinkEmail from "../emails/magic-link";
|
||||
|
||||
@ -73,14 +73,14 @@ export class EmailClient {
|
||||
switch (data.email) {
|
||||
case "magic_link":
|
||||
return {
|
||||
subject: "Magic sign-in link for Trigger.dev",
|
||||
subject: "Magic sign-in link for Core",
|
||||
component: <MagicLinkEmail magicLink={data.magicLink} />,
|
||||
};
|
||||
|
||||
case "welcome":
|
||||
return {
|
||||
subject: `You've been invited to join ${data.orgName} on C.O.R.E.`,
|
||||
component: <WelcomeEmail {...data} />,
|
||||
subject: `You've been invited to join on C.O.R.E.`,
|
||||
component: <WelcomeEmail />,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,20 +2,20 @@ import { EmailError, MailMessage, MailTransport, PlainTextMailMessage } from "./
|
||||
import { Resend } from "resend";
|
||||
|
||||
export type ResendMailTransportOptions = {
|
||||
type: 'resend',
|
||||
type: "resend";
|
||||
config: {
|
||||
apiKey?: string
|
||||
}
|
||||
}
|
||||
apiKey?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export class ResendMailTransport implements MailTransport {
|
||||
#client: Resend;
|
||||
|
||||
constructor(options: ResendMailTransportOptions) {
|
||||
this.#client = new Resend(options.config.apiKey)
|
||||
this.#client = new Resend(options.config.apiKey);
|
||||
}
|
||||
|
||||
async send({to, from, replyTo, subject, react}: MailMessage): Promise<void> {
|
||||
async send({ to, from, replyTo, subject, react }: MailMessage): Promise<void> {
|
||||
const result = await this.#client.emails.send({
|
||||
from: from,
|
||||
to,
|
||||
@ -25,6 +25,7 @@ export class ResendMailTransport implements MailTransport {
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
console.log(result);
|
||||
console.error(
|
||||
`Failed to send email to ${to}, ${subject}. Error ${result.error.name}: ${result.error.message}`
|
||||
);
|
||||
@ -32,7 +33,7 @@ export class ResendMailTransport implements MailTransport {
|
||||
}
|
||||
}
|
||||
|
||||
async sendPlainText({to, from, replyTo, subject, text}: PlainTextMailMessage): Promise<void> {
|
||||
async sendPlainText({ to, from, replyTo, subject, text }: PlainTextMailMessage): Promise<void> {
|
||||
const result = await this.#client.emails.send({
|
||||
from: from,
|
||||
to,
|
||||
|
||||
@ -74,6 +74,11 @@
|
||||
"TRIGGER_API_URL",
|
||||
"TRIGGER_SECRET_KEY",
|
||||
"EMBEDDING_MODEL",
|
||||
"MODEL"
|
||||
"MODEL",
|
||||
"COHERE_API_KEY",
|
||||
"RESEND_API_KEY",
|
||||
"FROM_EMAIL",
|
||||
"REPLY_TO_EMAIL",
|
||||
"EMAIL_TRANSPORT"
|
||||
]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user