파일 업로드 회사별로 보이도록 수정

This commit is contained in:
dohyeons
2025-11-04 17:57:28 +09:00
parent 958aeb2d53
commit acaa3414d2
3 changed files with 154 additions and 87 deletions

View File

@@ -232,13 +232,20 @@ export const uploadFiles = async (
// 자동 연결 로직 - target_objid 자동 생성
let finalTargetObjid = targetObjid;
if (autoLink === "true" && linkedTable && recordId) {
// 🔑 템플릿 파일(screen_files:)이나 temp_ 파일은 autoLink 무시
const isTemplateFile = targetObjid && (targetObjid.startsWith('screen_files:') || targetObjid.startsWith('temp_'));
if (!isTemplateFile && autoLink === "true" && linkedTable && recordId) {
// 가상 파일 컬럼의 경우 컬럼명도 포함한 target_objid 생성
if (isVirtualFileColumn === "true" && columnName) {
finalTargetObjid = `${linkedTable}:${recordId}:${columnName}`;
} else {
finalTargetObjid = `${linkedTable}:${recordId}`;
}
console.log("📎 autoLink 적용:", { original: targetObjid, final: finalTargetObjid });
} else if (isTemplateFile) {
console.log("🎨 템플릿 파일이므로 targetObjid 유지:", targetObjid);
}
const savedFiles = [];
@@ -363,6 +370,38 @@ export const deleteFile = async (
const { objid } = req.params;
const { writer = "system" } = req.body;
// 🔒 멀티테넌시: 현재 사용자의 회사 코드
const companyCode = req.user?.companyCode;
// 파일 정보 조회
const fileRecord = await queryOne<any>(
`SELECT * FROM attach_file_info WHERE objid = $1`,
[parseInt(objid)]
);
if (!fileRecord) {
res.status(404).json({
success: false,
message: "파일을 찾을 수 없습니다.",
});
return;
}
// 🔒 멀티테넌시: 회사 코드 일치 여부 확인 (최고 관리자 제외)
if (companyCode !== "*" && fileRecord.company_code !== companyCode) {
console.warn("⚠️ 다른 회사 파일 삭제 시도:", {
userId: req.user?.userId,
userCompanyCode: companyCode,
fileCompanyCode: fileRecord.company_code,
objid,
});
res.status(403).json({
success: false,
message: "접근 권한이 없습니다.",
});
return;
}
// 파일 상태를 DELETED로 변경 (논리적 삭제)
await query<any>(
"UPDATE attach_file_info SET status = $1 WHERE objid = $2",
@@ -510,6 +549,9 @@ export const getComponentFiles = async (
const { screenId, componentId, tableName, recordId, columnName } =
req.query;
// 🔒 멀티테넌시: 현재 사용자의 회사 코드 가져오기
const companyCode = req.user?.companyCode;
console.log("📂 [getComponentFiles] API 호출:", {
screenId,
componentId,
@@ -517,6 +559,7 @@ export const getComponentFiles = async (
recordId,
columnName,
user: req.user?.userId,
companyCode, // 🔒 멀티테넌시 로그
});
if (!screenId || !componentId) {
@@ -534,32 +577,16 @@ export const getComponentFiles = async (
templateTargetObjid,
});
// 모든 파일 조회해서 실제 저장된 target_objid 패턴 확인
const allFiles = await query<any>(
`SELECT target_objid, real_file_name, regdate
FROM attach_file_info
WHERE status = $1
ORDER BY regdate DESC
LIMIT 10`,
["ACTIVE"]
);
console.log(
"🗂️ [getComponentFiles] 최근 저장된 파일들의 target_objid:",
allFiles.map((f) => ({
target_objid: f.target_objid,
name: f.real_file_name,
}))
);
// 🔒 멀티테넌시: 회사별 필터링 추가
const templateFiles = await query<any>(
`SELECT * FROM attach_file_info
WHERE target_objid = $1 AND status = $2
WHERE target_objid = $1 AND status = $2 AND company_code = $3
ORDER BY regdate DESC`,
[templateTargetObjid, "ACTIVE"]
[templateTargetObjid, "ACTIVE", companyCode]
);
console.log(
"📁 [getComponentFiles] 템플릿 파일 결과:",
"📁 [getComponentFiles] 템플릿 파일 결과 (회사별 필터링):",
templateFiles.length
);
@@ -567,11 +594,12 @@ export const getComponentFiles = async (
let dataFiles: any[] = [];
if (tableName && recordId && columnName) {
const dataTargetObjid = `${tableName}:${recordId}:${columnName}`;
// 🔒 멀티테넌시: 회사별 필터링 추가
dataFiles = await query<any>(
`SELECT * FROM attach_file_info
WHERE target_objid = $1 AND status = $2
WHERE target_objid = $1 AND status = $2 AND company_code = $3
ORDER BY regdate DESC`,
[dataTargetObjid, "ACTIVE"]
[dataTargetObjid, "ACTIVE", companyCode]
);
}
@@ -643,6 +671,9 @@ export const previewFile = async (
const { objid } = req.params;
const { serverFilename } = req.query;
// 🔒 멀티테넌시: 현재 사용자의 회사 코드
const companyCode = req.user?.companyCode;
const fileRecord = await queryOne<any>(
"SELECT * FROM attach_file_info WHERE objid = $1 LIMIT 1",
[parseInt(objid)]
@@ -656,13 +687,28 @@ export const previewFile = async (
return;
}
// 🔒 멀티테넌시: 회사 코드 일치 여부 확인 (최고 관리자 제외)
if (companyCode !== "*" && fileRecord.company_code !== companyCode) {
console.warn("⚠️ 다른 회사 파일 접근 시도:", {
userId: req.user?.userId,
userCompanyCode: companyCode,
fileCompanyCode: fileRecord.company_code,
objid,
});
res.status(403).json({
success: false,
message: "접근 권한이 없습니다.",
});
return;
}
// 파일 경로에서 회사코드와 날짜 폴더 추출
const filePathParts = fileRecord.file_path!.split("/");
let companyCode = filePathParts[2] || "DEFAULT";
let fileCompanyCode = filePathParts[2] || "DEFAULT";
// company_* 처리 (실제 회사 코드로 변환)
if (companyCode === "company_*") {
companyCode = "company_*"; // 실제 디렉토리명 유지
if (fileCompanyCode === "company_*") {
fileCompanyCode = "company_*"; // 실제 디렉토리명 유지
}
const fileName = fileRecord.saved_file_name!;
@@ -674,7 +720,7 @@ export const previewFile = async (
}
const companyUploadDir = getCompanyUploadDir(
companyCode,
fileCompanyCode,
dateFolder || undefined
);
const filePath = path.join(companyUploadDir, fileName);
@@ -762,6 +808,9 @@ export const downloadFile = async (
try {
const { objid } = req.params;
// 🔒 멀티테넌시: 현재 사용자의 회사 코드
const companyCode = req.user?.companyCode;
const fileRecord = await queryOne<any>(
`SELECT * FROM attach_file_info WHERE objid = $1`,
[parseInt(objid)]
@@ -775,13 +824,28 @@ export const downloadFile = async (
return;
}
// 🔒 멀티테넌시: 회사 코드 일치 여부 확인 (최고 관리자 제외)
if (companyCode !== "*" && fileRecord.company_code !== companyCode) {
console.warn("⚠️ 다른 회사 파일 다운로드 시도:", {
userId: req.user?.userId,
userCompanyCode: companyCode,
fileCompanyCode: fileRecord.company_code,
objid,
});
res.status(403).json({
success: false,
message: "접근 권한이 없습니다.",
});
return;
}
// 파일 경로에서 회사코드와 날짜 폴더 추출 (예: /uploads/company_*/2025/09/05/timestamp_filename.ext)
const filePathParts = fileRecord.file_path!.split("/");
let companyCode = filePathParts[2] || "DEFAULT"; // /uploads/company_*/2025/09/05/filename.ext에서 company_* 추출
let fileCompanyCode = filePathParts[2] || "DEFAULT"; // /uploads/company_*/2025/09/05/filename.ext에서 company_* 추출
// company_* 처리 (실제 회사 코드로 변환)
if (companyCode === "company_*") {
companyCode = "company_*"; // 실제 디렉토리명 유지
if (fileCompanyCode === "company_*") {
fileCompanyCode = "company_*"; // 실제 디렉토리명 유지
}
const fileName = fileRecord.saved_file_name!;
@@ -794,7 +858,7 @@ export const downloadFile = async (
}
const companyUploadDir = getCompanyUploadDir(
companyCode,
fileCompanyCode,
dateFolder || undefined
);
const filePath = path.join(companyUploadDir, fileName);