mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-11 06:58:28 +02:00
Fix #3314
This commit is contained in:
@@ -29,26 +29,40 @@ const paramsSchema = z.strictObject({
|
|||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
});
|
});
|
||||||
|
|
||||||
const bodySchema = z.strictObject({
|
const bodySchema = z
|
||||||
name: z.string().nonempty(),
|
.strictObject({
|
||||||
siteId: z.number().int().positive(),
|
name: z.string().nonempty(),
|
||||||
hcEnabled: z.boolean().default(false),
|
siteId: z.number().int().positive(),
|
||||||
hcMode: z.string().default("http"),
|
hcEnabled: z.boolean().default(false),
|
||||||
hcHostname: z.string().optional(),
|
hcMode: z.string().default("http"),
|
||||||
hcPort: z.number().int().min(1).max(65535).optional(),
|
hcHostname: z.string().optional(),
|
||||||
hcPath: z.string().optional(),
|
hcPort: z.number().int().min(1).max(65535).optional(),
|
||||||
hcScheme: z.string().optional(),
|
hcPath: z.string().optional(),
|
||||||
hcMethod: z.string().default("GET"),
|
hcScheme: z.string().optional(),
|
||||||
hcInterval: z.number().int().positive().default(30),
|
hcMethod: z.string().default("GET"),
|
||||||
hcUnhealthyInterval: z.number().int().positive().default(30),
|
hcInterval: z.number().int().positive().default(30),
|
||||||
hcTimeout: z.number().int().positive().default(1),
|
hcUnhealthyInterval: z.number().int().positive().default(30),
|
||||||
hcHeaders: z.string().optional().nullable(),
|
hcTimeout: z.number().int().positive().default(1),
|
||||||
hcFollowRedirects: z.boolean().default(true),
|
hcHeaders: z.string().optional().nullable(),
|
||||||
hcStatus: z.number().int().optional().nullable(),
|
hcFollowRedirects: z.boolean().default(true),
|
||||||
hcTlsServerName: z.string().optional(),
|
hcStatus: z.number().int().optional().nullable(),
|
||||||
hcHealthyThreshold: z.number().int().positive().default(1),
|
hcTlsServerName: z.string().optional(),
|
||||||
hcUnhealthyThreshold: z.number().int().positive().default(1)
|
hcHealthyThreshold: z.number().int().positive().default(1),
|
||||||
});
|
hcUnhealthyThreshold: z.number().int().positive().default(1)
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
const hcHostnameMissing =
|
||||||
|
data.hcHostname === undefined ||
|
||||||
|
data.hcHostname.trim().length === 0;
|
||||||
|
|
||||||
|
if (data.hcEnabled === true && hcHostnameMissing) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
path: ["hcHostname"],
|
||||||
|
message: "hcHostname is required when hcEnabled is true"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export type CreateHealthCheckResponse = {
|
export type CreateHealthCheckResponse = {
|
||||||
targetHealthCheckId: number;
|
targetHealthCheckId: number;
|
||||||
@@ -57,7 +71,6 @@ const CreateHealthCheckResponseDataSchema = z.object({
|
|||||||
targetHealthCheckId: z.number()
|
targetHealthCheckId: z.number()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "put",
|
method: "put",
|
||||||
path: "/org/{orgId}/health-check",
|
path: "/org/{orgId}/health-check",
|
||||||
@@ -78,7 +91,9 @@ registry.registerPath({
|
|||||||
description: "Successful response",
|
description: "Successful response",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: createApiResponseSchema(CreateHealthCheckResponseDataSchema)
|
schema: createApiResponseSchema(
|
||||||
|
CreateHealthCheckResponseDataSchema
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,6 @@ const UpdateHealthCheckResponseDataSchema = z.object({
|
|||||||
hcUnhealthyThreshold: z.number().nullable()
|
hcUnhealthyThreshold: z.number().nullable()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "post",
|
method: "post",
|
||||||
path: "/org/{orgId}/health-check/{healthCheckId}",
|
path: "/org/{orgId}/health-check/{healthCheckId}",
|
||||||
@@ -126,7 +125,9 @@ registry.registerPath({
|
|||||||
description: "Successful response",
|
description: "Successful response",
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: createApiResponseSchema(UpdateHealthCheckResponseDataSchema)
|
schema: createApiResponseSchema(
|
||||||
|
UpdateHealthCheckResponseDataSchema
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,6 +216,32 @@ export async function updateHealthCheck(
|
|||||||
)
|
)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
|
if (!existingHealthCheck) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
"Standalone health check not found"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextHcEnabled = hcEnabled ?? existingHealthCheck.hcEnabled;
|
||||||
|
const nextHcHostname =
|
||||||
|
hcHostname !== undefined
|
||||||
|
? hcHostname
|
||||||
|
: existingHealthCheck.hcHostname;
|
||||||
|
const hcHostnameMissing =
|
||||||
|
!nextHcHostname || nextHcHostname.trim().length === 0;
|
||||||
|
|
||||||
|
if (nextHcEnabled && hcHostnameMissing) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"hcHostname is required when hcEnabled is true"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (name !== undefined) updateData.name = name;
|
if (name !== undefined) updateData.name = name;
|
||||||
if (siteId !== undefined) updateData.siteId = siteId;
|
if (siteId !== undefined) updateData.siteId = siteId;
|
||||||
if (hcEnabled !== undefined) updateData.hcEnabled = hcEnabled;
|
if (hcEnabled !== undefined) updateData.hcEnabled = hcEnabled;
|
||||||
|
|||||||
@@ -33,41 +33,59 @@ const createTargetParamsSchema = z.strictObject({
|
|||||||
resourceId: z.coerce.number().int().positive()
|
resourceId: z.coerce.number().int().positive()
|
||||||
});
|
});
|
||||||
|
|
||||||
const createTargetSchema = z.strictObject({
|
const createTargetSchema = z
|
||||||
siteId: z.int().positive(),
|
.strictObject({
|
||||||
ip: z.string().refine(isTargetValid),
|
siteId: z.int().positive(),
|
||||||
mode: z.enum(["http", "tcp", "udp", "ssh", "rdp", "vnc"]).optional(),
|
ip: z.string().refine(isTargetValid),
|
||||||
method: z.string().optional().nullable(),
|
mode: z.enum(["http", "tcp", "udp", "ssh", "rdp", "vnc"]).optional(),
|
||||||
port: z.int().min(1).max(65535),
|
method: z.string().optional().nullable(),
|
||||||
enabled: z.boolean().default(true),
|
port: z.int().min(1).max(65535),
|
||||||
hcEnabled: z.boolean().optional(),
|
enabled: z.boolean().default(true),
|
||||||
hcPath: z.string().min(1).optional().nullable(),
|
hcEnabled: z.boolean().optional(),
|
||||||
hcScheme: z.string().optional().nullable(),
|
hcPath: z.string().min(1).optional().nullable(),
|
||||||
hcMode: z.string().optional().nullable(),
|
hcScheme: z.string().optional().nullable(),
|
||||||
hcHostname: z.string().optional().nullable(),
|
hcMode: z.string().optional().nullable(),
|
||||||
hcPort: z.int().positive().optional().nullable(),
|
hcHostname: z.string().optional().nullable(),
|
||||||
hcInterval: z.int().positive().min(1).optional().nullable(),
|
hcPort: z.int().positive().optional().nullable(),
|
||||||
hcUnhealthyInterval: z.int().positive().min(1).optional().nullable(),
|
hcInterval: z.int().positive().min(1).optional().nullable(),
|
||||||
hcTimeout: z.int().positive().min(1).optional().nullable(),
|
hcUnhealthyInterval: z.int().positive().min(1).optional().nullable(),
|
||||||
hcHeaders: z
|
hcTimeout: z.int().positive().min(1).optional().nullable(),
|
||||||
.array(z.strictObject({ name: z.string(), value: z.string() }))
|
hcHeaders: z
|
||||||
.nullable()
|
.array(z.strictObject({ name: z.string(), value: z.string() }))
|
||||||
.optional(),
|
.nullable()
|
||||||
hcFollowRedirects: z.boolean().optional().nullable(),
|
.optional(),
|
||||||
hcMethod: z.string().min(1).optional().nullable(),
|
hcFollowRedirects: z.boolean().optional().nullable(),
|
||||||
hcStatus: z.int().optional().nullable(),
|
hcMethod: z.string().min(1).optional().nullable(),
|
||||||
hcTlsServerName: z.string().optional().nullable(),
|
hcStatus: z.int().optional().nullable(),
|
||||||
hcHealthyThreshold: z.int().positive().min(1).optional().nullable(),
|
hcTlsServerName: z.string().optional().nullable(),
|
||||||
hcUnhealthyThreshold: z.int().positive().min(1).optional().nullable(),
|
hcHealthyThreshold: z.int().positive().min(1).optional().nullable(),
|
||||||
path: z.string().optional().nullable(),
|
hcUnhealthyThreshold: z.int().positive().min(1).optional().nullable(),
|
||||||
pathMatchType: z.enum(["exact", "prefix", "regex"]).optional().nullable(),
|
path: z.string().optional().nullable(),
|
||||||
rewritePath: z.string().optional().nullable(),
|
pathMatchType: z
|
||||||
rewritePathType: z
|
.enum(["exact", "prefix", "regex"])
|
||||||
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
.optional()
|
||||||
.optional()
|
.nullable(),
|
||||||
.nullable(),
|
rewritePath: z.string().optional().nullable(),
|
||||||
priority: z.int().min(1).max(1000).optional().nullable()
|
rewritePathType: z
|
||||||
});
|
.enum(["exact", "prefix", "regex", "stripPrefix"])
|
||||||
|
.optional()
|
||||||
|
.nullable(),
|
||||||
|
priority: z.int().min(1).max(1000).optional().nullable()
|
||||||
|
})
|
||||||
|
.superRefine((data, ctx) => {
|
||||||
|
const hcHostnameMissing =
|
||||||
|
data.hcHostname === undefined ||
|
||||||
|
data.hcHostname === null ||
|
||||||
|
data.hcHostname.trim().length === 0;
|
||||||
|
|
||||||
|
if (data.hcEnabled === true && hcHostnameMissing) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
path: ["hcHostname"],
|
||||||
|
message: "hcHostname is required when hcEnabled is true"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export type CreateTargetResponse = Target & TargetHealthCheck;
|
export type CreateTargetResponse = Target & TargetHealthCheck;
|
||||||
|
|
||||||
|
|||||||
@@ -188,6 +188,38 @@ export async function updateTarget(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [existingHc] = await db
|
||||||
|
.select()
|
||||||
|
.from(targetHealthCheck)
|
||||||
|
.where(eq(targetHealthCheck.targetId, targetId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!existingHc) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Health check for target with ID ${targetId} not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextHcEnabled = parsedBody.data.hcEnabled ?? existingHc.hcEnabled;
|
||||||
|
const nextHcHostname =
|
||||||
|
parsedBody.data.hcHostname !== undefined
|
||||||
|
? parsedBody.data.hcHostname
|
||||||
|
: existingHc.hcHostname;
|
||||||
|
const hcHostnameMissing =
|
||||||
|
!nextHcHostname || nextHcHostname.trim().length === 0;
|
||||||
|
|
||||||
|
if (nextHcEnabled && hcHostnameMissing) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"hcHostname is required when hcEnabled is true"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const pathMatchTypeRemoved = parsedBody.data.pathMatchType === null;
|
const pathMatchTypeRemoved = parsedBody.data.pathMatchType === null;
|
||||||
const nextMode =
|
const nextMode =
|
||||||
parsedBody.data.mode === null ? undefined : parsedBody.data.mode;
|
parsedBody.data.mode === null ? undefined : parsedBody.data.mode;
|
||||||
@@ -218,21 +250,6 @@ export async function updateTarget(
|
|||||||
.where(eq(targets.targetId, targetId))
|
.where(eq(targets.targetId, targetId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
const [existingHc] = await trx
|
|
||||||
.select()
|
|
||||||
.from(targetHealthCheck)
|
|
||||||
.where(eq(targetHealthCheck.targetId, targetId))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!existingHc) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.NOT_FOUND,
|
|
||||||
`Health check for target with ID ${targetId} not found`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let hcHeaders = null;
|
let hcHeaders = null;
|
||||||
if (parsedBody.data.hcHeaders) {
|
if (parsedBody.data.hcHeaders) {
|
||||||
hcHeaders = JSON.stringify(parsedBody.data.hcHeaders);
|
hcHeaders = JSON.stringify(parsedBody.data.hcHeaders);
|
||||||
|
|||||||
Reference in New Issue
Block a user