mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-31 13:06:32 +00:00
Add inline creation
This commit is contained in:
@@ -142,6 +142,7 @@ export async function updateProxyResources(
|
|||||||
.values({
|
.values({
|
||||||
name: `${targetData.hostname}:${targetData.port}`,
|
name: `${targetData.hostname}:${targetData.port}`,
|
||||||
targetId: newTarget.targetId,
|
targetId: newTarget.targetId,
|
||||||
|
orgId: orgId,
|
||||||
hcEnabled: healthcheckData?.enabled || false,
|
hcEnabled: healthcheckData?.enabled || false,
|
||||||
hcPath: healthcheckData?.path,
|
hcPath: healthcheckData?.path,
|
||||||
hcScheme: healthcheckData?.scheme,
|
hcScheme: healthcheckData?.scheme,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import createHttpError from "http-errors";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import { OpenAPITags, registry } from "@server/openApi";
|
||||||
import { eq, inArray, sql } from "drizzle-orm";
|
import { and, eq, inArray, sql } from "drizzle-orm";
|
||||||
|
|
||||||
const paramsSchema = z.strictObject({
|
const paramsSchema = z.strictObject({
|
||||||
orgId: z.string().nonempty()
|
orgId: z.string().nonempty()
|
||||||
@@ -39,7 +39,17 @@ const querySchema = z.strictObject({
|
|||||||
.optional()
|
.optional()
|
||||||
.default("0")
|
.default("0")
|
||||||
.transform(Number)
|
.transform(Number)
|
||||||
.pipe(z.number().int().nonnegative())
|
.pipe(z.number().int().nonnegative()),
|
||||||
|
siteId: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((v) => (v !== undefined ? Number(v) : undefined))
|
||||||
|
.pipe(z.number().int().positive().optional()),
|
||||||
|
resourceId: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((v) => (v !== undefined ? Number(v) : undefined))
|
||||||
|
.pipe(z.number().int().positive().optional())
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ListAlertRulesResponse = {
|
export type ListAlertRulesResponse = {
|
||||||
@@ -102,12 +112,66 @@ export async function listAlertRules(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { limit, offset } = parsedQuery.data;
|
const { limit, offset, siteId, resourceId } = parsedQuery.data;
|
||||||
|
|
||||||
|
// Resolve siteId filter → matching alertRuleIds
|
||||||
|
let siteFilterRuleIds: number[] | null = null;
|
||||||
|
if (siteId !== undefined) {
|
||||||
|
const rows = await db
|
||||||
|
.select({ alertRuleId: alertSites.alertRuleId })
|
||||||
|
.from(alertSites)
|
||||||
|
.where(eq(alertSites.siteId, siteId));
|
||||||
|
siteFilterRuleIds = rows.map((r) => r.alertRuleId);
|
||||||
|
if (siteFilterRuleIds.length === 0) {
|
||||||
|
return response<ListAlertRulesResponse>(res, {
|
||||||
|
data: {
|
||||||
|
alertRules: [],
|
||||||
|
pagination: { total: 0, limit, offset }
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Alert rules retrieved successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve resourceId filter → matching alertRuleIds
|
||||||
|
let resourceFilterRuleIds: number[] | null = null;
|
||||||
|
if (resourceId !== undefined) {
|
||||||
|
const rows = await db
|
||||||
|
.select({ alertRuleId: alertResources.alertRuleId })
|
||||||
|
.from(alertResources)
|
||||||
|
.where(eq(alertResources.resourceId, resourceId));
|
||||||
|
resourceFilterRuleIds = rows.map((r) => r.alertRuleId);
|
||||||
|
if (resourceFilterRuleIds.length === 0) {
|
||||||
|
return response<ListAlertRulesResponse>(res, {
|
||||||
|
data: {
|
||||||
|
alertRules: [],
|
||||||
|
pagination: { total: 0, limit, offset }
|
||||||
|
},
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Alert rules retrieved successfully",
|
||||||
|
status: HttpCode.OK
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const whereClause = and(
|
||||||
|
eq(alertRules.orgId, orgId),
|
||||||
|
siteFilterRuleIds !== null
|
||||||
|
? inArray(alertRules.alertRuleId, siteFilterRuleIds)
|
||||||
|
: undefined,
|
||||||
|
resourceFilterRuleIds !== null
|
||||||
|
? inArray(alertRules.alertRuleId, resourceFilterRuleIds)
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
|
||||||
const list = await db
|
const list = await db
|
||||||
.select()
|
.select()
|
||||||
.from(alertRules)
|
.from(alertRules)
|
||||||
.where(eq(alertRules.orgId, orgId))
|
.where(whereClause)
|
||||||
.orderBy(sql`${alertRules.createdAt} DESC`)
|
.orderBy(sql`${alertRules.createdAt} DESC`)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.offset(offset);
|
.offset(offset);
|
||||||
@@ -115,7 +179,7 @@ export async function listAlertRules(
|
|||||||
const [{ count }] = await db
|
const [{ count }] = await db
|
||||||
.select({ count: sql<number>`count(*)` })
|
.select({ count: sql<number>`count(*)` })
|
||||||
.from(alertRules)
|
.from(alertRules)
|
||||||
.where(eq(alertRules.orgId, orgId));
|
.where(whereClause);
|
||||||
|
|
||||||
// Batch-fetch site and health-check associations for all returned rules
|
// Batch-fetch site and health-check associations for all returned rules
|
||||||
// in two queries rather than N+1 individual lookups.
|
// in two queries rather than N+1 individual lookups.
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ import { GetResourceResponse } from "@server/routers/resource/getResource";
|
|||||||
import type { ResourceContextType } from "@app/contexts/resourceContext";
|
import type { ResourceContextType } from "@app/contexts/resourceContext";
|
||||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
import UptimeBar from "@app/components/UptimeBar";
|
import UptimeAlertSection from "@app/components/UptimeAlertSection";
|
||||||
|
|
||||||
type MaintenanceSectionFormProps = {
|
type MaintenanceSectionFormProps = {
|
||||||
resource: GetResourceResponse;
|
resource: GetResourceResponse;
|
||||||
@@ -579,19 +579,12 @@ export default function GeneralForm() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsContainer>
|
<SettingsContainer>
|
||||||
<SettingsSection>
|
{resource?.resourceId && resource?.orgId && (
|
||||||
<SettingsSectionHeader>
|
<UptimeAlertSection
|
||||||
<SettingsSectionTitle>Uptime</SettingsSectionTitle>
|
orgId={resource.orgId}
|
||||||
<SettingsSectionDescription>
|
resourceId={resource.resourceId}
|
||||||
Site availability over the last 90 days.
|
/>
|
||||||
</SettingsSectionDescription>
|
|
||||||
</SettingsSectionHeader>
|
|
||||||
<SettingsSectionBody>
|
|
||||||
{resource?.resourceId && (
|
|
||||||
<UptimeBar resourceId={resource.resourceId} days={90} />
|
|
||||||
)}
|
)}
|
||||||
</SettingsSectionBody>
|
|
||||||
</SettingsSection>
|
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<SettingsSectionHeader>
|
<SettingsSectionHeader>
|
||||||
<SettingsSectionTitle>
|
<SettingsSectionTitle>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import UptimeBar from "@app/components/UptimeBar";
|
import UptimeAlertSection from "@app/components/UptimeAlertSection";
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -113,19 +113,12 @@ export default function GeneralPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsContainer>
|
<SettingsContainer>
|
||||||
<SettingsSection>
|
{site?.siteId && site?.orgId && (
|
||||||
<SettingsSectionHeader>
|
<UptimeAlertSection
|
||||||
<SettingsSectionTitle>Uptime</SettingsSectionTitle>
|
orgId={site.orgId}
|
||||||
<SettingsSectionDescription>
|
siteId={site.siteId}
|
||||||
Site availability over the last 90 days.
|
/>
|
||||||
</SettingsSectionDescription>
|
|
||||||
</SettingsSectionHeader>
|
|
||||||
<SettingsSectionBody>
|
|
||||||
{site?.siteId && (
|
|
||||||
<UptimeBar siteId={site.siteId} days={90} />
|
|
||||||
)}
|
)}
|
||||||
</SettingsSectionBody>
|
|
||||||
</SettingsSection>
|
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<SettingsSectionHeader>
|
<SettingsSectionHeader>
|
||||||
<SettingsSectionTitle>
|
<SettingsSectionTitle>
|
||||||
|
|||||||
300
src/components/UptimeAlertSection.tsx
Normal file
300
src/components/UptimeAlertSection.tsx
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useMemo } from "react";
|
||||||
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { BellPlus, BellRing } from "lucide-react";
|
||||||
|
import {
|
||||||
|
SettingsSection,
|
||||||
|
SettingsSectionHeader,
|
||||||
|
SettingsSectionTitle,
|
||||||
|
SettingsSectionDescription,
|
||||||
|
SettingsSectionBody
|
||||||
|
} from "@app/components/Settings";
|
||||||
|
import UptimeBar from "@app/components/UptimeBar";
|
||||||
|
import { Button } from "@app/components/ui/button";
|
||||||
|
import {
|
||||||
|
Credenza,
|
||||||
|
CredenzaBody,
|
||||||
|
CredenzaClose,
|
||||||
|
CredenzaContent,
|
||||||
|
CredenzaDescription,
|
||||||
|
CredenzaFooter,
|
||||||
|
CredenzaHeader,
|
||||||
|
CredenzaTitle
|
||||||
|
} from "@app/components/Credenza";
|
||||||
|
import { Input } from "@app/components/ui/input";
|
||||||
|
import { Label } from "@app/components/ui/label";
|
||||||
|
import { TagInput, type Tag } from "@app/components/tags/tag-input";
|
||||||
|
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
||||||
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
|
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||||
|
import { toast } from "@app/hooks/useToast";
|
||||||
|
import { orgQueries } from "@app/lib/queries";
|
||||||
|
|
||||||
|
interface UptimeAlertSectionProps {
|
||||||
|
orgId: string;
|
||||||
|
siteId?: number;
|
||||||
|
resourceId?: number;
|
||||||
|
days?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UptimeAlertSection({
|
||||||
|
orgId,
|
||||||
|
siteId,
|
||||||
|
resourceId,
|
||||||
|
days = 90
|
||||||
|
}: UptimeAlertSectionProps) {
|
||||||
|
const api = createApiClient(useEnvContext());
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [name, setName] = useState("Uptime Alert");
|
||||||
|
const [userTags, setUserTags] = useState<Tag[]>([]);
|
||||||
|
const [roleTags, setRoleTags] = useState<Tag[]>([]);
|
||||||
|
const [emailTags, setEmailTags] = useState<Tag[]>([]);
|
||||||
|
const [activeUserTagIndex, setActiveUserTagIndex] = useState<number | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [activeRoleTagIndex, setActiveRoleTagIndex] = useState<number | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [activeEmailTagIndex, setActiveEmailTagIndex] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const { data: alertRules, isLoading: alertRulesLoading } = useQuery(
|
||||||
|
orgQueries.alertRulesForSource({ orgId, siteId, resourceId })
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: orgUsers = [] } = useQuery(orgQueries.users({ orgId }));
|
||||||
|
const { data: orgRoles = [] } = useQuery(orgQueries.roles({ orgId }));
|
||||||
|
|
||||||
|
const allUsers = useMemo(
|
||||||
|
() =>
|
||||||
|
orgUsers.map((u) => ({
|
||||||
|
id: String(u.id),
|
||||||
|
text: getUserDisplayName({
|
||||||
|
email: u.email,
|
||||||
|
name: u.name,
|
||||||
|
username: u.username
|
||||||
|
})
|
||||||
|
})),
|
||||||
|
[orgUsers]
|
||||||
|
);
|
||||||
|
|
||||||
|
const allRoles = useMemo(
|
||||||
|
() =>
|
||||||
|
orgRoles
|
||||||
|
.map((r) => ({ id: String(r.roleId), text: r.name }))
|
||||||
|
.filter((r) => r.text !== "Admin"),
|
||||||
|
[orgRoles]
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasRules = (alertRules?.length ?? 0) > 0;
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
if (
|
||||||
|
userTags.length === 0 &&
|
||||||
|
roleTags.length === 0 &&
|
||||||
|
emailTags.length === 0
|
||||||
|
) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "No recipients",
|
||||||
|
description:
|
||||||
|
"Please add at least one user, role, or email to notify."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
await api.put(`/org/${orgId}/alert-rule`, {
|
||||||
|
name,
|
||||||
|
eventType: siteId ? "site_toggle" : "resource_toggle",
|
||||||
|
enabled: true,
|
||||||
|
cooldownSeconds: 300,
|
||||||
|
siteIds: siteId ? [siteId] : [],
|
||||||
|
healthCheckIds: [],
|
||||||
|
resourceIds: resourceId ? [resourceId] : [],
|
||||||
|
userIds: userTags.map((tag) => tag.id),
|
||||||
|
roleIds: roleTags.map((tag) => Number(tag.id)),
|
||||||
|
emails: emailTags.map((tag) => tag.text),
|
||||||
|
webhookActions: []
|
||||||
|
});
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Alert created",
|
||||||
|
description:
|
||||||
|
"You will be notified when this changes status."
|
||||||
|
});
|
||||||
|
|
||||||
|
setOpen(false);
|
||||||
|
setName("Uptime Alert");
|
||||||
|
setUserTags([]);
|
||||||
|
setRoleTags([]);
|
||||||
|
setEmailTags([]);
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: orgQueries.alertRulesForSource({
|
||||||
|
orgId,
|
||||||
|
siteId,
|
||||||
|
resourceId
|
||||||
|
}).queryKey
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Failed to create alert",
|
||||||
|
description: formatAxiosError(e, "An error occurred.")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const alertButton = alertRulesLoading ? null : hasRules ? (
|
||||||
|
<Button variant="outline" size="sm" asChild>
|
||||||
|
<Link href={`/${orgId}/settings/alerting`}>
|
||||||
|
<BellRing className="size-4 mr-2" />
|
||||||
|
View Alerts
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button variant="outline" size="sm" onClick={() => setOpen(true)}>
|
||||||
|
<BellPlus className="size-4 mr-2" />
|
||||||
|
Add Alert
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsSection>
|
||||||
|
<SettingsSectionHeader>
|
||||||
|
<div className="flex justify-between items-start">
|
||||||
|
<div>
|
||||||
|
<SettingsSectionTitle>Uptime</SettingsSectionTitle>
|
||||||
|
<SettingsSectionDescription>
|
||||||
|
Site availability over the last {days} days.
|
||||||
|
</SettingsSectionDescription>
|
||||||
|
</div>
|
||||||
|
{alertButton}
|
||||||
|
</div>
|
||||||
|
</SettingsSectionHeader>
|
||||||
|
<SettingsSectionBody>
|
||||||
|
<UptimeBar
|
||||||
|
siteId={siteId}
|
||||||
|
resourceId={resourceId}
|
||||||
|
days={days}
|
||||||
|
/>
|
||||||
|
</SettingsSectionBody>
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
|
<Credenza open={open} onOpenChange={setOpen}>
|
||||||
|
<CredenzaContent>
|
||||||
|
<CredenzaHeader>
|
||||||
|
<CredenzaTitle>Create Email Alert</CredenzaTitle>
|
||||||
|
<CredenzaDescription>
|
||||||
|
Get notified by email when this{" "}
|
||||||
|
{siteId ? "site" : "resource"} goes offline or
|
||||||
|
comes back online.
|
||||||
|
</CredenzaDescription>
|
||||||
|
</CredenzaHeader>
|
||||||
|
<CredenzaBody>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="alert-name">Name</Label>
|
||||||
|
<Input
|
||||||
|
id="alert-name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
placeholder="Alert name"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>Notify Users</Label>
|
||||||
|
<TagInput
|
||||||
|
activeTagIndex={activeUserTagIndex}
|
||||||
|
setActiveTagIndex={setActiveUserTagIndex}
|
||||||
|
placeholder="Select users..."
|
||||||
|
size="sm"
|
||||||
|
tags={userTags}
|
||||||
|
setTags={(newTags) => {
|
||||||
|
const next =
|
||||||
|
typeof newTags === "function"
|
||||||
|
? newTags(userTags)
|
||||||
|
: newTags;
|
||||||
|
setUserTags(next as Tag[]);
|
||||||
|
}}
|
||||||
|
enableAutocomplete
|
||||||
|
autocompleteOptions={allUsers}
|
||||||
|
restrictTagsToAutocompleteOptions
|
||||||
|
allowDuplicates={false}
|
||||||
|
sortTags
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>Notify Roles</Label>
|
||||||
|
<TagInput
|
||||||
|
activeTagIndex={activeRoleTagIndex}
|
||||||
|
setActiveTagIndex={setActiveRoleTagIndex}
|
||||||
|
placeholder="Select roles..."
|
||||||
|
size="sm"
|
||||||
|
tags={roleTags}
|
||||||
|
setTags={(newTags) => {
|
||||||
|
const next =
|
||||||
|
typeof newTags === "function"
|
||||||
|
? newTags(roleTags)
|
||||||
|
: newTags;
|
||||||
|
setRoleTags(next as Tag[]);
|
||||||
|
}}
|
||||||
|
enableAutocomplete
|
||||||
|
autocompleteOptions={allRoles}
|
||||||
|
restrictTagsToAutocompleteOptions
|
||||||
|
allowDuplicates={false}
|
||||||
|
sortTags
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>Additional Emails</Label>
|
||||||
|
<TagInput
|
||||||
|
activeTagIndex={activeEmailTagIndex}
|
||||||
|
setActiveTagIndex={setActiveEmailTagIndex}
|
||||||
|
placeholder="Enter email addresses..."
|
||||||
|
size="sm"
|
||||||
|
tags={emailTags}
|
||||||
|
setTags={(newTags) => {
|
||||||
|
const next =
|
||||||
|
typeof newTags === "function"
|
||||||
|
? newTags(emailTags)
|
||||||
|
: newTags;
|
||||||
|
setEmailTags(next as Tag[]);
|
||||||
|
}}
|
||||||
|
allowDuplicates={false}
|
||||||
|
sortTags
|
||||||
|
validateTag={(tag) =>
|
||||||
|
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(tag)
|
||||||
|
}
|
||||||
|
delimiterList={[",", "Enter"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CredenzaBody>
|
||||||
|
<CredenzaFooter>
|
||||||
|
<CredenzaClose asChild>
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
</CredenzaClose>
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={loading}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
Create Alert
|
||||||
|
</Button>
|
||||||
|
</CredenzaFooter>
|
||||||
|
</CredenzaContent>
|
||||||
|
</Credenza>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -267,6 +267,28 @@ export const orgQueries = {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
alertRulesForSource: ({
|
||||||
|
orgId,
|
||||||
|
siteId,
|
||||||
|
resourceId
|
||||||
|
}: {
|
||||||
|
orgId: string;
|
||||||
|
siteId?: number;
|
||||||
|
resourceId?: number;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["ORG", orgId, "ALERT_RULES", { siteId, resourceId }] as const,
|
||||||
|
queryFn: async ({ signal, meta }) => {
|
||||||
|
const sp = new URLSearchParams();
|
||||||
|
if (siteId != null) sp.set("siteId", String(siteId));
|
||||||
|
if (resourceId != null) sp.set("resourceId", String(resourceId));
|
||||||
|
const res = await meta!.api.get<
|
||||||
|
AxiosResponse<ListAlertRulesResponse>
|
||||||
|
>(`/org/${orgId}/alert-rules?${sp.toString()}`, { signal });
|
||||||
|
return res.data.data.alertRules;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
standaloneHealthChecks: ({ orgId }: { orgId: string }) =>
|
standaloneHealthChecks: ({ orgId }: { orgId: string }) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ["ORG", orgId, "STANDALONE_HEALTH_CHECKS"] as const,
|
queryKey: ["ORG", orgId, "STANDALONE_HEALTH_CHECKS"] as const,
|
||||||
|
|||||||
Reference in New Issue
Block a user