Skip to content

Commit c79d1d7

Browse files
committed
Fix list upload and snippet dup/error check
1 parent f03c2e5 commit c79d1d7

5 files changed

Lines changed: 209 additions & 34 deletions

File tree

routes/code.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
const express = require('express');
2+
const _ = require('lodash');
3+
const fs = require('fs');
4+
const router = express.Router();
5+
const settings = require('../settings.json');
6+
const logger = require('../utils').logger;
7+
const missingProps = require('../utils').missingProps;
8+
const multer = require('multer');
9+
const crypto = require('crypto');
10+
const storage = multer.diskStorage({
11+
destination(req, file, cb) {
12+
cb(null, './uploads/')
13+
},
14+
filename(req, file, cb) {
15+
crypto.pseudoRandomBytes(16, (err, raw) => {
16+
cb(null, raw.toString('hex') + Date.now());
17+
});
18+
}
19+
});
20+
const upload = multer({
21+
storage: storage,
22+
limits: {
23+
fileSize: 5*1024*1024
24+
}
25+
}).single('file');
26+
27+
const API = require('../models/API');
28+
const User = require('../models/User');
29+
const List = require('../models/List');
30+
const Snippet = require('../models/Snippet');
31+
32+
// Setup defaultVars and baseURL for all routes
33+
let defaultVars, baseURL;
34+
router.all('*', (req, res, next) => {
35+
defaultVars = req.app.get('defaultVars');
36+
baseURL = req.app.get('baseURL');
37+
38+
if (!req.session.loggedin) {
39+
res.redirect(baseURL + '/');
40+
41+
} else if (req.session.subscription.status === 3) {
42+
res.redirect(baseURL + '/settings/subscription/paymentOverdue');
43+
44+
} else if (req.session.subscription.status === 4) {
45+
req.flash('warning', 'Your account is currently soft-locked until you fix your account quotas.');
46+
res.redirect(baseURL + '/');
47+
48+
} else {
49+
next();
50+
}
51+
});
52+
53+
router.get('/api/:ref?', (req, res, next) => {
54+
API.getCond({ref: req.params.ref}).then(doc => {
55+
if (doc.owner !== req.session.user.id) {
56+
res.redirect(baseURL + '/view/api');
57+
} else {
58+
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}]`}));
60+
}
61+
}).catch(err => {
62+
res.redirect(baseURL + '/view/api');
63+
});
64+
});
65+
66+
router.post('/api/:ref', (req, res, next) => {
67+
if (missingProps(req.body, ['rename', 'code'])) {
68+
req.flash('warning', 'Missing expected form properties');
69+
res.send(baseURL + '/edit/api/' + req.params.ref);
70+
return;
71+
}
72+
73+
API.getCond({ref: req.params.ref}).then(doc => {
74+
if (doc.owner !== req.session.user.id) {
75+
res.send(baseURL + '/view/api');
76+
} 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+
});
88+
});
89+
}
90+
}
91+
});
92+
});
93+
94+
// Snippets
95+
router.get('/snippet/:ref?', (req, res, next) => {
96+
Snippet.getCond({ref: req.params.ref}).then(doc => {
97+
if (doc.owner !== req.session.user.id) {
98+
res.redirect(baseURL + '/view/snippet');
99+
} else {
100+
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}`}));
102+
}
103+
}).catch(err => {
104+
res.redirect(baseURL + '/view/snippet');
105+
});
106+
});
107+
108+
router.post('/snippet/:ref', (req, res, next) => {
109+
if (missingProps(req.body, ['name', 'tags'])) {
110+
req.flash('warning', 'Missing expected form properties');
111+
res.send(baseURL + '/edit/snippet/' + req.params.ref);
112+
return;
113+
}
114+
Snippet.getCond({ref: req.params.ref}).then(doc => {
115+
if (doc.owner !== req.session.user.id) {
116+
res.send(baseURL + '/view/snippet');
117+
} 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+
}
143+
});
144+
}
145+
}
146+
});
147+
});
148+
149+
module.exports = router;

routes/edit.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ router.get('/list/:ref', (req, res, next) => {
100100

101101
router.post('/list/:ref', (req, res, next) => {
102102
List.getCond({ref: req.params.ref}).then(doc => {
103-
104-
if (missingProps(req.body, ['name']) || req.file === undefined) {
105-
req.flash('warning', 'Missing expected form properties');
106-
res.redirect(baseURL + '/new/snippet');
107-
return;
108-
}
109-
110103
upload(req, res, err => {
111-
if (err) {
104+
if (missingProps(req.body, ['rename'])) {
105+
req.flash('warning', 'Missing expected form properties');
106+
res.redirect(baseURL + '/edit/list/' + req.params.ref);
107+
if (req.file !== undefined) {
108+
fs.unlink('./'+ req.file.path);
109+
}
110+
return;
111+
} else if (err) {
112112
req.flash('warning', 'This list is too big. Please keep your file size under 5MB.');
113113
res.redirect(baseURL + '/edit/list/' + req.params.ref);
114114

@@ -188,7 +188,7 @@ router.post('/snippet/:ref', (req, res, next) => {
188188
req.flash('warning', 'Only 32 chars max please! Accepted chars: a-Z0-9 _-.+[]{}()');
189189
res.send(baseURL + '/edit/snippet/' + req.params.ref);
190190
} else {
191-
Snippet.getCond({name: req.body.rename}).then(dup => {
191+
Snippet.getCond({name: req.body.rename, owner: req.session.user.id}).then(dup => {
192192

193193
if ((req.body.rename !== doc.name && dup === null) || (req.body.rename === doc.name)) {
194194
Snippet.update({name}, doc.ref).then(() => {

routes/new.js

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,15 @@ router.get('/list', (req, res, next) => {
9999
});
100100

101101
router.post('/list', (req, res, next) => {
102-
if (missingProps(req.body, ['name']) || req.file === undefined) {
103-
req.flash('warning', 'Missing expected form properties');
104-
res.redirect(baseURL + '/new/snippet');
105-
return;
106-
}
107-
108102
upload(req, res, err => {
109-
if (err) {
103+
if (missingProps(req.body, ['name']) || req.file === undefined) {
104+
req.flash('warning', 'Missing expected form properties');
105+
res.redirect(baseURL + '/new/list');
106+
if (req.file !== undefined) {
107+
fs.unlink('./'+ req.file.path);
108+
}
109+
return;
110+
} else if (err) {
110111
req.flash('warning', 'This list is too big. Please keep your file size under 5MB.');
111112
res.redirect(baseURL + '/new/list');
112113

@@ -153,7 +154,7 @@ router.post('/snippet', (req, res, next) => {
153154
return;
154155
}
155156

156-
let tags = req.body.tags.split(',').map(tag => tag.trim()).filter(tag => tag !== "");
157+
let tags = _.uniq(req.body.tags.split(',').map(tag => tag.trim()).filter(tag => tag !== ""));
157158
let rejects = tags.filter(tag => tag.match(/^([a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{1,32})$/g) === null);
158159

159160
if (req.body.name === '') {
@@ -167,7 +168,7 @@ router.post('/snippet', (req, res, next) => {
167168

168169
// Check tag requirements
169170
} else if (rejects.length > 0 || req.body.tags.length > 255) {
170-
req.flash('warning', 'Snippet tags invalid! Accepted chars: a-Z0-9 _-.+[]{}()');
171+
req.flash('warning', 'Snippet tags invalid! 32 chars per tag, accepted chars: a-Z0-9 _-.+[]{}()');
171172
res.redirect(baseURL + '/new/snippet');
172173

173174
// Is user within their snippet limits?
@@ -177,26 +178,26 @@ router.post('/snippet', (req, res, next) => {
177178

178179
} else {
179180
// Check for duplicate names
180-
Snippet.getCond({name: req.body.name}).then(dup => {
181+
Snippet.getCond({name: req.body.name, owner: req.session.user.id}).then(dup => {
181182

182183
// Add snippet
183-
if (dup === null) {
184-
Snippet.add({name: req.body.name, description: req.body.description, owner: req.session.user.id}).then(Snippet.getCond).then(model => {
185-
fs.writeFile('./data/snippets/' + model.id + '.snippet', `
184+
if (dup !== null) {
185+
req.flash('warning', `You already have another snippet named ${req.body.name}`);
186+
res.redirect(baseURL + '/new/snippet');
187+
return;
188+
}
189+
Snippet.add({name: req.body.name, description: req.body.description, owner: req.session.user.id, tags}).then(Snippet.getCond).then(model => {
190+
fs.writeFile('./data/snippets/' + model.id + '.snippet', `
186191
// Documentation: ${settings.general.basehref}documentation
187192
// Your awesome Snippet code here...`, 'utf8', err => {
188193

189-
// Increment total Snippets for user
190-
User.incVal('snippets', 1, req.session.user.username).then(() => {
191-
req.flash('info', `Snippet ${model.name} was added successfully!`);
192-
res.redirect(baseURL + '/edit/snippet/' + model.ref);
193-
});
194+
// Increment total Snippets for user
195+
User.incVal('snippets', 1, req.session.user.username).then(() => {
196+
req.flash('info', `Snippet ${model.name} was added successfully!`);
197+
res.redirect(baseURL + '/edit/snippet/' + model.ref);
194198
});
195199
});
196-
} else {
197-
req.flash('warning', `You already have another snippet named ${req.body.name}`);
198-
res.redirect(baseURL + '/new/snippet');
199-
}
200+
});
200201
});
201202
}
202203
});

src/js/nameLimits.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
11
$(() => {
2+
let limitsBad = false, tagsBad = false;
23
$('#limitsInput').on('input', function() {
3-
if ($(this).val().match(/^[a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{0,32}$/) === null) {
4+
5+
limitsBad = $(this).val().match(/^[a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{0,32}$/) === null
6+
badCheck();
7+
});
8+
9+
$('#tagsInput').on('input', function() {
10+
let tags = $(this).val().split(',').map(tag => tag.trim()).filter(tag => tag !== "");
11+
let rejects = tags.filter(tag => tag.match(/^([a-zA-Z0-9 _\-\.+\[\]\{\}\(\)]{0,32})$/g) === null);
12+
tagsBad = rejects.length > 0;
13+
badCheck();
14+
});
15+
badCheck();
16+
17+
function badCheck() {
18+
if (limitsBad) {
419
$('#limits').show();
5-
$("#submit").prop('disabled', true);
620
} else {
721
$('#limits').hide();
22+
}
23+
if (tagsBad) {
24+
$('#tagLimits').show();
25+
} else {
26+
$('#tagLimits').hide();
27+
}
28+
if (tagsBad || limitsBad) {
29+
$("#submit").prop('disabled', true);
30+
} else {
831
$("#submit").prop('disabled', false);
932
}
10-
});
33+
}
1134
});
1235

1336

views/pages/new/snippet.ejs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
<form action="" method="POST">
1616
<span class='red' id='limits'>Accepted chars: a-zA-Z0-9 _-.+[]{}()</span><br>
1717
<input type="text" id="limitsInput" name="name" maxlength='32' placeholder="Snippet Name" autofocus required autocomplete="off"><br>
18+
<span class='red' id='tagLimits'>32 chars per tag, accepted chars: a-zA-Z0-9 _-.+[]{}()</span><br>
19+
<input type="text" id="tagsInput" name="tags" maxlength='255' placeholder="Snippet Tags" autocomplete="off"><br>
1820
<input type="submit" id="submit" value="Add new Snippet">
1921
</form>
2022
</div>

0 commit comments

Comments
 (0)