From b7da5eaf40e9b6651891cf19a34cbf4d470f496d Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 3 Mar 2023 10:35:25 +0530 Subject: [PATCH] refactor: move ajv validator from express middleware to service layer Signed-off-by: Pranav C --- .../src/lib/controllers/apiTokenController.ts | 36 +- .../src/lib/controllers/auditController.ts | 13 +- .../src/lib/controllers/baseController.ts | 100 ++---- .../src/lib/controllers/columnController.ts | 2 - .../dataApis/dataAliasExportApis.ts | 6 +- .../src/lib/controllers/filterController.ts | 4 - .../controllers/formViewColumnController.ts | 2 - .../src/lib/controllers/formViewController.ts | 50 +-- .../lib/controllers/galleryViewController.ts | 3 - .../controllers/gridViewColumnController.ts | 2 - .../src/lib/controllers/gridViewController.ts | 35 +- .../src/lib/controllers/hookController.ts | 4 - .../lib/controllers/hookFilterController.ts | 3 - .../lib/controllers/kanbanViewController.ts | 5 +- .../controllers/modelVisibilityController.ts | 2 - .../lib/controllers/orgLicenseController.ts | 2 - .../src/lib/controllers/orgTokenController.ts | 56 +--- .../src/lib/controllers/orgUserController.ts | 3 - .../src/lib/controllers/pluginController.ts | 4 - .../src/lib/controllers/projectController.ts | 2 - .../lib/controllers/projectUserController.ts | 316 ++---------------- .../lib/controllers/sharedBaseController.ts | 97 ++---- .../src/lib/controllers/sortController.ts | 3 - .../src/lib/controllers/tableController.ts | 117 ++++++- .../lib/controllers/userController/index.ts | 22 +- .../src/lib/meta/api/helpers/apiHelpers.ts | 6 +- packages/nocodb/src/lib/models/Hook.ts | 164 ++++----- .../src/lib/services/apiTokenService.ts | 6 + .../nocodb/src/lib/services/auditService.ts | 11 + .../nocodb/src/lib/services/baseService.ts | 6 +- .../nocodb/src/lib/services/columnService.ts | 3 + .../src/lib/services/dataService/index.ts | 2 +- .../nocodb/src/lib/services/filterService.ts | 7 + .../src/lib/services/formViewColumnService.ts | 6 + .../src/lib/services/formViewService.ts | 5 + .../src/lib/services/galleryViewService.ts | 5 + .../src/lib/services/gridViewColumnService.ts | 3 + .../src/lib/services/gridViewService.ts | 4 + .../src/lib/services/hookFilterService.ts | 5 + .../nocodb/src/lib/services/hookService.ts | 10 + .../src/lib/services/kanbanViewService.ts | 10 +- .../lib/services/modelVisibilityService.ts | 7 +- .../src/lib/services/orgLicenseService.ts | 3 + .../src/lib/services/orgTokenService.ts | 6 + .../nocodb/src/lib/services/orgUserService.ts | 5 + .../nocodb/src/lib/services/pluginService.ts | 5 + .../nocodb/src/lib/services/projectService.ts | 4 +- .../src/lib/services/projectUserService.ts | 11 + .../src/lib/services/sharedBaseService.ts | 5 + .../nocodb/src/lib/services/sortService.ts | 5 + .../nocodb/src/lib/services/tableService.ts | 21 +- .../src/lib/services/userService/index.ts | 16 + 52 files changed, 513 insertions(+), 717 deletions(-) diff --git a/packages/nocodb/src/lib/controllers/apiTokenController.ts b/packages/nocodb/src/lib/controllers/apiTokenController.ts index 33e940c0e5..d472c9a800 100644 --- a/packages/nocodb/src/lib/controllers/apiTokenController.ts +++ b/packages/nocodb/src/lib/controllers/apiTokenController.ts @@ -1,31 +1,28 @@ import { Request, Response, Router } from 'express'; -import { OrgUserRoles } from 'nocodb-sdk'; -import { Tele } from 'nc-help'; -import { NcError } from '../meta/helpers/catchError'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; -import ApiToken from '../models/ApiToken'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; +import { apiTokenService } from '../services'; export async function apiTokenList(req: Request, res: Response) { - res.json(await ApiToken.list(req['user'].id)); + res.json(await apiTokenService.apiTokenList({ userId: req['user'].id })); } + export async function apiTokenCreate(req: Request, res: Response) { - Tele.emit('evt', { evt_type: 'apiToken:created' }); - res.json(await ApiToken.insert({ ...req.body, fk_user_id: req['user'].id })); + res.json( + await apiTokenService.apiTokenCreate({ + tokenBody: req.body, + userId: req['user'].id, + }) + ); } -export async function apiTokenDelete(req: Request, res: Response) { - const apiToken = await ApiToken.getByToken(req.params.token); - if ( - !req['user'].roles.includes(OrgUserRoles.SUPER_ADMIN) && - apiToken.fk_user_id !== req['user'].id - ) { - NcError.notFound('Token not found'); - } - Tele.emit('evt', { evt_type: 'apiToken:deleted' }); - // todo: verify token belongs to the user - res.json(await ApiToken.delete(req.params.token)); +export async function apiTokenDelete(req: Request, res: Response) { + res.json( + await apiTokenService.apiTokenDelete({ + token: req.params.token, + user: req['user'], + }) + ); } // todo: add reset token api to regenerate token @@ -41,7 +38,6 @@ router.get( router.post( '/api/v1/db/meta/projects/:projectId/api-tokens', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/ApiTokenReq'), ncMetaAclMw(apiTokenCreate, 'apiTokenCreate') ); router.delete( diff --git a/packages/nocodb/src/lib/controllers/auditController.ts b/packages/nocodb/src/lib/controllers/auditController.ts index ee015f638a..366fc70529 100644 --- a/packages/nocodb/src/lib/controllers/auditController.ts +++ b/packages/nocodb/src/lib/controllers/auditController.ts @@ -1,18 +1,17 @@ import { Request, Response, Router } from 'express'; import Audit from '../models/Audit'; -import { AuditOperationTypes } from 'nocodb-sdk'; import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; - -import { getAjvValidatorMw } from '../meta/api/helpers'; import { auditService } from '../services'; export async function commentRow(req: Request, res) { res.json( - await Audit.insert({ - ...req.body, + await auditService.commentRow({ + rowId: req.params.rowId, user: (req as any).user, - op_type: AuditOperationTypes.COMMENT, + body: { + ...req.body, + }, }) ); } @@ -60,12 +59,10 @@ router.get( ); router.post( '/api/v1/db/meta/audits/comments', - getAjvValidatorMw('swagger.json#/components/schemas/CommentReq'), ncMetaAclMw(commentRow, 'commentRow') ); router.post( '/api/v1/db/meta/audits/rows/:rowId/update', - getAjvValidatorMw('swagger.json#/components/schemas/AuditRowUpdateReq'), ncMetaAclMw(auditRowUpdate, 'auditRowUpdate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/baseController.ts b/packages/nocodb/src/lib/controllers/baseController.ts index 1ac4d3e3ba..f35deb5069 100644 --- a/packages/nocodb/src/lib/controllers/baseController.ts +++ b/packages/nocodb/src/lib/controllers/baseController.ts @@ -1,104 +1,66 @@ import { Request, Response } from 'express'; -import Project from '../models/Project'; import { BaseListType } from 'nocodb-sdk'; import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; -import { syncBaseMigration } from '../meta/helpers/syncMigration'; import Base from '../models/Base'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; -import { Tele } from 'nc-help'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw, populateMeta } from '../meta/api/helpers'; -export async function baseGet( - req: Request, - res: Response -) { - const base = await Base.get(req.params.baseId); +import { baseService } from '../services'; - base.config = base.getConnectionConfig(); +async function baseGet(req: Request, res: Response) { + const base = await baseService.baseGetWithConfig({ + baseId: req.params.baseId, + }); res.json(base); } -export async function baseUpdate( - req: Request, - res: Response -) { - const baseBody = req.body; - const project = await Project.getWithInfo(req.params.projectId); - const base = await Base.updateBase(req.params.baseId, { - ...baseBody, - type: baseBody.config?.client, - projectId: project.id, - id: req.params.baseId, - }); - - delete base.config; - - Tele.emit('evt', { - evt_type: 'base:updated', +async function baseUpdate(req: Request, res: Response) { + const base = await baseService.baseUpdate({ + baseId: req.params.baseId, + base: req.body, + projectId: req.params.projectId, }); - res.json(base); } -export async function baseList( +async function baseList( req: Request, - res: Response, - next + res: Response ) { - try { - const bases = await Base.list({ projectId: req.params.projectId }); + const bases = await baseService.baseList({ + projectId: req.params.projectId, + }); - res // todo: pagination - .json({ - bases: new PagedResponseImpl(bases, { - count: bases.length, - limit: bases.length, - }), - }); - } catch (e) { - console.log(e); - next(e); - } + res // todo: pagination + .json({ + bases: new PagedResponseImpl(bases, { + count: bases.length, + limit: bases.length, + }), + }); } export async function baseDelete( req: Request, res: Response ) { - const base = await Base.get(req.params.baseId); - const result = await base.delete(); - Tele.emit('evt', { evt_type: 'base:deleted' }); + const result = await baseService.baseDelete({ + baseId: req.params.baseId, + }); res.json(result); } async function baseCreate(req: Request, res) { - // type | base | projectId - const baseBody = req.body; - const project = await Project.getWithInfo(req.params.projectId); - const base = await Base.createBase({ - ...baseBody, - type: baseBody.config?.client, - projectId: project.id, - }); - - await syncBaseMigration(project, base); - - const info = await populateMeta(base, project); - - Tele.emit('evt_api_created', info); - - delete base.config; - - Tele.emit('evt', { - evt_type: 'base:created', + const base = await baseService.baseCreate({ + projectId: req.params.projectId, + base: req.body, }); res.json(base); } -export default (router) => { +const initRoutes = (router) => { router.get( '/api/v1/db/meta/projects/:projectId/bases/:baseId', metaApiMetrics, @@ -107,7 +69,6 @@ export default (router) => { router.patch( '/api/v1/db/meta/projects/:projectId/bases/:baseId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/BaseReq'), ncMetaAclMw(baseUpdate, 'baseUpdate') ); router.delete( @@ -118,7 +79,6 @@ export default (router) => { router.post( '/api/v1/db/meta/projects/:projectId/bases', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/BaseReq'), ncMetaAclMw(baseCreate, 'baseCreate') ); router.get( @@ -127,3 +87,5 @@ export default (router) => { ncMetaAclMw(baseList, 'baseList') ); }; + +export default initRoutes; diff --git a/packages/nocodb/src/lib/controllers/columnController.ts b/packages/nocodb/src/lib/controllers/columnController.ts index 61f2e0139d..8f6b34e12d 100644 --- a/packages/nocodb/src/lib/controllers/columnController.ts +++ b/packages/nocodb/src/lib/controllers/columnController.ts @@ -1,6 +1,5 @@ import { Request, Response, Router } from 'express'; import { ColumnReqType, TableType, UITypes } from 'nocodb-sdk'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { columnService } from '../services'; @@ -45,7 +44,6 @@ const router = Router({ mergeParams: true }); router.post( '/api/v1/db/meta/tables/:tableId/columns/', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/ColumnReq'), ncMetaAclMw(columnAdd, 'columnAdd') ); diff --git a/packages/nocodb/src/lib/controllers/dataApis/dataAliasExportApis.ts b/packages/nocodb/src/lib/controllers/dataApis/dataAliasExportApis.ts index f0f145f596..c628e31814 100644 --- a/packages/nocodb/src/lib/controllers/dataApis/dataAliasExportApis.ts +++ b/packages/nocodb/src/lib/controllers/dataApis/dataAliasExportApis.ts @@ -15,7 +15,11 @@ async function excelDataExport(req: Request, res: Response) { if (!targetView) { targetView = await View.getDefaultView(model.id); } - const { offset, elapsed, data } = await extractXlsxData({ view: targetView, query: req.query, siteUrl: (req as any).ncSiteUrl }); + const { offset, elapsed, data } = await extractXlsxData({ + view: targetView, + query: req.query, + siteUrl: (req as any).ncSiteUrl, + }); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, data, targetView.title); const buf = XLSX.write(wb, { type: 'base64', bookType: 'xlsx' }); diff --git a/packages/nocodb/src/lib/controllers/filterController.ts b/packages/nocodb/src/lib/controllers/filterController.ts index d83f3e22b7..bf48e30765 100644 --- a/packages/nocodb/src/lib/controllers/filterController.ts +++ b/packages/nocodb/src/lib/controllers/filterController.ts @@ -1,6 +1,5 @@ import { Request, Response, Router } from 'express'; import { FilterReqType } from 'nocodb-sdk'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; @@ -80,7 +79,6 @@ router.get( router.post( '/api/v1/db/meta/views/:viewId/filters', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), ncMetaAclMw(filterCreate, 'filterCreate') ); @@ -91,7 +89,6 @@ router.get( router.post( '/api/v1/db/meta/hooks/:hookId/filters', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), ncMetaAclMw(hookFilterCreate, 'filterCreate') ); @@ -103,7 +100,6 @@ router.get( router.patch( '/api/v1/db/meta/filters/:filterId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), ncMetaAclMw(filterUpdate, 'filterUpdate') ); router.delete( diff --git a/packages/nocodb/src/lib/controllers/formViewColumnController.ts b/packages/nocodb/src/lib/controllers/formViewColumnController.ts index 532b5eb82a..8b13309411 100644 --- a/packages/nocodb/src/lib/controllers/formViewColumnController.ts +++ b/packages/nocodb/src/lib/controllers/formViewColumnController.ts @@ -1,7 +1,6 @@ import { Request, Response, Router } from 'express'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { formViewColumnService } from '../services'; export async function columnUpdate(req: Request, res: Response) { @@ -17,7 +16,6 @@ const router = Router({ mergeParams: true }); router.patch( '/api/v1/db/meta/form-columns/:formViewColumnId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FormColumnReq'), ncMetaAclMw(columnUpdate, 'columnUpdate') ); export default router; diff --git a/packages/nocodb/src/lib/controllers/formViewController.ts b/packages/nocodb/src/lib/controllers/formViewController.ts index 02f8bc8e44..2bc61fd58d 100644 --- a/packages/nocodb/src/lib/controllers/formViewController.ts +++ b/packages/nocodb/src/lib/controllers/formViewController.ts @@ -1,50 +1,37 @@ import { Request, Response, Router } from 'express'; -// @ts-ignore -import Model from '../models/Model'; -import { Tele } from 'nc-help'; -// @ts-ignore -import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; -import { FormType, ViewTypes } from 'nocodb-sdk'; -// @ts-ignore -import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2'; -// @ts-ignore -import Project from '../models/Project'; -import View from '../models/View'; -import FormView from '../models/FormView'; +import { FormType } from 'nocodb-sdk'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; +import { formViewService } from '../services'; -// @ts-ignore export async function formViewGet(req: Request, res: Response) { - const formViewData = await FormView.getWithInfo(req.params.formViewId); + const formViewData = await formViewService.formViewGet({ + formViewId: req.params.formViewId, + }); res.json(formViewData); } export async function formViewCreate(req: Request, res) { - Tele.emit('evt', { evt_type: 'vtable:created', show_as: 'form' }); - const view = await View.insert({ - ...req.body, - // todo: sanitize - fk_model_id: req.params.tableId, - type: ViewTypes.FORM, + const view = await formViewService.formViewCreate({ + body: req.body, + tableId: req.params.tableId, }); res.json(view); } -// @ts-ignore + export async function formViewUpdate(req, res) { - Tele.emit('evt', { evt_type: 'view:updated', type: 'grid' }); - res.json(await FormView.update(req.params.formViewId, req.body)); + res.json( + await formViewService.formViewUpdate({ + formViewId: req.params.formViewId, + body: req.body, + }) + ); } -// @ts-ignore -export async function formViewDelete(req: Request, res: Response, next) {} - const router = Router({ mergeParams: true }); router.post( '/api/v1/db/meta/tables/:tableId/forms', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FormCreateReq'), ncMetaAclMw(formViewCreate, 'formViewCreate') ); router.get( @@ -55,12 +42,7 @@ router.get( router.patch( '/api/v1/db/meta/forms/:formViewId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FormReq'), ncMetaAclMw(formViewUpdate, 'formViewUpdate') ); -router.delete( - '/api/v1/db/meta/forms/:formViewId', - metaApiMetrics, - ncMetaAclMw(formViewDelete, 'formViewDelete') -); + export default router; diff --git a/packages/nocodb/src/lib/controllers/galleryViewController.ts b/packages/nocodb/src/lib/controllers/galleryViewController.ts index 947ea6b65a..f54dc901cd 100644 --- a/packages/nocodb/src/lib/controllers/galleryViewController.ts +++ b/packages/nocodb/src/lib/controllers/galleryViewController.ts @@ -2,7 +2,6 @@ import { Request, Response, Router } from 'express'; import { GalleryType } from 'nocodb-sdk'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { galleryViewService } from '../services'; export async function galleryViewGet(req: Request, res: Response) { @@ -35,13 +34,11 @@ const router = Router({ mergeParams: true }); router.post( '/api/v1/db/meta/tables/:tableId/galleries', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/GalleryReq'), ncMetaAclMw(galleryViewCreate, 'galleryViewCreate') ); router.patch( '/api/v1/db/meta/galleries/:galleryViewId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/GalleryReq'), ncMetaAclMw(galleryViewUpdate, 'galleryViewUpdate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/gridViewColumnController.ts b/packages/nocodb/src/lib/controllers/gridViewColumnController.ts index 3b535cce9e..1ce0a41be7 100644 --- a/packages/nocodb/src/lib/controllers/gridViewColumnController.ts +++ b/packages/nocodb/src/lib/controllers/gridViewColumnController.ts @@ -1,7 +1,6 @@ import { Request, Response, Router } from 'express'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { gridViewColumnService } from '../services'; export async function columnList(req: Request, res: Response) { @@ -30,7 +29,6 @@ router.get( router.patch( '/api/v1/db/meta/grid-columns/:gridViewColumnId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/GridColumnReq'), ncMetaAclMw(gridColumnUpdate, 'gridColumnUpdate') ); export default router; diff --git a/packages/nocodb/src/lib/controllers/gridViewController.ts b/packages/nocodb/src/lib/controllers/gridViewController.ts index 2038717232..865ccecdb6 100644 --- a/packages/nocodb/src/lib/controllers/gridViewController.ts +++ b/packages/nocodb/src/lib/controllers/gridViewController.ts @@ -1,42 +1,29 @@ import { Request, Router } from 'express'; -// @ts-ignore -import Model from '../models/Model'; -import { Tele } from 'nc-help'; -// @ts-ignore -import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; -import { ViewTypes } from 'nocodb-sdk'; -// @ts-ignore -import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2'; -// @ts-ignore -import Project from '../models/Project'; -import View from '../models/View'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import GridView from '../models/GridView'; -import { getAjvValidatorMw } from '../meta/api/helpers'; +import { gridViewService } from '../services'; -// @ts-ignore -export async function gridViewCreate(req: Request, res) { - const view = await View.insert({ - ...req.body, - // todo: sanitize - fk_model_id: req.params.tableId, - type: ViewTypes.GRID, +export async function gridViewCreate(req: Request, res) { + const view = await gridViewService.gridViewCreate({ + grid: req.body, + tableId: req.params.tableId, }); - Tele.emit('evt', { evt_type: 'vtable:created', show_as: 'grid' }); res.json(view); } export async function gridViewUpdate(req, res) { - Tele.emit('evt', { evt_type: 'view:updated', type: 'grid' }); - res.json(await GridView.update(req.params.viewId, req.body)); + res.json( + await gridViewService.gridViewUpdate({ + viewId: req.params.viewId, + grid: req.body, + }) + ); } const router = Router({ mergeParams: true }); router.post( '/api/v1/db/meta/tables/:tableId/grids/', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/GridReq'), ncMetaAclMw(gridViewCreate, 'gridViewCreate') ); router.patch( diff --git a/packages/nocodb/src/lib/controllers/hookController.ts b/packages/nocodb/src/lib/controllers/hookController.ts index c17280a9e9..c75e2b82d1 100644 --- a/packages/nocodb/src/lib/controllers/hookController.ts +++ b/packages/nocodb/src/lib/controllers/hookController.ts @@ -4,7 +4,6 @@ import { HookListType, HookType } from 'nocodb-sdk'; import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { hookService } from '../services'; export async function hookList( @@ -74,13 +73,11 @@ router.get( router.post( '/api/v1/db/meta/tables/:tableId/hooks/test', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/HookTestReq'), ncMetaAclMw(hookTest, 'hookTest') ); router.post( '/api/v1/db/meta/tables/:tableId/hooks', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/HookReq'), ncMetaAclMw(hookCreate, 'hookCreate') ); router.delete( @@ -91,7 +88,6 @@ router.delete( router.patch( '/api/v1/db/meta/hooks/:hookId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/HookReq'), ncMetaAclMw(hookUpdate, 'hookUpdate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/hookFilterController.ts b/packages/nocodb/src/lib/controllers/hookFilterController.ts index 71e3977802..c83a754d05 100644 --- a/packages/nocodb/src/lib/controllers/hookFilterController.ts +++ b/packages/nocodb/src/lib/controllers/hookFilterController.ts @@ -2,7 +2,6 @@ import { Request, Response, Router } from 'express'; import { T } from 'nc-help'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { hookFilterService } from '../services'; export async function filterGet(req: Request, res: Response) { @@ -66,7 +65,6 @@ router.get( router.post( '/hooks/:hookId/filters/', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), ncMetaAclMw(filterCreate, 'filterCreate') ); router.get( @@ -77,7 +75,6 @@ router.get( router.patch( '/hooks/:hookId/filters/:filterId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), ncMetaAclMw(filterUpdate, 'filterUpdate') ); router.delete( diff --git a/packages/nocodb/src/lib/controllers/kanbanViewController.ts b/packages/nocodb/src/lib/controllers/kanbanViewController.ts index 7ea705e6bb..bf36b43b90 100644 --- a/packages/nocodb/src/lib/controllers/kanbanViewController.ts +++ b/packages/nocodb/src/lib/controllers/kanbanViewController.ts @@ -5,7 +5,8 @@ import KanbanView from '../models/KanbanView'; import { T } from 'nc-help'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; + +// todo: map to service export async function kanbanViewGet(req: Request, res: Response) { res.json(await KanbanView.get(req.params.kanbanViewId)); @@ -32,13 +33,11 @@ const router = Router({ mergeParams: true }); router.post( '/api/v1/db/meta/tables/:tableId/kanbans', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/KanbanReq'), ncMetaAclMw(kanbanViewCreate, 'kanbanViewCreate') ); router.patch( '/api/v1/db/meta/kanbans/:kanbanViewId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/KanbanUpdateReq'), ncMetaAclMw(kanbanViewUpdate, 'kanbanViewUpdate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/modelVisibilityController.ts b/packages/nocodb/src/lib/controllers/modelVisibilityController.ts index 3f298c0d65..6af9a71506 100644 --- a/packages/nocodb/src/lib/controllers/modelVisibilityController.ts +++ b/packages/nocodb/src/lib/controllers/modelVisibilityController.ts @@ -1,7 +1,6 @@ import { Router } from 'express'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { modelVisibilityService } from '../services'; async function xcVisibilityMetaSetAll(req, res) { @@ -30,7 +29,6 @@ router.get( router.post( '/api/v1/db/meta/projects/:projectId/visibility-rules', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/VisibilityRuleReq'), ncMetaAclMw(xcVisibilityMetaSetAll, 'modelVisibilitySet') ); export default router; diff --git a/packages/nocodb/src/lib/controllers/orgLicenseController.ts b/packages/nocodb/src/lib/controllers/orgLicenseController.ts index de6d839537..23e5948ba1 100644 --- a/packages/nocodb/src/lib/controllers/orgLicenseController.ts +++ b/packages/nocodb/src/lib/controllers/orgLicenseController.ts @@ -2,7 +2,6 @@ import { Router } from 'express'; import { OrgUserRoles } from 'nocodb-sdk'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { orgLicenseService } from '../services'; async function licenseGet(_req, res) { @@ -26,7 +25,6 @@ router.get( router.post( '/api/v1/license', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/LicenseReq'), ncMetaAclMw(licenseSet, 'licenseSet', { allowedRoles: [OrgUserRoles.SUPER_ADMIN], blockApiTokenAccess: true, diff --git a/packages/nocodb/src/lib/controllers/orgTokenController.ts b/packages/nocodb/src/lib/controllers/orgTokenController.ts index 1e32de65c7..293ccd8c38 100644 --- a/packages/nocodb/src/lib/controllers/orgTokenController.ts +++ b/packages/nocodb/src/lib/controllers/orgTokenController.ts @@ -1,56 +1,35 @@ import { Request, Response, Router } from 'express'; -import { OrgUserRoles } from 'nocodb-sdk'; -import ApiToken from '../models/ApiToken'; -import { Tele } from 'nc-help'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { NcError } from '../meta/helpers/catchError'; import getHandler from '../meta/helpers/getHandler'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; -import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; import { apiTokenListEE } from '../meta/api/ee/orgTokenApis'; -import { getAjvValidatorMw } from '../meta/api/helpers'; +import { orgTokenService } from '../services'; async function apiTokenList(req, res) { - const fk_user_id = req.user.id; - let includeUnmappedToken = false; - if (req['user'].roles.includes(OrgUserRoles.SUPER_ADMIN)) { - includeUnmappedToken = true; - } - res.json( - new PagedResponseImpl( - await ApiToken.listWithCreatedBy({ - ...req.query, - fk_user_id, - includeUnmappedToken, - }), - { - ...req.query, - count: await ApiToken.count({ - includeUnmappedToken, - fk_user_id, - }), - } - ) + await orgTokenService.apiTokenList({ + query: req.query, + user: req['user'], + }) ); } export async function apiTokenCreate(req: Request, res: Response) { - Tele.emit('evt', { evt_type: 'org:apiToken:created' }); - res.json(await ApiToken.insert({ ...req.body, fk_user_id: req['user'].id })); + res.json( + await orgTokenService.apiTokenCreate({ + apiToken: req.body, + user: req['user'], + }) + ); } export async function apiTokenDelete(req: Request, res: Response) { - const fk_user_id = req['user'].id; - const apiToken = await ApiToken.getByToken(req.params.token); - if ( - !req['user'].roles.includes(OrgUserRoles.SUPER_ADMIN) && - apiToken.fk_user_id !== fk_user_id - ) { - NcError.notFound('Token not found'); - } - Tele.emit('evt', { evt_type: 'org:apiToken:deleted' }); - res.json(await ApiToken.delete(req.params.token)); + res.json( + await orgTokenService.apiTokenDelete({ + token: req.params.token, + user: req['user'], + }) + ); } const router = Router({ mergeParams: true }); @@ -66,7 +45,6 @@ router.get( router.post( '/api/v1/tokens', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/ApiTokenReq'), ncMetaAclMw(apiTokenCreate, 'apiTokenCreate', { // allowedRoles: [OrgUserRoles.SUPER], blockApiTokenAccess: true, diff --git a/packages/nocodb/src/lib/controllers/orgUserController.ts b/packages/nocodb/src/lib/controllers/orgUserController.ts index c89e031124..e2e6df488a 100644 --- a/packages/nocodb/src/lib/controllers/orgUserController.ts +++ b/packages/nocodb/src/lib/controllers/orgUserController.ts @@ -2,7 +2,6 @@ import { Router } from 'express'; import { OrgUserRoles } from 'nocodb-sdk'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { orgUserService } from '../services'; async function userList(req, res) { @@ -87,7 +86,6 @@ router.get( router.patch( '/api/v1/users/:userId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/OrgUserReq'), ncMetaAclMw(userUpdate, 'userUpdate', { allowedRoles: [OrgUserRoles.SUPER_ADMIN], blockApiTokenAccess: true, @@ -104,7 +102,6 @@ router.delete( router.post( '/api/v1/users', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/OrgUserReq'), ncMetaAclMw(userAdd, 'userAdd', { allowedRoles: [OrgUserRoles.SUPER_ADMIN], blockApiTokenAccess: true, diff --git a/packages/nocodb/src/lib/controllers/pluginController.ts b/packages/nocodb/src/lib/controllers/pluginController.ts index 156fce97db..8d3e1b6943 100644 --- a/packages/nocodb/src/lib/controllers/pluginController.ts +++ b/packages/nocodb/src/lib/controllers/pluginController.ts @@ -3,7 +3,6 @@ import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; import { PluginType } from 'nocodb-sdk'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { pluginService } from '../services'; export async function pluginList(_req: Request, res: Response) { @@ -42,8 +41,6 @@ router.get( router.post( '/api/v1/db/meta/plugins/test', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/PluginTestReq'), - ncMetaAclMw(pluginTest, 'pluginTest') ); router.get( @@ -54,7 +51,6 @@ router.get( router.patch( '/api/v1/db/meta/plugins/:pluginId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/PluginReq'), ncMetaAclMw(pluginUpdate, 'pluginUpdate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/projectController.ts b/packages/nocodb/src/lib/controllers/projectController.ts index 1be862a7e4..18d761efb1 100644 --- a/packages/nocodb/src/lib/controllers/projectController.ts +++ b/packages/nocodb/src/lib/controllers/projectController.ts @@ -11,7 +11,6 @@ import ProjectUser from '../models/ProjectUser'; import Noco from '../Noco'; import isDocker from 'is-docker'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import Filter from '../models/Filter'; import { projectService } from '../services'; @@ -155,7 +154,6 @@ export default (router) => { router.post( '/api/v1/db/meta/projects', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/ProjectReq'), ncMetaAclMw(projectCreate, 'projectCreate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/projectUserController.ts b/packages/nocodb/src/lib/controllers/projectUserController.ts index 6be2ff985c..f2bace15a4 100644 --- a/packages/nocodb/src/lib/controllers/projectUserController.ts +++ b/packages/nocodb/src/lib/controllers/projectUserController.ts @@ -1,305 +1,59 @@ -import { OrgUserRoles } from 'nocodb-sdk'; -import { Tele } from 'nc-help'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { Router } from 'express'; -import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; -import ProjectUser from '../models/ProjectUser'; -import validator from 'validator'; -import { NcError } from '../meta/helpers/catchError'; -import { v4 as uuidv4 } from 'uuid'; -import User from '../models/User'; -import Audit from '../models/Audit'; -import NocoCache from '../cache/NocoCache'; -import { CacheGetType, CacheScope, MetaTable } from '../utils/globals'; -import * as ejs from 'ejs'; -import NcPluginMgrv2 from '../meta/helpers/NcPluginMgrv2'; -import Noco from '../Noco'; -import { PluginCategory } from 'nocodb-sdk'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; -import { randomTokenString } from '../meta/helpers/stringHelpers'; -import { getAjvValidatorMw } from '../meta/api/helpers'; +import { projectUserService } from '../services'; async function userList(req, res) { res.json({ - users: new PagedResponseImpl( - await ProjectUser.getUsersList({ - ...req.query, - project_id: req.params.projectId, - }), - { - ...req.query, - count: await ProjectUser.getUsersCount(req.query), - } - ), + users: await projectUserService.userList({ + projectId: req.params.projectId, + query: req.query, + }), }); } -async function userInvite(req, res, next): Promise { - const emails = (req.body.email || '') - .toLowerCase() - .split(/\s*,\s*/) - .map((v) => v.trim()); - - // check for invalid emails - const invalidEmails = emails.filter((v) => !validator.isEmail(v)); - if (!emails.length) { - return NcError.badRequest('Invalid email address'); - } - if (invalidEmails.length) { - NcError.badRequest('Invalid email address : ' + invalidEmails.join(', ')); - } - - const invite_token = uuidv4(); - const error = []; - - for (const email of emails) { - // add user to project if user already exist - const user = await User.getByEmail(email); - - if (user) { - // check if this user has been added to this project - const projectUser = await ProjectUser.get(req.params.projectId, user.id); - if (projectUser) { - NcError.badRequest( - `${user.email} with role ${projectUser.roles} already exists in this project` - ); - } - - await ProjectUser.insert({ - project_id: req.params.projectId, - fk_user_id: user.id, - roles: req.body.roles || 'editor', - }); - - const cachedUser = await NocoCache.get( - `${CacheScope.USER}:${email}___${req.params.projectId}`, - CacheGetType.TYPE_OBJECT - ); - - if (cachedUser) { - cachedUser.roles = req.body.roles || 'editor'; - await NocoCache.set( - `${CacheScope.USER}:${email}___${req.params.projectId}`, - cachedUser - ); - } - - await Audit.insert({ - project_id: req.params.projectId, - op_type: 'AUTHENTICATION', - op_sub_type: 'INVITE', - user: req.user.email, - description: `invited ${email} to ${req.params.projectId} project `, - ip: req.clientIp, - }); - } else { - try { - // create new user with invite token - const { id } = await User.insert({ - invite_token, - invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), - email, - roles: OrgUserRoles.VIEWER, - token_version: randomTokenString(), - }); - - // add user to project - await ProjectUser.insert({ - project_id: req.params.projectId, - fk_user_id: id, - roles: req.body.roles, - }); - - const count = await User.count(); - Tele.emit('evt', { evt_type: 'project:invite', count }); - - await Audit.insert({ - project_id: req.params.projectId, - op_type: 'AUTHENTICATION', - op_sub_type: 'INVITE', - user: req.user.email, - description: `invited ${email} to ${req.params.projectId} project `, - ip: req.clientIp, - }); - // in case of single user check for smtp failure - // and send back token if failed - if ( - emails.length === 1 && - !(await sendInviteEmail(email, invite_token, req)) - ) { - return res.json({ invite_token, email }); - } else { - sendInviteEmail(email, invite_token, req); - } - } catch (e) { - console.log(e); - if (emails.length === 1) { - return next(e); - } else { - error.push({ email, error: e.message }); - } - } - } - } - - if (emails.length === 1) { - res.json({ - msg: 'success', - }); - } else { - return res.json({ invite_token, emails, error }); - } +async function userInvite(req, res): Promise { + res.json( + await projectUserService.userInvite({ + projectId: req.params.projectId, + projectUser: req.body, + req, + }) + ); } // @ts-ignore async function projectUserUpdate(req, res, next): Promise { - if (!req?.body?.project_id) { - return next(new Error('Missing project id in request body.')); - } - - if ( - req.session?.passport?.user?.roles?.owner && - req.session?.passport?.user?.id === req.params.userId && - req.body.roles.indexOf('owner') === -1 - ) { - NcError.badRequest("Super admin can't remove Super role themselves"); - } - try { - const user = await User.get(req.params.userId); - - if (!user) { - NcError.badRequest(`User with id '${req.params.userId}' doesn't exist`); - } - - // todo: handle roles which contains super - if ( - !req.session?.passport?.user?.roles?.owner && - req.body.roles.indexOf('owner') > -1 - ) { - NcError.forbidden('Insufficient privilege to add super admin role.'); - } - - await ProjectUser.update( - req.params.projectId, - req.params.userId, - req.body.roles - ); - - await Audit.insert({ - op_type: 'AUTHENTICATION', - op_sub_type: 'ROLES_MANAGEMENT', - user: req.user.email, - description: `updated roles for ${user.email} with ${req.body.roles} `, - ip: req.clientIp, - }); - - res.json({ - msg: 'User details updated successfully', - }); - } catch (e) { - next(e); - } + res.json( + await projectUserService.projectUserUpdate({ + projectUser: req.body, + projectId: req.params.projectId, + userId: req.params.userId, + req, + }) + ); } async function projectUserDelete(req, res): Promise { - const project_id = req.params.projectId; - - if (req.session?.passport?.user?.id === req.params.userId) { - NcError.badRequest("Admin can't delete themselves!"); - } - - if (!req.session?.passport?.user?.roles?.owner) { - const user = await User.get(req.params.userId); - if (user.roles?.split(',').includes('super')) - NcError.forbidden('Insufficient privilege to delete a super admin user.'); - - const projectUser = await ProjectUser.get(project_id, req.params.userId); - if (projectUser?.roles?.split(',').includes('super')) - NcError.forbidden('Insufficient privilege to delete a owner user.'); - } - - await ProjectUser.delete(project_id, req.params.userId); + await projectUserService.projectUserDelete({ + projectId: req.params.projectId, + userId: req.params.userId, + req, + }); res.json({ msg: 'success', }); } async function projectUserInviteResend(req, res): Promise { - const user = await User.get(req.params.userId); - - if (!user) { - NcError.badRequest(`User with id '${req.params.userId}' not found`); - } - - req.body.roles = user.roles; - const invite_token = uuidv4(); - - await User.update(user.id, { - invite_token, - invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), - }); - - const pluginData = await Noco.ncMeta.metaGet2(null, null, MetaTable.PLUGIN, { - category: PluginCategory.EMAIL, - active: true, - }); - - if (!pluginData) { - NcError.badRequest( - `No Email Plugin is found. Please go to App Store to configure first or copy the invitation URL to users instead.` - ); - } - - await sendInviteEmail(user.email, invite_token, req); - - await Audit.insert({ - op_type: 'AUTHENTICATION', - op_sub_type: 'RESEND_INVITE', - user: user.email, - description: `resent a invite to ${user.email} `, - ip: req.clientIp, - project_id: req.params.projectId, - }); - - res.json({ msg: 'success' }); -} - -export async function sendInviteEmail( - email: string, - token: string, - req: any -): Promise { - try { - const template = (await import('./userController/ui/emailTemplates/invite')) - .default; - - const emailAdapter = await NcPluginMgrv2.emailAdapter(); - - if (emailAdapter) { - await emailAdapter.mailSend({ - to: email, - subject: 'Verify email', - html: ejs.render(template, { - signupLink: `${req.ncSiteUrl}${ - Noco.getConfig()?.dashboardPath - }#/signup/${token}`, - projectName: req.body?.projectName, - roles: (req.body?.roles || '') - .split(',') - .map((r) => r.replace(/^./, (m) => m.toUpperCase())) - .join(', '), - adminEmail: req.session?.passport?.user?.email, - }), - }); - return true; - } - } catch (e) { - console.log( - 'Warning : `mailSend` failed, Please configure emailClient configuration.', - e.message - ); - throw e; - } + res.json( + await projectUserService.projectUserInviteResend({ + projectId: req.params.projectId, + userId: req.params.userId, + projectUser: req.body, + req, + }) + ); } const router = Router({ mergeParams: true }); @@ -311,13 +65,11 @@ router.get( router.post( '/api/v1/db/meta/projects/:projectId/users', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/ProjectUserReq'), ncMetaAclMw(userInvite, 'userInvite') ); router.patch( '/api/v1/db/meta/projects/:projectId/users/:userId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/ProjectUserReq'), ncMetaAclMw(projectUserUpdate, 'projectUserUpdate') ); router.delete( diff --git a/packages/nocodb/src/lib/controllers/sharedBaseController.ts b/packages/nocodb/src/lib/controllers/sharedBaseController.ts index a9bfcfbb48..1fd9fb27cd 100644 --- a/packages/nocodb/src/lib/controllers/sharedBaseController.ts +++ b/packages/nocodb/src/lib/controllers/sharedBaseController.ts @@ -1,93 +1,44 @@ import { Router } from 'express'; -import { Tele } from 'nc-help'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; -import { v4 as uuidv4 } from 'uuid'; -import Project from '../models/Project'; -import { NcError } from '../meta/helpers/catchError'; -import { getAjvValidatorMw } from '../meta/api/helpers'; -// todo: load from config -const config = { - dashboardPath: '/nc', -}; +import { sharedBaseService } from '../services'; async function createSharedBaseLink(req, res): Promise { - const project = await Project.get(req.params.projectId); - - let roles = req.body?.roles; - if (!roles || (roles !== 'editor' && roles !== 'viewer')) { - roles = 'viewer'; - } - - if (!project) { - NcError.badRequest('Invalid project id'); - } - const data: any = { - uuid: uuidv4(), + const sharedBase = await sharedBaseService.createSharedBaseLink({ + projectId: req.params.projectId, + roles: req.body?.roles, password: req.body?.password, - roles, - }; - - await Project.update(project.id, data); + siteUrl: req.ncSiteUrl, + }); - data.url = `${req.ncSiteUrl}${config.dashboardPath}#/nc/base/${data.uuid}`; - delete data.password; - Tele.emit('evt', { evt_type: 'sharedBase:generated-link' }); - res.json(data); + res.json(sharedBase); } -async function updateSharedBaseLink(req, res): Promise { - const project = await Project.get(req.params.projectId); - - let roles = req.body?.roles; - if (!roles || (roles !== 'editor' && roles !== 'viewer')) { - roles = 'viewer'; - } - if (!project) { - NcError.badRequest('Invalid project id'); - } - const data: any = { - uuid: project.uuid || uuidv4(), +async function updateSharedBaseLink(req, res): Promise { + const sharedBase = await sharedBaseService.updateSharedBaseLink({ + projectId: req.params.projectId, + roles: req.body?.roles, password: req.body?.password, - roles, - }; - - await Project.update(project.id, data); + siteUrl: req.ncSiteUrl, + }); - data.url = `${req.ncSiteUrl}${config.dashboardPath}#/nc/base/${data.uuid}`; - delete data.password; - Tele.emit('evt', { evt_type: 'sharedBase:generated-link' }); - res.json(data); + res.json(sharedBase); } async function disableSharedBaseLink(req, res): Promise { - const project = await Project.get(req.params.projectId); + const sharedBase = await sharedBaseService.disableSharedBaseLink({ + projectId: req.params.projectId, + }); - if (!project) { - NcError.badRequest('Invalid project id'); - } - const data: any = { - uuid: null, - }; - - await Project.update(project.id, data); - Tele.emit('evt', { evt_type: 'sharedBase:disable-link' }); - res.json({ uuid: null }); + res.json(sharedBase); } async function getSharedBaseLink(req, res): Promise { - const project = await Project.get(req.params.projectId); - - if (!project) { - NcError.badRequest('Invalid project id'); - } - const data: any = { - uuid: project.uuid, - roles: project.roles, - }; - if (data.uuid) - data.url = `${req.ncSiteUrl}${config.dashboardPath}#/nc/base/${data.shared_base_id}`; + const sharedBase = await sharedBaseService.getSharedBaseLink({ + projectId: req.params.projectId, + siteUrl: req.ncSiteUrl, + }); - res.json(data); + res.json(sharedBase); } const router = Router({ mergeParams: true }); @@ -97,12 +48,10 @@ router.get( ); router.post( '/api/v1/db/meta/projects/:projectId/shared', - getAjvValidatorMw('swagger.json#/components/schemas/SharedBaseReq'), ncMetaAclMw(createSharedBaseLink, 'createSharedBaseLink') ); router.patch( '/api/v1/db/meta/projects/:projectId/shared', - getAjvValidatorMw('swagger.json#/components/schemas/SharedBaseReq'), ncMetaAclMw(updateSharedBaseLink, 'updateSharedBaseLink') ); router.delete( diff --git a/packages/nocodb/src/lib/controllers/sortController.ts b/packages/nocodb/src/lib/controllers/sortController.ts index 397b605f57..332e31e419 100644 --- a/packages/nocodb/src/lib/controllers/sortController.ts +++ b/packages/nocodb/src/lib/controllers/sortController.ts @@ -1,5 +1,4 @@ import { Request, Response, Router } from 'express'; -import { getAjvValidatorMw } from '../meta/api/helpers'; import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; import { SortListType, SortReqType } from 'nocodb-sdk'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; @@ -59,7 +58,6 @@ router.get( router.post( '/api/v1/db/meta/views/:viewId/sorts/', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/SortReq'), ncMetaAclMw(sortCreate, 'sortCreate') ); @@ -72,7 +70,6 @@ router.get( router.patch( '/api/v1/db/meta/sorts/:sortId', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/SortReq'), ncMetaAclMw(sortUpdate, 'sortUpdate') ); router.delete( diff --git a/packages/nocodb/src/lib/controllers/tableController.ts b/packages/nocodb/src/lib/controllers/tableController.ts index d8987ef3cb..fdf6528111 100644 --- a/packages/nocodb/src/lib/controllers/tableController.ts +++ b/packages/nocodb/src/lib/controllers/tableController.ts @@ -1,11 +1,17 @@ import { Request, Response, Router } from 'express'; +import DOMPurify from 'isomorphic-dompurify'; import { TableListType, TableReqType, TableType } from 'nocodb-sdk'; -import { getAjvValidatorMw } from '../meta/api/helpers'; +import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2'; import { metaApiMetrics } from '../meta/helpers/apiMetrics'; +import { NcError } from '../meta/helpers/catchError'; +import getTableNameAlias from '../meta/helpers/getTableName'; import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; - +import Model from '../models/Model'; +import Project from '../models/Project'; +import { T } from 'nc-help'; import { tableService } from '../services'; +import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; export async function tableList(req: Request, res: Response) { res.json( @@ -58,15 +64,108 @@ export async function tableReorder(req: Request, res: Response) { ); } +// todo: move to table service +export async function tableUpdate(req: Request, res) { + const model = await Model.get(req.params.tableId); + const project = await Project.getWithInfo( + req.body.project_id || (req as any).ncProjectId + ); + const base = project.bases.find((b) => b.id === model.base_id); -export async function tableUpdate(req: Request, res) { - tableService.tableUpdate({ - tableId: req.params.tableId, - table: req.body, - }) + if (model.project_id !== project.id) { + NcError.badRequest('Model does not belong to project'); + } + + // if meta present update meta and return + // todo: allow user to update meta and other prop in single api call + if ('meta' in req.body) { + await Model.updateMeta(req.params.tableId, req.body.meta); + + return res.json({ msg: 'success' }); + } + + if (!req.body.table_name) { + NcError.badRequest( + 'Missing table name `table_name` property in request body' + ); + } + + if (base.is_meta && project.prefix) { + if (!req.body.table_name.startsWith(project.prefix)) { + req.body.table_name = `${project.prefix}${req.body.table_name}`; + } + } + + req.body.table_name = DOMPurify.sanitize(req.body.table_name); + + // validate table name + if (/^\s+|\s+$/.test(req.body.table_name)) { + NcError.badRequest( + 'Leading or trailing whitespace not allowed in table names' + ); + } + + if ( + !(await Model.checkTitleAvailable({ + table_name: req.body.table_name, + project_id: project.id, + base_id: base.id, + })) + ) { + NcError.badRequest('Duplicate table name'); + } + + if (!req.body.title) { + req.body.title = getTableNameAlias( + req.body.table_name, + project.prefix, + base + ); + } + + if ( + !(await Model.checkAliasAvailable({ + title: req.body.title, + project_id: project.id, + base_id: base.id, + })) + ) { + NcError.badRequest('Duplicate table alias'); + } + + const sqlMgr = await ProjectMgrv2.getSqlMgr(project); + const sqlClient = await NcConnectionMgrv2.getSqlClient(base); + + let tableNameLengthLimit = 255; + const sqlClientType = sqlClient.knex.clientType(); + if (sqlClientType === 'mysql2' || sqlClientType === 'mysql') { + tableNameLengthLimit = 64; + } else if (sqlClientType === 'pg') { + tableNameLengthLimit = 63; + } else if (sqlClientType === 'mssql') { + tableNameLengthLimit = 128; + } + + if (req.body.table_name.length > tableNameLengthLimit) { + NcError.badRequest(`Table name exceeds ${tableNameLengthLimit} characters`); + } + + await Model.updateAliasAndTableName( + req.params.tableId, + req.body.title, + req.body.table_name + ); + + await sqlMgr.sqlOpPlus(base, 'tableRename', { + ...req.body, + tn: req.body.table_name, + tn_old: model.table_name, + }); + + T.emit('evt', { evt_type: 'table:updated' }); - res.json({ msg: 'success' }) + res.json({ msg: 'success' }); } const router = Router({ mergeParams: true }); @@ -83,13 +182,11 @@ router.get( router.post( '/api/v1/db/meta/projects/:projectId/tables', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/TableReq'), ncMetaAclMw(tableCreate, 'tableCreate') ); router.post( '/api/v1/db/meta/projects/:projectId/:baseId/tables', metaApiMetrics, - getAjvValidatorMw('swagger.json#/components/schemas/TableReq'), ncMetaAclMw(tableCreate, 'tableCreate') ); router.get( diff --git a/packages/nocodb/src/lib/controllers/userController/index.ts b/packages/nocodb/src/lib/controllers/userController/index.ts index 70d17b40ef..8843b2e5ea 100644 --- a/packages/nocodb/src/lib/controllers/userController/index.ts +++ b/packages/nocodb/src/lib/controllers/userController/index.ts @@ -340,11 +340,7 @@ const mapRoutes = (router) => { catchError(signin) ); router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me)); - router.post( - '/auth/password/forgot', - getAjvValidatorMw('swagger.json#/components/schemas/ForgotPasswordReq'), - catchError(passwordForgot) - ); + router.post('/auth/password/forgot', catchError(passwordForgot)); router.post('/auth/token/validate/:tokenId', catchError(tokenValidate)); router.post( '/auth/password/reset/:tokenId', @@ -354,7 +350,6 @@ const mapRoutes = (router) => { router.post('/auth/email/validate/:tokenId', catchError(emailVerification)); router.post( '/user/password/change', - getAjvValidatorMw('swagger.json#/components/schemas/PasswordChangeReq'), ncMetaAclMw(passwordChange, 'passwordChange') ); router.post('/auth/token/refresh', catchError(refreshToken)); @@ -387,18 +382,13 @@ const mapRoutes = (router) => { extractProjectIdAndAuthenticate, catchError(me) ); - router.post( - '/api/v1/db/auth/password/forgot', - getAjvValidatorMw('swagger.json#/components/schemas/ForgotPasswordReq'), - catchError(passwordForgot) - ); + router.post('/api/v1/db/auth/password/forgot', catchError(passwordForgot)); router.post( '/api/v1/db/auth/token/validate/:tokenId', catchError(tokenValidate) ); router.post( '/api/v1/db/auth/password/reset/:tokenId', - getAjvValidatorMw('swagger.json#/components/schemas/PasswordResetReq'), catchError(passwordReset) ); router.post( @@ -407,7 +397,6 @@ const mapRoutes = (router) => { ); router.post( '/api/v1/db/auth/password/change', - getAjvValidatorMw('swagger.json#/components/schemas/PasswordChangeReq'), ncMetaAclMw(passwordChange, 'passwordChange') ); router.post('/api/v1/db/auth/token/refresh', catchError(refreshToken)); @@ -432,11 +421,7 @@ const mapRoutes = (router) => { extractProjectIdAndAuthenticate, catchError(me) ); - router.post( - '/api/v1/auth/password/forgot', - getAjvValidatorMw('swagger.json#/components/schemas/ForgotPasswordReq'), - catchError(passwordForgot) - ); + router.post('/api/v1/auth/password/forgot', catchError(passwordForgot)); router.post( '/api/v1/auth/token/validate/:tokenId', catchError(tokenValidate) @@ -451,7 +436,6 @@ const mapRoutes = (router) => { ); router.post( '/api/v1/auth/password/change', - getAjvValidatorMw('swagger.json#/components/schemas/PasswordChangeReq'), ncMetaAclMw(passwordChange, 'passwordChange') ); router.post('/api/v1/auth/token/refresh', catchError(refreshToken)); diff --git a/packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts b/packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts index 25a077a52d..e1633ccc89 100644 --- a/packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts +++ b/packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts @@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express'; import Ajv, { ErrorObject } from 'ajv'; // @ts-ignore import swagger from '../../../../schema/swagger.json'; -import { NcError } from '../../helpers/catchError' +import { NcError } from '../../helpers/catchError'; export function parseHrtimeToSeconds(hrtime) { const seconds = (hrtime[0] + hrtime[1] / 1e6).toFixed(3); @@ -38,7 +38,7 @@ export const getAjvValidatorMw = (schema) => { }; // a function to validate the payload against the schema -export const ajvValidator = (schema, payload) => { +export const validatePayload = (schema, payload) => { // Validate the request body against the schema const valid = ajv.validate( typeof schema === 'string' ? { $ref: schema } : schema, @@ -55,4 +55,4 @@ export const ajvValidator = (schema, payload) => { errors, }); } -} +}; diff --git a/packages/nocodb/src/lib/models/Hook.ts b/packages/nocodb/src/lib/models/Hook.ts index 1761dc8c12..ea0db07985 100644 --- a/packages/nocodb/src/lib/models/Hook.ts +++ b/packages/nocodb/src/lib/models/Hook.ts @@ -1,42 +1,42 @@ -import { BoolType, HookReqType, HookType } from 'nocodb-sdk' +import { BoolType, HookReqType, HookType } from 'nocodb-sdk'; import { CacheDelDirection, CacheGetType, CacheScope, MetaTable, -} from '../utils/globals' -import Noco from '../Noco' -import Model from './Model' -import NocoCache from '../cache/NocoCache' -import Filter from './Filter' -import HookFilter from './HookFilter' -import { extractProps } from '../meta/helpers/extractProps' +} from '../utils/globals'; +import Noco from '../Noco'; +import Model from './Model'; +import NocoCache from '../cache/NocoCache'; +import Filter from './Filter'; +import HookFilter from './HookFilter'; +import { extractProps } from '../meta/helpers/extractProps'; export default class Hook implements HookType { - id?: string - fk_model_id?: string - title?: string - description?: string - env?: string - type?: string - event?: 'after' | 'before' - operation?: 'insert' | 'delete' | 'update' - async?: BoolType - payload?: string - url?: string - headers?: string - condition?: BoolType - notification?: string - retries?: number - retry_interval?: number - timeout?: number - active?: BoolType + id?: string; + fk_model_id?: string; + title?: string; + description?: string; + env?: string; + type?: string; + event?: 'after' | 'before'; + operation?: 'insert' | 'delete' | 'update'; + async?: BoolType; + payload?: string; + url?: string; + headers?: string; + condition?: BoolType; + notification?: string; + retries?: number; + retry_interval?: number; + timeout?: number; + active?: BoolType; - project_id?: string - base_id?: string + project_id?: string; + base_id?: string; constructor(hook: Partial) { - Object.assign(this, hook) + Object.assign(this, hook); } public static async get(hookId: string, ncMeta = Noco.ncMeta) { @@ -44,17 +44,17 @@ export default class Hook implements HookType { hookId && (await NocoCache.get( `${CacheScope.HOOK}:${hookId}`, - CacheGetType.TYPE_OBJECT, - )) + CacheGetType.TYPE_OBJECT + )); if (!hook) { - hook = await ncMeta.metaGet2(null, null, MetaTable.HOOKS, hookId) - await NocoCache.set(`${CacheScope.HOOK}:${hookId}`, hook) + hook = await ncMeta.metaGet2(null, null, MetaTable.HOOKS, hookId); + await NocoCache.set(`${CacheScope.HOOK}:${hookId}`, hook); } - return hook && new Hook(hook) + return hook && new Hook(hook); } public async getFilters(ncMeta = Noco.ncMeta) { - return await Filter.rootFilterListByHook({ hookId: this.id }, ncMeta) + return await Filter.rootFilterListByHook({ hookId: this.id }, ncMeta); } // public static async insert(hook: Partial) { @@ -81,9 +81,9 @@ export default class Hook implements HookType { event?: 'after' | 'before'; operation?: 'insert' | 'delete' | 'update'; }, - ncMeta = Noco.ncMeta, + ncMeta = Noco.ncMeta ) { - let hooks = await NocoCache.getList(CacheScope.HOOK, [param.fk_model_id]) + let hooks = await NocoCache.getList(CacheScope.HOOK, [param.fk_model_id]); if (!hooks.length) { hooks = await ncMeta.metaList(null, null, MetaTable.HOOKS, { condition: { @@ -96,29 +96,31 @@ export default class Hook implements HookType { orderBy: { created_at: 'asc', }, - }) - await NocoCache.setList(CacheScope.HOOK, [param.fk_model_id], hooks) + }); + await NocoCache.setList(CacheScope.HOOK, [param.fk_model_id], hooks); } // filter event & operation if (param.event) { hooks = hooks.filter( - (h) => h.event?.toLowerCase() === param.event?.toLowerCase(), - ) + (h) => h.event?.toLowerCase() === param.event?.toLowerCase() + ); } if (param.operation) { hooks = hooks.filter( - (h) => h.operation?.toLowerCase() === param.operation?.toLowerCase(), - ) + (h) => h.operation?.toLowerCase() === param.operation?.toLowerCase() + ); } - return hooks?.map((h) => new Hook(h)) + return hooks?.map((h) => new Hook(h)); } public static async insert( - hook: Partial, - ncMeta = Noco.ncMeta, + hook: Partial< + Hook & { + created_at?; + updated_at?; + } + >, + ncMeta = Noco.ncMeta ) { const insertObj = extractProps(hook, [ 'fk_model_id', @@ -140,49 +142,49 @@ export default class Hook implements HookType { 'base_id', 'created_at', 'updated_at', - ]) + ]); if (insertObj.event) { - insertObj.event = insertObj.event.toLowerCase() as 'after' | 'before' + insertObj.event = insertObj.event.toLowerCase() as 'after' | 'before'; } if (insertObj.operation) { insertObj.operation = insertObj.operation.toLowerCase() as | 'insert' | 'delete' - | 'update' + | 'update'; } if (insertObj.notification && typeof insertObj.notification === 'object') { - insertObj.notification = JSON.stringify(insertObj.notification) + insertObj.notification = JSON.stringify(insertObj.notification); } if (!(hook.project_id && hook.base_id)) { - const model = await Model.getByIdOrName({ id: hook.fk_model_id }, ncMeta) - insertObj.project_id = model.project_id - insertObj.base_id = model.base_id + const model = await Model.getByIdOrName({ id: hook.fk_model_id }, ncMeta); + insertObj.project_id = model.project_id; + insertObj.base_id = model.base_id; } const { id } = await ncMeta.metaInsert2( null, null, MetaTable.HOOKS, - insertObj, - ) + insertObj + ); await NocoCache.appendToList( CacheScope.HOOK, [hook.fk_model_id], - `${CacheScope.HOOK}:${id}`, - ) + `${CacheScope.HOOK}:${id}` + ); - return this.get(id, ncMeta) + return this.get(id, ncMeta); } public static async update( hookId: string, hook: Partial, - ncMeta = Noco.ncMeta, + ncMeta = Noco.ncMeta ) { const updateObj = extractProps(hook, [ 'title', @@ -201,38 +203,38 @@ export default class Hook implements HookType { 'retry_interval', 'timeout', 'active', - ]) + ]); if (updateObj.event) { - updateObj.event = updateObj.event.toLowerCase() as 'after' | 'before' + updateObj.event = updateObj.event.toLowerCase() as 'after' | 'before'; } if (updateObj.operation) { updateObj.operation = updateObj.operation.toLowerCase() as | 'insert' | 'delete' - | 'update' + | 'update'; } if (updateObj.notification && typeof updateObj.notification === 'object') { - updateObj.notification = JSON.stringify(updateObj.notification) + updateObj.notification = JSON.stringify(updateObj.notification); } // get existing cache - const key = `${CacheScope.HOOK}:${hookId}` - let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT) + const key = `${CacheScope.HOOK}:${hookId}`; + let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT); if (o) { // update data - o = { ...o, ...updateObj } + o = { ...o, ...updateObj }; // replace notification - o.notification = updateObj.notification + o.notification = updateObj.notification; // set cache - await NocoCache.set(key, o) + await NocoCache.set(key, o); } // set meta - await ncMeta.metaUpdate(null, null, MetaTable.HOOKS, updateObj, hookId) + await ncMeta.metaUpdate(null, null, MetaTable.HOOKS, updateObj, hookId); - return this.get(hookId, ncMeta) + return this.get(hookId, ncMeta); } static async delete(hookId: any, ncMeta = Noco.ncMeta) { @@ -243,22 +245,22 @@ export default class Hook implements HookType { MetaTable.FILTER_EXP, { condition: { fk_hook_id: hookId }, - }, - ) + } + ); for (const filter of filterList) { await NocoCache.deepDel( CacheScope.FILTER_EXP, `${CacheScope.FILTER_EXP}:${filter.id}`, - CacheDelDirection.CHILD_TO_PARENT, - ) - await HookFilter.delete(filter.id) + CacheDelDirection.CHILD_TO_PARENT + ); + await HookFilter.delete(filter.id); } // Delete Hook await NocoCache.deepDel( CacheScope.HOOK, `${CacheScope.HOOK}:${hookId}`, - CacheDelDirection.CHILD_TO_PARENT, - ) - return await ncMeta.metaDelete(null, null, MetaTable.HOOKS, hookId) + CacheDelDirection.CHILD_TO_PARENT + ); + return await ncMeta.metaDelete(null, null, MetaTable.HOOKS, hookId); } } diff --git a/packages/nocodb/src/lib/services/apiTokenService.ts b/packages/nocodb/src/lib/services/apiTokenService.ts index af94b7936b..329bcb305c 100644 --- a/packages/nocodb/src/lib/services/apiTokenService.ts +++ b/packages/nocodb/src/lib/services/apiTokenService.ts @@ -1,5 +1,6 @@ import { ApiTokenReqType, OrgUserRoles } from 'nocodb-sdk'; import { T } from 'nc-help'; +import { validatePayload } from '../meta/api/helpers'; import { NcError } from '../meta/helpers/catchError'; import ApiToken from '../models/ApiToken'; import User from '../models/User'; @@ -11,6 +12,11 @@ export async function apiTokenCreate(param: { userId: string; tokenBody: ApiTokenReqType; }) { + await validatePayload( + 'swagger.json#/components/schemas/ApiTokenReq', + param.tokenBody + ); + T.emit('evt', { evt_type: 'apiToken:created' }); return ApiToken.insert({ ...param.tokenBody, fk_user_id: param.userId }); } diff --git a/packages/nocodb/src/lib/services/auditService.ts b/packages/nocodb/src/lib/services/auditService.ts index 211c8d4f2d..f5f2724ba3 100644 --- a/packages/nocodb/src/lib/services/auditService.ts +++ b/packages/nocodb/src/lib/services/auditService.ts @@ -2,6 +2,7 @@ import { AuditRowUpdatePayloadType, CommentRowPayloadType, } from 'nocodb-sdk/build/main/lib/CustomAPI'; +import { validatePayload } from '../meta/api/helpers'; import Audit from '../models/Audit'; import { AuditOperationSubTypes, AuditOperationTypes } from 'nocodb-sdk'; import Model from '../models/Model'; @@ -14,6 +15,11 @@ export async function commentRow(param: { body: CommentRowPayloadType; user: any; }) { + await validatePayload( + 'swagger.json#/components/schemas/CommentReq', + param.body + ); + return await Audit.insert({ ...param.body, user: param.user?.email, @@ -25,6 +31,11 @@ export async function auditRowUpdate(param: { rowId: string; body: AuditRowUpdatePayloadType; }) { + await validatePayload( + 'swagger.json#/components/schemas/AuditRowUpdateReq', + param.body + ); + const model = await Model.getByIdOrName({ id: param.body.fk_model_id }); return await Audit.insert({ fk_model_id: param.body.fk_model_id, diff --git a/packages/nocodb/src/lib/services/baseService.ts b/packages/nocodb/src/lib/services/baseService.ts index dbb5001bf0..21adb99ac8 100644 --- a/packages/nocodb/src/lib/services/baseService.ts +++ b/packages/nocodb/src/lib/services/baseService.ts @@ -3,7 +3,7 @@ import { BaseReqType } from 'nocodb-sdk'; import { syncBaseMigration } from '../meta/helpers/syncMigration'; import Base from '../models/Base'; import { T } from 'nc-help'; -import { populateMeta } from '../meta/api/helpers'; +import { populateMeta, validatePayload } from '../meta/api/helpers'; export async function baseGetWithConfig(param: { baseId: any }) { const base = await Base.get(param.baseId); @@ -18,6 +18,8 @@ export async function baseUpdate(param: { base: BaseReqType; projectId: string; }) { + validatePayload('swagger.json#/components/schemas/BaseReq', param.base); + const baseBody = param.base; const project = await Project.getWithInfo(param.projectId); const base = await Base.updateBase(param.baseId, { @@ -53,6 +55,8 @@ export async function baseCreate(param: { projectId: string; base: BaseReqType; }) { + validatePayload('swagger.json#/components/schemas/BaseReq', param.base); + // type | base | projectId const baseBody = param.base; const project = await Project.getWithInfo(param.projectId); diff --git a/packages/nocodb/src/lib/services/columnService.ts b/packages/nocodb/src/lib/services/columnService.ts index f2c741f546..185186d0a2 100644 --- a/packages/nocodb/src/lib/services/columnService.ts +++ b/packages/nocodb/src/lib/services/columnService.ts @@ -18,6 +18,7 @@ import { generateFkName, randomID, validateLookupPayload, + validatePayload, validateRequiredField, validateRollupPayload, } from '../meta/api/helpers'; @@ -835,6 +836,8 @@ export async function columnAdd(param: { tableId: string; column: ColumnReqType; }) { + validatePayload('swagger.json#/components/schemas/ColumnReq', param.column); + const table = await Model.getWithInfo({ id: param.tableId, }); diff --git a/packages/nocodb/src/lib/services/dataService/index.ts b/packages/nocodb/src/lib/services/dataService/index.ts index 1f1d09b20a..c125530352 100644 --- a/packages/nocodb/src/lib/services/dataService/index.ts +++ b/packages/nocodb/src/lib/services/dataService/index.ts @@ -795,4 +795,4 @@ export async function relationDataAdd(param: { return true; } -export * from './helpers' +export * from './helpers'; diff --git a/packages/nocodb/src/lib/services/filterService.ts b/packages/nocodb/src/lib/services/filterService.ts index c52089118b..351d312378 100644 --- a/packages/nocodb/src/lib/services/filterService.ts +++ b/packages/nocodb/src/lib/services/filterService.ts @@ -1,4 +1,5 @@ import { FilterReqType } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import Filter from '../models/Filter'; import { T } from 'nc-help'; @@ -6,6 +7,8 @@ export async function hookFilterCreate(param: { filter: FilterReqType; hookId: any; }) { + validatePayload('swagger.json#/components/schemas/FilterReq', param.filter); + const filter = await Filter.insert({ ...param.filter, fk_hook_id: param.hookId, @@ -29,6 +32,8 @@ export async function filterCreate(param: { filter: FilterReqType; viewId: string; }) { + validatePayload('swagger.json#/components/schemas/FilterReq', param.filter); + const filter = await Filter.insert({ ...param.filter, fk_view_id: param.viewId, @@ -42,6 +47,8 @@ export async function filterUpdate(param: { filter: FilterReqType; filterId: string; }) { + validatePayload('swagger.json#/components/schemas/FilterReq', param.filter); + // todo: type correction const filter = await Filter.update(param.filterId, param.filter as Filter); diff --git a/packages/nocodb/src/lib/services/formViewColumnService.ts b/packages/nocodb/src/lib/services/formViewColumnService.ts index b91d171f3b..ecceec1f81 100644 --- a/packages/nocodb/src/lib/services/formViewColumnService.ts +++ b/packages/nocodb/src/lib/services/formViewColumnService.ts @@ -1,3 +1,4 @@ +import { validatePayload } from '../meta/api/helpers'; import { FormViewColumn } from '../models'; import { T } from 'nc-help'; export async function columnUpdate(param: { @@ -5,6 +6,11 @@ export async function columnUpdate(param: { // todo: replace with FormColumnReq formViewColumn: FormViewColumn; }) { + validatePayload( + 'swagger.json#/components/schemas/FormColumnReq', + param.formViewColumn + ); + T.emit('evt', { evt_type: 'formViewColumn:updated' }); return await FormViewColumn.update( param.formViewColumnId, diff --git a/packages/nocodb/src/lib/services/formViewService.ts b/packages/nocodb/src/lib/services/formViewService.ts index eec4c66624..94c6f5c9a5 100644 --- a/packages/nocodb/src/lib/services/formViewService.ts +++ b/packages/nocodb/src/lib/services/formViewService.ts @@ -1,5 +1,6 @@ import { T } from 'nc-help'; import { FormReqType, ViewTypes } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import { FormView, View } from '../models'; export async function formViewGet(param: { formViewId: string }) { @@ -11,6 +12,8 @@ export async function formViewCreate(param: { tableId: string; body: FormReqType; }) { + validatePayload('swagger.json#/components/schemas/FormCreateReq', param.body); + T.emit('evt', { evt_type: 'vtable:created', show_as: 'form' }); const view = await View.insert({ ...param.body, @@ -26,6 +29,8 @@ export async function formViewUpdate(param: { formViewId: string; body: FormReqType; }) { + validatePayload('swagger.json#/components/schemas/FormReq', param.body); + T.emit('evt', { evt_type: 'view:updated', type: 'grid' }); await FormView.update(param.formViewId, param.body); } diff --git a/packages/nocodb/src/lib/services/galleryViewService.ts b/packages/nocodb/src/lib/services/galleryViewService.ts index 9e8ab05d5d..8c564fa74d 100644 --- a/packages/nocodb/src/lib/services/galleryViewService.ts +++ b/packages/nocodb/src/lib/services/galleryViewService.ts @@ -1,5 +1,6 @@ import { GalleryReqType, ViewTypes } from 'nocodb-sdk'; import { T } from 'nc-help'; +import { validatePayload } from '../meta/api/helpers'; import { GalleryView, View } from '../models'; export async function galleryViewGet(param: { galleryViewId: string }) { @@ -10,6 +11,8 @@ export async function galleryViewCreate(param: { tableId: string; gallery: GalleryReqType; }) { + validatePayload('swagger.json#/components/schemas/GalleryReq', param.gallery); + T.emit('evt', { evt_type: 'vtable:created', show_as: 'gallery' }); const view = await View.insert({ ...param.gallery, @@ -24,6 +27,8 @@ export async function galleryViewUpdate(param: { galleryViewId: string; gallery: GalleryReqType; }) { + validatePayload('swagger.json#/components/schemas/GalleryReq', param.gallery); + T.emit('evt', { evt_type: 'view:updated', type: 'gallery' }); await GalleryView.update(param.galleryViewId, param.gallery); } diff --git a/packages/nocodb/src/lib/services/gridViewColumnService.ts b/packages/nocodb/src/lib/services/gridViewColumnService.ts index e24dd07177..70d5d3df76 100644 --- a/packages/nocodb/src/lib/services/gridViewColumnService.ts +++ b/packages/nocodb/src/lib/services/gridViewColumnService.ts @@ -1,4 +1,5 @@ import { GridColumnReqType } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import GridViewColumn from '../models/GridViewColumn'; import { T } from 'nc-help'; @@ -10,6 +11,8 @@ export async function gridColumnUpdate(param: { gridViewColumnId: string; grid: GridColumnReqType; }) { + validatePayload('swagger.json#/components/schemas/GridColumnReq', param.grid); + T.emit('evt', { evt_type: 'gridViewColumn:updated' }); return await GridViewColumn.update(param.gridViewColumnId, param.grid); } diff --git a/packages/nocodb/src/lib/services/gridViewService.ts b/packages/nocodb/src/lib/services/gridViewService.ts index c99f63552e..92d01ced57 100644 --- a/packages/nocodb/src/lib/services/gridViewService.ts +++ b/packages/nocodb/src/lib/services/gridViewService.ts @@ -1,5 +1,6 @@ import { T } from 'nc-help'; import { GridReqType, ViewTypes } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import { View } from '../models'; import { GridView } from '../models'; @@ -7,6 +8,8 @@ export async function gridViewCreate(param: { tableId: string; grid: GridReqType; }) { + validatePayload('swagger.json#/components/schemas/GridReq', param.grid); + const view = await View.insert({ ...param.grid, // todo: sanitize @@ -17,6 +20,7 @@ export async function gridViewCreate(param: { return view; } +// todo: json schema validation export async function gridViewUpdate(param: { viewId: string; grid: GridReqType; diff --git a/packages/nocodb/src/lib/services/hookFilterService.ts b/packages/nocodb/src/lib/services/hookFilterService.ts index d4c0b2636d..b8b1e04a42 100644 --- a/packages/nocodb/src/lib/services/hookFilterService.ts +++ b/packages/nocodb/src/lib/services/hookFilterService.ts @@ -1,5 +1,6 @@ import { T } from 'nc-help'; import { FilterReqType } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import Filter from '../models/Filter'; export async function filterGet(param: { hookId: string }) { @@ -32,6 +33,8 @@ export async function filterCreate(param: { hookId: string; filter: FilterReqType; }) { + validatePayload('swagger.json#/components/schemas/FilterReq', param.filter); + const filter = await Filter.insert({ ...param.filter, fk_hook_id: param.hookId, @@ -46,6 +49,8 @@ export async function filterUpdate(param: { filterId: string; filter: FilterReqType; }) { + validatePayload('swagger.json#/components/schemas/FilterReq', param.filter); + const filter = await Filter.update(param.filterId, { ...param.filter, fk_hook_id: param.hookId, diff --git a/packages/nocodb/src/lib/services/hookService.ts b/packages/nocodb/src/lib/services/hookService.ts index 2e20732c17..9acf6d4b48 100644 --- a/packages/nocodb/src/lib/services/hookService.ts +++ b/packages/nocodb/src/lib/services/hookService.ts @@ -1,4 +1,5 @@ import { T } from 'nc-help'; +import { validatePayload } from '../meta/api/helpers'; import { Hook, Model } from '../models'; import { HookReqType, HookTestReqType } from 'nocodb-sdk'; @@ -14,6 +15,8 @@ export async function hookCreate(param: { tableId: string; hook: HookReqType; }) { + validatePayload('swagger.json#/components/schemas/HookReq', param.hook); + T.emit('evt', { evt_type: 'webhooks:created' }); // todo: type correction const hook = await Hook.insert({ @@ -30,6 +33,8 @@ export async function hookDelete(param: { hookId: string }) { } export async function hookUpdate(param: { hookId: string; hook: HookReqType }) { + validatePayload('swagger.json#/components/schemas/HookReq', param.hook); + T.emit('evt', { evt_type: 'webhooks:updated' }); // todo: correction in swagger @@ -40,6 +45,11 @@ export async function hookTest(param: { tableId: string; hookTest: HookTestReqType; }) { + validatePayload( + 'swagger.json#/components/schemas/HookTestReq', + param.hookTest + ); + const model = await Model.getByIdOrName({ id: param.tableId }); const { diff --git a/packages/nocodb/src/lib/services/kanbanViewService.ts b/packages/nocodb/src/lib/services/kanbanViewService.ts index 160044e26a..e6561ae323 100644 --- a/packages/nocodb/src/lib/services/kanbanViewService.ts +++ b/packages/nocodb/src/lib/services/kanbanViewService.ts @@ -1,4 +1,5 @@ import { KanbanReqType, ViewTypes } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import { KanbanView, View } from '../models'; import { T } from 'nc-help'; @@ -10,7 +11,8 @@ export async function kanbanViewCreate(param: { tableId: string; kanban: KanbanReqType; }) { - T.emit('evt', { evt_type: 'vtable:created', show_as: 'kanban' }); + validatePayload('swagger.json#/components/schemas/KanbanReq', param.kanban), + T.emit('evt', { evt_type: 'vtable:created', show_as: 'kanban' }); const view = await View.insert({ ...param.kanban, // todo: sanitize @@ -24,6 +26,10 @@ export async function kanbanViewUpdate(param: { kanbanViewId: string; kanban: KanbanReqType; }) { - T.emit('evt', { evt_type: 'view:updated', type: 'kanban' }); + validatePayload( + 'swagger.json#/components/schemas/KanbanUpdateReq', + param.kanban + ), + T.emit('evt', { evt_type: 'view:updated', type: 'kanban' }); return await KanbanView.update(param.kanbanViewId, param.kanban); } diff --git a/packages/nocodb/src/lib/services/modelVisibilityService.ts b/packages/nocodb/src/lib/services/modelVisibilityService.ts index eb2f21ec7e..719d3de487 100644 --- a/packages/nocodb/src/lib/services/modelVisibilityService.ts +++ b/packages/nocodb/src/lib/services/modelVisibilityService.ts @@ -1,4 +1,5 @@ import { VisibilityRuleReqType } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import { NcError } from '../meta/helpers/catchError'; import ModelRoleVisibility from '../models/ModelRoleVisibility'; import { T } from 'nc-help'; @@ -8,7 +9,11 @@ export async function xcVisibilityMetaSetAll(param: { visibilityRule: VisibilityRuleReqType; projectId: string; }) { - T.emit('evt', { evt_type: 'uiAcl:updated' }); + validatePayload( + 'swagger.json#/components/schemas/VisibilityRuleReq', + param.visibilityRule + ), + T.emit('evt', { evt_type: 'uiAcl:updated' }); for (const d of param.visibilityRule) { for (const role of Object.keys(d.disabled)) { const view = await View.get(d.id); diff --git a/packages/nocodb/src/lib/services/orgLicenseService.ts b/packages/nocodb/src/lib/services/orgLicenseService.ts index c39d7814a9..d6f8033575 100644 --- a/packages/nocodb/src/lib/services/orgLicenseService.ts +++ b/packages/nocodb/src/lib/services/orgLicenseService.ts @@ -1,4 +1,5 @@ import { NC_LICENSE_KEY } from '../constants'; +import { validatePayload } from '../meta/api/helpers'; import Store from '../models/Store'; import Noco from '../Noco'; @@ -9,6 +10,8 @@ export async function licenseGet() { } export async function licenseSet(param: { key: string }) { + validatePayload('swagger.json#/components/schemas/LicenseReq', param); + await Store.saveOrUpdate({ value: param.key, key: NC_LICENSE_KEY }); await Noco.loadEEState(); return true; diff --git a/packages/nocodb/src/lib/services/orgTokenService.ts b/packages/nocodb/src/lib/services/orgTokenService.ts index 30228c336a..fdee8e5fbe 100644 --- a/packages/nocodb/src/lib/services/orgTokenService.ts +++ b/packages/nocodb/src/lib/services/orgTokenService.ts @@ -1,4 +1,5 @@ import { ApiTokenReqType, OrgUserRoles } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import { User } from '../models'; import ApiToken from '../models/ApiToken'; import { T } from 'nc-help'; @@ -32,6 +33,11 @@ export async function apiTokenCreate(param: { user: User; apiToken: ApiTokenReqType; }) { + validatePayload( + 'swagger.json#/components/schemas/ApiTokenReq', + param.apiToken + ); + T.emit('evt', { evt_type: 'org:apiToken:created' }); return await ApiToken.insert({ ...param.apiToken, diff --git a/packages/nocodb/src/lib/services/orgUserService.ts b/packages/nocodb/src/lib/services/orgUserService.ts index 318afea8c0..b4e2d2adac 100644 --- a/packages/nocodb/src/lib/services/orgUserService.ts +++ b/packages/nocodb/src/lib/services/orgUserService.ts @@ -8,6 +8,7 @@ import { v4 as uuidv4 } from 'uuid'; import validator from 'validator'; import { OrgUserRoles } from 'nocodb-sdk'; import { NC_APP_SETTINGS } from '../constants'; +import { validatePayload } from '../meta/api/helpers'; import { Audit, ProjectUser, Store, SyncSource, User } from '../models'; import Noco from '../Noco'; import { MetaTable } from '../utils/globals'; @@ -35,6 +36,8 @@ export async function userUpdate(param: { user: Partial; userId: string; }) { + validatePayload('swagger.json#/components/schemas/OrgUserReq', param.user); + const updateBody = extractProps(param.user, ['roles']); const user = await User.get(param.userId); @@ -95,6 +98,8 @@ export async function userAdd(param: { // todo: refactor req: any; }) { + validatePayload('swagger.json#/components/schemas/OrgUserReq', param.user); + // allow only viewer or creator role if ( param.user.roles && diff --git a/packages/nocodb/src/lib/services/pluginService.ts b/packages/nocodb/src/lib/services/pluginService.ts index 2cb885e001..67302d8ce7 100644 --- a/packages/nocodb/src/lib/services/pluginService.ts +++ b/packages/nocodb/src/lib/services/pluginService.ts @@ -1,4 +1,5 @@ import { T } from 'nc-help'; +import { validatePayload } from '../meta/api/helpers'; import { Plugin } from '../models'; import { PluginTestReqType, PluginType } from 'nocodb-sdk'; import NcPluginMgrv2 from '../meta/helpers/NcPluginMgrv2'; @@ -8,6 +9,8 @@ export async function pluginList() { } export async function pluginTest(param: { body: PluginTestReqType }) { + validatePayload('swagger.json#/components/schemas/PluginTestReq', param.body); + T.emit('evt', { evt_type: 'plugin:tested' }); return await NcPluginMgrv2.test(param.body); } @@ -19,6 +22,8 @@ export async function pluginUpdate(param: { pluginId: string; plugin: PluginType; }) { + validatePayload('swagger.json#/components/schemas/PluginReq', param.plugin); + const plugin = await Plugin.update(param.pluginId, param.plugin); T.emit('evt', { evt_type: plugin.active ? 'plugin:installed' : 'plugin:uninstalled', diff --git a/packages/nocodb/src/lib/services/projectService.ts b/packages/nocodb/src/lib/services/projectService.ts index eaf4b98a64..08a848f7a5 100644 --- a/packages/nocodb/src/lib/services/projectService.ts +++ b/packages/nocodb/src/lib/services/projectService.ts @@ -1,7 +1,7 @@ import DOMPurify from 'isomorphic-dompurify'; import { OrgUserRoles, ProjectReqType } from 'nocodb-sdk'; import { promisify } from 'util'; -import { populateMeta } from '../meta/api/helpers'; +import { populateMeta, validatePayload } from '../meta/api/helpers'; import { extractPropsAndSanitize } from '../meta/helpers/extractProps'; import syncMigration from '../meta/helpers/syncMigration'; import Project from '../models/Project'; @@ -15,6 +15,8 @@ export async function projectCreate(param: { project: ProjectReqType; user: any; }) { + validatePayload('swagger.json#/components/schemas/ProjectReq', param.project); + const projectBody: ProjectReqType & Record = param.project; if (!projectBody.external) { const ranId = nanoid(); diff --git a/packages/nocodb/src/lib/services/projectUserService.ts b/packages/nocodb/src/lib/services/projectUserService.ts index 2ff67913d4..12e6a92735 100644 --- a/packages/nocodb/src/lib/services/projectUserService.ts +++ b/packages/nocodb/src/lib/services/projectUserService.ts @@ -1,5 +1,6 @@ import { OrgUserRoles, ProjectUserReqType } from 'nocodb-sdk'; import { T } from 'nc-help'; +import { validatePayload } from '../meta/api/helpers'; import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; import ProjectUser from '../models/ProjectUser'; import validator from 'validator'; @@ -33,6 +34,11 @@ export async function userInvite(param: { projectUser: ProjectUserReqType; req: any; }): Promise { + validatePayload( + 'swagger.json#/components/schemas/ProjectUserReq', + param.projectUser + ); + const emails = (param.projectUser.email || '') .toLowerCase() .split(/\s*,\s*/) @@ -157,6 +163,11 @@ export async function projectUserUpdate(param: { req: any; projectId: string; }): Promise { + validatePayload( + 'swagger.json#/components/schemas/ProjectUserReq', + param.projectUser + ); + // todo: use param.projectId if (!param.projectUser?.project_id) { NcError.badRequest('Missing project id in request body.'); diff --git a/packages/nocodb/src/lib/services/sharedBaseService.ts b/packages/nocodb/src/lib/services/sharedBaseService.ts index 53213d7627..a3549781f4 100644 --- a/packages/nocodb/src/lib/services/sharedBaseService.ts +++ b/packages/nocodb/src/lib/services/sharedBaseService.ts @@ -1,5 +1,6 @@ import { T } from 'nc-help'; import { v4 as uuidv4 } from 'uuid'; +import { validatePayload } from '../meta/api/helpers'; import Project from '../models/Project'; import { NcError } from '../meta/helpers/catchError'; // todo: load from config @@ -13,6 +14,8 @@ export async function createSharedBaseLink(param: { password: string; siteUrl: string; }): Promise { + validatePayload('swagger.json#/components/schemas/SharedBaseReq', param); + const project = await Project.get(param.projectId); let roles = param?.roles; @@ -44,6 +47,8 @@ export async function updateSharedBaseLink(param: { password: string; siteUrl: string; }): Promise { + validatePayload('swagger.json#/components/schemas/SharedBaseReq', param); + const project = await Project.get(param.projectId); let roles = param.roles; diff --git a/packages/nocodb/src/lib/services/sortService.ts b/packages/nocodb/src/lib/services/sortService.ts index e3931bad2c..c2d05948c1 100644 --- a/packages/nocodb/src/lib/services/sortService.ts +++ b/packages/nocodb/src/lib/services/sortService.ts @@ -1,4 +1,5 @@ import { SortReqType } from 'nocodb-sdk'; +import { validatePayload } from '../meta/api/helpers'; import Sort from '../models/Sort'; import { T } from 'nc-help'; @@ -13,12 +14,16 @@ export async function sortDelete(param: { sortId: string }) { } export async function sortUpdate(param: { sortId: any; sort: SortReqType }) { + validatePayload('swagger.json#/components/schemas/SortReq', param.sort); + const sort = await Sort.update(param.sortId, param.sort); T.emit('evt', { evt_type: 'sort:updated' }); return sort; } export async function sortCreate(param: { viewId: any; sort: SortReqType }) { + validatePayload('swagger.json#/components/schemas/SortReq', param.sort); + const sort = await Sort.insert({ ...param.sort, fk_view_id: param.viewId, diff --git a/packages/nocodb/src/lib/services/tableService.ts b/packages/nocodb/src/lib/services/tableService.ts index 1ff534548f..fabf00b1c0 100644 --- a/packages/nocodb/src/lib/services/tableService.ts +++ b/packages/nocodb/src/lib/services/tableService.ts @@ -9,6 +9,7 @@ import { UITypes, } from 'nocodb-sdk'; import ProjectMgrv2 from '../db/sql-mgr/v2/ProjectMgrv2'; +import { validatePayload } from '../meta/api/helpers'; import { NcError } from '../meta/helpers/catchError'; import getColumnPropsFromUIDT from '../meta/helpers/getColumnPropsFromUIDT'; import getColumnUiType from '../meta/helpers/getColumnUiType'; @@ -16,14 +17,16 @@ import getTableNameAlias, { getColumnNameAlias, } from '../meta/helpers/getTableName'; import mapDefaultDisplayValue from '../meta/helpers/mapDefaultDisplayValue'; -import Audit from '../models/Audit'; -import Column from '../models/Column'; -import LinkToAnotherRecordColumn from '../models/LinkToAnotherRecordColumn'; -import Model from '../models/Model'; -import ModelRoleVisibility from '../models/ModelRoleVisibility'; -import Project from '../models/Project'; -import User from '../models/User'; -import View from '../models/View'; +import { + Audit, + Column, + LinkToAnotherRecordColumn, + Model, + ModelRoleVisibility, + Project, + User, + View, +} from '../models'; import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; import { T } from 'nc-help'; @@ -304,6 +307,8 @@ export async function tableCreate(args: { table: TableReqType; user: User; }) { + validatePayload('swagger.json#/components/schemas/TableReq', args.table); + const project = await Project.getWithInfo(args.projectId); let base = project.bases[0]; diff --git a/packages/nocodb/src/lib/services/userService/index.ts b/packages/nocodb/src/lib/services/userService/index.ts index c18d35bd2e..d9e42bd651 100644 --- a/packages/nocodb/src/lib/services/userService/index.ts +++ b/packages/nocodb/src/lib/services/userService/index.ts @@ -13,6 +13,7 @@ import * as ejs from 'ejs'; import bcrypt from 'bcryptjs'; import { promisify } from 'util'; import { NC_APP_SETTINGS } from '../../constants'; +import { validatePayload } from '../../meta/api/helpers'; import { NcError } from '../../meta/helpers/catchError'; import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2'; import { Audit, Store, User } from '../../models'; @@ -79,6 +80,11 @@ export async function passwordChange(param: { user: UserType; req: any; }): Promise { + validatePayload( + 'swagger.json#/components/schemas/PasswordChangeReq', + param.body + ); + const { currentPassword, newPassword } = param.body; if (!currentPassword || !newPassword) { @@ -129,6 +135,11 @@ export async function passwordForgot(param: { siteUrl: string; req: any; }): Promise { + validatePayload( + 'swagger.json#/components/schemas/ForgotPasswordReq', + param.body + ); + const _email = param.body.email; if (!_email) { @@ -203,6 +214,11 @@ export async function passwordReset(param: { // todo: exclude req: any; }): Promise { + validatePayload( + 'swagger.json#/components/schemas/PasswordResetReq', + param.body + ); + const { token, body, req } = param; const user = await Noco.ncMeta.metaGet(null, null, MetaTable.USERS, {