Browse Source

坐痛了文档上传解析后还要保存真是文档,还要在数据库保存和部门关系

master
ma-zhongxu 10 months ago
parent
commit
3916855161
  1. 4
      collector/utils/files/index.js
  2. 58
      server/endpoints/admin.js
  3. 32
      server/endpoints/dept.js
  4. 157
      server/endpoints/deptDocument.js
  5. 146
      server/endpoints/deptUsers.js
  6. 1
      server/endpoints/system.js
  7. 91
      server/endpoints/workspaces.js
  8. 4
      server/index.js
  9. 41
      server/models/dept.js
  10. 225
      server/models/deptDocument.js
  11. 167
      server/models/deptUsers.js
  12. 16
      server/prisma/migrations/20250226090554_init/migration.sql
  13. 17
      server/prisma/schema.prisma
  14. 33
      server/utils/files/index.js
  15. 53
      server/utils/files/multer.js

4
collector/utils/files/index.js

@ -72,8 +72,8 @@ function trashFile(filepath) {
} catch { } catch {
return; return;
} }
fs.rmSync(filepath);
console.log("=====:::::", filepath);
// fs.rmSync(filepath);
return; return;
} }

58
server/endpoints/admin.js

@ -79,6 +79,64 @@ function adminEndpoints(app) {
} }
); );
// app.post(
// "/admin/users/new",
// [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
// async (request, response) => {
// try {
// const currUser = await userFromSession(request, response);
// const newUserParams = reqBody(request);
// const roleValidation = validRoleSelection(currUser, newUserParams);
//
// // 验证角色权限
// if (!roleValidation.valid) {
// response
// .status(200)
// .json({ user: null, error: roleValidation.error });
// return;
// }
//
// // 使用事务确保原子性
// const result = await prisma.$transaction(async (prisma) => {
// // 1. 创建用户
// const newUser = await prisma.users.create({
// data: newUserParams,
// });
//
// // 2. 将用户和部门关联到 dept_users 表
// if (newUserParams.deptId) {
// await prisma.dept_users.create({
// data: {
// deptId: newUserParams.deptId,
// userId: newUser.id,
// createdAt: new Date(),
// updatedAt: new Date(),
// },
// });
// }
//
// // 3. 记录事件日志
// await EventLogs.logEvent(
// "user_created",
// {
// userName: newUser.username,
// createdBy: currUser.username,
// },
// currUser.id
// );
//
// return { user: newUser, error: null };
// });
//
// // 返回成功响应
// response.status(200).json(result);
// } catch (e) {
// console.error(e);
// response.status(500).json({ user: null, error: e.message });
// }
// }
// );
app.post( app.post(
"/admin/user/:id", "/admin/user/:id",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])], [validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],

32
server/endpoints/dept.js

@ -20,6 +20,38 @@ function deptEndpoints(app) {
} }
} }
); );
// 获取部门树状结构
app.get(
"/dept/tree",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
async (_request, response) => {
try {
const deptTree = await Dept.getDeptTree();
response.status(200).json({ deptTree });
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);
// 懒加载子部门列表
app.get(
"/dept/children",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const parentId = request.query.parentId
? parseInt(request.query.parentId)
: null;
const children = await Dept.getChildrenByParentId(parentId);
response.status(200).json({ children });
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);
app.post("/dept/add", app.post("/dept/add",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])], [validatedRequest, strictMultiUserRoleValid([ROLES.admin])],

157
server/endpoints/deptDocument.js

@ -0,0 +1,157 @@
const { DeptDocument } = require("../models/deptDocument");
const { validatedRequest } = require("../utils/middleware/validatedRequest");
const {
strictMultiUserRoleValid,
ROLES,
} = require("../utils/middleware/multiUserProtected");
function deptDocumentEndpoints(app) {
if (!app) return;
// 获取部门文档列表
app.get(
"/deptDocument/list",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
async (_request, response) => {
try {
const deptDocuments = await DeptDocument.where();
response.status(200).json({ deptDocuments });
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);
// 添加部门文档
app.post(
"/deptDocument/add",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const documentData = request.body; // 获取请求体中的文档数据
// 检查文档路径是否唯一
const isDocpathUnique = await DeptDocument.checkDocpathUnique(
documentData.docpath
);
if (!isDocpathUnique) {
return response.status(400).json({
success: false,
message: `文档路径 '${documentData.docpath}' 已存在`,
});
}
// 插入文档数据
const { deptDocument, error } = await DeptDocument.create(documentData);
if (error) {
return response.status(500).json({
success: false,
message: "添加部门文档失败",
error: error,
});
}
// 返回成功响应
response.status(200).json({
success: true,
data: deptDocument,
});
} catch (error) {
console.error("添加部门文档失败:", error);
response.status(500).json({
success: false,
message: "添加部门文档失败,服务器内部错误",
});
}
}
);
// 编辑部门文档
app.post(
"/deptDocument/edit",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const documentData = request.body; // 获取请求体中的文档数据
// 检查文档是否存在
const existingDocument = await DeptDocument.get({ id: documentData.id });
if (!existingDocument) {
return response.status(404).json({
success: false,
message: "文档不存在",
});
}
// 更新文档数据
const { success, error, deptDocument } = await DeptDocument.update(
documentData.id,
documentData
);
if (!success) {
return response.status(500).json({
success: false,
message: "编辑部门文档失败",
error: error,
});
}
// 返回成功响应
response.status(200).json({
success: true,
data: deptDocument,
});
} catch (error) {
console.error("编辑部门文档失败:", error);
response.status(500).json({
success: false,
message: "编辑部门文档失败,服务器内部错误",
});
}
}
);
// 删除部门文档
app.delete(
"/deptDocument/:id",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const documentId = parseInt(request.params.id); // 获取文档 ID
// 检查文档是否存在
const existingDocument = await DeptDocument.get({ id: documentId });
if (!existingDocument) {
return response.status(404).json({
success: false,
message: "文档不存在",
});
}
// 删除文档
const success = await DeptDocument.delete({ id: documentId });
if (!success) {
return response.status(500).json({
success: false,
message: "删除部门文档失败",
});
}
// 返回成功响应
response.status(200).json({
success: true,
message: "文档删除成功",
});
} catch (error) {
console.error("删除部门文档失败:", error);
response.status(500).json({
success: false,
message: "删除部门文档失败,服务器内部错误",
});
}
}
);
}
module.exports = { deptDocumentEndpoints };

146
server/endpoints/deptUsers.js

@ -0,0 +1,146 @@
const { DeptUsers } = require("../models/deptUsers");
const { validatedRequest } = require("../utils/middleware/validatedRequest");
const {
strictMultiUserRoleValid,
ROLES,
} = require("../utils/middleware/multiUserProtected");
function deptUsersEndpoints(app) {
if (!app) return;
// 获取部门用户关联列表
app.get(
"/deptUsers/list",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
async (_request, response) => {
try {
const deptUsers = await DeptUsers.where();
response.status(200).json({ deptUsers });
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);
// 添加部门用户关联
app.post(
"/deptUsers/add",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const deptUserData = request.body; // 获取请求体中的数据
// 插入部门用户关联数据
const { deptUser, error } = await DeptUsers.create(deptUserData);
if (error) {
return response.status(500).json({
success: false,
message: "添加部门用户关联失败",
error: error,
});
}
// 返回成功响应
response.status(200).json({
success: true,
data: deptUser,
});
} catch (error) {
console.error("添加部门用户关联失败:", error);
response.status(500).json({
success: false,
message: "添加部门用户关联失败,服务器内部错误",
});
}
}
);
// 编辑部门用户关联
app.post(
"/deptUsers/edit",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const deptUserData = request.body; // 获取请求体中的数据
// 检查关联是否存在
const existingDeptUser = await DeptUsers.get({ id: deptUserData.id });
if (!existingDeptUser) {
return response.status(404).json({
success: false,
message: "部门用户关联不存在",
});
}
// 更新部门用户关联
const { success, error, deptUser } = await DeptUsers.update(
deptUserData.id,
deptUserData
);
if (!success) {
return response.status(500).json({
success: false,
message: "编辑部门用户关联失败",
error: error,
});
}
// 返回成功响应
response.status(200).json({
success: true,
data: deptUser,
});
} catch (error) {
console.error("编辑部门用户关联失败:", error);
response.status(500).json({
success: false,
message: "编辑部门用户关联失败,服务器内部错误",
});
}
}
);
// 删除部门用户关联
app.delete(
"/deptUsers/:id",
[validatedRequest, strictMultiUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const id = parseInt(request.params.id); // 获取关联 ID
// 检查关联是否存在
const existingDeptUser = await DeptUsers.get({ id });
if (!existingDeptUser) {
return response.status(404).json({
success: false,
message: "部门用户关联不存在",
});
}
// 删除部门用户关联
const success = await DeptUsers.delete({ id });
if (!success) {
return response.status(500).json({
success: false,
message: "删除部门用户关联失败",
});
}
// 返回成功响应
response.status(200).json({
success: true,
message: "部门用户关联删除成功",
});
} catch (error) {
console.error("删除部门用户关联失败:", error);
response.status(500).json({
success: false,
message: "删除部门用户关联失败,服务器内部错误",
});
}
}
);
}
module.exports = { deptUsersEndpoints };

1
server/endpoints/system.js

@ -700,6 +700,7 @@ function systemEndpoints(app) {
try { try {
const user = await userFromSession(request, response); const user = await userFromSession(request, response);
const uploadedFileName = request.randomFileName; const uploadedFileName = request.randomFileName;
console.log("Uploaded File::::", uploadedFileName);
if (!uploadedFileName) { if (!uploadedFileName) {
return response.status(400).json({ message: "File upload failed." }); return response.status(400).json({ message: "File upload failed." });
} }

91
server/endpoints/workspaces.js

@ -12,7 +12,7 @@ const { Document } = require("../models/documents");
const { DocumentVectors } = require("../models/vectors"); const { DocumentVectors } = require("../models/vectors");
const { WorkspaceChats } = require("../models/workspaceChats"); const { WorkspaceChats } = require("../models/workspaceChats");
const { getVectorDbClass } = require("../utils/helpers"); const { getVectorDbClass } = require("../utils/helpers");
const { handleFileUpload, handlePfpUpload } = require("../utils/files/multer");
const { handleFileUpload, handlePfpUpload, handleCommUpload } = require("../utils/files/multer");
const { validatedRequest } = require("../utils/middleware/validatedRequest"); const { validatedRequest } = require("../utils/middleware/validatedRequest");
const { Telemetry } = require("../models/telemetry"); const { Telemetry } = require("../models/telemetry");
const { const {
@ -34,6 +34,11 @@ const { getTTSProvider } = require("../utils/TextToSpeech");
const { WorkspaceThread } = require("../models/workspaceThread"); const { WorkspaceThread } = require("../models/workspaceThread");
const truncate = require("truncate"); const truncate = require("truncate");
const { purgeDocument } = require("../utils/files/purgeDocument"); const { purgeDocument } = require("../utils/files/purgeDocument");
const { User } = require("../models/user");
const { DeptUsers } = require("../models/deptUsers");
const { DeptDocument } = require("../models/deptDocument");
const { v4: uuidv4 } = require("uuid");
const { moveAndRenameFile } = require("../utils/files/index");
function workspaceEndpoints(app) { function workspaceEndpoints(app) {
if (!app) return; if (!app) return;
@ -107,6 +112,56 @@ function workspaceEndpoints(app) {
} }
); );
// app.post(
// "/workspace/:slug/upload",
// [
// validatedRequest,
// flexUserRoleValid([ROLES.admin, ROLES.manager]),
// handleFileUpload,
// ],
// async function (request, response) {
// 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;
// }
//
// 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.error(e.message, e);
// response.sendStatus(500).end();
// }
// }
// );
app.post( app.post(
"/workspace/:slug/upload", "/workspace/:slug/upload",
[ [
@ -116,6 +171,11 @@ function workspaceEndpoints(app) {
], ],
async function (request, response) { async function (request, response) {
try { try {
const user = await userFromSession(request, response);
const deptUserRecord = await DeptUsers.get({ userId: user.id });
if (!deptUserRecord.deptUser) {
return response.status(500).json({ success: false, error: "没有发现用户部门" });
}
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();
@ -131,12 +191,39 @@ function workspaceEndpoints(app) {
return; return;
} }
const { success, reason } =
const { success, reason, documents } =
await Collector.processDocument(originalname); 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;
} }
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 假设路径字符串
const location = documents[0].location;
// 将路径中的反斜杠替换为正斜杠(可选,但通常更通用)
const unixStylePath = location.replace(/\\/g, '/');
// 找到最后一个目录分隔符的位置
const lastIndex = unixStylePath.lastIndexOf('/');
// 提取文件名
const parsedFileName = unixStylePath.substring(lastIndex + 1);
const fileExtension = path.extname(request.file.path).toLowerCase();
const sourceFile = path.resolve(__dirname, request.file.destination, request.file.originalname);
const targetDir =
process.env.NODE_ENV === "development"
? path.resolve(__dirname, `../../server/storage/localFile`)
: path.resolve(process.env.STORAGE_DIR, `../../server/storage/localFile`);
const newFileName = uuidv4() + fileExtension; // 新文件名
moveAndRenameFile(sourceFile, targetDir, newFileName);
const deptDocData = {
deptId: deptUserRecord.deptUser.deptId,
parsedFileName: parsedFileName,
parsedFilePath: location,
realFileName: originalname,
realFileAlias: newFileName,
realFilePath: targetDir,
};
await DeptDocument.create(deptDocData);
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Collector.log( Collector.log(
`Document ${originalname} uploaded processed and successfully. It is now available in documents.` `Document ${originalname} uploaded processed and successfully. It is now available in documents.`

4
server/index.js

@ -28,6 +28,8 @@ const { browserExtensionEndpoints } = require("./endpoints/browserExtension");
const { communityHubEndpoints } = require("./endpoints/communityHub"); const { communityHubEndpoints } = require("./endpoints/communityHub");
const { agentFlowEndpoints } = require("./endpoints/agentFlows"); const { agentFlowEndpoints } = require("./endpoints/agentFlows");
const { deptEndpoints } = require("./endpoints/dept"); const { deptEndpoints } = require("./endpoints/dept");
const { deptDocumentEndpoints } = require("./endpoints/deptDocument");
const { deptUsersEndpoints } = require("./endpoints/deptUsers");
const app = express(); const app = express();
const apiRouter = express.Router(); const apiRouter = express.Router();
const FILE_LIMIT = "3GB"; const FILE_LIMIT = "3GB";
@ -53,6 +55,8 @@ systemEndpoints(apiRouter);
extensionEndpoints(apiRouter); extensionEndpoints(apiRouter);
workspaceEndpoints(apiRouter); workspaceEndpoints(apiRouter);
deptEndpoints(apiRouter); deptEndpoints(apiRouter);
deptDocumentEndpoints(apiRouter);
deptUsersEndpoints(apiRouter);
workspaceThreadEndpoints(apiRouter); workspaceThreadEndpoints(apiRouter);
chatEndpoints(apiRouter); chatEndpoints(apiRouter);
adminEndpoints(apiRouter); adminEndpoints(apiRouter);

41
server/models/dept.js

@ -233,6 +233,47 @@ const Dept = {
throw error; throw error;
} }
}, },
/**
* 获取部门树状结构
* @returns {Promise<Array>}
*/
getDeptTree:async function () {
try {
// 查询所有部门
const allDepts = await prisma.dept.findMany();
// 构建树状结构
const buildTree = (parentId = null) => {
return allDepts
.filter((dept) => dept.parentId === parentId)
.map((dept) => ({
...dept,
children: buildTree(dept.deptId), // 递归获取子部门
}));
};
return buildTree();
} catch (error) {
console.error("获取部门树状结构失败:", error);
throw error;
}
},
/**
* 根据父部门 ID 获取子部门列表
* @param {number} parentId - 父部门 ID
* @returns {Promise<Array>}
*/
getChildrenByParentId:async function (parentId = null) {
try {
const children = await prisma.dept.findMany({
where: { parentId },
});
return children;
} catch (error) {
console.error("获取子部门列表失败:", error);
throw error;
}
},
}; };
module.exports = { Dept }; module.exports = { Dept };

225
server/models/deptDocument.js

@ -0,0 +1,225 @@
const prisma = require("../utils/prisma");
/**
* @typedef {Object} DeptDocument
* @property {number} id
* @property {number} deptId
* @property {string} realDocId
* @property {string} filename
* @property {string} docpath
* @property {string} [metadata]
* @property {string} [tag]
* @property {string} realFilename
* @property {boolean} public
* @property {Date} createdAt
* @property {Date} lastUpdatedAt
*/
const DeptDocument = {
writable: [
"deptId",
"parsedFileName",
"parsedFilePath",
"realFileName",
"realFileAlias",
"realFilePath",
"isPublic",
"tags",
"delTag",
],
validations: {
filename: (newValue = "") => {
if (typeof newValue !== "string" || newValue.length > 255) {
throw new Error(
"Filename must be a string and cannot be longer than 255 characters"
);
}
return newValue;
},
docpath: (newValue = "") => {
if (typeof newValue !== "string" || newValue.length > 255) {
throw new Error(
"Document path must be a string and cannot be longer than 255 characters"
);
}
return newValue;
},
public: (newValue = false) => {
if (typeof newValue !== "boolean") {
throw new Error("Public must be a boolean");
}
return newValue;
},
},
castColumnValue: function (key, value) {
switch (key) {
case "public":
return Boolean(value);
default:
return value;
}
},
/**
* 创建部门文档
* @param {Object} data - 部门文档数据
* @returns {Promise<{ deptDocument: DeptDocument | null, error: string | null }>}
*/
create: async function (data) {
try {
const validatedData = {};
for (const key of this.writable) {
if (data[key] !== undefined) {
if (this.validations[key]) {
validatedData[key] = this.validations[key](data[key]);
} else {
validatedData[key] = this.castColumnValue(key, data[key]);
}
}
}
const deptDocument = await prisma.dept_document.create({
data: {
...validatedData,
createdAt: new Date(),
lastUpdatedAt: new Date(),
},
});
return { deptDocument, error: null };
} catch (error) {
console.error("FAILED TO CREATE DEPT DOCUMENT.", error.message);
return { deptDocument: null, error: error.message };
}
},
/**
* 更新部门文档
* @param {number} id - 文档 ID
* @param {Object} updates - 更新的字段
* @returns {Promise<{ success: boolean, error: string | null, deptDocument: DeptDocument | null }>}
*/
update: async function (id, updates = {}) {
try {
if (!id) throw new Error("No document id provided for update");
const currentDocument = await prisma.dept_document.findUnique({
where: { id },
});
if (!currentDocument) throw new Error("Document not found");
const validatedUpdates = {};
for (const key of this.writable) {
if (updates[key] !== undefined) {
if (this.validations[key]) {
validatedUpdates[key] = this.validations[key](updates[key]);
} else {
validatedUpdates[key] = this.castColumnValue(key, updates[key]);
}
}
}
validatedUpdates.lastUpdatedAt = new Date();
const updatedDocument = await prisma.dept_document.update({
where: { id },
data: validatedUpdates,
});
return { success: true, error: null, deptDocument: updatedDocument };
} catch (error) {
console.error(error.message);
return { success: false, error: error.message, deptDocument: null };
}
},
/**
* 获取部门文档
* @param {Object} clause - 查询条件
* @returns {Promise<{ deptDocument: DeptDocument | null }>}
*/
get: async function (clause = {}) {
try {
const deptDocument = await prisma.dept_document.findFirst({
where: clause,
});
return deptDocument ? { deptDocument } : null;
} catch (error) {
console.error(error.message);
return null;
}
},
/**
* 删除部门文档
* @param {Object} clause - 删除条件
* @returns {Promise<boolean>}
*/
delete: async function (clause = {}) {
try {
const affectedRows = await prisma.dept_document.deleteMany({
where: clause,
});
return affectedRows.count > 0;
} catch (error) {
console.error(error.message);
return false;
}
},
/**
* 查询部门文档列表
* @param {Object} clause - 查询条件
* @param {number} limit - 限制数量
* @returns {Promise<DeptDocument[]>}
*/
where: async function (clause = {}, limit = null) {
try {
const deptDocuments = await prisma.dept_document.findMany({
where: clause,
take: limit !== null ? limit : undefined,
});
return deptDocuments;
} catch (error) {
console.error(error.message);
return [];
}
},
/**
* 检查文档路径是否唯一
* @param {string} docpath - 文档路径
* @returns {Promise<boolean>}
*/
checkDocpathUnique: async function (docpath) {
try {
const existingDocument = await prisma.dept_document.findFirst({
where: { docpath },
});
return !existingDocument;
} catch (error) {
console.error("检查文档路径唯一性失败:", error);
throw error;
}
},
/**
* 检查文档是否属于指定部门
* @param {number} id - 文档 ID
* @param {number} deptId - 部门 ID
* @returns {Promise<boolean>}
*/
checkDocumentBelongsToDept: async function (id, deptId) {
try {
const document = await prisma.dept_document.findFirst({
where: { id, deptId },
});
return !!document;
} catch (error) {
console.error("检查文档所属部门失败:", error);
throw error;
}
},
};
module.exports = { DeptDocument };

167
server/models/deptUsers.js

@ -0,0 +1,167 @@
const prisma = require("../utils/prisma");
/**
* @typedef {Object} DeptUser
* @property {number} id
* @property {number} deptId
* @property {number} userId
* @property {Date} createdAt
* @property {Date} updatedAt
*/
const DeptUsers = {
writable: ["deptId", "userId"],
validations: {
deptId: (newValue) => {
const num = Number(newValue);
if (isNaN(num)) {
throw new Error("Dept ID must be a number");
}
return num;
},
userId: (newValue) => {
const num = Number(newValue);
if (isNaN(num)) {
throw new Error("User ID must be a number");
}
return num;
},
},
castColumnValue: function (key, value) {
switch (key) {
case "deptId":
case "userId":
return Number(value);
default:
return value;
}
},
/**
* 创建部门用户关联
* @param {Object} data - 部门用户数据
* @returns {Promise<{ deptUser: DeptUser | null, error: string | null }>}
*/
create: async function (data) {
try {
const validatedData = {};
for (const key of this.writable) {
if (data[key] !== undefined) {
if (this.validations[key]) {
validatedData[key] = this.validations[key](data[key]);
} else {
validatedData[key] = this.castColumnValue(key, data[key]);
}
}
}
const deptUser = await prisma.dept_users.create({
data: {
...validatedData,
createdAt: new Date(),
updatedAt: new Date(),
},
});
return { deptUser, error: null };
} catch (error) {
console.error("FAILED TO CREATE DEPT USER.", error.message);
return { deptUser: null, error: error.message };
}
},
/**
* 更新部门用户关联
* @param {number} id - 关联 ID
* @param {Object} updates - 更新的字段
* @returns {Promise<{ success: boolean, error: string | null, deptUser: DeptUser | null }>}
*/
update: async function (id, updates = {}) {
try {
if (!id) throw new Error("No ID provided for update");
const currentDeptUser = await prisma.dept_users.findUnique({
where: { id },
});
if (!currentDeptUser) throw new Error("Dept user not found");
const validatedUpdates = {};
for (const key of this.writable) {
if (updates[key] !== undefined) {
if (this.validations[key]) {
validatedUpdates[key] = this.validations[key](updates[key]);
} else {
validatedUpdates[key] = this.castColumnValue(key, updates[key]);
}
}
}
validatedUpdates.updatedAt = new Date();
const updatedDeptUser = await prisma.dept_users.update({
where: { id },
data: validatedUpdates,
});
return { success: true, error: null, deptUser: updatedDeptUser };
} catch (error) {
console.error(error.message);
return { success: false, error: error.message, deptUser: null };
}
},
/**
* 获取部门用户关联
* @param {Object} clause - 查询条件
* @returns {Promise<{ deptUser: DeptUser | null }>}
*/
get: async function (clause = {}) {
try {
const deptUser = await prisma.dept_users.findFirst({
where: clause,
});
return deptUser ? { deptUser } : null;
} catch (error) {
console.error(error.message);
return null;
}
},
/**
* 删除部门用户关联
* @param {Object} clause - 删除条件
* @returns {Promise<boolean>}
*/
delete: async function (clause = {}) {
try {
const affectedRows = await prisma.dept_users.deleteMany({
where: clause,
});
return affectedRows.count > 0;
} catch (error) {
console.error(error.message);
return false;
}
},
/**
* 查询部门用户关联列表
* @param {Object} clause - 查询条件
* @param {number} limit - 限制数量
* @returns {Promise<DeptUser[]>}
*/
where: async function (clause = {}, limit = null) {
try {
const deptUsers = await prisma.dept_users.findMany({
where: clause,
take: limit !== null ? limit : undefined,
});
return deptUsers;
} catch (error) {
console.error(error.message);
return [];
}
},
};
module.exports = { DeptUsers };

16
server/prisma/migrations/20250226090554_init/migration.sql

@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "dept_document" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"deptId" INTEGER NOT NULL,
"parsedFileName" TEXT NOT NULL,
"parsedFilePath" TEXT NOT NULL,
"realFileName" TEXT NOT NULL,
"realFileAlias" TEXT NOT NULL,
"realFilePath" TEXT NOT NULL,
"isPublic" INTEGER,
"tags" TEXT,
"delTag" BOOLEAN NOT NULL DEFAULT false,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"lastUpdatedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "dept_document_deptId_fkey" FOREIGN KEY ("deptId") REFERENCES "dept" ("deptId") ON DELETE CASCADE ON UPDATE CASCADE
);

17
server/prisma/schema.prisma

@ -330,6 +330,7 @@ model dept {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now()) lastUpdatedAt DateTime @default(now())
dept_users dept_users[] dept_users dept_users[]
dept_document dept_document[]
} }
model dept_users { model dept_users {
@ -341,3 +342,19 @@ model dept_users {
user users @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction) user users @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)
dept dept @relation(fields: [deptId], references: [deptId], onDelete: Cascade, onUpdate: NoAction) dept dept @relation(fields: [deptId], references: [deptId], onDelete: Cascade, onUpdate: NoAction)
} }
model dept_document {
id Int @id @default(autoincrement())
deptId Int
dept dept @relation(fields: [deptId], references: [deptId], onDelete: Cascade)
parsedFileName String
parsedFilePath String
realFileName String
realFileAlias String
realFilePath String
isPublic Int?
tags String?
delTag Boolean @default(false)
createdAt DateTime @default(now())
lastUpdatedAt DateTime @default(now())
}

33
server/utils/files/index.js

@ -32,9 +32,9 @@ async function viewLocalFiles() {
type: "folder", type: "folder",
items: [], items: [],
}; };
console.log("111111111111111111111111111111111111111111111111111111111111111111111111111111111");
// console.log("111111111111111111111111111111111111111111111111111111111111111111111111111111111");
for (const file of fs.readdirSync(documentsPath)) { for (const file of fs.readdirSync(documentsPath)) {
console.log("file:", file);
// console.log("file:", file);
if (path.extname(file) === ".md") continue; if (path.extname(file) === ".md") continue;
const folderPath = path.resolve(documentsPath, file); const folderPath = path.resolve(documentsPath, file);
const isFolder = fs.lstatSync(folderPath).isDirectory(); const isFolder = fs.lstatSync(folderPath).isDirectory();
@ -50,7 +50,7 @@ async function viewLocalFiles() {
if (path.extname(subfile) !== ".json") continue; if (path.extname(subfile) !== ".json") continue;
const filePath = path.join(folderPath, subfile); const filePath = path.join(folderPath, subfile);
const rawData = fs.readFileSync(filePath, "utf8"); const rawData = fs.readFileSync(filePath, "utf8");
console.log("rawData:", rawData);
// console.log("rawData:", rawData);
const cachefilename = `${file}/${subfile}`; const cachefilename = `${file}/${subfile}`;
const { pageContent, ...metadata } = JSON.parse(rawData); const { pageContent, ...metadata } = JSON.parse(rawData);
subdocs.items.push({ subdocs.items.push({
@ -292,6 +292,32 @@ function purgeEntireVectorCache() {
return; return;
} }
/**
* 移动文件到目标目录并重命名
* @param {string} sourceFilePath - 源文件路径
* @param {string} targetDirectory - 目标目录路径
* @param {string} newFileName - 新文件名
*/
function moveAndRenameFile(sourceFilePath, targetDirectory, newFileName) {
// 1. 检查源文件是否存在
if (!fs.existsSync(sourceFilePath)) {
throw new Error(`源文件不存在: ${sourceFilePath}`);
}
// 2. 检查目标目录是否存在,如果不存在则创建
if (!fs.existsSync(targetDirectory)) {
fs.mkdirSync(targetDirectory, { recursive: true }); // recursive: true 确保创建多层目录
}
// 3. 构造目标文件的完整路径(使用新文件名)
const targetFilePath = path.join(targetDirectory, newFileName);
// 4. 移动文件并重命名
fs.renameSync(sourceFilePath, targetFilePath);
console.log(`文件已移动到: ${targetFilePath}`);
}
module.exports = { module.exports = {
findDocumentInDocuments, findDocumentInDocuments,
cachedVectorInformation, cachedVectorInformation,
@ -305,4 +331,5 @@ module.exports = {
documentsPath, documentsPath,
hasVectorCachedFiles, hasVectorCachedFiles,
purgeEntireVectorCache, purgeEntireVectorCache,
moveAndRenameFile,
}; };

53
server/utils/files/multer.js

@ -10,10 +10,12 @@ const { normalizePath } = require(".");
*/ */
const fileUploadStorage = multer.diskStorage({ const fileUploadStorage = multer.diskStorage({
destination: function (_, __, cb) { destination: function (_, __, cb) {
console.log("进来了进来了进来了::::",process.env.NODE_ENV);
const uploadOutput = const uploadOutput =
process.env.NODE_ENV === "development" process.env.NODE_ENV === "development"
? path.resolve(__dirname, `../../../collector/hotdir`) ? path.resolve(__dirname, `../../../collector/hotdir`)
: path.resolve(process.env.STORAGE_DIR, `../../collector/hotdir`); : path.resolve(process.env.STORAGE_DIR, `../../collector/hotdir`);
console.log("又来了又来了又来了:::",uploadOutput)
cb(null, uploadOutput); cb(null, uploadOutput);
}, },
filename: function (_, file, cb) { filename: function (_, file, cb) {
@ -168,9 +170,60 @@ function handlePfpUpload(request, response, next) {
}); });
} }
// 文件存储配置
const commUploadStorage = multer.diskStorage({
destination: async function (_, __, cb) {
try {
// const uploadOutput = "D:\\code2\\anything-llm-master\\server\\storage\\real";
console.log("进来了进来了进来了::::",process.env.NODE_ENV);
const uploadOutput =
process.env.NODE_ENV === "development"
? path.resolve(__dirname, `../../../server/storage/localFile`)
: path.resolve(process.env.STORAGE_DIR, `../../server/storage/localFile`);
console.log("又来了又来了又来了:::",uploadOutput)
if (process.env.NODE_ENV !== "development" && !process.env.STORAGE_DIR) {
throw new Error("STORAGE_DIR environment variable is required in production.");
}
fs.mkdirSync(uploadOutput, { recursive: true });
cb(null, uploadOutput);
} catch (err) {
cb(err);
}
},
filename: function (req, file, cb) {
const randomFileName = `${v4()}${path.extname(file.originalname)}`;
req.randomFileName = randomFileName;
cb(null, randomFileName);
},
});
// 文件上传中间件
function handleCommUpload(request, response, next) {
console.log("进来了进来了");
const upload = multer({ storage: commUploadStorage, limits: { fileSize: 100 * 1024 * 1024 }, }).single("file");
upload(request, response, function (err) {
console.log("handleCommUpload", err);
if (err) {
response
.status(500)
.json({
success: false,
error: `Invalid file upload. ${err.message}`,
})
.end();
return;
}
next();
});
}
module.exports = { module.exports = {
handleFileUpload, handleFileUpload,
handleAPIFileUpload, handleAPIFileUpload,
handleAssetUpload, handleAssetUpload,
handlePfpUpload, handlePfpUpload,
handleCommUpload,
}; };
Loading…
Cancel
Save