Skip to content

Commit

Permalink
Add new workspace user management endpoint (#2842)
Browse files Browse the repository at this point in the history
deprecate old endpoint which users workspace ID vs slug
resolves #2838
  • Loading branch information
timothycarambat authored Dec 16, 2024
1 parent 827c3f4 commit ff02428
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 0 deletions.
128 changes: 128 additions & 0 deletions server/endpoints/api/admin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { SystemSettings } = require("../../../models/systemSettings");
const { User } = require("../../../models/user");
const { Workspace } = require("../../../models/workspace");
const { WorkspaceChats } = require("../../../models/workspaceChats");
const { WorkspaceUser } = require("../../../models/workspaceUsers");
const { canModifyAdmin } = require("../../../utils/helpers/admin");
const { multiUserMode, reqBody } = require("../../../utils/http");
const { validApiKey } = require("../../../utils/middleware/validApiKey");
Expand Down Expand Up @@ -420,6 +421,7 @@ function apiAdminEndpoints(app) {
}
}
);

app.get(
"/v1/admin/workspaces/:workspaceId/users",
[validApiKey],
Expand Down Expand Up @@ -474,12 +476,14 @@ function apiAdminEndpoints(app) {
}
}
);

app.post(
"/v1/admin/workspaces/:workspaceId/update-users",
[validApiKey],
async (request, response) => {
/*
#swagger.tags = ['Admin']
#swagger.deprecated = true
#swagger.parameters['workspaceId'] = {
in: 'path',
description: 'id of the workspace in the database.',
Expand Down Expand Up @@ -539,6 +543,130 @@ function apiAdminEndpoints(app) {
}
}
);

app.post(
"/v1/admin/workspaces/:workspaceSlug/manage-users",
[validApiKey],
async (request, response) => {
/*
#swagger.tags = ['Admin']
#swagger.parameters['workspaceSlug'] = {
in: 'path',
description: 'slug of the workspace in the database',
required: true,
type: 'string'
}
#swagger.description = 'Set workspace permissions to be accessible by the given user ids and admins. Methods are disabled until multi user mode is enabled via the UI.'
#swagger.requestBody = {
description: 'Array of user ids who will be given access to the target workspace. <code>reset</code> will remove all existing users from the workspace and only add the new users - default <code>false</code>.',
required: true,
content: {
"application/json": {
example: {
userIds: [1,2,4,12],
reset: false
}
}
}
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
type: 'object',
example: {
success: true,
error: null,
users: [
{"userId": 1, "username": "main-admin", "role": "admin"},
{"userId": 2, "username": "sample-sam", "role": "default"}
]
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
}
}
#swagger.responses[401] = {
description: "Instance is not in Multi-User mode. Method denied",
}
*/
try {
if (!multiUserMode(response)) {
response.sendStatus(401).end();
return;
}

const { workspaceSlug } = request.params;
const { userIds: _uids, reset = false } = reqBody(request);
const userIds = (
await User.where({ id: { in: _uids.map(Number) } })
).map((user) => user.id);
const workspace = await Workspace.get({ slug: String(workspaceSlug) });
const workspaceUsers = await Workspace.workspaceUsers(workspace.id);

if (!workspace) {
response
.status(404)
.json({
success: false,
error: `Workspace ${workspaceSlug} not found`,
users: workspaceUsers,
});
return;
}

if (userIds.length === 0) {
response
.status(404)
.json({
success: false,
error: `No valid user IDs provided.`,
users: workspaceUsers,
});
return;
}

// Reset all users in the workspace and add the new users as the only users in the workspace
if (reset) {
const { success, error } = await Workspace.updateUsers(
workspace.id,
userIds
);
return response
.status(200)
.json({
success,
error,
users: await Workspace.workspaceUsers(workspace.id),
});
}

// Add new users to the workspace if they are not already in the workspace
const existingUserIds = workspaceUsers.map((user) => user.userId);
const usersToAdd = userIds.filter(
(userId) => !existingUserIds.includes(userId)
);
if (usersToAdd.length > 0)
await WorkspaceUser.createManyUsers(usersToAdd, workspace.id);
response
.status(200)
.json({
success: true,
error: null,
users: await Workspace.workspaceUsers(workspace.id),
});
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);

app.post(
"/v1/admin/workspace-chats",
[validApiKey],
Expand Down
11 changes: 11 additions & 0 deletions server/models/workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ const Workspace = {
}
},

/**
* Get all users for a workspace.
* @param {number} workspaceId - The ID of the workspace to get users for.
* @returns {Promise<Array<{userId: number, username: string, role: string}>>} A promise that resolves to an array of user objects.
*/
workspaceUsers: async function (workspaceId) {
try {
const users = (
Expand Down Expand Up @@ -270,6 +275,12 @@ const Workspace = {
}
},

/**
* Update the users for a workspace. Will remove all existing users and replace them with the new list.
* @param {number} workspaceId - The ID of the workspace to update.
* @param {number[]} userIds - An array of user IDs to add to the workspace.
* @returns {Promise<{success: boolean, error: string | null}>} A promise that resolves to an object containing the success status and an error message if applicable.
*/
updateUsers: async function (workspaceId, userIds = []) {
try {
await WorkspaceUser.delete({ workspace_id: Number(workspaceId) });
Expand Down
6 changes: 6 additions & 0 deletions server/models/workspaceUsers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ const WorkspaceUser = {
return;
},

/**
* Create many workspace users.
* @param {Array<number>} userIds - An array of user IDs to create workspace users for.
* @param {number} workspaceId - The ID of the workspace to create workspace users for.
* @returns {Promise<void>} A promise that resolves when the workspace users are created.
*/
createManyUsers: async function (userIds = [], workspaceId) {
if (userIds.length === 0) return;
try {
Expand Down
89 changes: 89 additions & 0 deletions server/swagger/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,95 @@
}
}
}
},
"deprecated": true
}
},
"/v1/admin/workspaces/{workspaceSlug}/manage-users": {
"post": {
"tags": [
"Admin"
],
"description": "Set workspace permissions to be accessible by the given user ids and admins. Methods are disabled until multi user mode is enabled via the UI.",
"parameters": [
{
"name": "workspaceSlug",
"in": "path",
"required": true,
"schema": {
"type": "string"
},
"description": "slug of the workspace in the database"
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"example": {
"success": true,
"error": null,
"users": [
{
"userId": 1,
"username": "main-admin",
"role": "admin"
},
{
"userId": 2,
"username": "sample-sam",
"role": "default"
}
]
}
}
}
}
},
"401": {
"description": "Instance is not in Multi-User mode. Method denied"
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidAPIKey"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/InvalidAPIKey"
}
}
}
},
"404": {
"description": "Not Found"
},
"500": {
"description": "Internal Server Error"
}
},
"requestBody": {
"description": "Array of user ids who will be given access to the target workspace. <code>reset</code> will remove all existing users from the workspace and only add the new users - default <code>false</code>.",
"required": true,
"content": {
"application/json": {
"example": {
"userIds": [
1,
2,
4,
12
],
"reset": false
}
}
}
}
}
},
Expand Down

0 comments on commit ff02428

Please sign in to comment.