[FEAT] Add user management endpoints to list all users via developer API (#1708)

* add user management endpoints to list all users via developer api

* rename users to user management in swagger docs

* linting

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
Sean Hatfield 2024-06-17 16:24:41 -07:00 committed by GitHub
parent a598c8e04c
commit 1c473621aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 178 additions and 53 deletions

View File

@ -4,6 +4,7 @@ const { apiAuthEndpoints } = require("./auth");
const { apiDocumentEndpoints } = require("./document");
const { apiSystemEndpoints } = require("./system");
const { apiWorkspaceEndpoints } = require("./workspace");
const { apiUserManagementEndpoints } = require("./userManagement");
// All endpoints must be documented and pass through the validApiKey Middleware.
// How to JSDoc an endpoint
@ -16,6 +17,7 @@ function developerEndpoints(app, router) {
apiSystemEndpoints(router);
apiWorkspaceEndpoints(router);
apiDocumentEndpoints(router);
apiUserManagementEndpoints(router);
}
module.exports = { developerEndpoints };

View File

@ -0,0 +1,64 @@
const { User } = require("../../../models/user");
const { multiUserMode } = require("../../../utils/http");
const { validApiKey } = require("../../../utils/middleware/validApiKey");
function apiUserManagementEndpoints(app) {
if (!app) return;
app.get("/v1/users", [validApiKey], async (request, response) => {
/*
#swagger.tags = ['User Management']
#swagger.description = 'List all users'
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
type: 'object',
example: {
users: [
{
"id": 1,
"username": "john_doe",
"role": "admin"
},
{
"id": 2,
"username": "jane_smith",
"role": "default"
}
]
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
}
}
#swagger.responses[401] = {
description: "Instance is not in Multi-User mode. Permission denied.",
}
*/
try {
if (!multiUserMode(response))
return response
.status(401)
.send("Instance is not in Multi-User mode. Permission denied.");
const users = await User.where();
const filteredUsers = users.map((user) => ({
id: user.id,
username: user.username,
role: user.role,
}));
response.status(200).json({ users: filteredUsers });
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
});
}
module.exports = { apiUserManagementEndpoints };

View File

@ -1,73 +1,76 @@
const swaggerAutogen = require('swagger-autogen')({ openapi: '3.0.0' });
const fs = require('fs')
const path = require('path')
const swaggerAutogen = require("swagger-autogen")({ openapi: "3.0.0" });
const fs = require("fs");
const path = require("path");
const doc = {
info: {
version: '1.0.0',
title: 'AnythingLLM Developer API',
description: 'API endpoints that enable programmatic reading, writing, and updating of your AnythingLLM instance. UI supplied by Swagger.io.',
version: "1.0.0",
title: "AnythingLLM Developer API",
description:
"API endpoints that enable programmatic reading, writing, and updating of your AnythingLLM instance. UI supplied by Swagger.io.",
},
// Swagger-autogen does not allow us to use relative paths as these will resolve to
// http:///api in the openapi.json file, so we need to monkey-patch this post-generation.
host: '/api',
schemes: ['http'],
host: "/api",
schemes: ["http"],
securityDefinitions: {
BearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT'
}
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
security: [
{ BearerAuth: [] }
],
security: [{ BearerAuth: [] }],
definitions: {
InvalidAPIKey: {
message: 'Invalid API Key',
message: "Invalid API Key",
},
}
},
};
const outputFile = path.resolve(__dirname, './openapi.json');
const outputFile = path.resolve(__dirname, "./openapi.json");
const endpointsFiles = [
'../endpoints/api/auth/index.js',
'../endpoints/api/admin/index.js',
'../endpoints/api/document/index.js',
'../endpoints/api/workspace/index.js',
'../endpoints/api/system/index.js',
"../endpoints/api/auth/index.js",
"../endpoints/api/admin/index.js",
"../endpoints/api/document/index.js",
"../endpoints/api/workspace/index.js",
"../endpoints/api/system/index.js",
"../endpoints/api/userManagement/index.js",
];
swaggerAutogen(outputFile, endpointsFiles, doc)
.then(({ data }) => {
// Remove Authorization parameters from arguments.
for (const path of Object.keys(data.paths)) {
if (data.paths[path].hasOwnProperty('get')) {
let parameters = data.paths[path].get?.parameters || [];
parameters = parameters.filter((arg) => arg.name !== 'Authorization');
data.paths[path].get.parameters = parameters;
}
if (data.paths[path].hasOwnProperty('post')) {
let parameters = data.paths[path].post?.parameters || [];
parameters = parameters.filter((arg) => arg.name !== 'Authorization');
data.paths[path].post.parameters = parameters;
}
if (data.paths[path].hasOwnProperty('delete')) {
let parameters = data.paths[path].delete?.parameters || [];
parameters = parameters.filter((arg) => arg.name !== 'Authorization');
data.paths[path].delete.parameters = parameters;
}
swaggerAutogen(outputFile, endpointsFiles, doc).then(({ data }) => {
// Remove Authorization parameters from arguments.
for (const path of Object.keys(data.paths)) {
if (data.paths[path].hasOwnProperty("get")) {
let parameters = data.paths[path].get?.parameters || [];
parameters = parameters.filter((arg) => arg.name !== "Authorization");
data.paths[path].get.parameters = parameters;
}
const openApiSpec = {
...data,
servers: [{
url: "/api"
}]
if (data.paths[path].hasOwnProperty("post")) {
let parameters = data.paths[path].post?.parameters || [];
parameters = parameters.filter((arg) => arg.name !== "Authorization");
data.paths[path].post.parameters = parameters;
}
fs.writeFileSync(outputFile, JSON.stringify(openApiSpec, null, 2), { encoding: 'utf-8', flag: 'w' });
console.log(`Swagger-autogen: \x1b[32mPatched servers.url ✔\x1b[0m`)
})
if (data.paths[path].hasOwnProperty("delete")) {
let parameters = data.paths[path].delete?.parameters || [];
parameters = parameters.filter((arg) => arg.name !== "Authorization");
data.paths[path].delete.parameters = parameters;
}
}
const openApiSpec = {
...data,
servers: [
{
url: "/api",
},
],
};
fs.writeFileSync(outputFile, JSON.stringify(openApiSpec, null, 2), {
encoding: "utf-8",
flag: "w",
});
console.log(`Swagger-autogen: \x1b[32mPatched servers.url ✔\x1b[0m`);
});

View File

@ -2370,6 +2370,62 @@
}
}
}
},
"/v1/users": {
"get": {
"tags": [
"User Management"
],
"description": "List all users",
"parameters": [],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"example": {
"users": [
{
"id": 1,
"username": "john_doe",
"role": "admin"
},
{
"id": 2,
"username": "jane_smith",
"role": "default"
}
]
}
}
}
}
},
"401": {
"description": "Instance is not in Multi-User mode. Permission denied."
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidAPIKey"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/InvalidAPIKey"
}
}
}
},
"500": {
"description": "Internal Server Error"
}
}
}
}
},
"components": {