diff --git a/server/emails/templates/NotifyTrialExpiring.tsx b/server/emails/templates/NotifyTrialExpiring.tsx
index 7cd6d30ac..7c712e278 100644
--- a/server/emails/templates/NotifyTrialExpiring.tsx
+++ b/server/emails/templates/NotifyTrialExpiring.tsx
@@ -64,7 +64,7 @@ export const NotifyTrialExpiring = ({
Some features and resources may now be
- restricted or disconnected. To restore full
+ restricted. To restore full
access and continue using all the features
you had during your trial, please upgrade to
a paid plan.
@@ -85,7 +85,7 @@ export const NotifyTrialExpiring = ({
{orgName} will end on{" "}
{trialEndsAt}
{isLastDay
- ? " — that's tomorrow!"
+ ? " - that's tomorrow!"
: `, in ${daysRemaining} days`}
.
@@ -93,8 +93,7 @@ export const NotifyTrialExpiring = ({
After your trial ends, your account will be
moved to the free plan and some
- functionality may be restricted or your
- sites may disconnect.
+ functionality may be restricted.
diff --git a/server/lib/billing/limitSet.ts b/server/lib/billing/limitSet.ts
index ae9a18ffe..e45ae637d 100644
--- a/server/lib/billing/limitSet.ts
+++ b/server/lib/billing/limitSet.ts
@@ -25,7 +25,7 @@ export const tier1LimitSet: LimitSet = {
export const tier2LimitSet: LimitSet = {
[FeatureId.USERS]: {
- value: 100,
+ value: 50,
description: "Team limit"
},
[FeatureId.SITES]: {
@@ -48,7 +48,7 @@ export const tier2LimitSet: LimitSet = {
export const tier3LimitSet: LimitSet = {
[FeatureId.USERS]: {
- value: 500,
+ value: 250,
description: "Business limit"
},
[FeatureId.SITES]: {
diff --git a/server/private/lib/billing/getOrgTierData.ts b/server/private/lib/billing/getOrgTierData.ts
index 1dc9f83a4..9df9b3b74 100644
--- a/server/private/lib/billing/getOrgTierData.ts
+++ b/server/private/lib/billing/getOrgTierData.ts
@@ -19,12 +19,13 @@ import { eq, and, ne } from "drizzle-orm";
export async function getOrgTierData(
orgId: string
-): Promise<{ tier: Tier | null; active: boolean }> {
+): Promise<{ tier: Tier | null; active: boolean; isTrial: boolean }> {
let tier: Tier | null = null;
let active = false;
+ let isTrial = false;
if (build !== "saas") {
- return { tier, active };
+ return { tier, active, isTrial };
}
try {
@@ -35,7 +36,7 @@ export async function getOrgTierData(
.limit(1);
if (!org) {
- return { tier, active };
+ return { tier, active, isTrial };
}
let orgIdToUse = org.orgId;
@@ -44,7 +45,7 @@ export async function getOrgTierData(
logger.warn(
`Org ${orgId} is not a billing org and does not have a billingOrgId`
);
- return { tier, active };
+ return { tier, active, isTrial };
}
orgIdToUse = org.billingOrgId;
}
@@ -57,7 +58,7 @@ export async function getOrgTierData(
.limit(1);
if (!customer) {
- return { tier, active };
+ return { tier, active, isTrial };
}
// Query for active subscriptions that are not license type
@@ -84,11 +85,13 @@ export async function getOrgTierData(
tier = subscription.type;
active = true;
}
+
+ isTrial = subscription.trial ?? false;
}
} catch (error) {
// If org not found or error occurs, return null tier and inactive
// This is acceptable behavior as per the function signature
}
- return { tier, active };
+ return { tier, active, isTrial };
}
diff --git a/server/routers/auth/deleteMyAccount.ts b/server/routers/auth/deleteMyAccount.ts
index b824e582b..07bdf883d 100644
--- a/server/routers/auth/deleteMyAccount.ts
+++ b/server/routers/auth/deleteMyAccount.ts
@@ -104,8 +104,9 @@ export async function deleteMyAccount(
(r) => r.isBillingOrg && r.isOwner
)?.orgId;
if (primaryOrgId) {
- const { tier, active } = await getOrgTierData(primaryOrgId);
- if (active && tier) {
+ const { tier, active, isTrial } =
+ await getOrgTierData(primaryOrgId);
+ if (active && tier && !isTrial) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
diff --git a/server/routers/siteResource/deleteSiteResource.ts b/server/routers/siteResource/deleteSiteResource.ts
index df43d5c25..7dbb111ad 100644
--- a/server/routers/siteResource/deleteSiteResource.ts
+++ b/server/routers/siteResource/deleteSiteResource.ts
@@ -63,17 +63,26 @@ export async function deleteSiteResource(
);
}
- await db.transaction(async (trx) => {
- // Delete the site resource
- const [removedSiteResource] = await trx
- .delete(siteResources)
- .where(eq(siteResources.siteResourceId, siteResourceId))
- .returning();
+ // Delete the site resource
+ const [removedSiteResource] = await db
+ .delete(siteResources)
+ .where(eq(siteResources.siteResourceId, siteResourceId))
+ .returning();
+ // Run in the background after the response is sent. Wrapped in its
+ // own transaction so it always executes on the primary — avoiding any
+ // replica-lag issues while still allowing the HTTP response to return
+ // early.
+ db.transaction(async (trx) => {
await rebuildClientAssociationsFromSiteResource(
removedSiteResource,
trx
);
+ }).catch((err) => {
+ logger.error(
+ `Error rebuilding client associations for site resource ${removedSiteResource!.siteResourceId}:`,
+ err
+ );
});
logger.info(`Deleted site resource ${siteResourceId}`);