Browse Source

feat: attachment upload by url

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2214/head
Pranav C 3 years ago
parent
commit
72ce83af50
  1. 33
      packages/nc-gui/components/project/spreadsheet/components/editableCell/EditableAttachmentCell.vue
  2. 12099
      packages/nc-plugin/package-lock.json
  3. 2
      packages/nc-plugin/package.json
  4. 4
      packages/nc-plugin/src/index.ts
  5. 34
      packages/nc-plugin/src/lib/IStorageAdapterV2.ts
  6. 22
      packages/nocodb-sdk/src/lib/Api.ts
  7. 14
      packages/nocodb/package-lock.json
  8. 2
      packages/nocodb/package.json
  9. 5
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSqlv2.ts
  10. 44
      packages/nocodb/src/lib/noco/meta/api/attachmentApis.ts
  11. 4
      packages/nocodb/src/lib/noco/meta/helpers/NcPluginMgrv2.ts
  12. 46
      packages/nocodb/src/lib/noco/plugins/adapters/storage/Local.ts
  13. 4
      packages/nocodb/src/lib/utils/projectAcl.ts
  14. 37
      packages/nocodb/src/plugins/backblaze/Backblaze.ts
  15. 4
      packages/nocodb/src/plugins/backblaze/BackblazePlugin.ts
  16. 27
      packages/nocodb/src/plugins/gcs/Gcs.ts
  17. 4
      packages/nocodb/src/plugins/gcs/GcsPlugin.ts
  18. 37
      packages/nocodb/src/plugins/linode/LinodeObjectStorage.ts
  19. 4
      packages/nocodb/src/plugins/linode/LinodeObjectStoragePlugin.ts
  20. 74
      packages/nocodb/src/plugins/mino/Minio.ts
  21. 4
      packages/nocodb/src/plugins/mino/MinioPlugin.ts
  22. 37
      packages/nocodb/src/plugins/ovhCloud/OvhCloud.ts
  23. 4
      packages/nocodb/src/plugins/ovhCloud/OvhCloudPlugin.ts
  24. 36
      packages/nocodb/src/plugins/s3/S3.ts
  25. 4
      packages/nocodb/src/plugins/s3/S3Plugin.ts
  26. 37
      packages/nocodb/src/plugins/scaleway/ScalewayObjectStorage.ts
  27. 4
      packages/nocodb/src/plugins/scaleway/ScalewayObjectStoragePlugin.ts
  28. 37
      packages/nocodb/src/plugins/spaces/Spaces.ts
  29. 4
      packages/nocodb/src/plugins/spaces/SpacesPlugin.ts
  30. 4
      packages/nocodb/src/plugins/upcloud/UpCloudPlugin.ts
  31. 37
      packages/nocodb/src/plugins/upcloud/UpoCloud.ts
  32. 37
      packages/nocodb/src/plugins/vultr/Vultr.ts
  33. 4
      packages/nocodb/src/plugins/vultr/VultrPlugin.ts
  34. 58
      scripts/sdk/swagger.json

33
packages/nc-gui/components/project/spreadsheet/components/editableCell/EditableAttachmentCell.vue

@ -62,7 +62,11 @@
</v-tooltip> </v-tooltip>
</div> </div>
</div> </div>
<div v-if="isForm || active && !isPublicGrid && !isLocked" class="add d-flex align-center justify-center px-1 nc-attachment-add" @click="addFile"> <div
v-if="isForm || active && !isPublicGrid && !isLocked"
class="add d-flex align-center justify-center px-1 nc-attachment-add"
@click="addFile"
>
<v-icon v-if="uploading" small color="primary" class="nc-attachment-add-spinner"> <v-icon v-if="uploading" small color="primary" class="nc-attachment-add-spinner">
mdi-loading mdi-spin mdi-loading mdi-spin
</v-icon> </v-icon>
@ -76,9 +80,15 @@
> >
<v-icon x-small color=""> <v-icon x-small color="">
mdi-plus mdi-plus
</v-icon> Attachment </v-icon>
Attachment
</v-btn> </v-btn>
<v-icon v-else-if="_isUIAllowed('tableAttachment')" v-show="active" small color="primary nc-attachment-add-icon"> <v-icon
v-else-if="_isUIAllowed('tableAttachment')"
v-show="active"
small
color="primary nc-attachment-add-icon"
>
mdi-plus mdi-plus
</v-icon> </v-icon>
</div> </div>
@ -110,6 +120,8 @@
</v-icon> </v-icon>
<span class="caption">Attach File</span> <span class="caption">Attach File</span>
</v-btn> </v-btn>
<!-- <v-text-field v-model="urlString" @keypress.enter="uploadByUrl" />-->
</div> </div>
<div class="d-flex flex-wrap h-100"> <div class="d-flex flex-wrap h-100">
@ -261,7 +273,8 @@ export default {
showImage: false, showImage: false,
selectedImage: null, selectedImage: null,
dragOver: false, dragOver: false,
localFilesState: [] localFilesState: [],
urlString: ''
}), }),
watch: { watch: {
value(val, prev) { value(val, prev) {
@ -286,6 +299,18 @@ export default {
mounted() { mounted() {
}, },
methods: { methods: {
async uploadByUrl() {
const data = await this.$api.storage.uploadByUrl(
{
path: ['noco', this.projectName, this.meta.title, this.column.title].join('/')
},
[{
url: this.urlString
}]
)
this.localState.push(...data)
},
openUrl(url, target) { openUrl(url, target) {
window.open(url, target) window.open(url, target)
}, },

12099
packages/nc-plugin/package-lock.json generated

File diff suppressed because it is too large Load Diff

2
packages/nc-plugin/package.json

@ -1,6 +1,6 @@
{ {
"name": "nc-plugin", "name": "nc-plugin",
"version": "0.1.1", "version": "0.1.3",
"description": "Xgene plugin template", "description": "Xgene plugin template",
"main": "build/main/index.js", "main": "build/main/index.js",
"typings": "build/main/index.d.ts", "typings": "build/main/index.d.ts",

4
packages/nc-plugin/src/index.ts

@ -5,6 +5,7 @@ import XcPluginMigration from './lib/XcPluginMigration';
import XcStoragePlugin from './lib/XcStoragePlugin'; import XcStoragePlugin from './lib/XcStoragePlugin';
import XcEmailPlugin from './lib/XcEmailPlugin'; import XcEmailPlugin from './lib/XcEmailPlugin';
import IStorageAdapter, {XcFile} from './lib/IStorageAdapter'; import IStorageAdapter, {XcFile} from './lib/IStorageAdapter';
import IStorageAdapterV2 from './lib/IStorageAdapterV2';
import IEmailAdapter, {XcEmail} from './lib/IEmailAdapter'; import IEmailAdapter, {XcEmail} from './lib/IEmailAdapter';
import IWebhookNotificationAdapter from './lib/IWebhookNotificationAdapter'; import IWebhookNotificationAdapter from './lib/IWebhookNotificationAdapter';
import XcWebhookNotificationPlugin from './lib/XcWebhookNotificationPlugin'; import XcWebhookNotificationPlugin from './lib/XcWebhookNotificationPlugin';
@ -21,5 +22,6 @@ export {
XcFile, XcFile,
XcEmail, XcEmail,
IWebhookNotificationAdapter, IWebhookNotificationAdapter,
XcWebhookNotificationPlugin XcWebhookNotificationPlugin,
IStorageAdapterV2
} }

34
packages/nc-plugin/src/lib/IStorageAdapterV2.ts

@ -0,0 +1,34 @@
import IStorageAdapter from "./IStorageAdapter";
export default interface IStorageAdapterV2 extends IStorageAdapter {
fileCreateByUrl(destPath: string, url: string, fileMeta?: FileMeta): Promise<any>
}
interface FileMeta {
fileName?: string;
mimetype?: string;
size?: number | string;
}
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

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

@ -3501,5 +3501,27 @@ export class Api<
type: ContentType.FormData, type: ContentType.FormData,
...params, ...params,
}), }),
/**
* No description
*
* @tags Storage
* @name UploadByUrl
* @summary Attachment
* @request POST:/api/v1/db/storage/upload-by-url
*/
uploadByUrl: (
query: { path: string },
data: { url?: string }[],
params: RequestParams = {}
) =>
this.request<any, any>({
path: `/api/v1/db/storage/upload-by-url`,
method: 'POST',
query: query,
body: data,
type: ContentType.Json,
...params,
}),
}; };
} }

14
packages/nocodb/package-lock.json generated

@ -71,7 +71,7 @@
"nc-common": "0.0.6", "nc-common": "0.0.6",
"nc-help": "0.2.59", "nc-help": "0.2.59",
"nc-lib-gui": "0.91.1", "nc-lib-gui": "0.91.1",
"nc-plugin": "^0.1.1", "nc-plugin": "0.1.2",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk", "nocodb-sdk": "file:../nocodb-sdk",
"nodemailer": "^6.4.10", "nodemailer": "^6.4.10",
@ -16083,9 +16083,9 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}, },
"node_modules/nc-plugin": { "node_modules/nc-plugin": {
"version": "0.1.1", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.1.tgz", "resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.2.tgz",
"integrity": "sha512-oJLpPX9fvdxZjNZmX0SLq0XYjCnT6oFcRCapTLr6Gxuc4ay6tP7Spj96bbENNxe8bHJPdO1mlb4BEK6nA0nELg==", "integrity": "sha512-9NZJPIgD6r3GnCNJelfZFga+RQPDuHZCoFus2qaZsNdbJEP7LSTOeZ1Pw8l89fvVbp0VFLO3Bcg8ggJppKt8JQ==",
"dependencies": { "dependencies": {
"@bitauth/libauth": "^1.17.1", "@bitauth/libauth": "^1.17.1",
"nc-common": "0.0.6" "nc-common": "0.0.6"
@ -37373,9 +37373,9 @@
} }
}, },
"nc-plugin": { "nc-plugin": {
"version": "0.1.1", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.1.tgz", "resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.2.tgz",
"integrity": "sha512-oJLpPX9fvdxZjNZmX0SLq0XYjCnT6oFcRCapTLr6Gxuc4ay6tP7Spj96bbENNxe8bHJPdO1mlb4BEK6nA0nELg==", "integrity": "sha512-9NZJPIgD6r3GnCNJelfZFga+RQPDuHZCoFus2qaZsNdbJEP7LSTOeZ1Pw8l89fvVbp0VFLO3Bcg8ggJppKt8JQ==",
"requires": { "requires": {
"@bitauth/libauth": "^1.17.1", "@bitauth/libauth": "^1.17.1",
"nc-common": "0.0.6" "nc-common": "0.0.6"

2
packages/nocodb/package.json

@ -154,7 +154,7 @@
"nc-common": "0.0.6", "nc-common": "0.0.6",
"nc-help": "0.2.59", "nc-help": "0.2.59",
"nc-lib-gui": "0.91.1", "nc-lib-gui": "0.91.1",
"nc-plugin": "^0.1.1", "nc-plugin": "0.1.2",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"nocodb-sdk": "file:../nocodb-sdk", "nocodb-sdk": "file:../nocodb-sdk",
"nodemailer": "^6.4.10", "nodemailer": "^6.4.10",

5
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSqlv2.ts

@ -1527,6 +1527,11 @@ class BaseModelSqlv2 {
for (const data of datas) { for (const data of datas) {
await this.validate(data); await this.validate(data);
} }
// let chunkSize = 50;
//
// if (this.isSqlite && datas[0]) {
// chunkSize = Math.max(1, Math.floor(999 / Object.keys(datas[0]).length));
// }
// fallbacks to `10` if database client is sqlite // fallbacks to `10` if database client is sqlite
// to avoid `too many SQL variables` error // to avoid `too many SQL variables` error

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

@ -47,6 +47,45 @@ export async function upload(req: Request, res: Response) {
res.json(attachments); 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(6)}${_fileName || url.split('/').pop()}`;
let attachmentUrl = await (storageAdapter as any).fileCreateByUrl(
slash(path.join(destPath, fileName)),
url
);
if (!attachmentUrl) {
attachmentUrl = `${(req as any).ncSiteUrl}/download/${filePath.join(
'/'
)}/${fileName}`;
}
return {
url: attachmentUrl,
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) { export async function fileRead(req, res) {
try { try {
const storageAdapter = await NcPluginMgrv2.storageAdapter(); const storageAdapter = await NcPluginMgrv2.storageAdapter();
@ -79,6 +118,7 @@ export async function fileRead(req, res) {
res.status(404).send('Not found'); res.status(404).send('Not found');
} }
} }
const router = Router({ mergeParams: true }); const router = Router({ mergeParams: true });
router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => { router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => {
@ -124,6 +164,10 @@ router.post(
}).any(), }).any(),
ncMetaAclMw(upload, 'upload') ncMetaAclMw(upload, 'upload')
); );
router.post(
'/api/v1/db/storage/upload-by-url',
ncMetaAclMw(uploadViaURL, 'uploadViaURL')
);
router.get(/^\/download\/(.+)$/, catchError(fileRead)); router.get(/^\/download\/(.+)$/, catchError(fileRead));
export default router; export default router;

4
packages/nocodb/src/lib/noco/meta/helpers/NcPluginMgrv2.ts

@ -1,6 +1,6 @@
import { import {
IEmailAdapter, IEmailAdapter,
IStorageAdapter, IStorageAdapterV2,
IWebhookNotificationAdapter IWebhookNotificationAdapter
// XcEmailPlugin, // XcEmailPlugin,
// XcPlugin, // XcPlugin,
@ -105,7 +105,7 @@ class NcPluginMgrv2 {
public static async storageAdapter( public static async storageAdapter(
ncMeta = Noco.ncMeta ncMeta = Noco.ncMeta
): Promise<IStorageAdapter> { ): Promise<IStorageAdapterV2> {
const pluginData = await ncMeta.metaGet2(null, null, MetaTable.PLUGIN, { const pluginData = await ncMeta.metaGet2(null, null, MetaTable.PLUGIN, {
category: PluginCategory.STORAGE, category: PluginCategory.STORAGE,
active: true active: true

46
packages/nocodb/src/lib/noco/plugins/adapters/storage/Local.ts

@ -3,12 +3,12 @@ import path from 'path';
import mkdirp from 'mkdirp'; import mkdirp from 'mkdirp';
import IStorageAdapter, { import { IStorageAdapterV2, XcFile } from 'nc-plugin';
XcFile
} from '../../../../../interface/IStorageAdapter';
import NcConfigFactory from '../../../../utils/NcConfigFactory'; import NcConfigFactory from '../../../../utils/NcConfigFactory';
export default class Local implements IStorageAdapter { import request from 'request';
export default class Local implements IStorageAdapterV2 {
constructor() {} constructor() {}
public async fileCreate(key: string, file: XcFile): Promise<any> { public async fileCreate(key: string, file: XcFile): Promise<any> {
@ -24,6 +24,44 @@ export default class Local implements IStorageAdapter {
} }
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const destPath = path.join(NcConfigFactory.getToolDir(), ...key.split('/'));
return new Promise((resolve, reject) => {
mkdirp.sync(path.dirname(destPath));
const file = fs.createWriteStream(destPath);
const sendReq = request.get(url);
// verify response code
sendReq.on('response', response => {
if (response.statusCode !== 200) {
return reject('Response status was ' + response.statusCode);
}
sendReq.pipe(file);
});
// close() is async, call cb after close completes
file.on('finish', () => {
file.close(err => {
if (err) {
return reject(err);
}
resolve(null);
});
});
// check for request errors
sendReq.on('error', err => {
fs.unlink(destPath, () => reject(err.message)); // delete the (partial) file and then return the error
});
file.on('error', err => {
// Handle errors
fs.unlink(destPath, () => reject(err.message)); // delete the (partial) file and then return the error
});
});
}
// todo: implement // todo: implement
fileDelete(_path: string): Promise<any> { fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);

4
packages/nocodb/src/lib/utils/projectAcl.ts

@ -132,7 +132,8 @@ export default {
relationDataRemove: true, relationDataRemove: true,
relationDataAdd: true, relationDataAdd: true,
dataCount: true, dataCount: true,
upload: true upload: true,
uploadViaURL: true
}, },
commenter: { commenter: {
formViewGet: true, formViewGet: true,
@ -242,6 +243,7 @@ export default {
super: '*', super: '*',
user: { user: {
upload: true, upload: true,
uploadViaURL: true,
passwordChange: true, passwordChange: true,
pluginList: true, pluginList: true,
pluginRead: true, pluginRead: true,

37
packages/nocodb/src/plugins/backblaze/Backblaze.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class Backblaze implements IStorageAdapter { export default class Backblaze implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -40,6 +41,38 @@ export default class Backblaze implements IStorageAdapter {
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }

4
packages/nocodb/src/plugins/backblaze/BackblazePlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import Backblaze from './Backblaze'; import Backblaze from './Backblaze';
class BackblazePlugin extends XcStoragePlugin { class BackblazePlugin extends XcStoragePlugin {
private static storageAdapter: Backblaze; private static storageAdapter: Backblaze;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return BackblazePlugin.storageAdapter; return BackblazePlugin.storageAdapter;
} }

27
packages/nocodb/src/plugins/gcs/Gcs.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { Storage, StorageOptions } from '@google-cloud/storage'; import { Storage, StorageOptions } from '@google-cloud/storage';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class Gcs implements IStorageAdapter { export default class Gcs implements IStorageAdapterV2 {
private storageClient: Storage; private storageClient: Storage;
private bucketName: string; private bucketName: string;
private input: any; private input: any;
@ -92,6 +93,28 @@ export default class Gcs implements IStorageAdapter {
throw e; throw e;
} }
} }
fileCreateByUrl(destPath: string, url: string): Promise<any> {
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
this.storageClient
.bucket(this.bucketName)
.file(destPath)
.save(body)
.then(res => resolve(res))
.catch(reject);
}
);
});
}
} }
/** /**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd * @copyright Copyright (c) 2021, Xgene Cloud Ltd

4
packages/nocodb/src/plugins/gcs/GcsPlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import Gcs from './Gcs'; import Gcs from './Gcs';
class GcsPlugin extends XcStoragePlugin { class GcsPlugin extends XcStoragePlugin {
private static storageAdapter: Gcs; private static storageAdapter: Gcs;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return GcsPlugin.storageAdapter; return GcsPlugin.storageAdapter;
} }

37
packages/nocodb/src/plugins/linode/LinodeObjectStorage.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class LinodeObjectStorage implements IStorageAdapter { export default class LinodeObjectStorage implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -40,6 +41,38 @@ export default class LinodeObjectStorage implements IStorageAdapter {
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }

4
packages/nocodb/src/plugins/linode/LinodeObjectStoragePlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import LinodeObjectStorage from './LinodeObjectStorage'; import LinodeObjectStorage from './LinodeObjectStorage';
class LinodeObjectStoragePlugin extends XcStoragePlugin { class LinodeObjectStoragePlugin extends XcStoragePlugin {
private static storageAdapter: LinodeObjectStorage; private static storageAdapter: LinodeObjectStorage;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return LinodeObjectStoragePlugin.storageAdapter; return LinodeObjectStoragePlugin.storageAdapter;
} }

74
packages/nocodb/src/plugins/mino/Minio.ts

@ -1,12 +1,11 @@
import fs from "fs"; import fs from 'fs';
import path from "path"; import path from 'path';
import {Client as MinioClient} from "minio";
import {IStorageAdapter, XcFile} from "nc-plugin";
export default class Minio implements IStorageAdapter {
import { Client as MinioClient } from 'minio';
import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class Minio implements IStorageAdapterV2 {
private minioClient: MinioClient; private minioClient: MinioClient;
private input: any; private input: any;
@ -14,13 +13,11 @@ export default class Minio implements IStorageAdapter {
this.input = input; this.input = input;
} }
async fileCreate(key: string, file: XcFile): Promise<any> { async fileCreate(key: string, file: XcFile): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters // Configure the file stream and obtain the upload parameters
const fileStream = fs.createReadStream(file.path); const fileStream = fs.createReadStream(file.path);
fileStream.on('error', (err) => { fileStream.on('error', err => {
console.log('File Error', err); console.log('File Error', err);
reject(err); reject(err);
}); });
@ -28,14 +25,21 @@ export default class Minio implements IStorageAdapter {
// uploadParams.Body = fileStream; // uploadParams.Body = fileStream;
// uploadParams.Key = key; // uploadParams.Key = key;
const metaData = { const metaData = {
'Content-Type': file.mimetype, 'Content-Type': file.mimetype
// 'X-Amz-Meta-Testing': 1234, // 'X-Amz-Meta-Testing': 1234,
// 'example': 5678 // 'example': 5678
} };
// call S3 to retrieve upload file to specified bucket // call S3 to retrieve upload file to specified bucket
this.minioClient.putObject(this.input?.bucket, key, fileStream,metaData).then(()=>{ this.minioClient
resolve(`http${this.input.useSSL ? 's' : ''}://${this.input.endPoint}:${this.input.port}/${this.input.bucket}/${key}`) .putObject(this.input?.bucket, key, fileStream, metaData)
}).catch(reject) .then(() => {
resolve(
`http${this.input.useSSL ? 's' : ''}://${this.input.endPoint}:${
this.input.port
}/${this.input.bucket}/${key}`
);
})
.catch(reject);
}); });
} }
@ -85,8 +89,46 @@ export default class Minio implements IStorageAdapter {
} }
} }
} async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// uploadParams.Body = fileStream;
// uploadParams.Key = key;
const metaData = {
// 'Content-Type': file.mimetype
// 'X-Amz-Meta-Testing': 1234,
// 'example': 5678
};
// call S3 to retrieve upload file to specified bucket
this.minioClient
.putObject(this.input?.bucket, key, body, metaData)
.then(() => {
resolve(
`http${this.input.useSSL ? 's' : ''}://${this.input.endPoint}:${
this.input.port
}/${this.input.bucket}/${key}`
);
})
.catch(reject);
}
);
});
}
}
/** /**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd * @copyright Copyright (c) 2021, Xgene Cloud Ltd

4
packages/nocodb/src/plugins/mino/MinioPlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import Minio from './Minio'; import Minio from './Minio';
class MinioPlugin extends XcStoragePlugin { class MinioPlugin extends XcStoragePlugin {
private static storageAdapter: Minio; private static storageAdapter: Minio;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return MinioPlugin.storageAdapter; return MinioPlugin.storageAdapter;
} }

37
packages/nocodb/src/plugins/ovhCloud/OvhCloud.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class OvhCloud implements IStorageAdapter { export default class OvhCloud implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -40,6 +41,38 @@ export default class OvhCloud implements IStorageAdapter {
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }

4
packages/nocodb/src/plugins/ovhCloud/OvhCloudPlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import OvhCloud from './OvhCloud'; import OvhCloud from './OvhCloud';
class OvhCloudPlugin extends XcStoragePlugin { class OvhCloudPlugin extends XcStoragePlugin {
private static storageAdapter: OvhCloud; private static storageAdapter: OvhCloud;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return OvhCloudPlugin.storageAdapter; return OvhCloudPlugin.storageAdapter;
} }

36
packages/nocodb/src/plugins/s3/S3.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class S3 implements IStorageAdapter { export default class S3 implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -39,6 +40,37 @@ export default class S3 implements IStorageAdapter {
}); });
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);

4
packages/nocodb/src/plugins/s3/S3Plugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import S3 from './S3'; import S3 from './S3';
class S3Plugin extends XcStoragePlugin { class S3Plugin extends XcStoragePlugin {
private static storageAdapter: S3; private static storageAdapter: S3;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return S3Plugin.storageAdapter; return S3Plugin.storageAdapter;
} }

37
packages/nocodb/src/plugins/scaleway/ScalewayObjectStorage.ts

@ -1,9 +1,10 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import request from 'request';
export default class ScalewayObjectStorage implements IStorageAdapter { export default class ScalewayObjectStorage implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -88,4 +89,36 @@ export default class ScalewayObjectStorage implements IStorageAdapter {
}); });
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
} }

4
packages/nocodb/src/plugins/scaleway/ScalewayObjectStoragePlugin.ts

@ -1,4 +1,4 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import ScalewayObjectStorage from './ScalewayObjectStorage'; import ScalewayObjectStorage from './ScalewayObjectStorage';
@ -10,7 +10,7 @@ class ScalewayObjectStoragePlugin extends XcStoragePlugin {
); );
await ScalewayObjectStoragePlugin.storageAdapter.init(); await ScalewayObjectStoragePlugin.storageAdapter.init();
} }
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return ScalewayObjectStoragePlugin.storageAdapter; return ScalewayObjectStoragePlugin.storageAdapter;
} }
} }

37
packages/nocodb/src/plugins/spaces/Spaces.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class Spaces implements IStorageAdapter { export default class Spaces implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -40,6 +41,38 @@ export default class Spaces implements IStorageAdapter {
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }

4
packages/nocodb/src/plugins/spaces/SpacesPlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import Spaces from './Spaces'; import Spaces from './Spaces';
class SpacesPlugin extends XcStoragePlugin { class SpacesPlugin extends XcStoragePlugin {
private static storageAdapter: Spaces; private static storageAdapter: Spaces;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return SpacesPlugin.storageAdapter; return SpacesPlugin.storageAdapter;
} }

4
packages/nocodb/src/plugins/upcloud/UpCloudPlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import UpoCloud from './UpoCloud'; import UpoCloud from './UpoCloud';
class UpCloudPlugin extends XcStoragePlugin { class UpCloudPlugin extends XcStoragePlugin {
private static storageAdapter: UpoCloud; private static storageAdapter: UpoCloud;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return UpCloudPlugin.storageAdapter; return UpCloudPlugin.storageAdapter;
} }

37
packages/nocodb/src/plugins/upcloud/UpoCloud.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class UpoCloud implements IStorageAdapter { export default class UpoCloud implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -40,6 +41,38 @@ export default class UpoCloud implements IStorageAdapter {
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }

37
packages/nocodb/src/plugins/vultr/Vultr.ts

@ -2,9 +2,10 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import AWS from 'aws-sdk'; import AWS from 'aws-sdk';
import { IStorageAdapter, XcFile } from 'nc-plugin'; import { IStorageAdapterV2, XcFile } from 'nc-plugin';
import request from 'request';
export default class Vultr implements IStorageAdapter { export default class Vultr implements IStorageAdapterV2 {
private s3Client: AWS.S3; private s3Client: AWS.S3;
private input: any; private input: any;
@ -40,6 +41,38 @@ export default class Vultr implements IStorageAdapter {
}); });
} }
async fileCreateByUrl(key: string, url: string): Promise<any> {
const uploadParams: any = {
ACL: 'public-read'
};
return new Promise((resolve, reject) => {
// Configure the file stream and obtain the upload parameters
request(
{
url: url,
encoding: null
},
(err, _, body) => {
if (err) return reject(err);
uploadParams.Body = body;
uploadParams.Key = key;
// call S3 to retrieve upload file to specified bucket
this.s3Client.upload(uploadParams, (err1, data) => {
if (err) {
console.log('Error', err);
reject(err1);
}
if (data) {
resolve(data.Location);
}
});
}
);
});
}
public async fileDelete(_path: string): Promise<any> { public async fileDelete(_path: string): Promise<any> {
return Promise.resolve(undefined); return Promise.resolve(undefined);
} }

4
packages/nocodb/src/plugins/vultr/VultrPlugin.ts

@ -1,11 +1,11 @@
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin'; import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
import Vultr from './Vultr'; import Vultr from './Vultr';
class VultrPlugin extends XcStoragePlugin { class VultrPlugin extends XcStoragePlugin {
private static storageAdapter: Vultr; private static storageAdapter: Vultr;
public getAdapter(): IStorageAdapter { public getAdapter(): IStorageAdapterV2 {
return VultrPlugin.storageAdapter; return VultrPlugin.storageAdapter;
} }

58
scripts/sdk/swagger.json

@ -2625,14 +2625,14 @@
"name": "tableName", "name": "tableName",
"in": "path", "in": "path",
"required": true "required": true
},{ },
{
"schema": { "schema": {
"type": "string" "type": "string"
}, },
"in": "query", "in": "query",
"name": "column_name", "name": "column_name",
"description": "description": "Column name of the column you want to group by, eg. `column_name=column1`"
"Column name of the column you want to group by, eg. `column_name=column1`"
} }
], ],
"get": { "get": {
@ -2907,14 +2907,14 @@
"name": "viewName", "name": "viewName",
"in": "path", "in": "path",
"required": true "required": true
},{ },
{
"schema": { "schema": {
"type": "string" "type": "string"
}, },
"in": "query", "in": "query",
"name": "column_name", "name": "column_name",
"description": "description": "Column name of the column you want to group by, eg. `column_name=column1`"
"Column name of the column you want to group by, eg. `column_name=column1`"
} }
], ],
"get": { "get": {
@ -5248,6 +5248,52 @@
] ]
} }
}, },
"/api/v1/db/storage/upload-by-url": {
"post": {
"summary": "Attachment",
"operationId": "storage-upload-by-url",
"responses": {},
"tags": [
"Storage"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"fileName": {
"type": "string"
},
"mimetype": {
"type": "string"
},
"size": {
"type": "string"
}
}
}
}
}
}
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "path",
"in": "query",
"required": true
}
]
}
},
"/api/v1/db/meta/projects/{projectId}/users/{userId}/resend-invite": { "/api/v1/db/meta/projects/{projectId}/users/{userId}/resend-invite": {
"parameters": [ "parameters": [
{ {

Loading…
Cancel
Save