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 = "") {
const result = path
.normalize(filepath.trim())
.normalize(filepath.replace(/\s/g, "-").trim())
.replace(/^(\.\.(\/|\\|$))+/, "")
.trim();
if (["..", ".", "/"].includes(result)) throw new Error("Invalid path.");

View File

@ -108,10 +108,13 @@ export default function Footer() {
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"
>
{React.createElement(ICON_COMPONENTS[item.icon], {
weight: "fill",
className: "h-5 w-5",
})}
{React.createElement(
ICON_COMPONENTS?.[item.icon] ?? ICON_COMPONENTS.Info,
{
weight: "fill",
className: "h-5 w-5",
}
)}
</a>
))}
{!isMobile && <SettingsButton />}

View File

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

View File

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

View File

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

View File

@ -111,39 +111,45 @@ function workspaceEndpoints(app) {
handleFileUpload,
],
async function (request, response) {
const Collector = new CollectorApi();
const { originalname } = request.file;
const processingOnline = await Collector.online();
try {
const Collector = new CollectorApi();
const { originalname } = request.file;
const processingOnline = await Collector.online();
if (!processingOnline) {
response
.status(500)
.json({
success: false,
error: `Document processing API is not online. Document ${originalname} will not be processed automatically.`,
})
.end();
return;
if (!processingOnline) {
response
.status(500)
.json({
success: false,
error: `Document processing API is not online. Document ${originalname} will not be processed automatically.`,
})
.end();
return;
}
const { success, reason } =
await Collector.processDocument(originalname);
if (!success) {
response.status(500).json({ success: false, error: reason }).end();
return;
}
Collector.log(
`Document ${originalname} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent(
"document_uploaded",
{
documentName: originalname,
},
response.locals?.user?.id
);
response.status(200).json({ success: true, error: null });
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
const { success, reason } = await Collector.processDocument(originalname);
if (!success) {
response.status(500).json({ success: false, error: reason }).end();
return;
}
Collector.log(
`Document ${originalname} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent(
"document_uploaded",
{
documentName: originalname,
},
response.locals?.user?.id
);
response.status(200).json({ success: true, error: null });
}
);
@ -151,37 +157,42 @@ function workspaceEndpoints(app) {
"/workspace/:slug/upload-link",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
const Collector = new CollectorApi();
const { link = "" } = reqBody(request);
const processingOnline = await Collector.online();
try {
const Collector = new CollectorApi();
const { link = "" } = reqBody(request);
const processingOnline = await Collector.online();
if (!processingOnline) {
response
.status(500)
.json({
success: false,
error: `Document processing API is not online. Link ${link} will not be processed automatically.`,
})
.end();
return;
if (!processingOnline) {
response
.status(500)
.json({
success: false,
error: `Document processing API is not online. Link ${link} will not be processed automatically.`,
})
.end();
return;
}
const { success, reason } = await Collector.processLink(link);
if (!success) {
response.status(500).json({ success: false, error: reason }).end();
return;
}
Collector.log(
`Link ${link} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("link_uploaded");
await EventLogs.logEvent(
"link_uploaded",
{ link },
response.locals?.user?.id
);
response.status(200).json({ success: true, error: null });
} catch (e) {
console.log(e.message, e);
response.sendStatus(500).end();
}
const { success, reason } = await Collector.processLink(link);
if (!success) {
response.status(500).json({ success: false, error: reason }).end();
return;
}
Collector.log(
`Link ${link} uploaded processed and successfully. It is now available in documents.`
);
await Telemetry.sendTelemetry("link_uploaded");
await EventLogs.logEvent(
"link_uploaded",
{ link },
response.locals?.user?.id
);
response.status(200).json({ success: true, error: null });
}
);

View File

@ -10,6 +10,20 @@ const User = {
"role",
"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.
castColumnValue: function (key, value) {
switch (key) {
@ -19,6 +33,12 @@ const User = {
return String(value);
}
},
filterFields: function (user = {}) {
const { password, ...rest } = user;
return { ...rest };
},
create: async function ({ username, password, role = "default" }) {
const passwordCheck = this.checkPasswordComplexity(password);
if (!passwordCheck.checkedOK) {
@ -30,12 +50,12 @@ const User = {
const hashedPassword = bcrypt.hashSync(password, 10);
const user = await prisma.users.create({
data: {
username,
username: this.validations.username(username),
password: hashedPassword,
role,
role: String(role),
},
});
return { user, error: null };
return { user: this.filterFields(user), error: null };
} catch (error) {
console.error("FAILED TO CREATE USER.", error.message);
return { user: null, error: error.message };
@ -69,7 +89,13 @@ const User = {
// and force-casts to the proper type;
Object.entries(updates).forEach(([key, value]) => {
if (this.writable.includes(key)) {
updates[key] = this.castColumnValue(key, value);
if (this.validations.hasOwnProperty(key)) {
updates[key] = this.validations[key](
this.castColumnValue(key, value)
);
} else {
updates[key] = this.castColumnValue(key, value);
}
return;
}
delete updates[key];
@ -127,6 +153,17 @@ const User = {
},
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 {
const user = await prisma.users.findFirst({ where: clause });
return user ? { ...user } : null;
@ -162,7 +199,7 @@ const User = {
where: clause,
...(limit !== null ? { take: limit } : {}),
});
return users;
return users.map((usr) => this.filterFields(usr));
} catch (error) {
console.error(error.message);
return [];

View File

@ -38,7 +38,10 @@ class VoyageAiEmbedder {
Array.isArray(textInput) ? textInput : [textInput],
{ 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 = []) {
@ -50,6 +53,12 @@ class VoyageAiEmbedder {
return embeddings;
} catch (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;
}
}