@@ -3,16 +3,18 @@ const fork = require('child_process').fork;
33const util = require ( 'util' ) ;
44const async = require ( 'async' ) ;
55const _ = require ( 'lodash' ) ;
6- const EventEmitter = require ( 'events' ) . EventEmitter ;
76const logger = require ( '../../utils' ) . logger ;
87const redis = require ( '../../utils' ) . redis ;
98const settings = require ( '../../settings' ) ;
109const fs = require ( 'fs' ) ;
1110
12- const API = require ( '../../models/API' ) ;
13- const List = require ( '../../models/List' ) ;
14- const User = require ( '../../models/User' ) ;
11+ const API = require ( '../../models/API' ) ;
12+ const List = require ( '../../models/List' ) ;
13+ const User = require ( '../../models/User' ) ;
1514const Snippet = require ( '../../models/Snippet' ) ;
15+ const Version = require ( '../../models/Version' ) ;
16+
17+ const EventEmitter = require ( 'events' ) . EventEmitter ;
1618
1719const GeneratorForker = function ( options ) {
1820 let self = this ;
@@ -22,19 +24,19 @@ const GeneratorForker = function(options) {
2224 results : options . results
2325 } ;
2426
25- this . name = options . name ;
26- this . startTime = new Date ( ) . getTime ( ) ;
27- this . jobCount = 0 ;
28- this . memory = 0 ;
29- this . listCache = 0 ;
27+ this . name = options . name ;
28+ this . startTime = new Date ( ) . getTime ( ) ;
29+ this . jobCount = 0 ;
30+ this . memory = 0 ;
31+ this . listCache = 0 ;
32+ this . attempted = false ;
3033 this . snippetCache = 0 ;
31- this . attempted = false ;
3234
3335 // Queue to push generate requests into
3436 this . queue = async . queue ( ( task , callback ) => {
3537 this . jobCount ++ ;
3638
37- // Realtime or Speedtest
39+ // Linter, Demo, or normal request?
3840 if ( task . socket !== undefined ) {
3941 if ( task . data . type === "snippet" ) {
4042 var options = { key : null , src : task . data . src , ref : null } ;
@@ -104,8 +106,6 @@ const GeneratorForker = function(options) {
104106
105107 this . fork ( ) ;
106108
107- generatorChecks ( ) ;
108-
109109 // See if child process is alive during 5 second check
110110 setInterval ( generatorChecks , 5000 ) ;
111111
@@ -168,21 +168,26 @@ const GeneratorForker = function(options) {
168168util . inherits ( GeneratorForker , EventEmitter ) ;
169169
170170GeneratorForker . prototype . fork = function ( ) {
171+ let self = this ;
172+
171173 // Fork new Generator with provided info
172174 this . generator = fork ( __dirname + '/Generator' , [ this . name , JSON . stringify ( this . info ) ] , { silent : true } ) ;
173175
174176 // Handle all events
175177 // {type, mode, data}
176178 this . generator . on ( 'message' , msg => {
179+
177180 if ( msg . type === 'lookup' ) {
178181 if ( msg . mode === 'api' ) {
179182 API . getCond ( { ref : msg . data } ) . then ( doc => {
180183 this . generator . send ( { type : 'response' , mode : 'api' , data : doc } ) ;
181184 } ) ;
185+
182186 } else if ( msg . mode === 'user' ) {
183187 User . getCond ( { [ "u.id" ] : msg . data } ) . then ( doc => {
184188 this . generator . send ( { type : 'response' , mode : 'user' , data : doc } ) ;
185189 } ) ;
190+
186191 } else if ( msg . mode === 'list' ) {
187192 // Check if list exists in the cache
188193 redis . exists ( "list:" + msg . data . ref , ( err , result ) => {
@@ -197,24 +202,27 @@ GeneratorForker.prototype.fork = function() {
197202 redis . hgetall ( "list:" + msg . data . ref , ( err , obj ) => {
198203
199204 // Verify user has permission to access this list
200- if ( Number ( obj . owner ) === msg . data . user . id ) {
205+ if ( Number ( obj . owner ) === msg . data . user . id && obj . ref !== undefined ) {
201206
202207 // Update lastUsed time
203- if ( obj . ref !== undefined ) {
204- redis . hset ( "list:" + obj . ref , "lastUsed" , new Date ( ) . getTime ( ) ) ;
205- }
206-
207- this . generator . send ( { type : 'response' , mode : 'list' , data : true } ) ;
208- } else {
209- this . generator . send ( { type : 'response' , mode : 'list' , data : false } ) ;
208+ redis . hset ( "list:" + obj . ref , "lastUsed" , new Date ( ) . getTime ( ) ) ;
210209 }
210+
211+ this . generator . send ( {
212+ type : 'response' ,
213+ mode : 'list' ,
214+ data : Number ( obj . owner ) === msg . data . user . id
215+ } ) ;
211216 } ) ;
212217
213218 // Add list to cache if user has permission
214219 } else {
220+
215221 List . getCond ( { ref : msg . data . ref , owner : msg . data . user . id } ) . then ( doc => {
222+
216223 if ( doc === null ) {
217224 this . generator . send ( { type : 'response' , mode : 'list' , data : false } ) ;
225+
218226 } else {
219227 fs . readFile ( process . cwd ( ) + '/data/lists/' + doc . id + '.list' , 'utf8' , ( err , file ) => {
220228 redis . hmset ( "list:" + doc . ref , {
@@ -238,15 +246,13 @@ GeneratorForker.prototype.fork = function() {
238246 }
239247 } ) ;
240248 } else if ( msg . mode === 'snippet' ) {
241- // global
242- let obj ;
243- let glob = false ;
244- if ( msg . data . indexOf ( '/' ) === - 1 ) {
245- obj = `snippet:${ msg . data } ` ;
246- glob = true ;
249+ let obj , tmp = msg . data . signature . split ( '/' ) ;
250+
251+ // No version supplied
252+ if ( tmp . length === 2 ) {
253+ obj = `snippet:${ tmp [ 0 ] } /${ tmp [ 1 ] } ` ;
247254 } else {
248- msg . data = msg . data . split ( '/' ) ;
249- obj = `snippet:${ msg . data [ 0 ] } /${ msg . data [ 1 ] } ` ;
255+ obj = `snippet:${ tmp [ 0 ] } /${ tmp [ 1 ] } /${ tmp [ 2 ] } ` ;
250256 }
251257
252258 // Check if snippet exists in the cache
@@ -255,11 +261,15 @@ GeneratorForker.prototype.fork = function() {
255261 // Snippet exists in the cache
256262 if ( result === 1 ) {
257263
258- // Update TTL
259- redis . expire ( obj , settings . generators [ this . name ] . redisSnippetTTL ) ;
260- redis . expire ( `${ obj } :contents` , settings . generators [ this . name ] . redisSnippetTTL ) ;
261-
262264 redis . hgetall ( obj , ( err , obj ) => {
265+ // Check if user is authorized to use this snippet
266+ if ( obj . published !== 1 && obj . owner !== msg . data . user . id ) {
267+ return this . generator . send ( { type : 'response' , mode : 'snippet' , data : false } ) ;
268+ }
269+
270+ // Update TTL
271+ redis . expire ( obj , settings . generators [ this . name ] . redisSnippetTTL ) ;
272+ redis . expire ( `${ obj } :contents` , settings . generators [ this . name ] . redisSnippetTTL ) ;
263273
264274 // Update lastUsed time
265275 if ( obj . ref !== undefined ) {
@@ -271,52 +281,89 @@ GeneratorForker.prototype.fork = function() {
271281
272282 // Add snippet to cache if user has permission
273283 } else {
274- Snippet . getCond ( { username : msg . data [ 0 ] , name : msg . data [ 1 ] } ) . then ( doc => {
275- if ( doc === null ) {
276- this . generator . send ( { type : 'response' , mode : 'snippet' , data : false } ) ;
277- } else {
278- fs . readFile ( process . cwd ( ) + '/data/snippets/' + doc . id + '.snippet' , 'utf8' , ( err , file ) => {
279- // prepend and append
280- file = `(function() {
284+ msg . data . user = msg . data . user || { username : null } ;
285+ User . getCond ( { username : msg . data . user . username } ) . then ( user => {
286+ if ( user === null ) user = { id : - 1 } ;
287+ let query = { username : tmp [ 0 ] , name : tmp [ 1 ] } ;
288+
289+ Snippet . getCond ( query ) . then ( doc => {
290+
291+ // No matching snippet found
292+ if ( doc === null ) {
293+ return this . generator . send ( { type : 'response' , mode : 'snippet' , data : false } ) ;
294+ }
295+
296+ // If not published, use version 1
297+ let version = doc . published === 0 ? 1 : Number ( tmp [ 2 ] ) ;
298+
299+ // Published snippets require version number
300+ if ( doc . published && version === undefined ) {
301+ return this . generator . send ( { type : 'response' , mode : 'snippet' , data : "missing_version" } ) ;
302+ }
303+
304+ Version . getVersion ( doc . ref , version ) . then ( ver => {
305+
306+ if ( ver === null ) {
307+ this . generator . send ( { type : 'response' , mode : 'snippet' , data : "invalid_version" } ) ;
308+ }
309+
310+ // If snippet isn't published and this is a demo user OR the user doesn't own snippet
311+ else if ( ( ! ver . published && user === null ) || ( ! ver . published && user . id !== doc . owner ) ) {
312+ this . generator . send ( { type : 'response' , mode : 'snippet' , data : false } ) ;
313+
314+ } else {
315+ fs . readFile ( process . cwd ( ) + '/data/snippets/' + doc . id + '-' + ver . version + '.snippet' , 'utf8' , ( err , file ) => {
316+ // prepend and append
317+ file = `(function() {
281318 let snippet = {};
282319 ${ file }
283320 return snippet;
284321})()` ;
285- redis . hmset ( obj , {
286- added : new Date ( ) . getTime ( ) ,
287- size : file . length ,
288- owner : doc . owner ,
289- lastUsed : new Date ( ) . getTime ( )
290- } , ( err , res ) => {
291- redis . SET ( `${ obj } :contents` , file , ( a , b ) => {
292-
293- // Add TTL
294- redis . expire ( obj , settings . generators [ this . name ] . redisSnippetTTL ) ;
295- redis . expire ( `${ obj } :contents` , settings . generators [ this . name ] . redisSnippetTTL ) ;
296-
297- this . generator . send ( { type : 'response' , mode : 'snippet' , data : true } ) ;
322+ redis . hmset ( obj , {
323+ added : new Date ( ) . getTime ( ) ,
324+ size : file . length ,
325+ owner : Number ( doc . owner ) ,
326+ published : Number ( ver . published ) ,
327+ lastUsed : new Date ( ) . getTime ( )
328+ } , ( err , res ) => {
329+ redis . SET ( `${ obj } :contents` , file , ( a , b ) => {
330+
331+ // Add TTL
332+ redis . expire ( obj , settings . generators [ this . name ] . redisSnippetTTL ) ;
333+ redis . expire ( `${ obj } :contents` , settings . generators [ this . name ] . redisSnippetTTL ) ;
334+ this . generator . send ( { type : 'response' , mode : 'snippet' , data : true } ) ;
335+ } ) ;
336+ } ) ;
298337 } ) ;
299- } ) ;
338+ }
300339 } ) ;
301- }
340+ } ) ;
302341 } ) ;
303342 }
304343 } ) ;
305344 }
345+
306346 } else if ( msg . type === 'done' ) {
307347 this . emit ( 'taskFinished' , { error : msg . data . error , results : msg . data . results , fmt : msg . data . fmt } ) ;
348+
308349 } else if ( msg . type === 'cmdComplete' ) {
309350 if ( msg . mode === 'memory' ) {
310351 this . emit ( 'memComplete' , msg . content ) ;
352+
311353 } else if ( msg . mode === 'lists' ) {
312354 this . emit ( 'listsComplete' , msg . content ) ;
355+
313356 } else if ( msg . mode === 'listCache' ) {
314357 this . emit ( 'listCacheComplete' , msg . content ) ;
358+
315359 } else if ( msg . mode === 'snippetCache' ) {
316360 this . emit ( 'snippetCacheComplete' , msg . content ) ;
361+
317362 }
363+
318364 } else if ( msg . type === 'pong' ) {
319365 this . emit ( 'pong' ) ;
366+
320367 } else if ( msg . type === 'ping' ) {
321368 this . send ( {
322369 type : 'pong'
@@ -326,8 +373,8 @@ GeneratorForker.prototype.fork = function() {
326373} ;
327374
328375// Opts contains options and mode
329- // cb(err, results, fmt)
330376GeneratorForker . prototype . generate = function ( opts , cb ) {
377+
331378 // Send generator a new task using the given mode and options
332379 this . send ( {
333380 type : 'task' ,
0 commit comments