const cookieParser = require('cookie-parser'); const cors = require("cors"); const fs = require("fs"); const path = require("path"); const express = require("express"); const axios = require("axios"); const SERVER_PORT = process.env.port || process.env.PORT || 53000; const authProvider = require('./auth/AuthProvider'); const {getFetch, updateFetch} = require('./fetch'); const bodyParser = require('body-parser'); const https = require('https'); const JSZIP = require('jszip'); require('dotenv').config({ path: './env/.env.test' }); const session = require('express-session'); const multer = require('multer'); const { throws } = require('assert'); let _ZIP; 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, saveUninitialized: true, cookie: { httpOnly: true, secure: true, // set this to true on production sameSite: 'none', maxAge: 60 * 60 * 24 * 1000 } })); serverApp.set(express.json()); serverApp.use(cookieParser()); serverApp.use(express.urlencoded({ extended: false })); serverApp.use("/static",express.static(path.join(__dirname, 'static'))); serverApp.use("/node_modules",express.static(path.join(__dirname, 'node_modules'))); 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, }; const server = https.createServer(options, serverApp); const corsOption = { origin: "*", } serverApp.use(cors(corsOption)); serverApp.use(bodyParser.json()); server.listen(SERVER_PORT, function () { console.log(`\n${serverApp.name} listening to ${SERVER_PORT}`); }); serverApp.get("/tab", isAuthenticated, async function (req, res, next) { res.sendFile(path.join(__dirname, "/views/hello.html"), { idTokenClaims: req.session.account.idTokenClaims } ); } ); function isAuthenticated(req, res, next) { if (!req.session.isAuthenticated) { return res.redirect('/auth/signin'); // redirect to sign-in route } next(); }; function isAccessToken(req, res, next) { if (!req.session.accessToken) { return authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/api-redirect' })(req, res, next); } next(); } serverApp.get("/auth/signin", authProvider.login({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/tab' })) serverApp.post("/redirect", authProvider.handleRedirect()); serverApp.post("/api-get", isAuthenticated, isAccessToken, 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); } }); serverApp.get("/api-redirect", isAuthenticated, 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); } }) serverApp.get("/post-redirect", isAuthenticated, 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); } } ) serverApp.post("/api-update", authProvider.acquireToken({ scopes: [], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/post-redirect' })); serverApp.post("/api-post", authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/post-redirect' })); serverApp.post("/getGroupList", authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/group-redirect' })); serverApp.get("/group-redirect", isAuthenticated, async function (req, res, next) { // return; try { 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; sites.sharePoint = sitesSharePoint; const resultObj = { oneDrive : { teams: oneDrive, }, joinedTeams : { teams : teams, items : {}, }, sites : { teams : sites, }, } if (teams && teams.length) { const options = { responseType: 'arraybuffer', headers: { Authorization: `Bearer ${req.session.accessToken}`, ConsistencyLevel: 'eventual', }, }; for (let team of teams) { 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; } if (item && item.value) { resultObj.joinedTeams.items[team.id] = item.value; } } } res.json(resultObj); } catch (error) { next(error); } } ) serverApp.post('/makeFolder', isAuthenticated, (req, res, next)=>{ if (!req.session.accessToken) { return authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/makeFolder' })(req, res, next); } next(); }, async (req, res, next)=>{ const options = { headers: { Authorization: `Bearer ${req.session.accessToken}`, }, }; const {siteId, path, param} = 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.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", JSON.parse(param), options); res.json(result.data) } } catch(error) { console.log(error.response.data.error); // resultObj.success = 'F'; // resultObj.message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.\n' + error.response.data.error.message; // return res.json(resultObj); } }) 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' })(req, res, next); } next(); }, async (req, res, next)=>{ const startTime = new Date(); const folderParam = { name: '', folder: { }, '@microsoft.graph.conflictBehavior': 'rename' } 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) { const fileInfo = JSON.parse(item); const param = {...folderParam}; param.name = fileInfo.name; let folderPath = ''; if (fileInfo.path) { folderPath = ':' + fileInfo.path; } let uri = endPoint + "/sites/"+ siteId + path + 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('.')); const fileName = file.originalname; let filePath = req.body[ fileName + "_path"]; file.originalname = Buffer.from(file.originalname, 'ascii').toString('utf8'); try { let formatPath = ''; if (filePath) { if (Array.isArray(filePath) && filePath.length > 0) { formatPath = ":" + filePath[0]; if (filePath.length > 1) { req.body[ fileName + "_path"] = filePath.splice(1); } } 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 }} 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}); } } } const endTime = new Date(); console.log('시작 시간 :', startTime, '종료 시간 :', endTime); res.json({success:'S', message: '요청하신 파일 업로드가 정상적으로 처리 되었습니다.'}); } }) serverApp.post('/api/download', isAuthenticated, (req, res, next)=>{ if (!req.session.accessToken) { return authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/api/download' })(req, res, next); } next(); }, async (req, res, next)=>{ if (req.body) { const {siteId, path, fileIds, zipName} = req.body; if (siteId && path && fileIds) { const options = { headers: { Authorization: `Bearer ${req.session.accessToken}`, }, }; const arr = JSON.parse(fileIds); if (arr.length === 1) { const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + "/drive/items/" + arr[0], options); if (sitesInfo && sitesInfo.data) { const data = sitesInfo.data; if (data.folder) { const folderObj = await getFolderItems(endPoint + "/sites/"+ siteId + "/drive/items/", arr, options); const zip = new JSZIP(); if (folderObj) { createZipFile(folderObj, zip); const now = new Date(); const year = now.getFullYear().toString(); let month = now.getMonth() + 1; if (month < 10) month = "0" + month; let date = now.getDate(); if (date < 10) date = "0" + date; zip.generateAsync({ type: 'nodebuffer', mimeType: 'application/epub+zip', compression: 'DEFLATE', compressionOptions: { level: 9 }, }).then((resZip)=>{ return res.json({success: 'S', data: resZip, name: data.name + '_' + year + '-' + month + '-' + date + '.zip'}); }) .catch((error)=>{ console.log(error); return res.json({success: 'F', message:'파일 다운로드 중 오류가 발생하였습니다.', error: error}); }); } } else { const dataUrl = data['@microsoft.graph.downloadUrl']; const response = await axios.get(dataUrl, {responseType: 'arraybuffer', headers: { withCredentials:true, },}); if (response.data) { const file = response.data; return res.json({success: 'S', data: file, type: data.file.mimeType, name: data.name}); } else { return res.json({message:'파일 다운로드에 실패하였습니다.', success:'F'}) } } } } else { try { const url = endPoint + "/sites/"+ siteId + "/drive/items/"; const zip = new JSZIP(); const downObj = await getFolderItems(url, arr, options); if (downObj) { createZipFile(downObj, zip); const now = new Date(); const year = now.getFullYear().toString(); let month = now.getMonth() + 1; if (month < 10) month = "0" + month; let date = now.getDate(); if (date < 10) date = "0" + date; zip.generateAsync({ type: 'nodebuffer', mimeType: 'application/epub+zip', compression: 'DEFLATE', compressionOptions: { level: 9 }, }).then((resZip)=>{ return res.json({success: 'S', data: resZip, name: zipName + '_' + year + '-' + month + '-' + date + '.zip'}); }) .catch((error)=>{ console.log(error); return res.json({success: 'F', message:'파일 다운로드 중 오류가 발생하였습니다.', error: error}); }); } } catch(err) { // console.log(sitesInfo.data); console.log(err); } // } } } else { return res.json({message:'다운로드 파일 정보를 확인 할 수 없습니다.', success:'F'}) } } } ) function createZipFile(obj, zip) { if (obj.files.length > 0) { for (let file of obj.files) { zip.file(file.name, file.data); } } if (obj.folder.length > 0) { for (let folder of obj.folder) { zip.folder(folder.name); if (folder.subFolder) { createZipFile(folder.subFolder, zip.folder(folder.name)); } } } } async function getFolderItems(url, array, options) { const files = []; // let subFolder = []; let folder = []; for (let fileId of array) { const sitesInfo = await axios.get(url + fileId, options); const data = sitesInfo.data; if (data) { if (data.folder) { const folderObj = {name : data.name, subFolder : []}; if (data.folder.childCount) { const itemsData = await axios.get(url + fileId + '/children', options); if (itemsData && itemsData.data && itemsData.data.value.length > 0) { const idArr = []; for (let children of itemsData.data.value) { idArr.push(children.id); } let result = await getFolderItems(url, idArr, options); // subFolder.push(result); folderObj.subFolder = result; } } folder.push(folderObj); } else { const dataUrl = data['@microsoft.graph.downloadUrl']; const response = await axios.get(dataUrl, {responseType: 'arraybuffer', headers: { withCredentials:true, },}); files.push({name : data.name, data : response.data, type: data.file.mimeType}); } } } const resultObj = { folder : folder, files : files, }; return resultObj; } serverApp.post('/deleteItems', isAuthenticated, (req, res, next)=>{ if (!req.session.accessToken) { return authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/deleteItems' })(req, res, next); } next(); }, async (req, res, next)=>{ if (req.body) { const {siteId, itemIds} = req.body; const resultObj = {success: '', message:''}; if (siteId && itemIds) { const itemIdArr = JSON.parse(itemIds); if (itemIdArr.length > 0) { const options = { headers: { Authorization: `Bearer ${req.session.accessToken}`, }, }; for (let itemId of itemIdArr) { try{ await axios.delete(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, options); } catch(error) { console.log(error.response.data.error); resultObj.success = 'F'; resultObj.message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.
' + error.response.data.error.message; return res.json(resultObj); } } resultObj.success = 'S'; resultObj.message = '파일 정보가 삭제되었습니다.'; res.json(resultObj); } } else { resultObj.success = 'F'; resultObj.message = '파라미터 정보를 확인해주세요.'; res.json(resultObj); } } }) serverApp.post('/api/update-name', isAuthenticated, (req, res, next)=>{ if (!req.session.accessToken) { return authProvider.acquireToken({ scopes: ['.default'], redirectUri: 'https://localhost:53000/redirect', successRedirect: '/api/update-name' })(req, res, next); } next(); }, async (req, res, next)=>{ if (req.body) { const {siteId, itemId, name} = req.body; const resultObj = {success: '', message:''}; if (siteId && itemId && name) { const options = { headers: { Authorization: `Bearer ${req.session.accessToken}`, }, }; 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 = '선택하신 파일의 이름 변경 중 오류가 발생하였습니다.
' + error.response.data.error.message; return res.json(resultObj); } resultObj.success = 'S'; resultObj.message = '선택하신 파일의 이름이 변경되었습니다.'; return res.json(resultObj); } } else { resultObj.success = 'F'; resultObj.message = '파라미터 정보를 확인해주세요.'; return res.json(resultObj); } })