Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/void/src/cli/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,16 @@ export type DomainResponse = {
status: string;
created_at: string;
cname_target?: string;
ownership_verification?: {
type: 'txt';
name: string;
value: string;
};
ssl_validation_records?: Array<{
txt_name?: string;
txt_value?: string;
status?: string;
}>;
};

export type DomainStatusResponse = DomainResponse & {
Expand Down
70 changes: 69 additions & 1 deletion packages/void/src/cli/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,32 @@ async function domainAdd(
): Promise<void> {
const domain = await client.addDomain(projectId, hostname);
log.success(`Domain ${hostname} added (status: ${domain.status})`);
note(`Add a CNAME record:\n ${hostname} → ${domain.cname_target}`, 'DNS Setup');

const sections: Array<string> = [];

if (domain.cname_target) {
sections.push(`CNAME (traffic)\n ${hostname} → ${domain.cname_target}`);
}

if (domain.ownership_verification) {
sections.push(
`TXT (ownership verification)\n ${domain.ownership_verification.name} → ${domain.ownership_verification.value}`,
);
}

const sslRecords = (domain.ssl_validation_records ?? []).filter(
(r): r is { txt_name: string; txt_value: string; status?: string } =>
Boolean(r.txt_name) && Boolean(r.txt_value),
);
if (sslRecords.length > 0) {
const lines = sslRecords.map((r) => ` ${r.txt_name} → ${r.txt_value}`).join('\n');
sections.push(`TXT (SSL validation)\n${lines}`);
}

if (sections.length > 0) {
note(sections.join('\n\n'), 'DNS Setup');
}

log.info('Run `void domain status ' + hostname + '` to check verification progress.');
}

Expand Down Expand Up @@ -107,8 +132,51 @@ async function domainStatus(
`Status: ${status.status}`,
`SSL Status: ${status.ssl_status}`,
];
if (status.ownership_verification) {
lines.push(`Ownership: pending`);
}
if (status.verification_errors && status.verification_errors.length > 0) {
lines.push(`Errors: ${status.verification_errors.join(', ')}`);
}
note(lines.join('\n'), 'Domain Status');

// Gate on actual validation states, not the aggregate `status.status`.
// Once the DB row is flipped to `active`, the API keeps returning
// `status: 'active'` even if CF later reports SSL/ownership re-validation
// (cert renewal, ownership re-check). Keying off the per-field states
// ensures pending records still surface in that window. CF stops returning
// `ownership_verification` once ownership is verified, so its presence is
// itself the pending signal.
const ownershipPending = Boolean(status.ownership_verification);
Comment thread
Brooooooklyn marked this conversation as resolved.
const sslPending = status.ssl_status !== 'active';

if (ownershipPending || sslPending) {
const pendingSections: Array<string> = [];

if (ownershipPending && status.ownership_verification) {
pendingSections.push(
`TXT (ownership verification)\n ${status.ownership_verification.name} → ${status.ownership_verification.value}`,
);
}
Comment thread
cursor[bot] marked this conversation as resolved.

if (sslPending) {
// Defensive: drop any per-record entries CF has already marked active.
// CF normally clears `ssl.validation_records` once SSL is active, but
// during cert renewals or partial states a stale `active` row can sit
// alongside a pending one — we only want to surface what the user still
// needs to add.
const sslRecords = (status.ssl_validation_records ?? []).filter(
(r): r is { txt_name: string; txt_value: string; status?: string } =>
Boolean(r.txt_name) && Boolean(r.txt_value) && r.status !== 'active',
);
if (sslRecords.length > 0) {
const lines = sslRecords.map((r) => ` ${r.txt_name} → ${r.txt_value}`).join('\n');
pendingSections.push(`TXT (SSL validation)\n${lines}`);
}
}

if (pendingSections.length > 0) {
note(pendingSections.join('\n\n'), 'Add these DNS records to your provider');
}
}
}
9 changes: 9 additions & 0 deletions packages/void/src/cli/wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,15 @@ function generateWrapperSource(
lines.push('async function wrappedFetch(request, env, ctx) {');
lines.push(' const url = new URL(request.url);');

// Readiness probe — platform polls this to confirm the worker is live
// after WfP upload. Handled here (before original.fetch) so the user's
// framework handler doesn't route it to env.ASSETS, which returns CF's
// "Assets have not yet been deployed" stub during asset propagation
// and would trip the platform's probe.
lines.push(' if (request.method === "GET" && url.pathname === "/__void/ready") {');
lines.push(' return Response.json({ ready: true });');
lines.push(' }');

// Migration handler
if (migrations && migrations.length > 0) {
lines.push(' if (request.method === "POST" && url.pathname === "/__void/migrate") {');
Expand Down
Loading