@shopware-ag/app-server-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +43 -0
  2. package/dist/commonjs/app.d.ts +31 -0
  3. package/dist/commonjs/app.d.ts.map +1 -0
  4. package/dist/commonjs/app.js +25 -0
  5. package/dist/commonjs/app.js.map +1 -0
  6. package/dist/commonjs/context-resolver.d.ts +30 -0
  7. package/dist/commonjs/context-resolver.d.ts.map +1 -0
  8. package/dist/commonjs/context-resolver.js +70 -0
  9. package/dist/commonjs/context-resolver.js.map +1 -0
  10. package/dist/commonjs/framework/hono.d.ts +36 -0
  11. package/dist/commonjs/framework/hono.d.ts.map +1 -0
  12. package/dist/commonjs/framework/hono.js +84 -0
  13. package/dist/commonjs/framework/hono.js.map +1 -0
  14. package/dist/commonjs/helper/app-actions.d.ts +13 -0
  15. package/dist/commonjs/helper/app-actions.d.ts.map +1 -0
  16. package/dist/commonjs/helper/app-actions.js +54 -0
  17. package/dist/commonjs/helper/app-actions.js.map +1 -0
  18. package/dist/commonjs/http-client.d.ts +67 -0
  19. package/dist/commonjs/http-client.d.ts.map +1 -0
  20. package/dist/commonjs/http-client.js +155 -0
  21. package/dist/commonjs/http-client.js.map +1 -0
  22. package/dist/commonjs/mod.d.ts +6 -0
  23. package/dist/commonjs/mod.d.ts.map +1 -0
  24. package/dist/commonjs/mod.js +16 -0
  25. package/dist/commonjs/mod.js.map +1 -0
  26. package/dist/commonjs/package.json +3 -0
  27. package/dist/commonjs/registration.d.ts +18 -0
  28. package/dist/commonjs/registration.d.ts.map +1 -0
  29. package/dist/commonjs/registration.js +89 -0
  30. package/dist/commonjs/registration.js.map +1 -0
  31. package/dist/commonjs/repository.d.ts +51 -0
  32. package/dist/commonjs/repository.d.ts.map +1 -0
  33. package/dist/commonjs/repository.js +68 -0
  34. package/dist/commonjs/repository.js.map +1 -0
  35. package/dist/commonjs/service/cloudflare.d.ts +43 -0
  36. package/dist/commonjs/service/cloudflare.d.ts.map +1 -0
  37. package/dist/commonjs/service/cloudflare.js +45 -0
  38. package/dist/commonjs/service/cloudflare.js.map +1 -0
  39. package/dist/commonjs/service/deno.d.ts +18 -0
  40. package/dist/commonjs/service/deno.d.ts.map +1 -0
  41. package/dist/commonjs/service/deno.js +48 -0
  42. package/dist/commonjs/service/deno.js.map +1 -0
  43. package/dist/commonjs/signer.d.ts +12 -0
  44. package/dist/commonjs/signer.d.ts.map +1 -0
  45. package/dist/commonjs/signer.js +58 -0
  46. package/dist/commonjs/signer.js.map +1 -0
  47. package/dist/commonjs/types.d.ts +28 -0
  48. package/dist/commonjs/types.d.ts.map +1 -0
  49. package/dist/commonjs/types.js +3 -0
  50. package/dist/commonjs/types.js.map +1 -0
  51. package/dist/deno/app.d.ts +31 -0
  52. package/dist/deno/app.d.ts.map +1 -0
  53. package/dist/deno/app.js +21 -0
  54. package/dist/deno/app.js.map +1 -0
  55. package/dist/deno/context-resolver.d.ts +30 -0
  56. package/dist/deno/context-resolver.d.ts.map +1 -0
  57. package/dist/deno/context-resolver.js +65 -0
  58. package/dist/deno/context-resolver.js.map +1 -0
  59. package/dist/deno/framework/hono.d.ts +36 -0
  60. package/dist/deno/framework/hono.d.ts.map +1 -0
  61. package/dist/deno/framework/hono.js +81 -0
  62. package/dist/deno/framework/hono.js.map +1 -0
  63. package/dist/deno/helper/app-actions.d.ts +13 -0
  64. package/dist/deno/helper/app-actions.d.ts.map +1 -0
  65. package/dist/deno/helper/app-actions.js +49 -0
  66. package/dist/deno/helper/app-actions.js.map +1 -0
  67. package/dist/deno/http-client.d.ts +67 -0
  68. package/dist/deno/http-client.d.ts.map +1 -0
  69. package/dist/deno/http-client.js +148 -0
  70. package/dist/deno/http-client.js.map +1 -0
  71. package/dist/deno/mod.d.ts +6 -0
  72. package/dist/deno/mod.d.ts.map +1 -0
  73. package/dist/deno/mod.js +5 -0
  74. package/dist/deno/mod.js.map +1 -0
  75. package/dist/deno/package.json +3 -0
  76. package/dist/deno/registration.d.ts +18 -0
  77. package/dist/deno/registration.d.ts.map +1 -0
  78. package/dist/deno/registration.js +84 -0
  79. package/dist/deno/registration.js.map +1 -0
  80. package/dist/deno/repository.d.ts +51 -0
  81. package/dist/deno/repository.d.ts.map +1 -0
  82. package/dist/deno/repository.js +63 -0
  83. package/dist/deno/repository.js.map +1 -0
  84. package/dist/deno/service/cloudflare.d.ts +43 -0
  85. package/dist/deno/service/cloudflare.d.ts.map +1 -0
  86. package/dist/deno/service/cloudflare.js +41 -0
  87. package/dist/deno/service/cloudflare.js.map +1 -0
  88. package/dist/deno/service/deno.d.ts +18 -0
  89. package/dist/deno/service/deno.d.ts.map +1 -0
  90. package/dist/deno/service/deno.js +44 -0
  91. package/dist/deno/service/deno.js.map +1 -0
  92. package/dist/deno/signer.d.ts +12 -0
  93. package/dist/deno/signer.d.ts.map +1 -0
  94. package/dist/deno/signer.js +54 -0
  95. package/dist/deno/signer.js.map +1 -0
  96. package/dist/deno/types.d.ts +28 -0
  97. package/dist/deno/types.d.ts.map +1 -0
  98. package/dist/deno/types.js +2 -0
  99. package/dist/deno/types.js.map +1 -0
  100. package/dist/esm/app.d.ts +31 -0
  101. package/dist/esm/app.d.ts.map +1 -0
  102. package/dist/esm/app.js +21 -0
  103. package/dist/esm/app.js.map +1 -0
  104. package/dist/esm/context-resolver.d.ts +30 -0
  105. package/dist/esm/context-resolver.d.ts.map +1 -0
  106. package/dist/esm/context-resolver.js +65 -0
  107. package/dist/esm/context-resolver.js.map +1 -0
  108. package/dist/esm/framework/hono.d.ts +36 -0
  109. package/dist/esm/framework/hono.d.ts.map +1 -0
  110. package/dist/esm/framework/hono.js +81 -0
  111. package/dist/esm/framework/hono.js.map +1 -0
  112. package/dist/esm/helper/app-actions.d.ts +13 -0
  113. package/dist/esm/helper/app-actions.d.ts.map +1 -0
  114. package/dist/esm/helper/app-actions.js +49 -0
  115. package/dist/esm/helper/app-actions.js.map +1 -0
  116. package/dist/esm/http-client.d.ts +67 -0
  117. package/dist/esm/http-client.d.ts.map +1 -0
  118. package/dist/esm/http-client.js +148 -0
  119. package/dist/esm/http-client.js.map +1 -0
  120. package/dist/esm/mod.d.ts +6 -0
  121. package/dist/esm/mod.d.ts.map +1 -0
  122. package/dist/esm/mod.js +5 -0
  123. package/dist/esm/mod.js.map +1 -0
  124. package/dist/esm/package.json +3 -0
  125. package/dist/esm/registration.d.ts +18 -0
  126. package/dist/esm/registration.d.ts.map +1 -0
  127. package/dist/esm/registration.js +84 -0
  128. package/dist/esm/registration.js.map +1 -0
  129. package/dist/esm/repository.d.ts +51 -0
  130. package/dist/esm/repository.d.ts.map +1 -0
  131. package/dist/esm/repository.js +63 -0
  132. package/dist/esm/repository.js.map +1 -0
  133. package/dist/esm/service/cloudflare.d.ts +43 -0
  134. package/dist/esm/service/cloudflare.d.ts.map +1 -0
  135. package/dist/esm/service/cloudflare.js +41 -0
  136. package/dist/esm/service/cloudflare.js.map +1 -0
  137. package/dist/esm/service/deno.d.ts +18 -0
  138. package/dist/esm/service/deno.d.ts.map +1 -0
  139. package/dist/esm/service/deno.js +44 -0
  140. package/dist/esm/service/deno.js.map +1 -0
  141. package/dist/esm/signer.d.ts +12 -0
  142. package/dist/esm/signer.d.ts.map +1 -0
  143. package/dist/esm/signer.js +54 -0
  144. package/dist/esm/signer.js.map +1 -0
  145. package/dist/esm/types.d.ts +28 -0
  146. package/dist/esm/types.d.ts.map +1 -0
  147. package/dist/esm/types.js +2 -0
  148. package/dist/esm/types.js.map +1 -0
  149. package/package.json +72 -0
@@ -0,0 +1,18 @@
1
+ import { SimpleShop } from "../repository.js";
2
+ import type { ShopRepositoryInterface } from "../repository.js";
3
+ /**
4
+ * Deno KV integration
5
+ * @module
6
+ */
7
+ /**
8
+ * DenoKVRepository is a ShopRepositoryInterface implementation that uses the Deno KV storage to save the shop data
9
+ */
10
+ export declare class DenoKVRepository implements ShopRepositoryInterface<SimpleShop> {
11
+ private namespace;
12
+ constructor(namespace?: string);
13
+ createShop(id: string, url: string, secret: string): Promise<void>;
14
+ getShopById(id: string): Promise<SimpleShop | null>;
15
+ updateShop(shop: SimpleShop): Promise<void>;
16
+ deleteShop(id: string): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=deno.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../../../src/service/deno.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAiB,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E;;;GAGG;AAEH;;GAEG;AACH,qBAAa,gBAAiB,YAAW,uBAAuB,CAAC,UAAU,CAAC;IAC/D,OAAO,CAAC,SAAS;gBAAT,SAAS,SAAU;IAEjC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlE,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA2BnD,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO3C,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAM3C"}
@@ -0,0 +1,44 @@
1
+ import { SimpleShop } from "../repository.js";
2
+ /**
3
+ * Deno KV integration
4
+ * @module
5
+ */
6
+ /**
7
+ * DenoKVRepository is a ShopRepositoryInterface implementation that uses the Deno KV storage to save the shop data
8
+ */
9
+ export class DenoKVRepository {
10
+ namespace;
11
+ constructor(namespace = "shops") {
12
+ this.namespace = namespace;
13
+ }
14
+ async createShop(id, url, secret) {
15
+ // @ts-ignore
16
+ const kv = await Deno.openKv();
17
+ await kv.set([this.namespace, id], new SimpleShop(id, url, secret));
18
+ }
19
+ async getShopById(id) {
20
+ // @ts-ignore
21
+ const kv = await Deno.openKv();
22
+ const result = await kv.get([this.namespace, id]);
23
+ if (result.key === null) {
24
+ return null;
25
+ }
26
+ const data = result.value;
27
+ const shop = new SimpleShop(data.shopId, data.shopUrl, data.shopSecret);
28
+ if (data.shopClientId && data.shopClientSecret) {
29
+ shop.setShopCredentials(data.shopClientId, data.shopClientSecret);
30
+ }
31
+ return shop;
32
+ }
33
+ async updateShop(shop) {
34
+ // @ts-ignore
35
+ const kv = await Deno.openKv();
36
+ await kv.set([this.namespace, shop.getShopId()], shop);
37
+ }
38
+ async deleteShop(id) {
39
+ // @ts-ignore
40
+ const kv = await Deno.openKv();
41
+ await kv.delete([this.namespace, id]);
42
+ }
43
+ }
44
+ //# sourceMappingURL=deno.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deno.js","sourceRoot":"","sources":["../../../src/service/deno.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;GAGG;AAEH;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACR;IAApB,YAAoB,YAAY,OAAO;QAAnB,cAAS,GAAT,SAAS,CAAU;IAAG,CAAC;IAE3C,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,GAAW,EAAE,MAAc;QACvD,aAAa;QACb,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAE/B,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,IAAI,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU;QAC3B,aAAa;QACb,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QAElD,IAAI,MAAM,CAAC,GAAG,KAAK,IAAI,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,KAMnB,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAExE,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAChD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAgB;QAChC,aAAa;QACb,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAE/B,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU;QAC1B,aAAa;QACb,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QAE/B,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;IACvC,CAAC;CACD","sourcesContent":["import { SimpleShop } from \"../repository.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\n/**\n * Deno KV integration\n * @module\n */\n\n/**\n * DenoKVRepository is a ShopRepositoryInterface implementation that uses the Deno KV storage to save the shop data\n */\nexport class DenoKVRepository implements ShopRepositoryInterface<SimpleShop> {\n\tconstructor(private namespace = \"shops\") {}\n\n\tasync createShop(id: string, url: string, secret: string): Promise<void> {\n\t\t// @ts-ignore\n\t\tconst kv = await Deno.openKv();\n\n\t\tawait kv.set([this.namespace, id], new SimpleShop(id, url, secret));\n\t}\n\n\tasync getShopById(id: string): Promise<SimpleShop | null> {\n\t\t// @ts-ignore\n\t\tconst kv = await Deno.openKv();\n\n\t\tconst result = await kv.get([this.namespace, id]);\n\n\t\tif (result.key === null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst data = result.value as {\n\t\t\tshopId: string;\n\t\t\tshopUrl: string;\n\t\t\tshopSecret: string;\n\t\t\tshopClientId: string | null;\n\t\t\tshopClientSecret: string | null;\n\t\t};\n\n\t\tconst shop = new SimpleShop(data.shopId, data.shopUrl, data.shopSecret);\n\n\t\tif (data.shopClientId && data.shopClientSecret) {\n\t\t\tshop.setShopCredentials(data.shopClientId, data.shopClientSecret);\n\t\t}\n\n\t\treturn shop;\n\t}\n\n\tasync updateShop(shop: SimpleShop): Promise<void> {\n\t\t// @ts-ignore\n\t\tconst kv = await Deno.openKv();\n\n\t\tawait kv.set([this.namespace, shop.getShopId()], shop);\n\t}\n\n\tasync deleteShop(id: string): Promise<void> {\n\t\t// @ts-ignore\n\t\tconst kv = await Deno.openKv();\n\n\t\tawait kv.delete([this.namespace, id]);\n\t}\n}\n"]}
@@ -0,0 +1,12 @@
1
+ export declare class WebCryptoHmacSigner {
2
+ private encoder;
3
+ private keyCache;
4
+ constructor();
5
+ signResponse(response: Response, secret: string): Promise<void>;
6
+ verifyGetRequest(request: Request, secret: string): Promise<void>;
7
+ getKeyForSecret(secret: string): Promise<CryptoKey>;
8
+ sign(message: string, secret: string): Promise<string>;
9
+ verify(signature: string, data: string, secret: string): Promise<boolean>;
10
+ buf2hex(buf: ArrayBuffer): string;
11
+ }
12
+ //# sourceMappingURL=signer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"AAAA,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAyB;;IAOnC,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/D,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjE,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAmBnD,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYtD,MAAM,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC;IAMnB,OAAO,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM;CAKjC"}
@@ -0,0 +1,54 @@
1
+ export class WebCryptoHmacSigner {
2
+ encoder;
3
+ keyCache;
4
+ constructor() {
5
+ this.encoder = new TextEncoder();
6
+ this.keyCache = new Map();
7
+ }
8
+ async signResponse(response, secret) {
9
+ response.headers.set("shopware-app-signature", await this.sign(await response.clone().text(), secret));
10
+ }
11
+ async verifyGetRequest(request, secret) {
12
+ const url = new URL(request.url);
13
+ const signature = url.searchParams.get("shopware-shop-signature");
14
+ if (signature === null) {
15
+ throw new Error("Missing shopware-shop-signature query parameter");
16
+ }
17
+ url.searchParams.delete("shopware-shop-signature");
18
+ let body = "";
19
+ for (const searchKey of url.searchParams.keys()) {
20
+ body += `${searchKey}=${decodeURIComponent(url.searchParams.get(searchKey))}&`;
21
+ }
22
+ body = body.slice(0, -1);
23
+ if (body === "") {
24
+ throw new Error("Missing query parameters to verify the GET request");
25
+ }
26
+ if (!(await this.verify(signature, body, secret))) {
27
+ throw new Error("Invalid signature");
28
+ }
29
+ }
30
+ async getKeyForSecret(secret) {
31
+ if (this.keyCache.has(secret)) {
32
+ return this.keyCache.get(secret);
33
+ }
34
+ const secretKeyData = this.encoder.encode(secret);
35
+ const key = await crypto.subtle.importKey("raw", secretKeyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]);
36
+ this.keyCache.set(secret, key);
37
+ return key;
38
+ }
39
+ async sign(message, secret) {
40
+ const key = await this.getKeyForSecret(secret);
41
+ const mac = await crypto.subtle.sign("HMAC", key, this.encoder.encode(message));
42
+ return this.buf2hex(mac);
43
+ }
44
+ async verify(signature, data, secret) {
45
+ const signed = await this.sign(data, secret);
46
+ return signature === signed;
47
+ }
48
+ buf2hex(buf) {
49
+ return Array.prototype.map
50
+ .call(new Uint8Array(buf), (x) => `00${x.toString(16)}`.slice(-2))
51
+ .join("");
52
+ }
53
+ }
54
+ //# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/signer.ts"],"names":[],"mappings":"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","sourcesContent":["export class WebCryptoHmacSigner {\n\tprivate encoder: TextEncoder;\n\tprivate keyCache: Map<string, CryptoKey>;\n\n\tconstructor() {\n\t\tthis.encoder = new TextEncoder();\n\t\tthis.keyCache = new Map<string, CryptoKey>();\n\t}\n\n\tasync signResponse(response: Response, secret: string): Promise<void> {\n\t\tresponse.headers.set(\n\t\t\t\"shopware-app-signature\",\n\t\t\tawait this.sign(await response.clone().text(), secret),\n\t\t);\n\t}\n\n\tasync verifyGetRequest(request: Request, secret: string): Promise<void> {\n\t\tconst url = new URL(request.url);\n\t\tconst signature = url.searchParams.get(\"shopware-shop-signature\");\n\n\t\tif (signature === null) {\n\t\t\tthrow new Error(\"Missing shopware-shop-signature query parameter\");\n\t\t}\n\n\t\turl.searchParams.delete(\"shopware-shop-signature\");\n\n\t\tlet body = \"\";\n\n\t\tfor (const searchKey of url.searchParams.keys()) {\n\t\t\tbody += `${searchKey}=${decodeURIComponent(url.searchParams.get(searchKey) as string)}&`;\n\t\t}\n\n\t\tbody = body.slice(0, -1);\n\n\t\tif (body === \"\") {\n\t\t\tthrow new Error(\"Missing query parameters to verify the GET request\");\n\t\t}\n\n\t\tif (!(await this.verify(signature, body, secret))) {\n\t\t\tthrow new Error(\"Invalid signature\");\n\t\t}\n\t}\n\n\tasync getKeyForSecret(secret: string): Promise<CryptoKey> {\n\t\tif (this.keyCache.has(secret)) {\n\t\t\treturn this.keyCache.get(secret) as CryptoKey;\n\t\t}\n\n\t\tconst secretKeyData = this.encoder.encode(secret);\n\t\tconst key = await crypto.subtle.importKey(\n\t\t\t\"raw\",\n\t\t\tsecretKeyData,\n\t\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\t\tfalse,\n\t\t\t[\"sign\", \"verify\"],\n\t\t);\n\n\t\tthis.keyCache.set(secret, key);\n\n\t\treturn key;\n\t}\n\n\tasync sign(message: string, secret: string): Promise<string> {\n\t\tconst key = await this.getKeyForSecret(secret);\n\n\t\tconst mac = await crypto.subtle.sign(\n\t\t\t\"HMAC\",\n\t\t\tkey as CryptoKey,\n\t\t\tthis.encoder.encode(message),\n\t\t);\n\n\t\treturn this.buf2hex(mac);\n\t}\n\n\tasync verify(\n\t\tsignature: string,\n\t\tdata: string,\n\t\tsecret: string,\n\t): Promise<boolean> {\n\t\tconst signed = await this.sign(data, secret);\n\n\t\treturn signature === signed;\n\t}\n\n\tbuf2hex(buf: ArrayBuffer): string {\n\t\treturn Array.prototype.map\n\t\t\t.call(new Uint8Array(buf), (x) => `00${x.toString(16)}`.slice(-2))\n\t\t\t.join(\"\");\n\t}\n}\n"]}
@@ -0,0 +1,28 @@
1
+ export type BrowserAppModuleRequest = {
2
+ "shop-id": string;
3
+ "shop-url": string;
4
+ timestamp: string;
5
+ "sw-version": string;
6
+ "sw-context-language": string;
7
+ "sw-user-language": string;
8
+ };
9
+ export type Source = {
10
+ url: string;
11
+ shopId: string;
12
+ appVersion: string;
13
+ };
14
+ export type Meta = {
15
+ timestamp: number;
16
+ reference: string;
17
+ language: string;
18
+ };
19
+ export type ActionButtonRequest = {
20
+ source: Source;
21
+ data: {
22
+ ids: string[];
23
+ entity: string;
24
+ action: string;
25
+ };
26
+ meta: Meta;
27
+ };
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACL,GAAG,EAAE,MAAM,EAAE,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KACf,CAAC;IACF,IAAI,EAAE,IAAI,CAAC;CACX,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export type BrowserAppModuleRequest = {\n\t\"shop-id\": string;\n\t\"shop-url\": string;\n\ttimestamp: string;\n\t\"sw-version\": string;\n\t\"sw-context-language\": string;\n\t\"sw-user-language\": string;\n};\n\nexport type Source = {\n\turl: string;\n\tshopId: string;\n\tappVersion: string;\n};\n\nexport type Meta = {\n\ttimestamp: number;\n\treference: string;\n\tlanguage: string;\n};\n\nexport type ActionButtonRequest = {\n\tsource: Source;\n\tdata: {\n\t\tids: string[];\n\t\tentity: string;\n\t\taction: string;\n\t};\n\tmeta: Meta;\n};\n"]}
@@ -0,0 +1,31 @@
1
+ import { ContextResolver } from "./context-resolver.js";
2
+ import { Registration } from "./registration.js";
3
+ import type { ShopInterface, ShopRepositoryInterface } from "./repository.js";
4
+ import { WebCryptoHmacSigner } from "./signer.js";
5
+ /**
6
+ * AppServer is the main class, this is where you start your app
7
+ */
8
+ export declare class AppServer<Shop extends ShopInterface = ShopInterface> {
9
+ cfg: Configuration;
10
+ repository: ShopRepositoryInterface<Shop>;
11
+ registration: Registration;
12
+ contextResolver: ContextResolver<Shop>;
13
+ signer: WebCryptoHmacSigner;
14
+ constructor(cfg: Configuration, repository: ShopRepositoryInterface<Shop>);
15
+ }
16
+ interface Configuration {
17
+ /**
18
+ * Your app name
19
+ */
20
+ appName: string;
21
+ /**
22
+ * Your app secret
23
+ */
24
+ appSecret: string;
25
+ /**
26
+ * URL to authorize callback url
27
+ */
28
+ authorizeCallbackUrl: string;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD;;GAEG;AACH,qBAAa,SAAS,CAAC,IAAI,SAAS,aAAa,GAAG,aAAa;IAMxD,GAAG,EAAE,aAAa;IAClB,UAAU,EAAE,uBAAuB,CAAC,IAAI,CAAC;IAN1C,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,EAAE,mBAAmB,CAAC;gBAG3B,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,uBAAuB,CAAC,IAAI,CAAC;CAMjD;AAED,UAAU,aAAa;IACtB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,oBAAoB,EAAE,MAAM,CAAC;CAC7B"}
@@ -0,0 +1,21 @@
1
+ import { ContextResolver } from "./context-resolver.js";
2
+ import { Registration } from "./registration.js";
3
+ import { WebCryptoHmacSigner } from "./signer.js";
4
+ /**
5
+ * AppServer is the main class, this is where you start your app
6
+ */
7
+ export class AppServer {
8
+ cfg;
9
+ repository;
10
+ registration;
11
+ contextResolver;
12
+ signer;
13
+ constructor(cfg, repository) {
14
+ this.cfg = cfg;
15
+ this.repository = repository;
16
+ this.registration = new Registration(this);
17
+ this.contextResolver = new ContextResolver(this);
18
+ this.signer = new WebCryptoHmacSigner();
19
+ }
20
+ }
21
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD;;GAEG;AACH,MAAM,OAAO,SAAS;IAMb;IACA;IAND,YAAY,CAAe;IAC3B,eAAe,CAAwB;IACvC,MAAM,CAAsB;IAEnC,YACQ,GAAkB,EAClB,UAAyC;QADzC,QAAG,GAAH,GAAG,CAAe;QAClB,eAAU,GAAV,UAAU,CAA+B;QAEhD,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;IACzC,CAAC;CACD","sourcesContent":["import { ContextResolver } from \"./context-resolver.js\";\nimport { Registration } from \"./registration.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"./repository.js\";\nimport { WebCryptoHmacSigner } from \"./signer.js\";\n\n/**\n * AppServer is the main class, this is where you start your app\n */\nexport class AppServer<Shop extends ShopInterface = ShopInterface> {\n\tpublic registration: Registration;\n\tpublic contextResolver: ContextResolver<Shop>;\n\tpublic signer: WebCryptoHmacSigner;\n\n\tconstructor(\n\t\tpublic cfg: Configuration,\n\t\tpublic repository: ShopRepositoryInterface<Shop>,\n\t) {\n\t\tthis.registration = new Registration(this);\n\t\tthis.contextResolver = new ContextResolver(this);\n\t\tthis.signer = new WebCryptoHmacSigner();\n\t}\n}\n\ninterface Configuration {\n\t/**\n\t * Your app name\n\t */\n\tappName: string;\n\n\t/**\n\t * Your app secret\n\t */\n\tappSecret: string;\n\n\t/**\n\t * URL to authorize callback url\n\t */\n\tauthorizeCallbackUrl: string;\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import type { AppServer } from "./app.js";
2
+ import { HttpClient } from "./http-client.js";
3
+ import type { ShopInterface } from "./repository.js";
4
+ /**
5
+ * ContextResolver is a helper class to create a Context object from a request.
6
+ * The context contains the shop, the payload and an instance of the HttpClient
7
+ */
8
+ export declare class ContextResolver<Shop extends ShopInterface = ShopInterface> {
9
+ private app;
10
+ constructor(app: AppServer);
11
+ /**
12
+ * Create a context from a request body
13
+ */
14
+ fromAPI<Payload = unknown>(req: Request): Promise<Context<Shop, Payload>>;
15
+ /**
16
+ * Create a context from a request query parameters
17
+ * This is usually a module request from the shopware admin
18
+ */
19
+ fromBrowser<Payload = unknown>(req: Request): Promise<Context<Shop, Payload>>;
20
+ }
21
+ /**
22
+ * Context is the parsed data from the request
23
+ */
24
+ export declare class Context<Shop extends ShopInterface = ShopInterface, Payload = unknown> {
25
+ shop: Shop;
26
+ payload: Payload;
27
+ httpClient: HttpClient;
28
+ constructor(shop: Shop, payload: Payload, httpClient: HttpClient);
29
+ }
30
+ //# sourceMappingURL=context-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-resolver.d.ts","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;GAGG;AACH,qBAAa,eAAe,CAAC,IAAI,SAAS,aAAa,GAAG,aAAa;IAC1D,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,SAAS;IAElC;;OAEG;IACU,OAAO,CAAC,OAAO,GAAG,OAAO,EACrC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAmClC;;;OAGG;IACU,WAAW,CAAC,OAAO,GAAG,OAAO,EACzC,GAAG,EAAE,OAAO,GACV,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;CA6BlC;AAED;;GAEG;AACH,qBAAa,OAAO,CACnB,IAAI,SAAS,aAAa,GAAG,aAAa,EAC1C,OAAO,GAAG,OAAO;IAGT,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,OAAO;IAChB,UAAU,EAAE,UAAU;gBAFtB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,UAAU;CAE9B"}
@@ -0,0 +1,65 @@
1
+ import { HttpClient } from "./http-client.js";
2
+ /**
3
+ * ContextResolver is a helper class to create a Context object from a request.
4
+ * The context contains the shop, the payload and an instance of the HttpClient
5
+ */
6
+ export class ContextResolver {
7
+ app;
8
+ constructor(app) {
9
+ this.app = app;
10
+ }
11
+ /**
12
+ * Create a context from a request body
13
+ */
14
+ async fromAPI(req) {
15
+ const webHookContent = await req.text();
16
+ const webHookBody = JSON.parse(webHookContent);
17
+ const shop = await this.app.repository.getShopById(webHookBody.source.shopId);
18
+ if (shop === null) {
19
+ throw new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);
20
+ }
21
+ const signature = req.headers.get("shopware-shop-signature");
22
+ if (signature === null) {
23
+ throw new Error("Missing shopware-shop-signature header");
24
+ }
25
+ if (!(await this.app.signer.verify(signature, webHookContent, shop.getShopSecret()))) {
26
+ throw new Error("Invalid signature");
27
+ }
28
+ return new Context(shop, webHookBody, new HttpClient(shop));
29
+ }
30
+ /**
31
+ * Create a context from a request query parameters
32
+ * This is usually a module request from the shopware admin
33
+ */
34
+ async fromBrowser(req) {
35
+ const url = new URL(req.url);
36
+ const shopId = url.searchParams.get("shop-id");
37
+ if (shopId === null) {
38
+ throw new Error("Missing shop-id query parameter");
39
+ }
40
+ const shop = await this.app.repository.getShopById(shopId);
41
+ if (shop === null) {
42
+ throw new Error(`Cannot find shop by id ${shopId}`);
43
+ }
44
+ await this.app.signer.verifyGetRequest(req, shop.getShopSecret());
45
+ const paramsObject = {};
46
+ url.searchParams.forEach((value, key) => {
47
+ paramsObject[key] = value;
48
+ });
49
+ return new Context(shop, paramsObject, new HttpClient(shop));
50
+ }
51
+ }
52
+ /**
53
+ * Context is the parsed data from the request
54
+ */
55
+ export class Context {
56
+ shop;
57
+ payload;
58
+ httpClient;
59
+ constructor(shop, payload, httpClient) {
60
+ this.shop = shop;
61
+ this.payload = payload;
62
+ this.httpClient = httpClient;
63
+ }
64
+ }
65
+ //# sourceMappingURL=context-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-resolver.js","sourceRoot":"","sources":["../../src/context-resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C;;;GAGG;AACH,MAAM,OAAO,eAAe;IACP;IAApB,YAAoB,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAEtC;;OAEG;IACI,KAAK,CAAC,OAAO,CACnB,GAAY;QAEZ,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAE/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CACjD,WAAW,CAAC,MAAM,CAAC,MAAM,CACzB,CAAC;QAEF,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0BAA0B,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAE7D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC3D,CAAC;QAED,IACC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAC7B,SAAS,EACT,cAAc,EACd,IAAI,CAAC,aAAa,EAAE,CACpB,CAAC,EACD,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,IAAI,OAAO,CACjB,IAAY,EACZ,WAAW,EACX,IAAI,UAAU,CAAC,IAAI,CAAC,CACpB,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CACvB,GAAY;QAEZ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE/C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAE3D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAElE,MAAM,YAAY,GAA2B,EAAE,CAAC;QAEhD,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACvC,YAAY,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,OAAO,CACjB,IAAY,EACZ,YAAuB,EACvB,IAAI,UAAU,CAAC,IAAI,CAAC,CACpB,CAAC;IACH,CAAC;CACD;AAED;;GAEG;AACH,MAAM,OAAO,OAAO;IAKX;IACA;IACA;IAHR,YACQ,IAAU,EACV,OAAgB,EAChB,UAAsB;QAFtB,SAAI,GAAJ,IAAI,CAAM;QACV,YAAO,GAAP,OAAO,CAAS;QAChB,eAAU,GAAV,UAAU,CAAY;IAC3B,CAAC;CACJ","sourcesContent":["import type { AppServer } from \"./app.js\";\nimport { HttpClient } from \"./http-client.js\";\nimport type { ShopInterface } from \"./repository.js\";\n\n/**\n * ContextResolver is a helper class to create a Context object from a request.\n * The context contains the shop, the payload and an instance of the HttpClient\n */\nexport class ContextResolver<Shop extends ShopInterface = ShopInterface> {\n\tconstructor(private app: AppServer) {}\n\n\t/**\n\t * Create a context from a request body\n\t */\n\tpublic async fromAPI<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst webHookContent = await req.text();\n\t\tconst webHookBody = JSON.parse(webHookContent);\n\n\t\tconst shop = await this.app.repository.getShopById(\n\t\t\twebHookBody.source.shopId,\n\t\t);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${webHookBody.source.shopId}`);\n\t\t}\n\n\t\tconst signature = req.headers.get(\"shopware-shop-signature\");\n\n\t\tif (signature === null) {\n\t\t\tthrow new Error(\"Missing shopware-shop-signature header\");\n\t\t}\n\n\t\tif (\n\t\t\t!(await this.app.signer.verify(\n\t\t\t\tsignature,\n\t\t\t\twebHookContent,\n\t\t\t\tshop.getShopSecret(),\n\t\t\t))\n\t\t) {\n\t\t\tthrow new Error(\"Invalid signature\");\n\t\t}\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\twebHookBody,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n\n\t/**\n\t * Create a context from a request query parameters\n\t * This is usually a module request from the shopware admin\n\t */\n\tpublic async fromBrowser<Payload = unknown>(\n\t\treq: Request,\n\t): Promise<Context<Shop, Payload>> {\n\t\tconst url = new URL(req.url);\n\n\t\tconst shopId = url.searchParams.get(\"shop-id\");\n\n\t\tif (shopId === null) {\n\t\t\tthrow new Error(\"Missing shop-id query parameter\");\n\t\t}\n\n\t\tconst shop = await this.app.repository.getShopById(shopId);\n\n\t\tif (shop === null) {\n\t\t\tthrow new Error(`Cannot find shop by id ${shopId}`);\n\t\t}\n\n\t\tawait this.app.signer.verifyGetRequest(req, shop.getShopSecret());\n\n\t\tconst paramsObject: Record<string, string> = {};\n\n\t\turl.searchParams.forEach((value, key) => {\n\t\t\tparamsObject[key] = value;\n\t\t});\n\n\t\treturn new Context<Shop, Payload>(\n\t\t\tshop as Shop,\n\t\t\tparamsObject as Payload,\n\t\t\tnew HttpClient(shop),\n\t\t);\n\t}\n}\n\n/**\n * Context is the parsed data from the request\n */\nexport class Context<\n\tShop extends ShopInterface = ShopInterface,\n\tPayload = unknown,\n> {\n\tconstructor(\n\t\tpublic shop: Shop,\n\t\tpublic payload: Payload,\n\t\tpublic httpClient: HttpClient,\n\t) {}\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { AppServer } from "../app.js";
2
+ import type { Context } from "../context-resolver.js";
3
+ import type { ShopInterface, ShopRepositoryInterface } from "../repository.js";
4
+ interface MiddlewareConfig {
5
+ appName: string | ((c: HonoContext<DataTypes>) => string);
6
+ appSecret: string | ((c: HonoContext<DataTypes>) => string);
7
+ appUrl?: string | null;
8
+ registrationUrl?: string | null;
9
+ registerConfirmationUrl?: string | null;
10
+ appPath?: string | null;
11
+ shopRepository: ShopRepositoryInterface | ((c: HonoContext<DataTypes>) => ShopRepositoryInterface);
12
+ }
13
+ interface DataTypes {
14
+ app: AppServer;
15
+ context: Context<ShopInterface, unknown>;
16
+ shop: ShopInterface;
17
+ }
18
+ interface HonoContext<DataTypes> {
19
+ env: any;
20
+ req: {
21
+ path: string;
22
+ method: string;
23
+ url: string;
24
+ raw: Request;
25
+ };
26
+ header: (key: string, value: string) => void;
27
+ res: Response;
28
+ get<K extends keyof DataTypes>(key: K): DataTypes[K];
29
+ set<K extends keyof DataTypes>(key: K, value: DataTypes[K]): void;
30
+ }
31
+ /**
32
+ * Configure the Hono server to handle the app registration and context resolution
33
+ */
34
+ export declare function configureAppServer(honoExternal: unknown, cfg: MiddlewareConfig): void;
35
+ export {};
36
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../../src/framework/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE/E,UAAU,gBAAgB;IACzB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC,CAAC;IAC1D,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC,CAAC;IAC5D,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,cAAc,EACX,uBAAuB,GACvB,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,KAAK,uBAAuB,CAAC,CAAC;CAC5D;AAED,UAAU,SAAS;IAClB,GAAG,EAAE,SAAS,CAAC;IACf,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACzC,IAAI,EAAE,aAAa,CAAC;CACpB;AAED,UAAU,WAAW,CAAC,SAAS;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,OAAO,CAAC;KACb,CAAC;IACF,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,GAAG,EAAE,QAAQ,CAAC;IACd,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACrD,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAClE;AAmBD;;GAEG;AACH,wBAAgB,kBAAkB,CACjC,YAAY,EAAE,OAAO,EACrB,GAAG,EAAE,gBAAgB,QA0FrB"}
@@ -0,0 +1,81 @@
1
+ import { AppServer } from "../app.js";
2
+ let app = null;
3
+ /**
4
+ * Configure the Hono server to handle the app registration and context resolution
5
+ */
6
+ export function configureAppServer(honoExternal, cfg) {
7
+ const hono = honoExternal;
8
+ cfg.registrationUrl = cfg.registrationUrl || "/app/register";
9
+ cfg.registerConfirmationUrl =
10
+ cfg.registerConfirmationUrl || "/app/register/confirm";
11
+ cfg.appPath = cfg.appPath || "/app/*";
12
+ hono.use("*", async (ctx, next) => {
13
+ if (app === null) {
14
+ const appUrl = cfg.appUrl || buildBaseUrl(ctx.req.url);
15
+ if (typeof cfg.shopRepository === "function") {
16
+ cfg.shopRepository = cfg.shopRepository(ctx);
17
+ }
18
+ if (typeof cfg.appName === "function") {
19
+ cfg.appName = cfg.appName(ctx);
20
+ }
21
+ if (typeof cfg.appSecret === "function") {
22
+ cfg.appSecret = cfg.appSecret(ctx);
23
+ }
24
+ app = new AppServer({
25
+ appName: cfg.appName,
26
+ appSecret: cfg.appSecret,
27
+ authorizeCallbackUrl: appUrl + cfg.registerConfirmationUrl,
28
+ }, cfg.shopRepository);
29
+ }
30
+ ctx.set("app", app);
31
+ await next();
32
+ });
33
+ hono.use(cfg.appPath, async (ctx, next) => {
34
+ const app = ctx.get("app");
35
+ // Don't validate signature for registration
36
+ if (ctx.req.path === cfg.registrationUrl ||
37
+ ctx.req.path === cfg.registerConfirmationUrl) {
38
+ await next();
39
+ return;
40
+ }
41
+ let context;
42
+ try {
43
+ context =
44
+ ctx.req.method === "GET"
45
+ ? await app.contextResolver.fromBrowser(ctx.req.raw)
46
+ : await app.contextResolver.fromAPI(ctx.req.raw);
47
+ }
48
+ catch (_e) {
49
+ return jsonResponse({ message: "Invalid request" }, 400);
50
+ }
51
+ ctx.set("shop", context.shop);
52
+ ctx.set("context", context);
53
+ await next();
54
+ const cloned = ctx.res.clone();
55
+ await ctx
56
+ .get("app")
57
+ .signer.signResponse(cloned, ctx.get("shop").getShopSecret());
58
+ ctx.header("shopware-app-signature", cloned.headers.get("shopware-app-signature"));
59
+ });
60
+ hono.get(cfg.registrationUrl, async (ctx) => {
61
+ const app = ctx.get("app");
62
+ return await app.registration.authorize(ctx.req.raw);
63
+ });
64
+ hono.post(cfg.registerConfirmationUrl, async (ctx) => {
65
+ const app = ctx.get("app");
66
+ return await app.registration.authorizeCallback(ctx.req.raw);
67
+ });
68
+ }
69
+ function jsonResponse(body, status = 200) {
70
+ return new Response(JSON.stringify(body), {
71
+ status,
72
+ headers: {
73
+ "Content-Type": "application/json",
74
+ },
75
+ });
76
+ }
77
+ function buildBaseUrl(url) {
78
+ const u = new URL(url);
79
+ return `${u.protocol}//${u.host}`;
80
+ }
81
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../../src/framework/hono.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppServer } from \"../app.js\";\nimport type { Context } from \"../context-resolver.js\";\nimport type { ShopInterface, ShopRepositoryInterface } from \"../repository.js\";\n\ninterface MiddlewareConfig {\n\tappName: string | ((c: HonoContext<DataTypes>) => string);\n\tappSecret: string | ((c: HonoContext<DataTypes>) => string);\n\tappUrl?: string | null;\n\tregistrationUrl?: string | null;\n\tregisterConfirmationUrl?: string | null;\n\tappPath?: string | null;\n\tshopRepository:\n\t\t| ShopRepositoryInterface\n\t\t| ((c: HonoContext<DataTypes>) => ShopRepositoryInterface);\n}\n\ninterface DataTypes {\n\tapp: AppServer;\n\tcontext: Context<ShopInterface, unknown>;\n\tshop: ShopInterface;\n}\n\ninterface HonoContext<DataTypes> {\n\tenv: any;\n\treq: {\n\t\tpath: string;\n\t\tmethod: string;\n\t\turl: string;\n\t\traw: Request;\n\t};\n\theader: (key: string, value: string) => void;\n\tres: Response;\n\tget<K extends keyof DataTypes>(key: K): DataTypes[K];\n\tset<K extends keyof DataTypes>(key: K, value: DataTypes[K]): void;\n}\n\ninterface Hono {\n\tuse: (\n\t\tpath: string,\n\t\thandler: (ctx: HonoContext<DataTypes>, next: () => Promise<void>) => void,\n\t) => void;\n\tget: (\n\t\tpath: string,\n\t\thandler: (ctx: HonoContext<DataTypes>, next: () => void) => void,\n\t) => void;\n\tpost: (\n\t\tpath: string,\n\t\thandler: (ctx: HonoContext<DataTypes>, next: () => void) => void,\n\t) => void;\n}\n\nlet app: AppServer | null = null;\n\n/**\n * Configure the Hono server to handle the app registration and context resolution\n */\nexport function configureAppServer(\n\thonoExternal: unknown,\n\tcfg: MiddlewareConfig,\n) {\n\tconst hono = honoExternal as Hono;\n\n\tcfg.registrationUrl = cfg.registrationUrl || \"/app/register\";\n\tcfg.registerConfirmationUrl =\n\t\tcfg.registerConfirmationUrl || \"/app/register/confirm\";\n\tcfg.appPath = cfg.appPath || \"/app/*\";\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\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);\n\t\t}\n\n\t\tctx.set(\"app\", app);\n\n\t\tawait next();\n\t});\n\n\thono.use(cfg.appPath, async (ctx, next) => {\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) {\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\tctx.set(\"shop\", context.shop);\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\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\treturn `${u.protocol}//${u.host}`;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Opens in the Administration a new tab to the given URL and adds the shop-id as query parameter with the signature
3
+ */
4
+ export declare function createNewTabResponse(redirectUrl: string): Response;
5
+ /**
6
+ * Shows in the Administration a new notification with the given status and message
7
+ */
8
+ export declare function createNotificationResponse(status: "success" | "error" | "info" | "warning", message: string): Response;
9
+ /**
10
+ * Opens in the Administration a new modal with the given iframe URL
11
+ */
12
+ export declare function createModalResponse(iframeUrl: string, size?: "small" | "medium" | "large" | "fullscreen", expand?: boolean): Response;
13
+ //# sourceMappingURL=app-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-actions.d.ts","sourceRoot":"","sources":["../../../src/helper/app-actions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAclE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,EAChD,OAAO,EAAE,MAAM,GACb,QAAQ,CAeV;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAuB,EAC5D,MAAM,UAAQ,GACZ,QAAQ,CAgBV"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Opens in the Administration a new tab to the given URL and adds the shop-id as query parameter with the signature
3
+ */
4
+ export function createNewTabResponse(redirectUrl) {
5
+ return new Response(JSON.stringify({
6
+ actionType: "openNewTab",
7
+ payload: {
8
+ redirectUrl,
9
+ },
10
+ }), {
11
+ headers: {
12
+ "content-type": "application/json",
13
+ },
14
+ });
15
+ }
16
+ /**
17
+ * Shows in the Administration a new notification with the given status and message
18
+ */
19
+ export function createNotificationResponse(status, message) {
20
+ return new Response(JSON.stringify({
21
+ actionType: "notification",
22
+ payload: {
23
+ status,
24
+ message,
25
+ },
26
+ }), {
27
+ headers: {
28
+ "content-type": "application/json",
29
+ },
30
+ });
31
+ }
32
+ /**
33
+ * Opens in the Administration a new modal with the given iframe URL
34
+ */
35
+ export function createModalResponse(iframeUrl, size = "medium", expand = false) {
36
+ return new Response(JSON.stringify({
37
+ actionType: "openModal",
38
+ payload: {
39
+ iframeUrl,
40
+ size,
41
+ expand,
42
+ },
43
+ }), {
44
+ headers: {
45
+ "content-type": "application/json",
46
+ },
47
+ });
48
+ }
49
+ //# sourceMappingURL=app-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-actions.js","sourceRoot":"","sources":["../../../src/helper/app-actions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACvD,OAAO,IAAI,QAAQ,CAClB,IAAI,CAAC,SAAS,CAAC;QACd,UAAU,EAAE,YAAY;QACxB,OAAO,EAAE;YACR,WAAW;SACX;KACD,CAAC,EACF;QACC,OAAO,EAAE;YACR,cAAc,EAAE,kBAAkB;SAClC;KACD,CACD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CACzC,MAAgD,EAChD,OAAe;IAEf,OAAO,IAAI,QAAQ,CAClB,IAAI,CAAC,SAAS,CAAC;QACd,UAAU,EAAE,cAAc;QAC1B,OAAO,EAAE;YACR,MAAM;YACN,OAAO;SACP;KACD,CAAC,EACF;QACC,OAAO,EAAE;YACR,cAAc,EAAE,kBAAkB;SAClC;KACD,CACD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAClC,SAAiB,EACjB,OAAoD,QAAQ,EAC5D,MAAM,GAAG,KAAK;IAEd,OAAO,IAAI,QAAQ,CAClB,IAAI,CAAC,SAAS,CAAC;QACd,UAAU,EAAE,WAAW;QACvB,OAAO,EAAE;YACR,SAAS;YACT,IAAI;YACJ,MAAM;SACN;KACD,CAAC,EACF;QACC,OAAO,EAAE;YACR,cAAc,EAAE,kBAAkB;SAClC;KACD,CACD,CAAC;AACH,CAAC","sourcesContent":["/**\n * Opens in the Administration a new tab to the given URL and adds the shop-id as query parameter with the signature\n */\nexport function createNewTabResponse(redirectUrl: string): Response {\n\treturn new Response(\n\t\tJSON.stringify({\n\t\t\tactionType: \"openNewTab\",\n\t\t\tpayload: {\n\t\t\t\tredirectUrl,\n\t\t\t},\n\t\t}),\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t},\n\t\t},\n\t);\n}\n\n/**\n * Shows in the Administration a new notification with the given status and message\n */\nexport function createNotificationResponse(\n\tstatus: \"success\" | \"error\" | \"info\" | \"warning\",\n\tmessage: string,\n): Response {\n\treturn new Response(\n\t\tJSON.stringify({\n\t\t\tactionType: \"notification\",\n\t\t\tpayload: {\n\t\t\t\tstatus,\n\t\t\t\tmessage,\n\t\t\t},\n\t\t}),\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t},\n\t\t},\n\t);\n}\n\n/**\n * Opens in the Administration a new modal with the given iframe URL\n */\nexport function createModalResponse(\n\tiframeUrl: string,\n\tsize: \"small\" | \"medium\" | \"large\" | \"fullscreen\" = \"medium\",\n\texpand = false,\n): Response {\n\treturn new Response(\n\t\tJSON.stringify({\n\t\t\tactionType: \"openModal\",\n\t\t\tpayload: {\n\t\t\t\tiframeUrl,\n\t\t\t\tsize,\n\t\t\t\texpand,\n\t\t\t},\n\t\t}),\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t},\n\t\t},\n\t);\n}\n"]}