2023-07-27 07:31:52 +02:00
|
|
|
/*
|
2024-02-12 03:37:45 +01:00
|
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
2023-07-27 07:31:52 +02:00
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-09-17 20:27:08 +02:00
|
|
|
import { URL } from 'node:url';
|
2023-03-23 05:48:14 +01:00
|
|
|
import * as http from 'node:http';
|
|
|
|
import * as https from 'node:https';
|
2023-08-05 03:33:00 +02:00
|
|
|
import { Injectable } from '@nestjs/common';
|
2023-03-23 05:48:14 +01:00
|
|
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
|
|
import { Upload } from '@aws-sdk/lib-storage';
|
2023-09-15 07:28:29 +02:00
|
|
|
import { NodeHttpHandler, NodeHttpHandlerOptions } from '@smithy/node-http-handler';
|
2023-09-20 04:33:36 +02:00
|
|
|
import type { MiMeta } from '@/models/Meta.js';
|
2022-12-04 02:16:03 +01:00
|
|
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
2022-12-04 07:03:09 +01:00
|
|
|
import { bindThis } from '@/decorators.js';
|
2023-03-23 05:48:14 +01:00
|
|
|
import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3';
|
2022-09-17 20:27:08 +02:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class S3Service {
|
|
|
|
constructor(
|
|
|
|
private httpRequestService: HttpRequestService,
|
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
2022-12-04 07:03:09 +01:00
|
|
|
@bindThis
|
2023-08-16 10:51:28 +02:00
|
|
|
public getS3Client(meta: MiMeta): S3Client {
|
2023-03-13 01:45:21 +01:00
|
|
|
const u = meta.objectStorageEndpoint
|
2023-03-23 05:48:14 +01:00
|
|
|
? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}`
|
|
|
|
: `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent
|
2023-03-13 01:45:21 +01:00
|
|
|
|
2023-03-23 05:48:14 +01:00
|
|
|
const agent = this.httpRequestService.getAgentByUrl(new URL(u), !meta.objectStorageUseProxy);
|
|
|
|
const handlerOption: NodeHttpHandlerOptions = {};
|
|
|
|
if (meta.objectStorageUseSSL) {
|
|
|
|
handlerOption.httpsAgent = agent as https.Agent;
|
|
|
|
} else {
|
|
|
|
handlerOption.httpAgent = agent as http.Agent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new S3Client({
|
|
|
|
endpoint: meta.objectStorageEndpoint ? u : undefined,
|
|
|
|
credentials: (meta.objectStorageAccessKey !== null && meta.objectStorageSecretKey !== null) ? {
|
|
|
|
accessKeyId: meta.objectStorageAccessKey,
|
|
|
|
secretAccessKey: meta.objectStorageSecretKey,
|
|
|
|
} : undefined,
|
2023-03-25 10:45:14 +01:00
|
|
|
region: meta.objectStorageRegion ? meta.objectStorageRegion : undefined, // 空文字列もundefinedにするため ?? は使わない
|
2023-03-23 05:48:14 +01:00
|
|
|
tls: meta.objectStorageUseSSL,
|
|
|
|
forcePathStyle: meta.objectStorageEndpoint ? meta.objectStorageS3ForcePathStyle : false, // AWS with endPoint omitted
|
|
|
|
requestHandler: new NodeHttpHandler(handlerOption),
|
2022-09-17 20:27:08 +02:00
|
|
|
});
|
|
|
|
}
|
2023-03-23 05:48:14 +01:00
|
|
|
|
|
|
|
@bindThis
|
2023-08-16 10:51:28 +02:00
|
|
|
public async upload(meta: MiMeta, input: PutObjectCommandInput) {
|
2023-03-23 05:48:14 +01:00
|
|
|
const client = this.getS3Client(meta);
|
|
|
|
return new Upload({
|
|
|
|
client,
|
|
|
|
params: input,
|
|
|
|
partSize: (client.config.endpoint && (await client.config.endpoint()).hostname === 'storage.googleapis.com')
|
|
|
|
? 500 * 1024 * 1024
|
|
|
|
: 8 * 1024 * 1024,
|
|
|
|
}).done();
|
|
|
|
}
|
|
|
|
|
|
|
|
@bindThis
|
2023-08-16 10:51:28 +02:00
|
|
|
public delete(meta: MiMeta, input: DeleteObjectCommandInput) {
|
2023-03-23 05:48:14 +01:00
|
|
|
const client = this.getS3Client(meta);
|
|
|
|
return client.send(new DeleteObjectCommand(input));
|
|
|
|
}
|
2022-09-17 20:27:08 +02:00
|
|
|
}
|