Browse Source

refactor: move ajv validator from express middleware to service layer

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5239/head
Pranav C 2 years ago
parent
commit
b7da5eaf40
  1. 36
      packages/nocodb/src/lib/controllers/apiTokenController.ts
  2. 13
      packages/nocodb/src/lib/controllers/auditController.ts
  3. 100
      packages/nocodb/src/lib/controllers/baseController.ts
  4. 2
      packages/nocodb/src/lib/controllers/columnController.ts
  5. 6
      packages/nocodb/src/lib/controllers/dataApis/dataAliasExportApis.ts
  6. 4
      packages/nocodb/src/lib/controllers/filterController.ts
  7. 2
      packages/nocodb/src/lib/controllers/formViewColumnController.ts
  8. 50
      packages/nocodb/src/lib/controllers/formViewController.ts
  9. 3
      packages/nocodb/src/lib/controllers/galleryViewController.ts
  10. 2
      packages/nocodb/src/lib/controllers/gridViewColumnController.ts
  11. 35
      packages/nocodb/src/lib/controllers/gridViewController.ts
  12. 4
      packages/nocodb/src/lib/controllers/hookController.ts
  13. 3
      packages/nocodb/src/lib/controllers/hookFilterController.ts
  14. 5
      packages/nocodb/src/lib/controllers/kanbanViewController.ts
  15. 2
      packages/nocodb/src/lib/controllers/modelVisibilityController.ts
  16. 2
      packages/nocodb/src/lib/controllers/orgLicenseController.ts
  17. 56
      packages/nocodb/src/lib/controllers/orgTokenController.ts
  18. 3
      packages/nocodb/src/lib/controllers/orgUserController.ts
  19. 4
      packages/nocodb/src/lib/controllers/pluginController.ts
  20. 2
      packages/nocodb/src/lib/controllers/projectController.ts
  21. 316
      packages/nocodb/src/lib/controllers/projectUserController.ts
  22. 97
      packages/nocodb/src/lib/controllers/sharedBaseController.ts
  23. 3
      packages/nocodb/src/lib/controllers/sortController.ts
  24. 117
      packages/nocodb/src/lib/controllers/tableController.ts
  25. 22
      packages/nocodb/src/lib/controllers/userController/index.ts
  26. 6
      packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts
  27. 164
      packages/nocodb/src/lib/models/Hook.ts
  28. 6
      packages/nocodb/src/lib/services/apiTokenService.ts
  29. 11
      packages/nocodb/src/lib/services/auditService.ts
  30. 6
      packages/nocodb/src/lib/services/baseService.ts
  31. 3
      packages/nocodb/src/lib/services/columnService.ts
  32. 2
      packages/nocodb/src/lib/services/dataService/index.ts
  33. 7
      packages/nocodb/src/lib/services/filterService.ts
  34. 6
      packages/nocodb/src/lib/services/formViewColumnService.ts
  35. 5
      packages/nocodb/src/lib/services/formViewService.ts
  36. 5
      packages/nocodb/src/lib/services/galleryViewService.ts
  37. 3
      packages/nocodb/src/lib/services/gridViewColumnService.ts
  38. 4
      packages/nocodb/src/lib/services/gridViewService.ts
  39. 5
      packages/nocodb/src/lib/services/hookFilterService.ts
  40. 10
      packages/nocodb/src/lib/services/hookService.ts
  41. 10
      packages/nocodb/src/lib/services/kanbanViewService.ts
  42. 7
      packages/nocodb/src/lib/services/modelVisibilityService.ts
  43. 3
      packages/nocodb/src/lib/services/orgLicenseService.ts
  44. 6
      packages/nocodb/src/lib/services/orgTokenService.ts
  45. 5
      packages/nocodb/src/lib/services/orgUserService.ts
  46. 5
      packages/nocodb/src/lib/services/pluginService.ts
  47. 4
      packages/nocodb/src/lib/services/projectService.ts
  48. 11
      packages/nocodb/src/lib/services/projectUserService.ts
  49. 5
      packages/nocodb/src/lib/services/sharedBaseService.ts
  50. 5
      packages/nocodb/src/lib/services/sortService.ts
  51. 21
      packages/nocodb/src/lib/services/tableService.ts
  52. 16
      packages/nocodb/src/lib/services/userService/index.ts

36
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(

13
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<any, any>, 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(

100
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<any, any, any>,
res: Response<Base>
) {
const base = await Base.get(req.params.baseId);
import { baseService } from '../services';
base.config = base.getConnectionConfig();
async function baseGet(req: Request<any>, res: Response<Base>) {
const base = await baseService.baseGetWithConfig({
baseId: req.params.baseId,
});
res.json(base);
}
export async function baseUpdate(
req: Request<any, any, any>,
res: Response<any>
) {
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<any, any, any>, res: Response<any>) {
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<any, any, any>,
res: Response<BaseListType>,
next
res: Response<BaseListType>
) {
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<any, any, any>,
res: Response<any>
) {
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<any, any>, 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;

2
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')
);

6
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' });

4
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(

2
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;

50
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<FormType>) {
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<any, any>, 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;

3
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<GalleryType>) {
@ -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(

2
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;

35
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<any, any>, 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<any>, 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(

4
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(

3
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(

5
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<KanbanType>) {
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(

2
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;

2
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,

56
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,

3
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,

4
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(

2
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(

316
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<any> {
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<any> {
res.json(
await projectUserService.userInvite({
projectId: req.params.projectId,
projectUser: req.body,
req,
})
);
}
// @ts-ignore
async function projectUserUpdate(req, res, next): Promise<any> {
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<any> {
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<any> {
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<any> {
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(

97
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<any> {
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<any> {
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<any> {
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<any> {
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<any> {
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(

3
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(

117
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<TableListType>) {
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<any, any>, 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<any, any>, 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(

22
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));

6
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,
});
}
}
};

164
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<Hook | HookReqType>) {
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<Hook>) {
@ -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<Hook & {
created_at?;
updated_at?;
}>,
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<Hook>,
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);
}
}

6
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 });
}

11
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,

6
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);

3
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,
});

2
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';

7
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);

6
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,

5
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);
}

5
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);
}

3
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);
}

4
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;

5
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,

10
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 {

10
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);
}

7
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);

3
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;

6
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,

5
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<UserType>;
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 &&

5
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',

4
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<string, any> = param.project;
if (!projectBody.external) {
const ranId = nanoid();

11
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<any> {
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<any> {
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.');

5
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<any> {
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<any> {
validatePayload('swagger.json#/components/schemas/SharedBaseReq', param);
const project = await Project.get(param.projectId);
let roles = param.roles;

5
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,

21
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];

16
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<any> {
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<any> {
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<any> {
validatePayload(
'swagger.json#/components/schemas/PasswordResetReq',
param.body
);
const { token, body, req } = param;
const user = await Noco.ncMeta.metaGet(null, null, MetaTable.USERS, {

Loading…
Cancel
Save