2020-07-01 22:56:53 +02:00
|
|
|
const fs = require("fs")
|
2020-10-09 11:00:57 +02:00
|
|
|
const { join } = require("../../../utilities/centralPath")
|
2020-07-01 22:56:53 +02:00
|
|
|
const AWS = require("aws-sdk")
|
|
|
|
const fetch = require("node-fetch")
|
2020-10-21 12:27:42 +02:00
|
|
|
const sanitize = require("sanitize-s3-objectkey")
|
2020-07-07 22:29:20 +02:00
|
|
|
const { budibaseAppsDir } = require("../../../utilities/budibaseDir")
|
2020-09-16 13:18:47 +02:00
|
|
|
const PouchDB = require("../../../db")
|
2020-10-28 21:35:06 +01:00
|
|
|
const env = require("../../../environment")
|
2020-07-01 22:56:53 +02:00
|
|
|
|
2020-11-05 12:44:18 +01:00
|
|
|
/**
|
|
|
|
* Finalises the deployment, updating the quota for the user API key
|
|
|
|
* The verification process returns the levels to update to.
|
|
|
|
* Calls the "deployment-success" lambda.
|
|
|
|
* @param {object} quota The usage quota levels returned from the verifyDeploy
|
|
|
|
* @returns {Promise<object>} The usage has been updated against the user API key.
|
|
|
|
*/
|
2020-10-08 11:56:32 +02:00
|
|
|
exports.updateDeploymentQuota = async function(quota) {
|
2020-10-08 21:23:58 +02:00
|
|
|
const DEPLOYMENT_SUCCESS_URL =
|
2020-10-28 21:35:06 +01:00
|
|
|
env.DEPLOYMENT_CREDENTIALS_URL + "deploy/success"
|
2020-10-08 21:23:58 +02:00
|
|
|
|
|
|
|
const response = await fetch(DEPLOYMENT_SUCCESS_URL, {
|
|
|
|
method: "POST",
|
|
|
|
body: JSON.stringify({
|
2020-10-28 21:35:06 +01:00
|
|
|
apiKey: env.BUDIBASE_API_KEY,
|
2020-10-08 21:23:58 +02:00
|
|
|
quota,
|
|
|
|
}),
|
2020-10-09 18:07:46 +02:00
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
Accept: "application/json",
|
|
|
|
},
|
2020-10-08 21:23:58 +02:00
|
|
|
})
|
2020-10-08 11:56:32 +02:00
|
|
|
|
|
|
|
if (response.status !== 200) {
|
2020-10-08 21:23:58 +02:00
|
|
|
throw new Error(`Error updating deployment quota for API Key`)
|
2020-10-08 11:56:32 +02:00
|
|
|
}
|
|
|
|
|
2020-10-29 11:28:27 +01:00
|
|
|
return await response.json()
|
2020-10-08 11:56:32 +02:00
|
|
|
}
|
|
|
|
|
2020-10-07 21:37:55 +02:00
|
|
|
/**
|
|
|
|
* Verifies the users API key and
|
2020-11-05 12:44:18 +01:00
|
|
|
* Verifies that the deployment fits within the quota of the user
|
|
|
|
* Links to the "check-api-key" lambda.
|
2020-10-29 11:28:27 +01:00
|
|
|
* @param {String} appId - appId being deployed
|
2020-10-07 21:37:55 +02:00
|
|
|
* @param {String} appId - appId being deployed
|
|
|
|
* @param {quota} quota - current quota being changed with this application
|
|
|
|
*/
|
2020-10-29 11:28:27 +01:00
|
|
|
exports.verifyDeployment = async function({ appId, quota }) {
|
2020-10-28 21:35:06 +01:00
|
|
|
const response = await fetch(env.DEPLOYMENT_CREDENTIALS_URL, {
|
2020-07-01 22:56:53 +02:00
|
|
|
method: "POST",
|
|
|
|
body: JSON.stringify({
|
2020-10-28 21:35:06 +01:00
|
|
|
apiKey: env.BUDIBASE_API_KEY,
|
2020-10-07 21:37:55 +02:00
|
|
|
appId,
|
|
|
|
quota,
|
2020-07-07 22:29:20 +02:00
|
|
|
}),
|
2020-07-01 22:56:53 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
2020-07-07 22:29:20 +02:00
|
|
|
throw new Error(
|
2020-10-28 21:35:06 +01:00
|
|
|
`Error fetching temporary credentials for api key: ${env.BUDIBASE_API_KEY}`
|
2020-07-07 22:29:20 +02:00
|
|
|
)
|
2020-07-01 22:56:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const json = await response.json()
|
2020-10-20 16:06:34 +02:00
|
|
|
if (json.errors) {
|
|
|
|
throw new Error(json.errors)
|
|
|
|
}
|
|
|
|
|
|
|
|
// set credentials here, means any time we're verified we're ready to go
|
|
|
|
if (json.credentials) {
|
|
|
|
AWS.config.update({
|
|
|
|
accessKeyId: json.credentials.AccessKeyId,
|
|
|
|
secretAccessKey: json.credentials.SecretAccessKey,
|
|
|
|
sessionToken: json.credentials.SessionToken,
|
|
|
|
})
|
|
|
|
}
|
2020-07-01 22:56:53 +02:00
|
|
|
|
|
|
|
return json
|
2020-07-07 22:29:20 +02:00
|
|
|
}
|
2020-07-01 22:56:53 +02:00
|
|
|
|
2020-07-03 00:22:20 +02:00
|
|
|
const CONTENT_TYPE_MAP = {
|
|
|
|
html: "text/html",
|
|
|
|
css: "text/css",
|
2020-07-07 22:29:20 +02:00
|
|
|
js: "application/javascript",
|
|
|
|
}
|
2020-07-03 00:22:20 +02:00
|
|
|
|
2020-07-06 20:43:40 +02:00
|
|
|
/**
|
|
|
|
* Recursively walk a directory tree and execute a callback on all files.
|
2020-07-07 18:47:18 +02:00
|
|
|
* @param {String} dirPath - Directory to traverse
|
|
|
|
* @param {Function} callback - callback to execute on files
|
2020-07-06 20:43:40 +02:00
|
|
|
*/
|
|
|
|
function walkDir(dirPath, callback) {
|
|
|
|
for (let filename of fs.readdirSync(dirPath)) {
|
2020-07-07 22:29:20 +02:00
|
|
|
const filePath = `${dirPath}/${filename}`
|
2020-07-06 20:43:40 +02:00
|
|
|
const stat = fs.lstatSync(filePath)
|
2020-07-07 22:29:20 +02:00
|
|
|
|
2020-07-06 20:43:40 +02:00
|
|
|
if (stat.isFile()) {
|
2020-07-07 18:47:18 +02:00
|
|
|
callback(filePath)
|
2020-07-06 20:43:40 +02:00
|
|
|
} else {
|
|
|
|
walkDir(filePath, callback)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-15 17:22:13 +02:00
|
|
|
|
2020-09-23 21:23:40 +02:00
|
|
|
async function prepareUploadForS3({ s3Key, metadata, s3, file }) {
|
|
|
|
const extension = [...file.name.split(".")].pop()
|
|
|
|
const fileBytes = fs.readFileSync(file.path)
|
2020-09-23 17:15:09 +02:00
|
|
|
|
|
|
|
const upload = await s3
|
2020-09-16 13:18:47 +02:00
|
|
|
.upload({
|
2020-10-21 12:27:42 +02:00
|
|
|
// windows filepaths need to be converted to forward slashes for s3
|
|
|
|
Key: sanitize(s3Key).replace(/\\/g, "/"),
|
2020-09-16 13:18:47 +02:00
|
|
|
Body: fileBytes,
|
2020-09-23 21:23:40 +02:00
|
|
|
ContentType: file.type || CONTENT_TYPE_MAP[extension.toLowerCase()],
|
2020-09-16 13:18:47 +02:00
|
|
|
Metadata: metadata,
|
|
|
|
})
|
|
|
|
.promise()
|
2020-09-23 17:15:09 +02:00
|
|
|
|
|
|
|
return {
|
2020-09-23 21:23:40 +02:00
|
|
|
size: file.size,
|
|
|
|
name: file.name,
|
|
|
|
extension,
|
2020-09-23 17:15:09 +02:00
|
|
|
url: upload.Location,
|
|
|
|
key: upload.Key,
|
|
|
|
}
|
2020-09-16 13:18:47 +02:00
|
|
|
}
|
|
|
|
|
2020-09-23 17:15:09 +02:00
|
|
|
exports.prepareUploadForS3 = prepareUploadForS3
|
|
|
|
|
2020-11-10 18:08:02 +01:00
|
|
|
exports.uploadAppAssets = async function({ appId, bucket, accountId }) {
|
2020-07-01 22:56:53 +02:00
|
|
|
const s3 = new AWS.S3({
|
|
|
|
params: {
|
2020-07-07 22:29:20 +02:00
|
|
|
Bucket: bucket,
|
|
|
|
},
|
2020-07-01 22:56:53 +02:00
|
|
|
})
|
|
|
|
|
2020-10-07 01:24:56 +02:00
|
|
|
const appAssetsPath = join(budibaseAppsDir(), appId, "public")
|
2020-07-01 22:56:53 +02:00
|
|
|
|
|
|
|
const appPages = fs.readdirSync(appAssetsPath)
|
|
|
|
|
2020-09-15 17:22:13 +02:00
|
|
|
let uploads = []
|
2020-07-01 22:56:53 +02:00
|
|
|
|
|
|
|
for (let page of appPages) {
|
2020-09-15 17:22:13 +02:00
|
|
|
// Upload HTML, CSS and JS for each page of the web app
|
2020-10-07 01:24:56 +02:00
|
|
|
walkDir(join(appAssetsPath, page), function(filePath) {
|
2020-09-16 13:18:47 +02:00
|
|
|
const appAssetUpload = prepareUploadForS3({
|
2020-09-23 21:23:40 +02:00
|
|
|
file: {
|
|
|
|
path: filePath,
|
|
|
|
name: [...filePath.split("/")].pop(),
|
|
|
|
},
|
2020-09-17 13:45:28 +02:00
|
|
|
s3Key: filePath.replace(appAssetsPath, `assets/${appId}`),
|
2020-09-16 13:18:47 +02:00
|
|
|
s3,
|
2020-09-17 17:36:39 +02:00
|
|
|
metadata: { accountId },
|
2020-09-16 13:18:47 +02:00
|
|
|
})
|
|
|
|
uploads.push(appAssetUpload)
|
|
|
|
})
|
2020-07-01 22:56:53 +02:00
|
|
|
}
|
|
|
|
|
2020-09-15 17:22:13 +02:00
|
|
|
// Upload file attachments
|
2020-10-29 11:28:27 +01:00
|
|
|
const db = new PouchDB(appId)
|
2020-10-08 21:23:58 +02:00
|
|
|
let fileUploads
|
|
|
|
try {
|
|
|
|
fileUploads = await db.get("_local/fileuploads")
|
|
|
|
} catch (err) {
|
|
|
|
fileUploads = { _id: "_local/fileuploads", uploads: [] }
|
|
|
|
}
|
2020-09-16 13:18:47 +02:00
|
|
|
|
2020-10-08 21:23:58 +02:00
|
|
|
for (let file of fileUploads.uploads) {
|
|
|
|
if (file.uploaded) continue
|
2020-09-17 16:08:28 +02:00
|
|
|
|
2020-10-08 21:23:58 +02:00
|
|
|
const attachmentUpload = prepareUploadForS3({
|
|
|
|
file,
|
|
|
|
s3Key: `assets/${appId}/attachments/${file.processedFileName}`,
|
|
|
|
s3,
|
|
|
|
metadata: { accountId },
|
|
|
|
})
|
2020-09-15 17:22:13 +02:00
|
|
|
|
2020-10-08 21:23:58 +02:00
|
|
|
uploads.push(attachmentUpload)
|
|
|
|
|
|
|
|
// mark file as uploaded
|
|
|
|
file.uploaded = true
|
2020-09-16 13:18:47 +02:00
|
|
|
}
|
2020-09-15 17:22:13 +02:00
|
|
|
|
2020-10-08 21:23:58 +02:00
|
|
|
db.put(fileUploads)
|
|
|
|
|
2020-07-01 22:56:53 +02:00
|
|
|
try {
|
2020-11-10 18:08:02 +01:00
|
|
|
return await Promise.all(uploads)
|
2020-07-01 22:56:53 +02:00
|
|
|
} catch (err) {
|
|
|
|
console.error("Error uploading budibase app assets to s3", err)
|
|
|
|
throw err
|
|
|
|
}
|
2020-07-07 22:29:20 +02:00
|
|
|
}
|