Skip to content

Commit 3d9ed95

Browse files
authored
Merge pull request #101 from RandomAPI/publish
Publish
2 parents 9f7c680 + 1ecd75a commit 3d9ed95

57 files changed

Lines changed: 1587 additions & 365 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/0.1/Generator.js

Lines changed: 101 additions & 43 deletions
Large diffs are not rendered by default.

api/0.1/GeneratorForker.js

Lines changed: 103 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@ const fork = require('child_process').fork;
33
const util = require('util');
44
const async = require('async');
55
const _ = require('lodash');
6-
const EventEmitter = require('events').EventEmitter;
76
const logger = require('../../utils').logger;
87
const redis = require('../../utils').redis;
98
const settings = require('../../settings');
109
const 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');
1514
const Snippet = require('../../models/Snippet');
15+
const Version = require('../../models/Version');
16+
17+
const EventEmitter = require('events').EventEmitter;
1618

1719
const 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) {
168168
util.inherits(GeneratorForker, EventEmitter);
169169

170170
GeneratorForker.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)
330376
GeneratorForker.prototype.generate = function(opts, cb) {
377+
331378
// Send generator a new task using the given mode and options
332379
this.send({
333380
type: 'task',

app.js

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const app = express();
1313
const server = http.createServer(app);
1414
const _ = require('lodash');
1515
const logger = require('./utils').logger;
16+
const syslog = require('./utils').syslog;
1617
const settings = require('./utils').settings;
1718

1819
const User = require('./models/User');
@@ -23,7 +24,7 @@ const Subscription = require('./models/Subscription');
2324
const session = require('express-session');
2425
const redisStore = require('connect-redis')(session);
2526

26-
// Initialize generators and list/api caches
27+
// Initialize generators
2728
const GeneratorForker = require('./api/0.1/GeneratorForker');
2829
let Generators = {};
2930

@@ -63,6 +64,9 @@ app.set('views', path.join(__dirname, '.viewsMin/pages'));
6364
app.set('view engine', 'ejs');
6465
app.set('port', settings.general.port);
6566

67+
app.set('baseURL', settings.general.baseURL);
68+
app.set('basehref', settings.general.basehref);
69+
6670
// CORS and GZIP
6771
app.use(cors());
6872
app.use(compress());
@@ -92,13 +96,11 @@ app.use(bodyParser.json({limit: '5mb'}));
9296
app.use(bodyParser.urlencoded({ limit: '5mb', extended: false }));
9397
app.use(express.static(path.join(__dirname, 'public')));
9498

95-
let defaultVars = {}; // Default vars to send into views
96-
9799
app.use('*', (req, res, next) => {
98100
// Skip if user is accessing api
99101
if (req.params[0].slice(0, 5) === '/api/') next();
100102
else {
101-
let info = req.flash('info');
103+
let info = req.flash('info');
102104
let warning = req.flash('warning');
103105

104106
if (info.length) {
@@ -109,14 +111,24 @@ app.use('*', (req, res, next) => {
109111
messages = "";
110112
}
111113

112-
app.set('baseURL', settings.general.baseURL);
113-
app.set('basehref', settings.general.basehref);
114+
// Default vars to send into views
115+
app.set('defaultVars', {
116+
session: req.session,
117+
recaptcha: settings.recaptcha.siteKey,
118+
publishableKey: settings.stripe.publishableKey,
119+
basehref: settings.general.basehref,
120+
title: null,
121+
originalUrl: req.originalUrl,
122+
filesize,
123+
messages
124+
});
114125

115-
defaultVars = {filesize, messages, session: req.session, recaptcha: settings.recaptcha.siteKey, publishableKey: settings.stripe.publishableKey, basehref: settings.general.basehref, title: null, originalUrl: req.originalUrl };
116-
app.set('defaultVars', defaultVars);
126+
// Attach user, tier, and subscription details
127+
// to request object for logged in users
117128
if (req.session.loggedin) {
118129
User.getCond({username: req.session.user.username}).then(user => {
119130
if (user === null) {
131+
syslog(req.session);
120132
delete req.session.loggedin;
121133
res.redirect(settings.general.baseURL + '/logout');
122134
return;
@@ -137,24 +149,26 @@ app.use('*', (req, res, next) => {
137149
});
138150

139151
// Routes
140-
app.use('/', require('./routes/index'));
141-
app.use('/code', require('./routes/code'));
142-
app.use('/charge', require('./routes/charge'));
143-
app.use('/new', require('./routes/new'));
144-
app.use('/view', require('./routes/view'));
145-
app.use('/edit', require('./routes/edit'));
146-
app.use('/delete', require('./routes/delete'));
147-
app.use('/api', require('./routes/api'));
152+
app.use('/', require('./routes/index'));
153+
app.use('/ajax', require('./routes/ajax'));
154+
app.use('/code', require('./routes/code'));
155+
app.use('/publish', require('./routes/publish'));
156+
app.use('/charge', require('./routes/charge'));
157+
app.use('/new', require('./routes/new'));
158+
app.use('/view', require('./routes/view'));
159+
app.use('/edit', require('./routes/edit'));
160+
app.use('/delete', require('./routes/delete'));
161+
app.use('/api', require('./routes/api'));
148162
app.use('/settings', require('./routes/settings'));
149163

150164
// production error handler
151165
// no stacktraces leaked to user
152-
app.use((req, res, next) => {
153-
res.sendStatus(404);
166+
app.use((req, res, next) => res.sendStatus(404));
167+
app.use((err, req, res, next) => {
168+
syslog(err.stack);
169+
res.sendStatus(404)
154170
});
155171

156-
app.use((err, req, res, next) => res.send(err.stack));
157-
158172
module.exports = {
159173
server,
160174
app

0 commit comments

Comments
 (0)