mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-19 11:26:37 +00:00
Compare commits
3 Commits
1.15.4-s.3
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
874794c996 | ||
|
|
5e37c4e85f | ||
|
|
4e7eac368f |
@@ -46,6 +46,8 @@ export class UsageService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let orgIdToUse = await this.getBillingOrg(orgId, transaction);
|
||||||
|
|
||||||
// Truncate value to 11 decimal places
|
// Truncate value to 11 decimal places
|
||||||
value = this.truncateValue(value);
|
value = this.truncateValue(value);
|
||||||
|
|
||||||
@@ -57,7 +59,6 @@ export class UsageService {
|
|||||||
try {
|
try {
|
||||||
let usage;
|
let usage;
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
const orgIdToUse = await this.getBillingOrg(orgId, transaction);
|
|
||||||
usage = await this.internalAddUsage(
|
usage = await this.internalAddUsage(
|
||||||
orgIdToUse,
|
orgIdToUse,
|
||||||
featureId,
|
featureId,
|
||||||
@@ -66,7 +67,6 @@ export class UsageService {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
const orgIdToUse = await this.getBillingOrg(orgId, trx);
|
|
||||||
usage = await this.internalAddUsage(
|
usage = await this.internalAddUsage(
|
||||||
orgIdToUse,
|
orgIdToUse,
|
||||||
featureId,
|
featureId,
|
||||||
@@ -92,7 +92,7 @@ export class UsageService {
|
|||||||
const delay = baseDelay + jitter;
|
const delay = baseDelay + jitter;
|
||||||
|
|
||||||
logger.warn(
|
logger.warn(
|
||||||
`Deadlock detected for ${orgId}/${featureId}, retrying attempt ${attempt}/${maxRetries} after ${delay.toFixed(0)}ms`
|
`Deadlock detected for ${orgIdToUse}/${featureId}, retrying attempt ${attempt}/${maxRetries} after ${delay.toFixed(0)}ms`
|
||||||
);
|
);
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
@@ -100,7 +100,7 @@ export class UsageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
`Failed to add usage for ${orgId}/${featureId} after ${attempt} attempts:`,
|
`Failed to add usage for ${orgIdToUse}/${featureId} after ${attempt} attempts:`,
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -169,7 +169,7 @@ export class UsageService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgIdToUse = await this.getBillingOrg(orgId);
|
let orgIdToUse = await this.getBillingOrg(orgId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Truncate value to 11 decimal places if provided
|
// Truncate value to 11 decimal places if provided
|
||||||
@@ -227,7 +227,7 @@ export class UsageService {
|
|||||||
orgId: string,
|
orgId: string,
|
||||||
featureId: FeatureId
|
featureId: FeatureId
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
const orgIdToUse = await this.getBillingOrg(orgId);
|
let orgIdToUse = await this.getBillingOrg(orgId);
|
||||||
|
|
||||||
const cacheKey = `customer_${orgIdToUse}_${featureId}`;
|
const cacheKey = `customer_${orgIdToUse}_${featureId}`;
|
||||||
const cached = cache.get<string>(cacheKey);
|
const cached = cache.get<string>(cacheKey);
|
||||||
@@ -274,7 +274,7 @@ export class UsageService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgIdToUse = await this.getBillingOrg(orgId, trx);
|
let orgIdToUse = await this.getBillingOrg(orgId, trx);
|
||||||
|
|
||||||
const usageId = `${orgIdToUse}-${featureId}`;
|
const usageId = `${orgIdToUse}-${featureId}`;
|
||||||
|
|
||||||
@@ -382,7 +382,7 @@ export class UsageService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgIdToUse = await this.getBillingOrg(orgId, trx);
|
let orgIdToUse = await this.getBillingOrg(orgId, trx);
|
||||||
|
|
||||||
// This method should check the current usage against the limits set for the organization
|
// This method should check the current usage against the limits set for the organization
|
||||||
// and kick out all of the sites on the org
|
// and kick out all of the sites on the org
|
||||||
|
|||||||
@@ -23,9 +23,14 @@ export async function verifyApiKeyRoleAccess(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { roleIds } = req.body;
|
let allRoleIds: number[] = [];
|
||||||
const allRoleIds =
|
if (!isNaN(singleRoleId)) {
|
||||||
roleIds || (isNaN(singleRoleId) ? [] : [singleRoleId]);
|
// If roleId is provided in URL params, query params, or body (single), use it exclusively
|
||||||
|
allRoleIds = [singleRoleId];
|
||||||
|
} else if (req.body?.roleIds) {
|
||||||
|
// Only use body.roleIds if no single roleId was provided
|
||||||
|
allRoleIds = req.body.roleIds;
|
||||||
|
}
|
||||||
|
|
||||||
if (allRoleIds.length === 0) {
|
if (allRoleIds.length === 0) {
|
||||||
return next();
|
return next();
|
||||||
|
|||||||
@@ -23,8 +23,14 @@ export async function verifyRoleAccess(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roleIds = req.body?.roleIds;
|
let allRoleIds: number[] = [];
|
||||||
const allRoleIds = roleIds || (isNaN(singleRoleId) ? [] : [singleRoleId]);
|
if (!isNaN(singleRoleId)) {
|
||||||
|
// If roleId is provided in URL params, query params, or body (single), use it exclusively
|
||||||
|
allRoleIds = [singleRoleId];
|
||||||
|
} else if (req.body?.roleIds) {
|
||||||
|
// Only use body.roleIds if no single roleId was provided
|
||||||
|
allRoleIds = req.body.roleIds;
|
||||||
|
}
|
||||||
|
|
||||||
if (allRoleIds.length === 0) {
|
if (allRoleIds.length === 0) {
|
||||||
return next();
|
return next();
|
||||||
|
|||||||
@@ -78,8 +78,7 @@ export async function getOrgTierData(
|
|||||||
if (
|
if (
|
||||||
subscription.type === "tier1" ||
|
subscription.type === "tier1" ||
|
||||||
subscription.type === "tier2" ||
|
subscription.type === "tier2" ||
|
||||||
subscription.type === "tier3" ||
|
subscription.type === "tier3"
|
||||||
subscription.type === "enterprise"
|
|
||||||
) {
|
) {
|
||||||
tier = subscription.type;
|
tier = subscription.type;
|
||||||
active = true;
|
active = true;
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export async function signSshKey(
|
|||||||
if (!userOrg.pamUsername) {
|
if (!userOrg.pamUsername) {
|
||||||
if (req.user?.email) {
|
if (req.user?.email) {
|
||||||
// Extract username from email (first part before @)
|
// Extract username from email (first part before @)
|
||||||
usernameToUse = req.user?.email.split("@")[0];
|
usernameToUse = req.user?.email.split("@")[0].replace(/[^a-zA-Z0-9_-]/g, "");
|
||||||
if (!usernameToUse) {
|
if (!usernameToUse) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import response from "@server/lib/response";
|
|||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and, ne } from "drizzle-orm";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
|
|
||||||
@@ -93,7 +93,8 @@ export async function updateClient(
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(clients.niceId, niceId),
|
eq(clients.niceId, niceId),
|
||||||
eq(clients.orgId, clients.orgId)
|
eq(clients.orgId, clients.orgId),
|
||||||
|
ne(clients.clientId, clientId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|||||||
@@ -197,6 +197,7 @@ export async function updateSiteBandwidth(
|
|||||||
usageService
|
usageService
|
||||||
.checkLimitSet(
|
.checkLimitSet(
|
||||||
orgId,
|
orgId,
|
||||||
|
|
||||||
FeatureId.EGRESS_DATA_MB,
|
FeatureId.EGRESS_DATA_MB,
|
||||||
bandwidthUsage
|
bandwidthUsage
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Resource,
|
Resource,
|
||||||
resources
|
resources
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and, ne } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -33,7 +33,15 @@ const updateResourceParamsSchema = z.strictObject({
|
|||||||
const updateHttpResourceBodySchema = z
|
const updateHttpResourceBodySchema = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
name: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
niceId: z.string().min(1).max(255).regex(/^[a-zA-Z0-9-]+$/, "niceId can only contain letters, numbers, and dashes").optional(),
|
niceId: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.max(255)
|
||||||
|
.regex(
|
||||||
|
/^[a-zA-Z0-9-]+$/,
|
||||||
|
"niceId can only contain letters, numbers, and dashes"
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
subdomain: subdomainSchema.nullable().optional(),
|
subdomain: subdomainSchema.nullable().optional(),
|
||||||
ssl: z.boolean().optional(),
|
ssl: z.boolean().optional(),
|
||||||
sso: z.boolean().optional(),
|
sso: z.boolean().optional(),
|
||||||
@@ -248,14 +256,13 @@ async function updateHttpResource(
|
|||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(resources.niceId, updateData.niceId),
|
eq(resources.niceId, updateData.niceId),
|
||||||
eq(resources.orgId, resource.orgId)
|
eq(resources.orgId, resource.orgId),
|
||||||
|
ne(resources.resourceId, resource.resourceId) // exclude the current resource from the search
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
if (
|
if (existingResource) {
|
||||||
existingResource &&
|
|
||||||
existingResource.resourceId !== resource.resourceId
|
|
||||||
) {
|
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.CONFLICT,
|
HttpCode.CONFLICT,
|
||||||
@@ -343,7 +350,10 @@ async function updateHttpResource(
|
|||||||
headers = null;
|
headers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLicensed = await isLicensedOrSubscribed(resource.orgId, tierMatrix.maintencePage);
|
const isLicensed = await isLicensedOrSubscribed(
|
||||||
|
resource.orgId,
|
||||||
|
tierMatrix.maintencePage
|
||||||
|
);
|
||||||
if (!isLicensed) {
|
if (!isLicensed) {
|
||||||
updateData.maintenanceModeEnabled = undefined;
|
updateData.maintenanceModeEnabled = undefined;
|
||||||
updateData.maintenanceModeType = undefined;
|
updateData.maintenanceModeType = undefined;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { sites } from "@server/db";
|
import { sites } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and, ne } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -19,8 +19,8 @@ const updateSiteBodySchema = z
|
|||||||
.strictObject({
|
.strictObject({
|
||||||
name: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional(),
|
||||||
niceId: z.string().min(1).max(255).optional(),
|
niceId: z.string().min(1).max(255).optional(),
|
||||||
dockerSocketEnabled: z.boolean().optional(),
|
dockerSocketEnabled: z.boolean().optional()
|
||||||
remoteSubnets: z.string().optional()
|
// remoteSubnets: z.string().optional()
|
||||||
// subdomain: z
|
// subdomain: z
|
||||||
// .string()
|
// .string()
|
||||||
// .min(1)
|
// .min(1)
|
||||||
@@ -86,18 +86,19 @@ export async function updateSite(
|
|||||||
|
|
||||||
// if niceId is provided, check if it's already in use by another site
|
// if niceId is provided, check if it's already in use by another site
|
||||||
if (updateData.niceId) {
|
if (updateData.niceId) {
|
||||||
const existingSite = await db
|
const [existingSite] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
eq(sites.niceId, updateData.niceId),
|
eq(sites.niceId, updateData.niceId),
|
||||||
eq(sites.orgId, sites.orgId)
|
eq(sites.orgId, sites.orgId),
|
||||||
|
ne(sites.siteId, siteId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
if (existingSite.length > 0 && existingSite[0].siteId !== siteId) {
|
if (existingSite) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.CONFLICT,
|
HttpCode.CONFLICT,
|
||||||
@@ -107,22 +108,22 @@ export async function updateSite(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if remoteSubnets is provided, ensure it's a valid comma-separated list of cidrs
|
// // if remoteSubnets is provided, ensure it's a valid comma-separated list of cidrs
|
||||||
if (updateData.remoteSubnets) {
|
// if (updateData.remoteSubnets) {
|
||||||
const subnets = updateData.remoteSubnets
|
// const subnets = updateData.remoteSubnets
|
||||||
.split(",")
|
// .split(",")
|
||||||
.map((s) => s.trim());
|
// .map((s) => s.trim());
|
||||||
for (const subnet of subnets) {
|
// for (const subnet of subnets) {
|
||||||
if (!isValidCIDR(subnet)) {
|
// if (!isValidCIDR(subnet)) {
|
||||||
return next(
|
// return next(
|
||||||
createHttpError(
|
// createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
// HttpCode.BAD_REQUEST,
|
||||||
`Invalid CIDR format: ${subnet}`
|
// `Invalid CIDR format: ${subnet}`
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
const updatedSite = await db
|
const updatedSite = await db
|
||||||
.update(sites)
|
.update(sites)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export const isOrgSubscribed = cache(async (orgId: string) => {
|
|||||||
try {
|
try {
|
||||||
const subRes = await getCachedSubscription(orgId);
|
const subRes = await getCachedSubscription(orgId);
|
||||||
subscribed =
|
subscribed =
|
||||||
(subRes.data.data.tier == "tier1" || subRes.data.data.tier == "tier2" || subRes.data.data.tier == "tier3" || subRes.data.data.tier == "enterprise") &&
|
(subRes.data.data.tier == "tier1" || subRes.data.data.tier == "tier2" || subRes.data.data.tier == "tier3") &&
|
||||||
subRes.data.data.active;
|
subRes.data.data.active;
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ export function SubscriptionStatusProvider({
|
|||||||
if (
|
if (
|
||||||
subscription.type == "tier1" ||
|
subscription.type == "tier1" ||
|
||||||
subscription.type == "tier2" ||
|
subscription.type == "tier2" ||
|
||||||
subscription.type == "tier3" ||
|
subscription.type == "tier3"
|
||||||
subscription.type == "enterprise"
|
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
tier: subscription.type,
|
tier: subscription.type,
|
||||||
@@ -62,7 +61,7 @@ export function SubscriptionStatusProvider({
|
|||||||
const isSubscribed = () => {
|
const isSubscribed = () => {
|
||||||
const { tier, active } = getTier();
|
const { tier, active } = getTier();
|
||||||
return (
|
return (
|
||||||
(tier == "tier1" || tier == "tier2" || tier == "tier3" || tier == "enterprise") &&
|
(tier == "tier1" || tier == "tier2" || tier == "tier3") &&
|
||||||
active
|
active
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user