anote-server-libs 0.12.1 → 0.12.2
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/.vscode/settings.json +2 -2
- package/models/ApiCall.ts +40 -40
- package/models/Migration.ts +43 -43
- package/models/repository/MemoryCache.ts +87 -87
- package/models/repository/ModelDao.ts +268 -268
- package/package.json +35 -35
- package/services/WithBody.ts +65 -65
- package/services/WithTransaction.js +20 -7
- package/services/WithTransaction.ts +271 -260
- package/services/utils.js +24 -24
- package/services/utils.ts +289 -289
- package/tsconfig.json +29 -29
- package/models/repository/CryptModelDao.js +0 -82
package/services/WithBody.ts
CHANGED
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
import {Request, Response} from 'express';
|
|
2
|
-
import Ajv, {_} from 'ajv';
|
|
3
|
-
|
|
4
|
-
export const ajv = new Ajv();
|
|
5
|
-
ajv.addKeyword({
|
|
6
|
-
keyword: 'maxDigits',
|
|
7
|
-
type: 'number',
|
|
8
|
-
schemaType: 'number',
|
|
9
|
-
validate: (schema: number, data: any) => typeof data === 'number' && Math.round(data * 10 ** schema) / (10 ** schema) === data
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export function WithBody(schema: any, collapseTopLevelNulls = true) {
|
|
13
|
-
if(schema.type === 'object') {
|
|
14
|
-
const required: string[] = [];
|
|
15
|
-
const keys = Object.getOwnPropertyNames(schema.properties);
|
|
16
|
-
keys.forEach(key => {
|
|
17
|
-
if(schema.properties[key].required === true) {
|
|
18
|
-
required.push(key);
|
|
19
|
-
}
|
|
20
|
-
if(schema.properties[key].required === true || schema.properties[key].required === false) {
|
|
21
|
-
delete schema.properties[key].required;
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
schema.required = required;
|
|
25
|
-
}
|
|
26
|
-
const validate = ajv.compile(schema);
|
|
27
|
-
return function(_: any, __: string, descriptor: PropertyDescriptor) {
|
|
28
|
-
if(typeof descriptor.value === 'function') {
|
|
29
|
-
const previousMethod = descriptor.value;
|
|
30
|
-
descriptor.value = function(this: any, req: Request, res: Response) {
|
|
31
|
-
if(req.method.toUpperCase() !== 'POST' && req.method.toUpperCase() !== 'PUT') {
|
|
32
|
-
return previousMethod.call(this, req, res);
|
|
33
|
-
}
|
|
34
|
-
if(!req.body) {
|
|
35
|
-
res.status(400).json({
|
|
36
|
-
error: {
|
|
37
|
-
errorKey: 'client.body.missing',
|
|
38
|
-
additionalInfo: 'client.extended.badPayload'
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
} else {
|
|
42
|
-
if(collapseTopLevelNulls) {
|
|
43
|
-
Object.keys(req.body).forEach(k => {
|
|
44
|
-
if(req.body[k] === null) delete req.body[k];
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
const valid = validate(req.body);
|
|
48
|
-
if(valid) {
|
|
49
|
-
return previousMethod.call(this, req, res);
|
|
50
|
-
} else {
|
|
51
|
-
res.status(400).json({
|
|
52
|
-
error: {
|
|
53
|
-
errorKey: 'client.body.missing',
|
|
54
|
-
additionalInfo: 'client.extended.badPayload',
|
|
55
|
-
detailedInfo: validate.errors
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
return descriptor;
|
|
62
|
-
}
|
|
63
|
-
return undefined;
|
|
64
|
-
};
|
|
65
|
-
}
|
|
1
|
+
import {Request, Response} from 'express';
|
|
2
|
+
import Ajv, {_} from 'ajv';
|
|
3
|
+
|
|
4
|
+
export const ajv = new Ajv();
|
|
5
|
+
ajv.addKeyword({
|
|
6
|
+
keyword: 'maxDigits',
|
|
7
|
+
type: 'number',
|
|
8
|
+
schemaType: 'number',
|
|
9
|
+
validate: (schema: number, data: any) => typeof data === 'number' && Math.round(data * 10 ** schema) / (10 ** schema) === data
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export function WithBody(schema: any, collapseTopLevelNulls = true) {
|
|
13
|
+
if(schema.type === 'object') {
|
|
14
|
+
const required: string[] = [];
|
|
15
|
+
const keys = Object.getOwnPropertyNames(schema.properties);
|
|
16
|
+
keys.forEach(key => {
|
|
17
|
+
if(schema.properties[key].required === true) {
|
|
18
|
+
required.push(key);
|
|
19
|
+
}
|
|
20
|
+
if(schema.properties[key].required === true || schema.properties[key].required === false) {
|
|
21
|
+
delete schema.properties[key].required;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
schema.required = required;
|
|
25
|
+
}
|
|
26
|
+
const validate = ajv.compile(schema);
|
|
27
|
+
return function(_: any, __: string, descriptor: PropertyDescriptor) {
|
|
28
|
+
if(typeof descriptor.value === 'function') {
|
|
29
|
+
const previousMethod = descriptor.value;
|
|
30
|
+
descriptor.value = function(this: any, req: Request, res: Response) {
|
|
31
|
+
if(req.method.toUpperCase() !== 'POST' && req.method.toUpperCase() !== 'PUT') {
|
|
32
|
+
return previousMethod.call(this, req, res);
|
|
33
|
+
}
|
|
34
|
+
if(!req.body) {
|
|
35
|
+
res.status(400).json({
|
|
36
|
+
error: {
|
|
37
|
+
errorKey: 'client.body.missing',
|
|
38
|
+
additionalInfo: 'client.extended.badPayload'
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
if(collapseTopLevelNulls) {
|
|
43
|
+
Object.keys(req.body).forEach(k => {
|
|
44
|
+
if(req.body[k] === null) delete req.body[k];
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const valid = validate(req.body);
|
|
48
|
+
if(valid) {
|
|
49
|
+
return previousMethod.call(this, req, res);
|
|
50
|
+
} else {
|
|
51
|
+
res.status(400).json({
|
|
52
|
+
error: {
|
|
53
|
+
errorKey: 'client.body.missing',
|
|
54
|
+
additionalInfo: 'client.extended.badPayload',
|
|
55
|
+
detailedInfo: validate.errors
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return descriptor;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -45,7 +45,11 @@ function releaseAndDecrement(repo, dbClient) {
|
|
|
45
45
|
try {
|
|
46
46
|
dbClient.release();
|
|
47
47
|
}
|
|
48
|
-
catch (
|
|
48
|
+
catch (err) {
|
|
49
|
+
const log = utils_1.utils.logger;
|
|
50
|
+
if (log)
|
|
51
|
+
log.error('Error releasing dbClient: %j', { err, stack: new Error().stack });
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
exports.withTransactionConfig.activeCount = Math.max(0, exports.withTransactionConfig.activeCount - 1);
|
|
51
55
|
}
|
|
@@ -73,7 +77,7 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
73
77
|
};
|
|
74
78
|
exports.withTransactionConfig.activeCount++;
|
|
75
79
|
const connectTimeoutHandler = setTimeout(() => {
|
|
76
|
-
logger.error('Error timed out getting a client, exiting...');
|
|
80
|
+
logger.error('Error timed out getting a client, exiting... stack: %s', new Error().stack);
|
|
77
81
|
process.exit(22);
|
|
78
82
|
}, exports.withTransactionConfig.timeoutMillis);
|
|
79
83
|
Promise.all([
|
|
@@ -92,7 +96,14 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
92
96
|
dbClient.queryPatched = true;
|
|
93
97
|
}
|
|
94
98
|
dbClient.removeAllListeners('error');
|
|
95
|
-
dbClient.on('error', (err) =>
|
|
99
|
+
dbClient.on('error', (err) => {
|
|
100
|
+
if (utils_1.utils.clientErrorHandler) {
|
|
101
|
+
utils_1.utils.clientErrorHandler(err, dbClient);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
logger.error('SQL [Client %d] ERROR (no handler): %j', dbClient?.processID, { err, stack: new Error().stack });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
96
107
|
res.locals.dbClient = dbClient;
|
|
97
108
|
res.locals.dbClientCommited = false;
|
|
98
109
|
res.locals.dbClientCommit = (cb) => {
|
|
@@ -130,10 +141,10 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
130
141
|
if (res.statusCode > 303 && res.statusCode !== 412) {
|
|
131
142
|
if (logger) {
|
|
132
143
|
if (res.statusCode > 499) {
|
|
133
|
-
logger.error('Uncaught 500: %j', obj
|
|
144
|
+
logger.error('Uncaught 500 at %s %s: %j', req.method, req.originalUrl, { body: obj, stack: new Error().stack });
|
|
134
145
|
}
|
|
135
146
|
else {
|
|
136
|
-
logger.warn('Client error 4XX: %j', obj
|
|
147
|
+
logger.warn('Client error 4XX at %s %s: %j', req.method, req.originalUrl, { body: obj, stack: new Error().stack });
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
150
|
(repo.db ? dbClient.query('ROLLBACK') : dbClient.rollback()).catch((err) => obj && obj.error && (obj.error.additionalInfo2 = { message: err.message })).then(() => {
|
|
@@ -161,7 +172,7 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
161
172
|
res.end = () => {
|
|
162
173
|
if (res.statusCode > 303 && res.statusCode !== 412) {
|
|
163
174
|
if (logger && res.statusCode > 499) {
|
|
164
|
-
logger.error('Uncaught 500 with no details
|
|
175
|
+
logger.error('Uncaught 500 with no details at %s %s: %j', req.method, req.originalUrl, { stack: new Error().stack });
|
|
165
176
|
}
|
|
166
177
|
(repo.db ? dbClient.query('ROLLBACK') : dbClient.rollback()).catch(() => undefined).then(() => {
|
|
167
178
|
releaseAndDecrement(repo, dbClient);
|
|
@@ -189,6 +200,7 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
189
200
|
};
|
|
190
201
|
if (lock) {
|
|
191
202
|
dbClient.query('SELECT pg_advisory_xact_lock(' + lock + ')').then(() => finish()).catch((err) => {
|
|
203
|
+
logger.error('Error acquiring advisory lock %d at %s %s: %j', lock, req.method, req.originalUrl, { err, stack: new Error().stack });
|
|
192
204
|
res.status(500).json({
|
|
193
205
|
error: {
|
|
194
206
|
errorKey: 'internal.db',
|
|
@@ -203,7 +215,7 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
203
215
|
}
|
|
204
216
|
}).catch((err) => {
|
|
205
217
|
releaseAndDecrement(repo, dbClient);
|
|
206
|
-
logger.error('Error beginning transaction: %j', err);
|
|
218
|
+
logger.error('Error beginning transaction at %s %s: %j', req.method, req.originalUrl, { err, stack: new Error().stack });
|
|
207
219
|
res.status(500).json({
|
|
208
220
|
error: {
|
|
209
221
|
errorKey: 'internal.db',
|
|
@@ -213,6 +225,7 @@ function withTransaction(repo, logger, previousMethod, lock, commitIfLost = true
|
|
|
213
225
|
});
|
|
214
226
|
}).catch(err => {
|
|
215
227
|
clearTimeout(connectTimeoutHandler);
|
|
228
|
+
logger.error('Error connecting to database at %s %s: %j', req.method, req.originalUrl, { err, stack: new Error().stack });
|
|
216
229
|
exports.withTransactionConfig.activeCount = Math.max(0, exports.withTransactionConfig.activeCount - 1);
|
|
217
230
|
res.status(500).json({
|
|
218
231
|
error: {
|