summaryrefslogtreecommitdiff
path: root/src/server/file
diff options
context:
space:
mode:
authorAkihiko Odaki <nekomanma@pixiv.co.jp>2018-03-29 01:20:40 +0900
committerAkihiko Odaki <nekomanma@pixiv.co.jp>2018-03-29 01:54:41 +0900
commit90f8fe7e538bb7e52d2558152a0390e693f39b11 (patch)
tree0f830887053c8f352b1cd0c13ca715fd14c1f030 /src/server/file
parentImplement remote account resolution (diff)
downloadsharkey-90f8fe7e538bb7e52d2558152a0390e693f39b11.tar.gz
sharkey-90f8fe7e538bb7e52d2558152a0390e693f39b11.tar.bz2
sharkey-90f8fe7e538bb7e52d2558152a0390e693f39b11.zip
Introduce processor
Diffstat (limited to 'src/server/file')
-rw-r--r--src/server/file/assets/avatar.jpgbin0 -> 1322 bytes
-rw-r--r--src/server/file/assets/bad-egg.pngbin0 -> 4783 bytes
-rw-r--r--src/server/file/assets/dummy.pngbin0 -> 6285 bytes
-rw-r--r--src/server/file/assets/not-an-image.pngbin0 -> 4711 bytes
-rw-r--r--src/server/file/assets/thumbnail-not-available.pngbin0 -> 8822 bytes
-rw-r--r--src/server/file/server.ts168
6 files changed, 168 insertions, 0 deletions
diff --git a/src/server/file/assets/avatar.jpg b/src/server/file/assets/avatar.jpg
new file mode 100644
index 0000000000..3c803f568e
--- /dev/null
+++ b/src/server/file/assets/avatar.jpg
Binary files differ
diff --git a/src/server/file/assets/bad-egg.png b/src/server/file/assets/bad-egg.png
new file mode 100644
index 0000000000..a7c5930bd4
--- /dev/null
+++ b/src/server/file/assets/bad-egg.png
Binary files differ
diff --git a/src/server/file/assets/dummy.png b/src/server/file/assets/dummy.png
new file mode 100644
index 0000000000..39332b0c1b
--- /dev/null
+++ b/src/server/file/assets/dummy.png
Binary files differ
diff --git a/src/server/file/assets/not-an-image.png b/src/server/file/assets/not-an-image.png
new file mode 100644
index 0000000000..bf98b293f7
--- /dev/null
+++ b/src/server/file/assets/not-an-image.png
Binary files differ
diff --git a/src/server/file/assets/thumbnail-not-available.png b/src/server/file/assets/thumbnail-not-available.png
new file mode 100644
index 0000000000..f960ce4d00
--- /dev/null
+++ b/src/server/file/assets/thumbnail-not-available.png
Binary files differ
diff --git a/src/server/file/server.ts b/src/server/file/server.ts
new file mode 100644
index 0000000000..3bda5b14fe
--- /dev/null
+++ b/src/server/file/server.ts
@@ -0,0 +1,168 @@
+/**
+ * File Server
+ */
+
+import * as fs from 'fs';
+import * as express from 'express';
+import * as bodyParser from 'body-parser';
+import * as cors from 'cors';
+import * as mongodb from 'mongodb';
+import * as _gm from 'gm';
+import * as stream from 'stream';
+
+import DriveFile, { getGridFSBucket } from '../api/models/drive-file';
+
+const gm = _gm.subClass({
+ imageMagick: true
+});
+
+/**
+ * Init app
+ */
+const app = express();
+
+app.disable('x-powered-by');
+app.locals.cache = true;
+app.use(bodyParser.urlencoded({ extended: true }));
+app.use(cors());
+
+/**
+ * Statics
+ */
+app.use('/assets', express.static(`${__dirname}/assets`, {
+ maxAge: 1000 * 60 * 60 * 24 * 365 // 一年
+}));
+
+app.get('/', (req, res) => {
+ res.send('yee haw');
+});
+
+app.get('/default-avatar.jpg', (req, res) => {
+ const file = fs.createReadStream(`${__dirname}/assets/avatar.jpg`);
+ send(file, 'image/jpeg', req, res);
+});
+
+app.get('/app-default.jpg', (req, res) => {
+ const file = fs.createReadStream(`${__dirname}/assets/dummy.png`);
+ send(file, 'image/png', req, res);
+});
+
+interface ISend {
+ contentType: string;
+ stream: stream.Readable;
+}
+
+function thumbnail(data: stream.Readable, type: string, resize: number): ISend {
+ const readable: stream.Readable = (() => {
+ // 画像ではない場合
+ if (!/^image\/.*$/.test(type)) {
+ // 使わないことにしたストリームはしっかり取り壊しておく
+ data.destroy();
+ return fs.createReadStream(`${__dirname}/assets/not-an-image.png`);
+ }
+
+ const imageType = type.split('/')[1];
+
+ // 画像でもPNGかJPEGでないならダメ
+ if (imageType != 'png' && imageType != 'jpeg') {
+ // 使わないことにしたストリームはしっかり取り壊しておく
+ data.destroy();
+ return fs.createReadStream(`${__dirname}/assets/thumbnail-not-available.png`);
+ }
+
+ return data;
+ })();
+
+ let g = gm(readable);
+
+ if (resize) {
+ g = g.resize(resize, resize);
+ }
+
+ const stream = g
+ .compress('jpeg')
+ .quality(80)
+ .interlace('line')
+ .noProfile() // Remove EXIF
+ .stream();
+
+ return {
+ contentType: 'image/jpeg',
+ stream
+ };
+}
+
+const commonReadableHandlerGenerator = (req: express.Request, res: express.Response) => (e: Error): void => {
+ console.dir(e);
+ req.destroy();
+ res.destroy(e);
+};
+
+function send(readable: stream.Readable, type: string, req: express.Request, res: express.Response): void {
+ readable.on('error', commonReadableHandlerGenerator(req, res));
+
+ const data = ((): ISend => {
+ if (req.query.thumbnail !== undefined) {
+ return thumbnail(readable, type, req.query.size);
+ }
+ return {
+ contentType: type,
+ stream: readable
+ };
+ })();
+
+ if (readable !== data.stream) {
+ data.stream.on('error', commonReadableHandlerGenerator(req, res));
+ }
+
+ if (req.query.download !== undefined) {
+ res.header('Content-Disposition', 'attachment');
+ }
+
+ res.header('Content-Type', data.contentType);
+
+ data.stream.pipe(res);
+
+ data.stream.on('end', () => {
+ res.end();
+ });
+}
+
+async function sendFileById(req: express.Request, res: express.Response): Promise<void> {
+ // Validate id
+ if (!mongodb.ObjectID.isValid(req.params.id)) {
+ res.status(400).send('incorrect id');
+ return;
+ }
+
+ const fileId = new mongodb.ObjectID(req.params.id);
+
+ // Fetch (drive) file
+ const file = await DriveFile.findOne({ _id: fileId });
+
+ // validate name
+ if (req.params.name !== undefined && req.params.name !== file.filename) {
+ res.status(404).send('there is no file has given name');
+ return;
+ }
+
+ if (file == null) {
+ res.status(404).sendFile(`${__dirname}/assets/dummy.png`);
+ return;
+ }
+
+ const bucket = await getGridFSBucket();
+
+ const readable = bucket.openDownloadStream(fileId);
+
+ send(readable, file.contentType, req, res);
+}
+
+/**
+ * Routing
+ */
+
+app.get('/:id', sendFileById);
+app.get('/:id/:name', sendFileById);
+
+module.exports = app;