@shopware-ag/app-server-sdk 1.1.25 → 1.1.27
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/commonjs/helper/media.d.ts +17 -0
- package/dist/commonjs/helper/media.d.ts.map +1 -1
- package/dist/commonjs/helper/media.js +41 -0
- package/dist/commonjs/helper/media.js.map +1 -1
- package/dist/commonjs/integration/cloudflare-kv.d.ts +22 -4
- package/dist/commonjs/integration/cloudflare-kv.d.ts.map +1 -1
- package/dist/commonjs/integration/cloudflare-kv.js +80 -11
- package/dist/commonjs/integration/cloudflare-kv.js.map +1 -1
- package/dist/commonjs/integration/hono.d.ts +22 -9
- package/dist/commonjs/integration/hono.d.ts.map +1 -1
- package/dist/commonjs/integration/hono.js +24 -19
- package/dist/commonjs/integration/hono.js.map +1 -1
- package/dist/esm/helper/media.d.ts +17 -0
- package/dist/esm/helper/media.d.ts.map +1 -1
- package/dist/esm/helper/media.js +40 -0
- package/dist/esm/helper/media.js.map +1 -1
- package/dist/esm/integration/cloudflare-kv.d.ts +22 -4
- package/dist/esm/integration/cloudflare-kv.d.ts.map +1 -1
- package/dist/esm/integration/cloudflare-kv.js +78 -10
- package/dist/esm/integration/cloudflare-kv.js.map +1 -1
- package/dist/esm/integration/hono.d.ts +22 -9
- package/dist/esm/integration/hono.d.ts.map +1 -1
- package/dist/esm/integration/hono.js +24 -19
- package/dist/esm/integration/hono.js.map +1 -1
- package/package.json +1 -1
|
@@ -16,6 +16,23 @@ export declare function uploadMediaFile(httpClient: HttpClient, { private: isPri
|
|
|
16
16
|
fileName: string;
|
|
17
17
|
file: Blob | Promise<Blob>;
|
|
18
18
|
}): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Uploads a media file by URL to the Shopware instance.
|
|
21
|
+
*
|
|
22
|
+
* @param {HttpClient} httpClient - The HTTP client instance.
|
|
23
|
+
* @param {Object} options - The options for uploading the media file.
|
|
24
|
+
* @param {boolean} [options.private] - Whether the media file should be private.
|
|
25
|
+
* @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.
|
|
26
|
+
* @param {string} options.fileName - The name of the file to upload.
|
|
27
|
+
* @param {string} options.url - The URL of the media file to upload.
|
|
28
|
+
* @returns {Promise<string>} - The ID of the uploaded media file.
|
|
29
|
+
*/
|
|
30
|
+
export declare function uploadMediaByUrl(httpClient: HttpClient, { private: isPrivate, mediaFolderId, fileName, url, }: {
|
|
31
|
+
private?: boolean;
|
|
32
|
+
mediaFolderId?: string | null;
|
|
33
|
+
fileName: string;
|
|
34
|
+
url: string;
|
|
35
|
+
}): Promise<string>;
|
|
19
36
|
/**
|
|
20
37
|
* Retrieves the default media folder ID for a given entity.
|
|
21
38
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,UAAU,EACtB,EACC,OAAO,EAAE,SAAiB,EAC1B,aAAoB,EACpB,QAAQ,EACR,IAAI,GACJ,EAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,GACC,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAClD,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED,UAAU,wBAAwB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,wBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,UAAU,EACtB,EACC,OAAO,EAAE,SAAiB,EAC1B,aAAoB,EACpB,QAAQ,EACR,IAAI,GACJ,EAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,GACC,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACrC,UAAU,EAAE,UAAU,EACtB,EACC,OAAO,EAAE,SAAiB,EAC1B,aAAoB,EACpB,QAAQ,EACR,GAAG,GACH,EAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACZ,GACC,OAAO,CAAC,MAAM,CAAC,CAmCjB;AAGD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAClD,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED,UAAU,wBAAwB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,wBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadMediaFile = uploadMediaFile;
|
|
4
|
+
exports.uploadMediaByUrl = uploadMediaByUrl;
|
|
4
5
|
exports.getMediaDefaultFolderByEntity = getMediaDefaultFolderByEntity;
|
|
5
6
|
exports.getMediaFolderByName = getMediaFolderByName;
|
|
6
7
|
exports.createMediaFolder = createMediaFolder;
|
|
@@ -48,6 +49,46 @@ async function uploadMediaFile(httpClient, { private: isPrivate = false, mediaFo
|
|
|
48
49
|
throw e;
|
|
49
50
|
}
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Uploads a media file by URL to the Shopware instance.
|
|
54
|
+
*
|
|
55
|
+
* @param {HttpClient} httpClient - The HTTP client instance.
|
|
56
|
+
* @param {Object} options - The options for uploading the media file.
|
|
57
|
+
* @param {boolean} [options.private] - Whether the media file should be private.
|
|
58
|
+
* @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.
|
|
59
|
+
* @param {string} options.fileName - The name of the file to upload.
|
|
60
|
+
* @param {string} options.url - The URL of the media file to upload.
|
|
61
|
+
* @returns {Promise<string>} - The ID of the uploaded media file.
|
|
62
|
+
*/
|
|
63
|
+
async function uploadMediaByUrl(httpClient, { private: isPrivate = false, mediaFolderId = null, fileName, url, }) {
|
|
64
|
+
const repository = new admin_api_js_1.EntityRepository(httpClient, "media");
|
|
65
|
+
const mediaId = (0, admin_api_js_1.uuid)();
|
|
66
|
+
await repository.upsert([
|
|
67
|
+
{
|
|
68
|
+
id: mediaId,
|
|
69
|
+
private: isPrivate,
|
|
70
|
+
mediaFolderId,
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
const splitFileName = fileName.split(".");
|
|
74
|
+
if (splitFileName.length < 2) {
|
|
75
|
+
throw new Error("Invalid file name, should have an extension");
|
|
76
|
+
}
|
|
77
|
+
const extension = (splitFileName.slice(-1)[0] || "").toLowerCase();
|
|
78
|
+
const baseFileName = splitFileName.slice(0, -1).join(".");
|
|
79
|
+
try {
|
|
80
|
+
await httpClient.post(`/_action/media/${mediaId}/upload`, {
|
|
81
|
+
fileName: baseFileName,
|
|
82
|
+
extension,
|
|
83
|
+
url,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
await repository.delete([{ id: mediaId }]);
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
return mediaId;
|
|
91
|
+
}
|
|
51
92
|
/**
|
|
52
93
|
* Retrieves the default media folder ID for a given entity.
|
|
53
94
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":";;AAeA,0CAwDC;
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HttpClient } from \"../http-client.js\";\nimport { EntityRepository, uuid } from \"./admin-api.js\";\nimport { Criteria } from \"./criteria.js\";\n\n/**\n * Uploads a media file to the Shopware instance.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {Object} options - The options for uploading the media file.\n * @param {boolean} [options.private] - Whether the media file should be private.\n * @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.\n * @param {string} options.fileName - The name of the file to upload.\n * @param {Blob|Promise<Blob>} options.file - The file to upload.\n * @returns {Promise<string>} - The ID of the uploaded media file.\n */\nexport async function uploadMediaFile(\n\thttpClient: HttpClient,\n\t{\n\t\tprivate: isPrivate = false,\n\t\tmediaFolderId = null,\n\t\tfileName,\n\t\tfile,\n\t}: {\n\t\tprivate?: boolean;\n\t\tmediaFolderId?: string | null;\n\t\tfileName: string;\n\t\tfile: Blob | Promise<Blob>;\n\t},\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media\");\n\n\tconst mediaId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaId,\n\t\t\tprivate: isPrivate,\n\t\t\tmediaFolderId,\n\t\t},\n\t]);\n\n\tconst splitFileName = fileName.split(\".\");\n\n\tif (splitFileName.length < 2) {\n\t\tthrow new Error(\"Invalid file name, should have an extension\");\n\t}\n\n\tconst extension = (splitFileName.slice(-1)[0] || \"\").toLowerCase();\n\tconst baseFileName = splitFileName.slice(0, -1).join(\".\");\n\n\tconst params = new URLSearchParams();\n\n\tparams.append(\"extension\", extension);\n\tparams.append(\"fileName\", baseFileName);\n\n\tconst resolved = await file;\n\n\ttry {\n\t\tawait httpClient.post(\n\t\t\t`/_action/media/${mediaId}/upload?${params.toString()}`,\n\t\t\tresolved,\n\t\t\t{\n\t\t\t\t\"Content-Type\": resolved.type,\n\t\t\t},\n\t\t);\n\n\t\treturn mediaId;\n\t} catch (e) {\n\t\tawait repository.delete([{ id: mediaId }]);\n\t\tthrow e;\n\t}\n}\n\n/**\n * Uploads a media file by URL to the Shopware instance.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {Object} options - The options for uploading the media file.\n * @param {boolean} [options.private] - Whether the media file should be private.\n * @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.\n * @param {string} options.fileName - The name of the file to upload.\n * @param {string} options.url - The URL of the media file to upload.\n * @returns {Promise<string>} - The ID of the uploaded media file.\n */\nexport async function uploadMediaByUrl(\n\thttpClient: HttpClient,\n\t{\n\t\tprivate: isPrivate = false,\n\t\tmediaFolderId = null,\n\t\tfileName,\n\t\turl,\n\t}: {\n\t\tprivate?: boolean;\n\t\tmediaFolderId?: string | null;\n\t\tfileName: string;\n\t\turl: string;\n\t},\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media\");\n\n\tconst mediaId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaId,\n\t\t\tprivate: isPrivate,\n\t\t\tmediaFolderId,\n\t\t},\n\t]);\n\n\tconst splitFileName = fileName.split(\".\");\n\n\tif (splitFileName.length < 2) {\n\t\tthrow new Error(\"Invalid file name, should have an extension\");\n\t}\n\n\tconst extension = (splitFileName.slice(-1)[0] || \"\").toLowerCase();\n\tconst baseFileName = splitFileName.slice(0, -1).join(\".\");\n\n\ttry {\n\t\tawait httpClient.post(`/_action/media/${mediaId}/upload`, {\n\t\t\tfileName: baseFileName,\n\t\t\textension,\n\t\t\turl,\n\t\t});\n\t} catch (e) {\n\t\tawait repository.delete([{ id: mediaId }]);\n\n\t\tthrow e;\n\t}\n\n\treturn mediaId;\n}\n\n\n/**\n * Retrieves the default media folder ID for a given entity.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} entity - The entity name to get the default folder ID for.\n * @returns {Promise<string|null>} - The ID of the default media folder or null if not found.\n */\nexport async function getMediaDefaultFolderByEntity(\n\thttpClient: HttpClient,\n\tentity: string,\n): Promise<string | null> {\n\tconst mediaDefaultFolder = new EntityRepository<{ folder: { id: string } }>(\n\t\thttpClient,\n\t\t\"media_default_folder\",\n\t);\n\n\tconst criteria = new Criteria();\n\tcriteria.addFilter(Criteria.equals(\"entity\", entity));\n\n\tconst folders = await mediaDefaultFolder.search(criteria);\n\n\tconst firstFolder = folders.first();\n\treturn firstFolder?.folder?.id || null;\n}\n\n/**\n * Retrieves the media folder ID by its name.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} name - The name of the media folder.\n * @returns {Promise<string|null>} - The ID of the media folder or null if not found.\n */\nexport async function getMediaFolderByName(\n\thttpClient: HttpClient,\n\tname: string,\n): Promise<string | null> {\n\tconst mediaFolder = new EntityRepository<{ id: string }>(\n\t\thttpClient,\n\t\t\"media_folder\",\n\t);\n\n\tconst criteria = new Criteria();\n\tcriteria.addFilter(Criteria.equals(\"name\", name));\n\n\tconst folders = await mediaFolder.search(criteria);\n\n\treturn folders.first()?.id || null;\n}\n\ninterface CreateMediaFolderOptions {\n\tparentId?: string;\n}\n\n/**\n * Creates a new media folder.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} name - The name of the media folder.\n * @param {Object} options - Additional options for creating the media folder.\n * @param {string} [options.parentId] - The ID of the parent folder, if any.\n * @returns {Promise<string>} - The ID of the newly created media folder.\n */\nexport async function createMediaFolder(\n\thttpClient: HttpClient,\n\tname: string,\n\toptions: CreateMediaFolderOptions,\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media_folder\");\n\n\tconst mediaFolderId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaFolderId,\n\t\t\tname,\n\t\t\tparentId: options.parentId || null,\n\t\t\tconfiguration: {},\n\t\t},\n\t]);\n\n\treturn mediaFolderId;\n}\n"]}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { SimpleShop } from "../repository.js";
|
|
2
2
|
import type { ShopRepositoryInterface } from "../repository.js";
|
|
3
|
+
import type { HttpClientTokenCacheInterface, HttpClientTokenCacheItem } from "../http-client.js";
|
|
3
4
|
/**
|
|
4
5
|
* Cloudflare KV integration
|
|
5
6
|
* @module
|
|
6
7
|
*/
|
|
7
|
-
/**
|
|
8
|
-
* Cloudflare KV implementation of the ShopRepositoryInterface
|
|
9
|
-
*/
|
|
10
8
|
export declare class CloudflareShopRepository implements ShopRepositoryInterface<SimpleShop> {
|
|
11
9
|
private storage;
|
|
12
10
|
constructor(storage: KVNamespace);
|
|
@@ -15,6 +13,26 @@ export declare class CloudflareShopRepository implements ShopRepositoryInterface
|
|
|
15
13
|
getShopById(id: string): Promise<SimpleShop | null>;
|
|
16
14
|
updateShop(shop: SimpleShop): Promise<void>;
|
|
17
15
|
protected serializeShop(shop: SimpleShop): string;
|
|
18
|
-
protected deserializeShop(data: string): SimpleShop;
|
|
16
|
+
protected deserializeShop(data: (string | null)[]): SimpleShop;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Cloudflare KV implementation for HttpClientTokenCacheInterface
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export declare class CloudflareHttpClientTokenCache implements HttpClientTokenCacheInterface {
|
|
23
|
+
private storage;
|
|
24
|
+
constructor(storage: KVNamespace);
|
|
25
|
+
/**
|
|
26
|
+
* Get a token from KV storage for the given shop ID
|
|
27
|
+
*/
|
|
28
|
+
getToken(shopId: string): Promise<HttpClientTokenCacheItem | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Store a token in KV storage for the given shop ID
|
|
31
|
+
*/
|
|
32
|
+
setToken(shopId: string, token: HttpClientTokenCacheItem): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Remove a token from KV storage for the given shop ID
|
|
35
|
+
*/
|
|
36
|
+
clearToken(shopId: string): Promise<void>;
|
|
19
37
|
}
|
|
20
38
|
//# sourceMappingURL=cloudflare-kv.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-kv.d.ts","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.d.ts","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAEjG;;;GAGG;AACH,qBAAa,wBACZ,YAAW,uBAAuB,CAAC,UAAU,CAAC;IAElC,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAIlC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAcnD,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIjD,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,UAAU;CA4B9D;AAED;;;GAGG;AACH,qBAAa,8BAA+B,YAAW,6BAA6B;IACvE,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAIxC;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;IAmBxE;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9E;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C"}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CloudflareShopRepository = void 0;
|
|
3
|
+
exports.CloudflareHttpClientTokenCache = exports.CloudflareShopRepository = void 0;
|
|
4
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
4
5
|
const repository_js_1 = require("../repository.js");
|
|
5
6
|
/**
|
|
6
7
|
* Cloudflare KV integration
|
|
7
8
|
* @module
|
|
8
9
|
*/
|
|
9
|
-
/**
|
|
10
|
-
* Cloudflare KV implementation of the ShopRepositoryInterface
|
|
11
|
-
*/
|
|
12
10
|
class CloudflareShopRepository {
|
|
13
11
|
storage;
|
|
14
12
|
constructor(storage) {
|
|
@@ -16,34 +14,105 @@ class CloudflareShopRepository {
|
|
|
16
14
|
this.storage = storage;
|
|
17
15
|
}
|
|
18
16
|
async createShop(id, url, secret) {
|
|
19
|
-
await
|
|
17
|
+
await Promise.all([
|
|
18
|
+
this.storage.put(id, this.serializeShop(new repository_js_1.SimpleShop(id, url, secret))),
|
|
19
|
+
this.storage.put(`${id}_active`, JSON.stringify(false)),
|
|
20
|
+
]);
|
|
20
21
|
}
|
|
21
22
|
async deleteShop(id) {
|
|
22
|
-
await
|
|
23
|
+
await Promise.all([
|
|
24
|
+
this.storage.delete(id),
|
|
25
|
+
this.storage.delete(`${id}_active`),
|
|
26
|
+
this.storage.delete(`${id}_credentials`)
|
|
27
|
+
]);
|
|
23
28
|
}
|
|
24
29
|
async getShopById(id) {
|
|
25
|
-
const
|
|
26
|
-
|
|
30
|
+
const kvValues = await Promise.all([
|
|
31
|
+
this.storage.get(id),
|
|
32
|
+
this.storage.get(`${id}_active`),
|
|
33
|
+
this.storage.get(`${id}_credentials`)
|
|
34
|
+
]);
|
|
35
|
+
if (kvValues[0] === null) {
|
|
27
36
|
return null;
|
|
28
37
|
}
|
|
29
|
-
return this.deserializeShop(
|
|
38
|
+
return this.deserializeShop(kvValues);
|
|
30
39
|
}
|
|
31
40
|
async updateShop(shop) {
|
|
32
|
-
await
|
|
41
|
+
await Promise.all([
|
|
42
|
+
this.storage.put(shop.getShopId(), this.serializeShop(shop)),
|
|
43
|
+
this.storage.put(`${shop.getShopId()}_active`, JSON.stringify(shop.getShopActive())),
|
|
44
|
+
this.storage.put(`${shop.getShopId()}_credentials`, JSON.stringify({ clientId: shop.getShopClientId(), clientSecret: shop.getShopClientSecret() }))
|
|
45
|
+
]);
|
|
33
46
|
}
|
|
34
47
|
serializeShop(shop) {
|
|
35
48
|
return JSON.stringify(shop);
|
|
36
49
|
}
|
|
37
50
|
deserializeShop(data) {
|
|
38
|
-
const obj = JSON.parse(data);
|
|
51
|
+
const obj = JSON.parse(data[0]);
|
|
39
52
|
const shop = new repository_js_1.SimpleShop(obj.shopId || "", obj.shopUrl || "", obj.shopSecret || "");
|
|
40
53
|
shop.setShopCredentials(obj.shopClientId || "", obj.shopClientSecret || "");
|
|
41
54
|
if (obj.shopActive === undefined) {
|
|
42
55
|
obj.shopActive = true;
|
|
43
56
|
}
|
|
44
57
|
shop.setShopActive(obj.shopActive);
|
|
58
|
+
if (data[1] !== null) {
|
|
59
|
+
shop.setShopActive(JSON.parse(data[1]));
|
|
60
|
+
}
|
|
61
|
+
if (data[2] !== null) {
|
|
62
|
+
const credentials = JSON.parse(data[2]);
|
|
63
|
+
shop.setShopCredentials(credentials.clientId || "", credentials.clientSecret || "");
|
|
64
|
+
}
|
|
45
65
|
return shop;
|
|
46
66
|
}
|
|
47
67
|
}
|
|
48
68
|
exports.CloudflareShopRepository = CloudflareShopRepository;
|
|
69
|
+
/**
|
|
70
|
+
* Cloudflare KV implementation for HttpClientTokenCacheInterface
|
|
71
|
+
* @module
|
|
72
|
+
*/
|
|
73
|
+
class CloudflareHttpClientTokenCache {
|
|
74
|
+
storage;
|
|
75
|
+
constructor(storage) {
|
|
76
|
+
this.storage = storage;
|
|
77
|
+
this.storage = storage;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get a token from KV storage for the given shop ID
|
|
81
|
+
*/
|
|
82
|
+
async getToken(shopId) {
|
|
83
|
+
const tokenData = await this.storage.get(`token_${shopId}`);
|
|
84
|
+
if (tokenData === null) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const parsedToken = JSON.parse(tokenData);
|
|
89
|
+
// Convert the ISO string back to a Date object
|
|
90
|
+
return {
|
|
91
|
+
token: parsedToken.token,
|
|
92
|
+
expiresIn: new Date(parsedToken.expiresIn)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Store a token in KV storage for the given shop ID
|
|
101
|
+
*/
|
|
102
|
+
async setToken(shopId, token) {
|
|
103
|
+
// Convert the Date object to an ISO string for storage
|
|
104
|
+
const tokenData = {
|
|
105
|
+
token: token.token,
|
|
106
|
+
expiresIn: token.expiresIn.toISOString()
|
|
107
|
+
};
|
|
108
|
+
await this.storage.put(`token_${shopId}`, JSON.stringify(tokenData));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Remove a token from KV storage for the given shop ID
|
|
112
|
+
*/
|
|
113
|
+
async clearToken(shopId) {
|
|
114
|
+
await this.storage.delete(`token_${shopId}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
exports.CloudflareHttpClientTokenCache = CloudflareHttpClientTokenCache;
|
|
49
118
|
//# sourceMappingURL=cloudflare-kv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":";;;AAAA,oDAA8C;
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"@cloudflare/workers-types\" />\nimport { SimpleShop } from \"../repository.js\";\nimport type { ShopRepositoryInterface } from \"../repository.js\";\nimport type { HttpClientTokenCacheInterface, HttpClientTokenCacheItem } from \"../http-client.js\";\n\n/**\n * Cloudflare KV integration\n * @module\n */\nexport class CloudflareShopRepository\n\timplements ShopRepositoryInterface<SimpleShop>\n{\n\tconstructor(private storage: KVNamespace) {\n\t\tthis.storage = storage;\n\t}\n\n\tasync createShop(id: string, url: string, secret: string): Promise<void> {\n\t\tawait Promise.all([\n this.storage.put(id, this.serializeShop(new SimpleShop(id, url, secret))),\n this.storage.put(`${id}_active`, JSON.stringify(false)),\n ])\n\t}\n\n\tasync deleteShop(id: string): Promise<void> {\n\t\tawait Promise.all([\n this.storage.delete(id),\n this.storage.delete(`${id}_active`),\n this.storage.delete(`${id}_credentials`)\n ])\n\t}\n\n\tasync getShopById(id: string): Promise<SimpleShop | null> {\n\t\tconst kvValues = await Promise.all([\n this.storage.get(id),\n this.storage.get(`${id}_active`),\n this.storage.get(`${id}_credentials`)\n ]);\n\n\t\tif (kvValues[0] === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.deserializeShop(kvValues);\n\t}\n\n\tasync updateShop(shop: SimpleShop): Promise<void> {\n\t\tawait Promise.all([\n this.storage.put(shop.getShopId(), this.serializeShop(shop)),\n this.storage.put(`${shop.getShopId()}_active`, JSON.stringify(shop.getShopActive())),\n this.storage.put(`${shop.getShopId()}_credentials`, JSON.stringify({ clientId: shop.getShopClientId(), clientSecret: shop.getShopClientSecret() }))\n ])\n\t}\n\n\tprotected serializeShop(shop: SimpleShop): string {\n\t\treturn JSON.stringify(shop);\n\t}\n\n\tprotected deserializeShop(data: (string | null)[]): SimpleShop {\n\t\tconst obj = JSON.parse(data[0]!);\n\n\t\tconst shop = new SimpleShop(\n\t\t\tobj.shopId || \"\",\n\t\t\tobj.shopUrl || \"\",\n\t\t\tobj.shopSecret || \"\",\n\t\t);\n\n\t\tshop.setShopCredentials(obj.shopClientId || \"\", obj.shopClientSecret || \"\");\n\n\t\tif (obj.shopActive === undefined) {\n\t\t\tobj.shopActive = true;\n\t\t}\n\n\t\tshop.setShopActive(obj.shopActive);\n\n\t\tif (data[1] !== null) {\n\t\t\tshop.setShopActive(JSON.parse(data[1]!));\n\t\t}\n\n\t\tif (data[2] !== null) {\n\t\t\tconst credentials = JSON.parse(data[2]!);\n\t\t\tshop.setShopCredentials(credentials.clientId || \"\", credentials.clientSecret || \"\");\n\t\t}\n\n\t\treturn shop;\n\t}\n}\n\n/**\n * Cloudflare KV implementation for HttpClientTokenCacheInterface\n * @module\n */\nexport class CloudflareHttpClientTokenCache implements HttpClientTokenCacheInterface {\n\tconstructor(private storage: KVNamespace) {\n\t\tthis.storage = storage;\n\t}\n\n\t/**\n\t * Get a token from KV storage for the given shop ID\n\t */\n\tasync getToken(shopId: string): Promise<HttpClientTokenCacheItem | null> {\n\t\tconst tokenData = await this.storage.get(`token_${shopId}`);\n\t\t\n\t\tif (tokenData === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parsedToken = JSON.parse(tokenData);\n\t\t\t// Convert the ISO string back to a Date object\n\t\t\treturn {\n\t\t\t\ttoken: parsedToken.token,\n\t\t\t\texpiresIn: new Date(parsedToken.expiresIn)\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Store a token in KV storage for the given shop ID\n\t */\n\tasync setToken(shopId: string, token: HttpClientTokenCacheItem): Promise<void> {\n\t\t// Convert the Date object to an ISO string for storage\n\t\tconst tokenData = {\n\t\t\ttoken: token.token,\n\t\t\texpiresIn: token.expiresIn.toISOString()\n\t\t};\n\t\t\n\t\tawait this.storage.put(`token_${shopId}`, JSON.stringify(tokenData));\n\t}\n\n\t/**\n\t * Remove a token from KV storage for the given shop ID\n\t */\n\tasync clearToken(shopId: string): Promise<void> {\n\t\tawait this.storage.delete(`token_${shopId}`);\n\t}\n}\n"]}
|
|
@@ -10,15 +10,7 @@ declare module "hono" {
|
|
|
10
10
|
context: Context<ShopInterface, unknown>;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
interface
|
|
14
|
-
/**
|
|
15
|
-
* The name of the app
|
|
16
|
-
*/
|
|
17
|
-
appName: string | ((c: HonoContext) => string);
|
|
18
|
-
/**
|
|
19
|
-
* The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.
|
|
20
|
-
*/
|
|
21
|
-
appSecret: string | ((c: HonoContext) => string);
|
|
13
|
+
interface MiddlewareConfigBase {
|
|
22
14
|
/**
|
|
23
15
|
* The URL of the app. This is the base URL of the app. This will automatically determined by default
|
|
24
16
|
*/
|
|
@@ -89,10 +81,30 @@ interface MiddlewareConfig {
|
|
|
89
81
|
* }
|
|
90
82
|
*/
|
|
91
83
|
appIframeRedirects?: Record<string, string>;
|
|
84
|
+
}
|
|
85
|
+
interface AppServerConfig {
|
|
86
|
+
/**
|
|
87
|
+
* An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.
|
|
88
|
+
*/
|
|
89
|
+
appServer: AppServer;
|
|
90
|
+
}
|
|
91
|
+
interface ParametersConfig {
|
|
92
|
+
/**
|
|
93
|
+
* The name of the app
|
|
94
|
+
*/
|
|
95
|
+
appName: string | ((c: HonoContext) => string);
|
|
96
|
+
/**
|
|
97
|
+
* The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.
|
|
98
|
+
*/
|
|
99
|
+
appSecret: string | ((c: HonoContext) => string);
|
|
92
100
|
/**
|
|
93
101
|
* The repository to fetch and store the shop data
|
|
94
102
|
*/
|
|
95
103
|
shopRepository: ShopRepositoryInterface | ((c: HonoContext) => ShopRepositoryInterface);
|
|
104
|
+
/**
|
|
105
|
+
* An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.
|
|
106
|
+
*/
|
|
107
|
+
appServer?: undefined;
|
|
96
108
|
/**
|
|
97
109
|
* The token cache to use for the HttpClient. This is used to cache the access token for the shopware shop. If you don't provide a token cache, the HttpClient will use an in-memory cache.
|
|
98
110
|
*/
|
|
@@ -102,6 +114,7 @@ interface MiddlewareConfig {
|
|
|
102
114
|
*/
|
|
103
115
|
setup?: (app: AppServer) => void;
|
|
104
116
|
}
|
|
117
|
+
type MiddlewareConfig = (MiddlewareConfigBase & AppServerConfig) | (MiddlewareConfigBase & ParametersConfig);
|
|
105
118
|
/**
|
|
106
119
|
* Configure the Hono server to handle the app registration and context resolution
|
|
107
120
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AACzD,OAAO,EAAc,KAAK,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AACzD,OAAO,EAAc,KAAK,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAGD,UAAU,oBAAoB;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAGD,UAAU,eAAe;IACxB;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,gBAAgB;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C;;OAEG;IACH,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IACjD;;OAEG;IACH,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;IAEjD;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;OAEG;IACH,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,6BAA6B,CAAC,CAAC;IAE3G;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;CACjC;AAGD,KAAK,gBAAgB,GAClB,CAAC,oBAAoB,GAAG,eAAe,CAAC,GACxC,CAAC,oBAAoB,GAAG,gBAAgB,CAAC,CAAC;AAE7C;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QAwMnE"}
|
|
@@ -22,26 +22,31 @@ function configureAppServer(hono, cfg) {
|
|
|
22
22
|
cfg.appIframePath = cfg.appIframePath || "/client-api/*";
|
|
23
23
|
hono.use("*", async (ctx, next) => {
|
|
24
24
|
if (app === null) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
cfg.shopRepository = cfg.shopRepository(ctx);
|
|
25
|
+
if (cfg.appServer) {
|
|
26
|
+
app = cfg.appServer;
|
|
28
27
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
else {
|
|
29
|
+
const appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);
|
|
30
|
+
if (typeof cfg.shopRepository === "function") {
|
|
31
|
+
cfg.shopRepository = cfg.shopRepository(ctx);
|
|
32
|
+
}
|
|
33
|
+
if (typeof cfg.appName === "function") {
|
|
34
|
+
cfg.appName = cfg.appName(ctx);
|
|
35
|
+
}
|
|
36
|
+
if (typeof cfg.appSecret === "function") {
|
|
37
|
+
cfg.appSecret = cfg.appSecret(ctx);
|
|
38
|
+
}
|
|
39
|
+
if (typeof cfg.httpClientTokenCache === "function") {
|
|
40
|
+
cfg.httpClientTokenCache = cfg.httpClientTokenCache(ctx);
|
|
41
|
+
}
|
|
42
|
+
app = new app_js_1.AppServer({
|
|
43
|
+
appName: cfg.appName,
|
|
44
|
+
appSecret: cfg.appSecret,
|
|
45
|
+
authorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,
|
|
46
|
+
}, cfg.shopRepository, cfg.httpClientTokenCache);
|
|
47
|
+
if (cfg.setup) {
|
|
48
|
+
cfg.setup(app);
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
// @ts-ignore
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSignedCookie, setSignedCookie } from \"hono/cookie\";\nimport { AppServer } from \"../app.js\";\nimport { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\nimport { HttpClient, type HttpClientTokenCacheInterface } from \"../http-client.js\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\t/**\n\t * The name of the app\n\t */\n\tappName: string | ((c: HonoContext) => string);\n\t/**\n\t * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.\n\t */\n\tappSecret: string | ((c: HonoContext) => string);\n\n\t/**\n\t * The URL of the app. This is the base URL of the app. This will automatically determined by default\n\t */\n\tappUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration endpoint\n\t *\n\t * @default \"/app/register\"\n\t */\n\tregistrationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration confirmation endpoint\n\t *\n\t * @default \"/app/register/confirm\"\n\t */\n\tregisterConfirmationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app installation lifecycle endpoint\n\t *\n\t * @default \"/app/install\"\n\t */\n\tappInstallUrl?: string | null;\n\n\t/**\n\t * The relative url of the app activation lifecycle endpoint\n\t *\n\t * @default \"/app/activate\"\n\t */\n\tappActivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app update lifecycle endpoint\n\t *\n\t * @default \"/app/update\"\n\t */\n\tappUpdateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deactivation lifecycle endpoint\n\t *\n\t * @default \"/app/deactivate\"\n\t */\n\tappDeactivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deletion lifecycle endpoint\n\t *\n\t * @default \"/app/delete\"\n\t */\n\tappDeleteUrl?: string | null;\n\n\t/**\n\t * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed\n\t *\n\t * @default \"/app/*\"\n\t */\n\tappPath?: string | null;\n\n\t/**\n\t * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects\n\t */\n\tappIframeEnable?: boolean;\n\n\t/**\n\t * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by\n\t *\n\t * @default \"/client-api/*\"\n\t */\n\tappIframePath?: string | null;\n\n\t/**\n\t * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.\n\t *\n\t * @default {\n\t * \"/app/module\": \"https://my-static-client-side-app.com\"\n\t * }\n\t */\n\tappIframeRedirects?: Record<string, string>;\n\n\t/**\n\t * The repository to fetch and store the shop data\n\t */\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n\n\t/**\n\t * The token cache to use for the HttpClient. This is used to cache the access token for the shopware shop. If you don't provide a token cache, the HttpClient will use an in-memory cache.\n\t */\n\thttpClientTokenCache?: HttpClientTokenCacheInterface | ((c: HonoContext) => HttpClientTokenCacheInterface);\n\n\t/**\n\t * A callback to setup the app server. It will be called after the app server is created and before the first request is handled\n\t */\n\tsetup?: (app: AppServer) => void;\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\tcfg.appInstallUrl = cfg.appInstallUrl || \"/app/install\";\n\tcfg.appUpdateUrl = cfg.appUpdateUrl || \"/app/update\";\n\n\tcfg.appIframePath = cfg.appIframePath || \"/client-api/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.httpClientTokenCache === \"function\") {\n\t\t\t\tcfg.httpClientTokenCache = cfg.httpClientTokenCache(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t\tcfg.httpClientTokenCache,\n\t\t\t);\n\n\t\t\tif (cfg.setup) {\n\t\t\t\tcfg.setup(app);\n\t\t\t}\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appInstallUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.install(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appUpdateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.update(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n\n\tif (cfg.appIframeEnable) {\n\t\thono.use(cfg.appIframePath, async (ctx, next) => {\n\t\t\tconst shopId = await getSignedCookie(\n\t\t\t\tctx,\n\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t\"shop\",\n\t\t\t);\n\n\t\t\tif (!shopId) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tconst shop = await ctx.get(\"app\").repository.getShopById(shopId);\n\n\t\t\tif (!shop) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tctx.set(\"shop\", shop);\n\t\t\t// @ts-ignore\n\t\t\tctx.set(\"context\", new Context(shop, {}, new HttpClient(shop, ctx.get('app').httpClientTokenCache)));\n\n\t\t\tawait next();\n\t\t});\n\n\t\tfor (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {\n\t\t\thono.get(path, async (ctx) => {\n\t\t\t\tconst url = new URL(ctx.req.url);\n\n\t\t\t\tif (redirect.startsWith(\"/\")) {\n\t\t\t\t\turl.pathname = redirect;\n\t\t\t\t\tredirect = url.toString();\n\t\t\t\t} else {\n\t\t\t\t\tconst newUrl = new URL(redirect);\n\n\t\t\t\t\tfor (const [key, value] of url.searchParams) {\n\t\t\t\t\t\tnewUrl.searchParams.set(key, value);\n\t\t\t\t\t}\n\n\t\t\t\t\tredirect = newUrl.toString();\n\t\t\t\t}\n\n\t\t\t\tawait setSignedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"shop\",\n\t\t\t\t\tctx.get(\"shop\").getShopId(),\n\t\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t);\n\n\t\t\t\treturn ctx.redirect(redirect);\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\tlet protocol = u.protocol;\n\n\tif (typeof process !== \"undefined\" && process.env?.SHOPWARE_APP_SDK_FORCE_HTTPS) {\n\t\tprotocol = \"https:\";\n\t}\n\n\treturn `${protocol}//${u.host}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSignedCookie, setSignedCookie } from \"hono/cookie\";\nimport { AppServer } from \"../app.js\";\nimport { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\nimport { HttpClient, type HttpClientTokenCacheInterface } from \"../http-client.js\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\n// Define a base interface with common parameters\ninterface MiddlewareConfigBase {\n\t/**\n\t * The URL of the app. This is the base URL of the app. This will automatically determined by default\n\t */\n\tappUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration endpoint\n\t *\n\t * @default \"/app/register\"\n\t */\n\tregistrationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration confirmation endpoint\n\t *\n\t * @default \"/app/register/confirm\"\n\t */\n\tregisterConfirmationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app installation lifecycle endpoint\n\t *\n\t * @default \"/app/install\"\n\t */\n\tappInstallUrl?: string | null;\n\n\t/**\n\t * The relative url of the app activation lifecycle endpoint\n\t *\n\t * @default \"/app/activate\"\n\t */\n\tappActivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app update lifecycle endpoint\n\t *\n\t * @default \"/app/update\"\n\t */\n\tappUpdateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deactivation lifecycle endpoint\n\t *\n\t * @default \"/app/deactivate\"\n\t */\n\tappDeactivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deletion lifecycle endpoint\n\t *\n\t * @default \"/app/delete\"\n\t */\n\tappDeleteUrl?: string | null;\n\n\t/**\n\t * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed\n\t *\n\t * @default \"/app/*\"\n\t */\n\tappPath?: string | null;\n\n\t/**\n\t * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects\n\t */\n\tappIframeEnable?: boolean;\n\n\t/**\n\t * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by\n\t *\n\t * @default \"/client-api/*\"\n\t */\n\tappIframePath?: string | null;\n\n\t/**\n\t * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.\n\t *\n\t * @default {\n\t * \"/app/module\": \"https://my-static-client-side-app.com\"\n\t * }\n\t */\n\tappIframeRedirects?: Record<string, string>;\n}\n\n// Define specific interfaces for each configuration option\ninterface AppServerConfig {\n\t/**\n\t * An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.\n\t */\n\tappServer: AppServer;\n}\n\ninterface ParametersConfig {\n\t/**\n\t * The name of the app\n\t */\n\tappName: string | ((c: HonoContext) => string);\n\t/**\n\t * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.\n\t */\n\tappSecret: string | ((c: HonoContext) => string);\n\t/**\n\t * The repository to fetch and store the shop data\n\t */\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n\t\n\t/**\n\t * An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.\n\t */\n\tappServer?: undefined;\n\n\t/**\n\t * The token cache to use for the HttpClient. This is used to cache the access token for the shopware shop. If you don't provide a token cache, the HttpClient will use an in-memory cache.\n\t */\n\thttpClientTokenCache?: HttpClientTokenCacheInterface | ((c: HonoContext) => HttpClientTokenCacheInterface);\n\n\t/**\n\t * A callback to setup the app server. It will be called after the app server is created and before the first request is handled\n\t */\n\tsetup?: (app: AppServer) => void;\n}\n\n// Create a discriminated union type using intersection types\ntype MiddlewareConfig = \n\t| (MiddlewareConfigBase & AppServerConfig)\n\t| (MiddlewareConfigBase & ParametersConfig);\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\tcfg.appInstallUrl = cfg.appInstallUrl || \"/app/install\";\n\tcfg.appUpdateUrl = cfg.appUpdateUrl || \"/app/update\";\n\n\tcfg.appIframePath = cfg.appIframePath || \"/client-api/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tif (cfg.appServer) {\n\t\t\t\tapp = cfg.appServer;\n\t\t\t} else {\n\t\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t\t}\n\n\t\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t\t}\n\n\t\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t\t}\n\n\t\t\t\tif (typeof cfg.httpClientTokenCache === \"function\") {\n\t\t\t\t\tcfg.httpClientTokenCache = cfg.httpClientTokenCache(ctx);\n\t\t\t\t}\n\n\t\t\t\tapp = new AppServer(\n\t\t\t\t\t{\n\t\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t\t},\n\t\t\t\t\tcfg.shopRepository,\n\t\t\t\t\tcfg.httpClientTokenCache,\n\t\t\t\t);\n\n\t\t\t\tif (cfg.setup) {\n\t\t\t\t\tcfg.setup(app);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appInstallUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.install(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appUpdateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.update(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n\n\tif (cfg.appIframeEnable) {\n\t\thono.use(cfg.appIframePath, async (ctx, next) => {\n\t\t\tconst shopId = await getSignedCookie(\n\t\t\t\tctx,\n\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t\"shop\",\n\t\t\t);\n\n\t\t\tif (!shopId) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tconst shop = await ctx.get(\"app\").repository.getShopById(shopId);\n\n\t\t\tif (!shop) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tctx.set(\"shop\", shop);\n\t\t\t// @ts-ignore\n\t\t\tctx.set(\"context\", new Context(shop, {}, new HttpClient(shop, ctx.get('app').httpClientTokenCache)));\n\n\t\t\tawait next();\n\t\t});\n\n\t\tfor (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {\n\t\t\thono.get(path, async (ctx) => {\n\t\t\t\tconst url = new URL(ctx.req.url);\n\n\t\t\t\tif (redirect.startsWith(\"/\")) {\n\t\t\t\t\turl.pathname = redirect;\n\t\t\t\t\tredirect = url.toString();\n\t\t\t\t} else {\n\t\t\t\t\tconst newUrl = new URL(redirect);\n\n\t\t\t\t\tfor (const [key, value] of url.searchParams) {\n\t\t\t\t\t\tnewUrl.searchParams.set(key, value);\n\t\t\t\t\t}\n\n\t\t\t\t\tredirect = newUrl.toString();\n\t\t\t\t}\n\n\t\t\t\tawait setSignedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"shop\",\n\t\t\t\t\tctx.get(\"shop\").getShopId(),\n\t\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t);\n\n\t\t\t\treturn ctx.redirect(redirect);\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\tlet protocol = u.protocol;\n\n\tif (typeof process !== \"undefined\" && process.env?.SHOPWARE_APP_SDK_FORCE_HTTPS) {\n\t\tprotocol = \"https:\";\n\t}\n\n\treturn `${protocol}//${u.host}`;\n}\n"]}
|
|
@@ -16,6 +16,23 @@ export declare function uploadMediaFile(httpClient: HttpClient, { private: isPri
|
|
|
16
16
|
fileName: string;
|
|
17
17
|
file: Blob | Promise<Blob>;
|
|
18
18
|
}): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Uploads a media file by URL to the Shopware instance.
|
|
21
|
+
*
|
|
22
|
+
* @param {HttpClient} httpClient - The HTTP client instance.
|
|
23
|
+
* @param {Object} options - The options for uploading the media file.
|
|
24
|
+
* @param {boolean} [options.private] - Whether the media file should be private.
|
|
25
|
+
* @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.
|
|
26
|
+
* @param {string} options.fileName - The name of the file to upload.
|
|
27
|
+
* @param {string} options.url - The URL of the media file to upload.
|
|
28
|
+
* @returns {Promise<string>} - The ID of the uploaded media file.
|
|
29
|
+
*/
|
|
30
|
+
export declare function uploadMediaByUrl(httpClient: HttpClient, { private: isPrivate, mediaFolderId, fileName, url, }: {
|
|
31
|
+
private?: boolean;
|
|
32
|
+
mediaFolderId?: string | null;
|
|
33
|
+
fileName: string;
|
|
34
|
+
url: string;
|
|
35
|
+
}): Promise<string>;
|
|
19
36
|
/**
|
|
20
37
|
* Retrieves the default media folder ID for a given entity.
|
|
21
38
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,UAAU,EACtB,EACC,OAAO,EAAE,SAAiB,EAC1B,aAAoB,EACpB,QAAQ,EACR,IAAI,GACJ,EAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,GACC,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAClD,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED,UAAU,wBAAwB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,wBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACpC,UAAU,EAAE,UAAU,EACtB,EACC,OAAO,EAAE,SAAiB,EAC1B,aAAoB,EACpB,QAAQ,EACR,IAAI,GACJ,EAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,GACC,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACrC,UAAU,EAAE,UAAU,EACtB,EACC,OAAO,EAAE,SAAiB,EAC1B,aAAoB,EACpB,QAAQ,EACR,GAAG,GACH,EAAE;IACF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACZ,GACC,OAAO,CAAC,MAAM,CAAC,CAmCjB;AAGD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAClD,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAaxB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED,UAAU,wBAAwB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,wBAAwB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAejB"}
|
package/dist/esm/helper/media.js
CHANGED
|
@@ -42,6 +42,46 @@ export async function uploadMediaFile(httpClient, { private: isPrivate = false,
|
|
|
42
42
|
throw e;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Uploads a media file by URL to the Shopware instance.
|
|
47
|
+
*
|
|
48
|
+
* @param {HttpClient} httpClient - The HTTP client instance.
|
|
49
|
+
* @param {Object} options - The options for uploading the media file.
|
|
50
|
+
* @param {boolean} [options.private] - Whether the media file should be private.
|
|
51
|
+
* @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.
|
|
52
|
+
* @param {string} options.fileName - The name of the file to upload.
|
|
53
|
+
* @param {string} options.url - The URL of the media file to upload.
|
|
54
|
+
* @returns {Promise<string>} - The ID of the uploaded media file.
|
|
55
|
+
*/
|
|
56
|
+
export async function uploadMediaByUrl(httpClient, { private: isPrivate = false, mediaFolderId = null, fileName, url, }) {
|
|
57
|
+
const repository = new EntityRepository(httpClient, "media");
|
|
58
|
+
const mediaId = uuid();
|
|
59
|
+
await repository.upsert([
|
|
60
|
+
{
|
|
61
|
+
id: mediaId,
|
|
62
|
+
private: isPrivate,
|
|
63
|
+
mediaFolderId,
|
|
64
|
+
},
|
|
65
|
+
]);
|
|
66
|
+
const splitFileName = fileName.split(".");
|
|
67
|
+
if (splitFileName.length < 2) {
|
|
68
|
+
throw new Error("Invalid file name, should have an extension");
|
|
69
|
+
}
|
|
70
|
+
const extension = (splitFileName.slice(-1)[0] || "").toLowerCase();
|
|
71
|
+
const baseFileName = splitFileName.slice(0, -1).join(".");
|
|
72
|
+
try {
|
|
73
|
+
await httpClient.post(`/_action/media/${mediaId}/upload`, {
|
|
74
|
+
fileName: baseFileName,
|
|
75
|
+
extension,
|
|
76
|
+
url,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
await repository.delete([{ id: mediaId }]);
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
return mediaId;
|
|
84
|
+
}
|
|
45
85
|
/**
|
|
46
86
|
* Retrieves the default media folder ID for a given entity.
|
|
47
87
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HttpClient } from \"../http-client.js\";\nimport { EntityRepository, uuid } from \"./admin-api.js\";\nimport { Criteria } from \"./criteria.js\";\n\n/**\n * Uploads a media file to the Shopware instance.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {Object} options - The options for uploading the media file.\n * @param {boolean} [options.private] - Whether the media file should be private.\n * @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.\n * @param {string} options.fileName - The name of the file to upload.\n * @param {Blob|Promise<Blob>} options.file - The file to upload.\n * @returns {Promise<string>} - The ID of the uploaded media file.\n */\nexport async function uploadMediaFile(\n\thttpClient: HttpClient,\n\t{\n\t\tprivate: isPrivate = false,\n\t\tmediaFolderId = null,\n\t\tfileName,\n\t\tfile,\n\t}: {\n\t\tprivate?: boolean;\n\t\tmediaFolderId?: string | null;\n\t\tfileName: string;\n\t\tfile: Blob | Promise<Blob>;\n\t},\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media\");\n\n\tconst mediaId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaId,\n\t\t\tprivate: isPrivate,\n\t\t\tmediaFolderId,\n\t\t},\n\t]);\n\n\tconst splitFileName = fileName.split(\".\");\n\n\tif (splitFileName.length < 2) {\n\t\tthrow new Error(\"Invalid file name, should have an extension\");\n\t}\n\n\tconst extension = (splitFileName.slice(-1)[0] || \"\").toLowerCase();\n\tconst baseFileName = splitFileName.slice(0, -1).join(\".\");\n\n\tconst params = new URLSearchParams();\n\n\tparams.append(\"extension\", extension);\n\tparams.append(\"fileName\", baseFileName);\n\n\tconst resolved = await file;\n\n\ttry {\n\t\tawait httpClient.post(\n\t\t\t`/_action/media/${mediaId}/upload?${params.toString()}`,\n\t\t\tresolved,\n\t\t\t{\n\t\t\t\t\"Content-Type\": resolved.type,\n\t\t\t},\n\t\t);\n\n\t\treturn mediaId;\n\t} catch (e) {\n\t\tawait repository.delete([{ id: mediaId }]);\n\t\tthrow e;\n\t}\n}\n\n/**\n * Retrieves the default media folder ID for a given entity.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} entity - The entity name to get the default folder ID for.\n * @returns {Promise<string|null>} - The ID of the default media folder or null if not found.\n */\nexport async function getMediaDefaultFolderByEntity(\n\thttpClient: HttpClient,\n\tentity: string,\n): Promise<string | null> {\n\tconst mediaDefaultFolder = new EntityRepository<{ folder: { id: string } }>(\n\t\thttpClient,\n\t\t\"media_default_folder\",\n\t);\n\n\tconst criteria = new Criteria();\n\tcriteria.addFilter(Criteria.equals(\"entity\", entity));\n\n\tconst folders = await mediaDefaultFolder.search(criteria);\n\n\tconst firstFolder = folders.first();\n\treturn firstFolder?.folder?.id || null;\n}\n\n/**\n * Retrieves the media folder ID by its name.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} name - The name of the media folder.\n * @returns {Promise<string|null>} - The ID of the media folder or null if not found.\n */\nexport async function getMediaFolderByName(\n\thttpClient: HttpClient,\n\tname: string,\n): Promise<string | null> {\n\tconst mediaFolder = new EntityRepository<{ id: string }>(\n\t\thttpClient,\n\t\t\"media_folder\",\n\t);\n\n\tconst criteria = new Criteria();\n\tcriteria.addFilter(Criteria.equals(\"name\", name));\n\n\tconst folders = await mediaFolder.search(criteria);\n\n\treturn folders.first()?.id || null;\n}\n\ninterface CreateMediaFolderOptions {\n\tparentId?: string;\n}\n\n/**\n * Creates a new media folder.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} name - The name of the media folder.\n * @param {Object} options - Additional options for creating the media folder.\n * @param {string} [options.parentId] - The ID of the parent folder, if any.\n * @returns {Promise<string>} - The ID of the newly created media folder.\n */\nexport async function createMediaFolder(\n\thttpClient: HttpClient,\n\tname: string,\n\toptions: CreateMediaFolderOptions,\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media_folder\");\n\n\tconst mediaFolderId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaFolderId,\n\t\t\tname,\n\t\t\tparentId: options.parentId || null,\n\t\t\tconfiguration: {},\n\t\t},\n\t]);\n\n\treturn mediaFolderId;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/helper/media.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HttpClient } from \"../http-client.js\";\nimport { EntityRepository, uuid } from \"./admin-api.js\";\nimport { Criteria } from \"./criteria.js\";\n\n/**\n * Uploads a media file to the Shopware instance.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {Object} options - The options for uploading the media file.\n * @param {boolean} [options.private] - Whether the media file should be private.\n * @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.\n * @param {string} options.fileName - The name of the file to upload.\n * @param {Blob|Promise<Blob>} options.file - The file to upload.\n * @returns {Promise<string>} - The ID of the uploaded media file.\n */\nexport async function uploadMediaFile(\n\thttpClient: HttpClient,\n\t{\n\t\tprivate: isPrivate = false,\n\t\tmediaFolderId = null,\n\t\tfileName,\n\t\tfile,\n\t}: {\n\t\tprivate?: boolean;\n\t\tmediaFolderId?: string | null;\n\t\tfileName: string;\n\t\tfile: Blob | Promise<Blob>;\n\t},\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media\");\n\n\tconst mediaId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaId,\n\t\t\tprivate: isPrivate,\n\t\t\tmediaFolderId,\n\t\t},\n\t]);\n\n\tconst splitFileName = fileName.split(\".\");\n\n\tif (splitFileName.length < 2) {\n\t\tthrow new Error(\"Invalid file name, should have an extension\");\n\t}\n\n\tconst extension = (splitFileName.slice(-1)[0] || \"\").toLowerCase();\n\tconst baseFileName = splitFileName.slice(0, -1).join(\".\");\n\n\tconst params = new URLSearchParams();\n\n\tparams.append(\"extension\", extension);\n\tparams.append(\"fileName\", baseFileName);\n\n\tconst resolved = await file;\n\n\ttry {\n\t\tawait httpClient.post(\n\t\t\t`/_action/media/${mediaId}/upload?${params.toString()}`,\n\t\t\tresolved,\n\t\t\t{\n\t\t\t\t\"Content-Type\": resolved.type,\n\t\t\t},\n\t\t);\n\n\t\treturn mediaId;\n\t} catch (e) {\n\t\tawait repository.delete([{ id: mediaId }]);\n\t\tthrow e;\n\t}\n}\n\n/**\n * Uploads a media file by URL to the Shopware instance.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {Object} options - The options for uploading the media file.\n * @param {boolean} [options.private] - Whether the media file should be private.\n * @param {string} [options.mediaFolderId] - The ID of the media folder to upload the file to.\n * @param {string} options.fileName - The name of the file to upload.\n * @param {string} options.url - The URL of the media file to upload.\n * @returns {Promise<string>} - The ID of the uploaded media file.\n */\nexport async function uploadMediaByUrl(\n\thttpClient: HttpClient,\n\t{\n\t\tprivate: isPrivate = false,\n\t\tmediaFolderId = null,\n\t\tfileName,\n\t\turl,\n\t}: {\n\t\tprivate?: boolean;\n\t\tmediaFolderId?: string | null;\n\t\tfileName: string;\n\t\turl: string;\n\t},\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media\");\n\n\tconst mediaId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaId,\n\t\t\tprivate: isPrivate,\n\t\t\tmediaFolderId,\n\t\t},\n\t]);\n\n\tconst splitFileName = fileName.split(\".\");\n\n\tif (splitFileName.length < 2) {\n\t\tthrow new Error(\"Invalid file name, should have an extension\");\n\t}\n\n\tconst extension = (splitFileName.slice(-1)[0] || \"\").toLowerCase();\n\tconst baseFileName = splitFileName.slice(0, -1).join(\".\");\n\n\ttry {\n\t\tawait httpClient.post(`/_action/media/${mediaId}/upload`, {\n\t\t\tfileName: baseFileName,\n\t\t\textension,\n\t\t\turl,\n\t\t});\n\t} catch (e) {\n\t\tawait repository.delete([{ id: mediaId }]);\n\n\t\tthrow e;\n\t}\n\n\treturn mediaId;\n}\n\n\n/**\n * Retrieves the default media folder ID for a given entity.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} entity - The entity name to get the default folder ID for.\n * @returns {Promise<string|null>} - The ID of the default media folder or null if not found.\n */\nexport async function getMediaDefaultFolderByEntity(\n\thttpClient: HttpClient,\n\tentity: string,\n): Promise<string | null> {\n\tconst mediaDefaultFolder = new EntityRepository<{ folder: { id: string } }>(\n\t\thttpClient,\n\t\t\"media_default_folder\",\n\t);\n\n\tconst criteria = new Criteria();\n\tcriteria.addFilter(Criteria.equals(\"entity\", entity));\n\n\tconst folders = await mediaDefaultFolder.search(criteria);\n\n\tconst firstFolder = folders.first();\n\treturn firstFolder?.folder?.id || null;\n}\n\n/**\n * Retrieves the media folder ID by its name.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} name - The name of the media folder.\n * @returns {Promise<string|null>} - The ID of the media folder or null if not found.\n */\nexport async function getMediaFolderByName(\n\thttpClient: HttpClient,\n\tname: string,\n): Promise<string | null> {\n\tconst mediaFolder = new EntityRepository<{ id: string }>(\n\t\thttpClient,\n\t\t\"media_folder\",\n\t);\n\n\tconst criteria = new Criteria();\n\tcriteria.addFilter(Criteria.equals(\"name\", name));\n\n\tconst folders = await mediaFolder.search(criteria);\n\n\treturn folders.first()?.id || null;\n}\n\ninterface CreateMediaFolderOptions {\n\tparentId?: string;\n}\n\n/**\n * Creates a new media folder.\n *\n * @param {HttpClient} httpClient - The HTTP client instance.\n * @param {string} name - The name of the media folder.\n * @param {Object} options - Additional options for creating the media folder.\n * @param {string} [options.parentId] - The ID of the parent folder, if any.\n * @returns {Promise<string>} - The ID of the newly created media folder.\n */\nexport async function createMediaFolder(\n\thttpClient: HttpClient,\n\tname: string,\n\toptions: CreateMediaFolderOptions,\n): Promise<string> {\n\tconst repository = new EntityRepository(httpClient, \"media_folder\");\n\n\tconst mediaFolderId = uuid();\n\n\tawait repository.upsert([\n\t\t{\n\t\t\tid: mediaFolderId,\n\t\t\tname,\n\t\t\tparentId: options.parentId || null,\n\t\t\tconfiguration: {},\n\t\t},\n\t]);\n\n\treturn mediaFolderId;\n}\n"]}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { SimpleShop } from "../repository.js";
|
|
2
2
|
import type { ShopRepositoryInterface } from "../repository.js";
|
|
3
|
+
import type { HttpClientTokenCacheInterface, HttpClientTokenCacheItem } from "../http-client.js";
|
|
3
4
|
/**
|
|
4
5
|
* Cloudflare KV integration
|
|
5
6
|
* @module
|
|
6
7
|
*/
|
|
7
|
-
/**
|
|
8
|
-
* Cloudflare KV implementation of the ShopRepositoryInterface
|
|
9
|
-
*/
|
|
10
8
|
export declare class CloudflareShopRepository implements ShopRepositoryInterface<SimpleShop> {
|
|
11
9
|
private storage;
|
|
12
10
|
constructor(storage: KVNamespace);
|
|
@@ -15,6 +13,26 @@ export declare class CloudflareShopRepository implements ShopRepositoryInterface
|
|
|
15
13
|
getShopById(id: string): Promise<SimpleShop | null>;
|
|
16
14
|
updateShop(shop: SimpleShop): Promise<void>;
|
|
17
15
|
protected serializeShop(shop: SimpleShop): string;
|
|
18
|
-
protected deserializeShop(data: string): SimpleShop;
|
|
16
|
+
protected deserializeShop(data: (string | null)[]): SimpleShop;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Cloudflare KV implementation for HttpClientTokenCacheInterface
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
export declare class CloudflareHttpClientTokenCache implements HttpClientTokenCacheInterface {
|
|
23
|
+
private storage;
|
|
24
|
+
constructor(storage: KVNamespace);
|
|
25
|
+
/**
|
|
26
|
+
* Get a token from KV storage for the given shop ID
|
|
27
|
+
*/
|
|
28
|
+
getToken(shopId: string): Promise<HttpClientTokenCacheItem | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Store a token in KV storage for the given shop ID
|
|
31
|
+
*/
|
|
32
|
+
setToken(shopId: string, token: HttpClientTokenCacheItem): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Remove a token from KV storage for the given shop ID
|
|
35
|
+
*/
|
|
36
|
+
clearToken(shopId: string): Promise<void>;
|
|
19
37
|
}
|
|
20
38
|
//# sourceMappingURL=cloudflare-kv.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-kv.d.ts","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.d.ts","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAEjG;;;GAGG;AACH,qBAAa,wBACZ,YAAW,uBAAuB,CAAC,UAAU,CAAC;IAElC,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAIlC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAcnD,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjD,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAIjD,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,GAAG,UAAU;CA4B9D;AAED;;;GAGG;AACH,qBAAa,8BAA+B,YAAW,6BAA6B;IACvE,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAIxC;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC;IAmBxE;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9E;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C"}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
1
2
|
import { SimpleShop } from "../repository.js";
|
|
2
3
|
/**
|
|
3
4
|
* Cloudflare KV integration
|
|
4
5
|
* @module
|
|
5
6
|
*/
|
|
6
|
-
/**
|
|
7
|
-
* Cloudflare KV implementation of the ShopRepositoryInterface
|
|
8
|
-
*/
|
|
9
7
|
export class CloudflareShopRepository {
|
|
10
8
|
storage;
|
|
11
9
|
constructor(storage) {
|
|
@@ -13,33 +11,103 @@ export class CloudflareShopRepository {
|
|
|
13
11
|
this.storage = storage;
|
|
14
12
|
}
|
|
15
13
|
async createShop(id, url, secret) {
|
|
16
|
-
await
|
|
14
|
+
await Promise.all([
|
|
15
|
+
this.storage.put(id, this.serializeShop(new SimpleShop(id, url, secret))),
|
|
16
|
+
this.storage.put(`${id}_active`, JSON.stringify(false)),
|
|
17
|
+
]);
|
|
17
18
|
}
|
|
18
19
|
async deleteShop(id) {
|
|
19
|
-
await
|
|
20
|
+
await Promise.all([
|
|
21
|
+
this.storage.delete(id),
|
|
22
|
+
this.storage.delete(`${id}_active`),
|
|
23
|
+
this.storage.delete(`${id}_credentials`)
|
|
24
|
+
]);
|
|
20
25
|
}
|
|
21
26
|
async getShopById(id) {
|
|
22
|
-
const
|
|
23
|
-
|
|
27
|
+
const kvValues = await Promise.all([
|
|
28
|
+
this.storage.get(id),
|
|
29
|
+
this.storage.get(`${id}_active`),
|
|
30
|
+
this.storage.get(`${id}_credentials`)
|
|
31
|
+
]);
|
|
32
|
+
if (kvValues[0] === null) {
|
|
24
33
|
return null;
|
|
25
34
|
}
|
|
26
|
-
return this.deserializeShop(
|
|
35
|
+
return this.deserializeShop(kvValues);
|
|
27
36
|
}
|
|
28
37
|
async updateShop(shop) {
|
|
29
|
-
await
|
|
38
|
+
await Promise.all([
|
|
39
|
+
this.storage.put(shop.getShopId(), this.serializeShop(shop)),
|
|
40
|
+
this.storage.put(`${shop.getShopId()}_active`, JSON.stringify(shop.getShopActive())),
|
|
41
|
+
this.storage.put(`${shop.getShopId()}_credentials`, JSON.stringify({ clientId: shop.getShopClientId(), clientSecret: shop.getShopClientSecret() }))
|
|
42
|
+
]);
|
|
30
43
|
}
|
|
31
44
|
serializeShop(shop) {
|
|
32
45
|
return JSON.stringify(shop);
|
|
33
46
|
}
|
|
34
47
|
deserializeShop(data) {
|
|
35
|
-
const obj = JSON.parse(data);
|
|
48
|
+
const obj = JSON.parse(data[0]);
|
|
36
49
|
const shop = new SimpleShop(obj.shopId || "", obj.shopUrl || "", obj.shopSecret || "");
|
|
37
50
|
shop.setShopCredentials(obj.shopClientId || "", obj.shopClientSecret || "");
|
|
38
51
|
if (obj.shopActive === undefined) {
|
|
39
52
|
obj.shopActive = true;
|
|
40
53
|
}
|
|
41
54
|
shop.setShopActive(obj.shopActive);
|
|
55
|
+
if (data[1] !== null) {
|
|
56
|
+
shop.setShopActive(JSON.parse(data[1]));
|
|
57
|
+
}
|
|
58
|
+
if (data[2] !== null) {
|
|
59
|
+
const credentials = JSON.parse(data[2]);
|
|
60
|
+
shop.setShopCredentials(credentials.clientId || "", credentials.clientSecret || "");
|
|
61
|
+
}
|
|
42
62
|
return shop;
|
|
43
63
|
}
|
|
44
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Cloudflare KV implementation for HttpClientTokenCacheInterface
|
|
67
|
+
* @module
|
|
68
|
+
*/
|
|
69
|
+
export class CloudflareHttpClientTokenCache {
|
|
70
|
+
storage;
|
|
71
|
+
constructor(storage) {
|
|
72
|
+
this.storage = storage;
|
|
73
|
+
this.storage = storage;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get a token from KV storage for the given shop ID
|
|
77
|
+
*/
|
|
78
|
+
async getToken(shopId) {
|
|
79
|
+
const tokenData = await this.storage.get(`token_${shopId}`);
|
|
80
|
+
if (tokenData === null) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const parsedToken = JSON.parse(tokenData);
|
|
85
|
+
// Convert the ISO string back to a Date object
|
|
86
|
+
return {
|
|
87
|
+
token: parsedToken.token,
|
|
88
|
+
expiresIn: new Date(parsedToken.expiresIn)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Store a token in KV storage for the given shop ID
|
|
97
|
+
*/
|
|
98
|
+
async setToken(shopId, token) {
|
|
99
|
+
// Convert the Date object to an ISO string for storage
|
|
100
|
+
const tokenData = {
|
|
101
|
+
token: token.token,
|
|
102
|
+
expiresIn: token.expiresIn.toISOString()
|
|
103
|
+
};
|
|
104
|
+
await this.storage.put(`token_${shopId}`, JSON.stringify(tokenData));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Remove a token from KV storage for the given shop ID
|
|
108
|
+
*/
|
|
109
|
+
async clearToken(shopId) {
|
|
110
|
+
await this.storage.delete(`token_${shopId}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
45
113
|
//# sourceMappingURL=cloudflare-kv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"cloudflare-kv.js","sourceRoot":"","sources":["../../../src/integration/cloudflare-kv.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"@cloudflare/workers-types\" />\nimport { SimpleShop } from \"../repository.js\";\nimport type { ShopRepositoryInterface } from \"../repository.js\";\nimport type { HttpClientTokenCacheInterface, HttpClientTokenCacheItem } from \"../http-client.js\";\n\n/**\n * Cloudflare KV integration\n * @module\n */\nexport class CloudflareShopRepository\n\timplements ShopRepositoryInterface<SimpleShop>\n{\n\tconstructor(private storage: KVNamespace) {\n\t\tthis.storage = storage;\n\t}\n\n\tasync createShop(id: string, url: string, secret: string): Promise<void> {\n\t\tawait Promise.all([\n this.storage.put(id, this.serializeShop(new SimpleShop(id, url, secret))),\n this.storage.put(`${id}_active`, JSON.stringify(false)),\n ])\n\t}\n\n\tasync deleteShop(id: string): Promise<void> {\n\t\tawait Promise.all([\n this.storage.delete(id),\n this.storage.delete(`${id}_active`),\n this.storage.delete(`${id}_credentials`)\n ])\n\t}\n\n\tasync getShopById(id: string): Promise<SimpleShop | null> {\n\t\tconst kvValues = await Promise.all([\n this.storage.get(id),\n this.storage.get(`${id}_active`),\n this.storage.get(`${id}_credentials`)\n ]);\n\n\t\tif (kvValues[0] === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn this.deserializeShop(kvValues);\n\t}\n\n\tasync updateShop(shop: SimpleShop): Promise<void> {\n\t\tawait Promise.all([\n this.storage.put(shop.getShopId(), this.serializeShop(shop)),\n this.storage.put(`${shop.getShopId()}_active`, JSON.stringify(shop.getShopActive())),\n this.storage.put(`${shop.getShopId()}_credentials`, JSON.stringify({ clientId: shop.getShopClientId(), clientSecret: shop.getShopClientSecret() }))\n ])\n\t}\n\n\tprotected serializeShop(shop: SimpleShop): string {\n\t\treturn JSON.stringify(shop);\n\t}\n\n\tprotected deserializeShop(data: (string | null)[]): SimpleShop {\n\t\tconst obj = JSON.parse(data[0]!);\n\n\t\tconst shop = new SimpleShop(\n\t\t\tobj.shopId || \"\",\n\t\t\tobj.shopUrl || \"\",\n\t\t\tobj.shopSecret || \"\",\n\t\t);\n\n\t\tshop.setShopCredentials(obj.shopClientId || \"\", obj.shopClientSecret || \"\");\n\n\t\tif (obj.shopActive === undefined) {\n\t\t\tobj.shopActive = true;\n\t\t}\n\n\t\tshop.setShopActive(obj.shopActive);\n\n\t\tif (data[1] !== null) {\n\t\t\tshop.setShopActive(JSON.parse(data[1]!));\n\t\t}\n\n\t\tif (data[2] !== null) {\n\t\t\tconst credentials = JSON.parse(data[2]!);\n\t\t\tshop.setShopCredentials(credentials.clientId || \"\", credentials.clientSecret || \"\");\n\t\t}\n\n\t\treturn shop;\n\t}\n}\n\n/**\n * Cloudflare KV implementation for HttpClientTokenCacheInterface\n * @module\n */\nexport class CloudflareHttpClientTokenCache implements HttpClientTokenCacheInterface {\n\tconstructor(private storage: KVNamespace) {\n\t\tthis.storage = storage;\n\t}\n\n\t/**\n\t * Get a token from KV storage for the given shop ID\n\t */\n\tasync getToken(shopId: string): Promise<HttpClientTokenCacheItem | null> {\n\t\tconst tokenData = await this.storage.get(`token_${shopId}`);\n\t\t\n\t\tif (tokenData === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parsedToken = JSON.parse(tokenData);\n\t\t\t// Convert the ISO string back to a Date object\n\t\t\treturn {\n\t\t\t\ttoken: parsedToken.token,\n\t\t\t\texpiresIn: new Date(parsedToken.expiresIn)\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Store a token in KV storage for the given shop ID\n\t */\n\tasync setToken(shopId: string, token: HttpClientTokenCacheItem): Promise<void> {\n\t\t// Convert the Date object to an ISO string for storage\n\t\tconst tokenData = {\n\t\t\ttoken: token.token,\n\t\t\texpiresIn: token.expiresIn.toISOString()\n\t\t};\n\t\t\n\t\tawait this.storage.put(`token_${shopId}`, JSON.stringify(tokenData));\n\t}\n\n\t/**\n\t * Remove a token from KV storage for the given shop ID\n\t */\n\tasync clearToken(shopId: string): Promise<void> {\n\t\tawait this.storage.delete(`token_${shopId}`);\n\t}\n}\n"]}
|
|
@@ -10,15 +10,7 @@ declare module "hono" {
|
|
|
10
10
|
context: Context<ShopInterface, unknown>;
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
interface
|
|
14
|
-
/**
|
|
15
|
-
* The name of the app
|
|
16
|
-
*/
|
|
17
|
-
appName: string | ((c: HonoContext) => string);
|
|
18
|
-
/**
|
|
19
|
-
* The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.
|
|
20
|
-
*/
|
|
21
|
-
appSecret: string | ((c: HonoContext) => string);
|
|
13
|
+
interface MiddlewareConfigBase {
|
|
22
14
|
/**
|
|
23
15
|
* The URL of the app. This is the base URL of the app. This will automatically determined by default
|
|
24
16
|
*/
|
|
@@ -89,10 +81,30 @@ interface MiddlewareConfig {
|
|
|
89
81
|
* }
|
|
90
82
|
*/
|
|
91
83
|
appIframeRedirects?: Record<string, string>;
|
|
84
|
+
}
|
|
85
|
+
interface AppServerConfig {
|
|
86
|
+
/**
|
|
87
|
+
* An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.
|
|
88
|
+
*/
|
|
89
|
+
appServer: AppServer;
|
|
90
|
+
}
|
|
91
|
+
interface ParametersConfig {
|
|
92
|
+
/**
|
|
93
|
+
* The name of the app
|
|
94
|
+
*/
|
|
95
|
+
appName: string | ((c: HonoContext) => string);
|
|
96
|
+
/**
|
|
97
|
+
* The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.
|
|
98
|
+
*/
|
|
99
|
+
appSecret: string | ((c: HonoContext) => string);
|
|
92
100
|
/**
|
|
93
101
|
* The repository to fetch and store the shop data
|
|
94
102
|
*/
|
|
95
103
|
shopRepository: ShopRepositoryInterface | ((c: HonoContext) => ShopRepositoryInterface);
|
|
104
|
+
/**
|
|
105
|
+
* An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.
|
|
106
|
+
*/
|
|
107
|
+
appServer?: undefined;
|
|
96
108
|
/**
|
|
97
109
|
* The token cache to use for the HttpClient. This is used to cache the access token for the shopware shop. If you don't provide a token cache, the HttpClient will use an in-memory cache.
|
|
98
110
|
*/
|
|
@@ -102,6 +114,7 @@ interface MiddlewareConfig {
|
|
|
102
114
|
*/
|
|
103
115
|
setup?: (app: AppServer) => void;
|
|
104
116
|
}
|
|
117
|
+
type MiddlewareConfig = (MiddlewareConfigBase & AppServerConfig) | (MiddlewareConfigBase & ParametersConfig);
|
|
105
118
|
/**
|
|
106
119
|
* Configure the Hono server to handle the app registration and context resolution
|
|
107
120
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AACzD,OAAO,EAAc,KAAK,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AACzD,OAAO,EAAc,KAAK,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAE3B,GAAG,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9B,IAAI,EAAE,aAAa,CAAC;QAEpB,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;KACzC;CACD;AAGD,UAAU,oBAAoB;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAGD,UAAU,eAAe;IACxB;;OAEG;IACH,SAAS,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,gBAAgB;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IAC/C;;OAEG;IACH,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAC;IACjD;;OAEG;IACH,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,uBAAuB,CAAC,CAAC;IAEjD;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB;;OAEG;IACH,oBAAoB,CAAC,EAAE,6BAA6B,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,KAAK,6BAA6B,CAAC,CAAC;IAE3G;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;CACjC;AAGD,KAAK,gBAAgB,GAClB,CAAC,oBAAoB,GAAG,eAAe,CAAC,GACxC,CAAC,oBAAoB,GAAG,gBAAgB,CAAC,CAAC;AAE7C;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,QAwMnE"}
|
|
@@ -19,26 +19,31 @@ export function configureAppServer(hono, cfg) {
|
|
|
19
19
|
cfg.appIframePath = cfg.appIframePath || "/client-api/*";
|
|
20
20
|
hono.use("*", async (ctx, next) => {
|
|
21
21
|
if (app === null) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
cfg.shopRepository = cfg.shopRepository(ctx);
|
|
22
|
+
if (cfg.appServer) {
|
|
23
|
+
app = cfg.appServer;
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
25
|
+
else {
|
|
26
|
+
const appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);
|
|
27
|
+
if (typeof cfg.shopRepository === "function") {
|
|
28
|
+
cfg.shopRepository = cfg.shopRepository(ctx);
|
|
29
|
+
}
|
|
30
|
+
if (typeof cfg.appName === "function") {
|
|
31
|
+
cfg.appName = cfg.appName(ctx);
|
|
32
|
+
}
|
|
33
|
+
if (typeof cfg.appSecret === "function") {
|
|
34
|
+
cfg.appSecret = cfg.appSecret(ctx);
|
|
35
|
+
}
|
|
36
|
+
if (typeof cfg.httpClientTokenCache === "function") {
|
|
37
|
+
cfg.httpClientTokenCache = cfg.httpClientTokenCache(ctx);
|
|
38
|
+
}
|
|
39
|
+
app = new AppServer({
|
|
40
|
+
appName: cfg.appName,
|
|
41
|
+
appSecret: cfg.appSecret,
|
|
42
|
+
authorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,
|
|
43
|
+
}, cfg.shopRepository, cfg.httpClientTokenCache);
|
|
44
|
+
if (cfg.setup) {
|
|
45
|
+
cfg.setup(app);
|
|
46
|
+
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
// @ts-ignore
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSignedCookie, setSignedCookie } from \"hono/cookie\";\nimport { AppServer } from \"../app.js\";\nimport { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\nimport { HttpClient, type HttpClientTokenCacheInterface } from \"../http-client.js\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\ninterface MiddlewareConfig {\n\t/**\n\t * The name of the app\n\t */\n\tappName: string | ((c: HonoContext) => string);\n\t/**\n\t * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.\n\t */\n\tappSecret: string | ((c: HonoContext) => string);\n\n\t/**\n\t * The URL of the app. This is the base URL of the app. This will automatically determined by default\n\t */\n\tappUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration endpoint\n\t *\n\t * @default \"/app/register\"\n\t */\n\tregistrationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration confirmation endpoint\n\t *\n\t * @default \"/app/register/confirm\"\n\t */\n\tregisterConfirmationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app installation lifecycle endpoint\n\t *\n\t * @default \"/app/install\"\n\t */\n\tappInstallUrl?: string | null;\n\n\t/**\n\t * The relative url of the app activation lifecycle endpoint\n\t *\n\t * @default \"/app/activate\"\n\t */\n\tappActivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app update lifecycle endpoint\n\t *\n\t * @default \"/app/update\"\n\t */\n\tappUpdateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deactivation lifecycle endpoint\n\t *\n\t * @default \"/app/deactivate\"\n\t */\n\tappDeactivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deletion lifecycle endpoint\n\t *\n\t * @default \"/app/delete\"\n\t */\n\tappDeleteUrl?: string | null;\n\n\t/**\n\t * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed\n\t *\n\t * @default \"/app/*\"\n\t */\n\tappPath?: string | null;\n\n\t/**\n\t * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects\n\t */\n\tappIframeEnable?: boolean;\n\n\t/**\n\t * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by\n\t *\n\t * @default \"/client-api/*\"\n\t */\n\tappIframePath?: string | null;\n\n\t/**\n\t * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.\n\t *\n\t * @default {\n\t * \"/app/module\": \"https://my-static-client-side-app.com\"\n\t * }\n\t */\n\tappIframeRedirects?: Record<string, string>;\n\n\t/**\n\t * The repository to fetch and store the shop data\n\t */\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n\n\t/**\n\t * The token cache to use for the HttpClient. This is used to cache the access token for the shopware shop. If you don't provide a token cache, the HttpClient will use an in-memory cache.\n\t */\n\thttpClientTokenCache?: HttpClientTokenCacheInterface | ((c: HonoContext) => HttpClientTokenCacheInterface);\n\n\t/**\n\t * A callback to setup the app server. It will be called after the app server is created and before the first request is handled\n\t */\n\tsetup?: (app: AppServer) => void;\n}\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\tcfg.appInstallUrl = cfg.appInstallUrl || \"/app/install\";\n\tcfg.appUpdateUrl = cfg.appUpdateUrl || \"/app/update\";\n\n\tcfg.appIframePath = cfg.appIframePath || \"/client-api/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t}\n\n\t\t\tif (typeof cfg.httpClientTokenCache === \"function\") {\n\t\t\t\tcfg.httpClientTokenCache = cfg.httpClientTokenCache(ctx);\n\t\t\t}\n\n\t\t\tapp = new AppServer(\n\t\t\t\t{\n\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t},\n\t\t\t\tcfg.shopRepository,\n\t\t\t\tcfg.httpClientTokenCache,\n\t\t\t);\n\n\t\t\tif (cfg.setup) {\n\t\t\t\tcfg.setup(app);\n\t\t\t}\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appInstallUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.install(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appUpdateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.update(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n\n\tif (cfg.appIframeEnable) {\n\t\thono.use(cfg.appIframePath, async (ctx, next) => {\n\t\t\tconst shopId = await getSignedCookie(\n\t\t\t\tctx,\n\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t\"shop\",\n\t\t\t);\n\n\t\t\tif (!shopId) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tconst shop = await ctx.get(\"app\").repository.getShopById(shopId);\n\n\t\t\tif (!shop) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tctx.set(\"shop\", shop);\n\t\t\t// @ts-ignore\n\t\t\tctx.set(\"context\", new Context(shop, {}, new HttpClient(shop, ctx.get('app').httpClientTokenCache)));\n\n\t\t\tawait next();\n\t\t});\n\n\t\tfor (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {\n\t\t\thono.get(path, async (ctx) => {\n\t\t\t\tconst url = new URL(ctx.req.url);\n\n\t\t\t\tif (redirect.startsWith(\"/\")) {\n\t\t\t\t\turl.pathname = redirect;\n\t\t\t\t\tredirect = url.toString();\n\t\t\t\t} else {\n\t\t\t\t\tconst newUrl = new URL(redirect);\n\n\t\t\t\t\tfor (const [key, value] of url.searchParams) {\n\t\t\t\t\t\tnewUrl.searchParams.set(key, value);\n\t\t\t\t\t}\n\n\t\t\t\t\tredirect = newUrl.toString();\n\t\t\t\t}\n\n\t\t\t\tawait setSignedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"shop\",\n\t\t\t\t\tctx.get(\"shop\").getShopId(),\n\t\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t);\n\n\t\t\t\treturn ctx.redirect(redirect);\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\tlet protocol = u.protocol;\n\n\tif (typeof process !== \"undefined\" && process.env?.SHOPWARE_APP_SDK_FORCE_HTTPS) {\n\t\tprotocol = \"https:\";\n\t}\n\n\treturn `${protocol}//${u.host}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/integration/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSignedCookie, setSignedCookie } from \"hono/cookie\";\nimport { AppServer } from \"../app.js\";\nimport { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\nimport type { Hono, Context as HonoContext } from \"hono\";\nimport { HttpClient, type HttpClientTokenCacheInterface } from \"../http-client.js\";\n\ndeclare module \"hono\" {\n\tinterface ContextVariableMap {\n\t\t// @ts-ignore\n\t\tapp: AppServer<ShopInterface>;\n\t\tshop: ShopInterface;\n\t\t// @ts-ignore\n\t\tcontext: Context<ShopInterface, unknown>;\n\t}\n}\n\n// Define a base interface with common parameters\ninterface MiddlewareConfigBase {\n\t/**\n\t * The URL of the app. This is the base URL of the app. This will automatically determined by default\n\t */\n\tappUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration endpoint\n\t *\n\t * @default \"/app/register\"\n\t */\n\tregistrationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app registration confirmation endpoint\n\t *\n\t * @default \"/app/register/confirm\"\n\t */\n\tregisterConfirmationUrl?: string | null;\n\n\t/**\n\t * The relative url of the app installation lifecycle endpoint\n\t *\n\t * @default \"/app/install\"\n\t */\n\tappInstallUrl?: string | null;\n\n\t/**\n\t * The relative url of the app activation lifecycle endpoint\n\t *\n\t * @default \"/app/activate\"\n\t */\n\tappActivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app update lifecycle endpoint\n\t *\n\t * @default \"/app/update\"\n\t */\n\tappUpdateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deactivation lifecycle endpoint\n\t *\n\t * @default \"/app/deactivate\"\n\t */\n\tappDeactivateUrl?: string | null;\n\n\t/**\n\t * The relative url of the app deletion lifecycle endpoint\n\t *\n\t * @default \"/app/delete\"\n\t */\n\tappDeleteUrl?: string | null;\n\n\t/**\n\t * The relative url of the app scope. All requests matching this will be the signature automatically validated and the response will be signed\n\t *\n\t * @default \"/app/*\"\n\t */\n\tappPath?: string | null;\n\n\t/**\n\t * Enable the app iframe integration. This will automatically set a cookie to identifiy the shopware shop and validate the request from a client side application. See appIframeRedirects\n\t */\n\tappIframeEnable?: boolean;\n\n\t/**\n\t * The relative url of the app iframe scope. All requests matching this will require that the request has an cookie set with the shopware shop. This cookie will be automatically set by\n\t *\n\t * @default \"/client-api/*\"\n\t */\n\tappIframePath?: string | null;\n\n\t/**\n\t * A mapping of the app iframe paths to the actual paths. This route will set a cookie automatically before the redirect to the actual path. In that way the client side application can send requests to /app-iframe/* with the cookie set and the server will automatically validate the request and knows which shop the request is for.\n\t *\n\t * @default {\n\t * \"/app/module\": \"https://my-static-client-side-app.com\"\n\t * }\n\t */\n\tappIframeRedirects?: Record<string, string>;\n}\n\n// Define specific interfaces for each configuration option\ninterface AppServerConfig {\n\t/**\n\t * An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.\n\t */\n\tappServer: AppServer;\n}\n\ninterface ParametersConfig {\n\t/**\n\t * The name of the app\n\t */\n\tappName: string | ((c: HonoContext) => string);\n\t/**\n\t * The secret of the app. When the app is published in the Shopware Store, the Shopware Store provides this value.\n\t */\n\tappSecret: string | ((c: HonoContext) => string);\n\t/**\n\t * The repository to fetch and store the shop data\n\t */\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext) => ShopRepositoryInterface);\n\t\n\t/**\n\t * An already constructed AppServer instance. When provided, other parameters like appName and appSecret are not required.\n\t */\n\tappServer?: undefined;\n\n\t/**\n\t * The token cache to use for the HttpClient. This is used to cache the access token for the shopware shop. If you don't provide a token cache, the HttpClient will use an in-memory cache.\n\t */\n\thttpClientTokenCache?: HttpClientTokenCacheInterface | ((c: HonoContext) => HttpClientTokenCacheInterface);\n\n\t/**\n\t * A callback to setup the app server. It will be called after the app server is created and before the first request is handled\n\t */\n\tsetup?: (app: AppServer) => void;\n}\n\n// Create a discriminated union type using intersection types\ntype MiddlewareConfig = \n\t| (MiddlewareConfigBase & AppServerConfig)\n\t| (MiddlewareConfigBase & ParametersConfig);\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(hono: Hono, cfg: MiddlewareConfig) {\n\tlet app: AppServer | null = null;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appActivateUrl = cfg.appActivateUrl || \"/app/activate\";\n\tcfg.appDeactivateUrl = cfg.appDeactivateUrl || \"/app/deactivate\";\n\tcfg.appDeleteUrl = cfg.appDeleteUrl || \"/app/delete\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\n\tcfg.appInstallUrl = cfg.appInstallUrl || \"/app/install\";\n\tcfg.appUpdateUrl = cfg.appUpdateUrl || \"/app/update\";\n\n\tcfg.appIframePath = cfg.appIframePath || \"/client-api/*\";\n\n\thono.use(\"*\", async (ctx, next) => {\n\t\tif (app === null) {\n\t\t\tif (cfg.appServer) {\n\t\t\t\tapp = cfg.appServer;\n\t\t\t} else {\n\t\t\t\tconst appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);\n\n\t\t\t\tif (typeof cfg.shopRepository === \"function\") {\n\t\t\t\t\tcfg.shopRepository = cfg.shopRepository(ctx);\n\t\t\t\t}\n\n\t\t\t\tif (typeof cfg.appName === \"function\") {\n\t\t\t\t\tcfg.appName = cfg.appName(ctx);\n\t\t\t\t}\n\n\t\t\t\tif (typeof cfg.appSecret === \"function\") {\n\t\t\t\t\tcfg.appSecret = cfg.appSecret(ctx);\n\t\t\t\t}\n\n\t\t\t\tif (typeof cfg.httpClientTokenCache === \"function\") {\n\t\t\t\t\tcfg.httpClientTokenCache = cfg.httpClientTokenCache(ctx);\n\t\t\t\t}\n\n\t\t\t\tapp = new AppServer(\n\t\t\t\t\t{\n\t\t\t\t\t\tappName: cfg.appName,\n\t\t\t\t\t\tappSecret: cfg.appSecret,\n\t\t\t\t\t\tauthorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,\n\t\t\t\t\t},\n\t\t\t\t\tcfg.shopRepository,\n\t\t\t\t\tcfg.httpClientTokenCache,\n\t\t\t\t);\n\n\t\t\t\tif (cfg.setup) {\n\t\t\t\t\tcfg.setup(app);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\n\t\t// @ts-ignore\n\t\tconst app = ctx.get(\"app\") as AppServer;\n\n\t\t// Don't validate signature for registration\n\t\tif (\n\t\t\tctx.req.path === cfg.registrationUrl ||\n\t\t\tctx.req.path === cfg.registerConfirmationUrl ||\n\t\t\tctx.req.path === cfg.appActivateUrl ||\n\t\t\tctx.req.path === cfg.appDeactivateUrl ||\n\t\t\tctx.req.path === cfg.appDeleteUrl\n\t\t) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tlet context: Context<ShopInterface, unknown>;\n\t\ttry {\n\t\t\tcontext =\n\t\t\t\tctx.req.method === \"GET\"\n\t\t\t\t\t? await app.contextResolver.fromBrowser(ctx.req.raw)\n\t\t\t\t\t: await app.contextResolver.fromAPI(ctx.req.raw);\n\t\t} catch (_e) {\n\t\t\treturn jsonResponse({ message: \"Invalid request\" }, 400);\n\t\t}\n\n\t\t// @ts-ignore\n\t\tctx.set(\"shop\", context.shop);\n\t\t// @ts-ignore\n\t\tctx.set(\"context\", context);\n\n\t\tawait next();\n\n\t\tconst cloned = ctx.res.clone();\n\n\t\tawait ctx\n\t\t\t.get(\"app\")\n\t\t\t.signer.signResponse(cloned, ctx.get(\"shop\").getShopSecret());\n\n\t\tctx.header(\n\t\t\t\"shopware-app-signature\",\n\t\t\tcloned.headers.get(\"shopware-app-signature\") as string,\n\t\t);\n\t});\n\n\thono.get(cfg.registrationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorize(ctx.req.raw);\n\t});\n\n\thono.post(cfg.registerConfirmationUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.authorizeCallback(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appInstallUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.install(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appActivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.activate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appUpdateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.update(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeactivateUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.deactivate(ctx.req.raw);\n\t});\n\n\thono.post(cfg.appDeleteUrl, async (ctx) => {\n\t\tconst app = ctx.get(\"app\");\n\n\t\treturn await app.registration.delete(ctx.req.raw);\n\t});\n\n\tif (cfg.appIframeEnable) {\n\t\thono.use(cfg.appIframePath, async (ctx, next) => {\n\t\t\tconst shopId = await getSignedCookie(\n\t\t\t\tctx,\n\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t\"shop\",\n\t\t\t);\n\n\t\t\tif (!shopId) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tconst shop = await ctx.get(\"app\").repository.getShopById(shopId);\n\n\t\t\tif (!shop) {\n\t\t\t\treturn ctx.json({ message: \"Shop not found\" }, { status: 400 });\n\t\t\t}\n\n\t\t\tctx.set(\"shop\", shop);\n\t\t\t// @ts-ignore\n\t\t\tctx.set(\"context\", new Context(shop, {}, new HttpClient(shop, ctx.get('app').httpClientTokenCache)));\n\n\t\t\tawait next();\n\t\t});\n\n\t\tfor (let [path, redirect] of Object.entries(cfg.appIframeRedirects || {})) {\n\t\t\thono.get(path, async (ctx) => {\n\t\t\t\tconst url = new URL(ctx.req.url);\n\n\t\t\t\tif (redirect.startsWith(\"/\")) {\n\t\t\t\t\turl.pathname = redirect;\n\t\t\t\t\tredirect = url.toString();\n\t\t\t\t} else {\n\t\t\t\t\tconst newUrl = new URL(redirect);\n\n\t\t\t\t\tfor (const [key, value] of url.searchParams) {\n\t\t\t\t\t\tnewUrl.searchParams.set(key, value);\n\t\t\t\t\t}\n\n\t\t\t\t\tredirect = newUrl.toString();\n\t\t\t\t}\n\n\t\t\t\tawait setSignedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\t\"shop\",\n\t\t\t\t\tctx.get(\"shop\").getShopId(),\n\t\t\t\t\tctx.get(\"app\").cfg.appSecret,\n\t\t\t\t);\n\n\t\t\t\treturn ctx.redirect(redirect);\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction jsonResponse(body: object, status = 200): Response {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t});\n}\n\nfunction buildBaseUrl(url: string): string {\n\tconst u = new URL(url);\n\n\tlet protocol = u.protocol;\n\n\tif (typeof process !== \"undefined\" && process.env?.SHOPWARE_APP_SDK_FORCE_HTTPS) {\n\t\tprotocol = \"https:\";\n\t}\n\n\treturn `${protocol}//${u.host}`;\n}\n"]}
|