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 { Request, Response, Router } from 'express'; |
||||||
import * as XLSX from 'xlsx'; |
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 { |
import { |
||||||
extractCsvData, |
extractCsvData, |
||||||
extractXlsxData, |
extractXlsxData, |
||||||
getViewAndModelFromRequestByAliasOrId, |
} from '../../services/dataService/helpers'; |
||||||
} from './helpers'; |
import { getViewAndModelFromRequestByAliasOrId } from './helpers'; |
||||||
import apiMetrics from '../../helpers/apiMetrics'; |
|
||||||
import View from '../../../models/View'; |
|
||||||
|
|
||||||
async function excelDataExport(req: Request, res: Response) { |
async function excelDataExport(req: Request, res: Response) { |
||||||
const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); |
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 { Request, Response, Router } from 'express'; |
||||||
import Model from '../../../models/Model'; |
import Model from '../../models/Model'; |
||||||
import { nocoExecute } from 'nc-help'; |
import { nocoExecute } from 'nc-help'; |
||||||
import Base from '../../../models/Base'; |
import Base from '../../models/Base'; |
||||||
import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; |
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||||
import View from '../../../models/View'; |
import View from '../../models/View'; |
||||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||||
import Project from '../../../models/Project'; |
import Project from '../../models/Project'; |
||||||
import { NcError } from '../../helpers/catchError'; |
import { NcError } from '../../meta/helpers/catchError'; |
||||||
import apiMetrics from '../../helpers/apiMetrics'; |
import apiMetrics from '../../meta/helpers/apiMetrics'; |
||||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||||
|
|
||||||
export async function dataList(req: Request, res: Response) { |
export async function dataList(req: Request, res: Response) { |
||||||
const { model, view } = await getViewAndModelFromRequest(req); |
const { model, view } = await getViewAndModelFromRequest(req); |
@ -1,7 +1,7 @@ |
|||||||
import { Request, Response, Router } from 'express'; |
import { Request, Response, Router } from 'express'; |
||||||
import View from '../../models/View'; |
import View from '../models/View'; |
||||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||||
import { extractCsvData } from './dataApis/helpers'; |
import { extractCsvData } from './dataControllers/helpers'; |
||||||
|
|
||||||
async function exportCsv(req: Request, res: Response) { |
async function exportCsv(req: Request, res: Response) { |
||||||
const view = await View.get(req.params.viewId); |
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 { Request, Response, Router } from 'express'; |
||||||
import { MapType, ViewTypes } from 'nocodb-sdk'; |
import { MapType } from 'nocodb-sdk'; |
||||||
import View from '../../models/View'; |
import ncMetaAclMw from '../meta/helpers/ncMetaAclMw'; |
||||||
import ncMetaAclMw from '../helpers/ncMetaAclMw'; |
import { metaApiMetrics } from '../meta/helpers/apiMetrics'; |
||||||
import { Tele } from 'nc-help'; |
import { mapViewService } from '../services'; |
||||||
import { metaApiMetrics } from '../helpers/apiMetrics'; |
|
||||||
import MapView from '../../models/MapView'; |
|
||||||
|
|
||||||
export async function mapViewGet(req: Request, res: Response<MapType>) { |
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) { |
export async function mapViewCreate(req: Request<any, any>, res) { |
||||||
Tele.emit('evt', { evt_type: 'vtable:created', show_as: 'map' }); |
const view = await mapViewService.mapViewCreate({ |
||||||
const view = await View.insert({ |
tableId: req.params.tableId, |
||||||
...req.body, |
map: req.body, |
||||||
// todo: sanitize
|
|
||||||
fk_model_id: req.params.tableId, |
|
||||||
type: ViewTypes.MAP, |
|
||||||
}); |
}); |
||||||
res.json(view); |
res.json(view); |
||||||
} |
} |
||||||
|
|
||||||
export async function mapViewUpdate(req, res) { |
export async function mapViewUpdate(req, res) { |
||||||
Tele.emit('evt', { evt_type: 'view:updated', type: 'map' }); |
res.json( |
||||||
res.json(await MapView.update(req.params.mapViewId, req.body)); |
await mapViewService.mapViewUpdate({ |
||||||
|
mapViewId: req.params.mapViewId, |
||||||
|
map: req.body, |
||||||
|
}) |
||||||
|
); |
||||||
} |
} |
||||||
|
|
||||||
const router = Router({ mergeParams: true }); |
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 { Request, Response, Router } from 'express'; |
||||||
import * as XLSX from 'xlsx'; |
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 { nocoExecute } from 'nc-help'; |
||||||
import papaparse from 'papaparse'; |
import papaparse from 'papaparse'; |
||||||
import { ErrorMessages, isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk'; |
import { ErrorMessages, isSystemColumn, UITypes, ViewTypes } from 'nocodb-sdk'; |
||||||
import Column from '../../../models/Column'; |
import getAst from '../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
||||||
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn'; |
import catchError, { NcError } from '../../meta/helpers/catchError'; |
||||||
import LookupColumn from '../../../models/LookupColumn'; |
import { |
||||||
import catchError, { NcError } from '../../helpers/catchError'; |
Base, |
||||||
import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; |
Column, |
||||||
|
LinkToAnotherRecordColumn, |
||||||
|
LookupColumn, |
||||||
|
Model, |
||||||
|
View, |
||||||
|
} from '../../models'; |
||||||
|
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; |
||||||
|
|
||||||
async function exportExcel(req: Request, res: Response) { |
async function exportExcel(req: Request, res: Response) { |
||||||
const view = await View.getByUUID(req.params.publicDataUuid); |
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 { Request, Response, Router } from 'express'; |
||||||
|
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw'; |
||||||
import SyncSource from '../../../models/SyncSource'; |
import { syncService } from '../../services'; |
||||||
import { Tele } from 'nc-help'; |
|
||||||
import { PagedResponseImpl } from '../../helpers/PagedResponse'; |
|
||||||
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; |
|
||||||
import Project from '../../../models/Project'; |
|
||||||
|
|
||||||
export async function syncSourceList(req: Request, res: Response) { |
export async function syncSourceList(req: Request, res: Response) { |
||||||
// todo: pagination
|
// todo: pagination
|
||||||
res.json( |
res.json( |
||||||
new PagedResponseImpl( |
syncService.syncSourceList({ |
||||||
await SyncSource.list(req.params.projectId, req.params.baseId) |
projectId: req.params.projectId, |
||||||
) |
}) |
||||||
); |
); |
||||||
} |
} |
||||||
|
|
||||||
export async function syncCreate(req: Request, res: Response) { |
export async function syncCreate(req: Request, res: Response) { |
||||||
Tele.emit('evt', { evt_type: 'webhooks:created' }); |
res.json( |
||||||
const project = await Project.getWithInfo(req.params.projectId); |
await syncService.syncCreate({ |
||||||
|
projectId: req.params.projectId, |
||||||
const sync = await SyncSource.insert({ |
baseId: req.params.baseId, |
||||||
...req.body, |
userId: (req as any).user.id, |
||||||
fk_user_id: (req as any).user.id, |
syncPayload: req.body, |
||||||
base_id: req.params.baseId ? req.params.baseId : project.bases[0].id, |
}) |
||||||
project_id: req.params.projectId, |
); |
||||||
}); |
|
||||||
res.json(sync); |
|
||||||
} |
} |
||||||
|
|
||||||
export async function syncDelete(req: Request, res: Response<any>) { |
export async function syncDelete(req: Request, res: Response<any>) { |
||||||
Tele.emit('evt', { evt_type: 'webhooks:deleted' }); |
res.json( |
||||||
res.json(await SyncSource.delete(req.params.syncId)); |
await syncService.syncDelete({ |
||||||
|
syncId: req.params.syncId, |
||||||
|
}) |
||||||
|
); |
||||||
} |
} |
||||||
|
|
||||||
export async function syncUpdate(req: Request, res: Response) { |
export async function syncUpdate(req: Request, res: Response) { |
||||||
Tele.emit('evt', { evt_type: 'webhooks:updated' }); |
res.json( |
||||||
|
await syncService.syncUpdate({ |
||||||
res.json(await SyncSource.update(req.params.syncId, req.body)); |
syncId: req.params.syncId, |
||||||
|
syncPayload: req.body, |
||||||
|
}) |
||||||
|
); |
||||||
} |
} |
||||||
|
|
||||||
const router = Router({ mergeParams: true }); |
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 { 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) { |
export async function reset(req: Request<any, any>, res) { |
||||||
const service = new TestResetService({ |
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