@gusnips/one-click-dns

中文文档
0.1.5发布于 2026/11/01MITTypeScriptESM + CJS零依赖

将DNS记录放在客户的区域中:找到他们的DNS主机,将它们发送到签名的Domain Connect链接,使用API令牌编写,或显示该主机的步骤

中文使用文档
文档目录9 节
  1. 第一个电话
  2. 添加带有令牌的记录
  3. 根据你自己的账户核对一个作家
  4. 一键链接(域连接)
  5. 哪些提供程序打开了您的模板
  6. 模板
  7. 保持单独的SPF服务
  8. 主机步骤
  9. 检查

@gusnips / one-click-dns

将DNS记录放在客户的区域中。该包找到它们的DNS所在的位置,然后为您提供添加记录的最佳方式:签名的Domain Connect链接、粘贴的API令牌或该主机的步骤。

npm install @gusnips/one-click-dns
# or
bun add @gusnips/one-click-dns

它有三个切入点:

  • @gusnips/one-click-dns:在浏览器中运行。主机列表,记录拼写和模板预设。
  • @gusnips/one-click-dns/server读取DNS,建立签名链接。Node或Bun。
  • @gusnips/one-click-dns/writers:写入带有API令牌的记录。服务器只。

将密钥和令牌保存在服务器上。浏览器条目中不需要它们。

第一个电话

import { planConnection, systemDnsLookups } from "@gusnips/one-click-dns/server";

const plan = await planConnection({ hostname: "help.acme.com" }, systemDnsLookups(2000));

console.log(plan.reading?.hosts); // ["cloudflare"]
console.log(plan.methods.map((method) => method.kind)); // ["api-token", "manual"]

methods 列出了联系的方式,最好是第一个:

kind这意味着什么?
domain-connect一个点击。你把这个人送到 url,他们的DNS主机要求他们批准,记录就会出现。
api-token此人粘贴一个令牌,然后您的服务器写入该记录。
manual这个人用手抄写记录。主人的名字在里面 hosts. 总是最后一个。

plan.reading 是 null DNS没有应答。这与“主机未知”不同,所以不要将其显示为一个。

添加带有令牌的记录

import { cloudflare } from "@gusnips/one-click-dns/writers";

const outcome = await cloudflare.addRecord({
  token, // the person's Cloudflare API token
  record: { type: "CNAME", name: "help.acme.com", value: "abc123.domains.example.app" },
});
// "created", "unchanged" or "unproxied"

令牌仅用于此调用。它永远不会被存储、记录或放入错误中。这个人已经有的记录永远不会被覆盖:你得到一个 conflict 代替。

当写入失败时,它抛出 DnsWriteError 与一个 code. 用你自己的语言编写代码:

code告诉那个人什么
token_invalid主机不接受令牌。再次粘贴。
zone_read_missing令牌不能列出域。给它许可吧。
dns_edit_missing令牌不能改变DNS记录。给它许可吧。
zone_not_found令牌的帐户不包含此域。
conflict这个名字已经有另一个记录了(params.type). 他们决定如何处理它。
unavailable主人没有回答。再试一次。

四位主持人今天有一位作家。每个人粘贴的内容是不同的。测试用的是假网络。Cloudflare和DigitalOcean也是在一个真实账户上运行的,使用的是虚构的下划线名称,之后会被删除。而Porkbun和Name.com则不然。

作家token 是它需要什么检查了多少
cloudflareAPI令牌Zone:Read 和 DNS:Edit在2026-10-29对真实区域运行:创建、重复、TXT、冲突和错误的令牌
porkbunapikey:secretapikey“API访问”在Porkbun中为域打开(默认是关闭的)根据主机的API规范编写。从不针对真实帐户运行
digitalocean个人访问令牌domain:read 和 domain:create,或完全访问2026-10-29对一个真实的域运行:创建、重复、TXT、冲突和一个错误的令牌
name-comusername:apitoken在帐户设置、安全中打开API访问根据主持人的文档写的。永远不要与真实账户对账

writers 将主机id映射到它的写入器,所以 writers[host] 是 undefined 对于一个没有编剧的主持人来说。

根据你自己的账户核对一个作家

存储库有一个脚本,可以在真实区域上尝试写入器并删除它所创建的内容。使用一个备用区域和一个限制于此的令牌:

HOST=cloudflare ZONE=example.com TOKEN=your-token bun run live

它将CNAME和TXT记录写入虚构的下划线名称下,分别运行第二次(两者都必须读为“未改变”),检查冲突记录是否被拒绝,并报告错误的令牌,然后删除其记录。支持Cloudflare和DigitalOcean。

一个链接需要一个你的DNS主机已经开启的模板和一个密钥对。您在DNS中发布公钥,并将私钥保存在服务器上。

const plan = await planConnection(
  {
    hostname: "help.acme.com",
    connect: {
      providerId: "example.com", // your id in the Templates repository
      keyId: "_dck1", // the TXT record name that holds your public key
      serviceId: "custom-domain", // your template
      privateKeyPem,
      params: (zone) => ({
        domain: zone,
        host: "help",
        target: "abc123",
        redirect_uri: "https://app.example.com/done",
      }),
    },
  },
  systemDnsLookups(2000),
);

当主机有你的模板时, plan.methods 从a开始 domain-connect 方法。把人送回 redirect_uri 证明不了什么。在标记域完成之前,自己检查DNS。

只有这些提供者被要求,因为他们 _domainconnect 值和设置从DNS: Cloudflare, Vercel, Simply.com和GoDaddy读取。永远不会获取客户DNS中的名称。更多的提供者以同样的方式加入,一次一个值。

这里列出的提供商是包可以与之通信的提供商。这并不是说提供程序会打开你的模板。例如,GoDaddy在2026-10-29告诉我们,它不接受直接登录的新模板,也没有给出日期。询问每个提供者,并使用 templateCoverage 看看是谁答应了。没有联系的地方, plan.methods 仍然以主机的步骤结束。

哪些提供程序打开了您的模板

合并模板不是提供程序使用的模板。每个提供程序都手动打开模板,唯一的方法就是询问它。 templateCoverage 请求列表中的每个提供者一次:

import { templateCoverage } from "@gusnips/one-click-dns/server";

const coverage = await templateCoverage(
  {},
  { providerId: "example.com", serviceId: "custom-domain" },
);
// { cloudflare: "not_supported", godaddy: "supported", vercel: "not_supported", simply: "not_supported" }

"unanswered" 表示提供者很忙或没有回复。它从不意味着不。时不时地运行它,看看什么时候提供程序开始支持你的模板。

模板

模板是一个公开的小JSON文件 域连接模板 存储库。它列出了您可能要求主机添加的记录。预设构建一个遵循存储库规则的记录:

import { cnameTemplate, templateFileName } from "@gusnips/one-click-dns";

const template = cnameTemplate(
  {
    providerId: "example.com",
    providerName: "Example",
    logoUrl: "https://example.com/logo.svg",
    syncPubKeyDomain: "example.com",
    syncRedirectDomain: "app.example.com",
  },
  { pointsTo: "%target%.domains.example.com" },
);

console.log(templateFileName(template)); // "example.com.custom-domain.json"

有CNAME, DMARC, SPF, DKIM, MX和验证TXT的预设。

其中两个改变了域名电子邮件的工作方式。MX预设将替换域拥有的MX记录,因此它的邮件将转到其他地方。SPF预置将发送者添加到域的SPF记录中。在你提供任何一个链接之前,在你自己的屏幕上征求对方的同意,并说明模板的变化 description, DNS主机在批准时显示。

模板属于归档它的公司。它命名您的密钥域,主机根据在那里发布的密钥检查每个链接。所以你不能使用别人的模板,也没有人可以使用你的模板。客户的域不在模板中:它随每个链接一起提供,因此一个模板为每个客户服务。

文件中测试模板 在线编辑器、运行 dc-template-linter -merge-or-fail 打开一个pull request,然后要求每个DNS主机打开它。

保持单独的SPF服务

同一模板的两个链接可以替换彼此的记录,即使在发布任何一个链接之前检查DNS也是如此。对于SPF,这可以删除其他服务的发送权限。

当一个模板提供多个发送服务时,选择加入单独的实例:

const template = spfTemplate(identity, { multiInstance: true, version: 2 });

将更新后的模板归档,并等待DNS提供程序将其打开。在签署链接之前,要求该提供商的副本发布该标志:

import { discoverConnect, systemDnsLookups } from "@gusnips/one-click-dns/server";

const discovery = await discoverConnect(
  { txt: systemDnsLookups(2000).txt },
  {
    zone: "acme.com",
    providerId: "example.com",
    serviceId: "spf",
    budgetMs: 2000,
    requireMultiInstance: true,
  },
);

此检查需要提供程序条目和 supportsMultiInstance: true 和字面模板标志 multiInstance: true. 缺少或错误的支持返回 not_offered. 模板检查读取相同的受大小限制的响应,并且不发出额外的请求。其他发现调用保持当前的行为。

尚未有内置提供程序验证实例支持。 Cloudflare说它忽略了这个标志;其他提供程序未经验证。因此,要求实例返回 not_offered 对于每个内置提供程序。保持手动撤退。对于一个习惯 providers 列表,设置 supportsMultiInstance: true 只有在验证该提供者的行为之后,而不仅仅是它的模板JSON。

只有在需要的时候才签字 discovery.found 是真的。签名同步链接带有模板标志;规范定义了 instanceId 对于异步应用和恢复,而不是对于同步参数。如果稍后通过异步API删除一个应用程序,则传递一个稳定的 instanceId 来自完整发送域和服务的SPF包括:同一对重复使用相同的id,不同的域或服务使用不同的id。包不会为您选择该id。该标志不会检查SPF的查找限制,也不会重定向到您的应用程序,以证明DNS已更改。

主机步骤

DNS_HOSTS 拥有33个主机:如何从名称服务器,DNS页面上的标签和陷阱(默认情况下启用代理,尾随点,需要引号的TXT)中识别它们。该包包含数据而不包含句子,因此您可以用自己的单词和语言编写步骤。 dnsRecordRow 按主机希望输入的方式拼写记录:

import { dnsRecordRow } from "@gusnips/one-click-dns";

dnsRecordRow("route53", "acme.com", { type: "TXT", name: "acme.com", value: "v=spf1 ~all" });
// { type: "TXT", name: "", value: "\"v=spf1 ~all\"", atRoot: true, rootUnsure: false }

名称服务器模式只有在主机记录它或在DNS中看到它时才会出现在列表中。只有当主机自己的页面显示标签时,标签才会被引用。

检查

bun run lint
bun run typecheck
bun run build
bun test src/path/to/file.test.ts

版本与发布记录

@gusnips/one-click-dns 共发布 7 个版本,首次发布于 2026/10/29;过去 12 个月发布了 7 个版本。

版本发布日期距今
0.1.5latest2026/11/012 天前
0.1.42026/10/294 天前
0.1.32026/10/295 天前
0.1.22026/10/295 天前
0.1.12026/10/295 天前
0.1.02026/10/295 天前

关于 @gusnips/one-click-dns 的常见问题

@gusnips/one-click-dns 是什么?

将DNS记录放在客户的区域中:找到他们的DNS主机,将它们发送到签名的Domain Connect链接,使用API令牌编写,或显示该主机的步骤

如何安装 @gusnips/one-click-dns?

在项目目录运行 npm install @gusnips/one-click-dns,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@gusnips/server(>=0.14.3)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@gusnips/one-click-dns 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@gusnips/one-click-dns 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@gusnips/one-click-dns 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@gusnips/one-click-dns 可以商用吗?

@gusnips/one-click-dns 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@gusnips/one-click-dns 还在维护吗?

最新版本 0.1.5 发布于 2026/11/01(2 天前);至今共发布 7 个版本,其中过去 12 个月发布了 7 个;首次发布于 2026/10/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。