Adding the ability to get all apps, with the status attached.
This commit is contained in:
parent
91ed6679c4
commit
0bbd45b413
|
@ -34,6 +34,10 @@ exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
|||
exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR
|
||||
exports.SEPARATOR = SEPARATOR
|
||||
|
||||
function isDevApp(app) {
|
||||
return app.appId.startsWith(exports.APP_DEV_PREFIX)
|
||||
}
|
||||
|
||||
/**
|
||||
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
||||
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
|
||||
|
@ -160,7 +164,7 @@ exports.getDeployedAppID = appId => {
|
|||
* different users/companies apps as there is no security around it - all apps are returned.
|
||||
* @return {Promise<object[]>} returns the app information document stored in each app database.
|
||||
*/
|
||||
exports.getAllApps = async (devApps = false) => {
|
||||
exports.getAllApps = async ({ dev, all } = {}) => {
|
||||
const CouchDB = getCouch()
|
||||
let allDbs = await CouchDB.allDbs()
|
||||
const appDbNames = allDbs.filter(dbName =>
|
||||
|
@ -176,12 +180,19 @@ exports.getAllApps = async (devApps = false) => {
|
|||
const apps = response
|
||||
.filter(result => result.status === "fulfilled")
|
||||
.map(({ value }) => value)
|
||||
return apps.filter(app => {
|
||||
if (devApps) {
|
||||
return app.appId.startsWith(exports.APP_DEV_PREFIX)
|
||||
}
|
||||
return !app.appId.startsWith(exports.APP_DEV_PREFIX)
|
||||
})
|
||||
if (!all) {
|
||||
return apps.filter(app => {
|
||||
if (dev) {
|
||||
return isDevApp(app)
|
||||
}
|
||||
return !isDevApp(app)
|
||||
})
|
||||
} else {
|
||||
return apps.map(app => ({
|
||||
...app,
|
||||
status: isDevApp(app) ? "development" : "published"
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</script>
|
||||
|
||||
<h1
|
||||
style="{textAlign ? `text-align:${textAlign}` : ``}"
|
||||
style={textAlign ? `text-align:${textAlign}` : ``}
|
||||
class:noPadding
|
||||
class="spectrum-Heading spectrum-Heading--size{size}"
|
||||
>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
FAILURE: "FAILURE",
|
||||
}
|
||||
|
||||
const POLL_INTERVAL = 1000
|
||||
const POLL_INTERVAL = 10000
|
||||
|
||||
let loading = false
|
||||
let feedbackModal
|
||||
|
|
|
@ -117,13 +117,17 @@ async function createInstance(template) {
|
|||
}
|
||||
|
||||
exports.fetch = async function (ctx) {
|
||||
const isDev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||
const apps = await getAllApps(isDev)
|
||||
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
||||
const apps = await getAllApps({ dev, all })
|
||||
|
||||
// get the locks for all the dev apps
|
||||
if (isDev) {
|
||||
if (dev || all) {
|
||||
const locks = await getAllLocks()
|
||||
for (let app of apps) {
|
||||
if (app.status !== "development") {
|
||||
continue
|
||||
}
|
||||
const lock = locks.find(lock => lock.appId === app.appId)
|
||||
if (lock) {
|
||||
app.lockedBy = lock.user
|
||||
|
|
|
@ -5,9 +5,17 @@ const {
|
|||
} = require("../../db/utils")
|
||||
const { InternalTables } = require("../../db/utils")
|
||||
const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
|
||||
const { getGlobalUsers } = require("../../utilities/workerRequests")
|
||||
const { getGlobalUsers, addAppRoleToSelf } = require("../../utilities/workerRequests")
|
||||
const { getFullUser } = require("../../utilities/users")
|
||||
|
||||
function removeGlobalProps(user) {
|
||||
// make sure to always remove some of the global user props
|
||||
delete user.password
|
||||
delete user.roles
|
||||
delete user.builder
|
||||
return user
|
||||
}
|
||||
|
||||
exports.fetchMetadata = async function (ctx) {
|
||||
const database = new CouchDB(ctx.appId)
|
||||
const global = await getGlobalUsers(ctx, ctx.appId)
|
||||
|
@ -37,7 +45,8 @@ exports.updateSelfMetadata = async function (ctx) {
|
|||
// overwrite the ID with current users
|
||||
ctx.request.body._id = ctx.user._id
|
||||
if (ctx.user.builder && ctx.user.builder.global) {
|
||||
ctx.request.body.roleId = BUILTIN_ROLE_IDS.ADMIN
|
||||
// specific case, update self role in global user
|
||||
await addAppRoleToSelf(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN)
|
||||
}
|
||||
// make sure no stale rev
|
||||
delete ctx.request.body._rev
|
||||
|
@ -47,11 +56,7 @@ exports.updateSelfMetadata = async function (ctx) {
|
|||
exports.updateMetadata = async function (ctx) {
|
||||
const appId = ctx.appId
|
||||
const db = new CouchDB(appId)
|
||||
const user = ctx.request.body
|
||||
// make sure to always remove some of the global user props
|
||||
delete user.password
|
||||
delete user.roles
|
||||
delete user.builder
|
||||
const user = removeGlobalProps(ctx.request.body)
|
||||
const metadata = {
|
||||
tableId: InternalTables.USER_METADATA,
|
||||
_id: user._id,
|
||||
|
|
|
@ -117,9 +117,6 @@ describe("/users", () => {
|
|||
|
||||
describe("update", () => {
|
||||
beforeEach(() => {
|
||||
workerRequests.saveGlobalUser.mockImplementationOnce(() => ({
|
||||
_id: "us_test@test.com"
|
||||
}))
|
||||
})
|
||||
|
||||
it("should be able to update the user", async () => {
|
||||
|
@ -151,9 +148,6 @@ describe("/users", () => {
|
|||
describe("find", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks()
|
||||
workerRequests.saveGlobalUser.mockImplementationOnce(() => ({
|
||||
_id: "us_uuid1",
|
||||
}))
|
||||
workerRequests.getGlobalUsers.mockImplementationOnce(() => ({
|
||||
_id: "us_uuid1",
|
||||
roleId: BUILTIN_ROLE_IDS.POWER,
|
||||
|
|
|
@ -19,6 +19,7 @@ const StaticDatabases = {
|
|||
|
||||
const AppStatus = {
|
||||
DEV: "dev",
|
||||
ALL: "all",
|
||||
DEPLOYED: "PUBLISHED",
|
||||
}
|
||||
|
||||
|
|
|
@ -118,53 +118,38 @@ exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => {
|
|||
return users
|
||||
}
|
||||
|
||||
exports.saveGlobalUser = async (ctx, appId, body) => {
|
||||
const globalUser = body._id
|
||||
? await exports.getGlobalUsers(ctx, appId, body._id)
|
||||
: {}
|
||||
const preRoles = globalUser.roles || {}
|
||||
if (body.roleId) {
|
||||
preRoles[appId] = body.roleId
|
||||
exports.getGlobalSelf = async ctx => {
|
||||
const endpoint = `/api/admin/users/self`
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
||||
request(ctx, { method: "GET" })
|
||||
)
|
||||
const json = await response.json()
|
||||
if (json.status !== 200 && response.status !== 200) {
|
||||
ctx.throw(400, "Unable to get self globally.")
|
||||
}
|
||||
// make sure no dev app IDs in roles
|
||||
const roles = {}
|
||||
for (let [appId, roleId] of Object.entries(preRoles)) {
|
||||
roles[getDeployedAppID(appId)] = roleId
|
||||
}
|
||||
const endpoint = `/api/admin/users`
|
||||
return json
|
||||
}
|
||||
|
||||
exports.addAppRoleToSelf = async (ctx, appId, roleId) => {
|
||||
const self = await exports.getGlobalSelf(ctx)
|
||||
const endpoint = `/api/admin/users/self`
|
||||
const reqCfg = {
|
||||
method: "POST",
|
||||
body: {
|
||||
...globalUser,
|
||||
password: body.password || undefined,
|
||||
status: body.status,
|
||||
email: body.email,
|
||||
roles,
|
||||
builder: {
|
||||
global: true,
|
||||
},
|
||||
roles: {
|
||||
...self.roles,
|
||||
[appId]: roleId,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
checkSlashesInUrl(env.WORKER_URL + endpoint),
|
||||
request(ctx, reqCfg)
|
||||
)
|
||||
const json = await response.json()
|
||||
if (json.status !== 200 && response.status !== 200) {
|
||||
ctx.throw(400, "Unable to save global user.")
|
||||
}
|
||||
delete body.password
|
||||
delete body.roles
|
||||
delete body.builder
|
||||
// TODO: for now these have been left in as they are
|
||||
// TODO: pretty important to keeping relationships working
|
||||
// TODO: however if user metadata is changed this should be removed
|
||||
// delete body.email
|
||||
// delete body.roleId
|
||||
// delete body.status
|
||||
return {
|
||||
...body,
|
||||
_id: json._id,
|
||||
ctx.throw(400, "Unable to save self globally.")
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ const CouchDB = require("../../../db")
|
|||
|
||||
exports.fetch = async ctx => {
|
||||
// always use the dev apps as they'll be most up to date (true)
|
||||
const apps = await getAllApps(true)
|
||||
const apps = await getAllApps({ dev: true })
|
||||
const promises = []
|
||||
for (let app of apps) {
|
||||
// use dev app IDs
|
||||
|
|
|
@ -98,7 +98,7 @@ exports.destroy = async ctx => {
|
|||
|
||||
exports.getSelf = async ctx => {
|
||||
ctx.params = {
|
||||
id: ctx.user._id
|
||||
id: ctx.user._id,
|
||||
}
|
||||
// this will set the body
|
||||
await exports.find(ctx)
|
||||
|
@ -172,12 +172,16 @@ exports.invite = async ctx => {
|
|||
}
|
||||
|
||||
exports.inviteAccept = async ctx => {
|
||||
const { inviteCode } = ctx.request.body
|
||||
const { inviteCode, password, firstName, lastName } = ctx.request.body
|
||||
try {
|
||||
const email = await checkInviteCode(inviteCode)
|
||||
// redirect the request
|
||||
delete ctx.request.body.inviteCode
|
||||
ctx.request.body.email = email
|
||||
// only pass through certain props for accepting
|
||||
ctx.request.body = {
|
||||
firstName,
|
||||
lastName,
|
||||
password,
|
||||
email,
|
||||
}
|
||||
// this will flesh out the body response
|
||||
await exports.save(ctx)
|
||||
} catch (err) {
|
||||
|
|
|
@ -78,8 +78,13 @@ function buildConfigGetValidation() {
|
|||
}
|
||||
|
||||
router
|
||||
.post("/api/admin/configs", buildConfigSaveValidation(), controller.save)
|
||||
.delete("/api/admin/configs/:id", controller.destroy)
|
||||
.post(
|
||||
"/api/admin/configs",
|
||||
adminOnly,
|
||||
buildConfigSaveValidation(),
|
||||
controller.save
|
||||
)
|
||||
.delete("/api/admin/configs/:id", adminOnly, controller.destroy)
|
||||
.get("/api/admin/configs", controller.fetch)
|
||||
.get("/api/admin/configs/checklist", controller.configChecklist)
|
||||
.get(
|
||||
|
@ -90,6 +95,7 @@ router
|
|||
.get("/api/admin/configs/:type", buildConfigGetValidation(), controller.find)
|
||||
.post(
|
||||
"/api/admin/configs/upload/:type/:name",
|
||||
adminOnly,
|
||||
buildUploadValidation(),
|
||||
controller.upload
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../../controllers/admin/groups")
|
||||
const joiValidator = require("../../../middleware/joi-validator")
|
||||
const adminOnly = require("../../../middleware/adminOnly")
|
||||
const Joi = require("joi")
|
||||
|
||||
const router = Router()
|
||||
|
@ -24,9 +25,14 @@ function buildGroupSaveValidation() {
|
|||
}
|
||||
|
||||
router
|
||||
.post("/api/admin/groups", buildGroupSaveValidation(), controller.save)
|
||||
.post(
|
||||
"/api/admin/groups",
|
||||
adminOnly,
|
||||
buildGroupSaveValidation(),
|
||||
controller.save
|
||||
)
|
||||
.get("/api/admin/groups", controller.fetch)
|
||||
.delete("/api/admin/groups/:id", controller.destroy)
|
||||
.delete("/api/admin/groups/:id", adminOnly, controller.destroy)
|
||||
.get("/api/admin/groups/:id", controller.find)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../../controllers/admin/roles")
|
||||
const adminOnly = require("../../../middleware/adminOnly")
|
||||
|
||||
const router = Router()
|
||||
|
||||
router
|
||||
.get("/api/admin/roles", controller.fetch)
|
||||
.get("/api/admin/roles/:appId", controller.find)
|
||||
.get("/api/admin/roles", adminOnly, controller.fetch)
|
||||
.get("/api/admin/roles/:appId", adminOnly, controller.find)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -3,6 +3,7 @@ const controller = require("../../controllers/admin/templates")
|
|||
const joiValidator = require("../../../middleware/joi-validator")
|
||||
const Joi = require("joi")
|
||||
const { TemplatePurpose, TemplateTypes } = require("../../../constants")
|
||||
const adminOnly = require("../../../middleware/adminOnly")
|
||||
|
||||
const router = Router()
|
||||
|
||||
|
@ -21,11 +22,16 @@ function buildTemplateSaveValidation() {
|
|||
|
||||
router
|
||||
.get("/api/admin/template/definitions", controller.definitions)
|
||||
.post("/api/admin/template", buildTemplateSaveValidation(), controller.save)
|
||||
.post(
|
||||
"/api/admin/template",
|
||||
adminOnly,
|
||||
buildTemplateSaveValidation(),
|
||||
controller.save
|
||||
)
|
||||
.get("/api/admin/template", controller.fetch)
|
||||
.get("/api/admin/template/:type", controller.fetchByType)
|
||||
.get("/api/admin/template/:ownerId", controller.fetchByOwner)
|
||||
.get("/api/admin/template/:id", controller.find)
|
||||
.delete("/api/admin/template/:id/:rev", controller.destroy)
|
||||
.delete("/api/admin/template/:id/:rev", adminOnly, controller.destroy)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const Router = require("@koa/router")
|
||||
const controller = require("../../controllers/admin/users")
|
||||
const joiValidator = require("../../../middleware/joi-validator")
|
||||
const adminOnly = require("../../../middleware/adminOnly")
|
||||
const Joi = require("joi")
|
||||
|
||||
const router = Router()
|
||||
|
@ -14,12 +15,11 @@ function buildUserSaveValidation(isSelf = false) {
|
|||
builder: Joi.object({
|
||||
global: Joi.boolean().optional(),
|
||||
apps: Joi.array().optional(),
|
||||
}).unknown(true).optional(),
|
||||
// maps appId -> roleId for the user
|
||||
roles: Joi.object()
|
||||
.pattern(/.*/, Joi.string())
|
||||
.required()
|
||||
})
|
||||
.unknown(true)
|
||||
.optional(),
|
||||
// maps appId -> roleId for the user
|
||||
roles: Joi.object().pattern(/.*/, Joi.string()).required().unknown(true),
|
||||
}
|
||||
if (!isSelf) {
|
||||
schema = {
|
||||
|
@ -28,9 +28,7 @@ function buildUserSaveValidation(isSelf = false) {
|
|||
_rev: Joi.string(),
|
||||
}
|
||||
}
|
||||
return joiValidator.body(Joi.object(schema)
|
||||
.required()
|
||||
.unknown(true))
|
||||
return joiValidator.body(Joi.object(schema).required().unknown(true))
|
||||
}
|
||||
|
||||
function buildInviteValidation() {
|
||||
|
@ -48,24 +46,30 @@ function buildInviteAcceptValidation() {
|
|||
}).required().unknown(true))
|
||||
}
|
||||
|
||||
function buildUpdateSelfValidation() {
|
||||
// prettier-ignore
|
||||
return joiValidator.body(Joi.object({
|
||||
inviteCode: Joi.string().required(),
|
||||
password: Joi.string().required(),
|
||||
}).required().unknown(true))
|
||||
}
|
||||
|
||||
router
|
||||
.post("/api/admin/users", buildUserSaveValidation(), controller.save)
|
||||
.post(
|
||||
"/api/admin/users",
|
||||
adminOnly,
|
||||
buildUserSaveValidation(),
|
||||
controller.save
|
||||
)
|
||||
.get("/api/admin/users", controller.fetch)
|
||||
.post("/api/admin/users/init", controller.adminUser)
|
||||
.get("/api/admin/users/self", controller.getSelf)
|
||||
.post("/api/admin/users/self", buildUserSaveValidation(true), controller.updateSelf)
|
||||
.delete("/api/admin/users/:id", controller.destroy)
|
||||
.post(
|
||||
"/api/admin/users/self",
|
||||
buildUserSaveValidation(true),
|
||||
controller.updateSelf
|
||||
)
|
||||
.delete("/api/admin/users/:id", adminOnly, controller.destroy)
|
||||
.get("/api/admin/users/:id", controller.find)
|
||||
.get("/api/admin/roles/:appId")
|
||||
.post("/api/admin/users/invite", buildInviteValidation(), controller.invite)
|
||||
.post(
|
||||
"/api/admin/users/invite",
|
||||
adminOnly,
|
||||
buildInviteValidation(),
|
||||
controller.invite
|
||||
)
|
||||
.post(
|
||||
"/api/admin/users/invite/accept",
|
||||
buildInviteAcceptValidation(),
|
||||
|
|
|
@ -3,4 +3,4 @@ module.exports = async (ctx, next) => {
|
|||
ctx.throw(403, "Admin user only endpoint.")
|
||||
}
|
||||
return next()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue