@domainkit/capsuledb
域库 Storage 在 PostgreSQL 作为声明 顶点DB
胶囊。 安装到下面 DomainKit.layer 和整个持久性生命周期——提供者
授权、连接、附件、交互式流程续作、计划/核准/接收
尝试,多领域批次,以及观察到的准备状态——生命在你的数据库中,范围扩大到你
租户.
联合国 域名- kit.dev
主机拥有 SqlClient 和它的寿命。 这个软件包拥有自己的桌子,从不暴露
行,查询,或生客户端.
安装
需要卡普尔DB >=0.6.0 <0.7.0 和效果 >=4.1.2 <5.0.0。 。 。 .
bun add @domainkit/capsuledb capsuledb @effect/sql-pg
把它接起来
import { Config, Layer } from "effect";
import { PgClient } from "@effect/sql-pg";
import { Cloudflare, Custody, DomainKit, Vercel } from "domainkit";
import { PgStorage } from "@domainkit/capsuledb";
export const DomainKitLive = DomainKit.layer({
providers: [
Cloudflare.provider({
oauth: {
clientId: Config.String("CF_CLIENT_ID"),
clientSecret: Config.Redacted("CF_CLIENT_SECRET"),
},
}),
Vercel.provider(),
],
}).pipe(
// `provideMerge`, not `provide`: `domainkit/server`'s handlers read attempts and receipts
// straight from Storage, so the layer they are given has to still carry it.
Layer.provideMerge(Layer.mergeAll(PgStorage.layer(), Custody.layerConfig())),
Layer.provide(PgClient.layerConfig({ url: Config.Redacted("DATABASE_URL") })),
);
PgStorage.layer() 在启动时准备:它创建了 CapsuleDB 的分类账,应用待迁移,以及
仅提供 Storage。胶囊服务永远无法观测到一个其表格为
失踪.
Custody.layerConfig() 读取 DOMAINKIT_CUSTODY_KEY 在它面前封存每个供货商的证书
到达一行。 换成KMS Custody.layerFromAsync; 没有简单文字模式, 而此
软件包从不看到.
或释放出SQL,并在靴子上坚持
如果你的移民是你们的, 取SQL代替:
capsuledb emit \
--module ./node_modules/@domainkit/capsuledb/dist/index.mjs \
--export capsule \
--dialect postgres \
--out ./drizzle
上面写着CapsuleDB的分类账DDL, 胶囊的迁移, 和准备行,加上一个
capsuledb.emit.json 索引命名它拥有的文件。 用你自己的管道应用,然后启动
以断言模式 :
PgStorage.layer({ mode: "assert" });
除非数据库已经与胶囊相匹配, 否则将不应用任何模式并失败, 所以丢失
迁移是一个启动失败,而不是一个运行时间的惊喜. capsuledb check 比较已排放
以 CI 中的当前胶囊创建文件夹.
或者在部署时准备
无服务器主机在其部署和靴子中编写数据库而未触及到计划.
PgStorage.manifest() 使胶囊的表率相等 PgStorage.layer() 安装,所以
使用堆栈 capsuledb/alchemy 编制函数所主张的内容:
import { PgStorage } from "@domainkit/capsuledb";
import * as CapsuleDB from "capsuledb/alchemy";
const manifest = yield * PgStorage.manifest();
const registry =
yield *
CapsuleDB.Registry("domainkit-storage", {
url,
provider: "Postgres",
manifest,
});
// Pass registry.fingerprint into the function's version-scoped environment.
函数靴与 readiness: "first-use"数字 :
PgStorage.layer({ mode: "assert", readiness: "first-use" });
该图层构建时没有语句;第一个存储查询运行一个缓存检查. 检查失败
表面为 SqlError 在询问中,然后在下一个重审。 传出同样的 prefix 财务报告和审定财务报表
registryPrefix 改为 manifest 财务报告和审定财务报表 layer。 。 。 .
表格
| 图表 | 密钥 | 持有 |
|---|---|---|
domainkit_authorizations | id | 供应商授权、账户标签、能力、撤销状态、密封证书 |
domainkit_connections | id | 委托人处理一项授权 |
domainkit_attachments | id专用 (owner_id, domain) | 域、 区域、 提供者目标 |
domainkit_continuations | id | tTL 交互式流量状态 |
domainkit_attempts | id | 计划、批准、接收、状况、租赁、失败 |
domainkit_readiness | (owner_id, domain) | 最新观察、必要证据、退后 |
domainkit_batches | id专用 (owner_id, idempotency_key) | 批量状态、核准摘要、拒绝 |
domainkit_batch_items | (batch_id, attachment_id) | 一个域在批次中的位置:位置、尝试指针、计划失败 |
批量物品是一个指针: 计划,批准,接收,租赁,失败 它显示一个客户全部 使用它的名字,所以没有存储两次, 批量状态重算从 在交易中试图移动它.
准备状态按域而不是附件键,所以仅观察公共 DNS 的宿主就会得到
同一行; attachment_id 当附件存在时链接, 当附件被清除时链接
已删除.
每张桌子都带着 owner_id,并按 Principal.Service 您的主机提供,所以
属于另一个租户的行读作缺席。 表格上没有外国密钥
之间; 在释放的 SQL 中添加您想要的 .
PgStorage.layer({ prefix }) 重新命名表格。 前缀是物理布局的一部分: 它
更改已生成的 DDL 和迁移校验和, 因此在第一次部署前修好, 永远不要
之后再换. registryPrefix capsuleDB 自己的分类账表格也这样做,必须匹配
capsuledb emit --prefix。 。 。 .
它的保证
- 总的过渡——核准、索偿、完成、失败、能力提升、证书
替换——在一个交易中运行于 a
FOR UPDATE排队. - 续作由
DELETE ... RETURNING,因此重播 OAuth 回调失败NotFound而不是连接两次. - 撤销为两相:标记
pending,然后删除 仅保留被撤销的证书。 相撞之间,或 刷新旋转证书的中途, 留下一行recoverRevocations稍后结束, 所以,新颁发的证书 永远不会成为提供者的孤儿. withLock使用保留连接的会话咨询锁定失败Busy而不是说 等待,所以一个证书刷新单飞 没有持有交易 通过一个HTTP呼叫.
包裹通过 Testing.conformance.storage 从 domainkit/testing,同一个套房
备忘录执行证.