diff --git a/packages/nocodb-nest/src/app.module.ts b/packages/nocodb-nest/src/app.module.ts index 5c7d6c927c..eb4e2fb781 100644 --- a/packages/nocodb-nest/src/app.module.ts +++ b/packages/nocodb-nest/src/app.module.ts @@ -27,9 +27,11 @@ import { GridColumnsModule } from './modules/grid-columns/grid-columns.module'; import { MapsModule } from './modules/maps/maps.module'; import { ProjectUsersModule } from './modules/project-users/project-users.module'; import { ModelVisibilitiesModule } from './modules/model-visibilities/model-visibilities.module'; +import { HookFiltersModule } from './modules/hook-filters/hook-filters.module'; +import { ApiTokensModule } from './modules/api-tokens/api-tokens.module'; @Module({ - imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule, SharedBasesModule, FormsModule, GridsModule, KanbansModule, GalleriesModule, FormColumnsModule, GridColumnsModule, MapsModule, ProjectUsersModule, ModelVisibilitiesModule], + imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule, SharedBasesModule, FormsModule, GridsModule, KanbansModule, GalleriesModule, FormColumnsModule, GridColumnsModule, MapsModule, ProjectUsersModule, ModelVisibilitiesModule, HookFiltersModule, ApiTokensModule], controllers: [], providers: [Connection, MetaService, JwtStrategy, ExtractProjectIdMiddleware], exports: [Connection, MetaService], diff --git a/packages/nocodb-nest/src/modules/api-tokens/api-tokens.controller.spec.ts b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.controller.spec.ts new file mode 100644 index 0000000000..6084000f5d --- /dev/null +++ b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ApiTokensController } from './api-tokens.controller'; +import { ApiTokensService } from './api-tokens.service'; + +describe('ApiTokensController', () => { + let controller: ApiTokensController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ApiTokensController], + providers: [ApiTokensService], + }).compile(); + + controller = module.get(ApiTokensController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/api-tokens/api-tokens.controller.ts b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.controller.ts new file mode 100644 index 0000000000..31d76ea046 --- /dev/null +++ b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.controller.ts @@ -0,0 +1,49 @@ +import { + Controller, + Delete, + Get, + Post, + UseGuards, + Request, + Body, + Param, +} from '@nestjs/common'; +import { PagedResponseImpl } from '../../helpers/PagedResponse'; +import { + Acl, + ExtractProjectIdMiddleware, +} from '../../middlewares/extract-project-id/extract-project-id.middleware'; +import { ApiTokensService } from './api-tokens.service'; +import { AuthGuard } from '@nestjs/passport'; + +@Controller() +@UseGuards(ExtractProjectIdMiddleware, AuthGuard('jwt')) +export class ApiTokensController { + constructor(private readonly apiTokensService: ApiTokensService) {} + + @Get('/api/v1/db/meta/projects/:projectId/api-tokens') + @Acl('apiTokenList') + async apiTokenList(@Request() req) { + return new PagedResponseImpl( + await this.apiTokensService.apiTokenList({ userId: req['user'].id }), + ); + } + + @Post('/api/v1/db/meta/projects/:projectId/api-tokens') + @Acl('apiTokenCreate') + async apiTokenCreate(@Request() req, @Body() body) { + return await this.apiTokensService.apiTokenCreate({ + tokenBody: body, + userId: req['user'].id, + }); + } + + @Delete('/api/v1/db/meta/projects/:projectId/api-tokens/:token') + @Acl('apiTokenDelete') + async apiTokenDelete(@Request() req, @Param('token') token: string) { + return await this.apiTokensService.apiTokenDelete({ + token, + user: req['user'], + }); + } +} diff --git a/packages/nocodb-nest/src/modules/api-tokens/api-tokens.module.ts b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.module.ts new file mode 100644 index 0000000000..f62a8267eb --- /dev/null +++ b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { ApiTokensService } from './api-tokens.service'; +import { ApiTokensController } from './api-tokens.controller'; + +@Module({ + controllers: [ApiTokensController], + providers: [ApiTokensService] +}) +export class ApiTokensModule {} diff --git a/packages/nocodb-nest/src/modules/api-tokens/api-tokens.service.spec.ts b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.service.spec.ts new file mode 100644 index 0000000000..3bea289c6e --- /dev/null +++ b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ApiTokensService } from './api-tokens.service'; + +describe('ApiTokensService', () => { + let service: ApiTokensService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ApiTokensService], + }).compile(); + + service = module.get(ApiTokensService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/api-tokens/api-tokens.service.ts b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.service.ts new file mode 100644 index 0000000000..68acdbf337 --- /dev/null +++ b/packages/nocodb-nest/src/modules/api-tokens/api-tokens.service.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@nestjs/common'; +import { T } from 'nc-help'; +import { ApiTokenReqType, OrgUserRoles } from 'nocodb-sdk' +import { validatePayload } from '../../helpers' +import { NcError } from '../../helpers/catchError' +import { ApiToken, User } from '../../models' + +@Injectable() +export class ApiTokensService { + async apiTokenList(param: { userId: string }) { + return await ApiToken.list(param.userId); + } + async apiTokenCreate(param: { + userId: string; + tokenBody: ApiTokenReqType; + }) { + validatePayload( + 'swagger.json#/components/schemas/ApiTokenReq', + param.tokenBody + ); + + T.emit('evt', { evt_type: 'apiToken:created' }); + return await ApiToken.insert({ + ...param.tokenBody, + fk_user_id: param.userId, + }); + } + + async apiTokenDelete(param: { token; user: User }) { + const apiToken = await ApiToken.getByToken(param.token); + if ( + !param.user.roles.includes(OrgUserRoles.SUPER_ADMIN) && + apiToken.fk_user_id !== param.user.id + ) { + NcError.notFound('Token not found'); + } + T.emit('evt', { evt_type: 'apiToken:deleted' }); + + // todo: verify token belongs to the user + return await ApiToken.delete(param.token); + } + +} diff --git a/packages/nocodb-nest/src/modules/hook-filters/hook-filters.controller.spec.ts b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.controller.spec.ts new file mode 100644 index 0000000000..68bee3f0a2 --- /dev/null +++ b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { HookFiltersController } from './hook-filters.controller'; +import { HookFiltersService } from './hook-filters.service'; + +describe('HookFiltersController', () => { + let controller: HookFiltersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [HookFiltersController], + providers: [HookFiltersService], + }).compile(); + + controller = module.get(HookFiltersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/hook-filters/hook-filters.controller.ts b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.controller.ts new file mode 100644 index 0000000000..5e5d1dd6c6 --- /dev/null +++ b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.controller.ts @@ -0,0 +1,8 @@ +import { Controller } from '@nestjs/common'; +import { HookFiltersService } from './hook-filters.service'; + +@Controller('hook-filters') +export class HookFiltersController { + constructor(private readonly hookFiltersService: HookFiltersService) {} + +} diff --git a/packages/nocodb-nest/src/modules/hook-filters/hook-filters.module.ts b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.module.ts new file mode 100644 index 0000000000..52efc3fc97 --- /dev/null +++ b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { HookFiltersService } from './hook-filters.service'; +import { HookFiltersController } from './hook-filters.controller'; + +@Module({ + controllers: [HookFiltersController], + providers: [HookFiltersService] +}) +export class HookFiltersModule {} diff --git a/packages/nocodb-nest/src/modules/hook-filters/hook-filters.service.spec.ts b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.service.spec.ts new file mode 100644 index 0000000000..a29cb5ab66 --- /dev/null +++ b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { HookFiltersService } from './hook-filters.service'; + +describe('HookFiltersService', () => { + let service: HookFiltersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [HookFiltersService], + }).compile(); + + service = module.get(HookFiltersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/hook-filters/hook-filters.service.ts b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.service.ts new file mode 100644 index 0000000000..2d537692bd --- /dev/null +++ b/packages/nocodb-nest/src/modules/hook-filters/hook-filters.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class HookFiltersService {}