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
- 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.
173 lines
3.9 KiB
TypeScript
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);
|
|
}
|