-
-
Notifications
You must be signed in to change notification settings - Fork 214
/
utility.ts
225 lines (217 loc) · 6.92 KB
/
utility.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/**
* @file utility.ts
* @author tngan
* @desc Library for some common functions (e.g. de/inflation, en/decoding)
*/
import { pki, util, asn1 } from 'node-forge';
import { inflate, deflate } from 'pako';
const BASE64_STR = 'base64';
/**
* @desc Mimic lodash.zipObject
* @param arr1 {string[]}
* @param arr2 {[]}
*/
export function zipObject(arr1: string[], arr2: any[], skipDuplicated = true) {
return arr1.reduce((res, l, i) => {
if (skipDuplicated) {
res[l] = arr2[i];
return res;
}
// if key exists, aggregate with array in order to get rid of duplicate key
if (res[l] !== undefined) {
res[l] = Array.isArray(res[l])
? res[l].concat(arr2[i])
: [res[l]].concat(arr2[i]);
return res;
}
res[l] = arr2[i];
return res;
}, {});
}
/**
* @desc Alternative to lodash.flattenDeep
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_flattendeep
* @param input {[]}
*/
export function flattenDeep(input: any[]) {
return Array.isArray(input)
? input.reduce( (a, b) => a.concat(flattenDeep(b)) , [])
: [input];
}
/**
* @desc Alternative to lodash.last
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_last
* @param input {[]}
*/
export function last(input: any[]) {
return input.slice(-1)[0];
}
/**
* @desc Alternative to lodash.uniq
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_uniq
* @param input {string[]}
*/
export function uniq(input: string[]) {
const set = new Set(input);
return [... set];
}
/**
* @desc Alternative to lodash.get
* @reference https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_get
* @param obj
* @param path
* @param defaultValue
*/
export function get(obj, path, defaultValue) {
return path.split('.')
.reduce((a, c) => (a && a[c] ? a[c] : (defaultValue || null)), obj);
}
/**
* @desc Check if the input is string
* @param {any} input
*/
export function isString(input: any) {
return typeof input === 'string';
}
/**
* @desc Encode string with base64 format
* @param {string} message plain-text message
* @return {string} base64 encoded string
*/
function base64Encode(message: string | number[]) {
return Buffer.from(message as string).toString(BASE64_STR);
}
/**
* @desc Decode string from base64 format
* @param {string} base64Message encoded string
* @param {boolean} isBytes determine the return value type (True: bytes False: string)
* @return {bytes/string} decoded bytes/string depends on isBytes, default is {string}
*/
export function base64Decode(base64Message: string, isBytes?: boolean): string | Buffer {
const bytes = Buffer.from(base64Message, BASE64_STR);
return Boolean(isBytes) ? bytes : bytes.toString();
}
/**
* @desc Compress the string
* @param {string} message
* @return {string} compressed string
*/
function deflateString(message: string): number[] {
const input = Array.prototype.map.call(message, char => char.charCodeAt(0));
return Array.from(deflate(input, { raw: true }));
}
/**
* @desc Decompress the compressed string
* @param {string} compressedString
* @return {string} decompressed string
*/
export function inflateString(compressedString: string): string {
const inputBuffer = Buffer.from(compressedString, BASE64_STR);
const input = Array.prototype.map.call(inputBuffer.toString('binary'), char => char.charCodeAt(0));
return Array.from(inflate(input, { raw: true }))
.map((byte: number) => String.fromCharCode(byte))
.join('');
}
/**
* @desc Abstract the normalizeCerString and normalizePemString
* @param {buffer} File stream or string
* @param {string} String for header and tail
* @return {string} A formatted certificate string
*/
function _normalizeCerString(bin: string | Buffer, format: string) {
return bin.toString().replace(/\n/g, '').replace(/\r/g, '').replace(`-----BEGIN ${format}-----`, '').replace(`-----END ${format}-----`, '').replace(/ /g, '').replace(/\t/g, '');
}
/**
* @desc Parse the .cer to string format without line break, header and footer
* @param {string} certString declares the certificate contents
* @return {string} certificiate in string format
*/
function normalizeCerString(certString: string | Buffer) {
return _normalizeCerString(certString, 'CERTIFICATE');
}
/**
* @desc Normalize the string in .pem format without line break, header and footer
* @param {string} pemString
* @return {string} private key in string format
*/
function normalizePemString(pemString: string | Buffer) {
return _normalizeCerString(pemString.toString(), 'RSA PRIVATE KEY');
}
/**
* @desc Return the complete URL
* @param {object} req HTTP request
* @return {string} URL
*/
function getFullURL(req) {
return `${req.protocol}://${req.get('host')}${req.originalUrl}`;
}
/**
* @desc Parse input string, return default value if it is undefined
* @param {string/boolean}
* @return {boolean}
*/
function parseString(str, defaultValue = '') {
return str || defaultValue;
}
/**
* @desc Override the object by another object (rtl)
* @param {object} default object
* @param {object} object applied to the default object
* @return {object} result object
*/
function applyDefault(obj1, obj2) {
return Object.assign({}, obj1, obj2);
}
/**
* @desc Get public key in pem format from the certificate included in the metadata
* @param {string} x509 certificate
* @return {string} public key fetched from the certificate
*/
function getPublicKeyPemFromCertificate(x509Certificate: string) {
const certDerBytes = util.decode64(x509Certificate);
const obj = asn1.fromDer(certDerBytes);
const cert = pki.certificateFromAsn1(obj);
return pki.publicKeyToPem(cert.publicKey);
}
/**
* @desc Read private key from pem-formatted string
* @param {string | Buffer} keyString pem-formattted string
* @param {string} protected passphrase of the key
* @return {string} string in pem format
* If passphrase is used to protect the .pem content (recommend)
*/
export function readPrivateKey(keyString: string | Buffer, passphrase: string | undefined, isOutputString?: boolean) {
return isString(passphrase) ? this.convertToString(pki.privateKeyToPem(pki.decryptRsaPrivateKey(String(keyString), passphrase)), isOutputString) : keyString;
}
/**
* @desc Inline syntax sugar
*/
function convertToString(input, isOutputString) {
return Boolean(isOutputString) ? String(input) : input;
}
/**
* @desc Check if the input is an array with non-zero size
*/
export function isNonEmptyArray(a) {
return Array.isArray(a) && a.length > 0;
}
export function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
return value !== null && value !== undefined;
}
const utility = {
isString,
base64Encode,
base64Decode,
deflateString,
inflateString,
normalizeCerString,
normalizePemString,
getFullURL,
parseString,
applyDefault,
getPublicKeyPemFromCertificate,
readPrivateKey,
convertToString,
isNonEmptyArray,
};
export default utility;