Browse Source

Merge branch 'develop' into feat/pnpm

pull/5903/head
Wing-Kam Wong 1 year ago
parent
commit
3ee1747c6c
  1. 28
      packages/nc-gui/components/smartsheet/Grid.vue
  2. 9
      packages/nc-gui/components/smartsheet/Kanban.vue
  3. 6
      packages/nc-gui/components/smartsheet/Pagination.vue
  4. 4
      packages/nc-gui/components/smartsheet/header/Icon.vue
  5. 2
      packages/nc-gui/components/tabs/Smartsheet.vue
  6. 1
      packages/nc-gui/components/virtual-cell/components/ItemChip.vue
  7. 1
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  8. 1
      packages/nc-gui/components/virtual-cell/components/ListItems.vue
  9. 4
      packages/nc-gui/composables/useMultiSelect/index.ts
  10. 2
      packages/nc-lib-gui/package.json
  11. 2
      packages/nocodb-sdk/package.json
  12. 14
      packages/nocodb/package.json
  13. 2
      packages/nocodb/src/controllers/test/TestResetService/index.ts
  14. 262
      packages/nocodb/src/db/BaseModelSqlv2.ts
  15. 41
      packages/nocodb/src/db/sql-client/lib/mssql/MssqlClient.ts
  16. 2
      packages/nocodb/src/db/sql-client/lib/oracle/OracleClient.ts
  17. 2
      packages/nocodb/src/db/sql-client/lib/pg/PgClient.ts
  18. 73
      packages/nocodb/src/db/sql-client/lib/snowflake/SnowflakeClient.ts
  19. 1
      packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts
  20. 43
      packages/nocodb/src/db/sql-data-mapper/lib/BaseModel.ts
  21. 2
      packages/nocodb/src/db/sql-mgr/code/models/xc/ModelXcMetaMssql.ts
  22. 2
      packages/nocodb/src/db/sql-mgr/code/models/xc/ModelXcMetaMysql.ts
  23. 2
      packages/nocodb/src/db/sql-mgr/code/models/xc/ModelXcMetaOracle.ts
  24. 2
      packages/nocodb/src/db/sql-migrator/lib/KnexMigrator.ts
  25. 1
      packages/nocodb/src/db/sql-migrator/lib/KnexMigratorv2.ts
  26. 2
      packages/nocodb/src/helpers/apiMetrics.ts
  27. 2
      packages/nocodb/src/helpers/webhookHelpers.ts
  28. 2
      packages/nocodb/src/meta/migrations/v1/nc_011_remove_old_ses_plugin.ts
  29. 1
      packages/nocodb/src/models/Model.ts
  30. 1
      packages/nocodb/src/models/View.ts
  31. 2
      packages/nocodb/src/modules/jobs/jobs/at-import/at-import.controller.ts
  32. 69
      packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts
  33. 1
      packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts
  34. 38
      packages/nocodb/src/plugins/storage/Local.ts
  35. 4
      packages/nocodb/src/services/auth.service.ts
  36. 8
      packages/nocodb/src/services/tables.service.ts
  37. 1
      packages/nocodb/src/utils/common/XcAudit.ts

28
packages/nc-gui/components/smartsheet/Grid.vue

@ -112,7 +112,7 @@ const expandedFormRow = ref<Row>()
const expandedFormRowState = ref<Record<string, any>>() const expandedFormRowState = ref<Record<string, any>>()
const gridWrapper = ref<HTMLElement>() const gridWrapper = ref<HTMLElement>()
const tableHeadEl = ref<HTMLElement>() const tableHeadEl = ref<HTMLElement>()
const tableBodyEl = ref<HTMLElement>() const tableBodyEl = ref<HTMLTableSectionElement>()
const fillHandle = ref<HTMLElement>() const fillHandle = ref<HTMLElement>()
const gridRect = useElementBounding(gridWrapper) const gridRect = useElementBounding(gridWrapper)
@ -917,8 +917,6 @@ function addEmptyRow(row?: number) {
const fillHandleTop = ref() const fillHandleTop = ref()
const fillHandleLeft = ref() const fillHandleLeft = ref()
const cellRefs = ref<{ el: HTMLElement }[]>([])
const showFillHandle = computed( const showFillHandle = computed(
() => () =>
!readOnly.value && !readOnly.value &&
@ -929,17 +927,15 @@ const showFillHandle = computed(
) )
const refreshFillHandle = () => { const refreshFillHandle = () => {
const cellRef = cellRefs.value.find( nextTick(() => {
(cell) => const cellRef = document.querySelector('.last-cell')
cell.el.dataset.rowIndex === String(isNaN(selectedRange.end.row) ? activeCell.row : selectedRange.end.row) && if (cellRef) {
cell.el.dataset.colIndex === String(isNaN(selectedRange.end.col) ? activeCell.col : selectedRange.end.col), const cellRect = cellRef.getBoundingClientRect()
) if (!cellRect || !gridWrapper.value) return
if (cellRef) { fillHandleTop.value = cellRect.top + cellRect.height - gridRect.top.value + gridWrapper.value.scrollTop
const cellRect = useElementBounding(cellRef.el) fillHandleLeft.value = cellRect.left + cellRect.width - gridRect.left.value + gridWrapper.value.scrollLeft
if (!cellRect || !gridWrapper.value) return }
fillHandleTop.value = cellRect.top.value + cellRect.height.value - gridRect.top.value + gridWrapper.value.scrollTop })
fillHandleLeft.value = cellRect.left.value + cellRect.width.value - gridRect.left.value + gridWrapper.value.scrollLeft
}
} }
const addRowExpandOnClose = (row: Row) => { const addRowExpandOnClose = (row: Row) => {
@ -1132,7 +1128,6 @@ useEventListener(document, 'mouseup', () => {
<SmartsheetTableDataCell <SmartsheetTableDataCell
v-for="(columnObj, colIndex) of fields" v-for="(columnObj, colIndex) of fields"
:key="columnObj.id" :key="columnObj.id"
ref="cellRefs"
class="cell relative nc-grid-cell" class="cell relative nc-grid-cell"
:class="{ :class="{
'cursor-pointer': hasEditPermission, 'cursor-pointer': hasEditPermission,
@ -1141,6 +1136,9 @@ useEventListener(document, 'mouseup', () => {
hasEditPermission && hasEditPermission &&
((activeCell.row === rowIndex && activeCell.col === colIndex) || ((activeCell.row === rowIndex && activeCell.col === colIndex) ||
(selectedRange._start?.row === rowIndex && selectedRange._start?.col === colIndex)), (selectedRange._start?.row === rowIndex && selectedRange._start?.col === colIndex)),
'last-cell':
rowIndex === (isNaN(selectedRange.end.row) ? activeCell.row : selectedRange.end.row) &&
colIndex === (isNaN(selectedRange.end.col) ? activeCell.col : selectedRange.end.col),
'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row), 'nc-required-cell': isColumnRequiredAndNull(columnObj, row.row),
'align-middle': !rowHeight || rowHeight === 1, 'align-middle': !rowHeight || rowHeight === 1,
'align-top': rowHeight && rowHeight !== 1, 'align-top': rowHeight && rowHeight !== 1,

9
packages/nc-gui/components/smartsheet/Kanban.vue

@ -21,6 +21,7 @@ import {
onBeforeUnmount, onBeforeUnmount,
provide, provide,
useAttachment, useAttachment,
useDebounceFn,
useKanbanViewStoreOrThrow, useKanbanViewStoreOrThrow,
useUndoRedo, useUndoRedo,
} from '#imports' } from '#imports'
@ -30,6 +31,8 @@ interface Attachment {
url: string url: string
} }
const INFINITY_SCROLL_THRESHOLD = 100
const meta = inject(MetaInj, ref()) const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref()) const view = inject(ActiveViewInj, ref())
@ -275,14 +278,14 @@ async function onMove(event: any, stackKey: string) {
} }
} }
const kanbanListScrollHandler = async (e: any) => { const kanbanListScrollHandler = useDebounceFn(async (e: any) => {
if (e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight) { if (e.target.scrollTop + e.target.clientHeight + INFINITY_SCROLL_THRESHOLD >= e.target.scrollHeight) {
const stackTitle = e.target.getAttribute('data-stack-title') const stackTitle = e.target.getAttribute('data-stack-title')
const pageSize = appInfo.defaultLimit || 25 const pageSize = appInfo.defaultLimit || 25
const page = Math.ceil(formattedData.value.get(stackTitle)!.length / pageSize) const page = Math.ceil(formattedData.value.get(stackTitle)!.length / pageSize)
await loadMoreKanbanData(stackTitle, { offset: page * pageSize }) await loadMoreKanbanData(stackTitle, { offset: page * pageSize })
} }
} })
const kanbanListRef = (kanbanListElement: HTMLElement) => { const kanbanListRef = (kanbanListElement: HTMLElement) => {
if (kanbanListElement) { if (kanbanListElement) {

6
packages/nc-gui/components/smartsheet/Pagination.vue

@ -2,9 +2,11 @@
import { ChangePageInj, PaginationDataInj, computed, iconMap, inject, isRtlLang, useI18n } from '#imports' import { ChangePageInj, PaginationDataInj, computed, iconMap, inject, isRtlLang, useI18n } from '#imports'
import type { Language } from '~/lib' import type { Language } from '~/lib'
const props = defineProps<{ interface Props {
alignCountOnRight?: boolean alignCountOnRight?: boolean
}>() }
const { alignCountOnRight } = defineProps<Props>()
const { locale } = useI18n() const { locale } = useI18n()

4
packages/nc-gui/components/smartsheet/header/Icon.vue

@ -1,8 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ColumnType, isVirtualCol } from 'nocodb-sdk' import type { ColumnType } from 'nocodb-sdk'
import { isVirtualCol } from 'nocodb-sdk'
const { column } = defineProps<{ column: ColumnType }>() const { column } = defineProps<{ column: ColumnType }>()
</script> </script>
<template> <template>

2
packages/nc-gui/components/tabs/Smartsheet.vue

@ -72,7 +72,7 @@ const onDrop = async (event: DragEvent) => {
event.preventDefault() event.preventDefault()
try { try {
// Access the dropped data // Access the dropped data
const data = JSON.parse(event.dataTransfer?.getData('text/json')!) const data = JSON.parse(event.dataTransfer!.getData('text/json'))
// Do something with the received data // Do something with the received data
// if dragged item is not from the same base, return // if dragged item is not from the same base, return

1
packages/nc-gui/components/virtual-cell/components/ItemChip.vue

@ -9,7 +9,6 @@ import {
inject, inject,
isAttachment, isAttachment,
ref, ref,
renderValue,
useExpandedFormDetached, useExpandedFormDetached,
useLTARStoreOrThrow, useLTARStoreOrThrow,
} from '#imports' } from '#imports'

1
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -13,7 +13,6 @@ import {
iconMap, iconMap,
inject, inject,
ref, ref,
renderValue,
useLTARStoreOrThrow, useLTARStoreOrThrow,
useSmartsheetRowStoreOrThrow, useSmartsheetRowStoreOrThrow,
useVModel, useVModel,

1
packages/nc-gui/components/virtual-cell/components/ListItems.vue

@ -12,7 +12,6 @@ import {
inject, inject,
isDrawerExist, isDrawerExist,
ref, ref,
renderValue,
useLTARStoreOrThrow, useLTARStoreOrThrow,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
useSmartsheetRowStoreOrThrow, useSmartsheetRowStoreOrThrow,

4
packages/nc-gui/composables/useMultiSelect/index.ts

@ -26,7 +26,6 @@ import {
useI18n, useI18n,
useMetas, useMetas,
useProject, useProject,
useUIPermission,
} from '#imports' } from '#imports'
const MAIN_MOUSE_PRESSED = 0 const MAIN_MOUSE_PRESSED = 0
@ -80,9 +79,6 @@ export function useMultiSelect(
() => !(activeCell.row === null || activeCell.col === null || isNaN(activeCell.row) || isNaN(activeCell.col)), () => !(activeCell.row === null || activeCell.col === null || isNaN(activeCell.row) || isNaN(activeCell.col)),
) )
const { isUIAllowed } = useUIPermission()
const hasEditPermission = $computed(() => isUIAllowed('xcDatatableEditable'))
function makeActive(row: number, col: number) { function makeActive(row: number, col: number) {
if (activeCell.row === row && activeCell.col === col) { if (activeCell.row === row && activeCell.col === col) {
return return

2
packages/nc-lib-gui/package.json

@ -1,6 +1,6 @@
{ {
"name": "nc-lib-gui", "name": "nc-lib-gui",
"version": "0.109.4", "version": "0.109.6",
"description": "NocoDB GUI", "description": "NocoDB GUI",
"author": { "author": {
"name": "NocoDB", "name": "NocoDB",

2
packages/nocodb-sdk/package.json

@ -1,6 +1,6 @@
{ {
"name": "nocodb-sdk", "name": "nocodb-sdk",
"version": "0.109.4", "version": "0.109.6",
"description": "NocoDB SDK", "description": "NocoDB SDK",
"main": "build/main/index.js", "main": "build/main/index.js",
"typings": "build/main/index.d.ts", "typings": "build/main/index.d.ts",

14
packages/nocodb/package.json

@ -1,6 +1,6 @@
{ {
"name": "nocodb", "name": "nocodb",
"version": "0.109.4", "version": "0.109.6",
"description": "NocoDB Backend", "description": "NocoDB Backend",
"main": "dist/bundle.js", "main": "dist/bundle.js",
"author": { "author": {
@ -20,14 +20,12 @@
}, },
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"scripts": { "scripts": {
"build": "nest build", "build": "pnpm run docker:build",
"build:obfuscate": "EE=true webpack --config webpack.config.js", "build:obfuscate": "EE=true webpack --config webpack.config.js",
"obfuscate:build:publish": "pnpm run build:obfuscate && pnpm publish .", "obfuscate:build:publish": "pnpm run build:obfuscate && pnpm publish .",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start", "start": "pnpm run watch:run",
"start:dev": "nest start --watch", "start:prod": "node docker/main",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",
@ -119,7 +117,7 @@
"mysql2": "^3.2.0", "mysql2": "^3.2.0",
"nanoid": "^3.1.20", "nanoid": "^3.1.20",
"nc-help": "^0.2.88", "nc-help": "^0.2.88",
"nc-lib-gui": "0.109.4", "nc-lib-gui": "0.109.6",
"nc-plugin": "^0.1.3", "nc-plugin": "^0.1.3",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk", "nocodb-sdk": "file:../nocodb-sdk",

2
packages/nocodb/src/controllers/test/TestResetService/index.ts

@ -1,6 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import Project from '../../../models/Project'; import Project from '../../../models/Project';
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; // import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2';
import Noco from '../../../Noco'; import Noco from '../../../Noco';
import User from '../../../models/User'; import User from '../../../models/User';
import NocoCache from '../../../cache/NocoCache'; import NocoCache from '../../../cache/NocoCache';

262
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -43,6 +43,7 @@ import genRollupSelectv2 from './genRollupSelectv2';
import conditionV2 from './conditionV2'; import conditionV2 from './conditionV2';
import sortV2 from './sortV2'; import sortV2 from './sortV2';
import { customValidators } from './util/customValidators'; import { customValidators } from './util/customValidators';
import Transaction = Knex.Transaction;
import type { XKnex } from './CustomKnex'; import type { XKnex } from './CustomKnex';
import type { import type {
XcFilter, XcFilter,
@ -58,7 +59,6 @@ import type {
SelectOption, SelectOption,
} from '../models'; } from '../models';
import type { SortType } from 'nocodb-sdk'; import type { SortType } from 'nocodb-sdk';
import Transaction = Knex.Transaction;
dayjs.extend(utc); dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
@ -149,13 +149,18 @@ class BaseModelSqlv2 {
id?: any, id?: any,
validateFormula = false, validateFormula = false,
query: any = {}, query: any = {},
{
ignoreView = false,
}: {
ignoreView?: boolean;
} = {},
): Promise<any> { ): Promise<any> {
const qb = this.dbDriver(this.tnPath); const qb = this.dbDriver(this.tnPath);
const { ast, dependencyFields } = await getAst({ const { ast, dependencyFields } = await getAst({
query, query,
model: this.model, model: this.model,
view: this.viewId && (await View.get(this.viewId)), view: ignoreView ? null : this.viewId && (await View.get(this.viewId)),
}); });
await this.selectObject({ await this.selectObject({
@ -1385,30 +1390,25 @@ class BaseModelSqlv2 {
if (colOptions?.type === 'hm') { if (colOptions?.type === 'hm') {
const listLoader = new DataLoader(async (ids: string[]) => { const listLoader = new DataLoader(async (ids: string[]) => {
try { if (ids.length > 1) {
if (ids.length > 1) { const data = await this.multipleHmList(
const data = await this.multipleHmList( {
colId: column.id,
ids,
},
(listLoader as any).args,
);
return ids.map((id: string) => (data[id] ? data[id] : []));
} else {
return [
await this.hmList(
{ {
colId: column.id, colId: column.id,
ids, id: ids[0],
}, },
(listLoader as any).args, (listLoader as any).args,
); ),
return ids.map((id: string) => (data[id] ? data[id] : [])); ];
} else {
return [
await this.hmList(
{
colId: column.id,
id: ids[0],
},
(listLoader as any).args,
),
];
}
} catch (e) {
console.log(e);
return [];
} }
}); });
const self: BaseModelSqlv2 = this; const self: BaseModelSqlv2 = this;
@ -1429,31 +1429,26 @@ class BaseModelSqlv2 {
// }); // });
} else if (colOptions.type === 'mm') { } else if (colOptions.type === 'mm') {
const listLoader = new DataLoader(async (ids: string[]) => { const listLoader = new DataLoader(async (ids: string[]) => {
try { if (ids?.length > 1) {
if (ids?.length > 1) { const data = await this.multipleMmList(
const data = await this.multipleMmList( {
parentIds: ids,
colId: column.id,
},
(listLoader as any).args,
);
return data;
} else {
return [
await this.mmList(
{ {
parentIds: ids, parentId: ids[0],
colId: column.id, colId: column.id,
}, },
(listLoader as any).args, (listLoader as any).args,
); ),
];
return data;
} else {
return [
await this.mmList(
{
parentId: ids[0],
colId: column.id,
},
(listLoader as any).args,
),
];
}
} catch (e) {
console.log(e);
return [];
} }
}); });
@ -1476,26 +1471,21 @@ class BaseModelSqlv2 {
colId: colOptions.fk_child_column_id, colId: colOptions.fk_child_column_id,
}); });
const readLoader = new DataLoader(async (ids: string[]) => { const readLoader = new DataLoader(async (ids: string[]) => {
try { const data = await (
const data = await ( await Model.getBaseModelSQL({
await Model.getBaseModelSQL({ id: pCol.fk_model_id,
id: pCol.fk_model_id, dbDriver: this.dbDriver,
dbDriver: this.dbDriver, })
}) ).list(
).list( {
{ // limit: ids.length,
// limit: ids.length, where: `(${pCol.column_name},in,${ids.join(',')})`,
where: `(${pCol.column_name},in,${ids.join(',')})`, fieldsSet: (readLoader as any).args?.fieldsSet,
fieldsSet: (readLoader as any).args?.fieldsSet, },
}, true,
true, );
); const gs = groupBy(data, pCol.title);
const gs = groupBy(data, pCol.title); return ids.map(async (id: string) => gs?.[id]?.[0]);
return ids.map(async (id: string) => gs?.[id]?.[0]);
} catch (e) {
console.log(e);
return [];
}
}); });
// defining HasMany count method within GQL Type class // defining HasMany count method within GQL Type class
@ -1836,7 +1826,12 @@ class BaseModelSqlv2 {
// handle if autogenerated primary key is used // handle if autogenerated primary key is used
if (ag) { if (ag) {
if (!response) await this.execAndParse(query); if (!response) await this.execAndParse(query);
response = await this.readByPk(data[ag.title]); response = await this.readByPk(
data[ag.title],
false,
{},
{ ignoreView: true },
);
} else if ( } else if (
!response || !response ||
(typeof response?.[0] !== 'object' && response?.[0] !== null) (typeof response?.[0] !== 'object' && response?.[0] !== null)
@ -1864,7 +1859,7 @@ class BaseModelSqlv2 {
})) as any })) as any
)[0].id; )[0].id;
} }
response = await this.readByPk(id); response = await this.readByPk(id, false, {}, { ignoreView: true });
} else { } else {
response = data; response = data;
} }
@ -1873,6 +1868,9 @@ class BaseModelSqlv2 {
Array.isArray(response) Array.isArray(response)
? response?.[0]?.[ai.title] ? response?.[0]?.[ai.title]
: response?.[ai.title], : response?.[ai.title],
false,
{},
{ ignoreView: true },
); );
} }
@ -1889,7 +1887,7 @@ class BaseModelSqlv2 {
let trx: Transaction = _trx; let trx: Transaction = _trx;
try { try {
// retrieve data for handling params in hook // retrieve data for handling params in hook
const data = await this.readByPk(id); const data = await this.readByPk(id, false, {}, { ignoreView: true });
await this.beforeDelete(id, trx, cookie); await this.beforeDelete(id, trx, cookie);
const execQueries: ((trx: Transaction) => Promise<any>)[] = []; const execQueries: ((trx: Transaction) => Promise<any>)[] = [];
@ -2018,7 +2016,7 @@ class BaseModelSqlv2 {
await this.beforeUpdate(data, trx, cookie); await this.beforeUpdate(data, trx, cookie);
const prevData = await this.readByPk(id); const prevData = await this.readByPk(id, false, {}, { ignoreView: true });
const query = this.dbDriver(this.tnPath) const query = this.dbDriver(this.tnPath)
.update(updateObj) .update(updateObj)
@ -2026,7 +2024,7 @@ class BaseModelSqlv2 {
await this.execAndParse(query); await this.execAndParse(query);
const newData = await this.readByPk(id); const newData = await this.readByPk(id, false, {}, { ignoreView: true });
await this.afterUpdate(prevData, newData, trx, cookie, updateObj); await this.afterUpdate(prevData, newData, trx, cookie, updateObj);
return newData; return newData;
} catch (e) { } catch (e) {
@ -2210,7 +2208,7 @@ class BaseModelSqlv2 {
})) as any })) as any
).rows[0].id; ).rows[0].id;
} }
response = await this.readByPk(id); response = await this.readByPk(id, false, {}, { ignoreView: true });
} else { } else {
response = data; response = data;
} }
@ -2478,7 +2476,9 @@ class BaseModelSqlv2 {
if (!raw) { if (!raw) {
for (const pkValues of updatePkValues) { for (const pkValues of updatePkValues) {
newData.push(await this.readByPk(pkValues)); newData.push(
await this.readByPk(pkValues, false, {}, { ignoreView: true }),
);
} }
} }
@ -2570,7 +2570,9 @@ class BaseModelSqlv2 {
// pk not specified - bypass // pk not specified - bypass
continue; continue;
} }
deleted.push(await this.readByPk(pkValues)); deleted.push(
await this.readByPk(pkValues, false, {}, { ignoreView: true }),
);
res.push(d); res.push(d);
} }
@ -2704,18 +2706,16 @@ class BaseModelSqlv2 {
await parentTable.getColumns(); await parentTable.getColumns();
const childTn = this.getTnPath(childTable); const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable);
switch (colOptions.type) { switch (colOptions.type) {
case 'mm': case 'mm':
{ {
const vChildCol = await colOptions.getMMChildColumn(); const vChildCol = await colOptions.getMMChildColumn();
const vParentCol = await colOptions.getMMParentColumn();
const vTable = await colOptions.getMMModel(); const vTable = await colOptions.getMMModel();
const vTn = this.getTnPath(vTable); const vTn = this.getTnPath(vTable);
execQueries.push((trx, qb) => execQueries.push(() =>
this.dbDriver(vTn) this.dbDriver(vTn)
.where({ .where({
[vChildCol.column_name]: this.dbDriver(childTn) [vChildCol.column_name]: this.dbDriver(childTn)
@ -2975,104 +2975,12 @@ class BaseModelSqlv2 {
modelId: this.model.id, modelId: this.model.id,
tnPath: this.tnPath, tnPath: this.tnPath,
}); });
/*
const view = await View.get(this.viewId);
// handle form view data submission
if (
(hookName === 'after.insert' || hookName === 'after.bulkInsert') &&
view.type === ViewTypes.FORM
) {
try {
const formView = await view.getView<FormView>();
const { columns } = await FormView.getWithInfo(formView.fk_view_id);
const allColumns = await this.model.getColumns();
const fieldById = columns.reduce(
(o: Record<string, any>, f: Record<string, any>) => ({
...o,
[f.fk_column_id]: f,
}),
{},
);
let order = 1;
const filteredColumns = allColumns
?.map((c: Record<string, any>) => ({
...c,
fk_column_id: c.id,
fk_view_id: formView.fk_view_id,
...(fieldById[c.id] ? fieldById[c.id] : {}),
order: (fieldById[c.id] && fieldById[c.id].order) || order++,
id: fieldById[c.id] && fieldById[c.id].id,
}))
.sort(
(a: Record<string, any>, b: Record<string, any>) =>
a.order - b.order,
)
.filter(
(f: Record<string, any>) =>
f.show &&
f.uidt !== UITypes.Rollup &&
f.uidt !== UITypes.Lookup &&
f.uidt !== UITypes.Formula &&
f.uidt !== UITypes.QrCode &&
f.uidt !== UITypes.Barcode &&
f.uidt !== UITypes.SpecificDBType,
)
.sort(
(a: Record<string, any>, b: Record<string, any>) =>
a.order - b.order,
)
.map((c: Record<string, any>) => ({
...c,
required: !!(c.required || 0),
}));
const emails = Object.entries(JSON.parse(formView?.email) || {})
.filter((a) => a[1])
.map((a) => a[0]);
if (emails?.length) {
const transformedData = _transformSubmittedFormDataForEmail(
newData,
formView,
filteredColumns,
);
(await NcPluginMgrv2.emailAdapter(false))?.mailSend({
to: emails.join(','),
subject: 'NocoDB Form',
html: ejs.render(formSubmissionEmailTemplate, {
data: transformedData,
tn: this.tnPath,
_tn: this.model.title,
}),
});
}
} catch (e) {
console.log(e);
}
}
try {
const [event, operation] = hookName.split('.');
const hooks = await Hook.list({
fk_model_id: this.model.id,
event,
operation,
});
for (const hook of hooks) {
if (hook.active) {
invokeWebhook(hook, this.model, view, prevData, newData, req?.user);
}
}
} catch (e) {
console.log('hooks :: error', hookName, e);
}*/
} }
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async errorInsert(e, data, trx, cookie) {} protected async errorInsert(e, data, trx, cookie) {}
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async errorUpdate(e, data, trx, cookie) {} protected async errorUpdate(e, data, trx, cookie) {}
// todo: handle composite primary key // todo: handle composite primary key
@ -3086,7 +2994,7 @@ class BaseModelSqlv2 {
); );
} }
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async errorDelete(e, id, trx, cookie) {} protected async errorDelete(e, id, trx, cookie) {}
async validate(columns) { async validate(columns) {
@ -3227,7 +3135,12 @@ class BaseModelSqlv2 {
break; break;
} }
const response = await this.readByPk(rowId); const response = await this.readByPk(
rowId,
false,
{},
{ ignoreView: true },
);
await this.afterInsert(response, this.dbDriver, cookie); await this.afterInsert(response, this.dbDriver, cookie);
await this.afterAddChild(rowId, childId, cookie); await this.afterAddChild(rowId, childId, cookie);
} }
@ -3276,7 +3189,12 @@ class BaseModelSqlv2 {
const childTn = this.getTnPath(childTable); const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable); const parentTn = this.getTnPath(parentTable);
const prevData = await this.readByPk(rowId); const prevData = await this.readByPk(
rowId,
false,
{},
{ ignoreView: true },
);
switch (colOptions.type) { switch (colOptions.type) {
case RelationTypes.MANY_TO_MANY: case RelationTypes.MANY_TO_MANY:
@ -3329,7 +3247,7 @@ class BaseModelSqlv2 {
break; break;
} }
const newData = await this.readByPk(rowId); const newData = await this.readByPk(rowId, false, {}, { ignoreView: true });
await this.afterUpdate(prevData, newData, this.dbDriver, cookie); await this.afterUpdate(prevData, newData, this.dbDriver, cookie);
await this.afterRemoveChild(rowId, childId, cookie); await this.afterRemoveChild(rowId, childId, cookie);
} }

41
packages/nocodb/src/db/sql-client/lib/mssql/MssqlClient.ts

@ -2101,7 +2101,7 @@ class MssqlClient extends KnexClient {
table = table.onUpdate(relation.ur); table = table.onUpdate(relation.ur);
} }
if (relation.dr) { if (relation.dr) {
table = table.onDelete(relation.dr); table.onDelete(relation.dr);
} }
}) })
.toQuery()); .toQuery());
@ -2192,28 +2192,31 @@ class MssqlClient extends KnexClient {
table = table.onUpdate(args.onUpdate); table = table.onUpdate(args.onUpdate);
} }
if (args.onDelete) { if (args.onDelete) {
table = table.onDelete(args.onDelete); table.onDelete(args.onDelete);
} }
}, },
); );
const upStatement = const upQb = this.sqlClient.schema.table(
this.querySeparator() + this.getTnPath(args.childTable),
(await this.sqlClient.schema function (table) {
.table(this.getTnPath(args.childTable), function (table) { table = table
table = table .foreign(args.childColumn, foreignKeyName)
.foreign(args.childColumn, foreignKeyName) .references(args.parentColumn)
.references(args.parentColumn) .on(self.getTnPath(args.parentTable));
.on(self.getTnPath(args.parentTable));
if (args.onUpdate) { if (args.onUpdate) {
table = table.onUpdate(args.onUpdate); table = table.onUpdate(args.onUpdate);
} }
if (args.onDelete) { if (args.onDelete) {
table = table.onDelete(args.onDelete); table.onDelete(args.onDelete);
} }
}) },
.toQuery()); );
await upQb;
const upStatement = this.querySeparator() + upQb.toQuery();
this.emit(`Success : ${upStatement}`); this.emit(`Success : ${upStatement}`);
@ -2221,7 +2224,7 @@ class MssqlClient extends KnexClient {
this.querySeparator() + this.querySeparator() +
this.sqlClient.schema this.sqlClient.schema
.table(this.getTnPath(args.childTable), function (table) { .table(this.getTnPath(args.childTable), function (table) {
table = table.dropForeign(args.childColumn, foreignKeyName); table.dropForeign(args.childColumn, foreignKeyName);
}) })
.toQuery(); .toQuery();

2
packages/nocodb/src/db/sql-client/lib/oracle/OracleClient.ts

@ -1914,7 +1914,7 @@ class OracleClient extends KnexClient {
* @returns {String} message * @returns {String} message
*/ */
async totalRecords(_args: any = {}): Promise<Result> { async totalRecords(_args: any = {}): Promise<Result> {
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
const func = this.totalRecords.name; const func = this.totalRecords.name;
throw new Error('Function not supported for oracle yet'); throw new Error('Function not supported for oracle yet');
} }

2
packages/nocodb/src/db/sql-client/lib/pg/PgClient.ts

@ -2359,7 +2359,7 @@ class PGClient extends KnexClient {
table = table.onUpdate(relation.ur); table = table.onUpdate(relation.ur);
} }
if (relation.dr) { if (relation.dr) {
table = table.onDelete(relation.dr); table.onDelete(relation.dr);
} }
}) })
.toQuery()); .toQuery());

73
packages/nocodb/src/db/sql-client/lib/snowflake/SnowflakeClient.ts

@ -1970,23 +1970,24 @@ class SnowflakeClient extends KnexClient {
relationsList = relationsList.data.list; relationsList = relationsList.data.list;
for (const relation of relationsList) { for (const relation of relationsList) {
downQuery += const downQb = this.sqlClient.schema.table(
this.querySeparator() + relation.tn,
(await this.sqlClient.schema function (table) {
.table(relation.tn, function (table) { table = table
table = table .foreign(relation.cn, null)
.foreign(relation.cn, null) .references(relation.rcn)
.references(relation.rcn) .on(relation.rtn);
.on(relation.rtn);
if (relation.ur) {
if (relation.ur) { table = table.onUpdate(relation.ur);
table = table.onUpdate(relation.ur); }
} if (relation.dr) {
if (relation.dr) { table.onDelete(relation.dr);
table = table.onDelete(relation.dr); }
} },
}) );
.toQuery()); await downQb;
downQuery += this.querySeparator() + downQb.toQuery();
} }
let indexList: any = await this.indexList(args); let indexList: any = await this.indexList(args);
@ -2060,8 +2061,6 @@ class SnowflakeClient extends KnexClient {
const foreignKeyName = args.foreignKeyName || null; const foreignKeyName = args.foreignKeyName || null;
try { try {
// s = await this.sqlClient.schema.index(Object.keys(args.columns));
await this.sqlClient.schema.table(args.childTable, (table) => { await this.sqlClient.schema.table(args.childTable, (table) => {
table = table table = table
.foreign(args.childColumn, foreignKeyName) .foreign(args.childColumn, foreignKeyName)
@ -2072,27 +2071,27 @@ class SnowflakeClient extends KnexClient {
table = table.onUpdate(args.onUpdate); table = table.onUpdate(args.onUpdate);
} }
if (args.onDelete) { if (args.onDelete) {
table = table.onDelete(args.onDelete); table.onDelete(args.onDelete);
} }
}); });
const upStatement = const upQb = this.sqlClient.schema.table(args.childTable, (table) => {
this.querySeparator() + table = table
(await this.sqlClient.schema .foreign(args.childColumn, foreignKeyName)
.table(args.childTable, (table) => { .references(args.parentColumn)
table = table .on(this.getTnPath(args.parentTable));
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(this.getTnPath(args.parentTable));
if (args.onUpdate) { if (args.onUpdate) {
table = table.onUpdate(args.onUpdate); table = table.onUpdate(args.onUpdate);
} }
if (args.onDelete) { if (args.onDelete) {
table = table.onDelete(args.onDelete); table.onDelete(args.onDelete);
} }
}) });
.toQuery());
await upQb;
const upStatement = this.querySeparator() + upQb.toQuery();
this.emit(`Success : ${upStatement}`); this.emit(`Success : ${upStatement}`);
@ -2100,7 +2099,7 @@ class SnowflakeClient extends KnexClient {
this.querySeparator() + this.querySeparator() +
this.sqlClient.schema this.sqlClient.schema
.table(args.childTable, (table) => { .table(args.childTable, (table) => {
table = table.dropForeign(args.childColumn, foreignKeyName); table.dropForeign(args.childColumn, foreignKeyName);
}) })
.toQuery(); .toQuery();

1
packages/nocodb/src/db/sql-client/lib/sqlite/SqliteClient.ts

@ -1866,6 +1866,7 @@ class SqliteClient extends KnexClient {
/* Filter relations for current table */ /* Filter relations for current table */
if (args.tn) { if (args.tn) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
relations = relations.filter( relations = relations.filter(
(r) => r.tn === args.tn || r.rtn === args.tn, (r) => r.tn === args.tn || r.rtn === args.tn,
); );

43
packages/nocodb/src/db/sql-data-mapper/lib/BaseModel.ts

@ -321,7 +321,7 @@ abstract class BaseModel {
* @returns {Object} Table row data * @returns {Object} Table row data
*/ */
// @ts-ignore // @ts-ignore
async readByPk(id, { conditionGraph }) { async readByPk(id) {
try { try {
return await this._run( return await this._run(
this.$db.select().where(this._wherePk(id)).first(), this.$db.select().where(this._wherePk(id)).first(),
@ -704,10 +704,7 @@ abstract class BaseModel {
*/ */
async exists(id, _) { async exists(id, _) {
try { try {
return ( return Object.keys(await this.readByPk(id)).length !== 0;
Object.keys(await this.readByPk(id, { conditionGraph: null }))
.length !== 0
);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
throw e; throw e;
@ -1341,7 +1338,7 @@ abstract class BaseModel {
* @param {Object} data - insert data * @param {Object} data - insert data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeInsert(data, trx?: any, cookie?: {}) {} async beforeInsert(data, trx?: any, cookie?: {}) {}
/** /**
@ -1350,7 +1347,7 @@ abstract class BaseModel {
* @param {Object} response - inserted data * @param {Object} response - inserted data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterInsert(response, trx?: any, cookie?: {}) {} async afterInsert(response, trx?: any, cookie?: {}) {}
/** /**
@ -1360,7 +1357,7 @@ abstract class BaseModel {
* @param {Object} data - insert data * @param {Object} data - insert data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorInsert(err, data, trx?: any, cookie?: {}) {} async errorInsert(err, data, trx?: any, cookie?: {}) {}
/** /**
@ -1369,7 +1366,7 @@ abstract class BaseModel {
* @param {Object} data - update data * @param {Object} data - update data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeUpdate(data, trx?: any, cookie?: {}) {} async beforeUpdate(data, trx?: any, cookie?: {}) {}
/** /**
@ -1378,7 +1375,7 @@ abstract class BaseModel {
* @param {Object} response - updated data * @param {Object} response - updated data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterUpdate(response, trx?: any, cookie?: {}) {} async afterUpdate(response, trx?: any, cookie?: {}) {}
/** /**
@ -1388,7 +1385,7 @@ abstract class BaseModel {
* @param {Object} data - update data * @param {Object} data - update data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorUpdate(err, data, trx?: any, cookie?: {}) {} async errorUpdate(err, data, trx?: any, cookie?: {}) {}
/** /**
@ -1397,7 +1394,7 @@ abstract class BaseModel {
* @param {Object} data - delete data * @param {Object} data - delete data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeDelete(data, trx?: any, cookie?: {}) {} async beforeDelete(data, trx?: any, cookie?: {}) {}
/** /**
@ -1406,7 +1403,7 @@ abstract class BaseModel {
* @param {Object} response - Deleted data * @param {Object} response - Deleted data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterDelete(response, trx?: any, cookie?: {}) {} async afterDelete(response, trx?: any, cookie?: {}) {}
/** /**
@ -1416,7 +1413,7 @@ abstract class BaseModel {
* @param {Object} data - delete data * @param {Object} data - delete data
* @param {Object} trx? - knex transaction reference * @param {Object} trx? - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorDelete(err, data, trx?: any, cookie?: {}) {} async errorDelete(err, data, trx?: any, cookie?: {}) {}
/** /**
@ -1425,7 +1422,7 @@ abstract class BaseModel {
* @param {Object[]} data - insert data * @param {Object[]} data - insert data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeInsertb(data, trx?: any) {} async beforeInsertb(data, trx?: any) {}
/** /**
@ -1434,7 +1431,7 @@ abstract class BaseModel {
* @param {Object[]} response - inserted data * @param {Object[]} response - inserted data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterInsertb(response, trx?: any) {} async afterInsertb(response, trx?: any) {}
/** /**
@ -1444,7 +1441,7 @@ abstract class BaseModel {
* @param {Object} data - delete data * @param {Object} data - delete data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorInsertb(err, data, trx?: any) {} async errorInsertb(err, data, trx?: any) {}
/** /**
@ -1453,7 +1450,7 @@ abstract class BaseModel {
* @param {Object[]} data - update data * @param {Object[]} data - update data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeUpdateb(data, trx?: any) {} async beforeUpdateb(data, trx?: any) {}
/** /**
@ -1462,7 +1459,7 @@ abstract class BaseModel {
* @param {Object[]} response - updated data * @param {Object[]} response - updated data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterUpdateb(response, trx?: any) {} async afterUpdateb(response, trx?: any) {}
/** /**
@ -1472,7 +1469,7 @@ abstract class BaseModel {
* @param {Object[]} data - delete data * @param {Object[]} data - delete data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorUpdateb(err, data, trx?: any) {} async errorUpdateb(err, data, trx?: any) {}
/** /**
@ -1481,7 +1478,7 @@ abstract class BaseModel {
* @param {Object[]} data - delete data * @param {Object[]} data - delete data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeDeleteb(data, trx?: any) {} async beforeDeleteb(data, trx?: any) {}
/** /**
@ -1490,7 +1487,7 @@ abstract class BaseModel {
* @param {Object[]} response - deleted data * @param {Object[]} response - deleted data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterDeleteb(response, trx?: any) {} async afterDeleteb(response, trx?: any) {}
/** /**
@ -1500,7 +1497,7 @@ abstract class BaseModel {
* @param {Object[]} data - delete data * @param {Object[]} data - delete data
* @param {Object} trx - knex transaction reference * @param {Object} trx - knex transaction reference
*/ */
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorDeleteb(err, data, trx?: any) {} async errorDeleteb(err, data, trx?: any) {}
} }

2
packages/nocodb/src/db/sql-mgr/code/models/xc/ModelXcMetaMssql.ts

@ -590,7 +590,7 @@ class ModelXcMetaMssql extends BaseModelXcMeta {
case 'set': case 'set':
return 'MultiSelect'; return 'MultiSelect';
case 'json': case 'json':
return 'LongText'; return 'JSON';
case 'blob': case 'blob':
return 'LongText'; return 'LongText';
case 'geometry': case 'geometry':

2
packages/nocodb/src/db/sql-mgr/code/models/xc/ModelXcMetaMysql.ts

@ -322,7 +322,7 @@ class ModelXcMetaMysql extends BaseModelXcMeta {
case 'set': case 'set':
return 'MultiSelect'; return 'MultiSelect';
case 'json': case 'json':
return 'LongText'; return 'JSON';
case 'blob': case 'blob':
return 'LongText'; return 'LongText';
case 'geometry': case 'geometry':

2
packages/nocodb/src/db/sql-mgr/code/models/xc/ModelXcMetaOracle.ts

@ -370,7 +370,7 @@ class ModelXcMetaOracle extends BaseModelXcMeta {
case 'set': case 'set':
return 'MultiSelect'; return 'MultiSelect';
case 'json': case 'json':
return 'LongText'; return 'JSON';
} }
} }

2
packages/nocodb/src/db/sql-migrator/lib/KnexMigrator.ts

@ -137,6 +137,7 @@ export default class KnexMigrator extends SqlMigrator {
), ),
); );
// @ts-ignore // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const dirStat = await promisify(fs.stat)( const dirStat = await promisify(fs.stat)(
path.join( path.join(
this.toolDir, this.toolDir,
@ -232,6 +233,7 @@ export default class KnexMigrator extends SqlMigrator {
); );
// @ts-ignore // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const metaStat = await promisify(fs.stat)( const metaStat = await promisify(fs.stat)(
path.join( path.join(
this.toolDir, this.toolDir,

1
packages/nocodb/src/db/sql-migrator/lib/KnexMigratorv2.ts

@ -114,6 +114,7 @@ export default class KnexMigratorv2 {
}*/ }*/
// @ts-ignore // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async _initDbOnFs(base: Base) { async _initDbOnFs(base: Base) {
// this.emit( // this.emit(
// 'Creating folder: ', // 'Creating folder: ',

2
packages/nocodb/src/helpers/apiMetrics.ts

@ -3,7 +3,7 @@ import type { Request } from 'express';
const countMap = {}; const countMap = {};
// @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unused-vars
const metrics = async (req: Request, c = 150) => { const metrics = async (req: Request, c = 150) => {
if (!req?.route?.path) return; if (!req?.route?.path) return;
const event = `a:api:${req.route.path}:${req.method}`; const event = `a:api:${req.route.path}:${req.method}`;

2
packages/nocodb/src/helpers/webhookHelpers.ts

@ -271,7 +271,7 @@ export async function invokeWebhook(
return; return;
} }
if (hook.condition) { if (hook.condition && !testHook) {
if (isBulkOperation) { if (isBulkOperation) {
const filteredData = []; const filteredData = [];
for (const data of newData) { for (const data of newData) {

2
packages/nocodb/src/meta/migrations/v1/nc_011_remove_old_ses_plugin.ts

@ -5,7 +5,7 @@ const up = async (knex: Knex) => {
await knex('nc_plugins').del().where({ title: 'SES' }); await knex('nc_plugins').del().where({ title: 'SES' });
}; };
const down = async (knex: Knex) => { const down = async (_: Knex) => {
// await knex('nc_plugins').insert([ses]); // await knex('nc_plugins').insert([ses]);
}; };

1
packages/nocodb/src/models/Model.ts

@ -65,6 +65,7 @@ export default class Model implements TableType {
} }
// @ts-ignore // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async getViews(force = false, ncMeta = Noco.ncMeta): Promise<View[]> { public async getViews(force = false, ncMeta = Noco.ncMeta): Promise<View[]> {
this.views = await View.listWithInfo(this.id, ncMeta); this.views = await View.listWithInfo(this.id, ncMeta);
return this.views; return this.views;

1
packages/nocodb/src/models/View.ts

@ -1,4 +1,3 @@
import { title } from 'process';
import { isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk'; import { isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk';
import Noco from '../Noco'; import Noco from '../Noco';
import { import {

2
packages/nocodb/src/modules/jobs/jobs/at-import/at-import.controller.ts

@ -65,7 +65,7 @@ export class AtImportController {
@Post('/api/v1/db/meta/syncs/:syncId/abort') @Post('/api/v1/db/meta/syncs/:syncId/abort')
@HttpCode(200) @HttpCode(200)
async abortImport(@Request() req) { async abortImport(@Request() _) {
return {}; return {};
} }
} }

69
packages/nocodb/src/modules/jobs/jobs/at-import/at-import.processor.ts

@ -348,32 +348,6 @@ export class AtImportProcessor {
} }
}; };
const nc_DumpTableSchema = async () => {
console.log('[');
// const ncTblList = await api.base.tableList(
// ncCreatedProjectSchema.id,
// syncDB.baseId
// );
const ncTblList = { list: [] };
ncTblList['list'] = await this.tablesService.getAccessibleTables({
projectId: ncCreatedProjectSchema.id,
baseId: syncDB.baseId,
roles: userRole,
});
for (let i = 0; i < ncTblList.list.length; i++) {
// const ncTbl = await api.dbTable.read(ncTblList.list[i].id);
const ncTbl = await this.tablesService.getTableWithAccessibleViews({
tableId: ncTblList.list[i].id,
user: syncDB.user,
});
console.log(JSON.stringify(ncTbl, null, 2));
console.log(',');
}
console.log(']');
};
// retrieve nc column schema from using aTbl field ID as reference // retrieve nc column schema from using aTbl field ID as reference
// //
const nc_getColumnSchema = async (aTblFieldId) => { const nc_getColumnSchema = async (aTblFieldId) => {
@ -1563,45 +1537,6 @@ export class AtImportProcessor {
return rec; return rec;
}; };
const nocoReadDataSelected = async (projName, table, callback, fields) => {
return new Promise((resolve, reject) => {
base(table.title)
.select({
pageSize: 100,
// maxRecords: 100,
fields: fields,
})
.eachPage(
async function page(records, fetchNextPage) {
// This function (`page`) will get called for each page of records.
// records.forEach(record => callback(table, record));
logBasic(
`:: ${table.title} / ${fields} : ${
recordCnt + 1
} ~ ${(recordCnt += 100)}`,
);
await Promise.all(
records.map((r) => callback(projName, table, r, fields)),
);
// To fetch the next page of records, call `fetchNextPage`.
// If there are more records, `page` will get called again.
// If there are no more records, `done` will get called.
fetchNextPage();
},
function done(err) {
if (err) {
console.error(err);
reject(err);
}
resolve(null);
},
);
});
};
//////////
const nc_isLinkExists = (airtableFieldId) => { const nc_isLinkExists = (airtableFieldId) => {
return !!ncLinkMappingTable.find( return !!ncLinkMappingTable.find(
(x) => x.aTbl.typeOptions.symmetricColumnId === airtableFieldId, (x) => x.aTbl.typeOptions.symmetricColumnId === airtableFieldId,
@ -2294,7 +2229,6 @@ export class AtImportProcessor {
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
let recordCnt = 0;
try { try {
logBasic('SDK initialized'); logBasic('SDK initialized');
logDetailed('Project initialization started'); logDetailed('Project initialization started');
@ -2374,7 +2308,6 @@ export class AtImportProcessor {
if (syncDB.options.syncData) { if (syncDB.options.syncData) {
try { try {
// await nc_DumpTableSchema();
const _perfStart = recordPerfStart(); const _perfStart = recordPerfStart();
const ncTblList = { list: [] }; const ncTblList = { list: [] };
ncTblList['list'] = await this.tablesService.getAccessibleTables({ ncTblList['list'] = await this.tablesService.getAccessibleTables({
@ -2404,8 +2337,6 @@ export class AtImportProcessor {
}); });
recordPerfStats(_perfStart, 'dbTable.read'); recordPerfStats(_perfStart, 'dbTable.read');
recordCnt = 0;
recordsMap[ncTbl.id] = await importData({ recordsMap[ncTbl.id] = await importData({
projectName: syncDB.projectName, projectName: syncDB.projectName,
table: ncTbl, table: ncTbl,

1
packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts

@ -1214,6 +1214,7 @@ export class ImportService {
storageAdapter as any storageAdapter as any
).fileReadByStream(linkFile); ).fileReadByStream(linkFile);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
handledLinks = await this.importLinkFromCsvStream({ handledLinks = await this.importLinkFromCsvStream({
idMap, idMap,
linkStream: linkReadStream, linkStream: linkReadStream,

38
packages/nocodb/src/plugins/storage/Local.ts

@ -3,6 +3,7 @@ import path from 'path';
import { promisify } from 'util'; import { promisify } from 'util';
import mkdirp from 'mkdirp'; import mkdirp from 'mkdirp';
import axios from 'axios'; import axios from 'axios';
import { NcError } from '../../helpers/catchError';
import { getToolDir } from '../../utils/nc-config'; import { getToolDir } from '../../utils/nc-config';
import type { IStorageAdapterV2, XcFile } from 'nc-plugin'; import type { IStorageAdapterV2, XcFile } from 'nc-plugin';
import type { Readable } from 'stream'; import type { Readable } from 'stream';
@ -11,7 +12,7 @@ export default class Local implements IStorageAdapterV2 {
constructor() {} constructor() {}
public async fileCreate(key: string, file: XcFile): Promise<any> { public async fileCreate(key: string, file: XcFile): Promise<any> {
const destPath = path.join(getToolDir(), ...key.split('/')); const destPath = this.validateAndNormalisePath(key);
try { try {
await mkdirp(path.dirname(destPath)); await mkdirp(path.dirname(destPath));
const data = await promisify(fs.readFile)(file.path); const data = await promisify(fs.readFile)(file.path);
@ -24,7 +25,7 @@ export default class Local implements IStorageAdapterV2 {
} }
async fileCreateByUrl(key: string, url: string): Promise<any> { async fileCreateByUrl(key: string, url: string): Promise<any> {
const destPath = path.join(getToolDir(), ...key.split('/')); const destPath = this.validateAndNormalisePath(key);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.get(url, { .get(url, {
@ -71,7 +72,7 @@ export default class Local implements IStorageAdapterV2 {
stream: Readable, stream: Readable,
): Promise<void> { ): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const destPath = path.join(getToolDir(), ...key.split('/')); const destPath = this.validateAndNormalisePath(key);
try { try {
mkdirp(path.dirname(destPath)).then(() => { mkdirp(path.dirname(destPath)).then(() => {
const writableStream = fs.createWriteStream(destPath); const writableStream = fs.createWriteStream(destPath);
@ -86,12 +87,12 @@ export default class Local implements IStorageAdapterV2 {
} }
public async fileReadByStream(key: string): Promise<Readable> { public async fileReadByStream(key: string): Promise<Readable> {
const srcPath = path.join(getToolDir(), ...key.split('/')); const srcPath = this.validateAndNormalisePath(key);
return fs.createReadStream(srcPath, { encoding: 'utf8' }); return fs.createReadStream(srcPath, { encoding: 'utf8' });
} }
public async getDirectoryList(key: string): Promise<string[]> { public async getDirectoryList(key: string): Promise<string[]> {
const destDir = path.join(getToolDir(), ...key.split('/')); const destDir = this.validateAndNormalisePath(key);
return fs.promises.readdir(destDir); return fs.promises.readdir(destDir);
} }
@ -103,7 +104,7 @@ export default class Local implements IStorageAdapterV2 {
public async fileRead(filePath: string): Promise<any> { public async fileRead(filePath: string): Promise<any> {
try { try {
const fileData = await fs.promises.readFile( const fileData = await fs.promises.readFile(
path.join(getToolDir(), ...filePath.split('/')), this.validateAndNormalisePath(filePath, true),
); );
return fileData; return fileData;
} catch (e) { } catch (e) {
@ -118,4 +119,29 @@ export default class Local implements IStorageAdapterV2 {
test(): Promise<boolean> { test(): Promise<boolean> {
return Promise.resolve(false); return Promise.resolve(false);
} }
// method for validate/normalise the path for avoid path traversal attack
protected validateAndNormalisePath(
fileOrFolderPath: string,
throw404 = false,
): string {
// Get the absolute path to the base directory
const absoluteBasePath = path.resolve(getToolDir(), 'nc');
// Get the absolute path to the file
const absolutePath = path.resolve(
path.join(getToolDir(), ...fileOrFolderPath.split('/')),
);
// Check if the resolved path is within the intended directory
if (!absolutePath.startsWith(absoluteBasePath)) {
if (throw404) {
NcError.notFound();
} else {
NcError.badRequest('Invalid path');
}
}
return absolutePath;
}
} }

4
packages/nocodb/src/services/auth.service.ts

@ -40,8 +40,8 @@ export class AuthService {
email: _email, email: _email,
firstname, firstname,
lastname, lastname,
token, // token,
ignore_subscribe, // ignore_subscribe,
} = createUserDto as any; } = createUserDto as any;
let { password } = createUserDto; let { password } = createUserDto;

8
packages/nocodb/src/services/tables.service.ts

@ -14,13 +14,7 @@ import getColumnPropsFromUIDT from '../helpers/getColumnPropsFromUIDT';
import getColumnUiType from '../helpers/getColumnUiType'; import getColumnUiType from '../helpers/getColumnUiType';
import getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName'; import getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName';
import mapDefaultDisplayValue from '../helpers/mapDefaultDisplayValue'; import mapDefaultDisplayValue from '../helpers/mapDefaultDisplayValue';
import { import { Audit, Column, Model, ModelRoleVisibility, Project } from '../models';
Audit,
Column,
Model,
ModelRoleVisibility,
Project,
} from '../models';
import Noco from '../Noco'; import Noco from '../Noco';
import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2';
import { validatePayload } from '../helpers'; import { validatePayload } from '../helpers';

1
packages/nocodb/src/utils/common/XcAudit.ts

@ -9,5 +9,6 @@ export default class XcAudit {
private static app: Noco; private static app: Noco;
// @ts-ignore // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public static async log(data: { project }) {} public static async log(data: { project }) {}
} }

Loading…
Cancel
Save