Browse Source

fix: user cache

pull/7202/head
mertmit 12 months ago
parent
commit
e8b4678803
  1. 10
      packages/nocodb/src/helpers/initAdminFromEnv.ts
  2. 12
      packages/nocodb/src/models/Base.ts
  3. 201
      packages/nocodb/src/models/BaseUser.ts
  4. 5
      packages/nocodb/src/models/User.ts
  5. 17
      packages/nocodb/src/services/base-users/base-users.service.ts
  6. 4
      packages/nocodb/src/utils/globals.ts

10
packages/nocodb/src/helpers/initAdminFromEnv.ts

@ -169,11 +169,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
existingUserWithNewEmail.id,
);
// clear cache
await NocoCache.delAll(
CacheScope.USER,
`${existingUserWithNewEmail.email}___*`,
);
await NocoCache.del(
`${CacheScope.USER}:${existingUserWithNewEmail.id}`,
);
@ -237,11 +232,6 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
// check user account already present with the new admin email
const existingUserWithNewEmail = await User.getByEmail(email, ncMeta);
if (existingUserWithNewEmail?.id) {
// clear cache
await NocoCache.delAll(
CacheScope.USER,
`${existingUserWithNewEmail.email}___*`,
);
await NocoCache.del(
`${CacheScope.USER}:${existingUserWithNewEmail.id}`,
);

12
packages/nocodb/src/models/Base.ts

@ -210,8 +210,6 @@ export default class Base implements BaseType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${o.id}`);
}
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.del(CacheScope.INSTANCE_META);
// remove item in cache list
@ -321,8 +319,6 @@ export default class Base implements BaseType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${base.id}`);
}
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.deepDel(
CacheScope.PROJECT,
`${CacheScope.PROJECT}:${baseId}`,
@ -360,7 +356,9 @@ export default class Base implements BaseType {
static async getWithInfoByTitle(title: string, ncMeta = Noco.ncMeta) {
const base = await this.getByTitle(title, ncMeta);
if (base) await base.getSources(ncMeta);
if (base) {
await base.getSources(ncMeta);
}
return base;
}
@ -442,7 +440,9 @@ export default class Base implements BaseType {
// parse meta
base.meta = parseMetaProp(base);
if (base) await base.getSources(ncMeta);
if (base) {
await base.getSources(ncMeta);
}
return base;
}

201
packages/nocodb/src/models/BaseUser.ts

@ -1,8 +1,9 @@
import { ProjectRoles } from 'nocodb-sdk';
import type { BaseType } from 'nocodb-sdk';
import User from '~/models/User';
import type User from '~/models/User';
import Base from '~/models/Base';
import {
CacheDelDirection,
// CacheDelDirection,
CacheGetType,
CacheScope,
@ -44,10 +45,15 @@ export default class BaseUser {
true,
);
// reset all user bases cache
await NocoCache.delAll(CacheScope.USER_PROJECT, `${baseUser.fk_user_id}:*`);
const res = this.get(base_id, fk_user_id, ncMeta);
return this.get(base_id, fk_user_id, ncMeta);
await NocoCache.appendToList(
CacheScope.BASE_USER,
[base_id],
`${CacheScope.BASE_USER}:${base_id}:${fk_user_id}`,
);
return res;
}
// public static async update(id, user: Partial<BaseUser>, ncMeta = Noco.ncMeta) {
@ -58,7 +64,7 @@ export default class BaseUser {
baseId &&
userId &&
(await NocoCache.get(
`${CacheScope.PROJECT_USER}:${baseId}:${userId}`,
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
CacheGetType.TYPE_OBJECT,
));
if (!baseUser) {
@ -67,7 +73,7 @@ export default class BaseUser {
base_id: baseId,
});
await NocoCache.set(
`${CacheScope.PROJECT_USER}:${baseId}:${userId}`,
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
baseUser,
);
}
@ -88,39 +94,50 @@ export default class BaseUser {
},
ncMeta = Noco.ncMeta,
): Promise<(Partial<User> & BaseUser)[]> {
const queryBuilder = ncMeta
.knex(MetaTable.USERS)
.select(
`${MetaTable.USERS}.id`,
`${MetaTable.USERS}.email`,
`${MetaTable.USERS}.invite_token`,
`${MetaTable.USERS}.roles as main_roles`,
`${MetaTable.USERS}.created_at as created_at`,
`${MetaTable.PROJECT_USERS}.base_id`,
`${MetaTable.PROJECT_USERS}.roles as roles`,
);
const cachedList = await NocoCache.getList(CacheScope.BASE_USER, [base_id]);
let { list: baseUsers } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !baseUsers.length) {
const queryBuilder = ncMeta
.knex(MetaTable.USERS)
.select(
`${MetaTable.USERS}.id`,
`${MetaTable.USERS}.email`,
`${MetaTable.USERS}.invite_token`,
`${MetaTable.USERS}.roles as main_roles`,
`${MetaTable.USERS}.created_at as created_at`,
`${MetaTable.PROJECT_USERS}.base_id`,
`${MetaTable.PROJECT_USERS}.roles as roles`,
);
if (limit) {
queryBuilder.offset(offset).limit(limit);
}
if (limit) {
queryBuilder.offset(offset).limit(limit);
}
if (query) {
queryBuilder.where('email', 'like', `%${query.toLowerCase?.()}%`);
}
if (query) {
queryBuilder.where('email', 'like', `%${query.toLowerCase?.()}%`);
}
queryBuilder.leftJoin(MetaTable.PROJECT_USERS, function () {
this.on(
`${MetaTable.PROJECT_USERS}.fk_user_id`,
'=',
`${MetaTable.USERS}.id`,
).andOn(
`${MetaTable.PROJECT_USERS}.base_id`,
'=',
ncMeta.knex.raw('?', [base_id]),
);
});
queryBuilder.leftJoin(MetaTable.PROJECT_USERS, function () {
this.on(
`${MetaTable.PROJECT_USERS}.fk_user_id`,
'=',
`${MetaTable.USERS}.id`,
).andOn(
`${MetaTable.PROJECT_USERS}.base_id`,
'=',
ncMeta.knex.raw('?', [base_id]),
);
});
const baseUsers = await queryBuilder;
baseUsers = await queryBuilder;
baseUsers = baseUsers.map((baseUser) => {
return this.castType(baseUser);
});
await NocoCache.setList(CacheScope.BASE_USER, [base_id], baseUsers);
}
return baseUsers;
}
@ -163,26 +180,14 @@ export default class BaseUser {
ncMeta = Noco.ncMeta,
) {
// get existing cache
const key = `${CacheScope.PROJECT_USER}:${baseId}:${userId}`;
const key = `${CacheScope.BASE_USER}:${baseId}:${userId}`;
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
o.roles = roles;
// set cache
await NocoCache.set(key, o);
}
// update user cache
const user = await User.get(userId);
if (user) {
const email = user.email;
for (const key of [`${CacheScope.USER}:${email}___${baseId}`]) {
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
o.roles = roles;
// set cache
await NocoCache.set(key, o);
}
}
}
// set meta
return await ncMeta.metaUpdate(
null,
@ -206,76 +211,41 @@ export default class BaseUser {
) {
const updateObj = extractProps(baseUser, ['starred', 'hidden', 'order']);
// get existing cache
const key = `${CacheScope.PROJECT_USER}:${baseId}:${userId}`;
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
Object.assign(o, updateObj);
// set cache
await NocoCache.set(key, o);
}
// update user cache
const user = await User.get(userId);
if (user) {
const email = user.email;
for (const key of [
`${CacheScope.USER}:${email}`,
`${CacheScope.USER}:${email}___${baseId}`,
]) {
const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
Object.assign(o, updateObj);
// set cache
await NocoCache.set(key, o);
}
}
}
const key = `${CacheScope.BASE_USER}:${baseId}:${userId}`;
// set meta
return await ncMeta.metaUpdate(
await ncMeta.metaUpdate(null, null, MetaTable.PROJECT_USERS, updateObj, {
fk_user_id: userId,
base_id: baseId,
});
// delete cache
await NocoCache.del(key);
// cache and return
return await this.get(baseId, userId, ncMeta);
}
static async delete(baseId: string, userId: string, ncMeta = Noco.ncMeta) {
// delete meta
const response = await ncMeta.metaDelete(
null,
null,
MetaTable.PROJECT_USERS,
updateObj,
{
fk_user_id: userId,
base_id: baseId,
},
);
}
static async delete(baseId: string, userId: string, ncMeta = Noco.ncMeta) {
const { email } = await ncMeta.metaGet2(null, null, MetaTable.USERS, {
id: userId,
});
if (email) {
await NocoCache.delAll(CacheScope.USER, `${email}*`);
}
// remove base from user base list cache
const cachedList = await NocoCache.getList(CacheScope.USER_PROJECT, [
userId,
]);
let { list: cachedProjectList } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && cachedProjectList?.length) {
cachedProjectList = cachedProjectList.filter((p) => p.id !== baseId);
// delete the whole list first so that the old one won't be included
await NocoCache.del(`${CacheScope.USER_PROJECT}:${userId}:list`);
if (cachedProjectList.length > 0) {
// set the updated list (i.e. excluding the to-be-deleted base id)
await NocoCache.setList(
CacheScope.USER_PROJECT,
[userId],
cachedProjectList,
);
}
}
// delete cache
await NocoCache.deepDel(
CacheScope.BASE_USER,
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
CacheDelDirection.CHILD_TO_PARENT,
);
await NocoCache.del(`${CacheScope.PROJECT_USER}:${baseId}:${userId}`);
return await ncMeta.metaDelete(null, null, MetaTable.PROJECT_USERS, {
fk_user_id: userId,
base_id: baseId,
});
return response;
}
static async getProjectsIdList(
@ -292,18 +262,7 @@ export default class BaseUser {
params: any,
ncMeta = Noco.ncMeta,
): Promise<BaseType[]> {
// let baseList: BaseType[];
// todo: pagination
// todo: caching based on filter type
// = await NocoCache.getList(CacheScope.USER_PROJECT, [
// userId,
// ]);
// if (baseList.length) {
// return baseList;
// }
// TODO implement CacheScope.USER_BASE
const qb = ncMeta
.knex(MetaTable.PROJECT)
.select(`${MetaTable.PROJECT}.id`)
@ -375,8 +334,6 @@ export default class BaseUser {
for (const base of baseList) {
base.meta = parseMetaProp(base);
}
await NocoCache.setList(CacheScope.USER_PROJECT, [userId], baseList);
}
const castedProjectList = baseList

5
packages/nocodb/src/models/User.ts

@ -106,9 +106,6 @@ export default class User implements UserType {
// delete the email-based cache to avoid unexpected behaviour since we can update email as well
await NocoCache.del(`${CacheScope.USER}:${existingUser.email}`);
// as <baseId> is unknown, delete user:<email>___<baseId> in cache
await NocoCache.delAll(CacheScope.USER, `${existingUser.email}___*`);
// get existing cache
const keys = [
// update user:<id>
@ -240,8 +237,6 @@ export default class User implements UserType {
if (!user) NcError.badRequest('User not found');
// clear all user related cache
await NocoCache.delAll(CacheScope.USER, `${userId}___*`);
await NocoCache.delAll(CacheScope.USER, `${user.email}___*`);
await NocoCache.del(`${CacheScope.USER}:${userId}`);
await NocoCache.del(`${CacheScope.USER}:${user.email}`);

17
packages/nocodb/src/services/base-users/base-users.service.ts

@ -11,7 +11,6 @@ import * as ejs from 'ejs';
import validator from 'validator';
import type { ProjectUserReqType, UserType } from 'nocodb-sdk';
import type { NcRequest } from '~/interface/config';
import NocoCache from '~/cache/NocoCache';
import { validatePayload } from '~/helpers';
import Noco from '~/Noco';
import { AppHooksService } from '~/services/app-hooks/app-hooks.service';
@ -20,8 +19,7 @@ import NcPluginMgrv2 from '~/helpers/NcPluginMgrv2';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { randomTokenString } from '~/helpers/stringHelpers';
import { Base, BaseUser, User } from '~/models';
import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals';
import { MetaTable } from '~/utils/globals';
import { extractProps } from '~/helpers/extractProps';
import { getProjectRolePower } from '~/utils/roleHelper';
@ -131,19 +129,6 @@ export class BaseUsersService {
ip: param.req.clientIp,
req: param.req,
});
const cachedUser = await NocoCache.get(
`${CacheScope.USER}:${email}___${param.baseId}`,
CacheGetType.TYPE_OBJECT,
);
if (cachedUser) {
cachedUser.roles = param.baseUser.roles || 'editor';
await NocoCache.set(
`${CacheScope.USER}:${email}___${param.baseId}`,
cachedUser,
);
}
} else {
try {
// create new user with invite token

4
packages/nocodb/src/utils/globals.ts

@ -149,11 +149,11 @@ export enum CacheScope {
AUDIT = 'audit',
HOOK = 'hook',
PLUGIN = 'plugin',
PROJECT_USER = 'baseUser',
BASE_USER = 'baseUser',
MODEL_ROLE_VISIBILITY = 'modelRoleVisibility',
API_TOKEN = 'apiToken',
INSTANCE_META = 'instanceMeta',
USER_PROJECT = 'userProject',
USER_BASE = 'userBase',
DASHBOARD_PROJECT_DB_PROJECT_LINKING = 'dashboardProjectDBProjectLinking',
SINGLE_QUERY = 'singleQuery',
JOBS = 'nc_jobs',

Loading…
Cancel
Save