diff --git a/packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue b/packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue index e35df9917d..e96d5f45fe 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue @@ -333,11 +333,14 @@ export default { this.uploading = true for (const file of this.$refs.file.files) { try { - - const data = await this.$api.dbView.upload(this.$store.state.project.projectId, this.viewId, { - files: file, - json: '{}' - }) + const data = await this.$api.dbViewRow.upload( + 'noco', + this.projectName, + this.meta.title, + this.column.title, { + files: file, + json: '{}' + }) this.localState.push(...data) } catch (e) { diff --git a/packages/nocodb-sdk/src/lib/Api.ts b/packages/nocodb-sdk/src/lib/Api.ts index f9f805339d..d3f6fcf87e 100644 --- a/packages/nocodb-sdk/src/lib/Api.ts +++ b/packages/nocodb-sdk/src/lib/Api.ts @@ -1449,23 +1449,6 @@ export class Api< }), }; dbTableColumn = { - /** - * @description Read project details - * - * @tags DB Table column - * @name List - * @summary Column List - * @request GET:/api/v1/db/meta/tables/{tableId}/columns - * @response `200` `ColumnListType` - * @response `201` `ColumnType` Created - */ - list: (tableId: string, params: RequestParams = {}) => - this.request({ - path: `/api/v1/db/meta/tables/${tableId}/columns`, - method: 'GET', - ...params, - }), - /** * No description * @@ -1488,23 +1471,6 @@ export class Api< ...params, }), - /** - * @description Read project details - * - * @tags DB Table column - * @name Read - * @summary Column Read - * @request GET:/api/v1/db/meta/columns/{columnId} - * @response `200` `ColumnType` OK - */ - read: (columnId: string, params: RequestParams = {}) => - this.request({ - path: `/api/v1/db/meta/columns/${columnId}`, - method: 'GET', - format: 'json', - ...params, - }), - /** * No description * @@ -1620,7 +1586,7 @@ export class Api< * * @tags DB View * @name ShowAllColumn - * @request POST:/api/v1/db/meta/views/{viewId}/showAll + * @request POST:/api/v1/db/meta/views/{viewId}/show-all * @response `200` `void` OK */ showAllColumn: ( @@ -1629,7 +1595,7 @@ export class Api< params: RequestParams = {} ) => this.request({ - path: `/api/v1/db/meta/views/${viewId}/showAll`, + path: `/api/v1/db/meta/views/${viewId}/show-all`, method: 'POST', query: query, ...params, @@ -1640,7 +1606,7 @@ export class Api< * * @tags DB View * @name HideAllColumn - * @request POST:/api/v1/db/meta/views/{viewId}/hideAll + * @request POST:/api/v1/db/meta/views/{viewId}/hide-all * @response `200` `void` OK */ hideAllColumn: ( @@ -1649,7 +1615,7 @@ export class Api< params: RequestParams = {} ) => this.request({ - path: `/api/v1/db/meta/views/${viewId}/hideAll`, + path: `/api/v1/db/meta/views/${viewId}/hide-all`, method: 'POST', query: query, ...params, @@ -1842,28 +1808,6 @@ export class Api< format: 'json', ...params, }), - - /** - * No description - * - * @tags DB View - * @name Upload - * @summary Attachment - * @request POST:/projects/{projectId}/views/{viewId}/upload - */ - upload: ( - projectId: string, - viewId: string, - data: { files?: any; json?: string }, - params: RequestParams = {} - ) => - this.request({ - path: `/projects/${projectId}/views/${viewId}/upload`, - method: 'POST', - body: data, - type: ContentType.FormData, - ...params, - }), }; dbViewShare = { /** @@ -1968,22 +1912,6 @@ export class Api< ...params, }), - /** - * No description - * - * @tags DB View Column - * @name Read - * @request GET:/api/v1/db/meta/views/{viewId}/columns/{columnId} - * @response `200` `any` OK - */ - read: (viewId: string, columnId: string, params: RequestParams = {}) => - this.request({ - path: `/api/v1/db/meta/views/${viewId}/columns/${columnId}`, - method: 'GET', - format: 'json', - ...params, - }), - /** * No description * @@ -2850,6 +2778,30 @@ export class Api< wrapped: true, ...params, }), + + /** + * No description + * + * @tags DB View row + * @name Upload + * @summary Attachment + * @request POST:/api/v1/db/data-attachment/{orgs}/{projectName}/{tableName}/{columnName} + */ + upload: ( + orgs: string, + projectName: string, + tableName: string, + columnName: string, + data: { files?: any; json?: string }, + params: RequestParams = {} + ) => + this.request({ + path: `/api/v1/db/data-attachment/${orgs}/${projectName}/${tableName}/${columnName}`, + method: 'POST', + body: data, + type: ContentType.FormData, + ...params, + }), }; public = { /** @@ -2883,14 +2835,14 @@ export class Api< */ dataCreate: ( sharedViewUuid: string, - data: any, + data: object, params: RequestParams = {} ) => this.request({ path: `/api/v1/db/public/shared-view/${sharedViewUuid}/rows`, method: 'POST', body: data, - type: ContentType.Json, + type: ContentType.FormData, format: 'json', ...params, }), @@ -2919,30 +2871,6 @@ export class Api< ...params, }), - /** - * No description - * - * @tags Public - * @name DataNestedExcludedList - * @request GET:/api/v1/db/public/shared-view/{sharedViewUuid}/rows/{rowId}/{relationType}/{columnName}/exclude - * @response `200` `any` OK - */ - dataNestedExcludedList: ( - sharedViewUuid: string, - rowId: string, - relationType: 'mm' | 'hm', - columnName: string, - query?: { limit?: string; offset?: string }, - params: RequestParams = {} - ) => - this.request({ - path: `/api/v1/db/public/shared-view/${sharedViewUuid}/rows/${rowId}/${relationType}/${columnName}/exclude`, - method: 'GET', - query: query, - format: 'json', - ...params, - }), - /** * No description * diff --git a/packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts b/packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts index b5604a2e21..386e59d0a6 100644 --- a/packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts +++ b/packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts @@ -12,12 +12,13 @@ import NcPluginMgrv2 from '../helpers/NcPluginMgrv2'; // const storageAdapter = new Local(); export async function upload(req: Request, res: Response) { - const destPath = path.join( - 'nc', - 'uploads', - req.params.projectId, - req.params.viewId - ); + const filePath = sanitizeUrlPath([ + req.params.orgs, + req.params.projectName, + req.params.tableName, + req.params.columnName + ]); + const destPath = path.join('nc', 'uploads', ...filePath); const storageAdapter = await NcPluginMgrv2.storageAdapter(); const attachments = await Promise.all( @@ -30,9 +31,9 @@ export async function upload(req: Request, res: Response) { ); if (!url) { - url = `${(req as any).ncSiteUrl}/download/${req.params.projectId}/${ - req.params.viewId - }/${fileName}`; + url = `${ + (req as any).ncSiteUrl + }/api/v1/db/data-attachment/${filePath.join('/')}/${fileName}`; } return { @@ -67,8 +68,10 @@ export async function fileRead(req, res) { path.join( 'nc', 'uploads', - req.params.projectId, - req.params.viewId, + req.params.orgs, + req.params.projectName, + req.params.tableName, + req.params.columnName, req.params.fileName ) ) @@ -82,15 +85,6 @@ export async function fileRead(req, res) { } const router = Router({ mergeParams: true }); -router.post( - '/projects/:projectId/views/:viewId/upload', - multer({ - storage: multer.diskStorage({}) - }).any(), - ncMetaAclMw(upload, 'upload') -); -router.get('/download/:projectId/:viewId/:fileName', catchError(fileRead)); - router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => { try { // const type = mimetypes[path.extname(req.params.fileName).slice(1)] || 'text/plain'; @@ -122,4 +116,21 @@ router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => { res.status(404).send('Not found'); } }); + +export function sanitizeUrlPath(paths) { + return paths.map(url => url.replace(/[/.?#]+/g, '_')); +} + +router.post( + '/api/v1/db/data-attachment/:orgs/:projectName/:tableName/:columnName', + multer({ + storage: multer.diskStorage({}) + }).any(), + ncMetaAclMw(upload, 'upload') +); +router.get( + '/api/v1/db/data-attachment/:orgs/:projectName/:tableName/:columnName/:fileName', + catchError(fileRead) +); + export default router; diff --git a/packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts b/packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts index 6e77eb92c4..328ce70a8e 100644 --- a/packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts +++ b/packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts @@ -15,6 +15,7 @@ import path from 'path'; import { nanoid } from 'nanoid'; import { mimeIcons } from '../../../../utils/mimeTypes'; import slash from 'slash'; +import { sanitizeUrlPath } from '../attachmentApis'; export async function dataList(req: Request, res: Response) { try { @@ -83,6 +84,7 @@ async function dataInsert( 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, @@ -122,18 +124,26 @@ async function dataInsert( for (const file of req.files || []) { // remove `_` prefix and `[]` suffix const fieldName = file?.fieldname?.replace(/^_|\[\d*]$/g, ''); + + const filePath = sanitizeUrlPath([ + 'noco', + 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', base.project_id, view.id, fileName)), + slash(path.join('nc', 'uploads', ...filePath, fileName)), file ); if (!url) { - url = `${(req as any).ncSiteUrl}/download/${base.project_id}/${ - view.id - }/${fileName}`; + url = `${ + (req as any).ncSiteUrl + }/api/v1/db/data-attachment/${filePath.join('/')}/${fileName}`; } attachments[fieldName].push({ diff --git a/scripts/sdk/swagger.json b/scripts/sdk/swagger.json index c945c05408..6aca38d910 100644 --- a/scripts/sdk/swagger.json +++ b/scripts/sdk/swagger.json @@ -3282,13 +3282,13 @@ } }, - "/projects/{projectId}/views/{viewId}/upload": { + "/api/v1/db/data-attachment/{orgs}/{projectName}/{tableName}/{columnName}": { "post": { "summary": "Attachment", - "operationId": "db-view-upload", + "operationId": "db-view-row-upload", "responses": {}, "tags": [ - "DB View" + "DB View row" ], "requestBody": { "content": { @@ -3312,7 +3312,7 @@ "schema": { "type": "string" }, - "name": "projectId", + "name": "orgs", "in": "path", "required": true }, @@ -3320,7 +3320,23 @@ "schema": { "type": "string" }, - "name": "viewId", + "name": "projectName", + "in": "path", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "tableName", + "in": "path", + "required": true + }, + { + "schema": { + "type": "string" + }, + "name": "columnName", "in": "path", "required": true } @@ -3395,8 +3411,11 @@ }, "requestBody": { "content": { - "application/json": { - "schema": {} + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { } + } } } },