Browse Source

wip(api): set cache for project list

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/4426/head
Pranav C 2 years ago
parent
commit
6553defb34
  1. 4
      packages/nc-gui/components.d.ts
  2. 12
      packages/nc-gui/components/tabs/auth/UserManagement.vue
  3. 11
      packages/nocodb/src/lib/meta/api/projectApis.ts
  4. 4
      packages/nocodb/src/lib/models/Project.ts
  5. 95
      packages/nocodb/src/lib/models/ProjectUser.ts
  6. 1
      packages/nocodb/src/lib/utils/globals.ts

4
packages/nc-gui/components.d.ts vendored

@ -131,7 +131,6 @@ declare module '@vue/runtime-core' {
MdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] MdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
MdiChevronLeft: typeof import('~icons/mdi/chevron-left')['default'] MdiChevronLeft: typeof import('~icons/mdi/chevron-left')['default']
MdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] MdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
MdiClipboard: typeof import('~icons/mdi/clipboard')['default']
MdiClose: typeof import('~icons/mdi/close')['default'] MdiClose: typeof import('~icons/mdi/close')['default']
MdiCloseBox: typeof import('~icons/mdi/close-box')['default'] MdiCloseBox: typeof import('~icons/mdi/close-box')['default']
MdiCloseCircle: typeof import('~icons/mdi/close-circle')['default'] MdiCloseCircle: typeof import('~icons/mdi/close-circle')['default']
@ -142,7 +141,6 @@ declare module '@vue/runtime-core' {
MdiCommentTextOutline: typeof import('~icons/mdi/comment-text-outline')['default'] MdiCommentTextOutline: typeof import('~icons/mdi/comment-text-outline')['default']
MdiContentCopy: typeof import('~icons/mdi/content-copy')['default'] MdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
MdiContentSave: typeof import('~icons/mdi/content-save')['default'] MdiContentSave: typeof import('~icons/mdi/content-save')['default']
MdiCopy: typeof import('~icons/mdi/copy')['default']
MdiCurrencyUsd: typeof import('~icons/mdi/currency-usd')['default'] MdiCurrencyUsd: typeof import('~icons/mdi/currency-usd')['default']
MdiDatabaseOutline: typeof import('~icons/mdi/database-outline')['default'] MdiDatabaseOutline: typeof import('~icons/mdi/database-outline')['default']
MdiDatabaseSync: typeof import('~icons/mdi/database-sync')['default'] MdiDatabaseSync: typeof import('~icons/mdi/database-sync')['default']
@ -180,7 +178,6 @@ declare module '@vue/runtime-core' {
MdiInformation: typeof import('~icons/mdi/information')['default'] MdiInformation: typeof import('~icons/mdi/information')['default']
MdiJson: typeof import('~icons/mdi/json')['default'] MdiJson: typeof import('~icons/mdi/json')['default']
MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] MdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
MdiKeyChainVariant: typeof import('~icons/mdi/key-chain-variant')['default']
MdiKeyChange: typeof import('~icons/mdi/key-change')['default'] MdiKeyChange: typeof import('~icons/mdi/key-change')['default']
MdiKeyStar: typeof import('~icons/mdi/key-star')['default'] MdiKeyStar: typeof import('~icons/mdi/key-star')['default']
MdiLink: typeof import('~icons/mdi/link')['default'] MdiLink: typeof import('~icons/mdi/link')['default']
@ -207,7 +204,6 @@ declare module '@vue/runtime-core' {
MdiRocketLaunchOutline: typeof import('~icons/mdi/rocket-launch-outline')['default'] MdiRocketLaunchOutline: typeof import('~icons/mdi/rocket-launch-outline')['default']
MdiScriptTextKeyOutline: typeof import('~icons/mdi/script-text-key-outline')['default'] MdiScriptTextKeyOutline: typeof import('~icons/mdi/script-text-key-outline')['default']
MdiScriptTextOutline: typeof import('~icons/mdi/script-text-outline')['default'] MdiScriptTextOutline: typeof import('~icons/mdi/script-text-outline')['default']
MdiShieldAccountOutline: typeof import('~icons/mdi/shield-account-outline')['default']
MdiShieldKeyOutline: typeof import('~icons/mdi/shield-key-outline')['default'] MdiShieldKeyOutline: typeof import('~icons/mdi/shield-key-outline')['default']
MdiSlack: typeof import('~icons/mdi/slack')['default'] MdiSlack: typeof import('~icons/mdi/slack')['default']
MdiSort: typeof import('~icons/mdi/sort')['default'] MdiSort: typeof import('~icons/mdi/sort')['default']

12
packages/nc-gui/components/tabs/auth/UserManagement.vue

@ -251,20 +251,21 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
</div> </div>
</div> </div>
<div v-for="(user, index) of users" :key="index" <div v-for="(user, index) of users" :key="index" class="flex flex-row items-center border-b-1 py-2 px-2 nc-user-row">
class="flex flex-row items-center border-b-1 py-2 px-2 nc-user-row">
<div class="flex w-4/6 flex-wrap nc-user-email"> <div class="flex w-4/6 flex-wrap nc-user-email">
{{ user.email }} {{ user.email }}
</div> </div>
<div class="flex w-1/6 justify-center flex-wrap ml-4"> <div class="flex w-1/6 justify-center flex-wrap ml-4">
<div v-if="isSuperAdmin(user)" class="rounded-full px-2 py-1 nc-user-role" <div
v-if="isSuperAdmin(user)"
class="rounded-full px-2 py-1 nc-user-role"
:style="{ backgroundColor: projectRoleTagColors[OrgUserRoles.SUPER_ADMIN] }" :style="{ backgroundColor: projectRoleTagColors[OrgUserRoles.SUPER_ADMIN] }"
> >
Super Admin Super Admin
</div> </div>
<div <div
v-else-if="user.roles" v-if="user.roles"
class="rounded-full px-2 py-1 nc-user-role" class="rounded-full px-2 py-1 nc-user-role"
:style="{ backgroundColor: projectRoleTagColors[user.roles] }" :style="{ backgroundColor: projectRoleTagColors[user.roles] }"
> >
@ -311,8 +312,7 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<a-dropdown :trigger="['click']" class="flex" placement="bottomRight" <a-dropdown :trigger="['click']" class="flex" placement="bottomRight" overlay-class-name="nc-dropdown-user-mgmt">
overlay-class-name="nc-dropdown-user-mgmt">
<div class="flex flex-row items-center"> <div class="flex flex-row items-center">
<a-button type="text" class="!px-0"> <a-button type="text" class="!px-0">
<div class="flex flex-row items-center h-[1.2rem]"> <div class="flex flex-row items-center h-[1.2rem]">

11
packages/nocodb/src/lib/meta/api/projectApis.ts

@ -1,5 +1,5 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { OrgUserRoles } from 'nocodb-sdk'; import { OrgUserRoles, ProjectType } from 'nocodb-sdk';
import Project from '../../models/Project'; import Project from '../../models/Project';
import { ModelTypes, ProjectListType, UITypes } from 'nocodb-sdk'; import { ModelTypes, ProjectListType, UITypes } from 'nocodb-sdk';
import DOMPurify from 'isomorphic-dompurify'; import DOMPurify from 'isomorphic-dompurify';
@ -77,14 +77,13 @@ export async function projectList(
next next
) { ) {
try { try {
const projects = await ProjectUser.getProjectsList( const projects = req.user?.roles?.includes(OrgUserRoles.SUPER_ADMIN)
req.user.id, ? await Project.list(req.query)
req.user?.roles?.includes(OrgUserRoles.SUPER_ADMIN) : await ProjectUser.getProjectsList(req.user.id, req.query);
);
res // todo: pagination res // todo: pagination
.json( .json(
new PagedResponseImpl(projects, { new PagedResponseImpl(projects as ProjectType[], {
count: projects.length, count: projects.length,
limit: projects.length, limit: projects.length,
}) })

4
packages/nocodb/src/lib/models/Project.ts

@ -194,6 +194,8 @@ export default class Project implements ProjectType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${o.id}`); await NocoCache.del(`${CacheScope.PROJECT}:ref:${o.id}`);
} }
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.del(CacheScope.INSTANCE_META); await NocoCache.del(CacheScope.INSTANCE_META);
// remove item in cache list // remove item in cache list
@ -292,6 +294,8 @@ export default class Project implements ProjectType {
await NocoCache.del(`${CacheScope.PROJECT}:ref:${project.id}`); await NocoCache.del(`${CacheScope.PROJECT}:ref:${project.id}`);
} }
await NocoCache.delAll(CacheScope.USER_PROJECT, '*');
await NocoCache.deepDel( await NocoCache.deepDel(
CacheScope.PROJECT, CacheScope.PROJECT,
`${CacheScope.PROJECT}:${projectId}`, `${CacheScope.PROJECT}:${projectId}`,

95
packages/nocodb/src/lib/models/ProjectUser.ts

@ -36,6 +36,12 @@ export default class ProjectUser {
true true
); );
await NocoCache.appendToList(
CacheScope.USER_PROJECT,
[projectUser.fk_user_id],
`${CacheScope.USER_PROJECT}:${projectUser.project_id}`
);
return this.get(project_id, fk_user_id, ncMeta); return this.get(project_id, fk_user_id, ncMeta);
} }
@ -166,7 +172,7 @@ export default class ProjectUser {
); );
} }
static async delete(projectId: string, userId, ncMeta = Noco.ncMeta) { static async delete(projectId: string, userId: string, ncMeta = Noco.ncMeta) {
// await NocoCache.deepDel( // await NocoCache.deepDel(
// CacheScope.PROJECT_USER, // CacheScope.PROJECT_USER,
// `${CacheScope.PROJECT_USER}:${projectId}:${userId}`, // `${CacheScope.PROJECT_USER}:${projectId}:${userId}`,
@ -178,6 +184,20 @@ export default class ProjectUser {
if (email) { if (email) {
await NocoCache.delAll(CacheScope.USER, `${email}*`); await NocoCache.delAll(CacheScope.USER, `${email}*`);
} }
// remove project from user project list cache
let cachedProjectList = await NocoCache.getList(CacheScope.USER_PROJECT, [
userId,
]);
if (cachedProjectList?.length) {
cachedProjectList = cachedProjectList.filter((p) => p.id !== projectId);
await NocoCache.setList(
CacheScope.USER_PROJECT,
[userId],
cachedProjectList
);
}
await NocoCache.del(`${CacheScope.PROJECT_USER}:${projectId}:${userId}`); await NocoCache.del(`${CacheScope.PROJECT_USER}:${projectId}:${userId}`);
return await ncMeta.metaDelete(null, null, MetaTable.PROJECT_USERS, { return await ncMeta.metaDelete(null, null, MetaTable.PROJECT_USERS, {
fk_user_id: userId, fk_user_id: userId,
@ -196,83 +216,44 @@ export default class ProjectUser {
static async getProjectsList( static async getProjectsList(
userId: string, userId: string,
isSuperAdmin: boolean, _params: any,
ncMeta = Noco.ncMeta ncMeta = Noco.ncMeta
): Promise<ProjectType[]> { ): Promise<ProjectType[]> {
// todo: pagination // todo: pagination
// todo: caching let projectList = await NocoCache.getList(CacheScope.USER_PROJECT, [
// let projectList = await NocoCache.getList(CacheScope.PROJECT, []); userId,
]);
if (projectList.length) {
return projectList;
}
const qb = ncMeta projectList = await ncMeta
.knex(MetaTable.PROJECT) .knex(MetaTable.PROJECT)
.select(`${MetaTable.PROJECT}.*`) .select(`${MetaTable.PROJECT}.*`)
[isSuperAdmin ? 'leftJoin' : 'innerJoin']( .innerJoin(MetaTable.PROJECT_USERS, function () {
MetaTable.PROJECT_USERS,
function () {
this.on( this.on(
`${MetaTable.PROJECT_USERS}.project_id`, `${MetaTable.PROJECT_USERS}.project_id`,
`${MetaTable.PROJECT}.id` `${MetaTable.PROJECT}.id`
); );
if (!isSuperAdmin) { // if (!isSuperAdmin) {
this.andOn( this.andOn(
`${MetaTable.PROJECT_USERS}.fk_user_id`, `${MetaTable.PROJECT_USERS}.fk_user_id`,
ncMeta.knex.raw('?', [userId]) ncMeta.knex.raw('?', [userId])
); );
} // }
} })
)
// .innerJoin(MetaTable.USERS, function () {
// this.on(
// `${MetaTable.PROJECT_USERS}.fk_user_id`,
// `${MetaTable.USERS}.id`
// );
// })
// .where(function () {
// this.where(`${MetaTable.PROJECT_USERS}.fk_user_id`, userId)
// .orWhere(
// `${MetaTable.USERS}.roles`,
// 'like',
// `%${OrgUserRoles.SUPER_ADMIN}%`
// );
// })
.where(function () { .where(function () {
this.where(`${MetaTable.PROJECT}.deleted`, false).orWhereNull( this.where(`${MetaTable.PROJECT}.deleted`, false).orWhereNull(
`${MetaTable.PROJECT}.deleted` `${MetaTable.PROJECT}.deleted`
); );
}); });
// if (!projectList.length) {
// projectList = await ncMeta.metaList2(null, null, MetaTable.PROJECT, {
// xcCondition: {
// _or: [
// {
// deleted: {
// eq: false,
// },
// },
// {
// deleted: {
// eq: null,
// },
// },
// ],
// },
// })
// await NocoCache.setList(CacheScope.PROJECT, [], projectList)
// }
// projectList = projectList.filter(
// (p) => p.deleted === 0 || p.deleted === false || p.deleted === null,
// )
// return projectList.map((m) => new Project(m))
//
//
// return await ncMeta.metaList2(null, null, MetaTable.PROJECT_USERS, {
// condition: { fk_user_id: userId },
// })
// }
console.log(qb.toQuery()); if (projectList?.length) {
await NocoCache.setList(CacheScope.USER_PROJECT, [userId], projectList);
}
return qb; return projectList;
} }
} }

1
packages/nocodb/src/lib/utils/globals.ts

@ -136,6 +136,7 @@ export enum CacheScope {
MODEL_ROLE_VISIBILITY = 'modelRoleVisibility', MODEL_ROLE_VISIBILITY = 'modelRoleVisibility',
API_TOKEN = 'apiToken', API_TOKEN = 'apiToken',
INSTANCE_META = 'instanceMeta', INSTANCE_META = 'instanceMeta',
USER_PROJECT = 'userProject',
} }
export enum CacheGetType { export enum CacheGetType {
TYPE_ARRAY = 'TYPE_ARRAY', TYPE_ARRAY = 'TYPE_ARRAY',

Loading…
Cancel
Save