Merge pull request #3122 from Budibase/fix/mike-d-various-fixes
Various fixes, including replacing live replication from prod -> dev
This commit is contained in:
commit
1aee0e8f2a
|
@ -45,22 +45,6 @@ class Replication {
|
|||
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}`)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the target DB back to the state of the source DB
|
||||
*/
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<Body size="S">
|
||||
Personalise the platform by adding your first name and last name.
|
||||
</Body>
|
||||
<Input disabled bind:value={$auth.user.email} label="Email" />
|
||||
<Input bind:value={$values.firstName} label="First name" />
|
||||
<Input bind:value={$values.lastName} label="Last name" />
|
||||
</ModalContent>
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
<script>
|
||||
import { store, automationStore } from "builderStore"
|
||||
import { roles } from "stores/backend"
|
||||
import { Icon, ActionGroup, Tabs, Tab } from "@budibase/bbui"
|
||||
import { Icon, ActionGroup, Tabs, Tab, notifications } from "@budibase/bbui"
|
||||
import DeployModal from "components/deploy/DeployModal.svelte"
|
||||
import RevertModal from "components/deploy/RevertModal.svelte"
|
||||
import VersionModal from "components/deploy/VersionModal.svelte"
|
||||
import NPSFeedbackForm from "components/feedback/NPSFeedbackForm.svelte"
|
||||
import { get } from "builderStore/api"
|
||||
import { get, post } from "builderStore/api"
|
||||
import { auth, admin } from "stores/portal"
|
||||
import { isActive, goto, layout, redirect } from "@roxi/routify"
|
||||
import Logo from "assets/bb-emblem.svg"
|
||||
import { capitalise } from "helpers"
|
||||
import UpgradeModal from "../../../../components/upgrade/UpgradeModal.svelte"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
// Get Package and set store
|
||||
export let application
|
||||
let promise = getPackage()
|
||||
// sync once when you load the app
|
||||
let hasSynced = false
|
||||
$: selected = capitalise(
|
||||
$layout.children.find(layout => $isActive(layout.path))?.title ?? "data"
|
||||
)
|
||||
|
@ -67,6 +70,16 @@
|
|||
return state
|
||||
})
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if (!hasSynced && application) {
|
||||
const res = await post(`/api/applications/${application}/sync`)
|
||||
if (res.status !== 200) {
|
||||
notifications.error("Failed to sync with production database")
|
||||
}
|
||||
hasSynced = true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#await promise}
|
||||
|
|
|
@ -16,7 +16,13 @@
|
|||
admin = false
|
||||
|
||||
async function createUser() {
|
||||
const res = await users.create({ email: $email, password, builder, admin })
|
||||
const res = await users.create({
|
||||
email: $email,
|
||||
password,
|
||||
builder,
|
||||
admin,
|
||||
forceResetPassword: true,
|
||||
})
|
||||
if (res.status) {
|
||||
notifications.error(res.message)
|
||||
} else {
|
||||
|
|
|
@ -35,12 +35,21 @@ export function createUsersStore() {
|
|||
return await response.json()
|
||||
}
|
||||
|
||||
async function create({ email, password, admin, builder }) {
|
||||
async function create({
|
||||
email,
|
||||
password,
|
||||
admin,
|
||||
builder,
|
||||
forceResetPassword,
|
||||
}) {
|
||||
const body = {
|
||||
email,
|
||||
password,
|
||||
roles: {},
|
||||
}
|
||||
if (forceResetPassword) {
|
||||
body.forceResetPassword = forceResetPassword
|
||||
}
|
||||
if (builder) {
|
||||
body.builder = { global: true }
|
||||
}
|
||||
|
|
|
@ -25,7 +25,12 @@ const { BASE_LAYOUTS } = require("../../constants/layouts")
|
|||
const { createHomeScreen } = require("../../constants/screens")
|
||||
const { cloneDeep } = require("lodash/fp")
|
||||
const { processObject } = require("@budibase/string-templates")
|
||||
const { getAllApps } = require("@budibase/auth/db")
|
||||
const {
|
||||
getAllApps,
|
||||
isDevAppID,
|
||||
getDeployedAppID,
|
||||
Replication,
|
||||
} = require("@budibase/auth/db")
|
||||
const { USERS_TABLE_SCHEMA } = require("../../constants")
|
||||
const {
|
||||
getDeployedApps,
|
||||
|
@ -134,7 +139,7 @@ async function createInstance(template) {
|
|||
return { _id: appId }
|
||||
}
|
||||
|
||||
exports.fetch = async function (ctx) {
|
||||
exports.fetch = async ctx => {
|
||||
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
||||
const apps = await getAllApps(CouchDB, { dev, all })
|
||||
|
@ -159,7 +164,7 @@ exports.fetch = async function (ctx) {
|
|||
ctx.body = apps
|
||||
}
|
||||
|
||||
exports.fetchAppDefinition = async function (ctx) {
|
||||
exports.fetchAppDefinition = async ctx => {
|
||||
const db = new CouchDB(ctx.params.appId)
|
||||
const layouts = await getLayouts(db)
|
||||
const userRoleId = getUserRoleId(ctx)
|
||||
|
@ -175,7 +180,7 @@ exports.fetchAppDefinition = async function (ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
exports.fetchAppPackage = async function (ctx) {
|
||||
exports.fetchAppPackage = async ctx => {
|
||||
const db = new CouchDB(ctx.params.appId)
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
const layouts = await getLayouts(db)
|
||||
|
@ -196,7 +201,7 @@ exports.fetchAppPackage = async function (ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
exports.create = async function (ctx) {
|
||||
exports.create = async ctx => {
|
||||
const { useTemplate, templateKey, templateString } = ctx.request.body
|
||||
const instanceConfig = {
|
||||
useTemplate,
|
||||
|
@ -252,13 +257,13 @@ exports.create = async function (ctx) {
|
|||
ctx.body = newApplication
|
||||
}
|
||||
|
||||
exports.update = async function (ctx) {
|
||||
exports.update = async ctx => {
|
||||
const data = await updateAppPackage(ctx, ctx.request.body, ctx.params.appId)
|
||||
ctx.status = 200
|
||||
ctx.body = data
|
||||
}
|
||||
|
||||
exports.updateClient = async function (ctx) {
|
||||
exports.updateClient = async ctx => {
|
||||
// Get current app version
|
||||
const db = new CouchDB(ctx.params.appId)
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
|
@ -280,7 +285,7 @@ exports.updateClient = async function (ctx) {
|
|||
ctx.body = data
|
||||
}
|
||||
|
||||
exports.revertClient = async function (ctx) {
|
||||
exports.revertClient = async ctx => {
|
||||
// Check app can be reverted
|
||||
const db = new CouchDB(ctx.params.appId)
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
|
@ -303,7 +308,7 @@ exports.revertClient = async function (ctx) {
|
|||
ctx.body = data
|
||||
}
|
||||
|
||||
exports.delete = async function (ctx) {
|
||||
exports.delete = async ctx => {
|
||||
const db = new CouchDB(ctx.params.appId)
|
||||
|
||||
const result = await db.destroy()
|
||||
|
@ -318,6 +323,35 @@ exports.delete = async function (ctx) {
|
|||
ctx.body = result
|
||||
}
|
||||
|
||||
exports.sync = async ctx => {
|
||||
const appId = ctx.params.appId
|
||||
if (!isDevAppID(appId)) {
|
||||
ctx.throw(400, "This action cannot be performed for production apps")
|
||||
}
|
||||
const prodAppId = getDeployedAppID(appId)
|
||||
const replication = new Replication({
|
||||
source: prodAppId,
|
||||
target: appId,
|
||||
})
|
||||
let error
|
||||
try {
|
||||
await replication.replicate({
|
||||
filter: function (doc) {
|
||||
return doc._id !== DocumentTypes.APP_METADATA
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
error = err
|
||||
}
|
||||
if (error) {
|
||||
ctx.throw(400, error)
|
||||
} else {
|
||||
ctx.body = {
|
||||
message: "App sync completed successfully.",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateAppPackage = async (ctx, appPackage, appId) => {
|
||||
const url = await getAppUrlIfNotInUse(ctx)
|
||||
const db = new CouchDB(appId)
|
||||
|
|
|
@ -28,14 +28,23 @@ exports.fetchSelf = async ctx => {
|
|||
...metadata,
|
||||
})
|
||||
} catch (err) {
|
||||
let response
|
||||
// user didn't exist in app, don't pretend they do
|
||||
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
|
||||
ctx.body = {}
|
||||
response = {}
|
||||
}
|
||||
// user has a role of some sort, return them
|
||||
else {
|
||||
ctx.body = user
|
||||
else if (err.status === 404) {
|
||||
const metadata = {
|
||||
_id: userId,
|
||||
}
|
||||
const dbResp = await db.put(metadata)
|
||||
user._rev = dbResp.rev
|
||||
response = user
|
||||
} else {
|
||||
response = user
|
||||
}
|
||||
ctx.body = response
|
||||
}
|
||||
} else {
|
||||
ctx.body = user
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const CouchDB = require("../../../db")
|
||||
const Deployment = require("./Deployment")
|
||||
const { Replication } = require("@budibase/auth/db")
|
||||
const { Replication, getDeployedAppID } = require("@budibase/auth/db")
|
||||
const { DocumentTypes, getAutomationParams } = require("../../../db/utils")
|
||||
const {
|
||||
disableAllCrons,
|
||||
|
@ -87,7 +87,7 @@ async function initDeployedApp(prodAppId) {
|
|||
|
||||
async function deployApp(deployment) {
|
||||
try {
|
||||
const productionAppId = deployment.appId.replace("_dev", "")
|
||||
const productionAppId = getDeployedAppID(deployment.appId)
|
||||
|
||||
const replication = new Replication({
|
||||
source: deployment.appId,
|
||||
|
@ -104,23 +104,8 @@ async function deployApp(deployment) {
|
|||
appDoc.instance._id = productionAppId
|
||||
await db.put(appDoc)
|
||||
console.log("New app doc written successfully.")
|
||||
|
||||
console.log("Setting up live repl between dev and prod")
|
||||
// Set up live sync between the live and dev instances
|
||||
const liveReplication = new Replication({
|
||||
source: productionAppId,
|
||||
target: deployment.appId,
|
||||
})
|
||||
liveReplication.subscribe({
|
||||
filter: function (doc) {
|
||||
return doc._id !== DocumentTypes.APP_METADATA
|
||||
},
|
||||
})
|
||||
console.log("Set up live repl between dev and prod")
|
||||
|
||||
console.log("Initialising deployed app")
|
||||
await initDeployedApp(productionAppId)
|
||||
console.log("Init complete, setting deployment to successful")
|
||||
console.log("Deployed app initialised, setting deployment to successful")
|
||||
deployment.setStatus(DeploymentStatus.SUCCESS)
|
||||
await storeDeploymentHistory(deployment)
|
||||
} catch (err) {
|
||||
|
|
|
@ -7,6 +7,7 @@ const usage = require("../../middleware/usageQuota")
|
|||
const router = Router()
|
||||
|
||||
router
|
||||
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
|
||||
.post("/api/applications", authorized(BUILDER), usage, controller.create)
|
||||
.get("/api/applications/:appId/definition", controller.fetchAppDefinition)
|
||||
.get("/api/applications", controller.fetch)
|
||||
|
|
|
@ -81,7 +81,9 @@ async function getFullLinkedDocs(ctx, appId, links) {
|
|||
row => row.doc
|
||||
)
|
||||
// convert the unique db rows back to a full list of linked rows
|
||||
const linked = linkedRowIds.map(id => dbRows.find(row => row._id === id))
|
||||
const linked = linkedRowIds
|
||||
.map(id => dbRows.find(row => row && row._id === id))
|
||||
.filter(row => row != null)
|
||||
// need to handle users as specific cases
|
||||
let [users, other] = partition(linked, linkRow =>
|
||||
linkRow._id.startsWith(USER_METDATA_PREFIX)
|
||||
|
@ -172,13 +174,18 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => {
|
|||
row[link.fieldName] = []
|
||||
}
|
||||
const linkedRow = linked.find(row => row._id === link.id)
|
||||
const linkedTableId =
|
||||
linkedRow.tableId || getRelatedTableForField(table, link.fieldName)
|
||||
const linkedTable = await getLinkedTable(db, linkedTableId, linkedTables)
|
||||
if (!linkedRow || !linkedTable) {
|
||||
continue
|
||||
if (linkedRow) {
|
||||
const linkedTableId =
|
||||
linkedRow.tableId || getRelatedTableForField(table, link.fieldName)
|
||||
const linkedTable = await getLinkedTable(
|
||||
db,
|
||||
linkedTableId,
|
||||
linkedTables
|
||||
)
|
||||
if (linkedTable) {
|
||||
row[link.fieldName].push(processFormulas(linkedTable, linkedRow))
|
||||
}
|
||||
}
|
||||
row[link.fieldName].push(processFormulas(linkedTable, linkedRow))
|
||||
}
|
||||
}
|
||||
return rows
|
||||
|
|
Loading…
Reference in New Issue