Skip to content

Instantly share code, notes, and snippets.

@alexp1917
Last active November 26, 2022 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alexp1917/1a6d5584cf757bf6ca547bcc69000033 to your computer and use it in GitHub Desktop.
Save alexp1917/1a6d5584cf757bf6ca547bcc69000033 to your computer and use it in GitHub Desktop.

Server function for iFrame plugin

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