Skip to content

Commit 6ae2dd5

Browse files
committed
initial commit
0 parents  commit 6ae2dd5

Some content is hidden

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

76 files changed

+5878
-0
lines changed

.clang-format

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
Language: JavaScript
3+
BasedOnStyle: Google
4+
ColumnLimit: 100

.gitignore

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
2+
# Created by https://www.gitignore.io/api/node,bower
3+
4+
### Node ###
5+
# Logs
6+
logs
7+
*.log
8+
npm-debug.log*
9+
10+
# Runtime data
11+
pids
12+
*.pid
13+
*.seed
14+
*.pid.lock
15+
16+
# Directory for instrumented libs generated by jscoverage/JSCover
17+
lib-cov
18+
19+
# Coverage directory used by tools like istanbul
20+
coverage
21+
22+
# nyc test coverage
23+
.nyc_output
24+
25+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26+
.grunt
27+
28+
# Bower dependency directory (https://bower.io/)
29+
30+
# node-waf configuration
31+
.lock-wscript
32+
33+
# Compiled binary addons (http://nodejs.org/api/addons.html)
34+
build/Release
35+
36+
# Dependency directories
37+
node_modules
38+
jspm_packages
39+
40+
# Optional npm cache directory
41+
.npm
42+
43+
# Optional eslint cache
44+
.eslintcache
45+
46+
# Optional REPL history
47+
.node_repl_history
48+
49+
# Output of 'npm pack'
50+
*.tgz
51+
52+
# Yarn Integrity file
53+
.yarn-integrity
54+
55+
# dotenv environment variables file
56+
.env
57+
58+
59+
# End of https://www.gitignore.io/api/node,bower
60+
61+
# server configuration
62+
config/
63+
64+
# pm2 process manifest
65+
process.json

API.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
== API description (v2) ==
2+
3+
/images - image of what ever is in ?tag=
4+
5+
Token authenticated:
6+
/token - token info
7+
/users - add user (POST)
8+
/stores - add store (POST)
9+
10+
User authenticated:
11+
/users - (admin) all users
12+
/users/:id - (admin/me) one user
13+
/users/:id/password - (me) change password
14+
/users/:id/codes - (me) all my codes, add code
15+
/users/:id/codes/:code - (me) remove code
16+
/products - (admin) all products (GET)
17+
/products/:id - (admin) one product (GET)
18+
/purchases - my purchases across stores (GET)
19+
/purchases/count - number of purchases I've made (GET)
20+
/stores - all stores I have access to {id, name, admin}
21+
/stores/:id - one store {id, name, debt}
22+
/stores/:id/products - items in this store
23+
/stores/:id/purchases - purchase in this store (POST)
24+
/stores/:id/users - (admin) users [{user, debt}]
25+
/stores/:id/accesses - (admin) accesses [{user, level}]
26+
27+
Store authenticated:
28+
/codes/:code - (item/user) item or user in this store
29+
/users - users of this store {id, name, debt}
30+
/users/:id - one user of this store {id, name, debt}
31+
/users/:id/codes - add code (with a previous code as identification)
32+
/products - all items in this store
33+
/products/:id - (admin) one item in this store
34+
/purchases - purchase in this store
35+
/purchases/count - number of purchases in this store (GET)
36+
/stores/:id - (this) - this store info

app/account_management.js

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
const bcrypt = require('bcrypt');
2+
const config = require('config');
3+
const express = require('express');
4+
const jwt = require('jsonwebtoken');
5+
const path = require('path');
6+
7+
const auth = require('./auth');
8+
const send_mail = require('./mail');
9+
10+
const User = require('./models/user');
11+
12+
const api = express.Router();
13+
14+
const auth_token = auth.token('User');
15+
16+
// Deprecated. Old emails may contain valid tokens with a link to here
17+
api.get('/user', (req, res) => res.redirect(`/signup?token=${req.query.token || ''}`));
18+
19+
api.route('/signup')
20+
.get(auth_token, (req, res) => res.sendFile(path.join(__dirname, '../static/private', 'signup.html')))
21+
.post(auth_token, (req, res, next) => {
22+
if (!req.body.name || !req.body.email || !req.body.password) {
23+
return next(400);
24+
}
25+
let name = req.body.name;
26+
let email = req.body.email.toLowerCase();
27+
let password = req.body.password;
28+
User.count({email}).exec()
29+
.then(count => !count || Promise.reject(new Error('User already registered')))
30+
.then(() => bcrypt.hash(password, 8))
31+
.then(passwordhash => jwt.sign({name, email, passwordhash}, config.secret))
32+
.then(token => {
33+
const link = `${config.server}/activate?token=${token}`;
34+
return send_mail.userActivate({to: email}, {link})
35+
.then(() => req.token.consume(email, token));
36+
})
37+
.then(() => res.redirect('/login'))
38+
.catch(err => next(err));
39+
});
40+
41+
const update = express.Router();
42+
update.put('/email', auth.authenticate, (req, res, next) => {
43+
if (!req.body.email) {
44+
return next(400);
45+
}
46+
const email = req.body.email.toLowerCase();
47+
if (email === req.user.email) {
48+
return next(400);
49+
}
50+
const id = req.user.id;
51+
const token = jwt.sign({id, update: {$set: {email}}}, config.secret);
52+
const link = `${config.server}/confirm?token=${token}`;
53+
send_mail.emailConfirm({to: email}, {link})
54+
.then(() => res.status(204).end())
55+
.catch(err => next(err));
56+
})
57+
update.put('/password', auth.authenticate, (req, res, next) => {
58+
if (!req.body.old || req.body.password == req.body.old) {
59+
return next(400);
60+
}
61+
req.user.validPassword(req.body.old)
62+
.catch(() => Promise.reject(401))
63+
.then(user => user.setPassword(req.body.password))
64+
.then(user => user.save())
65+
.then(() => next())
66+
.catch(err => next(err));
67+
}, auth.login, (req, res) => res.end());
68+
update.use(auth.catch);
69+
api.use('/update', update);
70+
71+
api.get('/activate', (req, res, next) => {
72+
const user = jwt.verify(req.query.token, config.secret);
73+
new User(user).save()
74+
.then(() => res.redirect('/login'))
75+
.catch(err => next(err));
76+
});
77+
api.get('/confirm', (req, res, next) => {
78+
const payload = jwt.verify(req.query.token, config.secret);
79+
User.findByIdAndUpdate(payload.id, payload.update).exec()
80+
.then(() => res.redirect('/login'))
81+
.catch(err => next(err));
82+
});
83+
84+
module.exports = api;

app/api/private.js

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
const config = require('config');
2+
const express = require('express');
3+
const fs = require('fs');
4+
const multer = require('multer');
5+
const request = require('request');
6+
const JSONStream = require('JSONStream');
7+
const auth = require('../auth');
8+
9+
const Image = require('../models/image');
10+
const Product = require('../models/product');
11+
12+
const api = express.Router();
13+
const upload = multer({dest: '/tmp/'});
14+
15+
api.route('/products/:id/images/:image_id')
16+
.get((req, res, next) => {
17+
Product.findById(req.params.id).exec()
18+
.then(product => product || Promise.reject(404))
19+
.then(product => {
20+
if (product.image) {
21+
return res.redirect(`/api/v1/images/${product.image}`);
22+
}
23+
const words = product.name.split(' ');
24+
let name = '', i = 0;
25+
while (name.length < 6 && i < words.length) {
26+
name += words[i++];
27+
}
28+
const tag = encodeURIComponent(name.replace(/[^0-9a-zåäöA-ZÅÄÖ_]/g, ''));
29+
const instagram = `https://api.instagram.com/v1/tags/${tag}/media/recent?count=1&access_token=${config.instagram && config.instagram.token}`;
30+
request(instagram)
31+
.pipe(JSONStream.parse('data.*', data => data.images.low_resolution.url))
32+
.on('data', url => res.redirect(url))
33+
.on('end', () => res.headersSent || res.end());
34+
});
35+
})
36+
.post(upload.single('image'), (req, res, next) => {
37+
Product.findById(req.params.id).exec()
38+
.then(product => product || Promise.reject(404))
39+
.then(product => {
40+
return new Promise((resolve, reject) => {
41+
fs.readFile(req.file.path, (err, data) => {
42+
if (err) {
43+
return reject(err.message);
44+
}
45+
const contentType = req.file.mimetype;
46+
const log = {email: req.user.email};
47+
const image = new Image({data, contentType, updatedAt: [log]});
48+
product.image = image._id;
49+
resolve(Promise.all([product.save(), image.save()]));
50+
})
51+
})
52+
})
53+
.then(() => res.status(204).end())
54+
.catch(err => next(err));
55+
});
56+
57+
api.route('/images/:id')
58+
.get((req, res, next) => {
59+
Image.findById(req.params.id).exec()
60+
.then(image => image || Promise.reject(404))
61+
.then(image => {
62+
res.contentType(image.contentType);
63+
res.send(image.data);
64+
})
65+
});
66+
67+
api.use(auth.catch);
68+
69+
module.exports = api;

app/api/public.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const express = require('express');
2+
const auth = require('../auth');
3+
4+
const User = require('../models/user');
5+
const Store = require('../models/store');
6+
7+
const api = express.Router();
8+
9+
api.get('/users/:id/jwt', auth.dev, (req, res, next) => {
10+
User.findById(req.params.id)
11+
.exec()
12+
.then(user => user || Promise.reject(404))
13+
.then(user => auth.jwt(User.modelName, user.id, user.passwordhash))
14+
.then(jwt => res.send(jwt))
15+
.catch(err => next(err));
16+
}, auth.next);
17+
api.get('/stores/:id/jwt', auth.dev, (req, res, next) => {
18+
Store.findById(req.params.id)
19+
.exec()
20+
.then(store => store || Promise.reject(404))
21+
.then(store => auth.jwt(Store.modelName, store.id))
22+
.then(jwt => res.send(jwt))
23+
.catch(err => next(err));
24+
}, auth.next);
25+
26+
api.get('/tokens/:token', auth.token(), (req, res, next) => {
27+
const type = req.token.type;
28+
const capacity = req.token.capacity;
29+
const total = capacity + req.token.__v;
30+
res.json({type, capacity, total});
31+
});
32+
33+
api.use(auth.catch);
34+
35+
module.exports = api;

0 commit comments

Comments
 (0)