Implement new billing model
This commit is contained in:
parent
d0820ebbf1
commit
03e426dd23
4 changed files with 49 additions and 15 deletions
|
|
@ -291,8 +291,12 @@ export const createInvitedUser = async (
|
|||
streamServerClient: StreamChat,
|
||||
transporter: Transporter,
|
||||
recipientEmail: string,
|
||||
senderEmail: string
|
||||
senderEmail: string,
|
||||
options?: {
|
||||
isTemporary?: boolean;
|
||||
}
|
||||
): Promise<{ success: boolean; error?: string; userId?: string }> => {
|
||||
const isTemporary = options?.isTemporary ?? true;
|
||||
const xtabloUrl = process.env.XTABLO_URL || "https://app.xtablo.com";
|
||||
|
||||
// Create a new user account for the invited email
|
||||
|
|
@ -318,6 +322,16 @@ export const createInvitedUser = async (
|
|||
return { success: false, error: createUserError.message };
|
||||
}
|
||||
|
||||
const { error: updateProfileError } = await supabase
|
||||
.from("profiles")
|
||||
.update({ is_temporary: isTemporary })
|
||||
.eq("id", newUser.user.id);
|
||||
|
||||
if (updateProfileError) {
|
||||
console.error("Error setting invited user temporary status:", updateProfileError);
|
||||
return { success: false, error: updateProfileError.message };
|
||||
}
|
||||
|
||||
await streamServerClient.upsertUser({
|
||||
id: newUser.user.id,
|
||||
name: recipientEmail.split("@")[0],
|
||||
|
|
|
|||
|
|
@ -58,7 +58,8 @@ const bookSlot = factory.createHandlers(async (c) => {
|
|||
streamServerClient,
|
||||
transporter,
|
||||
data.user_details.email,
|
||||
ownerData.email
|
||||
ownerData.email,
|
||||
{ isTemporary: true }
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,11 @@ const upsertStreamUserFromProfile = async (
|
|||
streamServerClient: AuthEnv["Variables"]["streamServerClient"],
|
||||
userId: string
|
||||
) => {
|
||||
const { data: profile } = await supabase.from("profiles").select("name").eq("id", userId).maybeSingle();
|
||||
const { data: profile } = await supabase
|
||||
.from("profiles")
|
||||
.select("name")
|
||||
.eq("id", userId)
|
||||
.maybeSingle();
|
||||
|
||||
await streamServerClient.upsertUser({
|
||||
id: userId,
|
||||
|
|
@ -255,10 +259,7 @@ const deleteTablo = factory.createHandlers(async (c) => {
|
|||
}
|
||||
}
|
||||
|
||||
const { error } = await supabase
|
||||
.from("tablos")
|
||||
.update({ deleted_at: deletedAt })
|
||||
.eq("id", id);
|
||||
const { error } = await supabase.from("tablos").update({ deleted_at: deletedAt }).eq("id", id);
|
||||
|
||||
if (error) {
|
||||
return c.json({ error: error.message }, 500);
|
||||
|
|
@ -352,7 +353,8 @@ const inviteToTablo = (
|
|||
streamServerClient,
|
||||
transporter,
|
||||
recipientEmail,
|
||||
sender.email
|
||||
sender.email,
|
||||
{ isTemporary: true }
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
|
|
@ -427,9 +429,7 @@ ${introEmail ? `<p>${introEmail}</p>` : ""}
|
|||
});
|
||||
});
|
||||
|
||||
const cancelPendingInvite = (
|
||||
middlewareManager: ReturnType<typeof MiddlewareManager.getInstance>
|
||||
) =>
|
||||
const cancelPendingInvite = (middlewareManager: ReturnType<typeof MiddlewareManager.getInstance>) =>
|
||||
factory.createHandlers(middlewareManager.regularUserCheck, async (c) => {
|
||||
const user = c.get("user");
|
||||
const supabase = c.get("supabase");
|
||||
|
|
|
|||
|
|
@ -35,7 +35,10 @@ const getMe = factory.createHandlers(async (c) => {
|
|||
|
||||
const { data, error } = await supabase.from("profiles").select("*").eq("id", user.id).single();
|
||||
|
||||
const userData = data as Tables<"profiles">;
|
||||
const userData = data as Tables<"profiles"> & {
|
||||
organization_id: number | null;
|
||||
plan: string | null;
|
||||
};
|
||||
|
||||
if (!userData) {
|
||||
return c.json({ error: "User not found" }, 404);
|
||||
|
|
@ -45,11 +48,21 @@ const getMe = factory.createHandlers(async (c) => {
|
|||
return c.json({ error: error.message }, 500);
|
||||
}
|
||||
|
||||
let effectivePlan: string | null = userData.plan;
|
||||
if (userData.organization_id) {
|
||||
const { plan: organizationPlan } = await getOrganizationPlan(
|
||||
supabase,
|
||||
userData.organization_id
|
||||
);
|
||||
effectivePlan = organizationPlan;
|
||||
}
|
||||
|
||||
const user_id = data.id;
|
||||
const token = streamServerClient.createToken(user_id);
|
||||
|
||||
return c.json({
|
||||
...userData,
|
||||
plan: effectivePlan,
|
||||
streamToken: token,
|
||||
});
|
||||
});
|
||||
|
|
@ -313,6 +326,11 @@ const getOrganization = factory.createHandlers(async (c) => {
|
|||
return c.json({ error: "Failed to resolve organization plan" }, 500);
|
||||
}
|
||||
|
||||
const membersWithEffectivePlan = (members || []).map((member) => ({
|
||||
...member,
|
||||
plan,
|
||||
}));
|
||||
|
||||
const { data: billingState, error: billingError } = await getOrganizationBillingState(
|
||||
supabase,
|
||||
organizationId
|
||||
|
|
@ -329,7 +347,7 @@ const getOrganization = factory.createHandlers(async (c) => {
|
|||
member_count: members?.length || 0,
|
||||
tablo_count: tabloCount || 0,
|
||||
},
|
||||
members: members || [],
|
||||
members: membersWithEffectivePlan,
|
||||
trial_starts_at: billingState.trial_starts_at,
|
||||
trial_ends_at: billingState.trial_ends_at,
|
||||
is_trial_expired: billingState.is_trial_expired,
|
||||
|
|
@ -469,7 +487,8 @@ const inviteToOrganization = factory.createHandlers(async (c) => {
|
|||
streamServerClient,
|
||||
transporter,
|
||||
recipientEmail,
|
||||
senderProfile.email
|
||||
senderProfile.email,
|
||||
{ isTemporary: false }
|
||||
);
|
||||
|
||||
if (!invitedUser.success || !invitedUser.userId) {
|
||||
|
|
@ -490,7 +509,7 @@ const inviteToOrganization = factory.createHandlers(async (c) => {
|
|||
|
||||
const { error: assignOrganizationError } = await supabase
|
||||
.from("profiles")
|
||||
.update({ organization_id: organizationId })
|
||||
.update({ organization_id: organizationId, is_temporary: false })
|
||||
.eq("id", invitedUser.userId);
|
||||
|
||||
if (assignOrganizationError) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue