mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(server): separate s3 & r2 storage to plugin (#5805)
This commit is contained in:
@@ -29,6 +29,7 @@ import { MailModule } from './fundamentals/mailer';
|
|||||||
import { MetricsModule } from './fundamentals/metrics';
|
import { MetricsModule } from './fundamentals/metrics';
|
||||||
import { PrismaModule } from './fundamentals/prisma';
|
import { PrismaModule } from './fundamentals/prisma';
|
||||||
import { SessionModule } from './fundamentals/session';
|
import { SessionModule } from './fundamentals/session';
|
||||||
|
import { StorageProviderModule } from './fundamentals/storage';
|
||||||
import { RateLimiterModule } from './fundamentals/throttler';
|
import { RateLimiterModule } from './fundamentals/throttler';
|
||||||
import { WebSocketModule } from './fundamentals/websocket';
|
import { WebSocketModule } from './fundamentals/websocket';
|
||||||
import { pluginsMap } from './plugins';
|
import { pluginsMap } from './plugins';
|
||||||
@@ -43,6 +44,7 @@ export const FunctionalityModules = [
|
|||||||
RateLimiterModule,
|
RateLimiterModule,
|
||||||
SessionModule,
|
SessionModule,
|
||||||
MailModule,
|
MailModule,
|
||||||
|
StorageProviderModule,
|
||||||
];
|
];
|
||||||
|
|
||||||
export class AppModuleBuilder {
|
export class AppModuleBuilder {
|
||||||
|
|||||||
@@ -20,19 +20,19 @@ const env = process.env;
|
|||||||
AFFiNE.metrics.enabled = !AFFiNE.node.test;
|
AFFiNE.metrics.enabled = !AFFiNE.node.test;
|
||||||
|
|
||||||
if (env.R2_OBJECT_STORAGE_ACCOUNT_ID) {
|
if (env.R2_OBJECT_STORAGE_ACCOUNT_ID) {
|
||||||
AFFiNE.storage.providers.r2 = {
|
AFFiNE.plugins.use('cloudflare-r2', {
|
||||||
accountId: env.R2_OBJECT_STORAGE_ACCOUNT_ID,
|
accountId: env.R2_OBJECT_STORAGE_ACCOUNT_ID,
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: env.R2_OBJECT_STORAGE_ACCESS_KEY_ID!,
|
accessKeyId: env.R2_OBJECT_STORAGE_ACCESS_KEY_ID!,
|
||||||
secretAccessKey: env.R2_OBJECT_STORAGE_SECRET_ACCESS_KEY!,
|
secretAccessKey: env.R2_OBJECT_STORAGE_SECRET_ACCESS_KEY!,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
AFFiNE.storage.storages.avatar.provider = 'r2';
|
AFFiNE.storage.storages.avatar.provider = 'cloudflare-r2';
|
||||||
AFFiNE.storage.storages.avatar.bucket = 'account-avatar';
|
AFFiNE.storage.storages.avatar.bucket = 'account-avatar';
|
||||||
AFFiNE.storage.storages.avatar.publicLinkFactory = key =>
|
AFFiNE.storage.storages.avatar.publicLinkFactory = key =>
|
||||||
`https://avatar.affineassets.com/${key}`;
|
`https://avatar.affineassets.com/${key}`;
|
||||||
|
|
||||||
AFFiNE.storage.storages.blob.provider = 'r2';
|
AFFiNE.storage.storages.blob.provider = 'cloudflare-r2';
|
||||||
AFFiNE.storage.storages.blob.bucket = `workspace-blobs-${
|
AFFiNE.storage.storages.blob.bucket = `workspace-blobs-${
|
||||||
AFFiNE.affine.canary ? 'canary' : 'prod'
|
AFFiNE.affine.canary ? 'canary' : 'prod'
|
||||||
}`;
|
}`;
|
||||||
|
|||||||
@@ -87,8 +87,31 @@ AFFiNE.port = 3010;
|
|||||||
AFFiNE.plugins.use('redis', {
|
AFFiNE.plugins.use('redis', {
|
||||||
/* override options */
|
/* override options */
|
||||||
});
|
});
|
||||||
|
//
|
||||||
|
//
|
||||||
// /* Payment Plugin */
|
// /* Payment Plugin */
|
||||||
AFFiNE.plugins.use('payment', {
|
AFFiNE.plugins.use('payment', {
|
||||||
stripe: { keys: {}, apiVersion: '2023-10-16' },
|
stripe: { keys: {}, apiVersion: '2023-10-16' },
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
|
//
|
||||||
|
// /* Cloudflare R2 Plugin */
|
||||||
|
// /* Enable if you choose to store workspace blobs or user avatars in Cloudflare R2 Storage Service */
|
||||||
|
// AFFiNE.plugins.use('cloudflare-r2', {
|
||||||
|
// accountId: '',
|
||||||
|
// credentials: {
|
||||||
|
// accessKeyId: '',
|
||||||
|
// secretAccessKey: '',
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// /* AWS S3 Plugin */
|
||||||
|
// /* Enable if you choose to store workspace blobs or user avatars in AWS S3 Storage Service */
|
||||||
|
// AFFiNE.plugins.use('aws-s3', {
|
||||||
|
// credentials: {
|
||||||
|
// accessKeyId: '',
|
||||||
|
// secretAccessKey: '',
|
||||||
|
// })
|
||||||
|
// /* Update the provider of storages */
|
||||||
|
// AFFiNE.storage.storages.blob.provider = 'r2';
|
||||||
|
// AFFiNE.storage.storages.avatar.provider = 'r2';
|
||||||
|
|||||||
@@ -6,15 +6,18 @@ import type {
|
|||||||
PutObjectMetadata,
|
PutObjectMetadata,
|
||||||
StorageProvider,
|
StorageProvider,
|
||||||
} from '../../../fundamentals';
|
} from '../../../fundamentals';
|
||||||
import { Config, createStorageProvider, OnEvent } from '../../../fundamentals';
|
import { Config, OnEvent, StorageProviderFactory } from '../../../fundamentals';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AvatarStorage {
|
export class AvatarStorage {
|
||||||
public readonly provider: StorageProvider;
|
public readonly provider: StorageProvider;
|
||||||
private readonly storageConfig: Config['storage']['storages']['avatar'];
|
private readonly storageConfig: Config['storage']['storages']['avatar'];
|
||||||
|
|
||||||
constructor(private readonly config: Config) {
|
constructor(
|
||||||
this.provider = createStorageProvider(this.config.storage, 'avatar');
|
private readonly config: Config,
|
||||||
|
private readonly storageFactory: StorageProviderFactory
|
||||||
|
) {
|
||||||
|
this.provider = this.storageFactory.create('avatar');
|
||||||
this.storageConfig = this.config.storage.storages.avatar;
|
this.storageConfig = this.config.storage.storages.avatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import type {
|
|||||||
StorageProvider,
|
StorageProvider,
|
||||||
} from '../../../fundamentals';
|
} from '../../../fundamentals';
|
||||||
import {
|
import {
|
||||||
Config,
|
|
||||||
createStorageProvider,
|
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
OnEvent,
|
OnEvent,
|
||||||
|
StorageProviderFactory,
|
||||||
} from '../../../fundamentals';
|
} from '../../../fundamentals';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -18,9 +17,9 @@ export class WorkspaceBlobStorage {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly event: EventEmitter,
|
private readonly event: EventEmitter,
|
||||||
private readonly config: Config
|
private readonly storageFactory: StorageProviderFactory
|
||||||
) {
|
) {
|
||||||
this.provider = createStorageProvider(this.config.storage, 'blob');
|
this.provider = this.storageFactory.create('blob');
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(workspaceId: string, key: string, blob: BlobInputType) {
|
async put(workspaceId: string, key: string, blob: BlobInputType) {
|
||||||
|
|||||||
@@ -1,37 +1,34 @@
|
|||||||
import { homedir } from 'node:os';
|
import { homedir } from 'node:os';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
|
|
||||||
import { S3ClientConfigType } from '@aws-sdk/client-s3';
|
|
||||||
|
|
||||||
export type StorageProviderType = 'fs' | 'r2' | 's3';
|
|
||||||
export interface FsStorageConfig {
|
export interface FsStorageConfig {
|
||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
export type R2StorageConfig = S3ClientConfigType & {
|
|
||||||
accountId: string;
|
|
||||||
};
|
|
||||||
export type S3StorageConfig = S3ClientConfigType;
|
|
||||||
|
|
||||||
export type StorageTargetConfig<Ext = unknown> = {
|
export interface StorageProvidersConfig {
|
||||||
|
fs: FsStorageConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StorageProviderType = keyof StorageProvidersConfig;
|
||||||
|
|
||||||
|
export type StorageConfig<Ext = unknown> = {
|
||||||
provider: StorageProviderType;
|
provider: StorageProviderType;
|
||||||
bucket: string;
|
bucket: string;
|
||||||
} & Ext;
|
} & Ext;
|
||||||
|
|
||||||
|
export interface StoragesConfig {
|
||||||
|
avatar: StorageConfig<{ publicLinkFactory: (key: string) => string }>;
|
||||||
|
blob: StorageConfig;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AFFiNEStorageConfig {
|
export interface AFFiNEStorageConfig {
|
||||||
/**
|
/**
|
||||||
* All providers for object storage
|
* All providers for object storage
|
||||||
*
|
*
|
||||||
* Support different providers for different usage at the same time.
|
* Support different providers for different usage at the same time.
|
||||||
*/
|
*/
|
||||||
providers: {
|
providers: StorageProvidersConfig;
|
||||||
fs?: FsStorageConfig;
|
storages: StoragesConfig;
|
||||||
s3?: S3StorageConfig;
|
|
||||||
r2?: R2StorageConfig;
|
|
||||||
};
|
|
||||||
storages: {
|
|
||||||
avatar: StorageTargetConfig<{ publicLinkFactory: (key: string) => string }>;
|
|
||||||
blob: StorageTargetConfig;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StorageProviders = AFFiNEStorageConfig['providers'];
|
export type StorageProviders = AFFiNEStorageConfig['providers'];
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export {
|
|||||||
export { PrismaService } from './prisma';
|
export { PrismaService } from './prisma';
|
||||||
export { SessionService } from './session';
|
export { SessionService } from './session';
|
||||||
export * from './storage';
|
export * from './storage';
|
||||||
|
export { type StorageProvider, StorageProviderFactory } from './storage';
|
||||||
export { AuthThrottlerGuard, CloudThrottlerGuard, Throttle } from './throttler';
|
export { AuthThrottlerGuard, CloudThrottlerGuard, Throttle } from './throttler';
|
||||||
export {
|
export {
|
||||||
getRequestFromHost,
|
getRequestFromHost,
|
||||||
|
|||||||
@@ -1,36 +1,24 @@
|
|||||||
import { createRequire } from 'node:module';
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
|
||||||
export const StorageProvide = Symbol('Storage');
|
import { registerStorageProvider, StorageProviderFactory } from './providers';
|
||||||
|
import { FsStorageProvider } from './providers/fs';
|
||||||
|
|
||||||
let storageModule: typeof import('@affine/storage');
|
registerStorageProvider('fs', (config, bucket) => {
|
||||||
try {
|
if (!config.storage.providers.fs) {
|
||||||
storageModule = await import('@affine/storage');
|
throw new Error('Missing fs storage provider configuration');
|
||||||
} catch {
|
}
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
storageModule =
|
|
||||||
process.arch === 'arm64'
|
|
||||||
? require('../../../storage.arm64.node')
|
|
||||||
: process.arch === 'arm'
|
|
||||||
? require('../../../storage.armv7.node')
|
|
||||||
: require('../../../storage.node');
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mergeUpdatesInApplyWay = storageModule.mergeUpdatesInApplyWay;
|
return new FsStorageProvider(config.storage.providers.fs, bucket);
|
||||||
|
});
|
||||||
|
|
||||||
export const verifyChallengeResponse = async (
|
@Global()
|
||||||
response: any,
|
@Module({
|
||||||
bits: number,
|
providers: [StorageProviderFactory],
|
||||||
resource: string
|
exports: [StorageProviderFactory],
|
||||||
) => {
|
})
|
||||||
if (typeof response !== 'string' || !response || !resource) return false;
|
export class StorageProviderModule {}
|
||||||
return storageModule.verifyChallengeResponse(response, bits, resource);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mintChallengeResponse = async (resource: string, bits: number) => {
|
|
||||||
if (!resource) return null;
|
|
||||||
return storageModule.mintChallengeResponse(resource, bits);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export * from './native';
|
||||||
export type {
|
export type {
|
||||||
BlobInputType,
|
BlobInputType,
|
||||||
BlobOutputType,
|
BlobOutputType,
|
||||||
@@ -39,5 +27,5 @@ export type {
|
|||||||
PutObjectMetadata,
|
PutObjectMetadata,
|
||||||
StorageProvider,
|
StorageProvider,
|
||||||
} from './providers';
|
} from './providers';
|
||||||
export { createStorageProvider } from './providers';
|
export { registerStorageProvider, StorageProviderFactory } from './providers';
|
||||||
export { toBuffer } from './providers/utils';
|
export { autoMetadata, toBuffer } from './providers/utils';
|
||||||
|
|||||||
30
packages/backend/server/src/fundamentals/storage/native.ts
Normal file
30
packages/backend/server/src/fundamentals/storage/native.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { createRequire } from 'node:module';
|
||||||
|
|
||||||
|
let storageModule: typeof import('@affine/storage');
|
||||||
|
try {
|
||||||
|
storageModule = await import('@affine/storage');
|
||||||
|
} catch {
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
storageModule =
|
||||||
|
process.arch === 'arm64'
|
||||||
|
? require('../../../storage.arm64.node')
|
||||||
|
: process.arch === 'arm'
|
||||||
|
? require('../../../storage.armv7.node')
|
||||||
|
: require('../../../storage.node');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mergeUpdatesInApplyWay = storageModule.mergeUpdatesInApplyWay;
|
||||||
|
|
||||||
|
export const verifyChallengeResponse = async (
|
||||||
|
response: any,
|
||||||
|
bits: number,
|
||||||
|
resource: string
|
||||||
|
) => {
|
||||||
|
if (typeof response !== 'string' || !response || !resource) return false;
|
||||||
|
return storageModule.verifyChallengeResponse(response, bits, resource);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mintChallengeResponse = async (resource: string, bits: number) => {
|
||||||
|
if (!resource) return null;
|
||||||
|
return storageModule.mintChallengeResponse(resource, bits);
|
||||||
|
};
|
||||||
@@ -1,34 +1,37 @@
|
|||||||
import { AFFiNEStorageConfig, Storages } from '../../config/storage';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { FsStorageProvider } from './fs';
|
|
||||||
|
import { Config } from '../../config';
|
||||||
|
import type { StorageProviderType, Storages } from '../../config/storage';
|
||||||
import type { StorageProvider } from './provider';
|
import type { StorageProvider } from './provider';
|
||||||
import { R2StorageProvider } from './r2';
|
|
||||||
import { S3StorageProvider } from './s3';
|
|
||||||
|
|
||||||
export function createStorageProvider(
|
const availableProviders = new Map<
|
||||||
config: AFFiNEStorageConfig,
|
StorageProviderType,
|
||||||
storage: Storages
|
(config: Config, bucket: string) => StorageProvider
|
||||||
): StorageProvider {
|
>();
|
||||||
const storageConfig = config.storages[storage];
|
|
||||||
const providerConfig = config.providers[storageConfig.provider] as any;
|
export function registerStorageProvider(
|
||||||
if (!providerConfig) {
|
type: StorageProviderType,
|
||||||
throw new Error(
|
providerFactory: (config: Config, bucket: string) => StorageProvider
|
||||||
`Failed to create ${storageConfig.provider} storage, configuration not correctly set`
|
) {
|
||||||
);
|
availableProviders.set(type, providerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StorageProviderFactory {
|
||||||
|
constructor(private readonly config: Config) {}
|
||||||
|
|
||||||
|
create(storage: Storages): StorageProvider {
|
||||||
|
const storageConfig = this.config.storage.storages[storage];
|
||||||
|
const providerFactory = availableProviders.get(storageConfig.provider);
|
||||||
|
|
||||||
|
if (!providerFactory) {
|
||||||
|
throw new Error(
|
||||||
|
`Unknown storage provider type: ${storageConfig.provider}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerFactory(this.config, storageConfig.bucket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storageConfig.provider === 's3') {
|
|
||||||
return new S3StorageProvider(providerConfig, storageConfig.bucket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storageConfig.provider === 'r2') {
|
|
||||||
return new R2StorageProvider(providerConfig, storageConfig.bucket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (storageConfig.provider === 'fs') {
|
|
||||||
return new FsStorageProvider(providerConfig, storageConfig.bucket);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Unknown storage provider type: ${storageConfig.provider}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type * from './provider';
|
export type * from './provider';
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
import { GCloudConfig } from './gcloud/config';
|
import { GCloudConfig } from './gcloud/config';
|
||||||
import { PaymentConfig } from './payment';
|
import { PaymentConfig } from './payment';
|
||||||
import { RedisOptions } from './redis';
|
import { RedisOptions } from './redis';
|
||||||
|
import { R2StorageConfig, S3StorageConfig } from './storage';
|
||||||
|
|
||||||
declare module '../fundamentals/config' {
|
declare module '../fundamentals/config' {
|
||||||
interface PluginsConfig {
|
interface PluginsConfig {
|
||||||
readonly payment: PaymentConfig;
|
readonly payment: PaymentConfig;
|
||||||
readonly redis: RedisOptions;
|
readonly redis: RedisOptions;
|
||||||
readonly gcloud: GCloudConfig;
|
readonly gcloud: GCloudConfig;
|
||||||
|
readonly 'cloudflare-r2': R2StorageConfig;
|
||||||
|
readonly 'aws-s3': S3StorageConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AvailablePlugins = keyof PluginsConfig;
|
export type AvailablePlugins = keyof PluginsConfig;
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import type { AvailablePlugins } from '../fundamentals/config';
|
|||||||
import { GCloudModule } from './gcloud';
|
import { GCloudModule } from './gcloud';
|
||||||
import { PaymentModule } from './payment';
|
import { PaymentModule } from './payment';
|
||||||
import { RedisModule } from './redis';
|
import { RedisModule } from './redis';
|
||||||
|
import { AwsS3Module, CloudflareR2Module } from './storage';
|
||||||
|
|
||||||
export const pluginsMap = new Map<AvailablePlugins, AFFiNEModule>([
|
export const pluginsMap = new Map<AvailablePlugins, AFFiNEModule>([
|
||||||
['payment', PaymentModule],
|
['payment', PaymentModule],
|
||||||
['redis', RedisModule],
|
['redis', RedisModule],
|
||||||
['gcloud', GCloudModule],
|
['gcloud', GCloudModule],
|
||||||
|
['cloudflare-r2', CloudflareR2Module],
|
||||||
|
['aws-s3', AwsS3Module],
|
||||||
]);
|
]);
|
||||||
|
|||||||
40
packages/backend/server/src/plugins/storage/index.ts
Normal file
40
packages/backend/server/src/plugins/storage/index.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { OptionalModule } from '../../fundamentals';
|
||||||
|
import { registerStorageProvider } from '../../fundamentals/storage';
|
||||||
|
import { R2StorageProvider } from './providers/r2';
|
||||||
|
import { S3StorageProvider } from './providers/s3';
|
||||||
|
|
||||||
|
registerStorageProvider('cloudflare-r2', (config, bucket) => {
|
||||||
|
if (!config.plugins['cloudflare-r2']) {
|
||||||
|
throw new Error('Missing cloudflare-r2 storage provider configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new R2StorageProvider(config.plugins['cloudflare-r2'], bucket);
|
||||||
|
});
|
||||||
|
registerStorageProvider('aws-s3', (config, bucket) => {
|
||||||
|
if (!config.plugins['aws-s3']) {
|
||||||
|
throw new Error('Missing aws-s3 storage provider configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new S3StorageProvider(config.plugins['aws-s3'], bucket);
|
||||||
|
});
|
||||||
|
|
||||||
|
@OptionalModule({
|
||||||
|
requires: [
|
||||||
|
'plugins.cloudflare-r2.accountId',
|
||||||
|
'plugins.cloudflare-r2.credentials.accessKeyId',
|
||||||
|
'plugins.cloudflare-r2.credentials.secretAccessKey',
|
||||||
|
],
|
||||||
|
if: config => config.flavor.graphql,
|
||||||
|
})
|
||||||
|
export class CloudflareR2Module {}
|
||||||
|
|
||||||
|
@OptionalModule({
|
||||||
|
requires: [
|
||||||
|
'plugins.aws-s3.credentials.accessKeyId',
|
||||||
|
'plugins.aws-s3.credentials.secretAccessKey',
|
||||||
|
],
|
||||||
|
if: config => config.flavor.graphql,
|
||||||
|
})
|
||||||
|
export class AwsS3Module {}
|
||||||
|
|
||||||
|
export type { R2StorageConfig, S3StorageConfig } from './types';
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
import { R2StorageConfig } from '../../config/storage';
|
import type { R2StorageConfig } from '../types';
|
||||||
import { S3StorageProvider } from './s3';
|
import { S3StorageProvider } from './s3';
|
||||||
|
|
||||||
export class R2StorageProvider extends S3StorageProvider {
|
export class R2StorageProvider extends S3StorageProvider {
|
||||||
override readonly type = 'r2' as any /* cast 'r2' to 's3' */;
|
override readonly type = 'cloudflare-r2' as any /* cast 'r2' to 's3' */;
|
||||||
|
|
||||||
constructor(config: R2StorageConfig, bucket: string) {
|
constructor(config: R2StorageConfig, bucket: string) {
|
||||||
super(
|
super(
|
||||||
@@ -11,21 +11,22 @@ import {
|
|||||||
} from '@aws-sdk/client-s3';
|
} from '@aws-sdk/client-s3';
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
import { S3StorageConfig } from '../../config/storage';
|
|
||||||
import {
|
import {
|
||||||
|
autoMetadata,
|
||||||
BlobInputType,
|
BlobInputType,
|
||||||
GetObjectMetadata,
|
GetObjectMetadata,
|
||||||
ListObjectsMetadata,
|
ListObjectsMetadata,
|
||||||
PutObjectMetadata,
|
PutObjectMetadata,
|
||||||
StorageProvider,
|
StorageProvider,
|
||||||
} from './provider';
|
toBuffer,
|
||||||
import { autoMetadata, toBuffer } from './utils';
|
} from '../../../fundamentals/storage';
|
||||||
|
import type { S3StorageConfig } from '../types';
|
||||||
|
|
||||||
export class S3StorageProvider implements StorageProvider {
|
export class S3StorageProvider implements StorageProvider {
|
||||||
protected logger: Logger;
|
protected logger: Logger;
|
||||||
protected client: S3Client;
|
protected client: S3Client;
|
||||||
|
|
||||||
readonly type = 's3';
|
readonly type = 'aws-s3';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: S3StorageConfig,
|
config: S3StorageConfig,
|
||||||
16
packages/backend/server/src/plugins/storage/types.ts
Normal file
16
packages/backend/server/src/plugins/storage/types.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { S3ClientConfigType } from '@aws-sdk/client-s3';
|
||||||
|
|
||||||
|
type WARNING = '__YOU_SHOULD_NOT_MANUALLY_CONFIGURATE_THIS_TYPE__';
|
||||||
|
export type R2StorageConfig = S3ClientConfigType & {
|
||||||
|
accountId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type S3StorageConfig = S3ClientConfigType;
|
||||||
|
|
||||||
|
declare module '../../fundamentals/config/storage' {
|
||||||
|
interface StorageProvidersConfig {
|
||||||
|
// the type here is only existing for extends [StorageProviderType] with better type inference and checking.
|
||||||
|
'cloudflare-r2'?: WARNING;
|
||||||
|
'aws-s3'?: WARNING;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user