Patchpass V1
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { prisma } from "./db";
|
||||
|
||||
// ── Platform limits ────────────────────────────────────────────────────────────
|
||||
|
||||
/** Max change requests an agent may create per rolling hour. */
|
||||
export const MAX_REQUESTS_PER_HOUR = 15;
|
||||
|
||||
/** Max agents a single human account may own. */
|
||||
export const MAX_AGENTS_PER_USER = 5;
|
||||
|
||||
/** Pending-request cap bounds (human-configurable per agent). */
|
||||
export const MIN_PENDING_LIMIT = 1;
|
||||
export const MAX_PENDING_LIMIT = 10;
|
||||
export const DEFAULT_PENDING_LIMIT = 5;
|
||||
|
||||
/** Request expiry bounds, in seconds. */
|
||||
export const DEFAULT_EXPIRY_SECONDS = 30 * 60; // 30 minutes
|
||||
export const MIN_EXPIRY_SECONDS = 60; // 1 minute
|
||||
export const MAX_EXPIRY_SECONDS = 12 * 60 * 60; // 12 hours
|
||||
|
||||
/** Auto-delete retention bounds, in days. */
|
||||
export const MIN_AUTO_DELETE_DAYS = 7;
|
||||
export const DEFAULT_AUTO_DELETE_DAYS = 30;
|
||||
|
||||
// ── Pure clamps ────────────────────────────────────────────────────────────────
|
||||
|
||||
export function clampPendingLimit(value: number): number {
|
||||
if (!Number.isFinite(value)) return DEFAULT_PENDING_LIMIT;
|
||||
return Math.min(MAX_PENDING_LIMIT, Math.max(MIN_PENDING_LIMIT, Math.trunc(value)));
|
||||
}
|
||||
|
||||
export function clampExpirySeconds(value: number | undefined | null): number {
|
||||
if (value === undefined || value === null || !Number.isFinite(value)) {
|
||||
return DEFAULT_EXPIRY_SECONDS;
|
||||
}
|
||||
return Math.min(MAX_EXPIRY_SECONDS, Math.max(MIN_EXPIRY_SECONDS, Math.trunc(value)));
|
||||
}
|
||||
|
||||
export function clampAutoDeleteDays(value: number): number {
|
||||
if (!Number.isFinite(value)) return DEFAULT_AUTO_DELETE_DAYS;
|
||||
return Math.max(MIN_AUTO_DELETE_DAYS, Math.trunc(value));
|
||||
}
|
||||
|
||||
// ── Limit checks (DB-backed) ────────────────────────────────────────────────────
|
||||
|
||||
export type LimitResult = { allowed: true } | { allowed: false; reason: string };
|
||||
|
||||
/** An agent may create at most MAX_REQUESTS_PER_HOUR requests per rolling hour. */
|
||||
export async function checkAgentHourlyLimit(agentId: number): Promise<LimitResult> {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
||||
const count = await prisma.changeRequest.count({
|
||||
where: { agentId, createdAt: { gte: oneHourAgo } },
|
||||
});
|
||||
if (count >= MAX_REQUESTS_PER_HOUR) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Rate limit reached: an agent may create at most ${MAX_REQUESTS_PER_HOUR} requests per hour`,
|
||||
};
|
||||
}
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
/** An agent may have at most `maxPendingRequests` requests in PENDING state. */
|
||||
export async function checkAgentPendingLimit(
|
||||
agentId: number,
|
||||
maxPendingRequests: number,
|
||||
): Promise<LimitResult> {
|
||||
const count = await prisma.changeRequest.count({
|
||||
where: { agentId, state: "PENDING" },
|
||||
});
|
||||
if (count >= maxPendingRequests) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Pending limit reached: this agent may have at most ${maxPendingRequests} pending requests at a time`,
|
||||
};
|
||||
}
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
/** A human may own at most MAX_AGENTS_PER_USER agents. */
|
||||
export async function checkAgentCountLimit(userId: number): Promise<LimitResult> {
|
||||
const count = await prisma.agent.count({ where: { ownerId: userId } });
|
||||
if (count >= MAX_AGENTS_PER_USER) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `Agent limit reached: a human may own at most ${MAX_AGENTS_PER_USER} agents`,
|
||||
};
|
||||
}
|
||||
return { allowed: true };
|
||||
}
|
||||
Reference in New Issue
Block a user