From 6f4b55c0f508937b2108b8fcd9f82553ed5b3c1b Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 16 Oct 2020 12:38:07 +0100 Subject: [PATCH 1/4] making deploy process async, adding local deployment history --- .../pages/[application]/deploy/index.svelte | 30 +++- .../src/api/controllers/deploy/index.js | 166 +++++++++++++++--- packages/server/src/api/routes/deploy.js | 9 +- 3 files changed, 179 insertions(+), 26 deletions(-) diff --git a/packages/builder/src/pages/[application]/deploy/index.svelte b/packages/builder/src/pages/[application]/deploy/index.svelte index 2884a4570e..4ab9cd2789 100644 --- a/packages/builder/src/pages/[application]/deploy/index.svelte +++ b/packages/builder/src/pages/[application]/deploy/index.svelte @@ -1,4 +1,5 @@
@@ -60,6 +74,11 @@ Rocket flying through sky +
+ {#each deployments as deployment} +
{JSON.stringify(deployment)}
+ {/each} +
diff --git a/packages/server/src/api/controllers/deploy/index.js b/packages/server/src/api/controllers/deploy/index.js index 327f820a7e..fd1bc65ef1 100644 --- a/packages/server/src/api/controllers/deploy/index.js +++ b/packages/server/src/api/controllers/deploy/index.js @@ -6,6 +6,7 @@ const { updateDeploymentQuota, } = require("./aws") const { DocumentTypes, SEPARATOR, UNICODE_MAX } = require("../../../db/utils") +const newid = require("../../../db/newid") function replicate(local, remote) { return new Promise((resolve, reject) => { @@ -61,47 +62,164 @@ async function getCurrentInstanceQuota(instanceId) { } } -exports.deployApp = async function(ctx) { - try { - const clientAppLookupDB = new PouchDB("client_app_lookup") - const { clientId } = await clientAppLookupDB.get(ctx.user.appId) +async function storeLocalDeploymentHistory(deployment) { + const db = new PouchDB(deployment.instanceId) - const instanceQuota = await getCurrentInstanceQuota(ctx.user.instanceId) + let deploymentDoc + try { + deploymentDoc = await db.get("_local/deployments") + } catch (err) { + deploymentDoc = { _id: "_local/deployments", history: {} } + } + + const deploymentId = deployment._id || newid() + + // first time deployment + if (!deploymentDoc.history[deploymentId]) + deploymentDoc.history[deploymentId] = {} + + deploymentDoc.history[deploymentId] = { + ...deploymentDoc.history[deploymentId], + ...deployment, + updatedAt: Date.now(), + } + + await db.put(deploymentDoc) + return { + _id: deploymentId, + ...deploymentDoc.history[deploymentId], + } +} + +async function deployApp({ instanceId, appId, clientId, deploymentId }) { + try { + const instanceQuota = await getCurrentInstanceQuota(instanceId) const credentials = await verifyDeployment({ - instanceId: ctx.user.instanceId, - appId: ctx.user.appId, + instanceId, + appId, quota: instanceQuota, }) - ctx.log.info(`Uploading assets for appID ${ctx.user.appId} assets to s3..`) + console.log(`Uploading assets for appID ${appId} assets to s3..`) - if (credentials.errors) { - ctx.throw(500, credentials.errors) - return - } + if (credentials.errors) throw new Error(credentials.errors) - await uploadAppAssets({ - clientId, - appId: ctx.user.appId, - instanceId: ctx.user.instanceId, - ...credentials, - }) + await uploadAppAssets({ clientId, appId, instanceId, ...credentials }) // replicate the DB to the couchDB cluster in prod - ctx.log.info("Replicating local PouchDB to remote..") + console.log("Replicating local PouchDB to remote..") await replicateCouch({ - instanceId: ctx.user.instanceId, + instanceId, clientId, credentials: credentials.couchDbCreds, }) await updateDeploymentQuota(credentials.quota) - ctx.body = { + await storeLocalDeploymentHistory({ + _id: deploymentId, + instanceId, + quota: credentials.quota, status: "SUCCESS", - completed: Date.now(), - } + }) } catch (err) { - ctx.throw(err.status || 500, `Deployment Failed: ${err.message}`) + await storeLocalDeploymentHistory({ + _id: deploymentId, + instanceId, + status: "FAILURE", + err: err.message, + }) + throw new Error(`Deployment Failed: ${err.message}`) } } + +exports.fetchDeployments = async function(ctx) { + try { + const db = new PouchDB(ctx.user.instanceId) + const deploymentDoc = await db.get("_local/deployments") + ctx.body = Object.values(deploymentDoc.history) + } catch (err) { + ctx.body = [] + } +} + +exports.deploymentProgress = async function(ctx) { + try { + const db = new PouchDB(ctx.user.instanceId) + const deploymentDoc = await db.get("_local/deployments") + ctx.body = deploymentDoc[ctx.params.deploymentId] + } catch (err) { + ctx.throw( + 500, + `Error fetching data for deployment ${ctx.params.deploymentId}` + ) + } +} + +exports.deployApp = async function(ctx) { + const clientAppLookupDB = new PouchDB("client_app_lookup") + const { clientId } = await clientAppLookupDB.get(ctx.user.appId) + + const deployment = await storeLocalDeploymentHistory({ + instanceId: ctx.user.instanceId, + appId: ctx.user.appId, + status: "PENDING", + }) + + deployApp({ + ...ctx.user, + clientId, + deploymentId: deployment._id, + }) + + ctx.body = deployment + + // const instanceQuota = await getCurrentInstanceQuota(ctx.user.instanceId) + // const credentials = await verifyDeployment({ + // instanceId: ctx.user.instanceId, + // appId: ctx.user.appId, + // quota: instanceQuota, + // }) + + // ctx.log.info(`Uploading assets for appID ${ctx.user.appId} assets to s3..`) + + // if (credentials.errors) { + // ctx.throw(500, credentials.errors) + // return + // } + + // await uploadAppAssets({ + // clientId, + // appId: ctx.user.appId, + // instanceId: ctx.user.instanceId, + // ...credentials, + // }) + + // // replicate the DB to the couchDB cluster in prod + // ctx.log.info("Replicating local PouchDB to remote..") + // await replicateCouch({ + // instanceId: ctx.user.instanceId, + // clientId, + // credentials: credentials.couchDbCreds, + // }) + + // await updateDeploymentQuota(credentials.quota) + + // const deployment = await storeLocalDeploymentHistory({ + // quota: credentials.quota, + // status: "SUCCESS", + // }) + + // ctx.body = { + // status: "SUCCESS", + // completed: Date.now(), + // } + // } catch (err) { + // ctx.throw(err.status || 500, `Deployment Failed: ${err.message}`) + // await storeLocalDeploymentHistory({ + // appId: ctx.user.appId, + // instanceId: ctx.user.instanceId, + // status: "FAILURE", + // }) + // } +} diff --git a/packages/server/src/api/routes/deploy.js b/packages/server/src/api/routes/deploy.js index eab5680859..4f7aa9b33b 100644 --- a/packages/server/src/api/routes/deploy.js +++ b/packages/server/src/api/routes/deploy.js @@ -5,6 +5,13 @@ const { BUILDER } = require("../../utilities/accessLevels") const router = Router() -router.post("/deploy", authorized(BUILDER), controller.deployApp) +router + .get("/api/deployments", authorized(BUILDER), controller.fetchDeployments) + .get( + "/api/deploy/:deploymentId", + authorized(BUILDER), + controller.deploymentProgress + ) + .post("/api/deploy", authorized(BUILDER), controller.deployApp) module.exports = router From 71ceee4299dc8165ff6003101551889456e6e707 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sat, 17 Oct 2020 15:13:25 +0100 Subject: [PATCH 2/4] deployment history rendering in side bar --- .../deploy/DeploymentHistory.svelte | 122 ++++++++++++++++++ .../pages/[application]/deploy/index.svelte | 20 +-- 2 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 packages/builder/src/components/deploy/DeploymentHistory.svelte diff --git a/packages/builder/src/components/deploy/DeploymentHistory.svelte b/packages/builder/src/components/deploy/DeploymentHistory.svelte new file mode 100644 index 0000000000..d9dc549294 --- /dev/null +++ b/packages/builder/src/components/deploy/DeploymentHistory.svelte @@ -0,0 +1,122 @@ + + +
+ Deployment History + {#if deployments.length > 0} + + + View Your Deployed App → + + + {/if} + {#each deployments as deployment} +
+
+ + {formatDate(deployment.updatedAt, 'fullDate')} + + + {formatDate(deployment.updatedAt, 'timeOnly')} + +
+
+ {deployment.status} +
+
+ {/each} +
+ + diff --git a/packages/builder/src/pages/[application]/deploy/index.svelte b/packages/builder/src/pages/[application]/deploy/index.svelte index 4ab9cd2789..afeb17f5e2 100644 --- a/packages/builder/src/pages/[application]/deploy/index.svelte +++ b/packages/builder/src/pages/[application]/deploy/index.svelte @@ -5,6 +5,7 @@ import { notifier } from "builderStore/store/notifications" import api from "builderStore/api" import Spinner from "components/common/Spinner.svelte" + import DeploymentHistory from "components/deploy/DeploymentHistory.svelte" import analytics from "analytics" let deployed = false @@ -43,8 +44,8 @@ async function fetchDeployments() { try { - const response = api.get(`/api/deployments`) - deployments = await response.json + const response = await api.get(`/api/deployments`) + deployments = await response.json() } catch (err) { console.error(err) notifier.danger("Error fetching deployment history. Please try again.") @@ -74,11 +75,7 @@ Rocket flying through sky -
- {#each deployments as deployment} -
{JSON.stringify(deployment)}
- {/each} -
+ From ae71f9ed012bfda89c515757065f1c59e1d61aed Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sun, 18 Oct 2020 18:09:14 +0100 Subject: [PATCH 3/4] deployment history design updates and polling --- .../deploy/DeploymentHistory.svelte | 98 +++++-- .../pages/[application]/deploy/index.svelte | 39 +-- packages/builder/yarn.lock | 251 +++++++++++++++++- .../src/api/controllers/deploy/index.js | 2 +- 4 files changed, 327 insertions(+), 63 deletions(-) diff --git a/packages/builder/src/components/deploy/DeploymentHistory.svelte b/packages/builder/src/components/deploy/DeploymentHistory.svelte index d9dc549294..abfe6eefb4 100644 --- a/packages/builder/src/components/deploy/DeploymentHistory.svelte +++ b/packages/builder/src/components/deploy/DeploymentHistory.svelte @@ -1,5 +1,9 @@ -
- Deployment History - {#if deployments.length > 0} - +{#if deployments.length > 0} +
+
+

Deployment History

View Your Deployed App → - - {/if} - {#each deployments as deployment} -
-
- - {formatDate(deployment.updatedAt, 'fullDate')} - - - {formatDate(deployment.updatedAt, 'timeOnly')} - -
-
- {deployment.status} -
-
- {/each} -
+ +
+ {#each deployments as deployment} +
+
+ + {formatDate(deployment.updatedAt, 'fullDate')} + + + {formatDate(deployment.updatedAt, 'timeOnly')} + +
+
+ {deployment.status} +
+
+ {/each} +
+
+{/if}