Files
cloudflare-domain-manager/apps/api/src/services/binding-service.ts
T
Denozordec a458465153
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 10s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m8s
quality / api (push) Successful in 1m9s
CD / quality (push) Successful in 2m31s
CD / publish (push) Successful in 1m35s
feat(certificates): enhance service certificate management and monitoring
- Added new endpoints for listing and checking service certificates, improving visibility into SSL status.
- Integrated certificate monitoring options into service binding updates, allowing for flexible SSL management.
- Updated the service detail grid to include SSL monitoring controls, enhancing user interaction with certificate settings.
- Refactored related components and schemas to support the new certificate features, ensuring consistency across the application.
- Improved test coverage for certificate functionalities, validating the new features and ensuring reliability.
2026-08-20 00:32:43 +07:00

173 lines
3.9 KiB
TypeScript

import type { Db } from "@cfdm/db";
import { repos } from "@cfdm/db";
import type { ServiceBindingView } from "@cfdm/shared";
import type { CloudflareClient } from "../lib/cf-client.js";
import * as dnsService from "./dns-service.js";
export interface CreateBindingRequest {
domain_id: number;
service_id: number;
hostname?: string;
target_ip?: string;
}
export interface UpdateBindingRequest {
service_id?: number;
hostname?: string;
target_ip?: string;
cert_monitoring?: string;
}
function normalizeHostname(hostname?: string): string {
const h = hostname?.trim();
return h ? h : "@";
}
async function syncTargetIp(
db: Db,
cf: CloudflareClient,
domainId: number,
bindingId: number,
hostname: string,
dnsRecordId: number | null,
targetIp: string,
): Promise<number> {
if (dnsRecordId) {
await dnsService.update(db, cf, domainId, dnsRecordId, {
record_type: "A",
name: hostname,
content: targetIp,
proxied: false,
});
return dnsRecordId;
}
const record = await dnsService.create(db, cf, domainId, {
record_type: "A",
name: hostname,
content: targetIp,
ttl: 1,
proxied: false,
});
repos.setBindingDnsRecordId(db, bindingId, record.id);
return record.id;
}
export function listAll(db: Db): ServiceBindingView[] {
return repos.listAllBindings(db);
}
export function listByDomain(db: Db, domainId: number): ServiceBindingView[] {
repos.getDomain(db, domainId);
return repos.listBindingsByDomain(db, domainId);
}
export async function create(
db: Db,
cf: CloudflareClient,
req: CreateBindingRequest,
): Promise<ServiceBindingView> {
repos.getDomain(db, req.domain_id);
repos.getService(db, req.service_id);
const hostname = normalizeHostname(req.hostname);
const binding = repos.insertBinding(
db,
req.domain_id,
req.service_id,
hostname,
null,
);
const ip = req.target_ip?.trim();
if (ip) {
await syncTargetIp(db, cf, req.domain_id, binding.id, hostname, null, ip);
}
return repos.getBindingView(db, binding.id);
}
export async function update(
db: Db,
cf: CloudflareClient,
id: number,
req: UpdateBindingRequest,
): Promise<ServiceBindingView> {
const existing = repos.getBinding(db, id);
if (req.cert_monitoring !== undefined) {
repos.updateBindingLbConfig(db, id, {
cert_monitoring: req.cert_monitoring,
});
}
const hasIdentityPatch =
req.service_id !== undefined ||
req.hostname !== undefined ||
req.target_ip !== undefined;
if (!hasIdentityPatch) {
return repos.getBindingView(db, id);
}
const serviceId = req.service_id ?? existing.service_id;
if (req.service_id) repos.getService(db, req.service_id);
const hostname = req.hostname
? normalizeHostname(req.hostname)
: existing.hostname;
repos.updateBindingFields(
db,
id,
serviceId,
hostname,
existing.dns_record_id,
);
const ip = req.target_ip?.trim();
if (ip) {
await syncTargetIp(
db,
cf,
existing.domain_id,
id,
hostname,
existing.dns_record_id,
ip,
);
}
return repos.getBindingView(db, id);
}
export function remove(db: Db, id: number): void {
repos.getBinding(db, id);
repos.deleteBinding(db, id);
}
export async function setDomainServices(
db: Db,
domainId: number,
serviceIds: number[],
): Promise<number[]> {
repos.getDomain(db, domainId);
for (const sid of serviceIds) {
repos.getService(db, sid);
}
const existing = repos.listBindingsByDomain(db, domainId);
for (const binding of existing) {
if (!serviceIds.includes(binding.service_id)) {
repos.deleteBinding(db, binding.id);
}
}
for (const sid of serviceIds) {
const already = existing.some((b) => b.service_id === sid);
if (!already) {
repos.insertBinding(db, domainId, sid, "@", null);
}
}
return repos
.listBindingsByDomain(db, domainId)
.map((b) => b.service_id);
}