backend-manager 4.2.25 → 5.0.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/.claude/settings.local.json +8 -0
- package/CHANGELOG.md +7 -0
- package/package.json +3 -2
- package/src/manager/helpers/middleware.js +55 -15
- package/src/manager/helpers/settings.js +18 -3
- package/src/manager/index.js +10 -9
- package/src/manager/libraries/openai.js +634 -455
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
17
|
---#
|
|
18
|
+
# [5.0.0] - 2025-07-10
|
|
19
|
+
### ⚠️ BREAKING
|
|
20
|
+
- `Manager.init()` no longer wraps the initializeApp() in `try/catch` block.
|
|
21
|
+
- `Settings()` API tries to look for a method-specific file first (e.g., `name/get.js`, `name/post.js`, etc.) before falling back to `name/index.js`. This allows for more modular and organized code structure. Also, `name.js` is no longer valid, we now look for `name/index.js` this is to make it consistent with the `Middleware()` API.
|
|
22
|
+
- `Middleware()` API now tries to load method-specific files (e.g., `name/get.js`, `name/post.js`, etc.) before falling back to `name/index.js`.
|
|
23
|
+
- `ai.request()` no longer accepts `options.message.images`. Use `options.message.attachments` instead.
|
|
24
|
+
|
|
18
25
|
# [4.2.22] - 2024-12-19
|
|
19
26
|
### Changed
|
|
20
27
|
- `Manager.install()` now automatically binds the fn with the proper `this` context (this may be breaking).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
"_test": "npm run prepare && ./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
|
|
12
12
|
"test": "./node_modules/mocha/bin/mocha test/ --recursive --timeout=10000",
|
|
13
13
|
"test:usage": "./node_modules/mocha/bin/mocha test/usage.js --timeout=10000",
|
|
14
|
-
"test:payment-resolver": "./node_modules/mocha/bin/mocha test/payment-resolver.js --timeout=10000",
|
|
14
|
+
"test:payment-resolver": "./node_modules/mocha/bin/mocha test/payment-resolver/index.js --timeout=10000",
|
|
15
15
|
"test:user": "./node_modules/mocha/bin/mocha test/user.js --timeout=10000",
|
|
16
|
+
"test:ai": "./node_modules/mocha/bin/mocha test/ai/index.js --timeout=10000",
|
|
16
17
|
"start": "node src/manager/index.js",
|
|
17
18
|
"prepare": "node -e \"require('prepare-package')()\"",
|
|
18
19
|
"prepare:watch": "nodemon -w ./src -e '*' --exec 'npm run prepare'"
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const powertools = require('node-powertools');
|
|
8
8
|
const { merge } = require('lodash');
|
|
9
|
+
const JSON5 = require('json5');
|
|
9
10
|
|
|
10
11
|
function Middleware(m, req, res) {
|
|
11
12
|
const self = this;
|
|
@@ -27,17 +28,6 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
27
28
|
return cors(req, res, async () => {
|
|
28
29
|
const assistant = Manager.Assistant({req: req, res: res});
|
|
29
30
|
|
|
30
|
-
// Set properties
|
|
31
|
-
const data = assistant.request.data;
|
|
32
|
-
const headers = assistant.request.headers;
|
|
33
|
-
const method = assistant.request.method;
|
|
34
|
-
const url = assistant.request.url;
|
|
35
|
-
const geolocation = assistant.request.geolocation;
|
|
36
|
-
const client = assistant.request.client;
|
|
37
|
-
|
|
38
|
-
// Strip URL
|
|
39
|
-
const strippedUrl = stripUrl(url);
|
|
40
|
-
|
|
41
31
|
// Set options
|
|
42
32
|
options = options || {};
|
|
43
33
|
options.authenticate = typeof options.authenticate === 'boolean' ? options.authenticate : true;
|
|
@@ -47,11 +37,42 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
47
37
|
options.cleanSettings = typeof options.cleanSettings === 'undefined' ? true : options.cleanSettings;
|
|
48
38
|
options.includeNonSchemaSettings = typeof options.includeNonSchemaSettings === 'undefined' ? false : options.includeNonSchemaSettings;
|
|
49
39
|
options.schema = typeof options.schema === 'undefined' ? undefined : options.schema;
|
|
40
|
+
options.parseMultipartFormData = typeof options.parseMultipartFormData === 'undefined' ? true : options.parseMultipartFormData;
|
|
50
41
|
|
|
51
42
|
// Set base path
|
|
52
43
|
options.routesDir = typeof options.routesDir === 'undefined' ? `${Manager.cwd}/routes` : options.routesDir;
|
|
53
44
|
options.schemasDir = typeof options.schemasDir === 'undefined' ? `${Manager.cwd}/schemas` : options.schemasDir;
|
|
54
45
|
|
|
46
|
+
// Parse multipart/form-data if needed
|
|
47
|
+
if (options.parseMultipartFormData && req.headers['content-type']?.includes('multipart/form-data')) {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = await assistant.parseMultipartFormData();
|
|
50
|
+
const json = parsed.fields.json || '{}';
|
|
51
|
+
|
|
52
|
+
// Parsed JSON
|
|
53
|
+
if (json) {
|
|
54
|
+
assistant.request.body = JSON5.parse(json);
|
|
55
|
+
assistant.request.data = merge({}, assistant.request.body, assistant.request.query);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Log that it was parsed successfully
|
|
59
|
+
assistant.log(`Middleware.run(): Parsed multipart form data successfully`);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return assistant.respond(new Error(`Failed to parse multipart form data: ${e.message}`), {code: 400, sentry: true});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Set properties
|
|
66
|
+
const data = assistant.request.data;
|
|
67
|
+
const headers = assistant.request.headers;
|
|
68
|
+
const method = assistant.request.method.toLowerCase();
|
|
69
|
+
const url = assistant.request.url;
|
|
70
|
+
const geolocation = assistant.request.geolocation;
|
|
71
|
+
const client = assistant.request.client;
|
|
72
|
+
|
|
73
|
+
// Strip URL
|
|
74
|
+
const strippedUrl = stripUrl(url);
|
|
75
|
+
|
|
55
76
|
// Log
|
|
56
77
|
assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city}) [${method} > ${strippedUrl}]`, JSON.stringify(data));
|
|
57
78
|
assistant.log(`Middleware.process(): Headers`, JSON.stringify(headers));
|
|
@@ -68,11 +89,24 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
// Load library
|
|
71
|
-
//
|
|
92
|
+
// Support for methods, so first check for method specific file then fallback to index.js
|
|
72
93
|
let library;
|
|
94
|
+
|
|
73
95
|
try {
|
|
74
|
-
|
|
75
|
-
|
|
96
|
+
// First try to load method-specific file (e.g., get.js, post.js, etc.)
|
|
97
|
+
const methodFile = `${method}.js`
|
|
98
|
+
const methodFilePath = path.resolve(routesDir, methodFile);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const finalPath = methodFilePath;
|
|
102
|
+
library = new (require(finalPath))();
|
|
103
|
+
assistant.log(`Middleware.process(): Loaded method-specific library: ${methodFile}`);
|
|
104
|
+
} catch (methodError) {
|
|
105
|
+
// Fallback to index.js if method-specific file doesn't exist or fails to load
|
|
106
|
+
const finalPath = path.resolve(routesDir, `index.js`);
|
|
107
|
+
library = new (require(finalPath))();
|
|
108
|
+
assistant.log(`Middleware.process(): Method-specific file (${methodFile}) not found, using index.js fallback`);
|
|
109
|
+
}
|
|
76
110
|
} catch (e) {
|
|
77
111
|
return assistant.respond(new Error(`Unable to load library @ (${libPath}): ${e.message}`), {code: 500, sentry: true});
|
|
78
112
|
}
|
|
@@ -113,7 +147,7 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
113
147
|
// assistant.schema.name = options.schema;
|
|
114
148
|
assistant.settings = Manager.Settings().resolve(assistant, undefined, data, {dir: schemasDir, schema: options.schema});
|
|
115
149
|
} catch (e) {
|
|
116
|
-
return assistant.respond(new Error(`Unable to resolve schema ${options.schema}: ${e.message}`), {code: 500, sentry: true});
|
|
150
|
+
return assistant.respond(new Error(`Unable to resolve schema ${options.schema}: ${e.message}`), {code: e.code || 500, sentry: true});
|
|
117
151
|
}
|
|
118
152
|
|
|
119
153
|
// Merge settings with data
|
|
@@ -128,6 +162,12 @@ Middleware.prototype.run = function (libPath, options) {
|
|
|
128
162
|
|
|
129
163
|
// Log
|
|
130
164
|
assistant.log(`Middleware.process(): Resolved settings with schema=${options.schema}`, JSON.stringify(assistant.settings));
|
|
165
|
+
|
|
166
|
+
// Log multipart files if they exist
|
|
167
|
+
const files = assistant.request.multipartData.files || {};
|
|
168
|
+
if (files) {
|
|
169
|
+
assistant.log(`Middleware.process(): Multipart files`, JSON.stringify(files));
|
|
170
|
+
}
|
|
131
171
|
} else {
|
|
132
172
|
assistant.settings = data;
|
|
133
173
|
}
|
|
@@ -42,9 +42,24 @@ Settings.prototype.resolve = function (assistant, schema, settings, options) {
|
|
|
42
42
|
typeof schema === 'undefined'
|
|
43
43
|
&& typeof options.schema !== 'undefined'
|
|
44
44
|
) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// Try to load method-specific schema first, then fallback to main schema
|
|
46
|
+
const method = (assistant?.request?.method || '').toLowerCase();
|
|
47
|
+
const methodFile = `${method}.js`;
|
|
48
|
+
const schemaFile = options.schema.replace('.js', '');
|
|
49
|
+
let schemaPath;
|
|
50
|
+
|
|
51
|
+
// First try method-specific schema (e.g., test/get.js, test/post.js)
|
|
52
|
+
const methodSchemaPath = path.resolve(options.dir, `${schemaFile}/${methodFile}`);
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
schema = loadSchema(assistant, methodSchemaPath, settings, options);
|
|
56
|
+
assistant.log(`Settings.resolve(): Loaded method-specific schema: ${schemaFile}/${methodFile}`);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
// Fallback to main schema if method-specific doesn't exist
|
|
59
|
+
schemaPath = path.resolve(options.dir, `${schemaFile}/index.js`);
|
|
60
|
+
schema = loadSchema(assistant, schemaPath, settings, options);
|
|
61
|
+
assistant.log(`Settings.resolve(): Method-specific schema not found, using main schema fallback`);
|
|
62
|
+
}
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
// If schema is not an object, throw an error
|
package/src/manager/index.js
CHANGED
|
@@ -16,7 +16,7 @@ const wrappers = './functions/wrappers';
|
|
|
16
16
|
|
|
17
17
|
const BEM_CONFIG_TEMPLATE_PATH = path.resolve(__dirname, '../../templates/backend-manager-config.json');
|
|
18
18
|
|
|
19
|
-
function Manager(
|
|
19
|
+
function Manager() {
|
|
20
20
|
const self = this;
|
|
21
21
|
|
|
22
22
|
// Constants
|
|
@@ -64,7 +64,9 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
64
64
|
options.useFirebaseLogger = typeof options.useFirebaseLogger === 'undefined' ? true : options.useFirebaseLogger;
|
|
65
65
|
options.serviceAccountPath = typeof options.serviceAccountPath === 'undefined' ? 'service-account.json' : options.serviceAccountPath;
|
|
66
66
|
options.backendManagerConfigPath = typeof options.backendManagerConfigPath === 'undefined' ? 'backend-manager-config.json' : options.backendManagerConfigPath;
|
|
67
|
-
options.fetchStats = typeof options.fetchStats === 'undefined'
|
|
67
|
+
options.fetchStats = typeof options.fetchStats === 'undefined'
|
|
68
|
+
? options.projectType === 'firebase'
|
|
69
|
+
: options.fetchStats;
|
|
68
70
|
options.checkNodeVersion = typeof options.checkNodeVersion === 'undefined' ? true : options.checkNodeVersion;
|
|
69
71
|
options.uniqueAppName = options.uniqueAppName || undefined;
|
|
70
72
|
options.assistant = options.assistant || {};
|
|
@@ -167,10 +169,9 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
167
169
|
|
|
168
170
|
// Handle dev environments
|
|
169
171
|
if (self.assistant.isDevelopment()) {
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const semverRequired = semverMajor(semverCoerce(self.package?.engines?.node || '0.0.0'));
|
|
172
|
+
const version = require('wonderful-version');
|
|
173
|
+
const nodeUsing = version.major(process.versions.node);
|
|
174
|
+
const nodeRequired = version.major(self.package?.engines?.node || '0.0.0');
|
|
174
175
|
|
|
175
176
|
// Fix firebase-tools overwriting console.log
|
|
176
177
|
// https://stackoverflow.com/questions/56026747/firebase-console-log-on-localhost
|
|
@@ -182,9 +183,9 @@ Manager.prototype.init = function (exporter, options) {
|
|
|
182
183
|
console.info = logFix;
|
|
183
184
|
}
|
|
184
185
|
|
|
185
|
-
// Reject if
|
|
186
|
-
if (
|
|
187
|
-
const msg = `Node.js version mismatch: using ${
|
|
186
|
+
// Reject if we're using an unsupported Node.js version
|
|
187
|
+
if (version.is(nodeUsing, '<', nodeRequired)) {
|
|
188
|
+
const msg = `Node.js version mismatch: using ${nodeUsing} but asked for ${nodeRequired}`;
|
|
188
189
|
if (options.checkNodeVersion) {
|
|
189
190
|
self.assistant.error(new Error(msg));
|
|
190
191
|
return process.exit(1);
|