mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-19 20:50:09 +01:00
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into render
This commit is contained in:
commit
c6ad94d81a
@ -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.");
|
||||||
|
@ -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(
|
||||||
weight: "fill",
|
ICON_COMPONENTS?.[item.icon] ?? ICON_COMPONENTS.Info,
|
||||||
className: "h-5 w-5",
|
{
|
||||||
})}
|
weight: "fill",
|
||||||
|
className: "h-5 w-5",
|
||||||
|
}
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
{!isMobile && <SettingsButton />}
|
{!isMobile && <SettingsButton />}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -111,39 +111,45 @@ function workspaceEndpoints(app) {
|
|||||||
handleFileUpload,
|
handleFileUpload,
|
||||||
],
|
],
|
||||||
async function (request, response) {
|
async function (request, response) {
|
||||||
const Collector = new CollectorApi();
|
try {
|
||||||
const { originalname } = request.file;
|
const Collector = new CollectorApi();
|
||||||
const processingOnline = await Collector.online();
|
const { originalname } = request.file;
|
||||||
|
const processingOnline = await Collector.online();
|
||||||
|
|
||||||
if (!processingOnline) {
|
if (!processingOnline) {
|
||||||
response
|
response
|
||||||
.status(500)
|
.status(500)
|
||||||
.json({
|
.json({
|
||||||
success: false,
|
success: false,
|
||||||
error: `Document processing API is not online. Document ${originalname} will not be processed automatically.`,
|
error: `Document processing API is not online. Document ${originalname} will not be processed automatically.`,
|
||||||
})
|
})
|
||||||
.end();
|
.end();
|
||||||
return;
|
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",
|
"/workspace/:slug/upload-link",
|
||||||
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
|
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
|
||||||
async (request, response) => {
|
async (request, response) => {
|
||||||
const Collector = new CollectorApi();
|
try {
|
||||||
const { link = "" } = reqBody(request);
|
const Collector = new CollectorApi();
|
||||||
const processingOnline = await Collector.online();
|
const { link = "" } = reqBody(request);
|
||||||
|
const processingOnline = await Collector.online();
|
||||||
|
|
||||||
if (!processingOnline) {
|
if (!processingOnline) {
|
||||||
response
|
response
|
||||||
.status(500)
|
.status(500)
|
||||||
.json({
|
.json({
|
||||||
success: false,
|
success: false,
|
||||||
error: `Document processing API is not online. Link ${link} will not be processed automatically.`,
|
error: `Document processing API is not online. Link ${link} will not be processed automatically.`,
|
||||||
})
|
})
|
||||||
.end();
|
.end();
|
||||||
return;
|
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 });
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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)) {
|
||||||
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;
|
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 [];
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user