한글뷰어 기능수정

This commit is contained in:
koj729
2026-06-18 08:52:23 +09:00
parent cb0c42fbeb
commit 9268e4e6bc
38 changed files with 2544 additions and 211 deletions

View File

@@ -2581,6 +2581,64 @@ exports.uploadData = async (req, res, next) => {
let insertLogResult = await insertLog(params);
if (insertLogResult.message == 'insertLog_success') {
// PPT/PPTX 파일 업로드 즉시 PDF 변환 처리
for (let i = 0; i < insertDataResult.rows.length; i++) {
try {
let row = insertDataResult.rows[i];
let resourcePath = params.resourcePathArr[i];
let ext = (resourcePath.split('.').pop()).replace('.', '').toLowerCase();
if (['ppt', 'pptx'].includes(ext)) {
let dataId = row.data_id;
let objectKey = params.objectKeyArr[i];
let storageType = params.storageType;
let userInfoString = params.userInfoString;
let userIp = req.ip;
let bucket = projectId;
let command = new GetObjectCommand({
Bucket: bucket,
Key: objectKey,
});
let url = await getSignedUrl(s3, command, { expiresIn: 60 * 60 * 6 }); // 유효시간 6시간
let initiator = `DEV_LOCAL_${JSON.parse(userInfoString).user_id}`;
let type = 'archive';
if (env == 'production') {
if (deploymentType == 'ONPREMISE') initiator = 'HYHC_ONPREMISE';
if (deploymentType == 'CLOUD') initiator = `AWS_CLOUD_${cloudType}`;
}
const job = await convertPdfQueue.add(
`'${initiator}'에서 문서를 PDF로 변환`,
{ resourcePath, url, objectKey, bucket, storageType, dataId, projectId, userInfoString, userIp, initiator, type, serviceName }
);
let resourcePathClean = resourcePath.startsWith('/') ? resourcePath.slice(1) : resourcePath;
let pathArray = getPathArray(resourcePathClean);
convertingDataArr.push({
dataId: dataId,
resourcePath: resourcePath,
depth1: pathArray[0],
depth2: pathArray[1],
depth3: pathArray[2],
jobId: job.id
});
// 변환 시작 소켓 전송
let startEventData = { projectId: projectId, resourcePath: resourcePath, convertingDataArr: convertingDataArr };
let io = getIo();
io.emit('convert_start', startEventData);
console.log(`[PPT Auto-Convert] queued job ${job.id} for dataId ${dataId} (${resourcePath})`);
}
} catch (autoErr) {
console.error(`[PPT Auto-Convert Failed] index ${i}:`, autoErr);
}
}
let resultData = {
message: 'uploadData_success',
projectId: projectId,
@@ -2881,8 +2939,81 @@ exports.removeTarget = async(req, res) => {
let permission = JSON.parse(params.userInfoString).permission;
let depth = getDepth(params.resourcePathArr[0]);
let isRecycleBinModal = params.isRecycleBinModal;
const isExpiredFolder = params.isExpiredFolder === true;
if (!isRecycleBinModal && (depth == 1 && permission < 191) || (depth >= 2 && permission < 7)) {
if (isExpiredFolder) {
// 자동 기한 만료 삭제의 경우 안전 검증
if (depth !== 3 || params.dataType !== 'folder') {
return res.status(400).json({
message: 'removeTarget_failed',
error: '보존 정책 자동 삭제는 3단계 폴더만 대상이 될 수 있습니다.'
});
}
const client = await pool.connect();
try {
// 1. 최신 시스템 정책 조회
const policyRes = await client.query("SELECT * FROM ver4.tb_system_policy WHERE policy_key = 'GLOBAL_DELETE_POLICY'");
if (policyRes.rows.length === 0 || !policyRes.rows[0].is_active) {
return res.status(400).json({
message: 'removeTarget_failed',
error: '자동 삭제 정책이 비활성화 상태입니다.'
});
}
const { limit_file_count, limit_days } = policyRes.rows[0];
// 2. 해당 폴더의 실제 정보 조회 (최종 활동 시각 확인)
const resourcePath = params.resourcePathArr[0];
const projectId = req.baseUrl.split('/')[1];
const folderQuery = `
SELECT last_folder_act_date
FROM ver4.${tbData}
WHERE project_id = $1 AND is_folder = true AND data_depth = 3 AND is_removed = false
AND path1 = $2 AND path2 = $3 AND path3 = $4;
`;
const folderRes = await client.query(folderQuery, [
projectId,
getPathSegment(resourcePath, 1),
getPathSegment(resourcePath, 2),
getPathSegment(resourcePath, 3)
]);
if (folderRes.rows.length === 0) {
return res.status(404).json({
message: 'removeTarget_failed',
error: '해당 폴더를 찾을 수 없거나 이미 삭제되었습니다.'
});
}
const lastFolderActDate = new Date(folderRes.rows[0].last_folder_act_date);
const expiryDate = new Date(lastFolderActDate.getTime() + limit_days * 24 * 60 * 60 * 1000);
if (expiryDate > new Date()) {
return res.status(400).json({
message: 'removeTarget_failed',
error: `해당 폴더는 아직 만료 기한이 지나지 않았습니다. (남은 기한 검증 실패)`
});
}
// 3. 하위 파일 개수 계산
const filesCount = await getFilesCount(projectId, params.storageType || 'ONPREMISE', resourcePath);
if (Number(filesCount) >= limit_file_count) {
return res.status(400).json({
message: 'removeTarget_failed',
error: `해당 폴더의 파일 개수가 기준(${limit_file_count}개) 이상입니다. (파일 개수 검증 실패)`
});
}
} catch (err) {
console.error("isExpiredFolder verification error:", err);
return res.status(500).json({
message: 'removeTarget_failed',
error: '만료 폴더 검증 처리 중 오류가 발생했습니다.'
});
} finally {
client.release();
}
}
if (!isExpiredFolder && !isRecycleBinModal && ((depth == 1 && permission < 191) || (depth >= 2 && permission < 7))) {
return res.status(200).json({
message: 'removeTarget_failed_permission',
});
@@ -3221,7 +3352,7 @@ exports.addConvetPdfLog = async(req, res) => {
exports.removeConvertingData = async(req, res) => {
const projectId = req.baseUrl.split('/')[1];
let { params } = req.body;
let { resourcePath, dataId, userInfoString } = params;
let { resourcePath, dataId, userInfoString, stdout } = params;
//// 배열에서 파일 정보 삭제
convertingDataArr = convertingDataArr.filter(data => data.dataId !== dataId);
@@ -3238,7 +3369,8 @@ exports.removeConvertingData = async(req, res) => {
convertingDataArr: convertingDataArr,
resourcePath: resourcePath,
dataId: dataId,
userInfoString: userInfoString
userInfoString: userInfoString,
stdout: stdout || ''
};
let io = getIo();
@@ -4363,6 +4495,7 @@ async function updateThumbnailInfoAction(projectId, params) {
}
}
// 삭제예정(2025.10.31): 이호성
// exports.get3dViewerThumbUrl = async(req, res, next) => {
// const dataId = req.query.dataId;