Browse Source

Merge branch 'develop' of https://github.com/nocodb/nocodb into develop

pull/1668/head
Wing-Kam Wong 2 years ago
parent
commit
ab1b832cc7
  1. 4
      .github/workflows/release-nightly-dev.yml
  2. 13
      packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue
  3. 132
      packages/nocodb-sdk/src/lib/Api.ts
  4. 51
      packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts
  5. 18
      packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts
  6. 33
      scripts/sdk/swagger.json

4
.github/workflows/release-nightly-dev.yml

@ -12,8 +12,8 @@ on:
- DEV - DEV
# - PROD # - PROD
schedule: schedule:
# at the end of every day # every 6 hours
- cron: '0 0 * * *' - cron: '0 */6 * * *'
jobs: jobs:
# enrich tag for nightly auto release # enrich tag for nightly auto release

13
packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue

@ -333,11 +333,14 @@ export default {
this.uploading = true this.uploading = true
for (const file of this.$refs.file.files) { for (const file of this.$refs.file.files) {
try { try {
const data = await this.$api.dbViewRow.upload(
const data = await this.$api.dbView.upload(this.$store.state.project.projectId, this.viewId, { 'noco',
files: file, this.projectName,
json: '{}' this.meta.title,
}) this.column.title, {
files: file,
json: '{}'
})
this.localState.push(...data) this.localState.push(...data)
} catch (e) { } catch (e) {

132
packages/nocodb-sdk/src/lib/Api.ts

@ -1449,23 +1449,6 @@ export class Api<
}), }),
}; };
dbTableColumn = { 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<ColumnListType, any>({
path: `/api/v1/db/meta/tables/${tableId}/columns`,
method: 'GET',
...params,
}),
/** /**
* No description * No description
* *
@ -1488,23 +1471,6 @@ export class Api<
...params, ...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<ColumnType, any>({
path: `/api/v1/db/meta/columns/${columnId}`,
method: 'GET',
format: 'json',
...params,
}),
/** /**
* No description * No description
* *
@ -1620,7 +1586,7 @@ export class Api<
* *
* @tags DB View * @tags DB View
* @name ShowAllColumn * @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 * @response `200` `void` OK
*/ */
showAllColumn: ( showAllColumn: (
@ -1629,7 +1595,7 @@ export class Api<
params: RequestParams = {} params: RequestParams = {}
) => ) =>
this.request<void, any>({ this.request<void, any>({
path: `/api/v1/db/meta/views/${viewId}/showAll`, path: `/api/v1/db/meta/views/${viewId}/show-all`,
method: 'POST', method: 'POST',
query: query, query: query,
...params, ...params,
@ -1640,7 +1606,7 @@ export class Api<
* *
* @tags DB View * @tags DB View
* @name HideAllColumn * @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 * @response `200` `void` OK
*/ */
hideAllColumn: ( hideAllColumn: (
@ -1649,7 +1615,7 @@ export class Api<
params: RequestParams = {} params: RequestParams = {}
) => ) =>
this.request<void, any>({ this.request<void, any>({
path: `/api/v1/db/meta/views/${viewId}/hideAll`, path: `/api/v1/db/meta/views/${viewId}/hide-all`,
method: 'POST', method: 'POST',
query: query, query: query,
...params, ...params,
@ -1842,28 +1808,6 @@ export class Api<
format: 'json', format: 'json',
...params, ...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<any, any>({
path: `/projects/${projectId}/views/${viewId}/upload`,
method: 'POST',
body: data,
type: ContentType.FormData,
...params,
}),
}; };
dbViewShare = { dbViewShare = {
/** /**
@ -1968,22 +1912,6 @@ export class Api<
...params, ...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<any, any>({
path: `/api/v1/db/meta/views/${viewId}/columns/${columnId}`,
method: 'GET',
format: 'json',
...params,
}),
/** /**
* No description * No description
* *
@ -2850,6 +2778,30 @@ export class Api<
wrapped: true, wrapped: true,
...params, ...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<any, any>({
path: `/api/v1/db/data-attachment/${orgs}/${projectName}/${tableName}/${columnName}`,
method: 'POST',
body: data,
type: ContentType.FormData,
...params,
}),
}; };
public = { public = {
/** /**
@ -2883,14 +2835,14 @@ export class Api<
*/ */
dataCreate: ( dataCreate: (
sharedViewUuid: string, sharedViewUuid: string,
data: any, data: object,
params: RequestParams = {} params: RequestParams = {}
) => ) =>
this.request<any, any>({ this.request<any, any>({
path: `/api/v1/db/public/shared-view/${sharedViewUuid}/rows`, path: `/api/v1/db/public/shared-view/${sharedViewUuid}/rows`,
method: 'POST', method: 'POST',
body: data, body: data,
type: ContentType.Json, type: ContentType.FormData,
format: 'json', format: 'json',
...params, ...params,
}), }),
@ -2919,30 +2871,6 @@ export class Api<
...params, ...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<any, any>({
path: `/api/v1/db/public/shared-view/${sharedViewUuid}/rows/${rowId}/${relationType}/${columnName}/exclude`,
method: 'GET',
query: query,
format: 'json',
...params,
}),
/** /**
* No description * No description
* *

51
packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts

@ -12,12 +12,13 @@ import NcPluginMgrv2 from '../helpers/NcPluginMgrv2';
// const storageAdapter = new Local(); // const storageAdapter = new Local();
export async function upload(req: Request, res: Response) { export async function upload(req: Request, res: Response) {
const destPath = path.join( const filePath = sanitizeUrlPath([
'nc', req.params.orgs,
'uploads', req.params.projectName,
req.params.projectId, req.params.tableName,
req.params.viewId req.params.columnName
); ]);
const destPath = path.join('nc', 'uploads', ...filePath);
const storageAdapter = await NcPluginMgrv2.storageAdapter(); const storageAdapter = await NcPluginMgrv2.storageAdapter();
const attachments = await Promise.all( const attachments = await Promise.all(
@ -30,9 +31,9 @@ export async function upload(req: Request, res: Response) {
); );
if (!url) { if (!url) {
url = `${(req as any).ncSiteUrl}/download/${req.params.projectId}/${ url = `${
req.params.viewId (req as any).ncSiteUrl
}/${fileName}`; }/api/v1/db/data-attachment/${filePath.join('/')}/${fileName}`;
} }
return { return {
@ -67,8 +68,10 @@ export async function fileRead(req, res) {
path.join( path.join(
'nc', 'nc',
'uploads', 'uploads',
req.params.projectId, req.params.orgs,
req.params.viewId, req.params.projectName,
req.params.tableName,
req.params.columnName,
req.params.fileName req.params.fileName
) )
) )
@ -82,15 +85,6 @@ export async function fileRead(req, res) {
} }
const router = Router({ mergeParams: true }); 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) => { router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => {
try { try {
// const type = mimetypes[path.extname(req.params.fileName).slice(1)] || 'text/plain'; // 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'); 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; export default router;

18
packages/nocodb/src/lib/noco/meta/api/publicApis/publicDataApis.ts

@ -15,6 +15,7 @@ import path from 'path';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { mimeIcons } from '../../../../utils/mimeTypes'; import { mimeIcons } from '../../../../utils/mimeTypes';
import slash from 'slash'; import slash from 'slash';
import { sanitizeUrlPath } from '../attachmentApis';
export async function dataList(req: Request, res: Response) { export async function dataList(req: Request, res: Response) {
try { try {
@ -83,6 +84,7 @@ async function dataInsert(
id: view?.fk_model_id id: view?.fk_model_id
}); });
const base = await Base.get(model.base_id); const base = await Base.get(model.base_id);
const project = await base.getProject();
const baseModel = await Model.getBaseModelSQL({ const baseModel = await Model.getBaseModelSQL({
id: model.id, id: model.id,
@ -122,18 +124,26 @@ async function dataInsert(
for (const file of req.files || []) { for (const file of req.files || []) {
// remove `_` prefix and `[]` suffix // remove `_` prefix and `[]` suffix
const fieldName = file?.fieldname?.replace(/^_|\[\d*]$/g, ''); 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) { if (fieldName in fields && fields[fieldName].uidt === UITypes.Attachment) {
attachments[fieldName] = attachments[fieldName] || []; attachments[fieldName] = attachments[fieldName] || [];
const fileName = `${nanoid(6)}_${file.originalname}`; const fileName = `${nanoid(6)}_${file.originalname}`;
let url = await storageAdapter.fileCreate( let url = await storageAdapter.fileCreate(
slash(path.join('nc', 'uploads', base.project_id, view.id, fileName)), slash(path.join('nc', 'uploads', ...filePath, fileName)),
file file
); );
if (!url) { if (!url) {
url = `${(req as any).ncSiteUrl}/download/${base.project_id}/${ url = `${
view.id (req as any).ncSiteUrl
}/${fileName}`; }/api/v1/db/data-attachment/${filePath.join('/')}/${fileName}`;
} }
attachments[fieldName].push({ attachments[fieldName].push({

33
scripts/sdk/swagger.json

@ -3282,13 +3282,13 @@
} }
}, },
"/projects/{projectId}/views/{viewId}/upload": { "/api/v1/db/data-attachment/{orgs}/{projectName}/{tableName}/{columnName}": {
"post": { "post": {
"summary": "Attachment", "summary": "Attachment",
"operationId": "db-view-upload", "operationId": "db-view-row-upload",
"responses": {}, "responses": {},
"tags": [ "tags": [
"DB View" "DB View row"
], ],
"requestBody": { "requestBody": {
"content": { "content": {
@ -3312,7 +3312,7 @@
"schema": { "schema": {
"type": "string" "type": "string"
}, },
"name": "projectId", "name": "orgs",
"in": "path", "in": "path",
"required": true "required": true
}, },
@ -3320,7 +3320,23 @@
"schema": { "schema": {
"type": "string" "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", "in": "path",
"required": true "required": true
} }
@ -3395,8 +3411,11 @@
}, },
"requestBody": { "requestBody": {
"content": { "content": {
"application/json": { "multipart/form-data": {
"schema": {} "schema": {
"type": "object",
"properties": { }
}
} }
} }
}, },

Loading…
Cancel
Save