@unchainedshop/file-upload 4.3.4 → 4.5.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 +135 -1
- package/lib/build-hashed-filename.d.ts +0 -1
- package/lib/build-hashed-filename.js +2 -5
- package/lib/director/FileAdapter.d.ts +2 -3
- package/lib/director/FileAdapter.js +0 -1
- package/lib/director/FileDirector.d.ts +2 -3
- package/lib/director/FileDirector.js +0 -1
- package/lib/file-upload-index.d.ts +5 -6
- package/lib/file-upload-index.js +4 -5
- package/lib/put-expiration.d.ts +0 -1
- package/lib/put-expiration.js +0 -1
- package/lib/types.d.ts +0 -1
- package/lib/types.js +0 -1
- package/package.json +11 -10
- package/lib/build-hashed-filename.d.ts.map +0 -1
- package/lib/build-hashed-filename.js.map +0 -1
- package/lib/director/FileAdapter.d.ts.map +0 -1
- package/lib/director/FileAdapter.js.map +0 -1
- package/lib/director/FileDirector.d.ts.map +0 -1
- package/lib/director/FileDirector.js.map +0 -1
- package/lib/file-upload-index.d.ts.map +0 -1
- package/lib/file-upload-index.js.map +0 -1
- package/lib/put-expiration.d.ts.map +0 -1
- package/lib/put-expiration.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/src/build-hashed-filename.ts +0 -22
- package/src/director/FileAdapter.ts +0 -52
- package/src/director/FileDirector.ts +0 -26
- package/src/file-upload-index.test.ts +0 -12
- package/src/file-upload-index.ts +0 -7
- package/src/put-expiration.ts +0 -7
- package/src/types.ts +0 -19
- package/tsconfig.json +0 -9
package/README.md
CHANGED
|
@@ -1 +1,135 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://npmjs.com/package/@unchainedshop/file-upload)
|
|
2
|
+
[](https://opensource.org/licenses/EUPL-1.2)
|
|
3
|
+
|
|
4
|
+
# @unchainedshop/file-upload
|
|
5
|
+
|
|
6
|
+
File upload abstraction layer for the Unchained Engine. Provides a pluggable system for handling file uploads with support for signed URLs, streaming, and multiple storage backends.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @unchainedshop/file-upload
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { FileDirector, FileAdapter, type IFileAdapter } from '@unchainedshop/file-upload';
|
|
18
|
+
|
|
19
|
+
// Create a custom file adapter
|
|
20
|
+
const MyStorageAdapter: IFileAdapter = {
|
|
21
|
+
...FileAdapter,
|
|
22
|
+
key: 'my-storage',
|
|
23
|
+
label: 'My Storage Backend',
|
|
24
|
+
version: '1.0.0',
|
|
25
|
+
|
|
26
|
+
async createSignedURL(directoryName, fileName, unchainedAPI) {
|
|
27
|
+
// Return signed URL for direct upload
|
|
28
|
+
return {
|
|
29
|
+
_id: 'file-id',
|
|
30
|
+
directoryName,
|
|
31
|
+
fileName,
|
|
32
|
+
type: 'application/octet-stream',
|
|
33
|
+
url: 'https://storage.example.com/file',
|
|
34
|
+
putURL: 'https://storage.example.com/upload',
|
|
35
|
+
expiryDate: new Date(Date.now() + 3600000),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async createDownloadURL(file, expiry) {
|
|
40
|
+
return `https://storage.example.com/${file.path}`;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Register the adapter
|
|
45
|
+
FileDirector.registerAdapter(MyStorageAdapter);
|
|
46
|
+
|
|
47
|
+
// Register upload callback for a directory
|
|
48
|
+
FileDirector.registerFileUploadCallback('product-images', async (file, unchainedAPI) => {
|
|
49
|
+
// Process uploaded file
|
|
50
|
+
console.log('File uploaded:', file.name);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API Overview
|
|
55
|
+
|
|
56
|
+
### FileDirector
|
|
57
|
+
|
|
58
|
+
| Method | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `registerAdapter` | Register a file storage adapter |
|
|
61
|
+
| `getAdapter` | Get adapter by key |
|
|
62
|
+
| `getAdapters` | Get all registered adapters |
|
|
63
|
+
| `unregisterAdapter` | Remove an adapter |
|
|
64
|
+
| `registerFileUploadCallback` | Register callback for upload completion |
|
|
65
|
+
| `getFileUploadCallback` | Get registered callback for directory |
|
|
66
|
+
|
|
67
|
+
### FileAdapter Base
|
|
68
|
+
|
|
69
|
+
Base implementation for file adapters. Override these methods:
|
|
70
|
+
|
|
71
|
+
| Method | Description |
|
|
72
|
+
|--------|-------------|
|
|
73
|
+
| `createSignedURL` | Generate signed URL for direct upload |
|
|
74
|
+
| `createDownloadURL` | Generate download URL for a file |
|
|
75
|
+
| `createDownloadStream` | Get readable stream for file download |
|
|
76
|
+
| `uploadFileFromStream` | Upload file from a stream |
|
|
77
|
+
| `uploadFileFromURL` | Upload file from external URL |
|
|
78
|
+
| `removeFiles` | Delete files from storage |
|
|
79
|
+
|
|
80
|
+
### Utility Functions
|
|
81
|
+
|
|
82
|
+
| Export | Description |
|
|
83
|
+
|--------|-------------|
|
|
84
|
+
| `buildHashedFilename` | Generate hashed filename for storage |
|
|
85
|
+
| `resolveExpirationDate` | Calculate expiration date for signed URLs |
|
|
86
|
+
|
|
87
|
+
### Types
|
|
88
|
+
|
|
89
|
+
| Export | Description |
|
|
90
|
+
|--------|-------------|
|
|
91
|
+
| `IFileAdapter` | Interface for file adapter implementations |
|
|
92
|
+
| `UploadFileData` | Data returned after file upload |
|
|
93
|
+
| `UploadedFile` | Representation of an uploaded file |
|
|
94
|
+
| `UploadFileCallback` | Callback function type for upload events |
|
|
95
|
+
|
|
96
|
+
## Implementing a Custom Adapter
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { FileAdapter, type IFileAdapter } from '@unchainedshop/file-upload';
|
|
100
|
+
|
|
101
|
+
const S3Adapter: IFileAdapter = {
|
|
102
|
+
...FileAdapter,
|
|
103
|
+
key: 's3-adapter',
|
|
104
|
+
label: 'AWS S3 Storage',
|
|
105
|
+
version: '1.0.0',
|
|
106
|
+
|
|
107
|
+
async createSignedURL(directoryName, fileName, unchainedAPI) {
|
|
108
|
+
// Generate presigned S3 URL
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
async createDownloadURL(file, expiry) {
|
|
112
|
+
// Generate presigned download URL
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
async uploadFileFromStream(directoryName, rawFile, unchainedAPI) {
|
|
116
|
+
// Stream upload to S3
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async removeFiles(files, unchainedContext) {
|
|
120
|
+
// Delete objects from S3
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
async createDownloadStream(file, unchainedAPI) {
|
|
124
|
+
// Return S3 object as stream
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
async uploadFileFromURL(directoryName, fileInput, unchainedAPI) {
|
|
128
|
+
// Fetch and upload from URL
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
EUPL-1.2
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { sha1, slugify } from '@unchainedshop/utils';
|
|
2
|
-
import baseX from 'base-x';
|
|
3
|
-
const b62 = baseX('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
|
4
2
|
export default async function buildHashedFilename(directoryName, fileName, expiryDate) {
|
|
5
3
|
const hashed = await sha1(`${directoryName}${fileName}${expiryDate.getTime()}`);
|
|
6
4
|
const splittedFilename = fileName.split('.');
|
|
@@ -9,7 +7,6 @@ export default async function buildHashedFilename(directoryName, fileName, expir
|
|
|
9
7
|
const slugifiedFilenameWithExtension = [slugify(fileNameWithoutExtension), ext]
|
|
10
8
|
.filter(Boolean)
|
|
11
9
|
.join('.');
|
|
12
|
-
const
|
|
13
|
-
return `${
|
|
10
|
+
const base64url = Buffer.from(hashed).toString('base64url');
|
|
11
|
+
return `${base64url}.${slugifiedFilenameWithExtension}`;
|
|
14
12
|
}
|
|
15
|
-
//# sourceMappingURL=build-hashed-filename.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable } from 'node:stream';
|
|
2
|
-
import { IBaseAdapter } from '@unchainedshop/utils';
|
|
3
|
-
import { UploadedFile, UploadFileData } from '../types.
|
|
2
|
+
import { type IBaseAdapter } from '@unchainedshop/utils';
|
|
3
|
+
import type { UploadedFile, UploadFileData } from '../types.ts';
|
|
4
4
|
export interface IFileAdapter<Context = unknown> extends IBaseAdapter {
|
|
5
5
|
createDownloadURL: (file: UploadedFile, expiry?: number) => Promise<string | null>;
|
|
6
6
|
createSignedURL: (directoryName: string, fileName: string, unchainedAPI: Context) => Promise<(UploadFileData & {
|
|
@@ -17,4 +17,3 @@ export interface IFileAdapter<Context = unknown> extends IBaseAdapter {
|
|
|
17
17
|
createDownloadStream: (file: UploadedFile, unchainedAPI: Context) => Promise<Readable>;
|
|
18
18
|
}
|
|
19
19
|
export declare const FileAdapter: Omit<IFileAdapter, 'key' | 'label' | 'version'>;
|
|
20
|
-
//# sourceMappingURL=FileAdapter.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IFileAdapter } from './FileAdapter.
|
|
2
|
-
import { UploadedFile } from '../types.
|
|
1
|
+
import type { IFileAdapter } from './FileAdapter.ts';
|
|
2
|
+
import type { UploadedFile } from '../types.ts';
|
|
3
3
|
export type UploadFileCallback<UnchainedAPI = unknown> = (file: UploadedFile, unchainedAPI: UnchainedAPI) => Promise<void>;
|
|
4
4
|
export declare const FileDirector: {
|
|
5
5
|
registerFileUploadCallback<UnchainedAPI>(directoryName: string, fn: UploadFileCallback<UnchainedAPI>): void;
|
|
@@ -11,4 +11,3 @@ export declare const FileDirector: {
|
|
|
11
11
|
registerAdapter: (A: IFileAdapter<unknown>) => void;
|
|
12
12
|
unregisterAdapter: (key: string) => boolean;
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=FileDirector.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import buildHashedFilename from './build-hashed-filename.
|
|
2
|
-
import resolveExpirationDate from './put-expiration.
|
|
3
|
-
export { FileDirector } from './director/FileDirector.
|
|
4
|
-
export { FileAdapter, IFileAdapter } from './director/FileAdapter.
|
|
1
|
+
import buildHashedFilename from './build-hashed-filename.ts';
|
|
2
|
+
import resolveExpirationDate from './put-expiration.ts';
|
|
3
|
+
export { FileDirector } from './director/FileDirector.ts';
|
|
4
|
+
export { FileAdapter, type IFileAdapter } from './director/FileAdapter.ts';
|
|
5
5
|
export { buildHashedFilename, resolveExpirationDate };
|
|
6
|
-
export type * from './types.
|
|
7
|
-
//# sourceMappingURL=file-upload-index.d.ts.map
|
|
6
|
+
export type * from './types.ts';
|
package/lib/file-upload-index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import buildHashedFilename from
|
|
2
|
-
import resolveExpirationDate from
|
|
3
|
-
export { FileDirector } from
|
|
4
|
-
export { FileAdapter } from
|
|
1
|
+
import buildHashedFilename from "./build-hashed-filename.js";
|
|
2
|
+
import resolveExpirationDate from "./put-expiration.js";
|
|
3
|
+
export { FileDirector } from "./director/FileDirector.js";
|
|
4
|
+
export { FileAdapter } from "./director/FileAdapter.js";
|
|
5
5
|
export { buildHashedFilename, resolveExpirationDate };
|
|
6
|
-
//# sourceMappingURL=file-upload-index.js.map
|
package/lib/put-expiration.d.ts
CHANGED
package/lib/put-expiration.js
CHANGED
|
@@ -2,4 +2,3 @@ const { UNCHAINED_PUT_URL_EXPIRY = '86400000' } = process.env;
|
|
|
2
2
|
export const expiryOffsetInMs = () => parseInt(UNCHAINED_PUT_URL_EXPIRY, 10) || 24 * 60 * 60 * 1000;
|
|
3
3
|
const resolveExpirationDate = () => new Date(new Date().getTime() + expiryOffsetInMs());
|
|
4
4
|
export default resolveExpirationDate;
|
|
5
|
-
//# sourceMappingURL=put-expiration.js.map
|
package/lib/types.d.ts
CHANGED
package/lib/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unchainedshop/file-upload",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"main": "lib/file-upload-index.js",
|
|
5
5
|
"types": "lib/file-upload-index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
7
8
|
"scripts": {
|
|
8
9
|
"clean": "tsc -b --clean",
|
|
9
10
|
"build": "tsc -b",
|
|
10
11
|
"prepublishOnly": "npm run clean && npm run build",
|
|
11
12
|
"watch": "tsc -w",
|
|
12
|
-
"test": "
|
|
13
|
-
"test:watch": "
|
|
13
|
+
"test": "node --test",
|
|
14
|
+
"test:watch": "node --test --watch"
|
|
14
15
|
},
|
|
15
16
|
"repository": {
|
|
16
17
|
"type": "git",
|
|
@@ -22,8 +23,10 @@
|
|
|
22
23
|
"core"
|
|
23
24
|
],
|
|
24
25
|
"authors": [
|
|
25
|
-
"
|
|
26
|
-
"
|
|
26
|
+
"Pascal Kaufmann",
|
|
27
|
+
"Mikael Araya",
|
|
28
|
+
"Vedran Rudelj",
|
|
29
|
+
"Joël Meiller"
|
|
27
30
|
],
|
|
28
31
|
"license": "EUPL-1.2",
|
|
29
32
|
"bugs": {
|
|
@@ -31,13 +34,11 @@
|
|
|
31
34
|
},
|
|
32
35
|
"homepage": "https://github.com/unchainedshop/unchained#readme",
|
|
33
36
|
"dependencies": {
|
|
34
|
-
"@unchainedshop/logger": "^4.
|
|
35
|
-
"@unchainedshop/utils": "^4.
|
|
36
|
-
"base-x": "^5.0.1"
|
|
37
|
+
"@unchainedshop/logger": "^4.5.0",
|
|
38
|
+
"@unchainedshop/utils": "^4.5.0"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
39
|
-
"@types/node": "^
|
|
40
|
-
"tsx": "^4.20.3",
|
|
41
|
+
"@types/node": "^25.0.0",
|
|
41
42
|
"typescript": "^5.8.3"
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-hashed-filename.d.ts","sourceRoot":"","sources":["../src/build-hashed-filename.ts"],"names":[],"mappings":"AAKA,wBAA8B,mBAAmB,CAC/C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,IAAI,GACf,OAAO,CAAC,MAAM,CAAC,CAYjB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-hashed-filename.js","sourceRoot":"","sources":["../src/build-hashed-filename.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,MAAM,QAAQ,CAAC;AAE3B,MAAM,GAAG,GAAG,KAAK,CAAC,gEAAgE,CAAC,CAAC;AAEpF,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,mBAAmB,CAC/C,aAAqB,EACrB,QAAgB,EAChB,UAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAEhF,MAAM,gBAAgB,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,gBAAgB,EAAE,GAAG,EAAE,CAAC;IACpC,MAAM,wBAAwB,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IAClE,MAAM,8BAA8B,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,EAAE,GAAG,CAAC;SAC5E,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAExC,OAAO,GAAG,YAAY,IAAI,8BAA8B,EAAE,CAAC;AAC7D,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FileAdapter.d.ts","sourceRoot":"","sources":["../../src/director/FileAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAe,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,YAAY,CAAC,OAAO,GAAG,OAAO,CAAE,SAAQ,YAAY;IACnE,iBAAiB,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnF,eAAe,EAAE,CACf,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC,CAAC,cAAc,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3D,WAAW,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,oBAAoB,EAAE,CACpB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,GAAG,EACZ,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAC1B,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,CACjB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,EACD,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,oBAAoB,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxF;AACD,eAAO,MAAM,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,KAAK,GAAG,OAAO,GAAG,SAAS,CAqBvE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FileAdapter.js","sourceRoot":"","sources":["../../src/director/FileAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAgB,MAAM,sBAAsB,CAAC;AA6BjE,MAAM,CAAC,MAAM,WAAW,GAAoD;IAC1E,GAAG,WAAW;IAEd,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,iBAAiB;QACrB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,oBAAoB;QACxB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,oBAAoB;QACxB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,iBAAiB;QACrB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;CACF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FileDirector.d.ts","sourceRoot":"","sources":["../../src/director/FileDirector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,kBAAkB,CAAC,YAAY,GAAG,OAAO,IAAI,CACvD,IAAI,EAAE,YAAY,EAClB,YAAY,EAAE,YAAY,KACvB,OAAO,CAAC,IAAI,CAAC,CAAC;AAMnB,eAAO,MAAM,YAAY;+BAGI,YAAY,iBAAiB,MAAM,MAAM,kBAAkB,CAAC,YAAY,CAAC;yCAM/D,MAAM;;;;;;;CAG5C,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FileDirector.js","sourceRoot":"","sources":["../../src/director/FileDirector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AASpD,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA8B,CAAC;AAEjE,MAAM,YAAY,GAAG,YAAY,CAAe,cAAc,CAAC,CAAC;AAEhE,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,YAAY;IAEf,0BAA0B,CAAe,aAAqB,EAAE,EAAoC;QAClG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3C,kBAAkB,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,qBAAqB,CAAC,aAAqB;QACzC,OAAO,kBAAkB,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC;IACvD,CAAC;CACF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-upload-index.d.ts","sourceRoot":"","sources":["../src/file-upload-index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAC7D,OAAO,qBAAqB,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,CAAC;AACtD,mBAAmB,YAAY,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-upload-index.js","sourceRoot":"","sources":["../src/file-upload-index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAC7D,OAAO,qBAAqB,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAgB,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"put-expiration.d.ts","sourceRoot":"","sources":["../src/put-expiration.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB,cAAsE,CAAC;AAEpG,QAAA,MAAM,qBAAqB,YAA4D,CAAC;AAExF,eAAe,qBAAqB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"put-expiration.js","sourceRoot":"","sources":["../src/put-expiration.ts"],"names":[],"mappings":"AAAA,MAAM,EAAE,wBAAwB,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAE9D,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,wBAAwB,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpG,MAAM,qBAAqB,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC,CAAC;AAExF,eAAe,qBAAqB,CAAC"}
|
package/lib/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
|
package/lib/types.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { sha1, slugify } from '@unchainedshop/utils';
|
|
2
|
-
import baseX from 'base-x';
|
|
3
|
-
|
|
4
|
-
const b62 = baseX('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
|
5
|
-
|
|
6
|
-
export default async function buildHashedFilename(
|
|
7
|
-
directoryName: string,
|
|
8
|
-
fileName: string,
|
|
9
|
-
expiryDate: Date,
|
|
10
|
-
): Promise<string> {
|
|
11
|
-
const hashed = await sha1(`${directoryName}${fileName}${expiryDate.getTime()}`);
|
|
12
|
-
|
|
13
|
-
const splittedFilename = fileName.split('.');
|
|
14
|
-
const ext = splittedFilename?.pop();
|
|
15
|
-
const fileNameWithoutExtension = splittedFilename.join('.') || '';
|
|
16
|
-
const slugifiedFilenameWithExtension = [slugify(fileNameWithoutExtension), ext]
|
|
17
|
-
.filter(Boolean)
|
|
18
|
-
.join('.');
|
|
19
|
-
const b62converted = b62.encode(hashed);
|
|
20
|
-
|
|
21
|
-
return `${b62converted}-${slugifiedFilenameWithExtension}`;
|
|
22
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import type { Readable } from 'node:stream';
|
|
2
|
-
import { BaseAdapter, IBaseAdapter } from '@unchainedshop/utils';
|
|
3
|
-
import { UploadedFile, UploadFileData } from '../types.js';
|
|
4
|
-
|
|
5
|
-
export interface IFileAdapter<Context = unknown> extends IBaseAdapter {
|
|
6
|
-
createDownloadURL: (file: UploadedFile, expiry?: number) => Promise<string | null>;
|
|
7
|
-
createSignedURL: (
|
|
8
|
-
directoryName: string,
|
|
9
|
-
fileName: string,
|
|
10
|
-
unchainedAPI: Context,
|
|
11
|
-
) => Promise<(UploadFileData & { putURL: string }) | null>;
|
|
12
|
-
removeFiles: (files: UploadedFile[], unchainedContext: Context) => Promise<void>;
|
|
13
|
-
uploadFileFromStream: (
|
|
14
|
-
directoryName: string,
|
|
15
|
-
rawFile: any,
|
|
16
|
-
unchainedAPI: Context,
|
|
17
|
-
options?: Record<string, any>,
|
|
18
|
-
) => Promise<UploadFileData>;
|
|
19
|
-
uploadFileFromURL: (
|
|
20
|
-
directoryName: string,
|
|
21
|
-
fileInput: {
|
|
22
|
-
fileLink: string;
|
|
23
|
-
fileName: string;
|
|
24
|
-
fileId?: string;
|
|
25
|
-
headers?: Record<string, unknown>;
|
|
26
|
-
},
|
|
27
|
-
unchainedAPI: Context,
|
|
28
|
-
) => Promise<UploadFileData>;
|
|
29
|
-
createDownloadStream: (file: UploadedFile, unchainedAPI: Context) => Promise<Readable>;
|
|
30
|
-
}
|
|
31
|
-
export const FileAdapter: Omit<IFileAdapter, 'key' | 'label' | 'version'> = {
|
|
32
|
-
...BaseAdapter,
|
|
33
|
-
|
|
34
|
-
async createSignedURL() {
|
|
35
|
-
throw new Error('Method not implemented');
|
|
36
|
-
},
|
|
37
|
-
async removeFiles() {
|
|
38
|
-
throw new Error('Method not implemented');
|
|
39
|
-
},
|
|
40
|
-
async createDownloadURL() {
|
|
41
|
-
throw new Error('Method not implemented');
|
|
42
|
-
},
|
|
43
|
-
async uploadFileFromStream() {
|
|
44
|
-
throw new Error('Method not implemented');
|
|
45
|
-
},
|
|
46
|
-
async createDownloadStream() {
|
|
47
|
-
throw new Error('Method not implemented');
|
|
48
|
-
},
|
|
49
|
-
async uploadFileFromURL() {
|
|
50
|
-
throw new Error('Method not implemented');
|
|
51
|
-
},
|
|
52
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { BaseDirector } from '@unchainedshop/utils';
|
|
2
|
-
import { IFileAdapter } from './FileAdapter.js';
|
|
3
|
-
import { UploadedFile } from '../types.js';
|
|
4
|
-
|
|
5
|
-
export type UploadFileCallback<UnchainedAPI = unknown> = (
|
|
6
|
-
file: UploadedFile,
|
|
7
|
-
unchainedAPI: UnchainedAPI,
|
|
8
|
-
) => Promise<void>;
|
|
9
|
-
|
|
10
|
-
const FileUploadRegistry = new Map<string, UploadFileCallback>();
|
|
11
|
-
|
|
12
|
-
const baseDirector = BaseDirector<IFileAdapter>('FileDirector');
|
|
13
|
-
|
|
14
|
-
export const FileDirector = {
|
|
15
|
-
...baseDirector,
|
|
16
|
-
|
|
17
|
-
registerFileUploadCallback<UnchainedAPI>(directoryName: string, fn: UploadFileCallback<UnchainedAPI>) {
|
|
18
|
-
if (!FileUploadRegistry.has(directoryName)) {
|
|
19
|
-
FileUploadRegistry.set(directoryName, fn);
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
getFileUploadCallback(directoryName: string) {
|
|
24
|
-
return FileUploadRegistry.get(directoryName) || null;
|
|
25
|
-
},
|
|
26
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test';
|
|
2
|
-
import assert from 'node:assert';
|
|
3
|
-
import buildHashedFilename from './build-hashed-filename.js';
|
|
4
|
-
|
|
5
|
-
describe('File Uploader', () => {
|
|
6
|
-
it('Generate unique file name', async () => {
|
|
7
|
-
assert.equal(
|
|
8
|
-
await buildHashedFilename('root', 'file1.jpg', new Date(new Date('2022-12-04T17:13:09.285Z'))),
|
|
9
|
-
'NFJAbVaH6tRDuGDn4IPZYqh0AP-file1.jpg',
|
|
10
|
-
);
|
|
11
|
-
});
|
|
12
|
-
});
|
package/src/file-upload-index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import buildHashedFilename from './build-hashed-filename.js';
|
|
2
|
-
import resolveExpirationDate from './put-expiration.js';
|
|
3
|
-
|
|
4
|
-
export { FileDirector } from './director/FileDirector.js';
|
|
5
|
-
export { FileAdapter, IFileAdapter } from './director/FileAdapter.js';
|
|
6
|
-
export { buildHashedFilename, resolveExpirationDate };
|
|
7
|
-
export type * from './types.js';
|
package/src/put-expiration.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
const { UNCHAINED_PUT_URL_EXPIRY = '86400000' } = process.env;
|
|
2
|
-
|
|
3
|
-
export const expiryOffsetInMs = () => parseInt(UNCHAINED_PUT_URL_EXPIRY, 10) || 24 * 60 * 60 * 1000;
|
|
4
|
-
|
|
5
|
-
const resolveExpirationDate = () => new Date(new Date().getTime() + expiryOffsetInMs());
|
|
6
|
-
|
|
7
|
-
export default resolveExpirationDate;
|
package/src/types.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export interface UploadFileData {
|
|
2
|
-
_id: string;
|
|
3
|
-
directoryName: string;
|
|
4
|
-
expiryDate: Date | null;
|
|
5
|
-
fileName: string;
|
|
6
|
-
size?: number;
|
|
7
|
-
type: string;
|
|
8
|
-
url: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface UploadedFile {
|
|
12
|
-
_id: string;
|
|
13
|
-
path: string;
|
|
14
|
-
meta?: Record<string, unknown>;
|
|
15
|
-
name: string;
|
|
16
|
-
size?: number;
|
|
17
|
-
type?: string;
|
|
18
|
-
url?: string;
|
|
19
|
-
}
|