@terreno/api 0.7.0 → 0.7.1
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/dist/__tests__/versionCheck.test.d.ts +1 -0
- package/dist/__tests__/versionCheck.test.js +263 -0
- package/dist/expressServer.js +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/models/versionConfig.d.ts +17 -0
- package/dist/models/versionConfig.js +66 -0
- package/dist/terrenoApp.js +2 -2
- package/dist/vendor/wesleytodd-openapi/index.d.ts +27 -0
- package/dist/vendor/wesleytodd-openapi/index.js +176 -0
- package/dist/vendor/wesleytodd-openapi/lib/convert-yaml.d.ts +2 -0
- package/dist/vendor/wesleytodd-openapi/lib/convert-yaml.js +13 -0
- package/dist/vendor/wesleytodd-openapi/lib/generate-doc.d.ts +2 -0
- package/dist/vendor/wesleytodd-openapi/lib/generate-doc.js +148 -0
- package/dist/vendor/wesleytodd-openapi/lib/layer-schema.d.ts +2 -0
- package/dist/vendor/wesleytodd-openapi/lib/layer-schema.js +12 -0
- package/dist/vendor/wesleytodd-openapi/lib/minimum-doc.d.ts +6 -0
- package/dist/vendor/wesleytodd-openapi/lib/minimum-doc.js +11 -0
- package/dist/vendor/wesleytodd-openapi/lib/ui.d.ts +1 -0
- package/dist/vendor/wesleytodd-openapi/lib/ui.js +37 -0
- package/dist/vendor/wesleytodd-openapi/lib/validate.d.ts +2 -0
- package/dist/vendor/wesleytodd-openapi/lib/validate.js +168 -0
- package/dist/versionCheckPlugin.d.ts +15 -0
- package/dist/versionCheckPlugin.js +106 -0
- package/package.json +9 -2
- package/src/__tests__/versionCheck.test.ts +132 -0
- package/src/expressServer.ts +1 -2
- package/src/index.ts +2 -0
- package/src/models/versionConfig.ts +92 -0
- package/src/terrenoApp.ts +1 -2
- package/src/vendor/wesleytodd-openapi/LICENSE +15 -0
- package/src/vendor/wesleytodd-openapi/index.js +189 -0
- package/src/vendor/wesleytodd-openapi/lib/convert-yaml.js +13 -0
- package/src/vendor/wesleytodd-openapi/lib/generate-doc.js +153 -0
- package/src/vendor/wesleytodd-openapi/lib/layer-schema.js +13 -0
- package/src/vendor/wesleytodd-openapi/lib/minimum-doc.js +12 -0
- package/src/vendor/wesleytodd-openapi/lib/ui.js +71 -0
- package/src/vendor/wesleytodd-openapi/lib/validate.js +152 -0
- package/src/versionCheckPlugin.ts +81 -0
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Vendored from https://github.com/wesleytodd/express-openapi (branch: main / tag: v1.1.0)
|
|
2
|
+
// NOTE: This file uses the main-branch algorithm (regexp-based route traversal) rather than
|
|
3
|
+
// the express-5 branch version, because the express-5 version requires router.getRoutes()
|
|
4
|
+
// which only exists on the bjohansebas/router fork and not on the Express 5 app router.
|
|
5
|
+
// The main-branch approach works correctly with the openApiCompat.ts regexp shim.
|
|
6
|
+
// License: ISC (see ../LICENSE)
|
|
7
|
+
'use strict';
|
|
8
|
+
var __assign = (this && this.__assign) || function () {
|
|
9
|
+
__assign = Object.assign || function(t) {
|
|
10
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
11
|
+
s = arguments[i];
|
|
12
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
13
|
+
t[p] = s[p];
|
|
14
|
+
}
|
|
15
|
+
return t;
|
|
16
|
+
};
|
|
17
|
+
return __assign.apply(this, arguments);
|
|
18
|
+
};
|
|
19
|
+
var pathToRegexp = require('path-to-regexp');
|
|
20
|
+
var minimumViableDocument = require('./minimum-doc');
|
|
21
|
+
var _a = require('./layer-schema'), getSchema = _a.get, setSchema = _a.set;
|
|
22
|
+
module.exports = function generateDocument(baseDocument, router, basePath) {
|
|
23
|
+
// Merge document with select minimum defaults
|
|
24
|
+
var doc = Object.assign({
|
|
25
|
+
openapi: minimumViableDocument.openapi
|
|
26
|
+
}, baseDocument, {
|
|
27
|
+
info: Object.assign({}, minimumViableDocument.info, baseDocument.info),
|
|
28
|
+
paths: Object.assign({}, minimumViableDocument.paths, baseDocument.paths)
|
|
29
|
+
});
|
|
30
|
+
// Iterate the middleware stack and add any paths and schemas, etc
|
|
31
|
+
router && router.stack.forEach(function (_layer) {
|
|
32
|
+
iterateStack('', null, _layer, function (path, routeLayer, layer) {
|
|
33
|
+
if (basePath && path.startsWith(basePath)) {
|
|
34
|
+
path = path.replace(basePath, '');
|
|
35
|
+
}
|
|
36
|
+
var schema = getSchema(layer.handle);
|
|
37
|
+
if (!schema || !layer.method) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
var operation = Object.assign({}, schema);
|
|
41
|
+
// Add route params to schema
|
|
42
|
+
if (routeLayer && routeLayer.keys && routeLayer.keys.length) {
|
|
43
|
+
var keys_1 = {};
|
|
44
|
+
var params_1 = routeLayer.keys.map(function (k, i) {
|
|
45
|
+
var prev = i > 0 && routeLayer.keys[i - 1];
|
|
46
|
+
// do not count parameters without a name if they are next to a named parameter
|
|
47
|
+
if (typeof k.name === 'number' && prev && prev.offset + prev.name.length + 1 >= k.offset) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
var param;
|
|
51
|
+
if (schema.parameters) {
|
|
52
|
+
param = schema.parameters.find(function (p) { return p.name === k.name && p.in === 'path'; });
|
|
53
|
+
}
|
|
54
|
+
// Reformat the path
|
|
55
|
+
keys_1[k.name] = '{' + k.name + '}';
|
|
56
|
+
return Object.assign({
|
|
57
|
+
name: k.name,
|
|
58
|
+
in: 'path',
|
|
59
|
+
required: !k.optional,
|
|
60
|
+
schema: k.schema || { type: 'string' }
|
|
61
|
+
}, param || {});
|
|
62
|
+
})
|
|
63
|
+
.filter(function (e) { return e; });
|
|
64
|
+
if (schema.parameters) {
|
|
65
|
+
schema.parameters.forEach(function (p) {
|
|
66
|
+
if (!params_1.find(function (pp) { return p.name === pp.name; })) {
|
|
67
|
+
params_1.push(p);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
operation.parameters = params_1;
|
|
72
|
+
path = pathToRegexp.compile(path.replace(/\*|\(\*\)/g, '(.*)'))(keys_1, { encode: function (value) { return value; } });
|
|
73
|
+
}
|
|
74
|
+
doc.paths[path] = doc.paths[path] || {};
|
|
75
|
+
doc.paths[path][layer.method] = operation;
|
|
76
|
+
setSchema(layer.handle, operation);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
return doc;
|
|
80
|
+
};
|
|
81
|
+
function iterateStack(path, routeLayer, layer, cb) {
|
|
82
|
+
cb(path, routeLayer, layer);
|
|
83
|
+
if (layer.name === 'router') {
|
|
84
|
+
layer.handle.stack.forEach(function (l) {
|
|
85
|
+
path = path || '';
|
|
86
|
+
iterateStack(path + split(layer.regexp, layer.keys).join('/'), layer, l, cb);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (!layer.route) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(layer.route.path)) {
|
|
93
|
+
var r_1 = layer.regexp.toString();
|
|
94
|
+
layer.route.path.forEach(function (p, i) { return iterateStack(path + p, layer, __assign(__assign({}, layer), {
|
|
95
|
+
// Checking if p is a string here since p may be a regex expression
|
|
96
|
+
keys: layer.keys.filter(function (k) { return typeof p === 'string' ? p.includes("/:".concat(k.name)) : false; }),
|
|
97
|
+
// There may be an issue here if the regex has a '|', but that seems to only be the case with user defined regex
|
|
98
|
+
regexp: new RegExp("(".concat(r_1.substring(2, r_1.length - 3).split('|')[i], ")")), route: __assign(__assign({}, layer.route), { path: '' }) }), cb); });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
layer.route.stack.forEach(function (l) { return iterateStack(path + layer.route.path, layer, l, cb); });
|
|
102
|
+
}
|
|
103
|
+
function processComplexMatch(thing, keys) {
|
|
104
|
+
var i = 0;
|
|
105
|
+
return thing
|
|
106
|
+
.toString()
|
|
107
|
+
// The replace below replaces the regex used by Express to match dynamic parameters
|
|
108
|
+
// (i.e. /:id, /:name, etc...) with the name(s) of those parameter(s)
|
|
109
|
+
// This could have been accomplished with replaceAll for Node version 15 and above
|
|
110
|
+
// no-useless-escape is disabled since we need three backslashes
|
|
111
|
+
.replace(/\(\?\:\(\[\^\\\/\]\+\?\)\)/g, function () { return "{".concat(keys[i++].name, "}"); }) // eslint-disable-line no-useless-escape
|
|
112
|
+
.replace(/\\(.)/g, '$1')
|
|
113
|
+
// The replace below removes the regex used at the start of the string and
|
|
114
|
+
// the regex used to match the query parameters
|
|
115
|
+
.replace(/\/\^|\/\?(.*)/g, '')
|
|
116
|
+
.split('/');
|
|
117
|
+
}
|
|
118
|
+
// https://github.com/expressjs/express/issues/3308#issuecomment-300957572
|
|
119
|
+
function split(thing, keys) {
|
|
120
|
+
// In express v5 the router layers regexp (path-to-regexp@3.2.0)
|
|
121
|
+
// has some additional handling for end of lines, remove those
|
|
122
|
+
//
|
|
123
|
+
// layer.regexp
|
|
124
|
+
// v4 ^\\/sub-route\\/?(?=\\/|$)
|
|
125
|
+
// v5 ^\\/sub-route(?:\\/(?=$))?(?=\\/|$)
|
|
126
|
+
//
|
|
127
|
+
// l.regexp
|
|
128
|
+
// v4 ^\\/endpoint\\/?$
|
|
129
|
+
// v5 ^\\/endpoint(?:\\/)?$
|
|
130
|
+
if (typeof thing === 'string') {
|
|
131
|
+
return thing.split('/');
|
|
132
|
+
}
|
|
133
|
+
else if (thing.fast_slash) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
var match = thing
|
|
138
|
+
.toString()
|
|
139
|
+
.replace('\\/?', '')
|
|
140
|
+
.replace('(?=\\/|$)', '$')
|
|
141
|
+
// Added this line to catch the express v5 case after the v4 part is stripped off
|
|
142
|
+
.replace('(?:\\/(?=$))?$', '$')
|
|
143
|
+
.match(/^\/\^((?:\\[.*+?^${}()|[\]\\/]|[^.*+?^${}()|[\]\\/])*)\$\//);
|
|
144
|
+
return match
|
|
145
|
+
? match[1].replace(/\\(.)/g, '$1').split('/')
|
|
146
|
+
: processComplexMatch(thing, keys);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Vendored from https://github.com/wesleytodd/express-openapi (branch: express-5)
|
|
2
|
+
// License: ISC (see ../LICENSE)
|
|
3
|
+
'use strict';
|
|
4
|
+
var schemas = new Map();
|
|
5
|
+
module.exports = {
|
|
6
|
+
set: function (handler, schema) {
|
|
7
|
+
schemas.set(handler, schema);
|
|
8
|
+
},
|
|
9
|
+
get: function (handler) {
|
|
10
|
+
return schemas.get(handler);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function serveSwaggerUI(documentUrl: any, opts?: {}): any[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Vendored from https://github.com/wesleytodd/express-openapi (branch: express-5)
|
|
2
|
+
// License: ISC (see ../LICENSE)
|
|
3
|
+
'use strict';
|
|
4
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
5
|
+
var t = {};
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
9
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
10
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
11
|
+
t[p[i]] = s[p[i]];
|
|
12
|
+
}
|
|
13
|
+
return t;
|
|
14
|
+
};
|
|
15
|
+
var path = require('path');
|
|
16
|
+
var serve = require('serve-static');
|
|
17
|
+
module.exports.serveSwaggerUI = function serveSwaggerUI(documentUrl, opts) {
|
|
18
|
+
if (opts === void 0) { opts = {}; }
|
|
19
|
+
var plugins = opts.plugins, options = __rest(opts, ["plugins"]);
|
|
20
|
+
return [serve(path.resolve(require.resolve('swagger-ui-dist'), '..'), { index: false }),
|
|
21
|
+
function returnUiInit(req, res, next) {
|
|
22
|
+
if (req.path.endsWith('/swagger-ui-init.js')) {
|
|
23
|
+
res.type('.js');
|
|
24
|
+
res.send("window.onload = function () {\n window.ui = SwaggerUIBundle({\n url: '".concat(documentUrl, "',\n dom_id: '#swagger-ui',\n ").concat((plugins === null || plugins === void 0 ? void 0 : plugins.length) ? "plugins: [".concat(plugins, "],") : '', "\n ...").concat(JSON.stringify(options), "\n })\n}"));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
next();
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
function renderSwaggerHtml(req, res) {
|
|
31
|
+
res.type('html').send(renderHtmlPage('Swagger UI', "\n <link rel=\"stylesheet\" type=\"text/css\" href=\"./swagger-ui.css\" >\n ", "\n <div id=\"swagger-ui\"></div>\n <script src=\"./swagger-ui-bundle.js\"></script>\n <script src=\"./swagger-ui-standalone-preset.js\"></script>\n <script src=\"./swagger-ui-init.js\"></script>\n "));
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
};
|
|
35
|
+
function renderHtmlPage(title, head, body) {
|
|
36
|
+
return "<!DOCTYPE html>\n<html>\n <head>\n <title>".concat(title, "</title>\n <meta charset=\"utf-8\"/>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <style>\n html {\n box-sizing: border-box;\n overflow: -moz-scrollbars-vertical;\n overflow-y: scroll;\n }\n *,\n *:before,\n *:after {\n box-sizing: inherit;\n }\n body {\n margin: 0;\n padding: 0;\n background: #fafafa;\n }\n </style>\n ").concat(head, "\n </head>\n <body>\n ").concat(body, "\n </body>\n</html>\n ");
|
|
37
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Vendored from https://github.com/wesleytodd/express-openapi (branch: express-5)
|
|
2
|
+
// License: ISC (see ../LICENSE)
|
|
3
|
+
'use strict';
|
|
4
|
+
var __assign = (this && this.__assign) || function () {
|
|
5
|
+
__assign = Object.assign || function(t) {
|
|
6
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
7
|
+
s = arguments[i];
|
|
8
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
9
|
+
t[p] = s[p];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
13
|
+
return __assign.apply(this, arguments);
|
|
14
|
+
};
|
|
15
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
16
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
17
|
+
if (!m) return o;
|
|
18
|
+
var i = m.call(o), r, ar = [], e;
|
|
19
|
+
try {
|
|
20
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
21
|
+
}
|
|
22
|
+
catch (error) { e = { error: error }; }
|
|
23
|
+
finally {
|
|
24
|
+
try {
|
|
25
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
26
|
+
}
|
|
27
|
+
finally { if (e) throw e.error; }
|
|
28
|
+
}
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
var Ajv = require('ajv');
|
|
32
|
+
var addFormats = require('ajv-formats');
|
|
33
|
+
var addKeywords = require('ajv-keywords');
|
|
34
|
+
var httpErrors = require('http-errors');
|
|
35
|
+
var BASE_REQ_SCHEMA = {
|
|
36
|
+
type: 'object',
|
|
37
|
+
required: ['headers', 'params', 'query'],
|
|
38
|
+
properties: {
|
|
39
|
+
headers: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
required: [],
|
|
42
|
+
properties: {}
|
|
43
|
+
},
|
|
44
|
+
params: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
required: [],
|
|
47
|
+
properties: {}
|
|
48
|
+
},
|
|
49
|
+
query: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
required: [],
|
|
52
|
+
properties: {}
|
|
53
|
+
},
|
|
54
|
+
body: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
required: [],
|
|
57
|
+
properties: {}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
module.exports = function makeValidatorMiddleware(middleware, schema, opts) {
|
|
62
|
+
var ajv;
|
|
63
|
+
var validate;
|
|
64
|
+
function makeValidator() {
|
|
65
|
+
var reqSchema = structuredClone(BASE_REQ_SCHEMA);
|
|
66
|
+
// Compile req schema on first request
|
|
67
|
+
// Build param validation
|
|
68
|
+
schema.parameters && schema.parameters.forEach(function (p) {
|
|
69
|
+
switch (p.in) {
|
|
70
|
+
case 'path':
|
|
71
|
+
reqSchema.properties.params.properties[p.name] = p.schema;
|
|
72
|
+
p.required && !reqSchema.properties.params.required.includes(p.name) && reqSchema.properties.params.required.push(p.name);
|
|
73
|
+
break;
|
|
74
|
+
case 'query':
|
|
75
|
+
reqSchema.properties.query.properties[p.name] = p.schema;
|
|
76
|
+
p.required && !reqSchema.properties.query.required.includes(p.name) && reqSchema.properties.query.required.push(p.name);
|
|
77
|
+
break;
|
|
78
|
+
case 'header': {
|
|
79
|
+
var name_1 = p.name.toLowerCase();
|
|
80
|
+
reqSchema.properties.headers.properties[name_1] = p.schema;
|
|
81
|
+
p.required && !reqSchema.properties.headers.required.includes(p.name) && reqSchema.properties.headers.required.push(name_1);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// Compile req body schema
|
|
87
|
+
schema.requestBody && Object.entries(schema.requestBody.content)
|
|
88
|
+
.forEach(function (_a) {
|
|
89
|
+
var _b = __read(_a, 2), contentType = _b[0], schema = _b[1].schema;
|
|
90
|
+
switch (contentType) {
|
|
91
|
+
case 'application/json':
|
|
92
|
+
reqSchema.properties.body = schema;
|
|
93
|
+
break;
|
|
94
|
+
default:
|
|
95
|
+
throw new TypeError("Validation of content type not supported: ".concat(contentType));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// Add components for references
|
|
99
|
+
reqSchema.components = middleware.document && middleware.document.components;
|
|
100
|
+
return ajv.compile(reqSchema);
|
|
101
|
+
}
|
|
102
|
+
return function validateMiddleware(req, res, next) {
|
|
103
|
+
// Restrict validation to only "route" layers
|
|
104
|
+
// This prevents running any validation
|
|
105
|
+
// if we are in a .use call which could
|
|
106
|
+
// be a non-routable request thus
|
|
107
|
+
if (!req.route) {
|
|
108
|
+
return next();
|
|
109
|
+
}
|
|
110
|
+
// Create ajv instance on first request
|
|
111
|
+
if (!ajv) {
|
|
112
|
+
ajv = new Ajv({
|
|
113
|
+
coerceTypes: opts.coerce === 'false' ? opts.coerce : true,
|
|
114
|
+
strict: opts.strict === true ? opts.strict : false
|
|
115
|
+
});
|
|
116
|
+
addFormats(ajv);
|
|
117
|
+
if (middleware.router && !middleware.router.handle) {
|
|
118
|
+
return next();
|
|
119
|
+
}
|
|
120
|
+
if (opts.keywords) {
|
|
121
|
+
addKeywords(ajv, opts.keywords);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!validate) {
|
|
125
|
+
validate = makeValidator();
|
|
126
|
+
}
|
|
127
|
+
// Validate request
|
|
128
|
+
var r = req;
|
|
129
|
+
if (opts.coerce !== true) {
|
|
130
|
+
r = makeReqCopy(req);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Redifine query and params as normal objects we can write to, so we can coerce the data
|
|
134
|
+
Object.defineProperty(req, 'query', {
|
|
135
|
+
value: __assign({}, req.query),
|
|
136
|
+
writable: true,
|
|
137
|
+
enumerable: true,
|
|
138
|
+
configurable: true
|
|
139
|
+
});
|
|
140
|
+
Object.defineProperty(req, 'params', {
|
|
141
|
+
value: __assign({}, req.params),
|
|
142
|
+
writable: true,
|
|
143
|
+
enumerable: true,
|
|
144
|
+
configurable: true
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
var validationStatus = validate(r);
|
|
148
|
+
if (validationStatus === true) {
|
|
149
|
+
return next();
|
|
150
|
+
}
|
|
151
|
+
// build error?
|
|
152
|
+
var err = new Error('Request validation failed');
|
|
153
|
+
err.validationErrors = validate.errors;
|
|
154
|
+
err.validationSchema = validate.schema;
|
|
155
|
+
next(httpErrors(400, err));
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
// This is because ajv modifies the original data,
|
|
159
|
+
// preventing this requires that we dont pass the
|
|
160
|
+
// actual req. An issue has been opened (@TODO open the issue)
|
|
161
|
+
function makeReqCopy(req) {
|
|
162
|
+
return JSON.parse(JSON.stringify({
|
|
163
|
+
headers: req.headers,
|
|
164
|
+
params: req.params,
|
|
165
|
+
query: req.query,
|
|
166
|
+
body: req.body
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type express from "express";
|
|
2
|
+
import type { TerrenoPlugin } from "./terrenoPlugin";
|
|
3
|
+
export type VersionCheckStatus = "ok" | "warning" | "required";
|
|
4
|
+
export interface VersionCheckResponse {
|
|
5
|
+
message?: string;
|
|
6
|
+
status: VersionCheckStatus;
|
|
7
|
+
updateUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* TerrenoPlugin that adds a public GET /version-check endpoint for upgrade enforcement.
|
|
11
|
+
* Compares client build number against admin-configured thresholds per platform.
|
|
12
|
+
*/
|
|
13
|
+
export declare class VersionCheckPlugin implements TerrenoPlugin {
|
|
14
|
+
register(app: express.Application): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
13
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.VersionCheckPlugin = void 0;
|
|
40
|
+
var api_1 = require("./api");
|
|
41
|
+
var versionConfig_1 = require("./models/versionConfig");
|
|
42
|
+
var DEFAULT_WARNING_MESSAGE = "A new version is available. Please update for the best experience.";
|
|
43
|
+
var DEFAULT_REQUIRED_MESSAGE = "This version is no longer supported. Please update to continue.";
|
|
44
|
+
/**
|
|
45
|
+
* TerrenoPlugin that adds a public GET /version-check endpoint for upgrade enforcement.
|
|
46
|
+
* Compares client build number against admin-configured thresholds per platform.
|
|
47
|
+
*/
|
|
48
|
+
var VersionCheckPlugin = /** @class */ (function () {
|
|
49
|
+
function VersionCheckPlugin() {
|
|
50
|
+
}
|
|
51
|
+
VersionCheckPlugin.prototype.register = function (app) {
|
|
52
|
+
var _this = this;
|
|
53
|
+
app.get("/version-check", (0, api_1.asyncHandler)(function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
54
|
+
var versionParam, platform, version, platformNormalized, config, requiredVersion, warningVersion, response;
|
|
55
|
+
var _a, _b, _c, _d, _e, _f;
|
|
56
|
+
return __generator(this, function (_g) {
|
|
57
|
+
switch (_g.label) {
|
|
58
|
+
case 0:
|
|
59
|
+
versionParam = req.query.version;
|
|
60
|
+
platform = req.query.platform;
|
|
61
|
+
version = typeof versionParam === "string"
|
|
62
|
+
? parseInt(versionParam, 10)
|
|
63
|
+
: typeof versionParam === "number"
|
|
64
|
+
? versionParam
|
|
65
|
+
: undefined;
|
|
66
|
+
if (version === undefined || Number.isNaN(version)) {
|
|
67
|
+
return [2 /*return*/, res.json({ status: "ok" })];
|
|
68
|
+
}
|
|
69
|
+
platformNormalized = platform === "web" || platform === "mobile" ? platform : "web";
|
|
70
|
+
return [4 /*yield*/, versionConfig_1.VersionConfig.findOneOrNone({ _singleton: "config" })];
|
|
71
|
+
case 1:
|
|
72
|
+
config = _g.sent();
|
|
73
|
+
if (!config) {
|
|
74
|
+
return [2 /*return*/, res.json({ status: "ok" })];
|
|
75
|
+
}
|
|
76
|
+
requiredVersion = platformNormalized === "web"
|
|
77
|
+
? ((_a = config.webRequiredVersion) !== null && _a !== void 0 ? _a : 0)
|
|
78
|
+
: ((_b = config.mobileRequiredVersion) !== null && _b !== void 0 ? _b : 0);
|
|
79
|
+
warningVersion = platformNormalized === "web"
|
|
80
|
+
? ((_c = config.webWarningVersion) !== null && _c !== void 0 ? _c : 0)
|
|
81
|
+
: ((_d = config.mobileWarningVersion) !== null && _d !== void 0 ? _d : 0);
|
|
82
|
+
response = {
|
|
83
|
+
status: "ok",
|
|
84
|
+
};
|
|
85
|
+
if (requiredVersion > 0 && version < requiredVersion) {
|
|
86
|
+
response.status = "required";
|
|
87
|
+
response.message = (_e = config.requiredMessage) !== null && _e !== void 0 ? _e : DEFAULT_REQUIRED_MESSAGE;
|
|
88
|
+
if (config.updateUrl) {
|
|
89
|
+
response.updateUrl = config.updateUrl;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (warningVersion > 0 && version < warningVersion) {
|
|
93
|
+
response.status = "warning";
|
|
94
|
+
response.message = (_f = config.warningMessage) !== null && _f !== void 0 ? _f : DEFAULT_WARNING_MESSAGE;
|
|
95
|
+
if (config.updateUrl) {
|
|
96
|
+
response.updateUrl = config.updateUrl;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return [2 /*return*/, res.json(response)];
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}); }));
|
|
103
|
+
};
|
|
104
|
+
return VersionCheckPlugin;
|
|
105
|
+
}());
|
|
106
|
+
exports.VersionCheckPlugin = VersionCheckPlugin;
|
package/package.json
CHANGED
|
@@ -7,9 +7,16 @@
|
|
|
7
7
|
"@sentry/bun": "^10.25.0",
|
|
8
8
|
"@sentry/profiling-node": "^10.25.0",
|
|
9
9
|
"@types/qs": "^6.14.0",
|
|
10
|
-
"@wesleytodd/openapi": "^1.1.0",
|
|
11
10
|
"ajv": "8.18.0",
|
|
12
11
|
"ajv-formats": "^3.0.1",
|
|
12
|
+
"ajv-keywords": "^5.1.0",
|
|
13
|
+
"http-errors": "^2.0.0",
|
|
14
|
+
"path-to-regexp": "^6.3.0",
|
|
15
|
+
"router": "^2.2.0",
|
|
16
|
+
"serve-static": "^1.15.0",
|
|
17
|
+
"swagger-parser": "^10.0.3",
|
|
18
|
+
"swagger-ui-dist": "^5.11.8",
|
|
19
|
+
"yaml": "^2.4.0",
|
|
13
20
|
"axios": "^1.13.2",
|
|
14
21
|
"better-auth": "^1.2.8",
|
|
15
22
|
"cors": "^2.8.5",
|
|
@@ -93,5 +100,5 @@
|
|
|
93
100
|
"updateSnapshot": "bun test --update-snapshots"
|
|
94
101
|
},
|
|
95
102
|
"types": "dist/index.d.ts",
|
|
96
|
-
"version": "0.7.
|
|
103
|
+
"version": "0.7.1"
|
|
97
104
|
}
|