Skip to content

Commit 5f048b2

Browse files
committed
Add tags and seperate routes/pages for editing/coding
1 parent 477553b commit 5f048b2

14 files changed

Lines changed: 165 additions & 144 deletions

File tree

app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ app.use('*', (req, res, next) => {
138138

139139
// Routes
140140
app.use('/', require('./routes/index'));
141+
app.use('/code', require('./routes/code'));
141142
app.use('/charge', require('./routes/charge'));
142143
app.use('/new', require('./routes/new'));
143144
app.use('/view', require('./routes/view'));

models/Snippet.js

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,34 @@ const logger = require('../utils').logger;
44
const andify = require('../utils').andify;
55
const db = require('./db').connection;
66
const moment = require('moment');
7+
const async = require('async');
8+
const _ = require('lodash');
79
const Promise = require('bluebird');
810

911
module.exports = {
1012
add(data) {
1113
return new Promise((resolve, reject) => {
12-
let self = this;
14+
let tags = data.tags;
15+
delete data.tags;
1316
data.ref = this.genRandomRef();
1417
data.description = "";
1518

1619
db.query('INSERT INTO `snippet` SET ?', data, (err, result) => {
17-
err ? reject(err) : resolve({['s.id']: result.insertId});
20+
if (err) reject(err);
21+
else {
22+
this.addTags(tags, result.insertId).then(() => {
23+
resolve({['s.id']: result.insertId});
24+
});
25+
}
1826
});
1927
});
2028
},
2129
remove(cond) {
2230
return new Promise((resolve, reject) => {
23-
db.query('DELETE FROM `snippet` WHERE ?', cond, (err, data) => {
24-
if (err) reject(err);
25-
else resolve();
31+
db.query('DELETE FROM `snippettags` WHERE ?', {snippetID: cond.id}, () => {
32+
db.query('DELETE FROM `snippet` WHERE ?', cond, (err, data) => {
33+
err ? reject(err) : resolve();
34+
});
2635
});
2736
});
2837
},
@@ -68,6 +77,57 @@ module.exports = {
6877
}
6978
});
7079
},
80+
getTags(id) {
81+
let tags = []
82+
return new Promise((resolve, reject) => {
83+
db.query(`SELECT s.*, t.name FROM snippettags s INNER JOIN tags t ON (s.tagID=t.id) WHERE s.snippetID = ${db.escape(id)}`, (err, data) => {
84+
data.forEach(tag => tags.push(tag.name));
85+
resolve(tags);
86+
});
87+
});
88+
},
89+
updateTags(tags, id) {
90+
let self = this;
91+
return new Promise((resolve, reject) => {
92+
self.getTags(id).then(oldTags => {
93+
let remove = _.difference(oldTags, tags);
94+
let add = _.difference(tags, oldTags);
95+
96+
this.removeTags(remove, id)
97+
.then(this.addTags(add, id))
98+
.then(resolve);
99+
});
100+
});
101+
},
102+
addTags(tags, id) {
103+
return new Promise((resolve, reject) => {
104+
async.series([
105+
106+
// Add tags to tags table
107+
cb => {
108+
async.each(tags, (tag, finished) => {
109+
db.query(`INSERT IGNORE INTO tags (name) VALUES (${db.escape(tag)});`, () => finished());
110+
}, cb);
111+
},
112+
// And then link Snippet id to tags
113+
cb => {
114+
async.each(tags, (tag, finished) => {
115+
db.query(`INSERT INTO snippettags(snippetID, tagID) VALUES (${id}, \
116+
(SELECT id FROM tags WHERE name=${db.escape(tag)}))`, () => finished());
117+
}, cb);
118+
}
119+
], resolve);
120+
});
121+
},
122+
removeTags(tags, id) {
123+
return new Promise((resolve, reject) => {
124+
// Remove link between Snippets and tags
125+
async.each(tags, (tag, finished) => {
126+
db.query(`DELETE FROM snippettags WHERE snippetID = ${id} AND \
127+
tagID = (SELECT id FROM tags WHERE name=${db.escape(tag)})`, () => finished());
128+
}, resolve);
129+
});
130+
},
71131
getSnippets(owner) {
72132
return new Promise((resolve, reject) => {
73133
db.query('SELECT * FROM `snippet` WHERE ? ORDER BY `modified` DESC', {owner}, (err, data) => {

routes/code.js

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -56,37 +56,30 @@ router.get('/api/:ref?', (req, res, next) => {
5656
res.redirect(baseURL + '/view/api');
5757
} else {
5858
doc.code = fs.readFileSync('./data/apis/' + doc.id + '.api'); // Read api src into this...
59-
res.render('code/api', _.merge(defaultVars, {api: doc, socket: ':' + settings.general.socket, title: `Edit API ${doc.name} [${doc.ref}]`}));
59+
res.render('code/api', _.merge(defaultVars, {api: doc, socket: ':' + settings.general.socket, title: `Coding API ${doc.name} [${doc.ref}]`}));
6060
}
6161
}).catch(err => {
6262
res.redirect(baseURL + '/view/api');
6363
});
6464
});
6565

6666
router.post('/api/:ref', (req, res, next) => {
67-
if (missingProps(req.body, ['rename', 'code'])) {
67+
if (missingProps(req.body, ['code'])) {
6868
req.flash('warning', 'Missing expected form properties');
69-
res.send(baseURL + '/edit/api/' + req.params.ref);
69+
res.send(baseURL + '/code/api/' + req.params.ref);
7070
return;
7171
}
7272

7373
API.getCond({ref: req.params.ref}).then(doc => {
7474
if (doc.owner !== req.session.user.id) {
7575
res.send(baseURL + '/view/api');
7676
} else {
77-
let name = req.body.rename;
78-
if (name === undefined || name === "") name = doc.name;
79-
if (name.match(/^[a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32}$/) === null) {
80-
req.flash('warning', 'Only 32 chars max please! Accepted chars: a-Z0-9 _-.+[]{}()');
81-
res.send(baseURL + '/edit/api/' + req.params.ref);
82-
} else {
83-
API.update({name}, doc.ref).then(() => {
84-
fs.writeFile('./data/apis/' + doc.id + '.api', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
85-
req.flash('info', `API ${name} [${doc.ref}] was updated successfully!`);
86-
res.send(baseURL + '/view/api');
87-
});
77+
API.modified(doc.ref).then(() => {
78+
fs.writeFile('./data/apis/' + doc.id + '.api', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
79+
req.flash('info', `API ${doc.name} [${doc.ref}] was updated successfully!`);
80+
res.send(baseURL + '/view/api');
8881
});
89-
}
82+
});
9083
}
9184
});
9285
});
@@ -98,50 +91,30 @@ router.get('/snippet/:ref?', (req, res, next) => {
9891
res.redirect(baseURL + '/view/snippet');
9992
} else {
10093
doc.code = fs.readFileSync('./data/snippets/' + doc.id + '.snippet'); // Read snippet src into this...
101-
res.render('code/snippet', _.merge(defaultVars, {snippet: doc, socket: ':' + settings.general.socket, title: `Edit Snippet ${doc.name}`}));
94+
res.render('code/snippet', _.merge(defaultVars, {snippet: doc, socket: ':' + settings.general.socket, title: `Coding Snippet ${doc.name}`}));
10295
}
10396
}).catch(err => {
10497
res.redirect(baseURL + '/view/snippet');
10598
});
10699
});
107100

108101
router.post('/snippet/:ref', (req, res, next) => {
109-
if (missingProps(req.body, ['name', 'tags'])) {
102+
if (missingProps(req.body, ['code'])) {
110103
req.flash('warning', 'Missing expected form properties');
111-
res.send(baseURL + '/edit/snippet/' + req.params.ref);
104+
res.send(baseURL + '/code/snippet/' + req.params.ref);
112105
return;
113106
}
114107
Snippet.getCond({ref: req.params.ref}).then(doc => {
115108
if (doc.owner !== req.session.user.id) {
116109
res.send(baseURL + '/view/snippet');
117110
} else {
118-
let name = req.body.rename;
119-
if (name === undefined || name === "") name = doc.name;
120-
if (name.match(/^[a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32}$/) === null) {
121-
req.flash('warning', 'Only 32 chars max please! Accepted chars: a-Z0-9 _-.+[]{}()');
122-
res.send(baseURL + '/edit/snippet/' + req.params.ref);
123-
} else {
124-
Snippet.getCond({name: req.body.rename, owner: req.session.user.id}).then(dup => {
125-
126-
if ((req.body.rename !== doc.name && dup === null) || (req.body.rename === doc.name)) {
127-
Snippet.update({name}, doc.ref).then(() => {
128-
fs.writeFile('./data/snippets/' + doc.id + '.snippet', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
129-
req.app.get('removeSnippet')(`${req.session.user.username}/${doc.name}`);
130-
req.flash('info', `Snippet ${name} was updated successfully!`);
131-
res.send(baseURL + '/view/snippet');
132-
});
133-
});
134-
135-
// Duplicate name - still update the file contents though
136-
} else {
137-
fs.writeFile('./data/snippets/' + doc.id + '.snippet', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
138-
req.app.get('removeSnippet')(`${req.session.user.username}/${doc.name}`);
139-
req.flash('warning', `You already have another snippet named ${req.body.rename}`);
140-
res.send(baseURL + '/edit/snippet/' + req.params.ref);
141-
});
142-
}
111+
Snippet.modified(doc.ref).then(() => {
112+
fs.writeFile('./data/snippets/' + doc.id + '.snippet', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
113+
req.app.get('removeSnippet')(`${req.session.user.username}/${doc.name}`);
114+
req.flash('info', `Snippet ${doc.name} was updated successfully!`);
115+
res.send(baseURL + '/view/snippet');
143116
});
144-
}
117+
});
145118
}
146119
});
147120
});

routes/edit.js

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -56,35 +56,33 @@ router.get('/api/:ref?', (req, res, next) => {
5656
res.redirect(baseURL + '/view/api');
5757
} else {
5858
doc.code = fs.readFileSync('./data/apis/' + doc.id + '.api'); // Read api src into this...
59-
res.render('edit/api', _.merge(defaultVars, {api: doc, socket: ':' + settings.general.socket, title: `Edit API ${doc.name} [${doc.ref}]`}));
59+
res.render('edit/api', _.merge(defaultVars, {api: doc, title: `Edit API ${doc.name} [${doc.ref}]`}));
6060
}
61-
}).catch(err => {
61+
}, () => {
6262
res.redirect(baseURL + '/view/api');
6363
});
6464
});
6565

6666
router.post('/api/:ref', (req, res, next) => {
67-
if (missingProps(req.body, ['rename', 'code'])) {
67+
if (missingProps(req.body, ['rename'])) {
6868
req.flash('warning', 'Missing expected form properties');
69-
res.send(baseURL + '/edit/api/' + req.params.ref);
69+
res.redirect(baseURL + '/edit/api/' + req.params.ref);
7070
return;
7171
}
7272

7373
API.getCond({ref: req.params.ref}).then(doc => {
7474
if (doc.owner !== req.session.user.id) {
75-
res.send(baseURL + '/view/api');
75+
res.redirect(baseURL + '/view/api');
7676
} else {
7777
let name = req.body.rename;
7878
if (name === undefined || name === "") name = doc.name;
7979
if (name.match(/^[a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32}$/) === null) {
8080
req.flash('warning', 'Only 32 chars max please! Accepted chars: a-Z0-9 _-.+[]{}()');
81-
res.send(baseURL + '/edit/api/' + req.params.ref);
81+
res.redirect(baseURL + '/edit/api/' + req.params.ref);
8282
} else {
8383
API.update({name}, doc.ref).then(() => {
84-
fs.writeFile('./data/apis/' + doc.id + '.api', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
85-
req.flash('info', `API ${name} [${doc.ref}] was updated successfully!`);
86-
res.send(baseURL + '/view/api');
87-
});
84+
req.flash('info', `API ${name} [${doc.ref}] was updated successfully!`);
85+
res.redirect(baseURL + '/view/api');
8886
});
8987
}
9088
}
@@ -161,52 +159,57 @@ router.post('/list/:ref', (req, res, next) => {
161159
// Snippets
162160
router.get('/snippet/:ref?', (req, res, next) => {
163161
Snippet.getCond({ref: req.params.ref}).then(doc => {
164-
if (doc.owner !== req.session.user.id) {
165-
res.redirect(baseURL + '/view/snippet');
166-
} else {
167-
doc.code = fs.readFileSync('./data/snippets/' + doc.id + '.snippet'); // Read snippet src into this...
168-
res.render('edit/snippet', _.merge(defaultVars, {snippet: doc, socket: ':' + settings.general.socket, title: `Edit Snippet ${doc.name}`}));
169-
}
170-
}).catch(err => {
162+
Snippet.getTags(doc.id).then(tags => {
163+
if (doc.owner !== req.session.user.id) {
164+
res.redirect(baseURL + '/view/snippet');
165+
} else {
166+
doc.code = fs.readFileSync('./data/snippets/' + doc.id + '.snippet'); // Read snippet src into this...
167+
res.render('edit/snippet', _.merge(defaultVars, {snippet: doc, tags, socket: ':' + settings.general.socket, title: `Edit Snippet ${doc.name}`}));
168+
}
169+
});
170+
}, () => {
171171
res.redirect(baseURL + '/view/snippet');
172172
});
173173
});
174174

175175
router.post('/snippet/:ref', (req, res, next) => {
176-
if (missingProps(req.body, ['name', 'tags'])) {
176+
if (missingProps(req.body, ['rename', 'tags'])) {
177177
req.flash('warning', 'Missing expected form properties');
178-
res.send(baseURL + '/edit/snippet/' + req.params.ref);
178+
res.redirect(baseURL + '/edit/snippet/' + req.params.ref);
179179
return;
180180
}
181+
182+
let tags = _.uniq(req.body.tags.split(',').map(tag => tag.trim()).filter(tag => tag !== ""));
183+
let rejects = tags.filter(tag => tag.match(/^([a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32})$/g) === null);
184+
181185
Snippet.getCond({ref: req.params.ref}).then(doc => {
182186
if (doc.owner !== req.session.user.id) {
183-
res.send(baseURL + '/view/snippet');
187+
res.redirect(baseURL + '/view/snippet');
184188
} else {
185189
let name = req.body.rename;
186190
if (name === undefined || name === "") name = doc.name;
187191
if (name.match(/^[a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32}$/) === null) {
188192
req.flash('warning', 'Only 32 chars max please! Accepted chars: a-Z0-9 _-.+[]{}()');
189-
res.send(baseURL + '/edit/snippet/' + req.params.ref);
193+
res.redirect(baseURL + '/edit/snippet/' + req.params.ref);
194+
195+
} else if (rejects.length > 0 || req.body.tags.length > 255) {
196+
req.flash('warning', 'Snippet tags invalid! 32 chars per tag, accepted chars: a-Z0-9 _-.+[]{}()');
197+
res.redirect(baseURL + '/edit/snippet/' + req.params.ref);
198+
190199
} else {
191200
Snippet.getCond({name: req.body.rename, owner: req.session.user.id}).then(dup => {
201+
if (req.body.rename !== doc.name && dup !== null) {
202+
req.flash('warning', `You already have another snippet named ${req.body.rename}`);
203+
res.redirect(baseURL + '/edit/snippet/' + req.params.ref);
204+
return;
205+
}
192206

193-
if ((req.body.rename !== doc.name && dup === null) || (req.body.rename === doc.name)) {
194-
Snippet.update({name}, doc.ref).then(() => {
195-
fs.writeFile('./data/snippets/' + doc.id + '.snippet', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
196-
req.app.get('removeSnippet')(`${req.session.user.username}/${doc.name}`);
197-
req.flash('info', `Snippet ${name} was updated successfully!`);
198-
res.send(baseURL + '/view/snippet');
199-
});
200-
});
201-
202-
// Duplicate name - still update the file contents though
203-
} else {
204-
fs.writeFile('./data/snippets/' + doc.id + '.snippet', req.body.code.replace(/\r\n/g, '\n').slice(0, 8192), 'utf8', err => {
205-
req.app.get('removeSnippet')(`${req.session.user.username}/${doc.name}`);
206-
req.flash('warning', `You already have another snippet named ${req.body.rename}`);
207-
res.send(baseURL + '/edit/snippet/' + req.params.ref);
207+
Snippet.update({name}, doc.id).then(() => {
208+
Snippet.updateTags(tags, doc.id).then(() => {
209+
req.flash('info', `Snippet ${name} was updated successfully!`);
210+
res.redirect(baseURL + '/view/snippet');
208211
});
209-
}
212+
});
210213
});
211214
}
212215
}

routes/new.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ router.post('/api', (req, res, next) => {
8686
// Increment total APIs for user
8787
User.incVal('apis', 1, req.session.user.username).then(() => {
8888
req.flash('info', `API ${model.name} [${model.ref}] was added successfully!`);
89-
res.redirect(baseURL + '/edit/api/' + model.ref);
89+
res.redirect(baseURL + '/code/api/' + model.ref);
9090
});
9191
});
9292
});
@@ -194,7 +194,7 @@ router.post('/snippet', (req, res, next) => {
194194
// Increment total Snippets for user
195195
User.incVal('snippets', 1, req.session.user.username).then(() => {
196196
req.flash('info', `Snippet ${model.name} was added successfully!`);
197-
res.redirect(baseURL + '/edit/snippet/' + model.ref);
197+
res.redirect(baseURL + '/code/snippet/' + model.ref);
198198
});
199199
});
200200
});

0 commit comments

Comments
 (0)