Merge branch 'feature/draft-apps' of github.com:Budibase/budibase into feature/draft-apps
This commit is contained in:
commit
17504443df
|
@ -31,7 +31,7 @@ class Replication {
|
|||
* Two way replication operation, intended to be promise based.
|
||||
* @param {Object} opts - PouchDB replication options
|
||||
*/
|
||||
sync(opts) {
|
||||
sync(opts = {}) {
|
||||
this.replication = this.promisify(this.source.sync, opts)
|
||||
return this.replication
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class Replication {
|
|||
* One way replication operation, intended to be promise based.
|
||||
* @param {Object} opts - PouchDB replication options
|
||||
*/
|
||||
replicate(opts) {
|
||||
replicate(opts = {}) {
|
||||
this.replication = this.promisify(this.source.replicate.to, opts)
|
||||
return this.replication
|
||||
}
|
||||
|
@ -61,8 +61,13 @@ class Replication {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the target DB back to the state of the source DB
|
||||
*/
|
||||
async rollback() {
|
||||
await this.target.destroy()
|
||||
// Recreate the DB again
|
||||
this.target = getDB(this.target.name)
|
||||
await this.replicate()
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ exports.StaticDatabases = {
|
|||
GLOBAL: {
|
||||
name: "global-db",
|
||||
},
|
||||
DEPLOYMENTS: {
|
||||
name: "deployments",
|
||||
},
|
||||
}
|
||||
|
||||
const DocumentTypes = {
|
||||
|
@ -22,6 +25,7 @@ const DocumentTypes = {
|
|||
TEMPLATE: "template",
|
||||
APP: "app",
|
||||
APP_DEV: "app_dev",
|
||||
APP_METADATA: "app_metadata",
|
||||
ROLE: "role",
|
||||
}
|
||||
|
||||
|
@ -162,7 +166,7 @@ exports.getAllApps = async (devApps = false) => {
|
|||
const appDbNames = allDbs.filter(dbName =>
|
||||
dbName.startsWith(exports.APP_PREFIX)
|
||||
)
|
||||
const appPromises = appDbNames.map(db => new CouchDB(db).get(db))
|
||||
const appPromises = appDbNames.map(db => new CouchDB(db).get(DocumentTypes.APP_METADATA))
|
||||
if (appPromises.length === 0) {
|
||||
return []
|
||||
} else {
|
||||
|
@ -172,9 +176,9 @@ exports.getAllApps = async (devApps = false) => {
|
|||
.map(({ value }) => value)
|
||||
return apps.filter(app => {
|
||||
if (devApps) {
|
||||
return app._id.startsWith(exports.APP_DEV_PREFIX)
|
||||
return app.appId.startsWith(exports.APP_DEV_PREFIX)
|
||||
}
|
||||
return !app._id.startsWith(exports.APP_DEV_PREFIX)
|
||||
return !app.appId.startsWith(exports.APP_DEV_PREFIX)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,14 +51,14 @@ export const getFrontendStore = () => {
|
|||
store.actions = {
|
||||
initialise: async pkg => {
|
||||
const { layouts, screens, application, clientLibPath } = pkg
|
||||
const components = await fetchComponentLibDefinitions(application._id)
|
||||
const components = await fetchComponentLibDefinitions(application.appId)
|
||||
store.update(state => ({
|
||||
...state,
|
||||
libraries: application.componentLibraries,
|
||||
components,
|
||||
name: application.name,
|
||||
description: application.description,
|
||||
appId: application._id,
|
||||
appId: application.appId,
|
||||
url: application.url,
|
||||
layouts,
|
||||
screens,
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { Button, Modal, notifications, ModalContent } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import api from "builderStore/api"
|
||||
import analytics from "analytics"
|
||||
import FeedbackIframe from "components/feedback/FeedbackIframe.svelte"
|
||||
|
||||
const DeploymentStatus = {
|
||||
SUCCESS: "SUCCESS",
|
||||
PENDING: "PENDING",
|
||||
FAILURE: "FAILURE",
|
||||
}
|
||||
|
||||
const POLL_INTERVAL = 1000
|
||||
|
||||
|
||||
let loading = false
|
||||
let feedbackModal
|
||||
let deployments = []
|
||||
let poll
|
||||
let publishModal
|
||||
|
||||
$: appId = $store.appId
|
||||
|
||||
async function deployApp() {
|
||||
try {
|
||||
notifications.info(`Deployment started. Please wait.`)
|
||||
const response = await api.post("/api/deploy")
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) {
|
||||
throw new Error()
|
||||
}
|
||||
|
||||
if (analytics.requestFeedbackOnDeploy()) {
|
||||
feedbackModal.show()
|
||||
}
|
||||
} catch (err) {
|
||||
analytics.captureException(err)
|
||||
notifications.error("Deployment unsuccessful. Please try again later.")
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchDeployments() {
|
||||
try {
|
||||
const response = await api.get(`/api/deployments`)
|
||||
const json = await response.json()
|
||||
|
||||
if (deployments.length > 0) {
|
||||
checkIncomingDeploymentStatus(deployments, json)
|
||||
}
|
||||
|
||||
deployments = json
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
clearInterval(poll)
|
||||
notifications.error(
|
||||
"Error fetching deployment history. Please try again."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Required to check any updated deployment statuses between polls
|
||||
function checkIncomingDeploymentStatus(current, incoming) {
|
||||
for (let incomingDeployment of incoming) {
|
||||
if (incomingDeployment.status === DeploymentStatus.FAILURE || incomingDeployment.status === DeploymentStatus.SUCCESS) {
|
||||
const currentDeployment = current.find(
|
||||
deployment => deployment._id === incomingDeployment._id
|
||||
)
|
||||
|
||||
// We have just been notified of an ongoing deployments status change
|
||||
if (
|
||||
!currentDeployment ||
|
||||
currentDeployment.status === DeploymentStatus.PENDING
|
||||
) {
|
||||
if (incomingDeployment.status === DeploymentStatus.FAILURE) {
|
||||
notifications.error(incomingDeployment.err)
|
||||
} else {
|
||||
notifications.send("Published to Production.", "success", "CheckmarkCircle")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMount(() => {
|
||||
fetchDeployments()
|
||||
poll = setInterval(fetchDeployments, POLL_INTERVAL)
|
||||
})
|
||||
|
||||
onDestroy(() => clearInterval(poll))
|
||||
</script>
|
||||
|
||||
|
||||
<Button
|
||||
secondary
|
||||
on:click={publishModal.show}
|
||||
>
|
||||
Publish
|
||||
</Button>
|
||||
<Modal bind:this={publishModal}>
|
||||
<ModalContent
|
||||
title="Publish to Production"
|
||||
confirmText="Publish"
|
||||
onConfirm={deployApp}
|
||||
>
|
||||
<span>The changes you have made will be published to the production version of the application.</span>
|
||||
</ModalContent>
|
||||
</Modal>
|
|
@ -0,0 +1,35 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { Button, Icon, Modal, notifications, ModalContent } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import { apps } from "stores/portal"
|
||||
import api from "builderStore/api"
|
||||
|
||||
let revertModal
|
||||
|
||||
$: appId = $store.appId
|
||||
|
||||
const revert = async () => {
|
||||
try {
|
||||
const response = await api.post(`/api/dev/${appId}/revert`)
|
||||
const json = await response.json()
|
||||
if (response.status !== 200) throw json.message
|
||||
|
||||
notifications.info("Changes reverted.")
|
||||
} catch (err) {
|
||||
notifications.error(`Error reverting changes: ${err}`)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<Icon name="Revert" hoverable on:click={revertModal.show} />
|
||||
<Modal bind:this={revertModal}>
|
||||
<ModalContent
|
||||
title="Revert Changes"
|
||||
confirmText="Revert"
|
||||
onConfirm={revert}
|
||||
>
|
||||
<span>The changes you have made will be deleted and the application reverted back to its production state.</span>
|
||||
</ModalContent>
|
||||
</Modal>
|
|
@ -41,7 +41,7 @@
|
|||
</MenuItem>
|
||||
{/if}
|
||||
{#if app.lockedBy && app.lockedBy?.email === $auth.user?.email}
|
||||
<MenuItem on:click={() => releaseLock(app._id)} icon="LockOpen">
|
||||
<MenuItem on:click={() => releaseLock(app.appId)} icon="LockOpen">
|
||||
Release Lock
|
||||
</MenuItem>
|
||||
{/if}
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
<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">
|
||||
<MenuItem on:click={() => releaseLock(app.appId)} icon="LockOpen">
|
||||
Release Lock
|
||||
</MenuItem>
|
||||
{/if}
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
|
||||
// Select Correct Application/DB in prep for creating user
|
||||
const applicationPkg = await get(
|
||||
`/api/applications/${appJson._id}/appPackage`
|
||||
`/api/applications/${appJson.instance._id}/appPackage`
|
||||
)
|
||||
const pkg = await applicationPkg.json()
|
||||
if (applicationPkg.ok) {
|
||||
|
@ -112,7 +112,7 @@
|
|||
}
|
||||
const userResp = await api.post(`/api/users/metadata/self`, user)
|
||||
await userResp.json()
|
||||
$goto(`/builder/app/${appJson._id}`)
|
||||
$goto(`/builder/app/${appJson.instance._id}`)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
notifications.error(error)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<script>
|
||||
import { store, automationStore } from "builderStore"
|
||||
import { roles } from "stores/backend"
|
||||
import { Button, ActionGroup, ActionButton, Tabs, Tab } from "@budibase/bbui"
|
||||
import { Button, Icon, Modal, ModalContent, ActionGroup, ActionButton, Tabs, Tab } from "@budibase/bbui"
|
||||
import SettingsLink from "components/settings/Link.svelte"
|
||||
import ThemeEditorDropdown from "components/settings/ThemeEditorDropdown.svelte"
|
||||
import FeedbackNavLink from "components/feedback/FeedbackNavLink.svelte"
|
||||
import DeployModal from "components/deploy/DeployModal.svelte"
|
||||
import RevertModal from "components/deploy/RevertModal.svelte"
|
||||
import { get } from "builderStore/api"
|
||||
import { isActive, goto, layout } from "@roxi/routify"
|
||||
import Logo from "/assets/bb-logo.svg"
|
||||
|
@ -81,25 +83,13 @@
|
|||
<ActionGroup />
|
||||
</div>
|
||||
<div class="toprightnav">
|
||||
<ThemeEditorDropdown />
|
||||
<FeedbackNavLink />
|
||||
<div class="topnavitemright">
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://github.com/Budibase/budibase/discussions"
|
||||
>
|
||||
<i class="ri-github-fill" />
|
||||
</a>
|
||||
</div>
|
||||
<SettingsLink />
|
||||
<Button
|
||||
secondary
|
||||
<RevertModal />
|
||||
<Icon name="Play" hoverable
|
||||
on:click={() => {
|
||||
window.open(`/${application}`)
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Button>
|
||||
/>
|
||||
<DeployModal />
|
||||
</div>
|
||||
</div>
|
||||
<div class="beta">
|
||||
|
@ -153,6 +143,7 @@
|
|||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.topleftnav {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
<!-- routify:options index=4 -->
|
||||
<slot />
|
||||
<!-- <slot /> -->
|
|
@ -62,16 +62,16 @@
|
|||
|
||||
const openApp = app => {
|
||||
if (appStatus === AppStatus.DEV) {
|
||||
$goto(`../../app/${app._id}`)
|
||||
$goto(`../../app/${app.appId}`)
|
||||
} else {
|
||||
window.open(`/${app._id}`, "_blank")
|
||||
window.open(`/${app.appId}`, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
const exportApp = app => {
|
||||
try {
|
||||
download(
|
||||
`/api/backups/export?appId=${app._id}&appname=${encodeURIComponent(
|
||||
`/api/backups/export?appId=${app.appId}&appname=${encodeURIComponent(
|
||||
app.name
|
||||
)}`
|
||||
)
|
||||
|
@ -157,7 +157,7 @@
|
|||
class:appGrid={layout === "grid"}
|
||||
class:appTable={layout === "table"}
|
||||
>
|
||||
{#each $apps as app, idx (app._id)}
|
||||
{#each $apps as app, idx (app.appId)}
|
||||
<svelte:component
|
||||
this={layout === "grid" ? AppCard : AppRow}
|
||||
deletable={appStatus === AppStatus.PUBLISHED}
|
||||
|
|
|
@ -13,6 +13,7 @@ export function createAppStore() {
|
|||
} else {
|
||||
store.set([])
|
||||
}
|
||||
return json
|
||||
} catch (error) {
|
||||
store.set([])
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export { organisation } from "./organisation"
|
||||
export { admin } from "./admin"
|
||||
export { apps } from "./apps"
|
||||
export { email } from "./email"
|
||||
export { email } from "./email"
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
const CouchDB = require("../src/db")
|
||||
const { DocumentTypes } = require("../src/db/utils")
|
||||
|
||||
const appName = process.argv[2].toLowerCase()
|
||||
const remoteUrl = process.argv[3]
|
||||
|
@ -18,7 +19,7 @@ const run = async () => {
|
|||
let apps = []
|
||||
for (let dbName of appDbNames) {
|
||||
const db = new CouchDB(dbName)
|
||||
apps.push(db.get(dbName))
|
||||
apps.push(db.get(DocumentTypes.APP_METADATA))
|
||||
}
|
||||
apps = await Promise.all(apps)
|
||||
const app = apps.find(
|
||||
|
@ -32,7 +33,7 @@ const run = async () => {
|
|||
return
|
||||
}
|
||||
|
||||
const instanceDb = new CouchDB(app._id)
|
||||
const instanceDb = new CouchDB(app.appId)
|
||||
const remoteDb = new CouchDB(`${remoteUrl}/${appName}`)
|
||||
|
||||
instanceDb.replicate
|
||||
|
|
|
@ -85,7 +85,6 @@ async function getAppUrlIfNotInUse(ctx) {
|
|||
}
|
||||
|
||||
async function createInstance(template) {
|
||||
// TODO: Do we need the normal app ID?
|
||||
const baseAppId = generateAppID()
|
||||
const appId = generateDevAppID(baseAppId)
|
||||
|
||||
|
@ -125,7 +124,7 @@ exports.fetch = async function (ctx) {
|
|||
if (isDev) {
|
||||
const locks = await getAllLocks()
|
||||
for (let app of apps) {
|
||||
const lock = locks.find(lock => lock.appId === app._id)
|
||||
const lock = locks.find(lock => lock.appId === app.appId)
|
||||
if (lock) {
|
||||
app.lockedBy = lock.user
|
||||
} else {
|
||||
|
@ -156,7 +155,7 @@ exports.fetchAppDefinition = async function (ctx) {
|
|||
|
||||
exports.fetchAppPackage = async function (ctx) {
|
||||
const db = new CouchDB(ctx.params.appId)
|
||||
const application = await db.get(ctx.params.appId)
|
||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||
const [layouts, screens] = await Promise.all([getLayouts(db), getScreens(db)])
|
||||
|
||||
ctx.body = {
|
||||
|
@ -181,7 +180,8 @@ exports.create = async function (ctx) {
|
|||
const url = await getAppUrlIfNotInUse(ctx)
|
||||
const appId = instance._id
|
||||
const newApplication = {
|
||||
_id: appId,
|
||||
_id: DocumentTypes.APP_METADATA,
|
||||
appId: instance._id,
|
||||
type: "app",
|
||||
version: packageJson.version,
|
||||
componentLibraries: ["@budibase/standard-components"],
|
||||
|
@ -244,7 +244,7 @@ exports.delete = async function (ctx) {
|
|||
}
|
||||
|
||||
const createEmptyAppPackage = async (ctx, app) => {
|
||||
const db = new CouchDB(app._id)
|
||||
const db = new CouchDB(app.instance._id)
|
||||
|
||||
let screensAndLayouts = []
|
||||
for (let layout of BASE_LAYOUTS) {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const CouchDB = require("../../db")
|
||||
const { DocumentTypes } = require("../../db/utils")
|
||||
const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
|
||||
|
||||
exports.fetchAppComponentDefinitions = async function (ctx) {
|
||||
const appId = ctx.params.appId || ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const app = await db.get(appId)
|
||||
const app = await db.get(DocumentTypes.APP_METADATA)
|
||||
|
||||
let componentManifests = await Promise.all(
|
||||
app.componentLibraries.map(async library => {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const PouchDB = require("../../../db")
|
||||
const Deployment = require("./Deployment")
|
||||
const { Replication } = require("@budibase/auth/db")
|
||||
const { Replication, StaticDatabases } = require("@budibase/auth/db")
|
||||
const { DocumentTypes } = require("../../../db/utils")
|
||||
|
||||
// the max time we can wait for an invalidation to complete before considering it failed
|
||||
const MAX_PENDING_TIME_MS = 30 * 60000
|
||||
const DeploymentStatus = {
|
||||
|
@ -26,16 +28,16 @@ async function checkAllDeployments(deployments) {
|
|||
return { updated, deployments }
|
||||
}
|
||||
|
||||
async function storeLocalDeploymentHistory(deployment) {
|
||||
async function storeDeploymentHistory(deployment) {
|
||||
const appId = deployment.getAppId()
|
||||
const deploymentJSON = deployment.getJSON()
|
||||
const db = new PouchDB(appId)
|
||||
const db = new PouchDB(StaticDatabases.DEPLOYMENTS.name)
|
||||
|
||||
let deploymentDoc
|
||||
try {
|
||||
deploymentDoc = await db.get("_local/deployments")
|
||||
deploymentDoc = await db.get(appId)
|
||||
} catch (err) {
|
||||
deploymentDoc = { _id: "_local/deployments", history: {} }
|
||||
deploymentDoc = { _id: appId, history: {} }
|
||||
}
|
||||
|
||||
const deploymentId = deploymentJSON._id
|
||||
|
@ -65,12 +67,9 @@ async function deployApp(deployment) {
|
|||
})
|
||||
|
||||
await replication.replicate()
|
||||
|
||||
// Strip the _dev prefix and update the appID document in the new DB
|
||||
const db = new PouchDB(productionAppId)
|
||||
const appDoc = await db.get(deployment.appId)
|
||||
appDoc._id = productionAppId
|
||||
delete appDoc._rev
|
||||
const appDoc = await db.get(DocumentTypes.APP_METADATA)
|
||||
appDoc.appId = productionAppId
|
||||
appDoc.instance._id = productionAppId
|
||||
await db.put(appDoc)
|
||||
|
||||
|
@ -79,13 +78,17 @@ async function deployApp(deployment) {
|
|||
source: productionAppId,
|
||||
target: deployment.appId,
|
||||
})
|
||||
liveReplication.subscribe()
|
||||
liveReplication.subscribe({
|
||||
filter: function (doc) {
|
||||
return doc._id !== DocumentTypes.APP_METADATA
|
||||
},
|
||||
})
|
||||
|
||||
deployment.setStatus(DeploymentStatus.SUCCESS)
|
||||
await storeLocalDeploymentHistory(deployment)
|
||||
await storeDeploymentHistory(deployment)
|
||||
} catch (err) {
|
||||
deployment.setStatus(DeploymentStatus.FAILURE, err.message)
|
||||
await storeLocalDeploymentHistory(deployment)
|
||||
await storeDeploymentHistory(deployment)
|
||||
throw {
|
||||
...err,
|
||||
message: `Deployment Failed: ${err.message}`,
|
||||
|
@ -95,8 +98,8 @@ async function deployApp(deployment) {
|
|||
|
||||
exports.fetchDeployments = async function (ctx) {
|
||||
try {
|
||||
const db = new PouchDB(ctx.appId)
|
||||
const deploymentDoc = await db.get("_local/deployments")
|
||||
const db = new PouchDB(StaticDatabases.DEPLOYMENTS.name)
|
||||
const deploymentDoc = await db.get(ctx.appId)
|
||||
const { updated, deployments } = await checkAllDeployments(
|
||||
deploymentDoc,
|
||||
ctx.user
|
||||
|
@ -112,8 +115,8 @@ exports.fetchDeployments = async function (ctx) {
|
|||
|
||||
exports.deploymentProgress = async function (ctx) {
|
||||
try {
|
||||
const db = new PouchDB(ctx.appId)
|
||||
const deploymentDoc = await db.get("_local/deployments")
|
||||
const db = new PouchDB(StaticDatabases.DEPLOYMENTS.name)
|
||||
const deploymentDoc = await db.get(ctx.appId)
|
||||
ctx.body = deploymentDoc[ctx.params.deploymentId]
|
||||
} catch (err) {
|
||||
ctx.throw(
|
||||
|
@ -126,7 +129,7 @@ exports.deploymentProgress = async function (ctx) {
|
|||
exports.deployApp = async function (ctx) {
|
||||
let deployment = new Deployment(ctx.appId)
|
||||
deployment.setStatus(DeploymentStatus.PENDING)
|
||||
deployment = await storeLocalDeploymentHistory(deployment)
|
||||
deployment = await storeDeploymentHistory(deployment)
|
||||
|
||||
await deployApp(deployment)
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
const fetch = require("node-fetch")
|
||||
const CouchDB = require("../../db")
|
||||
const env = require("../../environment")
|
||||
const { checkSlashesInUrl } = require("../../utilities")
|
||||
const { request } = require("../../utilities/workerRequests")
|
||||
const { clearLock } = require("../../utilities/redis")
|
||||
const { Replication } = require("@budibase/auth").db
|
||||
const { DocumentTypes } = require("../../db/utils")
|
||||
|
||||
async function redirect(ctx, method) {
|
||||
const { devPath } = ctx.params
|
||||
|
@ -45,3 +48,37 @@ exports.clearLock = async ctx => {
|
|||
message: "Lock released successfully.",
|
||||
}
|
||||
}
|
||||
|
||||
exports.revert = async ctx => {
|
||||
const { appId } = ctx.params
|
||||
const productionAppId = appId.replace("_dev", "")
|
||||
|
||||
// App must have been deployed first
|
||||
try {
|
||||
const db = new CouchDB(productionAppId, { skip_setup: true })
|
||||
const info = await db.info()
|
||||
if (info.error) throw info.error
|
||||
} catch (err) {
|
||||
return ctx.throw(400, "App has not yet been deployed")
|
||||
}
|
||||
|
||||
try {
|
||||
const replication = new Replication({
|
||||
source: productionAppId,
|
||||
target: appId,
|
||||
})
|
||||
|
||||
await replication.rollback()
|
||||
// update appID in reverted app to be dev version again
|
||||
const db = new CouchDB(appId)
|
||||
const appDoc = await db.get(DocumentTypes.APP_METADATA)
|
||||
appDoc.appId = appId
|
||||
appDoc.instance._id = appId
|
||||
await db.put(appDoc)
|
||||
ctx.body = {
|
||||
message: "Reverted changes successfully.",
|
||||
}
|
||||
} catch (err) {
|
||||
ctx.throw(400, `Unable to revert. ${err}`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ const env = require("../../../environment")
|
|||
const { objectStoreUrl, clientLibraryPath } = require("../../../utilities")
|
||||
const { upload } = require("../../../utilities/fileSystem")
|
||||
const { attachmentsRelativeURL } = require("../../../utilities")
|
||||
const { DocumentTypes } = require("../../../db/utils")
|
||||
|
||||
async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
||||
const response = await upload({
|
||||
|
@ -85,7 +86,7 @@ exports.serveApp = async function (ctx) {
|
|||
}
|
||||
const App = require("./templates/BudibaseApp.svelte").default
|
||||
const db = new CouchDB(appId, { skip_setup: true })
|
||||
const appInfo = await db.get(appId)
|
||||
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
||||
|
||||
const { head, html, css } = App.render({
|
||||
title: appInfo.name,
|
||||
|
@ -125,7 +126,7 @@ exports.serveComponentLibrary = async function (ctx) {
|
|||
return send(ctx, "/index.js", { root: componentLibraryPath })
|
||||
}
|
||||
const db = new CouchDB(appId)
|
||||
const appInfo = await db.get(appId)
|
||||
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
||||
|
||||
let componentLib = "componentlibrary"
|
||||
if (appInfo && appInfo.version) {
|
||||
|
|
|
@ -13,6 +13,8 @@ if (env.isDev() || env.isTest()) {
|
|||
.delete("/api/admin/:devPath(.*)", controller.redirectDelete)
|
||||
}
|
||||
|
||||
router.delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
|
||||
router
|
||||
.delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
|
||||
.post("/api/dev/:appId/revert", authorized(BUILDER), controller.revert)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -21,7 +21,7 @@ exports.clearAllApps = async () => {
|
|||
return
|
||||
}
|
||||
for (let app of apps) {
|
||||
const appId = app._id
|
||||
const { appId } = app
|
||||
await appController.delete(new Request(null, { appId }))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ const AppStatus = {
|
|||
const DocumentTypes = {
|
||||
APP: CoreDocTypes.APP,
|
||||
APP_DEV: CoreDocTypes.APP_DEV,
|
||||
APP_METADATA: CoreDocTypes.APP_METADATA,
|
||||
ROLE: CoreDocTypes.ROLE,
|
||||
TABLE: "ta",
|
||||
ROW: "ro",
|
||||
|
|
|
@ -89,7 +89,7 @@ class TestConfiguration {
|
|||
if (this.server) {
|
||||
this.server.close()
|
||||
}
|
||||
cleanup(this.allApps.map(app => app._id))
|
||||
cleanup(this.allApps.map(app => app.appId))
|
||||
}
|
||||
|
||||
defaultHeaders() {
|
||||
|
@ -141,7 +141,7 @@ class TestConfiguration {
|
|||
|
||||
async createApp(appName) {
|
||||
this.app = await this._req({ name: appName }, null, controllers.app.create)
|
||||
this.appId = this.app._id
|
||||
this.appId = this.app.appId
|
||||
this.allApps.push(this.app)
|
||||
return this.app
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
const env = require("../environment")
|
||||
const { APP_PREFIX } = require("../db/utils")
|
||||
const CouchDB = require("../db")
|
||||
const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants")
|
||||
const { getAllApps } = require("@budibase/auth/db")
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const fetch = require("node-fetch")
|
||||
const { DocumentTypes } = require("@budibase/auth").db
|
||||
const CouchDB = require("../../db")
|
||||
const env = require("../../environment")
|
||||
|
||||
|
@ -14,7 +15,9 @@ exports.getApps = async ctx => {
|
|||
allDbs = await CouchDB.allDbs()
|
||||
}
|
||||
const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX))
|
||||
const appPromises = appDbNames.map(db => new CouchDB(db).get(db))
|
||||
const appPromises = appDbNames.map(db =>
|
||||
new CouchDB(db).get(DocumentTypes.APP_METADATA)
|
||||
)
|
||||
|
||||
const apps = await Promise.allSettled(appPromises)
|
||||
const body = {}
|
||||
|
@ -26,7 +29,7 @@ exports.getApps = async ctx => {
|
|||
let url = app.url || encodeURI(`${app.name}`)
|
||||
url = `/${url.replace(URL_REGEX_SLASH, "")}`
|
||||
body[url] = {
|
||||
appId: app._id,
|
||||
appId: app.instance._id,
|
||||
name: app.name,
|
||||
url,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue