Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | 3x 3x 4x 10x 5x 5x 5x 3x 3x 2x 2x 2x 2x 2x 2x 2x 10x 10x 15x 15x | import { S3Client } from "@aws-sdk/client-s3";
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
export type AttachmentBucketMap = Record<string, string>;
const NO_MAP_CONFIGURED = "__NO_MAP_CONFIGURED__";
let cachedAttachmentBucketMap: AttachmentBucketMap | undefined;
let cachedAttachmentBucketMapRaw = NO_MAP_CONFIGURED;
export function isLegacyUploadBucket(bucket: string): boolean {
return bucket.startsWith("uploads");
}
export function parseAttachmentBucketMap(rawMap?: string): AttachmentBucketMap {
if (cachedAttachmentBucketMap && cachedAttachmentBucketMapRaw === (rawMap ?? NO_MAP_CONFIGURED)) {
return cachedAttachmentBucketMap;
}
cachedAttachmentBucketMapRaw = rawMap ?? NO_MAP_CONFIGURED;
if (!rawMap) {
cachedAttachmentBucketMap = {};
return cachedAttachmentBucketMap;
}
const parsedMap: unknown = JSON.parse(rawMap);
Iif (typeof parsedMap !== "object" || parsedMap === null || Array.isArray(parsedMap)) {
throw new Error("LEGACY_ATTACHMENT_BUCKET_MAP must be a JSON object");
}
cachedAttachmentBucketMap = Object.entries(parsedMap).reduce<AttachmentBucketMap>(
(acc, [sourceBucket, destinationBucket]) => {
Iif (
typeof sourceBucket !== "string" ||
sourceBucket.length === 0 ||
typeof destinationBucket !== "string" ||
destinationBucket.length === 0
) {
throw new Error("LEGACY_ATTACHMENT_BUCKET_MAP must map non-empty strings");
}
acc[sourceBucket] = destinationBucket;
return acc;
},
{},
);
return cachedAttachmentBucketMap;
}
export function getAttachmentBucketMap(rawMap?: string, onInvalid?: (message: string) => void) {
try {
return parseAttachmentBucketMap(rawMap);
} catch (error) {
onInvalid?.(error instanceof Error ? error.message : String(error));
return {};
}
}
export function resolveTargetBucket(
sourceBucket: string,
attachmentBucketMap: AttachmentBucketMap,
) {
const destinationBucket = attachmentBucketMap[sourceBucket] || sourceBucket;
return {
sourceBucket,
destinationBucket,
remapped: destinationBucket !== sourceBucket,
};
}
export function createAttachmentBucketClientFactory({
region,
legacyS3AccessRoleArn,
}: {
region?: string;
legacyS3AccessRoleArn?: string;
}) {
const clientCache = new Map<string, Promise<S3Client>>();
const stsClient = new STSClient({ region });
return async (bucket: string) => {
const cachedClient = clientCache.get(bucket);
if (cachedClient) {
return cachedClient;
}
const clientPromise = (async () => {
if (!isLegacyUploadBucket(bucket) || !legacyS3AccessRoleArn) {
return new S3Client({ region });
}
const assumedRoleResponse = await stsClient.send(
new AssumeRoleCommand({
RoleArn: legacyS3AccessRoleArn,
RoleSessionName: "AttachmentArchiveLegacyS3Access",
}),
);
const assumedCredentials = assumedRoleResponse.Credentials;
if (!assumedCredentials) {
throw new Error("No assumed credentials returned for legacy S3 access role");
}
return new S3Client({
region,
credentials: {
accessKeyId: assumedCredentials.AccessKeyId as string,
secretAccessKey: assumedCredentials.SecretAccessKey as string,
sessionToken: assumedCredentials.SessionToken,
},
});
})();
clientCache.set(bucket, clientPromise);
return clientPromise;
};
}
|