feat(api, web): enhance health check and domain management features
Build, Test, and Push CFDM Docker Image / test (push) Successful in 3m3s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 1m48s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
Build, Test, and Push CFDM Docker Image / test (push) Successful in 3m3s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 1m48s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
- Integrated domain monitoring routes and bulk update functionality for domains in the API. - Improved health check service to include domain monitoring and logging of health status changes. - Updated web components to reflect health status with new HealthCheckBadge and enhanced domain filtering options. - Refactored domain service to support bulk updates and improved domain management capabilities. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
dd167a4fec
commit
64585ccd47
38 files changed
+4199
-677
No files matched your search
@@ -0,0 +1,25 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import { certificateSchema } from '@/lib/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const certKeys = {
|
||||
all: ['certificates'] as const,
|
||||
summary: ['certificates', 'summary'] as const,
|
||||
}
|
||||
|
||||
export const certificatesQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: certKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/certificates')
|
||||
return z.array(certificateSchema).parse(data)
|
||||
},
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
|
||||
export const certSummaryQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: certKeys.summary,
|
||||
queryFn: () => api.get<[string, number][]>('/api/v1/certificates/summary'),
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import { dnsRecordSchema } from '@/lib/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const dnsKeys = {
|
||||
all: ['dns'] as const,
|
||||
list: (domainId: number) => [...dnsKeys.all, domainId] as const,
|
||||
}
|
||||
|
||||
export const dnsListQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: dnsKeys.list(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/dns`)
|
||||
return z.array(dnsRecordSchema).parse(data)
|
||||
},
|
||||
staleTime: 1000 * 30,
|
||||
})
|
||||
@@ -0,0 +1,111 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import {
|
||||
domainMonitorResultSchema,
|
||||
domainMonitorSchema,
|
||||
ipHealthStatusSchema,
|
||||
notificationLogSchema,
|
||||
createDomainMonitorSchema,
|
||||
} from '@/lib/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
type CreateDomainMonitorBody = z.input<typeof createDomainMonitorSchema>
|
||||
|
||||
export const healthStatusKeys = {
|
||||
all: ['health-status'] as const,
|
||||
list: (scope: 'binding' | 'group', refId: number) =>
|
||||
[...healthStatusKeys.all, scope, refId] as const,
|
||||
}
|
||||
|
||||
export function healthStatusQueryOptions(
|
||||
scope: 'binding' | 'group',
|
||||
refId: number,
|
||||
) {
|
||||
return queryOptions({
|
||||
queryKey: healthStatusKeys.list(scope, refId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(
|
||||
`/api/v1/health-status?scope=${scope}&ref_id=${refId}`,
|
||||
)
|
||||
return z.array(ipHealthStatusSchema).parse(data)
|
||||
},
|
||||
refetchInterval: 10_000,
|
||||
staleTime: 5_000,
|
||||
})
|
||||
}
|
||||
|
||||
export async function runHealthCheck() {
|
||||
return api.post<{ checked: number }>('/api/v1/health-check/run', {})
|
||||
}
|
||||
|
||||
export const domainMonitorKeys = {
|
||||
all: ['domain-monitors'] as const,
|
||||
list: (domainId: number) => [...domainMonitorKeys.all, 'list', domainId] as const,
|
||||
results: (domainId: number, limit?: number) =>
|
||||
[...domainMonitorKeys.all, 'results', domainId, limit] as const,
|
||||
}
|
||||
|
||||
export const domainMonitorsQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: domainMonitorKeys.list(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/monitors`)
|
||||
return z.array(domainMonitorSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
/** Domain-scoped results include joined hostname/type from monitors. */
|
||||
export const domainMonitorResultEventSchema = domainMonitorResultSchema.extend({
|
||||
hostname: z.string(),
|
||||
type: z.string(),
|
||||
})
|
||||
|
||||
export type DomainMonitorResultEvent = z.infer<
|
||||
typeof domainMonitorResultEventSchema
|
||||
>
|
||||
|
||||
export const domainMonitorResultsQueryOptions = (domainId: number, limit = 50) =>
|
||||
queryOptions({
|
||||
queryKey: domainMonitorKeys.results(domainId, limit),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(
|
||||
`/api/v1/domains/${domainId}/monitor-results?limit=${limit}`,
|
||||
)
|
||||
return z.array(domainMonitorResultEventSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export async function createDomainMonitor(
|
||||
domainId: number,
|
||||
body: CreateDomainMonitorBody,
|
||||
) {
|
||||
const data = await api.post<unknown>(
|
||||
`/api/v1/domains/${domainId}/monitors`,
|
||||
createDomainMonitorSchema.parse(body),
|
||||
)
|
||||
return domainMonitorSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function deleteDomainMonitor(domainId: number, monitorId: number) {
|
||||
return api.delete<{ deleted: boolean }>(
|
||||
`/api/v1/domains/${domainId}/monitors/${monitorId}`,
|
||||
)
|
||||
}
|
||||
|
||||
export async function runDomainMonitors(domainId: number) {
|
||||
return api.post<{ checked: number }>(`/api/v1/domains/${domainId}/monitors/run`, {})
|
||||
}
|
||||
|
||||
export const notificationLogKeys = {
|
||||
all: ['notification-log'] as const,
|
||||
list: (limit?: number) => [...notificationLogKeys.all, 'list', limit] as const,
|
||||
}
|
||||
|
||||
export const notificationLogQueryOptions = (limit = 50) =>
|
||||
queryOptions({
|
||||
queryKey: notificationLogKeys.list(limit),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/notifications/log?limit=${limit}`)
|
||||
return z.array(notificationLogSchema).parse(data)
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,86 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import {
|
||||
domainListItemSchema,
|
||||
domainSchema,
|
||||
subdomainSchema,
|
||||
type BulkUpdateDomainsInput,
|
||||
type CreateSubdomainInput,
|
||||
type UpdateDomainInput,
|
||||
type UpdateSubdomainInput,
|
||||
} from '@/lib/schemas'
|
||||
import { dnsKeys } from '@/queries/dns'
|
||||
import { serviceBindingKeys } from '@/queries/services'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const domainKeys = {
|
||||
all: ['domains'] as const,
|
||||
list: (groupId?: number) => [...domainKeys.all, 'list', groupId] as const,
|
||||
detail: (id: number) => [...domainKeys.all, 'detail', id] as const,
|
||||
}
|
||||
|
||||
export const domainsListQueryOptions = (groupId?: number) =>
|
||||
queryOptions({
|
||||
queryKey: domainKeys.list(groupId),
|
||||
queryFn: async () => {
|
||||
const qs = groupId ? `?group_id=${groupId}` : ''
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains${qs}`)
|
||||
return z.array(domainListItemSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const domainDetailQueryOptions = (id: number) =>
|
||||
queryOptions({
|
||||
queryKey: domainKeys.detail(id),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown>(`/api/v1/domains/${id}`)
|
||||
return domainSchema.parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const subdomainKeys = {
|
||||
all: ['subdomains'] as const,
|
||||
list: (domainId: number) => [...subdomainKeys.all, domainId] as const,
|
||||
}
|
||||
|
||||
export const subdomainsListQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: subdomainKeys.list(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/subdomains`)
|
||||
return z.array(subdomainSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export async function createSubdomain(domainId: number, body: CreateSubdomainInput) {
|
||||
const data = await api.post<unknown>(`/api/v1/domains/${domainId}/subdomains`, body)
|
||||
return subdomainSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function updateSubdomain(id: number, body: UpdateSubdomainInput) {
|
||||
const data = await api.patch<unknown>(`/api/v1/subdomains/${id}`, body)
|
||||
return subdomainSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function updateDomain(id: number, body: UpdateDomainInput) {
|
||||
const data = await api.patch<unknown>(`/api/v1/domains/${id}`, body)
|
||||
return domainSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function bulkUpdateDomains(body: BulkUpdateDomainsInput) {
|
||||
return api.post<{ updated: number }>('/api/v1/domains/bulk', body)
|
||||
}
|
||||
|
||||
export async function deleteSubdomain(id: number) {
|
||||
return api.delete<{ deleted: boolean }>(`/api/v1/subdomains/${id}`)
|
||||
}
|
||||
|
||||
export function invalidateDomainPage(
|
||||
queryClient: import('@tanstack/react-query').QueryClient,
|
||||
domainId: number,
|
||||
) {
|
||||
void queryClient.invalidateQueries({ queryKey: subdomainKeys.list(domainId) })
|
||||
void queryClient.invalidateQueries({ queryKey: serviceBindingKeys.byDomain(domainId) })
|
||||
void queryClient.invalidateQueries({ queryKey: domainKeys.detail(domainId) })
|
||||
void queryClient.invalidateQueries({ queryKey: dnsKeys.list(domainId) })
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import { groupSchema, groupWithStatsSchema } from '@/lib/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const groupKeys = {
|
||||
all: ['groups'] as const,
|
||||
detail: (id: number) => [...groupKeys.all, 'detail', id] as const,
|
||||
}
|
||||
|
||||
export const groupsQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: groupKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/groups')
|
||||
return z.array(groupSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const groupDetailQueryOptions = (id: number) =>
|
||||
queryOptions({
|
||||
queryKey: groupKeys.detail(id),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown>(`/api/v1/groups/${id}`)
|
||||
return groupWithStatsSchema.parse(data)
|
||||
},
|
||||
})
|
||||
@@ -1,238 +1,6 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import {
|
||||
certificateSchema,
|
||||
dnsRecordSchema,
|
||||
domainListItemSchema,
|
||||
domainSchema,
|
||||
groupSchema,
|
||||
groupWithStatsSchema,
|
||||
ipHealthStatusSchema,
|
||||
serviceBindingSchema,
|
||||
serviceGroupsResponseSchema,
|
||||
serviceViewSchema,
|
||||
subdomainSchema,
|
||||
type CreateSubdomainInput,
|
||||
type UpdateDomainInput,
|
||||
type UpdateSubdomainInput,
|
||||
} from '@/lib/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const groupKeys = {
|
||||
all: ['groups'] as const,
|
||||
detail: (id: number) => [...groupKeys.all, 'detail', id] as const,
|
||||
}
|
||||
|
||||
export const groupsQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: groupKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/groups')
|
||||
return z.array(groupSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const groupDetailQueryOptions = (id: number) =>
|
||||
queryOptions({
|
||||
queryKey: groupKeys.detail(id),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown>(`/api/v1/groups/${id}`)
|
||||
return groupWithStatsSchema.parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const serviceKeys = {
|
||||
all: ['services'] as const,
|
||||
}
|
||||
|
||||
export const serviceGroupKeys = {
|
||||
all: ['service-groups'] as const,
|
||||
}
|
||||
|
||||
export const serviceGroupsQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: serviceGroupKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown>('/api/v1/service-groups')
|
||||
return serviceGroupsResponseSchema.parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const servicesQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: serviceKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/services')
|
||||
return z.array(serviceViewSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const serviceBindingKeys = {
|
||||
all: ['service-bindings'] as const,
|
||||
byDomain: (domainId: number) => [...serviceBindingKeys.all, 'domain', domainId] as const,
|
||||
}
|
||||
|
||||
export const serviceBindingsQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: serviceBindingKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/service-bindings')
|
||||
return z.array(serviceBindingSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const domainServiceBindingsQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: serviceBindingKeys.byDomain(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/service-bindings`)
|
||||
return z.array(serviceBindingSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const domainKeys = {
|
||||
all: ['domains'] as const,
|
||||
list: (groupId?: number) => [...domainKeys.all, 'list', groupId] as const,
|
||||
detail: (id: number) => [...domainKeys.all, 'detail', id] as const,
|
||||
}
|
||||
|
||||
export const domainsListQueryOptions = (groupId?: number) =>
|
||||
queryOptions({
|
||||
queryKey: domainKeys.list(groupId),
|
||||
queryFn: async () => {
|
||||
const qs = groupId ? `?group_id=${groupId}` : ''
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains${qs}`)
|
||||
return z.array(domainListItemSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const domainDetailQueryOptions = (id: number) =>
|
||||
queryOptions({
|
||||
queryKey: domainKeys.detail(id),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown>(`/api/v1/domains/${id}`)
|
||||
return domainSchema.parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const dnsKeys = {
|
||||
all: ['dns'] as const,
|
||||
list: (domainId: number) => [...dnsKeys.all, domainId] as const,
|
||||
}
|
||||
|
||||
export const dnsListQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: dnsKeys.list(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/dns`)
|
||||
return z.array(dnsRecordSchema).parse(data)
|
||||
},
|
||||
staleTime: 1000 * 30,
|
||||
})
|
||||
|
||||
export const certKeys = {
|
||||
all: ['certificates'] as const,
|
||||
summary: ['certificates', 'summary'] as const,
|
||||
}
|
||||
|
||||
export const certificatesQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: certKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/certificates')
|
||||
return z.array(certificateSchema).parse(data)
|
||||
},
|
||||
staleTime: 1000 * 60 * 5,
|
||||
})
|
||||
|
||||
export const certSummaryQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: certKeys.summary,
|
||||
queryFn: () => api.get<[string, number][]>('/api/v1/certificates/summary'),
|
||||
})
|
||||
|
||||
export const subdomainKeys = {
|
||||
all: ['subdomains'] as const,
|
||||
list: (domainId: number) => [...subdomainKeys.all, domainId] as const,
|
||||
}
|
||||
|
||||
export const subdomainsListQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: subdomainKeys.list(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/subdomains`)
|
||||
return z.array(subdomainSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export interface CreateServiceBindingBody {
|
||||
domain_id: number
|
||||
service_id: number
|
||||
hostname?: string
|
||||
target_ip?: string
|
||||
}
|
||||
|
||||
export async function createSubdomain(domainId: number, body: CreateSubdomainInput) {
|
||||
const data = await api.post<unknown>(`/api/v1/domains/${domainId}/subdomains`, body)
|
||||
return subdomainSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function updateSubdomain(id: number, body: UpdateSubdomainInput) {
|
||||
const data = await api.patch<unknown>(`/api/v1/subdomains/${id}`, body)
|
||||
return subdomainSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function updateDomain(id: number, body: UpdateDomainInput) {
|
||||
const data = await api.patch<unknown>(`/api/v1/domains/${id}`, body)
|
||||
return domainSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function deleteSubdomain(id: number) {
|
||||
return api.delete<{ deleted: boolean }>(`/api/v1/subdomains/${id}`)
|
||||
}
|
||||
|
||||
export async function createServiceBinding(body: CreateServiceBindingBody) {
|
||||
const data = await api.post<unknown>('/api/v1/service-bindings', body)
|
||||
return serviceBindingSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function deleteServiceBinding(id: number) {
|
||||
return api.delete<{ deleted: boolean }>(`/api/v1/service-bindings/${id}`)
|
||||
}
|
||||
|
||||
export function invalidateDomainPage(
|
||||
queryClient: import('@tanstack/react-query').QueryClient,
|
||||
domainId: number,
|
||||
) {
|
||||
void queryClient.invalidateQueries({ queryKey: subdomainKeys.list(domainId) })
|
||||
void queryClient.invalidateQueries({ queryKey: serviceBindingKeys.byDomain(domainId) })
|
||||
void queryClient.invalidateQueries({ queryKey: domainKeys.detail(domainId) })
|
||||
void queryClient.invalidateQueries({ queryKey: dnsKeys.list(domainId) })
|
||||
}
|
||||
|
||||
export const healthStatusKeys = {
|
||||
all: ['health-status'] as const,
|
||||
list: (scope: 'binding' | 'group', refId: number) =>
|
||||
[...healthStatusKeys.all, scope, refId] as const,
|
||||
}
|
||||
|
||||
export function healthStatusQueryOptions(
|
||||
scope: 'binding' | 'group',
|
||||
refId: number,
|
||||
) {
|
||||
return queryOptions({
|
||||
queryKey: healthStatusKeys.list(scope, refId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(
|
||||
`/api/v1/health-status?scope=${scope}&ref_id=${refId}`,
|
||||
)
|
||||
return z.array(ipHealthStatusSchema).parse(data)
|
||||
},
|
||||
refetchInterval: 10_000,
|
||||
staleTime: 5_000,
|
||||
})
|
||||
}
|
||||
|
||||
export async function runHealthCheck() {
|
||||
return api.post<{ checked: number }>('/api/v1/health-check/run', {})
|
||||
}
|
||||
export * from '@/queries/certificates'
|
||||
export * from '@/queries/dns'
|
||||
export * from '@/queries/domain-health'
|
||||
export * from '@/queries/domains'
|
||||
export * from '@/queries/groups'
|
||||
export * from '@/queries/services'
|
||||
@@ -0,0 +1,73 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import {
|
||||
serviceBindingSchema,
|
||||
serviceGroupsResponseSchema,
|
||||
serviceViewSchema,
|
||||
} from '@/lib/schemas'
|
||||
import { z } from 'zod'
|
||||
|
||||
export const serviceKeys = {
|
||||
all: ['services'] as const,
|
||||
}
|
||||
|
||||
export const serviceGroupKeys = {
|
||||
all: ['service-groups'] as const,
|
||||
}
|
||||
|
||||
export const serviceGroupsQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: serviceGroupKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown>('/api/v1/service-groups')
|
||||
return serviceGroupsResponseSchema.parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const servicesQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: serviceKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/services')
|
||||
return z.array(serviceViewSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const serviceBindingKeys = {
|
||||
all: ['service-bindings'] as const,
|
||||
byDomain: (domainId: number) => [...serviceBindingKeys.all, 'domain', domainId] as const,
|
||||
}
|
||||
|
||||
export const serviceBindingsQueryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: serviceBindingKeys.all,
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>('/api/v1/service-bindings')
|
||||
return z.array(serviceBindingSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export const domainServiceBindingsQueryOptions = (domainId: number) =>
|
||||
queryOptions({
|
||||
queryKey: serviceBindingKeys.byDomain(domainId),
|
||||
queryFn: async () => {
|
||||
const data = await api.get<unknown[]>(`/api/v1/domains/${domainId}/service-bindings`)
|
||||
return z.array(serviceBindingSchema).parse(data)
|
||||
},
|
||||
})
|
||||
|
||||
export interface CreateServiceBindingBody {
|
||||
domain_id: number
|
||||
service_id: number
|
||||
hostname?: string
|
||||
target_ip?: string
|
||||
}
|
||||
|
||||
export async function createServiceBinding(body: CreateServiceBindingBody) {
|
||||
const data = await api.post<unknown>('/api/v1/service-bindings', body)
|
||||
return serviceBindingSchema.parse(data)
|
||||
}
|
||||
|
||||
export async function deleteServiceBinding(id: number) {
|
||||
return api.delete<{ deleted: boolean }>(`/api/v1/service-bindings/${id}`)
|
||||
}
|
||||
Reference in new issue
Block a user