make sure S3 clients are properly disposed
This commit is contained in:
parent
db4661397b
commit
4d9317ad2b
4 changed files with 112 additions and 39 deletions
|
|
@ -6,24 +6,61 @@
|
|||
import { URL } from 'node:url';
|
||||
import * as http from 'node:http';
|
||||
import * as https from 'node:https';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||
import { Upload } from '@aws-sdk/lib-storage';
|
||||
import { NodeHttpHandler, NodeHttpHandlerOptions } from '@smithy/node-http-handler';
|
||||
import type { MiMeta } from '@/models/Meta.js';
|
||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { InternalEventService } from '@/core/InternalEventService.js';
|
||||
import type { InternalEventTypes } from '@/core/GlobalEventService.js';
|
||||
import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3';
|
||||
|
||||
@Injectable()
|
||||
export class S3Service {
|
||||
export class S3Service implements OnApplicationShutdown {
|
||||
private client?: S3Client;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.meta)
|
||||
private readonly meta: MiMeta,
|
||||
|
||||
private httpRequestService: HttpRequestService,
|
||||
private readonly internalEventService: InternalEventService,
|
||||
) {
|
||||
this.internalEventService.on('metaUpdated', this.onMetaUpdated);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public getS3Client(meta: MiMeta): S3Client {
|
||||
private onMetaUpdated(body: InternalEventTypes['metaUpdated']): void {
|
||||
if (this.needsChange(body.before, body.after)) {
|
||||
this.disposeClient();
|
||||
this.client = this.createS3Client(body.after);
|
||||
}
|
||||
}
|
||||
|
||||
private needsChange(before: MiMeta | undefined, after: MiMeta): boolean {
|
||||
if (before == null) return true;
|
||||
if (before.objectStorageEndpoint !== after.objectStorageEndpoint) return true;
|
||||
if (before.objectStorageUseSSL !== after.objectStorageUseSSL) return true;
|
||||
if (before.objectStorageUseProxy !== after.objectStorageUseProxy) return true;
|
||||
if (before.objectStorageAccessKey !== after.objectStorageAccessKey) return true;
|
||||
if (before.objectStorageSecretKey !== after.objectStorageSecretKey) return true;
|
||||
if (before.objectStorageRegion !== after.objectStorageRegion) return true;
|
||||
if (before.objectStorageUseSSL !== after.objectStorageUseSSL) return true;
|
||||
if (before.objectStorageS3ForcePathStyle !== after.objectStorageS3ForcePathStyle) return true;
|
||||
if (before.objectStorageRegion !== after.objectStorageRegion) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private getS3Client(): S3Client {
|
||||
return this.client ??= this.createS3Client(this.meta);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private createS3Client(meta: MiMeta): S3Client {
|
||||
const u = meta.objectStorageEndpoint
|
||||
? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}`
|
||||
: `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent
|
||||
|
|
@ -52,8 +89,8 @@ export class S3Service {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public async upload(meta: MiMeta, input: PutObjectCommandInput) {
|
||||
const client = this.getS3Client(meta);
|
||||
public async upload(input: PutObjectCommandInput) {
|
||||
const client = this.getS3Client();
|
||||
return new Upload({
|
||||
client,
|
||||
params: input,
|
||||
|
|
@ -64,8 +101,27 @@ export class S3Service {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public delete(meta: MiMeta, input: DeleteObjectCommandInput) {
|
||||
const client = this.getS3Client(meta);
|
||||
public delete(input: DeleteObjectCommandInput) {
|
||||
const client = this.getS3Client();
|
||||
return client.send(new DeleteObjectCommand(input));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private disposeClient(): void {
|
||||
if (this.client) {
|
||||
this.client.destroy();
|
||||
this.client = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private dispose(): void {
|
||||
this.disposeClient();
|
||||
this.internalEventService.off('metaUpdated', this.onMetaUpdated);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
onApplicationShutdown() {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue