mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-07 10:30:32 +00:00
Working on complete auth flow
This commit is contained in:
@@ -27,6 +27,7 @@ import { handleSubscriptionLifesycle } from "../subscriptionLifecycle";
|
||||
import { AudienceIds, moveEmailToAudience } from "#private/lib/resend";
|
||||
import { getSubType } from "./getSubType";
|
||||
import privateConfig from "#private/lib/config";
|
||||
import { getLicensePriceSet, LicenseId } from "@server/lib/billing/licenses";
|
||||
|
||||
export async function handleSubscriptionCreated(
|
||||
subscription: Stripe.Subscription
|
||||
@@ -182,6 +183,33 @@ export async function handleSubscriptionCreated(
|
||||
`Retrieved licenseId ${licenseId} from checkout session for subscription ${subscription.id}`
|
||||
);
|
||||
|
||||
// Determine users and sites based on license type
|
||||
const priceSet = getLicensePriceSet();
|
||||
const subscriptionPriceId =
|
||||
fullSubscription.items.data[0]?.price.id;
|
||||
|
||||
let numUsers: number;
|
||||
let numSites: number;
|
||||
|
||||
if (subscriptionPriceId === priceSet[LicenseId.SMALL_LICENSE]) {
|
||||
numUsers = 25;
|
||||
numSites = 25;
|
||||
} else if (
|
||||
subscriptionPriceId === priceSet[LicenseId.BIG_LICENSE]
|
||||
) {
|
||||
numUsers = 50;
|
||||
numSites = 50;
|
||||
} else {
|
||||
logger.error(
|
||||
`Unknown price ID ${subscriptionPriceId} for subscription ${subscription.id}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`License type determined: ${numUsers} users, ${numSites} sites for subscription ${subscription.id}`
|
||||
);
|
||||
|
||||
const response = await fetch(
|
||||
`${privateConfig.getRawPrivateConfig().server.fossorial_api}/api/v1/license-internal/enterprise/paid-for`, // this says enterprise but it does both
|
||||
{
|
||||
@@ -193,7 +221,10 @@ export async function handleSubscriptionCreated(
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
licenseId: parseInt(licenseId)
|
||||
licenseId: parseInt(licenseId),
|
||||
paidFor: true,
|
||||
users: numUsers,
|
||||
sites: numSites
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
@@ -24,11 +24,21 @@ import { eq, and } from "drizzle-orm";
|
||||
import logger from "@server/logger";
|
||||
import { handleSubscriptionLifesycle } from "../subscriptionLifecycle";
|
||||
import { AudienceIds, moveEmailToAudience } from "#private/lib/resend";
|
||||
import { getSubType } from "./getSubType";
|
||||
import stripe from "#private/lib/stripe";
|
||||
|
||||
export async function handleSubscriptionDeleted(
|
||||
subscription: Stripe.Subscription
|
||||
): Promise<void> {
|
||||
try {
|
||||
// Fetch the subscription from Stripe with expanded price.tiers
|
||||
const fullSubscription = await stripe!.subscriptions.retrieve(
|
||||
subscription.id,
|
||||
{
|
||||
expand: ["items.data.price.tiers"]
|
||||
}
|
||||
);
|
||||
|
||||
const [existingSubscription] = await db
|
||||
.select()
|
||||
.from(subscriptions)
|
||||
@@ -64,25 +74,33 @@ export async function handleSubscriptionDeleted(
|
||||
return;
|
||||
}
|
||||
|
||||
await handleSubscriptionLifesycle(customer.orgId, subscription.status);
|
||||
const type = getSubType(fullSubscription);
|
||||
if (type === "saas") {
|
||||
logger.debug(`Handling SaaS subscription deletion for orgId ${customer.orgId} and subscription ID ${subscription.id}`);
|
||||
|
||||
const [orgUserRes] = await db
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgs.orgId, customer.orgId),
|
||||
eq(userOrgs.isOwner, true)
|
||||
await handleSubscriptionLifesycle(customer.orgId, subscription.status);
|
||||
|
||||
const [orgUserRes] = await db
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgs.orgId, customer.orgId),
|
||||
eq(userOrgs.isOwner, true)
|
||||
)
|
||||
)
|
||||
)
|
||||
.innerJoin(users, eq(userOrgs.userId, users.userId));
|
||||
.innerJoin(users, eq(userOrgs.userId, users.userId));
|
||||
|
||||
if (orgUserRes) {
|
||||
const email = orgUserRes.user.email;
|
||||
if (orgUserRes) {
|
||||
const email = orgUserRes.user.email;
|
||||
|
||||
if (email) {
|
||||
moveEmailToAudience(email, AudienceIds.Churned);
|
||||
if (email) {
|
||||
moveEmailToAudience(email, AudienceIds.Churned);
|
||||
}
|
||||
}
|
||||
} else if (type === "license") {
|
||||
logger.debug(`Handling license subscription deletion for orgId ${customer.orgId} and subscription ID ${subscription.id}`);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
|
||||
@@ -26,6 +26,7 @@ import logger from "@server/logger";
|
||||
import { getFeatureIdByMetricId } from "@server/lib/billing/features";
|
||||
import stripe from "#private/lib/stripe";
|
||||
import { handleSubscriptionLifesycle } from "../subscriptionLifecycle";
|
||||
import { getSubType } from "./getSubType";
|
||||
|
||||
export async function handleSubscriptionUpdated(
|
||||
subscription: Stripe.Subscription,
|
||||
@@ -74,11 +75,6 @@ export async function handleSubscriptionUpdated(
|
||||
})
|
||||
.where(eq(subscriptions.subscriptionId, subscription.id));
|
||||
|
||||
await handleSubscriptionLifesycle(
|
||||
existingCustomer.orgId,
|
||||
subscription.status
|
||||
);
|
||||
|
||||
// Upsert subscription items
|
||||
if (Array.isArray(fullSubscription.items?.data)) {
|
||||
const itemsToUpsert = fullSubscription.items.data.map((item) => ({
|
||||
@@ -141,14 +137,14 @@ export async function handleSubscriptionUpdated(
|
||||
// This item has cycled
|
||||
const meterId = item.plan.meter;
|
||||
if (!meterId) {
|
||||
logger.warn(
|
||||
logger.debug(
|
||||
`No meterId found for subscription item ${item.id}. Skipping usage reset.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const featureId = getFeatureIdByMetricId(meterId);
|
||||
if (!featureId) {
|
||||
logger.warn(
|
||||
logger.debug(
|
||||
`No featureId found for meterId ${meterId}. Skipping usage reset.`
|
||||
);
|
||||
continue;
|
||||
@@ -236,6 +232,20 @@ export async function handleSubscriptionUpdated(
|
||||
}
|
||||
}
|
||||
// --- end usage update ---
|
||||
|
||||
const type = getSubType(fullSubscription);
|
||||
if (type === "saas") {
|
||||
logger.debug(`Handling SAAS subscription lifecycle for org ${existingCustomer.orgId}`);
|
||||
// we only need to handle the limit lifecycle for saas subscriptions not for the licenses
|
||||
await handleSubscriptionLifesycle(
|
||||
existingCustomer.orgId,
|
||||
subscription.status
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
`Subscription ${subscription.id} is of type ${type}. No lifecycle handling needed.`
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
|
||||
@@ -6,6 +6,8 @@ export type GeneratedLicenseKey = {
|
||||
createdAt: string;
|
||||
tier: string;
|
||||
type: string;
|
||||
users: number;
|
||||
sites: number;
|
||||
};
|
||||
|
||||
export type ListGeneratedLicenseKeysResponse = GeneratedLicenseKey[];
|
||||
|
||||
@@ -158,6 +158,48 @@ export default function GenerateLicenseKeysTable({
|
||||
: t("licenseTierPersonal");
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "users",
|
||||
friendlyName: t("users"),
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
{t("users")}
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const users = row.original.users;
|
||||
return users === -1 ? "∞" : users;
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "sites",
|
||||
friendlyName: t("sites"),
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() =>
|
||||
column.toggleSorting(column.getIsSorted() === "asc")
|
||||
}
|
||||
>
|
||||
{t("sites")}
|
||||
<ArrowUpDown className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const sites = row.original.sites;
|
||||
return sites === -1 ? "∞" : sites;
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "terminateAt",
|
||||
friendlyName: t("licenseTableValidUntil"),
|
||||
|
||||
Reference in New Issue
Block a user