diff --git a/packages/builder/src/components/common/Dropzone.svelte b/packages/builder/src/components/common/Dropzone.svelte index 692bb5b0b5..7ec394063c 100644 --- a/packages/builder/src/components/common/Dropzone.svelte +++ b/packages/builder/src/components/common/Dropzone.svelte @@ -1,36 +1,13 @@ -
- Upload - + +
diff --git a/packages/builder/src/components/database/DataTable/ModelDataTable.svelte b/packages/builder/src/components/database/DataTable/ModelDataTable.svelte index 038c2d7481..9726b053fa 100644 --- a/packages/builder/src/components/database/DataTable/ModelDataTable.svelte +++ b/packages/builder/src/components/database/DataTable/ModelDataTable.svelte @@ -112,6 +112,11 @@ section { margin-bottom: 20px; } + + img { + object-fit: contain; + } + .title { font-size: 24px; font-weight: 600; diff --git a/packages/server/src/api/controllers/deploy/aws.js b/packages/server/src/api/controllers/deploy/aws.js index 4e52c179b4..9d994b3091 100644 --- a/packages/server/src/api/controllers/deploy/aws.js +++ b/packages/server/src/api/controllers/deploy/aws.js @@ -2,6 +2,7 @@ const fs = require("fs") const AWS = require("aws-sdk") const fetch = require("node-fetch") const { budibaseAppsDir } = require("../../../utilities/budibaseDir") +const PouchDB = require("../../../db") async function invalidateCDN(cfDistribution, appId) { const cf = new AWS.CloudFront({}) @@ -62,7 +63,6 @@ function walkDir(dirPath, callback) { } } } - /** * Walk a directory and return an array of promises for uploading all the files * inside that directory to s3. @@ -79,19 +79,8 @@ function uploadFiles({ }) { const uploads = [] - walkDir(path, function prepareUploadsForS3(filePath) { - const fileExtension = [...filePath.split(".")].pop() - const fileBytes = fs.readFileSync(filePath) - - const upload = s3 - .upload({ - Key: filePath.replace(path, s3Key), - Body: fileBytes, - ContentType: CONTENT_TYPE_MAP[fileExtension], - Metadata: metadata, - }) - .promise() - + walkDir(path, function(filePath) { + const upload = prepareUploadForS3(filePath, metadata) uploads.push(upload) }) @@ -99,8 +88,24 @@ function uploadFiles({ } +function prepareUploadForS3({ filePath, s3Key, metadata }) { + const fileExtension = [...filePath.split(".")].pop() + const fileBytes = fs.readFileSync(filePath) + return s3 + .upload({ + Key: s3Key, + Body: fileBytes, + ContentType: CONTENT_TYPE_MAP[fileExtension], + Metadata: metadata, + }) + .promise() +} + + + exports.uploadAppAssets = async function({ appId, + instanceId, credentials, bucket, cfDistribution, @@ -126,25 +131,40 @@ exports.uploadAppAssets = async function({ for (let page of appPages) { // Upload HTML, CSS and JS for each page of the web app - const pageAssetUploads = uploadFiles({ - path: `${appAssetsPath}/${page}`, - s3Key: `assets/${appId}`, - s3, - metadata: { accountId } - }); + walkDir(path, function(filePath) { + const appAssetUpload = prepareUploadForS3({ + filePath, + s3Key: filePath.replace(path, `assets/${appId}`), + s3, + metadata: { accountId } + }) + uploads.push(appAssetUpload) + }) uploads = [...uploads, ...pageAssetUploads]; } // Upload file attachments - const attachmentUploads = uploadFiles({ - path: `${budibaseAppsDir()}/${appId}/attachments`, - s3Key: `assets/${appId}/attachments`, - s3, - metadata: { accountId } - }) + const db = new PouchDB(instanceId) + const fileUploads = await db.get("_local/fileuploads") + if (fileUploads) { + fileUploads.awaitingUpload.forEach((file, idx) => { - uploads = [...uploads, ...attachmentUploads] + const attachmentUpload = prepareUploadForS3({ + filePath: file.path, + s3Key: `assets/${appId}/${file.name}`, + s3, + metadata: { accountId } + }) + + uploads.push(attachmentUpload) + + // move the pending upload to the uploaded array + fileUploads.awaitingUpload.splice(idx, 1); + fileUploads.uploaded.push(awaitingUpload); + }) + db.put(fileUploads); + } try { await Promise.all(uploads) diff --git a/packages/server/src/api/controllers/deploy/index.js b/packages/server/src/api/controllers/deploy/index.js index 8e35d45ef2..34579b64c7 100644 --- a/packages/server/src/api/controllers/deploy/index.js +++ b/packages/server/src/api/controllers/deploy/index.js @@ -42,6 +42,7 @@ exports.deployApp = async function(ctx) { await uploadAppAssets({ clientId, appId: ctx.user.appId, + instanceId: ctx.user.instanceId, ...credentials, }) diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index d9e030026a..f93e345bba 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -4,12 +4,14 @@ const { budibaseAppsDir, budibaseTempDir, } = require("../../../utilities/budibaseDir") +const CouchDB = require("../../../db") const setBuilderToken = require("../../../utilities/builder/setBuilderToken") const { ANON_LEVEL_ID } = require("../../../utilities/accessLevels") const jwt = require("jsonwebtoken") const fetch = require("node-fetch") const imageProcessing = require("./imageProcessing") const fs = require("fs") +const uuid = require("uuid") exports.serveBuilder = async function(ctx) { let builderPath = resolve(__dirname, "../../../../builder") @@ -27,19 +29,43 @@ exports.processLocalFileUpload = async function(ctx) { // create attachments dir if it doesnt exist !fs.existsSync(attachmentsPath) && fs.mkdirSync(attachmentsPath, { recursive: true }) + const filesToProcess = files.map(file => { + const fileExtension = [...file.path.split(".")].pop() + // filenames converted to UUIDs so they are unique + const fileName = `${uuid.v4()}.${fileExtension}` - const filesToProcess = files.map(file => ({ - ...file, - outputPath: join(attachmentsPath, file.name), - clientUrl: join("/attachments", file.name), - uploaded: false - })) + return { + ...file, + name: fileName, + extension: fileExtension, + outputPath: join(attachmentsPath, fileName), + clientUrl: join("/attachments", fileName) + } + }) // TODO: read the file (into memory first, then we will stream it) const imageProcessOperations = filesToProcess.map(file => imageProcessing.processImage(file)) try { + // TODO: get file sizes of images after resize const responses = await Promise.all(imageProcessOperations); + + let fileUploads + // local document used to track which files need to be uploaded + // db.get throws an error if the document doesn't exist + // need to use a promise to default + const db = new CouchDB(ctx.user.instanceId); + await db.get("_local/fileuploads") + .then(data => fileUploads = data) + .catch(() => fileUploads = { + _id: "_local/fileuploads", + awaitingUpload: [], + uploaded: [] + }) + + fileUploads.awaitingUpload = [...filesToProcess, ...fileUploads.awaitingUpload] + await db.put(fileUploads) + ctx.body = filesToProcess } catch (err) { ctx.throw(500, err);