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

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

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

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

@ -1,8 +1,8 @@
<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 }>()
</script>
<template>

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

@ -72,7 +72,7 @@ const onDrop = async (event: DragEvent) => {
event.preventDefault()
try {
// 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
// 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,
isAttachment,
ref,
renderValue,
useExpandedFormDetached,
useLTARStoreOrThrow,
} from '#imports'

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

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

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

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

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

@ -26,7 +26,6 @@ import {
useI18n,
useMetas,
useProject,
useUIPermission,
} from '#imports'
const MAIN_MOUSE_PRESSED = 0
@ -80,9 +79,6 @@ export function useMultiSelect(
() => !(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) {
if (activeCell.row === row && activeCell.col === col) {
return

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

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

2
packages/nocodb-sdk/package.json

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

14
packages/nocodb/package.json

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

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

@ -1,6 +1,6 @@
import axios from 'axios';
import Project from '../../../models/Project';
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2';
// import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2';
import Noco from '../../../Noco';
import User from '../../../models/User';
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 sortV2 from './sortV2';
import { customValidators } from './util/customValidators';
import Transaction = Knex.Transaction;
import type { XKnex } from './CustomKnex';
import type {
XcFilter,
@ -58,7 +59,6 @@ import type {
SelectOption,
} from '../models';
import type { SortType } from 'nocodb-sdk';
import Transaction = Knex.Transaction;
dayjs.extend(utc);
dayjs.extend(timezone);
@ -149,13 +149,18 @@ class BaseModelSqlv2 {
id?: any,
validateFormula = false,
query: any = {},
{
ignoreView = false,
}: {
ignoreView?: boolean;
} = {},
): Promise<any> {
const qb = this.dbDriver(this.tnPath);
const { ast, dependencyFields } = await getAst({
query,
model: this.model,
view: this.viewId && (await View.get(this.viewId)),
view: ignoreView ? null : this.viewId && (await View.get(this.viewId)),
});
await this.selectObject({
@ -1385,30 +1390,25 @@ class BaseModelSqlv2 {
if (colOptions?.type === 'hm') {
const listLoader = new DataLoader(async (ids: string[]) => {
try {
if (ids.length > 1) {
const data = await this.multipleHmList(
if (ids.length > 1) {
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,
ids,
id: ids[0],
},
(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;
@ -1429,31 +1429,26 @@ class BaseModelSqlv2 {
// });
} else if (colOptions.type === 'mm') {
const listLoader = new DataLoader(async (ids: string[]) => {
try {
if (ids?.length > 1) {
const data = await this.multipleMmList(
if (ids?.length > 1) {
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,
},
(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,
});
const readLoader = new DataLoader(async (ids: string[]) => {
try {
const data = await (
await Model.getBaseModelSQL({
id: pCol.fk_model_id,
dbDriver: this.dbDriver,
})
).list(
{
// limit: ids.length,
where: `(${pCol.column_name},in,${ids.join(',')})`,
fieldsSet: (readLoader as any).args?.fieldsSet,
},
true,
);
const gs = groupBy(data, pCol.title);
return ids.map(async (id: string) => gs?.[id]?.[0]);
} catch (e) {
console.log(e);
return [];
}
const data = await (
await Model.getBaseModelSQL({
id: pCol.fk_model_id,
dbDriver: this.dbDriver,
})
).list(
{
// limit: ids.length,
where: `(${pCol.column_name},in,${ids.join(',')})`,
fieldsSet: (readLoader as any).args?.fieldsSet,
},
true,
);
const gs = groupBy(data, pCol.title);
return ids.map(async (id: string) => gs?.[id]?.[0]);
});
// defining HasMany count method within GQL Type class
@ -1836,7 +1826,12 @@ class BaseModelSqlv2 {
// handle if autogenerated primary key is used
if (ag) {
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 (
!response ||
(typeof response?.[0] !== 'object' && response?.[0] !== null)
@ -1864,7 +1859,7 @@ class BaseModelSqlv2 {
})) as any
)[0].id;
}
response = await this.readByPk(id);
response = await this.readByPk(id, false, {}, { ignoreView: true });
} else {
response = data;
}
@ -1873,6 +1868,9 @@ class BaseModelSqlv2 {
Array.isArray(response)
? response?.[0]?.[ai.title]
: response?.[ai.title],
false,
{},
{ ignoreView: true },
);
}
@ -1889,7 +1887,7 @@ class BaseModelSqlv2 {
let trx: Transaction = _trx;
try {
// 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);
const execQueries: ((trx: Transaction) => Promise<any>)[] = [];
@ -2018,7 +2016,7 @@ class BaseModelSqlv2 {
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)
.update(updateObj)
@ -2026,7 +2024,7 @@ class BaseModelSqlv2 {
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);
return newData;
} catch (e) {
@ -2210,7 +2208,7 @@ class BaseModelSqlv2 {
})) as any
).rows[0].id;
}
response = await this.readByPk(id);
response = await this.readByPk(id, false, {}, { ignoreView: true });
} else {
response = data;
}
@ -2478,7 +2476,9 @@ class BaseModelSqlv2 {
if (!raw) {
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
continue;
}
deleted.push(await this.readByPk(pkValues));
deleted.push(
await this.readByPk(pkValues, false, {}, { ignoreView: true }),
);
res.push(d);
}
@ -2704,18 +2706,16 @@ class BaseModelSqlv2 {
await parentTable.getColumns();
const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable);
switch (colOptions.type) {
case 'mm':
{
const vChildCol = await colOptions.getMMChildColumn();
const vParentCol = await colOptions.getMMParentColumn();
const vTable = await colOptions.getMMModel();
const vTn = this.getTnPath(vTable);
execQueries.push((trx, qb) =>
execQueries.push(() =>
this.dbDriver(vTn)
.where({
[vChildCol.column_name]: this.dbDriver(childTn)
@ -2975,104 +2975,12 @@ class BaseModelSqlv2 {
modelId: this.model.id,
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) {}
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async errorUpdate(e, data, trx, cookie) {}
// 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) {}
async validate(columns) {
@ -3227,7 +3135,12 @@ class BaseModelSqlv2 {
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.afterAddChild(rowId, childId, cookie);
}
@ -3276,7 +3189,12 @@ class BaseModelSqlv2 {
const childTn = this.getTnPath(childTable);
const parentTn = this.getTnPath(parentTable);
const prevData = await this.readByPk(rowId);
const prevData = await this.readByPk(
rowId,
false,
{},
{ ignoreView: true },
);
switch (colOptions.type) {
case RelationTypes.MANY_TO_MANY:
@ -3329,7 +3247,7 @@ class BaseModelSqlv2 {
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.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);
}
if (relation.dr) {
table = table.onDelete(relation.dr);
table.onDelete(relation.dr);
}
})
.toQuery());
@ -2192,28 +2192,31 @@ class MssqlClient extends KnexClient {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
table.onDelete(args.onDelete);
}
},
);
const upStatement =
this.querySeparator() +
(await this.sqlClient.schema
.table(this.getTnPath(args.childTable), function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(self.getTnPath(args.parentTable));
const upQb = this.sqlClient.schema.table(
this.getTnPath(args.childTable),
function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(self.getTnPath(args.parentTable));
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
}
})
.toQuery());
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table.onDelete(args.onDelete);
}
},
);
await upQb;
const upStatement = this.querySeparator() + upQb.toQuery();
this.emit(`Success : ${upStatement}`);
@ -2221,7 +2224,7 @@ class MssqlClient extends KnexClient {
this.querySeparator() +
this.sqlClient.schema
.table(this.getTnPath(args.childTable), function (table) {
table = table.dropForeign(args.childColumn, foreignKeyName);
table.dropForeign(args.childColumn, foreignKeyName);
})
.toQuery();

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

@ -1914,7 +1914,7 @@ class OracleClient extends KnexClient {
* @returns {String} message
*/
async totalRecords(_args: any = {}): Promise<Result> {
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const func = this.totalRecords.name;
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);
}
if (relation.dr) {
table = table.onDelete(relation.dr);
table.onDelete(relation.dr);
}
})
.toQuery());

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

@ -1970,23 +1970,24 @@ class SnowflakeClient extends KnexClient {
relationsList = relationsList.data.list;
for (const relation of relationsList) {
downQuery +=
this.querySeparator() +
(await this.sqlClient.schema
.table(relation.tn, function (table) {
table = table
.foreign(relation.cn, null)
.references(relation.rcn)
.on(relation.rtn);
if (relation.ur) {
table = table.onUpdate(relation.ur);
}
if (relation.dr) {
table = table.onDelete(relation.dr);
}
})
.toQuery());
const downQb = this.sqlClient.schema.table(
relation.tn,
function (table) {
table = table
.foreign(relation.cn, null)
.references(relation.rcn)
.on(relation.rtn);
if (relation.ur) {
table = table.onUpdate(relation.ur);
}
if (relation.dr) {
table.onDelete(relation.dr);
}
},
);
await downQb;
downQuery += this.querySeparator() + downQb.toQuery();
}
let indexList: any = await this.indexList(args);
@ -2060,8 +2061,6 @@ class SnowflakeClient extends KnexClient {
const foreignKeyName = args.foreignKeyName || null;
try {
// s = await this.sqlClient.schema.index(Object.keys(args.columns));
await this.sqlClient.schema.table(args.childTable, (table) => {
table = table
.foreign(args.childColumn, foreignKeyName)
@ -2072,27 +2071,27 @@ class SnowflakeClient extends KnexClient {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
table.onDelete(args.onDelete);
}
});
const upStatement =
this.querySeparator() +
(await this.sqlClient.schema
.table(args.childTable, (table) => {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(this.getTnPath(args.parentTable));
const upQb = this.sqlClient.schema.table(args.childTable, (table) => {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
.on(this.getTnPath(args.parentTable));
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table = table.onDelete(args.onDelete);
}
})
.toQuery());
if (args.onUpdate) {
table = table.onUpdate(args.onUpdate);
}
if (args.onDelete) {
table.onDelete(args.onDelete);
}
});
await upQb;
const upStatement = this.querySeparator() + upQb.toQuery();
this.emit(`Success : ${upStatement}`);
@ -2100,7 +2099,7 @@ class SnowflakeClient extends KnexClient {
this.querySeparator() +
this.sqlClient.schema
.table(args.childTable, (table) => {
table = table.dropForeign(args.childColumn, foreignKeyName);
table.dropForeign(args.childColumn, foreignKeyName);
})
.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 */
if (args.tn) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
relations = relations.filter(
(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
*/
// @ts-ignore
async readByPk(id, { conditionGraph }) {
async readByPk(id) {
try {
return await this._run(
this.$db.select().where(this._wherePk(id)).first(),
@ -704,10 +704,7 @@ abstract class BaseModel {
*/
async exists(id, _) {
try {
return (
Object.keys(await this.readByPk(id, { conditionGraph: null }))
.length !== 0
);
return Object.keys(await this.readByPk(id)).length !== 0;
} catch (e) {
console.log(e);
throw e;
@ -1341,7 +1338,7 @@ abstract class BaseModel {
* @param {Object} data - insert data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeInsert(data, trx?: any, cookie?: {}) {}
/**
@ -1350,7 +1347,7 @@ abstract class BaseModel {
* @param {Object} response - inserted data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterInsert(response, trx?: any, cookie?: {}) {}
/**
@ -1360,7 +1357,7 @@ abstract class BaseModel {
* @param {Object} data - insert data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorInsert(err, data, trx?: any, cookie?: {}) {}
/**
@ -1369,7 +1366,7 @@ abstract class BaseModel {
* @param {Object} data - update data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeUpdate(data, trx?: any, cookie?: {}) {}
/**
@ -1378,7 +1375,7 @@ abstract class BaseModel {
* @param {Object} response - updated data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterUpdate(response, trx?: any, cookie?: {}) {}
/**
@ -1388,7 +1385,7 @@ abstract class BaseModel {
* @param {Object} data - update data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorUpdate(err, data, trx?: any, cookie?: {}) {}
/**
@ -1397,7 +1394,7 @@ abstract class BaseModel {
* @param {Object} data - delete data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeDelete(data, trx?: any, cookie?: {}) {}
/**
@ -1406,7 +1403,7 @@ abstract class BaseModel {
* @param {Object} response - Deleted data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterDelete(response, trx?: any, cookie?: {}) {}
/**
@ -1416,7 +1413,7 @@ abstract class BaseModel {
* @param {Object} data - delete data
* @param {Object} trx? - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorDelete(err, data, trx?: any, cookie?: {}) {}
/**
@ -1425,7 +1422,7 @@ abstract class BaseModel {
* @param {Object[]} data - insert data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeInsertb(data, trx?: any) {}
/**
@ -1434,7 +1431,7 @@ abstract class BaseModel {
* @param {Object[]} response - inserted data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterInsertb(response, trx?: any) {}
/**
@ -1444,7 +1441,7 @@ abstract class BaseModel {
* @param {Object} data - delete data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorInsertb(err, data, trx?: any) {}
/**
@ -1453,7 +1450,7 @@ abstract class BaseModel {
* @param {Object[]} data - update data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeUpdateb(data, trx?: any) {}
/**
@ -1462,7 +1459,7 @@ abstract class BaseModel {
* @param {Object[]} response - updated data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterUpdateb(response, trx?: any) {}
/**
@ -1472,7 +1469,7 @@ abstract class BaseModel {
* @param {Object[]} data - delete data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async errorUpdateb(err, data, trx?: any) {}
/**
@ -1481,7 +1478,7 @@ abstract class BaseModel {
* @param {Object[]} data - delete data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeDeleteb(data, trx?: any) {}
/**
@ -1490,7 +1487,7 @@ abstract class BaseModel {
* @param {Object[]} response - deleted data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async afterDeleteb(response, trx?: any) {}
/**
@ -1500,7 +1497,7 @@ abstract class BaseModel {
* @param {Object[]} data - delete data
* @param {Object} trx - knex transaction reference
*/
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
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':
return 'MultiSelect';
case 'json':
return 'LongText';
return 'JSON';
case 'blob':
return 'LongText';
case 'geometry':

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

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

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

@ -370,7 +370,7 @@ class ModelXcMetaOracle extends BaseModelXcMeta {
case 'set':
return 'MultiSelect';
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
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const dirStat = await promisify(fs.stat)(
path.join(
this.toolDir,
@ -232,6 +233,7 @@ export default class KnexMigrator extends SqlMigrator {
);
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const metaStat = await promisify(fs.stat)(
path.join(
this.toolDir,

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

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

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

@ -3,7 +3,7 @@ import type { Request } from 'express';
const countMap = {};
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const metrics = async (req: Request, c = 150) => {
if (!req?.route?.path) return;
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;
}
if (hook.condition) {
if (hook.condition && !testHook) {
if (isBulkOperation) {
const filteredData = [];
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' });
};
const down = async (knex: Knex) => {
const down = async (_: Knex) => {
// 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
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async getViews(force = false, ncMeta = Noco.ncMeta): Promise<View[]> {
this.views = await View.listWithInfo(this.id, ncMeta);
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 Noco from '../Noco';
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')
@HttpCode(200)
async abortImport(@Request() req) {
async abortImport(@Request() _) {
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
//
const nc_getColumnSchema = async (aTblFieldId) => {
@ -1563,45 +1537,6 @@ export class AtImportProcessor {
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) => {
return !!ncLinkMappingTable.find(
(x) => x.aTbl.typeOptions.symmetricColumnId === airtableFieldId,
@ -2294,7 +2229,6 @@ export class AtImportProcessor {
};
///////////////////////////////////////////////////////////////////////////////
let recordCnt = 0;
try {
logBasic('SDK initialized');
logDetailed('Project initialization started');
@ -2374,7 +2308,6 @@ export class AtImportProcessor {
if (syncDB.options.syncData) {
try {
// await nc_DumpTableSchema();
const _perfStart = recordPerfStart();
const ncTblList = { list: [] };
ncTblList['list'] = await this.tablesService.getAccessibleTables({
@ -2404,8 +2337,6 @@ export class AtImportProcessor {
});
recordPerfStats(_perfStart, 'dbTable.read');
recordCnt = 0;
recordsMap[ncTbl.id] = await importData({
projectName: syncDB.projectName,
table: ncTbl,

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

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

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

@ -3,6 +3,7 @@ import path from 'path';
import { promisify } from 'util';
import mkdirp from 'mkdirp';
import axios from 'axios';
import { NcError } from '../../helpers/catchError';
import { getToolDir } from '../../utils/nc-config';
import type { IStorageAdapterV2, XcFile } from 'nc-plugin';
import type { Readable } from 'stream';
@ -11,7 +12,7 @@ export default class Local implements IStorageAdapterV2 {
constructor() {}
public async fileCreate(key: string, file: XcFile): Promise<any> {
const destPath = path.join(getToolDir(), ...key.split('/'));
const destPath = this.validateAndNormalisePath(key);
try {
await mkdirp(path.dirname(destPath));
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> {
const destPath = path.join(getToolDir(), ...key.split('/'));
const destPath = this.validateAndNormalisePath(key);
return new Promise((resolve, reject) => {
axios
.get(url, {
@ -71,7 +72,7 @@ export default class Local implements IStorageAdapterV2 {
stream: Readable,
): Promise<void> {
return new Promise((resolve, reject) => {
const destPath = path.join(getToolDir(), ...key.split('/'));
const destPath = this.validateAndNormalisePath(key);
try {
mkdirp(path.dirname(destPath)).then(() => {
const writableStream = fs.createWriteStream(destPath);
@ -86,12 +87,12 @@ export default class Local implements IStorageAdapterV2 {
}
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' });
}
public async getDirectoryList(key: string): Promise<string[]> {
const destDir = path.join(getToolDir(), ...key.split('/'));
const destDir = this.validateAndNormalisePath(key);
return fs.promises.readdir(destDir);
}
@ -103,7 +104,7 @@ export default class Local implements IStorageAdapterV2 {
public async fileRead(filePath: string): Promise<any> {
try {
const fileData = await fs.promises.readFile(
path.join(getToolDir(), ...filePath.split('/')),
this.validateAndNormalisePath(filePath, true),
);
return fileData;
} catch (e) {
@ -118,4 +119,29 @@ export default class Local implements IStorageAdapterV2 {
test(): Promise<boolean> {
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,
firstname,
lastname,
token,
ignore_subscribe,
// token,
// ignore_subscribe,
} = createUserDto as any;
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 getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName';
import mapDefaultDisplayValue from '../helpers/mapDefaultDisplayValue';
import {
Audit,
Column,
Model,
ModelRoleVisibility,
Project,
} from '../models';
import { Audit, Column, Model, ModelRoleVisibility, Project } from '../models';
import Noco from '../Noco';
import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2';
import { validatePayload } from '../helpers';

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

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

Loading…
Cancel
Save