diff --git a/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts b/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts index b68a10f1d3..0a8b5c5a15 100644 --- a/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts +++ b/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts @@ -24,7 +24,6 @@ export async function getViewAndModelFromRequestByAliasOrId( const model = await Model.getByAliasOrId({ project_id: project.id, - base_id: project.bases?.[0]?.id, aliasOrId: req.params.tableName, }); const view = diff --git a/packages/nocodb/src/lib/meta/api/dataApis/oldDataApis.ts b/packages/nocodb/src/lib/meta/api/dataApis/oldDataApis.ts index cff7dc0534..5df008eb8a 100644 --- a/packages/nocodb/src/lib/meta/api/dataApis/oldDataApis.ts +++ b/packages/nocodb/src/lib/meta/api/dataApis/oldDataApis.ts @@ -108,7 +108,6 @@ async function getViewAndModelFromRequest(req) { const project = await Project.getWithInfo(req.params.projectId); const model = await Model.getByAliasOrId({ project_id: project.id, - base_id: project.bases?.[0]?.id, aliasOrId: req.params.tableName, }); const view = diff --git a/packages/nocodb/src/lib/meta/api/metaDiffApis.ts b/packages/nocodb/src/lib/meta/api/metaDiffApis.ts index 3257881ae4..97b432a23c 100644 --- a/packages/nocodb/src/lib/meta/api/metaDiffApis.ts +++ b/packages/nocodb/src/lib/meta/api/metaDiffApis.ts @@ -532,203 +532,205 @@ async function getMetaDiff( export async function metaDiff(req, res) { const project = await Project.getWithInfo(req.params.projectId); - const base = project.bases[0]; - - // @ts-ignore - const sqlClient = NcConnectionMgrv2.getSqlClient(base); - const changes = await getMetaDiff(sqlClient, project, base); + const changes = [] + for (const base of project.bases) { + // @ts-ignore + const sqlClient = NcConnectionMgrv2.getSqlClient(base); + changes.push(await getMetaDiff(sqlClient, project, base)); + } res.json(changes); } export async function metaDiffSync(req, res) { const project = await Project.getWithInfo(req.params.projectId); - const base = project.bases[0]; - const virtualColumnInsert: Array<() => Promise> = []; - - // @ts-ignore - const sqlClient = NcConnectionMgrv2.getSqlClient(base); - const changes = await getMetaDiff(sqlClient, project, base); - - /* Get all relations */ - // const relations = (await sqlClient.relationListAll())?.data?.list; - - for (const { table_name, detectedChanges } of changes) { - // reorder changes to apply relation remove changes - // before column remove to avoid foreign key constraint error - detectedChanges.sort((a, b) => { - return ( - applyChangesPriorityOrder.indexOf(b.type) - - applyChangesPriorityOrder.indexOf(a.type) - ); - }); - - for (const change of detectedChanges) { - switch (change.type) { - case MetaDiffType.TABLE_NEW: - { - const columns = ( - await sqlClient.columnList({ tn: table_name }) - )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); - - mapDefaultPrimaryValue(columns); - - const model = await Model.insert(project.id, base.id, { - table_name: table_name, - title: getTableNameAlias(table_name, project.prefix, base), - type: ModelTypes.TABLE, - }); - - for (const column of columns) { - await Column.insert({ - uidt: getColumnUiType(base, column), - fk_model_id: model.id, - ...column, - title: getColumnNameAlias(column.column_name, base), + for (const base of project.bases) { + const virtualColumnInsert: Array<() => Promise> = []; + + // @ts-ignore + const sqlClient = NcConnectionMgrv2.getSqlClient(base); + const changes = await getMetaDiff(sqlClient, project, base); + + /* Get all relations */ + // const relations = (await sqlClient.relationListAll())?.data?.list; + + for (const { table_name, detectedChanges } of changes) { + // reorder changes to apply relation remove changes + // before column remove to avoid foreign key constraint error + detectedChanges.sort((a, b) => { + return ( + applyChangesPriorityOrder.indexOf(b.type) - + applyChangesPriorityOrder.indexOf(a.type) + ); + }); + + for (const change of detectedChanges) { + switch (change.type) { + case MetaDiffType.TABLE_NEW: + { + const columns = ( + await sqlClient.columnList({ tn: table_name }) + )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); + + mapDefaultPrimaryValue(columns); + + const model = await Model.insert(project.id, base.id, { + table_name: table_name, + title: getTableNameAlias(table_name, project.prefix, base), + type: ModelTypes.TABLE, }); + + for (const column of columns) { + await Column.insert({ + uidt: getColumnUiType(base, column), + fk_model_id: model.id, + ...column, + title: getColumnNameAlias(column.column_name, base), + }); + } } - } - break; - case MetaDiffType.VIEW_NEW: - { - const columns = ( - await sqlClient.columnList({ tn: table_name }) - )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); - - mapDefaultPrimaryValue(columns); - - const model = await Model.insert(project.id, base.id, { - table_name: table_name, - title: getTableNameAlias(table_name, project.prefix, base), - type: ModelTypes.VIEW, - }); - - for (const column of columns) { - await Column.insert({ - uidt: getColumnUiType(base, column), - fk_model_id: model.id, - ...column, - title: getColumnNameAlias(column.column_name, base), + break; + case MetaDiffType.VIEW_NEW: + { + const columns = ( + await sqlClient.columnList({ tn: table_name }) + )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); + + mapDefaultPrimaryValue(columns); + + const model = await Model.insert(project.id, base.id, { + table_name: table_name, + title: getTableNameAlias(table_name, project.prefix, base), + type: ModelTypes.VIEW, }); + + for (const column of columns) { + await Column.insert({ + uidt: getColumnUiType(base, column), + fk_model_id: model.id, + ...column, + title: getColumnNameAlias(column.column_name, base), + }); + } } - } - break; - case MetaDiffType.TABLE_REMOVE: - case MetaDiffType.VIEW_REMOVE: - { - await change.model.delete(); - } - break; - case MetaDiffType.TABLE_COLUMN_ADD: - case MetaDiffType.VIEW_COLUMN_ADD: - { - const columns = ( - await sqlClient.columnList({ tn: table_name }) - )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); - const column = columns.find((c) => c.cn === change.cn); - column.uidt = getColumnUiType(base, column); - //todo: inflection - column.title = getColumnNameAlias(column.cn, base); - await Column.insert({ fk_model_id: change.id, ...column }); - } - // update old - // populateParams.tableNames.push({ tn }); - // populateParams.oldMetas[tn] = oldMetas.find(m => m.tn === tn); - - break; - case MetaDiffType.TABLE_COLUMN_TYPE_CHANGE: - case MetaDiffType.VIEW_COLUMN_TYPE_CHANGE: - { - const columns = ( - await sqlClient.columnList({ tn: table_name }) - )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); - const column = columns.find((c) => c.cn === change.cn); - const metaFact = ModelXcMetaFactory.create( - { client: base.type }, - {} - ); - column.uidt = metaFact.getUIDataType(column); - column.title = change.column.title; - await Column.update(change.column.id, column); - } - break; - case MetaDiffType.TABLE_COLUMN_REMOVE: - case MetaDiffType.VIEW_COLUMN_REMOVE: - await change.column.delete(); - break; - case MetaDiffType.TABLE_RELATION_REMOVE: - case MetaDiffType.TABLE_VIRTUAL_M2M_REMOVE: - await change.column.delete(); - break; - case MetaDiffType.TABLE_RELATION_ADD: - { - virtualColumnInsert.push(async () => { - const parentModel = await Model.getByIdOrName({ - project_id: base.project_id, - base_id: base.id, - table_name: change.rtn, - }); - const childModel = await Model.getByIdOrName({ - project_id: base.project_id, - base_id: base.id, - table_name: change.tn, - }); - const parentCol = await parentModel - .getColumns() - .then((cols) => cols.find((c) => c.column_name === change.rcn)); - const childCol = await childModel - .getColumns() - .then((cols) => cols.find((c) => c.column_name === change.cn)); - - await Column.update(childCol.id, { - ...childCol, - uidt: UITypes.ForeignKey, - system: true, - }); - - if (change.relationType === RelationTypes.BELONGS_TO) { - const title = getUniqueColumnAliasName( - childModel.columns, - `${parentModel.title || parentModel.table_name}` - ); - await Column.insert({ - uidt: UITypes.LinkToAnotherRecord, - title, - fk_model_id: childModel.id, - fk_related_model_id: parentModel.id, - type: RelationTypes.BELONGS_TO, - fk_parent_column_id: parentCol.id, - fk_child_column_id: childCol.id, - virtual: false, + break; + case MetaDiffType.TABLE_REMOVE: + case MetaDiffType.VIEW_REMOVE: + { + await change.model.delete(); + } + break; + case MetaDiffType.TABLE_COLUMN_ADD: + case MetaDiffType.VIEW_COLUMN_ADD: + { + const columns = ( + await sqlClient.columnList({ tn: table_name }) + )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); + const column = columns.find((c) => c.cn === change.cn); + column.uidt = getColumnUiType(base, column); + //todo: inflection + column.title = getColumnNameAlias(column.cn, base); + await Column.insert({ fk_model_id: change.id, ...column }); + } + // update old + // populateParams.tableNames.push({ tn }); + // populateParams.oldMetas[tn] = oldMetas.find(m => m.tn === tn); + + break; + case MetaDiffType.TABLE_COLUMN_TYPE_CHANGE: + case MetaDiffType.VIEW_COLUMN_TYPE_CHANGE: + { + const columns = ( + await sqlClient.columnList({ tn: table_name }) + )?.data?.list?.map((c) => ({ ...c, column_name: c.cn })); + const column = columns.find((c) => c.cn === change.cn); + const metaFact = ModelXcMetaFactory.create( + { client: base.type }, + {} + ); + column.uidt = metaFact.getUIDataType(column); + column.title = change.column.title; + await Column.update(change.column.id, column); + } + break; + case MetaDiffType.TABLE_COLUMN_REMOVE: + case MetaDiffType.VIEW_COLUMN_REMOVE: + await change.column.delete(); + break; + case MetaDiffType.TABLE_RELATION_REMOVE: + case MetaDiffType.TABLE_VIRTUAL_M2M_REMOVE: + await change.column.delete(); + break; + case MetaDiffType.TABLE_RELATION_ADD: + { + virtualColumnInsert.push(async () => { + const parentModel = await Model.getByIdOrName({ + project_id: base.project_id, + base_id: base.id, + table_name: change.rtn, }); - } else if (change.relationType === RelationTypes.HAS_MANY) { - const title = getUniqueColumnAliasName( - childModel.columns, - `${childModel.title || childModel.table_name} List` - ); - await Column.insert({ - uidt: UITypes.LinkToAnotherRecord, - title, - fk_model_id: parentModel.id, - fk_related_model_id: childModel.id, - type: RelationTypes.HAS_MANY, - fk_parent_column_id: parentCol.id, - fk_child_column_id: childCol.id, - virtual: false, + const childModel = await Model.getByIdOrName({ + project_id: base.project_id, + base_id: base.id, + table_name: change.tn, }); - } - }); - } - break; + const parentCol = await parentModel + .getColumns() + .then((cols) => cols.find((c) => c.column_name === change.rcn)); + const childCol = await childModel + .getColumns() + .then((cols) => cols.find((c) => c.column_name === change.cn)); + + await Column.update(childCol.id, { + ...childCol, + uidt: UITypes.ForeignKey, + system: true, + }); + + if (change.relationType === RelationTypes.BELONGS_TO) { + const title = getUniqueColumnAliasName( + childModel.columns, + `${parentModel.title || parentModel.table_name}` + ); + await Column.insert({ + uidt: UITypes.LinkToAnotherRecord, + title, + fk_model_id: childModel.id, + fk_related_model_id: parentModel.id, + type: RelationTypes.BELONGS_TO, + fk_parent_column_id: parentCol.id, + fk_child_column_id: childCol.id, + virtual: false, + }); + } else if (change.relationType === RelationTypes.HAS_MANY) { + const title = getUniqueColumnAliasName( + childModel.columns, + `${childModel.title || childModel.table_name} List` + ); + await Column.insert({ + uidt: UITypes.LinkToAnotherRecord, + title, + fk_model_id: parentModel.id, + fk_related_model_id: childModel.id, + type: RelationTypes.HAS_MANY, + fk_parent_column_id: parentCol.id, + fk_child_column_id: childCol.id, + virtual: false, + }); + } + }); + } + break; + } } } + + await NcHelp.executeOperations(virtualColumnInsert, base.type); + + // populate m2m relations + await extractAndGenerateManyToManyRelations(await base.getModels()); } - await NcHelp.executeOperations(virtualColumnInsert, base.type); - - // populate m2m relations - await extractAndGenerateManyToManyRelations(await base.getModels()); - Tele.emit('evt', { evt_type: 'metaDiff:synced' }); res.json({ msg: 'success' }); diff --git a/packages/nocodb/src/lib/meta/api/modelVisibilityApis.ts b/packages/nocodb/src/lib/meta/api/modelVisibilityApis.ts index 68f95e928b..59b7006f71 100644 --- a/packages/nocodb/src/lib/meta/api/modelVisibilityApis.ts +++ b/packages/nocodb/src/lib/meta/api/modelVisibilityApis.ts @@ -3,7 +3,6 @@ import ModelRoleVisibility from '../../models/ModelRoleVisibility'; import { Router } from 'express'; import { Tele } from 'nc-help'; import ncMetaAclMw from '../helpers/ncMetaAclMw'; -import Project from '../../models/Project'; import { metaApiMetrics } from '../helpers/apiMetrics'; async function xcVisibilityMetaSetAll(req, res) { Tele.emit('evt', { evt_type: 'uiAcl:updated' }); @@ -49,13 +48,12 @@ export async function xcVisibilityMetaGet( const roles = ['owner', 'creator', 'viewer', 'editor', 'commenter', 'guest']; const defaultDisabled = roles.reduce((o, r) => ({ ...o, [r]: false }), {}); - const project = await Project.getWithInfo(projectId); - + let models = _models || (await Model.list({ project_id: projectId, - base_id: project?.bases?.[0]?.id, + base_id: undefined, })); models = includeM2M ? models : (models.filter((t) => !t.mm) as Model[]); diff --git a/packages/nocodb/src/lib/meta/api/projectApis.ts b/packages/nocodb/src/lib/meta/api/projectApis.ts index c710b468fa..cafc073080 100644 --- a/packages/nocodb/src/lib/meta/api/projectApis.ts +++ b/packages/nocodb/src/lib/meta/api/projectApis.ts @@ -452,15 +452,12 @@ async function populateMeta(base: Base, project: Project): Promise { return info; } -export async function projectInfoGet(req, res) { - const project = await Project.getWithInfo(req.params.projectId); +export async function projectInfoGet(_req, res) { res.json({ Node: process.version, Arch: process.arch, Platform: process.platform, Docker: isDocker(), - Database: project.bases?.[0]?.type, - ProjectOnRootDB: !!project?.is_meta, RootDB: Noco.getConfig()?.meta?.db?.client, PackageVersion: packageVersion, }); @@ -469,22 +466,25 @@ export async function projectInfoGet(req, res) { export async function projectCost(req, res) { let cost = 0; const project = await Project.getWithInfo(req.params.projectId); - const sqlClient = NcConnectionMgrv2.getSqlClient(project.bases[0]); - const userCount = await ProjectUser.getUsersCount(req.query); - const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords; - - if (recordCount > 100000) { - // 36,000 or $79/user/month - cost = Math.max(36000, 948 * userCount); - } else if (recordCount > 50000) { - // $36,000 or $50/user/month - cost = Math.max(36000, 600 * userCount); - } else if (recordCount > 10000) { - // $240/user/yr - cost = Math.min(240 * userCount, 36000); - } else if (recordCount > 1000) { - // $120/user/yr - cost = Math.min(120 * userCount, 36000); + + for (const base of project.bases) { + const sqlClient = NcConnectionMgrv2.getSqlClient(base); + const userCount = await ProjectUser.getUsersCount(req.query); + const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords; + + if (recordCount > 100000) { + // 36,000 or $79/user/month + cost = Math.max(36000, 948 * userCount); + } else if (recordCount > 50000) { + // $36,000 or $50/user/month + cost = Math.max(36000, 600 * userCount); + } else if (recordCount > 10000) { + // $240/user/yr + cost = Math.min(240 * userCount, 36000); + } else if (recordCount > 1000) { + // $120/user/yr + cost = Math.min(120 * userCount, 36000); + } } Tele.event({ diff --git a/packages/nocodb/src/lib/models/Model.ts b/packages/nocodb/src/lib/models/Model.ts index 2cb7f2e14f..01d1bf0545 100644 --- a/packages/nocodb/src/lib/models/Model.ts +++ b/packages/nocodb/src/lib/models/Model.ts @@ -597,7 +597,7 @@ export default class Model implements TableType { aliasOrId, }: { project_id: string; - base_id: string | undefined; + base_id?: string; aliasOrId: string; }, ncMeta = Noco.ncMeta @@ -610,27 +610,49 @@ export default class Model implements TableType { CacheGetType.TYPE_OBJECT )); if (!modelId) { - const model = await ncMeta.metaGet2( - null, - null, - MetaTable.MODELS, - { project_id, base_id }, - null, - { - _or: [ + const model = base_id + ? await ncMeta.metaGet2( + null, + null, + MetaTable.MODELS, + { project_id, base_id }, + null, { - id: { - eq: aliasOrId, - }, - }, + _or: [ + { + id: { + eq: aliasOrId, + }, + }, + { + title: { + eq: aliasOrId, + }, + }, + ], + } + ) + : await ncMeta.metaGet2( + null, + null, + MetaTable.MODELS, + { project_id }, + null, { - title: { - eq: aliasOrId, - }, - }, - ], - } - ); + _or: [ + { + id: { + eq: aliasOrId, + }, + }, + { + title: { + eq: aliasOrId, + }, + }, + ], + } + ); if (model) { await NocoCache.set( `${CacheScope.MODEL}:${project_id}:${aliasOrId}`,