@teleporthq/teleport-plugin-next-data-source 0.44.14 → 0.44.17
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/__tests__/_helpers/exposure.ts +29 -0
- package/__tests__/_helpers/first-page-runtime.ts +12 -2
- package/__tests__/blog-post-contents.test.ts +3 -1
- package/__tests__/blog-post-page-enrichment.test.ts +2 -1
- package/__tests__/blog-post-seo-fields.test.ts +124 -0
- package/__tests__/browser-row-policy.test.ts +65 -5
- package/__tests__/data-source-cache-wrapping.test.ts +48 -7
- package/__tests__/external-read-routes-guard.test.ts +991 -0
- package/__tests__/null-filter-where.test.ts +19 -2
- package/__tests__/range-control.test.ts +29 -0
- package/__tests__/search-relevance.test.ts +42 -6
- package/__tests__/table-read-route-protected-tables.test.ts +54 -4
- package/dist/cjs/count-fetchers.d.ts.map +1 -1
- package/dist/cjs/count-fetchers.js +1 -1
- package/dist/cjs/count-fetchers.js.map +1 -1
- package/dist/cjs/data-source-fetchers.d.ts +4 -0
- package/dist/cjs/data-source-fetchers.d.ts.map +1 -1
- package/dist/cjs/data-source-fetchers.js +97 -20
- package/dist/cjs/data-source-fetchers.js.map +1 -1
- package/dist/cjs/fetchers/postgresql.d.ts +3 -1
- package/dist/cjs/fetchers/postgresql.d.ts.map +1 -1
- package/dist/cjs/fetchers/postgresql.js +11 -3
- package/dist/cjs/fetchers/postgresql.js.map +1 -1
- package/dist/cjs/fetchers/raw-query.d.ts +20 -1
- package/dist/cjs/fetchers/raw-query.d.ts.map +1 -1
- package/dist/cjs/fetchers/raw-query.js +37 -7
- package/dist/cjs/fetchers/raw-query.js.map +1 -1
- package/dist/cjs/fetchers/redshift.d.ts.map +1 -1
- package/dist/cjs/fetchers/redshift.js +2 -1
- package/dist/cjs/fetchers/redshift.js.map +1 -1
- package/dist/cjs/fetchers/teleport.d.ts.map +1 -1
- package/dist/cjs/fetchers/teleport.js +5 -3
- package/dist/cjs/fetchers/teleport.js.map +1 -1
- package/dist/cjs/fetchers/utils/external-read-guard.d.ts +53 -0
- package/dist/cjs/fetchers/utils/external-read-guard.d.ts.map +1 -0
- package/dist/cjs/fetchers/utils/external-read-guard.js +126 -0
- package/dist/cjs/fetchers/utils/external-read-guard.js.map +1 -0
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts +76 -12
- package/dist/cjs/fetchers/utils/table-access-guard.d.ts.map +1 -1
- package/dist/cjs/fetchers/utils/table-access-guard.js +123 -15
- package/dist/cjs/fetchers/utils/table-access-guard.js.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/pagination-controls.d.ts +4 -1
- package/dist/cjs/pagination-controls.d.ts.map +1 -1
- package/dist/cjs/pagination-controls.js +4 -1
- package/dist/cjs/pagination-controls.js.map +1 -1
- package/dist/cjs/pagination-plugin.js +1 -0
- package/dist/cjs/pagination-plugin.js.map +1 -1
- package/dist/cjs/range-control.d.ts +10 -5
- package/dist/cjs/range-control.d.ts.map +1 -1
- package/dist/cjs/range-control.js +23 -2
- package/dist/cjs/range-control.js.map +1 -1
- package/dist/cjs/transformations/blog-post.d.ts +5 -0
- package/dist/cjs/transformations/blog-post.d.ts.map +1 -1
- package/dist/cjs/transformations/blog-post.js +6 -2
- package/dist/cjs/transformations/blog-post.js.map +1 -1
- package/dist/cjs/transformations/index.d.ts +18 -2
- package/dist/cjs/transformations/index.d.ts.map +1 -1
- package/dist/cjs/transformations/index.js +26 -1
- package/dist/cjs/transformations/index.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/count-fetchers.d.ts.map +1 -1
- package/dist/esm/count-fetchers.js +1 -1
- package/dist/esm/count-fetchers.js.map +1 -1
- package/dist/esm/data-source-fetchers.d.ts +4 -0
- package/dist/esm/data-source-fetchers.d.ts.map +1 -1
- package/dist/esm/data-source-fetchers.js +97 -20
- package/dist/esm/data-source-fetchers.js.map +1 -1
- package/dist/esm/fetchers/postgresql.d.ts +3 -1
- package/dist/esm/fetchers/postgresql.d.ts.map +1 -1
- package/dist/esm/fetchers/postgresql.js +12 -4
- package/dist/esm/fetchers/postgresql.js.map +1 -1
- package/dist/esm/fetchers/raw-query.d.ts +20 -1
- package/dist/esm/fetchers/raw-query.d.ts.map +1 -1
- package/dist/esm/fetchers/raw-query.js +38 -8
- package/dist/esm/fetchers/raw-query.js.map +1 -1
- package/dist/esm/fetchers/redshift.d.ts.map +1 -1
- package/dist/esm/fetchers/redshift.js +2 -1
- package/dist/esm/fetchers/redshift.js.map +1 -1
- package/dist/esm/fetchers/teleport.d.ts.map +1 -1
- package/dist/esm/fetchers/teleport.js +6 -4
- package/dist/esm/fetchers/teleport.js.map +1 -1
- package/dist/esm/fetchers/utils/external-read-guard.d.ts +53 -0
- package/dist/esm/fetchers/utils/external-read-guard.d.ts.map +1 -0
- package/dist/esm/fetchers/utils/external-read-guard.js +118 -0
- package/dist/esm/fetchers/utils/external-read-guard.js.map +1 -0
- package/dist/esm/fetchers/utils/table-access-guard.d.ts +76 -12
- package/dist/esm/fetchers/utils/table-access-guard.d.ts.map +1 -1
- package/dist/esm/fetchers/utils/table-access-guard.js +119 -15
- package/dist/esm/fetchers/utils/table-access-guard.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/pagination-controls.d.ts +4 -1
- package/dist/esm/pagination-controls.d.ts.map +1 -1
- package/dist/esm/pagination-controls.js +4 -1
- package/dist/esm/pagination-controls.js.map +1 -1
- package/dist/esm/pagination-plugin.js +1 -0
- package/dist/esm/pagination-plugin.js.map +1 -1
- package/dist/esm/range-control.d.ts +10 -5
- package/dist/esm/range-control.d.ts.map +1 -1
- package/dist/esm/range-control.js +23 -2
- package/dist/esm/range-control.js.map +1 -1
- package/dist/esm/transformations/blog-post.d.ts +5 -0
- package/dist/esm/transformations/blog-post.d.ts.map +1 -1
- package/dist/esm/transformations/blog-post.js +6 -2
- package/dist/esm/transformations/blog-post.js.map +1 -1
- package/dist/esm/transformations/index.d.ts +18 -2
- package/dist/esm/transformations/index.d.ts.map +1 -1
- package/dist/esm/transformations/index.js +27 -2
- package/dist/esm/transformations/index.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/count-fetchers.ts +10 -2
- package/src/data-source-fetchers.ts +144 -23
- package/src/fetchers/postgresql.ts +38 -9
- package/src/fetchers/raw-query.ts +125 -13
- package/src/fetchers/redshift.ts +3 -0
- package/src/fetchers/teleport.ts +29 -8
- package/src/fetchers/utils/external-read-guard.ts +289 -0
- package/src/fetchers/utils/table-access-guard.ts +469 -20
- package/src/index.ts +1 -1
- package/src/pagination-controls.ts +4 -1
- package/src/pagination-plugin.ts +1 -0
- package/src/range-control.ts +40 -8
- package/src/transformations/blog-post.ts +107 -4
- package/src/transformations/index.ts +44 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw-query.js","sourceRoot":"","sources":["../../../src/fetchers/raw-query.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"raw-query.js","sourceRoot":"","sources":["../../../src/fetchers/raw-query.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redshift.d.ts","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"redshift.d.ts","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":"AAqBA,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA+MF,CAAA"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { replaceSecretReference, generateDateFormatterCode, generateSafeJSONParseCode, generateFilterTreeHelpersCode, generateSearchEscapeHelpersCode, } from '../utils';
|
|
2
|
+
import { DataExposure } from '@teleporthq/teleport-shared';
|
|
2
3
|
import { generateSearchSqlHelpersCode, REDSHIFT_SEARCH_SQL } from '../search-relevance';
|
|
3
4
|
export var generateRedshiftFetcher = function (config, tableName) {
|
|
4
5
|
var _a;
|
|
@@ -11,7 +12,7 @@ export var generateRedshiftFetcher = function (config, tableName) {
|
|
|
11
12
|
var ssl = redshiftConfig.ssl;
|
|
12
13
|
var sslConfig = redshiftConfig.sslConfig;
|
|
13
14
|
var schema = (_a = redshiftConfig.options) === null || _a === void 0 ? void 0 : _a.schema;
|
|
14
|
-
return "import { Client } from 'pg'\n\nconst getClient = () => {\n return new Client({\n host: ".concat(JSON.stringify(host), ",\n port: ").concat(port || 5439, ",\n user: ").concat(JSON.stringify(user), ",\n password: ").concat(replaceSecretReference(password), ",\n database: ").concat(JSON.stringify(database), ",\n ssl: ").concat(ssl === false
|
|
15
|
+
return "import { Client } from 'pg'\n\n// No query waits on the database longer than this (enforced by the client).\nconst getClient = () => {\n return new Client({\n query_timeout: ".concat(DataExposure.READ_QUERY_TIMEOUT_MS, ",\n host: ").concat(JSON.stringify(host), ",\n port: ").concat(port || 5439, ",\n user: ").concat(JSON.stringify(user), ",\n password: ").concat(replaceSecretReference(password), ",\n database: ").concat(JSON.stringify(database), ",\n ssl: ").concat(ssl === false
|
|
15
16
|
? '{ rejectUnauthorized: false }'
|
|
16
17
|
: sslConfig
|
|
17
18
|
? "{\n ".concat(sslConfig.ca ? "ca: ".concat(replaceSecretReference(sslConfig.ca), ",") : '', "\n ").concat(sslConfig.cert ? "cert: ".concat(replaceSecretReference(sslConfig.cert), ",") : '', "\n ").concat(sslConfig.key ? "key: ".concat(replaceSecretReference(sslConfig.key), ",") : '', "\n rejectUnauthorized: ").concat(sslConfig.rejectUnauthorized !== false, "\n }")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redshift.js","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,+BAA+B,GAChC,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAavF,MAAM,CAAC,IAAM,uBAAuB,GAAG,UACrC,MAA+B,EAC/B,SAAiB;;IAEjB,IAAM,cAAc,GAAG,MAAwB,CAAA;IAC/C,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,IAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,IAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAA;IAC9B,IAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAA;IAC1C,IAAM,MAAM,GAAG,MAAA,cAAc,CAAC,OAAO,0CAAE,MAAM,CAAA;IAE7C,OAAO,
|
|
1
|
+
{"version":3,"file":"redshift.js","sourceRoot":"","sources":["../../../src/fetchers/redshift.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,+BAA+B,GAChC,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAavF,MAAM,CAAC,IAAM,uBAAuB,GAAG,UACrC,MAA+B,EAC/B,SAAiB;;IAEjB,IAAM,cAAc,GAAG,MAAwB,CAAA;IAC/C,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAA;IAChC,IAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,IAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,IAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAA;IAC9B,IAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAA;IAC1C,IAAM,MAAM,GAAG,MAAA,cAAc,CAAC,OAAO,0CAAE,MAAM,CAAA;IAE7C,OAAO,4LAKY,YAAY,CAAC,qBAAqB,0BAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,0BACpB,IAAI,IAAI,IAAI,0BACZ,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,8BAChB,sBAAsB,CAAC,QAAQ,CAAC,8BAChC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,yBAElC,GAAG,KAAK,KAAK;QACX,CAAC,CAAC,+BAA+B;QACjC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,mBACF,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,cAAO,sBAAsB,CAAC,SAAS,CAAC,EAAE,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBAClE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAS,sBAAsB,CAAC,SAAS,CAAC,IAAI,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,qBACxE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,eAAQ,sBAAsB,CAAC,SAAS,CAAC,GAAG,CAAC,MAAG,CAAC,CAAC,CAAC,EAAE,yCACjD,SAAS,CAAC,kBAAkB,KAAK,KAAK,YAC5D;YACE,CAAC,CAAC,+BAA+B,CAAC,wDAAwD;8BAKhG,yBAAyB,EAAE,iBAE3B,6BAA6B,EAAE,iBAE/B,+BAA+B,EAAE,eACjC,4BAA4B,CAAC,mBAAmB,CAAC,iBAEjD,yBAAyB,EAAE,+IAOvB,MAAM,CAAC,CAAC,CAAC,iDAA0C,MAAM,OAAI,CAAC,CAAC,CAAC,EAAE,2oBAmB1D,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,2FAG5C,MAAM;QACJ,CAAC,CAAC,WAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,eAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAG;QAC7D,CAAC,CAAC,WAAI,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,MAAG,i7EAiEhB,SAAS,wvEAkExC,CAAA;AACD,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"teleport.d.ts","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAA;AAgD3B,eAAO,MAAM,sBAAsB,WACzB,OAAO,MAAM,EAAE,OAAO,CAAC,KAC9B;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAMpC,CAAA;AAED,eAAO,MAAM,uBAAuB,WAC1B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,qBACC,sBAAsB,KACvC,MAwdF,CAAA;AAED,eAAO,MAAM,4BAA4B,WAC/B,OAAO,MAAM,EAAE,OAAO,CAAC,aACpB,MAAM,KAChB,MA2FF,CAAA"}
|
|
@@ -6,7 +6,8 @@ import { generateRowOrderTiebreakHelper, rowIdentityColumn } from '../row-order'
|
|
|
6
6
|
import { generateProductFilterClauseHelper } from '../product-filter-fields';
|
|
7
7
|
import { collectLocalizedColumns, generateLocalizedColumnsHelper } from '../localized-columns';
|
|
8
8
|
import { REQUEST_LOCALE_PARAM } from '../request-locale';
|
|
9
|
-
import {
|
|
9
|
+
import { DataExposure } from '@teleporthq/teleport-shared';
|
|
10
|
+
import { generateReadGuardCall, generateTableAccessPreamble, sqlRouteFieldPolicy, } from './utils/table-access-guard';
|
|
10
11
|
import { generateRequestSqlGuardsCode } from './utils/request-sql-guards';
|
|
11
12
|
import { generateArrayOverlapSqlCode } from '../array-overlap-sql';
|
|
12
13
|
import { generateSearchSqlHelpersCode, PG_SEARCH_SQL } from '../search-relevance';
|
|
@@ -56,9 +57,10 @@ export var generateTeleportFetcher = function (config, tableName, transformOptio
|
|
|
56
57
|
? "key: ".concat(resolveEnvReference(dbConfig.sslConfig.key, 'TELEPORT_DB_SSL_KEY'), ",")
|
|
57
58
|
: '', "\n rejectUnauthorized: false\n }")
|
|
58
59
|
: "(process.env.".concat(DEFAULT_ENV_KEYS.ssl, " === 'false' ? false : process.env.").concat(DEFAULT_ENV_KEYS.ssl, " === 'true' ? { rejectUnauthorized: false } : undefined)");
|
|
59
|
-
return "".concat(PG_CLIENT_IMPORT, "\n\n").concat(generateTableAccessPreamble(transformOptions.trustedReaderRoles || []), "\nfunction normalizePostgresConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n if (/^postgresql:\\/(?!\\/)/i.test(connectionString)) {\n return connectionString.replace(/^postgresql:\\//i, 'postgresql://');\n }\n return connectionString;\n}\n\nfunction stripSslQueryParamsFromConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n try {\n var u = new URL(connectionString.replace(/^postgresql:/i, 'postgres:'));\n u.searchParams.delete('sslmode');\n u.searchParams.delete('ssl');\n u.searchParams.delete('sslrootcert');\n u.searchParams.delete('sslcert');\n u.searchParams.delete('sslkey');\n return u.toString().replace(/^postgres:/i, 'postgresql:');\n } catch (e) {\n return connectionString;\n }\n}\n\nconst getClient = () => {\n var ssl = ").concat(sslCode, ";\n var connStr = process.env.TELEPORT_DB_CONNECTION_STRING;\n if (connStr) {\n connStr = normalizePostgresConnectionString(connStr);\n }\n if (ssl === false && connStr) {\n connStr = stripSslQueryParamsFromConnectionString(connStr);\n }\n if (connStr) {\n return new Client(Object.assign(\n { connectionString: connStr },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n }\n return new Client(Object.assign(\n {\n host: ").concat(hostRef, ",\n port: parseInt(").concat(portRef, " || '5432', 10),\n user: ").concat(userRef, ",\n password: ").concat(passwordRef, ",\n database: ").concat(databaseRef, ",\n },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n}\n\n").concat(generateSafeJSONParseCode(), "\n\n").concat(generateFilterTreeHelpersCode(), "\n\n").concat(generateSearchEscapeHelpersCode(), "\n").concat(generateSearchSqlHelpersCode(PG_SEARCH_SQL), "\n").concat(generateArrayOverlapSqlCode(), "\n").concat(getTransformationCode(tableName, transformOptions), "\n").concat(getTransformWrapperCode(tableName, transformOptions), "\n").concat(generateRequestSqlGuardsCode(), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return paramIndex\n \n const buildCondition = (condition) => {\n const field = condition.source\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n // The products table's virtual fields (effective price, rating bucket,\n // on-sale flag, variant axes) are whole clauses, not columns - so they are\n // answered before any branch below can interpolate the field name raw.\n const productClause = productFilterClause(field, operand, value, (param) => {\n queryParams.push(param)\n return '$' + paramIndex++\n })\n if (productClause !== null) return productClause\n\n // A translatable column is compared against the value in every language,\n // so a localized slug resolves under any locale - see localized-columns.ts.\n const localizedClause = localizedEqualityClause(field, operand, value, (param) => {\n queryParams.push(param)\n return '$' + paramIndex++\n })\n if (localizedClause !== null) return localizedClause\n assertRequestColumn(field, 'filter field')\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n if (operand === 'array_overlap') {\n // The row's list (see array-overlap-sql.ts) shares any value with the set.\n const clause = arrayOverlapSql((localizedColumnList(field) || [field]), '$' + paramIndex)\n queryParams.push(value.map((entry) => String(entry)))\n paramIndex++\n return clause\n }\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders.join(', ')})`\n : `${field} IN (${placeholders.join(', ')})`\n }\n \n if (operand === 'array_overlap') {\n if (value === '' || value === null || value === undefined) return null\n // A single comma-joined string (the multi-select Category Filter's\n // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.\n const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)\n if (overlapValues.length === 0) return null\n const clause = arrayOverlapSql((localizedColumnList(field) || [field]), '$' + paramIndex)\n queryParams.push(overlapValues)\n paramIndex++\n return clause\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n const validOps = ['=', '!=', '>', '<', '>=', '<=']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n const clause = `${field} ${sqlOperator} $${paramIndex}`\n queryParams.push(value)\n paramIndex++\n return clause\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n \n return paramIndex\n}\n\n").concat(generateDateFormatterCode(), "\n\n").concat(generateSortFieldSqlHelper(tableName), "\n\n").concat(generateSortFallbackFieldHelper(tableName), "\n\n").concat(generateRowOrderTiebreakHelper(rowIdentityColumn(config, tableName)), "\n\n").concat(generateProductFilterClauseHelper(tableName), "\n\n").concat(generateLocalizedColumnsHelper(localizedColumns), "\n\n// Matches DDL / dangerous statements the raw-query branch should refuse.\n// Keep this list conservative \u2014 anything destructive or schema-changing is\n// out of scope for a client-triggered fetch. SELECT and CTEs (WITH ...) are\n// the only shapes consumers legitimately need.\nconst BLOCKED_RAW_QUERY_PATTERNS = [\n /\\bcreate\\s+(?:temporary\\s+|temp\\s+|unlogged\\s+|global\\s+|local\\s+)?table\\b/i,\n /\\bcreate\\s+(?:unique\\s+)?index\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:materialized\\s+)?view\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?trigger\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:aggregate\\s+)?function\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?procedure\\b/i,\n /\\bcreate\\s+(?:database|schema|sequence|extension|type|role|user)\\b/i,\n /\\bdrop\\s+(?:table|view|index|schema|database|sequence|trigger|function|procedure|role|user|extension|type|materialized)\\b/i,\n /\\balter\\s+(?:table|view|index|schema|database|sequence|role|user|system)\\b/i,\n /\\btruncate\\b/i,\n /\\bgrant\\b/i,\n /\\brevoke\\b/i,\n /\\binsert\\b/i,\n /\\bupdate\\b/i,\n /\\bdelete\\b/i,\n /\\bcopy\\b/i,\n /\\bvacuum\\b/i,\n /\\breindex\\b/i,\n /\\bcluster\\b/i,\n]\n\nfunction assertRawQuerySafe(rawQuery) {\n if (typeof rawQuery !== 'string' || rawQuery.length === 0) {\n throw new Error('rawQuery must be a non-empty string')\n }\n // Reject multi-statement payloads \u2014 only single SELECT / WITH statements.\n // A trailing semicolon is tolerated but any content after it fails.\n var trimmed = rawQuery.trim().replace(/;\\s*$/, '')\n if (trimmed.indexOf(';') !== -1) {\n throw new Error('rawQuery must contain exactly one statement')\n }\n for (var i = 0; i < BLOCKED_RAW_QUERY_PATTERNS.length; i++) {\n if (BLOCKED_RAW_QUERY_PATTERNS[i].test(trimmed)) {\n throw new Error('rawQuery contains a blocked statement')\n }\n }\n}\n\nexport default async function handler(req, res) {\n").concat(generateReadGuardCall(tableName, 'req.query && req.query.rawQuery'), "\n const client = getClient()\n\n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n\n // If the caller supplied a rawQuery, run it verbatim (after a safety\n // guard). The schema-driven branch below builds `SELECT * FROM ").concat(tableName, "`\n // with optional filters \u2014 it's the default read path and ignores\n // rawQuery. Page-load workflows and other consumers that need a JOIN\n // or a user-scoped filter pass their fully-rendered SQL here via\n // `fetchData({ rawQuery })` and expect it to execute verbatim.\n if (req.query && typeof req.query.rawQuery === 'string' && req.query.rawQuery.length > 0) {\n assertRawQuerySafe(req.query.rawQuery)\n // A page's own statement, read for a visitor, runs over the rows a\n // visitor is served of every narrowed table it names.\n const rawSql = __rowAccess.narrowRaw ? __brpNarrowRawQuery(req.query.rawQuery) : req.query.rawQuery\n const rawResult = await client.query(rawSql)\n const rawRows = Array.isArray(rawResult?.rows) ? rawResult.rows : []\n const rawPlain = rawRows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const rawSafe = JSON.parse(JSON.stringify(rawPlain, dateReplacer))\n return res.status(200).json({\n success: true,\n // Every row here ends up in a browser (a page's props or a client\n // refetch), so no credential column survives, whatever joined it.\n data: __taWithoutCredentials(\n __rowAccess.narrowRaw ? __brpStripHiddenOfRaw(req.query.rawQuery, rawSafe) : rawSafe,\n null\n ),\n timestamp: Date.now()\n })\n }\n\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n // A visitor never filters, sorts or searches by a column it is not served.\n if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)\n // The language the page is in - what a translatable sort orders by.\n const requestLocale = typeof req.query.").concat(REQUEST_LOCALE_PARAM, " === 'string' ? req.query.").concat(REQUEST_LOCALE_PARAM, " : null\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n let searchRank = ''\n let searchFrom = ''\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(schema
|
|
60
|
+
return "".concat(PG_CLIENT_IMPORT, "\n\n").concat(generateTableAccessPreamble(transformOptions.trustedReaderRoles || [], transformOptions.routeExposure ||
|
|
61
|
+
DataExposure.readExposureFor(transformOptions.dataExposure, { tableName: tableName }), sqlRouteFieldPolicy(config, tableName), tableName, transformOptions), "\nfunction normalizePostgresConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n if (/^postgresql:\\/(?!\\/)/i.test(connectionString)) {\n return connectionString.replace(/^postgresql:\\//i, 'postgresql://');\n }\n return connectionString;\n}\n\nfunction stripSslQueryParamsFromConnectionString(connectionString) {\n if (!connectionString || typeof connectionString !== 'string') return connectionString;\n try {\n var u = new URL(connectionString.replace(/^postgresql:/i, 'postgres:'));\n u.searchParams.delete('sslmode');\n u.searchParams.delete('ssl');\n u.searchParams.delete('sslrootcert');\n u.searchParams.delete('sslcert');\n u.searchParams.delete('sslkey');\n return u.toString().replace(/^postgres:/i, 'postgresql:');\n } catch (e) {\n return connectionString;\n }\n}\n\nconst getClient = () => {\n var ssl = ").concat(sslCode, ";\n var connStr = process.env.TELEPORT_DB_CONNECTION_STRING;\n if (connStr) {\n connStr = normalizePostgresConnectionString(connStr);\n }\n if (ssl === false && connStr) {\n connStr = stripSslQueryParamsFromConnectionString(connStr);\n }\n // No query waits on the database longer than this (enforced by the client:\n // a server-side statement_timeout does not survive a pooler).\n if (connStr) {\n return new Client(Object.assign(\n { connectionString: connStr, query_timeout: ").concat(DataExposure.READ_QUERY_TIMEOUT_MS, " },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n }\n return new Client(Object.assign(\n {\n host: ").concat(hostRef, ",\n port: parseInt(").concat(portRef, " || '5432', 10),\n user: ").concat(userRef, ",\n password: ").concat(passwordRef, ",\n database: ").concat(databaseRef, ",\n query_timeout: ").concat(DataExposure.READ_QUERY_TIMEOUT_MS, ",\n },\n ssl !== undefined ? { ssl: ssl } : {}\n ));\n}\n\n").concat(generateSafeJSONParseCode(), "\n\n").concat(generateFilterTreeHelpersCode(), "\n\n").concat(generateSearchEscapeHelpersCode(), "\n").concat(generateSearchSqlHelpersCode(PG_SEARCH_SQL), "\n").concat(generateArrayOverlapSqlCode(), "\n").concat(getTransformationCode(tableName, transformOptions), "\n").concat(getTransformWrapperCode(tableName, transformOptions), "\n").concat(generateRequestSqlGuardsCode(), "\nconst processFilters = (filters, conditions, queryParams, paramIndex) => {\n if (!filters) return paramIndex\n \n const filterTree = normalizeFilterTree(safeJSONParse(filters))\n if (!filterTree) return paramIndex\n \n const buildCondition = (condition) => {\n const field = condition.source\n const normalizedCondition = normalizeInOperand(condition.operand, condition.destination)\n if (normalizedCondition === null) return null\n const value = normalizedCondition.value\n const operand = normalizedCondition.operand\n \n // The products table's virtual fields (effective price, rating bucket,\n // on-sale flag, variant axes) are whole clauses, not columns - so they are\n // answered before any branch below can interpolate the field name raw.\n const productClause = productFilterClause(field, operand, value, (param) => {\n queryParams.push(param)\n return '$' + paramIndex++\n })\n if (productClause !== null) return productClause\n\n // A translatable column is compared against the value in every language,\n // so a localized slug resolves under any locale - see localized-columns.ts.\n const localizedClause = localizedEqualityClause(field, operand, value, (param) => {\n queryParams.push(param)\n return '$' + paramIndex++\n })\n if (localizedClause !== null) return localizedClause\n assertRequestColumn(field, 'filter field')\n \n if (Array.isArray(value)) {\n if (value.length === 0) return null\n if (operand === 'array_overlap') {\n // The row's list (see array-overlap-sql.ts) shares any value with the set.\n const clause = arrayOverlapSql((localizedColumnList(field) || [field]), '$' + paramIndex)\n queryParams.push(value.map((entry) => String(entry)))\n paramIndex++\n return clause\n }\n const placeholders = value.map(() => `$${paramIndex++}`)\n queryParams.push(...value)\n return operand === '!='\n ? `${field} NOT IN (${placeholders.join(', ')})`\n : `${field} IN (${placeholders.join(', ')})`\n }\n \n if (operand === 'array_overlap') {\n if (value === '' || value === null || value === undefined) return null\n // A single comma-joined string (the multi-select Category Filter's\n // ?categoryFilter=a,b,c) expands to multiple ids; one id stays one.\n const overlapValues = String(value).split(',').map((entry) => entry.trim()).filter(Boolean)\n if (overlapValues.length === 0) return null\n const clause = arrayOverlapSql((localizedColumnList(field) || [field]), '$' + paramIndex)\n queryParams.push(overlapValues)\n paramIndex++\n return clause\n }\n \n if (value === null) {\n if (operand === '=') return `${field} IS NULL`\n if (operand === '!=') return `${field} IS NOT NULL`\n return null\n }\n \n // 'like' reads a JSON-list cell for one member (a post's former slugs); the\n // value is a bound parameter, so a pattern never reaches the SQL text.\n const validOps = ['=', '!=', '>', '<', '>=', '<=', 'like']\n const sqlOperator = validOps.includes(operand) ? operand : '='\n const clause = `${field} ${sqlOperator} $${paramIndex}`\n queryParams.push(value)\n paramIndex++\n return clause\n }\n \n const clause = buildFilterTreeClause(filterTree, buildCondition)\n if (clause) conditions.push(clause)\n \n return paramIndex\n}\n\n").concat(generateDateFormatterCode(), "\n\n").concat(generateSortFieldSqlHelper(tableName), "\n\n").concat(generateSortFallbackFieldHelper(tableName), "\n\n").concat(generateRowOrderTiebreakHelper(rowIdentityColumn(config, tableName)), "\n\n").concat(generateProductFilterClauseHelper(tableName), "\n\n").concat(generateLocalizedColumnsHelper(localizedColumns), "\n\n// Matches DDL / dangerous statements the raw-query branch should refuse.\n// Keep this list conservative \u2014 anything destructive or schema-changing is\n// out of scope for a client-triggered fetch. SELECT and CTEs (WITH ...) are\n// the only shapes consumers legitimately need.\nconst BLOCKED_RAW_QUERY_PATTERNS = [\n /\\bcreate\\s+(?:temporary\\s+|temp\\s+|unlogged\\s+|global\\s+|local\\s+)?table\\b/i,\n /\\bcreate\\s+(?:unique\\s+)?index\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:materialized\\s+)?view\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?trigger\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?(?:aggregate\\s+)?function\\b/i,\n /\\bcreate\\s+(?:or\\s+replace\\s+)?procedure\\b/i,\n /\\bcreate\\s+(?:database|schema|sequence|extension|type|role|user)\\b/i,\n /\\bdrop\\s+(?:table|view|index|schema|database|sequence|trigger|function|procedure|role|user|extension|type|materialized)\\b/i,\n /\\balter\\s+(?:table|view|index|schema|database|sequence|role|user|system)\\b/i,\n /\\btruncate\\b/i,\n /\\bgrant\\b/i,\n /\\brevoke\\b/i,\n /\\binsert\\b/i,\n /\\bupdate\\b/i,\n /\\bdelete\\b/i,\n /\\bcopy\\b/i,\n /\\bvacuum\\b/i,\n /\\breindex\\b/i,\n /\\bcluster\\b/i,\n]\n\nfunction assertRawQuerySafe(rawQuery) {\n if (typeof rawQuery !== 'string' || rawQuery.length === 0) {\n throw new Error('rawQuery must be a non-empty string')\n }\n // Reject multi-statement payloads \u2014 only single SELECT / WITH statements.\n // A trailing semicolon is tolerated but any content after it fails.\n var trimmed = rawQuery.trim().replace(/;\\s*$/, '')\n if (trimmed.indexOf(';') !== -1) {\n throw new Error('rawQuery must contain exactly one statement')\n }\n for (var i = 0; i < BLOCKED_RAW_QUERY_PATTERNS.length; i++) {\n if (BLOCKED_RAW_QUERY_PATTERNS[i].test(trimmed)) {\n throw new Error('rawQuery contains a blocked statement')\n }\n }\n}\n\nexport default async function handler(req, res) {\n").concat(generateReadGuardCall(tableName, 'req.query && req.query.rawQuery'), "\n const client = getClient()\n\n try {\n await client.connect()\n ").concat(schema ? "await client.query('SET search_path TO ".concat(schema, "')") : '', "\n\n // If the caller supplied a rawQuery, run it verbatim (after a safety\n // guard). The schema-driven branch below builds `SELECT * FROM ").concat(tableName, "`\n // with optional filters \u2014 it's the default read path and ignores\n // rawQuery. Page-load workflows and other consumers that need a JOIN\n // or a user-scoped filter pass their fully-rendered SQL here via\n // `fetchData({ rawQuery })` and expect it to execute verbatim.\n if (req.query && typeof req.query.rawQuery === 'string' && req.query.rawQuery.length > 0) {\n assertRawQuerySafe(req.query.rawQuery)\n // A page's own statement, read for a visitor, runs over the rows a\n // visitor is served of every narrowed table it names.\n const rawSql = __rowAccess.narrowRaw ? __brpNarrowRawQuery(req.query.rawQuery) : req.query.rawQuery\n const rawResult = await client.query(rawSql)\n const rawRows = Array.isArray(rawResult?.rows) ? rawResult.rows : []\n const rawPlain = rawRows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const rawSafe = JSON.parse(JSON.stringify(rawPlain, dateReplacer))\n return res.status(200).json({\n success: true,\n // Every row here ends up in a browser (a page's props or a client\n // refetch), so no credential column survives, whatever joined it.\n data: __taWithoutCredentials(\n __rowAccess.narrowRaw ? __brpStripHiddenOfRaw(req.query.rawQuery, rawSafe) : rawSafe,\n null\n ),\n timestamp: Date.now()\n })\n }\n\n const { query, queryColumns, limit, page, perPage, sortBy, sortOrder, filters, sorts, offset } = req.query\n // A visitor never filters, sorts or searches by a column it is not served.\n if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)\n // The language the page is in - what a translatable sort orders by.\n const requestLocale = typeof req.query.").concat(REQUEST_LOCALE_PARAM, " === 'string' ? req.query.").concat(REQUEST_LOCALE_PARAM, " : null\n \n const conditions = []\n const queryParams = []\n let paramIndex = 1\n let searchRank = ''\n let searchFrom = ''\n \n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : (parsed ? [parsed] : [])\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(schema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(schema
|
|
60
62
|
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(schema), "]")
|
|
61
|
-
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n const search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n searchRank = search.rank\n searchFrom = search.from\n }\n }\n \n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n \n // A search's worked-out texts stay out of the rows: only the table's own columns.\n let sql = `SELECT ${searchFrom ? '").concat(tableName, ".*' : '*'} FROM ").concat(tableName, "${searchFrom}`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. Two ORDER BY clauses are built: the one\n // actually used (a translatable column in the request's language, the\n // products table's price rewrites, rows without a value last), and a\n // plain-column twin kept only as the fallback below.\n // A search orders its matches by where the words are first; the sort\n // orders the rows within each of those groups.\n let orderBySql = ''\n let plainOrderBySql = ''\n const searchOrder = searchRank ? searchRank + ', ' : ''\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const valid = parsedSorts.filter((sort) => sort && sort.field)\n valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))\n const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const orderClauses = valid.map((sort) => `${localizedSortFieldSql(sort.field, requestLocale) || sortFieldSql(sort.field)} ${orderOf(sort)}").concat(SORT_NULLS_LAST_SQL, "`)\n const plainClauses = valid.map((sort) => `${sortFallbackField(sort.field)} ${orderOf(sort)}`)\n\n if (orderClauses.length > 0) {\n // The row's own key last, so equal rows can't repeat across pages\n // or vanish between them.\n const tiebreak = rowOrderTiebreak(valid.map((sort) => sort.field), orderOf(valid[valid.length - 1]))\n orderBySql = ` ORDER BY ${searchOrder}${orderClauses.join(', ')}${tiebreak}`\n plainOrderBySql = ` ORDER BY ${searchOrder}${plainClauses.join(', ')}${tiebreak}`\n }\n }\n } else if (sortBy) {\n assertRequestColumn(sortBy, 'sort field')\n const sortByDirection = (sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const tiebreak = rowOrderTiebreak([sortBy], sortByDirection)\n orderBySql = ` ORDER BY ${searchOrder}${localizedSortFieldSql(sortBy, requestLocale) || sortBy} ${sortByDirection}").concat(SORT_NULLS_LAST_SQL, "${tiebreak}`\n plainOrderBySql = ` ORDER BY ${searchOrder}${sortBy} ${sortByDirection}${tiebreak}`\n }\n if (!orderBySql && searchRank) {\n orderBySql = plainOrderBySql = ' ORDER BY ' + searchRank + rowOrderTiebreak([], 'ASC')\n }\n const usedRewrittenSort = orderBySql !== plainOrderBySql\n\n const limitValue =
|
|
63
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n // A credential column is never searched: a search is an oracle for it.\n columns = schemaResult.rows.map(row => row.column_name).filter((column) => !__taIsCredentialColumn(column))\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n const search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n searchRank = search.rank\n searchFrom = search.from\n }\n }\n \n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n \n // A search's worked-out texts stay out of the rows: only the table's own columns.\n let sql = `SELECT ${searchFrom ? '").concat(tableName, ".*' : '*'} FROM ").concat(tableName, "${searchFrom}`\n \n if (conditions.length > 0) {\n sql += ` WHERE ${conditions.join(' AND ')}`\n }\n \n // Handle sorts - new array format. Two ORDER BY clauses are built: the one\n // actually used (a translatable column in the request's language, the\n // products table's price rewrites, rows without a value last), and a\n // plain-column twin kept only as the fallback below.\n // A search orders its matches by where the words are first; the sort\n // orders the rows within each of those groups.\n let orderBySql = ''\n let plainOrderBySql = ''\n const searchOrder = searchRank ? searchRank + ', ' : ''\n if (sorts) {\n const parsedSorts = safeJSONParse(sorts)\n if (Array.isArray(parsedSorts) && parsedSorts.length > 0) {\n const valid = parsedSorts.filter((sort) => sort && sort.field)\n valid.forEach((sort) => assertRequestColumn(sort.field, 'sort field'))\n const orderOf = (sort) => (sort.order || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const orderClauses = valid.map((sort) => `${localizedSortFieldSql(sort.field, requestLocale) || sortFieldSql(sort.field)} ${orderOf(sort)}").concat(SORT_NULLS_LAST_SQL, "`)\n const plainClauses = valid.map((sort) => `${sortFallbackField(sort.field)} ${orderOf(sort)}`)\n\n if (orderClauses.length > 0) {\n // The row's own key last, so equal rows can't repeat across pages\n // or vanish between them.\n const tiebreak = rowOrderTiebreak(valid.map((sort) => sort.field), orderOf(valid[valid.length - 1]))\n orderBySql = ` ORDER BY ${searchOrder}${orderClauses.join(', ')}${tiebreak}`\n plainOrderBySql = ` ORDER BY ${searchOrder}${plainClauses.join(', ')}${tiebreak}`\n }\n }\n } else if (sortBy) {\n assertRequestColumn(sortBy, 'sort field')\n const sortByDirection = (sortOrder || '').toUpperCase().startsWith('DESC') ? 'DESC' : 'ASC'\n const tiebreak = rowOrderTiebreak([sortBy], sortByDirection)\n orderBySql = ` ORDER BY ${searchOrder}${localizedSortFieldSql(sortBy, requestLocale) || sortBy} ${sortByDirection}").concat(SORT_NULLS_LAST_SQL, "${tiebreak}`\n plainOrderBySql = ` ORDER BY ${searchOrder}${sortBy} ${sortByDirection}${tiebreak}`\n }\n if (!orderBySql && searchRank) {\n orderBySql = plainOrderBySql = ' ORDER BY ' + searchRank + rowOrderTiebreak([], 'ASC')\n }\n const usedRewrittenSort = orderBySql !== plainOrderBySql\n\n // Whole numbers only, and a browser's read capped - see __taReadWindow.\n const __window = __taReadWindow(req)\n const limitValue = __window.limit || __window.perPage\n const offsetValue = __window.offset !== undefined ? __window.offset : (__window.page && __window.perPage ? (__window.page - 1) * __window.perPage : undefined)\n\n let sqlTail = ''\n if (limitValue !== undefined) {\n sqlTail += ` LIMIT ${limitValue}`\n }\n \n if (offsetValue !== undefined) {\n sqlTail += ` OFFSET ${offsetValue}`\n }\n\n const baseSql = sql\n sql = baseSql + orderBySql + sqlTail\n\n // The discount-aware price ordering is an inline sub-select over a JSON\n // column. It is written to be unraisable, but it runs inside ORDER BY for\n // the whole table - so if a database ever rejects it, fall back to ordering\n // by the stored list price rather than serving an empty products page. The\n // translatable-column ordering takes the same exit: a per-language column\n // the schema had at generation time may have been dropped since. So does\n // the empty-values-last ordering.\n let result\n try {\n result = await client.query(sql, queryParams)\n } catch (sortError) {\n if (!usedRewrittenSort) throw sortError\n console.warn(\n 'Rewritten sort failed; falling back to the plain column:',\n sortError && sortError.message\n )\n result = await client.query(baseSql + plainOrderBySql + sqlTail, queryParams)\n }\n const rows = Array.isArray(result?.rows) ? result.rows : []\n const plainRows = rows.map((row) =>\n row && typeof row.toJSON === 'function' ? row.toJSON() : row\n )\n const safeData = JSON.parse(JSON.stringify(plainRows, dateReplacer))\n ").concat(getTransformExpression(tableName)
|
|
62
64
|
? "const transformedData = ".concat(getTransformExpression(tableName))
|
|
63
65
|
: '', "\n\n // Stamp each row with a globally-correct 1-based `__rowNumber` (the page\n // offset is applied server-side above), so list index columns keep counting\n // across pages instead of restarting at 1. Renderer-internal field \u2014 it is\n // filtered from ordinary field bindings and only read by the index column.\n const __finalData = ").concat(getTransformExpression(tableName) ? 'transformedData' : 'safeData', "\n const __rowNumberStart = (typeof offsetValue === 'number' && !isNaN(offsetValue)) ? offsetValue : 0\n const __numberedData = Array.isArray(__finalData)\n ? __finalData.map((__row, __i) =>\n __row && typeof __row === 'object' && !Array.isArray(__row)\n ? Object.assign({}, __row, { __rowNumber: __rowNumberStart + __i + 1 })\n : __row)\n : __finalData\n\n return res.status(200).json({\n success: true,\n data: __taWithoutCredentials(\n __rowPolicy ? __brpStripHidden(__rowPolicy, __numberedData) : __numberedData,\n ").concat(JSON.stringify(tableName), "\n ),\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Teleport DB fetch error:', error)\n // A database error names tables, columns and values: it stays in the server\n // log. Only an error this route raised for the caller (it carries a status)\n // is repeated to it.\n return res.status(error.status || 500).json({\n success: false,\n error: error.status ? error.message : 'Failed to fetch data',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing database client:', error)\n }\n }\n }\n}\n");
|
|
64
66
|
};
|
|
@@ -68,6 +70,6 @@ export var generateTeleportCountFetcher = function (config, tableName) {
|
|
|
68
70
|
var hasSchema = !!((_a = dbConfig.options) === null || _a === void 0 ? void 0 : _a.schema);
|
|
69
71
|
return "\nasync function getCount(req, res) {\n".concat(generateReadGuardCall(tableName, null), "\n const client = getClient()\n\n try {\n await client.connect()\n const { query, queryColumns, filters } = req.query\n if (__rowPolicy) __brpAssertVisibleFields(__rowPolicy, req.query)\n const conditions = []\n const queryParams = []\n let paramIndex = 1\n let searchFrom = ''\n\n if (query) {\n let columns = []\n \n if (queryColumns) {\n const parsed = safeJSONParse(queryColumns)\n columns = Array.isArray(parsed) ? parsed : [parsed]\n } else {\n try {\n const schemaQuery = `\n SELECT column_name \n FROM information_schema.columns \n WHERE table_name = $1\n ").concat(hasSchema ? "AND table_schema = $2" : '', "\n ORDER BY ordinal_position\n `\n const schemaParams = ").concat(hasSchema
|
|
70
72
|
? "[".concat(JSON.stringify(tableName), ", ").concat(JSON.stringify(dbConfig.options.schema), "]")
|
|
71
|
-
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n columns = schemaResult.rows.map(row => row.column_name)\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n const search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n searchFrom = search.from\n }\n }\n\n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n\n let countSql = `SELECT COUNT(*) FROM ").concat(tableName, "${searchFrom}`\n if (conditions.length > 0) {\n countSql += ` WHERE ${conditions.join(' AND ')}`\n }\n\n const result = await client.query(countSql, queryParams)\n const count = parseInt(result.rows[0].count, 10)\n\n return res.status(200).json({\n success: true,\n count: count,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(error.status || 500).json({\n success: false,\n error: error.status ? error.message : 'Failed to get count',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing database client:', error)\n }\n }\n }\n}\n");
|
|
73
|
+
: "[".concat(JSON.stringify(tableName), "]"), "\n \n const schemaResult = await client.query(schemaQuery, schemaParams)\n // A credential column is never searched: a search is an oracle for it.\n columns = schemaResult.rows.map(row => row.column_name).filter((column) => !__taIsCredentialColumn(column))\n } catch (schemaError) {\n console.warn('Failed to fetch column names from information_schema:', schemaError.message)\n }\n }\n if (__rowPolicy) columns = __brpVisibleColumns(__rowPolicy, columns)\n \n const search = buildSearchSql(query, columns, paramIndex)\n if (search) {\n conditions.push(search.where)\n queryParams.push(...search.params)\n paramIndex = search.nextIndex\n searchFrom = search.from\n }\n }\n\n paramIndex = processFilters(filters, conditions, queryParams, paramIndex)\n if (__rowPolicy) conditions.push('(' + __rowPolicy.predicate + ')')\n\n let countSql = `SELECT COUNT(*) FROM ").concat(tableName, "${searchFrom}`\n if (conditions.length > 0) {\n countSql += ` WHERE ${conditions.join(' AND ')}`\n }\n\n const result = await client.query(countSql, queryParams)\n const count = parseInt(result.rows[0].count, 10)\n\n return res.status(200).json({\n success: true,\n count: count,\n timestamp: Date.now()\n })\n } catch (error) {\n console.error('Error getting count:', error)\n return res.status(error.status || 500).json({\n success: false,\n error: error.status ? error.message : 'Failed to get count',\n timestamp: Date.now()\n })\n } finally {\n if (client) {\n try {\n await client.end()\n } catch (error) {\n console.error('Error closing database client:', error)\n }\n }\n }\n}\n");
|
|
72
74
|
};
|
|
73
75
|
//# sourceMappingURL=teleport.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teleport.js","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,+BAA+B,GAChC,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,GAExB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,+BAA+B,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AACnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAA;AAC5E,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAA;AAC9F,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"teleport.js","sourceRoot":"","sources":["../../../src/fetchers/teleport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DataReadExposure } from '@teleporthq/teleport-types';
|
|
2
|
+
import { RequestFieldPolicy, SchemaColumn, SessionReaderPlacement } from './table-access-guard';
|
|
3
|
+
/**
|
|
4
|
+
* The same guard for a read route over an EXTERNAL source (MySQL, MariaDB,
|
|
5
|
+
* Redshift, ClickHouse, Turso, Supabase, MongoDB, Firestore, Airtable, Redis,
|
|
6
|
+
* REST, Google Sheets, CSV, JavaScript) as the TeleportHQ / Postgres routes
|
|
7
|
+
* carry inline: the table's exposure, the protected-table net, a row window of
|
|
8
|
+
* whole numbers (a browser's capped), request fields of a safe shape, no
|
|
9
|
+
* credential column in a row, and a generic error to the client.
|
|
10
|
+
*
|
|
11
|
+
* The drivers differ in everything else, so the guard wraps the driver's
|
|
12
|
+
* handler rather than living inside fourteen of them: the driver runs only for
|
|
13
|
+
* a request the guard let through, with `limit` / `perPage` / `page` /
|
|
14
|
+
* `offset` replaced by the whole numbers the guard settled on — a driver that
|
|
15
|
+
* splices them into its SQL text splices digits — and its answer passes back
|
|
16
|
+
* through the guard, which strips credentials and replaces any error text.
|
|
17
|
+
*/
|
|
18
|
+
/** How a request may name a field for a driver. */
|
|
19
|
+
export type RequestFieldRule = 'identifier' | 'label' | 'any';
|
|
20
|
+
/** SQL drivers splice the field into their statement; document stores read it as a path or an operator. */
|
|
21
|
+
export declare const requestFieldRuleOf: (dataSourceType: string) => RequestFieldRule;
|
|
22
|
+
/**
|
|
23
|
+
* What a request may name for a driver (see `RequestFieldPolicy`): the
|
|
24
|
+
* export's schema of the table — required for a SQL driver, so a table without
|
|
25
|
+
* one answers every named field 400 — and the driver's own shape rule.
|
|
26
|
+
*/
|
|
27
|
+
export declare const driverFieldPolicy: (dataSourceType: string, schemaColumns: string[] | null) => RequestFieldPolicy;
|
|
28
|
+
/**
|
|
29
|
+
* The columns a browser's search runs over when it names none (N3): the
|
|
30
|
+
* table's schema — for Supabase, whose PostgREST `ilike` refuses any other
|
|
31
|
+
* type, its text columns — else, for a source without one, the fields the
|
|
32
|
+
* project's own bindings read. Never a credential column: what a search
|
|
33
|
+
* matches, it lets the searcher test character by character.
|
|
34
|
+
*/
|
|
35
|
+
export declare const defaultSearchColumns: (dataSourceType: string, schema: SchemaColumn[] | null, bindingFields: string[]) => string[];
|
|
36
|
+
/**
|
|
37
|
+
* The driver's fetcher module with the guard around its handler: same
|
|
38
|
+
* imports, the preamble, the driver renamed, and a guarded `handler` exported
|
|
39
|
+
* in its place.
|
|
40
|
+
*/
|
|
41
|
+
export declare const guardExternalDriverModule: (driverCode: string, params: {
|
|
42
|
+
tableName: string;
|
|
43
|
+
dataSourceType: string;
|
|
44
|
+
exposure: DataReadExposure;
|
|
45
|
+
trustedReaderRoles: ReadonlyArray<string>;
|
|
46
|
+
/** The table's columns as the data source config lists them (null: no schema). */
|
|
47
|
+
schema?: SchemaColumn[] | null;
|
|
48
|
+
/** Where the route sits and whether its session reader checks revocation. */
|
|
49
|
+
reader?: SessionReaderPlacement;
|
|
50
|
+
}) => string;
|
|
51
|
+
/** The driver's count handler behind the same guard (the guard code comes with the handler module). */
|
|
52
|
+
export declare const guardExternalDriverCount: (countCode: string) => string;
|
|
53
|
+
//# sourceMappingURL=external-read-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-read-guard.d.ts","sourceRoot":"","sources":["../../../../src/fetchers/utils/external-read-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,OAAO,EAEL,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACvB,MAAM,sBAAsB,CAAA;AAE7B;;;;;;;;;;;;;;GAcG;AAEH,mDAAmD;AACnD,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,OAAO,GAAG,KAAK,CAAA;AAgB7D,2GAA2G;AAC3G,eAAO,MAAM,kBAAkB,mBAAoB,MAAM,KAAG,gBAkB3D,CAAA;AAKD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,mBACZ,MAAM,iBACP,MAAM,EAAE,GAAG,IAAI,KAC7B,kBAQF,CAAA;AAaD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,mBACf,MAAM,UACd,YAAY,EAAE,GAAG,IAAI,iBACd,MAAM,EAAE,KACtB,MAAM,EAQR,CAAA;AA+GD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,eACxB,MAAM;eAEL,MAAM;oBACD,MAAM;cACZ,gBAAgB;wBACN,cAAc,MAAM,CAAC;IACzC,kFAAkF;aACzE,YAAY,EAAE,GAAG,IAAI;IAC9B,6EAA6E;aACpE,sBAAsB;MAEhC,MAiCF,CAAA;AAED,uGAAuG;AACvG,eAAO,MAAM,wBAAwB,cAAe,MAAM,KAAG,MAU5D,CAAA"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { DataExposure, TableAccess } from '@teleporthq/teleport-shared';
|
|
13
|
+
import { generateTableAccessPreamble, } from './table-access-guard';
|
|
14
|
+
/**
|
|
15
|
+
* A SQL column: plain, quoted or table-qualified — what the Postgres routes
|
|
16
|
+
* accept (`request-sql-guards.ts`).
|
|
17
|
+
*/
|
|
18
|
+
var IDENTIFIER_FIELD_RE = /^"?[A-Za-z_][A-Za-z0-9_]*"?(?:\."?[A-Za-z_][A-Za-z0-9_]*"?)?$/;
|
|
19
|
+
/**
|
|
20
|
+
* A field of a document store or a base whose names are labels: anything but
|
|
21
|
+
* the characters its query language gives a meaning (`{}` closes an Airtable
|
|
22
|
+
* field reference, `$` opens a MongoDB operator, quotes and parentheses open
|
|
23
|
+
* literals and calls).
|
|
24
|
+
*/
|
|
25
|
+
var LABEL_FIELD_RE = /^[^{}'"`\\(),;$]{1,128}$/;
|
|
26
|
+
/** SQL drivers splice the field into their statement; document stores read it as a path or an operator. */
|
|
27
|
+
export var requestFieldRuleOf = function (dataSourceType) {
|
|
28
|
+
switch (dataSourceType) {
|
|
29
|
+
case 'mysql':
|
|
30
|
+
case 'tidb':
|
|
31
|
+
case 'mariadb':
|
|
32
|
+
case 'amazon-redshift':
|
|
33
|
+
case 'clickhouse':
|
|
34
|
+
case 'turso':
|
|
35
|
+
case 'supabase':
|
|
36
|
+
return 'identifier';
|
|
37
|
+
case 'mongodb':
|
|
38
|
+
case 'firestore':
|
|
39
|
+
case 'airtable':
|
|
40
|
+
return 'label';
|
|
41
|
+
default:
|
|
42
|
+
// REST, JavaScript, CSV, Google Sheets, Redis filter the rows in memory.
|
|
43
|
+
return 'any';
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/** The stores that read a `$`-key as an operator or a dotted key as a path. */
|
|
47
|
+
var DOCUMENT_STORES = new Set(['mongodb', 'firestore', 'redis']);
|
|
48
|
+
/**
|
|
49
|
+
* What a request may name for a driver (see `RequestFieldPolicy`): the
|
|
50
|
+
* export's schema of the table — required for a SQL driver, so a table without
|
|
51
|
+
* one answers every named field 400 — and the driver's own shape rule.
|
|
52
|
+
*/
|
|
53
|
+
export var driverFieldPolicy = function (dataSourceType, schemaColumns) {
|
|
54
|
+
var rule = requestFieldRuleOf(dataSourceType);
|
|
55
|
+
return __assign(__assign({ columns: rule === 'identifier' ? schemaColumns || [] : schemaColumns, documentStore: DOCUMENT_STORES.has(dataSourceType) }, (rule === 'identifier' && { pattern: IDENTIFIER_FIELD_RE })), (rule === 'label' && { pattern: LABEL_FIELD_RE }));
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The stores whose search turns the text into a pattern of their own query
|
|
59
|
+
* language (MongoDB's `$regex`): the text is escaped first, so it is matched
|
|
60
|
+
* as text — never an anchored, character-by-character probe, never a pattern
|
|
61
|
+
* that takes the server's time.
|
|
62
|
+
*/
|
|
63
|
+
var PATTERN_SEARCH_STORES = new Set(['mongodb']);
|
|
64
|
+
/** A text type, as the sources name theirs (`text`, `varchar(255)`, `character varying`, `string`). */
|
|
65
|
+
var TEXT_COLUMN_TYPE = /char|text|string|clob|enum/i;
|
|
66
|
+
/**
|
|
67
|
+
* The columns a browser's search runs over when it names none (N3): the
|
|
68
|
+
* table's schema — for Supabase, whose PostgREST `ilike` refuses any other
|
|
69
|
+
* type, its text columns — else, for a source without one, the fields the
|
|
70
|
+
* project's own bindings read. Never a credential column: what a search
|
|
71
|
+
* matches, it lets the searcher test character by character.
|
|
72
|
+
*/
|
|
73
|
+
export var defaultSearchColumns = function (dataSourceType, schema, bindingFields) {
|
|
74
|
+
var textOnly = dataSourceType === 'supabase';
|
|
75
|
+
var candidates = schema
|
|
76
|
+
? schema
|
|
77
|
+
.filter(function (column) { return !textOnly || !column.type || TEXT_COLUMN_TYPE.test(column.type); })
|
|
78
|
+
.map(function (column) { return column.name; })
|
|
79
|
+
: bindingFields;
|
|
80
|
+
return Array.from(new Set(candidates)).filter(function (name) { return !TableAccess.isCredentialColumnName(name); });
|
|
81
|
+
};
|
|
82
|
+
/** The guard's own code, after the shared preamble. */
|
|
83
|
+
var generateDriverGuardCode = function (tableName, dataSourceType, searchColumns) { return "\n// GENERATED \u2014 see external-read-guard.ts in teleport-plugin-next-data-source.\nvar __TQ_DRIVER_TABLE = ".concat(JSON.stringify(tableName), ";\nvar __TQ_DRIVER_WINDOW_KEYS = ['limit', 'perPage', 'page', 'offset'];\nvar __TQ_DRIVER_TIMEOUT_MS = ").concat(DataExposure.READ_QUERY_TIMEOUT_MS, ";\n// What a browser's search runs over when it names no columns (the driver's own\n// fallback reads every column, credentials included).\nvar __TQ_DRIVER_SEARCH_COLUMNS = ").concat(JSON.stringify(searchColumns), ";\nvar __TQ_DRIVER_PATTERN_SEARCH = ").concat(PATTERN_SEARCH_STORES.has(dataSourceType) ? 'true' : 'false', ";\n\nasync function __taGuardedDriverRead(req, res, run, kind) {\n var failure = kind === 'count' ? 'Failed to get count' : 'Failed to fetch data';\n var exposure = await __taGuardExposure(req);\n if (exposure) {\n return res.status(exposure.status).json({ success: false, error: exposure.message, timestamp: Date.now() });\n }\n var access = await __taGuardRead(req, __TQ_DRIVER_TABLE, null);\n if (access) {\n return res.status(access.status).json({ success: false, error: access.message, timestamp: Date.now() });\n }\n var window;\n try {\n window = __taReadWindow(req);\n __taAssertReadRequest(req);\n } catch (error) {\n return res.status(400).json({ success: false, error: error && error.status ? error.message : 'Invalid request', timestamp: Date.now() });\n }\n var internal = __taIsInternalRequest(req);\n var query = Object.assign({}, (req && req.query) || {});\n __TQ_DRIVER_WINDOW_KEYS.forEach(function(key) {\n if (window[key] === undefined) delete query[key];\n else query[key] = String(window[key]);\n });\n var searching = query.query !== undefined && query.query !== null && String(query.query) !== '';\n if (searching && !internal && (query.queryColumns === undefined || query.queryColumns === null || query.queryColumns === '')) {\n if (__TQ_DRIVER_SEARCH_COLUMNS.length === 0) {\n return res.status(400).json({ success: false, error: 'Invalid search', timestamp: Date.now() });\n }\n query.queryColumns = JSON.stringify(__TQ_DRIVER_SEARCH_COLUMNS);\n }\n if (searching && __TQ_DRIVER_PATTERN_SEARCH) {\n query.query = String(Array.isArray(query.query) ? query.query[0] : query.query).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n var forwarded = {\n method: req && req.method,\n headers: (req && req.headers) || {},\n cookies: req && req.cookies,\n // A browser's body never reaches the driver: a REST source configured for\n // POST would send it upstream, under the site's credentials, in place of\n // the configured one. The app's own server code may still supply one.\n body: internal ? req && req.body : undefined,\n query: query,\n __tqServerCall: !!(req && req.__tqServerCall === true),\n __tqOwnerScope: req && req.__tqOwnerScope,\n };\n // A row-owned page's read: the owner condition joins the request's filters.\n __taApplyOwnerScope(forwarded);\n var status = 200;\n var payload = null;\n var proxy = {\n status: function(code) { status = code; return proxy; },\n json: function(data) { payload = data; return proxy; },\n setHeader: function(name, value) {\n if (res && typeof res.setHeader === 'function') res.setHeader(name, value);\n return proxy;\n },\n };\n // No read waits on the source longer than __TQ_DRIVER_TIMEOUT_MS: a driver\n // without a query timeout of its own is answered for when it overruns.\n var timer = null;\n try {\n await Promise.race([\n run(forwarded, proxy),\n new Promise(function(_resolve, reject) {\n timer = setTimeout(function() { reject(new Error('the read timed out')); }, __TQ_DRIVER_TIMEOUT_MS);\n }),\n ]);\n } catch (error) {\n status = 500;\n payload = { success: false, error: error };\n } finally {\n if (timer) clearTimeout(timer);\n }\n if (status !== 200 || !payload || payload.success === false) {\n // The driver's error names tables, columns and values: server log only.\n var id = __taErrorId();\n console.error('[data-source] reading ' + __TQ_DRIVER_TABLE + ' failed (' + id + '):', payload && payload.error);\n return res.status(status >= 400 && status < 500 ? status : 500).json({ success: false, error: failure, id: id, timestamp: Date.now() });\n }\n if (Array.isArray(payload.data)) {\n payload = Object.assign({}, payload, { data: __taWithoutCredentials(payload.data, __TQ_DRIVER_TABLE) });\n }\n return res.status(200).json(payload);\n}\n"); };
|
|
84
|
+
var UNGUARDED_HANDLER = '__tqUnguardedHandler';
|
|
85
|
+
var UNGUARDED_COUNT = '__tqUnguardedGetCount';
|
|
86
|
+
var DRIVER_HANDLER_DECLARATION = 'export default async function handler(';
|
|
87
|
+
var COUNT_DECLARATION = 'async function getCount(req, res)';
|
|
88
|
+
/** The driver module's single-line imports, which must stay at the top of an ES module. */
|
|
89
|
+
var IMPORT_LINE = /^import\s.*$/gm;
|
|
90
|
+
/**
|
|
91
|
+
* The driver's fetcher module with the guard around its handler: same
|
|
92
|
+
* imports, the preamble, the driver renamed, and a guarded `handler` exported
|
|
93
|
+
* in its place.
|
|
94
|
+
*/
|
|
95
|
+
export var guardExternalDriverModule = function (driverCode, params) {
|
|
96
|
+
var _a;
|
|
97
|
+
var schema = (_a = params.schema) !== null && _a !== void 0 ? _a : null;
|
|
98
|
+
var schemaColumns = schema ? schema.map(function (column) { return column.name; }) : null;
|
|
99
|
+
if (driverCode.split(DRIVER_HANDLER_DECLARATION).length !== 2) {
|
|
100
|
+
// A driver that is not one handler cannot be wrapped; refusing to generate
|
|
101
|
+
// it is the only answer that does not serve it unguarded.
|
|
102
|
+
throw new Error('a data source read route must declare exactly one handler');
|
|
103
|
+
}
|
|
104
|
+
var imports = driverCode.match(IMPORT_LINE) || [];
|
|
105
|
+
var body = driverCode
|
|
106
|
+
.replace(IMPORT_LINE, '')
|
|
107
|
+
.trim()
|
|
108
|
+
.replace(DRIVER_HANDLER_DECLARATION, "async function ".concat(UNGUARDED_HANDLER, "("));
|
|
109
|
+
return "".concat(imports.join('\n'), "\n\n").concat(generateTableAccessPreamble(params.trustedReaderRoles, params.exposure, driverFieldPolicy(params.dataSourceType, schemaColumns), params.tableName, params.reader)).concat(generateDriverGuardCode(params.tableName, params.dataSourceType, defaultSearchColumns(params.dataSourceType, schema, params.exposure.fields || [])), "\n").concat(body, "\n\nexport default async function handler(req, res) {\n return __taGuardedDriverRead(req, res, ").concat(UNGUARDED_HANDLER, ", 'data')\n}\n");
|
|
110
|
+
};
|
|
111
|
+
/** The driver's count handler behind the same guard (the guard code comes with the handler module). */
|
|
112
|
+
export var guardExternalDriverCount = function (countCode) {
|
|
113
|
+
if (countCode.split(COUNT_DECLARATION).length !== 2) {
|
|
114
|
+
throw new Error('a data source count route must declare exactly one getCount');
|
|
115
|
+
}
|
|
116
|
+
return "".concat(countCode.replace(COUNT_DECLARATION, "async function ".concat(UNGUARDED_COUNT, "(req, res)")), "\n\nasync function getCount(req, res) {\n return __taGuardedDriverRead(req, res, ").concat(UNGUARDED_COUNT, ", 'count')\n}\n");
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=external-read-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-read-guard.js","sourceRoot":"","sources":["../../../../src/fetchers/utils/external-read-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,83 @@
|
|
|
1
|
+
import type { DataReadExposure } from '@teleporthq/teleport-types';
|
|
1
2
|
/**
|
|
2
|
-
* The
|
|
3
|
+
* The guard of a generated per-table read route
|
|
3
4
|
* (`/api/teleport-<table>-<source>` and its `-count` twin).
|
|
4
5
|
*
|
|
5
|
-
* Every table a page binds gets such a route, and the route
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* Every table a page binds gets such a route, and the route is a URL anyone
|
|
7
|
+
* can call. Three nets, in this order:
|
|
8
|
+
* 1. the table's EXPOSURE, decided at export time from the pages that bind it
|
|
9
|
+
* (`DataExposure.resolveDataExposure`): a table a public page shows is read
|
|
10
|
+
* by anyone; one only protected pages bind needs a session one of them
|
|
11
|
+
* admits; one no page binds is the app's own server code's;
|
|
12
|
+
* 2. the protected tables (see `TableAccess.PROTECTED_TABLES`): money and
|
|
13
|
+
* customer records, read only by a server segment presenting the app
|
|
14
|
+
* secret or a signed-in member of a trusted role (the generated admin);
|
|
15
|
+
* 3. the browser row policy (see `browser-row-policy.ts`): unpublished rows
|
|
16
|
+
* and private columns of the public tables.
|
|
17
|
+
* A server-side caller (the app secret, or the module's own in-process
|
|
18
|
+
* `fetchData`) passes all three.
|
|
19
|
+
*
|
|
20
|
+
* Every row count a request names is a whole number, and a browser's read is
|
|
21
|
+
* capped (`__taReadWindow`): a missing count gets the table's default, a large
|
|
22
|
+
* one the table's maximum.
|
|
23
|
+
*/
|
|
24
|
+
/** No route reads more than this many rows past the first, from a browser. */
|
|
25
|
+
export declare const BROWSER_READ_MAX_OFFSET = 1000000;
|
|
26
|
+
/**
|
|
27
|
+
* What a request may name as a field of the route's table (C1):
|
|
28
|
+
* - `columns`: the columns the export knows the table has (its schema in the
|
|
29
|
+
* data source config, plus every field the project's own bindings of the
|
|
30
|
+
* table name), or null for a source that has none to know (REST,
|
|
31
|
+
* JavaScript, CSV, Google Sheets, Redis: they filter in memory);
|
|
32
|
+
* - `virtualFields` / `virtualPrefixes`: the names the route itself answers
|
|
33
|
+
* with an expression (the products' effective price, rating, variant axes);
|
|
34
|
+
* - `documentStore`: a store that reads a `$`-key as an operator or a dotted
|
|
35
|
+
* key as a path (MongoDB, Firestore, Redis).
|
|
36
|
+
* A table with a schema but no known column answers every named field 400.
|
|
37
|
+
*/
|
|
38
|
+
export interface RequestFieldPolicy {
|
|
39
|
+
columns: string[] | null;
|
|
40
|
+
virtualFields?: string[];
|
|
41
|
+
virtualPrefixes?: string[];
|
|
42
|
+
documentStore?: boolean;
|
|
43
|
+
/** The shape every named field must also have (a driver's identifier or label rule). */
|
|
44
|
+
pattern?: RegExp;
|
|
45
|
+
}
|
|
46
|
+
/** A column of `config.selectedTables[tableName]`: its name, and its type when the config gives one. */
|
|
47
|
+
export interface SchemaColumn {
|
|
48
|
+
name: string;
|
|
49
|
+
type?: string;
|
|
50
|
+
}
|
|
51
|
+
/** The columns `config.selectedTables[tableName]` lists, with their types, or null when the config holds no schema for it. */
|
|
52
|
+
export declare const schemaOf: (config: unknown, tableName: string) => SchemaColumn[] | null;
|
|
53
|
+
/** The columns `config.selectedTables[tableName]` lists, or null when the config holds no schema for it. */
|
|
54
|
+
export declare const schemaColumnsOf: (config: unknown, tableName: string) => string[] | null;
|
|
55
|
+
/**
|
|
56
|
+
* The exposure check, the row window and the request-field check, for a
|
|
57
|
+
* module whose table is `exposure`'s. Uses `__taIsInternalRequest`,
|
|
58
|
+
* `__taRoleOf` and `__tqSessionToken` from the preamble.
|
|
59
|
+
*/
|
|
60
|
+
export declare const generateRouteExposureCode: (exposure: DataReadExposure | null | undefined, fieldPolicy?: RequestFieldPolicy, tableName?: string) => string;
|
|
61
|
+
/**
|
|
62
|
+
* Where a read route sits and what its session reader checks (see
|
|
63
|
+
* `EntityTransformOptions.routeProjectRoot` / `.sessionRevocation`).
|
|
64
|
+
*/
|
|
65
|
+
export interface SessionReaderPlacement {
|
|
66
|
+
/** The route's path back to the project root; absent, two folders. */
|
|
67
|
+
routeProjectRoot?: string;
|
|
68
|
+
/** The project has authentication; absent, the reader checks revocation. */
|
|
69
|
+
sessionRevocation?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Declares the session resolver, the `__ta*` guard and the route's exposure
|
|
73
|
+
* once per module.
|
|
14
74
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
75
|
+
export declare const generateTableAccessPreamble: (trustedReaderRoles: ReadonlyArray<string>, exposure?: DataReadExposure | null, fieldPolicy?: RequestFieldPolicy, tableName?: string, reader?: SessionReaderPlacement) => string;
|
|
76
|
+
/** The products table's virtual filter and sort fields (`product-filter-fields.ts`, `product-price-sort.ts`). */
|
|
77
|
+
export declare const PRODUCT_VIRTUAL_FIELDS: string[];
|
|
78
|
+
export declare const PRODUCT_VIRTUAL_PREFIXES: string[];
|
|
79
|
+
/** What a TeleportHQ / Postgres route's request may name: its schema, plus the products' virtual fields. */
|
|
80
|
+
export declare const sqlRouteFieldPolicy: (config: unknown, tableName: string) => RequestFieldPolicy;
|
|
17
81
|
/**
|
|
18
82
|
* The early return at the top of a handler, before the client connects:
|
|
19
83
|
* `tableName` is the module's table, `rawQueryExpr` the request's raw statement
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-access-guard.d.ts","sourceRoot":"","sources":["../../../../src/fetchers/utils/table-access-guard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"table-access-guard.d.ts","sourceRoot":"","sources":["../../../../src/fetchers/utils/table-access-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAIlE;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,UAAU,CAAA;AAE9C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,8HAA8H;AAC9H,eAAO,MAAM,QAAQ,WAAY,OAAO,aAAa,MAAM,KAAG,YAAY,EAAE,GAAG,IAwB9E,CAAA;AAED,4GAA4G;AAC5G,eAAO,MAAM,eAAe,WAAY,OAAO,aAAa,MAAM,KAAG,MAAM,EAAE,GAAG,IAG/E,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,aAC1B,gBAAgB,GAAG,IAAI,GAAG,SAAS,gBAChC,kBAAkB,cACpB,MAAM,KAChB,MAuTF,CAAA;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,2BAA2B,uBAClB,cAAc,MAAM,CAAC,aAC9B,gBAAgB,GAAG,IAAI,gBACpB,kBAAkB,cACpB,MAAM,WACV,sBAAsB,KAC7B,MAUF,CAAA;AAED,iHAAiH;AACjH,eAAO,MAAM,sBAAsB,UAA8C,CAAA;AACjF,eAAO,MAAM,wBAAwB,UAAuB,CAAA;AAE5D,4GAA4G;AAC5G,eAAO,MAAM,mBAAmB,WAAY,OAAO,aAAa,MAAM,KAAG,kBASxE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,cAAe,MAAM,gBAAgB,MAAM,GAAG,IAAI,KAAG,MAqBtF,CAAA"}
|