2021-09-28 07:12:41 +13:00
|
|
|
const env = require("../../environment")
|
2022-09-20 21:20:36 +12:00
|
|
|
const { getAllApps, getGlobalDBName } = require("@budibase/backend-core/db")
|
2022-10-13 07:15:28 +13:00
|
|
|
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
2022-10-12 06:21:58 +13:00
|
|
|
const { streamFile } = require("../../utilities/fileSystem")
|
2022-10-13 07:15:28 +13:00
|
|
|
const { stringToReadStream } = require("../../utilities")
|
2022-10-14 03:46:53 +13:00
|
|
|
const { getDocParams, DocumentType, isDevAppID } = require("../../db/utils")
|
2022-10-13 07:15:28 +13:00
|
|
|
const { create } = require("./application")
|
|
|
|
const { join } = require("path")
|
2022-10-07 07:10:45 +13:00
|
|
|
const sdk = require("../../sdk")
|
2021-09-28 07:12:41 +13:00
|
|
|
|
2022-10-13 07:15:28 +13:00
|
|
|
async function createApp(appName, appDirectory) {
|
|
|
|
const ctx = {
|
|
|
|
request: {
|
|
|
|
body: {
|
|
|
|
useTemplate: true,
|
|
|
|
name: appName,
|
|
|
|
},
|
|
|
|
files: {
|
|
|
|
templateFile: {
|
|
|
|
path: appDirectory,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
return create(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getAllDocType(db, docType) {
|
|
|
|
const response = await db.allDocs(
|
|
|
|
getDocParams(docType, null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
return response.rows.map(row => row.doc)
|
|
|
|
}
|
|
|
|
|
2021-09-28 07:12:41 +13:00
|
|
|
exports.exportApps = async ctx => {
|
2022-10-24 22:04:14 +13:00
|
|
|
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
|
|
|
|
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
|
|
|
|
}
|
2022-01-28 07:18:31 +13:00
|
|
|
const apps = await getAllApps({ all: true })
|
2022-10-13 05:02:23 +13:00
|
|
|
const globalDBString = await sdk.backups.exportDB(getGlobalDBName(), {
|
2022-08-12 00:50:05 +12:00
|
|
|
filter: doc => !doc._id.startsWith(DocumentType.USER),
|
2021-10-09 06:21:40 +13:00
|
|
|
})
|
2022-10-12 06:21:58 +13:00
|
|
|
// only export the dev apps as they will be the latest, the user can republish the apps
|
|
|
|
// in their self-hosted environment
|
2022-10-13 07:15:28 +13:00
|
|
|
let appMetadata = apps
|
|
|
|
.filter(app => isDevAppID(app.appId || app._id))
|
|
|
|
.map(app => ({ appId: app.appId || app._id, name: app.name }))
|
|
|
|
const tmpPath = await sdk.backups.exportMultipleApps(
|
|
|
|
appMetadata,
|
|
|
|
globalDBString
|
|
|
|
)
|
2022-10-12 06:21:58 +13:00
|
|
|
const filename = `cloud-export-${new Date().getTime()}.tar.gz`
|
|
|
|
ctx.attachment(filename)
|
|
|
|
ctx.body = streamFile(tmpPath)
|
2021-09-30 05:43:16 +13:00
|
|
|
}
|
|
|
|
|
2021-10-09 06:21:40 +13:00
|
|
|
async function hasBeenImported() {
|
|
|
|
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
|
|
|
return true
|
|
|
|
}
|
2022-01-28 07:18:31 +13:00
|
|
|
const apps = await getAllApps({ all: true })
|
2021-10-09 06:21:40 +13:00
|
|
|
return apps.length !== 0
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.hasBeenImported = async ctx => {
|
|
|
|
ctx.body = {
|
|
|
|
imported: await hasBeenImported(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-28 07:12:41 +13:00
|
|
|
exports.importApps = async ctx => {
|
|
|
|
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
|
|
|
ctx.throw(400, "Importing only allowed in self hosted environments.")
|
|
|
|
}
|
2021-10-09 06:21:40 +13:00
|
|
|
const beenImported = await hasBeenImported()
|
|
|
|
if (beenImported || !ctx.request.files || !ctx.request.files.importFile) {
|
2021-09-28 07:12:41 +13:00
|
|
|
ctx.throw(
|
|
|
|
400,
|
|
|
|
"Import file is required and environment must be fresh to import apps."
|
|
|
|
)
|
|
|
|
}
|
2022-10-13 07:15:28 +13:00
|
|
|
if (ctx.request.files.importFile.type !== "application/gzip") {
|
|
|
|
ctx.throw(400, "Import file must be a gzipped tarball.")
|
|
|
|
}
|
|
|
|
|
|
|
|
// initially get all the app databases out of the tarball
|
2022-10-14 03:46:53 +13:00
|
|
|
const tmpPath = sdk.backups.untarFile(ctx.request.files.importFile)
|
2022-10-13 07:15:28 +13:00
|
|
|
const globalDbImport = sdk.backups.getGlobalDBFile(tmpPath)
|
2022-10-14 03:46:53 +13:00
|
|
|
const appNames = sdk.backups.getListOfAppsInMulti(tmpPath)
|
2022-10-13 07:15:28 +13:00
|
|
|
|
|
|
|
const globalDb = getGlobalDB()
|
|
|
|
// load the global db first
|
|
|
|
await globalDb.load(stringToReadStream(globalDbImport))
|
|
|
|
for (let appName of appNames) {
|
2022-10-14 03:46:53 +13:00
|
|
|
await createApp(appName, join(tmpPath, appName))
|
2022-10-13 07:15:28 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
// if there are any users make sure to remove them
|
|
|
|
let users = await getAllDocType(globalDb, DocumentType.USER)
|
|
|
|
let userDeletionPromises = []
|
|
|
|
for (let user of users) {
|
|
|
|
userDeletionPromises.push(globalDb.remove(user._id, user._rev))
|
|
|
|
}
|
|
|
|
if (userDeletionPromises.length > 0) {
|
|
|
|
await Promise.all(userDeletionPromises)
|
|
|
|
}
|
2021-10-09 06:21:40 +13:00
|
|
|
|
2022-10-13 07:15:28 +13:00
|
|
|
await globalDb.bulkDocs(users)
|
2021-09-28 07:12:41 +13:00
|
|
|
ctx.body = {
|
|
|
|
message: "Apps successfully imported.",
|
|
|
|
}
|
|
|
|
}
|