· 9 years ago · Nov 12, 2016, 10:22 AM
1'use strict';
2const crypto = require('crypto');
3const jade = require('jade');
4const Cookies = require('cookies');
5const moment = require('moment-timezone');
6const util = require('./handler-util');
7const Post = require('./post');
8
9const trackingIdKey = 'tracking_id';
10
11const oneTimeTokenMap = new Map(); // ã‚ーをユーザーåã€å€¤ã‚’トークンã¨ã™ã‚‹é€£æƒ³é…列
12
13function handle(req, res) {
14 const cookies = new Cookies(req, res);
15 const trackingId = addTrackingCookie(cookies, req.user);
16 switch (req.method) {
17 case 'GET':
18 res.writeHead(200, {
19 'Content-Type': 'text/html',
20 'charset': 'utf-8'
21 });
22 Post.findAll({ order: 'id DESC' }).then((posts) => {
23 posts.forEach((post) => {
24 post.content = post.content.replace(/\+/g, ' ');
25 post.formattedCreatedAt = moment(post.createdAt).tz('Asia/Tokyo').format('YYYY年MM月DD日 HH時mm分ss秒');
26 });
27 const oneTimeToken = crypto.randomBytes(8).toString('hex');
28 oneTimeTokenMap.set(req.user, oneTimeToken);
29 res.end(jade.renderFile('./views/posts.jade', {
30 posts: posts,
31 user: req.user,
32 oneTimeToken: oneTimeToken
33 }));
34 console.info(
35 `閲覧ã•れã¾ã—ãŸ: user: ${req.user}, ` +
36 `trackinId: ${trackingId},` +
37 `remoteAddress: ${req.connection.remoteAddress}, ` +
38 `userAgent: ${req.headers['user-agent']} `
39 );
40 });
41 break;
42 case 'POST':
43 req.on('data', (data) => {
44 const decoded = decodeURIComponent(data);
45 const dataArray = decoded.split('&');
46 const content = dataArray[0] ? dataArray[0].split('content=')[1] : '';
47 const requestedOneTimeToken = dataArray[1] ? dataArray[1].split('oneTimeToken=')[1] : '';
48 if (oneTimeTokenMap.get(req.user) === requestedOneTimeToken) {
49 console.info('投稿ã•れã¾ã—ãŸ: ' + content);
50 Post.create({
51 content: content,
52 trackingCookie: trackingId,
53 postedBy: req.user
54 }).then(() => {
55 oneTimeTokenMap.delete(req.user);
56 handleRedirectPosts(req, res);
57 });
58 } else {
59 util.handleBadRequest(req, res);
60 }
61 });
62 break;
63 default:
64 util.handleBadRequest(req, res);
65 break;
66 }
67}
68
69function handleDelete(req, res) {
70 switch (req.method) {
71 case 'POST':
72 req.on('data', (data) => {
73 const decoded = decodeURIComponent(data);
74 const dataArray = decoded.split('&');
75 const id = dataArray[0] ? dataArray[0].split('id=')[1] : '';
76 const requestedOneTimeToken = dataArray[1] ? dataArray[1].split('oneTimeToken=')[1] : '';
77 if (oneTimeTokenMap.get(req.user) === requestedOneTimeToken) {
78 Post.findById(id).then((post) => {
79 if (req.user === post.postedBy || req.user === 'admin') {
80 post.destroy();
81 console.info(
82 `削除ã•れã¾ã—ãŸ: user: ${req.user}, ` +
83 `remoteAddress: ${req.connection.remoteAddress}, ` +
84 `userAgent: ${req.headers['user-agent']} `
85 );
86 oneTimeTokenMap.delete(req.user);
87 }
88 handleRedirectPosts(req, res);
89 });
90 } else {
91 util.handleBadRequest(req, res);
92 }
93 });
94 break;
95 default:
96 util.handleBadRequest(req, res);
97 break;
98 }
99}
100
101/**
102 * Cookieã«å«ã¾ã‚Œã¦ã„るトラッã‚ングIDã«ç•°å¸¸ãŒãªã‘れã°ãã®å€¤ã‚’è¿”ã—ã€
103 * å˜åœ¨ã—ãªã„å ´åˆã‚„異常ãªã‚‚ã®ã§ã‚ã‚‹å ´åˆã«ã¯ã€å†åº¦ä½œæˆã—Cookieã«ä»˜ä¸Žã—ã¦ãã®å€¤ã‚’è¿”ã™
104 * @param {Cookeies} cookies
105 * @param {String} userName
106 * @return {String} トラッã‚ングID
107 */
108function addTrackingCookie(cookies, userName) {
109 const requestedTrackingId = cookies.get(trackingIdKey);
110 if (isValidTrackingId(requestedTrackingId, userName)) {
111 return requestedTrackingId;
112 } else {
113 const originalId = parseInt(crypto.randomBytes(8).toString('hex'), 16);
114 const tomorrow = new Date(new Date().getTime() + (1000 * 60 * 60 * 24));
115 const trackingId = originalId + '_' + createValidHash(originalId, userName);
116 cookies.set(trackingIdKey, trackingId, { expires: tomorrow });
117 return trackingId;
118 }
119}
120
121function isValidTrackingId(trackingId, userName) {
122 if (!trackingId) {
123 return false;
124 }
125 const spilited = trackingId.split('_');
126 const originalId = spilited[0];
127 const requestedHash = spilited[1];
128 return createValidHash(originalId, userName) === requestedHash;
129}
130
131const secretKey =
132 '5a69bb55532235125986a0df24aca759f69bae045c7a66d6e2bc4652e3efb43da4' +
133 'd1256ca5ac705b9cf0eb2c6abb4adb78cba82f20596985c5216647ec218e84905a' +
134 '9f668a6d3090653b3be84d46a7a4578194764d8306541c0411cb23fbdbd611b5e0' +
135 'cd8fca86980a91d68dc05a3ac5fb52f16b33a6f3260c5a5eb88ffaee07774fe2c0' +
136 '825c42fbba7c909e937a9f947d90ded280bb18f5b43659d6fa0521dbc72ecc9b4b' +
137 'a7d958360c810dbd94bbfcfd80d0966e90906df302a870cdbffe655145cc4155a2' +
138 '0d0d019b67899a912e0892630c0386829aa2c1f1237bf4f63d73711117410c2fc5' +
139 '0c1472e87ecd6844d0805cd97c0ea8bbfbda507293beebc5d9';
140
141function createValidHash(originalId, userName) {
142 const sha1sum = crypto.createHash('sha1');
143 sha1sum.update(originalId + userName + secretKey);
144 return sha1sum.digest('hex');
145}
146
147function handleRedirectPosts(req, res) {
148 res.writeHead(303, {
149 'Location': '/posts'
150 });
151 res.end();
152}
153
154module.exports = {
155 handle: handle,
156 handleDelete: handleDelete
157};