junggilpark 1 anno fa
parent
commit
26aff9e946

+ 101 - 0
package-lock.json

@@ -23,6 +23,7 @@
                 "express": "^4.19.2",
                 "express-session": "^1.18.0",
                 "fs": "^0.0.1-security",
+                "multer": "^1.4.5-lts.1",
                 "path": "^0.12.7",
                 "restify": "^11.1.0",
                 "send": "^0.18.0",
@@ -517,6 +518,11 @@
                 "node": ">= 8"
             }
         },
+        "node_modules/append-field": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+            "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
+        },
         "node_modules/archiver": {
             "version": "7.0.1",
             "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
@@ -775,6 +781,22 @@
             "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
             "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
         },
+        "node_modules/buffer-from": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+            "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+        },
+        "node_modules/busboy": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+            "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+            "dependencies": {
+                "streamsearch": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=10.16.0"
+            }
+        },
         "node_modules/bytes": {
             "version": "3.1.2",
             "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -881,6 +903,47 @@
             "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
             "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
         },
+        "node_modules/concat-stream": {
+            "version": "1.6.2",
+            "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+            "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+            "engines": [
+                "node >= 0.8"
+            ],
+            "dependencies": {
+                "buffer-from": "^1.0.0",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.2.2",
+                "typedarray": "^0.0.6"
+            }
+        },
+        "node_modules/concat-stream/node_modules/readable-stream": {
+            "version": "2.3.8",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+            "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+            "dependencies": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+            }
+        },
+        "node_modules/concat-stream/node_modules/safe-buffer": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "node_modules/concat-stream/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
         "node_modules/content-disposition": {
             "version": "0.5.4",
             "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -2372,6 +2435,23 @@
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
             "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
         },
+        "node_modules/multer": {
+            "version": "1.4.5-lts.1",
+            "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
+            "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
+            "dependencies": {
+                "append-field": "^1.0.0",
+                "busboy": "^1.0.0",
+                "concat-stream": "^1.5.2",
+                "mkdirp": "^0.5.4",
+                "object-assign": "^4.1.1",
+                "type-is": "^1.6.4",
+                "xtend": "^4.0.0"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
         "node_modules/nan": {
             "version": "2.19.0",
             "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz",
@@ -3266,6 +3346,14 @@
             "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.1.tgz",
             "integrity": "sha512-BL8pv9QL8Ikd11oZwlRDp1qYMhGR0i50zI9ltoijKGc4ubQWal/Rc4p6SYJp1TBOGpE0uAGchwbxOZ1ycwTuqQ=="
         },
+        "node_modules/streamsearch": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+            "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+            "engines": {
+                "node": ">=10.0.0"
+            }
+        },
         "node_modules/streamx": {
             "version": "2.16.1",
             "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz",
@@ -3458,6 +3546,11 @@
                 "node": ">= 0.6"
             }
         },
+        "node_modules/typedarray": {
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+            "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+        },
         "node_modules/uid-safe": {
             "version": "2.1.5",
             "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
@@ -3676,6 +3769,14 @@
             "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
             "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
         },
+        "node_modules/xtend": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+            "engines": {
+                "node": ">=0.4"
+            }
+        },
         "node_modules/yallist": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
         "express": "^4.19.2",
         "express-session": "^1.18.0",
         "fs": "^0.0.1-security",
+        "multer": "^1.4.5-lts.1",
         "path": "^0.12.7",
         "restify": "^11.1.0",
         "send": "^0.18.0",

+ 225 - 51
src/app.js

@@ -6,15 +6,34 @@ const express = require("express");
 const axios = require("axios");
 const SERVER_PORT = process.env.port || process.env.PORT || 53000;
 const authProvider = require('./auth/AuthProvider');
-const {fetch, updateFetch} = require('./fetch');
+const {getFetch, updateFetch} = require('./fetch');
 const bodyParser = require('body-parser');
 const https = require('https');
 require('dotenv').config({ path: './env/.env.test' });
 const session = require('express-session');
-const messages = require('dote/src/messages');
+const multer  = require('multer');
+const { throws } = require('assert');
+const storage =  multer.diskStorage({
+  destination: function (req, file, cb) { 
+    cb(null, 'uploads/')  
+  },
+  filename: function (req, file, cb) {
+    cb(null, file.originalname)              // 원래 파일이름으로 저장
+  }
+})
+const upload = multer(storage);
 
 const serverApp = express();
-
+const endPoint = process.env.GRAPH_API_ENDPOINT + 'v1.0';
+Buffer.prototype.toArrayInteger = function(){
+  if (this.length > 0) {
+      const data = new Array(this.length);
+      for (let i = 0; i < this.length; i=i+1)
+          data[i] = this[i];
+      return data;
+  }
+  return [];
+}
 serverApp.use(session({
   secret: process.env.EXPRESS_SESSION_SECRET,
   resave: false,
@@ -97,7 +116,7 @@ serverApp.post("/api-get",
       param = req.session.param;
     }
     try {
-        const graphResponse = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0" + uri, req.session.accessToken, param);
+        const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
         res.json(graphResponse);
     } catch (error) {
         next(error);
@@ -113,7 +132,7 @@ serverApp.get("/api-redirect",
         param = req.session.param;
       }
       try {
-          const graphResponse = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0" + uri, req.session.accessToken, param);
+          const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
           res.json(graphResponse);
       } catch (error) {
           next(error);
@@ -129,7 +148,7 @@ serverApp.get("/post-redirect",
         param = req.session.param;
       }
     try {
-        const graphResponse = await updateFetch(process.env.GRAPH_API_ENDPOINT + "v1.0" + uri, req.session.accessToken, param);
+        const graphResponse = await updateFetch(endPoint + uri, req.session.accessToken, param);
         res.json(graphResponse);
     } catch (error) {
         next(error);
@@ -160,12 +179,11 @@ serverApp.get("/group-redirect",
   async function (req, res, next) {
     // return;
     try {
-        const oneDrive = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/me/drive/root", req.session.accessToken);
-        const sharePointIds = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/me/drive/SharePointIds", req.session.accessToken);
-        // const oneDriveItems = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/me/drive/root/children", req.session.accessToken);
-        const graphResponse = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/me/joinedTeams", req.session.accessToken);
-        const sites = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/root", req.session.accessToken);
-        const sitesSharePoint = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/root/SharePointIds", req.session.accessToken);
+        const oneDrive = await getFetch(endPoint + "/me/drive/root", req.session.accessToken);
+        const sharePointIds = await getFetch(endPoint + "/me/drive/SharePointIds", req.session.accessToken);
+        const graphResponse = await getFetch(endPoint + "/me/joinedTeams", req.session.accessToken);
+        const sites = await getFetch(endPoint + "/sites/root", req.session.accessToken);
+        const sitesSharePoint = await getFetch(endPoint + "/sites/root/SharePointIds", req.session.accessToken);
 
         const teams = graphResponse.value;
         oneDrive.sharePoint = sharePointIds;
@@ -173,7 +191,6 @@ serverApp.get("/group-redirect",
         const resultObj = {
           oneDrive : {
             teams: oneDrive,
-            // items: oneDriveItems.value,
           },
           joinedTeams : {
             teams : teams,
@@ -181,23 +198,9 @@ serverApp.get("/group-redirect",
           },
           sites : {
             teams : sites,
-            // items : {},
           },
         }
-        // if (sites) {
-        //   if (Array.isArray(sites)) {
-        //       for(let site of sites) {
-        //         const siteObj = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+site.id+"/drive/items/root/children", req.session.accessToken);
-        //         resultObj.sites.items[site.id] = siteObj.value;
-        //       }
-        //   }
-        //   else if (sites.id) {
-        //     const sitesObj = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+sites.id+"/drive/items/root/children", req.session.accessToken);
-        //     if (sitesObj) {
-        //       resultObj.sites.items[sites.id] = sitesObj.value;
-        //     }
-        //   }
-        // }
+
         if (teams && teams.length) {
           const options = {
             responseType: 'arraybuffer',
@@ -207,8 +210,8 @@ serverApp.get("/group-redirect",
             },
           };
           for (let team of teams) {
-            const item = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/groups/"+team.id+"/drive/items/root/children", req.session.accessToken);
-            const sharePoint = await fetch(process.env.GRAPH_API_ENDPOINT + "v1.0/groups/"+team.id+"/drive/SharePointIds", req.session.accessToken);
+            const item = await getFetch(endPoint + "/groups/"+team.id+"/drive/items/root/children", req.session.accessToken);
+            const sharePoint = await getFetch(endPoint + "/groups/"+team.id+"/drive/SharePointIds", req.session.accessToken);
             if (sharePoint) {
               team.sharePoint = sharePoint;
             }
@@ -245,10 +248,10 @@ serverApp.post('/makeFolder',
       };
       const {siteId, path, param} = req.body;
       try{
-        const sitesInfo = await axios.get(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+ siteId + path, options);
+        const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + path, options);
         if (sitesInfo.data) {
           const itemId = sitesInfo.data.id;
-          const result = await axios.post(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+ siteId +"/drive/items/" + itemId +"/children", JSON.parse(param), options);
+          const result = await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", JSON.parse(param), options);
           res.json(result.data)
         }
       }
@@ -261,40 +264,211 @@ serverApp.post('/makeFolder',
       }
 })
 
-serverApp.post('/uploadItems', 
+serverApp.post('/uploadItems', upload.array('file'),
   isAuthenticated,
   (req, res, next)=>{
     if (!req.session.accessToken) {
       return authProvider.acquireToken({
         scopes: ['.default'],
         redirectUri: 'https://localhost:53000/redirect',
-        successRedirect: '/uploadItems'
+        successRedirect: '/uploadItems' 
       })(req, res, next);
     }
     next();
   },
   async (req, res, next)=>{
-    if (req.body) {
-      const {siteId, path, param} = req.body;
-      if (siteId && param) {
-        const options = {
-          headers: {
-            Authorization: `Bearer ${req.session.accessToken}`,
-          },
-        };
+    const startTime = new Date();
+    const folderParam = {
+      name: '',
+      folder: { },
+      '@microsoft.graph.conflictBehavior': 'rename'
+    }
 
-        const sitesInfo = await axios.get(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+ siteId + path, options);
-        if (sitesInfo.data) {
-          const itemId = sitesInfo.data.id;
-          const fileInfo = JSON.parse(param);
-          const result = await axios.post(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+ siteId +"/drive/items/"+itemId+':/'+fileInfo.name+':/content', fileInfo, options);
-          res.json(result.data);
+    const files = req.files;
+    let {siteId, path, folder} = req.body;
+
+    if (siteId && path) {
+      const options = {
+        headers: {
+          Authorization: `Bearer ${req.session.accessToken}`,
+        },
+      };
+
+        if (folder) {
+          if (!Array.isArray(folder)) {
+            folder = [folder];
+          }
+          let beforeUri = '';
+          let beforeItemId = '';
+          for (let item of folder) {
+            console.log('beforeUri', beforeUri);
+            console.log('beforeItemId', beforeItemId);
+            const fileInfo = JSON.parse(item);
+            const param = {...folderParam};
+            param.name = fileInfo.name;
+            console.log('param :', param);
+            let folderPath = '';
+            if (fileInfo.path) {
+              folderPath = ':' + fileInfo.path; 
+            }
+            let uri = endPoint + "/sites/"+ siteId + path + folderPath;
+            console.log('folderPath :',folderPath);
+            try {
+              
+              let itemId = '';
+              if (beforeUri === uri) {
+                itemId = beforeItemId;
+              }
+              else {
+                const sitesInfo = await axios.get(uri, options);
+                itemId = sitesInfo.data.id;
+                beforeItemId = itemId;
+                beforeUri = uri;
+              }
+              await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", param, options);
+              
+              // await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", param, options);
+            }
+            catch(error) {
+                console.log(error);
+                // next(error);
+                return res.json({success:'F', message: '요청하신 파일 업로드 중 오류가 발생하였습니다.', error : error});
+            }
+          }
         }
+        if (files && files.length > 0) {
+          const promiseArray = [];
+          let beforeUri = '';
+          let beforeItemId = '';
+          for (let file of files) {
+            const fileName = file.originalname.substring(0, file.originalname.lastIndexOf('.'));
+            let filePath = req.body[ fileName + "_path"];
+            file.originalname = Buffer.from(file.originalname, 'ascii').toString('utf8');
 
+            try { 
+              let formatPath = '';
+              if (filePath) {
+                console.log(filePath);
+                if (Array.isArray(filePath) && filePath.length > 0) {
+                  formatPath = ":" + filePath[0];
+                  if (filePath.length > 1) {
+                    req.body[ fileName + "_path"] = filePath.splice(1); 
+                  }
+                  console.log('splice After', req.body[ fileName + "_path"]);
+                  console.log('formatPath : ', formatPath);
+                }
+                else if (filePath.trim()){
+                  formatPath = ":" + filePath;
+                }
+              }
+              let itemId = '';
+              const uri = endPoint + "/sites/"+ siteId + path + formatPath;
+              if (beforeUri === uri) {
+                itemId = beforeItemId;
+              }
+              else {
+                const sitesInfo = await axios.get(uri, options);
+                itemId = sitesInfo.data.id;
+                beforeUri = uri;
+                beforeItemId = itemId; 
+              }
+              // const uploadUri = endPoint + "/sites/"+ siteId +"/drive/items/"+itemId+':/'+file.originalname+':/content';
+              const fileOptions = { headers: {
+                Authorization: `Bearer ${req.session.accessToken}`,
+                "Content-Type" : file.mimeType
+              }}
+              // promiseArray.push(
+              //   {
+              //     url : uploadUri, 
+              //     mimeType: file.mimetype,
+              //     file : file.buffer,
+              //   }
+              // );
+              // console.log(promiseArray);
+              await axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+itemId+':/'+file.originalname+':/content', file.buffer, fileOptions);
+            }
+            catch(error) {
+              if (error) {
+                if (error.response) {
+                  console.log(error.response.data);
+                } 
+                else {
+                  console.log(error);
+                }
+              }
+              return res.json({success:'F', message: '요청하신파일 업로드 중 오류가 발생하였습니다.', error : error});
+            }
+          }
+
+          // Promise.all(promiseArray.map(obj=>{
+          //   console.log(obj);
+          //   return fetch(obj.url, {
+          //     method : 'PUT',
+          //     headers: {
+          //       Authorization: `Bearer ${req.session.accessToken}`,
+          //       "Content-Type" : obj.mimeType
+          //     }, 
+          //     data : obj.file,
+          //   })
+          // })).then((json)=>{
+          //   // console.log(json);
+          // }).catch((error)=> {
+          //   console.log(error);
+          //   return res.json({success:'F', message: '요청하신파일 업로드 중 오류가 발생하였습니다.', error : error});
+          // });
+        }
+        const endTime = new Date();
+        console.log('시작 시간 :', startTime, '종료 시간 :', endTime);
+        res.json({success:'S', message: '요청하신 파일 업로드가 정상적으로 처리 되었습니다.'});
       }
-    }
+
+    // }
 })
 
+function uploadFIles(uri, token, mimeType, file) {
+  return fetch(uri, {
+    method : 'PUT',
+    headers: {
+      Authorization: `Bearer ${token}`,
+      "Content-Type" : mimeType
+    },
+    data : file,
+  })
+}
+
+async function uploadItems(array, path, siteId, options) { 
+    for (let item of array) { 
+      try{
+        if (item) {
+          const param  = item.param;
+          console.log("============================ param ===============================");
+          console.log(param);
+          console.log("============================ param ===============================");
+          const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + path, options);
+          const itemId = sitesInfo.data.id;
+          console.log("============================ sitesInfo ===============================");
+          console.log(sitesInfo.data);
+          console.log("============================ sitesInfo ===============================");
+          if (sitesInfo.data) {
+            if (param.folder) {
+              await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", param, options);
+            }
+            else if (param.file) {
+              await axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+itemId+':/'+param.name+':/content', param, options);
+            }
+            
+            if (item.subfolder && item.subfolder.length > 0) {
+              await uploadItems(item.subfolder, path + '/' + param.name, siteId, options);
+            }
+          }
+        }
+      } catch(error) {
+        console.log(error);
+        return error;
+      }
+    }
+}
+
 serverApp.post('/deleteItems', 
   isAuthenticated,
   (req, res, next)=>{
@@ -321,7 +495,7 @@ serverApp.post('/deleteItems',
           };
           for (let itemId of itemIdArr) {
             try{
-              await axios.delete(process.env.GRAPH_API_ENDPOINT + "v1.0/sites/"+ siteId + "/drive/items/" + itemId, options);
+              await axios.delete(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, options);
             }
             catch(error) {
               console.log(error.response.data.error); 
@@ -342,4 +516,4 @@ serverApp.post('/deleteItems',
         res.json(resultObj);
       }
     }
-  })
+  })

+ 1 - 1
src/fetch.js

@@ -55,6 +55,6 @@ async function updateFetch(endpoint, accessToken, params) {
 }
 
 module.exports = {
-    fetch : (endpoint, accessToken)=> fetch(endpoint, accessToken),
+    getFetch : (endpoint, accessToken)=> fetch(endpoint, accessToken),
     updateFetch: (endpoint, accessToken, params)=> updateFetch(endpoint, accessToken, params),
 }

BIN
src/static/images/Settings.gif


+ 1 - 0
src/static/images/code.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .275.225.5.5.5z" fill="#fff"/><path d="M25.293 8 21 3.707V7.5c0 .275.225.5.5.5h3.793z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="m26.56 7.854-5.414-5.415A1.51 1.51 0 0 0 20.086 2H6.5C5.673 2 5 2.673 5 3.5v25c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5V8.914c0-.4-.156-.777-.44-1.06zM21 3.707 25.293 8H21.5a.501.501 0 0 1-.5-.5V3.707zM6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .276.224.5.5.5z" fill="#605E5C"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 24h15a.5.5 0 0 0 0-1h-15a.5.5 0 0 0 0 1zm0 2h15a.5.5 0 0 0 0-1h-15a.5.5 0 0 0 0 1z" fill="#C8C6C4"/><path d="m18 11-4 10" stroke="#69AFE5" stroke-linecap="round"/><path d="M20.852 12.645a.5.5 0 0 0-.704.71l.704-.71zm2.648 3.333.355.352a.5.5 0 0 0-.003-.707l-.352.355zm-3.355 2.67a.5.5 0 0 0 .71.704l-.71-.704zm-8.997.707a.5.5 0 0 0 .704-.71l-.704.71zM8.5 16.022l-.355-.352a.5.5 0 0 0 .003.707l.352-.355zm3.355-2.67a.5.5 0 0 0-.71-.704l.71.704zm8.293.003 3 2.978.704-.71-3-2.978-.704.71zm2.997 2.27-3 3.023.71.704 3-3.022-.71-.704zm-11.293 3.02-3-2.978-.704.71 3 2.978.704-.71zm-2.997-2.27 3-3.023-.71-.704-3 3.022.71.704z" fill="#69AFE5"/></svg>

+ 1 - 0
src/static/images/exe.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M28 11H4V7.5c0-.275.225-.5.5-.5h23c.275 0 .5.225.5.5V11zm-.5 14h-23a.501.501 0 0 1-.5-.5V12h24v12.5c0 .275-.225.5-.5.5z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="M27.5 6h-23C3.673 6 3 6.673 3 7.5v17c0 .827.673 1.5 1.5 1.5h23c.827 0 1.5-.673 1.5-1.5v-17c0-.827-.673-1.5-1.5-1.5zm-23 1h23c.275 0 .5.225.5.5V11H4V7.5c0-.275.225-.5.5-.5zm0 18h23c.275 0 .5-.225.5-.5V12H4v12.5c0 .276.224.5.5.5z" fill="#605E5C"/><path d="M6 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM9 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM12 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" fill="#C8C6C4"/><path d="M12 25H4.5a.5.5 0 0 1-.5-.5V12h8v13z" fill="#EDEBE9"/><path fill-rule="evenodd" clip-rule="evenodd" d="M9.5 15h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm0 2h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-3 2h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zM18.5 15h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1zm0 2h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1zm-4 2h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1zm-4 2h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1z" fill="#C8C6C4"/><path d="M25 23h-3a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1z" fill="#69AFE5"/></svg>

+ 1 - 0
src/static/images/font.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .275.225.5.5.5z" fill="#fff"/><path d="M25.293 8 21 3.707V7.5c0 .275.225.5.5.5h3.793z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="m26.56 7.854-5.414-5.415A1.51 1.51 0 0 0 20.086 2H6.5C5.673 2 5 2.673 5 3.5v25c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5V8.914c0-.4-.156-.777-.44-1.06zM21 3.707 25.293 8H21.5a.501.501 0 0 1-.5-.5V3.707zM6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .276.224.5.5.5z" fill="#605E5C"/><path fill-rule="evenodd" clip-rule="evenodd" d="M12.73 20.417h5.3l.827 1.969c.27.639.403 1.107.403 1.403a.702.702 0 0 1-.288.565c-.19.155-.56.246-1.105.273V25h5.4v-.373c-.558-.034-.994-.187-1.307-.459-.312-.273-.66-.867-1.044-1.782L16.151 11h-.353l-4.815 11.265c-.417.988-.797 1.645-1.14 1.968-.168.161-.538.293-1.11.394V25h4.31v-.373c-.66-.048-1.089-.15-1.287-.308-.199-.158-.298-.342-.298-.55 0-.262.114-.66.343-1.192l.928-2.16zm2.695-6.277 2.321 5.53h-4.703l2.382-5.53z" fill="#FF5C0D"/></svg>

+ 1 - 0
src/static/images/genericfile.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .275.225.5.5.5z" fill="#fff"/><path d="M25.293 8 21 3.707V7.5c0 .275.225.5.5.5h3.793z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="m26.56 7.854-5.414-5.415A1.51 1.51 0 0 0 20.086 2H6.5C5.673 2 5 2.673 5 3.5v25c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5V8.914c0-.4-.156-.777-.44-1.06zM21 3.707 25.293 8H21.5a.501.501 0 0 1-.5-.5V3.707zM6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .276.224.5.5.5z" fill="#605E5C"/></svg>

+ 1 - 0
src/static/images/html.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .275.225.5.5.5z" fill="#fff"/><path d="M25.293 8 21 3.707V7.5c0 .275.225.5.5.5h3.793z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="m26.56 7.854-5.414-5.415A1.51 1.51 0 0 0 20.086 2H6.5C5.673 2 5 2.673 5 3.5v25c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5V8.914c0-.4-.156-.777-.44-1.06zM21 3.707 25.293 8H21.5a.501.501 0 0 1-.5-.5V3.707zM6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .276.224.5.5.5z" fill="#605E5C"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 24h15a.5.5 0 0 0 0-1h-15a.5.5 0 0 0 0 1zm0 2h15a.5.5 0 0 0 0-1h-15a.5.5 0 0 0 0 1z" fill="#C8C6C4"/><path clip-rule="evenodd" d="M16 20.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9z" stroke="#69AFE5"/><path clip-rule="evenodd" d="M16 20.5c.828 0 1.5-2.015 1.5-4.5s-.672-4.5-1.5-4.5-1.5 2.015-1.5 4.5.672 4.5 1.5 4.5z" stroke="#69AFE5"/><path d="M12 15h8v-1h-8v1zm0 3h8v-1h-8v1z" fill="#69AFE5"/></svg>

File diff suppressed because it is too large
+ 0 - 0
src/static/images/sysfile.svg


+ 1 - 0
src/static/images/vector.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .275.225.5.5.5z" fill="#fff"/><path d="M25.293 8 21 3.707V7.5c0 .275.225.5.5.5h3.793z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="m26.56 7.854-5.414-5.415A1.51 1.51 0 0 0 20.086 2H6.5C5.673 2 5 2.673 5 3.5v25c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5V8.914c0-.4-.156-.777-.44-1.06zM21 3.707 25.293 8H21.5a.501.501 0 0 1-.5-.5V3.707zM6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .276.224.5.5.5z" fill="#605E5C"/><path d="m17.327 19.016 6.516 2.443a.242.242 0 0 1-.026.461l-3.117.78-.78 3.117a.242.242 0 0 1-.46.026l-2.444-6.516a.242.242 0 0 1 .311-.31z" fill="#0078D7"/><path d="M10.5 21.5c.716-4.704 4.828-8.415 10-9" stroke="#C8C6C4" stroke-linecap="round"/><path d="M11.5 25h-2v1h2v-1zm-2 0a.5.5 0 0 1-.5-.5H8A1.5 1.5 0 0 0 9.5 26v-1zm-.5-.5v-2H8v2h1zm0-2a.5.5 0 0 1 .5-.5v-1A1.5 1.5 0 0 0 8 22.5h1zm.5-.5h2v-1h-2v1zm2 0a.5.5 0 0 1 .5.5h1a1.5 1.5 0 0 0-1.5-1.5v1zm.5.5v2h1v-2h-1zm0 2a.5.5 0 0 1-.5.5v1a1.5 1.5 0 0 0 1.5-1.5h-1zM23.5 14h-2v1h2v-1zm-2 0a.5.5 0 0 1-.5-.5h-1a1.5 1.5 0 0 0 1.5 1.5v-1zm-.5-.5v-2h-1v2h1zm0-2a.5.5 0 0 1 .5-.5v-1a1.5 1.5 0 0 0-1.5 1.5h1zm.5-.5h2v-1h-2v1zm2 0a.5.5 0 0 1 .5.5h1a1.5 1.5 0 0 0-1.5-1.5v1zm.5.5v2h1v-2h-1zm0 2a.5.5 0 0 1-.5.5v1a1.5 1.5 0 0 0 1.5-1.5h-1zM14 17.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z" fill="#0078D7"/><path fill-rule="evenodd" clip-rule="evenodd" d="M11 15c0-1.654 1.346-3 3-3s3 1.346 3 3-1.346 3-3 3-3-1.346-3-3zm5 0a2 2 0 1 0-4 0 2 2 0 0 0 4 0z" fill="#fff"/></svg>

+ 1 - 0
src/static/images/xml.svg

@@ -0,0 +1 @@
+<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .275.225.5.5.5z" fill="#fff"/><path d="M25.293 8 21 3.707V7.5c0 .275.225.5.5.5h3.793z" fill="#fff"/><path opacity=".67" fill-rule="evenodd" clip-rule="evenodd" d="m26.56 7.854-5.414-5.415A1.51 1.51 0 0 0 20.086 2H6.5C5.673 2 5 2.673 5 3.5v25c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5V8.914c0-.4-.156-.777-.44-1.06zM21 3.707 25.293 8H21.5a.501.501 0 0 1-.5-.5V3.707zM6.5 29h19c.275 0 .5-.225.5-.5V9h-4.5c-.827 0-1.5-.673-1.5-1.5V3H6.5c-.275 0-.5.225-.5.5v25c0 .276.224.5.5.5z" fill="#605E5C"/><path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 24h15a.5.5 0 0 0 0-1h-15a.5.5 0 0 0 0 1zm0 2h15a.5.5 0 0 0 0-1h-15a.5.5 0 0 0 0 1z" fill="#C8C6C4"/><path clip-rule="evenodd" d="M16 20.5a4.5 4.5 0 1 0 0-9 4.5 4.5 0 0 0 0 9z" stroke="#69AFE5"/><path clip-rule="evenodd" d="M16 20.5c.828 0 1.5-2.015 1.5-4.5s-.672-4.5-1.5-4.5-1.5 2.015-1.5 4.5.672 4.5 1.5 4.5z" stroke="#69AFE5"/><path d="M12 15h8v-1h-8v1zm0 3h8v-1h-8v1z" fill="#69AFE5"/><path d="M22.084 17.223a.5.5 0 0 0 .832.554l-.832-.554zM23.5 16l.416.277a.5.5 0 0 0 0-.554L23.5 16zm-.584-1.777a.5.5 0 0 0-.832.554l.832-.554zm-13 .554a.5.5 0 0 0-.832-.554l.832.554zM8.5 16l-.416-.277a.5.5 0 0 0 0 .554L8.5 16zm.584 1.777a.5.5 0 0 0 .832-.554l-.832.554zm13.832 0 1-1.5-.832-.554-1 1.5.832.554zm1-2.054-1-1.5-.832.554 1 1.5.832-.554zm-14.832-1.5-1 1.5.832.554 1-1.5-.832-.554zm-1 2.054 1 1.5.832-.554-1-1.5-.832.554z" fill="#C8C6C4"/></svg>

+ 24 - 0
src/static/styles/custom.css

@@ -182,10 +182,12 @@ li {
 .file-content {
   width: 100%;
   height: calc(100% - 136px);
+  overflow: auto;
 }
 .file-content > div {
   display: flex;
   padding: 10px 20px;
+  align-items: center;
 }
 .file-content > div > div:not(:first-child):hover{
   cursor: pointer;
@@ -194,6 +196,8 @@ li {
 .file-title > div:nth-child(1){
   width: 5%;
   text-align: right;
+  display: flex;
+  justify-content: space-between;
 }
 .file-content > div > div:nth-child(2),
 .file-title > div:nth-child(2) {
@@ -433,4 +437,24 @@ li {
 
 .click-menu > div:hover{
   background-color: #eeeeee;
+}
+
+#load {
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  position: fixed;
+  display: block;
+  opacity: 0.8;
+  background: white;
+  z-index: 99;
+  text-align: center;
+}
+
+#load > img {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  z-index: 100;
 }

+ 387 - 65
src/views/hello.html

@@ -47,16 +47,18 @@
                     <div onclick="menuOpen(event, 'upload-items')">
                       <div>업로드</div>
                       <div class="upload-items click-menu">
-                        <div style="width: 160px;">폴더</div>
-                        <div style="width: 160px;">파일</div>
+                        <div style="width: 160px;" onclick="openFolder(event)">폴더</div>
+                        <div style="width: 160px;" onclick="openFile(event)">파일</div>
                       </div> 
                     </div>
+                    <input type="file" style="display: none;" id="folder-upload" name="folder" onchange="uploadFolders(event)" webkitdirectory multiple >
+                    <input type="file" style="display: none;" id="file-upload" name="file" onchange="uploadFiles(event)" multiple >
                     <div onclick="refreshDrive()">새로고침</div>
                     <div class="one-drive-button">OneDrive에서 열기</div>
                 </div>
                 <div class="panel"></div>
                 <div class="file-title">
-                    <div><img src="/static/images/file.png" width="20" alt="파일"></div>
+                    <div><input type="checkbox" id="all-check" onchange="allSelect(event)"><img src="/static/images/file.png" width="20" alt="파일"></div>
                     <div class="head-name" onclick="sorting('name')">이름</div>
                     <div class="head-size" onclick="sorting('size')">용량</div>
                     <div class="head-lastModifiedDateTime" onclick="sorting('lastModifiedDateTime')">수정된 날짜</div>
@@ -70,6 +72,7 @@
   </body>
 </html>
 <script>
+  let _formData = new FormData($('<form enctype="multipart/form-data"></form>')[0]);
   const groupId = '3df73dac-a8bc-4dd0-9159-fdb2c696c067';
   const groupMap = new Map();
   let _selectedData = [];
@@ -86,13 +89,113 @@
     })
 
   });
+  function openFolder(event) {
+    if (!$('.panel-item.on')[0]) return;
+    $('#folder-upload').click();
+  }
+
+  function openFile(event) {
+    if (!$('.panel-item.on')[0]) return;
+    $('#file-upload').click();
+  }
+  function allSelect(event) {
+    const isChecked = $(event.target).is(':checked');
+    $('.file-content input[type="checkbox"]').prop('checked', isChecked);
+  }
+
+  function uploadFiles(event) {
+    const files = event.target.files;
+    const alertTitle = '파일 업로드';
+    _formData = new FormData($('<form enctype="multipart/form-data"></form>')[0]);
+    if (files && files.length > 100) {
+      return alertMessage(alertTitle, '업로드 파일은 최대 100개 까지 가능합니다.<br>업로드 파일 수 : '+ files.length, null);
+    }
+    
+    let siteId = getSitesId();
+    
+    if (siteId === null) {
+      return alertMessage(alertTitle, '선택된 그룹 정보를 찾을 수 없습니다. 생성할 그룹을 선택해주세요.', null);
+    }
+          
+    const groupIndex = _listData.joinedTeams.teams.findIndex(obj => obj.sharePoint.siteId === siteId);
+    const pathArr = $('.panel').children();
+
+    if (groupIndex >= 0 && $('.panel').children().length === 1) {
+      return alertMessage(alertTitle,'채널에는 업로드 할 수 없습니다. 채널 리스트를 먼저 선택해 주세요.');
+    }
+
+    let sitePath = '/drive/root';
+    let filePath = '';
+    if (pathArr.length > 1) {
+      sitePath += ':';
+      for (let ii = 0; ii < pathArr.length; ii++) {
+        const path = pathArr.eq(ii).text();
+        if (ii !== 0 && path !== " > ") {
+          sitePath += "/" + path; 
+          filePath += "/" +path;
+        }
+      }
+    }
+    
+    for (let file of files) {
+
+      let fileName = file.name;
+      if (file.name.indexOf('.') > 0) {
+          fileName = file.name.substring(0, file.name.lastIndexOf('.'));
+      }
+      
+      _formData.append('file', file);
+      _formData.append(fileName + '_path', filePath);
+    }
+
+    _formData.append('siteId', siteId);
+    _formData.append('path', sitePath);
+          // _formData.getAll()
+    $('body').append(`<div id="load">
+      <img src="/static/images/Settings.gif" alt="loading">
+    </div>`);
+
+    $.ajax({
+      method: 'post',
+      processData : false,
+      contentType : false,
+      data : _formData,
+      url : '/uploadItems',
+      success: (res) => {
+        $('#load').remove();
+        let str = res.message;
+        
+        if (res.error) {
+          str += '<br>오류 : ' + res.error;
+        }
+
+        alertMessage(alertTitle, str, null);
+        const selectDrive = $('.panel-item.on');
+        selectDrive.removeClass('on');
+        selectDrive.click();
+        
+      },
+      error: (error) => {
+        $('#load').remove();
+        alertMessage(alertTitle, error, null);
+        // console.log('==============error=============\n');
+        // console.log(error);
+      },
+      
+    });
+
+  }
+  function uploadFolders(event) {
+    console.log(event);
+    event.file;
+  }
 
   function menuOpen(event, className) {
     event.preventDefault();
     event.stopPropagation();
     $('.click-menu:not(.'+className+')').css('display', 'none');
     $('.' + className).toggle();
-  }
+  } 
 
   function drawDriveFiles(jsonData, parentData, name) {
       _selectedData = [];
@@ -121,7 +224,7 @@
         }
         let str = "";
           jsonData.value.forEach((obj, idx)=>{
-            dragAndDrop(obj.id);
+            dragAndDrop();
             let images = "/static/images/";
             
 
@@ -135,10 +238,15 @@
             else if (obj.file && obj.name) {
               let ext = obj.name.substring(obj.name.lastIndexOf('.') + 1);
               let imageExt = ".svg";
-              if (obj.file.mimeType && obj.file.mimeType.includes('image')){
+              //photo 
+              // if (obj.file.mimeType && obj.file.mimeType.includes('image')){
+              if (['psd', 'bmp', 'rle', 'dib', 'jpeg', 'jpg', 'gif', 'png', 'tiff', 'raw', 'ico'].includes(ext)){
                 ext = 'photo';
               }
-              else if (['txt'].includes(ext)) {
+              else if (['ai', 'eps', 'svg'].includes(ext)) {
+                ext = 'vector';
+              }
+              else if (['txt', 'properties'].includes(ext)) {
                 ext = 'txt';
               }
               else if (['pptx', 'ppt', 'pptm'].includes(ext)) {
@@ -162,18 +270,35 @@
               else if (['zip', 'apk', 'rar', '7z', 'tar'].includes(ext)) {
                 ext = 'zip';
               }
+              else if (['jsp', 'js', 'css', 'php', 'config', 'ini', 'yml', 'json'].includes(ext)) {
+                ext = 'code';
+              }
+              else if (['html'].includes(ext)) {
+                ext = 'html';
+              }
+              else if (['exe', 'msi'].includes(ext)) {
+                ext = 'exe';
+              }
+              else if (['jar', 'war', 'cab', 'dll', ].includes(ext)) {
+                ext = 'sysfile';
+              }
               else if (['mp4', 'mov', 'Webm', 'mp3','wav', 'Ogg'].includes(ext)) {
                 ext = 'clipchamp';
               }
+              else if (['bdf', 'chr', 'dfont', 'eot','etx', 'fnt', 'gdr', 'gf', 'mcf', 'xft', 'ttf', 'woff2',
+              'mf', 'otf', 'pfa', 'pfb', 'pfr', 'sfd', 'suit', 'tfm', 'ttc', 'vlw', 'woff', 'xfn'].includes(ext)) {
+                ext = 'font';
+              }
+              else if (['xml'].includes(ext)) {
+                ext = 'xml';
+              }
               else {
-                ext = 'file';
-                imageExt = '.png';
+                ext = 'genericfile';
               }
-
               images += ext + imageExt;
             }
             else {
-              images += "file.png";
+              images += "genericfile.svg";
             }
             let amount = '0 Bytes';
             if (obj.size) {
@@ -194,7 +319,7 @@
               <div id="row_${idx}">
                 <div>
                   <input type="checkbox" name="row_${idx}" value="${obj.id}" onclick="event.stopPropagation()">
-                  <img src="${images}" width="20" height="20" alt="폴더 이미지">
+                  <img src="${images}" width="32" height="32" alt="폴더 이미지">
                 </div>
                 <div class="file_name"><span class="sp-name" onclick="${method}">${obj.name}</span></div>
                 <div>${amount}</div>
@@ -288,8 +413,9 @@
       const checkedArr = [];
       let siteId = getSitesId();
 
-      if (!siteId) return alert('그룹 정보를 확인 할 수 없습니다.');
-      
+      if (!siteId) {
+        return alertMessage('파일 삭제','그룹 정보를 확인 할 수 없습니다.');
+      }
       if (checkArr.length > 0) {
         for (let ii = 0; ii < checkArr.length; ii++) {
           if (checkArr.eq(ii).is(':checked')) {
@@ -299,7 +425,7 @@
             if (idx >= 0) {
               const sameIndex = team.items[team.teams[idx].id].findIndex(obj => obj.id === checkArr.eq(ii).val());
               if (sameIndex >= 0) {
-                return alert('채널 정보는 삭제하실수 없습니다.');
+                return alertMessage('파일 삭제', '채널 정보는 삭제하실수 없습니다.');
               };
             }
 
@@ -307,11 +433,11 @@
           }
         }
         if (checkedArr.length === 0) {
-          alert('선택된 정보가 없습니다. 삭제하실 파일을 먼저 선택해주세요.');
+          alertMessage('파일 삭제', '선택된 정보가 없습니다. 삭제하실 파일을 먼저 선택해주세요.');
         }
         else {
-          if (confirm('선택된 파일을 삭제하시겠습니까?')) {
-              $.ajax({
+          confirmMessage('파일 삭제', '선택된 파일을 삭제하시겠습니까?', ()=>{
+            $.ajax({
                 method:'post',
                 url : "/deleteItems",
                 data: {
@@ -324,16 +450,14 @@
                       selectedDrive.removeClass('on');
                       selectedDrive.click();
                   }
-                  alert(res.message);
+                  $('.modal').remove();
+                  alertMessage('파일 삭제', res.message);
                 },
                 error: (error)=> {
                   console.log(error);
                 }
               })
-            }
-            else {
-              alert('선택된 그룹 정보가 없습니다. 선택 그룹을 확인해 주세요.');
-            }
+          })
         }
       }
     }
@@ -390,7 +514,8 @@
         }
     }
 
-    function dragAndDrop(driveId) {
+    
+    function dragAndDrop() {
       const $fileContent = $('.file-content');
       $fileContent.off('dragover');
       $fileContent.off('dragleave');
@@ -405,23 +530,222 @@
         
       })
 
-      $fileContent.on("drop", function(e){
-            e.preventDefault();
-            $(this).css('background-color', '#f5f5f5');
-            const transfer = e.originalEvent.dataTransfer;
-            if (transfer && transfer.files && transfer.files.length > 0) {
-                for (let ii = 0; ii < transfer.files.length; ii++) {
-                  console.log(transfer.files[ii]);
-                }
+      $fileContent.on("drop", async function(e){
+        const transfer = e.originalEvent.dataTransfer;
+          e.preventDefault();
+          
+          $(this).css('background-color', '#f5f5f5');
+          const items = transfer.items;
+          _formData = new FormData($('<form enctype="multipart/form-data"></form>')[0]);
+          await getFilesDataTransferItems(items);
+          const folders = _formData.getAll('folder');
+          const files = _formData.getAll('folder');
+          const alertTitle = '파일 업로드';
+          if (folders && folders.length > 100) {
+            return alertMessage(alertTitle, '업로드 폴더는 최대 100개 까지 가능합니다.<br>업로드 폴더 수 : '+ folders.length, null);
+            // return alert('업로드 폴더는 최대 100개 까지 가능합니다.\n업로드 폴더 수 : '+ folders.length);
+          }
+
+          if (files && files.length > 100) {
+            return alertMessage(alertTitle, '업로드 파일은 최대 100개 까지 가능합니다.\n업로드 파일 수 : '+ files.length, null);
+          }
+          let siteId = getSitesId();
+          if (siteId === null) {
+            return alertMessage(alertTitle, '선택된 그룹 정보를 찾을 수 없습니다. 생성할 그룹을 선택해주세요.', null);
+          }
+          
+          const groupIndex = _listData.joinedTeams.teams.findIndex(obj => obj.sharePoint.siteId === siteId);
+          const pathArr = $('.panel').children();
+
+          if (groupIndex >= 0 && $('.panel').children().length === 1) {
+            return alertMessage(alertTitle,'채널에는 업로드 할 수 없습니다. 채널 리스트를 먼저 선택해 주세요.');
+          }
+
+          let sitePath = '/drive/root';
+          if (pathArr.length > 1) {
+            sitePath += ':';
+            for (let ii = 0; ii < pathArr.length; ii++) {
+              const path = pathArr.eq(ii).text();
+              if (ii !== 0 && path !== " > ") {
+                sitePath += "/" + path; 
+              }
             }
+          }
+
+          _formData.append('siteId', siteId);
+          _formData.append('path', sitePath);
+          // _formData.getAll()
+          $('body').append(`<div id="load">
+            <img src="/static/images/Settings.gif" alt="loading">
+          </div>`)
+          $.ajax({
+            method: 'post',
+            processData : false,
+            contentType : false,
+            data : _formData,
+            url : '/uploadItems',
+            success: (res) => {
+              $('#load').remove();
+              let str = res.message;
+              if (res.error) {
+                str += '<br>오류 : ' + res.error;
+              }
+              alertMessage(alertTitle, str, null);
+              // alert(res.message);
+              // console.log(res.error);
+              const selectDrive = $('.panel-item.on');
+              selectDrive.removeClass('on');
+              selectDrive.click();
+              
+            },
+            error: (error) => {
+              $('#load').remove();
+              alertMessage(alertTitle, error, null);
+              // console.log('==============error=============\n');
+              // console.log(error);
+            },
+            
+          });
             // const file = e.originalEvent.dataTransfer.files[0];
-            // encryptFile(file, driveId);
+            // encryptFile(file, driveId); 
             // if (file && file.type.startsWith("image")) {
-            //     displayImage(file);
+            //     displayImage(file);  
             // }
       });
     }
 
+    function alertMessage(title, message, color) {
+      $('body').append($(`<div class="modal" style="display:flex;">
+          <div class="modal-box">
+            <div class="header">
+                  <div class="title">${title}</div>
+                  <div class="x-button">
+                    <span><img src="/static/images/x-button.png" width="15" height="15" alt="닫기 버튼" onclick="modalClose()"></span>
+                  </div>
+                </div>
+                <div class="modal-content">
+                  ${message}
+                </div>
+                <div class="button-box">
+                    <div class="name-btn" onclick="modalClose()">확인</div>
+                </div>
+            </div>
+          </div>
+        </div>`));
+    }
+
+    function confirmMessage(title, message, method) {
+      $('body').append($(`<div class="modal" style="display:flex;">
+          <div class="modal-box">
+            <div class="header">
+                  <div class="title">${title}</div>
+                  <div class="x-button">
+                    <span><img src="/static/images/x-button.png" width="15" height="15" alt="닫기 버튼" onclick="modalClose()"></span>
+                  </div>
+                </div>
+                <div class="modal-content">
+                  ${message}
+                </div>
+                <div class="button-box">
+                    <div class="success-button" >확인</div>
+                    <div class="name-btn" onclick="modalClose()">취소</div>
+                </div>
+            </div>
+          </div>
+        </div>`));
+        $('.success-button').on('click', ()=>{
+          method();
+        })
+    }
+
+    function confirmMethod(method) {
+        method();
+    }
+
+    function getUploadItems(dataTransferItems) {
+        for (let item of dataTransferItems) {
+          if (item.isFile) {
+            item.file(file => {
+              _formData.append('file', file);
+              _formData.append(file.name + '_path', item.fullPath);
+            })
+          }
+          else if (item.isDirectory) {
+            _formData.append('folder', JSON.stringify({name : item.name, path : item.fullPath}));
+            // _formData.append('path', item.fullPath);
+            let dirReader = item.createReader();
+            dirReader.readEntries(entries => {
+              for (let entry of entries) {
+                getUploadItems(entry);
+              }
+            })
+          }
+        }
+    }
+    
+    function getFilesDataTransferItems(dataTransferItems) {
+      function traverseFileTreePromise(item, folder) {
+        return new Promise(resolve => {
+          let subfolder = [];
+          let files = [];
+          let fileObj = {};
+
+          if (item.isFile) {
+            item.file(file => {
+              let fileName = file.name;
+              if (file.name.indexOf('.') > 0) {
+                fileName = file.name.substring(0, file.name.lastIndexOf('.'));
+              }
+              let path = item.fullPath.substring(0, item.fullPath.lastIndexOf('/'));
+              if (!path) {
+                path = '';
+              }
+              _formData.append('file', file);
+              _formData.append(fileName + '_path', path);
+              resolve(fileObj);
+            });
+          } else if (item.isDirectory) {
+            let dirReader = item.createReader();
+            const path = item.fullPath.substring(0, item.fullPath.lastIndexOf('/'));
+            _formData.append('folder', JSON.stringify({name : item.name, path : path}));
+            dirReader.readEntries(entries => {
+              let entriesPromises = [];
+              fileObj = {
+                path : item.fullPath,
+                param : {
+                  name: item.name,
+                  folder: {},
+                  '@microsoft.graph.conflictBehavior': 'rename'
+                },
+                subfolder : subfolder,
+              };
+              folder.push(fileObj);
+                
+              for (let entr of entries) {
+                
+                entriesPromises.push(
+                  traverseFileTreePromise(entr, subfolder)
+                );
+              }
+              resolve(Promise.all(entriesPromises));
+            });
+          }
+        });
+      }
+
+      let files = [];
+      return new Promise((resolve, reject) => {
+        let entriesPromises = [];
+        for (let it of dataTransferItems)
+          entriesPromises.push(
+            traverseFileTreePromise(it.webkitGetAsEntry(), files)
+          );
+        Promise.all(entriesPromises).then(entries => {
+          resolve(files);
+        });
+      });
+  }
+
     function encryptFile(file, driveId) {
       const extArr = [ 
         'lnk', 'exe', 'com', 'cmd', 'bat', 'dll', 'ini', 
@@ -444,11 +768,11 @@
                             </div>
                         </div>`;
           $('body').append($(modal));
+          return;
         }
 
-        uploadFiles(itemId)
+        uploadFiles(driveId)
       }
-
     }
 
     function modalClose() {
@@ -478,6 +802,9 @@
     function siteDriveChildrenItems(siteId, path, name, value, event) {
       event.preventDefault();
       event.stopPropagation();
+      if ($('#all-check').is(':checked')) {
+        $('#all-check').prop('checked', false);
+      }
       if ($(event.target).hasClass('on')) {
         return;
       }
@@ -488,7 +815,7 @@
           return;
         }
       }
-      console.log('/sites/' +siteId + path);
+
       callApi('get', '/sites/' +siteId + path, (jsonData)=>{
         if (jsonData && jsonData.id) {
           if (jsonData.folder && jsonData.folder.childCount === 0) {
@@ -511,6 +838,7 @@
                                   <div>이 폴더는 비어 있습니다.</div>
                                 </div>`);
               $panel.html(panelStr);
+              dragAndDrop();
               return;
           }
           callApi('get', '/sites/' +siteId +'/drive/items/'+jsonData.id+'/children', (childrenData)=>{
@@ -546,25 +874,25 @@
       return amount;
     }
 
-    function uploadFiles(itemId) {
-      //
+    // function uploadFiles(itemId) {
+    //   //
 
-      $.ajax({
-            method: 'post',
-            url : "/api-post",
-            data: {
-              api_uri: '/groups/'+groupId+'/drive/items/'+itemId+'/content',
-              scopes : 'Files.ReadWrite',
-              param : params,
-            },
-            success: (res)=> {
-              callBackMethod(res, args);
-            },
-            error: (error)=> {
-              console.log(error);
-            }
-      });
-    }
+    //   $.ajax({
+    //         method: 'post',
+    //         url : "/api-post",
+    //         data: {
+    //           api_uri: '/groups/'+groupId+'/drive/items/'+itemId+'/content',
+    //           scopes : 'Files.ReadWrite',
+    //           param : params,
+    //         },
+    //         success: (res)=> {
+    //           callBackMethod(res, args);
+    //         },
+    //         error: (error)=> {
+    //           console.log(error);
+    //         }
+    //   });
+    // }
 
     function getGroupList() {
       $.ajax({
@@ -712,18 +1040,18 @@
     const name = $folderName.val();
     if (!name || !name.trim()) {
       $folderName.focus();
-      return alert("폴더명을 입력해주세요.");
+      return alertMessage("폴더 생성", "폴더명을 입력해주세요.");
     }
     let siteId = getSitesId();
     if (siteId === null) {
-      return alert('선택된 그룹 정보를 찾을 수 없습니다. 생성할 그룹을 선택해주세요.');
+      return alertMessage("폴더 생성", '선택된 그룹 정보를 찾을 수 없습니다. 생성할 그룹을 선택해주세요.');
     }
     
     const groupIndex = _listData.joinedTeams.teams.findIndex(obj => obj.sharePoint.siteId === siteId);
     const pathArr = $('.panel').children();
 
     if (groupIndex >= 0 && $('.panel').children().length === 1) {
-        return alert('채널 리스트를 먼저 선택해 주세요.');
+      return alertMessage("폴더 생성", '채널 리스트를 먼저 선택해 주세요.');
     }
     let sitePath = '/drive/root'
     if (pathArr.length > 1) {
@@ -749,12 +1077,11 @@
       },
       url : '/makeFolder',
       success: (res) => {
-        alert('폴더가 생성되었습니다.\n폴더명 : '+ res.name);
         $('.modal').remove();
+        alertMessage("폴더 생성", '폴더가 생성되었습니다.<br>폴더명 : '+ res.name);
         const selectDrive = $('.panel-item.on');
         selectDrive.removeClass('on');
         selectDrive.click();
-        
       },
       error: (error) => {
         console.log('==============error=============\n');
@@ -786,11 +1113,6 @@
       </div>`);
       $('body').append(modalContainer);
     
-    // callApi('post', '/sites/' +siteId +'/drive/items/'++'/children', (jsonData)=>{
-    //   console.log(jsonData);
-    //   callApi('post', '')
-    // })
-    
   }
 
   function modalClose(){

Some files were not shown because too many files changed in this diff