跳到主要内容
Deno 2.4 已发布,包含 deno bundle、bytes/text imports、稳定的 OTel 等功能
了解更多
Subhosting

Deno Subhosting 引入更灵活的域关联功能

Subhosting 使得在安全托管的沙盒中安全运行来自多个客户的不可信 JavaScript 变得容易。Subhosting 有许多用例,例如 为其用户提供边缘函数在靠近用户的地方托管电子商务店面 等——所有这些都无需担心安全性并维护生产基础设施。

当托管用户代码时,您有时希望他们的部署可以通过一个整洁的自定义域(如 user1.yourcompany.com)公开访问。现在,借助我们新的、更灵活的域名关联功能,管理跨用户部署的自定义域变得更加简单。这使您可以通过 Subhosting API 以编程方式管理域名映射并将自定义域附加到部署。

在这篇文章中,我们将介绍:

在我们深入探讨如何使用 API 之前,让我们回顾一下此功能现在支持的内容。

全组织范围的通配符子域

现在,您可以将同一通配符域下的不同子域分配给不同的部署。例如,对于 *.example.com 通配符域,您现在可以将 foo.example.com 分配给一个部署,将 bar.example.com 分配给另一个部署。这种灵活性允许更复杂的部署策略和更简单的资源管理。

用于简化域名管理的变量

为了更简单地以编程方式管理和引用用户部署,现在当您指定要与部署关联的域名时,会公开两个变量:

  • {deployment.id}: 部署的唯一标识符。
  • {project.name}: 部署所属项目的名称。

这些变量可以与任意字符串组合,只要它们是有效的域名,并且在替换后被您注册的通配符域覆盖。例如,您可以指定以下域名:

  • {deployment.id}.example.com
  • {project.name}.example.com
  • {project.name}-{deployment.id}.example.com
  • foo-{deployment.id}.example.com
  • foo-{deployment.id}-{project.name}.example.com

这些变量也可以与 deno.dev 域结合使用,但在这种情况下只允许以下两种格式:

  • {project.name}-{deployment.id}.deno.dev
  • {project.name}.deno.dev

这些增强功能提供了更好的自定义和自动化能力,使得以编程方式管理和引用部署变得更加容易。

让我们通过一个示例来看看这些功能是如何付诸实践的。

如何附加和分离自定义域

在您可以将自定义域附加到部署之前,您的自定义域需要先使用 POST /organizations/{organizationId}/domains 端点进行注册:

import { assert } from "jsr:@std/assert/assert";

const orgId = "your-organization-id";
const orgToken = "your-organization-token";

const res = await fetch(
  `https://api.deno.com/v1/organizations/${orgId}/domains`,
  {
    method: "POST",
    body: JSON.stringify({
      domain: "*.example.com",
    }),
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

此调用的响应正文包含 dnsRecords 字段,该字段是可用于设置名称服务器的 DNS 记录列表。

下一步是为该域颁发 TLS 证书。您可以通过 上传证书 来完成此操作,也可以通过 POST /domains/{domainId}/certificates/provision 来配置 TLS 证书。

import { assert } from "jsr:@std/assert/assert";

const orgToken = "your-organization-token";
// Domain ID you got from the previous step
const domainId = "your-domain-id";

const res = await fetch(
  `https://api.deno.com/v1/domains/${domainId}/certificates/provision`,
  {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

现在您已准备就绪 — 让我们使用 POST /projects/{projectId}/deployments 创建一个新的部署:

import { assert } from "jsr:@std/assert/assert";

const projectId = "your-project-id";
const orgToken = "your-organization-token";

const res = await fetch(
  `https://api.deno.com/v1/projects/${projectId}/deployments`,
  {
    method: "POST",
    body: JSON.stringify({
      entryPointUrl: "main.ts",
      assets: {
        "main.ts": {
          kind: "file",
          content: 'Deno.serve(() => new Response("hello"));',
        },
      },
      envVars: {},
      domains: [
        "foo.example.com",
        "{deployment.id}.example.com",
        "{project.name}-{deployment.id}.deno.dev",
      ],
    }),
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

请注意 JSON payload 中新的 domains 字段,它是一个自定义域数组,一旦部署成功创建,这些域将被附加到部署。此外,您可以在这里在域中使用 命名变量,它们将变为实际值。

假设新创建的部署 ID 是 chonky-dog-57,位于项目 my-project 下。在我们的示例中,以下所有域都将路由到此新创建的部署:

  • https://foo.example.com
  • https://chonky-dog-57.example.com
  • https://my-project-chonky-dog-57.deno.dev

如果我们想将自定义域附加到现有部署怎么办?我们可以使用 PUT /deployments/{deploymentId}/domains/{domain}

import { assert } from "jsr:@std/assert/assert";

const deploymentId = "chonky-dog-57";
const orgToken = "your-organization-token";

const extraDomain = "prefix-{project.name}.example.com";

const res = await fetch(`/deployments/${deploymentId}/domains/${extraDomain}`, {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${orgToken}`,
  },
});

assert(res.ok);

现在,任何访问 https://prefix-my-project.example.com 的用户都将被定向到现有的 chonky-dog-57 部署。

请注意,无论何时将域附加到部署,它都会自动从任何先前的部署中分离。

我们还可以使用 DELETE /deployments/{deploymentId}/domains/{domain} 手动将域从部署中分离:

import { assert } from "jsr:@std/assert/assert";

const deploymentId = "chonky-dog-57";
const orgToken = "your-organization-token";

const res = await fetch(
  `/deployments/${deploymentId}/domains/foo.example.com`,
  {
    method: "DELETE",
    headers: {
      "Authorization": `Bearer ${orgToken}`,
    },
  },
);

assert(res.ok);

现在,域 https://foo.example.com 将不再指向 chonky-dog-57 部署。

下一步是什么

Subhosting 仍然是那些希望运行用户代码而无需担心安全或维护生产基础设施的组织的出色选择。

我们将继续投入资源,使 Subhosting 成为安全运行第三方不可信代码的最简单方式,以便您可以专注于为最终用户提供价值。

🚨️ 想安全地运行用户不可信的代码,而无需从头构建?

查看 Deno Subhosting,您可以在几分钟内在安全、托管的沙盒中运行来自多个用户的 JavaScript。