set up live replication between prod and dev instances

This commit is contained in:
Martin McKeaveney 2021-05-13 17:24:32 +01:00
parent 51bdbb7878
commit 95c80f2b4a
7 changed files with 65 additions and 54 deletions

View File

@ -11,47 +11,9 @@ class Replication {
this.target = getDB(target)
}
sync(opts) {
return new Promise((resolve, reject) => {
this.source
.sync(this.target, opts)
.on("change", function (info) {
// handle change
})
.on("paused", function (err) {
// replication paused (e.g. replication up to date, user went offline)
})
.on("active", function () {
// replicate resumed (e.g. new changes replicating, user went back online)
})
.on("denied", function (err) {
// a document failed to replicate (e.g. due to permissions)
return reject(
new Error(`Denied: Document failed to replicate ${err}`)
)
})
.on("complete", function (info) {
return resolve(info)
})
.on("error", function (err) {
return reject(new Error(`Replication Error: ${err}`))
})
})
}
replicate() {
return new Promise((resolve, reject) => {
this.replication = this.source.replicate
.to(this.target)
// .on("change", function (info) {
// // handle change
// })
// .on("paused", function (err) {
// // replication paused (e.g. replication up to date, user went offline)
// })
// .on("active", function () {
// // replicate resumed (e.g. new changes replicating, user went back online)
// })
promisify(operation, opts = {}) {
return new Promise(resolve => {
operation(this.target, opts)
.on("denied", function (err) {
// a document failed to replicate (e.g. due to permissions)
throw new Error(`Denied: Document failed to replicate ${err}`)
@ -65,6 +27,40 @@ class Replication {
})
}
/**
* Two way replication operation, intended to be promise based.
* @param {Object} opts - PouchDB replication options
*/
sync(opts) {
this.replication = this.promisify(this.source.sync, opts)
return this.replication
}
/**
* One way replication operation, intended to be promise based.
* @param {Object} opts - PouchDB replication options
*/
replicate(opts) {
this.replication = this.promisify(this.source.replicate.to, opts)
return this.replication
}
/**
* Set up an ongoing live sync between 2 CouchDB databases.
* @param {Object} opts - PouchDB replication options
*/
subscribe(opts = {}) {
this.replication = this.source.replicate
.to(this.target, {
live: true,
retry: true,
...opts,
})
.on("error", function (err) {
throw new Error(`Replication Error: ${err}`)
})
}
async rollback() {
await this.target.destroy()
await this.replicate()

View File

@ -1,5 +1,5 @@
<script>
import { General, DangerZone, APIKeys } from "./tabs"
import { General, DangerZone } from "./tabs"
import { ModalContent, Tab, Tabs } from "@budibase/bbui"
</script>
@ -13,9 +13,9 @@
<Tab title="General">
<General />
</Tab>
<Tab title="API Keys">
<!-- <Tab title="API Keys">
<APIKeys />
</Tab>
</Tab> -->
<Tab title="Danger Zone">
<DangerZone />
</Tab>

View File

@ -18,6 +18,7 @@
export let openApp
export let deleteApp
export let releaseLock
export let deletable
</script>
<div class="wrapper">
@ -34,9 +35,11 @@
<MenuItem on:click={() => exportApp(app)} icon="Download">
Export
</MenuItem>
<MenuItem on:click={() => deleteApp(app)} icon="Delete">
{#if deletable}
<MenuItem on:click={() => deleteApp(app)} icon="Delete">
Delete
</MenuItem>
{/if}
{#if app.lockedBy && app.lockedBy?.email === $auth.user?.email}
<MenuItem on:click={() => releaseLock(app._id)} icon="LockOpen">
Release Lock

View File

@ -16,13 +16,14 @@
export let openApp
export let exportApp
export let deleteApp
export let last
export let releaseLock
export let last
export let deletable
</script>
<div class="title" class:last>
<div class="preview" use:gradient={{ seed: app.name }} />
<Link on:click={openApp}>
<Link on:click={() => openApp(app)}>
<Heading size="XS">
{app.name}
</Heading>
@ -45,7 +46,9 @@
<ActionMenu align="right">
<Icon hoverable slot="control" name="More" />
<MenuItem on:click={() => exportApp(app)} icon="Download">Export</MenuItem>
<MenuItem on:click={() => deleteApp(app)} icon="Delete">Delete</MenuItem>
{#if deletable}
<MenuItem on:click={() => deleteApp(app)} icon="Delete">Delete</MenuItem>
{/if}
{#if app.lockedBy && app.lockedBy?.email === $auth.user?.email}
<MenuItem on:click={() => releaseLock(app._id)} icon="LockOpen">
Release Lock

View File

@ -11,6 +11,7 @@ export const FrontendTypes = {
export const AppStatus = {
DEV: "dev",
DEPLOYED: "deployed"
}
// fields on the user table that cannot be edited

View File

@ -95,6 +95,7 @@
await del(`/api/applications/${appToDelete?._id}`)
await apps.load()
appToDelete = null
notifications.success("App deleted successfully.")
}
const releaseLock = async appId => {
@ -159,6 +160,7 @@
{#each $apps as app, idx (app._id)}
<svelte:component
this={layout === "grid" ? AppCard : AppRow}
deletable={appStatus === AppStatus.DEPLOYED}
{releaseLock}
{app}
{openApp}

View File

@ -57,24 +57,30 @@ async function storeLocalDeploymentHistory(deployment) {
async function deployApp(deployment) {
try {
const deployTarget = deployment.appId.replace("_dev", "")
const productionAppId = deployment.appId.replace("_dev", "")
const replication = new Replication({
source: deployment.appId,
target: deployTarget,
target: productionAppId,
})
await replication.replicate()
// Strip the _dev prefix and update the appID document in the new DB
const db = new PouchDB(deployTarget)
const db = new PouchDB(productionAppId)
const appDoc = await db.get(deployment.appId)
await db.remove(appDoc)
appDoc._id = deployTarget
appDoc._id = productionAppId
delete appDoc._rev
appDoc.instance._id = deployTarget
appDoc.instance._id = productionAppId
await db.put(appDoc)
// Set up live sync between the live and dev instances
const liveReplication = new Replication({
source: productionAppId,
target: deployment.appId,
})
liveReplication.subscribe()
deployment.setStatus(DeploymentStatus.SUCCESS)
await storeLocalDeploymentHistory(deployment)
} catch (err) {