@ krizaka/storage
每个Krizaka产品的文件都在一个端口后面。端口是根据它的功能来命名的(ObjectStore),提供者是管理员选择的值。无供应商SDK (纯HTTP通过注入 fetch),没有环境变量,没有挂钟。
- 港口。
put,get,head,delete,list(prefix),signedUrl(key, ttl)对于私人区域,publicUrl(key)当存在拉区或CDN时。 - 适配器。
remoteStore(一个形状为Bunny Storage的HTTP区域:AccessKey头,PUT/GET/DELETE,一个由其路径列出的带有尾部斜杠的目录,一个带有令牌签名链接的拉区),bucketStore(任何兼容s3的服务:签名V4,预签名GET,ListObjectsV2(没有SDK);folderStore(一个文件夹,用于开发、测试和Mac应用程序;密钥永远不会离开根目录),memoryObjectStore. 所有运行objectStoreContract(@krizaka/storage/testkit)。 - 每次上传都要遵守的规则 (
createStorage):传入大小限制,前缀必须在a中声明defineLayout(拒绝未知前缀),字节由幻数(sniffType: png, jpeg, gif, webp, pdf, mp4, webm, svg),从不通过扩展名,并且拒绝与字节相矛盾的声明类型。存储的名称是内容散列的,<name>.<sha10>.<ext>,所以URL永远不会改变内容。 - 管理员选择 (
@krizaka/storage/settings):storage.provider(local,http,s3),storage.maxMegabytes,storage.signedUrlSeconds,而非机密坐标(endpoint,region,bucket,zone,prefix,pullHostname,publicBaseUrl). 钥匙留在环境中;storeFromSettings将它们作为参数,并在没有需要的情况下拒绝所选择的提供者,而不是退回到另一个。一个管理类型的S3端点通过checkEndpoint(仅https,没有凭据,没有本地或私有地址)。 - 静态加密,保险库,区域和
where.encryptedStore(inner, key)(AES-256-GCMObjectStore;键是一个参数;提供者保存密文;没有链接),seal/open(数据库中的凭据,v1.信封),COUNTRY_OF_REGION(一个地区表:未列出的地区永远不是一个国家;residencyOfConfig只有在区域和端点一致的情况下才会显示“加拿大”,而不是兔形区域),where(kind, active, { fixed, devOnly })和storageNotice(信息面板的句子键,来自配置)。 - 环境管理 (
@krizaka/storage/resolve)。resolveStorage(env, admin)提供各种类型的数据(文档,头像):传递各自的数据DeploymentStorage):管理员的storage.*当管理员离开时,环境提供了秘密和退路local. 如果环境说none然后管理员继续打开,它抛出StorageErrorSTORAGE_KEEPING_OFF(在保存设置时调用它来拒绝早期):保持在环境中打开,永远不要单独通过设置。openResolved生成存储,并在给出密钥时进行加密。 - 供应商注册表 (adr - 005)。
PROVIDER_KINDS类型表是否为数据(bunny,s3预置DigitalOcean Spaces、AWS S3、OVHcloud和generic,dropbox、保留google-drive/onedrive,folder,device):作用域、运行时、devOnly、功能、坐标和凭据字段模式。openProvider(kind, coordinates, credentials, { runtime, environment, fetch, clock })(/node)是唯一的工厂,它拒绝(STORAGE_BAD_CONFIG)devOnly类型在prod,folder外dev,未知或未实现的类型和缺失的字段,并且永远不会回落。它返回store和aResidencyLabel(PROVINCE_OF_REGION:«Canada (qubec)»,«Canada (Ontario)»…),以便在保存前显示屏幕。testConnection(探测写、读、比较、删除)和checkEndpointDns(书面形式加上DNS答案:拒绝私人地址)为每个产品提供服务。 - 注册表打开OpenDAL类型和private类型 (ADR-005修正案,krizaka-ts#239)。
google-drive和onedrive在用户范围内实现(通过opendalStore和CLOUD_CONNECTORS:访问令牌从密封的刷新令牌更新,一个旋转的令牌返回OpenContext.onRotate并由服务部门再次密封);azblob,gcs,b2和webdav它们的字段名是OpenDAL自己的选项url字段传递checkEndpoint和DNS检查)。openProvider现在是异步的。每种类型映射到表中的适配器(BUILT_IN_ADAPTERS),而不是树枝。私有包在不触及核心的情况下添加了一个类:defaultProviderRegistry.with({ kind, open })(或createProviderRegistry({ kinds, adapters }))返回一个新的注册表;传递给openProvider(…, registry)和createProviderService({ registry }). 重复的名称被拒绝,秘密不能是坐标,凭证必须是secret字段(密封),以及运行时,devOnly、required-field和url规则适用于私有类型和内置类型。 Keyring是一个港口 ({ keyId, wrap, unwrap, openLegacy? })。localKeyring({ keyId, key, ring })是进程内默认值(旧的Keyring形状{ keys, legacy }现在是LocalKeys);sealWith,openWith和rewrapWith取任何密匙环,因此KMS (AWS KMS ca-central-1, OVHcloud KMS, Vault transit)包装数据密钥,密钥永远不会到达应用程序。createProviderService({ vault })要么是老的{ keyId, key, ring }或者一个Keyring. 存储v2.<key_id>…格式不变:移动到KMS是不变的rotateVaultKey,而不是重返大气层。- 信封。
sealEnvelope/openEnvelope/rewrapEnvelope:每个密封值的数据密钥,由密钥加密密钥封装(MEDIA_VAULT_KEY)由key_id(v2.<key_id>.<wrapped>.<sealed>);v1.值仍然使用遗留键打开;旋转键只重新包装数据键。 - 每租户信封加密 (ADR-020决定3,管理文件存储)。
KeyService是KMS港口(currentKeyId,wrap,unwrap绑定到a{ tenant }上下文,可选rotate);根密钥永远不会离开KMS。适配器:memoryKeyService(测试和开发人员的机器:版本,rotate,destroy),cloudKeyService用于aws-kms(使用SigV4签名的KMS JSON API,加密上下文中的租户,RotateKeyOnDemand),okms(OVHcloud KMS服务密钥;访问证书骑在fetch产品通过;旋转=在设置中命名的新键,然后是重新包装作业),然后是注入的fetch,没有SDK。tenantVault({ kms, keys, clock, audit })每个租户和轮换保留一个KEK,封装在数据库(TenantKeyStore:dbTenantKeyStore在storageSchemav2,memoryTenantKeyStore),以及每个对象版本的新AES-256-GCM DEK。tenantStore(inner, vault, tenantId)是一个ObjectStore下t/<tenantId>/,将完整键作为关联数据,因此另一个租户或重命名的对象永远不会打开。rotateTenant+rewrapAll(只有头,没有字节)+dropOldVersions;rewrapRoot根变化后的;shredTenant销毁密钥(墓碑优先,无种族)和每个副本,包括副本和备份,答案STORAGE_TENANT_SHREDDED. 每个关键事件都是一条审计线。合同:keyServiceContract,tenantKeyStoreContract. - 托管提供程序 (ADR-020决定2和7)。
managed是另一种注册表(管理范围):任何s3兼容的桶(预置OVHcloud与bhs和ca-east-tor、AWS S3、谷歌云存储互操作性模式、DigitalOcean Spaces(用于本地S3服务器的通用)加上KMS (kms:okms,aws-kms,或者local仅在开发中(在生产中拒绝),每个密钥都是一个密封的凭证,管理员只输入一次,再也不会看到。openProvider("managed", …, { managed: { keys, audit, mtlsFetch? } })返回forTenant(tenantId)(一个加密tenantStore下t/<tenantId>/)和探针存储,因此提供程序屏幕的“测试连接”证明桶,KMS和键表端到端;managedResidency说“加拿大(quamesim);复制:加拿大(安大略省)»从管理员声明。管理员的控制(@krizaka/storage/settings):managedStorageSettings(开/关,允许的提供者byo/managed/both,配额,最长链接,私有文件夹,设备缓存,垃圾,保留界限;全局和每个租户,由@krizaka/settings),resolveManagedPolicy(租户>计划>产品>全球;自相矛盾的策略被拒绝),providerAllowed(一扇门)linkSeconds,MANAGED_PRICE_IDS(目录条目;未输入数量)。切换租户的提供商是moveBatch+readDual在任何两个存储之间,在真实的S3服务器上用两个桶证明。 - 数据库中的提供者 (
@krizaka/storage/provider-admin, ADR-005切片2)。storageSchema(表storage.provider和storage.default_provider),dbProviderStore/memoryProviderStore(合同:providerStoreContract),createProviderService:创建(a)draft(密封在信封里的秘密),测试(只有绿色测试验证),setDefault(仅经过验证;之前的默认值变为retiring),changeKey(候选人先接受测试),disable/retire(当产品仍有物体时,拒绝使用),rotateVaultKey,bootstrap(进口旧的)MEDIA_*环境曾经是active提供者,source: env). 秘密回来了set/not set只有;每个更改都被注入audit有秘密的领域changed.moveBatch(get,检查SHA-256, put,回读和比较,然后切换产品事务中的行;返回失败,永远不会切换)和readDual(行提供程序,然后是退休的)是移动和双读。 - 用户自己的云 (ADR-005切片4)。
dropboxStore(dropbox类型,用户范围):仅应用文件夹,上传会话超过150mb,没有签名或公共链接(Dropbox链接超过我们的900 s上限,所以应用程序流)。pkcePair,dropboxAuthorizeUrl,exchangeDropboxCode,refreshDropboxToken,revokeDropbox为OAuth 2 PKCE步骤;刷新令牌与任何凭证一样是密封的,并且一个已撤销或过期的令牌可以应答STORAGE_DISCONNECTED(没有任何移动或删除)。用户自己的S3也是如此s3这种在user:<id>作用域,并对其端点进行DNS检查。 - 每个经典云都通过图书馆 (ADR-005修正案,krizaka-ts#235)。
opendalStore(@krizaka/storage/opendal)是Apache OpenDAL的一个适配器,适用于谷歌Drive, OneDrive, Dropbox, Azure Blob, GCS, B2, Swift和WebDAV。只有服务OPENDAL_SERVICES开放;fs/memory需要allowLocal;类型化端点通过checkEndpoint首先是DNS检查。OpenDAL的错误映射到STORAGE_*(PermissionDenied在OAuth云上→STORAGE_DISCONNECTED),readStream(key)流式传输大文件。opendal是一个 可选的对等 (一个本地模块59-82 MB, glibc≥2.38或musl),第一次使用时加载。CLOUD_CONNECTORS(/node)是用户云表(Dropbox,谷歌Drive, OneDrive:端点,最窄范围,撤销样式)。startConnection,finishConnection,refreshConnection,revokeConnection运行Arctic的授权码+ PKCE S256,并且connectionAccessToken提要opendalStore一个新的访问令牌,并将一个旋转的刷新令牌返回以重新密封。dropboxStore它的助手被取代,直到产品移动。 - 签名。 SigV4是根据AWS发布的向量进行检查的。HTTP区域的签名链接的令牌形式(密钥+路径+过期的SHA-256, url安全的base64)是Yomizaka对Bunny的拉区运行的令牌形式;Bunny的文档没有为它发布预期的输出。
import { createStorage, defineLayout, folderStore } from "@krizaka/storage";
import { limitsOf, readStorageSettings, storeFromSettings } from "@krizaka/storage/settings";
const settings = await readStorageSettings((key) => service.get(key, ctx));
const storage = createStorage({
store: storeFromSettings(settings, { accessKeyId: env.MEDIA_STORAGE_ACCESS_KEY_ID, secretAccessKey: env.MEDIA_STORAGE_SECRET }, { fetch, clock }),
layout: defineLayout(["people/", "site/home/"]),
limits: () => readStorageSettings(read).then(limitsOf),
});
const { key, url } = await storage.upload({ prefix: "people/", name: "ana.jpg", bytes, allow: ["image/jpeg", "image/png"], declaredType: file.type });
apache - 2.0。