mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-26 06:46:40 +00:00
Chungus
This commit is contained in:
@@ -10,6 +10,8 @@ import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { checkValidInvite } from "@server/auth/checkValidInvite";
|
||||
import { verifySession } from "@server/auth/sessions/verifySession";
|
||||
import { usageService } from "@server/lib/private/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/private/billing";
|
||||
|
||||
const acceptInviteBodySchema = z
|
||||
.object({
|
||||
@@ -131,6 +133,14 @@ export async function acceptInvite(
|
||||
.where(eq(userOrgs.orgId, existingInvite.orgId));
|
||||
});
|
||||
|
||||
if (totalUsers) {
|
||||
await usageService.updateDaily(
|
||||
existingInvite.orgId,
|
||||
FeatureId.USERS,
|
||||
totalUsers.length
|
||||
);
|
||||
}
|
||||
|
||||
return response<AcceptInviteResponse>(res, {
|
||||
data: { accepted: true, orgId: existingInvite.orgId },
|
||||
success: true,
|
||||
|
||||
@@ -10,6 +10,11 @@ import { db, UserOrg } from "@server/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { idp, idpOidcConfig, roles, userOrgs, users } from "@server/db";
|
||||
import { generateId } from "@server/auth/sessions/app";
|
||||
import { usageService } from "@server/lib/private/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/private/billing";
|
||||
import { build } from "@server/build";
|
||||
import { getOrgTierData } from "@server/routers/private/billing";
|
||||
import { TierId } from "@server/lib/private/billing/tiers";
|
||||
|
||||
const paramsSchema = z
|
||||
.object({
|
||||
@@ -93,6 +98,35 @@ export async function createOrgUser(
|
||||
roleId
|
||||
} = parsedBody.data;
|
||||
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
"No usage data found for this organization"
|
||||
)
|
||||
);
|
||||
}
|
||||
const rejectUsers = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
false,
|
||||
FeatureId.USERS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
} // We need to add one to know if we are violating the limit
|
||||
);
|
||||
if (rejectUsers) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"User limit exceeded. Please upgrade your plan."
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const [role] = await db
|
||||
.select()
|
||||
.from(roles)
|
||||
@@ -112,6 +146,19 @@ export async function createOrgUser(
|
||||
)
|
||||
);
|
||||
} else if (type === "oidc") {
|
||||
if (build === "saas") {
|
||||
const { tier } = await getOrgTierData(orgId);
|
||||
const subscribed = tier === TierId.STANDARD;
|
||||
if (!subscribed) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"This organization's current plan does not support this feature."
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!idpId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
@@ -218,6 +265,14 @@ export async function createOrgUser(
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.orgId, orgId));
|
||||
});
|
||||
|
||||
if (orgUsers) {
|
||||
await usageService.updateDaily(
|
||||
orgId,
|
||||
FeatureId.USERS,
|
||||
orgUsers.length
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return next(
|
||||
createHttpError(HttpCode.BAD_REQUEST, "User type is required")
|
||||
|
||||
@@ -17,6 +17,9 @@ import { sendEmail } from "@server/emails";
|
||||
import SendInviteLink from "@server/emails/templates/SendInviteLink";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
import { usageService } from "@server/lib/private/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/private/billing";
|
||||
import { build } from "@server/build";
|
||||
|
||||
const regenerateTracker = new NodeCache({ stdTTL: 3600, checkperiod: 600 });
|
||||
|
||||
@@ -28,10 +31,7 @@ const inviteUserParamsSchema = z
|
||||
|
||||
const inviteUserBodySchema = z
|
||||
.object({
|
||||
email: z
|
||||
.string()
|
||||
.toLowerCase()
|
||||
.email(),
|
||||
email: z.string().toLowerCase().email(),
|
||||
roleId: z.number(),
|
||||
validHours: z.number().gt(0).lte(168),
|
||||
sendEmail: z.boolean().optional(),
|
||||
@@ -99,7 +99,6 @@ export async function inviteUser(
|
||||
regenerate
|
||||
} = parsedBody.data;
|
||||
|
||||
|
||||
// Check if the organization exists
|
||||
const org = await db
|
||||
.select()
|
||||
@@ -112,6 +111,35 @@ export async function inviteUser(
|
||||
);
|
||||
}
|
||||
|
||||
if (build == "saas") {
|
||||
const usage = await usageService.getUsage(orgId, FeatureId.USERS);
|
||||
if (!usage) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.NOT_FOUND,
|
||||
"No usage data found for this organization"
|
||||
)
|
||||
);
|
||||
}
|
||||
const rejectUsers = await usageService.checkLimitSet(
|
||||
orgId,
|
||||
false,
|
||||
FeatureId.USERS,
|
||||
{
|
||||
...usage,
|
||||
instantaneousValue: (usage.instantaneousValue || 0) + 1
|
||||
} // We need to add one to know if we are violating the limit
|
||||
);
|
||||
if (rejectUsers) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"User limit exceeded. Please upgrade your plan."
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the user already exists in the `users` table
|
||||
const existingUser = await db
|
||||
.select()
|
||||
|
||||
@@ -2,13 +2,17 @@ import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, resources, sites, UserOrg } from "@server/db";
|
||||
import { userOrgs, userResources, users, userSites } from "@server/db";
|
||||
import { and, eq, exists } from "drizzle-orm";
|
||||
import { and, count, eq, exists } from "drizzle-orm";
|
||||
import response from "@server/lib/response";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { usageService } from "@server/lib/private/billing/usageService";
|
||||
import { FeatureId } from "@server/lib/private/billing";
|
||||
import { build } from "@server/build";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
|
||||
const removeUserSchema = z
|
||||
.object({
|
||||
@@ -115,8 +119,33 @@ export async function removeUserOrg(
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.orgId, orgId));
|
||||
|
||||
if (build === "saas") {
|
||||
const [rootUser] = await trx
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.userId, userId));
|
||||
|
||||
const [leftInOrgs] = await trx
|
||||
.select({ count: count() })
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.userId, userId));
|
||||
|
||||
// if the user is not an internal user and does not belong to any org, delete the entire user
|
||||
if (rootUser?.type !== UserType.Internal && !leftInOrgs.count) {
|
||||
await trx.delete(users).where(eq(users.userId, userId));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (userCount) {
|
||||
await usageService.updateDaily(
|
||||
orgId,
|
||||
FeatureId.USERS,
|
||||
userCount.length
|
||||
);
|
||||
}
|
||||
|
||||
return response(res, {
|
||||
data: null,
|
||||
success: true,
|
||||
|
||||
Reference in New Issue
Block a user