UNPKG

6.73 kBJavaScriptView Raw
1var Base = require('../base')
2 , Connector = require('./connector')
3 , fs = require('fs')
4 , socketIoPkgJson = require('socket.io-client/package.json')
5
6var debug = Base.logger('ix-publisher')
7
8var exportInfo
9var exportedProps
10var deviceData = null
11var _deviceData = false
12
13
14exports.publishAndFindProxy = function(ixApiDir, cb_err_data){
15 loadIxExports(ixApiDir, function(err, methodInfo){
16 if (err) { cb_err_data(err); return }
17 publishExportsAndGetProxy(methodInfo, cb_err_data)
18 })
19}
20
21var withDeviceData = exports.withDeviceData = function(cb_err_data){
22 if (_deviceData) { cb_err_data(null, deviceData); return }
23 _deviceData = true;
24 deviceData = { secret: Base.env.IX_DEVICE_SECRET }
25 if (Base.env.IX_DEVICE_KEY) {
26 console.log('DEVICE KEY: '+Base.env.IX_DEVICE_KEY)
27 deviceData.key = Base.env.IX_DEVICE_KEY
28 cb_err_data(null, deviceData)
29 }
30 else if (Base.env.IX_DEVICE_KEY_FILE === 'NONE') {
31 cb_err_data(null, deviceData)
32 }
33 else {
34 fs.readFile(Base.env.IX_DEVICE_KEY_FILE || '/factory/serial_number', { encoding: 'utf8' }, function(err,file){
35 debug('withDeviceKey', err, file)
36 var key = null
37 if (file) {
38 key = file.split(/\n/)[0].trim()
39 console.log('DEVICE KEY: '+key)
40 }
41 deviceData.key = key
42 cb_err_data(err, deviceData)
43 })
44 }
45}
46
47exports.ixCallNodejs = function(callData, responder){
48 debug('ixCallNodejs', callData)
49 var func = findProp(callData.api)
50 if (func) {
51 exports.ixCall(callData, responder, function(cd, clientCallback){
52 var args = cd.args.slice(0)
53 args.push(clientCallback)
54 func.apply({}, args)
55 ////
56 // 2015-01-23:
57 // Support coffeescript more easily, don't support return values.
58 ////
59 // if (returnVal !== void(0)) {
60 // // JSON doesn't easily differentiate between (undefined) and (null),
61 // // so we can't transmit (undefined) across the wire.
62 // // Therefore, use (null) as the "don't wait for the callback" value
63 // // (instead of using a special Base.IX_RETURN_UNDEFINED value, that
64 // // we try to convert to (undefined) when transmitting it back).
65 // //clientCallback(null, returnVal === Base.IX_RETURN_UNDEFINED ? void(0) : returnVal)
66 // // Always be async. http://nodejs.org/api/process.html#process_process_nexttick_callback
67 // process.nextTick(function(){ clientCallback(null, returnVal) })
68 // }
69 })
70 }
71 else {
72 debug('ixCallNodejs', "Property not exported: '"+callData.api+"'.")
73 throw 'Invalid function: '+callData.api
74 }
75}
76
77exports.ixCall = function(callData, responder, invoker){
78 var timeoutSecs = Number(callData.timeout || Base.env.IX_PROXY_TIMEOUT)
79 if (!(timeoutSecs > 0)) { // careful about NaN / Number(undefined)
80 timeoutSecs = 30 // default 30 seconds
81 }
82 var wait = timeoutSecs * 1000
83 var timerOn = true
84 var timer = setTimeout(function(){
85 if (timerOn) {
86 timerOn = false
87 var err = 'TIMEOUT: Callback not invoked after '+(wait/1000)+' seconds, aborting. (Start server with the environment variable FAAS_IX_PROXY_TIMEOUT set to customize the timeout.)'
88 debug('ixCall', err)
89 responder({ reason: err })
90 }
91 }, wait)
92 var clientCallback = function(err, data){
93 debug('clientCallback', { err: err, data: data })
94 if (err instanceof Error) {
95 err = {
96 name: err.name,
97 message: err.message
98 }
99 }
100 if (timerOn) {
101 timerOn = false
102 clearTimeout(timer)
103 responder(err, data)
104 }
105 }
106 try {
107 invoker(callData, clientCallback)
108 }
109 catch(e){
110 clientCallback(e)
111 }
112}
113
114var loadIxExports = function(ixApiDir, cb_err_exportInfo){
115 exportInfo = {}
116 exportedProps = {}
117 var ixDirname = ixApiDir || Base.env.IX_API_DIR || 'faas-ix'
118 var ixDir = process.cwd()+'/'+ixDirname
119 fs.readdir(ixDir, function(err, files){
120 debug('readdir', { err: err, files: files })
121 if (err) { cb_err_exportInfo(err, files); return }
122 var file, path, fexports, modName, meta
123 for (var i = 0; i < files.length; i++) {
124 file = files[i]
125 path = ixDir+'/'+file
126 try {
127 if (file.match(/\w$/)) { // Don't load auto-save files.
128 fexports = require(path) // Supports files and module directories.
129 debug('loadIxExports:file', file, fexports)
130 modName = file
131 modName = modName.replace(/\.(js|json|node)$/i, '')
132 modName = modName.replace(/\W/g, '_')
133 meta = exportInfo[modName]
134 if (exportInfo[modName]) {
135 console.log("Multiple definitions found for module '"+modName+"', only using definitions from file '"+meta.file+"' (discarding '"+file+"').")
136 }
137 else {
138 exportInfo[modName] = { file: file, exports: metaForObj(fexports) }
139 exportedProps[modName] = fexports
140 }
141 }
142 }
143 catch(e) {
144 console.log('Unable to load module '+path+':', e)
145 }
146 }
147 debug('loadIxExports:exportInfo', JSON.stringify(exportInfo))
148 cb_err_exportInfo(null, exportInfo)
149 })
150}
151
152var metaForObj = function(obj){
153 var info
154 var attName, att, metaInfo
155 if (Base.utils.isFobject(obj)) {
156 info = { isFunc: Base.utils.isFunction(obj) }
157 if (Base.utils.isFunction(obj)) {
158 info.isFunc = true // Top-level function.
159 }
160 for (attName in obj) { if (obj.hasOwnProperty(attName)) {
161 if (!info.atts) { info.atts = {} }
162 att = obj[attName]
163 metaInfo = metaForObj(att)
164 info.atts[attName] = metaInfo
165 } }
166 }
167 else { // Non-object property.
168 info = { value: obj }
169 }
170 // TODO: How to tell if it needs to be invoked with a callback? sync or async?
171 return info
172}
173
174var publishExportsAndGetProxy = exports.publishExportsAndGetProxy = function(exportInfo, cb_err_data) {
175 debug('sioVer', socketIoPkgJson.version)
176 withDeviceData(function(err, device){
177 Base.api({
178 url: { pathname: '/ix/publishAndProxy', query: { sioVer: socketIoPkgJson.version } }
179 , data: { exportInfo: exportInfo, device: device }
180 , method: 'POST'
181 }, cb_err_data)
182 })
183}
184
185exports.publishHttpAndFindProxy = function(cb_err_data) {
186 debug('sioVer', socketIoPkgJson.version)
187 withDeviceData(function(err, device){
188 Base.api({
189 url: { pathname: '/ix/httpAndProxy', query: { sioVer: socketIoPkgJson.version } }
190 , data: { device: device }
191 , method: 'POST'
192 }, cb_err_data)
193 })
194}
195
196var findProp = function(name){
197 var names = name.split('.')
198 return findPropHelper(names, exportedProps)
199}
200
201var findPropHelper = function(names, props){
202 var name = names.shift()
203 var prop = props[name]
204 if (prop && names.length > 0) {
205 prop = findPropHelper(names, prop)
206 }
207 return prop
208}