@slickteam/nestjs-cellar 1.2.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -24
- package/dist/cellar.interface.d.ts +6 -1
- package/dist/cellar.service.d.ts +7 -15
- package/dist/cellar.service.js +22 -53
- package/dist/cellar.service.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -13
package/README.md
CHANGED
|
@@ -1,45 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @slickteam/nestjs-cellar
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@slickteam/nestjs-cellar)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
Module NestJS pour intégrer facilement [Cellar](https://www.clever-cloud.com/product/cellar/), le service de stockage S3 de Clever Cloud.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
## Installation
|
|
8
9
|
|
|
9
10
|
```bash
|
|
10
|
-
npm
|
|
11
|
+
npm install @slickteam/nestjs-cellar
|
|
12
|
+
# ou
|
|
13
|
+
pnpm add @slickteam/nestjs-cellar
|
|
11
14
|
```
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Configuration
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Ajoutez les variables d'environnement suivantes :
|
|
19
|
+
|
|
20
|
+
| Variable | Requis | Défaut | Description |
|
|
21
|
+
| --------------------------- | ------ | ------ | -------------------------------------- |
|
|
22
|
+
| `CELLAR_HOST` | Oui | - | Endpoint Cellar |
|
|
23
|
+
| `CELLAR_KEY_ID` | Oui | - | Access Key ID |
|
|
24
|
+
| `CELLAR_KEY_SECRET` | Oui | - | Secret Access Key |
|
|
25
|
+
| `CELLAR_REGION` | Non | `fr` | Région du bucket |
|
|
26
|
+
| `CELLAR_TIMEOUT_SIGNED_URL` | Non | `3600` | Durée de validité des URLs signées (s) |
|
|
27
|
+
|
|
28
|
+
Exemple de fichier `.env` :
|
|
29
|
+
|
|
30
|
+
```env
|
|
31
|
+
CELLAR_HOST=cellar-c2.services.clever-cloud.com
|
|
32
|
+
CELLAR_KEY_ID=votre_key_id
|
|
33
|
+
CELLAR_KEY_SECRET=votre_key_secret
|
|
34
|
+
CELLAR_REGION=fr
|
|
35
|
+
CELLAR_TIMEOUT_SIGNED_URL=3600
|
|
21
36
|
```
|
|
22
37
|
|
|
23
|
-
|
|
38
|
+
## Utilisation
|
|
39
|
+
|
|
40
|
+
### Import du module
|
|
24
41
|
|
|
25
42
|
```ts
|
|
43
|
+
import { CellarModule } from '@slickteam/nestjs-cellar';
|
|
44
|
+
|
|
26
45
|
@Module({
|
|
27
46
|
imports: [CellarModule],
|
|
28
|
-
controllers: [],
|
|
29
|
-
providers: [],
|
|
30
|
-
exports: [],
|
|
31
47
|
})
|
|
32
|
-
class
|
|
48
|
+
export class AppModule {}
|
|
33
49
|
```
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
### Injection du service
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { CellarService } from '@slickteam/nestjs-cellar';
|
|
55
|
+
|
|
56
|
+
@Injectable()
|
|
57
|
+
export class MyService {
|
|
58
|
+
constructor(private readonly cellarService: CellarService) {}
|
|
59
|
+
|
|
60
|
+
async uploadDocument(file: Express.Multer.File) {
|
|
61
|
+
return this.cellarService.uploadFile('mon-bucket', file);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
### `CellarService`
|
|
69
|
+
|
|
70
|
+
| Méthode | Description |
|
|
71
|
+
| ------------------------- | ----------------------------------------------- |
|
|
72
|
+
| `listObjectsByBucketName` | Liste les objets d'un bucket |
|
|
73
|
+
| `uploadFile` | Upload un fichier dans un bucket |
|
|
74
|
+
| `getFile` | Récupère un fichier depuis un bucket |
|
|
75
|
+
| `deleteFile` | Supprime un fichier d'un bucket |
|
|
76
|
+
| `fileExists` | Vérifie l'existence d'un fichier |
|
|
77
|
+
| `getSignedUrl` | Génère une URL signée pour accéder à un fichier |
|
|
78
|
+
|
|
79
|
+
### Propriétés exposées
|
|
80
|
+
|
|
81
|
+
- `s3Client` : Instance du client S3 pour des opérations avancées
|
|
82
|
+
- `s3EndPoint` : URL de l'endpoint Cellar
|
|
83
|
+
- `timeoutSignedUrl` : Durée de validité des URLs signées
|
|
36
84
|
|
|
37
|
-
|
|
85
|
+
## Dépendances
|
|
38
86
|
|
|
39
|
-
|
|
40
|
-
|
|
87
|
+
| Package | Version |
|
|
88
|
+
| ------------------------------- | -------- |
|
|
89
|
+
| `@nestjs/common` | `^11.0` |
|
|
90
|
+
| `@nestjs/config` | `^4.0` |
|
|
91
|
+
| `@aws-sdk/client-s3` | `^3.980` |
|
|
92
|
+
| `@aws-sdk/s3-request-presigner` | `^3.980` |
|
|
41
93
|
|
|
42
|
-
|
|
94
|
+
## Licence
|
|
43
95
|
|
|
44
|
-
|
|
45
|
-
- `@aws-sdk/s3-request-presigner`: `^3.774.0`
|
|
96
|
+
MIT
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export interface CellarBucketObjectListContentItem {
|
|
2
2
|
name: string;
|
|
3
|
-
lastModified:
|
|
3
|
+
lastModified: Date | undefined;
|
|
4
4
|
eTag: string;
|
|
5
5
|
size: number;
|
|
6
6
|
storageClass: string;
|
|
7
7
|
}
|
|
8
|
+
export interface CellarUploadFile {
|
|
9
|
+
buffer: Buffer;
|
|
10
|
+
mimetype: string;
|
|
11
|
+
originalname: string;
|
|
12
|
+
}
|
package/dist/cellar.service.d.ts
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
import { GetObjectCommandOutput, ObjectCannedACL,
|
|
1
|
+
import { DeleteObjectCommandOutput, GetObjectCommandOutput, ObjectCannedACL, PutObjectCommandOutput, S3Client } from '@aws-sdk/client-s3';
|
|
2
2
|
import { ConfigService } from '@nestjs/config';
|
|
3
|
-
import { CellarBucketObjectListContentItem } from './cellar.interface';
|
|
3
|
+
import { CellarBucketObjectListContentItem, CellarUploadFile } from './cellar.interface';
|
|
4
4
|
export declare class CellarService {
|
|
5
5
|
private configService;
|
|
6
6
|
readonly timeoutSignedUrl: number;
|
|
7
7
|
readonly s3EndPoint: string;
|
|
8
8
|
readonly s3Client: S3Client;
|
|
9
9
|
constructor(configService: ConfigService);
|
|
10
|
-
listObjectsByBucketName(
|
|
11
|
-
|
|
12
|
-
uploadFile(bucketName: string, file: {
|
|
13
|
-
buffer: Buffer;
|
|
14
|
-
mimetype: string;
|
|
15
|
-
originalname: string;
|
|
16
|
-
}, ACL?: ObjectCannedACL): Promise<ServiceOutputTypes>;
|
|
17
|
-
deleteFile(bucketName: string, fileName: string): Promise<ServiceOutputTypes>;
|
|
18
|
-
private getObjectMetadata;
|
|
19
|
-
isFileExist(bucketName: string, fileName: string): Promise<boolean | never>;
|
|
20
|
-
getSignedUrl(bucketName: string, fileName: string): Promise<string>;
|
|
10
|
+
listObjectsByBucketName(bucketName: string): Promise<CellarBucketObjectListContentItem[]>;
|
|
11
|
+
uploadFile(bucketName: string, file: CellarUploadFile, acl?: ObjectCannedACL): Promise<PutObjectCommandOutput>;
|
|
21
12
|
getFile(bucketName: string, fileName: string): Promise<GetObjectCommandOutput>;
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
deleteFile(bucketName: string, fileName: string): Promise<DeleteObjectCommandOutput>;
|
|
14
|
+
fileExists(bucketName: string, fileName: string): Promise<boolean>;
|
|
15
|
+
getSignedUrl(bucketName: string, fileName: string): Promise<string>;
|
|
24
16
|
}
|
package/dist/cellar.service.js
CHANGED
|
@@ -14,7 +14,6 @@ const client_s3_1 = require("@aws-sdk/client-s3");
|
|
|
14
14
|
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
|
|
15
15
|
const common_1 = require("@nestjs/common");
|
|
16
16
|
const config_1 = require("@nestjs/config");
|
|
17
|
-
const logger = new common_1.Logger('CellarService');
|
|
18
17
|
let CellarService = class CellarService {
|
|
19
18
|
configService;
|
|
20
19
|
timeoutSignedUrl;
|
|
@@ -22,81 +21,51 @@ let CellarService = class CellarService {
|
|
|
22
21
|
s3Client;
|
|
23
22
|
constructor(configService) {
|
|
24
23
|
this.configService = configService;
|
|
25
|
-
this.s3EndPoint = `https://${this.configService.getOrThrow('
|
|
26
|
-
const regionBucket = this.configService.get('CELLAR_REGION') ?? 'fr';
|
|
24
|
+
this.s3EndPoint = `https://${this.configService.getOrThrow('CELLAR_HOST')}`;
|
|
27
25
|
this.timeoutSignedUrl = this.configService.get('CELLAR_TIMEOUT_SIGNED_URL') ?? 3600;
|
|
28
26
|
this.s3Client = new client_s3_1.S3Client({
|
|
29
27
|
endpoint: this.s3EndPoint,
|
|
30
|
-
region:
|
|
28
|
+
region: this.configService.get('CELLAR_REGION') ?? 'fr',
|
|
31
29
|
credentials: {
|
|
32
|
-
accessKeyId: this.configService.getOrThrow('
|
|
33
|
-
secretAccessKey: this.configService.getOrThrow('
|
|
30
|
+
accessKeyId: this.configService.getOrThrow('CELLAR_KEY_ID'),
|
|
31
|
+
secretAccessKey: this.configService.getOrThrow('CELLAR_KEY_SECRET'),
|
|
34
32
|
},
|
|
35
33
|
});
|
|
36
34
|
}
|
|
37
|
-
async listObjectsByBucketName(
|
|
38
|
-
const result = await this.s3Client.send(new client_s3_1.ListObjectsV2Command({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
size: c.Size ?? 0,
|
|
46
|
-
storageClass: c.StorageClass ?? '',
|
|
35
|
+
async listObjectsByBucketName(bucketName) {
|
|
36
|
+
const result = await this.s3Client.send(new client_s3_1.ListObjectsV2Command({ Bucket: bucketName }));
|
|
37
|
+
return (result.Contents?.map((object) => ({
|
|
38
|
+
name: object.Key ?? '',
|
|
39
|
+
lastModified: object.LastModified,
|
|
40
|
+
eTag: object.ETag ?? '',
|
|
41
|
+
size: object.Size ?? 0,
|
|
42
|
+
storageClass: object.StorageClass ?? '',
|
|
47
43
|
})) ?? []);
|
|
48
44
|
}
|
|
49
|
-
async
|
|
50
|
-
const command = new client_s3_1.GetObjectCommand({ Bucket: bucketName, Key: keyObject });
|
|
51
|
-
return (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, { expiresIn: this.timeoutSignedUrl });
|
|
52
|
-
}
|
|
53
|
-
async uploadFile(bucketName, file, ACL = 'bucket-owner-full-control') {
|
|
45
|
+
async uploadFile(bucketName, file, acl = 'bucket-owner-full-control') {
|
|
54
46
|
return this.s3Client.send(new client_s3_1.PutObjectCommand({
|
|
55
47
|
Bucket: bucketName,
|
|
56
48
|
Key: file.originalname,
|
|
57
49
|
Body: file.buffer,
|
|
58
|
-
ACL,
|
|
50
|
+
ACL: acl,
|
|
59
51
|
ContentType: file.mimetype,
|
|
60
52
|
}));
|
|
61
53
|
}
|
|
62
|
-
async
|
|
63
|
-
return this.s3Client.send(new client_s3_1.
|
|
64
|
-
Bucket: bucketName,
|
|
65
|
-
Key: fileName,
|
|
66
|
-
}));
|
|
54
|
+
async getFile(bucketName, fileName) {
|
|
55
|
+
return this.s3Client.send(new client_s3_1.GetObjectCommand({ Bucket: bucketName, Key: fileName }));
|
|
67
56
|
}
|
|
68
|
-
async
|
|
69
|
-
return this.s3Client.send(new client_s3_1.
|
|
57
|
+
async deleteFile(bucketName, fileName) {
|
|
58
|
+
return this.s3Client.send(new client_s3_1.DeleteObjectCommand({ Bucket: bucketName, Key: fileName }));
|
|
70
59
|
}
|
|
71
|
-
async
|
|
72
|
-
const result = await this.
|
|
73
|
-
const
|
|
74
|
-
return
|
|
60
|
+
async fileExists(bucketName, fileName) {
|
|
61
|
+
const result = await this.s3Client.send(new client_s3_1.HeadObjectCommand({ Bucket: bucketName, Key: fileName }));
|
|
62
|
+
const statusCode = result.$metadata.httpStatusCode ?? 500;
|
|
63
|
+
return statusCode >= 200 && statusCode < 400;
|
|
75
64
|
}
|
|
76
65
|
async getSignedUrl(bucketName, fileName) {
|
|
77
66
|
const command = new client_s3_1.GetObjectCommand({ Bucket: bucketName, Key: fileName });
|
|
78
67
|
return (0, s3_request_presigner_1.getSignedUrl)(this.s3Client, command, { expiresIn: this.timeoutSignedUrl });
|
|
79
68
|
}
|
|
80
|
-
async getFile(bucketName, fileName) {
|
|
81
|
-
return this.s3Client.send(new client_s3_1.GetObjectCommand({ Bucket: bucketName, Key: fileName }));
|
|
82
|
-
}
|
|
83
|
-
async uploadPdfToS3(bucketName, fileName, pdfBuffer) {
|
|
84
|
-
await this.uploadFile(bucketName, {
|
|
85
|
-
buffer: pdfBuffer,
|
|
86
|
-
originalname: fileName,
|
|
87
|
-
mimetype: 'application/pdf',
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
async downloadPdfFromS3(bucketName, fileName) {
|
|
91
|
-
const resultCommand = await this.s3Client.send(new client_s3_1.GetObjectCommand({ Bucket: bucketName, Key: fileName }));
|
|
92
|
-
const rawFileBytes = await resultCommand.Body?.transformToByteArray();
|
|
93
|
-
if (rawFileBytes) {
|
|
94
|
-
return Buffer.from(rawFileBytes);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
throw new Error(`Error when downloading PDF from S3 with fileName[${fileName}]`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
69
|
};
|
|
101
70
|
exports.CellarService = CellarService;
|
|
102
71
|
exports.CellarService = CellarService = __decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cellar.service.js","sourceRoot":"","sources":["../src/cellar.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,kDAW4B;AAC5B,wEAA6D;AAC7D,
|
|
1
|
+
{"version":3,"file":"cellar.service.js","sourceRoot":"","sources":["../src/cellar.service.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CellarBucketObjectListContentItem } from './cellar.interface';
|
|
1
|
+
import { CellarBucketObjectListContentItem, CellarUploadFile } from './cellar.interface';
|
|
2
2
|
import { CellarService } from './cellar.service';
|
|
3
3
|
declare class CellarModule {
|
|
4
4
|
}
|
|
5
|
-
export { CellarModule, CellarService, CellarBucketObjectListContentItem };
|
|
5
|
+
export { CellarModule, CellarService, CellarBucketObjectListContentItem, CellarUploadFile };
|