feat:Update pnpm-lock.yaml to link shared package; modify API routes to utilize new schemas for domain and service management; enhance DNS record handling with CNAME support; refactor service and subdomain routes for improved functionality; implement confirm dialog for domain deletion in the frontend; clean up unused components and improve UI consistency.
Build, Test, and Push CFDM Docker Image / test (push) Failing after 51s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been skipped
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
Build, Test, and Push CFDM Docker Image / test (push) Failing after 51s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been skipped
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import type { CertMonitoring } from '@cfdm/shared'
|
||||
|
||||
export const certMonitoringOptions: Array<{
|
||||
value: CertMonitoring
|
||||
label: string
|
||||
description: string
|
||||
}> = [
|
||||
{
|
||||
value: 'auto',
|
||||
label: 'Авто',
|
||||
description: 'Проверять, если хост обслуживается активным сервисом',
|
||||
},
|
||||
{
|
||||
value: 'required',
|
||||
label: 'Обязательно',
|
||||
description: 'Всегда проверять SSL, даже без привязок',
|
||||
},
|
||||
{
|
||||
value: 'skipped',
|
||||
label: 'Не проверять',
|
||||
description: 'Исключить из мониторинга сертификатов',
|
||||
},
|
||||
]
|
||||
|
||||
export function certMonitoringLabel(value: string): string {
|
||||
return certMonitoringOptions.find((o) => o.value === value)?.label ?? value
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
import type { ServiceBinding } from '@/lib/schemas'
|
||||
|
||||
function bindingIps(binding: ServiceBinding): string[] {
|
||||
if (binding.target_ips.length > 0) return binding.target_ips
|
||||
return binding.target_ip ? [binding.target_ip] : []
|
||||
}
|
||||
|
||||
export function getDomainIps(bindings: ServiceBinding[], domainId: number): string[] {
|
||||
const ips = bindings
|
||||
.filter((b) => b.domain_id === domainId && b.target_ip)
|
||||
.map((b) => b.target_ip as string)
|
||||
.filter((b) => b.domain_id === domainId)
|
||||
.flatMap(bindingIps)
|
||||
return [...new Set(ips)]
|
||||
}
|
||||
|
||||
@@ -22,11 +27,11 @@ export function groupBindingsByHostname(
|
||||
export function buildIpsByDomainId(bindings: ServiceBinding[]): Map<number, string[]> {
|
||||
const map = new Map<number, string[]>()
|
||||
for (const binding of bindings) {
|
||||
if (!binding.target_ip) continue
|
||||
const ips = bindingIps(binding)
|
||||
if (ips.length === 0) continue
|
||||
const existing = map.get(binding.domain_id) ?? []
|
||||
if (!existing.includes(binding.target_ip)) {
|
||||
map.set(binding.domain_id, [...existing, binding.target_ip])
|
||||
}
|
||||
const merged = [...new Set([...existing, ...ips])]
|
||||
map.set(binding.domain_id, merged)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const subdomainSchema = z.object({
|
||||
id: z.number(),
|
||||
domain_id: z.number(),
|
||||
name: z.string(),
|
||||
fqdn: z.string(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
|
||||
export type Subdomain = z.infer<typeof subdomainSchema>
|
||||
export {
|
||||
createSubdomainSchema,
|
||||
subdomainSchema,
|
||||
updateSubdomainSchema,
|
||||
type CreateSubdomainInput,
|
||||
type SubdomainRecord,
|
||||
type UpdateSubdomainInput,
|
||||
} from '@cfdm/shared'
|
||||
|
||||
+69
-20
@@ -50,8 +50,10 @@ export const serviceDomainBindingSchema = z
|
||||
zone_name: z.string(),
|
||||
hostname: z.string(),
|
||||
fqdn: z.string(),
|
||||
record_type: z.enum(['A', 'CNAME']).default('A'),
|
||||
target_ips: z.array(z.string()).optional(),
|
||||
target_ip: z.string().nullable().optional(),
|
||||
target_cname: z.string().nullable().optional(),
|
||||
sync_status: z.string().nullable(),
|
||||
})
|
||||
.transform((binding) => ({
|
||||
@@ -62,6 +64,10 @@ export const serviceDomainBindingSchema = z
|
||||
: binding.target_ip
|
||||
? [binding.target_ip]
|
||||
: [],
|
||||
target_cname: binding.target_cname?.trim() || null,
|
||||
record_type: binding.target_cname?.trim()
|
||||
? ('CNAME' as const)
|
||||
: (binding.record_type ?? 'A'),
|
||||
}))
|
||||
|
||||
export const serviceViewSchema = serviceSchema.extend({
|
||||
@@ -86,6 +92,7 @@ export const domainSchema = z.object({
|
||||
zone_name: z.string(),
|
||||
cf_zone_id: z.string(),
|
||||
status: z.string(),
|
||||
cert_monitoring: z.enum(['auto', 'required', 'skipped']).default('auto'),
|
||||
last_synced_at: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
@@ -96,22 +103,33 @@ export const domainListItemSchema = domainSchema.extend({
|
||||
service_count: z.number(),
|
||||
})
|
||||
|
||||
export const serviceBindingSchema = z.object({
|
||||
id: z.number(),
|
||||
domain_id: z.number(),
|
||||
service_id: z.number(),
|
||||
hostname: z.string(),
|
||||
dns_record_id: z.number().nullable(),
|
||||
zone_name: z.string(),
|
||||
group_id: z.number().nullable(),
|
||||
group_name: z.string().nullable(),
|
||||
service_name: z.string(),
|
||||
service_slug: z.string(),
|
||||
target_ip: z.string().nullable(),
|
||||
sync_status: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
export const serviceBindingSchema = z
|
||||
.object({
|
||||
id: z.number(),
|
||||
domain_id: z.number(),
|
||||
service_id: z.number(),
|
||||
hostname: z.string(),
|
||||
dns_record_id: z.number().nullable(),
|
||||
zone_name: z.string(),
|
||||
group_id: z.number().nullable(),
|
||||
group_name: z.string().nullable(),
|
||||
service_name: z.string(),
|
||||
service_slug: z.string(),
|
||||
target_ip: z.string().nullable(),
|
||||
target_ips: z.array(z.string()).optional(),
|
||||
sync_status: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
})
|
||||
.transform((binding) => ({
|
||||
...binding,
|
||||
target_ips:
|
||||
binding.target_ips && binding.target_ips.length > 0
|
||||
? binding.target_ips
|
||||
: binding.target_ip
|
||||
? [binding.target_ip]
|
||||
: [],
|
||||
}))
|
||||
|
||||
export const dnsRecordSchema = z.object({
|
||||
id: z.number(),
|
||||
@@ -169,10 +187,30 @@ const ipv4Schema = z
|
||||
'╨Э╨╡╨║╨╛╤А╤А╨╡╨║╤В╨╜╤Л╨╣ IPv4',
|
||||
)
|
||||
|
||||
const serviceDomainInputSchema = z.object({
|
||||
fqdn: z.string().min(1, '╨г╨║╨░╨╢╨╕╤В╨╡ FQDN'),
|
||||
target_ips: z.array(ipv4Schema).min(1, '╨Т╤Л╨▒╨╡╤А╨╕╤В╨╡ ╤Е╨╛╤В╤П ╨▒╤Л ╨╛╨┤╨╕╨╜ IP'),
|
||||
})
|
||||
const serviceDomainInputSchema = z
|
||||
.object({
|
||||
fqdn: z.string().min(1, 'Укажите FQDN'),
|
||||
target_ips: z.array(ipv4Schema).optional(),
|
||||
target_cname: z.string().min(1, 'Укажите CNAME-цель').optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const hasIps = (data.target_ips?.length ?? 0) > 0
|
||||
const hasCname = Boolean(data.target_cname?.trim())
|
||||
if (!hasIps && !hasCname) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: 'Укажите IP или CNAME-цель',
|
||||
path: ['target_ips'],
|
||||
})
|
||||
}
|
||||
if (hasIps && hasCname) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: 'Укажите либо IP, либо CNAME-цель',
|
||||
path: ['target_cname'],
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const createServiceSchema = z.object({
|
||||
name: z.string().min(1, '╨г╨║╨░╨╢╨╕╤В╨╡ ╨╜╨░╨╖╨▓╨░╨╜╨╕╨╡'),
|
||||
@@ -248,3 +286,14 @@ export type CreateServiceBindingInput = z.infer<typeof createServiceBindingSchem
|
||||
export type CreateDomainInput = z.infer<typeof createDomainSchema>
|
||||
export type LoginInput = z.infer<typeof loginSchema>
|
||||
export type CreateDnsRecordInput = z.infer<typeof createDnsRecordSchema>
|
||||
|
||||
export {
|
||||
createSubdomainSchema,
|
||||
subdomainSchema,
|
||||
updateDomainSchema,
|
||||
updateSubdomainSchema,
|
||||
type CreateSubdomainInput,
|
||||
type SubdomainRecord,
|
||||
type UpdateDomainInput,
|
||||
type UpdateSubdomainInput,
|
||||
} from '@cfdm/shared'
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
import { bindingToFqdn } from '@/lib/parse-fqdn'
|
||||
import type { ServiceView } from '@/lib/schemas'
|
||||
import type { ServiceGroupsResponse, ServiceView } from '@/lib/schemas'
|
||||
|
||||
export function formatServiceGroupLabel(
|
||||
groupName: string | null | undefined,
|
||||
serviceName: string,
|
||||
): string {
|
||||
const group = groupName?.trim() || 'Без группы'
|
||||
return `${group} / ${serviceName}`
|
||||
}
|
||||
|
||||
export function buildServiceGroupNameById(
|
||||
data: ServiceGroupsResponse,
|
||||
): Map<number, string | null> {
|
||||
const map = new Map<number, string | null>()
|
||||
for (const group of data.groups) {
|
||||
for (const service of group.services) {
|
||||
map.set(service.id, group.name)
|
||||
}
|
||||
}
|
||||
for (const service of data.ungrouped) {
|
||||
map.set(service.id, null)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
export function serviceDisplayFqdn(service: ServiceView): string {
|
||||
const first = service.domains?.[0]
|
||||
|
||||
Reference in New Issue
Block a user