junggilpark před 1 rokem
rodič
revize
2774a6c85f

+ 21 - 5
package-lock.json

@@ -27,6 +27,8 @@
                 "fs": "^0.0.1-security",
                 "http-errors": "^2.0.0",
                 "jszip": "^3.10.1",
+                "mime": "^4.0.3",
+                "mime-types": "^2.1.35",
                 "mssql": "^10.0.2",
                 "multer": "^1.4.5-lts.1",
                 "path": "^0.12.7",
@@ -3469,14 +3471,17 @@
             }
         },
         "node_modules/mime": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
-            "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz",
+            "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==",
+            "funding": [
+                "https://github.com/sponsors/broofa"
+            ],
             "bin": {
-                "mime": "cli.js"
+                "mime": "bin/cli.js"
             },
             "engines": {
-                "node": ">=10.0.0"
+                "node": ">=16"
             }
         },
         "node_modules/mime-db": {
@@ -4249,6 +4254,17 @@
                 "safe-json-stringify": "^1.0.4"
             }
         },
+        "node_modules/restify/node_modules/mime": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+            "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+            "bin": {
+                "mime": "cli.js"
+            },
+            "engines": {
+                "node": ">=10.0.0"
+            }
+        },
         "node_modules/restify/node_modules/semver": {
             "version": "7.6.0",
             "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",

+ 2 - 0
package.json

@@ -25,6 +25,8 @@
         "fs": "^0.0.1-security",
         "http-errors": "^2.0.0",
         "jszip": "^3.10.1",
+        "mime": "^4.0.3",
+        "mime-types": "^2.1.35",
         "mssql": "^10.0.2",
         "multer": "^1.4.5-lts.1",
         "path": "^0.12.7",

+ 359 - 376
src/app.js

@@ -19,7 +19,11 @@ const multer  = require('multer');
 const msal = require('@azure/msal-node');
 // const FormData = require("form-data");
 // const { v4: uuidv4 } = require('uuid');
+const mime = require('mime-types');
 const {init} = require('./ext.js');
+const BadRequest = require('./error/badRequest.js');
+const LabelBadResponse = require('./error/labelBadResponse.js');
+const NotFound = require('./error/notFound.js');
 let POSSIBLE_EXT;
 
 const apiUrl = 'http://192.168.20.99:5050';
@@ -78,15 +82,10 @@ serverApp.use(session({
   }
 }));
 
-serverApp.use(async (req, res, next)=>{
-  POSSIBLE_EXT = await init();
-  next();
-})
 serverApp.set(express.json());
 serverApp.use(cookieParser());
 serverApp.use(express.urlencoded({ extended: false }));
 serverApp.use("/static", express.static(globalPath.join(__dirname, 'static')));
-
 const options = {
   key: process.env.SSL_KEY_FILE ? fs.readFileSync(process.env.SSL_KEY_FILE) : undefined,
   cert: process.env.SSL_CRT_FILE ? fs.readFileSync(process.env.SSL_CRT_FILE) : undefined,
@@ -121,17 +120,25 @@ wsServer.on('request', function(req) {
 });
 
 async function getPercentageComplete(url, connection) {
-  const result = await axios.get(url);
-  if (result && result.data && result.data.percentageComplete >= 0) {
-    const percent = result.data.percentageComplete;
-    if (percent > 100) {
-      percent = 100;
-    }
-    connection.sendUTF(Number(percent.toFixed(2)));
-    if (result.data.percentageComplete < 100) {
-      setTimeout(()=>getPercentageComplete(url, connection), 1000);
+  try {
+    const result = await axios.get(url);
+    if (result && result.data && result.data.percentageComplete >= 0) {
+      let percent = result.data.percentageComplete;
+      if (percent > 100) {
+        percent = 100;
+      }
+      connection.sendUTF(Number(percent.toFixed(2)));
+      if (result.data.percentageComplete < 100) {
+        setTimeout(()=>getPercentageComplete(url, connection), 1000);
+      }
     }
   }
+  catch (error) {
+    console.log(error.message);
+    console.log(error.name);
+    console.log(error.errors);
+    connection.sendUTF(error);
+  }
 }
 
 const corsOption = {
@@ -143,12 +150,16 @@ serverApp.use(cors(corsOption));
 serverApp.use(bodyParser.json());
 
 serverApp.get("/tab", 
-  async function (req, res, next) {
+  errorHandler( async function (req, res, next) {
     res.sendFile(globalPath.join(__dirname, "/views/hello.html"), 
     );
-  }
+  }) 
 );
-serverApp.post("/getProfileOnBehalfOf", async (req, res, next) => {
+
+serverApp.post("/getProfileOnBehalfOf", errorHandler(async (req, res, next) => {
+  if (!req.body.token) {
+    throw BadRequest('토큰 정보를 찾을 수 없습니다.');
+  }
   result = await cca.acquireTokenOnBehalfOf({
     oboAssertion: req.body.token,
     scopes: [".default"]
@@ -159,7 +170,7 @@ serverApp.post("/getProfileOnBehalfOf", async (req, res, next) => {
   req.session.account = result.account;
   req.session.auth = result;
   return res.send(result);
-})
+}));
 
 serverApp.post("/redirect", (req, res, next)=>{
   console.log(req);
@@ -169,53 +180,42 @@ serverApp.get("/redirect", (req, res, next)=>{
 });
 
 serverApp.post("/api-get",   
-  async (req, res, next) => {
+  errorHandler(async (req, res, next) => {
     const uri = req.body.api_uri || req.session.apiUri;
     let param = {};
     if (req.session.param) {
       param = req.session.param;
     }
-    try {
-        const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
-        res.json(graphResponse);
-    } catch (error) {
-        next(error);
-    }
-});
+    
+    const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
+    res.json(graphResponse);
+}));
 
 serverApp.get("/api-redirect", 
-  async function (req, res, next) {
+  errorHandler(async function (req, res, next) {
       const uri = req.session.apiUri;
       let param = {};
       if (req.session.param) {
         param = req.session.param;
       }
-      try {
-          const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
-          res.json(graphResponse);
-      } catch (error) {
-          next(error);
-      }
-})
+      const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
+      res.json(graphResponse);
+}));
 
 serverApp.get("/post-redirect", 
-  async function (req, res, next) {
+  errorHandler(async function (req, res, next) {
     const uri = req.session.apiUri;
     let param = {};
       if (req.session.param) {
         param = req.session.param;
       }
-    try {
-        const graphResponse = await updateFetch(endPoint + uri, req.session.accessToken, param);
-        res.json(graphResponse);
-    } catch (error) {
-        next(error);
-    }
+
+      const graphResponse = await updateFetch(endPoint + uri, req.session.accessToken, param);
+    res.json(graphResponse);
   }
-)
+))
 
-serverApp.post("/getGroupList",  async function (req, res, next) {
-  try {
+serverApp.post("/getGroupList",  errorHandler(async function (req, res, next) {
       const options = getOptions(req.session.accessToken);
       const uriArr = [
         axios.get(endPoint + "/me/drive/root", options),
@@ -260,13 +260,10 @@ serverApp.post("/getGroupList",  async function (req, res, next) {
         }
       }
       res.json(resultObj);
-  } catch (error) {
-      next(error);
-  } 
-});
+}));
 
 serverApp.post('/api/makeFolder', 
-    async (req, res, next)=>{
+  errorHandler(async (req, res, next)=>{
 
       const options = getOptions(req.session.accessToken);
       const {siteId, path, name} = req.body;
@@ -276,30 +273,26 @@ serverApp.post('/api/makeFolder',
         folder: { },
         '@microsoft.graph.conflictBehavior': 'rename'
       }
-      try{
-        const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + path, options);
-        if (sitesInfo.data && sitesInfo.data.id) {
-          const itemId = sitesInfo.data.id;
-          const result = await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children",param, options);
-          
-          if (result.data) {
-            resultObj.message = "폴더가 생성되었습니다.<br>폴더명 : "+ name;
-            resultObj.success = 'S';
-          }
-          else {
-            resultObj.message = "폴더가 생성되지 않았습니다.";
-          }
+
+      const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + path, options);
+      if (sitesInfo.data && sitesInfo.data.id) {
+        const itemId = sitesInfo.data.id;
+        const result = await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children",param, options);
+        
+        if (result.data) {
+          resultObj.message = "폴더가 생성되었습니다.<br>폴더명 : "+ name;
+          resultObj.success = 'S';
         }
         else {
-          resultObj.message = "생성할 폴더 경로를 찾을 수 없습니다.";
-        } 
-      }
-      catch(error) {
-        resultObj.message = "폴더 생성 중 오류가 발생하였습니다.<br>" + getErrorMessage(error);
+          resultObj.message = "폴더가 생성되지 않았습니다.";
+        }
       }
+      else {
+        resultObj.message = "생성할 폴더 경로를 찾을 수 없습니다.";
+      } 
 
       res.json(resultObj);
-});
+}));
 
 function getErrorMessage(error) {   
   let errorText = "";
@@ -322,46 +315,45 @@ function getErrorMessage(error) {
 
 
 serverApp.post('/api/makeFile', 
-    async (req, res, next)=>{
-      const result = await createNewFile(req);
-      return res.json(result);
-})
-
-async function createNewFile(req) {
-  const resultObj = {message:'', success: 'F'};
-  const {siteId, path, name, fileName, ext} = req.body;
-  try{
-    const options = getOptions(req.session.accessToken);
-    
-    const typeName = fileName.substring(0, fileName.indexOf(' '));
-    const newFile = fs.readFileSync('./src/static/template/template' + ext);
-    const base64EncodeData = Buffer.from(newFile).toString('base64');
-    console.log(name);
-    const labelData = await setLabel(gateWayKey, req.session.account.idTokenClaims.email, name, 
-                                    "878173ae-cc36-4881-af57-604af868314c", base64EncodeData, "Make New " + fileName);
-    if (labelData.success === 'F') {
-      throw labelData.message;
-    }
-    
-    const parentData = await axios.get(endPoint + "/sites/"+ siteId +path, options);
-    if (parentData.data) {
-      const result = await axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+parentData.data.id+':/'+name+':/content', labelData.data, options);
-      if (result.data) {
-        resultObj.message ='요청하신 ' + typeName + ' 파일이 생성 되었습니다.';
-        resultObj.success = 'S';
-      } 
-      else{
-        resultObj.message = '요청하신 ' + typeName + ' 파일이 생성 되지 않았습니다.';
+  errorHandler(async (req, res, next)=>{
+      const resultObj = {message:'', success: 'F'};
+      const {siteId, path, name, fileName, ext} = req.body;
+      const options = getOptions(req.session.accessToken);
+      
+      const typeName = fileName.substring(0, fileName.indexOf(' '));
+      const newFile = fs.readFileSync('./src/static/template/template' + ext);
+      const base64EncodeData = Buffer.from(newFile).toString('base64');
+      const labelData = await setLabel(gateWayKey, req.session.account.idTokenClaims.email, name, 
+                                      "878173ae-cc36-4881-af57-604af868314c", base64EncodeData, "Make New " + fileName);
+      if (labelData.success === 'F') {
+        throw new Error(labelData.message);
       }
-    }
-  }
-  catch(error) {
-    resultObj.message = "요청하신 " + typeName + " 파일 생성 중 오류가 발생하였습니다.<br>" + getErrorMessage(error);
-  }
-  return resultObj;
-}
+      
+      const parentData = await axios.get(endPoint + "/sites/"+ siteId +path, options);
+      if (parentData.data) {
+        const result = await axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+parentData.data.id+':/'+name+':/content', labelData.data, options);
+        if (result.data) {
+          resultObj.message ='요청하신 ' + typeName + ' 파일이 생성 되었습니다.';
+          resultObj.success = 'S';
+        } 
+        else{
+          resultObj.message = '요청하신 ' + typeName + ' 파일이 생성 되지 않았습니다.';
+        }
+      }
+      return res.json(resultObj);
+}));
 
 
+/**
+ * 레이블 생성
+ * @param {*} apiKey 레이블 생성 키 
+ * @param {*} email 로그인 ID
+ * @param {*} dispFileName 파일명
+ * @param {*} aipGuid AIP GUID (UUID)
+ * @param {*} fileData 파일 데이터
+ * @param {*} comment 메모
+ * @returns 
+ */
 async function setLabel(apiKey, email, dispFileName, aipGuid, fileData, comment) {
   const param = {
     apiKey: apiKey,
@@ -377,61 +369,41 @@ async function setLabel(apiKey, email, dispFileName, aipGuid, fileData, comment)
     data : null,
   }
   const errorMessage = '레이블 적용 중 오류가 발생했습니다.<br>에러 : ';
-  try {
-
+  // try {
     const result = await axios.post(apiUrl + '/api/v1/stream/set-label', param);
     if (result && result.data) {
-      if (result.data.statusCode === 200 && !result.data.result.errorCode && result.data.result.fileData) {
+      if (result.data.success === true && !result.data.errorCode && result.data.result && result.data.result.fileData) {
         resultObj.data = Buffer.from(result.data.result.fileData, "base64");
         resultObj.success = 'S';
       }
       else {
-        resultObj.message = errorMessage + result.data.result.errorMessage;
+        let name = '';
+        if (result.data.result && result.data.result.dispFileName) {
+          name = ' (파일명 : ' + result.data.result.dispFileName + ')';
+        }
+        throw new LabelBadResponse(result.data.errorMessage + name);
+        // resultObj.message = errorMessage + result.data.result.errorMessage;
       }
     }
     else {
       resultObj.message = errorMessage + '수신 데이터 없음';
     }
-  }
-  catch (error) {
-    resultObj.message = errorMessage + JSON.stringify(error);
-  }
+  // }
+  // catch (error) {
+  //   resultObj.message = errorMessage + JSON.stringify(error);
+  // }
 
   return resultObj;
 }
 
-serverApp.post('/api/check-name', 
-    async (req, res, next)=>{
-
-      const options = getOptions(req.session.accessToken);
-      const {siteId, path, name} = req.body;
-      try{
-        const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + path, options);
-        if (sitesInfo.data) {
-          const itemId = sitesInfo.data.id;
-          const result = await axios.get(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", options);
-          if (result && result.data && result.data.value) {
-            let idx = result.data.value.findIndex(obj=>obj.name === name);
-            res.json({hasName: (idx > -1)});
-          }
-        }
-      }
-      catch(error) {
-        if (error.response) {
-          console.log("error.response", error.response);
-          const statusCode = err.response.status; // 400
-          const statusText = err.response.statusText; // Bad Request
-          const message = err.response.data.message[0]; // id should not be empty
-          console.log(`${statusCode} - ${statusText} - ${message}`);
-          res.json(error.response);
-        }
-        else {
-          console.log(error);
-          res.json(error);
-        }
-      }
-});
-
+/**
+ * 폴더 생성
+ * @param {*} options TEAMS API 호출 Options 
+ * @param {*} folder 생성 폴더 객체
+ * @param {*} siteId SITE ID
+ * @param {*} path SITE ROOT PATH
+ * @returns 
+ */
 async function uploadDrive(options, folder, siteId, path) {
   const startTime = new Date();
   const resultObj = {
@@ -452,6 +424,7 @@ async function uploadDrive(options, folder, siteId, path) {
       // '@microsoft.graph.conflictBehavior': 'rename'
     };
 
+    //SITE PATH 형식 /sites/{siteId}/drive/root(루트) => /sites/{siteId}/drive/root:/{folderPath}(루트하위) 
     let folderPath = '';
     if (fileInfo.path) {
       folderPath = fileInfo.path; 
@@ -494,12 +467,14 @@ async function uploadDrive(options, folder, siteId, path) {
   return resultObj;
 }
 
+/**
+ * 파일 업로드 API
+ */
 serverApp.post('/api/upload', upload.array('file'),
-  async (req, res, next)=>{
+  errorHandler(async (req, res, next)=>{
     const startTime = new Date();
     const files = req.files;
     let {siteId, path, folder, file_path} = req.body; 
-    try {
     
       if (siteId && path) {
         const options = getOptions(req.session.accessToken);
@@ -597,78 +572,82 @@ serverApp.post('/api/upload', upload.array('file'),
               }
             }
 
-                // const siteInfos = await Promise.all(siteInfoArr);
-                // siteInfos.forEach((obj)=>{
-                //   promiseArray.push(endPoint + "/sites/"+ siteId +"/drive/items/"+obj.data.id+':/');
-                // })
-                //file write 시 사용
-                //const writeArr = await Promise.all(fileWriteArray);
-
-                let writeTime = timeCheck(fileWriteTime, new Date());
-                console.log('파일 Write Time : '+ writeTime);
-                
-
-                const labelTime = new Date();
-                const labelResult = await Promise.all(labelPromiseArray);
-                // console.log(labelResult[0].data.result.fileData);
-                console.log('label time : ', timeCheck(labelTime, new Date()));
-                for (let idx in labelResult) {
-                  const obj = labelResult[idx];
-                  if (obj.data && !obj.data.result.errorCode && obj.data.result.fileData) {
-                    const data = Buffer.from(obj.data.result.fileData, 'base64');
-                    // promiseArray[idx] = new Promise(async (resolve)=> resolve(await axios.put(promiseArray[idx] + obj.data.result.dispFileName + ':/content', data, options)));
-                    promiseArray[idx] = axios.put(promiseArray[idx] + obj.data.result.dispFileName + ':/content', data, options);
-                    // promiseArray.push(axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+ siteInfos[idx].data.id + ':/' + obj.data.result.dispFileName + ':/content', data, options));
-                  }
-                  else {
-                    throw '파일명 : '+obj.data.result.dispFileName + '<br>에러 : ' + obj.data.result.errorMessage;
+              // const siteInfos = await Promise.all(siteInfoArr);
+              // siteInfos.forEach((obj)=>{
+              //   promiseArray.push(endPoint + "/sites/"+ siteId +"/drive/items/"+obj.data.id+':/');
+              // })
+              //file write 시 사용
+              //const writeArr = await Promise.all(fileWriteArray);
+
+              let writeTime = timeCheck(fileWriteTime, new Date());
+              console.log('파일 Write Time : '+ writeTime);
+              
+
+              const labelTime = new Date();
+              const labelResult = await Promise.all(labelPromiseArray);
+              // console.log(labelResult[0].data.result.fileData);
+              console.log('label time : ', timeCheck(labelTime, new Date()));
+              for (let idx in labelResult) {
+                const obj = labelResult[idx].data;
+                if (obj && obj.success === true && !obj.errorCode && obj.result.fileData) {
+                  const data = Buffer.from(obj.result.fileData, 'base64');
+                  // promiseArray[idx] = new Promise(async (resolve)=> resolve(await axios.put(promiseArray[idx] + obj.data.result.dispFileName + ':/content', data, options)));
+                  promiseArray[idx] = axios.put(promiseArray[idx] + obj.result.dispFileName + ':/content', data, options);
+                  // promiseArray.push(axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+ siteInfos[idx].data.id + ':/' + obj.data.result.dispFileName + ':/content', data, options));
+                }
+                else {
+                  let name = '';
+                  if (obj.result && obj.result.dispFileName) {
+                    name = ' (파일명 : ' + obj.result.dispFileName + ')';
                   }
+                  throw new LabelBadResponse(obj.data.errorMessage + name);
                 }
-                // labelResult.forEach((obj, idx)=>{
-                //   if (obj.data && !obj.data.result.errorCode && obj.data.result.fileData) {
-                //     const data = Buffer.from(obj.data.result.fileData, 'base64');
-                //     promiseArray[idx] = axios.put(promiseArray[idx] + obj.data.result.dispFileName + ':/content', data, options);
-                //     // promiseArray.push(axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+ siteInfos[idx].data.id + ':/' + obj.data.result.dispFileName + ':/content', data, options));
-                //   }
-                //   else {
-                //     throw '파일명 : '+obj.data.result.dispFileName + '<br>에러 : ' + obj.data.result.errorMessage;
-                //   }
-                // })
-                // const readArray = [];
-                // labelResult.forEach((obj)=>{
-                //   readArray.push(new Promise((resolve, reject)=>{
-                //       if (obj.data && !obj.data.result.errorCode) {
-                //         fs.readFile('Z:/Target/' + obj.data.result.outputFileName, (err, data)=>{
-                //           if (err) {
-                //             reject({err}); 
-                //           }
-                //           resolve({data});
-                //           //promiseArray[idx] = axios.put(promiseArray[idx], data, options);
-                //         });
-                //       }
-                //       else {
-                //         reject(obj.data.result.errorMessage);
-                //       }
-                //   }))
-                // });
-                // console.log('labelResult : ', labelResult);
-                // let readTime = new Date();
-                // const readResult = await Promise.all(readArray);
-
-                // console.log(readResult);
-                // console.log('File Read time : ', timeCheck(readTime, new Date()));
-                
-
-                let uploadTime = new Date();
-                // promiseArray.forEach((obj, idx)=>{
-                //   // console.log(readResult[idx].data);
-                //   // console.log(obj[idx]);
-                //   promiseArray[idx] = axios.put(obj, readResult[idx].data, options); 
-                // });
-
-                const uploadResult = await Promise.all(promiseArray);
-                
-                console.log('File uploadTime : ', timeCheck(uploadTime, new Date()));
+              }
+              // labelResult.forEach((obj, idx)=>{
+              //   if (obj.data && !obj.data.result.errorCode && obj.data.result.fileData) {
+              //     const data = Buffer.from(obj.data.result.fileData, 'base64');
+              //     promiseArray[idx] = axios.put(promiseArray[idx] + obj.data.result.dispFileName + ':/content', data, options);
+              //     // promiseArray.push(axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+ siteInfos[idx].data.id + ':/' + obj.data.result.dispFileName + ':/content', data, options));
+              //   }
+              //   else {
+              //     throw '파일명 : '+obj.data.result.dispFileName + '<br>에러 : ' + obj.data.result.errorMessage;
+              //   }
+              // })
+              // const readArray = [];
+              // labelResult.forEach((obj)=>{
+              //   readArray.push(new Promise((resolve, reject)=>{
+              //       if (obj.data && !obj.data.result.errorCode) {
+              //         fs.readFile('Z:/Target/' + obj.data.result.outputFileName, (err, data)=>{
+              //           if (err) {
+              //             reject({err}); 
+              //           }
+              //           resolve({data});
+              //           //promiseArray[idx] = axios.put(promiseArray[idx], data, options);
+              //         });
+              //       }
+              //       else {
+              //         reject(obj.data.result.errorMessage);
+              //       }
+              //   }))
+              // });
+              // console.log('labelResult : ', labelResult);
+              // let readTime = new Date();
+              // const readResult = await Promise.all(readArray);
+
+              // console.log(readResult);
+              // console.log('File Read time : ', timeCheck(readTime, new Date()));
+              
+
+              let uploadTime = new Date();
+              // promiseArray.forEach((obj, idx)=>{
+              //   // console.log(readResult[idx].data);
+              //   // console.log(obj[idx]);
+              //   promiseArray[idx] = axios.put(obj, readResult[idx].data, options); 
+              // });
+
+              const uploadResult = await Promise.all(promiseArray);
+              
+              console.log('File uploadTime : ', timeCheck(uploadTime, new Date()));
           }
           const endTime = new Date();
           let betweenTime = timeCheck(startTime, endTime);
@@ -676,15 +655,16 @@ serverApp.post('/api/upload', upload.array('file'),
           res.json({success:'S', message: '요청하신 파일 업로드가 정상적으로 처리 되었습니다.'});
       }
       else {
-        return res.json({success:'F', message: '업로드 요청 파라미터 정보를 확인해 주세요.'});
+        throw new NotFound(`업로드 요청 파라미터 정보를 확인해 주세요.<br>siteId : ${siteId}, path : ${path}`);
       }
-    }
-    catch(error) {
-      console.log(error);
-      res.json({success:'F', message: '파일 업로드 작업 중 오류가 발생했습니다.<br>에러 : ' + JSON.stringify(error.message)});
-    }
-});
+}));
 
+/**
+ * 시간 차 계산기
+ * @param {*} startTime 시작 시간 
+ * @param {*} endTime 종료 시간
+ * @returns 
+ */
 function timeCheck(startTime, endTime) {
   let betweenTime = endTime - startTime;
   if (betweenTime > 60000) {
@@ -696,10 +676,12 @@ function timeCheck(startTime, endTime) {
   return betweenTime;
 }
 
-
-serverApp.post('/api/download',
-  async (req, res, next)=>{
-    try {
+/**
+ * 파일 다운로드 API
+ */
+serverApp.post('/api/download', 
+  errorHandler(async (req, res, next)=>{
+    // try {
       let startTime = new Date();
       const {siteId, path, fileIds, zipName} = req.body;
       if (siteId && path && fileIds) {
@@ -722,20 +704,29 @@ serverApp.post('/api/download',
               fileData: Buffer.from(bufferData).toString('base64')
             }
             const deleteLabelResult = await axios.post(apiUrl + '/api/v1/stream/delete-label', param);
-            if (deleteLabelResult && deleteLabelResult.data && !deleteLabelResult.data.result.errorCode) {
-              const fileData = deleteLabelResult.data.result.fileData;
-              return res.json({success: 'S', data: fileData, name: deleteLabelResult.data.result.dispFileName})
+            const data = deleteLabelResult.data;
+            if (data && data.success === true && !data.errorCode && data.result.fileData) {
+              const fileData = data.result.fileData;
+              return res.json({success: 'S', data: fileData, name: data.result.dispFileName})
+              // return res.json({success: 'S', data: fileData, name: data.result.dispFileName})
+              // res.setHeader('Content-disposition', 'attachment; filename=' + data.result.dispFileName);
+              // res.setHeader('Content-type', mime.lookup(data.result.dispFileName));
+              // res.pipe(fileData);
             }
             else {
-              throw deleteLabelResult.data.result.dispFileName + ' : ' +deleteLabelResult.data.result.errorMessage;
+              let name = '';
+              if (data.result && data.result.dispFileName) {
+                name = ' (파일명 : ' + data.result.dispFileName + ')';
+              }
+              throw new LabelBadResponse(data.errorMessage + name);
             }
-          }
+          } 
           //폴더 또는 다중 파일 다운로드 
           else {
             const zip = new JSZIP();
             const timer = new Date();
-            console.log('getFolderItems Start : ', timer.toLocaleString());
-            await getFolderItems(url, arr, options, req.session, zip);
+            console.log('getFolderItems Start : ', timer.toLocaleString()); 
+            await getFolderItems(url, arr, req.session, zip);
             const timerEnd = new Date();
             console.log('getFolderItems end : ', timerEnd.toLocaleString(), ', 소요시간 : '+ timeCheck(timer,timerEnd));
             const now = new Date();
@@ -762,16 +753,25 @@ serverApp.post('/api/download',
       else {
         return res.json({message:'다운로드 파일 정보를 확인 할 수 없습니다.', success:'F'})
       }
-    }
-    catch(error) {
-      return res.json({success: 'F', message:'파일 다운로드 중 오류가 발생하였습니다.<br>'+ getErrorMessage(error)});
-    }
+    // }
+    // catch(error) {
+    //   return res.json({success: 'F', message:'파일 다운로드 중 오류가 발생하였습니다.<br>'+ getErrorMessage(error)});
+    // }
   }
-);
-
-async function getFolderItems(url, array, options, session, zip) {
-  const fileArr       = [];
-  const fileInfoArr   = [];
+));
+
+/**
+ * 다운로드 할 모든 파일, 폴더를 zip 객체에 세팅 
+ * @param {*} url 루트 주소
+ * @param {*} array 조회 객체 Array
+ * @param {*} session 세션
+ * @param {*} zip zip 객체(zip.folder(이름).folder(이름)... 으로 폴더 구분)
+ */
+async function getFolderItems(url, array, session, zip) {
+  const fileArr     = [];
+  const fileInfoArr = [];
+  const options     = getOptions(session.accessToken);
+  const email       = session.account.idTokenClaims.email;
   await Promise.all(array.map((obj)=>{
     return axios.get(url + obj.id, options);
   }))
@@ -785,8 +785,7 @@ async function getFolderItems(url, array, options, session, zip) {
             if (data.folder.childCount) {
               const itemsData = await axios.get(url + data.id + '/children', options);
               if (itemsData && itemsData.data && itemsData.data.value.length > 0) {
-                  promiseArray.push(new Promise((resolve)=>resolve(getFolderItems(url, itemsData.data.value, options, session, zip.folder(data.name)))));
-                  // promiseArray.push(getFolderItems(url, itemsData.data.value, options, session, zip.folder(data.name)));
+                  promiseArray.push(getFolderItems(url, itemsData.data.value, session, zip.folder(data.name)));
               }
             }
           }
@@ -798,7 +797,7 @@ async function getFolderItems(url, array, options, session, zip) {
               fileInfoArr.push(data);
             }
             else {
-              throw '파일 명 : ' + data.name + '<br>파일 데이터 정보가 없습니다.';
+              throw new NotFound('파일 데이터 정보가 없습니다. (' + data.name + ')');
             }
           }
         }
@@ -819,7 +818,7 @@ async function getFolderItems(url, array, options, session, zip) {
         const data = result[idx];
         const param = {
           apiKey: gateWayKey,
-          email : session.account.idTokenClaims.email,
+          email : email,
           dispFileName: fileInfoArr[idx].name,
           comment: "",
           fileData: Buffer.from(data.data).toString('base64')
@@ -834,24 +833,33 @@ async function getFolderItems(url, array, options, session, zip) {
       return Promise.all(
         deleteLabelFiles.map((item)=>{
             const data = item.data;
-            if (data.success && !data.result.errorCode) {
+            if (data.success && !data.errorCode && data.result && data.result.fileData) {
               zip.file(data.result.dispFileName, data.result.fileData, {base64: true});
             }
             else {
-              throw data.result.dispFileName + ' : ' + data.result.errorMessage;
+              let name = '';
+              if (data.result && data.result.dispFileName) {
+                name = ' (파일명 : ' + data.result.dispFileName + ')';
+              }
+              throw new LabelBadResponse(data.errorMessage + name);
             }
         }))
     }
   });
 }
 
-
+/**
+ * 업로드 가능 파일 확장자 API
+ */
 serverApp.post('/api/getPossibleExtList', async (req, res, next)=>{
     return res.json(POSSIBLE_EXT);
 })
 
+/**
+ * 파일 삭제 API
+ */
 serverApp.post('/api/delete', 
-  async (req, res, next)=>{
+  errorHandler(async (req, res, next)=>{
     if (req.body) {
       const {siteId, itemIds} = req.body;
       const resultObj = {success: '', message:''};
@@ -859,21 +867,23 @@ serverApp.post('/api/delete',
         const itemIdArr = JSON.parse(itemIds);
         if (itemIdArr.length > 0) {
           const options = getOptions(req.session.accessToken);
+          const deleteArr = [];
           for (let itemId of itemIdArr) {
-            try{
-              await axios.delete(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, options);
-            }
-            catch(error) {
-              resultObj.success = 'F';
-              let message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.';
-              if (error && error.response && error.response.data && error.response.data.error && error.response.data.error.message) {
-                console.log(error.response.data.error.message);
-                message += '<br>' + error.response.data.error.message;
-              }
-              resultObj.message = message;
-              return res.json(resultObj);
-            }
+            // try{
+              deleteArr.push(axios.delete(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, options));
+            // }
+            // catch(error) {
+            //   resultObj.success = 'F';
+            //   let message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.';
+            //   if (error && error.response && error.response.data && error.response.data.error && error.response.data.error.message) {
+            //     console.log(error.response.data.error.message);
+            //     message += '<br>' + error.response.data.error.message;
+            //   }
+            //   resultObj.message = message;
+            //   return res.json(resultObj);
+            // }
           }
+          await Promise.all(deleteArr);
           resultObj.success = 'S';
           resultObj.message = '파일 정보가 삭제되었습니다.';
           res.json(resultObj);
@@ -885,27 +895,27 @@ serverApp.post('/api/delete',
         res.json(resultObj);
       }
     }
-  })
+  }));
 
-serverApp.post('/api/update-name', 
-  // isAuthenticated,
-  // isAccessTokens,
-  async (req, res, next)=>{
+/**
+ * 파일명 변경 API
+ */
+serverApp.post('/api/update-name', errorHandler(async (req, res, next)=>{
     if (req.body) {
       const {siteId, itemId, name} = req.body;
       const resultObj = {success: '', message:''};
       if (siteId && itemId && name) {
         const options = getOptions(req.session.accessToken);
-          try{
-            await axios.patch(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, {name : name}, options);
-          }
-          catch(error) {
-            console.log(error.response.data.error); 
+          // try{
+          await axios.patch(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, {name : name}, options);
+          // }
+          // catch(error) {
+          //   console.log(error.response.data.error); 
             
-            resultObj.success = 'F';
-            resultObj.message = '선택하신 파일의 이름 변경 중 오류가 발생하였습니다.<br>' + error.response.data.error.message;
-            return res.json(resultObj);
-          }
+          //   resultObj.success = 'F';
+          //   resultObj.message = '선택하신 파일의 이름 변경 중 오류가 발생하였습니다.<br>' + error.response.data.error.message;
+          //   return res.json(resultObj);
+          // }
           resultObj.success = 'S';
           resultObj.message = '선택하신 파일의 이름이 변경되었습니다.';
           return res.json(resultObj);
@@ -916,12 +926,12 @@ serverApp.post('/api/update-name',
         resultObj.message = '파라미터 정보를 확인해주세요.';
         return res.json(resultObj);
       }
-  });
+  }));
   
-serverApp.post('/api/move-item', 
-  // isAuthenticated,
-  // isAccessTokens,
-  async (req, res, next)=>{
+/**
+ * 파일 이동 API
+ */
+serverApp.post('/api/move-item', errorHandler(async (req, res, next)=>{
     if (req.body) {
       const {id, name, siteId, text} = req.body;
       const resultObj = {message:'', successItems : [], failItems : [], locations: []};
@@ -959,88 +969,63 @@ serverApp.post('/api/move-item',
       }
       return res.json(resultObj);
     }
-});
+}));
   
-serverApp.post('/api/copy-item', 
-// isAuthenticated,
-// isAccessTokens,
-async (req, res, next)=>{
-  if (req.body) {
-    const {id, name, siteId, driveId, text} = req.body;
-    const resultObj = {message:'', successItems : [], failItems : [], locations: []};
-    if (name && isNaN(name)) {
-      const nameArray = JSON.parse(name);
-      const options = getOptions(req.session.accessToken);
+/**
+ * 파일 복사 API
+ */
+serverApp.post('/api/copy-item', errorHandler(async (req, res, next)=>{
+    if (req.body) {
+      const {id, name, siteId, driveId, text} = req.body;
+      const resultObj = {message:'', successItems : [], failItems : [], locations: []};
+      if (name && isNaN(name)) {
+        const nameArray = JSON.parse(name);
+        const options = getOptions(req.session.accessToken);
 
-      if (nameArray && nameArray.length > 0) {
-        for (let moveItem of nameArray) {
-          if (moveItem) {
-            const param = {
-              parentReference: {
-                id: id,
-                driveId : driveId,
-              },
-              name: moveItem.name
-            };
-            try {
-              const result = await axios.post(`${endPoint}/sites/${siteId}/drive/items/${moveItem.id}/copy`, param, options);
-              if (result) {
-                resultObj.successItems.push(moveItem);
-                resultObj.locations.push(result.headers.location);
+        if (nameArray && nameArray.length > 0) {
+          for (let moveItem of nameArray) {
+            if (moveItem) {
+              const param = {
+                parentReference: {
+                  id: id,
+                  driveId : driveId,
+                },
+                name: moveItem.name
+              };
+              try {
+                const result = await axios.post(`${endPoint}/sites/${siteId}/drive/items/${moveItem.id}/copy`, param, options);
+                if (result) {
+                  resultObj.successItems.push(moveItem);
+                  resultObj.locations.push(result.headers.location);
+                }
+              }
+              catch (error) {
+                // console.log(error);
+                console.log(error.message);
+                console.log(error.name);
+                console.log(error.errors);
+                resultObj.failItems.push(moveItem);
               }
-            }
-            catch (error) {
-              // console.log(error);
-              console.log(error.message);
-              console.log(error.name);
-              console.log(error.errors);
-              resultObj.failItems.push(moveItem);
             }
           }
+          resultObj.message = `요청 하신 ${nameArray.length} 개 파일 중 ${ resultObj.successItems.length} 개 파일이 ${text} 되었습니다.`;
         }
-        resultObj.message = `요청 하신 ${nameArray.length} 개 파일 중 ${ resultObj.successItems.length} 개 파일이 ${text} 되었습니다.`;
       }
+      return res.json(resultObj);
     }
-    return res.json(resultObj);
-  }
-});
+}));
   
-serverApp.post('/api/loading', 
-  async (req, res, next)=>{
-    if (req.body) {
-      const {url} = req.body;
-      if (url) {
-        try {
-          const result = await axios.get(`${url}`);
-          if (result && result.data) {
-            console.log(result.data);
-            return res.json(result.data);
-          }
-        }
-        catch (error) {
-          // console.log(error);
-          console.log(error.message);
-          console.log(error.name);
-          console.log(error.errors);
-          return res.json(error);
-        }
-      }
-  }
-});
-
-
 
-serverApp.post('/api/add-tab', 
-// isAuthenticated,
-// isAccessTokens,
-async (req, res, next)=>{
-  if (req.body) {
+/**
+ * 탭추가 API
+ */
+serverApp.post('/api/add-tab', errorHandler(async (req, res, next)=>{
+  if (req.body) {  
     const {name, siteId, path, teamId, teamName, bindId} = req.body;
     const resultObj = {message:'', success: 'F'};
     if (name && siteId && path && teamId && teamName) {
       const options = getOptions(req.session.accessToken);
 
-      try {
         const teamInfo = await axios.get(`${endPoint}/teams/${teamId}/channels`, options);
         const sharePoint = await axios.get(`${endPoint}/sites/${siteId}/drive`, options);
         if (teamInfo.data && teamInfo.data.value && sharePoint.data) {
@@ -1074,25 +1059,10 @@ async (req, res, next)=>{
             }
           }
         }
-      }
-      catch (error) {
-        resultObj.message = `채널명 : ${channelName}<br>파일명 : ${name}<br>요청하신 채널에 탭으로 설정 중 오류가 발생하였습니다.<br>` + getErrorMessage(error);
-      }
     }
     return res.json(resultObj);
   }
-});
-
-// // 데이터베이스 연결
-// pool.connect((err) => {
-//   // 연결이 안될 경우 에러 내용 콘솔에 출력
-//   if (err) {
-//       console.error('Error connecting to database:', err);
-//       return;
-//   }
-//   // 연결에 성공할 경우 연결 성공 메시지 콘솔에 출력
-//   console.log('Connected to database');
-// });
+}));
 
 function getOptions(token) {
   const options = {
@@ -1103,19 +1073,32 @@ function getOptions(token) {
   return options;
 }
 
+/**
+ * 에러 Handling
+ * @param {*} fn 함수 
+ * @returns 
+ */
+function errorHandler(fn) {
+  return function (req, res, next) {
+    fn(req, res, next).catch(next)
+  }
+}
+
 serverApp.use((err, req, res, next)=>{
-  console.log(err.message);
-  // console.log(err);
-  console.log(res.statusCode);
   if (err) {
-    res.status(400);
-    res.json({message: err.message});
-  } else {
-    res.status(500);
-    res.json({message: err.message});
+    console.error(err);
+    return res.status(err.status || 500).json({ 
+      name: err.name || 'Internal Server Error',
+      message: err.message || '서버 내부에서 오류가 발생했습니다.'
+    });
   }
 })
 
-server.listen(SERVER_PORT, function () {
+server.listen(SERVER_PORT, async function () {
   console.log(`\n${serverApp.name} listening to ${SERVER_PORT}`);
+  POSSIBLE_EXT = await init();
+  setInterval(async ()=>{
+    POSSIBLE_EXT = await init();
+    // console.log('POSSIBLE EXT UPDATE');
+  }, 60 * 3 * 1000); 
 }); 

+ 8 - 0
src/error/badRequest.js

@@ -0,0 +1,8 @@
+class BadRequest extends Error {
+    status = 400;
+    constructor(message = '잘못된 요청입니다.') {
+        super(message);
+        this.name = 'Bad Request';
+    }
+}
+module.exports = BadRequest;

+ 8 - 0
src/error/expiredToken.js

@@ -0,0 +1,8 @@
+class ExpiredToken extends Error {
+    status = 419;
+    constructor(message = '유효하지 않은 토큰입니다.') {
+        super(message);
+        this.name = 'Expired Token';
+    }
+}
+module.exports = ExpiredToken;

+ 8 - 0
src/error/labelBadResponse.js

@@ -0,0 +1,8 @@
+class LabelBadResponse extends Error {
+    status = 500;
+    constructor(message = '레이블 API 오류입니다.') {
+        super(message);
+        this.name = '레이블 작업 오류';
+    }
+}
+module.exports = LabelBadResponse;

+ 8 - 0
src/error/notFound.js

@@ -0,0 +1,8 @@
+class NotFound extends Error {
+    status = 405;
+    constructor(message = '데이터를 찾을 수 없습니다.') {
+        super(message);
+        this.name = 'Not Found Error';
+    }
+}
+module.exports = NotFound;

+ 12 - 7
src/views/hello.html

@@ -1297,7 +1297,11 @@
         console.log('=============================== Axios Error ===============================')
         console.log(error.response);
         console.log(error.response.data.error);
-        errorText = `status : ${error.response.status} - ${error.response.statusText}<br>message : ${error.response.data.error.message}`;
+        errorText = `status : ${error.response.status}<br>statusText : ${error.response.statusText}<br>message : ${error.response.data.error.message}`;
+      }
+      else if (error.responseJSON && error.status && error.statusText) {
+        console.log(error);
+        errorText = `status : ${error.status }<br>statusText : ${error.responseJSON.name}<br>message : ${error.responseJSON.message}`; 
       }
       else {
         console.log('================================ Error =====================================')
@@ -1565,15 +1569,15 @@
                 let idx = fileArr.findIndex(obj => obj === fileName); 
                 if (idx >= 0) {
                   result.push({success:'F', message:'업로드 경로에 이미 같은 이름의 파일이 있습니다.<br>파일을 업로드 하려면 기존 파일의 명칭을 변경해주세요.<br>파일명 : '+ fileName});
-                  resolve({success:'F', message:'업로드 경로에 이미 같은 이름의 파일이 있습니다.<br>파일을 업로드 하려면 기존 파일의 명칭을 변경해주세요.<br>파일명 : '+ fileName});
-                  // return alertMessage('파일 업로드','업로드 경로에 이미 같은 이름의 파일이 있습니다.<br>파일을 업로드 하려면 기존 파일의 명칭을 변경해주세요.<br>파일명 : '+ fileName);
+                  resolve();
                 }
               }
               _formData.append('file', file);
               _formData.append(fileName + '_path', path);
               _formData.append('file_path', path);
               result.push({success: 'S', message:''});
-              resolve({success: 'S', message:''});
+              // resolve({success: 'S', message:''});
+              resolve();
             });
           } 
           else if (item.isDirectory) {
@@ -1583,7 +1587,7 @@
               let idx = folderArr.findIndex(obj => obj === item.name);
               if (idx >= 0) {
                 result.push({success:'F', message:'업로드 경로에 이미 같은 이름의 폴더가 있습니다.<br>파일을 업로드 하려면 기존 폴더 명칭을 변경해주세요.<br>폴더명 : '+ item.name});
-                resolve({success:'F', message:'업로드 경로에 이미 같은 이름의 폴더가 있습니다.<br>폴더를 업로드 하려면 기존 폴더 명칭을 변경해주세요.<br>폴더명 : '+ item.name});
+                resolve();
               }
             }
             _formData.append('folder', JSON.stringify({name : item.name, path : path}));
@@ -2191,11 +2195,12 @@
       success : (res)=> {
           if (res.success === 'S') {
             refreshDrives();
+            modalClose('name');
           }
-          alertMessage('이름 변경',res.message);
+          alertMessage('이름 변경', res.message);
       },
       error : (error) => {
-          alertMessage('이름 변경','이름 변경 중 오류가 발생하였습니다.<br>' + getErrorMessage(error));
+          alertMessage('이름 변경','이름 변경 중 오류가 발생하였습니다.<br>' + getErrorMessage(error), null, 'red');
       }
     })
   }