diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index a6250b0791..3fbf234db6 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -169,7 +169,8 @@ exports.getAllApps = async ({ CouchDB, dev, all } = {}) => { dbName.startsWith(exports.APP_PREFIX) ) const appPromises = appDbNames.map(db => - new CouchDB(db).get(DocumentTypes.APP_METADATA) + // skip setup otherwise databases could be re-created + new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA) ) if (appPromises.length === 0) { return [] @@ -194,6 +195,21 @@ exports.getAllApps = async ({ CouchDB, dev, all } = {}) => { } } +exports.dbExists = async (CouchDB, dbName) => { + let exists = false + try { + const db = CouchDB(dbName, {skip_setup: true}) + // check if database exists + const info = await db.info() + if (info && !info.error) { + exists = true + } + } catch (err) { + exists = false + } + return exists +} + /** * Generates a new configuration ID. * @returns {string} The new configuration ID which the config doc can be stored under. diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 36dc399996..4b203eec95 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -105,12 +105,6 @@ async function createInstance(template) { if (!ok) { throw "Error loading database dump from template." } - try { - const response = await db.get(DocumentTypes.APP_METADATA) - _rev = response._rev - } catch (err) { - _rev = null - } } else { // create the users table await db.put(USERS_TABLE_SCHEMA) @@ -121,7 +115,7 @@ async function createInstance(template) { await createRoutingView(appId) await createAllSearchIndex(appId) - return { _id: appId, _rev } + return { _id: appId } } exports.fetch = async function (ctx) { @@ -188,11 +182,21 @@ exports.create = async function (ctx) { instanceConfig.file = ctx.request.files.templateFile } const instance = await createInstance(instanceConfig) + const appId = instance._id const url = await getAppUrlIfNotInUse(ctx) - const appId = instance._id + const db = new CouchDB(appId) + let _rev + try { + // if template there will be an existing doc + const existing = await db.get(DocumentTypes.APP_METADATA) + _rev = existing._rev + } catch (err) { + // nothing to do + } const newApplication = { _id: DocumentTypes.APP_METADATA, + _rev, appId: instance._id, type: "app", version: packageJson.version, @@ -204,11 +208,7 @@ exports.create = async function (ctx) { updatedAt: new Date().toISOString(), createdAt: new Date().toISOString(), } - if (instance._rev) { - newApplication._rev = instance._rev - } - const instanceDb = new CouchDB(appId) - await instanceDb.put(newApplication) + await db.put(newApplication, { force: true }) await createEmptyAppPackage(ctx, newApplication) /* istanbul ignore next */ diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 270cc9b312..be57f8b70f 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -59,6 +59,7 @@ exports.StaticDatabases = { exports.APP_PREFIX = APP_PREFIX exports.APP_DEV_PREFIX = APP_DEV_PREFIX exports.USER_METDATA_PREFIX = `${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}` +exports.LINK_USER_METADATA_PREFIX = `${DocumentTypes.LINK}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}` exports.ViewNames = ViewNames exports.InternalTables = InternalTables exports.DocumentTypes = DocumentTypes diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index e47c9894fa..7d4f948731 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -1,9 +1,11 @@ -const { getAppId, setCookie, getCookie } = require("@budibase/auth").utils +const { getAppId, setCookie, getCookie, clearCookie } = require("@budibase/auth").utils const { Cookies } = require("@budibase/auth").constants const { getRole } = require("@budibase/auth/roles") const { getGlobalSelf } = require("../utilities/workerRequests") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { generateUserMetadataID } = require("../db/utils") +const { dbExists } = require("@budibase/auth/db") +const CouchDB = require("../db") module.exports = async (ctx, next) => { // try to get the appID from the request @@ -13,6 +15,15 @@ module.exports = async (ctx, next) => { if (!appCookie && !requestAppId) { return next() } + // check the app exists referenced in cookie + if (appCookie) { + const appId = appCookie.appId + const exists = await dbExists(CouchDB, appId) + if (!exists) { + clearCookie(ctx, Cookies.CurrentApp) + return next() + } + } let updateCookie = false, appId, diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index 750414826a..c80b9ea08d 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -18,6 +18,7 @@ const download = require("download") const env = require("../../environment") const { homedir } = require("os") const fetch = require("node-fetch") +const { USER_METDATA_PREFIX, LINK_USER_METADATA_PREFIX } = require("../../db/utils") const DEFAULT_AUTOMATION_BUCKET = "https://prod-budi-automations.s3-eu-west-1.amazonaws.com" @@ -117,7 +118,10 @@ exports.performBackup = async (appId, backupName) => { const writeStream = fs.createWriteStream(path) // perform couch dump const instanceDb = new CouchDB(appId) - await instanceDb.dump(writeStream, {}) + await instanceDb.dump(writeStream, { + // filter out anything that has a user metadata structure in its ID + filter: doc => !(doc._id.includes(USER_METDATA_PREFIX) || doc.includes(LINK_USER_METADATA_PREFIX)) + }) // write the file to the object store await streamUpload( ObjectStoreBuckets.BACKUPS, diff --git a/packages/worker/src/api/controllers/app.js b/packages/worker/src/api/controllers/app.js index 75366c2365..9256b93a46 100644 --- a/packages/worker/src/api/controllers/app.js +++ b/packages/worker/src/api/controllers/app.js @@ -7,13 +7,8 @@ const APP_PREFIX = "app_" const URL_REGEX_SLASH = /\/|\\/g exports.getApps = async ctx => { - let allDbs // allDbs call of CouchDB is very inaccurate in production - if (env.COUCH_DB_URL) { - allDbs = await (await fetch(`${env.COUCH_DB_URL}/_all_dbs`)).json() - } else { - allDbs = await CouchDB.allDbs() - } + const allDbs = await CouchDB.allDbs() const appDbNames = allDbs.filter(dbName => dbName.startsWith(APP_PREFIX)) const appPromises = appDbNames.map(db => new CouchDB(db).get(DocumentTypes.APP_METADATA)