@queue-it/fastly 1.0.4 → 1.1.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.
- package/LICENSE +21 -21
- package/README.md +129 -91
- package/assembly/contextProvider.ts +122 -122
- package/assembly/helper.ts +1 -1
- package/assembly/index.ts +3 -3
- package/assembly/requestResponseHandler.ts +137 -137
- package/assembly/sdk/HttpContextProvider.ts +24 -24
- package/assembly/sdk/IntegrationConfig/CustomerIntegrationDecodingHandler.ts +221 -198
- package/assembly/sdk/IntegrationConfig/IntegrationConfigHelpers.ts +232 -232
- package/assembly/sdk/IntegrationConfig/IntegrationConfigModel.ts +93 -93
- package/assembly/sdk/KnownUser.ts +395 -395
- package/assembly/sdk/Models.ts +105 -105
- package/assembly/sdk/QueueITHelpers.ts +263 -263
- package/assembly/sdk/UserInQueueService.ts +245 -245
- package/assembly/sdk/UserInQueueStateCookieRepository.ts +189 -189
- package/assembly/sdk/helpers/Uri.ts +308 -308
- package/assembly/sdk/helpers/crypto.ts +340 -340
- package/package.json +1 -1
|
@@ -1,263 +1,263 @@
|
|
|
1
|
-
//@ts-ignore
|
|
2
|
-
import {RegExp} from "assemblyscript-regex";
|
|
3
|
-
import {KeyValuePair, RequestValidationResult} from "./Models";
|
|
4
|
-
import {IDateTimeProvider} from "./HttpContextProvider";
|
|
5
|
-
import {Date as WasiDate} from "as-wasi";
|
|
6
|
-
import {encodeURIComponent, decodeURIComponent} from "./helpers/Uri";
|
|
7
|
-
import {KnownUser} from "./KnownUser";
|
|
8
|
-
import {Response} from "@fastly/as-compute";
|
|
9
|
-
import {UserInQueueService} from "./UserInQueueService";
|
|
10
|
-
|
|
11
|
-
export class QueueUrlParams {
|
|
12
|
-
public timeStamp: i64 = 0;
|
|
13
|
-
public eventId: string = "";
|
|
14
|
-
public hashCode: string = "";
|
|
15
|
-
public extendableCookie: bool = false;
|
|
16
|
-
public cookieValidityMinutes: i64 = 0;
|
|
17
|
-
public queueITToken: string = "";
|
|
18
|
-
public queueITTokenWithoutHash: string = "";
|
|
19
|
-
public queueId: string = "";
|
|
20
|
-
public redirectType: string = "";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export class Utils {
|
|
25
|
-
|
|
26
|
-
static encodeUrl(url: string): string {
|
|
27
|
-
if (url.length == 0)
|
|
28
|
-
return "";
|
|
29
|
-
let decoded = encodeURIComponent(url);
|
|
30
|
-
decoded = decoded.replaceAll('!', '%' + '!'.charCodeAt(0).toString(16));
|
|
31
|
-
decoded = decoded.replaceAll('\'', '%' + '\''.charCodeAt(0).toString(16));
|
|
32
|
-
decoded = decoded.replaceAll('(', '%' + '('.charCodeAt(0).toString(16));
|
|
33
|
-
decoded = decoded.replaceAll(')', '%' + ')'.charCodeAt(0).toString(16));
|
|
34
|
-
decoded = decoded.replaceAll('*', '%' + '*'.charCodeAt(0).toString(16));
|
|
35
|
-
|
|
36
|
-
return decoded;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static decodeUrl(url: string): string {
|
|
40
|
-
return decodeURIComponent(url);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
static generateSHA256Hash: (a: string, b: string) => string = (secretKey: string, stringToHash: string): string => "";
|
|
44
|
-
|
|
45
|
-
static endsWith(str: string, search: string): bool {
|
|
46
|
-
if (str == search)
|
|
47
|
-
return true;
|
|
48
|
-
if (str.length == 0 || search.length == 0)
|
|
49
|
-
return false;
|
|
50
|
-
return str.substring(str.length - search.length, str.length) == search;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
static getCurrentTime(): i64 {
|
|
54
|
-
return Math.floor((WasiDate.now() / 1000) as f64) as i64;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private static _hex2bin: u8[] = [
|
|
58
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
59
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
60
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
61
|
-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0-9
|
|
62
|
-
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A-F
|
|
63
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
64
|
-
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a-f
|
|
65
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
66
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
67
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
68
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
69
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
70
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
71
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
72
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
73
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
static hex2bin(str: string): string {
|
|
77
|
-
let len = str.length;
|
|
78
|
-
let rv = '';
|
|
79
|
-
let i = 0;
|
|
80
|
-
|
|
81
|
-
let c1: i32;
|
|
82
|
-
let c2: i32;
|
|
83
|
-
|
|
84
|
-
while (len > 1) {
|
|
85
|
-
let h1 = str.charAt(i++);
|
|
86
|
-
c1 = h1.charCodeAt(0);
|
|
87
|
-
let h2 = str.charAt(i++);
|
|
88
|
-
c2 = h2.charCodeAt(0);
|
|
89
|
-
|
|
90
|
-
rv += String.fromCharCode((Utils._hex2bin[c1] << 4) + Utils._hex2bin[c2]);
|
|
91
|
-
len -= 2;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return rv;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
static addNoCacheHeaders(res: Response): void {
|
|
98
|
-
res.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate, max-age=0');
|
|
99
|
-
res.headers.set('Pragma', 'no-cache');
|
|
100
|
-
res.headers.set('Expires', 'Fri, 01 Jan 1990 00:00:00 GMT');
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
static getParameterByName(url: string, name: string): string {
|
|
104
|
-
const namePart = new RegExp(`[\[\]]`, 'g')
|
|
105
|
-
let match = namePart.exec(name);
|
|
106
|
-
while (match != null) {
|
|
107
|
-
let rxmatch = match.matches[0];
|
|
108
|
-
name = name.replaceAll(rxmatch, '\\$&')
|
|
109
|
-
match = namePart.exec(name);
|
|
110
|
-
}
|
|
111
|
-
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
|
|
112
|
-
const results = regex.exec(url);
|
|
113
|
-
if (results == null) return '';
|
|
114
|
-
if (results.matches.length < 3) {
|
|
115
|
-
return '';
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return decodeURIComponent(results.matches[2].replaceAll('+', ' '));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static removeQueueItToken(url: string): string {
|
|
122
|
-
const pattern = new RegExp("([\?&])(" + KnownUser.QueueITTokenKey + "=[^&]*)", 'gi');
|
|
123
|
-
const match = pattern.exec(url);
|
|
124
|
-
if (match == null) return url;
|
|
125
|
-
|
|
126
|
-
url = url.replaceAll(match.matches[0], '')
|
|
127
|
-
return url;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export class QueueParameterHelper {
|
|
132
|
-
public static readonly TimeStampKey: string = "ts";
|
|
133
|
-
public static readonly ExtendableCookieKey: string = "ce";
|
|
134
|
-
public static readonly CookieValidityMinutesKey: string = "cv";
|
|
135
|
-
public static readonly HashKey: string = "h";
|
|
136
|
-
public static readonly EventIdKey: string = "e";
|
|
137
|
-
public static readonly QueueIdKey: string = "q";
|
|
138
|
-
public static readonly RedirectTypeKey: string = "rt";
|
|
139
|
-
public static readonly KeyValueSeparatorChar: string = '_';
|
|
140
|
-
public static readonly KeyValueSeparatorGroupChar: string = '~';
|
|
141
|
-
|
|
142
|
-
public static extractQueueParams(queueitToken: string): QueueUrlParams | null {
|
|
143
|
-
if (queueitToken.length == 0) {
|
|
144
|
-
return null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const result = new QueueUrlParams();
|
|
148
|
-
result.queueITToken = queueitToken;
|
|
149
|
-
|
|
150
|
-
const paramList = result.queueITToken.split(QueueParameterHelper.KeyValueSeparatorGroupChar);
|
|
151
|
-
for (let i = 0; i < paramList.length; i++) {
|
|
152
|
-
let paramKeyValue = paramList[i];
|
|
153
|
-
|
|
154
|
-
let keyValueArr = paramKeyValue.split(QueueParameterHelper.KeyValueSeparatorChar);
|
|
155
|
-
if (keyValueArr.length != 2) {
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (keyValueArr[0] == QueueParameterHelper.HashKey) {
|
|
160
|
-
result.hashCode = keyValueArr[1];
|
|
161
|
-
} else if (keyValueArr[0] == QueueParameterHelper.TimeStampKey) {
|
|
162
|
-
result.timeStamp = I64.parseInt(keyValueArr[1]);
|
|
163
|
-
} else if (keyValueArr[0] == QueueParameterHelper.CookieValidityMinutesKey) {
|
|
164
|
-
result.cookieValidityMinutes = I64.parseInt(keyValueArr[1]);
|
|
165
|
-
} else if (keyValueArr[0] == QueueParameterHelper.EventIdKey) {
|
|
166
|
-
result.eventId = keyValueArr[1];
|
|
167
|
-
} else if (keyValueArr[0] == QueueParameterHelper.ExtendableCookieKey) {
|
|
168
|
-
let extendCookie = (keyValueArr[1].length > 0 ? keyValueArr[1] : "false").toLowerCase();
|
|
169
|
-
result.extendableCookie = extendCookie == "true";
|
|
170
|
-
} else if (keyValueArr[0] == QueueParameterHelper.QueueIdKey) {
|
|
171
|
-
result.queueId = keyValueArr[1];
|
|
172
|
-
|
|
173
|
-
} else if (keyValueArr[0] == QueueParameterHelper.RedirectTypeKey) {
|
|
174
|
-
result.redirectType = keyValueArr[1];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const hashWithPrefix = QueueParameterHelper.KeyValueSeparatorGroupChar +
|
|
179
|
-
QueueParameterHelper.HashKey +
|
|
180
|
-
QueueParameterHelper.KeyValueSeparatorChar +
|
|
181
|
-
result.hashCode;
|
|
182
|
-
result.queueITTokenWithoutHash = result.queueITToken.replaceAll(hashWithPrefix, "");
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export class CookieHelper {
|
|
188
|
-
public static toMapFromValue(cookieValue: string): Map<string, string> {
|
|
189
|
-
const items: string[] = cookieValue.split('&');
|
|
190
|
-
let result = new Map<string, string>();
|
|
191
|
-
for (let i = 0; i < items.length; i++) {
|
|
192
|
-
let item: string = items[i];
|
|
193
|
-
let keyValue = item.split('=');
|
|
194
|
-
if (keyValue.length == 2) {
|
|
195
|
-
result.set(keyValue[0], keyValue[1]);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return result;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
public static toValueFromKeyValueCollection(cookieValues: Array<KeyValuePair>): string {
|
|
202
|
-
let values = new Array<string>();
|
|
203
|
-
|
|
204
|
-
for (let i = 0; i < cookieValues.length; i++) {
|
|
205
|
-
let kvp: KeyValuePair = cookieValues[i];
|
|
206
|
-
values.push(kvp.key + "=" + kvp.value);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return values.join("&");
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export class ConnectorDiagnostics {
|
|
214
|
-
public isEnabled: bool = false;
|
|
215
|
-
public hasError: bool = false;
|
|
216
|
-
public validationResult: RequestValidationResult | null
|
|
217
|
-
|
|
218
|
-
private setStateWithTokenError(customerId: string, errorCode: string): void {
|
|
219
|
-
this.hasError = true;
|
|
220
|
-
const redirectUrl = "https://" + customerId + ".api2.queue-it.net/" + customerId + "/diagnostics/connector/error/?code=" + errorCode;
|
|
221
|
-
this.validationResult = new RequestValidationResult("ConnectorDiagnosticsRedirect", "",
|
|
222
|
-
"", redirectUrl, "", "")
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
private setStateWithSetupError(): void {
|
|
226
|
-
this.hasError = true;
|
|
227
|
-
this.validationResult = new RequestValidationResult("ConnectorDiagnosticsRedirect", "", "",
|
|
228
|
-
"https://api2.queue-it.net/diagnostics/connector/error/?code=setup", "", "")
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
public static verify(customerId: string, secretKey: string, queueitToken: string): ConnectorDiagnostics {
|
|
232
|
-
const diagnostics = new ConnectorDiagnostics();
|
|
233
|
-
const qParams = QueueParameterHelper.extractQueueParams(queueitToken);
|
|
234
|
-
|
|
235
|
-
if (qParams == null)
|
|
236
|
-
return diagnostics;
|
|
237
|
-
if (qParams.redirectType == null)
|
|
238
|
-
return diagnostics;
|
|
239
|
-
if (qParams.redirectType != "debug")
|
|
240
|
-
return diagnostics;
|
|
241
|
-
if (!(customerId != "" && secretKey != "")) {
|
|
242
|
-
diagnostics.setStateWithSetupError();
|
|
243
|
-
return diagnostics;
|
|
244
|
-
}
|
|
245
|
-
if (Utils.generateSHA256Hash(secretKey, qParams.queueITTokenWithoutHash) != qParams.hashCode) {
|
|
246
|
-
diagnostics.setStateWithTokenError(customerId, "hash");
|
|
247
|
-
return diagnostics;
|
|
248
|
-
}
|
|
249
|
-
if (qParams.timeStamp < Utils.getCurrentTime()) {
|
|
250
|
-
diagnostics.setStateWithTokenError(customerId, "timestamp");
|
|
251
|
-
return diagnostics;
|
|
252
|
-
}
|
|
253
|
-
diagnostics.isEnabled = true;
|
|
254
|
-
|
|
255
|
-
return diagnostics;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
export class DateTimeProvider implements IDateTimeProvider {
|
|
260
|
-
getCurrentTime(): Date {
|
|
261
|
-
return new Date(i64(WasiDate.now()));
|
|
262
|
-
}
|
|
263
|
-
}
|
|
1
|
+
//@ts-ignore
|
|
2
|
+
import {RegExp} from "assemblyscript-regex";
|
|
3
|
+
import {KeyValuePair, RequestValidationResult} from "./Models";
|
|
4
|
+
import {IDateTimeProvider} from "./HttpContextProvider";
|
|
5
|
+
import {Date as WasiDate} from "as-wasi";
|
|
6
|
+
import {encodeURIComponent, decodeURIComponent} from "./helpers/Uri";
|
|
7
|
+
import {KnownUser} from "./KnownUser";
|
|
8
|
+
import {Response} from "@fastly/as-compute";
|
|
9
|
+
import {UserInQueueService} from "./UserInQueueService";
|
|
10
|
+
|
|
11
|
+
export class QueueUrlParams {
|
|
12
|
+
public timeStamp: i64 = 0;
|
|
13
|
+
public eventId: string = "";
|
|
14
|
+
public hashCode: string = "";
|
|
15
|
+
public extendableCookie: bool = false;
|
|
16
|
+
public cookieValidityMinutes: i64 = 0;
|
|
17
|
+
public queueITToken: string = "";
|
|
18
|
+
public queueITTokenWithoutHash: string = "";
|
|
19
|
+
public queueId: string = "";
|
|
20
|
+
public redirectType: string = "";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export class Utils {
|
|
25
|
+
|
|
26
|
+
static encodeUrl(url: string): string {
|
|
27
|
+
if (url.length == 0)
|
|
28
|
+
return "";
|
|
29
|
+
let decoded = encodeURIComponent(url);
|
|
30
|
+
decoded = decoded.replaceAll('!', '%' + '!'.charCodeAt(0).toString(16));
|
|
31
|
+
decoded = decoded.replaceAll('\'', '%' + '\''.charCodeAt(0).toString(16));
|
|
32
|
+
decoded = decoded.replaceAll('(', '%' + '('.charCodeAt(0).toString(16));
|
|
33
|
+
decoded = decoded.replaceAll(')', '%' + ')'.charCodeAt(0).toString(16));
|
|
34
|
+
decoded = decoded.replaceAll('*', '%' + '*'.charCodeAt(0).toString(16));
|
|
35
|
+
|
|
36
|
+
return decoded;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static decodeUrl(url: string): string {
|
|
40
|
+
return decodeURIComponent(url);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static generateSHA256Hash: (a: string, b: string) => string = (secretKey: string, stringToHash: string): string => "";
|
|
44
|
+
|
|
45
|
+
static endsWith(str: string, search: string): bool {
|
|
46
|
+
if (str == search)
|
|
47
|
+
return true;
|
|
48
|
+
if (str.length == 0 || search.length == 0)
|
|
49
|
+
return false;
|
|
50
|
+
return str.substring(str.length - search.length, str.length) == search;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static getCurrentTime(): i64 {
|
|
54
|
+
return Math.floor((WasiDate.now() / 1000) as f64) as i64;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private static _hex2bin: u8[] = [
|
|
58
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
59
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
60
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
61
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0-9
|
|
62
|
+
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A-F
|
|
63
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
64
|
+
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a-f
|
|
65
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
66
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
67
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
68
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
69
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
70
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
71
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
72
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
73
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
static hex2bin(str: string): string {
|
|
77
|
+
let len = str.length;
|
|
78
|
+
let rv = '';
|
|
79
|
+
let i = 0;
|
|
80
|
+
|
|
81
|
+
let c1: i32;
|
|
82
|
+
let c2: i32;
|
|
83
|
+
|
|
84
|
+
while (len > 1) {
|
|
85
|
+
let h1 = str.charAt(i++);
|
|
86
|
+
c1 = h1.charCodeAt(0);
|
|
87
|
+
let h2 = str.charAt(i++);
|
|
88
|
+
c2 = h2.charCodeAt(0);
|
|
89
|
+
|
|
90
|
+
rv += String.fromCharCode((Utils._hex2bin[c1] << 4) + Utils._hex2bin[c2]);
|
|
91
|
+
len -= 2;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return rv;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static addNoCacheHeaders(res: Response): void {
|
|
98
|
+
res.headers.set('Cache-Control', 'no-cache, no-store, must-revalidate, max-age=0');
|
|
99
|
+
res.headers.set('Pragma', 'no-cache');
|
|
100
|
+
res.headers.set('Expires', 'Fri, 01 Jan 1990 00:00:00 GMT');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static getParameterByName(url: string, name: string): string {
|
|
104
|
+
const namePart = new RegExp(`[\[\]]`, 'g')
|
|
105
|
+
let match = namePart.exec(name);
|
|
106
|
+
while (match != null) {
|
|
107
|
+
let rxmatch = match.matches[0];
|
|
108
|
+
name = name.replaceAll(rxmatch, '\\$&')
|
|
109
|
+
match = namePart.exec(name);
|
|
110
|
+
}
|
|
111
|
+
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
|
|
112
|
+
const results = regex.exec(url);
|
|
113
|
+
if (results == null) return '';
|
|
114
|
+
if (results.matches.length < 3) {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return decodeURIComponent(results.matches[2].replaceAll('+', ' '));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static removeQueueItToken(url: string): string {
|
|
122
|
+
const pattern = new RegExp("([\?&])(" + KnownUser.QueueITTokenKey + "=[^&]*)", 'gi');
|
|
123
|
+
const match = pattern.exec(url);
|
|
124
|
+
if (match == null) return url;
|
|
125
|
+
|
|
126
|
+
url = url.replaceAll(match.matches[0], '')
|
|
127
|
+
return url;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export class QueueParameterHelper {
|
|
132
|
+
public static readonly TimeStampKey: string = "ts";
|
|
133
|
+
public static readonly ExtendableCookieKey: string = "ce";
|
|
134
|
+
public static readonly CookieValidityMinutesKey: string = "cv";
|
|
135
|
+
public static readonly HashKey: string = "h";
|
|
136
|
+
public static readonly EventIdKey: string = "e";
|
|
137
|
+
public static readonly QueueIdKey: string = "q";
|
|
138
|
+
public static readonly RedirectTypeKey: string = "rt";
|
|
139
|
+
public static readonly KeyValueSeparatorChar: string = '_';
|
|
140
|
+
public static readonly KeyValueSeparatorGroupChar: string = '~';
|
|
141
|
+
|
|
142
|
+
public static extractQueueParams(queueitToken: string): QueueUrlParams | null {
|
|
143
|
+
if (queueitToken.length == 0) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const result = new QueueUrlParams();
|
|
148
|
+
result.queueITToken = queueitToken;
|
|
149
|
+
|
|
150
|
+
const paramList = result.queueITToken.split(QueueParameterHelper.KeyValueSeparatorGroupChar);
|
|
151
|
+
for (let i = 0; i < paramList.length; i++) {
|
|
152
|
+
let paramKeyValue = paramList[i];
|
|
153
|
+
|
|
154
|
+
let keyValueArr = paramKeyValue.split(QueueParameterHelper.KeyValueSeparatorChar);
|
|
155
|
+
if (keyValueArr.length != 2) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (keyValueArr[0] == QueueParameterHelper.HashKey) {
|
|
160
|
+
result.hashCode = keyValueArr[1];
|
|
161
|
+
} else if (keyValueArr[0] == QueueParameterHelper.TimeStampKey) {
|
|
162
|
+
result.timeStamp = I64.parseInt(keyValueArr[1]);
|
|
163
|
+
} else if (keyValueArr[0] == QueueParameterHelper.CookieValidityMinutesKey) {
|
|
164
|
+
result.cookieValidityMinutes = I64.parseInt(keyValueArr[1]);
|
|
165
|
+
} else if (keyValueArr[0] == QueueParameterHelper.EventIdKey) {
|
|
166
|
+
result.eventId = keyValueArr[1];
|
|
167
|
+
} else if (keyValueArr[0] == QueueParameterHelper.ExtendableCookieKey) {
|
|
168
|
+
let extendCookie = (keyValueArr[1].length > 0 ? keyValueArr[1] : "false").toLowerCase();
|
|
169
|
+
result.extendableCookie = extendCookie == "true";
|
|
170
|
+
} else if (keyValueArr[0] == QueueParameterHelper.QueueIdKey) {
|
|
171
|
+
result.queueId = keyValueArr[1];
|
|
172
|
+
|
|
173
|
+
} else if (keyValueArr[0] == QueueParameterHelper.RedirectTypeKey) {
|
|
174
|
+
result.redirectType = keyValueArr[1];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const hashWithPrefix = QueueParameterHelper.KeyValueSeparatorGroupChar +
|
|
179
|
+
QueueParameterHelper.HashKey +
|
|
180
|
+
QueueParameterHelper.KeyValueSeparatorChar +
|
|
181
|
+
result.hashCode;
|
|
182
|
+
result.queueITTokenWithoutHash = result.queueITToken.replaceAll(hashWithPrefix, "");
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export class CookieHelper {
|
|
188
|
+
public static toMapFromValue(cookieValue: string): Map<string, string> {
|
|
189
|
+
const items: string[] = cookieValue.split('&');
|
|
190
|
+
let result = new Map<string, string>();
|
|
191
|
+
for (let i = 0; i < items.length; i++) {
|
|
192
|
+
let item: string = items[i];
|
|
193
|
+
let keyValue = item.split('=');
|
|
194
|
+
if (keyValue.length == 2) {
|
|
195
|
+
result.set(keyValue[0], keyValue[1]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public static toValueFromKeyValueCollection(cookieValues: Array<KeyValuePair>): string {
|
|
202
|
+
let values = new Array<string>();
|
|
203
|
+
|
|
204
|
+
for (let i = 0; i < cookieValues.length; i++) {
|
|
205
|
+
let kvp: KeyValuePair = cookieValues[i];
|
|
206
|
+
values.push(kvp.key + "=" + kvp.value);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return values.join("&");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export class ConnectorDiagnostics {
|
|
214
|
+
public isEnabled: bool = false;
|
|
215
|
+
public hasError: bool = false;
|
|
216
|
+
public validationResult: RequestValidationResult | null
|
|
217
|
+
|
|
218
|
+
private setStateWithTokenError(customerId: string, errorCode: string): void {
|
|
219
|
+
this.hasError = true;
|
|
220
|
+
const redirectUrl = "https://" + customerId + ".api2.queue-it.net/" + customerId + "/diagnostics/connector/error/?code=" + errorCode;
|
|
221
|
+
this.validationResult = new RequestValidationResult("ConnectorDiagnosticsRedirect", "",
|
|
222
|
+
"", redirectUrl, "", "")
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private setStateWithSetupError(): void {
|
|
226
|
+
this.hasError = true;
|
|
227
|
+
this.validationResult = new RequestValidationResult("ConnectorDiagnosticsRedirect", "", "",
|
|
228
|
+
"https://api2.queue-it.net/diagnostics/connector/error/?code=setup", "", "")
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
public static verify(customerId: string, secretKey: string, queueitToken: string): ConnectorDiagnostics {
|
|
232
|
+
const diagnostics = new ConnectorDiagnostics();
|
|
233
|
+
const qParams = QueueParameterHelper.extractQueueParams(queueitToken);
|
|
234
|
+
|
|
235
|
+
if (qParams == null)
|
|
236
|
+
return diagnostics;
|
|
237
|
+
if (qParams.redirectType == null)
|
|
238
|
+
return diagnostics;
|
|
239
|
+
if (qParams.redirectType != "debug")
|
|
240
|
+
return diagnostics;
|
|
241
|
+
if (!(customerId != "" && secretKey != "")) {
|
|
242
|
+
diagnostics.setStateWithSetupError();
|
|
243
|
+
return diagnostics;
|
|
244
|
+
}
|
|
245
|
+
if (Utils.generateSHA256Hash(secretKey, qParams.queueITTokenWithoutHash) != qParams.hashCode) {
|
|
246
|
+
diagnostics.setStateWithTokenError(customerId, "hash");
|
|
247
|
+
return diagnostics;
|
|
248
|
+
}
|
|
249
|
+
if (qParams.timeStamp < Utils.getCurrentTime()) {
|
|
250
|
+
diagnostics.setStateWithTokenError(customerId, "timestamp");
|
|
251
|
+
return diagnostics;
|
|
252
|
+
}
|
|
253
|
+
diagnostics.isEnabled = true;
|
|
254
|
+
|
|
255
|
+
return diagnostics;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export class DateTimeProvider implements IDateTimeProvider {
|
|
260
|
+
getCurrentTime(): Date {
|
|
261
|
+
return new Date(i64(WasiDate.now()));
|
|
262
|
+
}
|
|
263
|
+
}
|