core: storage: use alternative client

This commit is contained in:
undefined committed 2023-02-10 17:46:47 +08:00
1 parent 65311529bd
commit f30a39593f
1 file changed
+41 -40
+41 -40
View File
@@ -20,17 +20,6 @@ import { md5, streamToBuffer } from '../utils';
const logger = new Logger('storage'); const logger = new Logger('storage');
interface StorageOptions {
endPoint: string;
accessKey: string;
secretKey: string;
bucket: string;
region?: string;
pathStyle: boolean;
endPointForUser?: string;
endPointForJudge?: string;
}
function parseAlternativeEndpointUrl(endpoint: string): (originalUrl: string) => string { function parseAlternativeEndpointUrl(endpoint: string): (originalUrl: string) => string {
if (!endpoint) return (originalUrl) => originalUrl; if (!endpoint) return (originalUrl) => originalUrl;
const pathonly = endpoint.startsWith('/'); const pathonly = endpoint.startsWith('/');
@@ -71,8 +60,12 @@ const convertPath = (p: string) => {
class RemoteStorageService { class RemoteStorageService {
public client: S3Client; public client: S3Client;
public error = ''; public error = '';
public opts: StorageOptions; public bucket = 'hydro';
private replaceWithAlternativeUrlFor: Record<'user' | 'judge', (originalUrl: string) => string>; private replaceWithAlternativeUrlFor: Record<'user' | 'judge', (originalUrl: string) => string>;
private alternatives: Record<'user' | 'judge', S3Client> = {
user: null,
judge: null,
};
async start() { async start() {
try { try {
@@ -87,29 +80,35 @@ class RemoteStorageService {
endPointForUser, endPointForUser,
endPointForJudge, endPointForJudge,
} = builtinConfig.file; } = builtinConfig.file;
this.opts = { this.bucket = bucket;
endPoint, const base = {
accessKey,
secretKey,
bucket,
region, region,
pathStyle, forcePathStyle: pathStyle,
endPointForUser, credentials: {
endPointForJudge, accessKeyId: accessKey,
secretAccessKey: secretKey,
},
}; };
this.client = new S3Client({ this.client = new S3Client({
endpoint: this.opts.endPoint, endpoint: endPoint,
region: this.opts.region, ...base,
forcePathStyle: this.opts.pathStyle,
credentials: {
accessKeyId: this.opts.accessKey,
secretAccessKey: this.opts.secretKey,
},
}); });
this.replaceWithAlternativeUrlFor = { this.replaceWithAlternativeUrlFor = {
user: parseAlternativeEndpointUrl(this.opts.endPointForUser), user: parseAlternativeEndpointUrl(endPointForUser),
judge: parseAlternativeEndpointUrl(this.opts.endPointForJudge), judge: parseAlternativeEndpointUrl(endPointForJudge),
}; };
if (/^https?:\/\//.test(endPointForUser)) {
this.alternatives.user = new S3Client({
endpoint: endPointForUser,
...base,
});
}
if (/^https?:\/\//.test(endPointForJudge)) {
this.alternatives.judge = new S3Client({
endpoint: endPointForJudge,
...base,
});
}
logger.success('Storage connected.'); logger.success('Storage connected.');
this.error = null; this.error = null;
} catch (e) { } catch (e) {
@@ -124,7 +123,7 @@ class RemoteStorageService {
target = convertPath(target); target = convertPath(target);
if (typeof file === 'string') file = createReadStream(file); if (typeof file === 'string') file = createReadStream(file);
const params: PutObjectCommandInput = { const params: PutObjectCommandInput = {
Bucket: this.opts.bucket, Bucket: this.bucket,
Key: target, Key: target,
Body: file, Body: file,
Metadata: meta, Metadata: meta,
@@ -148,7 +147,7 @@ class RemoteStorageService {
async get(target: string, path?: string) { async get(target: string, path?: string) {
target = convertPath(target); target = convertPath(target);
const res = await this.client.send(new GetObjectCommand({ const res = await this.client.send(new GetObjectCommand({
Bucket: this.opts.bucket, Bucket: this.bucket,
Key: target, Key: target,
})); }));
if (!res.Body) throw new Error(); if (!res.Body) throw new Error();
@@ -175,12 +174,12 @@ class RemoteStorageService {
else target = target.map(convertPath); else target = target.map(convertPath);
if (typeof target === 'string') { if (typeof target === 'string') {
return await this.client.send(new DeleteObjectCommand({ return await this.client.send(new DeleteObjectCommand({
Bucket: this.opts.bucket, Bucket: this.bucket,
Key: target, Key: target,
})); }));
} }
return await this.client.send(new DeleteObjectsCommand({ return await this.client.send(new DeleteObjectsCommand({
Bucket: this.opts.bucket, Bucket: this.bucket,
Delete: { Delete: {
Objects: target.map((i) => ({ Key: i })), Objects: target.map((i) => ({ Key: i })),
}, },
@@ -190,7 +189,7 @@ class RemoteStorageService {
async getMeta(target: string) { async getMeta(target: string) {
target = convertPath(target); target = convertPath(target);
const res = await this.client.send(new HeadObjectCommand({ const res = await this.client.send(new HeadObjectCommand({
Bucket: this.opts.bucket, Bucket: this.bucket,
Key: target, Key: target,
})); }));
return { return {
@@ -203,8 +202,9 @@ class RemoteStorageService {
async signDownloadLink(target: string, filename?: string, noExpire = false, useAlternativeEndpointFor?: 'user' | 'judge'): Promise<string> { async signDownloadLink(target: string, filename?: string, noExpire = false, useAlternativeEndpointFor?: 'user' | 'judge'): Promise<string> {
target = convertPath(target); target = convertPath(target);
const url = await getSignedUrl(this.client, new GetObjectCommand({ const client = this.alternatives[useAlternativeEndpointFor] || this.client;
Bucket: this.opts.bucket, const url = await getSignedUrl(client, new GetObjectCommand({
Bucket: this.bucket,
Key: target, Key: target,
ResponseContentDisposition: filename ? `attachment; filename="${encodeRFC5987ValueChars(filename)}"` : '', ResponseContentDisposition: filename ? `attachment; filename="${encodeRFC5987ValueChars(filename)}"` : '',
}), { }), {
@@ -215,13 +215,14 @@ class RemoteStorageService {
} }
async signUpload(target: string, size: number) { async signUpload(target: string, size: number) {
const { url, fields } = await createPresignedPost(this.client, { const client = this.alternatives.user || this.client;
Bucket: this.opts.bucket, const { url, fields } = await createPresignedPost(client, {
Bucket: this.bucket,
Key: target, Key: target,
Conditions: [ Conditions: [
{ $key: target }, { $key: target },
{ acl: 'public-read' }, { acl: 'public-read' },
{ bucket: this.opts.bucket }, { bucket: this.bucket },
['content-length-range', size - 50, size + 50], ['content-length-range', size - 50, size + 50],
], ],
Fields: { Fields: {
@@ -239,7 +240,7 @@ class RemoteStorageService {
src = convertPath(src); src = convertPath(src);
target = convertPath(target); target = convertPath(target);
return await this.client.send(new CopyObjectCommand({ return await this.client.send(new CopyObjectCommand({
Bucket: this.opts.bucket, Bucket: this.bucket,
Key: target, Key: target,
CopySource: src, CopySource: src,
})); }));