无咖啡因综合症
@decaf-ts/integrations 集中用于Keycloak,Kibana,Nest-style Auth,和基于Org的授权脚手架的可重复使用的助手.
出口
@decaf-ts/integrations@decaf-ts/integrations/keycloak@decaf-ts/integrations/kibana@decaf-ts/integrations/nest@decaf-ts/integrations/namespaces@decaf-ts/integrations/feature-flags@decaf-ts/integrations/redis@decaf-ts/integrations/sftp@decaf-ts/integrations/comms(+ 数)comms/{email,gmail,slack,teams,telegram}(中文(简体) )@decaf-ts/integrations/devops(+ 数)devops/{github,gitlab}(中文(简体) )@decaf-ts/integrations/io(+ 数)io/{airtable,google-sheets,graphql,spreadsheet}(中文(简体) )@decaf-ts/integrations/shared/http@decaf-ts/integrations/loader@decaf-ts/integrations/plugins@decaf-ts/integrations/plugins/kibana@decaf-ts/integrations/plugins/superset
包含模块
- Keycloak 为域名,用户,角色,身份提供者提供帮助,以及客户范围的角色接线.
- Kibana 为空间、数据视图、仪表板和特定领域的访问控制(包括流利)提供助手
KibanaIndexBuilder(Builder Pattle)用于构建精确匹配的索引模式配置,前缀/glob,以及日志生成的匹配模式. - 用于从访问符中提取 Keycloak 角色、命名空间瞄准镜和用户上下文的 Nest- stype JWT 帮助工具, 外加
namespace(...)用于授权范围元数据的模型装饰器. - 租户基于 Org 的授权脚手架, Org 单位,主力,角色,权限,授予,有效权限,存储装订,以及授权有效载荷过滤器.
- 地物旗模型、阅读器、服务、装饰器和环境辅助工具,包括发动机能力衍生工具
EngineCapabilityFeatureFlagService(见 地物标志) (中文(简体) ). - 为模型,适配器,寄存器,服务,控制器,环境对象,和Angular组件提供动态对象加载帮助.
- Redis(和Redis相容相容相容相容相相容相容相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相
RedisAdapter,查询语句/插件、酒吧/子事件发送,以及 aRedisContextLock辅助@transactional()。 。 。 . - SFTP 客户端帮助器, 用于列表/ 下載/ 删除 SSH 连接( 用于
core.devops.ftp图表节点). - BI 仪表板嵌入插件( Kibana + Superset) , 共享 DOM 无
DashboardEmbedPlugin合同。 Kibana插件被生成出源+安装器;Superset插件是一个补丁和构建策略,可以修改Superset的内部嵌入式前端和SDK源. 两者都暴露出完全相同的API并具有Org-agnostic(没有空格切换). - 命名的证书管理 :
Credential型号 +CredentialService通过模型支持存储在休息时加密的秘密值ModelSecretService,加上命名空间 RLS 模板/标签(RlsBootstrapService, (中文(简体) ),runAsPrincipal()——见 全权证书和命名空间。 。 。 . - 文件系统支持
FsSecretService(加密)decaf.fs-secret.v1信封,永远无法想象)InfraCredentialBootstrap.ensure()基础设施证书——见 全权证书和命名空间。 。 。 . - 每个通讯/编导/io/blob/secret/HTTP服务的普通无装饰服务类别:直接构建这些类别,并启动明确服务
await instance.boot(config),或——在图-节点路径上——与bootWithoutEnvironment(service, config)看到了吗 建筑和助推服务。 。 。 .
通信、DevOps和IO服务
SAA-1027阶段3b中增加了图表节点家庭,直接构建了这些服务类别。 每个服务都延长 HttpApiService (轴心运输,无咖啡因错误) SpreadsheetService,这是本地计算。 消费者以明确的配置对象构建服务并启动服务(await instance.boot(config) 看到了吗 建筑和助推服务); 明确的配置胜过匹配 CommsEnvironment编号 :DevopsEnvironment编号 :IoEnvironment 切片,并且从环境中读取或由呼叫者提供秘密,从不嵌入图表文档. 图形节点执行不能继承这些环境切片:节点消费者通过 bootWithoutEnvironment 代替平面 .boot(config) 看到了吗 建筑和助推服务。 。 。 .
| 导出 | 服务 | 配置环境 | 业务 |
|---|---|---|---|
comms/slack | SlackService | COMMS_SLACK_TOKEN, (中文(简体) ), COMMS_SLACK_BASE_URL, (中文(简体) ), COMMS_SLACK_TIMEOUT | 发送信件、 列表/ 获取对话、 获取用户 |
comms/telegram | TelegramService | COMMS_TELEGRAM_TOKEN, (中文(简体) ), COMMS_TELEGRAM_BASE_URL, (中文(简体) ), COMMS_TELEGRAM_TIMEOUT | 发送消息、获取元数据、获取更新、聊天动作 |
comms/teams | TeamsService | COMMS_TEAMS_TOKEN, (中文(简体) ), COMMS_TEAMS_WEBHOOK_URL, (中文(简体) ), COMMS_TEAMS_BASE_URL, (中文(简体) ), COMMS_TEAMS_TIMEOUT | 博客帖子、频道消息、列表团队/频道 |
comms/gmail | GmailService | COMMS_GMAIL_TOKEN, (中文(简体) ), COMMS_GMAIL_USER_ID, (中文(简体) ), COMMS_GMAIL_BASE_URL, (中文(简体) ), COMMS_GMAIL_TIMEOUT | 发送、列表/获取信件、列表标签 |
comms/email | EmailService | COMMS_EMAIL_* (提供方,主机,端口,安全,用户名,密码,来自,符号,图表基址) | SMTP 发送、 Graph 发送/ 列表收件箱、 IMAP 列表( 可选) imapflow(中文(简体) ) |
devops/github | GithubService | DEVOPS_GITHUB_* (脚本,基础 URL,所有者,Repo) | 仓库、问题、评论、拉动请求 |
devops/gitlab | GitlabService | DEVOPS_GITLAB_* (脚本,基址,项目ID) | 项目、问题、评论、合并请求 |
io/google-sheets | GoogleSheetsService | IO_GOOGLE_SHEETS_* (按键/ api 键, 基准 URL) | 获取/更新/附加/清晰值,创建电子表格 |
io/airtable | AirtableService | IO_AIRTABLE_* (脚本、基址、基ID) | 列表/get/创建/更新/删除记录 |
io/graphql | GraphqlService | IO_GRAPHQL_* (端点,符号) | 查询/调试请求 |
io/spreadsheet | SpreadsheetService | 无(当地) | 剖析/序列化CSV、TSV和JSON |
Redis 适配器
这个 ./redis 导出时会添加一个 Redis( 以及任何 Redis 兼容服务器, 如 DragonflyDB) 持久性适配器 。 每个模型表作为单一的Redis散列存储(<prefix>:<table>,默认前缀 decaf:redis),其字段为记录标识,其值为序列化记录. 由于Redis hashes是无法查询的,因此查询会加载匹配表并应用JavaScript中的过滤,排序,聚合和页码,镜像核心RAM适配器.
import { RedisAdapter } from "@decaf-ts/integrations/redis";
const adapter = new RedisAdapter({ url: "redis://localhost:6379", user: "johndoe" });
await adapter.initialize();
const repo = new (adapter.repository<User>())(User, adapter);
await repo.create(new User({ name: "John" }));
RedisConfig 接受一个完整的 url 连接字符串( 它优先) 或离散 host, (中文(简体) ), port, (中文(简体) ), username, (中文(简体) ), password,以及 database 字段,加 prefix (键名空间,默认为 decaf:redis和 user (身份印有创建/更新的记录).
本地测试设置
npm run prepare-it-tests 将飞龙DB容器从 docker/dragonfly-compose.yml 打开 localhost:6379 (或运行) docker compose -f docker/dragonfly-compose.yml up -d --wait 然后用 npm run test:integration。 。 。 .
行为和限制
- 在加载表格(RAM-adapter semantics)中以JavaScript来评价查询;排序和汇总不会被推倒到服务器.
@transactional()由RedisContextLock,缓冲器在专用连接上写上,并在解剖上将其与WATCH编号 :MULTI编号 :EXEC乐观的锁定.createdBy编号 :updatedBy邮票需要用户身份:通过user在适配器配置中(或表示)UUID标记),否则用户标识抛出UnsupportedError。 。 。 .- 观察员活动在电视上广播
decaf:redis:eventspub/ sub 通道, 这样共享数据库的每个进程都会刷新其观察者 .
地物标志
这个 ./feature-flags 导出( 也从包根再出) 提供特性旗模型、 阅读器、 寄存器/ 环境支持 FeatureFlagService、出入控制装潢器和生成的能力 EngineCapabilityFeatureFlagService。 。 。 .
EngineCapabilityFeatureFlagService 是一个 FeatureFlagService 子类,其旗帜值完全取自已使用的图形引擎能力显示表—— 与图形服务器在 GET /graph/engines (每个引擎 : engineId 加号 capabilities: [{ feature, verdict }],以及 defaultEngineId) (中文(简体) ). 旗帜从不从环境读取,从不从持久性寄存器读取: syncFromEnvironment() 未用一个 InternalError 而不是阅读环境.
解析失败 :
- 引擎键( A)
engine<Pascal(engineId)>)当发动机出现在清单中时启用. - 特性密钥( A)
engine<Pascal(engineId)><Pascal(feature)>启用) 仅限 当表里声明这个特征时SUPPORTED。 。 。 .NOT_SUPPORTED财务报告和审定财务报表EXTENSION_POINT裁决,以及没有列入清单的特征,对残疾人的决心. - 使用
engineCapabilityFlagKey(engineId, feature?)他们不是靠手来集合的,不是靠手来集合的,也不是靠手来集合的engineCapabilityFlagRegistry(manifest)。 。 。 .
import { EngineCapabilityFeatureFlagService } from "@decaf-ts/integrations/feature-flags";
const flags = new EngineCapabilityFeatureFlagService();
await flags.initialize({
manifest: {
engines: [
{
engineId: "local",
capabilities: [
{ feature: "codeMode", verdict: "SUPPORTED" },
{ feature: "workflowList", verdict: "SUPPORTED" },
{ feature: "workflowLookup", verdict: "SUPPORTED" },
],
},
{
engineId: "dagster",
capabilities: [{ feature: "suspendResume", verdict: "NOT_SUPPORTED" }],
},
],
defaultEngineId: "local",
},
});
flags.isEngineEnabled("local"); // true
flags.isEngineFeatureEnabled("local", "codeMode"); // true
flags.isEngineFeatureEnabled("dagster", "suspendResume"); // false (fail closed)
flags.isEngineFeatureEnabled("mastra", "pinning"); // false (undeclared engine)
flags.isEnabled("engineLocalWorkflowList"); // true
显示输入是Angraph 显示表的结构子集,因此不需要导入Angraph 包并保留依赖方向. 所有读取路径( A)isEnabled, (中文(简体) ), resolveFeatureFlags, (中文(简体) ), isEnabledForSubject, (中文(简体) ), resolveFeatureFlagsForSubject仅针对衍生登记处的解决,以及 listEnabled() 返回启用的旗帜为 FeatureFlag 不接触存储器的模型.
全权证书和命名空间
这个 ./namespaces 导出时在授权脚手架上添加了命名空格证书管理和行级安全靴子.
信用管理. 这个 Credential 模型是名称空格证书 元数据 记录(租户/org单位/所有者主名、名称、证书类型、提供者、过期);秘密值本身通过模型支持存储在休息时加密 ModelSecretService (单位:千美元)@decaf-ts/crypto AES 信封, RLS 扫描 secrets ) (中文(简体) ). ModelSecretService 执行 (name, version) 特性 : store 财务报告和审定财务报表 rotate 拒绝另一行已经持有的组合 SECRET_ALREADY_EXISTS 写入前出错( e) :rotate 不包括正在更新的行)和基于名称的操作(retrieve, (中文(简体) ), delete, (中文(简体) ), metadata, (中文(简体) ), rotate解决最新问题 updatedAt 确定几场比赛的顺序. CredentialService 管理生命周期—— createCredential, (中文(简体) ), rotateCredential, (中文(简体) ), getCredential, (中文(简体) ), listCredentials, (中文(简体) ), markCredentialUsed, (中文(简体) ), deleteCredential - 而且故意暴露没有 resolveCredentialSecret- 有风格的检索:通过上下文, RLS 限制的秘密路径解析出秘密值.
命名空间 RLS. sql/002_rls.sql 从 src/namespaces/rls/rls.template.ts (绝不用手来编辑) 并强制读者/写作/admin语义(namespace.read 编号 : namespace.write 编号 : namespace.admin 权限密钥; namespace-reader 编号 : namespace-writer 编号 : namespace-admin 默认角色键)横跨 org-unit 关闭和主要身份:读者只查看内容表,作者额外写入非行政行,管理员查看一切(行政表-) permissions, (中文(简体) ), roles, (中文(简体) ), role_permissions, (中文(简体) ), role_assignments, (中文(简体) ), inheritance_blocks, (中文(简体) ), storage_bindings ——只管管理),用户总是看到自己的身份行和自己的证书/秘密.
import {
CredentialService,
RlsBootstrapService,
runAsPrincipal,
} from "@decaf-ts/integrations/namespaces";
import pg from "pg";
// Apply the generated RLS template through any pg-shaped executor.
const admin = new pg.Client({ connectionString: process.env.DECAF_PG_URL });
await admin.connect();
const rls = await new RlsBootstrapService().boot(admin);
await rls.apply(); // idempotent; safe on every deployment
// Bind the acting principal for request-scoped reads (transaction-local).
await rls.setPrincipal(principalId);
// ...or run a callback inside a transaction with the principal bound
// (rolls back by default; pass { commit: true } for real writes).
await runAsPrincipal(admin, principalId, async (executor) => {
/* RLS-scoped queries */
});
// Credential lifecycle (secret value encrypted at rest via ModelSecretService).
const credentials = new CredentialService();
const credential = await credentials.createCredential({
tenantId,
name: "smtp-relay",
credentialType: "smtp",
secret: { host: "...", username: "...", password: "..." },
});
SQL 先决条件( 按顺序): sql/001_constraints.sql (名称空间计划), sql/004_credentials.sql (证书/机密名列+索引;独有 uq_secrets_name_version (name, coalesce(version, '')) 指数执行每行一行 (name, version) - 版本是可选的,所以 NULL 版本崩溃到 '' – 一个已关闭的后卫在复制后中止脚本 (name, version) 那么行已经存在了 sql/002_rls.sql (RLS模板——通过 RlsBootstrapService.apply() 或迁移跑道; apply() 需要连接角色 SUPERUSER 或者说 BYPASSRLS 无法关闭 RlsFunctionOwnerError 在请求范围流量到达数据库之前.
基础设施证书. 这个 ./secrets 导出增加 FsSecretService ——一个文件系统支持,AES加密(decaf.fs-secret.v1 信封里有东西, 0o700 根数 / 0o600 文件, 默认 .decaf/secrets用于基础设施证书的秘密商店。 永远无法想象list编号 :metadata 和通过 secrets.fs 环境切片( E)rootPath, (中文(简体) ), keySecret, (中文(简体) ), keyId或明确的启动配置 keySecret 强制的. InfraCredentialBootstrap.ensure(seeds) 种子在初始化时为单质 retrieve(name) 在使用前立即读取一个值 :
import { createInfraCredentialBootstrap } from "@decaf-ts/integrations/secrets";
const infra = await createInfraCredentialBootstrap({ keySecret: "infra-master-key" });
await infra.ensure([{ name: "smtp/relay", value: { host: "...", password: "..." } }]);
const { host, password } = await infra.retrieve("smtp/relay");
SMTP 电子邮件发送
@decaf-ts/integrations/comms/email 出口 SmtpClient (也通过 ./smtp (原始内容存档于2018-03-29). subpath. SmtpClient 代表任择议定书的 SMTP 议定书 nodemailer 等同(同一库 n8n 使用),而不是手工滚动线条协议,同时保持相同 connect编号 :send编号 :close 在预建的原始 MIME 消息上表面 :
secure: true从第一个字节( 默认端口) 打开一个隐形- TLS 套接字465(三);secure: false(默认)通过 SSTATLS (默认端口) 连接到平文本并升级587) (中文(简体) ).- 未关闭的平稿 AUTH : 已配置了证书, 会话必须在之前加密
AUTH LOGIN试图;allowPlaintextAuth是无法提供 TLS 的服务器的明确选择退出. - 信封地址在到达任何SMTP命令之前会被验证.
import { SmtpClient, buildEmail } from "@decaf-ts/integrations/comms/email";
const client = new SmtpClient({
host: "smtp.example.com",
port: 587,
username: "[email protected]",
password: process.env.SMTP_PASSWORD,
});
await client.connect();
const mail = buildEmail({ from: "[email protected]", to: ["[email protected]"], subject: "Nightly report", text: "All systems nominal." });
await client.send(mail.from, mail.recipients, mail.raw);
await client.close();
EmailService 是一个普通的无装饰类: 直接构造, 并用明确的配置对象启动( 见 建筑和助推服务) (中文(简体) ). EmailService 解析器在 plaintext AUTH 上被失败关闭: 在将清晰的配置合并到 comms.email 环境切片, initialize() 默认未解决 allowPlaintextAuth 改为 false,因此,除非有效的配置明确选择在 – 仅以环境为主,否则只通过一个 TLS 保护会话发送证书 allowPlaintextAuth: true 不再能够到达一个靴子,其明确配置省略了密钥(关于完全无环境的节点路径,参见 节点消费者的迷幻靴) (中文(简体) ).
建筑和助推服务
通讯/发展/io/blob/secret/HTTP服务是普通的无装饰类——否 @service() 注册,没有自动银通,也没有自动启动。 直接构建服务并调用 boot(config) 带有明确配置对象:
import { SlackService } from "@decaf-ts/integrations/comms/slack";
const slack = await new SlackService().boot({ token: process.env.SLACK_TOKEN });
boot(config) 代表团 initialize(),将清晰的配置与匹配中可解析的任何值合并 CommsEnvironment编号 :DevopsEnvironment编号 :IoEnvironment 切片; 明确对象总是胜出 。 对于完全清晰,无环境的配置,通过启动配置对象中每一个需要的字段.
节点消费者的迷幻靴
平面 .boot(config) 在明确的配置下合并匹配的环境切片——配置对象中缺少的密钥继承其环境值. 这是应用程序代码的预期行为, 但是图节点执行必须保持环境- Hermetic( SAA- 1305 finding F1): 节点启动 EmailService 不能默默地拾取环境 comms.email.allowPlaintextAuth=true 选入,以及环境 timeout 绝不能到达 HttpApiService 节点靴子.
@decaf-ts/integrations/shared/http 因此,出口 bootWithoutEnvironment(service, config, ...rest),该选项将一个基于环境的配置服务用于此调用:
import { EmailService } from "@decaf-ts/integrations/comms/email";
import { bootWithoutEnvironment } from "@decaf-ts/integrations/shared/http";
// The comms.email environment slice is ignored for this boot:
// allowPlaintextAuth stays false unless the explicit config sets it.
const email = await bootWithoutEnvironment(new EmailService(), {
provider: "smtp",
host: "smtp.example.com",
port: 587,
username: "[email protected]",
password: process.env.SMTP_PASSWORD,
});
- 未关闭 : 明确配置对象是强制性的——缺失或非对象配置投出 a
ValidationError而不是回到环境配置. - 该靴子没有环境 : 服务本身
configFromEnvironment()钩子被阴影所笼罩() => undefined单身期间boot()调用,所以在明确的配置中缺少的密钥会保留未设置,而不是继承环境切片. - 后还原: 原始内容
configFromEnvironment()将描述符放回(或删除,当服务没有)finally; 阴影是任意的, 绝不触及原型, 因此其他服务情况不会受到影响, 拆卸不会安全. - 同样的靴子合同: 剩余
boot()(例如无咖啡因咖啡因)Context)不变地发送,并返回、启动同一服务实例.
所有13个集成服务(comms, devops, io, and survey/blob)通过此助推器启动的A-graph节点类型——将其用于任何节点侧或自动启动,其中环境衍生的配置不得漏入明确的启动配置. 平面 .boot(config) 对于想要环境合并的应用程序代码,仍然正确.
可选的同伴依赖关系
可选同伴在 peerDependenciesMeta; 每一个也都是一种依赖:
| 端点 | 需要时间 |
|---|---|
@kubernetes/client-node (单位:千美元)^2.0.2(中文(简体) ) | 土生土长的 Kubernetes 密钥后端(KubernetesSecretService) (中文(简体) ). 安装 : npm i @kubernetes/client-node。 。 。 . |
nodemailer (单位:千美元)>=7.0.0(中文(简体) ) | SMTP交付(SmtpClient, (中文(简体) ), EmailService SMTP路径. 安装 : npm i nodemailer。 。 。 . |
pg (单位:千美元)>=8.0.0(中文(简体) ) | 命名空间 RLS / 证书 / 模型- 秘密集成测试和应用驱动 RLS 靴子对 Postgres. 安装 : npm i pg。 。 。 . |
综合测试基础设施
npm run prepare-it-tests 所有嵌入式信息技术基础设施 --wait 健康标志:
docker/dragonfly-compose.yml- 飞龙DB( 无法兼容) 开机localhost:6379(Redis适配ITs).docker/postgres-compose.yml—— 说postgres:16在主机端口上55435页:1decaf_integration用户decaf(RLS矩阵、证书和模式机密IT; 覆盖连接字符串与DECAF_PG_URL默认值postgresql://decaf:decaf-integration@localhost:55435/decaf_integration) (中文(简体) ).docker/mailpit-compose.yml- 邮箱SMTP沉入1025(HTTP API/UI 打开8025),接受任何AUTH,包括平面文字,所以SMTP认证路径可以在当地被执行(电子邮件ITs).
Kubernetes秘密集成测试是选入的,因为它们需要活集成. 启动微管, 点 KUBECONFIG 并明确运行套房:
minikube start
npm run test:integration:kubernetes
该套房创建了可支配的命名空间,锻炼了完整的CRUD合同,之后删除了命名空间. 没有可达到的集群(和 KUBECONFIG它被跳过.
运行集成测试 npm run test:integration。 。 。 .
页:1
- 每个子路径都可以独立导入.
- 同伴依赖是可选择的标记,所以消费者只安装他们实际使用的东西.
- 该套装意在从无咖啡因工作空间中消耗出,但在出处一级是自成一体的.