diff --git a/packages/nocodb-nest/src/app.module.ts b/packages/nocodb-nest/src/app.module.ts index 1c5d4b7588..f2fceb2bef 100644 --- a/packages/nocodb-nest/src/app.module.ts +++ b/packages/nocodb-nest/src/app.module.ts @@ -17,9 +17,10 @@ import { ColumnsModule } from './modules/columns/columns.module'; import { ViewColumnsModule } from './modules/view-columns/view-columns.module'; import { BasesModule } from './modules/bases/bases.module'; import { HooksModule } from './modules/hooks/hooks.module'; +import { SharedBasesModule } from './modules/shared-bases/shared-bases.module'; @Module({ - imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule], + imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule, SharedBasesModule], controllers: [], providers: [Connection, MetaService, JwtStrategy, ExtractProjectIdMiddleware], exports: [Connection, MetaService], diff --git a/packages/nocodb-nest/src/modules/shared-bases/shared-bases.controller.spec.ts b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.controller.spec.ts new file mode 100644 index 0000000000..8d0fdf7528 --- /dev/null +++ b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SharedBasesController } from './shared-bases.controller'; +import { SharedBasesService } from './shared-bases.service'; + +describe('SharedBasesController', () => { + let controller: SharedBasesController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [SharedBasesController], + providers: [SharedBasesService], + }).compile(); + + controller = module.get(SharedBasesController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/shared-bases/shared-bases.controller.ts b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.controller.ts new file mode 100644 index 0000000000..d7d5d9a688 --- /dev/null +++ b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.controller.ts @@ -0,0 +1,67 @@ +import { + Controller, + Delete, + Get, + Patch, + Post, + UseGuards, +} from '@nestjs/common'; +import { + Acl, + ExtractProjectIdMiddleware, +} from '../../middlewares/extract-project-id/extract-project-id.middleware'; +import { SharedBasesService } from './shared-bases.service'; +import { AuthGuard } from '@nestjs/passport'; + +@Controller('shared-bases') +@UseGuards(ExtractProjectIdMiddleware, AuthGuard('jwt')) +export class SharedBasesController { + constructor(private readonly sharedBasesService: SharedBasesService) {} + + @Post('/api/v1/db/meta/projects/:projectId/shared') + @Acl('createSharedBaseLink') + async createSharedBaseLink(req, res): Promise { + const sharedBase = await this.sharedBasesService.createSharedBaseLink({ + projectId: req.params.projectId, + roles: req.body?.roles, + password: req.body?.password, + siteUrl: req.ncSiteUrl, + }); + + res.json(sharedBase); + } + + @Patch('/api/v1/db/meta/projects/:projectId/shared') + @Acl('updateSharedBaseLink') + async updateSharedBaseLink(req, res): Promise { + const sharedBase = await this.sharedBasesService.updateSharedBaseLink({ + projectId: req.params.projectId, + roles: req.body?.roles, + password: req.body?.password, + siteUrl: req.ncSiteUrl, + }); + + res.json(sharedBase); + } + + @Delete('/api/v1/db/meta/projects/:projectId/shared') + @Acl('disableSharedBaseLink') + async disableSharedBaseLink(req, res): Promise { + const sharedBase = await this.sharedBasesService.disableSharedBaseLink({ + projectId: req.params.projectId, + }); + + res.json(sharedBase); + } + + @Get('/api/v1/db/meta/projects/:projectId/shared') + @Acl('getSharedBaseLink') + async getSharedBaseLink(req, res): Promise { + const sharedBase = await this.sharedBasesService.getSharedBaseLink({ + projectId: req.params.projectId, + siteUrl: req.ncSiteUrl, + }); + + res.json(sharedBase); + } +} diff --git a/packages/nocodb-nest/src/modules/shared-bases/shared-bases.module.ts b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.module.ts new file mode 100644 index 0000000000..27d45c2ff0 --- /dev/null +++ b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { SharedBasesService } from './shared-bases.service'; +import { SharedBasesController } from './shared-bases.controller'; + +@Module({ + controllers: [SharedBasesController], + providers: [SharedBasesService] +}) +export class SharedBasesModule {} diff --git a/packages/nocodb-nest/src/modules/shared-bases/shared-bases.service.spec.ts b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.service.spec.ts new file mode 100644 index 0000000000..30a5ecc6cf --- /dev/null +++ b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { SharedBasesService } from './shared-bases.service'; + +describe('SharedBasesService', () => { + let service: SharedBasesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [SharedBasesService], + }).compile(); + + service = module.get(SharedBasesService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/shared-bases/shared-bases.service.ts b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.service.ts new file mode 100644 index 0000000000..a5736de11e --- /dev/null +++ b/packages/nocodb-nest/src/modules/shared-bases/shared-bases.service.ts @@ -0,0 +1,116 @@ +import { Injectable } from '@nestjs/common'; +import { T } from 'nc-help'; +import { validatePayload } from '../../helpers'; +import { NcError } from '../../helpers/catchError'; +import { Project } from '../../models'; + +import { v4 as uuidv4 } from 'uuid'; + +// todo: load from config +const config = { + dashboardPath: '/nc', +}; + +@Injectable() +export class SharedBasesService { + async createSharedBaseLink(param: { + projectId: string; + roles: string; + password: string; + siteUrl: string; + }): Promise { + validatePayload('swagger.json#/components/schemas/SharedBaseReq', param); + + const project = await Project.get(param.projectId); + + let roles = param?.roles; + if (!roles || (roles !== 'editor' && roles !== 'viewer')) { + roles = 'viewer'; + } + + if (!project) { + NcError.badRequest('Invalid project id'); + } + + const data: any = { + uuid: uuidv4(), + password: param?.password, + roles, + }; + + await Project.update(project.id, data); + + data.url = `${param.siteUrl}${config.dashboardPath}#/nc/base/${data.uuid}`; + delete data.password; + T.emit('evt', { evt_type: 'sharedBase:generated-link' }); + return data; + } + + async updateSharedBaseLink(param: { + projectId: string; + roles: string; + password: string; + siteUrl: string; + }): Promise { + validatePayload('swagger.json#/components/schemas/SharedBaseReq', param); + + const project = await Project.get(param.projectId); + + let roles = param.roles; + if (!roles || (roles !== 'editor' && roles !== 'viewer')) { + roles = 'viewer'; + } + + if (!project) { + NcError.badRequest('Invalid project id'); + } + const data: any = { + uuid: project.uuid || uuidv4(), + password: param.password, + roles, + }; + + await Project.update(project.id, data); + + data.url = `${param.siteUrl}${config.dashboardPath}#/nc/base/${data.uuid}`; + delete data.password; + T.emit('evt', { evt_type: 'sharedBase:generated-link' }); + return data; + } + + async disableSharedBaseLink(param: { projectId: string }): Promise { + const project = await Project.get(param.projectId); + + if (!project) { + NcError.badRequest('Invalid project id'); + } + const data: any = { + uuid: null, + }; + + await Project.update(project.id, data); + + T.emit('evt', { evt_type: 'sharedBase:disable-link' }); + + return { uuid: null }; + } + + async getSharedBaseLink(param: { + projectId: string; + siteUrl: string; + }): Promise { + const project = await Project.get(param.projectId); + + if (!project) { + NcError.badRequest('Invalid project id'); + } + const data: any = { + uuid: project.uuid, + roles: project.roles, + }; + if (data.uuid) + data.url = `${param.siteUrl}${config.dashboardPath}#/nc/base/${data.shared_base_id}`; + + return data; + } +}