mirror of https://github.com/nocodb/nocodb
Wing-Kam Wong
2 years ago
189 changed files with 11005 additions and 7247 deletions
@ -0,0 +1,49 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { apiTokenService } from '../services'; |
||||
|
||||
export async function apiTokenList(req: Request, res: Response) { |
||||
res.json(await apiTokenService.apiTokenList({ userId: req['user'].id })); |
||||
} |
||||
|
||||
export async function apiTokenCreate(req: Request, res: Response) { |
||||
res.json( |
||||
await apiTokenService.apiTokenCreate({ |
||||
tokenBody: req.body, |
||||
userId: req['user'].id, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
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
|
||||
|
||||
// deprecated apis
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenList, 'apiTokenList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens/:token', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete') |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,127 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import multer from 'multer'; |
||||
import { OrgUserRoles, ProjectRoles } from 'nocodb-sdk'; |
||||
import path from 'path'; |
||||
import Noco from '../Noco'; |
||||
import { MetaTable } from '../utils/globals'; |
||||
import extractProjectIdAndAuthenticate from '../meta/helpers/extractProjectIdAndAuthenticate'; |
||||
import catchError, { NcError } from '../meta/helpers/catchError'; |
||||
import { NC_ATTACHMENT_FIELD_SIZE } from '../constants'; |
||||
import { getCacheMiddleware } from '../meta/api/helpers'; |
||||
import { attachmentService } from '../services'; |
||||
|
||||
const isUploadAllowedMw = async (req: Request, _res: Response, next: any) => { |
||||
if (!req['user']?.id) { |
||||
if (!req['user']?.isPublicBase) { |
||||
NcError.unauthorized('Unauthorized'); |
||||
} |
||||
} |
||||
|
||||
try { |
||||
// check user is super admin or creator
|
||||
if ( |
||||
req['user'].roles?.includes(OrgUserRoles.SUPER_ADMIN) || |
||||
req['user'].roles?.includes(OrgUserRoles.CREATOR) || |
||||
req['user'].roles?.includes(ProjectRoles.EDITOR) || |
||||
// if viewer then check at-least one project have editor or higher role
|
||||
// todo: cache
|
||||
!!(await Noco.ncMeta |
||||
.knex(MetaTable.PROJECT_USERS) |
||||
.where(function () { |
||||
this.where('roles', ProjectRoles.OWNER); |
||||
this.orWhere('roles', ProjectRoles.CREATOR); |
||||
this.orWhere('roles', ProjectRoles.EDITOR); |
||||
}) |
||||
.andWhere('fk_user_id', req['user'].id) |
||||
.first()) |
||||
) |
||||
return next(); |
||||
} catch {} |
||||
NcError.badRequest('Upload not allowed'); |
||||
}; |
||||
|
||||
export async function upload(req: Request, res: Response) { |
||||
const attachments = await attachmentService.upload({ |
||||
files: (req as any).files, |
||||
path: req.query?.path as string, |
||||
}); |
||||
|
||||
res.json(attachments); |
||||
} |
||||
|
||||
export async function uploadViaURL(req: Request, res: Response) { |
||||
const attachments = await attachmentService.uploadViaURL({ |
||||
urls: req.body, |
||||
path: req.query?.path as string, |
||||
}); |
||||
|
||||
res.json(attachments); |
||||
} |
||||
|
||||
export async function fileRead(req, res) { |
||||
try { |
||||
const { img, type } = await attachmentService.fileRead({ |
||||
path: path.join('nc', 'uploads', req.params?.[0]), |
||||
}); |
||||
|
||||
res.writeHead(200, { 'Content-Type': type }); |
||||
res.end(img, 'binary'); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(404).send('Not found'); |
||||
} |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, |
||||
getCacheMiddleware(), |
||||
async (req, res) => { |
||||
try { |
||||
const { img, type } = await attachmentService.fileRead({ |
||||
path: path.join( |
||||
'nc', |
||||
req.params[0], |
||||
req.params[1], |
||||
'uploads', |
||||
...req.params[2].split('/') |
||||
), |
||||
}); |
||||
|
||||
res.writeHead(200, { 'Content-Type': type }); |
||||
res.end(img, 'binary'); |
||||
} catch (e) { |
||||
res.status(404).send('Not found'); |
||||
} |
||||
} |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/storage/upload', |
||||
multer({ |
||||
storage: multer.diskStorage({}), |
||||
limits: { |
||||
fieldSize: NC_ATTACHMENT_FIELD_SIZE, |
||||
}, |
||||
}).any(), |
||||
[ |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(isUploadAllowedMw), |
||||
catchError(upload), |
||||
] |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/storage/upload-by-url', |
||||
|
||||
[ |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(isUploadAllowedMw), |
||||
catchError(uploadViaURL), |
||||
] |
||||
); |
||||
|
||||
router.get(/^\/download\/(.+)$/, getCacheMiddleware(), catchError(fileRead)); |
||||
|
||||
export default router; |
@ -0,0 +1,91 @@
|
||||
import { Request, Response } from 'express'; |
||||
import { BaseListType } from 'nocodb-sdk'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import Base from '../models/Base'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
|
||||
import { baseService } from '../services'; |
||||
|
||||
async function baseGet(req: Request<any>, res: Response<Base>) { |
||||
const base = await baseService.baseGetWithConfig({ |
||||
baseId: req.params.baseId, |
||||
}); |
||||
|
||||
res.json(base); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
|
||||
async function baseList( |
||||
req: Request<any, any, any>, |
||||
res: Response<BaseListType> |
||||
) { |
||||
const bases = await baseService.baseList({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
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 result = await baseService.baseDelete({ |
||||
baseId: req.params.baseId, |
||||
}); |
||||
res.json(result); |
||||
} |
||||
|
||||
async function baseCreate(req: Request<any, any>, res) { |
||||
const base = await baseService.baseCreate({ |
||||
projectId: req.params.projectId, |
||||
base: req.body, |
||||
}); |
||||
|
||||
res.json(base); |
||||
} |
||||
|
||||
const initRoutes = (router) => { |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseGet, 'baseGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseUpdate, 'baseUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseDelete, 'baseDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/bases', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseCreate, 'baseCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/bases', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseList, 'baseList') |
||||
); |
||||
}; |
||||
|
||||
export default initRoutes; |
@ -0,0 +1,77 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { ColumnReqType, TableType, UITypes } from 'nocodb-sdk'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { columnService } from '../services'; |
||||
|
||||
export async function columnGet(req: Request, res: Response) { |
||||
res.json(await columnService.columnGet({ columnId: req.params.columnId })); |
||||
} |
||||
|
||||
export async function columnAdd( |
||||
req: Request<any, any, ColumnReqType & { uidt: UITypes }>, |
||||
res: Response<TableType> |
||||
) { |
||||
res.json( |
||||
await columnService.columnAdd({ |
||||
tableId: req.params.tableId, |
||||
column: req.body, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function columnSetAsPrimary(req: Request, res: Response) { |
||||
res.json( |
||||
await columnService.columnSetAsPrimary({ columnId: req.params.columnId }) |
||||
); |
||||
} |
||||
|
||||
export async function columnUpdate(req: Request, res: Response<TableType>) { |
||||
res.json( |
||||
await columnService.columnUpdate({ |
||||
columnId: req.params.columnId, |
||||
column: req.body, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function columnDelete(req: Request, res: Response<TableType>) { |
||||
res.json( |
||||
await columnService.columnDelete({ columnId: req.params.columnId, req }) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/columns/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnAdd, 'columnAdd') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/meta/columns/:columnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnUpdate, 'columnUpdate') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/meta/columns/:columnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnDelete, 'columnDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/columns/:columnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnGet, 'columnGet') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/meta/columns/:columnId/primary', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnSetAsPrimary, 'columnSetAsPrimary') |
||||
); |
||||
export default router; |
@ -0,0 +1,92 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { bulkDataService } from '../../services'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
|
||||
async function bulkDataInsert(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataInsert({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function bulkDataUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataUpdate({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: Integrate with filterArrJson bulkDataUpdateAll
|
||||
async function bulkDataUpdateAll(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataUpdateAll({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function bulkDataDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataDelete({ |
||||
body: req.body, |
||||
cookie: req, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: Integrate with filterArrJson bulkDataDeleteAll
|
||||
async function bulkDataDeleteAll(req: Request, res: Response) { |
||||
res.json( |
||||
await bulkDataService.bulkDataDeleteAll({ |
||||
// cookie: req,
|
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataInsert, 'bulkDataInsert') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataUpdate, 'bulkDataUpdate') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName/all', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataUpdateAll, 'bulkDataUpdateAll') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataDelete, 'bulkDataDelete') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName/all', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataDeleteAll, 'bulkDataDeleteAll') |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,259 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { dataService } from '../../services'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import { parseHrtimeToSeconds } from '../../meta/api/helpers'; |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function dataList(req: Request, res: Response) { |
||||
const startTime = process.hrtime(); |
||||
const responseData = await dataService.dataList({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}); |
||||
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(responseData); |
||||
} |
||||
|
||||
async function dataFindOne(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataFindOne({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataGroupBy(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataGroupBy({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataCount(req: Request, res: Response) { |
||||
const countResult = await dataService.dataCount({ |
||||
query: req.query, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
}); |
||||
|
||||
res.json(countResult); |
||||
} |
||||
|
||||
async function dataInsert(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataInsert({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
body: req.body, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataUpdate({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
body: req.body, |
||||
cookie: req, |
||||
rowId: req.params.rowId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataDelete({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
cookie: req, |
||||
rowId: req.params.rowId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataRead(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataRead({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataExist(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataExist({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function groupedDataList(req: Request, res: Response) { |
||||
const startTime = process.hrtime(); |
||||
const groupedData = await dataService.groupedDataList({ |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
viewName: req.params.viewName, |
||||
query: req.query, |
||||
columnId: req.params.columnId, |
||||
}); |
||||
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(groupedData); |
||||
} |
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// table data crud apis
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/find-one', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataFindOne, 'dataFindOne') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/groupby', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataGroupBy, 'dataGroupBy') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/group/:columnId', |
||||
apiMetrics, |
||||
ncMetaAclMw(groupedDataList, 'groupedDataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/exist', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataExist, 'dataExist') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/count', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/count', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
// table view data crud apis
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/find-one', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataFindOne, 'dataFindOne') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/groupby', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataGroupBy, 'dataGroupBy') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/group/:columnId', |
||||
apiMetrics, |
||||
ncMetaAclMw(groupedDataList, 'groupedDataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId/exist', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataExist, 'dataExist') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,13 +1,13 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import * as XLSX from 'xlsx'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { View } from '../../models'; |
||||
import { |
||||
extractCsvData, |
||||
extractXlsxData, |
||||
getViewAndModelFromRequestByAliasOrId, |
||||
} from './helpers'; |
||||
import apiMetrics from '../../helpers/apiMetrics'; |
||||
import View from '../../../models/View'; |
||||
} from '../../services/dataService/helpers'; |
||||
import { getViewAndModelFromRequestByAliasOrId } from './helpers'; |
||||
|
||||
async function excelDataExport(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
@ -0,0 +1,137 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import { dataAliasNestedService } from '../../services'; |
||||
|
||||
// todo: handle case where the given column is not ltar
|
||||
export async function mmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.mmList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.mmExcludedList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.hmExcludedList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.btExcludedList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: handle case where the given column is not ltar
|
||||
export async function hmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataAliasNestedService.hmList({ |
||||
query: req.query, |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataRemove(req, res) { |
||||
await dataAliasNestedService.relationDataRemove({ |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
cookie: req, |
||||
refRowId: req.params.refRowId, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
// todo: Give proper error message when reference row is already related and handle duplicate ref row id in hm
|
||||
async function relationDataAdd(req, res) { |
||||
await dataAliasNestedService.relationDataAdd({ |
||||
columnName: req.params.columnName, |
||||
rowId: req.params.rowId, |
||||
projectName: req.params.projectName, |
||||
tableName: req.params.tableName, |
||||
cookie: req, |
||||
refRowId: req.params.refRowId, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/bt/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(relationDataRemove, 'relationDataRemove') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmList, 'mmList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmList, 'hmList') |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,192 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { dataService } from '../../services'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataListByViewId({ |
||||
viewId: req.params.viewId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.mmList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.mmExcludedList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.hmExcludedList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.btExcludedList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmList(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.hmList({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataRead(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataReadByViewId({ |
||||
viewId: req.params.viewId, |
||||
rowId: req.params.rowId, |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataInsert(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataInsertByViewId({ |
||||
viewId: req.params.viewId, |
||||
body: req.body, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataUpdateByViewId({ |
||||
viewId: req.params.viewId, |
||||
rowId: req.params.rowId, |
||||
body: req.body, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function dataDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await dataService.dataDeleteByViewId({ |
||||
viewId: req.params.viewId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function relationDataDelete(req, res) { |
||||
await dataService.relationDataDelete({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataAdd(req, res) { |
||||
await dataService.relationDataAdd({ |
||||
viewId: req.params.viewId, |
||||
colId: req.params.colId, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get('/data/:viewId/', apiMetrics, ncMetaAclMw(dataList, 'dataList')); |
||||
router.post( |
||||
'/data/:viewId/', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
router.patch( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
router.delete( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/data/:viewId/:rowId/mm/:colId', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmList, 'mmList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/hm/:colId', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmList, 'hmList') |
||||
); |
||||
|
||||
router.get( |
||||
'/data/:viewId/:rowId/mm/:colId/exclude', |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/hm/:colId/exclude', |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/bt/:colId/exclude', |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/data/:viewId/:rowId/:relationType/:colId/:childId', |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/data/:viewId/:rowId/:relationType/:colId/:childId', |
||||
ncMetaAclMw(relationDataDelete, 'relationDataDelete') |
||||
); |
||||
export default router; |
@ -0,0 +1,270 @@
|
||||
import { NcError } from '../../meta/helpers/catchError'; |
||||
import Project from '../../models/Project'; |
||||
import Model from '../../models/Model'; |
||||
import View from '../../models/View'; |
||||
import { Request } from 'express'; |
||||
import Base from '../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import { isSystemColumn, UITypes } from 'nocodb-sdk'; |
||||
|
||||
import * as XLSX from 'xlsx'; |
||||
import Column from '../../models/Column'; |
||||
import LookupColumn from '../../models/LookupColumn'; |
||||
import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; |
||||
|
||||
import papaparse from 'papaparse'; |
||||
import { dataService } from '../../services'; |
||||
export async function getViewAndModelFromRequestByAliasOrId( |
||||
req: |
||||
| Request<{ projectName: string; tableName: string; viewName?: string }> |
||||
| Request |
||||
) { |
||||
const project = await Project.getWithInfoByTitleOrId(req.params.projectName); |
||||
|
||||
const model = await Model.getByAliasOrId({ |
||||
project_id: project.id, |
||||
aliasOrId: req.params.tableName, |
||||
}); |
||||
const view = |
||||
req.params.viewName && |
||||
(await View.getByTitleOrId({ |
||||
titleOrId: req.params.viewName, |
||||
fk_model_id: model.id, |
||||
})); |
||||
if (!model) NcError.notFound('Table not found'); |
||||
return { model, view }; |
||||
} |
||||
|
||||
export async function extractXlsxData(param: { |
||||
view: View; |
||||
query: any; |
||||
siteUrl: string; |
||||
}) { |
||||
const { view, query, siteUrl } = param; |
||||
const base = await Base.get(view.base_id); |
||||
|
||||
await view.getModelWithInfo(); |
||||
await view.getColumns(); |
||||
|
||||
view.model.columns = view.columns |
||||
.filter((c) => c.show) |
||||
.map( |
||||
(c) => |
||||
new Column({ ...c, ...view.model.columnsById[c.fk_column_id] } as any) |
||||
) |
||||
.filter((column) => !isSystemColumn(column) || view.show_system_fields); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: view.model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { offset, dbRows, elapsed } = await dataService.getDbRows({ |
||||
baseModel, |
||||
view, |
||||
query, |
||||
siteUrl, |
||||
}); |
||||
|
||||
const fields = query.fields as string[]; |
||||
|
||||
const data = XLSX.utils.json_to_sheet(dbRows, { header: fields }); |
||||
|
||||
return { offset, dbRows, elapsed, data }; |
||||
} |
||||
|
||||
export async function extractCsvData(view: View, req: Request) { |
||||
const base = await Base.get(view.base_id); |
||||
const fields = req.query.fields; |
||||
|
||||
await view.getModelWithInfo(); |
||||
await view.getColumns(); |
||||
|
||||
view.model.columns = view.columns |
||||
.filter((c) => c.show) |
||||
.map( |
||||
(c) => |
||||
new Column({ ...c, ...view.model.columnsById[c.fk_column_id] } as any) |
||||
) |
||||
.filter((column) => !isSystemColumn(column) || view.show_system_fields); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: view.model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const { offset, dbRows, elapsed } = await dataService.getDbRows({ |
||||
baseModel, |
||||
view, |
||||
query: req.query, |
||||
siteUrl: (req as any).ncSiteUrl, |
||||
}); |
||||
|
||||
const data = papaparse.unparse( |
||||
{ |
||||
fields: view.model.columns |
||||
.sort((c1, c2) => |
||||
Array.isArray(fields) |
||||
? fields.indexOf(c1.title as any) - fields.indexOf(c2.title as any) |
||||
: 0 |
||||
) |
||||
.filter( |
||||
(c) => |
||||
!fields || !Array.isArray(fields) || fields.includes(c.title as any) |
||||
) |
||||
.map((c) => c.title), |
||||
data: dbRows, |
||||
}, |
||||
{ |
||||
escapeFormulae: true, |
||||
} |
||||
); |
||||
|
||||
return { offset, dbRows, elapsed, data }; |
||||
} |
||||
//
|
||||
// async function getDbRows(baseModel, view: View, req: Request) {
|
||||
// let offset = +req.query.offset || 0;
|
||||
// const limit = 100;
|
||||
// // const size = +process.env.NC_EXPORT_MAX_SIZE || 1024;
|
||||
// const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000;
|
||||
// const dbRows = [];
|
||||
// const startTime = process.hrtime();
|
||||
// let elapsed, temp;
|
||||
//
|
||||
// const listArgs: any = { ...req.query };
|
||||
// try {
|
||||
// listArgs.filterArr = JSON.parse(listArgs.filterArrJson);
|
||||
// } catch (e) {}
|
||||
// try {
|
||||
// listArgs.sortArr = JSON.parse(listArgs.sortArrJson);
|
||||
// } catch (e) {}
|
||||
//
|
||||
// for (
|
||||
// elapsed = 0;
|
||||
// elapsed < timeout;
|
||||
// offset += limit,
|
||||
// temp = process.hrtime(startTime),
|
||||
// elapsed = temp[0] * 1000 + temp[1] / 1000000
|
||||
// ) {
|
||||
// const rows = await nocoExecute(
|
||||
// await getAst({
|
||||
// query: req.query,
|
||||
// includePkByDefault: false,
|
||||
// model: view.model,
|
||||
// view,
|
||||
// }),
|
||||
// await baseModel.list({ ...listArgs, offset, limit }),
|
||||
// {},
|
||||
// req.query
|
||||
// );
|
||||
//
|
||||
// if (!rows?.length) {
|
||||
// offset = -1;
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// for (const row of rows) {
|
||||
// const dbRow = { ...row };
|
||||
//
|
||||
// for (const column of view.model.columns) {
|
||||
// if (isSystemColumn(column) && !view.show_system_fields) continue;
|
||||
// dbRow[column.title] = await serializeCellValue({
|
||||
// value: row[column.title],
|
||||
// column,
|
||||
// siteUrl: req['ncSiteUrl'],
|
||||
// });
|
||||
// }
|
||||
// dbRows.push(dbRow);
|
||||
// }
|
||||
// }
|
||||
// return { offset, dbRows, elapsed };
|
||||
// }
|
||||
|
||||
export async function serializeCellValue({ |
||||
value, |
||||
column, |
||||
siteUrl, |
||||
}: { |
||||
column?: Column; |
||||
value: any; |
||||
siteUrl: string; |
||||
}) { |
||||
if (!column) { |
||||
return value; |
||||
} |
||||
|
||||
if (!value) return value; |
||||
|
||||
switch (column?.uidt) { |
||||
case UITypes.Attachment: { |
||||
let data = value; |
||||
try { |
||||
if (typeof value === 'string') { |
||||
data = JSON.parse(value); |
||||
} |
||||
} catch {} |
||||
|
||||
return (data || []).map( |
||||
(attachment) => |
||||
`${encodeURI(attachment.title)}(${encodeURI( |
||||
attachment.path ? `${siteUrl}/${attachment.path}` : attachment.url |
||||
)})` |
||||
); |
||||
} |
||||
case UITypes.Lookup: |
||||
{ |
||||
const colOptions = await column.getColOptions<LookupColumn>(); |
||||
const lookupColumn = await colOptions.getLookupColumn(); |
||||
return ( |
||||
await Promise.all( |
||||
[...(Array.isArray(value) ? value : [value])].map(async (v) => |
||||
serializeCellValue({ |
||||
value: v, |
||||
column: lookupColumn, |
||||
siteUrl, |
||||
}) |
||||
) |
||||
) |
||||
).join(', '); |
||||
} |
||||
break; |
||||
case UITypes.LinkToAnotherRecord: |
||||
{ |
||||
const colOptions = |
||||
await column.getColOptions<LinkToAnotherRecordColumn>(); |
||||
const relatedModel = await colOptions.getRelatedTable(); |
||||
await relatedModel.getColumns(); |
||||
return [...(Array.isArray(value) ? value : [value])] |
||||
.map((v) => { |
||||
return v[relatedModel.displayValue?.title]; |
||||
}) |
||||
.join(', '); |
||||
} |
||||
break; |
||||
default: |
||||
if (value && typeof value === 'object') { |
||||
return JSON.stringify(value); |
||||
} |
||||
return value; |
||||
} |
||||
} |
||||
|
||||
export async function getColumnByIdOrName( |
||||
columnNameOrId: string, |
||||
model: Model |
||||
) { |
||||
const column = (await model.getColumns()).find( |
||||
(c) => |
||||
c.title === columnNameOrId || |
||||
c.id === columnNameOrId || |
||||
c.column_name === columnNameOrId |
||||
); |
||||
|
||||
if (!column) |
||||
NcError.notFound(`Column with id/name '${columnNameOrId}' is not found`); |
||||
|
||||
return column; |
||||
} |
@ -0,0 +1,15 @@
|
||||
import dataController from './dataController'; |
||||
import oldDataController from './oldDataController'; |
||||
import dataAliasController from './dataAliasController'; |
||||
import bulkDataAliasController from './bulkDataAliasController'; |
||||
import dataAliasNestedController from './dataAliasNestedController'; |
||||
import dataAliasExportController from './dataAliasExportController'; |
||||
|
||||
export { |
||||
dataController, |
||||
oldDataController, |
||||
dataAliasController, |
||||
bulkDataAliasController, |
||||
dataAliasNestedController, |
||||
dataAliasExportController, |
||||
}; |
@ -1,14 +1,14 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../../models/Model'; |
||||
import Model from '../../models/Model'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import View from '../../../models/View'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import Project from '../../../models/Project'; |
||||
import { NcError } from '../../helpers/catchError'; |
||||
import apiMetrics from '../../helpers/apiMetrics'; |
||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import Base from '../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import View from '../../models/View'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import Project from '../../models/Project'; |
||||
import { NcError } from '../../meta/helpers/catchError'; |
||||
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||
import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequest(req); |
@ -1,7 +1,7 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import View from '../../models/View'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { extractCsvData } from './dataApis/helpers'; |
||||
import View from '../models/View'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { extractCsvData } from './dataControllers/helpers'; |
||||
|
||||
async function exportCsv(req: Request, res: Response) { |
||||
const view = await View.get(req.params.viewId); |
@ -0,0 +1,115 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { FilterReqType } from 'nocodb-sdk'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
|
||||
import { filterService } from '../services'; |
||||
|
||||
// @ts-ignore
|
||||
export async function filterGet(req: Request, res: Response) { |
||||
res.json(await filterService.filterGet({ filterId: req.params.filterId })); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterList(req: Request, res: Response) { |
||||
res.json( |
||||
await filterService.filterList({ |
||||
viewId: req.params.viewId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterChildrenRead(req: Request, res: Response) { |
||||
const filter = await filterService.filterChildrenList({ |
||||
filterId: req.params.filterParentId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterCreate(req: Request<any, any, FilterReqType>, res) { |
||||
const filter = await filterService.filterCreate({ |
||||
filter: req.body, |
||||
viewId: req.params.viewId, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterUpdate(req, res) { |
||||
const filter = await filterService.filterUpdate({ |
||||
filterId: req.params.filterId, |
||||
filter: req.body, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterDelete(req: Request, res: Response) { |
||||
const filter = await filterService.filterDelete({ |
||||
filterId: req.params.filterId, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function hookFilterList(req: Request, res: Response) { |
||||
res.json( |
||||
await filterService.hookFilterList({ |
||||
hookId: req.params.hookId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hookFilterCreate( |
||||
req: Request<any, any, FilterReqType>, |
||||
res |
||||
) { |
||||
const filter = await filterService.hookFilterCreate({ |
||||
filter: req.body, |
||||
hookId: req.params.hookId, |
||||
}); |
||||
res.json(filter); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/views/:viewId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterCreate, 'filterCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/hooks/:hookId/filters', |
||||
ncMetaAclMw(hookFilterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/hooks/:hookId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookFilterCreate, 'filterCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterGet, 'filterGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterUpdate, 'filterUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterDelete, 'filterDelete') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/filters/:filterParentId/children', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead') |
||||
); |
||||
export default router; |
@ -0,0 +1,21 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { formViewColumnService } from '../services'; |
||||
|
||||
export async function columnUpdate(req: Request, res: Response) { |
||||
res.json( |
||||
await formViewColumnService.columnUpdate({ |
||||
formViewColumnId: req.params.formViewColumnId, |
||||
formViewColumn: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.patch( |
||||
'/api/v1/db/meta/form-columns/:formViewColumnId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(columnUpdate, 'columnUpdate') |
||||
); |
||||
export default router; |
@ -0,0 +1,48 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { FormType } from 'nocodb-sdk'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { formViewService } from '../services'; |
||||
|
||||
export async function formViewGet(req: Request, res: Response<FormType>) { |
||||
const formViewData = await formViewService.formViewGet({ |
||||
formViewId: req.params.formViewId, |
||||
}); |
||||
res.json(formViewData); |
||||
} |
||||
|
||||
export async function formViewCreate(req: Request<any, any>, res) { |
||||
const view = await formViewService.formViewCreate({ |
||||
body: req.body, |
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json(view); |
||||
} |
||||
|
||||
export async function formViewUpdate(req, res) { |
||||
res.json( |
||||
await formViewService.formViewUpdate({ |
||||
formViewId: req.params.formViewId, |
||||
body: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/forms', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(formViewCreate, 'formViewCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/forms/:formViewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(formViewGet, 'formViewGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/forms/:formViewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(formViewUpdate, 'formViewUpdate') |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,49 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { GalleryType } from 'nocodb-sdk'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { galleryViewService } from '../services'; |
||||
|
||||
export async function galleryViewGet(req: Request, res: Response<GalleryType>) { |
||||
res.json( |
||||
await galleryViewService.galleryViewGet({ |
||||
galleryViewId: req.params.galleryViewId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function galleryViewCreate(req: Request<any, any>, res) { |
||||
const view = await galleryViewService.galleryViewCreate({ |
||||
gallery: req.body, |
||||
// todo: sanitize
|
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json(view); |
||||
} |
||||
|
||||
export async function galleryViewUpdate(req, res) { |
||||
res.json( |
||||
await galleryViewService.galleryViewUpdate({ |
||||
galleryViewId: req.params.galleryViewId, |
||||
gallery: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/galleries', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(galleryViewCreate, 'galleryViewCreate') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/galleries/:galleryViewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(galleryViewUpdate, 'galleryViewUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/galleries/:galleryViewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(galleryViewGet, 'galleryViewGet') |
||||
); |
||||
export default router; |
@ -0,0 +1,34 @@
|
||||
import { Request, Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { gridViewService } from '../services'; |
||||
|
||||
export async function gridViewCreate(req: Request<any>, res) { |
||||
const view = await gridViewService.gridViewCreate({ |
||||
grid: req.body, |
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json(view); |
||||
} |
||||
|
||||
export async function gridViewUpdate(req, res) { |
||||
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, |
||||
ncMetaAclMw(gridViewCreate, 'gridViewCreate') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/grids/:viewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(gridViewUpdate, 'gridViewUpdate') |
||||
); |
||||
export default router; |
@ -0,0 +1,98 @@
|
||||
import catchError from '../meta/helpers/catchError'; |
||||
import { Request, Response, Router } from 'express'; |
||||
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 { hookService } from '../services'; |
||||
|
||||
export async function hookList( |
||||
req: Request<any, any, any>, |
||||
res: Response<HookListType> |
||||
) { |
||||
// todo: pagination
|
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await hookService.hookList({ tableId: req.params.tableId }) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function hookCreate( |
||||
req: Request<any, HookType>, |
||||
res: Response<HookType> |
||||
) { |
||||
const hook = await hookService.hookCreate({ |
||||
hook: req.body, |
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json(hook); |
||||
} |
||||
|
||||
export async function hookDelete( |
||||
req: Request<any, HookType>, |
||||
res: Response<any> |
||||
) { |
||||
res.json(await hookService.hookDelete({ hookId: req.params.hookId })); |
||||
} |
||||
|
||||
export async function hookUpdate( |
||||
req: Request<any, HookType>, |
||||
res: Response<HookType> |
||||
) { |
||||
res.json( |
||||
await hookService.hookUpdate({ hookId: req.params.hookId, hook: req.body }) |
||||
); |
||||
} |
||||
|
||||
export async function hookTest(req: Request<any, any>, res: Response) { |
||||
await hookService.hookTest({ |
||||
hookTest: req.body, |
||||
tableId: req.params.tableId, |
||||
}); |
||||
res.json({ msg: 'Success' }); |
||||
} |
||||
|
||||
export async function tableSampleData(req: Request, res: Response) { |
||||
res // todo: pagination
|
||||
.json( |
||||
await hookService.tableSampleData({ |
||||
tableId: req.params.tableId, |
||||
// todo: replace any with type
|
||||
operation: req.params.operation as any, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/hooks', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookList, 'hookList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/hooks/test', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookTest, 'hookTest') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/hooks', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookCreate, 'hookCreate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/hooks/:hookId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookDelete, 'hookDelete') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/hooks/:hookId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookUpdate, 'hookUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/hooks/samplePayload/:operation', |
||||
metaApiMetrics, |
||||
catchError(tableSampleData) |
||||
); |
||||
export default router; |
@ -0,0 +1,90 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { T } from 'nc-help'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { hookFilterService } from '../services'; |
||||
|
||||
export async function filterGet(req: Request, res: Response) { |
||||
const filter = await hookFilterService.filterGet({ |
||||
hookId: req.params.hookId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterList(req: Request, res: Response) { |
||||
const filter = await hookFilterService.filterList({ |
||||
hookId: req.params.hookId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterChildrenRead(req: Request, res: Response) { |
||||
const filter = await hookFilterService.filterChildrenRead({ |
||||
hookId: req.params.hookId, |
||||
filterParentId: req.params.filterParentId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterCreate(req: Request<any, any>, res) { |
||||
const filter = await hookFilterService.filterCreate({ |
||||
filter: req.body, |
||||
hookId: req.params.hookId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterUpdate(req, res) { |
||||
const filter = await hookFilterService.filterUpdate({ |
||||
filterId: req.params.filterId, |
||||
filter: req.body, |
||||
hookId: req.params.hookId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function filterDelete(req: Request, res: Response) { |
||||
const filter = await hookFilterService.filterDelete({ |
||||
filterId: req.params.filterId, |
||||
}); |
||||
T.emit('evt', { evt_type: 'hookFilter:deleted' }); |
||||
res.json(filter); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/hooks/:hookId/filters/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/hooks/:hookId/filters/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterCreate, 'filterCreate') |
||||
); |
||||
router.get( |
||||
'/hooks/:hookId/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterGet, 'filterGet') |
||||
); |
||||
router.patch( |
||||
'/hooks/:hookId/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterUpdate, 'filterUpdate') |
||||
); |
||||
router.delete( |
||||
'/hooks/:hookId/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterDelete, 'filterDelete') |
||||
); |
||||
router.get( |
||||
'/hooks/:hookId/filters/:filterParentId/children', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead') |
||||
); |
||||
export default router; |
@ -1,29 +1,30 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { MapType, ViewTypes } from 'nocodb-sdk'; |
||||
import View from '../../models/View'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { Tele } from 'nc-help'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import MapView from '../../models/MapView'; |
||||
import { MapType } from 'nocodb-sdk'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { mapViewService } from '../services'; |
||||
|
||||
export async function mapViewGet(req: Request, res: Response<MapType>) { |
||||
res.json(await MapView.get(req.params.mapViewId)); |
||||
res.json( |
||||
await mapViewService.mapViewGet({ mapViewId: req.params.mapViewId }) |
||||
); |
||||
} |
||||
|
||||
export async function mapViewCreate(req: Request<any, any>, res) { |
||||
Tele.emit('evt', { evt_type: 'vtable:created', show_as: 'map' }); |
||||
const view = await View.insert({ |
||||
...req.body, |
||||
// todo: sanitize
|
||||
fk_model_id: req.params.tableId, |
||||
type: ViewTypes.MAP, |
||||
const view = await mapViewService.mapViewCreate({ |
||||
tableId: req.params.tableId, |
||||
map: req.body, |
||||
}); |
||||
res.json(view); |
||||
} |
||||
|
||||
export async function mapViewUpdate(req, res) { |
||||
Tele.emit('evt', { evt_type: 'view:updated', type: 'map' }); |
||||
res.json(await MapView.update(req.params.mapViewId, req.body)); |
||||
res.json( |
||||
await mapViewService.mapViewUpdate({ |
||||
mapViewId: req.params.mapViewId, |
||||
map: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
@ -0,0 +1,54 @@
|
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { Router } from 'express'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { metaDiffService } from '../services'; |
||||
|
||||
export async function metaDiff(req, res) { |
||||
res.json(await metaDiffService.metaDiff({ projectId: req.params.projectId })); |
||||
} |
||||
|
||||
export async function baseMetaDiff(req, res) { |
||||
res.json( |
||||
await metaDiffService.baseMetaDiff({ |
||||
baseId: req.params.baseId, |
||||
projectId: req.params.projectId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function metaDiffSync(req, res) { |
||||
await metaDiffService.metaDiffSync({ projectId: req.params.projectId }); |
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
export async function baseMetaDiffSync(req, res) { |
||||
await metaDiffService.baseMetaDiffSync({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router(); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(metaDiff, 'metaDiff') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(metaDiffSync, 'metaDiffSync') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseMetaDiff, 'baseMetaDiff') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/meta-diff/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseMetaDiffSync, 'baseMetaDiffSync') |
||||
); |
||||
export default router; |
@ -0,0 +1,34 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { modelVisibilityService } from '../services'; |
||||
|
||||
async function xcVisibilityMetaSetAll(req, res) { |
||||
await modelVisibilityService.xcVisibilityMetaSetAll({ |
||||
visibilityRule: req.body, |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/visibility-rules', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(async (req, res) => { |
||||
res.json( |
||||
await modelVisibilityService.xcVisibilityMetaGet({ |
||||
projectId: req.params.projectId, |
||||
includeM2M: |
||||
req.query.includeM2M === true || req.query.includeM2M === 'true', |
||||
}) |
||||
); |
||||
}, 'modelVisibilityList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/visibility-rules', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(xcVisibilityMetaSetAll, 'modelVisibilitySet') |
||||
); |
||||
export default router; |
@ -0,0 +1,63 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { getConditionalHandler } from '../meta/helpers/getHandler'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { orgTokenService, orgTokenServiceEE } from '../services'; |
||||
|
||||
async function apiTokenList(req, res) { |
||||
res.json( |
||||
await getConditionalHandler( |
||||
orgTokenService.apiTokenList, |
||||
orgTokenServiceEE.apiTokenListEE |
||||
)({ |
||||
query: req.query, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function apiTokenCreate(req: Request, res: Response) { |
||||
res.json( |
||||
await orgTokenService.apiTokenCreate({ |
||||
apiToken: req.body, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function apiTokenDelete(req: Request, res: Response) { |
||||
res.json( |
||||
await orgTokenService.apiTokenDelete({ |
||||
token: req.params.token, |
||||
user: req['user'], |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenList, 'apiTokenList', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.delete( |
||||
'/api/v1/tokens/:token', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
export default router; |
@ -0,0 +1,154 @@
|
||||
import { Router } from 'express'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { orgUserService } from '../services'; |
||||
|
||||
async function userList(req, res) { |
||||
res.json( |
||||
await orgUserService.userList({ |
||||
query: req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function userUpdate(req, res) { |
||||
res.json( |
||||
await orgUserService.userUpdate({ |
||||
user: req.body, |
||||
userId: req.params.userId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function userDelete(req, res) { |
||||
await orgUserService.userDelete({ |
||||
userId: req.params.userId, |
||||
}); |
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
async function userAdd(req, res) { |
||||
const result = await orgUserService.userAdd({ |
||||
user: req.body, |
||||
req, |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
async function userSettings(_req, res): Promise<any> { |
||||
await orgUserService.userSettings({}); |
||||
res.json({}); |
||||
} |
||||
|
||||
async function userInviteResend(req, res): Promise<any> { |
||||
await orgUserService.userInviteResend({ |
||||
userId: req.params.userId, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
async function generateResetUrl(req, res) { |
||||
const result = await orgUserService.generateResetUrl({ |
||||
siteUrl: req.ncSiteUrl, |
||||
userId: req.params.userId, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
async function appSettingsGet(_req, res) { |
||||
const settings = await orgUserService.appSettingsGet(); |
||||
res.json(settings); |
||||
} |
||||
|
||||
async function appSettingsSet(req, res) { |
||||
await orgUserService.appSettingsSet({ |
||||
settings: req.body, |
||||
}); |
||||
|
||||
res.json({ msg: 'Settings saved' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userList, 'userList', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.patch( |
||||
'/api/v1/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userUpdate, 'userUpdate', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.delete( |
||||
'/api/v1/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userDelete, 'userDelete', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userAdd, 'userAdd', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users/settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userSettings, 'userSettings', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users/:userId/resend-invite', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userInviteResend, 'userInviteResend', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/users/:userId/generate-reset-url', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(generateResetUrl, 'generateResetUrl', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/app-settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(appSettingsGet, 'appSettingsGet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/app-settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(appSettingsSet, 'appSettingsSet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
export default router; |
@ -0,0 +1,169 @@
|
||||
import { Request, Response } from 'express'; |
||||
import { ProjectType } from 'nocodb-sdk'; |
||||
import Project from '../models/Project'; |
||||
import { ProjectListType } from 'nocodb-sdk'; |
||||
import { packageVersion } from '../utils/packageVersion'; |
||||
import { T } from 'nc-help'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import ProjectUser from '../models/ProjectUser'; |
||||
import Noco from '../Noco'; |
||||
import isDocker from 'is-docker'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import Filter from '../models/Filter'; |
||||
|
||||
import { projectService } from '../services'; |
||||
|
||||
// // Project CRUD
|
||||
|
||||
export async function projectGet( |
||||
req: Request<any, any, any>, |
||||
res: Response<Project> |
||||
) { |
||||
const project = await projectService.getProjectWithInfo({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
projectService.sanitizeProject(project); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectUpdate( |
||||
req: Request<any, any, any>, |
||||
res: Response<ProjectListType> |
||||
) { |
||||
const project = await projectService.projectUpdate({ |
||||
projectId: req.params.projectId, |
||||
project: req.body, |
||||
}); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectList( |
||||
req: Request<any> & { user: { id: string; roles: string } }, |
||||
res: Response<ProjectListType> |
||||
) { |
||||
const projects = await projectService.projectList({ |
||||
user: req.user, |
||||
query: req.query, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(projects as ProjectType[], { |
||||
count: projects.length, |
||||
limit: projects.length, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function projectDelete(req: Request<any>, res: Response<boolean>) { |
||||
const deleted = await projectService.projectSoftDelete({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json(deleted); |
||||
} |
||||
|
||||
async function projectCreate(req: Request<any, any>, res) { |
||||
const project = await projectService.projectCreate({ |
||||
project: req.body, |
||||
user: req['user'], |
||||
}); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectInfoGet(_req, res) { |
||||
res.json({ |
||||
Node: process.version, |
||||
Arch: process.arch, |
||||
Platform: process.platform, |
||||
Docker: isDocker(), |
||||
RootDB: Noco.getConfig()?.meta?.db?.client, |
||||
PackageVersion: packageVersion, |
||||
}); |
||||
} |
||||
|
||||
export async function projectCost(req, res) { |
||||
let cost = 0; |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
|
||||
for (const base of project.bases) { |
||||
const sqlClient = await NcConnectionMgrv2.getSqlClient(base); |
||||
const userCount = await ProjectUser.getUsersCount(req.query); |
||||
const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords; |
||||
|
||||
if (recordCount > 100000) { |
||||
// 36,000 or $79/user/month
|
||||
cost = Math.max(36000, 948 * userCount); |
||||
} else if (recordCount > 50000) { |
||||
// $36,000 or $50/user/month
|
||||
cost = Math.max(36000, 600 * userCount); |
||||
} else if (recordCount > 10000) { |
||||
// $240/user/yr
|
||||
cost = Math.min(240 * userCount, 36000); |
||||
} else if (recordCount > 1000) { |
||||
// $120/user/yr
|
||||
cost = Math.min(120 * userCount, 36000); |
||||
} |
||||
} |
||||
|
||||
T.event({ |
||||
event: 'a:project:cost', |
||||
data: { |
||||
cost, |
||||
}, |
||||
}); |
||||
|
||||
res.json({ cost }); |
||||
} |
||||
|
||||
export async function hasEmptyOrNullFilters(req, res) { |
||||
res.json(await Filter.hasEmptyOrNullFilters(req.params.projectId)); |
||||
} |
||||
|
||||
export default (router) => { |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/info', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectInfoGet, 'projectInfoGet') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectGet, 'projectGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUpdate, 'projectUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/cost', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectCost, 'projectCost') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectDelete, 'projectDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectCreate, 'projectCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectList, 'projectList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/has-empty-or-null-filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hasEmptyOrNullFilters, 'hasEmptyOrNullFilters') |
||||
); |
||||
}; |
@ -0,0 +1,85 @@
|
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { Router } from 'express'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { projectUserService } from '../services'; |
||||
|
||||
async function userList(req, res) { |
||||
res.json({ |
||||
users: await projectUserService.userList({ |
||||
projectId: req.params.projectId, |
||||
query: req.query, |
||||
}), |
||||
}); |
||||
} |
||||
|
||||
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> { |
||||
res.json( |
||||
await projectUserService.projectUserUpdate({ |
||||
projectUser: req.body, |
||||
projectId: req.params.projectId, |
||||
userId: req.params.userId, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function projectUserDelete(req, res): Promise<any> { |
||||
await projectUserService.projectUserDelete({ |
||||
projectId: req.params.projectId, |
||||
userId: req.params.userId, |
||||
req, |
||||
}); |
||||
res.json({ |
||||
msg: 'success', |
||||
}); |
||||
} |
||||
|
||||
async function projectUserInviteResend(req, res): Promise<any> { |
||||
res.json( |
||||
await projectUserService.projectUserInviteResend({ |
||||
projectId: req.params.projectId, |
||||
userId: req.params.userId, |
||||
projectUser: req.body, |
||||
req, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userList, 'userList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userInvite, 'userInvite') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserUpdate, 'projectUserUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserDelete, 'projectUserDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId/resend-invite', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserInviteResend, 'projectUserInviteResend') |
||||
); |
||||
export default router; |
@ -0,0 +1,9 @@
|
||||
import publicDataController from './publicDataController'; |
||||
import publicDataExportController from './publicDataExportController'; |
||||
import publicMetaController from './publicMetaController'; |
||||
|
||||
export { |
||||
publicDataController, |
||||
publicDataExportController, |
||||
publicMetaController, |
||||
}; |
@ -0,0 +1,105 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import multer from 'multer'; |
||||
import { NC_ATTACHMENT_FIELD_SIZE } from '../../constants'; |
||||
import catchError from '../../meta/helpers/catchError'; |
||||
import { publicDataService } from '../../services'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
const pagedResponse = await publicDataService.dataList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
}); |
||||
res.json({ data: pagedResponse }); |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function groupedDataList(req: Request, res: Response) { |
||||
const groupedData = await publicDataService.groupedDataList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
groupColumnId: req.params.columnId, |
||||
}); |
||||
res.json(groupedData); |
||||
} |
||||
|
||||
async function dataInsert(req: Request & { files: any[] }, res: Response) { |
||||
const insertResult = await publicDataService.dataInsert({ |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
password: req.headers?.['xc-password'] as string, |
||||
body: req.body?.data, |
||||
siteUrl: (req as any).ncSiteUrl, |
||||
files: req.files, |
||||
}); |
||||
|
||||
res.json(insertResult); |
||||
} |
||||
|
||||
async function relDataList(req, res) { |
||||
const pagedResponse = await publicDataService.relDataList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
columnId: req.params.columnId, |
||||
}); |
||||
|
||||
res.json(pagedResponse); |
||||
} |
||||
|
||||
export async function publicMmList(req: Request, res: Response) { |
||||
const paginatedResponse = await publicDataService.publicMmList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
columnId: req.params.columnId, |
||||
rowId: req.params.rowId, |
||||
}); |
||||
res.json(paginatedResponse); |
||||
} |
||||
|
||||
export async function publicHmList(req: Request, res: Response) { |
||||
const paginatedResponse = await publicDataService.publicHmList({ |
||||
query: req.query, |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
columnId: req.params.columnId, |
||||
rowId: req.params.rowId, |
||||
}); |
||||
res.json(paginatedResponse); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows', |
||||
catchError(dataList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/group/:columnId', |
||||
catchError(groupedDataList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/nested/:columnId', |
||||
catchError(relDataList) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows', |
||||
multer({ |
||||
storage: multer.diskStorage({}), |
||||
limits: { |
||||
fieldSize: NC_ATTACHMENT_FIELD_SIZE, |
||||
}, |
||||
}).any(), |
||||
catchError(dataInsert) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/mm/:colId', |
||||
catchError(publicMmList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/hm/:colId', |
||||
catchError(publicHmList) |
||||
); |
||||
|
||||
export default router; |
@ -1,17 +1,19 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import * as XLSX from 'xlsx'; |
||||
import View from '../../../models/View'; |
||||
import Model from '../../../models/Model'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import papaparse from 'papaparse'; |
||||
import { ErrorMessages, isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk'; |
||||
import Column from '../../../models/Column'; |
||||
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn'; |
||||
import LookupColumn from '../../../models/LookupColumn'; |
||||
import catchError, { NcError } from '../../helpers/catchError'; |
||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import catchError, { NcError } from '../../meta/helpers/catchError'; |
||||
import { |
||||
Base, |
||||
Column, |
||||
LinkToAnotherRecordColumn, |
||||
LookupColumn, |
||||
Model, |
||||
View, |
||||
} from '../../models'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
|
||||
async function exportExcel(req: Request, res: Response) { |
||||
const view = await View.getByUUID(req.params.publicDataUuid); |
@ -0,0 +1,31 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import catchError from '../../meta/helpers/catchError'; |
||||
import { publicMetaService } from '../../services'; |
||||
|
||||
export async function viewMetaGet(req: Request, res: Response) { |
||||
res.json( |
||||
await publicMetaService.viewMetaGet({ |
||||
password: req.headers?.['xc-password'] as string, |
||||
sharedViewUuid: req.params.sharedViewUuid, |
||||
}) |
||||
); |
||||
} |
||||
async function publicSharedBaseGet(req, res): Promise<any> { |
||||
res.json( |
||||
await publicMetaService.publicSharedBaseGet({ |
||||
sharedBaseUuid: req.params.sharedBaseUuid, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/meta', |
||||
catchError(viewMetaGet) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/public/shared-base/:sharedBaseUuid/meta', |
||||
catchError(publicSharedBaseGet) |
||||
); |
||||
export default router; |
@ -0,0 +1,61 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { sharedBaseService } from '../services'; |
||||
|
||||
async function createSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.createSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
roles: req.body?.roles, |
||||
password: req.body?.password, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
async function updateSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.updateSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
roles: req.body?.roles, |
||||
password: req.body?.password, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
async function disableSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.disableSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
async function getSharedBaseLink(req, res): Promise<any> { |
||||
const sharedBase = await sharedBaseService.getSharedBaseLink({ |
||||
projectId: req.params.projectId, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(sharedBase); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(getSharedBaseLink, 'getSharedBaseLink') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(createSharedBaseLink, 'createSharedBaseLink') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(updateSharedBaseLink, 'updateSharedBaseLink') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(disableSharedBaseLink, 'disableSharedBaseLink') |
||||
); |
||||
export default router; |
@ -0,0 +1,80 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { SortListType, SortReqType } from 'nocodb-sdk'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
|
||||
import { sortService } from '../services'; |
||||
|
||||
// @ts-ignore
|
||||
export async function sortList( |
||||
req: Request<any, any, any>, |
||||
res: Response<SortListType> |
||||
) { |
||||
const sortList = await sortService.sortList({ |
||||
viewId: req.params.viewId, |
||||
}); |
||||
res.json({ |
||||
sorts: new PagedResponseImpl(sortList), |
||||
}); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function sortCreate(req: Request<any, any, SortReqType>, res) { |
||||
const sort = await sortService.sortCreate({ |
||||
sort: req.body, |
||||
viewId: req.params.viewId, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortUpdate(req, res) { |
||||
const sort = await sortService.sortUpdate({ |
||||
sortId: req.params.sortId, |
||||
sort: req.body, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortDelete(req: Request, res: Response) { |
||||
const sort = await sortService.sortDelete({ |
||||
sortId: req.params.sortId, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
export async function sortGet(req: Request, res: Response) { |
||||
const sort = await sortService.sortGet({ |
||||
sortId: req.params.sortId, |
||||
}); |
||||
res.json(sort); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/views/:viewId/sorts/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortList, 'sortList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/sorts/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortCreate, 'sortCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortGet, 'sortGet') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortUpdate, 'sortUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortDelete, 'sortDelete') |
||||
); |
||||
export default router; |
@ -0,0 +1,36 @@
|
||||
import { Router } from 'express'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import getSwaggerHtml from './swaggerHtml'; |
||||
import getRedocHtml from './redocHtml'; |
||||
import { swaggerService } from '../../services'; |
||||
|
||||
async function swaggerJson(req, res) { |
||||
const swagger = await swaggerService.swaggerJson({ |
||||
projectId: req.params.projectId, |
||||
siteUrl: req.ncSiteUrl, |
||||
}); |
||||
|
||||
res.json(swagger); |
||||
} |
||||
|
||||
function swaggerHtml(_, res) { |
||||
res.send(getSwaggerHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })); |
||||
} |
||||
|
||||
function redocHtml(_, res) { |
||||
res.send(getRedocHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// todo: auth
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/swagger.json', |
||||
ncMetaAclMw(swaggerJson, 'swaggerJson') |
||||
); |
||||
|
||||
router.get('/api/v1/db/meta/projects/:projectId/swagger', swaggerHtml); |
||||
|
||||
router.get('/api/v1/db/meta/projects/:projectId/redoc', redocHtml); |
||||
|
||||
export default router; |
@ -1,42 +1,42 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
|
||||
import SyncSource from '../../../models/SyncSource'; |
||||
import { Tele } from 'nc-help'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import Project from '../../../models/Project'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import { syncService } from '../../services'; |
||||
|
||||
export async function syncSourceList(req: Request, res: Response) { |
||||
// todo: pagination
|
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await SyncSource.list(req.params.projectId, req.params.baseId) |
||||
) |
||||
syncService.syncSourceList({ |
||||
projectId: req.params.projectId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function syncCreate(req: Request, res: Response) { |
||||
Tele.emit('evt', { evt_type: 'webhooks:created' }); |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
|
||||
const sync = await SyncSource.insert({ |
||||
...req.body, |
||||
fk_user_id: (req as any).user.id, |
||||
base_id: req.params.baseId ? req.params.baseId : project.bases[0].id, |
||||
project_id: req.params.projectId, |
||||
}); |
||||
res.json(sync); |
||||
res.json( |
||||
await syncService.syncCreate({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
userId: (req as any).user.id, |
||||
syncPayload: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function syncDelete(req: Request, res: Response<any>) { |
||||
Tele.emit('evt', { evt_type: 'webhooks:deleted' }); |
||||
res.json(await SyncSource.delete(req.params.syncId)); |
||||
res.json( |
||||
await syncService.syncDelete({ |
||||
syncId: req.params.syncId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function syncUpdate(req: Request, res: Response) { |
||||
Tele.emit('evt', { evt_type: 'webhooks:updated' }); |
||||
|
||||
res.json(await SyncSource.update(req.params.syncId, req.body)); |
||||
res.json( |
||||
await syncService.syncUpdate({ |
||||
syncId: req.params.syncId, |
||||
syncPayload: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
@ -0,0 +1,111 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { TableListType, TableReqType, TableType } from 'nocodb-sdk'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { tableService } from '../services'; |
||||
|
||||
export async function tableList(req: Request, res: Response<TableListType>) { |
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await tableService.getAccessibleTables({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
includeM2M: req.query?.includeM2M === 'true', |
||||
roles: (req as any).session?.passport?.user?.roles, |
||||
}) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function tableCreate(req: Request<any, any, TableReqType>, res) { |
||||
const result = await tableService.tableCreate({ |
||||
projectId: req.params.projectId, |
||||
baseId: req.params.baseId, |
||||
table: req.body, |
||||
user: (req as any).session?.passport?.user, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
export async function tableGet(req: Request, res: Response<TableType>) { |
||||
const table = await tableService.getTableWithAccessibleViews({ |
||||
tableId: req.params.tableId, |
||||
user: (req as any).session?.passport?.user, |
||||
}); |
||||
|
||||
res.json(table); |
||||
} |
||||
|
||||
export async function tableDelete(req: Request, res: Response) { |
||||
const result = await tableService.tableDelete({ |
||||
tableId: req.params.tableId, |
||||
user: (req as any).session?.passport?.user, |
||||
req, |
||||
}); |
||||
|
||||
res.json(result); |
||||
} |
||||
|
||||
export async function tableReorder(req: Request, res: Response) { |
||||
res.json( |
||||
await tableService.reorderTable({ |
||||
tableId: req.params.tableId, |
||||
order: req.body.order, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: move to table service
|
||||
export async function tableUpdate(req: Request<any, any>, res) { |
||||
await tableService.tableUpdate({ |
||||
tableId: req.params.tableId, |
||||
table: req.body, |
||||
projectId: (req as any).ncProjectId, |
||||
}); |
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableList, 'tableList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableList, 'tableList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableCreate, 'tableCreate') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableCreate, 'tableCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableGet, 'tableGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableUpdate, 'tableUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableDelete, 'tableDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/reorder', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableReorder, 'tableReorder') |
||||
); |
||||
export default router; |
@ -1,5 +1,5 @@
|
||||
import { Request, Router } from 'express'; |
||||
import { TestResetService } from '../../services/test/TestResetService'; |
||||
import { TestResetService } from '../services/test/TestResetService'; |
||||
|
||||
export async function reset(req: Request<any, any>, res) { |
||||
const service = new TestResetService({ |
@ -0,0 +1,445 @@
|
||||
import { Request, Response } from 'express'; |
||||
import { TableType, validatePassword } from 'nocodb-sdk'; |
||||
import { T } from 'nc-help'; |
||||
|
||||
const { isEmail } = require('validator'); |
||||
import * as ejs from 'ejs'; |
||||
|
||||
import bcrypt from 'bcryptjs'; |
||||
import { promisify } from 'util'; |
||||
|
||||
const { v4: uuidv4 } = require('uuid'); |
||||
|
||||
import passport from 'passport'; |
||||
import { getAjvValidatorMw } from '../../meta/api/helpers'; |
||||
import catchError, { NcError } from '../../meta/helpers/catchError'; |
||||
import extractProjectIdAndAuthenticate from '../../meta/helpers/extractProjectIdAndAuthenticate'; |
||||
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||
import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2'; |
||||
import { Audit, User } from '../../models'; |
||||
import Noco from '../../Noco'; |
||||
import { userService } from '../../services'; |
||||
|
||||
export async function signup(req: Request, res: Response<TableType>) { |
||||
const { |
||||
email: _email, |
||||
firstname, |
||||
lastname, |
||||
token, |
||||
ignore_subscribe, |
||||
} = req.body; |
||||
let { password } = req.body; |
||||
|
||||
// validate password and throw error if password is satisfying the conditions
|
||||
const { valid, error } = validatePassword(password); |
||||
if (!valid) { |
||||
NcError.badRequest(`Password : ${error}`); |
||||
} |
||||
|
||||
if (!isEmail(_email)) { |
||||
NcError.badRequest(`Invalid email`); |
||||
} |
||||
|
||||
const email = _email.toLowerCase(); |
||||
|
||||
let user = await User.getByEmail(email); |
||||
|
||||
if (user) { |
||||
if (token) { |
||||
if (token !== user.invite_token) { |
||||
NcError.badRequest(`Invalid invite url`); |
||||
} else if (user.invite_token_expires < new Date()) { |
||||
NcError.badRequest( |
||||
'Expired invite url, Please contact super admin to get a new invite url' |
||||
); |
||||
} |
||||
} else { |
||||
// todo : opening up signup for timebeing
|
||||
// return next(new Error(`Email '${email}' already registered`));
|
||||
} |
||||
} |
||||
|
||||
const salt = await promisify(bcrypt.genSalt)(10); |
||||
password = await promisify(bcrypt.hash)(password, salt); |
||||
const email_verification_token = uuidv4(); |
||||
|
||||
if (!ignore_subscribe) { |
||||
T.emit('evt_subscribe', email); |
||||
} |
||||
|
||||
if (user) { |
||||
if (token) { |
||||
await User.update(user.id, { |
||||
firstname, |
||||
lastname, |
||||
salt, |
||||
password, |
||||
email_verification_token, |
||||
invite_token: null, |
||||
invite_token_expires: null, |
||||
email: user.email, |
||||
}); |
||||
} else { |
||||
NcError.badRequest('User already exist'); |
||||
} |
||||
} else { |
||||
await userService.registerNewUserIfAllowed({ |
||||
firstname, |
||||
lastname, |
||||
email, |
||||
salt, |
||||
password, |
||||
email_verification_token, |
||||
}); |
||||
} |
||||
user = await User.getByEmail(email); |
||||
|
||||
try { |
||||
const template = (await import('./ui/emailTemplates/verify')).default; |
||||
await ( |
||||
await NcPluginMgrv2.emailAdapter() |
||||
).mailSend({ |
||||
to: email, |
||||
subject: 'Verify email', |
||||
html: ejs.render(template, { |
||||
verifyLink: |
||||
(req as any).ncSiteUrl + |
||||
`/email/verify/${user.email_verification_token}`, |
||||
}), |
||||
}); |
||||
} catch (e) { |
||||
console.log( |
||||
'Warning : `mailSend` failed, Please configure emailClient configuration.' |
||||
); |
||||
} |
||||
await promisify((req as any).login.bind(req))(user); |
||||
const refreshToken = userService.randomTokenString(); |
||||
await User.update(user.id, { |
||||
refresh_token: refreshToken, |
||||
email: user.email, |
||||
}); |
||||
|
||||
setTokenCookie(res, refreshToken); |
||||
|
||||
user = (req as any).user; |
||||
|
||||
await Audit.insert({ |
||||
op_type: 'AUTHENTICATION', |
||||
op_sub_type: 'SIGNUP', |
||||
user: user.email, |
||||
description: `signed up `, |
||||
ip: (req as any).clientIp, |
||||
}); |
||||
|
||||
res.json({ |
||||
token: userService.genJwt(user, Noco.getConfig()), |
||||
} as any); |
||||
} |
||||
|
||||
async function successfulSignIn({ |
||||
user, |
||||
err, |
||||
info, |
||||
req, |
||||
res, |
||||
auditDescription, |
||||
}) { |
||||
try { |
||||
if (!user || !user.email) { |
||||
if (err) { |
||||
return res.status(400).send(err); |
||||
} |
||||
if (info) { |
||||
return res.status(400).send(info); |
||||
} |
||||
return res.status(400).send({ msg: 'Your signin has failed' }); |
||||
} |
||||
|
||||
await promisify((req as any).login.bind(req))(user); |
||||
const refreshToken = userService.randomTokenString(); |
||||
|
||||
if (!user.token_version) { |
||||
user.token_version = userService.randomTokenString(); |
||||
} |
||||
|
||||
await User.update(user.id, { |
||||
refresh_token: refreshToken, |
||||
email: user.email, |
||||
token_version: user.token_version, |
||||
}); |
||||
setTokenCookie(res, refreshToken); |
||||
|
||||
await Audit.insert({ |
||||
op_type: 'AUTHENTICATION', |
||||
op_sub_type: 'SIGNIN', |
||||
user: user.email, |
||||
ip: req.clientIp, |
||||
description: auditDescription, |
||||
}); |
||||
|
||||
res.json({ |
||||
token: userService.genJwt(user, Noco.getConfig()), |
||||
} as any); |
||||
} catch (e) { |
||||
console.log(e); |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
async function signin(req, res, next) { |
||||
passport.authenticate( |
||||
'local', |
||||
{ session: false }, |
||||
async (err, user, info): Promise<any> => |
||||
await successfulSignIn({ |
||||
user, |
||||
err, |
||||
info, |
||||
req, |
||||
res, |
||||
auditDescription: 'signed in', |
||||
}) |
||||
)(req, res, next); |
||||
} |
||||
|
||||
async function googleSignin(req, res, next) { |
||||
passport.authenticate( |
||||
'google', |
||||
{ |
||||
session: false, |
||||
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath, |
||||
}, |
||||
async (err, user, info): Promise<any> => |
||||
await successfulSignIn({ |
||||
user, |
||||
err, |
||||
info, |
||||
req, |
||||
res, |
||||
auditDescription: 'signed in using Google Auth', |
||||
}) |
||||
)(req, res, next); |
||||
} |
||||
|
||||
function setTokenCookie(res: Response, token): void { |
||||
// create http only cookie with refresh token that expires in 7 days
|
||||
const cookieOptions = { |
||||
httpOnly: true, |
||||
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), |
||||
}; |
||||
res.cookie('refresh_token', token, cookieOptions); |
||||
} |
||||
|
||||
async function me(req, res): Promise<any> { |
||||
res.json(req?.session?.passport?.user ?? {}); |
||||
} |
||||
|
||||
async function passwordChange(req: Request<any, any>, res): Promise<any> { |
||||
if (!(req as any).isAuthenticated()) { |
||||
NcError.forbidden('Not allowed'); |
||||
} |
||||
|
||||
await userService.passwordChange({ |
||||
user: req['user'], |
||||
req, |
||||
body: req.body, |
||||
}); |
||||
|
||||
res.json({ msg: 'Password updated successfully' }); |
||||
} |
||||
|
||||
async function passwordForgot(req: Request<any, any>, res): Promise<any> { |
||||
await userService.passwordForgot({ |
||||
siteUrl: (req as any).ncSiteUrl, |
||||
body: req.body, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'Please check your email to reset the password' }); |
||||
} |
||||
|
||||
async function tokenValidate(req, res): Promise<any> { |
||||
await userService.tokenValidate({ |
||||
token: req.params.tokenId, |
||||
}); |
||||
res.json(true); |
||||
} |
||||
|
||||
async function passwordReset(req, res): Promise<any> { |
||||
await userService.passwordReset({ |
||||
token: req.params.tokenId, |
||||
body: req.body, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'Password reset successful' }); |
||||
} |
||||
|
||||
async function emailVerification(req, res): Promise<any> { |
||||
await userService.emailVerification({ |
||||
token: req.params.tokenId, |
||||
req, |
||||
}); |
||||
|
||||
res.json({ msg: 'Email verified successfully' }); |
||||
} |
||||
|
||||
async function refreshToken(req, res): Promise<any> { |
||||
try { |
||||
if (!req?.cookies?.refresh_token) { |
||||
return res.status(400).json({ msg: 'Missing refresh token' }); |
||||
} |
||||
|
||||
const user = await User.getByRefreshToken(req.cookies.refresh_token); |
||||
|
||||
if (!user) { |
||||
return res.status(400).json({ msg: 'Invalid refresh token' }); |
||||
} |
||||
|
||||
const refreshToken = userService.randomTokenString(); |
||||
|
||||
await User.update(user.id, { |
||||
email: user.email, |
||||
refresh_token: refreshToken, |
||||
}); |
||||
|
||||
setTokenCookie(res, refreshToken); |
||||
|
||||
res.json({ |
||||
token: userService.genJwt(user, Noco.getConfig()), |
||||
} as any); |
||||
} catch (e) { |
||||
return res.status(400).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
async function renderPasswordReset(req, res): Promise<any> { |
||||
try { |
||||
res.send( |
||||
ejs.render((await import('./ui/auth/resetPassword')).default, { |
||||
ncPublicUrl: process.env.NC_PUBLIC_URL || '', |
||||
token: JSON.stringify(req.params.tokenId), |
||||
baseUrl: `/`, |
||||
}) |
||||
); |
||||
} catch (e) { |
||||
return res.status(400).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
const mapRoutes = (router) => { |
||||
// todo: old api - /auth/signup?tool=1
|
||||
router.post( |
||||
'/auth/user/signup', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'), |
||||
catchError(signup) |
||||
); |
||||
router.post( |
||||
'/auth/user/signin', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'), |
||||
catchError(signin) |
||||
); |
||||
router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me)); |
||||
router.post('/auth/password/forgot', catchError(passwordForgot)); |
||||
router.post('/auth/token/validate/:tokenId', catchError(tokenValidate)); |
||||
router.post( |
||||
'/auth/password/reset/:tokenId', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/PasswordResetReq'), |
||||
catchError(passwordReset) |
||||
); |
||||
router.post('/auth/email/validate/:tokenId', catchError(emailVerification)); |
||||
router.post( |
||||
'/user/password/change', |
||||
ncMetaAclMw(passwordChange, 'passwordChange') |
||||
); |
||||
router.post('/auth/token/refresh', catchError(refreshToken)); |
||||
|
||||
/* Google auth apis */ |
||||
|
||||
router.post(`/auth/google/genTokenByCode`, catchError(googleSignin)); |
||||
|
||||
router.get('/auth/google', (req: any, res, next) => |
||||
passport.authenticate('google', { |
||||
scope: ['profile', 'email'], |
||||
state: req.query.state, |
||||
callbackURL: req.ncSiteUrl + Noco.getConfig().dashboardPath, |
||||
})(req, res, next) |
||||
); |
||||
|
||||
// deprecated APIs
|
||||
router.post( |
||||
'/api/v1/db/auth/user/signup', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'), |
||||
catchError(signup) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/auth/user/signin', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'), |
||||
catchError(signin) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/auth/user/me', |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(me) |
||||
); |
||||
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', |
||||
catchError(passwordReset) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/auth/email/validate/:tokenId', |
||||
catchError(emailVerification) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/auth/password/change', |
||||
ncMetaAclMw(passwordChange, 'passwordChange') |
||||
); |
||||
router.post('/api/v1/db/auth/token/refresh', catchError(refreshToken)); |
||||
router.get( |
||||
'/api/v1/db/auth/password/reset/:tokenId', |
||||
catchError(renderPasswordReset) |
||||
); |
||||
|
||||
// new API
|
||||
router.post( |
||||
'/api/v1/auth/user/signup', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SignUpReq'), |
||||
catchError(signup) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/user/signin', |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'), |
||||
catchError(signin) |
||||
); |
||||
router.get( |
||||
'/api/v1/auth/user/me', |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(me) |
||||
); |
||||
router.post('/api/v1/auth/password/forgot', catchError(passwordForgot)); |
||||
router.post( |
||||
'/api/v1/auth/token/validate/:tokenId', |
||||
catchError(tokenValidate) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/password/reset/:tokenId', |
||||
catchError(passwordReset) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/email/validate/:tokenId', |
||||
catchError(emailVerification) |
||||
); |
||||
router.post( |
||||
'/api/v1/auth/password/change', |
||||
ncMetaAclMw(passwordChange, 'passwordChange') |
||||
); |
||||
router.post('/api/v1/auth/token/refresh', catchError(refreshToken)); |
||||
// respond with password reset page
|
||||
router.get('/auth/password/reset/:tokenId', catchError(renderPasswordReset)); |
||||
}; |
||||
export { mapRoutes as userController }; |
@ -0,0 +1,59 @@
|
||||
// // Project CRUD
|
||||
import { Request, Response } from 'express'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import catchError from '../meta/helpers/catchError'; |
||||
import { utilService } from '../services'; |
||||
|
||||
export async function testConnection(req: Request, res: Response) { |
||||
res.json(await utilService.testConnection({ body: req.body })); |
||||
} |
||||
|
||||
export async function appInfo(req: Request, res: Response) { |
||||
res.json( |
||||
await utilService.appInfo({ |
||||
req: { |
||||
ncSiteUrl: (req as any).ncSiteUrl, |
||||
}, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function versionInfo(_req: Request, res: Response) { |
||||
res.json(await utilService.versionInfo()); |
||||
} |
||||
|
||||
export async function appHealth(_: Request, res: Response) { |
||||
res.json(await utilService.appHealth()); |
||||
} |
||||
|
||||
export async function axiosRequestMake(req: Request, res: Response) { |
||||
res.json(await utilService.axiosRequestMake({ body: req.body })); |
||||
} |
||||
|
||||
export async function aggregatedMetaInfo(_req: Request, res: Response) { |
||||
res.json(await utilService.aggregatedMetaInfo()); |
||||
} |
||||
|
||||
export async function urlToDbConfig(req: Request, res: Response) { |
||||
res.json( |
||||
await utilService.urlToDbConfig({ |
||||
body: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export default (router) => { |
||||
router.post( |
||||
'/api/v1/db/meta/connection/test', |
||||
ncMetaAclMw(testConnection, 'testConnection') |
||||
); |
||||
router.get('/api/v1/db/meta/nocodb/info', catchError(appInfo)); |
||||
router.post('/api/v1/db/meta/axiosRequestMake', catchError(axiosRequestMake)); |
||||
router.get('/api/v1/version', catchError(versionInfo)); |
||||
router.get('/api/v1/health', catchError(appHealth)); |
||||
router.post('/api/v1/url_to_config', catchError(urlToDbConfig)); |
||||
router.get( |
||||
'/api/v1/aggregated-meta-info', |
||||
ncMetaAclMw(aggregatedMetaInfo, 'aggregatedMetaInfo') |
||||
); |
||||
}; |
@ -0,0 +1,133 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||
import { View } from '../models'; |
||||
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||
import { viewService } from '../services'; |
||||
|
||||
// @ts-ignore
|
||||
export async function viewGet(req: Request, res: Response) {} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewList(req: Request<any, any, any>, res: Response) { |
||||
const filteredViewList = await viewService.viewList({ |
||||
tableId: req.params.tableId, |
||||
user: (req as any).session?.passport?.user, |
||||
}); |
||||
|
||||
res.json(new PagedResponseImpl(filteredViewList)); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function shareView( |
||||
req: Request<any, any, any>, |
||||
res: Response<View> |
||||
) { |
||||
res.json(await viewService.shareView({ viewId: req.params.viewId })); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewCreate(req: Request<any, any>, res, next) {} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewUpdate(req, res) { |
||||
const result = await viewService.viewUpdate({ |
||||
viewId: req.params.viewId, |
||||
view: req.body, |
||||
}); |
||||
res.json(result); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function viewDelete(req: Request, res: Response, next) { |
||||
const result = await viewService.viewDelete({ viewId: req.params.viewId }); |
||||
res.json(result); |
||||
} |
||||
|
||||
async function shareViewUpdate(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.shareViewUpdate({ |
||||
viewId: req.params.viewId, |
||||
sharedView: req.body, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function shareViewDelete(req: Request<any, any>, res) { |
||||
res.json(await viewService.shareViewDelete({ viewId: req.params.viewId })); |
||||
} |
||||
|
||||
async function showAllColumns(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.showAllColumns({ |
||||
viewId: req.params.viewId, |
||||
ignoreIds: <string[]>(req.query?.ignoreIds || []), |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function hideAllColumns(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.hideAllColumns({ |
||||
viewId: req.params.viewId, |
||||
ignoreIds: <string[]>(req.query?.ignoreIds || []), |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function shareViewList(req: Request<any, any>, res) { |
||||
res.json( |
||||
await viewService.shareViewList({ |
||||
tableId: req.params.tableId, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/views', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(viewList, 'viewList') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/views/:viewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(viewUpdate, 'viewUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/views/:viewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(viewDelete, 'viewDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/show-all', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(showAllColumns, 'showAllColumns') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/hide-all', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hideAllColumns, 'hideAllColumns') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/share', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(shareViewList, 'shareViewList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/share', |
||||
ncMetaAclMw(shareView, 'shareView') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/views/:viewId/share', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(shareViewUpdate, 'shareViewUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/views/:viewId/share', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(shareViewDelete, 'shareViewDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,53 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { Tele } from 'nc-help'; |
||||
import { NcError } from '../helpers/catchError'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import ApiToken from '../../models/ApiToken'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
export async function apiTokenList(req: Request, res: Response) { |
||||
res.json(await ApiToken.list(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 })); |
||||
} |
||||
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)); |
||||
} |
||||
|
||||
// todo: add reset token api to regenerate token
|
||||
|
||||
// deprecated apis
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenList, 'apiTokenList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/ApiTokenReq'), |
||||
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/api-tokens/:token', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,225 +0,0 @@
|
||||
// @ts-ignore
|
||||
import { Request, Response, Router } from 'express'; |
||||
import multer from 'multer'; |
||||
import { nanoid } from 'nanoid'; |
||||
import { OrgUserRoles, ProjectRoles } from 'nocodb-sdk'; |
||||
import path from 'path'; |
||||
import slash from 'slash'; |
||||
import Noco from '../../Noco'; |
||||
import { MetaTable } from '../../utils/globals'; |
||||
import mimetypes, { mimeIcons } from '../../utils/mimeTypes'; |
||||
import { Tele } from 'nc-help'; |
||||
import extractProjectIdAndAuthenticate from '../helpers/extractProjectIdAndAuthenticate'; |
||||
import catchError, { NcError } from '../helpers/catchError'; |
||||
import NcPluginMgrv2 from '../helpers/NcPluginMgrv2'; |
||||
import Local from '../../v1-legacy/plugins/adapters/storage/Local'; |
||||
import { NC_ATTACHMENT_FIELD_SIZE } from '../../constants'; |
||||
import { getCacheMiddleware, getAjvValidatorMw } from './helpers'; |
||||
|
||||
const isUploadAllowed = async (req: Request, _res: Response, next: any) => { |
||||
if (!req['user']?.id) { |
||||
if (!req['user']?.isPublicBase) { |
||||
NcError.unauthorized('Unauthorized'); |
||||
} |
||||
} |
||||
|
||||
try { |
||||
// check user is super admin or creator
|
||||
if ( |
||||
req['user'].roles?.includes(OrgUserRoles.SUPER_ADMIN) || |
||||
req['user'].roles?.includes(OrgUserRoles.CREATOR) || |
||||
req['user'].roles?.includes(ProjectRoles.EDITOR) || |
||||
// if viewer then check at-least one project have editor or higher role
|
||||
// todo: cache
|
||||
!!(await Noco.ncMeta |
||||
.knex(MetaTable.PROJECT_USERS) |
||||
.where(function () { |
||||
this.where('roles', ProjectRoles.OWNER); |
||||
this.orWhere('roles', ProjectRoles.CREATOR); |
||||
this.orWhere('roles', ProjectRoles.EDITOR); |
||||
}) |
||||
.andWhere('fk_user_id', req['user'].id) |
||||
.first()) |
||||
) |
||||
return next(); |
||||
} catch {} |
||||
NcError.badRequest('Upload not allowed'); |
||||
}; |
||||
|
||||
export async function upload(req: Request, res: Response) { |
||||
const filePath = sanitizeUrlPath( |
||||
req.query?.path?.toString()?.split('/') || [''] |
||||
); |
||||
const destPath = path.join('nc', 'uploads', ...filePath); |
||||
|
||||
const storageAdapter = await NcPluginMgrv2.storageAdapter(); |
||||
|
||||
const attachments = await Promise.all( |
||||
(req as any).files?.map(async (file) => { |
||||
const fileName = `${nanoid(18)}${path.extname(file.originalname)}`; |
||||
|
||||
const url = await storageAdapter.fileCreate( |
||||
slash(path.join(destPath, fileName)), |
||||
file |
||||
); |
||||
|
||||
let attachmentPath; |
||||
|
||||
// if `url` is null, then it is local attachment
|
||||
if (!url) { |
||||
// then store the attachement path only
|
||||
// url will be constructued in `useAttachmentCell`
|
||||
attachmentPath = `download/${filePath.join('/')}/${fileName}`; |
||||
} |
||||
|
||||
return { |
||||
...(url ? { url } : {}), |
||||
...(attachmentPath ? { path: attachmentPath } : {}), |
||||
title: file.originalname, |
||||
mimetype: file.mimetype, |
||||
size: file.size, |
||||
icon: mimeIcons[path.extname(file.originalname).slice(1)] || undefined, |
||||
}; |
||||
}) |
||||
); |
||||
|
||||
Tele.emit('evt', { evt_type: 'image:uploaded' }); |
||||
|
||||
res.json(attachments); |
||||
} |
||||
|
||||
export async function uploadViaURL(req: Request, res: Response) { |
||||
const filePath = sanitizeUrlPath( |
||||
req.query?.path?.toString()?.split('/') || [''] |
||||
); |
||||
const destPath = path.join('nc', 'uploads', ...filePath); |
||||
|
||||
const storageAdapter = await NcPluginMgrv2.storageAdapter(); |
||||
|
||||
const attachments = await Promise.all( |
||||
req.body?.map?.(async (urlMeta) => { |
||||
const { url, fileName: _fileName } = urlMeta; |
||||
|
||||
const fileName = `${nanoid(18)}${_fileName || url.split('/').pop()}`; |
||||
|
||||
const attachmentUrl = await (storageAdapter as any).fileCreateByUrl( |
||||
slash(path.join(destPath, fileName)), |
||||
url |
||||
); |
||||
|
||||
let attachmentPath; |
||||
|
||||
// if `attachmentUrl` is null, then it is local attachment
|
||||
if (!attachmentUrl) { |
||||
// then store the attachement path only
|
||||
// url will be constructued in `useAttachmentCell`
|
||||
attachmentPath = `download/${filePath.join('/')}/${fileName}`; |
||||
} |
||||
|
||||
return { |
||||
...(attachmentUrl ? { url: attachmentUrl } : {}), |
||||
...(attachmentPath ? { path: attachmentPath } : {}), |
||||
title: fileName, |
||||
mimetype: urlMeta.mimetype, |
||||
size: urlMeta.size, |
||||
icon: mimeIcons[path.extname(fileName).slice(1)] || undefined, |
||||
}; |
||||
}) |
||||
); |
||||
|
||||
Tele.emit('evt', { evt_type: 'image:uploaded' }); |
||||
|
||||
res.json(attachments); |
||||
} |
||||
|
||||
export async function fileRead(req, res) { |
||||
try { |
||||
// get the local storage adapter to display local attachments
|
||||
const storageAdapter = new Local(); |
||||
const type = |
||||
mimetypes[path.extname(req.params?.[0]).split('/').pop().slice(1)] || |
||||
'text/plain'; |
||||
|
||||
const img = await storageAdapter.fileRead( |
||||
slash( |
||||
path.join( |
||||
'nc', |
||||
'uploads', |
||||
req.params?.[0] |
||||
?.split('/') |
||||
.filter((p) => p !== '..') |
||||
.join('/') |
||||
) |
||||
) |
||||
); |
||||
res.writeHead(200, { 'Content-Type': type }); |
||||
res.end(img, 'binary'); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(404).send('Not found'); |
||||
} |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, |
||||
getCacheMiddleware(), |
||||
async (req, res) => { |
||||
try { |
||||
// const type = mimetypes[path.extname(req.params.fileName).slice(1)] || 'text/plain';
|
||||
const type = |
||||
mimetypes[path.extname(req.params[2]).split('/').pop().slice(1)] || |
||||
'text/plain'; |
||||
|
||||
const storageAdapter = await NcPluginMgrv2.storageAdapter(); |
||||
// const img = await this.storageAdapter.fileRead(slash(path.join('nc', req.params.projectId, req.params.dbAlias, 'uploads', req.params.fileName)));
|
||||
const img = await storageAdapter.fileRead( |
||||
slash( |
||||
path.join( |
||||
'nc', |
||||
req.params[0], |
||||
req.params[1], |
||||
'uploads', |
||||
...req.params[2].split('/') |
||||
) |
||||
) |
||||
); |
||||
res.writeHead(200, { 'Content-Type': type }); |
||||
res.end(img, 'binary'); |
||||
} catch (e) { |
||||
res.status(404).send('Not found'); |
||||
} |
||||
} |
||||
); |
||||
|
||||
export function sanitizeUrlPath(paths) { |
||||
return paths.map((url) => url.replace(/[/.?#]+/g, '_')); |
||||
} |
||||
|
||||
router.post( |
||||
'/api/v1/db/storage/upload', |
||||
multer({ |
||||
storage: multer.diskStorage({}), |
||||
limits: { |
||||
fieldSize: NC_ATTACHMENT_FIELD_SIZE, |
||||
}, |
||||
}).any(), |
||||
[ |
||||
getAjvValidatorMw('swagger.json#/components/schemas/AttachmentReq'), |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(isUploadAllowed), |
||||
catchError(upload), |
||||
] |
||||
); |
||||
|
||||
router.post('/api/v1/db/storage/upload-by-url', [ |
||||
getAjvValidatorMw('swagger.json#/components/schemas/AttachmentReq'), |
||||
extractProjectIdAndAuthenticate, |
||||
catchError(isUploadAllowed), |
||||
catchError(uploadViaURL), |
||||
]); |
||||
|
||||
router.get(/^\/download\/(.+)$/, getCacheMiddleware(), catchError(fileRead)); |
||||
|
||||
export default router; |
@ -1,129 +0,0 @@
|
||||
import { Request, Response } from 'express'; |
||||
import Project from '../../models/Project'; |
||||
import { BaseListType } from 'nocodb-sdk'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import { syncBaseMigration } from '../helpers/syncMigration'; |
||||
import Base from '../../models/Base'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { Tele } from 'nc-help'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw, populateMeta } from './helpers'; |
||||
|
||||
export async function baseGet( |
||||
req: Request<any, any, any>, |
||||
res: Response<Base> |
||||
) { |
||||
const base = await Base.get(req.params.baseId); |
||||
|
||||
base.config = base.getConnectionConfig(); |
||||
|
||||
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', |
||||
}); |
||||
|
||||
res.json(base); |
||||
} |
||||
|
||||
export async function baseList( |
||||
req: Request<any, any, any>, |
||||
res: Response<BaseListType>, |
||||
next |
||||
) { |
||||
try { |
||||
const bases = await Base.list({ 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); |
||||
} |
||||
} |
||||
|
||||
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' }); |
||||
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', |
||||
}); |
||||
|
||||
res.json(base); |
||||
} |
||||
|
||||
export default (router) => { |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseGet, 'baseGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/BaseReq'), |
||||
ncMetaAclMw(baseUpdate, 'baseUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId/bases/:baseId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseDelete, 'baseDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/bases', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/BaseReq'), |
||||
ncMetaAclMw(baseCreate, 'baseCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/bases', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(baseList, 'baseList') |
||||
); |
||||
}; |
@ -1,101 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { BaseModelSqlv2 } from '../../../db/sql-data-mapper/lib/sql/BaseModelSqlv2'; |
||||
import Model from '../../../models/Model'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import { getViewAndModelFromRequestByAliasOrId } from './helpers'; |
||||
import apiMetrics from '../../helpers/apiMetrics'; |
||||
|
||||
type BulkOperation = |
||||
| 'bulkInsert' |
||||
| 'bulkUpdate' |
||||
| 'bulkUpdateAll' |
||||
| 'bulkDelete' |
||||
| 'bulkDeleteAll'; |
||||
|
||||
async function getModelViewBase(req: Request) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
return { model, view, base }; |
||||
} |
||||
|
||||
async function executeBulkOperation<T extends BulkOperation>( |
||||
req: Request, |
||||
res: Response, |
||||
operation: T, |
||||
options: Parameters<typeof BaseModelSqlv2.prototype[T]> |
||||
) { |
||||
const { model, view, base } = await getModelViewBase(req); |
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
res.json(await baseModel[operation].apply(null, options)); |
||||
} |
||||
|
||||
async function bulkDataInsert(req: Request, res: Response) { |
||||
await executeBulkOperation(req, res, 'bulkInsert', [ |
||||
req.body, |
||||
{ cookie: req }, |
||||
]); |
||||
} |
||||
|
||||
async function bulkDataUpdate(req: Request, res: Response) { |
||||
await executeBulkOperation(req, res, 'bulkUpdate', [ |
||||
req.body, |
||||
{ cookie: req }, |
||||
]); |
||||
} |
||||
|
||||
// todo: Integrate with filterArrJson bulkDataUpdateAll
|
||||
async function bulkDataUpdateAll(req: Request, res: Response) { |
||||
await executeBulkOperation(req, res, 'bulkUpdateAll', [ |
||||
req.query, |
||||
req.body, |
||||
{ cookie: req }, |
||||
]); |
||||
} |
||||
|
||||
async function bulkDataDelete(req: Request, res: Response) { |
||||
await executeBulkOperation(req, res, 'bulkDelete', [ |
||||
req.body, |
||||
{ cookie: req }, |
||||
]); |
||||
} |
||||
|
||||
// todo: Integrate with filterArrJson bulkDataDeleteAll
|
||||
async function bulkDataDeleteAll(req: Request, res: Response) { |
||||
await executeBulkOperation(req, res, 'bulkDeleteAll', [req.query]); |
||||
} |
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataInsert, 'bulkDataInsert') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataUpdate, 'bulkDataUpdate') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName/all', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataUpdateAll, 'bulkDataUpdateAll') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataDelete, 'bulkDataDelete') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/bulk/:orgs/:projectName/:tableName/all', |
||||
apiMetrics, |
||||
ncMetaAclMw(bulkDataDeleteAll, 'bulkDataDeleteAll') |
||||
); |
||||
|
||||
export default router; |
@ -1,432 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../../models/Model'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import { NcError } from '../../helpers/catchError'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
import View from '../../../models/View'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import { getViewAndModelFromRequestByAliasOrId } from './helpers'; |
||||
import apiMetrics from '../../helpers/apiMetrics'; |
||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import { parseHrtimeToSeconds } from '../helpers'; |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function dataList(req: Request, res: Response) { |
||||
const startTime = process.hrtime(); |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
const responseData = await getDataList(model, view, req); |
||||
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(responseData); |
||||
} |
||||
|
||||
async function dataFindOne(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
res.json(await getFindOne(model, view, req)); |
||||
} |
||||
|
||||
async function dataGroupBy(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
res.json(await getDataGroupBy(model, view, req)); |
||||
} |
||||
|
||||
async function dataCount(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const countArgs: any = { ...req.query }; |
||||
try { |
||||
countArgs.filterArr = JSON.parse(countArgs.filterArrJson); |
||||
} catch (e) {} |
||||
|
||||
const count = await baseModel.count(countArgs); |
||||
|
||||
res.json({ count }); |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function dataInsert(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.insert(req.body, null, req)); |
||||
} |
||||
|
||||
async function dataUpdate(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.updateByPk(req.params.rowId, req.body, null, req)); |
||||
} |
||||
|
||||
async function dataDelete(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
const base = await Base.get(model.base_id); |
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
// todo: Should have error http status code
|
||||
const message = await baseModel.hasLTARData(req.params.rowId, model); |
||||
if (message.length) { |
||||
res.json({ message }); |
||||
return; |
||||
} |
||||
res.json(await baseModel.delByPk(req.params.rowId, null, req)); |
||||
} |
||||
|
||||
async function getDataList(model, view: View, req) { |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const requestObj = await getAst({ model, query: req.query, view }); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.sortArr = JSON.parse(listArgs.sortArrJson); |
||||
} catch (e) {} |
||||
|
||||
let data = []; |
||||
let count = 0; |
||||
try { |
||||
data = await nocoExecute( |
||||
requestObj, |
||||
await baseModel.list(listArgs), |
||||
{}, |
||||
listArgs |
||||
); |
||||
count = await baseModel.count(listArgs); |
||||
} catch (e) { |
||||
console.log(e); |
||||
NcError.internalServerError( |
||||
'Internal Server Error, check server log for more details' |
||||
); |
||||
} |
||||
|
||||
return new PagedResponseImpl(data, { |
||||
...req.query, |
||||
count, |
||||
}); |
||||
} |
||||
|
||||
async function getFindOne(model, view: View, req) { |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const args: any = { ...req.query }; |
||||
try { |
||||
args.filterArr = JSON.parse(args.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
args.sortArr = JSON.parse(args.sortArrJson); |
||||
} catch (e) {} |
||||
|
||||
const data = await baseModel.findOne(args); |
||||
return data |
||||
? await nocoExecute( |
||||
await getAst({ model, query: args, view }), |
||||
data, |
||||
{}, |
||||
{} |
||||
) |
||||
: {}; |
||||
} |
||||
|
||||
async function getDataGroupBy(model, view: View, req) { |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
const data = await baseModel.groupBy({ ...req.query }); |
||||
const count = await baseModel.count(listArgs); |
||||
|
||||
return new PagedResponseImpl(data, { |
||||
...req.query, |
||||
count, |
||||
}); |
||||
} |
||||
|
||||
async function dataRead(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const row = await baseModel.readByPk(req.params.rowId); |
||||
|
||||
if (!row) { |
||||
NcError.notFound(); |
||||
} |
||||
|
||||
res.json( |
||||
await nocoExecute( |
||||
await getAst({ model, query: req.query, view }), |
||||
row, |
||||
{}, |
||||
req.query |
||||
) |
||||
); |
||||
} |
||||
|
||||
async function dataExist(req: Request, res: Response) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.exist(req.params.rowId)); |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function groupedDataList(req: Request, res: Response) { |
||||
const startTime = process.hrtime(); |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
const groupedData = await getGroupedDataList(model, view, req); |
||||
const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); |
||||
res.setHeader('xc-db-response', elapsedSeconds); |
||||
res.json(groupedData); |
||||
} |
||||
|
||||
async function getGroupedDataList(model, view: View, req) { |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const requestObj = await getAst({ model, query: req.query, view }); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.sortArr = JSON.parse(listArgs.sortArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.options = JSON.parse(listArgs.optionsArrJson); |
||||
} catch (e) {} |
||||
|
||||
let data = []; |
||||
// let count = 0
|
||||
try { |
||||
const groupedData = await baseModel.groupedList({ |
||||
...listArgs, |
||||
groupColumnId: req.params.columnId, |
||||
}); |
||||
data = await nocoExecute( |
||||
{ key: 1, value: requestObj }, |
||||
groupedData, |
||||
{}, |
||||
listArgs |
||||
); |
||||
const countArr = await baseModel.groupedListCount({ |
||||
...listArgs, |
||||
groupColumnId: req.params.columnId, |
||||
}); |
||||
data = data.map((item) => { |
||||
// todo: use map to avoid loop
|
||||
const count = |
||||
countArr.find((countItem: any) => countItem.key === item.key)?.count ?? |
||||
0; |
||||
|
||||
item.value = new PagedResponseImpl(item.value, { |
||||
...req.query, |
||||
count: count, |
||||
}); |
||||
return item; |
||||
}); |
||||
} catch (e) { |
||||
console.log(e); |
||||
NcError.internalServerError( |
||||
'Internal Server Error, check server log for more details' |
||||
); |
||||
} |
||||
return data; |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// table data crud apis
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/find-one', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataFindOne, 'dataFindOne') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/groupby', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataGroupBy, 'dataGroupBy') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/group/:columnId', |
||||
apiMetrics, |
||||
ncMetaAclMw(groupedDataList, 'groupedDataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/exist', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataExist, 'dataExist') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/count', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/count', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataCount, 'dataCount') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
// table view data crud apis
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataList, 'dataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/find-one', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataFindOne, 'dataFindOne') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/groupby', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataGroupBy, 'dataGroupBy') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/group/:columnId', |
||||
apiMetrics, |
||||
ncMetaAclMw(groupedDataList, 'groupedDataList') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId/exist', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataExist, 'dataExist') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
|
||||
router.patch( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
|
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
export default router; |
@ -1,291 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../../models/Model'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import { |
||||
getColumnByIdOrName, |
||||
getViewAndModelFromRequestByAliasOrId, |
||||
} from './helpers'; |
||||
import { NcError } from '../../helpers/catchError'; |
||||
import apiMetrics from '../../helpers/apiMetrics'; |
||||
|
||||
// todo: handle case where the given column is not ltar
|
||||
export async function mmList(req: Request, res: Response, next) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.mmList( |
||||
{ |
||||
colId: column.id, |
||||
parentId: req.params.rowId, |
||||
}, |
||||
req.query as any |
||||
); |
||||
const count: any = await baseModel.mmListCount({ |
||||
colId: column.id, |
||||
parentId: req.params.rowId, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response, next) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.getMmChildrenExcludedList( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.getMmChildrenExcludedListCount( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response, next) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.getHmChildrenExcludedList( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.getHmChildrenExcludedListCount( |
||||
{ |
||||
colId: column.id, |
||||
pid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response, next) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.getBtChildrenExcludedList( |
||||
{ |
||||
colId: column.id, |
||||
cid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.getBtChildrenExcludedListCount( |
||||
{ |
||||
colId: column.id, |
||||
cid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
// todo: handle case where the given column is not ltar
|
||||
export async function hmList(req: Request, res: Response, next) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
const data = await baseModel.hmList( |
||||
{ |
||||
colId: column.id, |
||||
id: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
const count = await baseModel.hmListCount({ |
||||
colId: column.id, |
||||
id: req.params.rowId, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
} as any) |
||||
); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataRemove(req, res) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
|
||||
if (!model) NcError.notFound('Table not found'); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
|
||||
await baseModel.removeChild({ |
||||
colId: column.id, |
||||
childId: req.params.refRowId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
// todo: Give proper error message when reference row is already related and handle duplicate ref row id in hm
|
||||
async function relationDataAdd(req, res) { |
||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
||||
if (!model) NcError.notFound('Table not found'); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const column = await getColumnByIdOrName(req.params.columnName, model); |
||||
await baseModel.addChild({ |
||||
colId: column.id, |
||||
childId: req.params.refRowId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/bt/:columnName/exclude', |
||||
apiMetrics, |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/:relationType/:columnName/:refRowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(relationDataRemove, 'relationDataRemove') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/mm/:columnName', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmList, 'mmList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/hm/:columnName', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmList, 'hmList') |
||||
); |
||||
|
||||
export default router; |
@ -1,612 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../../models/Model'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
import View from '../../../models/View'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import { NcError } from '../../helpers/catchError'; |
||||
import apiMetrics from '../../helpers/apiMetrics'; |
||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
|
||||
export async function dataList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
res.json(await getDataList(model, view, req)); |
||||
} |
||||
|
||||
export async function mmList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = `${model.title}List`; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.mmList( |
||||
{ |
||||
colId: req.params.colId, |
||||
parentId: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
|
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count: any = await baseModel.mmListCount({ |
||||
colId: req.params.colId, |
||||
parentId: req.params.rowId, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function mmExcludedList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = 'List'; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.getMmChildrenExcludedList( |
||||
{ |
||||
colId: req.params.colId, |
||||
pid: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
|
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count = await baseModel.getMmChildrenExcludedListCount( |
||||
{ |
||||
colId: req.params.colId, |
||||
pid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmExcludedList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = 'List'; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.getHmChildrenExcludedList( |
||||
{ |
||||
colId: req.params.colId, |
||||
pid: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
|
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count = await baseModel.getHmChildrenExcludedListCount( |
||||
{ |
||||
colId: req.params.colId, |
||||
pid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function btExcludedList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = 'List'; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.getBtChildrenExcludedList( |
||||
{ |
||||
colId: req.params.colId, |
||||
cid: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
|
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count = await baseModel.getBtChildrenExcludedListCount( |
||||
{ |
||||
colId: req.params.colId, |
||||
cid: req.params.rowId, |
||||
}, |
||||
req.query |
||||
); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
export async function hmList(req: Request, res: Response, next) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = `${model.title}List`; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.hmList( |
||||
{ |
||||
colId: req.params.colId, |
||||
id: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count = await baseModel.hmListCount({ |
||||
colId: req.params.colId, |
||||
id: req.params.rowId, |
||||
}); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl(data, { |
||||
totalRows: count, |
||||
} as any) |
||||
); |
||||
} |
||||
|
||||
async function dataRead(req: Request, res: Response, next) { |
||||
try { |
||||
const model = await Model.getByIdOrName({ |
||||
id: req.params.viewId, |
||||
}); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json( |
||||
await nocoExecute( |
||||
await getAst({ model, query: req.query }), |
||||
await baseModel.readByPk(req.params.rowId), |
||||
{}, |
||||
{} |
||||
) |
||||
); |
||||
} catch (e) { |
||||
console.log(e); |
||||
NcError.internalServerError( |
||||
'Internal Server Error, check server log for more details' |
||||
); |
||||
} |
||||
} |
||||
|
||||
async function dataInsert(req: Request, res: Response, next) { |
||||
try { |
||||
const model = await Model.getByIdOrName({ |
||||
id: req.params.viewId, |
||||
}); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.insert(req.body, null, req)); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(500).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
// async function dataInsertNew(req: Request, res: Response) {
|
||||
// const { model, view } = await getViewAndModelFromRequest(req);
|
||||
//
|
||||
// const base = await Base.get(model.base_id);
|
||||
//
|
||||
// const baseModel = await Model.getBaseModelSQL({
|
||||
// id: model.id,
|
||||
// viewId: view?.id,
|
||||
// dbDriver: NcConnectionMgrv2.get(base)
|
||||
// });
|
||||
//
|
||||
// res.json(await baseModel.insert(req.body, null, req));
|
||||
// }
|
||||
|
||||
// async function dataUpdateNew(req: Request, res: Response) {
|
||||
// const { model, view } = await getViewAndModelFromRequest(req);
|
||||
// const base = await Base.get(model.base_id);
|
||||
//
|
||||
// const baseModel = await Model.getBaseModelSQL({
|
||||
// id: model.id,
|
||||
// viewId: view.id,
|
||||
// dbDriver: NcConnectionMgrv2.get(base)
|
||||
// });
|
||||
//
|
||||
// res.json(await baseModel.updateByPk(req.params.rowId, req.body, null, req));
|
||||
// }
|
||||
async function dataUpdate(req: Request, res: Response, next) { |
||||
try { |
||||
const model = await Model.getByIdOrName({ |
||||
id: req.params.viewId, |
||||
}); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.updateByPk(req.params.rowId, req.body, null, req)); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(500).json({ msg: e.message }); |
||||
} |
||||
} |
||||
//
|
||||
// async function dataDeleteNew(req: Request, res: Response) {
|
||||
// const { model, view } = await getViewAndModelFromRequest(req);
|
||||
// const base = await Base.get(model.base_id);
|
||||
// const baseModel = await Model.getBaseModelSQL({
|
||||
// id: model.id,
|
||||
// viewId: view.id,
|
||||
// dbDriver: NcConnectionMgrv2.get(base)
|
||||
// });
|
||||
//
|
||||
// res.json(await baseModel.delByPk(req.params.rowId, null, req));
|
||||
// }
|
||||
|
||||
async function dataDelete(req: Request, res: Response, next) { |
||||
try { |
||||
const model = await Model.getByIdOrName({ |
||||
id: req.params.viewId, |
||||
}); |
||||
if (!model) return next(new Error('Table not found')); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
res.json(await baseModel.delByPk(req.params.rowId, null, req)); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(500).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
async function getDataList(model, view: View, req) { |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const requestObj = await getAst({ query: req.query, model, view }); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.sortArr = JSON.parse(listArgs.sortArrJson); |
||||
} catch (e) {} |
||||
|
||||
let data = []; |
||||
let count = 0; |
||||
try { |
||||
data = await nocoExecute( |
||||
requestObj, |
||||
await baseModel.list(listArgs), |
||||
{}, |
||||
listArgs |
||||
); |
||||
count = await baseModel.count(listArgs); |
||||
} catch (e) { |
||||
// show empty result instead of throwing error here
|
||||
// e.g. search some text in a numeric field
|
||||
console.log(e); |
||||
NcError.internalServerError( |
||||
'Internal Server Error, check server log for more details' |
||||
); |
||||
} |
||||
|
||||
return new PagedResponseImpl(data, { |
||||
count, |
||||
...req.query, |
||||
}); |
||||
} |
||||
//@ts-ignore
|
||||
async function relationDataDelete(req, res) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) NcError.notFound('Table not found'); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
await baseModel.removeChild({ |
||||
colId: req.params.colId, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
//@ts-ignore
|
||||
async function relationDataAdd(req, res) { |
||||
const view = await View.get(req.params.viewId); |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id || req.params.viewId, |
||||
}); |
||||
|
||||
if (!model) NcError.notFound('Table not found'); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
await baseModel.addChild({ |
||||
colId: req.params.colId, |
||||
childId: req.params.childId, |
||||
rowId: req.params.rowId, |
||||
cookie: req, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// router.get('/data/:orgs/:projectName/:tableName',apiMetrics,ncMetaAclMw(dataListNew));
|
||||
// router.get(
|
||||
// '/data/:orgs/:projectName/:tableName/views/:viewName',
|
||||
// ncMetaAclMw(dataListNew)
|
||||
// );
|
||||
//
|
||||
// router.post(
|
||||
// '/data/:orgs/:projectName/:tableName/views/:viewName',
|
||||
// ncMetaAclMw(dataInsertNew)
|
||||
// );
|
||||
// router.patch(
|
||||
// '/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
|
||||
// ncMetaAclMw(dataUpdateNew)
|
||||
// );
|
||||
// router.delete(
|
||||
// '/data/:orgs/:projectName/:tableName/views/:viewName/:rowId',
|
||||
// ncMetaAclMw(dataDeleteNew)
|
||||
// );
|
||||
|
||||
router.get('/data/:viewId/', apiMetrics, ncMetaAclMw(dataList, 'dataList')); |
||||
router.post( |
||||
'/data/:viewId/', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataInsert, 'dataInsert') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataRead, 'dataRead') |
||||
); |
||||
router.patch( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataUpdate, 'dataUpdate') |
||||
); |
||||
router.delete( |
||||
'/data/:viewId/:rowId', |
||||
apiMetrics, |
||||
ncMetaAclMw(dataDelete, 'dataDelete') |
||||
); |
||||
|
||||
router.get( |
||||
'/data/:viewId/:rowId/mm/:colId', |
||||
apiMetrics, |
||||
ncMetaAclMw(mmList, 'mmList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/hm/:colId', |
||||
apiMetrics, |
||||
ncMetaAclMw(hmList, 'hmList') |
||||
); |
||||
|
||||
router.get( |
||||
'/data/:viewId/:rowId/mm/:colId/exclude', |
||||
ncMetaAclMw(mmExcludedList, 'mmExcludedList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/hm/:colId/exclude', |
||||
ncMetaAclMw(hmExcludedList, 'hmExcludedList') |
||||
); |
||||
router.get( |
||||
'/data/:viewId/:rowId/bt/:colId/exclude', |
||||
ncMetaAclMw(btExcludedList, 'btExcludedList') |
||||
); |
||||
|
||||
router.post( |
||||
'/data/:viewId/:rowId/:relationType/:colId/:childId', |
||||
ncMetaAclMw(relationDataAdd, 'relationDataAdd') |
||||
); |
||||
router.delete( |
||||
'/data/:viewId/:rowId/:relationType/:colId/:childId', |
||||
ncMetaAclMw(relationDataDelete, 'relationDataDelete') |
||||
); |
||||
export default router; |
@ -1,15 +0,0 @@
|
||||
import dataApis from './dataApis'; |
||||
import oldDataApis from './oldDataApis'; |
||||
import dataAliasApis from './dataAliasApis'; |
||||
import bulkDataAliasApis from './bulkDataAliasApis'; |
||||
import dataAliasNestedApis from './dataAliasNestedApis'; |
||||
import dataAliasExportApis from './dataAliasExportApis'; |
||||
|
||||
export { |
||||
dataApis, |
||||
oldDataApis, |
||||
dataAliasApis, |
||||
bulkDataAliasApis, |
||||
dataAliasNestedApis, |
||||
dataAliasExportApis, |
||||
}; |
@ -1,22 +0,0 @@
|
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import ApiToken from '../../../models/ApiToken'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
|
||||
export async function apiTokenListEE(req, res) { |
||||
let fk_user_id = req.user.id; |
||||
|
||||
// if super admin get all tokens
|
||||
if (req.user.roles.includes(OrgUserRoles.SUPER_ADMIN)) { |
||||
fk_user_id = undefined; |
||||
} |
||||
|
||||
res.json( |
||||
new PagedResponseImpl( |
||||
await ApiToken.listWithCreatedBy({ ...req.query, fk_user_id }), |
||||
{ |
||||
...req.query, |
||||
count: await ApiToken.count({}), |
||||
} |
||||
) |
||||
); |
||||
} |
@ -1,175 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
// @ts-ignore
|
||||
import Model from '../../models/Model'; |
||||
import { Tele } from 'nc-help'; |
||||
// @ts-ignore
|
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
// @ts-ignore
|
||||
import { Table, TableList, TableListParams, TableReq } from 'nocodb-sdk'; |
||||
// @ts-ignore
|
||||
import ProjectMgrv2 from '../../db/sql-mgr/v2/ProjectMgrv2'; |
||||
// @ts-ignore
|
||||
import Project from '../../models/Project'; |
||||
import Filter from '../../models/Filter'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
// @ts-ignore
|
||||
export async function filterGet(req: Request, res: Response, next) { |
||||
try { |
||||
const filter = await Filter.get(req.params.filterId); |
||||
|
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterList( |
||||
req: Request<any, any, any, TableListParams>, |
||||
res: Response, |
||||
next |
||||
) { |
||||
try { |
||||
const filter = await Filter.rootFilterList({ viewId: req.params.viewId }); |
||||
|
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
// @ts-ignore
|
||||
export async function filterChildrenRead( |
||||
req: Request<any, any, any, TableListParams>, |
||||
res: Response, |
||||
next |
||||
) { |
||||
try { |
||||
const filter = await Filter.parentFilterList({ |
||||
parentId: req.params.filterParentId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
export async function filterCreate( |
||||
req: Request<any, any, TableReq>, |
||||
res, |
||||
next |
||||
) { |
||||
try { |
||||
const filter = await Filter.insert({ |
||||
...req.body, |
||||
fk_view_id: req.params.viewId, |
||||
}); |
||||
|
||||
Tele.emit('evt', { evt_type: 'filter:created' }); |
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterUpdate(req, res, next) { |
||||
try { |
||||
const filter = await Filter.update(req.params.filterId, { |
||||
...req.body, |
||||
fk_view_id: req.params.viewId, |
||||
}); |
||||
Tele.emit('evt', { evt_type: 'filter:updated' }); |
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterDelete(req: Request, res: Response, next) { |
||||
try { |
||||
const filter = await Filter.delete(req.params.filterId); |
||||
Tele.emit('evt', { evt_type: 'filter:deleted' }); |
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
export async function hookFilterList( |
||||
req: Request<any, any, any, TableListParams>, |
||||
res: Response |
||||
) { |
||||
const filter = await Filter.rootFilterListByHook({ |
||||
hookId: req.params.hookId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} |
||||
|
||||
export async function hookFilterCreate(req: Request<any, any, TableReq>, res) { |
||||
const filter = await Filter.insert({ |
||||
...req.body, |
||||
fk_hook_id: req.params.hookId, |
||||
}); |
||||
|
||||
Tele.emit('evt', { evt_type: 'hookFilter:created' }); |
||||
res.json(filter); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/views/:viewId/filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/filters', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), |
||||
ncMetaAclMw(filterCreate, 'filterCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/hooks/:hookId/filters', |
||||
ncMetaAclMw(hookFilterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/hooks/:hookId/filters', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), |
||||
ncMetaAclMw(hookFilterCreate, 'filterCreate') |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterGet, 'filterGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), |
||||
ncMetaAclMw(filterUpdate, 'filterUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterDelete, 'filterDelete') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/filters/:filterParentId/children', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead') |
||||
); |
||||
export default router; |
@ -1,66 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
// @ts-ignore
|
||||
import Model from '../../models/Model'; |
||||
import { Tele } from 'nc-help'; |
||||
// @ts-ignore
|
||||
import { PagedResponseImpl } from '../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 ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
// @ts-ignore
|
||||
export async function formViewGet(req: Request, res: Response<FormType>) { |
||||
const formViewData = await FormView.getWithInfo(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, |
||||
}); |
||||
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)); |
||||
} |
||||
|
||||
// @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( |
||||
'/api/v1/db/meta/forms/:formViewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(formViewGet, 'formViewGet') |
||||
); |
||||
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; |
@ -1,20 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import FormViewColumn from '../../models/FormViewColumn'; |
||||
import { Tele } from 'nc-help'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
export async function columnUpdate(req: Request, res: Response) { |
||||
Tele.emit('evt', { evt_type: 'formViewColumn:updated' }); |
||||
res.json(await FormViewColumn.update(req.params.formViewColumnId, req.body)); |
||||
} |
||||
|
||||
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; |
@ -1,47 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { GalleryType, ViewTypes } from 'nocodb-sdk'; |
||||
import View from '../../models/View'; |
||||
import GalleryView from '../../models/GalleryView'; |
||||
import { Tele } from 'nc-help'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
export async function galleryViewGet(req: Request, res: Response<GalleryType>) { |
||||
res.json(await GalleryView.get(req.params.galleryViewId)); |
||||
} |
||||
|
||||
export async function galleryViewCreate(req: Request<any, any>, res) { |
||||
Tele.emit('evt', { evt_type: 'vtable:created', show_as: 'gallery' }); |
||||
const view = await View.insert({ |
||||
...req.body, |
||||
// todo: sanitize
|
||||
fk_model_id: req.params.tableId, |
||||
type: ViewTypes.GALLERY, |
||||
}); |
||||
res.json(view); |
||||
} |
||||
|
||||
export async function galleryViewUpdate(req, res) { |
||||
Tele.emit('evt', { evt_type: 'view:updated', type: 'gallery' }); |
||||
res.json(await GalleryView.update(req.params.galleryViewId, req.body)); |
||||
} |
||||
|
||||
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( |
||||
'/api/v1/db/meta/galleries/:galleryViewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(galleryViewGet, 'galleryViewGet') |
||||
); |
||||
export default router; |
@ -1,47 +0,0 @@
|
||||
import { Request, Router } from 'express'; |
||||
// @ts-ignore
|
||||
import Model from '../../models/Model'; |
||||
import { Tele } from 'nc-help'; |
||||
// @ts-ignore
|
||||
import { PagedResponseImpl } from '../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 '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import GridView from '../../models/GridView'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
// @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, |
||||
}); |
||||
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)); |
||||
} |
||||
|
||||
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( |
||||
'/api/v1/db/meta/grids/:viewId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(gridViewUpdate, 'gridViewUpdate') |
||||
); |
||||
export default router; |
@ -1,113 +0,0 @@
|
||||
import { Tele } from 'nc-help'; |
||||
import catchError from '../helpers/catchError'; |
||||
import { Request, Response, Router } from 'express'; |
||||
import Hook from '../../models/Hook'; |
||||
import { HookListType, HookType } from 'nocodb-sdk'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import { invokeWebhook } from '../helpers/webhookHelpers'; |
||||
import Model from '../../models/Model'; |
||||
import populateSamplePayload from '../helpers/populateSamplePayload'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
export async function hookList( |
||||
req: Request<any, any, any>, |
||||
res: Response<HookListType> |
||||
) { |
||||
res.json( |
||||
new PagedResponseImpl(await Hook.list({ fk_model_id: req.params.tableId })) |
||||
); |
||||
} |
||||
|
||||
export async function hookCreate( |
||||
req: Request<any, HookType>, |
||||
res: Response<HookType> |
||||
) { |
||||
Tele.emit('evt', { evt_type: 'webhooks:created' }); |
||||
const hook = await Hook.insert({ |
||||
...req.body, |
||||
fk_model_id: req.params.tableId, |
||||
}); |
||||
res.json(hook); |
||||
} |
||||
|
||||
export async function hookDelete( |
||||
req: Request<any, HookType>, |
||||
res: Response<any> |
||||
) { |
||||
Tele.emit('evt', { evt_type: 'webhooks:deleted' }); |
||||
res.json(await Hook.delete(req.params.hookId)); |
||||
} |
||||
|
||||
export async function hookUpdate( |
||||
req: Request<any, HookType>, |
||||
res: Response<HookType> |
||||
) { |
||||
Tele.emit('evt', { evt_type: 'webhooks:updated' }); |
||||
|
||||
res.json(await Hook.update(req.params.hookId, req.body)); |
||||
} |
||||
|
||||
export async function hookTest(req: Request<any, any>, res: Response) { |
||||
const model = await Model.getByIdOrName({ id: req.params.tableId }); |
||||
|
||||
const { |
||||
hook, |
||||
payload: { data, user }, |
||||
} = req.body; |
||||
await invokeWebhook( |
||||
new Hook(hook), |
||||
model, |
||||
data, |
||||
user, |
||||
(hook as any)?.filters, |
||||
true |
||||
); |
||||
|
||||
Tele.emit('evt', { evt_type: 'webhooks:tested' }); |
||||
|
||||
res.json({ msg: 'Success' }); |
||||
} |
||||
export async function tableSampleData(req: Request, res: Response) { |
||||
const model = await Model.getByIdOrName({ id: req.params.tableId }); |
||||
|
||||
res // todo: pagination
|
||||
.json(await populateSamplePayload(model, false, req.params.operation)); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/hooks', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookList, 'hookList') |
||||
); |
||||
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( |
||||
'/api/v1/db/meta/hooks/:hookId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hookDelete, 'hookDelete') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/hooks/:hookId', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/HookReq'), |
||||
ncMetaAclMw(hookUpdate, 'hookUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/tables/:tableId/hooks/samplePayload/:operation', |
||||
metaApiMetrics, |
||||
catchError(tableSampleData) |
||||
); |
||||
export default router; |
@ -1,145 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
// @ts-ignore
|
||||
import Model from '../../models/Model'; |
||||
import { Tele } from 'nc-help'; |
||||
// @ts-ignore
|
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
// @ts-ignore
|
||||
import { Table, TableList, TableListParams, TableReq } from 'nocodb-sdk'; |
||||
// @ts-ignore
|
||||
import ProjectMgrv2 from '../../db/sql-mgr/v2/ProjectMgrv2'; |
||||
// @ts-ignore
|
||||
import Project from '../../models/Project'; |
||||
import Filter from '../../models/Filter'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
// @ts-ignore
|
||||
export async function filterGet(req: Request, res: Response, next) { |
||||
try { |
||||
const filter = await Filter.getFilterObject({ hookId: req.params.hookId }); |
||||
|
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterList( |
||||
req: Request<any, any, any, TableListParams>, |
||||
res: Response, |
||||
next |
||||
) { |
||||
try { |
||||
const filter = await Filter.rootFilterListByHook({ |
||||
hookId: req.params.hookId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
// @ts-ignore
|
||||
export async function filterChildrenRead( |
||||
req: Request<any, any, any, TableListParams>, |
||||
res: Response, |
||||
next |
||||
) { |
||||
try { |
||||
const filter = await Filter.parentFilterListByHook({ |
||||
hookId: req.params.hookId, |
||||
parentId: req.params.filterParentId, |
||||
}); |
||||
|
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
export async function filterCreate( |
||||
req: Request<any, any, TableReq>, |
||||
res, |
||||
next |
||||
) { |
||||
try { |
||||
const filter = await Filter.insert({ |
||||
...req.body, |
||||
fk_hook_id: req.params.hookId, |
||||
}); |
||||
|
||||
Tele.emit('evt', { evt_type: 'hookFilter:created' }); |
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterUpdate(req, res, next) { |
||||
try { |
||||
const filter = await Filter.update(req.params.filterId, { |
||||
...req.body, |
||||
fk_hook_id: req.params.hookId, |
||||
}); |
||||
Tele.emit('evt', { evt_type: 'hookFilter:updated' }); |
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function filterDelete(req: Request, res: Response, next) { |
||||
try { |
||||
const filter = await Filter.delete(req.params.filterId); |
||||
Tele.emit('evt', { evt_type: 'hookFilter:deleted' }); |
||||
res.json(filter); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/hooks/:hookId/filters/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterList, 'filterList') |
||||
); |
||||
router.post( |
||||
'/hooks/:hookId/filters/', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), |
||||
ncMetaAclMw(filterCreate, 'filterCreate') |
||||
); |
||||
router.get( |
||||
'/hooks/:hookId/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterGet, 'filterGet') |
||||
); |
||||
router.patch( |
||||
'/hooks/:hookId/filters/:filterId', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/FilterReq'), |
||||
ncMetaAclMw(filterUpdate, 'filterUpdate') |
||||
); |
||||
router.delete( |
||||
'/hooks/:hookId/filters/:filterId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterDelete, 'filterDelete') |
||||
); |
||||
router.get( |
||||
'/hooks/:hookId/filters/:filterParentId/children', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(filterChildrenRead, 'filterChildrenRead') |
||||
); |
||||
export default router; |
@ -1,129 +0,0 @@
|
||||
import Model from '../../models/Model'; |
||||
import ModelRoleVisibility from '../../models/ModelRoleVisibility'; |
||||
import { Router } from 'express'; |
||||
import { Tele } from 'nc-help'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
async function xcVisibilityMetaSetAll(req, res) { |
||||
Tele.emit('evt', { evt_type: 'uiAcl:updated' }); |
||||
for (const d of req.body) { |
||||
for (const role of Object.keys(d.disabled)) { |
||||
const dataInDb = await ModelRoleVisibility.get({ |
||||
role, |
||||
// fk_model_id: d.fk_model_id,
|
||||
fk_view_id: d.id, |
||||
}); |
||||
if (dataInDb) { |
||||
if (d.disabled[role]) { |
||||
if (!dataInDb.disabled) { |
||||
await ModelRoleVisibility.update(d.id, role, { |
||||
disabled: d.disabled[role], |
||||
}); |
||||
} |
||||
} else { |
||||
await dataInDb.delete(); |
||||
} |
||||
} else if (d.disabled[role]) { |
||||
await ModelRoleVisibility.insert({ |
||||
fk_view_id: d.id, |
||||
disabled: d.disabled[role], |
||||
role, |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
Tele.emit('evt', { evt_type: 'uiAcl:updated' }); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function xcVisibilityMetaGet( |
||||
projectId, |
||||
_models: Model[] = null, |
||||
includeM2M = true |
||||
// type: 'table' | 'tableAndViews' | 'views' = 'table'
|
||||
) { |
||||
// todo: move to
|
||||
const roles = ['owner', 'creator', 'viewer', 'editor', 'commenter', 'guest']; |
||||
|
||||
const defaultDisabled = roles.reduce((o, r) => ({ ...o, [r]: false }), {}); |
||||
|
||||
let models = |
||||
_models || |
||||
(await Model.list({ |
||||
project_id: projectId, |
||||
base_id: undefined, |
||||
})); |
||||
|
||||
models = includeM2M ? models : (models.filter((t) => !t.mm) as Model[]); |
||||
|
||||
const result = await models.reduce(async (_obj, model) => { |
||||
const obj = await _obj; |
||||
// obj[model.id] = {
|
||||
// tn: model.tn,
|
||||
// _tn: model._tn,
|
||||
// order: model.order,
|
||||
// fk_model_id: model.id,
|
||||
// id: model.id,
|
||||
// type: model.type,
|
||||
// disabled: { ...defaultDisabled }
|
||||
// };
|
||||
// if (type === 'tableAndViews') {
|
||||
const views = await model.getViews(); |
||||
for (const view of views) { |
||||
obj[view.id] = { |
||||
ptn: model.table_name, |
||||
_ptn: model.title, |
||||
ptype: model.type, |
||||
tn: view.title, |
||||
_tn: view.title, |
||||
table_meta: model.meta, |
||||
...view, |
||||
disabled: { ...defaultDisabled }, |
||||
}; |
||||
// }
|
||||
} |
||||
|
||||
return obj; |
||||
}, Promise.resolve({})); |
||||
|
||||
const disabledList = await ModelRoleVisibility.list(projectId); |
||||
|
||||
for (const d of disabledList) { |
||||
// if (d.fk_model_id) result[d.fk_model_id].disabled[d.role] = !!d.disabled;
|
||||
// else if (type === 'tableAndViews' && d.fk_view_id)
|
||||
if (result[d.fk_view_id]) |
||||
result[d.fk_view_id].disabled[d.role] = !!d.disabled; |
||||
} |
||||
|
||||
return Object.values(result); |
||||
// ?.sort(
|
||||
// (a: any, b: any) =>
|
||||
// (a.order || 0) - (b.order || 0) ||
|
||||
// (a?._tn || a?.tn)?.localeCompare(b?._tn || b?.tn)
|
||||
// );
|
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/visibility-rules', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(async (req, res) => { |
||||
res.json( |
||||
await xcVisibilityMetaGet( |
||||
req.params.projectId, |
||||
null, |
||||
req.query.includeM2M === true || req.query.includeM2M === 'true' |
||||
) |
||||
); |
||||
}, 'modelVisibilityList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/visibility-rules', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/VisibilityRuleReq'), |
||||
ncMetaAclMw(xcVisibilityMetaSetAll, 'modelVisibilitySet') |
||||
); |
||||
export default router; |
@ -1,83 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import ApiToken from '../../models/ApiToken'; |
||||
import { Tele } from 'nc-help'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { NcError } from '../helpers/catchError'; |
||||
import getHandler from '../helpers/getHandler'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import { apiTokenListEE } from './ee/orgTokenApis'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
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, |
||||
}), |
||||
} |
||||
) |
||||
); |
||||
} |
||||
|
||||
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 })); |
||||
} |
||||
|
||||
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)); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
router.get( |
||||
'/api/v1/tokens', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(getHandler(apiTokenList, apiTokenListEE), 'apiTokenList', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/tokens', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/ApiTokenReq'), |
||||
ncMetaAclMw(apiTokenCreate, 'apiTokenCreate', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.delete( |
||||
'/api/v1/tokens/:token', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(apiTokenDelete, 'apiTokenDelete', { |
||||
// allowedRoles: [OrgUserRoles.SUPER],
|
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
export default router; |
@ -1,337 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { |
||||
AuditOperationSubTypes, |
||||
AuditOperationTypes, |
||||
PluginCategory, |
||||
} from 'nocodb-sdk'; |
||||
import { v4 as uuidv4 } from 'uuid'; |
||||
import validator from 'validator'; |
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { NC_APP_SETTINGS } from '../../constants'; |
||||
import Audit from '../../models/Audit'; |
||||
import ProjectUser from '../../models/ProjectUser'; |
||||
import Store from '../../models/Store'; |
||||
import SyncSource from '../../models/SyncSource'; |
||||
import User from '../../models/User'; |
||||
import Noco from '../../Noco'; |
||||
import { MetaTable } from '../../utils/globals'; |
||||
import { Tele } from 'nc-help'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { NcError } from '../helpers/catchError'; |
||||
import { extractProps } from '../helpers/extractProps'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import { randomTokenString } from '../helpers/stringHelpers'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
import { sendInviteEmail } from './projectUserApis'; |
||||
|
||||
async function userList(req, res) { |
||||
res.json( |
||||
new PagedResponseImpl(await User.list(req.query), { |
||||
...req.query, |
||||
count: await User.count(req.query), |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function userUpdate(req, res) { |
||||
const updateBody = extractProps(req.body, ['roles']); |
||||
|
||||
const user = await User.get(req.params.userId); |
||||
|
||||
if (user.roles.includes(OrgUserRoles.SUPER_ADMIN)) { |
||||
NcError.badRequest('Cannot update super admin roles'); |
||||
} |
||||
|
||||
res.json( |
||||
await User.update(req.params.userId, { |
||||
...updateBody, |
||||
token_version: null, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
async function userDelete(req, res) { |
||||
const ncMeta = await Noco.ncMeta.startTransaction(); |
||||
try { |
||||
const user = await User.get(req.params.userId, ncMeta); |
||||
|
||||
if (user.roles.includes(OrgUserRoles.SUPER_ADMIN)) { |
||||
NcError.badRequest('Cannot delete super admin'); |
||||
} |
||||
|
||||
// delete project user entry and assign to super admin
|
||||
const projectUsers = await ProjectUser.getProjectsIdList( |
||||
req.params.userId, |
||||
ncMeta |
||||
); |
||||
|
||||
// todo: clear cache
|
||||
|
||||
// TODO: assign super admin as project owner
|
||||
for (const projectUser of projectUsers) { |
||||
await ProjectUser.delete( |
||||
projectUser.project_id, |
||||
projectUser.fk_user_id, |
||||
ncMeta |
||||
); |
||||
} |
||||
|
||||
// delete sync source entry
|
||||
await SyncSource.deleteByUserId(req.params.userId, ncMeta); |
||||
|
||||
// delete user
|
||||
await User.delete(req.params.userId, ncMeta); |
||||
await ncMeta.commit(); |
||||
} catch (e) { |
||||
await ncMeta.rollback(e); |
||||
throw e; |
||||
} |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
async function userAdd(req, res, next) { |
||||
// allow only viewer or creator role
|
||||
if ( |
||||
req.body.roles && |
||||
![OrgUserRoles.VIEWER, OrgUserRoles.CREATOR].includes(req.body.roles) |
||||
) { |
||||
NcError.badRequest('Invalid role'); |
||||
} |
||||
|
||||
// extract emails from request body
|
||||
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) { |
||||
NcError.badRequest('User already exist'); |
||||
} else { |
||||
try { |
||||
// create new user with invite token
|
||||
await User.insert({ |
||||
invite_token, |
||||
invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), |
||||
email, |
||||
roles: req.body.roles || OrgUserRoles.VIEWER, |
||||
token_version: randomTokenString(), |
||||
}); |
||||
|
||||
const count = await User.count(); |
||||
Tele.emit('evt', { evt_type: 'org:user:invite', count }); |
||||
|
||||
await Audit.insert({ |
||||
op_type: AuditOperationTypes.ORG_USER, |
||||
op_sub_type: AuditOperationSubTypes.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 userSettings(_req, _res): Promise<any> { |
||||
NcError.notImplemented(); |
||||
} |
||||
|
||||
async function userInviteResend(req, res): Promise<any> { |
||||
const user = await User.get(req.params.userId); |
||||
|
||||
if (!user) { |
||||
NcError.badRequest(`User with id '${req.params.userId}' not found`); |
||||
} |
||||
|
||||
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: AuditOperationTypes.ORG_USER, |
||||
op_sub_type: AuditOperationSubTypes.RESEND_INVITE, |
||||
user: user.email, |
||||
description: `resent a invite to ${user.email} `, |
||||
ip: req.clientIp, |
||||
}); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
async function generateResetUrl(req, res) { |
||||
const user = await User.get(req.params.userId); |
||||
|
||||
if (!user) { |
||||
NcError.badRequest(`User with id '${req.params.userId}' not found`); |
||||
} |
||||
const token = uuidv4(); |
||||
await User.update(user.id, { |
||||
email: user.email, |
||||
reset_password_token: token, |
||||
reset_password_expires: new Date(Date.now() + 60 * 60 * 1000), |
||||
token_version: null, |
||||
}); |
||||
|
||||
res.json({ |
||||
reset_password_token: token, |
||||
reset_password_url: req.ncSiteUrl + `/auth/password/reset/${token}`, |
||||
}); |
||||
} |
||||
|
||||
async function appSettingsGet(_req, res) { |
||||
let settings = {}; |
||||
try { |
||||
settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value); |
||||
} catch {} |
||||
res.json(settings); |
||||
} |
||||
|
||||
async function appSettingsSet(req, res) { |
||||
await Store.saveOrUpdate({ |
||||
value: JSON.stringify(req.body), |
||||
key: NC_APP_SETTINGS, |
||||
}); |
||||
|
||||
res.json({ msg: 'Settings saved' }); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userList, 'userList', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.patch( |
||||
'/api/v1/users/:userId', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/OrgUserReq'), |
||||
ncMetaAclMw(userUpdate, 'userUpdate', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.delete( |
||||
'/api/v1/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userDelete, 'userDelete', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/OrgUserReq'), |
||||
ncMetaAclMw(userAdd, 'userAdd', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users/settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userSettings, 'userSettings', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
router.post( |
||||
'/api/v1/users/:userId/resend-invite', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userInviteResend, 'userInviteResend', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/users/:userId/generate-reset-url', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(generateResetUrl, 'generateResetUrl', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/app-settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(appSettingsGet, 'appSettingsGet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
router.post( |
||||
'/api/v1/app-settings', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(appSettingsSet, 'appSettingsSet', { |
||||
allowedRoles: [OrgUserRoles.SUPER_ADMIN], |
||||
blockApiTokenAccess: true, |
||||
}) |
||||
); |
||||
|
||||
export default router; |
@ -1,287 +0,0 @@
|
||||
import { Request, Response } from 'express'; |
||||
import { OrgUserRoles, ProjectType } from 'nocodb-sdk'; |
||||
import Project from '../../models/Project'; |
||||
import { ProjectListType } from 'nocodb-sdk'; |
||||
import DOMPurify from 'isomorphic-dompurify'; |
||||
import { packageVersion } from '../../utils/packageVersion'; |
||||
import { Tele } from 'nc-help'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import syncMigration from '../helpers/syncMigration'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import ProjectUser from '../../models/ProjectUser'; |
||||
import { customAlphabet } from 'nanoid'; |
||||
import Noco from '../../Noco'; |
||||
import isDocker from 'is-docker'; |
||||
import { NcError } from '../helpers/catchError'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { extractPropsAndSanitize } from '../helpers/extractProps'; |
||||
import NcConfigFactory from '../../utils/NcConfigFactory'; |
||||
import { promisify } from 'util'; |
||||
import { getAjvValidatorMw, populateMeta } from './helpers'; |
||||
import Filter from '../../models/Filter'; |
||||
|
||||
const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4); |
||||
|
||||
// // Project CRUD
|
||||
|
||||
export async function projectGet( |
||||
req: Request<any, any, any>, |
||||
res: Response<Project> |
||||
) { |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
|
||||
// delete datasource connection details
|
||||
project.bases?.forEach((b) => { |
||||
['config'].forEach((k) => delete b[k]); |
||||
}); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectUpdate( |
||||
req: Request<any, any, any>, |
||||
res: Response<ProjectListType> |
||||
) { |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
|
||||
const data: Partial<Project> = extractPropsAndSanitize(req?.body, [ |
||||
'title', |
||||
'meta', |
||||
'color', |
||||
]); |
||||
|
||||
if ( |
||||
data?.title && |
||||
project.title !== data.title && |
||||
(await Project.getByTitle(data.title)) |
||||
) { |
||||
NcError.badRequest('Project title already in use'); |
||||
} |
||||
|
||||
const result = await Project.update(req.params.projectId, data); |
||||
Tele.emit('evt', { evt_type: 'project:update' }); |
||||
res.json(result); |
||||
} |
||||
|
||||
export async function projectList( |
||||
req: Request<any> & { user: { id: string; roles: string } }, |
||||
res: Response<ProjectListType>, |
||||
next |
||||
) { |
||||
try { |
||||
const projects = req.user?.roles?.includes(OrgUserRoles.SUPER_ADMIN) |
||||
? await Project.list(req.query) |
||||
: await ProjectUser.getProjectsList(req.user.id, req.query); |
||||
|
||||
res // todo: pagination
|
||||
.json( |
||||
new PagedResponseImpl(projects as ProjectType[], { |
||||
count: projects.length, |
||||
limit: projects.length, |
||||
}) |
||||
); |
||||
} catch (e) { |
||||
console.log(e); |
||||
next(e); |
||||
} |
||||
} |
||||
|
||||
export async function projectDelete( |
||||
req: Request<any>, |
||||
res: Response<ProjectListType> |
||||
) { |
||||
const result = await Project.softDelete(req.params.projectId); |
||||
Tele.emit('evt', { evt_type: 'project:deleted' }); |
||||
res.json(result); |
||||
} |
||||
|
||||
//
|
||||
//
|
||||
|
||||
async function projectCreate(req: Request<any, any>, res) { |
||||
const projectBody = req.body; |
||||
if (!projectBody.external) { |
||||
const ranId = nanoid(); |
||||
projectBody.prefix = `nc_${ranId}__`; |
||||
projectBody.is_meta = true; |
||||
if (process.env.NC_MINIMAL_DBS) { |
||||
// if env variable NC_MINIMAL_DBS is set, then create a SQLite file/connection for each project
|
||||
// each file will be named as nc_<random_id>.db
|
||||
const fs = require('fs'); |
||||
const toolDir = NcConfigFactory.getToolDir(); |
||||
const nanoidv2 = customAlphabet( |
||||
'1234567890abcdefghijklmnopqrstuvwxyz', |
||||
14 |
||||
); |
||||
if (!(await promisify(fs.exists)(`${toolDir}/nc_minimal_dbs`))) { |
||||
await promisify(fs.mkdir)(`${toolDir}/nc_minimal_dbs`); |
||||
} |
||||
const dbId = nanoidv2(); |
||||
const projectTitle = DOMPurify.sanitize(projectBody.title); |
||||
projectBody.prefix = ''; |
||||
projectBody.bases = [ |
||||
{ |
||||
type: 'sqlite3', |
||||
config: { |
||||
client: 'sqlite3', |
||||
connection: { |
||||
client: 'sqlite3', |
||||
database: projectTitle, |
||||
connection: { |
||||
filename: `${toolDir}/nc_minimal_dbs/${projectTitle}_${dbId}.db`, |
||||
}, |
||||
}, |
||||
}, |
||||
inflection_column: 'camelize', |
||||
inflection_table: 'camelize', |
||||
}, |
||||
]; |
||||
} else { |
||||
const db = Noco.getConfig().meta?.db; |
||||
projectBody.bases = [ |
||||
{ |
||||
type: db?.client, |
||||
config: null, |
||||
is_meta: true, |
||||
inflection_column: 'camelize', |
||||
inflection_table: 'camelize', |
||||
}, |
||||
]; |
||||
} |
||||
} else { |
||||
if (process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED) { |
||||
NcError.badRequest('Connecting to external db is disabled'); |
||||
} |
||||
projectBody.is_meta = false; |
||||
} |
||||
|
||||
if (projectBody?.title.length > 50) { |
||||
NcError.badRequest('Project title exceeds 50 characters'); |
||||
} |
||||
|
||||
if (await Project.getByTitle(projectBody?.title)) { |
||||
NcError.badRequest('Project title already in use'); |
||||
} |
||||
|
||||
projectBody.title = DOMPurify.sanitize(projectBody.title); |
||||
|
||||
const project = await Project.createProject(projectBody); |
||||
await ProjectUser.insert({ |
||||
fk_user_id: (req as any).user.id, |
||||
project_id: project.id, |
||||
roles: 'owner', |
||||
}); |
||||
|
||||
await syncMigration(project); |
||||
|
||||
// populate metadata if existing table
|
||||
for (const base of await project.getBases()) { |
||||
const info = await populateMeta(base, project); |
||||
|
||||
Tele.emit('evt_api_created', info); |
||||
delete base.config; |
||||
} |
||||
|
||||
Tele.emit('evt', { |
||||
evt_type: 'project:created', |
||||
xcdb: !projectBody.external, |
||||
}); |
||||
|
||||
Tele.emit('evt', { evt_type: 'project:rest' }); |
||||
|
||||
res.json(project); |
||||
} |
||||
|
||||
export async function projectInfoGet(_req, res) { |
||||
res.json({ |
||||
Node: process.version, |
||||
Arch: process.arch, |
||||
Platform: process.platform, |
||||
Docker: isDocker(), |
||||
RootDB: Noco.getConfig()?.meta?.db?.client, |
||||
PackageVersion: packageVersion, |
||||
}); |
||||
} |
||||
|
||||
export async function projectCost(req, res) { |
||||
let cost = 0; |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
|
||||
for (const base of project.bases) { |
||||
const sqlClient = await NcConnectionMgrv2.getSqlClient(base); |
||||
const userCount = await ProjectUser.getUsersCount(req.query); |
||||
const recordCount = (await sqlClient.totalRecords())?.data.TotalRecords; |
||||
|
||||
if (recordCount > 100000) { |
||||
// 36,000 or $79/user/month
|
||||
cost = Math.max(36000, 948 * userCount); |
||||
} else if (recordCount > 50000) { |
||||
// $36,000 or $50/user/month
|
||||
cost = Math.max(36000, 600 * userCount); |
||||
} else if (recordCount > 10000) { |
||||
// $240/user/yr
|
||||
cost = Math.min(240 * userCount, 36000); |
||||
} else if (recordCount > 1000) { |
||||
// $120/user/yr
|
||||
cost = Math.min(120 * userCount, 36000); |
||||
} |
||||
} |
||||
|
||||
Tele.event({ |
||||
event: 'a:project:cost', |
||||
data: { |
||||
cost, |
||||
}, |
||||
}); |
||||
|
||||
res.json({ cost }); |
||||
} |
||||
|
||||
export async function hasEmptyOrNullFilters(req, res) { |
||||
res.json(await Filter.hasEmptyOrNullFilters(req.params.projectId)); |
||||
} |
||||
|
||||
export default (router) => { |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/info', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectInfoGet, 'projectInfoGet') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectGet, 'projectGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUpdate, 'projectUpdate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/cost', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectCost, 'projectCost') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/projects/:projectId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectDelete, 'projectDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/ProjectReq'), |
||||
ncMetaAclMw(projectCreate, 'projectCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectList, 'projectList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/has-empty-or-null-filters', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(hasEmptyOrNullFilters, 'hasEmptyOrNullFilters') |
||||
); |
||||
}; |
@ -1,333 +0,0 @@
|
||||
import { OrgUserRoles } from 'nocodb-sdk'; |
||||
import { Tele } from 'nc-help'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { Router } from 'express'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import ProjectUser from '../../models/ProjectUser'; |
||||
import validator from 'validator'; |
||||
import { NcError } from '../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 '../helpers/NcPluginMgrv2'; |
||||
import Noco from '../../Noco'; |
||||
import { PluginCategory } from 'nocodb-sdk'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { randomTokenString } from '../helpers/stringHelpers'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
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), |
||||
} |
||||
), |
||||
}); |
||||
} |
||||
|
||||
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 }); |
||||
} |
||||
} |
||||
|
||||
// @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); |
||||
} |
||||
} |
||||
|
||||
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); |
||||
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('./userApi/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; |
||||
} |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/users', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(userList, 'userList') |
||||
); |
||||
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( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserDelete, 'projectUserDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/projects/:projectId/users/:userId/resend-invite', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(projectUserInviteResend, 'projectUserInviteResend') |
||||
); |
||||
export default router; |
@ -1,5 +0,0 @@
|
||||
import publicDataApis from './publicDataApis'; |
||||
import publicDataExportApis from './publicDataExportApis'; |
||||
import publicMetaApis from './publicMetaApis'; |
||||
|
||||
export { publicDataApis, publicDataExportApis, publicMetaApis }; |
@ -1,472 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../../models/Model'; |
||||
import { nocoExecute } from 'nc-help'; |
||||
import Base from '../../../models/Base'; |
||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
||||
import View from '../../../models/View'; |
||||
import catchError, { NcError } from '../../helpers/catchError'; |
||||
import multer from 'multer'; |
||||
import { ErrorMessages, UITypes, ViewTypes } from 'nocodb-sdk'; |
||||
import Column from '../../../models/Column'; |
||||
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn'; |
||||
import NcPluginMgrv2 from '../../helpers/NcPluginMgrv2'; |
||||
import path from 'path'; |
||||
import { nanoid } from 'nanoid'; |
||||
import { mimeIcons } from '../../../utils/mimeTypes'; |
||||
import slash from 'slash'; |
||||
import { sanitizeUrlPath } from '../attachmentApis'; |
||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||
import { getColumnByIdOrName } from '../dataApis/helpers'; |
||||
import { NC_ATTACHMENT_FIELD_SIZE } from '../../../constants'; |
||||
|
||||
export async function dataList(req: Request, res: Response) { |
||||
try { |
||||
const view = await View.getByUUID(req.params.sharedViewUuid); |
||||
|
||||
if (!view) NcError.notFound('Not found'); |
||||
if ( |
||||
view.type !== ViewTypes.GRID && |
||||
view.type !== ViewTypes.KANBAN && |
||||
view.type !== ViewTypes.GALLERY && |
||||
view.type !== ViewTypes.MAP |
||||
) { |
||||
NcError.notFound('Not found'); |
||||
} |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id, |
||||
}); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.sortArr = JSON.parse(listArgs.sortArrJson); |
||||
} catch (e) {} |
||||
|
||||
let data = []; |
||||
let count = 0; |
||||
|
||||
try { |
||||
data = await nocoExecute( |
||||
await getAst({ |
||||
query: req.query, |
||||
model, |
||||
view, |
||||
}), |
||||
await baseModel.list(listArgs), |
||||
{}, |
||||
listArgs |
||||
); |
||||
count = await baseModel.count(listArgs); |
||||
} catch (e) { |
||||
// show empty result instead of throwing error here
|
||||
// e.g. search some text in a numeric field
|
||||
} |
||||
|
||||
res.json({ |
||||
data: new PagedResponseImpl(data, { ...req.query, count }), |
||||
}); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(500).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
// todo: Handle the error case where view doesnt belong to model
|
||||
async function groupedDataList(req: Request, res: Response) { |
||||
try { |
||||
const view = await View.getByUUID(req.params.sharedViewUuid); |
||||
|
||||
if (!view) NcError.notFound('Not found'); |
||||
|
||||
if ( |
||||
view.type !== ViewTypes.GRID && |
||||
view.type !== ViewTypes.KANBAN && |
||||
view.type !== ViewTypes.GALLERY |
||||
) { |
||||
NcError.notFound('Not found'); |
||||
} |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id, |
||||
}); |
||||
|
||||
res.json(await getGroupedDataList(model, view, req)); |
||||
} catch (e) { |
||||
console.log(e); |
||||
res.status(500).json({ msg: e.message }); |
||||
} |
||||
} |
||||
|
||||
async function getGroupedDataList(model, view: View, req) { |
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const requestObj = await getAst({ model, query: req.query, view }); |
||||
|
||||
const listArgs: any = { ...req.query }; |
||||
try { |
||||
listArgs.filterArr = JSON.parse(listArgs.filterArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.sortArr = JSON.parse(listArgs.sortArrJson); |
||||
} catch (e) {} |
||||
try { |
||||
listArgs.options = JSON.parse(listArgs.optionsArrJson); |
||||
} catch (e) {} |
||||
|
||||
let data = []; |
||||
|
||||
try { |
||||
const groupedData = await baseModel.groupedList({ |
||||
...listArgs, |
||||
groupColumnId: req.params.columnId, |
||||
}); |
||||
data = await nocoExecute( |
||||
{ key: 1, value: requestObj }, |
||||
groupedData, |
||||
{}, |
||||
listArgs |
||||
); |
||||
const countArr = await baseModel.groupedListCount({ |
||||
...listArgs, |
||||
groupColumnId: req.params.columnId, |
||||
}); |
||||
data = data.map((item) => { |
||||
// todo: use map to avoid loop
|
||||
const count = |
||||
countArr.find((countItem: any) => countItem.key === item.key)?.count ?? |
||||
0; |
||||
|
||||
item.value = new PagedResponseImpl(item.value, { |
||||
...req.query, |
||||
count: count, |
||||
}); |
||||
return item; |
||||
}); |
||||
} catch (e) { |
||||
// show empty result instead of throwing error here
|
||||
// e.g. search some text in a numeric field
|
||||
} |
||||
return data; |
||||
} |
||||
|
||||
async function dataInsert( |
||||
req: Request & { files: any[] }, |
||||
res: Response, |
||||
next |
||||
) { |
||||
const view = await View.getByUUID(req.params.sharedViewUuid); |
||||
|
||||
if (!view) return next(new Error('Not found')); |
||||
if (view.type !== ViewTypes.FORM) return next(new Error('Not found')); |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
return res.status(403).json(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const model = await Model.getByIdOrName({ |
||||
id: view?.fk_model_id, |
||||
}); |
||||
const base = await Base.get(model.base_id); |
||||
const project = await base.getProject(); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
await view.getViewWithInfo(); |
||||
await view.getColumns(); |
||||
await view.getModelWithInfo(); |
||||
await view.model.getColumns(); |
||||
|
||||
const fields = (view.model.columns = view.columns |
||||
.filter((c) => c.show) |
||||
.reduce((o, c) => { |
||||
o[view.model.columnsById[c.fk_column_id].title] = new Column({ |
||||
...c, |
||||
...view.model.columnsById[c.fk_column_id], |
||||
} as any); |
||||
return o; |
||||
}, {}) as any); |
||||
|
||||
let body = req.body?.data; |
||||
|
||||
if (typeof body === 'string') body = JSON.parse(body); |
||||
|
||||
const insertObject = Object.entries(body).reduce((obj, [key, val]) => { |
||||
if (key in fields) { |
||||
obj[key] = val; |
||||
} |
||||
return obj; |
||||
}, {}); |
||||
|
||||
const attachments = {}; |
||||
const storageAdapter = await NcPluginMgrv2.storageAdapter(); |
||||
|
||||
for (const file of req.files || []) { |
||||
// remove `_` prefix and `[]` suffix
|
||||
const fieldName = file?.fieldname?.replace(/^_|\[\d*]$/g, ''); |
||||
|
||||
const filePath = sanitizeUrlPath([ |
||||
'v1', |
||||
project.title, |
||||
model.title, |
||||
fieldName, |
||||
]); |
||||
|
||||
if (fieldName in fields && fields[fieldName].uidt === UITypes.Attachment) { |
||||
attachments[fieldName] = attachments[fieldName] || []; |
||||
const fileName = `${nanoid(6)}_${file.originalname}`; |
||||
let url = await storageAdapter.fileCreate( |
||||
slash(path.join('nc', 'uploads', ...filePath, fileName)), |
||||
file |
||||
); |
||||
|
||||
if (!url) { |
||||
url = `${(req as any).ncSiteUrl}/download/${filePath.join( |
||||
'/' |
||||
)}/${fileName}`;
|
||||
} |
||||
|
||||
attachments[fieldName].push({ |
||||
url, |
||||
title: file.originalname, |
||||
mimetype: file.mimetype, |
||||
size: file.size, |
||||
icon: mimeIcons[path.extname(file.originalname).slice(1)] || undefined, |
||||
}); |
||||
} |
||||
} |
||||
|
||||
for (const [column, data] of Object.entries(attachments)) { |
||||
insertObject[column] = JSON.stringify(data); |
||||
} |
||||
|
||||
res.json(await baseModel.nestedInsert(insertObject, null)); |
||||
} |
||||
|
||||
async function relDataList(req, res) { |
||||
const view = await View.getByUUID(req.params.sharedViewUuid); |
||||
|
||||
if (!view) NcError.notFound('Not found'); |
||||
if (view.type !== ViewTypes.FORM) NcError.notFound('Not found'); |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const column = await Column.get({ colId: req.params.columnId }); |
||||
const colOptions = await column.getColOptions<LinkToAnotherRecordColumn>(); |
||||
|
||||
const model = await colOptions.getRelatedTable(); |
||||
|
||||
const base = await Base.get(model.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: model.id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const requestObj = await getAst({ |
||||
query: req.query, |
||||
model, |
||||
extractOnlyPrimaries: true, |
||||
}); |
||||
|
||||
let data = []; |
||||
let count = 0; |
||||
try { |
||||
data = data = await nocoExecute( |
||||
requestObj, |
||||
await baseModel.list(req.query), |
||||
{}, |
||||
req.query |
||||
); |
||||
count = await baseModel.count(req.query); |
||||
} catch (e) { |
||||
// show empty result instead of throwing error here
|
||||
// e.g. search some text in a numeric field
|
||||
} |
||||
|
||||
res.json(new PagedResponseImpl(data, { ...req.query, count })); |
||||
} |
||||
|
||||
export async function publicMmList(req: Request, res: Response) { |
||||
const view = await View.getByUUID(req.params.sharedViewUuid); |
||||
|
||||
if (!view) NcError.notFound('Not found'); |
||||
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found'); |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const column = await getColumnByIdOrName( |
||||
req.params.colId, |
||||
await view.getModel() |
||||
); |
||||
|
||||
if (column.fk_model_id !== view.fk_model_id) |
||||
NcError.badRequest("Column doesn't belongs to the model"); |
||||
|
||||
const base = await Base.get(view.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: view.fk_model_id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = `List`; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.mmList( |
||||
{ |
||||
colId: req.params.colId, |
||||
parentId: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
|
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count: any = await baseModel.mmListCount({ |
||||
colId: req.params.colId, |
||||
parentId: req.params.rowId, |
||||
}); |
||||
|
||||
res.json(new PagedResponseImpl(data, { ...req.query, count })); |
||||
} |
||||
|
||||
export async function publicHmList(req: Request, res: Response) { |
||||
const view = await View.getByUUID(req.params.sharedViewUuid); |
||||
|
||||
if (!view) NcError.notFound('Not found'); |
||||
if (view.type !== ViewTypes.GRID) NcError.notFound('Not found'); |
||||
|
||||
if (view.password && view.password !== req.headers?.['xc-password']) { |
||||
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); |
||||
} |
||||
|
||||
const column = await getColumnByIdOrName( |
||||
req.params.colId, |
||||
await view.getModel() |
||||
); |
||||
|
||||
if (column.fk_model_id !== view.fk_model_id) |
||||
NcError.badRequest("Column doesn't belongs to the model"); |
||||
|
||||
const base = await Base.get(view.base_id); |
||||
|
||||
const baseModel = await Model.getBaseModelSQL({ |
||||
id: view.fk_model_id, |
||||
viewId: view?.id, |
||||
dbDriver: NcConnectionMgrv2.get(base), |
||||
}); |
||||
|
||||
const key = `List`; |
||||
const requestObj: any = { |
||||
[key]: 1, |
||||
}; |
||||
|
||||
const data = ( |
||||
await nocoExecute( |
||||
requestObj, |
||||
{ |
||||
[key]: async (args) => { |
||||
return await baseModel.hmList( |
||||
{ |
||||
colId: req.params.colId, |
||||
id: req.params.rowId, |
||||
}, |
||||
args |
||||
); |
||||
}, |
||||
}, |
||||
{}, |
||||
{ nested: { [key]: req.query } } |
||||
) |
||||
)?.[key]; |
||||
|
||||
const count = await baseModel.hmListCount({ |
||||
colId: req.params.colId, |
||||
id: req.params.rowId, |
||||
}); |
||||
|
||||
res.json(new PagedResponseImpl(data, { ...req.query, count })); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows', |
||||
catchError(dataList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/group/:columnId', |
||||
catchError(groupedDataList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/nested/:columnId', |
||||
catchError(relDataList) |
||||
); |
||||
router.post( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows', |
||||
multer({ |
||||
storage: multer.diskStorage({}), |
||||
limits: { |
||||
fieldSize: NC_ATTACHMENT_FIELD_SIZE, |
||||
}, |
||||
}).any(), |
||||
catchError(dataInsert) |
||||
); |
||||
|
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/mm/:colId', |
||||
catchError(publicMmList) |
||||
); |
||||
router.get( |
||||
'/api/v1/db/public/shared-view/:sharedViewUuid/rows/:rowId/hm/:colId', |
||||
catchError(publicHmList) |
||||
); |
||||
|
||||
export default router; |
@ -1,112 +0,0 @@
|
||||
import { Router } from 'express'; |
||||
import { Tele } from 'nc-help'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { v4 as uuidv4 } from 'uuid'; |
||||
import Project from '../../models/Project'; |
||||
import { NcError } from '../helpers/catchError'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
// todo: load from config
|
||||
const config = { |
||||
dashboardPath: '/nc', |
||||
}; |
||||
|
||||
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(), |
||||
password: req.body?.password, |
||||
roles, |
||||
}; |
||||
|
||||
await Project.update(project.id, data); |
||||
|
||||
data.url = `${req.ncSiteUrl}${config.dashboardPath}#/nc/base/${data.uuid}`; |
||||
delete data.password; |
||||
Tele.emit('evt', { evt_type: 'sharedBase:generated-link' }); |
||||
res.json(data); |
||||
} |
||||
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(), |
||||
password: req.body?.password, |
||||
roles, |
||||
}; |
||||
|
||||
await Project.update(project.id, data); |
||||
|
||||
data.url = `${req.ncSiteUrl}${config.dashboardPath}#/nc/base/${data.uuid}`; |
||||
delete data.password; |
||||
Tele.emit('evt', { evt_type: 'sharedBase:generated-link' }); |
||||
res.json(data); |
||||
} |
||||
|
||||
async function disableSharedBaseLink(req, res): Promise<any> { |
||||
const project = await Project.get(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 }); |
||||
} |
||||
|
||||
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}`; |
||||
|
||||
res.json(data); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(getSharedBaseLink, 'getSharedBaseLink') |
||||
); |
||||
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( |
||||
'/api/v1/db/meta/projects/:projectId/shared', |
||||
ncMetaAclMw(disableSharedBaseLink, 'disableSharedBaseLink') |
||||
); |
||||
export default router; |
@ -1,81 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
// @ts-ignore
|
||||
import Model from '../../models/Model'; |
||||
import { Tele } from 'nc-help'; |
||||
// @ts-ignore
|
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import { SortListType, SortType, TableType } from 'nocodb-sdk'; |
||||
// @ts-ignore
|
||||
import ProjectMgrv2 from '../../db/sql-mgr/v2/ProjectMgrv2'; |
||||
// @ts-ignore
|
||||
import Project from '../../models/Project'; |
||||
import Sort from '../../models/Sort'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
|
||||
// @ts-ignore
|
||||
export async function sortGet(req: Request, res: Response<TableType>) {} |
||||
|
||||
// @ts-ignore
|
||||
export async function sortList( |
||||
req: Request<any, any, any>, |
||||
res: Response<SortListType> |
||||
) { |
||||
const sortList = await Sort.list({ viewId: req.params.viewId }); |
||||
res.json({ |
||||
sorts: new PagedResponseImpl(sortList), |
||||
}); |
||||
} |
||||
|
||||
// @ts-ignore
|
||||
export async function sortCreate(req: Request<any, any, SortType>, res) { |
||||
const sort = await Sort.insert({ |
||||
...req.body, |
||||
fk_view_id: req.params.viewId, |
||||
} as Sort); |
||||
Tele.emit('evt', { evt_type: 'sort:created' }); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortUpdate(req, res) { |
||||
const sort = await Sort.update(req.params.sortId, req.body); |
||||
Tele.emit('evt', { evt_type: 'sort:updated' }); |
||||
res.json(sort); |
||||
} |
||||
|
||||
export async function sortDelete(req: Request, res: Response) { |
||||
Tele.emit('evt', { evt_type: 'sort:deleted' }); |
||||
const sort = await Sort.delete(req.params.sortId); |
||||
res.json(sort); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/views/:viewId/sorts/', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortList, 'sortList') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/views/:viewId/sorts/', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SortReq'), |
||||
ncMetaAclMw(sortCreate, 'sortCreate') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortGet, 'sortGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
getAjvValidatorMw('swagger.json#/components/schemas/SortReq'), |
||||
ncMetaAclMw(sortUpdate, 'sortUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/sorts/:sortId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(sortDelete, 'sortDelete') |
||||
); |
||||
export default router; |
@ -1,61 +0,0 @@
|
||||
// @ts-ignore
|
||||
import catchError, { NcError } from '../../helpers/catchError'; |
||||
import { Router } from 'express'; |
||||
import Model from '../../../models/Model'; |
||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
||||
import getSwaggerJSON from './helpers/getSwaggerJSON'; |
||||
import Project from '../../../models/Project'; |
||||
import getSwaggerHtml from './swaggerHtml'; |
||||
import getRedocHtml from './redocHtml'; |
||||
|
||||
async function swaggerJson(req, res) { |
||||
const project = await Project.get(req.params.projectId); |
||||
|
||||
if (!project) NcError.notFound(); |
||||
|
||||
const models = await Model.list({ |
||||
project_id: req.params.projectId, |
||||
base_id: null, |
||||
}); |
||||
|
||||
const swagger = await getSwaggerJSON(project, models); |
||||
|
||||
swagger.servers = [ |
||||
{ |
||||
url: req.ncSiteUrl, |
||||
}, |
||||
{ |
||||
url: '{customUrl}', |
||||
variables: { |
||||
customUrl: { |
||||
default: req.ncSiteUrl, |
||||
description: 'Provide custom nocodb app base url', |
||||
}, |
||||
}, |
||||
}, |
||||
] as any; |
||||
|
||||
res.json(swagger); |
||||
} |
||||
|
||||
function swaggerHtml(_, res) { |
||||
res.send(getSwaggerHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })); |
||||
} |
||||
|
||||
function redocHtml(_, res) { |
||||
res.send(getRedocHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
|
||||
// todo: auth
|
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/swagger.json', |
||||
ncMetaAclMw(swaggerJson, 'swaggerJson') |
||||
); |
||||
|
||||
router.get('/api/v1/db/meta/projects/:projectId/swagger', swaggerHtml); |
||||
|
||||
router.get('/api/v1/db/meta/projects/:projectId/redoc', redocHtml); |
||||
|
||||
export default router; |
@ -1,447 +0,0 @@
|
||||
import { Request, Response, Router } from 'express'; |
||||
import Model from '../../models/Model'; |
||||
import { Tele } from 'nc-help'; |
||||
import { PagedResponseImpl } from '../helpers/PagedResponse'; |
||||
import DOMPurify from 'isomorphic-dompurify'; |
||||
import { |
||||
AuditOperationSubTypes, |
||||
AuditOperationTypes, |
||||
isVirtualCol, |
||||
ModelTypes, |
||||
NormalColumnRequestType, |
||||
TableListType, |
||||
TableReqType, |
||||
TableType, |
||||
UITypes, |
||||
} from 'nocodb-sdk'; |
||||
import ProjectMgrv2 from '../../db/sql-mgr/v2/ProjectMgrv2'; |
||||
import Project from '../../models/Project'; |
||||
import Audit from '../../models/Audit'; |
||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
||||
import { getAjvValidatorMw } from './helpers'; |
||||
import { xcVisibilityMetaGet } from './modelVisibilityApis'; |
||||
import View from '../../models/View'; |
||||
import getColumnPropsFromUIDT from '../helpers/getColumnPropsFromUIDT'; |
||||
import mapDefaultDisplayValue from '../helpers/mapDefaultDisplayValue'; |
||||
import { NcError } from '../helpers/catchError'; |
||||
import getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName'; |
||||
import Column from '../../models/Column'; |
||||
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||
import getColumnUiType from '../helpers/getColumnUiType'; |
||||
import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; |
||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
||||
|
||||
export async function tableGet(req: Request, res: Response<TableType>) { |
||||
const table = await Model.getWithInfo({ |
||||
id: req.params.tableId, |
||||
}); |
||||
|
||||
// todo: optimise
|
||||
const viewList = <View[]>await xcVisibilityMetaGet(table.project_id, [table]); |
||||
|
||||
//await View.list(req.params.tableId)
|
||||
table.views = viewList.filter((table: any) => { |
||||
return Object.keys((req as any).session?.passport?.user?.roles).some( |
||||
(role) => |
||||
(req as any)?.session?.passport?.user?.roles[role] && |
||||
!table.disabled[role] |
||||
); |
||||
}); |
||||
|
||||
res.json(table); |
||||
} |
||||
|
||||
export async function tableReorder(req: Request, res: Response) { |
||||
res.json(Model.updateOrder(req.params.tableId, req.body.order)); |
||||
} |
||||
|
||||
export async function tableList(req: Request, res: Response<TableListType>) { |
||||
const viewList = await xcVisibilityMetaGet(req.params.projectId); |
||||
|
||||
// todo: optimise
|
||||
const tableViewMapping = viewList.reduce((o, view: any) => { |
||||
o[view.fk_model_id] = o[view.fk_model_id] || 0; |
||||
if ( |
||||
Object.keys((req as any).session?.passport?.user?.roles).some( |
||||
(role) => |
||||
(req as any)?.session?.passport?.user?.roles[role] && |
||||
!view.disabled[role] |
||||
) |
||||
) { |
||||
o[view.fk_model_id]++; |
||||
} |
||||
return o; |
||||
}, {}); |
||||
|
||||
const tableList = ( |
||||
await Model.list({ |
||||
project_id: req.params.projectId, |
||||
base_id: req.params.baseId, |
||||
}) |
||||
).filter((t) => tableViewMapping[t.id]); |
||||
|
||||
res.json( |
||||
new PagedResponseImpl( |
||||
req.query?.includeM2M === 'true' |
||||
? tableList |
||||
: (tableList.filter((t) => !t.mm) as Model[]) |
||||
) |
||||
); |
||||
} |
||||
|
||||
export async function tableCreate(req: Request<any, any, TableReqType>, res) { |
||||
const project = await Project.getWithInfo(req.params.projectId); |
||||
let base = project.bases[0]; |
||||
|
||||
if (req.params.baseId) { |
||||
base = project.bases.find((b) => b.id === req.params.baseId); |
||||
} |
||||
|
||||
if ( |
||||
!req.body.table_name || |
||||
(project.prefix && project.prefix === 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`); |
||||
} |
||||
|
||||
const mxColumnLength = Column.getMaxColumnNameLength(sqlClientType); |
||||
|
||||
for (const column of req.body.columns) { |
||||
if (column.column_name.length > mxColumnLength) { |
||||
NcError.badRequest( |
||||
`Column name ${column.column_name} exceeds ${mxColumnLength} characters` |
||||
); |
||||
} |
||||
} |
||||
|
||||
req.body.columns = req.body.columns?.map((c) => ({ |
||||
...getColumnPropsFromUIDT(c as any, base), |
||||
cn: c.column_name, |
||||
})); |
||||
await sqlMgr.sqlOpPlus(base, 'tableCreate', { |
||||
...req.body, |
||||
tn: req.body.table_name, |
||||
}); |
||||
|
||||
const columns: Array< |
||||
Omit<Column, 'column_name' | 'title'> & { |
||||
cn: string; |
||||
system?: boolean; |
||||
} |
||||
> = (await sqlClient.columnList({ tn: req.body.table_name }))?.data?.list; |
||||
|
||||
const tables = await Model.list({ |
||||
project_id: project.id, |
||||
base_id: base.id, |
||||
}); |
||||
|
||||
await Audit.insert({ |
||||
project_id: project.id, |
||||
base_id: base.id, |
||||
op_type: AuditOperationTypes.TABLE, |
||||
op_sub_type: AuditOperationSubTypes.CREATED, |
||||
user: (req as any)?.user?.email, |
||||
description: `created table ${req.body.table_name} with alias ${req.body.title} `, |
||||
ip: (req as any).clientIp, |
||||
}).then(() => {}); |
||||
|
||||
mapDefaultDisplayValue(req.body.columns); |
||||
|
||||
Tele.emit('evt', { evt_type: 'table:created' }); |
||||
|
||||
res.json( |
||||
await Model.insert(project.id, base.id, { |
||||
...req.body, |
||||
columns: columns.map((c, i) => { |
||||
const colMetaFromReq = req.body?.columns?.find( |
||||
(c1) => c.cn === c1.column_name |
||||
) as NormalColumnRequestType; |
||||
return { |
||||
...colMetaFromReq, |
||||
uidt: |
||||
(colMetaFromReq?.uidt as string) || |
||||
c.uidt || |
||||
getColumnUiType(base, c), |
||||
...c, |
||||
dtxp: [UITypes.MultiSelect, UITypes.SingleSelect].includes( |
||||
colMetaFromReq.uidt as any |
||||
) |
||||
? colMetaFromReq.dtxp |
||||
: c.dtxp, |
||||
title: colMetaFromReq?.title || getColumnNameAlias(c.cn, base), |
||||
column_name: c.cn, |
||||
order: i + 1, |
||||
} as NormalColumnRequestType; |
||||
}), |
||||
order: +(tables?.pop()?.order ?? 0) + 1, |
||||
}) |
||||
); |
||||
} |
||||
|
||||
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); |
||||
|
||||
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, |
||||
}); |
||||
|
||||
Tele.emit('evt', { evt_type: 'table:updated' }); |
||||
|
||||
res.json({ msg: 'success' }); |
||||
} |
||||
|
||||
export async function tableDelete(req: Request, res: Response) { |
||||
const table = await Model.getByIdOrName({ id: req.params.tableId }); |
||||
await table.getColumns(); |
||||
|
||||
const relationColumns = table.columns.filter( |
||||
(c) => c.uidt === UITypes.LinkToAnotherRecord |
||||
); |
||||
|
||||
if (relationColumns?.length) { |
||||
const referredTables = await Promise.all( |
||||
relationColumns.map(async (c) => |
||||
c |
||||
.getColOptions<LinkToAnotherRecordColumn>() |
||||
.then((opt) => opt.getRelatedTable()) |
||||
.then() |
||||
) |
||||
); |
||||
NcError.badRequest( |
||||
`Table can't be deleted since Table is being referred in following tables : ${referredTables.join( |
||||
', ' |
||||
)}. Delete LinkToAnotherRecord columns and try again.` |
||||
); |
||||
} |
||||
|
||||
const project = await Project.getWithInfo(table.project_id); |
||||
const base = project.bases.find((b) => b.id === table.base_id); |
||||
const sqlMgr = await ProjectMgrv2.getSqlMgr(project); |
||||
(table as any).tn = table.table_name; |
||||
table.columns = table.columns.filter((c) => !isVirtualCol(c)); |
||||
table.columns.forEach((c) => { |
||||
(c as any).cn = c.column_name; |
||||
}); |
||||
|
||||
if (table.type === ModelTypes.TABLE) { |
||||
await sqlMgr.sqlOpPlus(base, 'tableDelete', table); |
||||
} else if (table.type === ModelTypes.VIEW) { |
||||
await sqlMgr.sqlOpPlus(base, 'viewDelete', { |
||||
...table, |
||||
view_name: table.table_name, |
||||
}); |
||||
} |
||||
|
||||
await Audit.insert({ |
||||
project_id: project.id, |
||||
base_id: base.id, |
||||
op_type: AuditOperationTypes.TABLE, |
||||
op_sub_type: AuditOperationSubTypes.DELETED, |
||||
user: (req as any)?.user?.email, |
||||
description: `Deleted ${table.type} ${table.table_name} with alias ${table.title} `, |
||||
ip: (req as any).clientIp, |
||||
}).then(() => {}); |
||||
|
||||
Tele.emit('evt', { evt_type: 'table:deleted' }); |
||||
|
||||
res.json(await table.delete()); |
||||
} |
||||
|
||||
const router = Router({ mergeParams: true }); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableList, 'tableList') |
||||
); |
||||
router.get( |
||||
'/api/v1/db/meta/projects/:projectId/:baseId/tables', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableList, 'tableList') |
||||
); |
||||
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( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableGet, 'tableGet') |
||||
); |
||||
router.patch( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableUpdate, 'tableUpdate') |
||||
); |
||||
router.delete( |
||||
'/api/v1/db/meta/tables/:tableId', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableDelete, 'tableDelete') |
||||
); |
||||
router.post( |
||||
'/api/v1/db/meta/tables/:tableId/reorder', |
||||
metaApiMetrics, |
||||
ncMetaAclMw(tableReorder, 'tableReorder') |
||||
); |
||||
export default router; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue