Last active
November 26, 2022 09:01
-
-
Save alexp1917/1a6d5584cf757bf6ca547bcc69000033 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
var path = require('path'); | |
var querystring = require('querystring'); | |
var express = require('express'); | |
var Axios = require('axios'); | |
var bodyParser = require('body-parser'); | |
var session = require('express-session'); | |
var wpapi = require('wpapi'); | |
const { v4: uuidv4 } = require('uuid'); | |
var app = express(); | |
app.use(bodyParser.json()); | |
app.use(express.static(__dirname + '/www/')); | |
app.use(bodyParser.urlencoded({ extended: true })); | |
app.use(session({ | |
secret: process.env.SESSION_COOKIE_SECRET || uuidv4(), | |
resave: true, | |
saveUninitialized: false, | |
cookie: { secure: false, }, | |
})) | |
app.get('/', (req, res) => { | |
res.send('Send send'); | |
}); | |
// start http server | |
app.set('port', 3000); | |
let IP = process.env.IP || '0.0.0.0' | |
var server = app.listen(app.get('port'), IP, function() { | |
console.log('Server listening on port :' + server.address().port); | |
}); | |
// start socket server | |
var io = require('socket.io')(server); | |
let socketid = null; | |
let connectedUsers = []; | |
io.on('connection', function(socket) { | |
socketid = socket.id | |
socket.join(socketid); | |
connectedUsers.push({ socketid }) | |
socket.emit('user_connected', { connectedUsers, socketid }) | |
console.log('socket connected', connectedUsers); | |
}); | |
// forge credentials | |
var FORGE_CLIENT_ID = process.env.FORGE_CLIENT_ID; | |
var FORGE_CLIENT_SECRET = process.env.FORGE_CLIENT_SECRET; | |
var FORGE_CALLBACK_HOST = process.env.FORGE_CALLBACK_HOST; | |
var access_token = ''; | |
var scopes = 'data:read data:write data:create bucket:create bucket:read code:all'; | |
function isTokenExpired() { | |
// todo implement access_token expiration | |
return false; | |
} | |
async function getNewToken() { | |
var response = await Axios({ | |
method: 'POST', | |
url: 'https://developer.api.autodesk.com/authentication/v1/authenticate', | |
headers: { | |
'content-type': 'application/x-www-form-urlencoded' | |
}, | |
data: querystring.stringify({ | |
client_id: FORGE_CLIENT_ID, | |
client_secret: FORGE_CLIENT_SECRET, | |
grant_type: 'client_credentials', | |
scope: scopes | |
}) | |
}); | |
return response.data.access_token; | |
} | |
async function getToken() { | |
if (!access_token || isTokenExpired()) | |
access_token = await getNewToken(); | |
return access_token; | |
} | |
app.get('/api/forge/oauth/pdf', function apiForgePdfRoute(req, res) { | |
apiForgeStartDownloadHandler(req, res, 'pdf'); | |
}); | |
app.get('/api/forge/oauth/zip', function apiForgeZipRoute(req, res) { | |
apiForgeStartDownloadHandler(req, res, 'zip'); | |
}); | |
/** | |
* Main entrypoint to this API, calls {@link signandworkitemInventor} | |
* | |
* @param {'pdf'|'zip'} downloadType | |
**/ | |
async function apiForgeStartDownloadHandler(req, res, downloadType) { | |
try { | |
// generate activityId and save into session | |
let activityId = req.session.activityId = uuidv4(); | |
await signandworkitemInventor({ activityId, downloadType }); | |
// todo send activityId to frontend right away | |
res.sendStatus(204); | |
} catch (error) { | |
console.log('oauth/', downloadType, ': ', error); | |
res.send('Failed to authenticate'); | |
} | |
} | |
var LABEL = process.env.LABEL || 'Name' | |
//var alias = 'prod'; | |
var alias = 'beta'; | |
var qualifiedName = FORGE_CLIENT_ID + '.' + alias; | |
var inputBucketName = FORGE_CLIENT_ID.toLowerCase() + '_ainput'; | |
var outputBucketName = FORGE_CLIENT_ID.toLowerCase() + '_aoutput'; | |
var topAssemblyName = 'MasterAssembly.iam'; | |
var uploadZipName = 'MasterAssembly.zip'; | |
var pdfName = LABEL + '.pdf'; | |
var zipName = LABEL + '.zip'; | |
var AUTO_DESK_BASE_URL = 'https://developer.api.autodesk.com' | |
var AUTO_DESK_API_BASE_URL = AUTO_DESK_BASE_URL + '/oss/v2'; | |
var AUTO_DESK_API_BUCKETS = AUTO_DESK_API_BASE_URL + '/buckets'; | |
var AUTO_DESK_API_CLIENT_BUCKET = AUTO_DESK_API_BUCKETS + encodeURIComponent(outputBucketName); | |
var CLIENT_PDF_BUCKET = AUTO_DESK_API_CLIENT_BUCKET + '/objects/' + encodeURIComponent(pdfName); | |
var CLIENT_ZIP_BUCKET = AUTO_DESK_API_CLIENT_BUCKET + '/objects/' + encodeURIComponent(zipName); | |
/* | |
it appears that these urls are being used to download the final result, and they are shared among all users, which is the route of the problem? | |
*/ | |
var resultPdfUrl = 'https://developer.api.autodesk.com/oss/v2/' + 'buckets/' + encodeURIComponent(outputBucketName) + '/objects/' + encodeURIComponent(pdfName); | |
var resultZipUrl = 'https://developer.api.autodesk.com/oss/v2/' + 'buckets/' + encodeURIComponent(outputBucketName) + '/objects/' + encodeURIComponent(zipName); | |
var inventorInputSignedUrl = ''; | |
// this can be global, supposedly, since no arguments to this function | |
var getSignedUrl_cache = ''; | |
async function getSignedUrl() { | |
if (!getSignedUrl_cache) getSignedUrl_cache = await getNewSignedUrl(); | |
return getSignedUrl_cache; | |
} | |
async function getNewSignedUrl() { | |
return await Axios({ | |
method: 'POST', | |
url: 'https://developer.api.autodesk.com/oss/v2/buckets/' + encodeURIComponent(inputBucketName) + '/objects/' + encodeURIComponent(uploadZipName) + '/signed', | |
headers: { | |
Authorization: 'Bearer ' + access_token, | |
'content-type': 'application/json' | |
}, | |
data: {} | |
}) | |
.then(response => { | |
var url = response.data.signedUrl; | |
var substring = url.substring(0, 100); | |
console.log('Success - getNewSignedUrl', substring + (substring.length === 100 ? '...' : '')); | |
return url; | |
}); | |
} | |
function signandworkitemInventor({ activityId, downloadType, }) { | |
console.log('Start signing upload', activityId, 'of type:', downloadType); | |
return getSignedUrl() | |
.then(() => createWorkItem(downloadType, activityId)) | |
.then(() => console.log('created work item of type', downloadType)) | |
.catch(function(error) { | |
// Failed | |
console.log('Failed'); | |
console.log(error); | |
console.log('Failed signing input zip'); | |
}); | |
} | |
let model_json; | |
let paramJSON; | |
const jsonData = { | |
model_data: '', | |
ipaddr: '', | |
uuid: uuidv4() | |
} | |
function getCallerIP(request) { | |
var ip = request.headers['x-forwarded-for'] || | |
request.connection.remoteAddress || | |
request.socket.remoteAddress || | |
request.connection.socket.remoteAddress; | |
ip = ip.split(',')[0]; | |
ip = ip.split(':').slice(-1); //in case the ip returned in a format: "::ffff:146.xxx.xxx.xxx" | |
return ip; | |
} | |
app.post('/htmlvalues', function htmlValuesRoute(req, res) { | |
model_json = req.body; | |
paramJSON = 'data:application/json,' + JSON.stringify(model_json); | |
console.log(paramJSON); | |
jsonData.model_data = req.body; | |
jsonData.ipaddr = getCallerIP(req); | |
res.sendStatus(204); | |
}); | |
var user_id = {}; | |
app.post('/user_id', function userIdRoute(req, res) { | |
user_id = req.body; | |
console.log('user_id', user_id); | |
io.to(socketid).emit('get_id', user_id.user_id) | |
res.sendStatus(204); | |
}); | |
async function createWorkItem(type, activityId) { | |
// output varies by type | |
var { | |
outputName, | |
outputUrl, | |
} = ({ | |
pdf: { | |
outputName: 'OutputPDF', | |
outputUrl: CLIENT_PDF_BUCKET, | |
}, | |
zip: { | |
outputName: 'OutputZIP', | |
outputUrl: CLIENT_ZIP_BUCKET, | |
}, | |
}[type]); | |
var forgeData = { | |
activityId, | |
arguments: { | |
InventorDoc: { | |
url: await getSignedUrl(), | |
pathInZip: topAssemblyName | |
}, | |
InventorParams: { | |
url: paramJSON, | |
}, | |
[outputName]: { | |
url: outputUrl, | |
headers: { | |
Authorization: 'Bearer ' + await getToken(), | |
'Content-type': 'application/octet-stream', | |
}, | |
verb: 'put' | |
}, | |
onComplete: { | |
verb: 'post', | |
url: FORGE_CALLBACK_HOST + '/api/forge/datamanagement/signanddownload' + type, | |
}, | |
}, | |
}; | |
var text = JSON.stringify(forgeData); | |
console.log('created forgeData:', text); | |
return Axios({ | |
method: 'POST', | |
url: 'https://developer.api.autodesk.com/da/us-east/v3/workitems', | |
headers: { | |
'Authorization': 'Bearer ' + await getToken(), | |
'content-type': 'application/json' | |
}, | |
data: text, | |
}) | |
.then(function createWorkItem_success(response) { | |
// Success | |
console.log('Success creating new work item'); | |
var dirPath = `./www/Downloads/${user_id.user_id}_${model_json.ProjectName}`; | |
await mkdirp(dirPath); | |
io.to(socketid).emit('createWorkItem'); | |
}) | |
.catch(function createWorkItem_failed(error) { | |
// Failed | |
io.to(socketid).emit('createWorkItem_error', error) | |
console.log('Failed to create new work item'); | |
console.log(error); | |
console.log('Failed to create new work item'); | |
}); | |
} | |
async function mkdirp(dirPath) { | |
if (!(await fs.promises.stat(dirPath).then(() => true).catch(() => false))) { | |
await fs.promises.mkdir(dirPath, { recursive: true }); | |
} | |
} | |
app.post('/api/forge/datamanagement/signanddownloadzip', async function signAndDownloadZip(req, res) { | |
apiForgeCallbackHandler(req, res, 'zip'); | |
}); | |
app.post('/api/forge/datamanagement/signanddownloadpdf', async function signAndDownloadPdf(req, res) { | |
apiForgeCallbackHandler(req, res, 'pdf'); | |
}); | |
function getActivityIdFromCallbackRequest(request) { | |
// todo implement me | |
return null; | |
} | |
/** | |
* Sign PDF/ZIP & download file | |
* <p> | |
* second most important entrypoint of API | |
**/ | |
async function apiForgeCallbackHandler(req, res, type) { | |
let activityId = getActivityIdFromCallbackRequest(req); | |
let resultUrl = { | |
pdf: resultPdfUrl, | |
zip: resultZipUrl, | |
}[type] || (() => { throw new Error('no known type: ' + type )})(); | |
let url = await signResultUrl({ | |
activityId, | |
resultUrl, | |
}); | |
let downloadName = { | |
pdf: pdfName, | |
zip: zipName, | |
}[type] || (() => { throw new Error('no known type: ' + type )})(); | |
downloadFile({ activityId, downloadName, url }) | |
.catch(e => console.error('async execution of downloadFile filed', e)); | |
console.log(type, 'downloaded... (started)'); | |
res.sendStatus(204); | |
} | |
async function signResultUrl({ | |
// todo actually use activityId to determine result url for proper separation | |
activityId, | |
resultUrl, | |
}) { | |
let ax = await Axios({ | |
method: 'POST', | |
url: resultUrl + '/signed', | |
headers: { | |
Authorization: 'Bearer ' + access_token, | |
'content-type': 'application/json' | |
}, | |
data: {} | |
}); | |
var signedUrl = ax.data.signedUrl; | |
// todo move this to when actually downloaded | |
io.to(socketid).emit('downloadsReady', { | |
activityId, | |
signedUrl, | |
}); | |
return ax.data.signedUrl; | |
} | |
async function downloadFile({ | |
downloadName: name, | |
activityId, | |
url: currentReturnUrl, | |
}, | |
isLast = false) { | |
console.log('currentReturnUrl', currentReturnUrl) | |
// generate name | |
var d = new Date(); | |
var date = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`; | |
name = `${date}-${jsonData.uuid}-${name}`; | |
var folder_name = `${user_id.user_id}_${model_json.ProjectName}`; | |
var downloadedFileDir = path.resolve(__dirname, 'www', 'Downloads', folder_name); | |
var downloadedFilePath = path.resolve(downloadedFileDir, name); | |
await mkdirp(downloadedFileDir); | |
var response = await Axios({ | |
method: 'GET', | |
url: currentReturnUrl, | |
responseType: 'stream' | |
}); | |
var f = fs.createWriteStream(downloadedFilePath); | |
if (isLast) { | |
f.on('finish', function() { | |
unZipSvf(); | |
}); | |
} | |
response.data.pipe(f); | |
var url = `${FORGE_CALLBACK_HOST}/Downloads/${folder_name}/${name}` | |
io.to(socketid).emit('showLink', { | |
activityId, | |
url, | |
}); | |
// seems like createPosts does not exist | |
typeof createPosts === 'function' && | |
createPosts(user_id.user_id, model_json.ProjectNumber, model_json.ProjectName, url) | |
return true; | |
} | |
/* | |
var wp = new wpapi({ | |
endpoint: process.env.WP_API_ENDPOINT, | |
username: process.env.WP_API_USERNAME, | |
password: process.env.WP_API_PASSWORD, | |
auth: true | |
}); | |
var namespace = 'wp/v2'; | |
var route = '/projects'; | |
wp.project = wp.registerRoute(namespace, route); | |
const createPosts = async (user_id,proj_num,proj_name,location)=>{ | |
let ip = jsonData.ipaddr[0] | |
try{ | |
const dat = await wp.project().create({ | |
title:`${proj_num}-${proj_name}`, | |
content:querystring.stringify(jsonData.model_data), | |
fields: { | |
user_id:user_id, | |
project_name:proj_name, | |
download_url:location, | |
last_session:ip, | |
created_at:new Date(), | |
}, | |
status: 'publish' | |
}) | |
console.log(dat) | |
io.to(socketid).emit('wordpress_send',dat); | |
}catch(err){ | |
io.to(socketid).emit('wordpress_send_error',err); | |
console.log(err); | |
} | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment