Files
papermark/lib/files/aws-client.ts
Marc Seitz 62a969bd83 feat: add team-aware multi-region S3 storage support
Introduces team-specific S3 storage configuration and multi-region support, allowing teams to use either EU or US S3 buckets based on feature flags. Refactors file operations (upload, copy, delete, presigned URLs, Lambda invocations) to resolve storage region and credentials per team. Adds new storage config and multi-region S3Store, updates API routes and utility functions to use team-aware clients, and extends feature flags with 'usStorage'.
2025-06-26 11:42:35 +02:00

110 lines
3.0 KiB
TypeScript

import {
type StorageConfig,
getStorageConfig,
getTeamStorageConfigById,
} from "@/ee/features/storage/config";
import { LambdaClient } from "@aws-sdk/client-lambda";
import { S3Client } from "@aws-sdk/client-s3";
export const getS3Client = (storageRegion?: string) => {
const NEXT_PUBLIC_UPLOAD_TRANSPORT = process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT;
if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== "s3") {
throw new Error("Invalid upload transport");
}
const config = getStorageConfig(storageRegion);
return new S3Client({
endpoint: config.endpoint || undefined,
region: config.region,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
});
};
export const getS3ClientForTeam = async (teamId: string) => {
const NEXT_PUBLIC_UPLOAD_TRANSPORT = process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT;
if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== "s3") {
throw new Error("Invalid upload transport");
}
const config = await getTeamStorageConfigById(teamId);
return new S3Client({
endpoint: config.endpoint || undefined,
region: config.region,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
});
};
export const getLambdaClient = (storageRegion?: string) => {
const NEXT_PUBLIC_UPLOAD_TRANSPORT = process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT;
if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== "s3") {
throw new Error("Invalid upload transport");
}
const config = getStorageConfig(storageRegion);
return new LambdaClient({
region: config.region,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
});
};
export const getLambdaClientForTeam = async (teamId: string) => {
const NEXT_PUBLIC_UPLOAD_TRANSPORT = process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT;
if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== "s3") {
throw new Error("Invalid upload transport");
}
const config = await getTeamStorageConfigById(teamId);
return new LambdaClient({
region: config.region,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
});
};
/**
* Gets both S3 client and storage config for a team in a single call.
* This is more efficient than calling getS3ClientForTeam and getTeamStorageConfigById separately.
*
* @param teamId - The team ID
* @returns Promise<{ client: S3Client, config: StorageConfig }> - Both client and config
*/
export const getTeamS3ClientAndConfig = async (teamId: string) => {
const NEXT_PUBLIC_UPLOAD_TRANSPORT = process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT;
if (NEXT_PUBLIC_UPLOAD_TRANSPORT !== "s3") {
throw new Error("Invalid upload transport");
}
const config = await getTeamStorageConfigById(teamId);
const client = new S3Client({
endpoint: config.endpoint || undefined,
region: config.region,
credentials: {
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
},
});
return { client, config };
};