8e870de9d7
Spec review (live negative test) found s3-request-presigner unconditionally unsigns content-type: header-less PUT returns 200, so the previous docblock claiming signature enforcement was a false guarantee. Content-Type is now documented as convention-only; head() returns contentType so the complete step (Task 19) can verify it server-side against the session declaration. Co-Authored-By: Claude Code <noreply@anthropic.com>
26 lines
1.4 KiB
TypeScript
26 lines
1.4 KiB
TypeScript
// packages/storage/src/port.ts —— 存储端口(唯一抽象):生产 OSS 适配器(P5/G1)
|
||
// 与本包 MinIO 适配器实现同一接口;api(presign/verify)与 worker(variants put/get)
|
||
// 只依赖此端口,不感知后端差异。
|
||
export interface UploadCredential {
|
||
mode: 'presigned';
|
||
url: string;
|
||
method: 'PUT';
|
||
headers: Record<string, string>;
|
||
expiresAt: string; // ISO
|
||
}
|
||
export interface StoragePort {
|
||
/**
|
||
* 预签名上传凭证。注意:headers 为「约定必须发送」而非签名强制——SigV4 预签名
|
||
* 不含 Content-Length,且 SDK 将 Content-Type 列为不可签名头。字节核验在
|
||
* complete 阶段经 head().bytes 比对,Content-Type 一致性经 head().contentType
|
||
* 与会话申明值比对(服务端兜底,见 Task 19)。
|
||
*/
|
||
presignPut(objectKey: string, bytes: number, contentType: string, ttlSeconds: number): Promise<UploadCredential>;
|
||
presignGet(objectKey: string, ttlSeconds: number): Promise<string>;
|
||
head(objectKey: string): Promise<{ bytes: number; etag: string; contentType: string } | null>;
|
||
getStream(objectKey: string): Promise<NodeJS.ReadableStream>;
|
||
put(objectKey: string, body: Buffer, contentType: string): Promise<{ etag: string }>;
|
||
/** 复制到正式键;目标已存在时校验一致性并幂等返回(禁止覆盖语义)。 */
|
||
copyIfAbsent(src: string, dest: string, expectedBytes: number): Promise<{ created: boolean; etag: string }>;
|
||
}
|