Create Loops profiles on user signup (#180)
This commit is contained in:
parent
ba9f30fde0
commit
740c899764
3
src/env.d.ts
vendored
3
src/env.d.ts
vendored
@ -13,6 +13,9 @@ declare namespace Cloudflare {
|
||||
POSTHOG_HOST?: string;
|
||||
BETTER_AUTH_SECRET?: string;
|
||||
BETTER_AUTH_URL?: string;
|
||||
LOOPS_API_KEY?: string;
|
||||
LOOPS_TRANSACTIONAL_VERIFY_EMAIL_ID?: string;
|
||||
LOOPS_TRANSACTIONAL_RESET_PASSWORD_ID?: string;
|
||||
|
||||
// DataForSEO API Basic auth value (base64 of login:password)
|
||||
DATAFORSEO_API_KEY: string;
|
||||
|
||||
@ -9,6 +9,7 @@ import { getOrCreateDefaultHostedOrganization } from "@/server/auth/default-host
|
||||
import {
|
||||
sendHostedPasswordResetEmail,
|
||||
sendHostedVerificationEmail,
|
||||
upsertHostedSignupContact,
|
||||
} from "@/server/email/loops";
|
||||
|
||||
const hostedBaseUrlSchema = z
|
||||
@ -61,6 +62,25 @@ function createAuth() {
|
||||
}),
|
||||
plugins: [...baseAuthConfig.plugins, tanstackStartCookies()],
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
after: async (user) => {
|
||||
try {
|
||||
await upsertHostedSignupContact({
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to create Loops profile for signup:", {
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
session: {
|
||||
create: {
|
||||
before: async (session) => {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
|
||||
const LOOPS_TRANSACTIONAL_URL = "https://app.loops.so/api/v1/transactional";
|
||||
const LOOPS_CONTACT_UPDATE_URL = "https://app.loops.so/api/v1/contacts/update";
|
||||
|
||||
function getRequiredEnv(name: string) {
|
||||
const value: unknown = Reflect.get(env, name);
|
||||
@ -67,6 +68,77 @@ async function sendLoopsTransactionalEmail({
|
||||
);
|
||||
}
|
||||
|
||||
function getOptionalEnv(name: string) {
|
||||
const value: unknown = Reflect.get(env, name);
|
||||
const trimmed = typeof value === "string" ? value.trim() : "";
|
||||
|
||||
return trimmed || null;
|
||||
}
|
||||
|
||||
function getContactNameParts(name: string | null | undefined) {
|
||||
const trimmedName = name?.trim();
|
||||
|
||||
if (!trimmedName) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const [firstName, ...lastNameParts] = trimmedName.split(/\s+/);
|
||||
const lastName = lastNameParts.join(" ");
|
||||
|
||||
return {
|
||||
firstName,
|
||||
...(lastName ? { lastName } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function upsertHostedSignupContact({
|
||||
userId,
|
||||
email,
|
||||
name,
|
||||
}: {
|
||||
userId: string;
|
||||
email: string;
|
||||
name?: string | null;
|
||||
}) {
|
||||
const apiKey = getOptionalEnv("LOOPS_API_KEY");
|
||||
|
||||
if (!apiKey) {
|
||||
console.warn(
|
||||
"Skipping Loops signup contact sync: LOOPS_API_KEY is not set",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(LOOPS_CONTACT_UPDATE_URL, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email,
|
||||
userId,
|
||||
source: "openseo-signup",
|
||||
userGroup: "app-user",
|
||||
...getContactNameParts(name),
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const errorPayload = await response.json().catch(() => null);
|
||||
console.error("Loops signup contact sync error:", {
|
||||
status: response.status,
|
||||
email,
|
||||
userId,
|
||||
errorPayload,
|
||||
});
|
||||
|
||||
throw new Error(`Failed to sync Loops signup contact (${response.status})`);
|
||||
}
|
||||
|
||||
export async function sendHostedVerificationEmail({
|
||||
email,
|
||||
confirmationUrl,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user