app.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. const cookieParser = require('cookie-parser');
  2. const cors = require("cors");
  3. const fs = require("fs");
  4. const path = require("path");
  5. const express = require("express");
  6. const axios = require("axios");
  7. const SERVER_PORT = process.env.port || process.env.PORT || 53000;
  8. const authProvider = require('./auth/AuthProvider');
  9. const {getFetch, updateFetch} = require('./fetch');
  10. const bodyParser = require('body-parser');
  11. const https = require('https');
  12. const JSZIP = require('jszip');
  13. require('dotenv').config({ path: './env/.env.test' });
  14. const session = require('express-session');
  15. const multer = require('multer');
  16. const { throws } = require('assert');
  17. let _ZIP;
  18. const storage = multer.diskStorage({
  19. destination: function (req, file, cb) {
  20. cb(null, 'uploads/')
  21. },
  22. filename: function (req, file, cb) {
  23. cb(null, file.originalname) // 원래 파일이름으로 저장
  24. }
  25. })
  26. const upload = multer(storage);
  27. const serverApp = express();
  28. const endPoint = process.env.GRAPH_API_ENDPOINT + 'v1.0';
  29. Buffer.prototype.toArrayInteger = function(){
  30. if (this.length > 0) {
  31. const data = new Array(this.length);
  32. for (let i = 0; i < this.length; i=i+1)
  33. data[i] = this[i];
  34. return data;
  35. }
  36. return [];
  37. }
  38. serverApp.use(session({
  39. secret: process.env.EXPRESS_SESSION_SECRET,
  40. resave: false,
  41. saveUninitialized: true,
  42. cookie: {
  43. httpOnly: true,
  44. secure: true, // set this to true on production
  45. sameSite: 'none',
  46. maxAge: 60 * 60 * 24 * 1000
  47. }
  48. }));
  49. serverApp.set(express.json());
  50. serverApp.use(cookieParser());
  51. serverApp.use(express.urlencoded({ extended: false }));
  52. serverApp.use("/static",express.static(path.join(__dirname, 'static')));
  53. serverApp.use("/node_modules",express.static(path.join(__dirname, 'node_modules')));
  54. const options = {
  55. key: process.env.SSL_KEY_FILE ? fs.readFileSync(process.env.SSL_KEY_FILE) : undefined,
  56. cert: process.env.SSL_CRT_FILE ? fs.readFileSync(process.env.SSL_CRT_FILE) : undefined,
  57. };
  58. const server = https.createServer(options, serverApp);
  59. const corsOption = {
  60. origin: "*",
  61. }
  62. serverApp.use(cors(corsOption));
  63. serverApp.use(bodyParser.json());
  64. server.listen(SERVER_PORT, function () {
  65. console.log(`\n${serverApp.name} listening to ${SERVER_PORT}`);
  66. });
  67. serverApp.get("/tab",
  68. isAuthenticated,
  69. async function (req, res, next) {
  70. res.sendFile(path.join(__dirname, "/views/hello.html"),
  71. { idTokenClaims: req.session.account.idTokenClaims }
  72. );
  73. }
  74. );
  75. function isAuthenticated(req, res, next) {
  76. if (!req.session.isAuthenticated) {
  77. return res.redirect('/auth/signin'); // redirect to sign-in route
  78. }
  79. next();
  80. };
  81. function isAccessToken(req, res, next) {
  82. if (!req.session.accessToken) {
  83. return authProvider.acquireToken({
  84. scopes: ['.default'],
  85. redirectUri: 'https://localhost:53000/redirect',
  86. successRedirect: '/api-redirect'
  87. })(req, res, next);
  88. }
  89. next();
  90. }
  91. serverApp.get("/auth/signin", authProvider.login({
  92. scopes: ['.default'],
  93. redirectUri: 'https://localhost:53000/redirect',
  94. successRedirect: '/tab'
  95. }))
  96. serverApp.post("/redirect", authProvider.handleRedirect());
  97. serverApp.post("/api-get",
  98. isAuthenticated,
  99. isAccessToken,
  100. async (req, res, next) => {
  101. const uri = req.body.api_uri || req.session.apiUri;
  102. let param = {};
  103. if (req.session.param) {
  104. param = req.session.param;
  105. }
  106. try {
  107. const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
  108. res.json(graphResponse);
  109. } catch (error) {
  110. next(error);
  111. }
  112. });
  113. serverApp.get("/api-redirect",
  114. isAuthenticated,
  115. async function (req, res, next) {
  116. const uri = req.session.apiUri;
  117. let param = {};
  118. if (req.session.param) {
  119. param = req.session.param;
  120. }
  121. try {
  122. const graphResponse = await getFetch(endPoint + uri, req.session.accessToken, param);
  123. res.json(graphResponse);
  124. } catch (error) {
  125. next(error);
  126. }
  127. })
  128. serverApp.get("/post-redirect",
  129. isAuthenticated,
  130. async function (req, res, next) {
  131. const uri = req.session.apiUri;
  132. let param = {};
  133. if (req.session.param) {
  134. param = req.session.param;
  135. }
  136. try {
  137. const graphResponse = await updateFetch(endPoint + uri, req.session.accessToken, param);
  138. res.json(graphResponse);
  139. } catch (error) {
  140. next(error);
  141. }
  142. }
  143. )
  144. serverApp.post("/api-update", authProvider.acquireToken({
  145. scopes: [],
  146. redirectUri: 'https://localhost:53000/redirect',
  147. successRedirect: '/post-redirect'
  148. }));
  149. serverApp.post("/api-post", authProvider.acquireToken({
  150. scopes: ['.default'],
  151. redirectUri: 'https://localhost:53000/redirect',
  152. successRedirect: '/post-redirect'
  153. }));
  154. serverApp.post("/getGroupList", authProvider.acquireToken({
  155. scopes: ['.default'],
  156. redirectUri: 'https://localhost:53000/redirect',
  157. successRedirect: '/group-redirect'
  158. }));
  159. serverApp.get("/group-redirect",
  160. isAuthenticated,
  161. async function (req, res, next) {
  162. // return;
  163. try {
  164. const oneDrive = await getFetch(endPoint + "/me/drive/root", req.session.accessToken);
  165. const sharePointIds = await getFetch(endPoint + "/me/drive/SharePointIds", req.session.accessToken);
  166. const graphResponse = await getFetch(endPoint + "/me/joinedTeams", req.session.accessToken);
  167. const sites = await getFetch(endPoint + "/sites/root", req.session.accessToken);
  168. const sitesSharePoint = await getFetch(endPoint + "/sites/root/SharePointIds", req.session.accessToken);
  169. const teams = graphResponse.value;
  170. oneDrive.sharePoint = sharePointIds;
  171. sites.sharePoint = sitesSharePoint;
  172. const resultObj = {
  173. oneDrive : {
  174. teams: oneDrive,
  175. },
  176. joinedTeams : {
  177. teams : teams,
  178. items : {},
  179. },
  180. sites : {
  181. teams : sites,
  182. },
  183. }
  184. if (teams && teams.length) {
  185. const options = {
  186. responseType: 'arraybuffer',
  187. headers: {
  188. Authorization: `Bearer ${req.session.accessToken}`,
  189. ConsistencyLevel: 'eventual',
  190. },
  191. };
  192. for (let team of teams) {
  193. const item = await getFetch(endPoint + "/groups/"+team.id+"/drive/items/root/children", req.session.accessToken);
  194. const sharePoint = await getFetch(endPoint + "/groups/"+team.id+"/drive/SharePointIds", req.session.accessToken);
  195. if (sharePoint) {
  196. team.sharePoint = sharePoint;
  197. }
  198. if (item && item.value) {
  199. resultObj.joinedTeams.items[team.id] = item.value;
  200. }
  201. }
  202. }
  203. res.json(resultObj);
  204. } catch (error) {
  205. next(error);
  206. }
  207. }
  208. )
  209. serverApp.post('/makeFolder',
  210. isAuthenticated,
  211. (req, res, next)=>{
  212. if (!req.session.accessToken) {
  213. return authProvider.acquireToken({
  214. scopes: ['.default'],
  215. redirectUri: 'https://localhost:53000/redirect',
  216. successRedirect: '/makeFolder'
  217. })(req, res, next);
  218. }
  219. next();
  220. },
  221. async (req, res, next)=>{
  222. const options = {
  223. headers: {
  224. Authorization: `Bearer ${req.session.accessToken}`,
  225. },
  226. };
  227. const {siteId, path, param} = req.body;
  228. try{
  229. const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + path, options);
  230. if (sitesInfo.data) {
  231. const itemId = sitesInfo.data.id;
  232. const result = await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", JSON.parse(param), options);
  233. res.json(result.data)
  234. }
  235. }
  236. catch(error) {
  237. console.log(error.response.data.error);
  238. // resultObj.success = 'F';
  239. // resultObj.message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.\n' + error.response.data.error.message;
  240. // return res.json(resultObj);
  241. }
  242. })
  243. serverApp.post('/uploadItems', upload.array('file'),
  244. isAuthenticated,
  245. (req, res, next)=>{
  246. if (!req.session.accessToken) {
  247. return authProvider.acquireToken({
  248. scopes: ['.default'],
  249. redirectUri: 'https://localhost:53000/redirect',
  250. successRedirect: '/uploadItems'
  251. })(req, res, next);
  252. }
  253. next();
  254. },
  255. async (req, res, next)=>{
  256. const startTime = new Date();
  257. const folderParam = {
  258. name: '',
  259. folder: { },
  260. '@microsoft.graph.conflictBehavior': 'rename'
  261. }
  262. const files = req.files;
  263. let {siteId, path, folder} = req.body;
  264. if (siteId && path) {
  265. const options = {
  266. headers: {
  267. Authorization: `Bearer ${req.session.accessToken}`,
  268. },
  269. };
  270. if (folder) {
  271. if (!Array.isArray(folder)) {
  272. folder = [folder];
  273. }
  274. let beforeUri = '';
  275. let beforeItemId = '';
  276. for (let item of folder) {
  277. const fileInfo = JSON.parse(item);
  278. const param = {...folderParam};
  279. param.name = fileInfo.name;
  280. let folderPath = '';
  281. if (fileInfo.path) {
  282. folderPath = ':' + fileInfo.path;
  283. }
  284. let uri = endPoint + "/sites/"+ siteId + path + folderPath;
  285. try {
  286. let itemId = '';
  287. if (beforeUri === uri) {
  288. itemId = beforeItemId;
  289. }
  290. else {
  291. const sitesInfo = await axios.get(uri, options);
  292. itemId = sitesInfo.data.id;
  293. beforeItemId = itemId;
  294. beforeUri = uri;
  295. }
  296. await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", param, options);
  297. // await axios.post(endPoint + "/sites/"+ siteId +"/drive/items/" + itemId +"/children", param, options);
  298. }
  299. catch(error) {
  300. console.log(error);
  301. // next(error);
  302. return res.json({success:'F', message: '요청하신 파일 업로드 중 오류가 발생하였습니다.', error : error});
  303. }
  304. }
  305. }
  306. if (files && files.length > 0) {
  307. const promiseArray = [];
  308. let beforeUri = '';
  309. let beforeItemId = '';
  310. for (let file of files) {
  311. // const fileName = file.originalname.substring(0, file.originalname.lastIndexOf('.'));
  312. const fileName = file.originalname;
  313. let filePath = req.body[ fileName + "_path"];
  314. file.originalname = Buffer.from(file.originalname, 'ascii').toString('utf8');
  315. try {
  316. let formatPath = '';
  317. if (filePath) {
  318. if (Array.isArray(filePath) && filePath.length > 0) {
  319. formatPath = ":" + filePath[0];
  320. if (filePath.length > 1) {
  321. req.body[ fileName + "_path"] = filePath.splice(1);
  322. }
  323. }
  324. else if (filePath.trim()){
  325. formatPath = ":" + filePath;
  326. }
  327. }
  328. let itemId = '';
  329. const uri = endPoint + "/sites/"+ siteId + path + formatPath;
  330. if (beforeUri === uri) {
  331. itemId = beforeItemId;
  332. }
  333. else {
  334. const sitesInfo = await axios.get(uri, options);
  335. itemId = sitesInfo.data.id;
  336. beforeUri = uri;
  337. beforeItemId = itemId;
  338. }
  339. // const uploadUri = endPoint + "/sites/"+ siteId +"/drive/items/"+itemId+':/'+file.originalname+':/content';
  340. const fileOptions = { headers: {
  341. Authorization: `Bearer ${req.session.accessToken}`,
  342. "Content-Type" : file.mimeType
  343. }}
  344. await axios.put(endPoint + "/sites/"+ siteId +"/drive/items/"+itemId+':/'+file.originalname+':/content', file.buffer, fileOptions);
  345. }
  346. catch(error) {
  347. if (error) {
  348. if (error.response) {
  349. console.log(error.response.data);
  350. }
  351. else {
  352. console.log(error);
  353. }
  354. }
  355. return res.json({success:'F', message: '요청하신파일 업로드 중 오류가 발생하였습니다.', error : error});
  356. }
  357. }
  358. }
  359. const endTime = new Date();
  360. console.log('시작 시간 :', startTime, '종료 시간 :', endTime);
  361. res.json({success:'S', message: '요청하신 파일 업로드가 정상적으로 처리 되었습니다.'});
  362. }
  363. })
  364. serverApp.post('/api/download',
  365. isAuthenticated,
  366. (req, res, next)=>{
  367. if (!req.session.accessToken) {
  368. return authProvider.acquireToken({
  369. scopes: ['.default'],
  370. redirectUri: 'https://localhost:53000/redirect',
  371. successRedirect: '/api/download'
  372. })(req, res, next);
  373. }
  374. next();
  375. },
  376. async (req, res, next)=>{
  377. if (req.body) {
  378. const {siteId, path, fileIds, zipName} = req.body;
  379. if (siteId && path && fileIds) {
  380. const options = {
  381. headers: {
  382. Authorization: `Bearer ${req.session.accessToken}`,
  383. },
  384. };
  385. const arr = JSON.parse(fileIds);
  386. if (arr.length === 1) {
  387. const sitesInfo = await axios.get(endPoint + "/sites/"+ siteId + "/drive/items/" + arr[0], options);
  388. if (sitesInfo && sitesInfo.data) {
  389. const data = sitesInfo.data;
  390. if (data.folder) {
  391. const folderObj = await getFolderItems(endPoint + "/sites/"+ siteId + "/drive/items/", arr, options);
  392. const zip = new JSZIP();
  393. if (folderObj) {
  394. createZipFile(folderObj, zip);
  395. const now = new Date();
  396. const year = now.getFullYear().toString();
  397. let month = now.getMonth() + 1;
  398. if (month < 10) month = "0" + month;
  399. let date = now.getDate();
  400. if (date < 10) date = "0" + date;
  401. zip.generateAsync({
  402. type: 'nodebuffer',
  403. mimeType: 'application/epub+zip',
  404. compression: 'DEFLATE',
  405. compressionOptions: {
  406. level: 9
  407. },
  408. }).then((resZip)=>{
  409. return res.json({success: 'S', data: resZip, name: data.name + '_' + year + '-' + month + '-' + date + '.zip'});
  410. })
  411. .catch((error)=>{
  412. console.log(error);
  413. return res.json({success: 'F', message:'파일 다운로드 중 오류가 발생하였습니다.', error: error});
  414. });
  415. }
  416. }
  417. else {
  418. const dataUrl = data['@microsoft.graph.downloadUrl'];
  419. const response = await axios.get(dataUrl, {responseType: 'arraybuffer', headers: {
  420. withCredentials:true,
  421. },});
  422. if (response.data) {
  423. const file = response.data;
  424. return res.json({success: 'S', data: file, type: data.file.mimeType, name: data.name});
  425. }
  426. else {
  427. return res.json({message:'파일 다운로드에 실패하였습니다.', success:'F'})
  428. }
  429. }
  430. }
  431. }
  432. else {
  433. try {
  434. const url = endPoint + "/sites/"+ siteId + "/drive/items/";
  435. const zip = new JSZIP();
  436. const downObj = await getFolderItems(url, arr, options);
  437. if (downObj) {
  438. createZipFile(downObj, zip);
  439. const now = new Date();
  440. const year = now.getFullYear().toString();
  441. let month = now.getMonth() + 1;
  442. if (month < 10) month = "0" + month;
  443. let date = now.getDate();
  444. if (date < 10) date = "0" + date;
  445. zip.generateAsync({
  446. type: 'nodebuffer',
  447. mimeType: 'application/epub+zip',
  448. compression: 'DEFLATE',
  449. compressionOptions: {
  450. level: 9
  451. },
  452. }).then((resZip)=>{
  453. return res.json({success: 'S', data: resZip, name: zipName + '_' + year + '-' + month + '-' + date + '.zip'});
  454. })
  455. .catch((error)=>{
  456. console.log(error);
  457. return res.json({success: 'F', message:'파일 다운로드 중 오류가 발생하였습니다.', error: error});
  458. });
  459. }
  460. }
  461. catch(err) {
  462. // console.log(sitesInfo.data);
  463. console.log(err);
  464. }
  465. // }
  466. }
  467. }
  468. else {
  469. return res.json({message:'다운로드 파일 정보를 확인 할 수 없습니다.', success:'F'})
  470. }
  471. }
  472. }
  473. )
  474. function createZipFile(obj, zip) {
  475. if (obj.files.length > 0) {
  476. for (let file of obj.files) {
  477. zip.file(file.name, file.data);
  478. }
  479. }
  480. if (obj.folder.length > 0) {
  481. for (let folder of obj.folder) {
  482. zip.folder(folder.name);
  483. if (folder.subFolder) {
  484. createZipFile(folder.subFolder, zip.folder(folder.name));
  485. }
  486. }
  487. }
  488. }
  489. async function getFolderItems(url, array, options) {
  490. const files = [];
  491. // let subFolder = [];
  492. let folder = [];
  493. for (let fileId of array) {
  494. const sitesInfo = await axios.get(url + fileId, options);
  495. const data = sitesInfo.data;
  496. if (data) {
  497. if (data.folder) {
  498. const folderObj = {name : data.name, subFolder : []};
  499. if (data.folder.childCount) {
  500. const itemsData = await axios.get(url + fileId + '/children', options);
  501. if (itemsData && itemsData.data && itemsData.data.value.length > 0) {
  502. const idArr = [];
  503. for (let children of itemsData.data.value) {
  504. idArr.push(children.id);
  505. }
  506. let result = await getFolderItems(url, idArr, options);
  507. // subFolder.push(result);
  508. folderObj.subFolder = result;
  509. }
  510. }
  511. folder.push(folderObj);
  512. }
  513. else {
  514. const dataUrl = data['@microsoft.graph.downloadUrl'];
  515. const response = await axios.get(dataUrl, {responseType: 'arraybuffer', headers: {
  516. withCredentials:true,
  517. },});
  518. files.push({name : data.name, data : response.data, type: data.file.mimeType});
  519. }
  520. }
  521. }
  522. const resultObj = {
  523. folder : folder,
  524. files : files,
  525. };
  526. return resultObj;
  527. }
  528. serverApp.post('/deleteItems',
  529. isAuthenticated,
  530. (req, res, next)=>{
  531. if (!req.session.accessToken) {
  532. return authProvider.acquireToken({
  533. scopes: ['.default'],
  534. redirectUri: 'https://localhost:53000/redirect',
  535. successRedirect: '/deleteItems'
  536. })(req, res, next);
  537. }
  538. next();
  539. },
  540. async (req, res, next)=>{
  541. if (req.body) {
  542. const {siteId, itemIds} = req.body;
  543. const resultObj = {success: '', message:''};
  544. if (siteId && itemIds) {
  545. const itemIdArr = JSON.parse(itemIds);
  546. if (itemIdArr.length > 0) {
  547. const options = {
  548. headers: {
  549. Authorization: `Bearer ${req.session.accessToken}`,
  550. },
  551. };
  552. for (let itemId of itemIdArr) {
  553. try{
  554. await axios.delete(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, options);
  555. }
  556. catch(error) {
  557. console.log(error.response.data.error);
  558. resultObj.success = 'F';
  559. resultObj.message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.<br>' + error.response.data.error.message;
  560. return res.json(resultObj);
  561. }
  562. }
  563. resultObj.success = 'S';
  564. resultObj.message = '파일 정보가 삭제되었습니다.';
  565. res.json(resultObj);
  566. }
  567. }
  568. else {
  569. resultObj.success = 'F';
  570. resultObj.message = '파라미터 정보를 확인해주세요.';
  571. res.json(resultObj);
  572. }
  573. }
  574. })
  575. serverApp.post('/api/update-name',
  576. isAuthenticated,
  577. (req, res, next)=>{
  578. if (!req.session.accessToken) {
  579. return authProvider.acquireToken({
  580. scopes: ['.default'],
  581. redirectUri: 'https://localhost:53000/redirect',
  582. successRedirect: '/api/update-name'
  583. })(req, res, next);
  584. }
  585. next();
  586. },
  587. async (req, res, next)=>{
  588. if (req.body) {
  589. const {siteId, itemId, name} = req.body;
  590. const resultObj = {success: '', message:''};
  591. if (siteId && itemId && name) {
  592. const options = {
  593. headers: {
  594. Authorization: `Bearer ${req.session.accessToken}`,
  595. },
  596. };
  597. try{
  598. await axios.patch(endPoint + "/sites/"+ siteId + "/drive/items/" + itemId, {name : name}, options);
  599. }
  600. catch(error) {
  601. console.log(error.response.data.error);
  602. resultObj.success = 'F';
  603. resultObj.message = '선택하신 파일의 이름 변경 중 오류가 발생하였습니다.<br>' + error.response.data.error.message;
  604. return res.json(resultObj);
  605. }
  606. resultObj.success = 'S';
  607. resultObj.message = '선택하신 파일의 이름이 변경되었습니다.';
  608. return res.json(resultObj);
  609. }
  610. }
  611. else {
  612. resultObj.success = 'F';
  613. resultObj.message = '파라미터 정보를 확인해주세요.';
  614. return res.json(resultObj);
  615. }
  616. })