@vendure/asset-server-plugin 1.2.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/LICENSE +9 -0
- package/README.md +7 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +17 -0
- package/lib/index.js.map +1 -0
- package/lib/src/common.d.ts +3 -0
- package/lib/src/common.js +22 -0
- package/lib/src/common.js.map +1 -0
- package/lib/src/constants.d.ts +1 -0
- package/lib/src/constants.js +5 -0
- package/lib/src/constants.js.map +1 -0
- package/lib/src/default-asset-storage-strategy-factory.d.ts +6 -0
- package/lib/src/default-asset-storage-strategy-factory.js +22 -0
- package/lib/src/default-asset-storage-strategy-factory.js.map +1 -0
- package/lib/src/file-icon.png +0 -0
- package/lib/src/hashed-asset-naming-strategy.d.ts +21 -0
- package/lib/src/hashed-asset-naming-strategy.js +39 -0
- package/lib/src/hashed-asset-naming-strategy.js.map +1 -0
- package/lib/src/local-asset-storage-strategy.d.ts +25 -0
- package/lib/src/local-asset-storage-strategy.js +68 -0
- package/lib/src/local-asset-storage-strategy.js.map +1 -0
- package/lib/src/plugin.d.ts +141 -0
- package/lib/src/plugin.js +317 -0
- package/lib/src/plugin.js.map +1 -0
- package/lib/src/s3-asset-storage-strategy.d.ts +118 -0
- package/lib/src/s3-asset-storage-strategy.js +222 -0
- package/lib/src/s3-asset-storage-strategy.js.map +1 -0
- package/lib/src/sharp-asset-preview-strategy.d.ts +11 -0
- package/lib/src/sharp-asset-preview-strategy.js +64 -0
- package/lib/src/sharp-asset-preview-strategy.js.map +1 -0
- package/lib/src/transform-image.d.ts +24 -0
- package/lib/src/transform-image.js +102 -0
- package/lib/src/transform-image.js.map +1 -0
- package/lib/src/types.d.ts +98 -0
- package/lib/src/types.js +3 -0
- package/lib/src/types.js.map +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Michael Bromley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Vendure AssetServerPlugin
|
|
2
|
+
|
|
3
|
+
The `AssetServerPlugin` serves assets (images and other files) from the local file system. It can also perform on-the-fly image transformations and caches the results for subsequent calls.
|
|
4
|
+
|
|
5
|
+
`npm install @vendure/asset-server-plugin`
|
|
6
|
+
|
|
7
|
+
For documentation, see [www.vendure.io/docs/typescript-api/asset-server-plugin](https://www.vendure.io/docs/typescript-api/asset-server-plugin/)
|
package/lib/index.d.ts
ADDED
package/lib/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./src/plugin"), exports);
|
|
14
|
+
__exportStar(require("./src/s3-asset-storage-strategy"), exports);
|
|
15
|
+
__exportStar(require("./src/sharp-asset-preview-strategy"), exports);
|
|
16
|
+
__exportStar(require("./src/types"), exports);
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kEAAgD;AAChD,qEAAmD;AACnD,8CAA4B"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAssetUrlPrefixFn = void 0;
|
|
4
|
+
const constants_1 = require("@vendure/core/dist/common/constants");
|
|
5
|
+
function getAssetUrlPrefixFn(options) {
|
|
6
|
+
const { assetUrlPrefix, route } = options;
|
|
7
|
+
if (assetUrlPrefix == null) {
|
|
8
|
+
return (request, identifier) => `${request.protocol}://${request.get('host')}/${route}/`;
|
|
9
|
+
}
|
|
10
|
+
if (typeof assetUrlPrefix === 'string') {
|
|
11
|
+
return (...args) => assetUrlPrefix;
|
|
12
|
+
}
|
|
13
|
+
if (typeof assetUrlPrefix === 'function') {
|
|
14
|
+
return (request, identifier) => {
|
|
15
|
+
const ctx = request[constants_1.REQUEST_CONTEXT_KEY];
|
|
16
|
+
return assetUrlPrefix(ctx, identifier);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`The assetUrlPrefix option was of an unexpected type: ${JSON.stringify(assetUrlPrefix)}`);
|
|
20
|
+
}
|
|
21
|
+
exports.getAssetUrlPrefixFn = getAssetUrlPrefixFn;
|
|
22
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/common.ts"],"names":[],"mappings":";;;AAAA,mEAA0E;AAK1E,SAAgB,mBAAmB,CAAC,OAA2B;IAC3D,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1C,IAAI,cAAc,IAAI,IAAI,EAAE;QACxB,OAAO,CAAC,OAAgB,EAAE,UAAkB,EAAE,EAAE,CAC5C,GAAG,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,GAAG,CAAC;KAChE;IACD,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE;QACpC,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE,CAAC,cAAc,CAAC;KAC7C;IACD,IAAI,OAAO,cAAc,KAAK,UAAU,EAAE;QACtC,OAAO,CAAC,OAAgB,EAAE,UAAkB,EAAE,EAAE;YAC5C,MAAM,GAAG,GAAI,OAAe,CAAC,+BAAmB,CAAC,CAAC;YAClD,OAAO,cAAc,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC3C,CAAC,CAAC;KACL;IACD,MAAM,IAAI,KAAK,CAAC,wDAAwD,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;AAC9G,CAAC;AAhBD,kDAgBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const loggerCtx = "AssetServerPlugin";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,SAAS,GAAG,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { LocalAssetStorageStrategy } from './local-asset-storage-strategy';
|
|
2
|
+
import { AssetServerOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* By default the AssetServerPlugin will configure and use the LocalStorageStrategy to persist Assets.
|
|
5
|
+
*/
|
|
6
|
+
export declare function defaultAssetStorageStrategyFactory(options: AssetServerOptions): LocalAssetStorageStrategy;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultAssetStorageStrategyFactory = void 0;
|
|
4
|
+
const common_1 = require("./common");
|
|
5
|
+
const local_asset_storage_strategy_1 = require("./local-asset-storage-strategy");
|
|
6
|
+
/**
|
|
7
|
+
* By default the AssetServerPlugin will configure and use the LocalStorageStrategy to persist Assets.
|
|
8
|
+
*/
|
|
9
|
+
function defaultAssetStorageStrategyFactory(options) {
|
|
10
|
+
const { assetUrlPrefix, assetUploadDir, route } = options;
|
|
11
|
+
const prefixFn = common_1.getAssetUrlPrefixFn(options);
|
|
12
|
+
const toAbsoluteUrlFn = (request, identifier) => {
|
|
13
|
+
if (!identifier) {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
const prefix = prefixFn(request, identifier);
|
|
17
|
+
return identifier.startsWith(prefix) ? identifier : `${prefix}${identifier}`;
|
|
18
|
+
};
|
|
19
|
+
return new local_asset_storage_strategy_1.LocalAssetStorageStrategy(assetUploadDir, toAbsoluteUrlFn);
|
|
20
|
+
}
|
|
21
|
+
exports.defaultAssetStorageStrategyFactory = defaultAssetStorageStrategyFactory;
|
|
22
|
+
//# sourceMappingURL=default-asset-storage-strategy-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-asset-storage-strategy-factory.js","sourceRoot":"","sources":["../../src/default-asset-storage-strategy-factory.ts"],"names":[],"mappings":";;;AAEA,qCAA+C;AAC/C,iFAA2E;AAG3E;;GAEG;AACH,SAAgB,kCAAkC,CAAC,OAA2B;IAC1E,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1D,MAAM,QAAQ,GAAG,4BAAmB,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,eAAe,GAAG,CAAC,OAAgB,EAAE,UAAkB,EAAU,EAAE;QACrE,IAAI,CAAC,UAAU,EAAE;YACb,OAAO,EAAE,CAAC;SACb;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC7C,OAAO,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,UAAU,EAAE,CAAC;IACjF,CAAC,CAAC;IACF,OAAO,IAAI,wDAAyB,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;AAC1E,CAAC;AAXD,gFAWC"}
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DefaultAssetNamingStrategy, RequestContext } from '@vendure/core';
|
|
2
|
+
/**
|
|
3
|
+
* @description
|
|
4
|
+
* An extension of the {@link DefaultAssetNamingStrategy} which prefixes file names with
|
|
5
|
+
* the type (`'source'` or `'preview'`) as well as a 2-character sub-directory based on
|
|
6
|
+
* the md5 hash of the original file name.
|
|
7
|
+
*
|
|
8
|
+
* This is an implementation of the technique knows as "hashed directory" file storage,
|
|
9
|
+
* and the purpose is to reduce the number of files in a single directory, since a very large
|
|
10
|
+
* number of files can lead to performance issues when reading and writing to that directory.
|
|
11
|
+
*
|
|
12
|
+
* With this strategory, even with 200,000 total assets stored, each directory would
|
|
13
|
+
* only contain less than 800 files.
|
|
14
|
+
*
|
|
15
|
+
* @docsCategory AssetServerPlugin
|
|
16
|
+
*/
|
|
17
|
+
export declare class HashedAssetNamingStrategy extends DefaultAssetNamingStrategy {
|
|
18
|
+
generateSourceFileName(ctx: RequestContext, originalFileName: string, conflictFileName?: string): string;
|
|
19
|
+
generatePreviewFileName(ctx: RequestContext, originalFileName: string, conflictFileName?: string): string;
|
|
20
|
+
private getHashedDir;
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.HashedAssetNamingStrategy = void 0;
|
|
7
|
+
const core_1 = require("@vendure/core");
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
/**
|
|
11
|
+
* @description
|
|
12
|
+
* An extension of the {@link DefaultAssetNamingStrategy} which prefixes file names with
|
|
13
|
+
* the type (`'source'` or `'preview'`) as well as a 2-character sub-directory based on
|
|
14
|
+
* the md5 hash of the original file name.
|
|
15
|
+
*
|
|
16
|
+
* This is an implementation of the technique knows as "hashed directory" file storage,
|
|
17
|
+
* and the purpose is to reduce the number of files in a single directory, since a very large
|
|
18
|
+
* number of files can lead to performance issues when reading and writing to that directory.
|
|
19
|
+
*
|
|
20
|
+
* With this strategory, even with 200,000 total assets stored, each directory would
|
|
21
|
+
* only contain less than 800 files.
|
|
22
|
+
*
|
|
23
|
+
* @docsCategory AssetServerPlugin
|
|
24
|
+
*/
|
|
25
|
+
class HashedAssetNamingStrategy extends core_1.DefaultAssetNamingStrategy {
|
|
26
|
+
generateSourceFileName(ctx, originalFileName, conflictFileName) {
|
|
27
|
+
const filename = super.generateSourceFileName(ctx, originalFileName, conflictFileName);
|
|
28
|
+
return path_1.default.join('source', this.getHashedDir(filename), filename);
|
|
29
|
+
}
|
|
30
|
+
generatePreviewFileName(ctx, originalFileName, conflictFileName) {
|
|
31
|
+
const filename = super.generatePreviewFileName(ctx, originalFileName, conflictFileName);
|
|
32
|
+
return path_1.default.join('preview', this.getHashedDir(filename), filename);
|
|
33
|
+
}
|
|
34
|
+
getHashedDir(filename) {
|
|
35
|
+
return crypto_1.createHash('md5').update(filename).digest('hex').slice(0, 2);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.HashedAssetNamingStrategy = HashedAssetNamingStrategy;
|
|
39
|
+
//# sourceMappingURL=hashed-asset-naming-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hashed-asset-naming-strategy.js","sourceRoot":"","sources":["../../src/hashed-asset-naming-strategy.ts"],"names":[],"mappings":";;;;;;AAAA,wCAA2E;AAC3E,mCAAoC;AACpC,gDAAwB;AAExB;;;;;;;;;;;;;;GAcG;AACH,MAAa,yBAA0B,SAAQ,iCAA0B;IACrE,sBAAsB,CAAC,GAAmB,EAAE,gBAAwB,EAAE,gBAAyB;QAC3F,MAAM,QAAQ,GAAG,KAAK,CAAC,sBAAsB,CAAC,GAAG,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QACvF,OAAO,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IACtE,CAAC;IACD,uBAAuB,CACnB,GAAmB,EACnB,gBAAwB,EACxB,gBAAyB;QAEzB,MAAM,QAAQ,GAAG,KAAK,CAAC,uBAAuB,CAAC,GAAG,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QACxF,OAAO,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvE,CAAC;IAEO,YAAY,CAAC,QAAgB;QACjC,OAAO,mBAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxE,CAAC;CACJ;AAjBD,8DAiBC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { AssetStorageStrategy } from '@vendure/core';
|
|
3
|
+
import { Request } from 'express';
|
|
4
|
+
import { ReadStream } from 'fs';
|
|
5
|
+
import { Stream } from 'stream';
|
|
6
|
+
/**
|
|
7
|
+
* @description
|
|
8
|
+
* A persistence strategy which saves files to the local file system.
|
|
9
|
+
*
|
|
10
|
+
* @docsCategory AssetServerPlugin
|
|
11
|
+
*/
|
|
12
|
+
export declare class LocalAssetStorageStrategy implements AssetStorageStrategy {
|
|
13
|
+
private readonly uploadPath;
|
|
14
|
+
private readonly toAbsoluteUrlFn?;
|
|
15
|
+
toAbsoluteUrl: ((reqest: Request, identifier: string) => string) | undefined;
|
|
16
|
+
constructor(uploadPath: string, toAbsoluteUrlFn?: ((reqest: Request, identifier: string) => string) | undefined);
|
|
17
|
+
writeFileFromStream(fileName: string, data: ReadStream): Promise<string>;
|
|
18
|
+
writeFileFromBuffer(fileName: string, data: Buffer): Promise<string>;
|
|
19
|
+
fileExists(fileName: string): Promise<boolean>;
|
|
20
|
+
readFileToBuffer(identifier: string): Promise<Buffer>;
|
|
21
|
+
readFileToStream(identifier: string): Promise<Stream>;
|
|
22
|
+
deleteFile(identifier: string): Promise<void>;
|
|
23
|
+
private filePathToIdentifier;
|
|
24
|
+
private identifierToFilePath;
|
|
25
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LocalAssetStorageStrategy = void 0;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
/**
|
|
10
|
+
* @description
|
|
11
|
+
* A persistence strategy which saves files to the local file system.
|
|
12
|
+
*
|
|
13
|
+
* @docsCategory AssetServerPlugin
|
|
14
|
+
*/
|
|
15
|
+
class LocalAssetStorageStrategy {
|
|
16
|
+
constructor(uploadPath, toAbsoluteUrlFn) {
|
|
17
|
+
this.uploadPath = uploadPath;
|
|
18
|
+
this.toAbsoluteUrlFn = toAbsoluteUrlFn;
|
|
19
|
+
fs_extra_1.default.ensureDirSync(this.uploadPath);
|
|
20
|
+
if (toAbsoluteUrlFn) {
|
|
21
|
+
this.toAbsoluteUrl = toAbsoluteUrlFn;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async writeFileFromStream(fileName, data) {
|
|
25
|
+
const filePath = path_1.default.join(this.uploadPath, fileName);
|
|
26
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
27
|
+
const writeStream = fs_extra_1.default.createWriteStream(filePath, 'binary');
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
data.pipe(writeStream);
|
|
30
|
+
writeStream.on('close', () => resolve(this.filePathToIdentifier(filePath)));
|
|
31
|
+
writeStream.on('error', reject);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async writeFileFromBuffer(fileName, data) {
|
|
35
|
+
const filePath = path_1.default.join(this.uploadPath, fileName);
|
|
36
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
37
|
+
await fs_extra_1.default.writeFile(filePath, data, 'binary');
|
|
38
|
+
return this.filePathToIdentifier(filePath);
|
|
39
|
+
}
|
|
40
|
+
fileExists(fileName) {
|
|
41
|
+
return new Promise(resolve => {
|
|
42
|
+
fs_extra_1.default.access(this.identifierToFilePath(fileName), fs_extra_1.default.constants.F_OK, err => {
|
|
43
|
+
resolve(!err);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
readFileToBuffer(identifier) {
|
|
48
|
+
return fs_extra_1.default.readFile(this.identifierToFilePath(identifier));
|
|
49
|
+
}
|
|
50
|
+
readFileToStream(identifier) {
|
|
51
|
+
const readStream = fs_extra_1.default.createReadStream(this.identifierToFilePath(identifier), 'binary');
|
|
52
|
+
return Promise.resolve(readStream);
|
|
53
|
+
}
|
|
54
|
+
deleteFile(identifier) {
|
|
55
|
+
return fs_extra_1.default.unlink(this.identifierToFilePath(identifier));
|
|
56
|
+
}
|
|
57
|
+
filePathToIdentifier(filePath) {
|
|
58
|
+
const filePathDirname = path_1.default.dirname(filePath);
|
|
59
|
+
const deltaDirname = filePathDirname.replace(this.uploadPath, '');
|
|
60
|
+
const identifier = path_1.default.join(deltaDirname, path_1.default.basename(filePath));
|
|
61
|
+
return identifier.replace(/^[\\/]+/, '');
|
|
62
|
+
}
|
|
63
|
+
identifierToFilePath(identifier) {
|
|
64
|
+
return path_1.default.join(this.uploadPath, identifier);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.LocalAssetStorageStrategy = LocalAssetStorageStrategy;
|
|
68
|
+
//# sourceMappingURL=local-asset-storage-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-asset-storage-strategy.js","sourceRoot":"","sources":["../../src/local-asset-storage-strategy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { MiddlewareConsumer, NestModule, OnApplicationBootstrap } from '@nestjs/common';
|
|
2
|
+
import { Type } from '@vendure/common/lib/shared-types';
|
|
3
|
+
import { ProcessContext, RuntimeVendureConfig } from '@vendure/core';
|
|
4
|
+
import { AssetServerOptions } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* @description
|
|
7
|
+
* The `AssetServerPlugin` serves assets (images and other files) from the local file system, and can also be configured to use
|
|
8
|
+
* other storage strategies (e.g. {@link S3AssetStorageStrategy}. It can also perform on-the-fly image transformations
|
|
9
|
+
* and caches the results for subsequent calls.
|
|
10
|
+
*
|
|
11
|
+
* ## Installation
|
|
12
|
+
*
|
|
13
|
+
* `yarn add \@vendure/asset-server-plugin`
|
|
14
|
+
*
|
|
15
|
+
* or
|
|
16
|
+
*
|
|
17
|
+
* `npm install \@vendure/asset-server-plugin`
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { AssetServerPlugin } from '\@vendure/asset-server-plugin';
|
|
22
|
+
*
|
|
23
|
+
* const config: VendureConfig = {
|
|
24
|
+
* // Add an instance of the plugin to the plugins array
|
|
25
|
+
* plugins: [
|
|
26
|
+
* AssetServerPlugin.init({
|
|
27
|
+
* route: 'assets',
|
|
28
|
+
* assetUploadDir: path.join(__dirname, 'assets'),
|
|
29
|
+
* port: 4000,
|
|
30
|
+
* }),
|
|
31
|
+
* ],
|
|
32
|
+
* };
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* The full configuration is documented at [AssetServerOptions]({{< relref "asset-server-options" >}})
|
|
36
|
+
*
|
|
37
|
+
* ## Image transformation
|
|
38
|
+
*
|
|
39
|
+
* Asset preview images can be transformed (resized & cropped) on the fly by appending query parameters to the url:
|
|
40
|
+
*
|
|
41
|
+
* `http://localhost:3000/assets/some-asset.jpg?w=500&h=300&mode=resize`
|
|
42
|
+
*
|
|
43
|
+
* The above URL will return `some-asset.jpg`, resized to fit in the bounds of a 500px x 300px rectangle.
|
|
44
|
+
*
|
|
45
|
+
* ### Preview mode
|
|
46
|
+
*
|
|
47
|
+
* The `mode` parameter can be either `crop` or `resize`. See the [ImageTransformMode]({{< relref "image-transform-mode" >}}) docs for details.
|
|
48
|
+
*
|
|
49
|
+
* ### Focal point
|
|
50
|
+
*
|
|
51
|
+
* When cropping an image (`mode=crop`), Vendure will attempt to keep the most "interesting" area of the image in the cropped frame. It does this
|
|
52
|
+
* by finding the area of the image with highest entropy (the busiest area of the image). However, sometimes this does not yield a satisfactory
|
|
53
|
+
* result - part or all of the main subject may still be cropped out.
|
|
54
|
+
*
|
|
55
|
+
* This is where specifying the focal point can help. The focal point of the image may be specified by passing the `fpx` and `fpy` query parameters.
|
|
56
|
+
* These are normalized coordinates (i.e. a number between 0 and 1), so the `fpx=0&fpy=0` corresponds to the top left of the image.
|
|
57
|
+
*
|
|
58
|
+
* For example, let's say there is a very wide landscape image which we want to crop to be square. The main subject is a house to the far left of the
|
|
59
|
+
* image. The following query would crop it to a square with the house centered:
|
|
60
|
+
*
|
|
61
|
+
* `http://localhost:3000/assets/landscape.jpg?w=150&h=150&mode=crop&fpx=0.2&fpy=0.7`
|
|
62
|
+
*
|
|
63
|
+
* ### Transform presets
|
|
64
|
+
*
|
|
65
|
+
* Presets can be defined which allow a single preset name to be used instead of specifying the width, height and mode. Presets are
|
|
66
|
+
* configured via the AssetServerOptions [presets property]({{< relref "asset-server-options" >}}#presets).
|
|
67
|
+
*
|
|
68
|
+
* For example, defining the following preset:
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* new AssetServerPlugin({
|
|
72
|
+
* // ...
|
|
73
|
+
* presets: [
|
|
74
|
+
* { name: 'my-preset', width: 85, height: 85, mode: 'crop' },
|
|
75
|
+
* ],
|
|
76
|
+
* }),
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* means that a request to:
|
|
80
|
+
*
|
|
81
|
+
* `http://localhost:3000/assets/some-asset.jpg?preset=my-preset`
|
|
82
|
+
*
|
|
83
|
+
* is equivalent to:
|
|
84
|
+
*
|
|
85
|
+
* `http://localhost:3000/assets/some-asset.jpg?w=85&h=85&mode=crop`
|
|
86
|
+
*
|
|
87
|
+
* The AssetServerPlugin comes pre-configured with the following presets:
|
|
88
|
+
*
|
|
89
|
+
* name | width | height | mode
|
|
90
|
+
* -----|-------|--------|-----
|
|
91
|
+
* tiny | 50px | 50px | crop
|
|
92
|
+
* thumb | 150px | 150px | crop
|
|
93
|
+
* small | 300px | 300px | resize
|
|
94
|
+
* medium | 500px | 500px | resize
|
|
95
|
+
* large | 800px | 800px | resize
|
|
96
|
+
*
|
|
97
|
+
* ### Caching
|
|
98
|
+
* By default, the AssetServerPlugin will cache every transformed image, so that the transformation only needs to be performed a single time for
|
|
99
|
+
* a given configuration. Caching can be disabled per-request by setting the `?cache=false` query parameter.
|
|
100
|
+
*
|
|
101
|
+
* @docsCategory AssetServerPlugin
|
|
102
|
+
*/
|
|
103
|
+
export declare class AssetServerPlugin implements NestModule, OnApplicationBootstrap {
|
|
104
|
+
private processContext;
|
|
105
|
+
private static assetStorage;
|
|
106
|
+
private readonly cacheDir;
|
|
107
|
+
private presets;
|
|
108
|
+
private static options;
|
|
109
|
+
/**
|
|
110
|
+
* @description
|
|
111
|
+
* Set the plugin options.
|
|
112
|
+
*/
|
|
113
|
+
static init(options: AssetServerOptions): Type<AssetServerPlugin>;
|
|
114
|
+
/** @internal */
|
|
115
|
+
static configure(config: RuntimeVendureConfig): Promise<RuntimeVendureConfig>;
|
|
116
|
+
constructor(processContext: ProcessContext);
|
|
117
|
+
/** @internal */
|
|
118
|
+
onApplicationBootstrap(): void | Promise<void>;
|
|
119
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
120
|
+
/**
|
|
121
|
+
* Creates the image server instance
|
|
122
|
+
*/
|
|
123
|
+
private createAssetServer;
|
|
124
|
+
/**
|
|
125
|
+
* Reads the file requested and send the response to the browser.
|
|
126
|
+
*/
|
|
127
|
+
private sendAsset;
|
|
128
|
+
/**
|
|
129
|
+
* If an exception was thrown by the first handler, then it may be because a transformed image
|
|
130
|
+
* is being requested which does not yet exist. In this case, this handler will generate the
|
|
131
|
+
* transformed image, save it to cache, and serve the result as a response.
|
|
132
|
+
*/
|
|
133
|
+
private generateTransformedImage;
|
|
134
|
+
private getFileNameFromRequest;
|
|
135
|
+
private md5;
|
|
136
|
+
private addSuffix;
|
|
137
|
+
/**
|
|
138
|
+
* Attempt to get the mime type from the file name.
|
|
139
|
+
*/
|
|
140
|
+
private getMimeType;
|
|
141
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
var AssetServerPlugin_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.AssetServerPlugin = void 0;
|
|
17
|
+
const core_1 = require("@vendure/core");
|
|
18
|
+
const crypto_1 = require("crypto");
|
|
19
|
+
const express_1 = __importDefault(require("express"));
|
|
20
|
+
const file_type_1 = require("file-type");
|
|
21
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
22
|
+
const path_1 = __importDefault(require("path"));
|
|
23
|
+
const constants_1 = require("./constants");
|
|
24
|
+
const default_asset_storage_strategy_factory_1 = require("./default-asset-storage-strategy-factory");
|
|
25
|
+
const hashed_asset_naming_strategy_1 = require("./hashed-asset-naming-strategy");
|
|
26
|
+
const sharp_asset_preview_strategy_1 = require("./sharp-asset-preview-strategy");
|
|
27
|
+
const transform_image_1 = require("./transform-image");
|
|
28
|
+
/**
|
|
29
|
+
* @description
|
|
30
|
+
* The `AssetServerPlugin` serves assets (images and other files) from the local file system, and can also be configured to use
|
|
31
|
+
* other storage strategies (e.g. {@link S3AssetStorageStrategy}. It can also perform on-the-fly image transformations
|
|
32
|
+
* and caches the results for subsequent calls.
|
|
33
|
+
*
|
|
34
|
+
* ## Installation
|
|
35
|
+
*
|
|
36
|
+
* `yarn add \@vendure/asset-server-plugin`
|
|
37
|
+
*
|
|
38
|
+
* or
|
|
39
|
+
*
|
|
40
|
+
* `npm install \@vendure/asset-server-plugin`
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { AssetServerPlugin } from '\@vendure/asset-server-plugin';
|
|
45
|
+
*
|
|
46
|
+
* const config: VendureConfig = {
|
|
47
|
+
* // Add an instance of the plugin to the plugins array
|
|
48
|
+
* plugins: [
|
|
49
|
+
* AssetServerPlugin.init({
|
|
50
|
+
* route: 'assets',
|
|
51
|
+
* assetUploadDir: path.join(__dirname, 'assets'),
|
|
52
|
+
* port: 4000,
|
|
53
|
+
* }),
|
|
54
|
+
* ],
|
|
55
|
+
* };
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* The full configuration is documented at [AssetServerOptions]({{< relref "asset-server-options" >}})
|
|
59
|
+
*
|
|
60
|
+
* ## Image transformation
|
|
61
|
+
*
|
|
62
|
+
* Asset preview images can be transformed (resized & cropped) on the fly by appending query parameters to the url:
|
|
63
|
+
*
|
|
64
|
+
* `http://localhost:3000/assets/some-asset.jpg?w=500&h=300&mode=resize`
|
|
65
|
+
*
|
|
66
|
+
* The above URL will return `some-asset.jpg`, resized to fit in the bounds of a 500px x 300px rectangle.
|
|
67
|
+
*
|
|
68
|
+
* ### Preview mode
|
|
69
|
+
*
|
|
70
|
+
* The `mode` parameter can be either `crop` or `resize`. See the [ImageTransformMode]({{< relref "image-transform-mode" >}}) docs for details.
|
|
71
|
+
*
|
|
72
|
+
* ### Focal point
|
|
73
|
+
*
|
|
74
|
+
* When cropping an image (`mode=crop`), Vendure will attempt to keep the most "interesting" area of the image in the cropped frame. It does this
|
|
75
|
+
* by finding the area of the image with highest entropy (the busiest area of the image). However, sometimes this does not yield a satisfactory
|
|
76
|
+
* result - part or all of the main subject may still be cropped out.
|
|
77
|
+
*
|
|
78
|
+
* This is where specifying the focal point can help. The focal point of the image may be specified by passing the `fpx` and `fpy` query parameters.
|
|
79
|
+
* These are normalized coordinates (i.e. a number between 0 and 1), so the `fpx=0&fpy=0` corresponds to the top left of the image.
|
|
80
|
+
*
|
|
81
|
+
* For example, let's say there is a very wide landscape image which we want to crop to be square. The main subject is a house to the far left of the
|
|
82
|
+
* image. The following query would crop it to a square with the house centered:
|
|
83
|
+
*
|
|
84
|
+
* `http://localhost:3000/assets/landscape.jpg?w=150&h=150&mode=crop&fpx=0.2&fpy=0.7`
|
|
85
|
+
*
|
|
86
|
+
* ### Transform presets
|
|
87
|
+
*
|
|
88
|
+
* Presets can be defined which allow a single preset name to be used instead of specifying the width, height and mode. Presets are
|
|
89
|
+
* configured via the AssetServerOptions [presets property]({{< relref "asset-server-options" >}}#presets).
|
|
90
|
+
*
|
|
91
|
+
* For example, defining the following preset:
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* new AssetServerPlugin({
|
|
95
|
+
* // ...
|
|
96
|
+
* presets: [
|
|
97
|
+
* { name: 'my-preset', width: 85, height: 85, mode: 'crop' },
|
|
98
|
+
* ],
|
|
99
|
+
* }),
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* means that a request to:
|
|
103
|
+
*
|
|
104
|
+
* `http://localhost:3000/assets/some-asset.jpg?preset=my-preset`
|
|
105
|
+
*
|
|
106
|
+
* is equivalent to:
|
|
107
|
+
*
|
|
108
|
+
* `http://localhost:3000/assets/some-asset.jpg?w=85&h=85&mode=crop`
|
|
109
|
+
*
|
|
110
|
+
* The AssetServerPlugin comes pre-configured with the following presets:
|
|
111
|
+
*
|
|
112
|
+
* name | width | height | mode
|
|
113
|
+
* -----|-------|--------|-----
|
|
114
|
+
* tiny | 50px | 50px | crop
|
|
115
|
+
* thumb | 150px | 150px | crop
|
|
116
|
+
* small | 300px | 300px | resize
|
|
117
|
+
* medium | 500px | 500px | resize
|
|
118
|
+
* large | 800px | 800px | resize
|
|
119
|
+
*
|
|
120
|
+
* ### Caching
|
|
121
|
+
* By default, the AssetServerPlugin will cache every transformed image, so that the transformation only needs to be performed a single time for
|
|
122
|
+
* a given configuration. Caching can be disabled per-request by setting the `?cache=false` query parameter.
|
|
123
|
+
*
|
|
124
|
+
* @docsCategory AssetServerPlugin
|
|
125
|
+
*/
|
|
126
|
+
let AssetServerPlugin = AssetServerPlugin_1 = class AssetServerPlugin {
|
|
127
|
+
constructor(processContext) {
|
|
128
|
+
this.processContext = processContext;
|
|
129
|
+
this.cacheDir = 'cache';
|
|
130
|
+
this.presets = [
|
|
131
|
+
{ name: 'tiny', width: 50, height: 50, mode: 'crop' },
|
|
132
|
+
{ name: 'thumb', width: 150, height: 150, mode: 'crop' },
|
|
133
|
+
{ name: 'small', width: 300, height: 300, mode: 'resize' },
|
|
134
|
+
{ name: 'medium', width: 500, height: 500, mode: 'resize' },
|
|
135
|
+
{ name: 'large', width: 800, height: 800, mode: 'resize' },
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* @description
|
|
140
|
+
* Set the plugin options.
|
|
141
|
+
*/
|
|
142
|
+
static init(options) {
|
|
143
|
+
AssetServerPlugin_1.options = options;
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
/** @internal */
|
|
147
|
+
static async configure(config) {
|
|
148
|
+
const storageStrategyFactory = this.options.storageStrategyFactory || default_asset_storage_strategy_factory_1.defaultAssetStorageStrategyFactory;
|
|
149
|
+
this.assetStorage = await storageStrategyFactory(this.options);
|
|
150
|
+
config.assetOptions.assetPreviewStrategy = new sharp_asset_preview_strategy_1.SharpAssetPreviewStrategy({
|
|
151
|
+
maxWidth: this.options.previewMaxWidth || 1600,
|
|
152
|
+
maxHeight: this.options.previewMaxHeight || 1600,
|
|
153
|
+
});
|
|
154
|
+
config.assetOptions.assetStorageStrategy = this.assetStorage;
|
|
155
|
+
config.assetOptions.assetNamingStrategy =
|
|
156
|
+
this.options.namingStrategy || new hashed_asset_naming_strategy_1.HashedAssetNamingStrategy();
|
|
157
|
+
return config;
|
|
158
|
+
}
|
|
159
|
+
/** @internal */
|
|
160
|
+
onApplicationBootstrap() {
|
|
161
|
+
if (this.processContext.isWorker) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (AssetServerPlugin_1.options.presets) {
|
|
165
|
+
for (const preset of AssetServerPlugin_1.options.presets) {
|
|
166
|
+
const existingIndex = this.presets.findIndex(p => p.name === preset.name);
|
|
167
|
+
if (-1 < existingIndex) {
|
|
168
|
+
this.presets.splice(existingIndex, 1, preset);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.presets.push(preset);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const cachePath = path_1.default.join(AssetServerPlugin_1.options.assetUploadDir, this.cacheDir);
|
|
176
|
+
fs_extra_1.default.ensureDirSync(cachePath);
|
|
177
|
+
}
|
|
178
|
+
configure(consumer) {
|
|
179
|
+
if (this.processContext.isWorker) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
core_1.Logger.info('Creating asset server middleware', constants_1.loggerCtx);
|
|
183
|
+
consumer.apply(this.createAssetServer()).forRoutes(AssetServerPlugin_1.options.route);
|
|
184
|
+
core_1.registerPluginStartupMessage('Asset server', AssetServerPlugin_1.options.route);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Creates the image server instance
|
|
188
|
+
*/
|
|
189
|
+
createAssetServer() {
|
|
190
|
+
const assetServer = express_1.default.Router();
|
|
191
|
+
assetServer.use(this.sendAsset(), this.generateTransformedImage());
|
|
192
|
+
return assetServer;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Reads the file requested and send the response to the browser.
|
|
196
|
+
*/
|
|
197
|
+
sendAsset() {
|
|
198
|
+
return async (req, res, next) => {
|
|
199
|
+
var _a;
|
|
200
|
+
const key = this.getFileNameFromRequest(req);
|
|
201
|
+
try {
|
|
202
|
+
const file = await AssetServerPlugin_1.assetStorage.readFileToBuffer(key);
|
|
203
|
+
let mimeType = this.getMimeType(key);
|
|
204
|
+
if (!mimeType) {
|
|
205
|
+
mimeType = ((_a = (await file_type_1.fromBuffer(file))) === null || _a === void 0 ? void 0 : _a.mime) || 'application/octet-stream';
|
|
206
|
+
}
|
|
207
|
+
res.contentType(mimeType);
|
|
208
|
+
res.send(file);
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
const err = new Error('File not found');
|
|
212
|
+
err.status = 404;
|
|
213
|
+
return next(err);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* If an exception was thrown by the first handler, then it may be because a transformed image
|
|
219
|
+
* is being requested which does not yet exist. In this case, this handler will generate the
|
|
220
|
+
* transformed image, save it to cache, and serve the result as a response.
|
|
221
|
+
*/
|
|
222
|
+
generateTransformedImage() {
|
|
223
|
+
return async (err, req, res, next) => {
|
|
224
|
+
if (err && (err.status === 404 || err.statusCode === 404)) {
|
|
225
|
+
if (req.query) {
|
|
226
|
+
core_1.Logger.debug(`Pre-cached Asset not found: ${req.path}`, constants_1.loggerCtx);
|
|
227
|
+
let file;
|
|
228
|
+
try {
|
|
229
|
+
file = await AssetServerPlugin_1.assetStorage.readFileToBuffer(req.path);
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
res.status(404).send('Resource not found');
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const image = await transform_image_1.transformImage(file, req.query, this.presets || []);
|
|
236
|
+
try {
|
|
237
|
+
const imageBuffer = await image.toBuffer();
|
|
238
|
+
if (!req.query.cache || req.query.cache === 'true') {
|
|
239
|
+
const cachedFileName = this.getFileNameFromRequest(req);
|
|
240
|
+
await AssetServerPlugin_1.assetStorage.writeFileFromBuffer(cachedFileName, imageBuffer);
|
|
241
|
+
core_1.Logger.debug(`Saved cached asset: ${cachedFileName}`, constants_1.loggerCtx);
|
|
242
|
+
}
|
|
243
|
+
res.set('Content-Type', `image/${(await image.metadata()).format}`);
|
|
244
|
+
res.send(imageBuffer);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
catch (e) {
|
|
248
|
+
core_1.Logger.error(e, 'AssetServerPlugin', e.stack);
|
|
249
|
+
res.status(500).send(e.message);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
next();
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
getFileNameFromRequest(req) {
|
|
258
|
+
const { w, h, mode, preset, fpx, fpy } = req.query;
|
|
259
|
+
const focalPoint = fpx && fpy ? `_fpx${fpx}_fpy${fpy}` : '';
|
|
260
|
+
let imageParamHash = null;
|
|
261
|
+
if (w || h) {
|
|
262
|
+
const width = w || '';
|
|
263
|
+
const height = h || '';
|
|
264
|
+
imageParamHash = this.md5(`_transform_w${width}_h${height}_m${mode}${focalPoint}`);
|
|
265
|
+
}
|
|
266
|
+
else if (preset) {
|
|
267
|
+
if (this.presets && !!this.presets.find(p => p.name === preset)) {
|
|
268
|
+
imageParamHash = this.md5(`_transform_pre_${preset}${focalPoint}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (imageParamHash) {
|
|
272
|
+
return path_1.default.join(this.cacheDir, this.addSuffix(req.path, imageParamHash));
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
return req.path;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
md5(input) {
|
|
279
|
+
return crypto_1.createHash('md5').update(input).digest('hex');
|
|
280
|
+
}
|
|
281
|
+
addSuffix(fileName, suffix) {
|
|
282
|
+
const ext = path_1.default.extname(fileName);
|
|
283
|
+
const baseName = path_1.default.basename(fileName, ext);
|
|
284
|
+
const dirName = path_1.default.dirname(fileName);
|
|
285
|
+
return path_1.default.join(dirName, `${baseName}${suffix}${ext}`);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Attempt to get the mime type from the file name.
|
|
289
|
+
*/
|
|
290
|
+
getMimeType(fileName) {
|
|
291
|
+
const ext = path_1.default.extname(fileName);
|
|
292
|
+
switch (ext) {
|
|
293
|
+
case '.jpg':
|
|
294
|
+
case '.jpeg':
|
|
295
|
+
return 'image/jpeg';
|
|
296
|
+
case '.png':
|
|
297
|
+
return 'image/png';
|
|
298
|
+
case '.gif':
|
|
299
|
+
return 'image/gif';
|
|
300
|
+
case '.svg':
|
|
301
|
+
return 'image/svg+xml';
|
|
302
|
+
case '.tiff':
|
|
303
|
+
return 'image/tiff';
|
|
304
|
+
case '.webp':
|
|
305
|
+
return 'image/webp';
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
AssetServerPlugin = AssetServerPlugin_1 = __decorate([
|
|
310
|
+
core_1.VendurePlugin({
|
|
311
|
+
imports: [core_1.PluginCommonModule],
|
|
312
|
+
configuration: config => AssetServerPlugin_1.configure(config),
|
|
313
|
+
}),
|
|
314
|
+
__metadata("design:paramtypes", [core_1.ProcessContext])
|
|
315
|
+
], AssetServerPlugin);
|
|
316
|
+
exports.AssetServerPlugin = AssetServerPlugin;
|
|
317
|
+
//# sourceMappingURL=plugin.js.map
|