2023-07-27 08:31:52 +03:00
|
|
|
/*
|
|
|
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-09-17 21:27:08 +03:00
|
|
|
import { URL } from 'node:url';
|
2023-03-23 06:48:14 +02:00
|
|
|
import * as http from 'node:http';
|
|
|
|
import * as https from 'node:https';
|
2022-09-17 21:27:08 +03:00
|
|
|
import { Inject, Injectable } from '@nestjs/common';
|
2023-03-23 06:48:14 +02:00
|
|
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
|
|
import { Upload } from '@aws-sdk/lib-storage';
|
|
|
|
import { NodeHttpHandler, NodeHttpHandlerOptions } from '@aws-sdk/node-http-handler';
|
2022-09-17 21:27:08 +03:00
|
|
|
import { DI } from '@/di-symbols.js';
|
2022-09-20 23:33:11 +03:00
|
|
|
import type { Config } from '@/config.js';
|
2022-09-17 21:27:08 +03:00
|
|
|
import type { Meta } from '@/models/entities/Meta.js';
|
2022-12-04 03:16:03 +02:00
|
|
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
2022-12-04 08:03:09 +02:00
|
|
|
import { bindThis } from '@/decorators.js';
|
2023-03-23 06:48:14 +02:00
|
|
|
import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3';
|
2022-09-17 21:27:08 +03:00
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
export class S3Service {
|
|
|
|
constructor(
|
|
|
|
@Inject(DI.config)
|
|
|
|
private config: Config,
|
|
|
|
|
|
|
|
private httpRequestService: HttpRequestService,
|
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
2022-12-04 08:03:09 +02:00
|
|
|
@bindThis
|
2023-03-23 06:48:14 +02:00
|
|
|
public getS3Client(meta: Meta): S3Client {
|
2023-03-13 02:45:21 +02:00
|
|
|
const u = meta.objectStorageEndpoint
|
2023-03-23 06:48:14 +02:00
|
|
|
? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}`
|
|
|
|
: `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent
|
2023-03-13 02:45:21 +02:00
|
|
|
|
2023-03-23 06:48:14 +02: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 11:45:14 +02:00
|
|
|
region: meta.objectStorageRegion ? meta.objectStorageRegion : undefined, // 空文字列もundefinedにするため ?? は使わない
|
2023-03-23 06:48:14 +02:00
|
|
|
tls: meta.objectStorageUseSSL,
|
|
|
|
forcePathStyle: meta.objectStorageEndpoint ? meta.objectStorageS3ForcePathStyle : false, // AWS with endPoint omitted
|
|
|
|
requestHandler: new NodeHttpHandler(handlerOption),
|
2022-09-17 21:27:08 +03:00
|
|
|
});
|
|
|
|
}
|
2023-03-23 06:48:14 +02:00
|
|
|
|
|
|
|
@bindThis
|
|
|
|
public async upload(meta: Meta, input: PutObjectCommandInput) {
|
|
|
|
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
|
|
|
|
public delete(meta: Meta, input: DeleteObjectCommandInput) {
|
|
|
|
const client = this.getS3Client(meta);
|
|
|
|
return client.send(new DeleteObjectCommand(input));
|
|
|
|
}
|
2022-09-17 21:27:08 +03:00
|
|
|
}
|