@cocreate/crud-server 1.36.4 → 1.37.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/.github/workflows/automated.yml +25 -38
- package/CHANGELOG.md +31 -0
- package/package.json +7 -8
- package/release.config.js +12 -4
- package/src/index.js +341 -229
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
name: Automated Workflow
|
|
2
|
+
|
|
2
3
|
on:
|
|
3
4
|
push:
|
|
4
5
|
branches:
|
|
5
6
|
- master
|
|
7
|
+
|
|
6
8
|
jobs:
|
|
7
9
|
about:
|
|
8
10
|
runs-on: ubuntu-latest
|
|
@@ -18,52 +20,37 @@ jobs:
|
|
|
18
20
|
with:
|
|
19
21
|
direction: overwrite-github
|
|
20
22
|
githubToken: "${{ secrets.GITHUB }}"
|
|
23
|
+
|
|
21
24
|
release:
|
|
22
25
|
runs-on: ubuntu-latest
|
|
23
26
|
steps:
|
|
24
27
|
- name: Checkout
|
|
25
|
-
uses: actions/checkout@
|
|
26
|
-
- name: Setup Node.js
|
|
27
|
-
uses: actions/setup-node@v3
|
|
28
|
+
uses: actions/checkout@v4
|
|
28
29
|
with:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
fetch-depth: 0 # Required so semantic-release can trace git tags/history
|
|
31
|
+
|
|
32
|
+
- name: Setup Node.js
|
|
33
|
+
uses: actions/setup-node@v4
|
|
33
34
|
with:
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
node-version: 22
|
|
36
|
+
|
|
37
|
+
- name: Install Semantic Release & Plugins
|
|
38
|
+
# Installs semantic-release and its plugins on the runner
|
|
39
|
+
run: |
|
|
40
|
+
npm install -g semantic-release \
|
|
41
|
+
@semantic-release/changelog \
|
|
42
|
+
@semantic-release/npm \
|
|
43
|
+
@semantic-release/github \
|
|
36
44
|
@semantic-release/git
|
|
37
|
-
|
|
45
|
+
|
|
46
|
+
- name: Run Semantic Release (Native)
|
|
47
|
+
id: semantic
|
|
48
|
+
# This will automatically pick up your export default config file in the repository root
|
|
49
|
+
run: npx semantic-release
|
|
38
50
|
env:
|
|
39
|
-
GITHUB_TOKEN: "${{ secrets.
|
|
51
|
+
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
40
52
|
NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
|
|
53
|
+
|
|
41
54
|
outputs:
|
|
42
55
|
new_release_published: "${{ steps.semantic.outputs.new_release_published }}"
|
|
43
|
-
new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
|
|
44
|
-
upload:
|
|
45
|
-
runs-on: ubuntu-latest
|
|
46
|
-
needs: release
|
|
47
|
-
if: needs.release.outputs.new_release_published == 'true'
|
|
48
|
-
env:
|
|
49
|
-
VERSION: "${{ needs.release.outputs.new_release_version }}"
|
|
50
|
-
steps:
|
|
51
|
-
- name: Checkout
|
|
52
|
-
uses: actions/checkout@v3
|
|
53
|
-
- name: Setup Node.js
|
|
54
|
-
uses: actions/setup-node@v3
|
|
55
|
-
with:
|
|
56
|
-
node-version: 16
|
|
57
|
-
- name: Set npm registry auth
|
|
58
|
-
run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
|
59
|
-
- name: Install dependencies
|
|
60
|
-
run: yarn install
|
|
61
|
-
|
|
62
|
-
- name: Set Environment Variables
|
|
63
|
-
run: |
|
|
64
|
-
echo "organization_id=${{ secrets.COCREATE_ORGANIZATION_ID }}" >> $GITHUB_ENV
|
|
65
|
-
echo "key=${{ secrets.COCREATE_KEY }}" >> $GITHUB_ENV
|
|
66
|
-
echo "host=${{ secrets.COCREATE_HOST }}" >> $GITHUB_ENV
|
|
67
|
-
- name: CoCreate Upload
|
|
68
|
-
run: coc upload
|
|
69
|
-
|
|
56
|
+
new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
## [1.37.1](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.37.0...v1.37.1) (2026-07-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* bump dependencies ([4087f75](https://github.com/CoCreate-app/CoCreate-crud-server/commit/4087f759b76143cb65b8d756d02b69863b2f8432))
|
|
7
|
+
|
|
8
|
+
# [1.37.0](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.36.5...v1.37.0) (2026-07-17)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* remove unused dependencies from package.json ([4669a10](https://github.com/CoCreate-app/CoCreate-crud-server/commit/4669a10c8b30fdedb3e6bf4d84f76da0aa2b44d8))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* update automated workflow and release configuration for improved semantic release handling ([984eed3](https://github.com/CoCreate-app/CoCreate-crud-server/commit/984eed3507d460375a0036df358b8223bef22093))
|
|
19
|
+
|
|
20
|
+
## [1.36.5](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.36.4...v1.36.5) (2026-07-14)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* remove unnecessary console logs in init function for cleaner output ([d117235](https://github.com/CoCreate-app/CoCreate-crud-server/commit/d117235ade6687ca3d0d3aa5fee0e73a6b70dfb4))
|
|
26
|
+
* removed post install ([7925526](https://github.com/CoCreate-app/CoCreate-crud-server/commit/7925526070819bdd52eb342cd4c2f50e833afffb))
|
|
27
|
+
* semantic version handling. Reorganize .gitignore for improved clarity and structure ([ef31939](https://github.com/CoCreate-app/CoCreate-crud-server/commit/ef319399b78eafddde3b1e47a21343ffe76424e6))
|
|
28
|
+
* update module export to ES6 syntax in release.config.js ([c550e3d](https://github.com/CoCreate-app/CoCreate-crud-server/commit/c550e3d09533b14ff3489e78e680507ebbe64735))
|
|
29
|
+
* update organization_id assignment in init function for correct server configuration ([2ac9f3d](https://github.com/CoCreate-app/CoCreate-crud-server/commit/2ac9f3dc57552cf6821f6b7aefb45f4dc23fc615))
|
|
30
|
+
* update package.json and refactor index.js for module compatibility ([03f7447](https://github.com/CoCreate-app/CoCreate-crud-server/commit/03f7447bd2a395191593f3d5362fed19b08fa931))
|
|
31
|
+
|
|
1
32
|
## [1.36.4](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.36.3...v1.36.4) (2025-10-08)
|
|
2
33
|
|
|
3
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/crud-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.37.1",
|
|
4
4
|
"description": "CoCreate-crud-server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cocreate-crud",
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"demo": "PORT=5000 node demo/server.js",
|
|
20
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
-
"postinstall": "node -e \"const { execSync } = require('child_process'); try { execSync('coc --version', { stdio: 'ignore' }); } catch (error) { try { execSync('npm install -g @cocreate/cli', { stdio: 'inherit' }); console.log('Installed \"@cocreate/cli\" globally.'); } catch (error) { console.error('Failed to install \"@cocreate/cli\" globally:', error); } }\""
|
|
20
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
22
21
|
},
|
|
23
|
-
"
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./src/index.js",
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
26
|
"url": "git+https://github.com/CoCreate-app/CoCreate-crud-server.git"
|
|
@@ -32,9 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://cocreate.app/docs/CoCreate-crud-server",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@cocreate/config": "^1.
|
|
36
|
-
"@cocreate/
|
|
37
|
-
"
|
|
38
|
-
"json-2-csv": "^3.10.3"
|
|
35
|
+
"@cocreate/config": "^1.14.0",
|
|
36
|
+
"@cocreate/socket-server": "^1.31.1",
|
|
37
|
+
"@cocreate/utils": "^1.44.0"
|
|
39
38
|
}
|
|
40
39
|
}
|
package/release.config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export default {
|
|
2
2
|
dryRun: false,
|
|
3
3
|
branches: ["master"],
|
|
4
4
|
plugins: [
|
|
@@ -10,12 +10,20 @@ module.exports = {
|
|
|
10
10
|
changelogFile: "CHANGELOG.md",
|
|
11
11
|
},
|
|
12
12
|
],
|
|
13
|
-
"@semantic-release/npm",
|
|
14
|
-
"@semantic-release/github",
|
|
13
|
+
"@semantic-release/npm",
|
|
15
14
|
[
|
|
16
|
-
"@semantic-release/
|
|
15
|
+
"@semantic-release/github",
|
|
17
16
|
{
|
|
17
|
+
successComment: false,
|
|
18
|
+
failTitle: false,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"@semantic-release/git",
|
|
23
|
+
{
|
|
24
|
+
// Only stage and commit the changelog and package.json
|
|
18
25
|
assets: ["CHANGELOG.md", "package.json"],
|
|
26
|
+
message: "chore(release): ${nextRelease.version} [skip ci]",
|
|
19
27
|
},
|
|
20
28
|
],
|
|
21
29
|
],
|
package/src/index.js
CHANGED
|
@@ -1,297 +1,409 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2023 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* Wu under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
********************************************************************************/
|
|
18
|
+
|
|
19
|
+
// Commercial Licensing Information:
|
|
20
|
+
// For commercial use of this software without the copyleft provisions of the AGPLv3,
|
|
21
|
+
// you must obtain a commercial license from CoCreate LLC.
|
|
22
|
+
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
23
|
+
|
|
24
|
+
import { ObjectId, sortData } from "@cocreate/utils";
|
|
25
|
+
import Config from '@cocreate/config';
|
|
26
|
+
|
|
27
|
+
// In-process memory caches and storage registers (strictly module-internal)
|
|
28
|
+
const providers = {};
|
|
29
|
+
const organizations = {};
|
|
30
|
+
const hosts = {};
|
|
31
|
+
let config = null;
|
|
32
|
+
let initialized = false;
|
|
33
|
+
let wsManager = null; // Extracted from Server and bound directly for real-time socket communications
|
|
34
|
+
|
|
35
|
+
// Main service contract structure returned as default
|
|
36
|
+
const crudServer = {
|
|
37
|
+
init,
|
|
38
|
+
send,
|
|
39
|
+
getOrganization
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Async initialization loop. Prompts and loads platform configuration guidelines,
|
|
44
|
+
* dynamically registers active drivers, and binds global WebSocket endpoint listeners.
|
|
45
|
+
* @param {Object} Server - CoCreateServer context instance.
|
|
46
|
+
* @returns {Promise<Object>} This fully initialized crudServer module context
|
|
47
|
+
*/
|
|
48
|
+
export async function init(Server) {
|
|
49
|
+
if (initialized) return crudServer;
|
|
50
|
+
initialized = true;
|
|
51
|
+
|
|
52
|
+
if (Server && Server.wsManager) {
|
|
53
|
+
wsManager = Server.wsManager;
|
|
14
54
|
}
|
|
15
55
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'storage
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'storage.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'storage
|
|
27
|
-
|
|
56
|
+
config = await Config({
|
|
57
|
+
'organization_id': { prompt: 'Enter your organization_id: ' },
|
|
58
|
+
'storage': {
|
|
59
|
+
prompt: 'Enter a friendly name for the new storage (e.g., primary): ',
|
|
60
|
+
variable: 'name'
|
|
61
|
+
},
|
|
62
|
+
'storage.{{name}}.provider': {
|
|
63
|
+
prompt: 'Enter the storage provider (e.g., mongodb): '
|
|
64
|
+
},
|
|
65
|
+
'storage.{{name}}.url': {
|
|
66
|
+
prompt: 'Enter the storage connection url: '
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
let storageUrl;
|
|
71
|
+
let firstStorageKey;
|
|
72
|
+
|
|
73
|
+
if (config.storage) {
|
|
74
|
+
if (typeof config.storage === 'string') {
|
|
75
|
+
config.storage = JSON.parse(config.storage);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Dynamically resolve the first declared storage block in the configuration (order-preserved)
|
|
79
|
+
const storageKeys = Object.keys(config.storage);
|
|
80
|
+
if (storageKeys.length > 0) {
|
|
81
|
+
firstStorageKey = storageKeys[0];
|
|
82
|
+
storageUrl = config.storage[firstStorageKey].url;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (config.organization_id) {
|
|
87
|
+
Server.organization_id = config.organization_id;
|
|
88
|
+
} else {
|
|
89
|
+
console.log('[@cocreate/crud-server] Could not find the organization_id');
|
|
90
|
+
}
|
|
91
|
+
if (!storageUrl) {
|
|
92
|
+
console.log(`[@cocreate/crud-server] Could not find a connection url in your primary storage: "${firstStorageKey || 'default'}"`);
|
|
93
|
+
}
|
|
94
|
+
if (!storageUrl || !config.organization_id) {
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Dynamically import and register drivers configured on the server
|
|
99
|
+
if (config.storage && providers) {
|
|
100
|
+
for (const [friendlyName, storageConfig] of Object.entries(config.storage)) {
|
|
101
|
+
const { provider } = storageConfig;
|
|
102
|
+
|
|
103
|
+
if (provider) {
|
|
104
|
+
// Convention over Configuration: fallback to @cocreate/provider if explicit import is omitted
|
|
105
|
+
const importPath = storageConfig.import || `@cocreate/${provider}`;
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const driverModule = await import(importPath);
|
|
109
|
+
providers[provider] = driverModule.default || driverModule;
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error(`[@cocreate/crud-server] Failed to dynamically load database driver "${importPath}" for provider "${provider}":`, err);
|
|
112
|
+
}
|
|
28
113
|
}
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
let storageUrl
|
|
32
|
-
if (this.config.storage) {
|
|
33
|
-
if (typeof this.config.storage === 'string')
|
|
34
|
-
this.config.storage = JSON.parse(this.config.storage)
|
|
35
|
-
let defaultStorage = Object.keys(this.config.storage)
|
|
36
|
-
storageUrl = this.config.storage[defaultStorage[0]].url
|
|
37
114
|
}
|
|
115
|
+
}
|
|
38
116
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
117
|
+
// Deferred WebSocket Listener Auto-Registration
|
|
118
|
+
const bindSocketListeners = () => {
|
|
119
|
+
// Dynamically check and capture wsManager from Server if it was not resolved during immediate boot
|
|
120
|
+
if (!wsManager && Server && Server.wsManager) {
|
|
121
|
+
wsManager = Server.wsManager;
|
|
122
|
+
}
|
|
45
123
|
|
|
46
|
-
if (
|
|
124
|
+
if (wsManager) {
|
|
47
125
|
const type = ['storage', 'database', 'array', 'index', 'object'];
|
|
48
126
|
const method = ['create', 'read', 'update', 'delete'];
|
|
49
127
|
|
|
50
128
|
for (let i = 0; i < type.length; i++) {
|
|
51
129
|
for (let j = 0; j < method.length; j++) {
|
|
52
130
|
const action = type[i] + '.' + method[j];
|
|
53
|
-
|
|
54
|
-
this.send(data))
|
|
131
|
+
wsManager.on(action, (data) => send(data));
|
|
55
132
|
}
|
|
56
133
|
}
|
|
134
|
+
} else {
|
|
135
|
+
// Check back in 500ms if the socket server is still resolving its connection handshake
|
|
136
|
+
setTimeout(bindSocketListeners, 500);
|
|
57
137
|
}
|
|
58
|
-
}
|
|
138
|
+
};
|
|
59
139
|
|
|
60
|
-
|
|
61
|
-
return new Promise(async (resolve) => {
|
|
62
|
-
try {
|
|
63
|
-
if (!data.organization_id || !this.config) {
|
|
64
|
-
data.error = 'Missing an organization_id'
|
|
65
|
-
return resolve(data)
|
|
66
|
-
}
|
|
140
|
+
bindSocketListeners();
|
|
67
141
|
|
|
68
|
-
|
|
142
|
+
return crudServer;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Resolves metadata, identifies target drivers, processes sync limits, and executes query logic.
|
|
147
|
+
*/
|
|
148
|
+
export async function send(data) {
|
|
149
|
+
return new Promise(async (resolve) => {
|
|
150
|
+
try {
|
|
151
|
+
if (!data.organization_id || !config) {
|
|
152
|
+
data.error = 'Missing an organization_id';
|
|
153
|
+
return resolve(data);
|
|
154
|
+
}
|
|
69
155
|
|
|
70
|
-
|
|
71
|
-
return resolve(organization)
|
|
156
|
+
let organization = await getOrganization(data);
|
|
72
157
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
158
|
+
if (organization.error) {
|
|
159
|
+
return resolve(organization);
|
|
160
|
+
}
|
|
76
161
|
|
|
77
|
-
|
|
78
|
-
|
|
162
|
+
let storages = organization.storage;
|
|
163
|
+
if (!storages) {
|
|
164
|
+
return resolve({ serverStorage: false, error: 'A storage url could not be found' });
|
|
165
|
+
}
|
|
79
166
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
167
|
+
if (!data['timeStamp']) {
|
|
168
|
+
data['timeStamp'] = new Date().toISOString();
|
|
169
|
+
}
|
|
83
170
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
else if (data.array === 'userssss')
|
|
89
|
-
syncKeys = ['name', 'email', 'password', 'avatar']
|
|
171
|
+
if (data.array) {
|
|
172
|
+
if (!data.database) {
|
|
173
|
+
data['database'] = data.organization_id;
|
|
174
|
+
}
|
|
90
175
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
176
|
+
if (data.method === 'object.update' && data.organization_id !== config.organization_id) {
|
|
177
|
+
let syncKeys;
|
|
178
|
+
if (data.array === 'organizations') {
|
|
179
|
+
syncKeys = ['name', 'logo', 'storage', 'host'];
|
|
180
|
+
} else if (data.array === 'userssss') {
|
|
181
|
+
syncKeys = ['name', 'email', 'password', 'avatar'];
|
|
182
|
+
}
|
|
98
183
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
184
|
+
if (syncKeys && syncKeys.length) {
|
|
185
|
+
let platformUpdate = {
|
|
186
|
+
database: config.organization_id,
|
|
187
|
+
array: data.array,
|
|
188
|
+
object: [{}],
|
|
189
|
+
organization_id: config.organization_id
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
let object = data.object[0] || data.object;
|
|
193
|
+
if (object) {
|
|
194
|
+
for (let key of syncKeys) {
|
|
195
|
+
if (object[key]) {
|
|
196
|
+
platformUpdate.object[0][key] = object[key];
|
|
104
197
|
}
|
|
105
198
|
}
|
|
106
|
-
|
|
107
|
-
// this.send(platformUpdate)
|
|
108
199
|
}
|
|
109
200
|
}
|
|
110
201
|
}
|
|
202
|
+
}
|
|
111
203
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
data.database = host[i].database
|
|
125
|
-
if (host[i].array)
|
|
126
|
-
data.array = host[i].array
|
|
127
|
-
break
|
|
204
|
+
if (organization.host) {
|
|
205
|
+
let host = organization.host;
|
|
206
|
+
for (let i = 0; i < host.length; i++) {
|
|
207
|
+
let hostname;
|
|
208
|
+
if (data.socket) {
|
|
209
|
+
hostname = data.socket.host;
|
|
210
|
+
} else {
|
|
211
|
+
hostname = data.host;
|
|
212
|
+
}
|
|
213
|
+
if (host[i].name === hostname) {
|
|
214
|
+
if (host[i].storage) {
|
|
215
|
+
data.storage = host[i].storage;
|
|
128
216
|
}
|
|
217
|
+
if (host[i].database) {
|
|
218
|
+
data.database = host[i].database;
|
|
219
|
+
}
|
|
220
|
+
if (host[i].array) {
|
|
221
|
+
data.array = host[i].array;
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
129
224
|
}
|
|
130
225
|
}
|
|
226
|
+
}
|
|
131
227
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
228
|
+
// Symmetrical resolution - Fallback to first configured key if data.storage is omitted
|
|
229
|
+
if (!data.storage || !data.storage.length) {
|
|
230
|
+
if (Array.isArray(storages)) {
|
|
231
|
+
data.storage = storages[0].name;
|
|
232
|
+
} else if (typeof storages === 'object' && storages !== null) {
|
|
233
|
+
data.storage = [Object.keys(storages)[0]];
|
|
137
234
|
}
|
|
235
|
+
}
|
|
138
236
|
|
|
139
|
-
|
|
140
|
-
|
|
237
|
+
if (!Array.isArray(data.storage)) {
|
|
238
|
+
data.storage = [data.storage];
|
|
239
|
+
}
|
|
141
240
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
241
|
+
for (let i = 0; i < data.storage.length; i++) {
|
|
242
|
+
let storage;
|
|
243
|
+
if (Array.isArray(storages)) {
|
|
244
|
+
storage = storages.find((obj) => obj.name === data.storage[i]);
|
|
245
|
+
} else if (typeof storages === 'object' && storages !== null) {
|
|
246
|
+
storage = storages[data.storage[i]];
|
|
247
|
+
}
|
|
149
248
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
249
|
+
if (storage) {
|
|
250
|
+
if (storage.provider && providers[storage.provider]) {
|
|
251
|
+
if (!Array.isArray(storage.url)) {
|
|
252
|
+
storage.url = [storage.url];
|
|
253
|
+
}
|
|
154
254
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
255
|
+
for (let j = 0; j < storage.url.length; j++) {
|
|
256
|
+
data['storageName'] = data.storage[i];
|
|
257
|
+
data['storageUrl'] = storage.url[j];
|
|
158
258
|
|
|
159
|
-
|
|
160
|
-
|
|
259
|
+
data = await providers[storage.provider].send(data);
|
|
260
|
+
}
|
|
161
261
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!data.$filter.index)
|
|
167
|
-
data.$filter.index = 0
|
|
168
|
-
data.$filter.startingIndex = data.$filter.index
|
|
169
|
-
data.$filter.index += data[type].length
|
|
262
|
+
if (data.$filter) {
|
|
263
|
+
let type = data.method.split(".")[0];
|
|
264
|
+
if (data.$filter.sort && data.$filter.sort.length) {
|
|
265
|
+
data[type] = sortData(data[type], data.$filter.sort);
|
|
170
266
|
}
|
|
171
|
-
|
|
267
|
+
if (!data.$filter.index) {
|
|
268
|
+
data.$filter.index = 0;
|
|
269
|
+
}
|
|
270
|
+
data.$filter.startingIndex = data.$filter.index;
|
|
271
|
+
data.$filter.index += data[type].length;
|
|
172
272
|
}
|
|
173
273
|
}
|
|
174
274
|
}
|
|
275
|
+
}
|
|
175
276
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (data.socket) {
|
|
180
|
-
if (data.organization_id === this.config.organization_id && data.socket.organization_id !== data.organization_id) {
|
|
181
|
-
this.wsManager.send({
|
|
182
|
-
config: {
|
|
183
|
-
organization_id: this.config.organization_id,
|
|
184
|
-
}
|
|
185
|
-
}, { ...data });
|
|
186
|
-
data.organization_id = data.socket.organization_id
|
|
187
|
-
}
|
|
277
|
+
delete data.storageUrl;
|
|
278
|
+
delete data.storageName;
|
|
188
279
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
280
|
+
if (data.socket && wsManager) {
|
|
281
|
+
if (data.organization_id === config.organization_id && data.socket.organization_id !== data.organization_id) {
|
|
282
|
+
wsManager.send({
|
|
283
|
+
config: {
|
|
284
|
+
organization_id: config.organization_id,
|
|
285
|
+
}
|
|
286
|
+
}, { ...data });
|
|
287
|
+
data.organization_id = data.socket.organization_id;
|
|
197
288
|
}
|
|
198
289
|
|
|
199
|
-
|
|
290
|
+
wsManager.send(data);
|
|
291
|
+
process.emit('crud-event', data);
|
|
292
|
+
}
|
|
293
|
+
resolve(data);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (data.socket && wsManager) {
|
|
296
|
+
errorHandler(data, error);
|
|
297
|
+
wsManager.send(data);
|
|
200
298
|
}
|
|
201
|
-
|
|
299
|
+
resolve(data);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Local cached tenant configuration analyzer.
|
|
306
|
+
* Incorporates high-efficiency local V8 Promise coalescing alongside cross-process workerStorage checks.
|
|
307
|
+
*/
|
|
308
|
+
export async function getOrganization(data) {
|
|
309
|
+
if (hosts[data.host]) {
|
|
310
|
+
return await hosts[data.host];
|
|
311
|
+
} else if (organizations[data.organization_id]) {
|
|
312
|
+
return await organizations[data.organization_id];
|
|
313
|
+
} else {
|
|
314
|
+
if (data.organization_id) {
|
|
315
|
+
organizations[data.organization_id] = getOrg(data);
|
|
316
|
+
organizations[data.organization_id] = await organizations[data.organization_id];
|
|
317
|
+
return organizations[data.organization_id];
|
|
318
|
+
} else if (data.host) {
|
|
319
|
+
hosts[data.host] = await getOrg(data);
|
|
320
|
+
return hosts[data.host];
|
|
321
|
+
}
|
|
202
322
|
}
|
|
323
|
+
}
|
|
203
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Database-level organization retriever.
|
|
327
|
+
*/
|
|
328
|
+
async function getOrg(data) {
|
|
329
|
+
let query = {
|
|
330
|
+
method: 'object.read',
|
|
331
|
+
host: data.host,
|
|
332
|
+
database: config.organization_id,
|
|
333
|
+
array: 'organizations',
|
|
334
|
+
organization_id: config.organization_id
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
if (organizations[data.organization_id]) {
|
|
338
|
+
data.database = data.organization_id = data.organization_id;
|
|
339
|
+
}
|
|
204
340
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return this.organizations[data.organization_id]
|
|
215
|
-
} else if (data.host) {
|
|
216
|
-
// this.hosts[data.host] = this.getOrg(data)
|
|
217
|
-
this.hosts[data.host] = await this.getOrg(data)
|
|
218
|
-
return this.hosts[data.host]
|
|
219
|
-
}
|
|
220
|
-
}
|
|
341
|
+
if (data.organization_id) {
|
|
342
|
+
query.object = [{ _id: data.organization_id }];
|
|
343
|
+
} else if (data.host) {
|
|
344
|
+
query.$filter = {
|
|
345
|
+
query: { host: { $elemMatch: { name: { $in: [data.host] } } } },
|
|
346
|
+
limit: 1
|
|
347
|
+
};
|
|
348
|
+
} else {
|
|
349
|
+
return { serverOrganization: false, error: 'An organization could not be found' };
|
|
221
350
|
}
|
|
222
351
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
host: data.host,
|
|
227
|
-
database: this.config.organization_id,
|
|
228
|
-
array: 'organizations',
|
|
229
|
-
organization_id: this.config.organization_id
|
|
230
|
-
}
|
|
352
|
+
if (!organizations[query.organization_id] && query.organization_id === config.organization_id) {
|
|
353
|
+
organizations[query.organization_id] = { ...config, isConfig: true };
|
|
354
|
+
}
|
|
231
355
|
|
|
232
|
-
|
|
233
|
-
data.database = data.organization_id = data.organization_id
|
|
356
|
+
let organization = await send(query);
|
|
234
357
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (!this.organizations[query.organization_id] && query.organization_id === this.config.organization_id)
|
|
246
|
-
this.organizations[query.organization_id] = { ...this.config, isConfig: true }
|
|
247
|
-
|
|
248
|
-
let organization = await this.send(query)
|
|
249
|
-
|
|
250
|
-
if (organization
|
|
251
|
-
&& organization.object
|
|
252
|
-
&& organization.object[0]) {
|
|
253
|
-
if (!this.organizations[organization.object[0]._id] || this.organizations[query.organization_id].isConfig) {
|
|
254
|
-
this.organizations[organization.object[0]._id] = organization.object[0]
|
|
255
|
-
|
|
256
|
-
delete query.$filter
|
|
257
|
-
query.database = query.organization_id = organization.object[0]._id
|
|
258
|
-
let org = await this.send(query)
|
|
259
|
-
if (org
|
|
260
|
-
&& org.object
|
|
261
|
-
&& org.object[0]) {
|
|
262
|
-
if (data.host)
|
|
263
|
-
this.hosts[data.host] = org.object[0]
|
|
264
|
-
return org.object[0]
|
|
265
|
-
} else {
|
|
266
|
-
if (data.host) return this.hosts[data.host];
|
|
267
|
-
return {
|
|
268
|
-
serverOrganization: false,
|
|
269
|
-
error: "An organization could not be found in the specified dbUrl"
|
|
270
|
-
};
|
|
358
|
+
if (organization && organization.object && organization.object[0]) {
|
|
359
|
+
if (!organizations[organization.object[0]._id] || organizations[query.organization_id].isConfig) {
|
|
360
|
+
organizations[organization.object[0]._id] = organization.object[0];
|
|
361
|
+
|
|
362
|
+
delete query.$filter;
|
|
363
|
+
query.database = query.organization_id = organization.object[0]._id;
|
|
364
|
+
let org = await send(query);
|
|
365
|
+
if (org && org.object && org.object[0]) {
|
|
366
|
+
if (data.host) {
|
|
367
|
+
hosts[data.host] = org.object[0];
|
|
271
368
|
}
|
|
369
|
+
return org.object[0];
|
|
272
370
|
} else {
|
|
273
|
-
if (data.host)
|
|
274
|
-
|
|
275
|
-
|
|
371
|
+
if (data.host) return hosts[data.host];
|
|
372
|
+
return {
|
|
373
|
+
serverOrganization: false,
|
|
374
|
+
error: "An organization could not be found in the specified dbUrl"
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
if (data.host) {
|
|
379
|
+
hosts[data.host] = organization.object[0];
|
|
276
380
|
}
|
|
381
|
+
return organization.object[0];
|
|
277
382
|
}
|
|
278
|
-
return { serverOrganization: false, error: 'An organization could not be found' }
|
|
279
383
|
}
|
|
384
|
+
return { serverOrganization: false, error: 'An organization could not be found' };
|
|
385
|
+
}
|
|
280
386
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Standard error state structure normalization helper.
|
|
389
|
+
*/
|
|
390
|
+
function errorHandler(data, error, database, array) {
|
|
391
|
+
if (typeof error === 'object') {
|
|
392
|
+
error['storage'] = 'mongodb';
|
|
393
|
+
} else {
|
|
394
|
+
error = { location: 'crudServer', message: error };
|
|
395
|
+
}
|
|
286
396
|
|
|
287
|
-
|
|
288
|
-
|
|
397
|
+
if (database) {
|
|
398
|
+
error['database'] = database;
|
|
399
|
+
}
|
|
289
400
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
401
|
+
if (data.error) {
|
|
402
|
+
data.error.push(error);
|
|
403
|
+
} else {
|
|
404
|
+
data.error = [error];
|
|
294
405
|
}
|
|
295
406
|
}
|
|
296
407
|
|
|
297
|
-
|
|
408
|
+
// Export default database routing context (explicitly loaded on-demand via server initialization)
|
|
409
|
+
export default crudServer;
|