mirror of
https://github.com/fosrl/pangolin.git
synced 2026-07-06 12:19:50 +00:00
Rename to limit id
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export enum FeatureId {
|
||||
export enum LimitId {
|
||||
USERS = "users",
|
||||
SITES = "sites",
|
||||
EGRESS_DATA_MB = "egressDataMb",
|
||||
@@ -8,21 +8,23 @@ export enum FeatureId {
|
||||
TIER1 = "tier1"
|
||||
}
|
||||
|
||||
export async function getFeatureDisplayName(featureId: FeatureId): Promise<string> {
|
||||
export async function getFeatureDisplayName(
|
||||
featureId: LimitId
|
||||
): Promise<string> {
|
||||
switch (featureId) {
|
||||
case FeatureId.USERS:
|
||||
case LimitId.USERS:
|
||||
return "Users";
|
||||
case FeatureId.SITES:
|
||||
case LimitId.SITES:
|
||||
return "Sites";
|
||||
case FeatureId.EGRESS_DATA_MB:
|
||||
case LimitId.EGRESS_DATA_MB:
|
||||
return "Egress Data (MB)";
|
||||
case FeatureId.DOMAINS:
|
||||
case LimitId.DOMAINS:
|
||||
return "Domains";
|
||||
case FeatureId.REMOTE_EXIT_NODES:
|
||||
case LimitId.REMOTE_EXIT_NODES:
|
||||
return "Remote Exit Nodes";
|
||||
case FeatureId.ORGINIZATIONS:
|
||||
case LimitId.ORGINIZATIONS:
|
||||
return "Organizations";
|
||||
case FeatureId.TIER1:
|
||||
case LimitId.TIER1:
|
||||
return "Home Lab";
|
||||
default:
|
||||
return featureId;
|
||||
@@ -30,15 +32,16 @@ export async function getFeatureDisplayName(featureId: FeatureId): Promise<strin
|
||||
}
|
||||
|
||||
// this is from the old system
|
||||
export const FeatureMeterIds: Partial<Record<FeatureId, string>> = { // right now we are not charging for any data
|
||||
export const FeatureMeterIds: Partial<Record<LimitId, string>> = {
|
||||
// right now we are not charging for any data
|
||||
// [FeatureId.EGRESS_DATA_MB]: "mtr_61Srreh9eWrExDSCe41D3Ee2Ir7Wm5YW"
|
||||
};
|
||||
|
||||
export const FeatureMeterIdsSandbox: Partial<Record<FeatureId, string>> = {
|
||||
export const FeatureMeterIdsSandbox: Partial<Record<LimitId, string>> = {
|
||||
// [FeatureId.EGRESS_DATA_MB]: "mtr_test_61Snh2a2m6qome5Kv41DCpkOb237B3dQ"
|
||||
};
|
||||
|
||||
export function getFeatureMeterId(featureId: FeatureId): string | undefined {
|
||||
export function getFeatureMeterId(featureId: LimitId): string | undefined {
|
||||
if (
|
||||
process.env.ENVIRONMENT == "prod" &&
|
||||
process.env.SANDBOX_MODE !== "true"
|
||||
@@ -49,22 +52,20 @@ export function getFeatureMeterId(featureId: FeatureId): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
export function getFeatureIdByMetricId(
|
||||
metricId: string
|
||||
): FeatureId | undefined {
|
||||
return (Object.entries(FeatureMeterIds) as [FeatureId, string][]).find(
|
||||
export function getFeatureIdByMetricId(metricId: string): LimitId | undefined {
|
||||
return (Object.entries(FeatureMeterIds) as [LimitId, string][]).find(
|
||||
([_, v]) => v === metricId
|
||||
)?.[0];
|
||||
}
|
||||
|
||||
export type FeaturePriceSet = Partial<Record<FeatureId, string>>;
|
||||
export type FeaturePriceSet = Partial<Record<LimitId, string>>;
|
||||
|
||||
export const tier1FeaturePriceSet: FeaturePriceSet = {
|
||||
[FeatureId.TIER1]: "price_1SzVE3D3Ee2Ir7Wm6wT5Dl3G"
|
||||
[LimitId.TIER1]: "price_1SzVE3D3Ee2Ir7Wm6wT5Dl3G"
|
||||
};
|
||||
|
||||
export const tier1FeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
[FeatureId.TIER1]: "price_1SxgpPDCpkOb237Bfo4rIsoT"
|
||||
[LimitId.TIER1]: "price_1SxgpPDCpkOb237Bfo4rIsoT"
|
||||
};
|
||||
|
||||
export function getTier1FeaturePriceSet(): FeaturePriceSet {
|
||||
@@ -79,11 +80,11 @@ export function getTier1FeaturePriceSet(): FeaturePriceSet {
|
||||
}
|
||||
|
||||
export const tier2FeaturePriceSet: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SzVCcD3Ee2Ir7Wmn6U3KvPN"
|
||||
[LimitId.USERS]: "price_1SzVCcD3Ee2Ir7Wmn6U3KvPN"
|
||||
};
|
||||
|
||||
export const tier2FeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SxaEHDCpkOb237BD9lBkPiR"
|
||||
[LimitId.USERS]: "price_1SxaEHDCpkOb237BD9lBkPiR"
|
||||
};
|
||||
|
||||
export function getTier2FeaturePriceSet(): FeaturePriceSet {
|
||||
@@ -98,11 +99,11 @@ export function getTier2FeaturePriceSet(): FeaturePriceSet {
|
||||
}
|
||||
|
||||
export const tier3FeaturePriceSet: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SzVDKD3Ee2Ir7WmPtOKNusv"
|
||||
[LimitId.USERS]: "price_1SzVDKD3Ee2Ir7WmPtOKNusv"
|
||||
};
|
||||
|
||||
export const tier3FeaturePriceSetSandbox: FeaturePriceSet = {
|
||||
[FeatureId.USERS]: "price_1SxaEODCpkOb237BiXdCBSfs"
|
||||
[LimitId.USERS]: "price_1SxaEODCpkOb237BiXdCBSfs"
|
||||
};
|
||||
|
||||
export function getTier3FeaturePriceSet(): FeaturePriceSet {
|
||||
@@ -116,7 +117,7 @@ export function getTier3FeaturePriceSet(): FeaturePriceSet {
|
||||
}
|
||||
}
|
||||
|
||||
export function getFeatureIdByPriceId(priceId: string): FeatureId | undefined {
|
||||
export function getFeatureIdByPriceId(priceId: string): LimitId | undefined {
|
||||
// Check all feature price sets
|
||||
const allPriceSets = [
|
||||
getTier1FeaturePriceSet(),
|
||||
@@ -125,7 +126,7 @@ export function getFeatureIdByPriceId(priceId: string): FeatureId | undefined {
|
||||
];
|
||||
|
||||
for (const priceSet of allPriceSets) {
|
||||
const entry = (Object.entries(priceSet) as [FeatureId, string][]).find(
|
||||
const entry = (Object.entries(priceSet) as [LimitId, string][]).find(
|
||||
([_, price]) => price === priceId
|
||||
);
|
||||
if (entry) {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import Stripe from "stripe";
|
||||
import { FeatureId, FeaturePriceSet } from "./features";
|
||||
import { LimitId, FeaturePriceSet } from "./features";
|
||||
import { usageService } from "./usageService";
|
||||
|
||||
export async function getLineItems(
|
||||
featurePriceSet: FeaturePriceSet,
|
||||
orgId: string,
|
||||
orgId: string
|
||||
): Promise<Stripe.Checkout.SessionCreateParams.LineItem[]> {
|
||||
const users = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
const users = await usageService.getUsage(orgId, LimitId.USERS);
|
||||
|
||||
return Object.entries(featurePriceSet).map(([featureId, priceId]) => {
|
||||
let quantity: number | undefined;
|
||||
|
||||
if (featureId === FeatureId.USERS) {
|
||||
if (featureId === LimitId.USERS) {
|
||||
quantity = users?.instantaneousValue || 1;
|
||||
} else if (featureId === FeatureId.TIER1) {
|
||||
} else if (featureId === LimitId.TIER1) {
|
||||
quantity = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
import { FeatureId } from "./features";
|
||||
import { LimitId } from "./features";
|
||||
|
||||
export type LimitSet = Partial<{
|
||||
[key in FeatureId]: {
|
||||
[key in LimitId]: {
|
||||
value: number | null; // null indicates no limit
|
||||
description?: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
export const freeLimitSet: LimitSet = {
|
||||
[FeatureId.SITES]: { value: 5, description: "Basic limit" },
|
||||
[FeatureId.USERS]: { value: 5, description: "Basic limit" },
|
||||
[FeatureId.DOMAINS]: { value: 5, description: "Basic limit" },
|
||||
[FeatureId.REMOTE_EXIT_NODES]: { value: 1, description: "Basic limit" },
|
||||
[FeatureId.ORGINIZATIONS]: { value: 1, description: "Basic limit" },
|
||||
[LimitId.SITES]: { value: 5, description: "Basic limit" },
|
||||
[LimitId.USERS]: { value: 5, description: "Basic limit" },
|
||||
[LimitId.DOMAINS]: { value: 5, description: "Basic limit" },
|
||||
[LimitId.REMOTE_EXIT_NODES]: { value: 1, description: "Basic limit" },
|
||||
[LimitId.ORGINIZATIONS]: { value: 1, description: "Basic limit" }
|
||||
};
|
||||
|
||||
export const tier1LimitSet: LimitSet = {
|
||||
[FeatureId.USERS]: { value: 7, description: "Home limit" },
|
||||
[FeatureId.SITES]: { value: 10, description: "Home limit" },
|
||||
[FeatureId.DOMAINS]: { value: 10, description: "Home limit" },
|
||||
[FeatureId.REMOTE_EXIT_NODES]: { value: 1, description: "Home limit" },
|
||||
[FeatureId.ORGINIZATIONS]: { value: 1, description: "Home limit" },
|
||||
[LimitId.USERS]: { value: 7, description: "Home limit" },
|
||||
[LimitId.SITES]: { value: 10, description: "Home limit" },
|
||||
[LimitId.DOMAINS]: { value: 10, description: "Home limit" },
|
||||
[LimitId.REMOTE_EXIT_NODES]: { value: 1, description: "Home limit" },
|
||||
[LimitId.ORGINIZATIONS]: { value: 1, description: "Home limit" }
|
||||
};
|
||||
|
||||
export const tier2LimitSet: LimitSet = {
|
||||
[FeatureId.USERS]: {
|
||||
[LimitId.USERS]: {
|
||||
value: 50,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.SITES]: {
|
||||
[LimitId.SITES]: {
|
||||
value: 50,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.DOMAINS]: {
|
||||
[LimitId.DOMAINS]: {
|
||||
value: 50,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.REMOTE_EXIT_NODES]: {
|
||||
[LimitId.REMOTE_EXIT_NODES]: {
|
||||
value: 3,
|
||||
description: "Team limit"
|
||||
},
|
||||
[FeatureId.ORGINIZATIONS]: {
|
||||
[LimitId.ORGINIZATIONS]: {
|
||||
value: 1,
|
||||
description: "Team limit"
|
||||
}
|
||||
};
|
||||
|
||||
export const tier3LimitSet: LimitSet = {
|
||||
[FeatureId.USERS]: {
|
||||
[LimitId.USERS]: {
|
||||
value: 250,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.SITES]: {
|
||||
[LimitId.SITES]: {
|
||||
value: 250,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.DOMAINS]: {
|
||||
[LimitId.DOMAINS]: {
|
||||
value: 100,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.REMOTE_EXIT_NODES]: {
|
||||
[LimitId.REMOTE_EXIT_NODES]: {
|
||||
value: 20,
|
||||
description: "Business limit"
|
||||
},
|
||||
[FeatureId.ORGINIZATIONS]: {
|
||||
[LimitId.ORGINIZATIONS]: {
|
||||
value: 5,
|
||||
description: "Business limit"
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { db, limits } from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { LimitSet } from "./limitSet";
|
||||
import { FeatureId } from "./features";
|
||||
import { LimitId } from "./features";
|
||||
import logger from "@server/logger";
|
||||
|
||||
class LimitService {
|
||||
@@ -38,7 +38,7 @@ class LimitService {
|
||||
|
||||
async getOrgLimit(
|
||||
orgId: string,
|
||||
featureId: FeatureId
|
||||
featureId: LimitId
|
||||
): Promise<number | null> {
|
||||
const limitId = `${orgId}-${featureId}`;
|
||||
const [limit] = await db
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Transaction,
|
||||
orgs
|
||||
} from "@server/db";
|
||||
import { FeatureId, getFeatureMeterId } from "./features";
|
||||
import { LimitId, getFeatureMeterId } from "./features";
|
||||
import logger from "@server/logger";
|
||||
import { build } from "@server/build";
|
||||
import { regionalCache as cache } from "#dynamic/lib/cache";
|
||||
@@ -37,7 +37,7 @@ export class UsageService {
|
||||
|
||||
public async add(
|
||||
orgId: string,
|
||||
featureId: FeatureId,
|
||||
featureId: LimitId,
|
||||
value: number,
|
||||
transaction: any = null
|
||||
): Promise<Usage | null> {
|
||||
@@ -114,7 +114,7 @@ export class UsageService {
|
||||
|
||||
private async internalAddUsage(
|
||||
orgId: string, // here the orgId is the billing org already resolved by getBillingOrg in updateCount
|
||||
featureId: FeatureId,
|
||||
featureId: LimitId,
|
||||
value: number,
|
||||
trx: Transaction
|
||||
): Promise<Usage> {
|
||||
@@ -163,7 +163,7 @@ export class UsageService {
|
||||
|
||||
async updateCount(
|
||||
orgId: string,
|
||||
featureId: FeatureId,
|
||||
featureId: LimitId,
|
||||
value?: number,
|
||||
customerId?: string
|
||||
): Promise<void> {
|
||||
@@ -227,7 +227,7 @@ export class UsageService {
|
||||
|
||||
private async getCustomerId(
|
||||
orgId: string,
|
||||
featureId: FeatureId
|
||||
featureId: LimitId
|
||||
): Promise<string | null> {
|
||||
const orgIdToUse = await this.getBillingOrg(orgId);
|
||||
|
||||
@@ -269,7 +269,7 @@ export class UsageService {
|
||||
|
||||
public async getUsage(
|
||||
orgId: string,
|
||||
featureId: FeatureId,
|
||||
featureId: LimitId,
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<Usage | null> {
|
||||
if (noop()) {
|
||||
@@ -376,7 +376,7 @@ export class UsageService {
|
||||
|
||||
public async checkLimitSet(
|
||||
orgId: string,
|
||||
featureId?: FeatureId,
|
||||
featureId?: LimitId,
|
||||
usage?: Usage,
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<boolean> {
|
||||
@@ -424,7 +424,7 @@ export class UsageService {
|
||||
} else {
|
||||
currentUsage = await this.getUsage(
|
||||
orgIdToUse,
|
||||
limit.featureId as FeatureId,
|
||||
limit.featureId as LimitId,
|
||||
trx
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import { deletePeer } from "@server/routers/gerbil/peers";
|
||||
import { OlmErrorCodes } from "@server/routers/olm/error";
|
||||
import { sendTerminateClient } from "@server/routers/client/terminate";
|
||||
import { usageService } from "./billing/usageService";
|
||||
import { FeatureId } from "./billing";
|
||||
import { LimitId } from "./billing";
|
||||
|
||||
export type DeleteOrgByIdResult = {
|
||||
deletedNewtIds: string[];
|
||||
@@ -140,7 +140,9 @@ export async function deleteOrgById(
|
||||
.select({ count: count() })
|
||||
.from(orgDomains)
|
||||
.where(eq(orgDomains.domainId, domainId));
|
||||
logger.info(`Found ${orgCount.count} orgs using domain ${domainId}`);
|
||||
logger.info(
|
||||
`Found ${orgCount.count} orgs using domain ${domainId}`
|
||||
);
|
||||
if (orgCount.count === 1) {
|
||||
domainIdsToDelete.push(domainId);
|
||||
}
|
||||
@@ -152,7 +154,7 @@ export async function deleteOrgById(
|
||||
.where(inArray(domains.domainId, domainIdsToDelete));
|
||||
}
|
||||
|
||||
await usageService.add(orgId, FeatureId.ORGINIZATIONS, -1, trx); // here we are decreasing the org count BEFORE deleting the org because we need to still be able to get the org to get the billing org inside of here
|
||||
await usageService.add(orgId, LimitId.ORGINIZATIONS, -1, trx); // here we are decreasing the org count BEFORE deleting the org because we need to still be able to get the org to get the billing org inside of here
|
||||
|
||||
await trx.delete(orgs).where(eq(orgs.orgId, orgId));
|
||||
|
||||
@@ -199,22 +201,22 @@ export async function deleteOrgById(
|
||||
if (org.billingOrgId) {
|
||||
usageService.updateCount(
|
||||
org.billingOrgId,
|
||||
FeatureId.DOMAINS,
|
||||
LimitId.DOMAINS,
|
||||
domainCount ?? 0
|
||||
);
|
||||
usageService.updateCount(
|
||||
org.billingOrgId,
|
||||
FeatureId.SITES,
|
||||
LimitId.SITES,
|
||||
siteCount ?? 0
|
||||
);
|
||||
usageService.updateCount(
|
||||
org.billingOrgId,
|
||||
FeatureId.USERS,
|
||||
LimitId.USERS,
|
||||
userCount ?? 0
|
||||
);
|
||||
usageService.updateCount(
|
||||
org.billingOrgId,
|
||||
FeatureId.REMOTE_EXIT_NODES,
|
||||
LimitId.REMOTE_EXIT_NODES,
|
||||
remoteExitNodeCount ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
} from "@server/db";
|
||||
import { eq, and, inArray, ne, exists } from "drizzle-orm";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
|
||||
export async function assignUserToOrg(
|
||||
org: Org,
|
||||
@@ -61,7 +61,7 @@ export async function assignUserToOrg(
|
||||
);
|
||||
|
||||
if (orgsInBillingDomainThatTheUserIsStillIn.length === 0) {
|
||||
await usageService.add(org.orgId, FeatureId.USERS, 1, trx);
|
||||
await usageService.add(org.orgId, LimitId.USERS, 1, trx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ export async function removeUserFromOrg(
|
||||
);
|
||||
|
||||
if (orgsInBillingDomainThatTheUserIsStillIn.length === 0) {
|
||||
await usageService.add(org.orgId, FeatureId.USERS, -1, trx);
|
||||
await usageService.add(org.orgId, LimitId.USERS, -1, trx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
getTier1FeaturePriceSet,
|
||||
getTier3FeaturePriceSet,
|
||||
getTier2FeaturePriceSet,
|
||||
FeatureId,
|
||||
LimitId,
|
||||
type FeaturePriceSet
|
||||
} from "@server/lib/billing";
|
||||
import { getLineItems } from "@server/lib/billing/getLineItems";
|
||||
@@ -214,7 +214,7 @@ export async function changeTier(
|
||||
}
|
||||
|
||||
// Map to the corresponding feature in the new tier
|
||||
const newPriceId = targetPriceSet[FeatureId.USERS];
|
||||
const newPriceId = targetPriceSet[LimitId.USERS];
|
||||
|
||||
if (newPriceId) {
|
||||
return {
|
||||
|
||||
@@ -24,7 +24,7 @@ import { fromZodError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { Limit, limits, Usage, usage } from "@server/db";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { GetOrgUsageResponse } from "@server/routers/billing/types";
|
||||
|
||||
const getOrgSchema = z.strictObject({
|
||||
@@ -93,16 +93,16 @@ export async function getOrgUsage(
|
||||
// Get usage for org
|
||||
const usageData = [];
|
||||
|
||||
const sites = await usageService.getUsage(orgId, FeatureId.SITES);
|
||||
const users = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
const domains = await usageService.getUsage(orgId, FeatureId.DOMAINS);
|
||||
const sites = await usageService.getUsage(orgId, LimitId.SITES);
|
||||
const users = await usageService.getUsage(orgId, LimitId.USERS);
|
||||
const domains = await usageService.getUsage(orgId, LimitId.DOMAINS);
|
||||
const remoteExitNodes = await usageService.getUsage(
|
||||
orgId,
|
||||
FeatureId.REMOTE_EXIT_NODES
|
||||
LimitId.REMOTE_EXIT_NODES
|
||||
);
|
||||
const organizations = await usageService.getUsage(
|
||||
orgId,
|
||||
FeatureId.ORGINIZATIONS
|
||||
LimitId.ORGINIZATIONS
|
||||
);
|
||||
// const egressData = await usageService.getUsage(
|
||||
// orgId,
|
||||
|
||||
@@ -35,7 +35,7 @@ import logger from "@server/logger";
|
||||
import { and, eq, inArray, ne } from "drizzle-orm";
|
||||
import { getNextAvailableSubnet } from "@server/lib/exitNodes";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { CreateRemoteExitNodeResponse } from "@server/routers/remoteExitNode/types";
|
||||
|
||||
export const paramsSchema = z.object({
|
||||
@@ -79,7 +79,10 @@ export async function createRemoteExitNode(
|
||||
|
||||
const { remoteExitNodeId, secret } = parsedBody.data;
|
||||
|
||||
if (req.user && (!req.userOrgRoleIds || req.userOrgRoleIds.length === 0)) {
|
||||
if (
|
||||
req.user &&
|
||||
(!req.userOrgRoleIds || req.userOrgRoleIds.length === 0)
|
||||
) {
|
||||
return next(
|
||||
createHttpError(HttpCode.FORBIDDEN, "User does not have a role")
|
||||
);
|
||||
@@ -87,13 +90,13 @@ export async function createRemoteExitNode(
|
||||
|
||||
const usage = await usageService.getUsage(
|
||||
orgId,
|
||||
FeatureId.REMOTE_EXIT_NODES
|
||||
LimitId.REMOTE_EXIT_NODES
|
||||
);
|
||||
if (usage) {
|
||||
const rejectRemoteExitNodes = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
|
||||
FeatureId.REMOTE_EXIT_NODES,
|
||||
LimitId.REMOTE_EXIT_NODES,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
@@ -264,7 +267,7 @@ export async function createRemoteExitNode(
|
||||
if (orgsInBillingDomainThatTheNodeIsStillIn.length === 0) {
|
||||
await usageService.add(
|
||||
orgId,
|
||||
FeatureId.REMOTE_EXIT_NODES,
|
||||
LimitId.REMOTE_EXIT_NODES,
|
||||
1,
|
||||
trx
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ import createHttpError from "http-errors";
|
||||
import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
|
||||
const paramsSchema = z.strictObject({
|
||||
orgId: z.string().min(1),
|
||||
@@ -117,7 +117,7 @@ export async function deleteRemoteExitNode(
|
||||
if (orgsInBillingDomainThatTheNodeIsStillIn.length === 0) {
|
||||
await usageService.add(
|
||||
orgId,
|
||||
FeatureId.REMOTE_EXIT_NODES,
|
||||
LimitId.REMOTE_EXIT_NODES,
|
||||
-1,
|
||||
trx
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ import { getOrgTierData } from "#dynamic/lib/billing";
|
||||
import { deleteOrgById, sendTerminationMessages } from "@server/lib/deleteOrg";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
|
||||
const deleteMyAccountBody = z.strictObject({
|
||||
password: z.string().optional(),
|
||||
@@ -220,7 +220,7 @@ export async function deleteMyAccount(
|
||||
await trx.delete(users).where(eq(users.userId, userId));
|
||||
// loop through the other orgs and decrement the count
|
||||
for (const userOrg of otherOrgsTheUserWasIn) {
|
||||
await usageService.add(userOrg.orgId, FeatureId.USERS, -1, trx);
|
||||
await usageService.add(userOrg.orgId, LimitId.USERS, -1, trx);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { subdomainSchema } from "@server/lib/schemas";
|
||||
import { generateId } from "@server/auth/sessions/app";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { isSecondLevelDomain, isValidDomain } from "@server/lib/validators";
|
||||
import { build } from "@server/build";
|
||||
import config from "@server/lib/config";
|
||||
@@ -120,7 +120,7 @@ export async function createOrgDomain(
|
||||
}
|
||||
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.DOMAINS);
|
||||
const usage = await usageService.getUsage(orgId, LimitId.DOMAINS);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
@@ -132,7 +132,7 @@ export async function createOrgDomain(
|
||||
const rejectDomains = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
|
||||
FeatureId.DOMAINS,
|
||||
LimitId.DOMAINS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
@@ -346,7 +346,7 @@ export async function createOrgDomain(
|
||||
await trx.insert(dnsRecords).values(recordsToInsert);
|
||||
}
|
||||
|
||||
await usageService.add(orgId, FeatureId.DOMAINS, 1, trx);
|
||||
await usageService.add(orgId, LimitId.DOMAINS, 1, trx);
|
||||
});
|
||||
|
||||
if (!returned) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
|
||||
const paramsSchema = z.strictObject({
|
||||
domainId: z.string(),
|
||||
@@ -77,7 +77,7 @@ export async function deleteAccountDomain(
|
||||
|
||||
await trx.delete(domains).where(eq(domains.domainId, domainId));
|
||||
|
||||
await usageService.add(orgId, FeatureId.DOMAINS, -1, trx);
|
||||
await usageService.add(orgId, LimitId.DOMAINS, -1, trx);
|
||||
});
|
||||
|
||||
return response<DeleteAccountDomainResponse>(res, {
|
||||
|
||||
@@ -6,7 +6,7 @@ import createHttpError from "http-errors";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import response from "@server/lib/response";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing/features";
|
||||
import { LimitId } from "@server/lib/billing/features";
|
||||
import { checkExitNodeOrg } from "#dynamic/lib/exitNodes";
|
||||
import { build } from "@server/build";
|
||||
|
||||
@@ -171,8 +171,9 @@ export async function flushSiteBandwidthToDb(): Promise<void> {
|
||||
}
|
||||
|
||||
// PostgreSQL: batch UPDATE … FROM (VALUES …) - single round-trip per chunk.
|
||||
const valuesList = chunk.map(([publicKey, { bytesIn, bytesOut }]) =>
|
||||
sql`(${publicKey}::text, ${bytesIn}::real, ${bytesOut}::real)`
|
||||
const valuesList = chunk.map(
|
||||
([publicKey, { bytesIn, bytesOut }]) =>
|
||||
sql`(${publicKey}::text, ${bytesIn}::real, ${bytesOut}::real)`
|
||||
);
|
||||
const valuesClause = sql.join(valuesList, sql`, `);
|
||||
return dbQueryRows<{ orgId: string; pubKey: string }>(sql`
|
||||
@@ -228,7 +229,7 @@ export async function flushSiteBandwidthToDb(): Promise<void> {
|
||||
const totalBandwidth = orgUsageMap.get(orgId)!;
|
||||
const bandwidthUsage = await usageService.add(
|
||||
orgId,
|
||||
FeatureId.EGRESS_DATA_MB,
|
||||
LimitId.EGRESS_DATA_MB,
|
||||
totalBandwidth
|
||||
);
|
||||
if (bandwidthUsage) {
|
||||
@@ -236,7 +237,7 @@ export async function flushSiteBandwidthToDb(): Promise<void> {
|
||||
usageService
|
||||
.checkLimitSet(
|
||||
orgId,
|
||||
FeatureId.EGRESS_DATA_MB,
|
||||
LimitId.EGRESS_DATA_MB,
|
||||
bandwidthUsage
|
||||
)
|
||||
.catch((error: any) => {
|
||||
@@ -247,10 +248,7 @@ export async function flushSiteBandwidthToDb(): Promise<void> {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Error processing usage for org ${orgId}:`,
|
||||
error
|
||||
);
|
||||
logger.error(`Error processing usage for org ${orgId}:`, error);
|
||||
// Continue with other orgs.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
} from "@server/auth/sessions/app";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { build } from "@server/build";
|
||||
import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs";
|
||||
@@ -645,7 +645,7 @@ export async function validateOidcCallback(
|
||||
for (const orgCount of orgUserCounts) {
|
||||
await usageService.updateCount(
|
||||
orgCount.orgId,
|
||||
FeatureId.USERS,
|
||||
LimitId.USERS,
|
||||
orgCount.userCount
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import { getUniqueSiteName } from "@server/db/names";
|
||||
import moment from "moment";
|
||||
import { build } from "@server/build";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { INSPECT_MAX_BYTES } from "buffer";
|
||||
import { getNextAvailableClientSubnet } from "@server/lib/ip";
|
||||
|
||||
@@ -169,7 +169,7 @@ export async function registerNewt(
|
||||
|
||||
// SaaS billing check
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.SITES);
|
||||
const usage = await usageService.getUsage(orgId, LimitId.SITES);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
@@ -180,7 +180,7 @@ export async function registerNewt(
|
||||
}
|
||||
const rejectSites = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
FeatureId.SITES,
|
||||
LimitId.SITES,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
@@ -274,7 +274,7 @@ export async function registerNewt(
|
||||
)
|
||||
);
|
||||
|
||||
await usageService.add(orgId, FeatureId.SITES, 1, trx);
|
||||
await usageService.add(orgId, LimitId.SITES, 1, trx);
|
||||
});
|
||||
} finally {
|
||||
await releaseSubnetLock();
|
||||
|
||||
@@ -27,7 +27,7 @@ import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { isValidCIDR } from "@server/lib/validators";
|
||||
import { createCustomer } from "#dynamic/lib/billing";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId, limitsService, freeLimitSet } from "@server/lib/billing";
|
||||
import { LimitId, limitsService, freeLimitSet } from "@server/lib/billing";
|
||||
import { build } from "@server/build";
|
||||
import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs";
|
||||
import { doCidrsOverlap } from "@server/lib/ip";
|
||||
@@ -202,7 +202,7 @@ export async function createOrg(
|
||||
if (build == "saas" && billingOrgIdForNewOrg) {
|
||||
const usage = await usageService.getUsage(
|
||||
billingOrgIdForNewOrg,
|
||||
FeatureId.ORGINIZATIONS
|
||||
LimitId.ORGINIZATIONS
|
||||
);
|
||||
if (!usage) {
|
||||
return next(
|
||||
@@ -214,7 +214,7 @@ export async function createOrg(
|
||||
}
|
||||
const rejectOrgs = await usageService.checkLimitSet(
|
||||
billingOrgIdForNewOrg,
|
||||
FeatureId.ORGINIZATIONS,
|
||||
LimitId.ORGINIZATIONS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
@@ -421,7 +421,7 @@ export async function createOrg(
|
||||
if (customerId) {
|
||||
await usageService.updateCount(
|
||||
orgId,
|
||||
FeatureId.USERS,
|
||||
LimitId.USERS,
|
||||
1,
|
||||
customerId
|
||||
); // Only 1 because we are creating the org
|
||||
@@ -431,7 +431,7 @@ export async function createOrg(
|
||||
if (numOrgs) {
|
||||
usageService.updateCount(
|
||||
billingOrgIdForNewOrg || orgId,
|
||||
FeatureId.ORGINIZATIONS,
|
||||
LimitId.ORGINIZATIONS,
|
||||
numOrgs
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { getNextAvailableClientSubnet, isIpInCidr } from "@server/lib/ip";
|
||||
import { verifyExitNodeOrgAccess } from "#dynamic/lib/exitNodes";
|
||||
import { build } from "@server/build";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { generateId } from "@server/auth/sessions/app";
|
||||
|
||||
const createSiteParamsSchema = z.strictObject({
|
||||
@@ -160,7 +160,7 @@ export async function createSite(
|
||||
}
|
||||
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.SITES);
|
||||
const usage = await usageService.getUsage(orgId, LimitId.SITES);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
@@ -172,7 +172,7 @@ export async function createSite(
|
||||
const rejectSites = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
|
||||
FeatureId.SITES,
|
||||
LimitId.SITES,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
@@ -519,7 +519,7 @@ export async function createSite(
|
||||
});
|
||||
}
|
||||
|
||||
await usageService.add(orgId, FeatureId.SITES, 1, trx);
|
||||
await usageService.add(orgId, LimitId.SITES, 1, trx);
|
||||
});
|
||||
} finally {
|
||||
await releaseSubnetLock?.();
|
||||
|
||||
@@ -13,7 +13,7 @@ import { sendToClient } from "#dynamic/routers/ws";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { cleanupSiteAssociations } from "@server/lib/rebuildClientAssociations";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { ActionsEnum, checkUserActionPermission } from "@server/auth/actions";
|
||||
import {
|
||||
deleteAssociatedResourcesForSite,
|
||||
@@ -177,7 +177,7 @@ export async function deleteSite(
|
||||
}
|
||||
|
||||
await trx.delete(sites).where(eq(sites.siteId, siteId));
|
||||
await usageService.add(site.orgId, FeatureId.SITES, -1, trx);
|
||||
await usageService.add(site.orgId, LimitId.SITES, -1, trx);
|
||||
});
|
||||
|
||||
if (deleteResources) {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { fromError } from "zod-validation-error";
|
||||
import { checkValidInvite } from "@server/auth/checkValidInvite";
|
||||
import { verifySession } from "@server/auth/sessions/verifySession";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs";
|
||||
import { build } from "@server/build";
|
||||
import { assignUserToOrg } from "@server/lib/userOrg";
|
||||
@@ -104,7 +104,7 @@ export async function acceptInvite(
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(
|
||||
existingInvite.orgId,
|
||||
FeatureId.USERS
|
||||
LimitId.USERS
|
||||
);
|
||||
if (!usage) {
|
||||
return next(
|
||||
@@ -117,7 +117,7 @@ export async function acceptInvite(
|
||||
const rejectUsers = await usageService.checkLimitSet(
|
||||
existingInvite.orgId,
|
||||
|
||||
FeatureId.USERS,
|
||||
LimitId.USERS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
|
||||
@@ -12,7 +12,7 @@ import { and, eq, inArray } from "drizzle-orm";
|
||||
import { idp, idpOidcConfig, roles, userOrgs, users } from "@server/db";
|
||||
import { generateId } from "@server/auth/sessions/app";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { build } from "@server/build";
|
||||
import { calculateUserClientsForOrgs } from "@server/lib/calculateUserClientsForOrgs";
|
||||
import { isSubscribed } from "#dynamic/lib/isSubscribed";
|
||||
@@ -123,7 +123,7 @@ export async function createOrgUser(
|
||||
} = parsedBody.data;
|
||||
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
const usage = await usageService.getUsage(orgId, LimitId.USERS);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
@@ -135,7 +135,7 @@ export async function createOrgUser(
|
||||
const rejectUsers = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
|
||||
FeatureId.USERS,
|
||||
LimitId.USERS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
|
||||
@@ -25,7 +25,7 @@ import SendInviteLink from "@server/emails/templates/SendInviteLink";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
import { usageService } from "@server/lib/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/billing";
|
||||
import { LimitId } from "@server/lib/billing";
|
||||
import { TierFeature, tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import { build } from "@server/build";
|
||||
import cache from "#dynamic/lib/cache";
|
||||
@@ -73,7 +73,6 @@ const InviteUserResponseDataSchema = z.object({
|
||||
expiresAt: z.number()
|
||||
});
|
||||
|
||||
|
||||
registry.registerPath({
|
||||
method: "post",
|
||||
path: "/org/{orgId}/create-invite",
|
||||
@@ -94,7 +93,9 @@ registry.registerPath({
|
||||
description: "Successful response",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: createApiResponseSchema(InviteUserResponseDataSchema)
|
||||
schema: createApiResponseSchema(
|
||||
InviteUserResponseDataSchema
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,7 +182,7 @@ export async function inviteUser(
|
||||
}
|
||||
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
const usage = await usageService.getUsage(orgId, LimitId.USERS);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
@@ -192,7 +193,7 @@ export async function inviteUser(
|
||||
}
|
||||
const rejectUsers = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
FeatureId.USERS,
|
||||
LimitId.USERS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
|
||||
Reference in New Issue
Block a user