From a982f98a7a7e0edfa77f1eedb89fd03c2e6769de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 2 Apr 2024 09:57:12 +0000 Subject: [PATCH 01/30] chore: update sdk path --- packages/nc-gui/package.json | 2 +- packages/nocodb/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/package.json b/packages/nc-gui/package.json index 5682ad0c17b..c614c2b5184 100644 --- a/packages/nc-gui/package.json +++ b/packages/nc-gui/package.json @@ -75,7 +75,7 @@ "marked": "^4.3.0", "monaco-editor": "^0.45.0", "monaco-sql-languages": "^0.11.0", - "nocodb-sdk": "0.204.9", + "nocodb-sdk": "workspace:^", "papaparse": "^5.4.1", "parse-github-url": "^1.0.2", "pinia": "^2.1.7", diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index d6ee2ef82a6..3e94dfe1a73 100644 --- a/packages/nocodb/package.json +++ b/packages/nocodb/package.json @@ -137,7 +137,7 @@ "ncp": "^2.0.0", "nestjs-kafka": "^1.0.6", "nestjs-throttler-storage-redis": "^0.4.4", - "nocodb-sdk": "0.204.9", + "nocodb-sdk": "workspace:^", "nodemailer": "^6.9.13", "object-hash": "^3.0.0", "object-sizeof": "^2.6.4", From 2dc59ef415b82dcfdcfcf412d71b82f40dd38172 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 11:36:25 +0000 Subject: [PATCH 02/30] fix: clear single query cache when display value of linked table is changed --- packages/nocodb/src/models/Model.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index f53d71876e7..460cc162bc9 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -9,7 +9,7 @@ import dayjs from 'dayjs'; import type { BoolType, TableReqType, TableType } from 'nocodb-sdk'; import type { XKnex } from '~/db/CustomKnex'; -import type { LinkToAnotherRecordColumn } from '~/models/index'; +import type { LinksColumn, LinkToAnotherRecordColumn } from '~/models/index'; import Hook from '~/models/Hook'; import Audit from '~/models/Audit'; import View from '~/models/View'; @@ -817,6 +817,21 @@ export default class Model implements TableType { } } + // clear all single query cache of related views + for (const col of model.columns) { + if (!isLinksOrLTAR(col)) continue; + const colOptions = await col.getColOptions< + LinkToAnotherRecordColumn | LinksColumn + >(); + if (colOptions.fk_related_model_id === model.id) { + await View.clearSingleQueryCache( + colOptions.fk_related_model_id, + null, + ncMeta, + ); + } + } + return true; } From 0f9acb12333a079f1748dab498316b39099c832b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 11:36:25 +0000 Subject: [PATCH 03/30] refactor: use set to avoid duplicate --- packages/nocodb/src/models/Model.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index 460cc162bc9..5c696fea3a3 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -817,21 +817,24 @@ export default class Model implements TableType { } } + // use set to avoid duplicate + const relatedModelIds = new Set(); + // clear all single query cache of related views for (const col of model.columns) { if (!isLinksOrLTAR(col)) continue; const colOptions = await col.getColOptions< LinkToAnotherRecordColumn | LinksColumn >(); - if (colOptions.fk_related_model_id === model.id) { - await View.clearSingleQueryCache( - colOptions.fk_related_model_id, - null, - ncMeta, - ); - } + relatedModelIds.add(colOptions?.fk_related_model_id); } + await Promise.all( + Array.from(relatedModelIds).map(async (modelId: string) => { + await View.clearSingleQueryCache(modelId, null, ncMeta); + }), + ); + return true; } From e6a4e564c407f367d5a17f1bfc4e83998e2aed21 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 12:10:43 +0000 Subject: [PATCH 04/30] feat: org migrations and multiple refresh token --- .../nocodb/src/helpers/initAdminFromEnv.ts | 2 - packages/nocodb/src/meta/meta.service.ts | 2 +- .../meta/migrations/XcMigrationSourcev2.ts | 4 + .../nocodb/src/meta/migrations/v2/nc_011.ts | 6 +- .../src/meta/migrations/v2/nc_032_cleanup.ts | 8 +- .../v2/nc_043_user_refresh_token.ts | 27 +++++ packages/nocodb/src/models/User.ts | 18 ++- .../nocodb/src/models/UserRefreshToken.ts | 111 ++++++++++++++++++ .../src/services/users/users.service.ts | 48 +++++--- packages/nocodb/src/utils/globals.ts | 7 +- 10 files changed, 204 insertions(+), 29 deletions(-) create mode 100644 packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts create mode 100644 packages/nocodb/src/models/UserRefreshToken.ts diff --git a/packages/nocodb/src/helpers/initAdminFromEnv.ts b/packages/nocodb/src/helpers/initAdminFromEnv.ts index 4f68d8db28a..89b69e55a0d 100644 --- a/packages/nocodb/src/helpers/initAdminFromEnv.ts +++ b/packages/nocodb/src/helpers/initAdminFromEnv.ts @@ -185,7 +185,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { password, email_verification_token, token_version: randomTokenString(), - refresh_token: null, }, ncMeta, ); @@ -199,7 +198,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { password, email_verification_token, token_version: randomTokenString(), - refresh_token: null, }, ncMeta, ); diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 608e9eb6664..ee564124c20 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -228,7 +228,7 @@ export class MetaService { case MetaTable.USERS: prefix = 'us'; break; - case MetaTable.ORGS: + case MetaTable.ORGS_OLD: prefix = 'org'; break; case MetaTable.TEAMS: diff --git a/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts b/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts index 384429d5f53..87026749890 100644 --- a/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts +++ b/packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts @@ -29,6 +29,7 @@ import * as nc_039_sqlite_alter_column_types from '~/meta/migrations/v2/nc_039_s import * as nc_040_form_view_alter_column_types from '~/meta/migrations/v2/nc_040_form_view_alter_column_types'; import * as nc_041_calendar_view from '~/meta/migrations/v2/nc_041_calendar_view'; import * as nc_042_user_block from '~/meta/migrations/v2/nc_042_user_block'; +import * as nc_043_user_refresh_token from '~/meta/migrations/v2/nc_043_user_refresh_token'; // Create a custom migration source class export default class XcMigrationSourcev2 { @@ -69,6 +70,7 @@ export default class XcMigrationSourcev2 { 'nc_040_form_view_alter_column_types', 'nc_041_calendar_view', 'nc_042_user_block', + 'nc_043_user_refresh_token', ]); } @@ -140,6 +142,8 @@ export default class XcMigrationSourcev2 { return nc_041_calendar_view; case 'nc_042_user_block': return nc_042_user_block; + case 'nc_043_user_refresh_token': + return nc_043_user_refresh_token; } } } diff --git a/packages/nocodb/src/meta/migrations/v2/nc_011.ts b/packages/nocodb/src/meta/migrations/v2/nc_011.ts index 7aca8fb30ed..126b69318b9 100644 --- a/packages/nocodb/src/meta/migrations/v2/nc_011.ts +++ b/packages/nocodb/src/meta/migrations/v2/nc_011.ts @@ -700,7 +700,7 @@ const up = async (knex) => { table.timestamps(true, true); }); - await knex.schema.createTable(MetaTable.ORGS, (table) => { + await knex.schema.createTable(MetaTable.ORGS_OLD, (table) => { table.string('id', 20).primary().notNullable(); table.string('title'); @@ -712,13 +712,13 @@ const up = async (knex) => { table.string('title'); table.string('org_id', 20); - table.foreign('org_id').references(`${MetaTable.ORGS}.id`); + table.foreign('org_id').references(`${MetaTable.ORGS_OLD}.id`); table.timestamps(true, true); }); await knex.schema.createTable(MetaTable.TEAM_USERS, (table) => { table.string('org_id', 20); - table.foreign('org_id').references(`${MetaTable.ORGS}.id`); + table.foreign('org_id').references(`${MetaTable.ORGS_OLD}.id`); table.string('user_id', 20); table.foreign('user_id').references(`${MetaTable.USERS}.id`); table.timestamps(true, true); diff --git a/packages/nocodb/src/meta/migrations/v2/nc_032_cleanup.ts b/packages/nocodb/src/meta/migrations/v2/nc_032_cleanup.ts index d9b8b72486b..dde8c9c6459 100644 --- a/packages/nocodb/src/meta/migrations/v2/nc_032_cleanup.ts +++ b/packages/nocodb/src/meta/migrations/v2/nc_032_cleanup.ts @@ -7,11 +7,11 @@ const up = async (knex: Knex) => { await knex.schema.dropTable(MetaTable.TEAMS); - await knex.schema.dropTable(MetaTable.ORGS); + await knex.schema.dropTable(MetaTable.ORGS_OLD); }; const down = async (knex: Knex) => { - await knex.schema.createTable(MetaTable.ORGS, (table) => { + await knex.schema.createTable(MetaTable.ORGS_OLD, (table) => { table.string('id', 20).primary().notNullable(); table.string('title'); @@ -23,13 +23,13 @@ const down = async (knex: Knex) => { table.string('title'); table.string('org_id', 20); - table.foreign('org_id').references(`${MetaTable.ORGS}.id`); + table.foreign('org_id').references(`${MetaTable.ORGS_OLD}.id`); table.timestamps(true, true); }); await knex.schema.createTable(MetaTable.TEAM_USERS, (table) => { table.string('org_id', 20); - table.foreign('org_id').references(`${MetaTable.ORGS}.id`); + table.foreign('org_id').references(`${MetaTable.ORGS_OLD}.id`); table.string('user_id', 20); table.foreign('user_id').references(`${MetaTable.USERS}.id`); table.timestamps(true, true); diff --git a/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts b/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts new file mode 100644 index 00000000000..1274c96a045 --- /dev/null +++ b/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts @@ -0,0 +1,27 @@ +import type { Knex } from 'knex'; +import { MetaTable } from '~/utils/globals'; + +const up = async (knex: Knex) => { + await knex.schema.alterTable(MetaTable.USERS, (table) => { + table.dropColumn('refresh_token'); + }); + + await knex.schema.createTable(MetaTable.USER_REFRESH_TOKENS, (table) => { + table.string('fk_user_id', 20).index(); + table.string('token', 255).index(); + table.text('meta'); + + table.timestamp('expires_at'); + + table.timestamps(true, true); + }); +}; + +const down = async (knex: Knex) => { + await knex.schema.dropTable(MetaTable.USER_REFRESH_TOKENS); + await knex.schema.alterTable(MetaTable.USERS, (table) => { + table.string('refresh_token', 255); + }); +}; + +export { up, down }; diff --git a/packages/nocodb/src/models/User.ts b/packages/nocodb/src/models/User.ts index 6eba62d5eb7..be336424e3f 100644 --- a/packages/nocodb/src/models/User.ts +++ b/packages/nocodb/src/models/User.ts @@ -11,6 +11,7 @@ import { } from '~/utils/globals'; import { Base, BaseUser } from '~/models'; import { sanitiseUserObj } from '~/utils'; +import UserRefreshToken from '~/models/UserRefreshToken'; export default class User implements UserType { id: string; @@ -184,9 +185,21 @@ export default class User implements UserType { } static async getByRefreshToken(refresh_token, ncMeta = Noco.ncMeta) { - return await ncMeta.metaGet2(null, null, MetaTable.USERS, { + const userRefreshToken = await UserRefreshToken.getByToken( refresh_token, - }); + ncMeta, + ); + + if(!userRefreshToken){ + return null; + } + + return await ncMeta.metaGet2( + null, + null, + MetaTable.USERS, + userRefreshToken.fk_user_id, + ); } public static async list( @@ -262,6 +275,7 @@ export default class User implements UserType { args: { user?: User; baseId?: string; + orgId?: string; }, ncMeta = Noco.ncMeta, ) { diff --git a/packages/nocodb/src/models/UserRefreshToken.ts b/packages/nocodb/src/models/UserRefreshToken.ts new file mode 100644 index 00000000000..69b699a3a32 --- /dev/null +++ b/packages/nocodb/src/models/UserRefreshToken.ts @@ -0,0 +1,111 @@ +import dayjs from 'dayjs'; +import Noco from '~/Noco'; +import { extractProps } from '~/helpers/extractProps'; +import { MetaTable } from '~/utils/globals'; +import { parseMetaProp, stringifyMetaProp } from '~/utils/modelUtils'; + +export default class UserRefreshToken { + fk_user_id: string; + token: string; + expires_at: any; + meta?: any; + created_at?: any; + updated_at?: any; + + public static async insert( + syncLog: Partial, + ncMeta = Noco.ncMeta, + ) { + // clear old invalid tokens before inserting new one + // todo: verify the populated sql query + await ncMeta.metaDelete( + null, + null, + MetaTable.USER_REFRESH_TOKENS, + { + fk_user_id: syncLog.fk_user_id, + }, + { + expires_at: { + lt: dayjs().toDate(), + }, + }, + ); + + const insertObj = extractProps(syncLog, [ + 'fk_user_id', + 'token', + 'expires_at', + 'meta', + ]); + + // set default expiry as 90 days if missing + if (!('expires_at' in insertObj)) { + insertObj.expires_at = dayjs().add(90, 'day').toDate(); + } + + if ('meta' in insertObj) { + insertObj.meta = stringifyMetaProp(insertObj); + } + + await ncMeta.metaInsert2( + null, + null, + MetaTable.USER_REFRESH_TOKENS, + insertObj, + true, + ); + return insertObj; + } + + static async updateOldToken( + oldToken: string, + newToken: string, + ncMeta = Noco.ncMeta, + ) { + return await ncMeta.metaUpdate( + null, + null, + MetaTable.USER_REFRESH_TOKENS, + { + token: oldToken, + expires_at: dayjs().add(90, 'day').toDate(), + }, + { + token: newToken, + }, + ); + } + + static async deleteToken(token: string, ncMeta = Noco.ncMeta) { + return await ncMeta.metaDelete(null, null, MetaTable.USER_REFRESH_TOKENS, { + token, + }); + } + + static async deleteAllUserToken(userId: string, ncMeta = Noco.ncMeta) { + return await ncMeta.metaDelete(null, null, MetaTable.USER_REFRESH_TOKENS, { + fk_user_id: userId, + }); + } + + static async getByToken( + token: string, + ncMeta = Noco.ncMeta, + ): Promise { + const userToken = await ncMeta.metaGet2( + null, + null, + MetaTable.USER_REFRESH_TOKENS, + { + token, + }, + ); + + if (!userToken) return null; + + userToken.meta = parseMetaProp(userToken); + + return userToken; + } +} diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index e3817c078fe..ff183579672 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -27,6 +27,7 @@ import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2'; import { NcError } from '~/helpers/catchError'; import { BasesService } from '~/services/bases.service'; import { extractProps } from '~/helpers/extractProps'; +import UserRefreshToken from "~/models/UserRefreshToken"; @Injectable() export class UsersService { @@ -380,10 +381,15 @@ export class UsersService { const refreshToken = randomTokenString(); - await User.update(user.id, { - email: user.email, - refresh_token: refreshToken, - }); + // await User.update(user.id, { + // email: user.email, + // refresh_token: refreshToken, + // }); + + await UserRefreshToken.insert({ + token: refreshToken, + fk_user_id: user.id + }) setTokenCookie(param.res, refreshToken); @@ -495,10 +501,15 @@ export class UsersService { const refreshToken = randomTokenString(); - await User.update(user.id, { - refresh_token: refreshToken, - email: user.email, - }); + // await User.update(user.id, { + // refresh_token: refreshToken, + // email: user.email, + // }); + + await UserRefreshToken.insert({ + token: refreshToken, + fk_user_id: user.id + }) setTokenCookie(param.res, refreshToken); @@ -531,10 +542,12 @@ export class UsersService { this.clearCookie(param); const user = (param.req as any).user; if (user?.id) { - await User.update(user.id, { - refresh_token: null, - token_version: randomTokenString(), - }); + // await User.update(user.id, { + // refresh_token: null, + // token_version: randomTokenString(), + // }); + // todo: clear only token present in cookie + await UserRefreshToken.deleteAllUserToken(user.id); } return { msg: 'Signed out successfully' }; } catch (e) { @@ -572,10 +585,17 @@ export class UsersService { } await User.update(user.id, { - refresh_token: refreshToken, - email: user.email, + // refresh_token: refreshToken, + // email: user.email, token_version: user['token_version'], }); + + await UserRefreshToken.insert({ + token: refreshToken, + fk_user_id: user.id, + meta: req.user?.extra, + }); + setTokenCookie(res, refreshToken); } } diff --git a/packages/nocodb/src/utils/globals.ts b/packages/nocodb/src/utils/globals.ts index 039b99155c0..ff286fa015f 100644 --- a/packages/nocodb/src/utils/globals.ts +++ b/packages/nocodb/src/utils/globals.ts @@ -29,7 +29,7 @@ export enum MetaTable { KANBAN_VIEW = 'nc_kanban_view_v2', KANBAN_VIEW_COLUMNS = 'nc_kanban_view_columns_v2', USERS = 'nc_users_v2', - ORGS = 'nc_orgs_v2', + ORGS_OLD = 'nc_orgs_v2', TEAMS = 'nc_teams_v2', TEAM_USERS = 'nc_team_users_v2', VIEWS = 'nc_views_v2', @@ -46,6 +46,7 @@ export enum MetaTable { MAP_VIEW_COLUMNS = 'nc_map_view_columns_v2', STORE = 'nc_store', NOTIFICATION = 'notification', + USER_REFRESH_TOKENS = 'nc_user_refresh_tokens', } export enum MetaTableOldV2 { @@ -60,7 +61,7 @@ export const orderedMetaTables = [ MetaTable.AUDIT, MetaTable.TEAM_USERS, MetaTable.TEAMS, - MetaTable.ORGS, + MetaTable.ORGS_OLD, MetaTable.PROJECT_USERS, MetaTable.USERS, MetaTable.MAP_VIEW, @@ -151,7 +152,7 @@ export enum CacheScope { MAP_VIEW_COLUMN = 'mapViewColumn', KANBAN_VIEW_COLUMN = 'kanbanViewColumn', USER = 'user', - ORGS = 'orgs', + ORGS_OLD = 'orgs', TEAM = 'team', TEAM_USER = 'teamUser', VIEW = 'view', From c7d6915068235642d96bba0cba018a4ab1f5dd49 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 12:10:44 +0000 Subject: [PATCH 05/30] refactor: remove refresh token from user table --- packages/nocodb/src/helpers/initAdminFromEnv.ts | 2 -- packages/nocodb/src/models/User.ts | 3 --- packages/nocodb/src/utils/sanitiseUserObj.ts | 1 - 3 files changed, 6 deletions(-) diff --git a/packages/nocodb/src/helpers/initAdminFromEnv.ts b/packages/nocodb/src/helpers/initAdminFromEnv.ts index 89b69e55a0d..d627a99bcb2 100644 --- a/packages/nocodb/src/helpers/initAdminFromEnv.ts +++ b/packages/nocodb/src/helpers/initAdminFromEnv.ts @@ -218,7 +218,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { password, email_verification_token, token_version: randomTokenString(), - refresh_token: null, }, ncMeta, ); @@ -246,7 +245,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { password, email_verification_token, token_version: randomTokenString(), - refresh_token: null, roles, }, ncMeta, diff --git a/packages/nocodb/src/models/User.ts b/packages/nocodb/src/models/User.ts index be336424e3f..2c296470216 100644 --- a/packages/nocodb/src/models/User.ts +++ b/packages/nocodb/src/models/User.ts @@ -21,7 +21,6 @@ export default class User implements UserType { password?: string; salt?: string; - refresh_token?: string; invite_token?: string; invite_token_expires?: number | Date; reset_password_expires?: number | Date; @@ -51,7 +50,6 @@ export default class User implements UserType { 'email', 'password', 'salt', - 'refresh_token', 'invite_token', 'invite_token_expires', 'reset_password_expires', @@ -92,7 +90,6 @@ export default class User implements UserType { 'email', 'password', 'salt', - 'refresh_token', 'invite_token', 'invite_token_expires', 'reset_password_expires', diff --git a/packages/nocodb/src/utils/sanitiseUserObj.ts b/packages/nocodb/src/utils/sanitiseUserObj.ts index 01acdb2235e..0ef1edcff6c 100644 --- a/packages/nocodb/src/utils/sanitiseUserObj.ts +++ b/packages/nocodb/src/utils/sanitiseUserObj.ts @@ -1,7 +1,6 @@ const ignoreKeys = new Set([ 'password', 'salt', - 'refresh_token', 'invite_token', 'invite_token_expires', 'reset_password_expires', From 90a825a4f5c6fd615bc862929e1a6040ab90bd4f Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 12:10:44 +0000 Subject: [PATCH 06/30] chore: lint --- packages/nocodb/src/models/User.ts | 2 +- packages/nocodb/src/services/users/users.service.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nocodb/src/models/User.ts b/packages/nocodb/src/models/User.ts index 2c296470216..b2d300d5087 100644 --- a/packages/nocodb/src/models/User.ts +++ b/packages/nocodb/src/models/User.ts @@ -187,7 +187,7 @@ export default class User implements UserType { ncMeta, ); - if(!userRefreshToken){ + if (!userRefreshToken) { return null; } diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index ff183579672..7f5dd914eda 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -27,7 +27,7 @@ import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2'; import { NcError } from '~/helpers/catchError'; import { BasesService } from '~/services/bases.service'; import { extractProps } from '~/helpers/extractProps'; -import UserRefreshToken from "~/models/UserRefreshToken"; +import UserRefreshToken from '~/models/UserRefreshToken'; @Injectable() export class UsersService { @@ -388,8 +388,8 @@ export class UsersService { await UserRefreshToken.insert({ token: refreshToken, - fk_user_id: user.id - }) + fk_user_id: user.id, + }); setTokenCookie(param.res, refreshToken); @@ -508,8 +508,8 @@ export class UsersService { await UserRefreshToken.insert({ token: refreshToken, - fk_user_id: user.id - }) + fk_user_id: user.id, + }); setTokenCookie(param.res, refreshToken); From 03f5a3a8d392239c1c790dba442a57ef5e4c6a12 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 12:10:44 +0000 Subject: [PATCH 07/30] chore: invalidate token --- packages/nocodb/src/services/users/users.service.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index 7f5dd914eda..d504f3292cf 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -542,11 +542,10 @@ export class UsersService { this.clearCookie(param); const user = (param.req as any).user; if (user?.id) { - // await User.update(user.id, { - // refresh_token: null, - // token_version: randomTokenString(), - // }); - // todo: clear only token present in cookie + await User.update(user.id, { + token_version: randomTokenString(), + }); + // todo: clear only token present in cookie to avoid invalidating all refresh token await UserRefreshToken.deleteAllUserToken(user.id); } return { msg: 'Signed out successfully' }; From d4d994858d2bd204fac2e8205240cf05b2eb1395 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 12:10:44 +0000 Subject: [PATCH 08/30] refactor: review comments --- packages/nocodb/src/models/User.ts | 3 +-- packages/nocodb/src/models/UserRefreshToken.ts | 6 +++--- packages/nocodb/src/models/index.ts | 1 + packages/nocodb/src/services/users/users.service.ts | 10 +--------- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/nocodb/src/models/User.ts b/packages/nocodb/src/models/User.ts index b2d300d5087..447d7fed11d 100644 --- a/packages/nocodb/src/models/User.ts +++ b/packages/nocodb/src/models/User.ts @@ -9,9 +9,8 @@ import { CacheScope, MetaTable, } from '~/utils/globals'; -import { Base, BaseUser } from '~/models'; +import { Base, BaseUser, UserRefreshToken } from '~/models'; import { sanitiseUserObj } from '~/utils'; -import UserRefreshToken from '~/models/UserRefreshToken'; export default class User implements UserType { id: string; diff --git a/packages/nocodb/src/models/UserRefreshToken.ts b/packages/nocodb/src/models/UserRefreshToken.ts index 69b699a3a32..5bd644d6bd0 100644 --- a/packages/nocodb/src/models/UserRefreshToken.ts +++ b/packages/nocodb/src/models/UserRefreshToken.ts @@ -13,7 +13,7 @@ export default class UserRefreshToken { updated_at?: any; public static async insert( - syncLog: Partial, + userRefreshToken: Partial, ncMeta = Noco.ncMeta, ) { // clear old invalid tokens before inserting new one @@ -23,7 +23,7 @@ export default class UserRefreshToken { null, MetaTable.USER_REFRESH_TOKENS, { - fk_user_id: syncLog.fk_user_id, + fk_user_id: userRefreshToken.fk_user_id, }, { expires_at: { @@ -32,7 +32,7 @@ export default class UserRefreshToken { }, ); - const insertObj = extractProps(syncLog, [ + const insertObj = extractProps(userRefreshToken, [ 'fk_user_id', 'token', 'expires_at', diff --git a/packages/nocodb/src/models/index.ts b/packages/nocodb/src/models/index.ts index 2e62d9a5330..ab22cdbdf17 100644 --- a/packages/nocodb/src/models/index.ts +++ b/packages/nocodb/src/models/index.ts @@ -40,3 +40,4 @@ export { default as View } from './View'; export { default as LinksColumn } from './LinksColumn'; export { default as Notification } from './Notification'; export { default as PresignedUrl } from './PresignedUrl'; +export { default as UserRefreshToken } from './UserRefreshToken'; diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index d504f3292cf..44dec39945f 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -21,13 +21,12 @@ import { validatePayload } from '~/helpers'; import { MetaService } from '~/meta/meta.service'; import { MetaTable } from '~/utils/globals'; import Noco from '~/Noco'; -import { Store, User } from '~/models'; +import { Store, User, UserRefreshToken } from '~/models'; import { randomTokenString } from '~/helpers/stringHelpers'; import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2'; import { NcError } from '~/helpers/catchError'; import { BasesService } from '~/services/bases.service'; import { extractProps } from '~/helpers/extractProps'; -import UserRefreshToken from '~/models/UserRefreshToken'; @Injectable() export class UsersService { @@ -381,11 +380,6 @@ export class UsersService { const refreshToken = randomTokenString(); - // await User.update(user.id, { - // email: user.email, - // refresh_token: refreshToken, - // }); - await UserRefreshToken.insert({ token: refreshToken, fk_user_id: user.id, @@ -584,8 +578,6 @@ export class UsersService { } await User.update(user.id, { - // refresh_token: refreshToken, - // email: user.email, token_version: user['token_version'], }); From 6d815872c5128e1fa5d65f6ac5d24c22f1a6622e Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 12:10:44 +0000 Subject: [PATCH 09/30] refactor: cleanup --- packages/nocodb/src/services/users/users.service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index 44dec39945f..c95fc3e5654 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -495,11 +495,6 @@ export class UsersService { const refreshToken = randomTokenString(); - // await User.update(user.id, { - // refresh_token: refreshToken, - // email: user.email, - // }); - await UserRefreshToken.insert({ token: refreshToken, fk_user_id: user.id, From e356f30a0a51ce2636d813a0c16bc3b144108301 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 2 Apr 2024 18:12:51 +0530 Subject: [PATCH 10/30] refactor: add index Signed-off-by: Pranav C --- .../nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts b/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts index 1274c96a045..98e9a6b8c3f 100644 --- a/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts +++ b/packages/nocodb/src/meta/migrations/v2/nc_043_user_refresh_token.ts @@ -11,7 +11,7 @@ const up = async (knex: Knex) => { table.string('token', 255).index(); table.text('meta'); - table.timestamp('expires_at'); + table.timestamp('expires_at').index(); table.timestamps(true, true); }); From 0199b29df3922edca955fa07fa5391730e3c0f71 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Wed, 3 Apr 2024 01:33:14 +0000 Subject: [PATCH 11/30] docs: prefill --- .../docs/090.views/040.view-types/030.form.md | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/noco-docs/docs/090.views/040.view-types/030.form.md b/packages/noco-docs/docs/090.views/040.view-types/030.form.md index df09826fca9..a4b4c779778 100644 --- a/packages/noco-docs/docs/090.views/040.view-types/030.form.md +++ b/packages/noco-docs/docs/090.views/040.view-types/030.form.md @@ -111,14 +111,22 @@ For select based field types, you can configure the options layout to be display ![Options Layout](/img/v2/views/form-view/options-layout.png) ## Prefill Form Fields -Prefilling form fields is a way to pre-populate form fields with default values. This can be useful when you want to save time for users by prefilling some fields with default values. The prefilled fields and their values are visible in the URL of the form view & can be manually constructed by ensuring URL parameters are appropriately encoded. +Here's a more professional rephrasing of the given content: + +## Pre-Filling Form Fields +NocoDB offers a convenient feature that allows pre-filling form fields with specific values by setting URL parameters. This functionality enables the creation of custom URLs with desired field values, streamlining data entry and enhancing user experience. + +To construct a pre-filled form URL manually, ensure that the URL parameters are appropriately encoded in the following format: `?key1=value1&key2=value2`. + +For instance, the URL `https://wh8s5w.noco.to/#/nc/form/66da06-f074-47af-ace7-fde46df55?Status=Qualification&Priority=Very+high` pre-fills the `Status` field with `Qualification` and the `Priority` field with `Very high`. + +NocoDB provides an intuitive alternative approach to generate pre-filled URLs through the form builder. +1. Open the form builder and pre-fill the required form fields with the desired values. +2. Click on the `Share` button located in the top right corner. +3. Toggle the `Enable Public Viewing` button to enable sharing. +4. Toggle the `Enable Prefill` button to enable pre-filling. +5. Click on the `Copy Link` button to copy the pre-filled URL. -NocoDB provides an easier approach to construct prefilled URLs. One can use the form builder to prefill form fields with default values & auto-generate encoded prefilled URL. Follow the below steps to prefill form fields & generate a prefilled URL - -1. Open the form builder, prefill the required form fields with default values. -2. Click on the `Share` button in the top right corner. -3. Toggle `Enable Public Viewing` button to enable share. -4. Toggle `Enable Prefill` button to enable prefill. -5. Click on the `Copy Link` button to copy the link. ![Prefill](/img/v2/views/form-view/prefill.png) ![Prefill share](/img/v2/views/form-view/prefill-share.png) @@ -129,15 +137,15 @@ NocoDB provides an easier approach to construct prefilled URLs. One can use the ::: ### Prefill modes -1. **Default**: Standard mode. This mode will prefill the form fields with the default values set in the form builder. Users can edit the prefilled fields. When shared, the prefilled fields will be visible in the URL. In the image below, the `Number` field is prefilled with the value `1234`, `Currency` field is prefilled with the value `1000` and `Year` field is prefilled with value `2023`. +1. **Default**: Standard mode. This mode will prefill the form fields with the values set in the shared form URL. Users can edit the prefilled fields in the form. In the image below, the `Number` field is prefilled with the value `1234`, `Currency` field is prefilled with the value `1000` and `Year` field is prefilled with value `2023`. ![Prefill default](/img/v2/views/form-view/prefill-default.png) -2. **Hide prefilled fields**: This mode will prefill the form fields with the default values set in the form builder but will hide the prefilled fields from the user. When shared, the prefilled fields will be visible in the URL. In the image below, the `Number` field is prefilled with the value `1234`, `Currency` field is prefilled with the value `1000` and `Year` field is prefilled with value `2023`. +2. **Hide prefilled fields**: This mode will prefill the form fields with the values set in the shared form URL but will hide the prefilled fields in the form from the user. In the image below, the `Number` field is prefilled with the value `1234`, `Currency` field is prefilled with the value `1000` and `Year` field is prefilled with value `2023`. ![Prefill hide](/img/v2/views/form-view/prefill-hide.png) -3. **Lock prefilled fields as read-only**: This mode will prefill the form fields with the default values set in the form builder and will lock the prefilled fields as read-only. When shared, the prefilled fields will be visible in the URL. In the image below, the `Number` field is prefilled with the value `1234`, `Currency` field is prefilled with the value `1000` and `Year` field is prefilled with value `2023`. +3. **Lock prefilled fields as read-only**: This mode will prefill the form fields with the values set in the shared form URL and will lock the prefilled fields as read-only. In the image below, the `Number` field is prefilled with the value `1234`, `Currency` field is prefilled with the value `1000` and `Year` field is prefilled with value `2023`. ![Prefill lock](/img/v2/views/form-view/prefill-lock.png) From 445c16c172672292546deceaab65f2c33f16d7b4 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 3 Apr 2024 14:39:46 +0530 Subject: [PATCH 12/30] docs: typo correction Signed-off-by: Pranav C --- .../noco-docs/docs/090.views/040.view-types/030.form.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/noco-docs/docs/090.views/040.view-types/030.form.md b/packages/noco-docs/docs/090.views/040.view-types/030.form.md index a4b4c779778..cb0db7cb4fe 100644 --- a/packages/noco-docs/docs/090.views/040.view-types/030.form.md +++ b/packages/noco-docs/docs/090.views/040.view-types/030.form.md @@ -32,7 +32,7 @@ Form view builder layout can be divided into 4 sections: In the **Form View** area, click on in input boxes provided for **Title** & **Description** to add/update title & description to the form. :::info -Formatting options are supported for the description field. You can also use markdown to format the text. +Formatting options are supported for the description field. You can also use Markdown to format the text. ::: ![Form Title & Description](/img/v2/views/form-view/title-description.png) @@ -80,19 +80,19 @@ NocoDB allows you to configure the form view to perform various actions after a ![Form View Settings](/img/v2/views/form-view/post-submit-settings.png) :::info -Formatting options are supported for the `After Submit Message` field. You can also use markdown to format the text. +Formatting options are supported for the `After Submit Message` field. You can also use Markdown to format the text. ::: ## Field configuration To change the field label displayed on the form & add help-text, click on the required field in the **Form Area** and on the right side configuration panel, configure -1. **Label** `Opitonal` : Defaults to the field name. This doesn't affect the field name in the table. +1. **Label** `Optional` : Defaults to the field name. This doesn't affect the field name in the table. 2. **Help Text** `Optional` 3. **Required** : Toggle to mark the field as required ![Field Label & Help Text](/img/v2/views/form-view/field-config.png) :::info -Formatting options are supported for the `Help Text` field. You can also use markdown to format the text. +Formatting options are supported for the `Help Text` field. You can also use Markdown to format the text. ::: ### Field Type Specific Settings From 7592d2fd3771dbc41c0361a36b19ea7e47e3e120 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Wed, 3 Apr 2024 10:37:28 +0000 Subject: [PATCH 13/30] test: temporary disable --- tests/playwright/tests/db/views/viewCalendar.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playwright/tests/db/views/viewCalendar.spec.ts b/tests/playwright/tests/db/views/viewCalendar.spec.ts index be0d90420cb..3c6d99eb7fc 100644 --- a/tests/playwright/tests/db/views/viewCalendar.spec.ts +++ b/tests/playwright/tests/db/views/viewCalendar.spec.ts @@ -288,7 +288,7 @@ test.describe('Calendar View', () => { await dashboard.viewSidebar.deleteView({ title: 'Calendar' }); }); - test('Calendar Drag and Drop & Undo Redo Operations', async () => { + test.skip('Calendar Drag and Drop & Undo Redo Operations', async () => { test.slow(); await dashboard.treeView.openBase({ title: `xcdb${context.workerId}` }); From 7bf5ce48982301f17e2c21e93f240420e465f013 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Wed, 3 Apr 2024 12:05:20 +0000 Subject: [PATCH 14/30] fix(nc-gui): topbar tab text overflow issue --- packages/nc-gui/components/smartsheet/Topbar.vue | 2 +- packages/nc-gui/components/smartsheet/topbar/SelectMode.vue | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/Topbar.vue b/packages/nc-gui/components/smartsheet/Topbar.vue index 2f30b6f4c96..eded468dec5 100644 --- a/packages/nc-gui/components/smartsheet/Topbar.vue +++ b/packages/nc-gui/components/smartsheet/Topbar.vue @@ -29,7 +29,7 @@ const isSharedBase = computed(() => route.value.params.typeOrId === 'base') -
+
diff --git a/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue b/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue index 600629f4332..1b9f09ccad5 100644 --- a/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue +++ b/packages/nc-gui/components/smartsheet/topbar/SelectMode.vue @@ -62,9 +62,9 @@ const onClickDetails = () => { } .tab .tab-title { @apply min-w-0; - word-break: 'keep-all'; - white-space: 'nowrap'; - display: 'inline'; + word-break: keep-all; + white-space: nowrap; + display: inline; line-height: 0.95; } From a2ceee11e69c46c19d90a86b7b195cdacbf2cab7 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Wed, 3 Apr 2024 12:18:59 +0000 Subject: [PATCH 15/30] fix(nc-gui): shared view column display issue after deleting column from table --- .../composables/useSharedFormViewStore.ts | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/nc-gui/composables/useSharedFormViewStore.ts b/packages/nc-gui/composables/useSharedFormViewStore.ts index e10d40e92ea..676754d53b4 100644 --- a/packages/nc-gui/composables/useSharedFormViewStore.ts +++ b/packages/nc-gui/composables/useSharedFormViewStore.ts @@ -132,25 +132,27 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share {} as Record, ) - columns.value = viewMeta.model?.columns?.map((c) => { - if ( - !isSystemColumn(c) && - !isVirtualCol(c) && - !isAttachment(c) && - c.uidt !== UITypes.SpecificDBType && - c?.title && - c?.cdf && - !/^\w+\(\)|CURRENT_TIMESTAMP$/.test(c.cdf) - ) { - formState.value[c.title] = typeof c.cdf === 'string' ? c.cdf.replace(/^'|'$/g, '') : c.cdf - } + columns.value = (viewMeta.model?.columns || []) + .filter((c) => fieldById[c.id]) + .map((c) => { + if ( + !isSystemColumn(c) && + !isVirtualCol(c) && + !isAttachment(c) && + c.uidt !== UITypes.SpecificDBType && + c?.title && + c?.cdf && + !/^\w+\(\)|CURRENT_TIMESTAMP$/.test(c.cdf) + ) { + formState.value[c.title] = typeof c.cdf === 'string' ? c.cdf.replace(/^'|'$/g, '') : c.cdf + } - return { - ...c, - meta: { ...parseProp(fieldById[c.id].meta), ...parseProp(c.meta) }, - description: fieldById[c.id].description, - } - }) + return { + ...c, + meta: { ...parseProp(fieldById[c.id].meta), ...parseProp(c.meta) }, + description: fieldById[c.id].description, + } + }) const _sharedViewMeta = (viewMeta as any).meta sharedViewMeta.value = isString(_sharedViewMeta) ? JSON.parse(_sharedViewMeta) : _sharedViewMeta @@ -188,6 +190,8 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share if (password.value && password.value !== '') { passwordError.value = error.message } + } else if (error.error === NcErrorType.UNKNOWN_ERROR) { + console.log(e) } } } From 5827e9fee48ab940a90c761d3bd58065c63e8487 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Wed, 3 Apr 2024 12:18:59 +0000 Subject: [PATCH 16/30] fix(nocodb): Cannot read properties of undefined (reading 'rqd') --- packages/nocodb/src/services/public-metas.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nocodb/src/services/public-metas.service.ts b/packages/nocodb/src/services/public-metas.service.ts index 30b80bdc3fb..4a24bc78d2d 100644 --- a/packages/nocodb/src/services/public-metas.service.ts +++ b/packages/nocodb/src/services/public-metas.service.ts @@ -41,6 +41,9 @@ export class PublicMetasService { view.model.columns = view.columns .filter((c) => { const column = view.model.columnsById[c.fk_column_id]; + + if (!column) return false; + return ( c.show || (column.rqd && !column.cdf && !column.ai) || From e072c99b35a02b6e0b0272646910be444b8ec19c Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Wed, 3 Apr 2024 12:18:59 +0000 Subject: [PATCH 17/30] fix(nc-gui): PR ai review changes --- packages/nc-gui/composables/useSharedFormViewStore.ts | 2 +- packages/nocodb/src/services/public-metas.service.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/composables/useSharedFormViewStore.ts b/packages/nc-gui/composables/useSharedFormViewStore.ts index 676754d53b4..d4257c01287 100644 --- a/packages/nc-gui/composables/useSharedFormViewStore.ts +++ b/packages/nc-gui/composables/useSharedFormViewStore.ts @@ -191,7 +191,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share passwordError.value = error.message } } else if (error.error === NcErrorType.UNKNOWN_ERROR) { - console.log(e) + console.error('Error occurred while loading shared form view', e) } } } diff --git a/packages/nocodb/src/services/public-metas.service.ts b/packages/nocodb/src/services/public-metas.service.ts index 4a24bc78d2d..ce9d2e1c3fa 100644 --- a/packages/nocodb/src/services/public-metas.service.ts +++ b/packages/nocodb/src/services/public-metas.service.ts @@ -42,6 +42,7 @@ export class PublicMetasService { .filter((c) => { const column = view.model.columnsById[c.fk_column_id]; + // Check if column exists to prevent processing non-existent columns if (!column) return false; return ( From 68066e850f9d070f367da423256067a23aeb5844 Mon Sep 17 00:00:00 2001 From: mertmit Date: Wed, 3 Apr 2024 16:22:49 +0300 Subject: [PATCH 18/30] chore: bump pnpm-lock.yaml Signed-off-by: mertmit --- pnpm-lock.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8ceeb972bf..9649161c591 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -154,7 +154,7 @@ importers: specifier: ^0.11.0 version: 0.11.0 nocodb-sdk: - specifier: 0.204.9 + specifier: workspace:^ version: link:../nocodb-sdk papaparse: specifier: ^5.4.1 @@ -701,7 +701,7 @@ importers: specifier: ^0.4.4 version: 0.4.4(@nestjs/common@10.3.7)(@nestjs/core@10.3.7)(@nestjs/throttler@5.1.2)(ioredis@5.3.2)(reflect-metadata@0.2.1) nocodb-sdk: - specifier: 0.204.9 + specifier: workspace:^ version: link:../nocodb-sdk nodemailer: specifier: ^6.9.13 @@ -14446,7 +14446,7 @@ packages: dependencies: escape-string-regexp: 1.0.5 eslint: 8.33.0 - ignore: 5.3.1 + ignore: 5.3.0 dev: true /eslint-plugin-eslint-comments@3.2.0(eslint@8.56.0): @@ -23915,6 +23915,9 @@ packages: /sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} requiresBuild: true + peerDependenciesMeta: + node-gyp: + optional: true dependencies: '@mapbox/node-pre-gyp': 1.0.11 node-addon-api: 4.3.0 @@ -23929,6 +23932,9 @@ packages: /sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} requiresBuild: true + peerDependenciesMeta: + node-gyp: + optional: true dependencies: bindings: 1.5.0 node-addon-api: 7.0.0 From 68e83c8c519b858609ffad906e9bd74d2abb7d48 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 3 Apr 2024 19:09:33 +0000 Subject: [PATCH 19/30] fix: clear view column cache properly when deleting column --- .../nocodb/src/models/CalendarViewColumn.ts | 7 +- packages/nocodb/src/models/Column.ts | 76 ++++++++++++------- packages/nocodb/src/models/FormViewColumn.ts | 4 +- .../nocodb/src/models/GalleryViewColumn.ts | 7 +- packages/nocodb/src/models/GridViewColumn.ts | 2 - .../nocodb/src/models/KanbanViewColumn.ts | 4 +- packages/nocodb/src/models/MapViewColumn.ts | 4 +- 7 files changed, 53 insertions(+), 51 deletions(-) diff --git a/packages/nocodb/src/models/CalendarViewColumn.ts b/packages/nocodb/src/models/CalendarViewColumn.ts index 2aa263900e9..0fc4e494404 100644 --- a/packages/nocodb/src/models/CalendarViewColumn.ts +++ b/packages/nocodb/src/models/CalendarViewColumn.ts @@ -79,18 +79,13 @@ export default class CalendarViewColumn { insertObj.source_id = viewRef.source_id; } - const { id, fk_column_id } = await ncMeta.metaInsert2( + const { id } = await ncMeta.metaInsert2( null, null, MetaTable.CALENDAR_VIEW_COLUMNS, insertObj, ); - await NocoCache.set( - `${CacheScope.CALENDAR_VIEW_COLUMN}:${fk_column_id}`, - id, - ); - { const view = await View.get(column.fk_view_id, ncMeta); await View.clearSingleQueryCache(view.fk_model_id, [view], ncMeta); diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index d89a7aea418..2ad40a16b71 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -824,61 +824,81 @@ export default class Column implements ColumnType { } // Grid View Columns + const gridViewColumns = await ncMeta.metaList2( + null, + null, + MetaTable.GRID_VIEW_COLUMNS, + { + condition: { fk_column_id: id }, + }, + ); await ncMeta.metaDelete(null, null, MetaTable.GRID_VIEW_COLUMNS, { - fk_column_id: col.id, + fk_column_id: id, }); - const gridViewColumnId = await NocoCache.get( - `${CacheScope.GRID_VIEW_COLUMN}:${col.id}`, - CacheGetType.TYPE_STRING, - ); - if (gridViewColumnId) { + + for (const gridViewColumn of gridViewColumns) { await NocoCache.deepDel( - `${CacheScope.GRID_VIEW_COLUMN}:${gridViewColumnId}`, + `${CacheScope.GRID_VIEW_COLUMN}:${gridViewColumn.id}`, CacheDelDirection.CHILD_TO_PARENT, ); } // Form View Columns + const formViewColumns = await ncMeta.metaList2( + null, + null, + MetaTable.FORM_VIEW_COLUMNS, + { + condition: { fk_column_id: id }, + }, + ); await ncMeta.metaDelete(null, null, MetaTable.FORM_VIEW_COLUMNS, { - fk_column_id: col.id, + fk_column_id: id, }); - const formViewColumnId = await NocoCache.get( - `${CacheScope.FORM_VIEW_COLUMN}:${col.id}`, - CacheGetType.TYPE_STRING, - ); - if (formViewColumnId) { + + for (const formViewColumn of formViewColumns) { await NocoCache.deepDel( - `${CacheScope.FORM_VIEW_COLUMN}:${formViewColumnId}`, + `${CacheScope.FORM_VIEW_COLUMN}:${formViewColumn.id}`, CacheDelDirection.CHILD_TO_PARENT, ); } // Kanban View Columns + const kanbanViewColumns = await ncMeta.metaList2( + null, + null, + MetaTable.KANBAN_VIEW_COLUMNS, + { + condition: { fk_column_id: id }, + }, + ); await ncMeta.metaDelete(null, null, MetaTable.KANBAN_VIEW_COLUMNS, { - fk_column_id: col.id, + fk_column_id: id, }); - const kanbanViewColumnId = await NocoCache.get( - `${CacheScope.KANBAN_VIEW_COLUMN}:${col.id}`, - CacheGetType.TYPE_STRING, - ); - if (kanbanViewColumnId) { + + for (const kanbanViewColumn of kanbanViewColumns) { await NocoCache.deepDel( - `${CacheScope.KANBAN_VIEW_COLUMN}:${kanbanViewColumnId}`, + `${CacheScope.KANBAN_VIEW_COLUMN}:${kanbanViewColumn.id}`, CacheDelDirection.CHILD_TO_PARENT, ); } // Gallery View Column + const galleryViewColumns = await ncMeta.metaList2( + null, + null, + MetaTable.GALLERY_VIEW_COLUMNS, + { + condition: { fk_column_id: id }, + }, + ); await ncMeta.metaDelete(null, null, MetaTable.GALLERY_VIEW_COLUMNS, { - fk_column_id: col.id, + fk_column_id: id, }); - const galleryViewColumnId = await NocoCache.get( - `${CacheScope.GALLERY_VIEW_COLUMN}:${col.id}`, - CacheGetType.TYPE_STRING, - ); - if (galleryViewColumnId) { + + for (const galleryViewColumn of galleryViewColumns) { await NocoCache.deepDel( - `${CacheScope.GALLERY_VIEW_COLUMN}:${galleryViewColumnId}`, + `${CacheScope.GALLERY_VIEW_COLUMN}:${galleryViewColumn.id}`, CacheDelDirection.CHILD_TO_PARENT, ); } diff --git a/packages/nocodb/src/models/FormViewColumn.ts b/packages/nocodb/src/models/FormViewColumn.ts index 518b3cc2dc8..5265dbc794f 100644 --- a/packages/nocodb/src/models/FormViewColumn.ts +++ b/packages/nocodb/src/models/FormViewColumn.ts @@ -92,15 +92,13 @@ export default class FormViewColumn implements FormColumnType { insertObj.source_id = viewRef.source_id; } - const { id, fk_column_id } = await ncMeta.metaInsert2( + const { id } = await ncMeta.metaInsert2( null, null, MetaTable.FORM_VIEW_COLUMNS, insertObj, ); - await NocoCache.set(`${CacheScope.FORM_VIEW_COLUMN}:${fk_column_id}`, id); - return this.get(id, ncMeta).then(async (viewColumn) => { await NocoCache.appendToList( CacheScope.FORM_VIEW_COLUMN, diff --git a/packages/nocodb/src/models/GalleryViewColumn.ts b/packages/nocodb/src/models/GalleryViewColumn.ts index c893b7aa30c..6ea557ef759 100644 --- a/packages/nocodb/src/models/GalleryViewColumn.ts +++ b/packages/nocodb/src/models/GalleryViewColumn.ts @@ -66,18 +66,13 @@ export default class GalleryViewColumn { insertObj.source_id = viewRef.source_id; } - const { id, fk_column_id } = await ncMeta.metaInsert2( + const { id } = await ncMeta.metaInsert2( null, null, MetaTable.GALLERY_VIEW_COLUMNS, insertObj, ); - await NocoCache.set( - `${CacheScope.GALLERY_VIEW_COLUMN}:${fk_column_id}`, - id, - ); - // on new view column, delete any optimised single query cache { const view = await View.get(column.fk_view_id, ncMeta); diff --git a/packages/nocodb/src/models/GridViewColumn.ts b/packages/nocodb/src/models/GridViewColumn.ts index e5ab0fbdf94..b0eb14ed426 100644 --- a/packages/nocodb/src/models/GridViewColumn.ts +++ b/packages/nocodb/src/models/GridViewColumn.ts @@ -107,8 +107,6 @@ export default class GridViewColumn implements GridColumnType { insertObj, ); - await NocoCache.set(`${CacheScope.GRID_VIEW_COLUMN}:${fk_column_id}`, id); - await View.fixPVColumnForView(column.fk_view_id, ncMeta); // on new view column, delete any optimised single query cache diff --git a/packages/nocodb/src/models/KanbanViewColumn.ts b/packages/nocodb/src/models/KanbanViewColumn.ts index abb723979dd..dca4b4b1f3d 100644 --- a/packages/nocodb/src/models/KanbanViewColumn.ts +++ b/packages/nocodb/src/models/KanbanViewColumn.ts @@ -63,15 +63,13 @@ export default class KanbanViewColumn implements KanbanColumnType { insertObj.source_id = viewRef.source_id; } - const { id, fk_column_id } = await ncMeta.metaInsert2( + const { id } = await ncMeta.metaInsert2( null, null, MetaTable.KANBAN_VIEW_COLUMNS, insertObj, ); - await NocoCache.set(`${CacheScope.KANBAN_VIEW_COLUMN}:${fk_column_id}`, id); - return this.get(id, ncMeta).then(async (kanbanViewColumn) => { await NocoCache.appendToList( CacheScope.KANBAN_VIEW_COLUMN, diff --git a/packages/nocodb/src/models/MapViewColumn.ts b/packages/nocodb/src/models/MapViewColumn.ts index 6ecfdc38ab6..e482b6ba4f0 100644 --- a/packages/nocodb/src/models/MapViewColumn.ts +++ b/packages/nocodb/src/models/MapViewColumn.ts @@ -57,15 +57,13 @@ export default class MapViewColumn { insertObj.source_id = viewRef.source_id; } - const { id, fk_column_id } = await ncMeta.metaInsert2( + const { id } = await ncMeta.metaInsert2( null, null, MetaTable.MAP_VIEW_COLUMNS, insertObj, ); - await NocoCache.set(`${CacheScope.MAP_VIEW_COLUMN}:${fk_column_id}`, id); - return this.get(id, ncMeta).then(async (viewCol) => { await NocoCache.appendToList( CacheScope.MAP_VIEW_COLUMN, From c7b4cd06df3b6842a5d9ec8f3762885f4650331b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 3 Apr 2024 19:09:33 +0000 Subject: [PATCH 20/30] fix: on filter change reset page --- packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue index a51fa49d682..09485de0d95 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue @@ -81,7 +81,7 @@ const { activeView, parentId?.value, computed(() => autoSave.value), - () => reloadDataHook.trigger({ shouldShowLoading: showLoading.value }), + () => reloadDataHook.trigger({ shouldShowLoading: showLoading.value, offset: 0 }), modelValue.value || nestedFilters.value, !modelValue.value, webHook.value, From a367f5d7c86c5310b283bc8ab3c61af45019fba3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Wed, 3 Apr 2024 19:09:33 +0000 Subject: [PATCH 21/30] fix: trim url string value --- packages/nc-gui/components/cell/Url.vue | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/nc-gui/components/cell/Url.vue b/packages/nc-gui/components/cell/Url.vue index 82a8fd70cd4..3f3219ee260 100644 --- a/packages/nc-gui/components/cell/Url.vue +++ b/packages/nc-gui/components/cell/Url.vue @@ -47,6 +47,8 @@ const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))! const isForm = inject(IsFormInj)! +const trimVal = (val:string) => val && (val + '').trim(); + // Used in the logic of when to display error since we are not storing the url if it's not valid const localState = ref(value) @@ -54,21 +56,21 @@ const vModel = computed({ get: () => value, set: (val) => { localState.value = val - if (!parseProp(column.value.meta)?.validate || (val && isValidURL(val)) || !val || isForm.value) { + if (!parseProp(column.value.meta)?.validate || (val && isValidURL(trimVal(val))) || !val || isForm.value) { emit('update:modelValue', val) } }, }) -const isValid = computed(() => value && isValidURL(value)) +const isValid = computed(() => value && isValidURL(trimVal(value))) const url = computed(() => { - if (!value || !isValidURL(value)) return '' + if (!value || !isValidURL(trimVal(value))) return '' /** add url scheme if missing */ - if (/^https?:\/\//.test(value)) return value + if (/^https?:\/\//.test(trimVal(value))) return trimVal(value) - return `https://${value}` + return `https://${trimVal(value)}` }) const { cellUrlOptions } = useCellUrlConfig(url) @@ -84,7 +86,7 @@ watch( parseProp(column.value.meta)?.validate && !editEnabled.value && localState.value && - !isValidURL(localState.value) + !isValidURL(trimVal(localState.value)) ) { message.error(t('msg.error.invalidURL')) localState.value = undefined From 42ac02cd026a68114ac300fecde4e31f9cafc3fb Mon Sep 17 00:00:00 2001 From: Honmary66 <141604294+Honmary66@users.noreply.github.com> Date: Thu, 4 Apr 2024 07:43:18 +0530 Subject: [PATCH 22/30] Update fr-030.share-base.md --- .../fr/120.collaboration/fr-030.share-base.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/scripts/docs/fr/120.collaboration/fr-030.share-base.md b/scripts/docs/fr/120.collaboration/fr-030.share-base.md index e69de29bb2d..97e64e3df63 100644 --- a/scripts/docs/fr/120.collaboration/fr-030.share-base.md +++ b/scripts/docs/fr/120.collaboration/fr-030.share-base.md @@ -0,0 +1,110 @@ +*** + +titre : "Partager la base" +description: "Procédures pour partager publiquement une base et générer une iframe intégrée" +balises : \["Collaboration", "Bases", "Partage"] +mots-clés : \["Base NocoDB", "base de partage", "collaboration de base", "actions de base", "paramètres de base"] +----------------------------------------------------------------------------------------------------------------- + +Pour partager une base, suivez les étapes ci-dessous : + +1. Accédez au coin supérieur droit de la barre de navigation supérieure et cliquez sur le`Share`bouton. +2. In the `Shared base` section, toggle the switch to `Enable public access`afin d'activer la fonctionnalité de base partagée. +3. Le lien généré pour la base partagée sera affiché ci-dessus et pourra être utilisé pour partager ce projet avec d'autres. Pour copier l'URL, cliquez simplement sur le`Copy Link` option. + +![Share base](/img/v2/base/share-base-1.png) + +![Share base](/img/v2/base/share-base-2.png) + +## Copy base + +Le`Copy base`La fonctionnalité permet aux utilisateurs de créer une copie de la base (base d'importation) dans leur propre espace de travail. Cette fonctionnalité est également utile pour les utilisateurs qui souhaitent utiliser une base comme modèle pour de futurs projets. Pour copier une base, suivez les étapes ci-dessous : + +1. Access shared base URL that you wish to copy. +2. Clique sur le`Copy base` button located in the top right corner of the toolbar. +3. Un modal apparaîtra, vous invitant à sélectionner l'espace de travail dans lequel vous souhaitez copier la base. Sélectionnez l'espace de travail souhaité +4. Configurez si vous souhaitez copier la base avec ou sans données/vues. +5. Clique sur le`Copy base`bouton pour terminer le processus. + +![Copy base](/img/v2/base/share-base-copy-base.png)![Copy base](/img/v2/base/share-base-copy-base-2.png) + +## Modifier la base de partage + +Modifier le`Share base`Le paramètre rendra le généré précédemment`Share base`lien invalide et générer un nouveau lien à sa place. +Voici les étapes pour le modifier : + +1. Clique sur le`Share`bouton situé dans le coin supérieur droit de la barre d’outils. +2. Activez l'option intitulée`Enable public access`pour désactiver le partage de base. +3. Basculez la même option,`Enable public access,`pour réactiver le partage de base, générant ensuite un nouveau lien. + +![Enable public access](/img/v2/base/share-base-enable-public-access.png) + +## Désactiver la base de partage + +Désactivation`Share base`rendra le généré précédemment`Share base`lien invalide +Voici les étapes pour le désactiver : + +1. Cliquez sur le bouton « Partager » situé dans le coin supérieur droit de la barre d'outils. +2. Activez l'option intitulée`Enable public access` to deactivate the base share. + +![Enable public access](/img/v2/base/share-base-enable-public-access.png) + +## Share base Access Permissions + +La « Base partagée » peut être configurée selon deux modes : + +1. **Téléspectateur**- Les utilisateurs disposant du lien fourni auront**lecture seulement** access to the base data. +2. **Éditeur**- Les utilisateurs disposant du lien fourni auront**read and write** access to the base data. + +:::note + +* L'autorisation d'accès par défaut est définie sur`Viewer` +* Base partagée avec`Editor`l'autorisation d'accès n'est actuellement disponible que dans la version auto-hébergée + ::: + +Basculer`Enable Editor Access`bouton pour configurer les autorisations comme vous le souhaitez![Share base edit access](/img/v2/base/share-base-edit-access.png) + +## Cadre intégrable + +L'interface NocoDB peut être intégrée de manière transparente aux applications existantes grâce à l'utilisation du[IFRAME HTML](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe)attribut. Cette fonctionnalité permet aux utilisateurs d'intégrer l'interface NocoDB dans leurs applications, permettant une expérience utilisateur unifiée. Pour générer le code HTML intégrable, procédez comme suit : + +**Pour générer du code HTML intégrable :** + +1. Click the `Share`bouton situé dans le coin supérieur droit de la barre d’outils. +2. Au sein du`Shared base link`onglet, sélectionnez le bouton pour copier le`Embeddable HTML code`dans votre presse-papiers. + +![Share base iFrame](/img/v2/base/share-base-iframe.png) + +Exemple: + +```html + +``` + +### Intégrer dans le corps HTML de l'application + +Exemple de code avec iframe intégré généré ci-dessus + +```html + + + + + + +``` From 112c63382fcd5c14d0ca733a39115ba7ba6e3aff Mon Sep 17 00:00:00 2001 From: navi Date: Thu, 4 Apr 2024 06:42:00 +0100 Subject: [PATCH 23/30] New Crowdin updates (#8030) * New translations en.json (Polish) * New translations en.json (English) * Update source file en.json * New translations en.json (Russian) --- packages/nc-gui/lang/pl.json | 2 +- packages/nc-gui/lang/ru.json | 56 ++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/nc-gui/lang/pl.json b/packages/nc-gui/lang/pl.json index 82b9599cd0d..3e8a50166c9 100644 --- a/packages/nc-gui/lang/pl.json +++ b/packages/nc-gui/lang/pl.json @@ -701,7 +701,7 @@ "hideNocodbBranding": "Ukryj branding NocoDB", "showOnConditions": "Pokaż na warunkach", "showFieldOnConditionsMet": "Pokazuje pole tylko, gdy spełnione są warunki", - "limitOptions": "Limit options", + "limitOptions": "Ogranicz opcje", "limitOptionsSubtext": "Ogranicz opcje widoczne dla użytkowników, wybierając dostępne opcje", "clearSelection": "Wyczyść wybór" }, diff --git a/packages/nc-gui/lang/ru.json b/packages/nc-gui/lang/ru.json index 1d819d7b61f..20a6c40bc71 100644 --- a/packages/nc-gui/lang/ru.json +++ b/packages/nc-gui/lang/ru.json @@ -157,8 +157,8 @@ "groupingField": "Поле группировки", "insertAfter": "Вставить после", "insertBefore": "Вставить перед", - "insertAbove": "Insert above", - "insertBelow": "Insert below", + "insertAbove": "Вставить выше", + "insertBelow": "Вставить ниже", "hideField": "Скрыть поле", "sortAsc": "По Возрастанию", "sortDesc": "По убыванию", @@ -192,21 +192,21 @@ "enter": "Вход", "seconds": "Секунды", "paste": "Вставить", - "restore": "Restore", - "replace": "Replace", - "banner": "Banner", - "logo": "Logo", - "dropdown": "Dropdown", - "list": "List", - "apply": "Apply", - "text": "Text", - "appearance": "Appearance" + "restore": "Восстановить", + "replace": "Заменить", + "banner": "Баннер", + "logo": "Логотип", + "dropdown": "Выпадающий список", + "list": "Список", + "apply": "Применить", + "text": "Текст", + "appearance": "Внешний вид" }, "objects": { - "day": "Day", - "week": "Week", - "month": "Month", - "year": "Year", + "day": "День", + "week": "Неделя", + "month": "Месяц", + "year": "Год", "workspace": "Рабочее пространство", "workspaces": "Рабочие пространства", "project": "Проект", @@ -313,7 +313,7 @@ "isNotNull": "не равно Null" }, "title": { - "sso": "Authentication (SSO)", + "sso": "Аутентификация (SSO)", "docs": "Документация", "forum": "Форум", "parameter": "Параметр", @@ -341,7 +341,7 @@ "removeFile": "Удалить файл", "hasMany": "Имеет много", "manyToMany": "Многие ко многим", - "oneToOne": "One to One", + "oneToOne": "Один к одному", "virtualRelation": "Виртуальные отношения", "linkMore": "Ссылка Подробнее", "linkMoreRecords": "Связать больше записей", @@ -436,9 +436,9 @@ "surveyFormSubmitConfirmMsg": "Are you sure you want to submit this form?" }, "labels": { - "selectYear": "Select Year", - "save": "Save", - "cancel": "Cancel", + "selectYear": "Выберите год", + "save": "Сохранить", + "cancel": "Отмена", "metadataUrl": "Metadata URL", "audience-entityId": "Audience/ Entity ID", "redirectUrl": "Redirect URL", @@ -446,12 +446,12 @@ "saml": "Security Assertion Markup Language (SAML)", "newProvider": "New Provider", "generalSettings": "General Settings", - "ssoSettings": "SSO Settings", + "ssoSettings": "Настройки SSO", "organizeBy": "Organize by", - "previous": "Previous", - "nextMonth": "Next Month", - "previousMonth": "Previous Month", - "next": "Next", + "previous": "Предыдущий", + "nextMonth": "Следующий месяц", + "previousMonth": "Предыдущий месяц", + "next": "Следующий", "organiseBy": "Organise by", "heading1": "Заголовок 1", "heading2": "Заголовок 2", @@ -573,7 +573,7 @@ "where": "Где", "cache": "Кэш", "chat": "Чат", - "showOrHide": "Show or Hide", + "showOrHide": "Показать / Скрыть", "airtable": "Airtable", "csv": "CSV", "csvFile": "Файл CSV", @@ -589,7 +589,7 @@ "created": "Созданный", "sqlOutput": "Вывод SQL", "addOption": "Добавить настройку", - "interfaceColor": "Interface Color", + "interfaceColor": "Цвет интерфейса", "qrCodeValueColumn": "Столбец с QR-кодом", "barcodeValueColumn": "Колонка со значением штрих-кода", "barcodeFormat": "Формат штрих-кода", @@ -618,7 +618,7 @@ "joinCommunity": "Сообщество NocoDB", "joinReddit": "Присоединиться /r/NocoDB", "followNocodb": "Следите за NocoDB", - "communityTranslated": "(Community Translated)" + "communityTranslated": "(Перевод сообщества)" }, "twitter": "Twitter", "docReference": "Ссылка на документ", From 018599fd5f5bd011acbd47de07600794eb11ccfe Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Thu, 4 Apr 2024 08:28:23 +0000 Subject: [PATCH 24/30] fix(nocodb): filters issue on duplicated view --- packages/nocodb/src/models/View.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 4457e549bf6..7aa326f58dc 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1898,6 +1898,9 @@ export default class View implements ViewType { 'fk_parent_id', 'is_group', 'logical_op', + 'base_id', + 'source_id', + 'order', ]), fk_view_id: view_id, id: generatedId, From f9c13353e23de3380323658ec120d9342fe3e87f Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Apr 2024 09:45:34 +0000 Subject: [PATCH 25/30] fix: delete old token when generating new token --- packages/nocodb/src/models/UserRefreshToken.ts | 4 ++-- packages/nocodb/src/services/users/users.service.ts | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/nocodb/src/models/UserRefreshToken.ts b/packages/nocodb/src/models/UserRefreshToken.ts index 5bd644d6bd0..75214aface8 100644 --- a/packages/nocodb/src/models/UserRefreshToken.ts +++ b/packages/nocodb/src/models/UserRefreshToken.ts @@ -68,11 +68,11 @@ export default class UserRefreshToken { null, MetaTable.USER_REFRESH_TOKENS, { - token: oldToken, + token: newToken, expires_at: dayjs().add(90, 'day').toDate(), }, { - token: newToken, + token: oldToken, }, ); } diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index c95fc3e5654..4ccfbf14f98 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -370,9 +370,9 @@ export class UsersService { NcError.badRequest(`Missing refresh token`); } - const user = await User.getByRefreshToken( - param.req.cookies.refresh_token, - ); + const oldRefreshToken = param.req.cookies.refresh_token; + + const user = await User.getByRefreshToken(oldRefreshToken); if (!user) { NcError.badRequest(`Invalid refresh token`); @@ -380,10 +380,7 @@ export class UsersService { const refreshToken = randomTokenString(); - await UserRefreshToken.insert({ - token: refreshToken, - fk_user_id: user.id, - }); + await UserRefreshToken.updateOldToken(oldRefreshToken, refreshToken); setTokenCookie(param.res, refreshToken); From cbd331c39d8955c7c787b3898a091c4f62c135a3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Apr 2024 09:45:35 +0000 Subject: [PATCH 26/30] refactor: introduce env variable --- packages/nocodb/src/models/UserRefreshToken.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/models/UserRefreshToken.ts b/packages/nocodb/src/models/UserRefreshToken.ts index 75214aface8..a349e71c15c 100644 --- a/packages/nocodb/src/models/UserRefreshToken.ts +++ b/packages/nocodb/src/models/UserRefreshToken.ts @@ -1,9 +1,13 @@ +import process from 'process'; import dayjs from 'dayjs'; import Noco from '~/Noco'; import { extractProps } from '~/helpers/extractProps'; import { MetaTable } from '~/utils/globals'; import { parseMetaProp, stringifyMetaProp } from '~/utils/modelUtils'; +const NC_REFRESH_TOKEN_EXP_IN_DAYS = + +process.env.NC_REFRESH_TOKEN_EXP_IN_DAYS || 90; + export default class UserRefreshToken { fk_user_id: string; token: string; @@ -41,7 +45,9 @@ export default class UserRefreshToken { // set default expiry as 90 days if missing if (!('expires_at' in insertObj)) { - insertObj.expires_at = dayjs().add(90, 'day').toDate(); + insertObj.expires_at = dayjs() + .add(NC_REFRESH_TOKEN_EXP_IN_DAYS, 'day') + .toDate(); } if ('meta' in insertObj) { @@ -69,7 +75,7 @@ export default class UserRefreshToken { MetaTable.USER_REFRESH_TOKENS, { token: newToken, - expires_at: dayjs().add(90, 'day').toDate(), + expires_at: dayjs().add(NC_REFRESH_TOKEN_EXP_IN_DAYS, 'day').toDate(), }, { token: oldToken, From fd275a143d24137fdb8a11e6bb71fe97c5906874 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Apr 2024 16:31:11 +0530 Subject: [PATCH 27/30] refactor: avoid duplicate code Signed-off-by: Pranav C --- packages/nc-gui/components/cell/Url.vue | 14 +-- .../virtual-cell/components/UnLinkedItems.vue | 6 +- packages/nc-gui/composables/useLTARStore.ts | 7 +- packages/nocodb/src/models/Column.ts | 95 +++++-------------- 4 files changed, 36 insertions(+), 86 deletions(-) diff --git a/packages/nc-gui/components/cell/Url.vue b/packages/nc-gui/components/cell/Url.vue index 3f3219ee260..79044138fd6 100644 --- a/packages/nc-gui/components/cell/Url.vue +++ b/packages/nc-gui/components/cell/Url.vue @@ -47,7 +47,7 @@ const isExpandedFormOpen = inject(IsExpandedFormOpenInj, ref(false))! const isForm = inject(IsFormInj)! -const trimVal = (val:string) => val && (val + '').trim(); +const trim = (val: string) => val?.trim?.() // Used in the logic of when to display error since we are not storing the url if it's not valid const localState = ref(value) @@ -56,21 +56,21 @@ const vModel = computed({ get: () => value, set: (val) => { localState.value = val - if (!parseProp(column.value.meta)?.validate || (val && isValidURL(trimVal(val))) || !val || isForm.value) { + if (!parseProp(column.value.meta)?.validate || (val && isValidURL(trim(val))) || !val || isForm.value) { emit('update:modelValue', val) } }, }) -const isValid = computed(() => value && isValidURL(trimVal(value))) +const isValid = computed(() => value && isValidURL(trim(value))) const url = computed(() => { - if (!value || !isValidURL(trimVal(value))) return '' + if (!value || !isValidURL(trim(value))) return '' /** add url scheme if missing */ - if (/^https?:\/\//.test(trimVal(value))) return trimVal(value) + if (/^https?:\/\//.test(trim(value))) return trim(value) - return `https://${trimVal(value)}` + return `https://${trim(value)}` }) const { cellUrlOptions } = useCellUrlConfig(url) @@ -86,7 +86,7 @@ watch( parseProp(column.value.meta)?.validate && !editEnabled.value && localState.value && - !isValidURL(trimVal(localState.value)) + !isValidURL(trim(localState.value)) ) { message.error(t('msg.error.invalidURL')) localState.value = undefined diff --git a/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue b/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue index 306c3579155..7f21cdc8fb1 100644 --- a/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue +++ b/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue @@ -51,7 +51,7 @@ const { unlink, row, headerDisplayValue, - resetChildrenExcludedOffsetCount + resetChildrenExcludedOffsetCount, } = useLTARStoreOrThrow() const { addLTARRef, isNew, removeLTARRef, state: rowState } = useSmartsheetRowStoreOrThrow() @@ -102,7 +102,7 @@ watch( } loadChildrenExcludedList(rowState.value) } - if(!nextVal){ + if (!nextVal) { resetChildrenExcludedOffsetCount() } }, @@ -262,7 +262,7 @@ onUnmounted(() => { }) const onFilterChange = () => { - childrenExcludedListPagination.page = 1; + childrenExcludedListPagination.page = 1 resetChildrenExcludedOffsetCount() } diff --git a/packages/nc-gui/composables/useLTARStore.ts b/packages/nc-gui/composables/useLTARStore.ts index 182f41e0fa5..4ece4be094c 100644 --- a/packages/nc-gui/composables/useLTARStore.ts +++ b/packages/nc-gui/composables/useLTARStore.ts @@ -191,7 +191,8 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState( const loadChildrenExcludedList = async (activeState?: any) => { if (activeState) newRowState.state = activeState try { - let offset = childrenExcludedListPagination.size * (childrenExcludedListPagination.page - 1) - childrenExcludedOffsetCount.value + let offset = + childrenExcludedListPagination.size * (childrenExcludedListPagination.page - 1) - childrenExcludedOffsetCount.value if (offset < 0) { offset = 0 @@ -550,8 +551,8 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState( }) }) - const resetChildrenExcludedOffsetCount = () =>{ - childrenExcludedOffsetCount.value = 0; + const resetChildrenExcludedOffsetCount = () => { + childrenExcludedOffsetCount.value = 0 } const resetChildrenListOffsetCount = () => { diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 2ad40a16b71..e3c78f1b87a 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -823,84 +823,33 @@ export default class Column implements ColumnType { ); } - // Grid View Columns - const gridViewColumns = await ncMeta.metaList2( - null, - null, + // Delete from all view columns + const viewColumnTables = [ MetaTable.GRID_VIEW_COLUMNS, - { - condition: { fk_column_id: id }, - }, - ); - await ncMeta.metaDelete(null, null, MetaTable.GRID_VIEW_COLUMNS, { - fk_column_id: id, - }); - - for (const gridViewColumn of gridViewColumns) { - await NocoCache.deepDel( - `${CacheScope.GRID_VIEW_COLUMN}:${gridViewColumn.id}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - } - - // Form View Columns - const formViewColumns = await ncMeta.metaList2( - null, - null, MetaTable.FORM_VIEW_COLUMNS, - { - condition: { fk_column_id: id }, - }, - ); - await ncMeta.metaDelete(null, null, MetaTable.FORM_VIEW_COLUMNS, { - fk_column_id: id, - }); - - for (const formViewColumn of formViewColumns) { - await NocoCache.deepDel( - `${CacheScope.FORM_VIEW_COLUMN}:${formViewColumn.id}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - } - - // Kanban View Columns - const kanbanViewColumns = await ncMeta.metaList2( - null, - null, MetaTable.KANBAN_VIEW_COLUMNS, - { - condition: { fk_column_id: id }, - }, - ); - await ncMeta.metaDelete(null, null, MetaTable.KANBAN_VIEW_COLUMNS, { - fk_column_id: id, - }); - - for (const kanbanViewColumn of kanbanViewColumns) { - await NocoCache.deepDel( - `${CacheScope.KANBAN_VIEW_COLUMN}:${kanbanViewColumn.id}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - } - - // Gallery View Column - const galleryViewColumns = await ncMeta.metaList2( - null, - null, MetaTable.GALLERY_VIEW_COLUMNS, - { + ]; + const viewColumnCacheScope = [ + CacheScope.GRID_VIEW_COLUMN, + CacheScope.FORM_VIEW_COLUMN, + CacheScope.KANBAN_VIEW_COLUMN, + CacheScope.GALLERY_VIEW_COLUMN, + ]; + + for (let i; i < viewColumnTables.length; i++) { + const table = viewColumnTables[i]; + const cacheScope = viewColumnCacheScope[i]; + const viewColumns = await ncMeta.metaList2(null, null, table, { condition: { fk_column_id: id }, - }, - ); - await ncMeta.metaDelete(null, null, MetaTable.GALLERY_VIEW_COLUMNS, { - fk_column_id: id, - }); - - for (const galleryViewColumn of galleryViewColumns) { - await NocoCache.deepDel( - `${CacheScope.GALLERY_VIEW_COLUMN}:${galleryViewColumn.id}`, - CacheDelDirection.CHILD_TO_PARENT, - ); + }); + await ncMeta.metaDelete(null, null, table, { fk_column_id: id }); + for (const viewColumn of viewColumns) { + await NocoCache.deepDel( + `${cacheScope}:${viewColumn.id}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + } } // Get LTAR columns in which current column is referenced as foreign key From 2846bfe06d8267f99b16f293a93fd301e3cb7cc1 Mon Sep 17 00:00:00 2001 From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:55:33 +0530 Subject: [PATCH 28/30] fix(nc-gui): small changes --- packages/nc-gui/composables/useSharedFormViewStore.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nc-gui/composables/useSharedFormViewStore.ts b/packages/nc-gui/composables/useSharedFormViewStore.ts index d4257c01287..0a865f97d22 100644 --- a/packages/nc-gui/composables/useSharedFormViewStore.ts +++ b/packages/nc-gui/composables/useSharedFormViewStore.ts @@ -192,6 +192,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share } } else if (error.error === NcErrorType.UNKNOWN_ERROR) { console.error('Error occurred while loading shared form view', e) + message.error('Error occurred while loading shared form view') } } } From 0346534831a0c653ae6cbbe46187d74babce9bf3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Apr 2024 17:06:28 +0530 Subject: [PATCH 29/30] refactor: suggested review changes Signed-off-by: Pranav C --- packages/nocodb/src/models/UserRefreshToken.ts | 9 +++++++-- packages/nocodb/src/services/users/users.service.ts | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/nocodb/src/models/UserRefreshToken.ts b/packages/nocodb/src/models/UserRefreshToken.ts index a349e71c15c..ae4d805fd01 100644 --- a/packages/nocodb/src/models/UserRefreshToken.ts +++ b/packages/nocodb/src/models/UserRefreshToken.ts @@ -6,7 +6,12 @@ import { MetaTable } from '~/utils/globals'; import { parseMetaProp, stringifyMetaProp } from '~/utils/modelUtils'; const NC_REFRESH_TOKEN_EXP_IN_DAYS = - +process.env.NC_REFRESH_TOKEN_EXP_IN_DAYS || 90; + parseInt(process.env.NC_REFRESH_TOKEN_EXP_IN_DAYS, 10) || 90; + +// throw error if user provided invalid value +if (NC_REFRESH_TOKEN_EXP_IN_DAYS <= 0) { + throw new Error('NC_REFRESH_TOKEN_EXP_IN_DAYS must be a positive number'); +} export default class UserRefreshToken { fk_user_id: string; @@ -43,7 +48,7 @@ export default class UserRefreshToken { 'meta', ]); - // set default expiry as 90 days if missing + // set expiry based on the env or default value if (!('expires_at' in insertObj)) { insertObj.expires_at = dayjs() .add(NC_REFRESH_TOKEN_EXP_IN_DAYS, 'day') diff --git a/packages/nocodb/src/services/users/users.service.ts b/packages/nocodb/src/services/users/users.service.ts index 4ccfbf14f98..9010192250a 100644 --- a/packages/nocodb/src/services/users/users.service.ts +++ b/packages/nocodb/src/services/users/users.service.ts @@ -380,7 +380,12 @@ export class UsersService { const refreshToken = randomTokenString(); - await UserRefreshToken.updateOldToken(oldRefreshToken, refreshToken); + try { + await UserRefreshToken.updateOldToken(oldRefreshToken, refreshToken); + } catch (error) { + console.error('Failed to update old refresh token:', error); + NcError.internalServerError('Failed to update refresh token'); + } setTokenCookie(param.res, refreshToken); From 04330cb8864e5af33ce9bcf55d8d6253d9daa1da Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 4 Apr 2024 18:28:47 +0530 Subject: [PATCH 30/30] fix: typo correction Signed-off-by: Pranav C --- packages/nocodb/src/models/Column.ts | 2 +- packages/nocodb/src/services/views.service.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index e3c78f1b87a..cb7911001bc 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -837,7 +837,7 @@ export default class Column implements ColumnType { CacheScope.GALLERY_VIEW_COLUMN, ]; - for (let i; i < viewColumnTables.length; i++) { + for (let i = 0; i < viewColumnTables.length; i++) { const table = viewColumnTables[i]; const cacheScope = viewColumnCacheScope[i]; const viewColumns = await ncMeta.metaList2(null, null, table, { diff --git a/packages/nocodb/src/services/views.service.ts b/packages/nocodb/src/services/views.service.ts index 5aeaa8d62e3..3b2aa9ea8cd 100644 --- a/packages/nocodb/src/services/views.service.ts +++ b/packages/nocodb/src/services/views.service.ts @@ -76,6 +76,10 @@ export class ViewsService { }) { const model = await Model.get(param.tableId); + if (!model) { + NcError.tableNotFound(param.tableId); + } + const viewList = await xcVisibilityMetaGet({ baseId: model.base_id, models: [model],