@unchainedshop/ticketing 2.10.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 (36) hide show
  1. package/jest.config.js +17 -0
  2. package/lib/index.d.ts +38 -0
  3. package/lib/index.js +38 -0
  4. package/lib/index.js.map +1 -0
  5. package/lib/mobile-tickets/apple-wallet.d.ts +14 -0
  6. package/lib/mobile-tickets/apple-wallet.js +23 -0
  7. package/lib/mobile-tickets/apple-wallet.js.map +1 -0
  8. package/lib/mobile-tickets/apple-webservice.d.ts +6 -0
  9. package/lib/mobile-tickets/apple-webservice.js +177 -0
  10. package/lib/mobile-tickets/apple-webservice.js.map +1 -0
  11. package/lib/mobile-tickets/google-webservice.d.ts +6 -0
  12. package/lib/mobile-tickets/google-webservice.js +52 -0
  13. package/lib/mobile-tickets/google-webservice.js.map +1 -0
  14. package/lib/module.d.ts +26 -0
  15. package/lib/module.js +143 -0
  16. package/lib/module.js.map +1 -0
  17. package/lib/pdf-tickets/print-webservice.d.ts +6 -0
  18. package/lib/pdf-tickets/print-webservice.js +31 -0
  19. package/lib/pdf-tickets/print-webservice.js.map +1 -0
  20. package/lib/template-registry.d.ts +25 -0
  21. package/lib/template-registry.js +14 -0
  22. package/lib/template-registry.js.map +1 -0
  23. package/lib/types.d.ts +7 -0
  24. package/lib/types.js +2 -0
  25. package/lib/types.js.map +1 -0
  26. package/package.json +57 -0
  27. package/readme.md +93 -0
  28. package/src/index.ts +78 -0
  29. package/src/mobile-tickets/apple-wallet.ts +31 -0
  30. package/src/mobile-tickets/apple-webservice.ts +245 -0
  31. package/src/mobile-tickets/google-webservice.ts +70 -0
  32. package/src/module.ts +192 -0
  33. package/src/pdf-tickets/print-webservice.ts +43 -0
  34. package/src/template-registry.ts +49 -0
  35. package/src/types.ts +6 -0
  36. package/tsconfig.json +9 -0
package/jest.config.js ADDED
@@ -0,0 +1,17 @@
1
+ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2
+ export default {
3
+ preset: 'ts-jest/presets/default-esm', // or other ESM presets
4
+ testEnvironment: 'node',
5
+ extensionsToTreatAsEsm: ['.ts'],
6
+ transform: {
7
+ '^.+\\.tsx?$': [
8
+ 'ts-jest',
9
+ {
10
+ useESM: true,
11
+ },
12
+ ],
13
+ },
14
+ moduleNameMapper: {
15
+ '^(\\.{1,2}/.*)\\.js$': '$1',
16
+ },
17
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import express from 'express';
3
+ import { RendererTypes } from './template-registry.js';
4
+ import type { TicketingAPI } from './types.js';
5
+ export type { TicketingAPI, RendererTypes };
6
+ export declare const ticketingModules: {
7
+ passes: {
8
+ configure: ({ db }: import("@unchainedshop/types/core.js").ModuleInput<Record<string, never>>) => Promise<{
9
+ upsertAppleWalletPass: (token: import("@unchainedshop/types/warehousing.js").TokenSurrogate, unchainedAPI: import("@unchainedshop/types/core.js").UnchainedCore) => Promise<import("@unchainedshop/types/files.js").File>;
10
+ findAppleWalletPass: (passTypeIdentifier: any, serialNumber: any) => Promise<import("@unchainedshop/types/files.js").File>;
11
+ registerDeviceForAppleWalletPass: (passTypeIdentifier: any, serialNumber: any, { deviceLibraryIdentifier, pushToken }: {
12
+ deviceLibraryIdentifier: any;
13
+ pushToken: any;
14
+ }) => Promise<boolean>;
15
+ unregisterDeviceForAppleWalletPass: (passTypeIdentifier: any, serialNumber: any, deviceLibraryIdentifier: any) => Promise<boolean>;
16
+ findUpdatedAppleWalletPasses: (passTypeIdentifier: any, deviceLibraryIdentifier: any, passesUpdatedSince: any) => Promise<import("@unchainedshop/types/files.js").File[]>;
17
+ invalidateAppleWalletPasses: (unchainedAPI: import("@unchainedshop/types/core.js").UnchainedCore) => Promise<void>;
18
+ upsertGoogleWalletPass: (token: import("@unchainedshop/types/warehousing.js").TokenSurrogate, unchainedAPI: import("@unchainedshop/types/core.js").UnchainedCore) => Promise<{
19
+ asURL?: () => Promise<string>;
20
+ asBuffer?: () => Promise<Buffer>;
21
+ serialNumber?: string;
22
+ passTypeIdentifier?: string;
23
+ }>;
24
+ }>;
25
+ };
26
+ };
27
+ export declare function setupPDFTickets(app: express.Express, { renderOrderPDF, }: {
28
+ renderOrderPDF: any;
29
+ }): void;
30
+ export declare function setupMobileTickets(app: express.Express, { createGoogleWalletPass, createAppleWalletPass, }: {
31
+ createGoogleWalletPass: any;
32
+ createAppleWalletPass: any;
33
+ }): void;
34
+ export default function setupTicketing(app: express.Express, unchainedAPI: TicketingAPI, { renderOrderPDF, createAppleWalletPass, createGoogleWalletPass, }: {
35
+ renderOrderPDF: any;
36
+ createAppleWalletPass: any;
37
+ createGoogleWalletPass: any;
38
+ }): void;
package/lib/index.js ADDED
@@ -0,0 +1,38 @@
1
+ import { subscribe } from '@unchainedshop/events';
2
+ import { WorkerEventTypes } from '@unchainedshop/core-worker';
3
+ import { RendererTypes, registerRenderer } from './template-registry.js';
4
+ import loadAppleWalletHandler from './mobile-tickets/apple-webservice.js';
5
+ import loadGoogleWalletHandler from './mobile-tickets/google-webservice.js';
6
+ import loadPDFHandler from './pdf-tickets/print-webservice.js';
7
+ import passes from './module.js';
8
+ export const ticketingModules = {
9
+ passes,
10
+ };
11
+ export function setupPDFTickets(app, { renderOrderPDF, }) {
12
+ registerRenderer(RendererTypes.ORDER_PDF, renderOrderPDF);
13
+ loadPDFHandler(app);
14
+ }
15
+ export function setupMobileTickets(app, { createGoogleWalletPass, createAppleWalletPass, }) {
16
+ registerRenderer(RendererTypes.GOOGLE_WALLET, createGoogleWalletPass);
17
+ registerRenderer(RendererTypes.APPLE_WALLET, createAppleWalletPass);
18
+ loadAppleWalletHandler(app);
19
+ loadGoogleWalletHandler(app);
20
+ }
21
+ export default function setupTicketing(app, unchainedAPI, { renderOrderPDF, createAppleWalletPass, createGoogleWalletPass, }) {
22
+ setupPDFTickets(app, {
23
+ renderOrderPDF,
24
+ });
25
+ setupMobileTickets(app, {
26
+ createAppleWalletPass,
27
+ createGoogleWalletPass,
28
+ });
29
+ subscribe('TOKEN_INVALIDATED', async () => {
30
+ await unchainedAPI.modules.passes.invalidateAppleWalletPasses(unchainedAPI);
31
+ });
32
+ subscribe(WorkerEventTypes.FINISHED, async ({ payload: work }) => {
33
+ if ((work.type === 'EXPORT_TOKEN' || work.type === 'UPDATE_TOKEN_OWNERSHIP') && work.success) {
34
+ await unchainedAPI.modules.passes.invalidateAppleWalletPasses(unchainedAPI);
35
+ }
36
+ });
37
+ }
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,sBAAsB,MAAM,sCAAsC,CAAC;AAC1E,OAAO,uBAAuB,MAAM,uCAAuC,CAAC;AAC5E,OAAO,cAAc,MAAM,mCAAmC,CAAC;AAC/D,OAAO,MAAM,MAAM,aAAa,CAAC;AAKjC,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM;CACP,CAAC;AAEF,MAAM,UAAU,eAAe,CAC7B,GAAoB,EACpB,EACE,cAAc,GAGf;IAED,gBAAgB,CAAC,aAAa,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC1D,cAAc,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,GAAoB,EACpB,EACE,sBAAsB,EACtB,qBAAqB,GAItB;IAED,gBAAgB,CAAC,aAAa,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IACtE,gBAAgB,CAAC,aAAa,CAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IAEpE,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5B,uBAAuB,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,GAAoB,EACpB,YAA0B,EAC1B,EACE,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GAKvB;IAED,eAAe,CAAC,GAAG,EAAE;QACnB,cAAc;KACf,CAAC,CAAC;IACH,kBAAkB,CAAC,GAAG,EAAE;QACtB,qBAAqB;QACrB,sBAAsB;KACvB,CAAC,CAAC;IAEH,SAAS,CAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,YAAY,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAwB,EAAE,EAAE;QACrF,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,IAAI,IAAI,CAAC,IAAI,KAAK,wBAAwB,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7F,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,YAAY,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ import apn from '@hyperlink/node-apn';
4
+ import { Readable } from 'stream';
5
+ export declare const pushToApplePushNotificationService: (deviceTokens: any) => Promise<apn.Responses>;
6
+ export declare const buildPassBinary: (chainTokenId: string, pass: {
7
+ serialNumber: string;
8
+ asBuffer: () => Promise<Buffer>;
9
+ }) => Promise<{
10
+ _id: string;
11
+ filename: string;
12
+ createReadStream: () => Readable;
13
+ mimetype: string;
14
+ }>;
@@ -0,0 +1,23 @@
1
+ import apn from '@hyperlink/node-apn';
2
+ import { Readable } from 'stream';
3
+ export const pushToApplePushNotificationService = async (deviceTokens) => {
4
+ const apnProvider = new apn.Provider({
5
+ cert: process.env.PASS_CERTIFICATE_PATH,
6
+ key: process.env.PASS_CERTIFICATE_PATH,
7
+ passphrase: process.env.PASS_CERTIFICATE_SECRET,
8
+ production: true,
9
+ });
10
+ const note = new apn.Notification({});
11
+ return apnProvider.send(note, deviceTokens);
12
+ };
13
+ export const buildPassBinary = async (chainTokenId, pass) => {
14
+ const passBuffer = await pass.asBuffer();
15
+ const rawFile = {
16
+ _id: pass.serialNumber,
17
+ filename: `${chainTokenId}-${new Date().getTime()}.pkpass`,
18
+ createReadStream: () => Readable.from(passBuffer),
19
+ mimetype: 'application/vnd.apple.pkpass',
20
+ };
21
+ return rawFile;
22
+ };
23
+ //# sourceMappingURL=apple-wallet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-wallet.js","sourceRoot":"","sources":["../../src/mobile-tickets/apple-wallet.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,qBAAqB,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,MAAM,CAAC,MAAM,kCAAkC,GAAG,KAAK,EAAE,YAAY,EAAE,EAAE;IACvE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC;QACnC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB;QACvC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB;QACtC,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB;QAC/C,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACtC,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;AAC9C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,YAAoB,EACpB,IAGC,EACD,EAAE;IACF,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG;QACd,GAAG,EAAE,IAAI,CAAC,YAAY;QACtB,QAAQ,EAAE,GAAG,YAAY,IAAI,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,SAAS;QAC1D,gBAAgB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;QACjD,QAAQ,EAAE,8BAA8B;KACzC,CAAC;IACF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { Request, Response } from 'express';
2
+ import { TicketingAPI } from '../types.js';
3
+ export declare const appleWalletHandler: (req: Request & {
4
+ unchainedContext: TicketingAPI;
5
+ }, res: Response) => Promise<void>;
6
+ export default function loadAppleWalletHandler(app: any): void;
@@ -0,0 +1,177 @@
1
+ import { createLogger } from '@unchainedshop/logger';
2
+ import { useMiddlewareWithCurrentContext } from '@unchainedshop/api/express/index.js';
3
+ import express from 'express';
4
+ const logger = createLogger('unchained:apple-wallet-webservice');
5
+ const { APPLE_WALLET_WEBSERVICE_PATH = '/rest/apple-wallet' } = process.env;
6
+ const isAuthenticationTokenCorrect = (req, authenticationToken) => {
7
+ const expectedAuthorizationValue = `ApplePass ${authenticationToken}`;
8
+ return req.headers.authorization === expectedAuthorizationValue;
9
+ };
10
+ export const appleWalletHandler = async (req, res) => {
11
+ const resolvedContext = req.unchainedContext;
12
+ const { modules } = resolvedContext;
13
+ logger.info(`${req.path} (${JSON.stringify(req.query)})`);
14
+ if (req.path.startsWith('/download/')) {
15
+ try {
16
+ const [, , passFileName] = req.path.split('/');
17
+ const [tokenId] = passFileName.split('.pkpass');
18
+ if (!tokenId) {
19
+ res.writeHead(404);
20
+ res.end();
21
+ return;
22
+ }
23
+ const token = await modules.warehousing.findToken({
24
+ tokenId,
25
+ });
26
+ if (!token) {
27
+ res.writeHead(404);
28
+ res.end('Token not found');
29
+ return;
30
+ }
31
+ const { hash } = req.query;
32
+ const correctHash = await modules.warehousing.buildAccessKeyForToken(tokenId);
33
+ if (hash !== correctHash) {
34
+ res.writeHead(403);
35
+ res.end('Token hash invalid for current owner');
36
+ return;
37
+ }
38
+ const passFile = await modules.passes.upsertAppleWalletPass(token, resolvedContext);
39
+ const url = await modules.files.getUrl(passFile, {});
40
+ const response = await fetch(url);
41
+ const data = await response.arrayBuffer();
42
+ const uint8View = new Uint8Array(data);
43
+ res.status(200);
44
+ res.setHeader('Content-Type', 'application/vnd.apple.pkpass');
45
+ res.setHeader('Content-Disposition', `attachment; filename=${tokenId}.pkpass`);
46
+ res.end(uint8View);
47
+ return;
48
+ }
49
+ catch (e) {
50
+ console.error(e);
51
+ }
52
+ res.writeHead(500);
53
+ res.end();
54
+ return;
55
+ }
56
+ const [, apiVersion, endpoint, ...pathComponents] = req.path.split("/"); /* eslint-disable-line */
57
+ if (endpoint === 'devices') {
58
+ if (req.method === 'POST') {
59
+ // Register Device
60
+ const [deviceLibraryIdentifier, , passTypeIdentifier, serialNumber] = pathComponents;
61
+ const { pushToken } = req.body;
62
+ const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
63
+ if (pass) {
64
+ if (!isAuthenticationTokenCorrect(req, pass.meta.rawData._id || pass.meta.rawData.tokenId)) {
65
+ res.writeHead(401);
66
+ res.end();
67
+ return;
68
+ }
69
+ const newRegistration = await modules.passes.registerDeviceForAppleWalletPass(passTypeIdentifier, serialNumber, {
70
+ deviceLibraryIdentifier,
71
+ pushToken,
72
+ });
73
+ if (!newRegistration) {
74
+ res.writeHead(200);
75
+ res.end();
76
+ return;
77
+ }
78
+ res.writeHead(201);
79
+ res.end();
80
+ return;
81
+ }
82
+ }
83
+ else if (req.method === 'GET') {
84
+ // Get the List of Updatable Passes
85
+ const lastUpdated = new Date();
86
+ const [deviceLibraryIdentifier, , passTypeIdentifier] = pathComponents;
87
+ const passesUpdatedSince = req.query?.passesUpdatedSince && new Date(req.query.passesUpdatedSince);
88
+ const passes = await modules.passes.findUpdatedAppleWalletPasses(passTypeIdentifier, deviceLibraryIdentifier, passesUpdatedSince);
89
+ const serialNumbers = passes.map((t) => t.meta.serialNumber);
90
+ if (serialNumbers?.length) {
91
+ res.writeHead(200, {
92
+ 'Content-Type': 'application/json',
93
+ });
94
+ res.write(JSON.stringify({
95
+ serialNumbers,
96
+ lastUpdated,
97
+ }));
98
+ res.end();
99
+ return;
100
+ }
101
+ res.writeHead(204);
102
+ res.end();
103
+ return;
104
+ }
105
+ else if (req.method === 'DELETE') {
106
+ // Unregister Device
107
+ const [deviceLibraryIdentifier, , passTypeIdentifier, serialNumber] = pathComponents;
108
+ const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
109
+ if (pass) {
110
+ if (!isAuthenticationTokenCorrect(req, pass.meta.rawData._id || pass.meta.rawData.tokenId)) {
111
+ res.writeHead(401);
112
+ res.end();
113
+ return;
114
+ }
115
+ await modules.passes.unregisterDeviceForAppleWalletPass(passTypeIdentifier, serialNumber, deviceLibraryIdentifier);
116
+ // Unregistered
117
+ res.writeHead(200);
118
+ res.end();
119
+ return;
120
+ }
121
+ }
122
+ }
123
+ else if (endpoint === 'log') {
124
+ // Log Mesage
125
+ const { logs } = req.body;
126
+ logs?.forEach((log) => {
127
+ if (typeof log === 'string') {
128
+ logger.info(log);
129
+ }
130
+ });
131
+ res.writeHead(200);
132
+ res.end();
133
+ return;
134
+ }
135
+ else if (endpoint === 'passes') {
136
+ if (req.method === 'GET') {
137
+ // Get an updated Pass
138
+ const [passTypeIdentifier, serialNumber] = pathComponents;
139
+ const pass = await modules.passes.findAppleWalletPass(passTypeIdentifier, serialNumber);
140
+ if (pass) {
141
+ if (!isAuthenticationTokenCorrect(req, pass.meta.rawData._id || pass.meta.rawData.tokenId)) {
142
+ res.writeHead(401);
143
+ res.end();
144
+ return;
145
+ }
146
+ const { updated, created } = pass;
147
+ const lastModifiedDate = new Date(updated || created);
148
+ lastModifiedDate.setMilliseconds(0);
149
+ const ifModifiedSinceDate = new Date(req.header('if-modified-since'));
150
+ ifModifiedSinceDate.setMilliseconds(0);
151
+ if (ifModifiedSinceDate.getTime() >= lastModifiedDate.getTime()) {
152
+ res.writeHead(304);
153
+ res.end();
154
+ return;
155
+ }
156
+ const url = modules.files.getUrl(pass, {});
157
+ const result = await fetch(url);
158
+ const data = await result.arrayBuffer();
159
+ const uint8View = new Uint8Array(data);
160
+ res.writeHead(200, {
161
+ 'Content-Type': 'application/vnd.apple.pkpass',
162
+ 'Last-Modified': lastModifiedDate.toUTCString(),
163
+ });
164
+ res.end(uint8View);
165
+ return;
166
+ }
167
+ }
168
+ }
169
+ res.writeHead(404);
170
+ res.end();
171
+ };
172
+ export default function loadAppleWalletHandler(app) {
173
+ useMiddlewareWithCurrentContext(app, APPLE_WALLET_WEBSERVICE_PATH, express.json({
174
+ type: 'application/json',
175
+ }), appleWalletHandler);
176
+ }
177
+ //# sourceMappingURL=apple-webservice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-webservice.js","sourceRoot":"","sources":["../../src/mobile-tickets/apple-webservice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Request, Response } from 'express';
2
+ import { TicketingAPI } from '../types.js';
3
+ export declare const googleWalletHandler: (req: Request & {
4
+ unchainedContext: TicketingAPI;
5
+ }, res: Response) => Promise<void>;
6
+ export default function loadGoogleWalletHandler(app: any): void;
@@ -0,0 +1,52 @@
1
+ import { createLogger } from '@unchainedshop/logger';
2
+ import { useMiddlewareWithCurrentContext } from '@unchainedshop/api/express/index.js';
3
+ import express from 'express';
4
+ const logger = createLogger('unchained:google-wallet-webservice');
5
+ const { GOOGLE_WALLET_WEBSERVICE_PATH = '/rest/google-wallet' } = process.env;
6
+ export const googleWalletHandler = async (req, res) => {
7
+ const resolvedContext = req.unchainedContext;
8
+ const { modules } = resolvedContext;
9
+ logger.info(`${req.path} (${JSON.stringify(req.query)})`);
10
+ if (req.path.startsWith('/download/')) {
11
+ try {
12
+ const [, , tokenId] = req.path.split('/');
13
+ if (!tokenId) {
14
+ res.writeHead(404);
15
+ res.end();
16
+ return;
17
+ }
18
+ const token = await modules.warehousing.findToken({
19
+ tokenId,
20
+ });
21
+ if (!token) {
22
+ res.writeHead(404);
23
+ res.end('Token not found');
24
+ return;
25
+ }
26
+ const { hash } = req.query;
27
+ const correctHash = await modules.warehousing.buildAccessKeyForToken(tokenId);
28
+ if (hash !== correctHash) {
29
+ res.writeHead(403);
30
+ res.end('Token hash invalid for current owner');
31
+ return;
32
+ }
33
+ const pass = await modules.passes.upsertGoogleWalletPass(token, resolvedContext);
34
+ res.redirect(await pass.asURL());
35
+ return;
36
+ }
37
+ catch (e) {
38
+ console.error(e);
39
+ }
40
+ res.writeHead(500);
41
+ res.end();
42
+ return;
43
+ }
44
+ res.writeHead(404);
45
+ res.end();
46
+ };
47
+ export default function loadGoogleWalletHandler(app) {
48
+ useMiddlewareWithCurrentContext(app, GOOGLE_WALLET_WEBSERVICE_PATH, express.json({
49
+ type: 'application/json',
50
+ }), googleWalletHandler);
51
+ }
52
+ //# sourceMappingURL=google-webservice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-webservice.js","sourceRoot":"","sources":["../../src/mobile-tickets/google-webservice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,+BAA+B,EAAE,MAAM,qCAAqC,CAAC;AACtF,OAAO,OAA8B,MAAM,SAAS,CAAC;AAGrD,MAAM,MAAM,GAAG,YAAY,CAAC,oCAAoC,CAAC,CAAC;AAElE,MAAM,EAAE,6BAA6B,GAAG,qBAAqB,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAE9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,GAAiD,EACjD,GAAa,EACb,EAAE;IACF,MAAM,eAAe,GAAG,GAAG,CAAC,gBAAgB,CAAC;IAC7C,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;IACpC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE1D,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAE1C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC;gBAChD,OAAO;aACR,CAAC,CAAC;YAEH,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBAC3B,OAAO;YACT,CAAC;YAED,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC;YAC3B,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;YAC9E,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;gBACzB,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;gBAChD,OAAO;YACT,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,sBAAsB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;YAEjF,GAAG,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACjC,OAAO;QACT,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,GAAG,EAAE,CAAC;QACV,OAAO;IACT,CAAC;IACD,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACnB,GAAG,CAAC,GAAG,EAAE,CAAC;AACZ,CAAC,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,uBAAuB,CAAC,GAAG;IACjD,+BAA+B,CAC7B,GAAG,EACH,6BAA6B,EAC7B,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,kBAAkB;KACzB,CAAC,EACF,mBAAmB,CACpB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { ModuleInput, UnchainedCore } from '@unchainedshop/types/core.js';
3
+ import { TokenSurrogate } from '@unchainedshop/types/warehousing.js';
4
+ import { File } from '@unchainedshop/types/files.js';
5
+ export declare const APPLE_WALLET_PASSES_FILE_DIRECTORY = "apple-wallet-passes";
6
+ declare const ticketingModule: {
7
+ configure: ({ db }: ModuleInput<Record<string, never>>) => Promise<{
8
+ upsertAppleWalletPass: (token: TokenSurrogate, unchainedAPI: UnchainedCore) => Promise<File>;
9
+ findAppleWalletPass: (passTypeIdentifier: any, serialNumber: any) => Promise<File>;
10
+ registerDeviceForAppleWalletPass: (passTypeIdentifier: any, serialNumber: any, { deviceLibraryIdentifier, pushToken }: {
11
+ deviceLibraryIdentifier: any;
12
+ pushToken: any;
13
+ }) => Promise<boolean>;
14
+ unregisterDeviceForAppleWalletPass: (passTypeIdentifier: any, serialNumber: any, deviceLibraryIdentifier: any) => Promise<boolean>;
15
+ findUpdatedAppleWalletPasses: (passTypeIdentifier: any, deviceLibraryIdentifier: any, passesUpdatedSince: any) => Promise<Array<File>>;
16
+ invalidateAppleWalletPasses: (unchainedAPI: UnchainedCore) => Promise<void>;
17
+ upsertGoogleWalletPass: (token: TokenSurrogate, unchainedAPI: UnchainedCore) => Promise<{
18
+ asURL?: () => Promise<string>;
19
+ asBuffer?: () => Promise<Buffer>;
20
+ serialNumber?: string;
21
+ passTypeIdentifier?: string;
22
+ }>;
23
+ }>;
24
+ };
25
+ export type TicketingModule = Awaited<ReturnType<(typeof ticketingModule)['configure']>>;
26
+ export default ticketingModule;
package/lib/module.js ADDED
@@ -0,0 +1,143 @@
1
+ import { MediaObjectsCollection } from '@unchainedshop/core-files/db/MediaObjectsCollection.js';
2
+ import { createLogger } from '@unchainedshop/logger';
3
+ import { RendererTypes, getRenderer } from './template-registry.js';
4
+ import { buildPassBinary, pushToApplePushNotificationService } from './mobile-tickets/apple-wallet.js';
5
+ export const APPLE_WALLET_PASSES_FILE_DIRECTORY = 'apple-wallet-passes';
6
+ const logger = createLogger('unchained:apple-wallet-webservice');
7
+ const ticketingModule = {
8
+ configure: async ({ db }) => {
9
+ const MediaObjects = (await MediaObjectsCollection(db));
10
+ const upsertAppleWalletPass = async (token, unchainedAPI) => {
11
+ const createAppleWalletPass = getRenderer(RendererTypes.APPLE_WALLET);
12
+ const pass = await createAppleWalletPass(token, unchainedAPI);
13
+ const rawFile = Promise.resolve(
14
+ // wrap in promise to make stream upload work
15
+ await buildPassBinary(token.chainTokenId, pass));
16
+ const previousFile = await MediaObjects.findOne({
17
+ path: APPLE_WALLET_PASSES_FILE_DIRECTORY,
18
+ 'meta.passTypeIdentifier': pass.passTypeIdentifier,
19
+ 'meta.serialNumber': pass.serialNumber,
20
+ });
21
+ const registrations = previousFile?.meta?.registrations || [];
22
+ const pkpassFile = await unchainedAPI.services.files.uploadFileFromStream({
23
+ directoryName: APPLE_WALLET_PASSES_FILE_DIRECTORY,
24
+ rawFile,
25
+ meta: {
26
+ rawData: token,
27
+ passTypeIdentifier: pass.passTypeIdentifier,
28
+ serialNumber: pass.serialNumber,
29
+ registrations,
30
+ },
31
+ }, unchainedAPI);
32
+ if (previousFile) {
33
+ await unchainedAPI.services.files.removeFiles({
34
+ fileIds: [previousFile._id],
35
+ }, unchainedAPI);
36
+ }
37
+ // Push updates!
38
+ if (registrations?.length) {
39
+ try {
40
+ const pushTokens = registrations.map(({ pushToken }) => pushToken);
41
+ logger.info(`Send update pass notification to ${pushTokens.join(',')}`);
42
+ await pushToApplePushNotificationService(pushTokens);
43
+ }
44
+ catch (e) {
45
+ logger.error(e);
46
+ }
47
+ }
48
+ return pkpassFile;
49
+ };
50
+ const upsertGoogleWalletPass = async (token, unchainedAPI) => {
51
+ const createGoogleWalletPass = getRenderer(RendererTypes.GOOGLE_WALLET);
52
+ const pass = await createGoogleWalletPass(token, unchainedAPI);
53
+ return pass;
54
+ };
55
+ const findAppleWalletPass = async (passTypeIdentifier, serialNumber) => {
56
+ const mediaObject = await MediaObjects.findOne({
57
+ path: APPLE_WALLET_PASSES_FILE_DIRECTORY,
58
+ 'meta.passTypeIdentifier': passTypeIdentifier,
59
+ 'meta.serialNumber': serialNumber,
60
+ });
61
+ return mediaObject;
62
+ };
63
+ const registerDeviceForAppleWalletPass = async (passTypeIdentifier, serialNumber, { deviceLibraryIdentifier, pushToken }) => {
64
+ const result = await MediaObjects.updateOne({
65
+ path: APPLE_WALLET_PASSES_FILE_DIRECTORY,
66
+ 'meta.passTypeIdentifier': passTypeIdentifier,
67
+ 'meta.serialNumber': serialNumber,
68
+ 'meta.registrations.deviceLibraryIdentifier': {
69
+ $ne: deviceLibraryIdentifier,
70
+ },
71
+ }, {
72
+ $push: {
73
+ 'meta.registrations': {
74
+ deviceLibraryIdentifier,
75
+ pushToken,
76
+ },
77
+ },
78
+ });
79
+ return Boolean(result.modifiedCount);
80
+ };
81
+ const unregisterDeviceForAppleWalletPass = async (passTypeIdentifier, serialNumber, deviceLibraryIdentifier) => {
82
+ const result = await MediaObjects.updateOne({
83
+ path: APPLE_WALLET_PASSES_FILE_DIRECTORY,
84
+ 'meta.passTypeIdentifier': passTypeIdentifier,
85
+ 'meta.serialNumber': serialNumber,
86
+ 'meta.registrations.deviceLibraryIdentifier': deviceLibraryIdentifier,
87
+ }, {
88
+ $pull: {
89
+ 'meta.registrations': {
90
+ deviceLibraryIdentifier,
91
+ },
92
+ },
93
+ });
94
+ return Boolean(result.modifiedCount);
95
+ };
96
+ const findUpdatedAppleWalletPasses = async (passTypeIdentifier, deviceLibraryIdentifier, passesUpdatedSince) => {
97
+ const selector = {
98
+ $or: [
99
+ {
100
+ updated: passesUpdatedSince ? { $gt: passesUpdatedSince } : { $exists: true },
101
+ },
102
+ {
103
+ created: passesUpdatedSince ? { $gt: passesUpdatedSince } : { $exists: true },
104
+ },
105
+ ],
106
+ 'meta.registrations.deviceLibraryIdentifier': deviceLibraryIdentifier,
107
+ 'meta.passTypeIdentifier': passTypeIdentifier,
108
+ path: APPLE_WALLET_PASSES_FILE_DIRECTORY,
109
+ };
110
+ const updatedPasses = await MediaObjects.find(selector).toArray();
111
+ return updatedPasses;
112
+ };
113
+ const invalidateAppleWalletPasses = async (unchainedAPI) => {
114
+ const allPasses = await MediaObjects.find({
115
+ path: APPLE_WALLET_PASSES_FILE_DIRECTORY,
116
+ }).toArray();
117
+ const allTokens = await unchainedAPI.modules.warehousing.findTokens({});
118
+ for (const pass of allPasses) {
119
+ // Check if binary is already invalidated, if so, skip
120
+ const rawData = pass.meta.rawData;
121
+ if (rawData.invalidatedDate)
122
+ continue;
123
+ const redeemedToken = allTokens.find((t) => t._id === rawData._id && t.invalidatedDate);
124
+ // Find invalidated token for the binary, if found, invalidate binary
125
+ if (redeemedToken) {
126
+ logger.info('Ticket redeemed, void pass', rawData);
127
+ await upsertAppleWalletPass(redeemedToken, unchainedAPI);
128
+ }
129
+ }
130
+ };
131
+ return {
132
+ upsertAppleWalletPass,
133
+ findAppleWalletPass,
134
+ registerDeviceForAppleWalletPass,
135
+ unregisterDeviceForAppleWalletPass,
136
+ findUpdatedAppleWalletPasses,
137
+ invalidateAppleWalletPasses,
138
+ upsertGoogleWalletPass,
139
+ };
140
+ },
141
+ };
142
+ export default ticketingModule;
143
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Request, Response } from 'express';
2
+ import { Context } from '@unchainedshop/types/api.js';
3
+ export declare function printTicketsHandler(req: Request & {
4
+ unchainedContext: Context;
5
+ }, res: Response): Promise<void>;
6
+ export default function connectPrintWebservice(app: any): void;
@@ -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 (e) {
21
+ console.error(e);
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,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjB,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"}