Browse Source

Adding sync user endpoint to server which can be used by the worker.

master
Michael Drury 5 years ago
parent
commit
8bf2e7278e
  1. 11
      packages/auth/src/db/utils.js
  2. 85
      packages/server/src/api/controllers/user.js
  3. 5
      packages/server/src/api/routes/user.js
  4. 8
      packages/server/src/utilities/global.js
  5. 6
      packages/server/src/utilities/index.js

11
packages/auth/src/db/utils.js

@ -152,6 +152,17 @@ exports.getDeployedAppID = appId => {
return appId return appId
} }
/**
* Convert a deployed app ID to a development app ID.
*/
exports.getDevelopmentAppID = appId => {
if (!appId.startsWith(exports.APP_DEV_PREFIX)) {
const id = appId.split(exports.APP_PREFIX)[1]
return `${exports.APP_DEV_PREFIX}${id}`
}
return appId
}
exports.getCouchUrl = () => { exports.getCouchUrl = () => {
if (!env.COUCH_DB_URL) return if (!env.COUCH_DB_URL) return

85
packages/server/src/api/controllers/user.js

@ -4,12 +4,14 @@ const {
getUserMetadataParams, getUserMetadataParams,
} = require("../../db/utils") } = require("../../db/utils")
const { InternalTables } = require("../../db/utils") const { InternalTables } = require("../../db/utils")
const { getGlobalUsers } = require("../../utilities/global") const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global")
const { getFullUser } = require("../../utilities/users") const { getFullUser } = require("../../utilities/users")
const { isEqual } = require("lodash") const { isEqual } = require("lodash")
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
const { getDevelopmentAppID } = require("@budibase/auth/db")
const { doesDatabaseExist } = require("../../utilities")
exports.rawMetadata = async db => { async function rawMetadata(db) {
return ( return (
await db.allDocs( await db.allDocs(
getUserMetadataParams(null, { getUserMetadataParams(null, {
@ -19,45 +21,80 @@ exports.rawMetadata = async db => {
).rows.map(row => row.doc) ).rows.map(row => row.doc)
} }
async function combineMetadataAndUser(user, metadata) {
// skip users with no access
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
return null
}
delete user._rev
const metadataId = generateUserMetadataID(user._id)
const newDoc = {
...user,
_id: metadataId,
tableId: InternalTables.USER_METADATA,
}
const found = Array.isArray(metadata)
? metadata.find(doc => doc._id === metadataId)
: metadata
// copy rev over for the purposes of equality check
if (found) {
newDoc._rev = found._rev
}
if (found == null || !isEqual(newDoc, found)) {
return {
...found,
...newDoc,
}
}
return null
}
exports.syncGlobalUsers = async appId => { exports.syncGlobalUsers = async appId => {
// sync user metadata // sync user metadata
const db = new CouchDB(appId) const db = new CouchDB(appId)
const [users, metadata] = await Promise.all([ const [users, metadata] = await Promise.all([
getGlobalUsers(appId), getGlobalUsers(appId),
exports.rawMetadata(db), rawMetadata(db),
]) ])
const toWrite = [] const toWrite = []
for (let user of users) { for (let user of users) {
// skip users with no access const combined = await combineMetadataAndUser(user, metadata)
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) { if (combined) {
continue toWrite.push(combined)
} }
delete user._rev }
const metadataId = generateUserMetadataID(user._id) await db.bulkDocs(toWrite)
const newDoc = { }
...user,
_id: metadataId, exports.syncUser = async function (ctx) {
tableId: InternalTables.USER_METADATA, const user = await getRawGlobalUser(ctx.params.id)
} const roles = user.roles
const found = metadata.find(doc => doc._id === metadataId) delete user.roles
// copy rev over for the purposes of equality check for (let [prodAppId, roleId] of Object.entries(roles)) {
if (found) { if (roleId === BUILTIN_ROLE_IDS.PUBLIC) {
newDoc._rev = found._rev continue
} }
if (found == null || !isEqual(newDoc, found)) { const devAppId = getDevelopmentAppID(prodAppId)
toWrite.push({ for (let appId of [prodAppId, devAppId]) {
...found, if (!(await doesDatabaseExist(appId))) {
...newDoc, continue
}) }
const db = new CouchDB(appId)
const userId = generateUserMetadataID(user._id)
const metadata = await db.get(userId)
const combined = combineMetadataAndUser(user, metadata)
await db.put(combined)
} }
} }
await db.bulkDocs(toWrite) ctx.body = {
message: "User synced.",
}
} }
exports.fetchMetadata = async function (ctx) { exports.fetchMetadata = async function (ctx) {
const database = new CouchDB(ctx.appId) const database = new CouchDB(ctx.appId)
const global = await getGlobalUsers(ctx.appId) const global = await getGlobalUsers(ctx.appId)
const metadata = await exports.rawMetadata(database) const metadata = await rawMetadata(database)
const users = [] const users = []
for (let user of global) { for (let user of global) {
// find the metadata that matches up to the global ID // find the metadata that matches up to the global ID

5
packages/server/src/api/routes/user.js

@ -34,5 +34,10 @@ router
authorized(PermissionTypes.USER, PermissionLevels.WRITE), authorized(PermissionTypes.USER, PermissionLevels.WRITE),
controller.destroyMetadata controller.destroyMetadata
) )
.post(
"/api/users/sync/:id",
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
controller.syncUser
)
module.exports = router module.exports = router

8
packages/server/src/utilities/global.js

@ -46,9 +46,13 @@ exports.getCachedSelf = async (ctx, appId) => {
return processUser(appId, user) return processUser(appId, user)
} }
exports.getGlobalUser = async (appId, userId) => { exports.getRawGlobalUser = async userId => {
const db = getGlobalDB() const db = getGlobalDB()
let user = await db.get(getGlobalIDFromUserMetadataID(userId)) return db.get(getGlobalIDFromUserMetadataID(userId))
}
exports.getGlobalUser = async (appId, userId) => {
let user = await exports.getRawGlobalUser(userId)
return processUser(appId, user) return processUser(appId, user)
} }

6
packages/server/src/utilities/index.js

@ -134,3 +134,9 @@ exports.stringToReadStream = string => {
}, },
}) })
} }
exports.doesDatabaseExist = async dbName => {
const db = new CouchDB(dbName, { skip_setup: true })
const info = await db.info()
return info && !info.error
}

Loading…
Cancel
Save