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 {DeviceCodeCredential} = require('@azure/identity');
const {TokenCredentialAuthenticationProvider} = require('@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials')
const {Client} = require('@microsoft/microsoft-graph-client')
const credential = new DeviceCodeCredential({
tenantId : process.env.TENANT_ID,
clientId : process.env.CLIENT_ID,
userPromptCallback: (info) => {
console.log(info.message);
},
});
const authPr = new TokenCredentialAuthenticationProvider(credential, {
scopes: ['.default'],
});
const graphClient = Client.initWithMiddleware({ authProvider: authPr });
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) {
// const result = await graphClient.api('/me').get();
// console.log(result);
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',
withCredentials:true,
},
};
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);
// const image = await axios.get(endPoint + "/groups/" + team.id + "/photo/$value", options);
// if (image && image.data) {
// team.image = image.data;
// }
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('/api/upload', upload.array('file'),
isAuthenticated,
(req, res, next)=>{
if (!req.session.accessToken) {
return authProvider.acquireToken({
scopes: ['.default'],
redirectUri: 'https://localhost:53000/redirect',
successRedirect: '/api/upload'
})(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('/api/delete',
isAuthenticated,
(req, res, next)=>{
if (!req.session.accessToken) {
return authProvider.acquireToken({
scopes: ['.default'],
redirectUri: 'https://localhost:53000/redirect',
successRedirect: '/api/delete'
})(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) {
resultObj.success = 'F';
let message = '선택하신 파일 정보 삭제중 오류가 발생하였습니다.';
if (error && error.response && error.response.data && error.response.data.error && error.response.data.error.message) {
console.log(error.response.data.error.message);
message += '
' + error.response.data.error.message;
}
resultObj.message = 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);
}
});
serverApp.post('/api/copy-item',
isAuthenticated,
(req, res, next)=>{
if (!req.session.accessToken) {
return authProvider.acquireToken({
scopes: ['.default'],
redirectUri: 'https://localhost:53000/redirect',
successRedirect: '/api/copy-item'
})(req, res, next);
}
next();
},
async (req, res, next)=>{
if (req.body) {
const {id, name, siteId, driveId, text} = req.body;
const resultObj = {message:'', successItems : [], failItems : [], locations: []};
if (name && isNaN(name)) {
const nameArray = JSON.parse(name);
const options = {
headers: {
Authorization: `Bearer ${req.session.accessToken}`,
ContentType: "application/json",
},
};
if (nameArray && nameArray.length > 0) {
for (let moveItem of nameArray) {
if (moveItem) {
const param = {
parentReference: {
id: id,
driveId : driveId,
},
name: moveItem.name
};
try {
const result = await axios.post(`${endPoint}/sites/${siteId}/drive/items/${moveItem.id}/copy`, param, options);
if (result) {
console.log(result);
resultObj.successItems.push(moveItem.name);
resultObj.locations.push(result.headers.location);
}
}
catch (error) {
// console.log(error);
console.log(error.message);
console.log(error.name);
console.log(error.errors);
resultObj.failItems.push(moveItem.name);
}
}
}
resultObj.message = `요청 하신 ${nameArray.length} 개 파일 중 ${ resultObj.successItems.length} 개 파일이 ${text} 되었습니다.`;
}
}
return res.json(resultObj);
}
});
serverApp.post('/api/loading',
async (req, res, next)=>{
if (req.body) {
const {url} = req.body;
if (url) {
try {
const result = await axios.get(`${url}`);
if (result && result.data) {
console.log(result.data);
return res.json(result.data);
}
}
catch (error) {
// console.log(error);
console.log(error.message);
console.log(error.name);
console.log(error.errors);
return res.json(error);
}
}
}
});