-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
128 lines (106 loc) · 3.2 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const fs = require('fs');
const cors = require('cors');
const axios = require('axios');
const crypto = require('crypto');
const express = require('express');
const { promisify } = require('util');
const { isURL } = require('validator');
const bodyParser = require('body-parser');
const child_process = require('child_process');
const Sentry = require('@sentry/node');
const unlinkPromised = promisify(fs.unlink);
const execPromised = promisify(child_process.exec);
const PORT = process.env.PORT || 5000;
const app = express();
Sentry.init({ dsn: 'https://80d20d7afa644308b66f29f7883f0c60@sentry.io/1537395' });
app.use(Sentry.Handlers.requestHandler());
app.use(cors());
app.use(bodyParser.json());
const IMAGES_FOLDER = './downloads';
const downloadImage = (url) => {
const filename = `${IMAGES_FOLDER}/${Math.random().toString()}.jpeg`
return axios({
url,
responseType: 'stream',
}).then(
response =>
new Promise((resolve, reject) => {
response.data
.pipe(fs.createWriteStream(filename))
.on('finish', () => resolve(filename))
.on('error', e => reject(e));
}),
)
};
const removeImage = (filename) => unlinkPromised(filename);
const calculateImageHash = async (filename) => {
const { stdout } = await execPromised(`python ./hash.py ${filename}`);
const [pHash, wHash] = stdout.split('\n');
return { pHash, wHash };
};
const calculateFileHash = async (filename) => {
return new Promise(resolve => {
const hash = crypto.createHash('sha256');
fs
.createReadStream(filename)
.on('data', data => hash.update(data))
.on('end', () => resolve(hash.digest('hex')));
});
};
const calculateDiff = async (origin, candidates) => {
const args = `${origin} ${candidates.join(' ')}`;
const { stdout } = await execPromised(`python ./diff.py ${args}`);
return stdout;
}
const isValidHash = hash => hash.length > 0;
app.post('/hash', async (req, res) => {
req.setTimeout(1000 * 60 * 5);
const url = req.body.url;
if (!isURL(url)) {
res.status(500).send('Invalid URL');
res.end();
}
try {
const filename = await downloadImage(url);
const [binaryHash, { pHash, wHash }] = await Promise.all([
calculateFileHash(filename),
calculateImageHash(filename)
]);
await removeImage(filename);
res.send({
binaryHash,
pHash,
wHash
});
} catch (error) {
if (typeof error.toJSON === 'function') {
console.log(error.toJSON());
} else {
console.log(error);
}
}
res.end();
});
app.post('/diff', async (req, res) => {
const origin = req.body.origin;
const candidates = req.body.candidates;
if (!isValidHash(origin)) {
res.status(500).send('Invalid origin hash');
res.end();
}
if (!Array.isArray(candidates) || candidates.length < 1) {
res.status(500).send('Invalid candidates');
res.end();
}
for (const cand of candidates) {
if (!isValidHash(cand)) {
res.status(500).send(`Invalid hash ${cand}`);
res.end();
}
}
const diffs = await calculateDiff(origin, candidates);
res.send(diffs);
res.end();
});
app.use(Sentry.Handlers.errorHandler());
app.listen(PORT, () => console.log(`Image hash is running on port ${PORT}`));