Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into render

This commit is contained in:
timothycarambat 2024-05-22 13:43:09 -05:00
commit c6ad94d81a
8 changed files with 137 additions and 83 deletions

View File

@ -124,7 +124,7 @@ function isWithin(outer, inner) {
function normalizePath(filepath = "") { function normalizePath(filepath = "") {
const result = path const result = path
.normalize(filepath.trim()) .normalize(filepath.replace(/\s/g, "-").trim())
.replace(/^(\.\.(\/|\\|$))+/, "") .replace(/^(\.\.(\/|\\|$))+/, "")
.trim(); .trim();
if (["..", ".", "/"].includes(result)) throw new Error("Invalid path."); if (["..", ".", "/"].includes(result)) throw new Error("Invalid path.");

View File

@ -108,10 +108,13 @@ export default function Footer() {
rel="noreferrer" rel="noreferrer"
className="transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border" className="transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border-transparent border"
> >
{React.createElement(ICON_COMPONENTS[item.icon], { {React.createElement(
ICON_COMPONENTS?.[item.icon] ?? ICON_COMPONENTS.Info,
{
weight: "fill", weight: "fill",
className: "h-5 w-5", className: "h-5 w-5",
})} }
)}
</a> </a>
))} ))}
{!isMobile && <SettingsButton />} {!isMobile && <SettingsButton />}

View File

@ -33,10 +33,7 @@ function adminEndpoints(app) {
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
async (_request, response) => { async (_request, response) => {
try { try {
const users = (await User.where()).map((user) => { const users = await User.where();
const { password, ...rest } = user;
return rest;
});
response.status(200).json({ users }); response.status(200).json({ users });
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -73,10 +73,7 @@ function apiAdminEndpoints(app) {
return; return;
} }
const users = (await User.where()).map((user) => { const users = await User.where();
const { password, ...rest } = user;
return rest;
});
response.status(200).json({ users }); response.status(200).json({ users });
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -115,7 +115,7 @@ function systemEndpoints(app) {
if (await SystemSettings.isMultiUserMode()) { if (await SystemSettings.isMultiUserMode()) {
const { username, password } = reqBody(request); const { username, password } = reqBody(request);
const existingUser = await User.get({ username: String(username) }); const existingUser = await User._get({ username: String(username) });
if (!existingUser) { if (!existingUser) {
await EventLogs.logEvent( await EventLogs.logEvent(
@ -193,7 +193,7 @@ function systemEndpoints(app) {
// Return recovery codes to frontend // Return recovery codes to frontend
response.status(200).json({ response.status(200).json({
valid: true, valid: true,
user: existingUser, user: User.filterFields(existingUser),
token: makeJWT( token: makeJWT(
{ id: existingUser.id, username: existingUser.username }, { id: existingUser.id, username: existingUser.username },
"30d" "30d"
@ -206,7 +206,7 @@ function systemEndpoints(app) {
response.status(200).json({ response.status(200).json({
valid: true, valid: true,
user: existingUser, user: User.filterFields(existingUser),
token: makeJWT( token: makeJWT(
{ id: existingUser.id, username: existingUser.username }, { id: existingUser.id, username: existingUser.username },
"30d" "30d"
@ -1029,7 +1029,7 @@ function systemEndpoints(app) {
const updates = {}; const updates = {};
if (username) { if (username) {
updates.username = String(username); updates.username = User.validations.username(String(username));
} }
if (password) { if (password) {
updates.password = String(password); updates.password = String(password);

View File

@ -111,6 +111,7 @@ function workspaceEndpoints(app) {
handleFileUpload, handleFileUpload,
], ],
async function (request, response) { async function (request, response) {
try {
const Collector = new CollectorApi(); const Collector = new CollectorApi();
const { originalname } = request.file; const { originalname } = request.file;
const processingOnline = await Collector.online(); const processingOnline = await Collector.online();
@ -126,7 +127,8 @@ function workspaceEndpoints(app) {
return; return;
} }
const { success, reason } = await Collector.processDocument(originalname); const { success, reason } =
await Collector.processDocument(originalname);
if (!success) { if (!success) {
response.status(500).json({ success: false, error: reason }).end(); response.status(500).json({ success: false, error: reason }).end();
return; return;
@ -144,6 +146,10 @@ function workspaceEndpoints(app) {
response.locals?.user?.id response.locals?.user?.id
); );
response.status(200).json({ success: true, error: null }); response.status(200).json({ success: true, error: null });
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
} }
); );
@ -151,6 +157,7 @@ function workspaceEndpoints(app) {
"/workspace/:slug/upload-link", "/workspace/:slug/upload-link",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => { async (request, response) => {
try {
const Collector = new CollectorApi(); const Collector = new CollectorApi();
const { link = "" } = reqBody(request); const { link = "" } = reqBody(request);
const processingOnline = await Collector.online(); const processingOnline = await Collector.online();
@ -182,6 +189,10 @@ function workspaceEndpoints(app) {
response.locals?.user?.id response.locals?.user?.id
); );
response.status(200).json({ success: true, error: null }); response.status(200).json({ success: true, error: null });
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
} }
); );

View File

@ -10,6 +10,20 @@ const User = {
"role", "role",
"suspended", "suspended",
], ],
validations: {
username: (newValue = "") => {
try {
if (String(newValue).length > 100)
throw new Error("Username cannot be longer than 100 characters");
if (String(newValue).length < 2)
throw new Error("Username must be at least 2 characters");
return String(newValue);
} catch (e) {
throw new Error(e.message);
}
},
},
// validations for the above writable fields. // validations for the above writable fields.
castColumnValue: function (key, value) { castColumnValue: function (key, value) {
switch (key) { switch (key) {
@ -19,6 +33,12 @@ const User = {
return String(value); return String(value);
} }
}, },
filterFields: function (user = {}) {
const { password, ...rest } = user;
return { ...rest };
},
create: async function ({ username, password, role = "default" }) { create: async function ({ username, password, role = "default" }) {
const passwordCheck = this.checkPasswordComplexity(password); const passwordCheck = this.checkPasswordComplexity(password);
if (!passwordCheck.checkedOK) { if (!passwordCheck.checkedOK) {
@ -30,12 +50,12 @@ const User = {
const hashedPassword = bcrypt.hashSync(password, 10); const hashedPassword = bcrypt.hashSync(password, 10);
const user = await prisma.users.create({ const user = await prisma.users.create({
data: { data: {
username, username: this.validations.username(username),
password: hashedPassword, password: hashedPassword,
role, role: String(role),
}, },
}); });
return { user, error: null }; return { user: this.filterFields(user), error: null };
} catch (error) { } catch (error) {
console.error("FAILED TO CREATE USER.", error.message); console.error("FAILED TO CREATE USER.", error.message);
return { user: null, error: error.message }; return { user: null, error: error.message };
@ -69,7 +89,13 @@ const User = {
// and force-casts to the proper type; // and force-casts to the proper type;
Object.entries(updates).forEach(([key, value]) => { Object.entries(updates).forEach(([key, value]) => {
if (this.writable.includes(key)) { if (this.writable.includes(key)) {
if (this.validations.hasOwnProperty(key)) {
updates[key] = this.validations[key](
this.castColumnValue(key, value)
);
} else {
updates[key] = this.castColumnValue(key, value); updates[key] = this.castColumnValue(key, value);
}
return; return;
} }
delete updates[key]; delete updates[key];
@ -127,6 +153,17 @@ const User = {
}, },
get: async function (clause = {}) { get: async function (clause = {}) {
try {
const user = await prisma.users.findFirst({ where: clause });
return user ? this.filterFields({ ...user }) : null;
} catch (error) {
console.error(error.message);
return null;
}
},
// Returns user object with all fields
_get: async function (clause = {}) {
try { try {
const user = await prisma.users.findFirst({ where: clause }); const user = await prisma.users.findFirst({ where: clause });
return user ? { ...user } : null; return user ? { ...user } : null;
@ -162,7 +199,7 @@ const User = {
where: clause, where: clause,
...(limit !== null ? { take: limit } : {}), ...(limit !== null ? { take: limit } : {}),
}); });
return users; return users.map((usr) => this.filterFields(usr));
} catch (error) { } catch (error) {
console.error(error.message); console.error(error.message);
return []; return [];

View File

@ -38,7 +38,10 @@ class VoyageAiEmbedder {
Array.isArray(textInput) ? textInput : [textInput], Array.isArray(textInput) ? textInput : [textInput],
{ modelName: this.model } { modelName: this.model }
); );
return result || [];
// If given an array return the native Array[Array] format since that should be the outcome.
// But if given a single string, we need to flatten it so that we have a 1D array.
return (Array.isArray(textInput) ? result : result.flat()) || [];
} }
async embedChunks(textChunks = []) { async embedChunks(textChunks = []) {
@ -50,6 +53,12 @@ class VoyageAiEmbedder {
return embeddings; return embeddings;
} catch (error) { } catch (error) {
console.error("Voyage AI Failed to embed:", error); console.error("Voyage AI Failed to embed:", error);
if (
error.message.includes(
"Cannot read properties of undefined (reading '0')"
)
)
throw new Error("Voyage AI failed to embed: Rate limit reached");
throw error; throw error;
} }
} }