@unchainedshop/ticketing 4.0.0-rc.2 → 4.0.0-rc.21
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/lib/fastify.js +2 -2
- package/lib/fastify.js.map +1 -1
- package/lib/magic-key.js +4 -4
- package/lib/magic-key.js.map +1 -1
- package/lib/mobile-tickets/apple-handler-express.js +63 -64
- package/lib/mobile-tickets/apple-handler-express.js.map +1 -1
- package/lib/mobile-tickets/apple-handler-fastify.d.ts.map +1 -1
- package/lib/mobile-tickets/apple-handler-fastify.js +93 -42
- package/lib/mobile-tickets/apple-handler-fastify.js.map +1 -1
- package/lib/mobile-tickets/apple-wallet.d.ts +3 -3
- package/lib/mobile-tickets/apple-wallet.d.ts.map +1 -1
- package/lib/mobile-tickets/apple-wallet.js +3 -3
- package/lib/mobile-tickets/apple-wallet.js.map +1 -1
- package/lib/mobile-tickets/apple-webservice.d.ts +7 -0
- package/lib/mobile-tickets/apple-webservice.d.ts.map +1 -0
- package/lib/mobile-tickets/apple-webservice.js +177 -0
- package/lib/mobile-tickets/apple-webservice.js.map +1 -0
- package/lib/mobile-tickets/google-handler-express.d.ts.map +1 -1
- package/lib/mobile-tickets/google-handler-express.js +10 -12
- package/lib/mobile-tickets/google-handler-express.js.map +1 -1
- package/lib/mobile-tickets/google-handler-fastify.d.ts.map +1 -1
- package/lib/mobile-tickets/google-handler-fastify.js +19 -6
- package/lib/mobile-tickets/google-handler-fastify.js.map +1 -1
- package/lib/mobile-tickets/google-webservice.d.ts +7 -0
- package/lib/mobile-tickets/google-webservice.d.ts.map +1 -0
- package/lib/mobile-tickets/google-webservice.js +52 -0
- package/lib/mobile-tickets/google-webservice.js.map +1 -0
- package/lib/module.d.ts +8 -8
- package/lib/module.d.ts.map +1 -1
- package/lib/module.js +6 -3
- package/lib/module.js.map +1 -1
- package/lib/pdf-tickets/print-handler-express.d.ts.map +1 -1
- package/lib/pdf-tickets/print-handler-express.js +4 -3
- package/lib/pdf-tickets/print-handler-express.js.map +1 -1
- package/lib/pdf-tickets/print-handler-fastify.d.ts.map +1 -1
- package/lib/pdf-tickets/print-handler-fastify.js +8 -2
- package/lib/pdf-tickets/print-handler-fastify.js.map +1 -1
- package/lib/pdf-tickets/print-webservice.d.ts +7 -0
- package/lib/pdf-tickets/print-webservice.d.ts.map +1 -0
- package/lib/pdf-tickets/print-webservice.js +31 -0
- package/lib/pdf-tickets/print-webservice.js.map +1 -0
- package/lib/template-registry.d.ts +2 -2
- package/lib/template-registry.d.ts.map +1 -1
- package/package.json +14 -14
- package/readme.md +1 -1
- package/src/fastify.ts +2 -2
- package/src/magic-key.ts +4 -4
- package/src/mobile-tickets/apple-handler-express.ts +71 -71
- package/src/mobile-tickets/apple-handler-fastify.ts +103 -51
- package/src/mobile-tickets/apple-wallet.ts +3 -3
- package/src/mobile-tickets/google-handler-express.ts +11 -12
- package/src/mobile-tickets/google-handler-fastify.ts +21 -6
- package/src/module.ts +7 -4
- package/src/pdf-tickets/print-handler-express.ts +6 -3
- package/src/pdf-tickets/print-handler-fastify.ts +9 -2
- package/src/template-registry.ts +2 -2
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useMiddlewareWithCurrentContext } from '@unchainedshop/api/express/index.js';
|
|
2
|
+
import { checkAction } from '@unchainedshop/api/acl.js';
|
|
3
|
+
import { actions } from '@unchainedshop/api/roles/index.js';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import { RendererTypes, getRenderer } from '../template-registry.js';
|
|
6
|
+
const { UNCHAINED_PDF_PRINT_HANDLER_PATH = '/rest/print_tickets' } = process.env;
|
|
7
|
+
export async function printTicketsHandler(req, res) {
|
|
8
|
+
const { variant, orderId, otp } = req.query || {};
|
|
9
|
+
try {
|
|
10
|
+
if (typeof orderId !== 'string' ||
|
|
11
|
+
typeof otp !== 'string' ||
|
|
12
|
+
(variant && typeof variant !== 'string'))
|
|
13
|
+
throw new Error('Invalid query parameter');
|
|
14
|
+
await checkAction(req.unchainedContext, actions.viewOrder, [undefined, { orderId, otp }]);
|
|
15
|
+
const render = getRenderer(RendererTypes.ORDER_PDF);
|
|
16
|
+
const pdfStream = await render({ orderId, variant: variant }, req.unchainedContext);
|
|
17
|
+
res.setHeader('Content-Type', 'application/pdf');
|
|
18
|
+
pdfStream.pipe(res);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error(error);
|
|
22
|
+
res.status(403);
|
|
23
|
+
res.end();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export default function connectPrintWebservice(app) {
|
|
27
|
+
useMiddlewareWithCurrentContext(app, UNCHAINED_PDF_PRINT_HANDLER_PATH, express.json({
|
|
28
|
+
type: 'application/json',
|
|
29
|
+
}), printTicketsHandler);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=print-webservice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"print-webservice.js","sourceRoot":"","sources":["../../src/pdf-tickets/print-webservice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,qCAAqC,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAC5D,OAAO,OAA8B,MAAM,SAAS,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAErE,MAAM,EAAE,gCAAgC,GAAG,qBAAqB,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAEjF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAA4C,EAAE,GAAa;IACnG,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;IAElD,IAAI,CAAC;QACH,IACE,OAAO,OAAO,KAAK,QAAQ;YAC3B,OAAO,GAAG,KAAK,QAAQ;YACvB,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC;YAExC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAE7C,MAAM,WAAW,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAE1F,MAAM,MAAM,GAAG,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAiB,EAAE,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9F,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,iBAAiB,CAAC,CAAC;QACjD,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAAC,GAAG;IAChD,+BAA+B,CAC7B,GAAG,EACH,gCAAgC,EAChC,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,kBAAkB;KACzB,CAAC,EACF,mBAAmB,CACpB,CAAC;AACJ,CAAC"}
|
|
@@ -5,8 +5,8 @@ export type PDFRenderer = ({ orderId, variant, }: {
|
|
|
5
5
|
variant?: string;
|
|
6
6
|
}, context: UnchainedCore) => Promise<NodeJS.ReadableStream>;
|
|
7
7
|
export type PassRenderer = (token: TokenSurrogate, context: UnchainedCore) => Promise<{
|
|
8
|
-
asURL
|
|
9
|
-
asBuffer
|
|
8
|
+
asURL: () => Promise<string>;
|
|
9
|
+
asBuffer: () => Promise<Buffer>;
|
|
10
10
|
serialNumber?: string;
|
|
11
11
|
passTypeIdentifier?: string;
|
|
12
12
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-registry.d.ts","sourceRoot":"","sources":["../src/template-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,CACxB,EACE,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,OAAO,EAAE,aAAa,KACnB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAEpC,MAAM,MAAM,YAAY,GAAG,CACzB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,aAAa,KACnB,OAAO,CAAC;IACX,KAAK,
|
|
1
|
+
{"version":3,"file":"template-registry.d.ts","sourceRoot":"","sources":["../src/template-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,CACxB,EACE,OAAO,EACP,OAAO,GACR,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EACD,OAAO,EAAE,aAAa,KACnB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAEpC,MAAM,MAAM,YAAY,GAAG,CACzB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,aAAa,KACnB,OAAO,CAAC;IACX,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC,CAAC;AAEH,oBAAY,aAAa;IACvB,aAAa,kBAAkB;IAC/B,YAAY,iBAAiB;IAC7B,SAAS,UAAU;CACpB;AAED,eAAO,MAAM,SAAS,yCAAgD,CAAC;AAEvE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,KAAK,IAAI,CAAC,GAC/F,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,aAAa,GAAG,aAAa,CAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC,CAAC;AAErG,eAAO,MAAM,gBAAgB,EAAE,kBAK9B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,KAAK,WAAW,CAAC,GAC1E,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,aAAa,GAAG,aAAa,CAAC,YAAY,KAAK,YAAY,CAAC,CAAC;AAErF,eAAO,MAAM,WAAW,EAAE,aAEzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unchainedshop/ticketing",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.21",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -31,22 +31,22 @@
|
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://github.com/unchainedshop/unchained#readme",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@unchainedshop/api": "^4.0.0-rc.
|
|
35
|
-
"@unchainedshop/core": "^4.0.0-rc.
|
|
36
|
-
"@unchainedshop/core-files": "^4.0.0-rc.
|
|
37
|
-
"@unchainedshop/core-warehousing": "^4.0.0-rc.
|
|
38
|
-
"@unchainedshop/core-worker": "^4.0.0-rc.
|
|
39
|
-
"@unchainedshop/events": "^4.0.0-rc.
|
|
40
|
-
"@unchainedshop/logger": "^4.0.0-rc.
|
|
41
|
-
"@unchainedshop/mongodb": "^4.0.0-rc.
|
|
34
|
+
"@unchainedshop/api": "^4.0.0-rc.21",
|
|
35
|
+
"@unchainedshop/core": "^4.0.0-rc.21",
|
|
36
|
+
"@unchainedshop/core-files": "^4.0.0-rc.21",
|
|
37
|
+
"@unchainedshop/core-warehousing": "^4.0.0-rc.21",
|
|
38
|
+
"@unchainedshop/core-worker": "^4.0.0-rc.21",
|
|
39
|
+
"@unchainedshop/events": "^4.0.0-rc.21",
|
|
40
|
+
"@unchainedshop/logger": "^4.0.0-rc.21",
|
|
41
|
+
"@unchainedshop/mongodb": "^4.0.0-rc.21"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@
|
|
44
|
+
"@parse/node-apn": "^6.5.0",
|
|
45
45
|
"express": "5.x",
|
|
46
46
|
"fastify": ">= 5.2 < 6"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
|
-
"@
|
|
49
|
+
"@parse/node-apn": {
|
|
50
50
|
"optional": false
|
|
51
51
|
},
|
|
52
52
|
"express": {
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@types/express": "^5.0.
|
|
61
|
-
"@types/node": "^
|
|
60
|
+
"@types/express": "^5.0.3",
|
|
61
|
+
"@types/node": "^24.0.13",
|
|
62
62
|
"express": "^5.1.0",
|
|
63
|
-
"fastify": "^5.
|
|
63
|
+
"fastify": "^5.4.0",
|
|
64
64
|
"typescript": "^5.8.3"
|
|
65
65
|
}
|
|
66
66
|
}
|
package/readme.md
CHANGED
|
@@ -105,7 +105,7 @@ export default async (token, unchainedAPI: UnchainedCore) => {
|
|
|
105
105
|
// upsert object
|
|
106
106
|
|
|
107
107
|
const asURL = async () =>
|
|
108
|
-
createJwtNewObjects(issuerId, product._id, token.
|
|
108
|
+
createJwtNewObjects(issuerId, product._id, token.tokenSerialNumber);
|
|
109
109
|
|
|
110
110
|
return { asURL };
|
|
111
111
|
};
|
package/src/fastify.ts
CHANGED
|
@@ -18,13 +18,13 @@ export default (app: FastifyInstance) => {
|
|
|
18
18
|
|
|
19
19
|
app.route({
|
|
20
20
|
url: `${GOOGLE_WALLET_WEBSERVICE_PATH}/*`,
|
|
21
|
-
method: '
|
|
21
|
+
method: 'GET',
|
|
22
22
|
handler: googleWalletHandler,
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
app.route({
|
|
26
26
|
url: `${UNCHAINED_PDF_PRINT_HANDLER_PATH}/*`,
|
|
27
|
-
method: '
|
|
27
|
+
method: 'GET',
|
|
28
28
|
handler: printTicketsHandler,
|
|
29
29
|
});
|
|
30
30
|
};
|
package/src/magic-key.ts
CHANGED
|
@@ -20,7 +20,7 @@ const isMagicKeyValidForToken = async (
|
|
|
20
20
|
params: { tokenId: string } | null,
|
|
21
21
|
{ modules, getHeader }: Context & TicketingAPI,
|
|
22
22
|
) => {
|
|
23
|
-
const tokenId = params?.tokenId || (root && '
|
|
23
|
+
const tokenId = params?.tokenId || (root && 'tokenSerialNumber' in root && root._id) || null;
|
|
24
24
|
|
|
25
25
|
const token = await modules.warehousing.findToken({ tokenId });
|
|
26
26
|
// always return true if tokenId leads to nothing to show potentially allowed actions
|
|
@@ -37,7 +37,7 @@ const isMagicKeyValidForToken = async (
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
export default () => {
|
|
40
|
-
roles.allRoles
|
|
41
|
-
roles.allRoles
|
|
42
|
-
roles.allRoles
|
|
40
|
+
roles.allRoles?.ALL?.allow(roles.actions.viewOrder, isMagicKeyValidForOrder);
|
|
41
|
+
roles.allRoles?.ALL?.allow(roles.actions.viewToken, isMagicKeyValidForToken);
|
|
42
|
+
roles.allRoles?.ALL?.allow(roles.actions.updateToken, isMagicKeyValidForToken);
|
|
43
43
|
};
|
|
@@ -21,8 +21,7 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
21
21
|
const [tokenId] = passFileName.split('.pkpass');
|
|
22
22
|
|
|
23
23
|
if (!tokenId) {
|
|
24
|
-
res.
|
|
25
|
-
res.end();
|
|
24
|
+
res.status(404).end();
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
27
|
|
|
@@ -31,16 +30,14 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
31
30
|
});
|
|
32
31
|
|
|
33
32
|
if (!token) {
|
|
34
|
-
res.
|
|
35
|
-
res.end('Token not found');
|
|
33
|
+
res.status(404).send('Token not found');
|
|
36
34
|
return;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
const { hash } = req.query;
|
|
40
38
|
const correctHash = await modules.warehousing.buildAccessKeyForToken(tokenId);
|
|
41
|
-
if (hash !== correctHash) {
|
|
42
|
-
res.
|
|
43
|
-
res.end('Token hash invalid for current owner');
|
|
39
|
+
if (!hash || hash !== correctHash) {
|
|
40
|
+
res.status(403).send('Token hash invalid for current owner');
|
|
44
41
|
return;
|
|
45
42
|
}
|
|
46
43
|
|
|
@@ -50,6 +47,16 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
50
47
|
const signedUrl = await fileUploadAdapter.createDownloadURL(passFile);
|
|
51
48
|
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
52
49
|
|
|
50
|
+
if (!url) {
|
|
51
|
+
res.status(500);
|
|
52
|
+
res.send({
|
|
53
|
+
success: false,
|
|
54
|
+
message: 'Could not create download URL',
|
|
55
|
+
name: 'URL_SIGNING_FAILED',
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
const response = await fetch(url);
|
|
54
61
|
const data = await response.arrayBuffer();
|
|
55
62
|
const uint8View = new Uint8Array(data);
|
|
@@ -57,13 +64,12 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
57
64
|
res.status(200);
|
|
58
65
|
res.setHeader('Content-Type', 'application/vnd.apple.pkpass');
|
|
59
66
|
res.setHeader('Content-Disposition', `attachment; filename=${tokenId}.pkpass`);
|
|
60
|
-
res.
|
|
67
|
+
res.send(uint8View);
|
|
61
68
|
return;
|
|
62
69
|
} catch (e) {
|
|
63
|
-
|
|
70
|
+
logger.error(e);
|
|
64
71
|
}
|
|
65
|
-
res.
|
|
66
|
-
res.end();
|
|
72
|
+
res.status(500).end();
|
|
67
73
|
return;
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -80,11 +86,10 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
80
86
|
if (
|
|
81
87
|
!isAuthenticationTokenCorrect(
|
|
82
88
|
req,
|
|
83
|
-
(pass.meta
|
|
89
|
+
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
84
90
|
)
|
|
85
91
|
) {
|
|
86
|
-
res.
|
|
87
|
-
res.end();
|
|
92
|
+
res.status(401).end();
|
|
88
93
|
return;
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -98,13 +103,11 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
98
103
|
);
|
|
99
104
|
|
|
100
105
|
if (!newRegistration) {
|
|
101
|
-
res.
|
|
102
|
-
res.end();
|
|
106
|
+
res.status(200).end();
|
|
103
107
|
return;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
|
-
res.
|
|
107
|
-
res.end();
|
|
110
|
+
res.status(201).end();
|
|
108
111
|
return;
|
|
109
112
|
}
|
|
110
113
|
} else if (req.method === 'GET') {
|
|
@@ -119,24 +122,14 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
119
122
|
deviceLibraryIdentifier,
|
|
120
123
|
passesUpdatedSince,
|
|
121
124
|
);
|
|
122
|
-
const serialNumbers = passes.map((t) => t.meta
|
|
125
|
+
const serialNumbers = passes.map((t) => t.meta?.serialNumber).filter(Boolean) as string[];
|
|
123
126
|
|
|
124
127
|
if (serialNumbers?.length) {
|
|
125
|
-
res.
|
|
126
|
-
'Content-Type': 'application/json',
|
|
127
|
-
});
|
|
128
|
-
res.write(
|
|
129
|
-
JSON.stringify({
|
|
130
|
-
serialNumbers,
|
|
131
|
-
lastUpdated,
|
|
132
|
-
}),
|
|
133
|
-
);
|
|
134
|
-
res.end();
|
|
128
|
+
res.status(200).send({ serialNumbers, lastUpdated });
|
|
135
129
|
return;
|
|
136
130
|
}
|
|
137
131
|
|
|
138
|
-
res.
|
|
139
|
-
res.end();
|
|
132
|
+
res.status(204).end();
|
|
140
133
|
return;
|
|
141
134
|
} else if (req.method === 'DELETE') {
|
|
142
135
|
// Unregister Device
|
|
@@ -148,11 +141,10 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
148
141
|
if (
|
|
149
142
|
!isAuthenticationTokenCorrect(
|
|
150
143
|
req,
|
|
151
|
-
(pass.meta
|
|
144
|
+
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
152
145
|
)
|
|
153
146
|
) {
|
|
154
|
-
res.
|
|
155
|
-
res.end();
|
|
147
|
+
res.status(401).end();
|
|
156
148
|
return;
|
|
157
149
|
}
|
|
158
150
|
|
|
@@ -163,8 +155,7 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
163
155
|
);
|
|
164
156
|
|
|
165
157
|
// Unregistered
|
|
166
|
-
res.
|
|
167
|
-
res.end();
|
|
158
|
+
res.status(200).end();
|
|
168
159
|
return;
|
|
169
160
|
}
|
|
170
161
|
}
|
|
@@ -176,8 +167,7 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
176
167
|
logger.info(log);
|
|
177
168
|
}
|
|
178
169
|
});
|
|
179
|
-
res.
|
|
180
|
-
res.end();
|
|
170
|
+
res.status(200).end();
|
|
181
171
|
return;
|
|
182
172
|
} else if (endpoint === 'passes') {
|
|
183
173
|
if (req.method === 'GET') {
|
|
@@ -186,52 +176,62 @@ const appleWalletHandler = async (req: Request & { unchainedContext: TicketingAP
|
|
|
186
176
|
|
|
187
177
|
const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
|
|
188
178
|
|
|
189
|
-
if (pass) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
(pass.meta.rawData as any)._id || (pass.meta.rawData as any).tokenId,
|
|
194
|
-
)
|
|
195
|
-
) {
|
|
196
|
-
res.writeHead(401);
|
|
197
|
-
res.end();
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
179
|
+
if (!pass) {
|
|
180
|
+
res.status(404).end();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
200
183
|
|
|
201
|
-
|
|
184
|
+
if (
|
|
185
|
+
!isAuthenticationTokenCorrect(
|
|
186
|
+
req,
|
|
187
|
+
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
188
|
+
)
|
|
189
|
+
) {
|
|
190
|
+
res.status(401).end();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
202
193
|
|
|
203
|
-
|
|
204
|
-
lastModifiedDate.setMilliseconds(0);
|
|
194
|
+
const { updated, created } = pass;
|
|
205
195
|
|
|
206
|
-
|
|
207
|
-
|
|
196
|
+
const lastModifiedDate = new Date(updated || created);
|
|
197
|
+
lastModifiedDate.setMilliseconds(0);
|
|
208
198
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
res.end();
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
199
|
+
const ifModifiedSinceDate = new Date(req.header('if-modified-since')!);
|
|
200
|
+
ifModifiedSinceDate.setMilliseconds(0);
|
|
214
201
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
202
|
+
if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
|
|
203
|
+
res.status(304).end();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
218
206
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
207
|
+
const fileUploadAdapter = getFileAdapter();
|
|
208
|
+
const signedUrl = await fileUploadAdapter.createDownloadURL(pass);
|
|
209
|
+
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
222
210
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
if (!url) {
|
|
212
|
+
res.status(500);
|
|
213
|
+
res.send({
|
|
214
|
+
success: false,
|
|
215
|
+
message: 'Could not create download URL',
|
|
216
|
+
name: 'URL_SIGNING_FAILED',
|
|
226
217
|
});
|
|
227
|
-
res.end(uint8View);
|
|
228
218
|
return;
|
|
229
219
|
}
|
|
220
|
+
|
|
221
|
+
const result = await fetch(url);
|
|
222
|
+
const data = await result.arrayBuffer();
|
|
223
|
+
const uint8View = new Uint8Array(data);
|
|
224
|
+
|
|
225
|
+
res.writeHead(200, {
|
|
226
|
+
'Content-Type': 'application/vnd.apple.pkpass',
|
|
227
|
+
'Last-Modified': lastModifiedDate.toUTCString(),
|
|
228
|
+
});
|
|
229
|
+
res.send(uint8View);
|
|
230
|
+
return;
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
|
|
233
|
-
res.
|
|
234
|
-
res.end();
|
|
234
|
+
res.status(404).end();
|
|
235
235
|
};
|
|
236
236
|
|
|
237
237
|
export default appleWalletHandler;
|
|
@@ -38,15 +38,21 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
if (!token) {
|
|
41
|
+
logger.error('Token not found', { tokenId });
|
|
41
42
|
reply.status(404);
|
|
42
|
-
return reply.send(
|
|
43
|
+
return reply.send();
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
const { hash } = req.query as Record<string, string>;
|
|
46
47
|
const correctHash = await modules.warehousing.buildAccessKeyForToken(tokenId);
|
|
47
|
-
if (hash !== correctHash) {
|
|
48
|
+
if (!hash || hash !== correctHash) {
|
|
49
|
+
logger.error('Token hash invalid for current owner', { tokenId });
|
|
48
50
|
reply.status(403);
|
|
49
|
-
return reply.send(
|
|
51
|
+
return reply.send({
|
|
52
|
+
success: false,
|
|
53
|
+
message: 'Token hash invalid for current owner',
|
|
54
|
+
name: 'HASH_MISMATCH',
|
|
55
|
+
});
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
const passFile = await modules.passes.upsertAppleWalletPass(token, resolvedContext);
|
|
@@ -55,6 +61,15 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
55
61
|
const signedUrl = await fileUploadAdapter.createDownloadURL(passFile);
|
|
56
62
|
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
57
63
|
|
|
64
|
+
if (!url) {
|
|
65
|
+
reply.status(500);
|
|
66
|
+
return reply.send({
|
|
67
|
+
success: false,
|
|
68
|
+
message: 'Could not create download URL',
|
|
69
|
+
name: 'URL_SIGNING_FAILED',
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
const response = await fetch(url);
|
|
59
74
|
const data = await response.arrayBuffer();
|
|
60
75
|
const uint8View = new Uint8Array(data);
|
|
@@ -64,10 +79,14 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
64
79
|
reply.header('Content-Disposition', `attachment; filename=${tokenId}.pkpass`);
|
|
65
80
|
return reply.send(uint8View);
|
|
66
81
|
} catch (e) {
|
|
67
|
-
|
|
82
|
+
logger.error(e);
|
|
83
|
+
reply.status(500);
|
|
84
|
+
return reply.send({
|
|
85
|
+
success: false,
|
|
86
|
+
message: 'Error generating pass',
|
|
87
|
+
name: 'PASS_GENERATION_ERROR',
|
|
88
|
+
});
|
|
68
89
|
}
|
|
69
|
-
reply.status(500);
|
|
70
|
-
return reply.send();
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
const [, apiVersion, endpoint, ...pathComponents] = path.split("/"); /* eslint-disable-line */
|
|
@@ -83,9 +102,10 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
83
102
|
if (
|
|
84
103
|
!isAuthenticationTokenCorrect(
|
|
85
104
|
req,
|
|
86
|
-
(pass.meta
|
|
105
|
+
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
87
106
|
)
|
|
88
107
|
) {
|
|
108
|
+
logger.error('Unauthorized', { passTypeIdentifier, serialNumber });
|
|
89
109
|
reply.status(401);
|
|
90
110
|
return reply.send();
|
|
91
111
|
}
|
|
@@ -101,11 +121,15 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
101
121
|
|
|
102
122
|
if (!newRegistration) {
|
|
103
123
|
reply.status(200);
|
|
104
|
-
return reply.send(
|
|
124
|
+
return reply.send({
|
|
125
|
+
success: true,
|
|
126
|
+
});
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
reply.status(201);
|
|
108
|
-
return reply.send(
|
|
130
|
+
return reply.send({
|
|
131
|
+
success: true,
|
|
132
|
+
});
|
|
109
133
|
}
|
|
110
134
|
} else if (req.method === 'GET') {
|
|
111
135
|
// Get the List of Updatable Passes
|
|
@@ -120,21 +144,19 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
120
144
|
deviceLibraryIdentifier,
|
|
121
145
|
passesUpdatedSince,
|
|
122
146
|
);
|
|
123
|
-
const serialNumbers = passes.map((t) => t.meta
|
|
147
|
+
const serialNumbers = passes.map((t) => t.meta?.serialNumber).filter(Boolean) as string[];
|
|
124
148
|
|
|
125
149
|
if (serialNumbers?.length) {
|
|
126
150
|
reply.status(200);
|
|
127
|
-
reply.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}),
|
|
133
|
-
);
|
|
151
|
+
return reply.send({
|
|
152
|
+
success: true,
|
|
153
|
+
serialNumbers,
|
|
154
|
+
lastUpdated,
|
|
155
|
+
});
|
|
134
156
|
}
|
|
135
157
|
|
|
136
158
|
reply.status(204);
|
|
137
|
-
return reply.send();
|
|
159
|
+
return reply.send({ success: true });
|
|
138
160
|
} else if (req.method === 'DELETE') {
|
|
139
161
|
// Unregister Device
|
|
140
162
|
const [deviceLibraryIdentifier, , passTypeIdentifier, serialNumber] = pathComponents;
|
|
@@ -145,11 +167,16 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
145
167
|
if (
|
|
146
168
|
!isAuthenticationTokenCorrect(
|
|
147
169
|
req,
|
|
148
|
-
(pass.meta
|
|
170
|
+
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
149
171
|
)
|
|
150
172
|
) {
|
|
173
|
+
logger.error('Unauthorized', { passTypeIdentifier, serialNumber });
|
|
151
174
|
reply.status(401);
|
|
152
|
-
return reply.send(
|
|
175
|
+
return reply.send({
|
|
176
|
+
success: false,
|
|
177
|
+
message: 'Unauthorized',
|
|
178
|
+
name: 'UNAUTHORIZED',
|
|
179
|
+
});
|
|
153
180
|
}
|
|
154
181
|
|
|
155
182
|
await modules.passes.unregisterDeviceForAppleWalletPass(
|
|
@@ -160,7 +187,9 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
160
187
|
|
|
161
188
|
// Unregistered
|
|
162
189
|
reply.status(200);
|
|
163
|
-
return reply.send(
|
|
190
|
+
return reply.send({
|
|
191
|
+
success: true,
|
|
192
|
+
});
|
|
164
193
|
}
|
|
165
194
|
}
|
|
166
195
|
} else if (endpoint === 'log') {
|
|
@@ -172,7 +201,9 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
172
201
|
}
|
|
173
202
|
});
|
|
174
203
|
reply.status(200);
|
|
175
|
-
return reply.send(
|
|
204
|
+
return reply.send({
|
|
205
|
+
success: true,
|
|
206
|
+
});
|
|
176
207
|
} else if (endpoint === 'passes') {
|
|
177
208
|
if (req.method === 'GET') {
|
|
178
209
|
// Get an updated Pass
|
|
@@ -180,43 +211,64 @@ const appleWalletHandler: RouteHandlerMethod = async (
|
|
|
180
211
|
|
|
181
212
|
const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
|
|
182
213
|
|
|
183
|
-
if (pass) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
(pass.meta.rawData as any)._id || (pass.meta.rawData as any).tokenId,
|
|
188
|
-
)
|
|
189
|
-
) {
|
|
190
|
-
reply.status(401);
|
|
191
|
-
return reply.send();
|
|
192
|
-
}
|
|
214
|
+
if (!pass) {
|
|
215
|
+
reply.status(404);
|
|
216
|
+
return reply.send();
|
|
217
|
+
}
|
|
193
218
|
|
|
194
|
-
|
|
219
|
+
if (
|
|
220
|
+
!isAuthenticationTokenCorrect(
|
|
221
|
+
req,
|
|
222
|
+
(pass.meta?.rawData as any)?._id || (pass.meta?.rawData as any)?.tokenId,
|
|
223
|
+
)
|
|
224
|
+
) {
|
|
225
|
+
logger.error('Unauthorized', { passTypeIdentifier, serialNumber });
|
|
226
|
+
reply.status(401);
|
|
227
|
+
return reply.send({
|
|
228
|
+
success: false,
|
|
229
|
+
message: 'Unauthorized',
|
|
230
|
+
name: 'UNAUTHORIZED',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
195
233
|
|
|
196
|
-
|
|
197
|
-
lastModifiedDate.setMilliseconds(0);
|
|
234
|
+
const { updated, created } = pass;
|
|
198
235
|
|
|
199
|
-
|
|
200
|
-
|
|
236
|
+
const lastModifiedDate = new Date(updated || created);
|
|
237
|
+
lastModifiedDate.setMilliseconds(0);
|
|
201
238
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return reply.send();
|
|
205
|
-
}
|
|
239
|
+
const ifModifiedSinceDate = new Date(req.headers['if-modified-since']!);
|
|
240
|
+
ifModifiedSinceDate.setMilliseconds(0);
|
|
206
241
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
242
|
+
if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
|
|
243
|
+
reply.status(304);
|
|
244
|
+
return reply.send({
|
|
245
|
+
success: true,
|
|
246
|
+
message: 'Not modified',
|
|
247
|
+
name: 'NOT_MODIFIED',
|
|
248
|
+
});
|
|
249
|
+
}
|
|
210
250
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
251
|
+
const fileUploadAdapter = getFileAdapter();
|
|
252
|
+
const signedUrl = await fileUploadAdapter.createDownloadURL(pass);
|
|
253
|
+
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
214
254
|
|
|
215
|
-
|
|
216
|
-
reply.
|
|
217
|
-
reply.
|
|
218
|
-
|
|
255
|
+
if (!url) {
|
|
256
|
+
reply.status(500);
|
|
257
|
+
return reply.send({
|
|
258
|
+
success: false,
|
|
259
|
+
message: 'Could not create download URL',
|
|
260
|
+
name: 'URL_SIGNING_FAILED',
|
|
261
|
+
});
|
|
219
262
|
}
|
|
263
|
+
|
|
264
|
+
const result = await fetch(url);
|
|
265
|
+
const data = await result.arrayBuffer();
|
|
266
|
+
const uint8View = new Uint8Array(data);
|
|
267
|
+
|
|
268
|
+
reply.status(200);
|
|
269
|
+
reply.header('content-type', 'application/vnd.apple.pkpass');
|
|
270
|
+
reply.header('last-modified', lastModifiedDate.toUTCString());
|
|
271
|
+
return reply.send(uint8View);
|
|
220
272
|
}
|
|
221
273
|
}
|
|
222
274
|
|