-
Notifications
You must be signed in to change notification settings - Fork 12
/
index.js
181 lines (164 loc) · 6.72 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
var http = require('http');
var os = require( 'os' );
var fs = require('fs');
var path = require('path');
var express = require('express');
var ejs = require('ejs');
var body_parser = require('body-parser');
var defaults = {
SERVER_PORT: 54100,
CLIENT_PORT: 54101,
EMBED_TYPE: 'iframe',
};
var serverPort = process.env.SERVER_PORT || defaults.SERVER_PORT;
var clientPort = process.env.CLIENT_PORT || defaults.CLIENT_PORT;
//NOTE Set EMBED_TYPE to "iframe" or "dom"
var useIframe = (process.env.EMBED_TYPE || defaults.EMBED_TYPE) === 'iframe';
console.log('useIframe', useIframe);
function getIpAddress() {
var networkInterfaces = os.networkInterfaces();
var keys = Object.keys(networkInterfaces);
for (var x = 0; x < keys.length; ++x) {
var netIf = networkInterfaces[keys[x]];
for (var y = 0; y < netIf.length; ++ y) {
var addr = netIf[y];
if (addr.family === 'IPv4' && !addr.internal) {
return addr.address;
}
}
}
return '127.0.0.1';
}
var serverHostSansProtocol = ''+getIpAddress()+':'+serverPort;
var clientHostSansProtocol = ''+getIpAddress()+':'+clientPort;
var serverHost = '//'+serverHostSansProtocol;
module.exports.serverHost = serverHost;
module.exports.serverDirname = __dirname;
var platformScriptPathTemplate = '/3rd/:platform/platform.js';
var platformStylePathTemplate = __dirname+'/views/server/3rd/:platform/platform.css';
var platformWidgetInitPathTemplate = '/3rd/:platform/widget-init.html';
module.exports.platformScriptPathTemplate = platformScriptPathTemplate;
module.exports.platformStylePathTemplate = platformStylePathTemplate;
module.exports.platformWidgetInitPathTemplate = platformWidgetInitPathTemplate;
var demo3rdPartyId = 1122334455;
//NOTE For additional security, require 3rd parties to white list
//domains that they will be pasting their snippets onto
var demo3rdPartyAllowedHostsFixtures;
if (! useIframe) {
demo3rdPartyAllowedHostsFixtures = {
'1122334455': [ clientHostSansProtocol ]
};
}
//Platform server
var serverApp = express();
serverApp.use(body_parser.json());
function isValidPartyId(partyId) {
//NOTE this check is trivial for now, of course,
//in a production platform we would use something like an API key
//lookup against the requested resources
return partyId && partyId.length === 10;
}
function isOriginAllowedForThisPartyId(origin, partyId) {
var allowedHostsForParty = demo3rdPartyAllowedHostsFixtures[''+partyId];
// console.log('isOriginAllowedForThisPartyId', partyId, origin, allowedHostsForParty);
if (Array.isArray(allowedHostsForParty)) {
var hostSansProtocol = origin.split('//');
hostSansProtocol = hostSansProtocol[hostSansProtocol.length -1];
return (allowedHostsForParty.indexOf(hostSansProtocol) >= 0);
}
else {
return false;
}
}
//enable CORS - only if not using iframes
if (!useIframe) {
serverApp.use(function(req, res, next) {
//only for paths that come under /api/3rd
if ((/^\/api\/3rd\/.+$/).test(req.path)) {
var partyId = req.query.partyId;
if (! isValidPartyId(partyId)) {
res.status(403).end();
return;
}
var corsOrigin = req.headers.origin;
var corsMethod = req.headers['access-control-request-method'];
var corsHeaders = req.headers['access-control-request-headers'];
var hasACorsFlag = corsOrigin || corsMethod || corsHeaders;
//console.log('cors middleware xhr', hasACorsFlag, corsOrigin, corsMethod, corsHeaders);
if (hasACorsFlag) {
if (req.method !== 'OPTIONS') {
//pre-flight check: do *not* count on query parameters being set
//TODO Ask each 3rd party to login to an admin panel and white list
//the domains that they would like to embed these widgets on
//Then test here that the domain matches
if (! isOriginAllowedForThisPartyId((corsOrigin || ''), partyId)) {
res.status(403).end();
return;
}
}
res.header('Access-Control-Allow-Origin', corsOrigin);
res.header('Access-Control-Allow-Methods', corsMethod);
res.header('Access-Control-Allow-Headers', corsHeaders);
res.header('Access-Control-Max-Age', 60 * 60 * 24);
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
}
}
next();
});
}
//serve platform script
serverApp.get(platformScriptPathTemplate, function(req, res) {
var partyId = req.query.partyId;
var platform = req.params.platform;
var platformScriptPath = platformScriptPathTemplate.replace(':platform', platform);
var platformStylePath = platformStylePathTemplate.replace(':platform', platform);
//TODO inserting the CSS file into the platform script like this is quite wasteful
//This is a prime candidate for refactoring or at the very least result caching
fs.readFile(path.normalize(platformStylePath), function(err, data) {
data = ((!err && data && data.toString()) || '').replace( /(?:\r\n|\r|\n)/g , ' ');
res.render('server'+platformScriptPath, {
partyId: partyId,
serverHost: serverHost,
platformScript: platformScriptPath,
inlineCss: data,
useIframe: useIframe,
});
});
});
//serve platform script file
fs.readdir('platforms', function(err, files) {
var dirs = [];
if (Array.isArray(files)) {
dirs = files.filter(function(file) {
return fs.statSync('platforms/'+file).isDirectory();
});
}
console.log(dirs);
serverApp.set('view engine', 'html');
serverApp.engine('html', ejs.renderFile);
serverApp.engine('js', ejs.renderFile);
dirs.forEach(function(dir) {
serverApp.use(require('./platforms/'+dir));
});
serverApp.use(express.static('server'));
});
//3rd party using widgets served by platform server
var clientApp = express();
clientApp.set('view engine', 'html');
clientApp.engine('html', ejs.renderFile);
clientApp.get('/favicon.ico', function(req, res) {
res.status(404).end();
});
clientApp.get('/:platform/', function(req, res) {
var platformScriptPath = platformScriptPathTemplate.replace(':platform', req.params.platform);
res.render('client/'+req.params.platform+'/index', {
partyId: demo3rdPartyId,
serverHost: serverHost,
platformScript: platformScriptPath,
});
});
http.createServer(serverApp).listen(serverPort);
http.createServer(clientApp).listen(clientPort);