mirror of
https://github.com/nocodb/nocodb.git
synced 2026-04-25 04:26:10 +00:00
feat: attachment upload by url
Signed-off-by: Pranav C <pranavxc@gmail.com>
This commit is contained in:
@@ -62,7 +62,11 @@
|
||||
</v-tooltip>
|
||||
</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">
|
||||
mdi-loading mdi-spin
|
||||
</v-icon>
|
||||
@@ -76,9 +80,15 @@
|
||||
>
|
||||
<v-icon x-small color="">
|
||||
mdi-plus
|
||||
</v-icon> Attachment
|
||||
</v-icon>
|
||||
Attachment
|
||||
</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
|
||||
</v-icon>
|
||||
</div>
|
||||
@@ -110,6 +120,8 @@
|
||||
</v-icon>
|
||||
<span class="caption">Attach File</span>
|
||||
</v-btn>
|
||||
|
||||
<!-- <v-text-field v-model="urlString" @keypress.enter="uploadByUrl" />-->
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap h-100">
|
||||
@@ -261,7 +273,8 @@ export default {
|
||||
showImage: false,
|
||||
selectedImage: null,
|
||||
dragOver: false,
|
||||
localFilesState: []
|
||||
localFilesState: [],
|
||||
urlString: ''
|
||||
}),
|
||||
watch: {
|
||||
value(val, prev) {
|
||||
@@ -286,6 +299,18 @@ export default {
|
||||
mounted() {
|
||||
},
|
||||
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) {
|
||||
window.open(url, target)
|
||||
},
|
||||
|
||||
12099
packages/nc-plugin/package-lock.json
generated
12099
packages/nc-plugin/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nc-plugin",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"description": "Xgene plugin template",
|
||||
"main": "build/main/index.js",
|
||||
"typings": "build/main/index.d.ts",
|
||||
|
||||
@@ -5,6 +5,7 @@ import XcPluginMigration from './lib/XcPluginMigration';
|
||||
import XcStoragePlugin from './lib/XcStoragePlugin';
|
||||
import XcEmailPlugin from './lib/XcEmailPlugin';
|
||||
import IStorageAdapter, {XcFile} from './lib/IStorageAdapter';
|
||||
import IStorageAdapterV2 from './lib/IStorageAdapterV2';
|
||||
import IEmailAdapter, {XcEmail} from './lib/IEmailAdapter';
|
||||
import IWebhookNotificationAdapter from './lib/IWebhookNotificationAdapter';
|
||||
import XcWebhookNotificationPlugin from './lib/XcWebhookNotificationPlugin';
|
||||
@@ -21,5 +22,6 @@ export {
|
||||
XcFile,
|
||||
XcEmail,
|
||||
IWebhookNotificationAdapter,
|
||||
XcWebhookNotificationPlugin
|
||||
XcWebhookNotificationPlugin,
|
||||
IStorageAdapterV2
|
||||
}
|
||||
|
||||
34
packages/nc-plugin/src/lib/IStorageAdapterV2.ts
Normal file
34
packages/nc-plugin/src/lib/IStorageAdapterV2.ts
Normal file
@@ -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/>.
|
||||
*
|
||||
*/
|
||||
@@ -3501,5 +3501,27 @@ export class Api<
|
||||
type: ContentType.FormData,
|
||||
...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
14
packages/nocodb/package-lock.json
generated
@@ -71,7 +71,7 @@
|
||||
"nc-common": "0.0.6",
|
||||
"nc-help": "0.2.59",
|
||||
"nc-lib-gui": "0.91.1",
|
||||
"nc-plugin": "^0.1.1",
|
||||
"nc-plugin": "0.1.2",
|
||||
"ncp": "^2.0.0",
|
||||
"nocodb-sdk": "file:../nocodb-sdk",
|
||||
"nodemailer": "^6.4.10",
|
||||
@@ -16083,9 +16083,9 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"node_modules/nc-plugin": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.1.tgz",
|
||||
"integrity": "sha512-oJLpPX9fvdxZjNZmX0SLq0XYjCnT6oFcRCapTLr6Gxuc4ay6tP7Spj96bbENNxe8bHJPdO1mlb4BEK6nA0nELg==",
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.2.tgz",
|
||||
"integrity": "sha512-9NZJPIgD6r3GnCNJelfZFga+RQPDuHZCoFus2qaZsNdbJEP7LSTOeZ1Pw8l89fvVbp0VFLO3Bcg8ggJppKt8JQ==",
|
||||
"dependencies": {
|
||||
"@bitauth/libauth": "^1.17.1",
|
||||
"nc-common": "0.0.6"
|
||||
@@ -37373,9 +37373,9 @@
|
||||
}
|
||||
},
|
||||
"nc-plugin": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.1.tgz",
|
||||
"integrity": "sha512-oJLpPX9fvdxZjNZmX0SLq0XYjCnT6oFcRCapTLr6Gxuc4ay6tP7Spj96bbENNxe8bHJPdO1mlb4BEK6nA0nELg==",
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/nc-plugin/-/nc-plugin-0.1.2.tgz",
|
||||
"integrity": "sha512-9NZJPIgD6r3GnCNJelfZFga+RQPDuHZCoFus2qaZsNdbJEP7LSTOeZ1Pw8l89fvVbp0VFLO3Bcg8ggJppKt8JQ==",
|
||||
"requires": {
|
||||
"@bitauth/libauth": "^1.17.1",
|
||||
"nc-common": "0.0.6"
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"nc-common": "0.0.6",
|
||||
"nc-help": "0.2.59",
|
||||
"nc-lib-gui": "0.91.1",
|
||||
"nc-plugin": "^0.1.1",
|
||||
"nc-plugin": "0.1.2",
|
||||
"ncp": "^2.0.0",
|
||||
"nocodb-sdk": "file:../nocodb-sdk",
|
||||
"nodemailer": "^6.4.10",
|
||||
|
||||
@@ -1527,6 +1527,11 @@ class BaseModelSqlv2 {
|
||||
for (const data of datas) {
|
||||
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
|
||||
// to avoid `too many SQL variables` error
|
||||
|
||||
@@ -47,6 +47,45 @@ export async function upload(req: Request, res: Response) {
|
||||
|
||||
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) {
|
||||
try {
|
||||
const storageAdapter = await NcPluginMgrv2.storageAdapter();
|
||||
@@ -79,6 +118,7 @@ export async function fileRead(req, res) {
|
||||
res.status(404).send('Not found');
|
||||
}
|
||||
}
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
router.get(/^\/dl\/([^/]+)\/([^/]+)\/(.+)$/, async (req, res) => {
|
||||
@@ -124,6 +164,10 @@ router.post(
|
||||
}).any(),
|
||||
ncMetaAclMw(upload, 'upload')
|
||||
);
|
||||
router.post(
|
||||
'/api/v1/db/storage/upload-by-url',
|
||||
ncMetaAclMw(uploadViaURL, 'uploadViaURL')
|
||||
);
|
||||
router.get(/^\/download\/(.+)$/, catchError(fileRead));
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
IEmailAdapter,
|
||||
IStorageAdapter,
|
||||
IStorageAdapterV2,
|
||||
IWebhookNotificationAdapter
|
||||
// XcEmailPlugin,
|
||||
// XcPlugin,
|
||||
@@ -105,7 +105,7 @@ class NcPluginMgrv2 {
|
||||
|
||||
public static async storageAdapter(
|
||||
ncMeta = Noco.ncMeta
|
||||
): Promise<IStorageAdapter> {
|
||||
): Promise<IStorageAdapterV2> {
|
||||
const pluginData = await ncMeta.metaGet2(null, null, MetaTable.PLUGIN, {
|
||||
category: PluginCategory.STORAGE,
|
||||
active: true
|
||||
|
||||
@@ -3,12 +3,12 @@ import path from 'path';
|
||||
|
||||
import mkdirp from 'mkdirp';
|
||||
|
||||
import IStorageAdapter, {
|
||||
XcFile
|
||||
} from '../../../../../interface/IStorageAdapter';
|
||||
import { IStorageAdapterV2, XcFile } from 'nc-plugin';
|
||||
import NcConfigFactory from '../../../../utils/NcConfigFactory';
|
||||
|
||||
export default class Local implements IStorageAdapter {
|
||||
import request from 'request';
|
||||
|
||||
export default class Local implements IStorageAdapterV2 {
|
||||
constructor() {}
|
||||
|
||||
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
|
||||
fileDelete(_path: string): Promise<any> {
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -132,7 +132,8 @@ export default {
|
||||
relationDataRemove: true,
|
||||
relationDataAdd: true,
|
||||
dataCount: true,
|
||||
upload: true
|
||||
upload: true,
|
||||
uploadViaURL: true
|
||||
},
|
||||
commenter: {
|
||||
formViewGet: true,
|
||||
@@ -242,6 +243,7 @@ export default {
|
||||
super: '*',
|
||||
user: {
|
||||
upload: true,
|
||||
uploadViaURL: true,
|
||||
passwordChange: true,
|
||||
pluginList: true,
|
||||
pluginRead: true,
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import Backblaze from './Backblaze';
|
||||
|
||||
class BackblazePlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: Backblaze;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return BackblazePlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 bucketName: string;
|
||||
private input: any;
|
||||
@@ -92,6 +93,28 @@ export default class Gcs implements IStorageAdapter {
|
||||
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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import Gcs from './Gcs';
|
||||
|
||||
class GcsPlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: Gcs;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return GcsPlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import LinodeObjectStorage from './LinodeObjectStorage';
|
||||
|
||||
class LinodeObjectStoragePlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: LinodeObjectStorage;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return LinodeObjectStoragePlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import {Client as MinioClient} from "minio";
|
||||
import {IStorageAdapter, XcFile} from "nc-plugin";
|
||||
|
||||
export default class Minio implements IStorageAdapter {
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 input: any;
|
||||
|
||||
@@ -14,13 +13,11 @@ export default class Minio implements IStorageAdapter {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
||||
async fileCreate(key: string, file: XcFile): Promise<any> {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Configure the file stream and obtain the upload parameters
|
||||
const fileStream = fs.createReadStream(file.path);
|
||||
fileStream.on('error', (err) => {
|
||||
fileStream.on('error', err => {
|
||||
console.log('File Error', err);
|
||||
reject(err);
|
||||
});
|
||||
@@ -28,14 +25,21 @@ export default class Minio implements IStorageAdapter {
|
||||
// uploadParams.Body = fileStream;
|
||||
// uploadParams.Key = key;
|
||||
const metaData = {
|
||||
'Content-Type': file.mimetype,
|
||||
'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, fileStream,metaData).then(()=>{
|
||||
resolve(`http${this.input.useSSL ? 's' : ''}://${this.input.endPoint}:${this.input.port}/${this.input.bucket}/${key}`)
|
||||
}).catch(reject)
|
||||
this.minioClient
|
||||
.putObject(this.input?.bucket, key, fileStream, metaData)
|
||||
.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
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import Minio from './Minio';
|
||||
|
||||
class MinioPlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: Minio;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return MinioPlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import OvhCloud from './OvhCloud';
|
||||
|
||||
class OvhCloudPlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: OvhCloud;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return OvhCloudPlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import S3 from './S3';
|
||||
|
||||
class S3Plugin extends XcStoragePlugin {
|
||||
private static storageAdapter: S3;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return S3Plugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { IStorageAdapter, XcFile } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcFile } from 'nc-plugin';
|
||||
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 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import ScalewayObjectStorage from './ScalewayObjectStorage';
|
||||
|
||||
@@ -10,7 +10,7 @@ class ScalewayObjectStoragePlugin extends XcStoragePlugin {
|
||||
);
|
||||
await ScalewayObjectStoragePlugin.storageAdapter.init();
|
||||
}
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return ScalewayObjectStoragePlugin.storageAdapter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import Spaces from './Spaces';
|
||||
|
||||
class SpacesPlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: Spaces;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return SpacesPlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import UpoCloud from './UpoCloud';
|
||||
|
||||
class UpCloudPlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: UpoCloud;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return UpCloudPlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
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 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> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IStorageAdapter, XcStoragePlugin } from 'nc-plugin';
|
||||
import { IStorageAdapterV2, XcStoragePlugin } from 'nc-plugin';
|
||||
|
||||
import Vultr from './Vultr';
|
||||
|
||||
class VultrPlugin extends XcStoragePlugin {
|
||||
private static storageAdapter: Vultr;
|
||||
|
||||
public getAdapter(): IStorageAdapter {
|
||||
public getAdapter(): IStorageAdapterV2 {
|
||||
return VultrPlugin.storageAdapter;
|
||||
}
|
||||
|
||||
|
||||
@@ -2625,14 +2625,14 @@
|
||||
"name": "tableName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},{
|
||||
},
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "query",
|
||||
"name": "column_name",
|
||||
"description":
|
||||
"Column name of the column you want to group by, eg. `column_name=column1`"
|
||||
"description": "Column name of the column you want to group by, eg. `column_name=column1`"
|
||||
}
|
||||
],
|
||||
"get": {
|
||||
@@ -2907,14 +2907,14 @@
|
||||
"name": "viewName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},{
|
||||
},
|
||||
{
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"in": "query",
|
||||
"name": "column_name",
|
||||
"description":
|
||||
"Column name of the column you want to group by, eg. `column_name=column1`"
|
||||
"description": "Column name of the column you want to group by, eg. `column_name=column1`"
|
||||
}
|
||||
],
|
||||
"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": {
|
||||
"parameters": [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user