From 7228e6d11178dfa715fc2ae4e834dec129248214 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 29 Mar 2018 20:34:39 +0900 Subject: :v: --- src/server/api/index.ts | 55 +++++++++++++++ src/server/api/server.ts | 55 --------------- src/server/file/index.ts | 168 ++++++++++++++++++++++++++++++++++++++++++++++ src/server/file/server.ts | 168 ---------------------------------------------- src/server/index.ts | 6 +- src/server/web/index.ts | 66 ++++++++++++++++++ src/server/web/server.ts | 66 ------------------ 7 files changed, 292 insertions(+), 292 deletions(-) create mode 100644 src/server/api/index.ts delete mode 100644 src/server/api/server.ts create mode 100644 src/server/file/index.ts delete mode 100644 src/server/file/server.ts create mode 100644 src/server/web/index.ts delete mode 100644 src/server/web/server.ts (limited to 'src') diff --git a/src/server/api/index.ts b/src/server/api/index.ts new file mode 100644 index 0000000000..e89d196096 --- /dev/null +++ b/src/server/api/index.ts @@ -0,0 +1,55 @@ +/** + * API Server + */ + +import * as express from 'express'; +import * as bodyParser from 'body-parser'; +import * as cors from 'cors'; +import * as multer from 'multer'; + +// import authenticate from './authenticate'; +import endpoints from './endpoints'; + +/** + * Init app + */ +const app = express(); + +app.disable('x-powered-by'); +app.set('etag', false); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json({ + type: ['application/json', 'text/plain'], + verify: (req, res, buf, encoding) => { + if (buf && buf.length) { + (req as any).rawBody = buf.toString(encoding || 'utf8'); + } + } +})); +app.use(cors()); + +app.get('/', (req, res) => { + res.send('YEE HAW'); +}); + +/** + * Register endpoint handlers + */ +endpoints.forEach(endpoint => + endpoint.withFile ? + app.post(`/${endpoint.name}`, + endpoint.withFile ? multer({ storage: multer.diskStorage({}) }).single('file') : null, + require('./api-handler').default.bind(null, endpoint)) : + app.post(`/${endpoint.name}`, + require('./api-handler').default.bind(null, endpoint)) +); + +app.post('/signup', require('./private/signup').default); +app.post('/signin', require('./private/signin').default); + +require('./service/github')(app); +require('./service/twitter')(app); + +require('./bot/interfaces/line')(app); + +module.exports = app; diff --git a/src/server/api/server.ts b/src/server/api/server.ts deleted file mode 100644 index e89d196096..0000000000 --- a/src/server/api/server.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * API Server - */ - -import * as express from 'express'; -import * as bodyParser from 'body-parser'; -import * as cors from 'cors'; -import * as multer from 'multer'; - -// import authenticate from './authenticate'; -import endpoints from './endpoints'; - -/** - * Init app - */ -const app = express(); - -app.disable('x-powered-by'); -app.set('etag', false); -app.use(bodyParser.urlencoded({ extended: true })); -app.use(bodyParser.json({ - type: ['application/json', 'text/plain'], - verify: (req, res, buf, encoding) => { - if (buf && buf.length) { - (req as any).rawBody = buf.toString(encoding || 'utf8'); - } - } -})); -app.use(cors()); - -app.get('/', (req, res) => { - res.send('YEE HAW'); -}); - -/** - * Register endpoint handlers - */ -endpoints.forEach(endpoint => - endpoint.withFile ? - app.post(`/${endpoint.name}`, - endpoint.withFile ? multer({ storage: multer.diskStorage({}) }).single('file') : null, - require('./api-handler').default.bind(null, endpoint)) : - app.post(`/${endpoint.name}`, - require('./api-handler').default.bind(null, endpoint)) -); - -app.post('/signup', require('./private/signup').default); -app.post('/signin', require('./private/signin').default); - -require('./service/github')(app); -require('./service/twitter')(app); - -require('./bot/interfaces/line')(app); - -module.exports = app; diff --git a/src/server/file/index.ts b/src/server/file/index.ts new file mode 100644 index 0000000000..062d260cb4 --- /dev/null +++ b/src/server/file/index.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 '../../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 { + // 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; diff --git a/src/server/file/server.ts b/src/server/file/server.ts deleted file mode 100644 index 062d260cb4..0000000000 --- a/src/server/file/server.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** - * 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 '../../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 { - // 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; diff --git a/src/server/index.ts b/src/server/index.ts index 3908b8a52c..fe22d9c9b3 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -51,9 +51,9 @@ app.use((req, res, next) => { /** * Register modules */ -app.use('/api', require('./api/server')); -app.use('/files', require('./file/server')); -app.use(require('./web/server')); +app.use('/api', require('./api')); +app.use('/files', require('./file')); +app.use(require('./web')); function createServer() { if (config.https) { diff --git a/src/server/web/index.ts b/src/server/web/index.ts new file mode 100644 index 0000000000..2fc8f1b8ab --- /dev/null +++ b/src/server/web/index.ts @@ -0,0 +1,66 @@ +/** + * Web Client Server + */ + +import * as path from 'path'; +import ms = require('ms'); + +// express modules +import * as express from 'express'; +import * as bodyParser from 'body-parser'; +import * as favicon from 'serve-favicon'; +import * as compression from 'compression'; + +const client = `${__dirname}/../../client/`; + +// Create server +const app = express(); +app.disable('x-powered-by'); + +app.use('/docs', require('./docs/server')); + +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json({ + type: ['application/json', 'text/plain'] +})); +app.use(compression()); + +app.use((req, res, next) => { + res.header('X-Frame-Options', 'DENY'); + next(); +}); + +//#region static assets + +app.use(favicon(`${client}/assets/favicon.ico`)); +app.get('/apple-touch-icon.png', (req, res) => res.sendFile(`${client}/assets/apple-touch-icon.png`)); +app.use('/assets', express.static(`${client}/assets`, { + maxAge: ms('7 days') +})); +app.use('/assets/*.js', (req, res) => res.sendFile(`${client}/assets/404.js`)); +app.use('/assets', (req, res) => { + res.sendStatus(404); +}); + +app.use('/recover', (req, res) => res.sendFile(`${client}/assets/recover.html`)); + +// ServiceWroker +app.get(/^\/sw\.(.+?)\.js$/, (req, res) => + res.sendFile(`${client}/assets/sw.${req.params[0]}.js`)); + +// Manifest +app.get('/manifest.json', (req, res) => + res.sendFile(`${client}/assets/manifest.json`)); + +//#endregion + +app.get(/\/api:url/, require('./url-preview')); + +// Render base html for all requests +app.get('*', (req, res) => { + res.sendFile(path.resolve(`${client}/app/base.html`), { + maxAge: ms('7 days') + }); +}); + +module.exports = app; diff --git a/src/server/web/server.ts b/src/server/web/server.ts deleted file mode 100644 index 2fc8f1b8ab..0000000000 --- a/src/server/web/server.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Web Client Server - */ - -import * as path from 'path'; -import ms = require('ms'); - -// express modules -import * as express from 'express'; -import * as bodyParser from 'body-parser'; -import * as favicon from 'serve-favicon'; -import * as compression from 'compression'; - -const client = `${__dirname}/../../client/`; - -// Create server -const app = express(); -app.disable('x-powered-by'); - -app.use('/docs', require('./docs/server')); - -app.use(bodyParser.urlencoded({ extended: true })); -app.use(bodyParser.json({ - type: ['application/json', 'text/plain'] -})); -app.use(compression()); - -app.use((req, res, next) => { - res.header('X-Frame-Options', 'DENY'); - next(); -}); - -//#region static assets - -app.use(favicon(`${client}/assets/favicon.ico`)); -app.get('/apple-touch-icon.png', (req, res) => res.sendFile(`${client}/assets/apple-touch-icon.png`)); -app.use('/assets', express.static(`${client}/assets`, { - maxAge: ms('7 days') -})); -app.use('/assets/*.js', (req, res) => res.sendFile(`${client}/assets/404.js`)); -app.use('/assets', (req, res) => { - res.sendStatus(404); -}); - -app.use('/recover', (req, res) => res.sendFile(`${client}/assets/recover.html`)); - -// ServiceWroker -app.get(/^\/sw\.(.+?)\.js$/, (req, res) => - res.sendFile(`${client}/assets/sw.${req.params[0]}.js`)); - -// Manifest -app.get('/manifest.json', (req, res) => - res.sendFile(`${client}/assets/manifest.json`)); - -//#endregion - -app.get(/\/api:url/, require('./url-preview')); - -// Render base html for all requests -app.get('*', (req, res) => { - res.sendFile(path.resolve(`${client}/app/base.html`), { - maxAge: ms('7 days') - }); -}); - -module.exports = app; -- cgit v1.2.3-freya