@balena/pinejs 20.0.0-build-esm-78a099606f287bc0df4001425788a7ad1c708ba1-1 → 20.0.0-build-esm-b5b8bb0f9f64e45f22764bdbc82b5efa4eec185d-1

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.
@@ -1,143 +0,0 @@
1
- import {
2
- FileSizeExceededError,
3
- type IncomingFile,
4
- normalizeHref,
5
- type UploadResponse,
6
- WebResourceError,
7
- type WebResourceHandler,
8
- } from '../index.js';
9
- import {
10
- S3Client,
11
- type S3ClientConfig,
12
- DeleteObjectCommand,
13
- type PutObjectCommandInput,
14
- GetObjectCommand,
15
- } from '@aws-sdk/client-s3';
16
- import { Upload } from '@aws-sdk/lib-storage';
17
- import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
18
-
19
- import { randomUUID } from 'crypto';
20
- import type { WebResourceType as WebResource } from '@balena/sbvr-types';
21
- import memoize from 'memoizee';
22
-
23
- export interface S3HandlerProps {
24
- region: string;
25
- accessKey: string;
26
- secretKey: string;
27
- endpoint: string;
28
- bucket: string;
29
- maxSize?: number;
30
- signedUrlExpireTimeSeconds?: number;
31
- signedUrlCacheExpireTimeSeconds?: number;
32
- }
33
-
34
- export class S3Handler implements WebResourceHandler {
35
- private readonly config: S3ClientConfig;
36
- private readonly bucket: string;
37
- private readonly maxFileSize: number;
38
-
39
- protected readonly signedUrlExpireTimeSeconds: number;
40
- protected readonly signedUrlCacheExpireTimeSeconds: number;
41
- protected cachedGetSignedUrl: (fileKey: string) => Promise<string>;
42
-
43
- private client: S3Client;
44
-
45
- constructor(config: S3HandlerProps) {
46
- this.config = {
47
- region: config.region,
48
- credentials: {
49
- accessKeyId: config.accessKey,
50
- secretAccessKey: config.secretKey,
51
- },
52
- endpoint: config.endpoint,
53
- forcePathStyle: true,
54
- };
55
-
56
- this.signedUrlExpireTimeSeconds =
57
- config.signedUrlExpireTimeSeconds ?? 86400; // 24h
58
- this.signedUrlCacheExpireTimeSeconds =
59
- config.signedUrlCacheExpireTimeSeconds ?? 82800; // 22h
60
-
61
- this.maxFileSize = config.maxSize ?? 52428800;
62
- this.bucket = config.bucket;
63
- this.client = new S3Client(this.config);
64
-
65
- // Memoize expects maxAge in MS and s3 signing method in seconds.
66
- // Normalization to use only seconds and therefore convert here from seconds to MS
67
- this.cachedGetSignedUrl = memoize(this.s3SignUrl, {
68
- maxAge: this.signedUrlCacheExpireTimeSeconds * 1000,
69
- });
70
- }
71
-
72
- public async handleFile(resource: IncomingFile): Promise<UploadResponse> {
73
- let size = 0;
74
- const key = `${resource.fieldname}_${randomUUID()}_${
75
- resource.originalname
76
- }`;
77
- const params: PutObjectCommandInput = {
78
- Bucket: this.bucket,
79
- Key: key,
80
- Body: resource.stream,
81
- ContentType: resource.mimetype,
82
- };
83
- const upload = new Upload({ client: this.client, params });
84
-
85
- upload.on('httpUploadProgress', async (ev) => {
86
- size = ev.total ?? ev.loaded!;
87
- if (size > this.maxFileSize) {
88
- await upload.abort();
89
- }
90
- });
91
-
92
- try {
93
- await upload.done();
94
- } catch (err: any) {
95
- resource.stream.resume();
96
- if (size > this.maxFileSize) {
97
- throw new FileSizeExceededError(this.maxFileSize);
98
- }
99
- throw new WebResourceError(err);
100
- }
101
-
102
- const filename = this.getS3URL(key);
103
- return { size, filename };
104
- }
105
-
106
- public async removeFile(href: string): Promise<void> {
107
- const fileKey = this.getKeyFromHref(href);
108
-
109
- const command = new DeleteObjectCommand({
110
- Bucket: this.bucket,
111
- Key: fileKey,
112
- });
113
-
114
- await this.client.send(command);
115
- }
116
-
117
- public async onPreRespond(webResource: WebResource): Promise<WebResource> {
118
- if (webResource.href != null) {
119
- const fileKey = this.getKeyFromHref(webResource.href);
120
- webResource.href = await this.cachedGetSignedUrl(fileKey);
121
- }
122
- return webResource;
123
- }
124
-
125
- private s3SignUrl(fileKey: string): Promise<string> {
126
- const command = new GetObjectCommand({
127
- Bucket: this.bucket,
128
- Key: fileKey,
129
- });
130
- return getSignedUrl(this.client, command, {
131
- expiresIn: this.signedUrlExpireTimeSeconds,
132
- });
133
- }
134
-
135
- private getS3URL(key: string): string {
136
- return `${this.config.endpoint}/${this.bucket}/${key}`;
137
- }
138
-
139
- private getKeyFromHref(href: string): string {
140
- const hrefWithoutParams = normalizeHref(href);
141
- return hrefWithoutParams.substring(hrefWithoutParams.lastIndexOf('/') + 1);
142
- }
143
- }