json-rest-api 1.0.22 → 1.0.23
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.md +1 -2
- package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
- package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
- package/docs/API.md +51 -14
- package/docs/COMPARISON.md +2 -2
- package/docs/GUIDE/GUIDE_1_Initial_Setup.md +28 -1
- package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
- package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
- package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
- package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
- package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
- package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
- package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
- package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
- package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +250 -0
- package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
- package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
- package/docs/GUIDE/index.md +18 -9
- package/docs/QUICKSTART.md +5 -1
- package/docs/index.md +1 -1
- package/index.js +3 -2
- package/package.json +2 -2
- package/plugins/core/connectors/express-plugin.js +97 -184
- package/plugins/core/connectors/fastify-plugin.js +206 -0
- package/plugins/core/connectors/lib/connector-core.js +195 -0
- package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
- package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
- package/plugins/core/lib/dbIntrospection.js +1078 -0
- package/plugins/core/lib/dbTablesOperations.js +1104 -160
- package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -2
- package/plugins/core/lib/querying/knex-process-includes.js +3 -4
- package/plugins/core/lib/querying/knex-query-helpers.js +17 -71
- package/plugins/core/lib/querying/knex-relationship-includes.js +466 -206
- package/plugins/core/lib/querying/query-builder-utils.js +19 -0
- package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
- package/plugins/core/lib/querying/sort-helpers.js +33 -0
- package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
- package/plugins/core/lib/querying/url-helpers.js +8 -2
- package/plugins/core/lib/querying-writing/compile-schemas.js +12 -1
- package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -13
- package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
- package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
- package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
- package/plugins/core/lib/storage/canonical-storage-mapping.js +113 -0
- package/plugins/core/lib/storage/storage-adapter.js +79 -2
- package/plugins/core/lib/storage/storage-mapping.js +204 -0
- package/plugins/core/rest-api-anyapi-knex-plugin.js +355 -228
- package/plugins/core/rest-api-autofilter-plugin.js +414 -0
- package/plugins/core/rest-api-knex-plugin.js +249 -211
- package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
- package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
- package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
- package/plugins/core/rest-api-plugin-methods/common.js +121 -83
- package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
- package/plugins/core/rest-api-plugin-methods/get.js +23 -14
- package/plugins/core/rest-api-plugin-methods/patch.js +28 -12
- package/plugins/core/rest-api-plugin-methods/post.js +13 -8
- package/plugins/core/rest-api-plugin-methods/put.js +45 -21
- package/plugins/core/rest-api-plugin-methods/query.js +15 -53
- package/plugins/core/rest-api-plugin.js +2 -0
- package/plugins/core/rest-api-positioning-plugin.js +1 -1
- package/plugins/core/rest-api-query-projections-plugin.js +130 -0
- package/plugins/core/socketio-plugin.js +1 -3
- package/tests/autofilter.test.js +463 -0
- package/tests/custom-id-simplified-post.test.js +75 -0
- package/tests/custom-idproperty-relationships.test.js +33 -0
- package/tests/db-introspection.test.js +518 -0
- package/tests/db-table-operations.test.js +781 -0
- package/tests/fastify-plugin.test.js +141 -0
- package/tests/field-getters.test.js +1 -2
- package/tests/field-setters.test.js +0 -4
- package/tests/fixtures/api-configs.js +188 -219
- package/tests/helpers/fake-fastify.js +99 -0
- package/tests/http-connectors-parity.test.js +235 -0
- package/tests/projected-fields.test.js +319 -0
- package/tests/relationships.test.js +3 -3
- package/tests/searchschema-knex-behavior.test.js +187 -0
- package/tests/searchschema-merge.test.js +21 -69
- package/tests/storage-mapping.test.js +210 -0
- package/audit/00-charter.md +0 -103
- package/audit/01-scope-map.md +0 -84
- package/audit/02-coverage-matrix.csv +0 -174
- package/audit/03-invariants.md +0 -123
- package/audit/04-standards-rubric.md +0 -56
- package/audit/05-competitor-rubric.md +0 -77
- package/audit/06-findings.md +0 -160
- package/audit/07-risk-register.csv +0 -10
- package/audit/08-remediation-roadmap.md +0 -177
- package/audit/09-signoff-check.md +0 -23
- package/audit/10-final-synthesis.md +0 -29
- package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
- package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
- package/audit/evidence/code/boundary-leak-checks.txt +0 -72
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
- package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
- package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
- package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
- package/audit/evidence/code/critical-pass1-summary.csv +0 -67
- package/audit/evidence/code/critical-pass2-summary.csv +0 -67
- package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
- package/audit/evidence/code/error-model-signals.txt +0 -98
- package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
- package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
- package/audit/evidence/code/hook-flow-signals.txt +0 -282
- package/audit/evidence/code/idproperty-signals.txt +0 -157
- package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
- package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
- package/audit/evidence/code/imports-json-rest-api.txt +0 -354
- package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
- package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
- package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
- package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
- package/audit/evidence/code/permission-signals.txt +0 -193
- package/audit/evidence/code/query-include-signals.txt +0 -687
- package/audit/evidence/code/transaction-signals.txt +0 -28
- package/audit/evidence/code/validation-signals.txt +0 -650
- package/audit/evidence/code/write-path-signals.txt +0 -61
- package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
- package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
- package/audit/evidence/external/corpus-freeze.md +0 -21
- package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
- package/audit/evidence/external/npm-download-signals.txt +0 -23
- package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
- package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
- package/audit/evidence/external/readme-fortune-signals.txt +0 -26
- package/audit/evidence/external/readme-fortune.md +0 -165
- package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
- package/audit/evidence/external/readme-json-api-serializer.md +0 -690
- package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
- package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
- package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
- package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
- package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
- package/audit/evidence/external/readme-jsonapi-server.md +0 -104
- package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
- package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
- package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
- package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
- package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
- package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
- package/audit/evidence/external/standards-sources.txt +0 -6
- package/audit/evidence/runtime/baseline-commits.txt +0 -2
- package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
- package/audit/evidence/runtime/environment.txt +0 -4
- package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
- package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
- package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
- package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
- package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
- package/audit/evidence/runtime/test-exitcodes.txt +0 -4
- package/audit/evidence/runtime/test-summary.md +0 -107
- package/audit/evidence/runtime/trace-anchors.txt +0 -61
- package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
- package/audit/evidence/tests/failing-tests-summary.txt +0 -25
- package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
- package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
- package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
- package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
- package/audit/evidence/tests/query-include-test-signals.txt +0 -948
- package/audit/evidence/tests/relationship-test-signals.txt +0 -223
- package/audit/evidence/tests/skipped-tests.txt +0 -10
- package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
- package/audit/evidence/tests/test-suite-list.txt +0 -32
- package/audit/subsystems/docs-drift.md +0 -49
- package/audit/subsystems/pass2-cross-review.md +0 -131
- package/audit/subsystems/plugins-access.md +0 -51
- package/audit/subsystems/plugins-bulk.md +0 -50
- package/audit/subsystems/plugins-cors.md +0 -48
- package/audit/subsystems/plugins-file.md +0 -53
- package/audit/subsystems/plugins-multihome.md +0 -50
- package/audit/subsystems/plugins-positioning.md +0 -54
- package/audit/subsystems/plugins-socketio.md +0 -50
- package/audit/subsystems/query-engine.md +0 -61
- package/audit/subsystems/relationships.md +0 -55
- package/audit/subsystems/rest-orchestration.md +0 -66
- package/audit/subsystems/schema-core.md +0 -64
- package/audit/subsystems/storage-knex.md +0 -57
- package/audit/subsystems/tests.md +0 -48
- package/audit/subsystems/transport-express.md +0 -55
- package/audit/traces/trace-read-get.md +0 -22
- package/audit/traces/trace-read-query.md +0 -25
- package/audit/traces/trace-relationship-endpoints.md +0 -31
- package/audit/traces/trace-write-delete.md +0 -17
- package/audit/traces/trace-write-patch.md +0 -26
- package/audit/traces/trace-write-post.md +0 -22
- package/audit/traces/trace-write-put.md +0 -23
- package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
- package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
- package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
- package/plugins/core/rest-api-access.js +0 -459
- package/plugins/core/rest-api-multihome-plugin.js +0 -314
- package/tests/multihome.test.js +0 -553
- package/tests/multihome.test.js.bak +0 -550
- package/tests/ownership-optout.test.js +0 -76
- package/tests/rest-api-access.test.js +0 -206
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
export const MultiHomePlugin = {
|
|
2
|
-
name: 'multihome',
|
|
3
|
-
dependencies: ['rest-api'],
|
|
4
|
-
|
|
5
|
-
install ({ api, addHook, vars, helpers, log, scopes, pluginOptions }) {
|
|
6
|
-
if (!api.knex?.instance) {
|
|
7
|
-
throw new Error('Multihome plugin requires a storage plugin with knex support (rest-api-knex or rest-api-anyapi-knex)')
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Get configuration - hooked-api namespaces options by plugin name
|
|
11
|
-
const multihomeOptions = pluginOptions || {}
|
|
12
|
-
|
|
13
|
-
// Store configuration in vars (data only)
|
|
14
|
-
vars.multihome = {
|
|
15
|
-
field: multihomeOptions.field || 'multihome_id',
|
|
16
|
-
excludeResources: multihomeOptions.excludeResources || ['system_migrations', 'system_logs'],
|
|
17
|
-
requireAuth: multihomeOptions.requireAuth !== undefined ? multihomeOptions.requireAuth : true,
|
|
18
|
-
allowMissing: multihomeOptions.allowMissing || false
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const resolveStorageAdapter = (scopeName, hookContext = {}) => {
|
|
22
|
-
if (!scopeName) return null
|
|
23
|
-
if (hookContext.storageAdapter) return hookContext.storageAdapter
|
|
24
|
-
if (hookContext.knexQuery?.storageAdapter) return hookContext.knexQuery.storageAdapter
|
|
25
|
-
if (helpers.getStorageAdapter) {
|
|
26
|
-
return helpers.getStorageAdapter(scopeName)
|
|
27
|
-
}
|
|
28
|
-
return null
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const translateColumnReference = ({ field, tableName, scopeName, hookContext }) => {
|
|
32
|
-
const storageAdapter = resolveStorageAdapter(scopeName, hookContext)
|
|
33
|
-
const translated = storageAdapter?.translateColumn?.(field) ?? field
|
|
34
|
-
if (tableName) {
|
|
35
|
-
return `${tableName}.${translated}`
|
|
36
|
-
}
|
|
37
|
-
return translated
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const translateFilterValue = ({ field, value, scopeName, hookContext }) => {
|
|
41
|
-
const storageAdapter = resolveStorageAdapter(scopeName, hookContext)
|
|
42
|
-
if (!storageAdapter?.translateFilterValue) return value
|
|
43
|
-
return storageAdapter.translateFilterValue(field, value)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Default extractor function
|
|
47
|
-
function defaultSubdomainExtractor (request) {
|
|
48
|
-
// Extract from subdomain (e.g., 'mobily' from 'mobily.app.com')
|
|
49
|
-
const host = request.headers?.host || request.hostname || ''
|
|
50
|
-
const subdomain = host.split('.')[0]
|
|
51
|
-
|
|
52
|
-
// Don't use 'www' or 'api' as tenant IDs
|
|
53
|
-
if (subdomain && !['www', 'api', 'app'].includes(subdomain)) {
|
|
54
|
-
return subdomain
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Fallback to header
|
|
58
|
-
return request.headers?.['x-multihome-id'] || null
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Store extractor function in helpers
|
|
62
|
-
helpers.extractMultihomeId = multihomeOptions.extractor || defaultSubdomainExtractor
|
|
63
|
-
|
|
64
|
-
// Hook into transport layer to extract multihome_id
|
|
65
|
-
addHook('transport:request', 'extract-multihome-id', {}, async ({ context, request, helpers }) => {
|
|
66
|
-
// Extract multihome_id using configured extractor
|
|
67
|
-
const multihomeId = helpers.extractMultihomeId(request)
|
|
68
|
-
|
|
69
|
-
if (multihomeId) {
|
|
70
|
-
// Store in auth context (even if no user auth)
|
|
71
|
-
context.auth = context.auth || {}
|
|
72
|
-
context.auth.multihome_id = multihomeId
|
|
73
|
-
|
|
74
|
-
log.debug('Multihome ID extracted', {
|
|
75
|
-
multihome_id: multihomeId,
|
|
76
|
-
source: request.headers?.host
|
|
77
|
-
})
|
|
78
|
-
} else if (vars.multihome.requireAuth) {
|
|
79
|
-
log.warn('No multihome ID found in request', {
|
|
80
|
-
host: request.headers?.host,
|
|
81
|
-
headers: Object.keys(request.headers || {})
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
// Validate that resources have multihome field when added
|
|
87
|
-
addHook('scope:added', 'validate-multihome-field', {}, ({ context, vars }) => {
|
|
88
|
-
const { scopeName, scopeOptions } = context
|
|
89
|
-
|
|
90
|
-
// Skip excluded resources
|
|
91
|
-
if (vars.multihome.excludeResources.includes(scopeName)) {
|
|
92
|
-
log.debug(`Resource ${scopeName} excluded from multihome validation`)
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Check if schema has multihome field
|
|
97
|
-
const schema = scopeOptions.schema
|
|
98
|
-
if (schema && !schema[vars.multihome.field]) {
|
|
99
|
-
if (vars.multihome.allowMissing) {
|
|
100
|
-
log.warn(`Resource ${scopeName} missing ${vars.multihome.field} field - multihome filtering disabled for this
|
|
101
|
-
resource`)
|
|
102
|
-
} else {
|
|
103
|
-
throw new Error(
|
|
104
|
-
`Resource '${scopeName}' must have '${vars.multihome.field}' field in schema for multi-tenancy`
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
// Main filtering hook - adds WHERE clause to all queries
|
|
111
|
-
log.debug('Registering multihome filter hook')
|
|
112
|
-
addHook('knexQueryFiltering', 'multihome-filter', {}, async ({ context, vars }) => {
|
|
113
|
-
// Get query info from context
|
|
114
|
-
const { query, tableName, scopeName } = context.knexQuery
|
|
115
|
-
|
|
116
|
-
// Skip excluded resources first
|
|
117
|
-
if (vars.multihome.excludeResources.includes(scopeName)) {
|
|
118
|
-
log.trace(`Skipping multihome filter for excluded resource: ${scopeName}`)
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Skip if no multihome context
|
|
123
|
-
if (!context.auth?.multihome_id) {
|
|
124
|
-
if (vars.multihome.requireAuth) {
|
|
125
|
-
throw new Error('No multihome context available - cannot execute query')
|
|
126
|
-
}
|
|
127
|
-
return
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
log.debug('Applying multihome tenant filter', {
|
|
131
|
-
scopeName,
|
|
132
|
-
tableName,
|
|
133
|
-
tenant: context.auth.multihome_id,
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
// Check if this resource has multihome field
|
|
137
|
-
const scope = scopes[scopeName]
|
|
138
|
-
const hasMultihomeField = scope?.vars?.schemaInfo?.schemaStructure?.[vars.multihome.field]
|
|
139
|
-
|
|
140
|
-
if (!hasMultihomeField) {
|
|
141
|
-
if (vars.multihome.allowMissing) {
|
|
142
|
-
log.trace(`Resource ${scopeName} has no ${vars.multihome.field} field - skipping filter`)
|
|
143
|
-
return
|
|
144
|
-
} else {
|
|
145
|
-
throw new Error(`Resource ${scopeName} missing required ${vars.multihome.field} field`)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const storageAdapter = resolveStorageAdapter(scopeName, context)
|
|
150
|
-
if (storageAdapter && !context.storageAdapter) {
|
|
151
|
-
context.storageAdapter = storageAdapter
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const columnRef = translateColumnReference({
|
|
155
|
-
field: vars.multihome.field,
|
|
156
|
-
tableName,
|
|
157
|
-
scopeName,
|
|
158
|
-
hookContext: context,
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
const tenantValue = translateFilterValue({
|
|
162
|
-
field: vars.multihome.field,
|
|
163
|
-
value: context.auth.multihome_id,
|
|
164
|
-
scopeName,
|
|
165
|
-
hookContext: context,
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
if (tenantValue === null) {
|
|
169
|
-
query.whereNull(columnRef)
|
|
170
|
-
} else {
|
|
171
|
-
query.where(columnRef, tenantValue)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
log.trace('Added multihome filter', {
|
|
175
|
-
scopeName,
|
|
176
|
-
tableName,
|
|
177
|
-
multihome_id: context.auth.multihome_id
|
|
178
|
-
})
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
// Set multihome_id on new records
|
|
182
|
-
addHook('beforeSchemaValidate', 'set-multihome-id', {}, async ({ context, scopeName, vars }) => {
|
|
183
|
-
// Skip excluded resources first
|
|
184
|
-
if (vars.multihome.excludeResources.includes(scopeName)) {
|
|
185
|
-
return
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Skip if no multihome context
|
|
189
|
-
if (!context.auth?.multihome_id) {
|
|
190
|
-
if (vars.multihome.requireAuth) {
|
|
191
|
-
throw new Error('Cannot create record without multihome context')
|
|
192
|
-
}
|
|
193
|
-
return
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Check if this resource has multihome field
|
|
197
|
-
const scope = scopes[scopeName]
|
|
198
|
-
const hasMultihomeField = scope?.vars?.schemaInfo?.schemaStructure?.[vars.multihome.field]
|
|
199
|
-
|
|
200
|
-
if (!hasMultihomeField) {
|
|
201
|
-
if (!vars.multihome.allowMissing) {
|
|
202
|
-
throw new Error(`Resource ${scopeName} missing required ${vars.multihome.field} field`)
|
|
203
|
-
}
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// For POST, always set multihome_id
|
|
208
|
-
if (context.method === 'post') {
|
|
209
|
-
context.inputRecord.data.attributes = context.inputRecord.data.attributes || {}
|
|
210
|
-
context.inputRecord.data.attributes[vars.multihome.field] = context.auth.multihome_id
|
|
211
|
-
|
|
212
|
-
log.debug('Set multihome_id on new record', {
|
|
213
|
-
scopeName,
|
|
214
|
-
multihome_id: context.auth.multihome_id
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// For PUT/PATCH, validate multihome_id if provided
|
|
219
|
-
if ((context.method === 'put' || context.method === 'patch') &&
|
|
220
|
-
context.inputRecord.data.attributes?.[vars.multihome.field]) {
|
|
221
|
-
const providedId = context.inputRecord.data.attributes[vars.multihome.field]
|
|
222
|
-
if (providedId !== context.auth.multihome_id) {
|
|
223
|
-
throw new Error(
|
|
224
|
-
`Cannot set ${vars.multihome.field} to '${providedId}' - must match current context '${context.auth.multihome_id}'`
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
// Add checkPermissions hook to enforce tenant isolation
|
|
231
|
-
// This is the single source of truth for single-record access control
|
|
232
|
-
addHook('checkPermissions', 'multihome-check-permissions', {}, async ({ context, scopeName }) => {
|
|
233
|
-
// Extract the needed values from originalContext
|
|
234
|
-
const auth = context.originalContext?.auth
|
|
235
|
-
const minimalRecord = context.originalContext?.minimalRecord
|
|
236
|
-
const id = context.originalContext?.id
|
|
237
|
-
|
|
238
|
-
// Skip excluded resources
|
|
239
|
-
if (vars.multihome.excludeResources.includes(scopeName)) {
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Skip if no multihome context
|
|
244
|
-
if (!auth?.multihome_id) {
|
|
245
|
-
if (vars.multihome.requireAuth) {
|
|
246
|
-
throw new Error('No multihome context available')
|
|
247
|
-
}
|
|
248
|
-
return
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Get the scope to check if it has multihome field
|
|
252
|
-
const scope = scopes[scopeName]
|
|
253
|
-
const hasMultihomeField = scope?.vars?.schemaInfo?.schemaStructure?.[vars.multihome.field]
|
|
254
|
-
|
|
255
|
-
if (!hasMultihomeField) {
|
|
256
|
-
return // Resource doesn't support multihome
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// For operations on existing records, verify tenant ownership
|
|
260
|
-
if (minimalRecord) {
|
|
261
|
-
// minimalRecord is in JSON:API format, so tenant_id is in attributes
|
|
262
|
-
const recordTenant = minimalRecord.attributes?.[vars.multihome.field]
|
|
263
|
-
const userTenant = auth.multihome_id
|
|
264
|
-
|
|
265
|
-
if (recordTenant !== userTenant) {
|
|
266
|
-
log.error('Multihome permission violation', {
|
|
267
|
-
scopeName,
|
|
268
|
-
recordId: id,
|
|
269
|
-
recordTenant,
|
|
270
|
-
userTenant,
|
|
271
|
-
method: context.method
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
// Return 404 for GET to prevent information leakage
|
|
275
|
-
// Return 403 for other operations
|
|
276
|
-
if (context.method === 'get') {
|
|
277
|
-
const error = new Error('Resource not found')
|
|
278
|
-
error.code = 'REST_API_RESOURCE'
|
|
279
|
-
throw error
|
|
280
|
-
} else {
|
|
281
|
-
const error = new Error('Access denied - insufficient permissions')
|
|
282
|
-
error.code = 'REST_API_FORBIDDEN'
|
|
283
|
-
throw error
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
// Note: PUT, PATCH, and DELETE operations will fail naturally if the record
|
|
290
|
-
// doesn't exist due to tenant filtering in the GET operation that precedes them
|
|
291
|
-
|
|
292
|
-
// Add API method to get current multihome context
|
|
293
|
-
api.multihome = {
|
|
294
|
-
getCurrentTenant: () => {
|
|
295
|
-
// This would need to be called within a request context
|
|
296
|
-
// Real implementation would need access to current request context
|
|
297
|
-
return null
|
|
298
|
-
},
|
|
299
|
-
|
|
300
|
-
// Runtime configuration helper (for debugging/testing)
|
|
301
|
-
getConfig: () => ({
|
|
302
|
-
...vars.multihome,
|
|
303
|
-
hasCustomExtractor: helpers.extractMultihomeId !== defaultSubdomainExtractor
|
|
304
|
-
})
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
log.info('MultiHome plugin installed', {
|
|
308
|
-
field: vars.multihome.field,
|
|
309
|
-
excludedResources: vars.multihome.excludeResources,
|
|
310
|
-
requireAuth: vars.multihome.requireAuth,
|
|
311
|
-
hasCustomExtractor: helpers.extractMultihomeId !== defaultSubdomainExtractor
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
}
|