Show ANAME for apex domains
This commit is contained in:
parent
5ca8eeff51
commit
95b92a07f1
7 changed files with 45 additions and 24 deletions
|
|
@ -1519,8 +1519,9 @@ space carries my branding.
|
|||
Acceptance:
|
||||
|
||||
- Saving a domain under "Manage" shows it on the relay card with a "Pending"
|
||||
badge and the CNAME record to configure, whose copy button puts the target on
|
||||
the clipboard.
|
||||
badge and the DNS record to configure, whose copy button puts the target on
|
||||
the clipboard. A subdomain gets a CNAME; a bare domain, which DNS won't let
|
||||
take one, gets an ALIAS.
|
||||
- "Verify DNS record" flips the badge to "Verified" once the backend reports it.
|
||||
- The relay's displayed address then switches to the custom domain.
|
||||
|
||||
|
|
|
|||
|
|
@ -931,15 +931,23 @@ test("US-101 point a custom domain at a hosted relay", async ({seed, as}) => {
|
|||
|
||||
const domainForm = dialog(page, "Custom domain")
|
||||
|
||||
await domainForm.getByPlaceholder("relay.example.com").fill("other.test")
|
||||
await domainForm.getByPlaceholder("relay.example.com").fill("relay.other.test")
|
||||
await domainForm.getByRole("button", {name: "Save", exact: true}).click()
|
||||
|
||||
await expect(page.getByRole("alert")).toContainText("Custom domain saved.")
|
||||
await expect(page.getByText("other.test", {exact: true})).toBeVisible()
|
||||
await expect(page.getByText("relay.other.test", {exact: true})).toBeVisible()
|
||||
await expect(page.getByText("Pending", {exact: true})).toBeVisible()
|
||||
await expect(page.getByText("other.test CNAME space.test")).toBeVisible()
|
||||
await expect(page.getByText("relay.other.test CNAME space.test")).toBeVisible()
|
||||
|
||||
await page.locator('[data-tip="Copy CNAME target"]').click()
|
||||
// A bare domain can't take a CNAME, so it's shown as an ALIAS at the same target
|
||||
await page.getByRole("button", {name: "Manage"}).click()
|
||||
await domainForm.getByPlaceholder("relay.example.com").fill("other.test")
|
||||
await domainForm.getByRole("button", {name: "Save", exact: true}).click()
|
||||
|
||||
await expect(page.getByText("other.test", {exact: true})).toBeVisible()
|
||||
await expect(page.getByText("other.test ALIAS space.test")).toBeVisible()
|
||||
|
||||
await page.locator('[data-tip="Copy ALIAS target"]').click()
|
||||
|
||||
await expect(page.getByRole("alert")).toContainText("Copied to clipboard!")
|
||||
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe("space.test")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import type {Snippet} from "svelte"
|
||||
import cx from "classnames"
|
||||
import {Profiles} from "@welshman/app"
|
||||
import Compass from "@assets/icons/compass.svg?dataurl"
|
||||
import Letter from "@assets/icons/letter.svg?dataurl"
|
||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||
import UserRounded from "@assets/icons/user-rounded.svg?dataurl"
|
||||
|
|
@ -67,6 +68,11 @@
|
|||
<ImageIcon alt="Settings" src={UserRounded} class="rounded-full" size={8} />
|
||||
{/if}
|
||||
</PrimaryNavItem>
|
||||
{#if PLATFORM_RELAYS.length === 0}
|
||||
<PrimaryNavItem href="/spaces" title="All Spaces" prefix="no-highlight">
|
||||
<ImageIcon alt="All Spaces" src={Compass} size={8} />
|
||||
</PrimaryNavItem>
|
||||
{/if}
|
||||
<PrimaryNavItem
|
||||
title="Messages"
|
||||
onclick={chatHandler}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import cx from "classnames"
|
||||
import Widget from "@assets/icons/widget-4.svg?dataurl"
|
||||
import ImageIcon from "@lib/components/ImageIcon.svelte"
|
||||
import Divider from "@lib/components/Divider.svelte"
|
||||
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
|
||||
|
|
@ -46,11 +45,11 @@
|
|||
<PrimaryNavItem title="Home" href="/home">
|
||||
<ImageIcon alt="Home" src={PLATFORM_LOGO} class="rounded-full" size={10} />
|
||||
</PrimaryNavItem>
|
||||
<Divider />
|
||||
<Divider class="w-full" />
|
||||
<div
|
||||
bind:this={element}
|
||||
onscroll={measure}
|
||||
class="primary-nav__spaces"
|
||||
class="primary-nav__spaces mb-2"
|
||||
style:--fade-top="{Math.min(hiddenAbove, fadeSize)}px"
|
||||
style:--fade-bottom="{Math.min(hiddenBelow, fadeSize)}px">
|
||||
<DragList
|
||||
|
|
@ -62,8 +61,5 @@
|
|||
{/snippet}
|
||||
</DragList>
|
||||
</div>
|
||||
<PrimaryNavItem href="/spaces" title="All Spaces" prefix="no-highlight">
|
||||
<ImageIcon alt="All Spaces" src={Widget} size={8} />
|
||||
</PrimaryNavItem>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,11 @@
|
|||
const host = $derived(relayHost(current))
|
||||
const relayLabel = $derived(current.info_name || current.subdomain)
|
||||
const domainVerified = $derived(flagToBool(current.custom_domain_verified, false))
|
||||
const cnameTarget = $derived(canonicalRelayHost(current))
|
||||
const recordTarget = $derived(canonicalRelayHost(current))
|
||||
// DNS forbids a CNAME at a zone apex, so those domains point at the same
|
||||
// target using their provider's ALIAS/ANAME record instead.
|
||||
const isApex = $derived(current.custom_domain.split(".").length === 2)
|
||||
const recordType = $derived(isApex ? "ALIAS" : "CNAME")
|
||||
const isPaidPlan = $derived(current.plan_id !== "free")
|
||||
|
||||
// Adding, removing or verifying a custom domain moves the relay's host, so
|
||||
|
|
@ -156,7 +160,7 @@
|
|||
showMenu = false
|
||||
}
|
||||
|
||||
const copyCname = () => clip(cnameTarget)
|
||||
const copyTarget = () => clip(recordTarget)
|
||||
|
||||
// Verification runs in a backend poller; reload to pick up the result.
|
||||
const verify = async () => {
|
||||
|
|
@ -346,18 +350,23 @@
|
|||
</div>
|
||||
<div class="flex items-center gap-2 rounded-xl border border-line bg-surface px-3 py-2">
|
||||
<code class="min-w-0 flex-1 break-all font-mono text-xs text-content">
|
||||
{current.custom_domain} CNAME {cnameTarget}
|
||||
{current.custom_domain}
|
||||
{recordType}
|
||||
{recordTarget}
|
||||
</code>
|
||||
<Button
|
||||
class="button button-neutral button-sm shrink-0"
|
||||
data-tip="Copy CNAME target"
|
||||
onclick={copyCname}>
|
||||
data-tip="Copy {recordType} target"
|
||||
onclick={copyTarget}>
|
||||
<Icon icon={Copy} size={4} />
|
||||
</Button>
|
||||
</div>
|
||||
<p class="text-xs text-content-muted">
|
||||
For apex domains (e.g. example.com), use an ALIAS or ANAME record instead.
|
||||
</p>
|
||||
{#if isApex}
|
||||
<p class="text-xs text-content-muted">
|
||||
A bare domain can't use a CNAME. Your provider may call this record ANAME, or offer
|
||||
CNAME flattening instead.
|
||||
</p>
|
||||
{/if}
|
||||
<Button
|
||||
class="button button-neutral button-sm self-start"
|
||||
onclick={verify}
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ export const relayHost = (relay: HostedRelay): string =>
|
|||
? relay.custom_domain
|
||||
: canonicalRelayHost(relay)
|
||||
|
||||
// The host a custom domain must CNAME to.
|
||||
// The host a custom domain's DNS record must point at.
|
||||
export const canonicalRelayHost = (relay: HostedRelay): string =>
|
||||
`${relay.subdomain}.${relay.zooid_domain}`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type {Snippet} from "svelte"
|
||||
|
||||
interface Props {
|
||||
type Props = {
|
||||
children?: Snippet
|
||||
class?: string
|
||||
}
|
||||
|
||||
const {children}: Props = $props()
|
||||
const {children, class: className}: Props = $props()
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-2 p-2 text-xs uppercase text-content-subtle">
|
||||
<div class="flex items-center gap-2 p-2 text-xs uppercase text-content-subtle {className}">
|
||||
<div class="h-px grow" style="background-color: var(--line)"></div>
|
||||
{#if children}
|
||||
<p>{@render children?.()}</p>
|
||||
|
|
|
|||
Loading…
Reference in a new issue