fix: 修复配额说明重复和undefined问题

- 在editStorageForm中初始化oss_storage_quota_value和oss_quota_unit
- 删除重复的旧配额说明块,保留新的当前配额设置显示

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 19:39:53 +08:00
commit 4350113979
7649 changed files with 897277 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
import { DEFAULT_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation } from "./constants";
import { SelectorType, stringUnionSelector } from "./stringUnionSelector";
export const ENV_REQUEST_CHECKSUM_CALCULATION = "AWS_REQUEST_CHECKSUM_CALCULATION";
export const CONFIG_REQUEST_CHECKSUM_CALCULATION = "request_checksum_calculation";
export const NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS = {
environmentVariableSelector: (env) => stringUnionSelector(env, ENV_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation, SelectorType.ENV),
configFileSelector: (profile) => stringUnionSelector(profile, CONFIG_REQUEST_CHECKSUM_CALCULATION, RequestChecksumCalculation, SelectorType.CONFIG),
default: DEFAULT_REQUEST_CHECKSUM_CALCULATION,
};

View File

@@ -0,0 +1,9 @@
import { DEFAULT_RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation } from "./constants";
import { SelectorType, stringUnionSelector } from "./stringUnionSelector";
export const ENV_RESPONSE_CHECKSUM_VALIDATION = "AWS_RESPONSE_CHECKSUM_VALIDATION";
export const CONFIG_RESPONSE_CHECKSUM_VALIDATION = "response_checksum_validation";
export const NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS = {
environmentVariableSelector: (env) => stringUnionSelector(env, ENV_RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation, SelectorType.ENV),
configFileSelector: (profile) => stringUnionSelector(profile, CONFIG_RESPONSE_CHECKSUM_VALIDATION, ResponseChecksumValidation, SelectorType.CONFIG),
default: DEFAULT_RESPONSE_CHECKSUM_VALIDATION,
};

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,25 @@
export const RequestChecksumCalculation = {
WHEN_SUPPORTED: "WHEN_SUPPORTED",
WHEN_REQUIRED: "WHEN_REQUIRED",
};
export const DEFAULT_REQUEST_CHECKSUM_CALCULATION = RequestChecksumCalculation.WHEN_SUPPORTED;
export const ResponseChecksumValidation = {
WHEN_SUPPORTED: "WHEN_SUPPORTED",
WHEN_REQUIRED: "WHEN_REQUIRED",
};
export const DEFAULT_RESPONSE_CHECKSUM_VALIDATION = RequestChecksumCalculation.WHEN_SUPPORTED;
export var ChecksumAlgorithm;
(function (ChecksumAlgorithm) {
ChecksumAlgorithm["MD5"] = "MD5";
ChecksumAlgorithm["CRC32"] = "CRC32";
ChecksumAlgorithm["CRC32C"] = "CRC32C";
ChecksumAlgorithm["CRC64NVME"] = "CRC64NVME";
ChecksumAlgorithm["SHA1"] = "SHA1";
ChecksumAlgorithm["SHA256"] = "SHA256";
})(ChecksumAlgorithm || (ChecksumAlgorithm = {}));
export var ChecksumLocation;
(function (ChecksumLocation) {
ChecksumLocation["HEADER"] = "header";
ChecksumLocation["TRAILER"] = "trailer";
})(ChecksumLocation || (ChecksumLocation = {}));
export const DEFAULT_CHECKSUM_ALGORITHM = ChecksumAlgorithm.CRC32;

View File

@@ -0,0 +1,37 @@
import { setFeature } from "@aws-sdk/core";
import { RequestChecksumCalculation, ResponseChecksumValidation } from "./constants";
export const flexibleChecksumsInputMiddlewareOptions = {
name: "flexibleChecksumsInputMiddleware",
toMiddleware: "serializerMiddleware",
relation: "before",
tags: ["BODY_CHECKSUM"],
override: true,
};
export const flexibleChecksumsInputMiddleware = (config, middlewareConfig) => (next, context) => async (args) => {
const input = args.input;
const { requestValidationModeMember } = middlewareConfig;
const requestChecksumCalculation = await config.requestChecksumCalculation();
const responseChecksumValidation = await config.responseChecksumValidation();
switch (requestChecksumCalculation) {
case RequestChecksumCalculation.WHEN_REQUIRED:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED", "a");
break;
case RequestChecksumCalculation.WHEN_SUPPORTED:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED", "Z");
break;
}
switch (responseChecksumValidation) {
case ResponseChecksumValidation.WHEN_REQUIRED:
setFeature(context, "FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED", "c");
break;
case ResponseChecksumValidation.WHEN_SUPPORTED:
setFeature(context, "FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED", "b");
break;
}
if (requestValidationModeMember && !input[requestValidationModeMember]) {
if (responseChecksumValidation === ResponseChecksumValidation.WHEN_SUPPORTED) {
input[requestValidationModeMember] = "ENABLED";
}
}
return next(args);
};

View File

@@ -0,0 +1,123 @@
import { setFeature } from "@aws-sdk/core";
import { HttpRequest } from "@smithy/protocol-http";
import { createBufferedReadable } from "@smithy/util-stream";
import { ChecksumAlgorithm, DEFAULT_CHECKSUM_ALGORITHM, RequestChecksumCalculation } from "./constants";
import { getChecksumAlgorithmForRequest } from "./getChecksumAlgorithmForRequest";
import { getChecksumLocationName } from "./getChecksumLocationName";
import { hasHeader } from "./hasHeader";
import { hasHeaderWithPrefix } from "./hasHeaderWithPrefix";
import { isStreaming } from "./isStreaming";
import { selectChecksumAlgorithmFunction } from "./selectChecksumAlgorithmFunction";
import { stringHasher } from "./stringHasher";
export const flexibleChecksumsMiddlewareOptions = {
name: "flexibleChecksumsMiddleware",
step: "build",
tags: ["BODY_CHECKSUM"],
override: true,
};
export const flexibleChecksumsMiddleware = (config, middlewareConfig) => (next, context) => async (args) => {
if (!HttpRequest.isInstance(args.request)) {
return next(args);
}
if (hasHeaderWithPrefix("x-amz-checksum-", args.request.headers)) {
return next(args);
}
const { request, input } = args;
const { body: requestBody, headers } = request;
const { base64Encoder, streamHasher } = config;
const { requestChecksumRequired, requestAlgorithmMember } = middlewareConfig;
const requestChecksumCalculation = await config.requestChecksumCalculation();
const requestAlgorithmMemberName = requestAlgorithmMember?.name;
const requestAlgorithmMemberHttpHeader = requestAlgorithmMember?.httpHeader;
if (requestAlgorithmMemberName && !input[requestAlgorithmMemberName]) {
if (requestChecksumCalculation === RequestChecksumCalculation.WHEN_SUPPORTED || requestChecksumRequired) {
input[requestAlgorithmMemberName] = DEFAULT_CHECKSUM_ALGORITHM;
if (requestAlgorithmMemberHttpHeader) {
headers[requestAlgorithmMemberHttpHeader] = DEFAULT_CHECKSUM_ALGORITHM;
}
}
}
const checksumAlgorithm = getChecksumAlgorithmForRequest(input, {
requestChecksumRequired,
requestAlgorithmMember: requestAlgorithmMember?.name,
requestChecksumCalculation,
});
let updatedBody = requestBody;
let updatedHeaders = headers;
if (checksumAlgorithm) {
switch (checksumAlgorithm) {
case ChecksumAlgorithm.CRC32:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_CRC32", "U");
break;
case ChecksumAlgorithm.CRC32C:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_CRC32C", "V");
break;
case ChecksumAlgorithm.CRC64NVME:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_CRC64", "W");
break;
case ChecksumAlgorithm.SHA1:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_SHA1", "X");
break;
case ChecksumAlgorithm.SHA256:
setFeature(context, "FLEXIBLE_CHECKSUMS_REQ_SHA256", "Y");
break;
}
const checksumLocationName = getChecksumLocationName(checksumAlgorithm);
const checksumAlgorithmFn = selectChecksumAlgorithmFunction(checksumAlgorithm, config);
if (isStreaming(requestBody)) {
const { getAwsChunkedEncodingStream, bodyLengthChecker } = config;
updatedBody = getAwsChunkedEncodingStream(typeof config.requestStreamBufferSize === "number" && config.requestStreamBufferSize >= 8 * 1024
? createBufferedReadable(requestBody, config.requestStreamBufferSize, context.logger)
: requestBody, {
base64Encoder,
bodyLengthChecker,
checksumLocationName,
checksumAlgorithmFn,
streamHasher,
});
updatedHeaders = {
...headers,
"content-encoding": headers["content-encoding"]
? `${headers["content-encoding"]},aws-chunked`
: "aws-chunked",
"transfer-encoding": "chunked",
"x-amz-decoded-content-length": headers["content-length"],
"x-amz-content-sha256": "STREAMING-UNSIGNED-PAYLOAD-TRAILER",
"x-amz-trailer": checksumLocationName,
};
delete updatedHeaders["content-length"];
}
else if (!hasHeader(checksumLocationName, headers)) {
const rawChecksum = await stringHasher(checksumAlgorithmFn, requestBody);
updatedHeaders = {
...headers,
[checksumLocationName]: base64Encoder(rawChecksum),
};
}
}
try {
const result = await next({
...args,
request: {
...request,
headers: updatedHeaders,
body: updatedBody,
},
});
return result;
}
catch (e) {
if (e instanceof Error && e.name === "InvalidChunkSizeError") {
try {
if (!e.message.endsWith(".")) {
e.message += ".";
}
e.message +=
" Set [requestStreamBufferSize=number e.g. 65_536] in client constructor to instruct AWS SDK to buffer your input stream.";
}
catch (ignored) {
}
}
throw e;
}
};

View File

@@ -0,0 +1,40 @@
import { HttpRequest } from "@smithy/protocol-http";
import { getChecksumAlgorithmListForResponse } from "./getChecksumAlgorithmListForResponse";
import { getChecksumLocationName } from "./getChecksumLocationName";
import { isChecksumWithPartNumber } from "./isChecksumWithPartNumber";
import { validateChecksumFromResponse } from "./validateChecksumFromResponse";
export const flexibleChecksumsResponseMiddlewareOptions = {
name: "flexibleChecksumsResponseMiddleware",
toMiddleware: "deserializerMiddleware",
relation: "after",
tags: ["BODY_CHECKSUM"],
override: true,
};
export const flexibleChecksumsResponseMiddleware = (config, middlewareConfig) => (next, context) => async (args) => {
if (!HttpRequest.isInstance(args.request)) {
return next(args);
}
const input = args.input;
const result = await next(args);
const response = result.response;
const { requestValidationModeMember, responseAlgorithms } = middlewareConfig;
if (requestValidationModeMember && input[requestValidationModeMember] === "ENABLED") {
const { clientName, commandName } = context;
const isS3WholeObjectMultipartGetResponseChecksum = clientName === "S3Client" &&
commandName === "GetObjectCommand" &&
getChecksumAlgorithmListForResponse(responseAlgorithms).every((algorithm) => {
const responseHeader = getChecksumLocationName(algorithm);
const checksumFromResponse = response.headers[responseHeader];
return !checksumFromResponse || isChecksumWithPartNumber(checksumFromResponse);
});
if (isS3WholeObjectMultipartGetResponseChecksum) {
return result;
}
await validateChecksumFromResponse(response, {
config,
responseAlgorithms,
logger: context.logger,
});
}
return result;
};

View File

@@ -0,0 +1,2 @@
import { stringHasher } from "./stringHasher";
export const getChecksum = async (body, { checksumAlgorithmFn, base64Encoder }) => base64Encoder(await stringHasher(checksumAlgorithmFn, body));

View File

@@ -0,0 +1,18 @@
import { DEFAULT_CHECKSUM_ALGORITHM, RequestChecksumCalculation } from "./constants";
import { CLIENT_SUPPORTED_ALGORITHMS } from "./types";
export const getChecksumAlgorithmForRequest = (input, { requestChecksumRequired, requestAlgorithmMember, requestChecksumCalculation }) => {
if (!requestAlgorithmMember) {
return requestChecksumCalculation === RequestChecksumCalculation.WHEN_SUPPORTED || requestChecksumRequired
? DEFAULT_CHECKSUM_ALGORITHM
: undefined;
}
if (!input[requestAlgorithmMember]) {
return undefined;
}
const checksumAlgorithm = input[requestAlgorithmMember];
if (!CLIENT_SUPPORTED_ALGORITHMS.includes(checksumAlgorithm)) {
throw new Error(`The checksum algorithm "${checksumAlgorithm}" is not supported by the client.` +
` Select one of ${CLIENT_SUPPORTED_ALGORITHMS}.`);
}
return checksumAlgorithm;
};

View File

@@ -0,0 +1,11 @@
import { CLIENT_SUPPORTED_ALGORITHMS, PRIORITY_ORDER_ALGORITHMS } from "./types";
export const getChecksumAlgorithmListForResponse = (responseAlgorithms = []) => {
const validChecksumAlgorithms = [];
for (const algorithm of PRIORITY_ORDER_ALGORITHMS) {
if (!responseAlgorithms.includes(algorithm) || !CLIENT_SUPPORTED_ALGORITHMS.includes(algorithm)) {
continue;
}
validChecksumAlgorithms.push(algorithm);
}
return validChecksumAlgorithms;
};

View File

@@ -0,0 +1,2 @@
import { ChecksumAlgorithm } from "./constants";
export const getChecksumLocationName = (algorithm) => algorithm === ChecksumAlgorithm.MD5 ? "content-md5" : `x-amz-checksum-${algorithm.toLowerCase()}`;

View File

@@ -0,0 +1,2 @@
import { AwsCrc32 } from "@aws-crypto/crc32";
export const getCrc32ChecksumAlgorithmFunction = () => AwsCrc32;

View File

@@ -0,0 +1,21 @@
import { AwsCrc32 } from "@aws-crypto/crc32";
import { numToUint8 } from "@aws-crypto/util";
import * as zlib from "zlib";
class NodeCrc32 {
checksum = 0;
update(data) {
this.checksum = zlib.crc32(data, this.checksum);
}
async digest() {
return numToUint8(this.checksum);
}
reset() {
this.checksum = 0;
}
}
export const getCrc32ChecksumAlgorithmFunction = () => {
if (typeof zlib.crc32 === "undefined") {
return AwsCrc32;
}
return NodeCrc32;
};

View File

@@ -0,0 +1,10 @@
import { flexibleChecksumsInputMiddleware, flexibleChecksumsInputMiddlewareOptions, } from "./flexibleChecksumsInputMiddleware";
import { flexibleChecksumsMiddleware, flexibleChecksumsMiddlewareOptions, } from "./flexibleChecksumsMiddleware";
import { flexibleChecksumsResponseMiddleware, flexibleChecksumsResponseMiddlewareOptions, } from "./flexibleChecksumsResponseMiddleware";
export const getFlexibleChecksumsPlugin = (config, middlewareConfig) => ({
applyToStack: (clientStack) => {
clientStack.add(flexibleChecksumsMiddleware(config, middlewareConfig), flexibleChecksumsMiddlewareOptions);
clientStack.addRelativeTo(flexibleChecksumsInputMiddleware(config, middlewareConfig), flexibleChecksumsInputMiddlewareOptions);
clientStack.addRelativeTo(flexibleChecksumsResponseMiddleware(config, middlewareConfig), flexibleChecksumsResponseMiddlewareOptions);
},
});

View File

@@ -0,0 +1,9 @@
export const hasHeader = (header, headers) => {
const soughtHeader = header.toLowerCase();
for (const headerName of Object.keys(headers)) {
if (soughtHeader === headerName.toLowerCase()) {
return true;
}
}
return false;
};

View File

@@ -0,0 +1,9 @@
export const hasHeaderWithPrefix = (headerPrefix, headers) => {
const soughtHeaderPrefix = headerPrefix.toLowerCase();
for (const headerName of Object.keys(headers)) {
if (headerName.toLowerCase().startsWith(soughtHeaderPrefix)) {
return true;
}
}
return false;
};

View File

@@ -0,0 +1,6 @@
export * from "./NODE_REQUEST_CHECKSUM_CALCULATION_CONFIG_OPTIONS";
export * from "./NODE_RESPONSE_CHECKSUM_VALIDATION_CONFIG_OPTIONS";
export * from "./constants";
export * from "./flexibleChecksumsMiddleware";
export * from "./getFlexibleChecksumsPlugin";
export * from "./resolveFlexibleChecksumsConfig";

View File

@@ -0,0 +1,13 @@
export const isChecksumWithPartNumber = (checksum) => {
const lastHyphenIndex = checksum.lastIndexOf("-");
if (lastHyphenIndex !== -1) {
const numberPart = checksum.slice(lastHyphenIndex + 1);
if (!numberPart.startsWith("0")) {
const number = parseInt(numberPart, 10);
if (!isNaN(number) && number >= 1 && number <= 10000) {
return true;
}
}
}
return false;
};

View File

@@ -0,0 +1,2 @@
import { isArrayBuffer } from "@smithy/is-array-buffer";
export const isStreaming = (body) => body !== undefined && typeof body !== "string" && !ArrayBuffer.isView(body) && !isArrayBuffer(body);

View File

@@ -0,0 +1,10 @@
import { normalizeProvider } from "@smithy/util-middleware";
import { DEFAULT_REQUEST_CHECKSUM_CALCULATION, DEFAULT_RESPONSE_CHECKSUM_VALIDATION, } from "./constants";
export const resolveFlexibleChecksumsConfig = (input) => {
const { requestChecksumCalculation, responseChecksumValidation, requestStreamBufferSize } = input;
return Object.assign(input, {
requestChecksumCalculation: normalizeProvider(requestChecksumCalculation ?? DEFAULT_REQUEST_CHECKSUM_CALCULATION),
responseChecksumValidation: normalizeProvider(responseChecksumValidation ?? DEFAULT_RESPONSE_CHECKSUM_VALIDATION),
requestStreamBufferSize: Number(requestStreamBufferSize ?? 0),
});
};

View File

@@ -0,0 +1,25 @@
import { AwsCrc32c } from "@aws-crypto/crc32c";
import { Crc64Nvme, crc64NvmeCrtContainer } from "@aws-sdk/crc64-nvme";
import { ChecksumAlgorithm } from "./constants";
import { getCrc32ChecksumAlgorithmFunction } from "./getCrc32ChecksumAlgorithmFunction";
export const selectChecksumAlgorithmFunction = (checksumAlgorithm, config) => {
switch (checksumAlgorithm) {
case ChecksumAlgorithm.MD5:
return config.md5;
case ChecksumAlgorithm.CRC32:
return getCrc32ChecksumAlgorithmFunction();
case ChecksumAlgorithm.CRC32C:
return AwsCrc32c;
case ChecksumAlgorithm.CRC64NVME:
if (typeof crc64NvmeCrtContainer.CrtCrc64Nvme !== "function") {
return Crc64Nvme;
}
return crc64NvmeCrtContainer.CrtCrc64Nvme;
case ChecksumAlgorithm.SHA1:
return config.sha1;
case ChecksumAlgorithm.SHA256:
return config.sha256;
default:
throw new Error(`Unsupported checksum algorithm: ${checksumAlgorithm}`);
}
};

View File

@@ -0,0 +1,6 @@
import { toUint8Array } from "@smithy/util-utf8";
export const stringHasher = (checksumAlgorithmFn, body) => {
const hash = new checksumAlgorithmFn();
hash.update(toUint8Array(body || ""));
return hash.digest();
};

View File

@@ -0,0 +1,14 @@
export var SelectorType;
(function (SelectorType) {
SelectorType["ENV"] = "env";
SelectorType["CONFIG"] = "shared config entry";
})(SelectorType || (SelectorType = {}));
export const stringUnionSelector = (obj, key, union, type) => {
if (!(key in obj))
return undefined;
const value = obj[key].toUpperCase();
if (!Object.values(union).includes(value)) {
throw new TypeError(`Cannot load ${type} '${key}'. Expected one of ${Object.values(union)}, got '${obj[key]}'.`);
}
return value;
};

View File

@@ -0,0 +1,15 @@
import { ChecksumAlgorithm } from "./constants";
export const CLIENT_SUPPORTED_ALGORITHMS = [
ChecksumAlgorithm.CRC32,
ChecksumAlgorithm.CRC32C,
ChecksumAlgorithm.CRC64NVME,
ChecksumAlgorithm.SHA1,
ChecksumAlgorithm.SHA256,
];
export const PRIORITY_ORDER_ALGORITHMS = [
ChecksumAlgorithm.SHA256,
ChecksumAlgorithm.SHA1,
ChecksumAlgorithm.CRC32,
ChecksumAlgorithm.CRC32C,
ChecksumAlgorithm.CRC64NVME,
];

View File

@@ -0,0 +1,45 @@
import { createChecksumStream } from "@smithy/util-stream";
import { ChecksumAlgorithm } from "./constants";
import { getChecksum } from "./getChecksum";
import { getChecksumAlgorithmListForResponse } from "./getChecksumAlgorithmListForResponse";
import { getChecksumLocationName } from "./getChecksumLocationName";
import { isStreaming } from "./isStreaming";
import { selectChecksumAlgorithmFunction } from "./selectChecksumAlgorithmFunction";
export const validateChecksumFromResponse = async (response, { config, responseAlgorithms, logger }) => {
const checksumAlgorithms = getChecksumAlgorithmListForResponse(responseAlgorithms);
const { body: responseBody, headers: responseHeaders } = response;
for (const algorithm of checksumAlgorithms) {
const responseHeader = getChecksumLocationName(algorithm);
const checksumFromResponse = responseHeaders[responseHeader];
if (checksumFromResponse) {
let checksumAlgorithmFn;
try {
checksumAlgorithmFn = selectChecksumAlgorithmFunction(algorithm, config);
}
catch (error) {
if (algorithm === ChecksumAlgorithm.CRC64NVME) {
logger?.warn(`Skipping ${ChecksumAlgorithm.CRC64NVME} checksum validation: ${error.message}`);
continue;
}
throw error;
}
const { base64Encoder } = config;
if (isStreaming(responseBody)) {
response.body = createChecksumStream({
expectedChecksum: checksumFromResponse,
checksumSourceLocation: responseHeader,
checksum: new checksumAlgorithmFn(),
source: responseBody,
base64Encoder,
});
return;
}
const checksum = await getChecksum(responseBody, { checksumAlgorithmFn, base64Encoder });
if (checksum === checksumFromResponse) {
break;
}
throw new Error(`Checksum mismatch: expected "${checksum}" but received "${checksumFromResponse}"` +
` in response header "${responseHeader}".`);
}
}
};