@openstax/ts-utils 1.30.0 → 1.30.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.
- package/dist/cjs/services/fileServer/index.d.ts +11 -0
- package/dist/cjs/services/fileServer/localFileServer.js +47 -0
- package/dist/cjs/services/fileServer/s3FileServer.js +70 -0
- package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
- package/dist/esm/services/fileServer/index.d.ts +11 -0
- package/dist/esm/services/fileServer/localFileServer.js +47 -0
- package/dist/esm/services/fileServer/s3FileServer.js +68 -1
- package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/script/bin/copy-from-template.bash +6 -1
|
@@ -14,5 +14,16 @@ export interface FileServerAdapter {
|
|
|
14
14
|
putFileContent: (source: FileValue, content: string) => Promise<FileValue>;
|
|
15
15
|
getSignedViewerUrl: (source: FileValue) => Promise<string>;
|
|
16
16
|
getFileContent: (source: FileValue) => Promise<Buffer>;
|
|
17
|
+
getSignedFileUploadConfig: () => Promise<{
|
|
18
|
+
url: string;
|
|
19
|
+
payload: {
|
|
20
|
+
[key: string]: string;
|
|
21
|
+
};
|
|
22
|
+
}>;
|
|
23
|
+
copyFileTo: (source: FileValue, destinationPath: string) => Promise<FileValue>;
|
|
24
|
+
copyFileToDirectory: (source: FileValue, destinationDirectory: string) => Promise<FileValue>;
|
|
25
|
+
isTemporaryUpload: (source: FileValue) => boolean;
|
|
26
|
+
getFileChecksum: (source: FileValue) => Promise<string>;
|
|
27
|
+
filesEqual: (sourceA: FileValue, sourceB: FileValue) => Promise<boolean>;
|
|
17
28
|
}
|
|
18
29
|
export declare const isFileOrFolder: (thing: any) => thing is FileValue | FolderValue;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/* cspell:ignore originalname */
|
|
2
|
+
import crypto from 'crypto';
|
|
2
3
|
import fs from 'fs';
|
|
3
4
|
import https from 'https';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import cors from 'cors';
|
|
6
7
|
import express from 'express';
|
|
7
8
|
import multer from 'multer';
|
|
9
|
+
import { v4 as uuid } from 'uuid';
|
|
8
10
|
import { assertString } from '../../assertions';
|
|
9
11
|
import { resolveConfigValue } from '../../config';
|
|
10
12
|
import { ifDefined } from '../../guards';
|
|
@@ -66,9 +68,54 @@ export const localFileServer = (initializer) => (configProvider) => {
|
|
|
66
68
|
await fs.promises.writeFile(filePath, content);
|
|
67
69
|
return source;
|
|
68
70
|
};
|
|
71
|
+
const getSignedFileUploadConfig = async () => {
|
|
72
|
+
const prefix = 'uploads/' + uuid();
|
|
73
|
+
return {
|
|
74
|
+
url: `https://${await host}:${await port}/`,
|
|
75
|
+
payload: {
|
|
76
|
+
key: prefix + '/${filename}',
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
const copyFileTo = async (source, destinationPath) => {
|
|
81
|
+
const sourcePath = path.join(await fileDir, source.path);
|
|
82
|
+
const destPath = path.join(await fileDir, destinationPath);
|
|
83
|
+
const destDirectory = path.dirname(destPath);
|
|
84
|
+
await fs.promises.mkdir(destDirectory, { recursive: true });
|
|
85
|
+
await fs.promises.copyFile(sourcePath, destPath);
|
|
86
|
+
return {
|
|
87
|
+
...source,
|
|
88
|
+
path: destinationPath
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
const copyFileToDirectory = async (source, destination) => {
|
|
92
|
+
const destinationPath = path.join(destination, source.label);
|
|
93
|
+
return copyFileTo(source, destinationPath);
|
|
94
|
+
};
|
|
95
|
+
const isTemporaryUpload = (source) => {
|
|
96
|
+
return source.path.indexOf('uploads/') === 0;
|
|
97
|
+
};
|
|
98
|
+
const getFileChecksum = async (source) => {
|
|
99
|
+
const filePath = path.join(await fileDir, source.path);
|
|
100
|
+
const fileContent = await fs.promises.readFile(filePath);
|
|
101
|
+
return crypto.createHash('md5').update(fileContent).digest('hex');
|
|
102
|
+
};
|
|
103
|
+
const filesEqual = async (sourceA, sourceB) => {
|
|
104
|
+
const [aSum, bSum] = await Promise.all([
|
|
105
|
+
getFileChecksum(sourceA),
|
|
106
|
+
getFileChecksum(sourceB)
|
|
107
|
+
]);
|
|
108
|
+
return aSum === bSum;
|
|
109
|
+
};
|
|
69
110
|
return {
|
|
70
111
|
getSignedViewerUrl,
|
|
71
112
|
getFileContent,
|
|
72
113
|
putFileContent,
|
|
114
|
+
getSignedFileUploadConfig,
|
|
115
|
+
copyFileTo,
|
|
116
|
+
copyFileToDirectory,
|
|
117
|
+
isTemporaryUpload,
|
|
118
|
+
getFileChecksum,
|
|
119
|
+
filesEqual,
|
|
73
120
|
};
|
|
74
121
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* cspell:ignore presigner */
|
|
2
|
-
import { GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
import { CopyObjectCommand, GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
3
|
+
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
3
4
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { v4 as uuid } from 'uuid';
|
|
4
7
|
import { once } from '../..';
|
|
5
8
|
import { assertDefined } from '../../assertions';
|
|
6
9
|
import { resolveConfigValue } from '../../config';
|
|
@@ -41,9 +44,73 @@ export const s3FileServer = (initializer) => (configProvider) => {
|
|
|
41
44
|
await (await s3Service()).send(command);
|
|
42
45
|
return source;
|
|
43
46
|
};
|
|
47
|
+
/*
|
|
48
|
+
* https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_s3_presigned_post.html
|
|
49
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/HTTPPOSTExamples.html
|
|
50
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html
|
|
51
|
+
*/
|
|
52
|
+
const getSignedFileUploadConfig = async () => {
|
|
53
|
+
const prefix = 'uploads/' + uuid();
|
|
54
|
+
const bucket = (await bucketName());
|
|
55
|
+
const Conditions = [
|
|
56
|
+
{ acl: 'private' },
|
|
57
|
+
{ bucket },
|
|
58
|
+
['starts-with', '$key', prefix]
|
|
59
|
+
];
|
|
60
|
+
const defaultFields = {
|
|
61
|
+
acl: 'private',
|
|
62
|
+
};
|
|
63
|
+
const { url, fields } = await createPresignedPost(await s3Service(), {
|
|
64
|
+
Bucket: bucket,
|
|
65
|
+
Key: prefix + '/${filename}',
|
|
66
|
+
Conditions,
|
|
67
|
+
Fields: defaultFields,
|
|
68
|
+
Expires: 3600, // 1 hour
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
url, payload: fields
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const copyFileTo = async (source, destinationPath) => {
|
|
75
|
+
const bucket = (await bucketName());
|
|
76
|
+
const destinationPathWithoutLeadingSlash = destinationPath.replace(/^\//, '');
|
|
77
|
+
const command = new CopyObjectCommand({
|
|
78
|
+
Bucket: bucket,
|
|
79
|
+
Key: destinationPathWithoutLeadingSlash,
|
|
80
|
+
CopySource: path.join(bucket, source.path),
|
|
81
|
+
});
|
|
82
|
+
await (await s3Service()).send(command);
|
|
83
|
+
return {
|
|
84
|
+
...source,
|
|
85
|
+
path: destinationPathWithoutLeadingSlash
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const copyFileToDirectory = async (source, destination) => {
|
|
89
|
+
const destinationPath = path.join(destination, source.label);
|
|
90
|
+
return copyFileTo(source, destinationPath);
|
|
91
|
+
};
|
|
92
|
+
const isTemporaryUpload = (source) => {
|
|
93
|
+
return source.path.indexOf('uploads/') === 0;
|
|
94
|
+
};
|
|
95
|
+
const getFileChecksum = async (source) => {
|
|
96
|
+
const bucket = (await bucketName());
|
|
97
|
+
const command = new HeadObjectCommand({ Bucket: bucket, Key: source.path });
|
|
98
|
+
const response = await (await s3Service()).send(command);
|
|
99
|
+
return assertDefined(response.ETag);
|
|
100
|
+
};
|
|
101
|
+
const filesEqual = async (sourceA, sourceB) => {
|
|
102
|
+
const [aSum, bSum] = await Promise.all([getFileChecksum(sourceA), getFileChecksum(sourceB)]);
|
|
103
|
+
return aSum === bSum;
|
|
104
|
+
};
|
|
44
105
|
return {
|
|
45
106
|
getFileContent,
|
|
46
107
|
putFileContent,
|
|
47
108
|
getSignedViewerUrl,
|
|
109
|
+
getSignedFileUploadConfig,
|
|
110
|
+
copyFileTo,
|
|
111
|
+
copyFileToDirectory,
|
|
112
|
+
isTemporaryUpload,
|
|
113
|
+
getFileChecksum,
|
|
114
|
+
filesEqual,
|
|
48
115
|
};
|
|
49
116
|
};
|