Add access log widget
This commit is contained in:
		
							parent
							
								
									bc9a8283c6
								
							
						
					
					
						commit
						0a994e5b98
					
				
					 12 changed files with 181 additions and 0 deletions
				
			
		|  | @ -390,6 +390,9 @@ desktop: | |||
|       post: "Post" | ||||
|       placeholder: "What's happening?" | ||||
| 
 | ||||
|     mk-access-log-home-widget: | ||||
|       title: "Access log" | ||||
| 
 | ||||
|     mk-repost-form: | ||||
|       quote: "Quote..." | ||||
|       cancel: "Cancel" | ||||
|  |  | |||
|  | @ -390,6 +390,9 @@ desktop: | |||
|       post: "投稿" | ||||
|       placeholder: "いまどうしてる?" | ||||
| 
 | ||||
|     mk-access-log-home-widget: | ||||
|       title: "アクセスログ" | ||||
| 
 | ||||
|     mk-repost-form: | ||||
|       quote: "引用する..." | ||||
|       cancel: "キャンセル" | ||||
|  |  | |||
							
								
								
									
										19
									
								
								src/api/stream/requests.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/api/stream/requests.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| import * as websocket from 'websocket'; | ||||
| import Xev from 'xev'; | ||||
| 
 | ||||
| const ev = new Xev(); | ||||
| 
 | ||||
| export default function homeStream(request: websocket.request, connection: websocket.connection): void { | ||||
| 	const onRequest = request => { | ||||
| 		connection.send(JSON.stringify({ | ||||
| 			type: 'request', | ||||
| 			body: request | ||||
| 		})); | ||||
| 	}; | ||||
| 
 | ||||
| 	ev.addListener('request', onRequest); | ||||
| 
 | ||||
| 	connection.on('close', () => { | ||||
| 		ev.removeListener('request', onRequest); | ||||
| 	}); | ||||
| } | ||||
|  | @ -9,6 +9,7 @@ import isNativeToken from './common/is-native-token'; | |||
| import homeStream from './stream/home'; | ||||
| import messagingStream from './stream/messaging'; | ||||
| import serverStream from './stream/server'; | ||||
| import requestsStream from './stream/requests'; | ||||
| import channelStream from './stream/channel'; | ||||
| 
 | ||||
| module.exports = (server: http.Server) => { | ||||
|  | @ -27,6 +28,11 @@ module.exports = (server: http.Server) => { | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		if (request.resourceURL.pathname === '/requests') { | ||||
| 			requestsStream(request, connection); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		// Connect to Redis
 | ||||
| 		const subscriber = redis.createClient( | ||||
| 			config.redis.port, config.redis.host); | ||||
|  |  | |||
							
								
								
									
										21
									
								
								src/log-request.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/log-request.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| import * as crypto from 'crypto'; | ||||
| import * as express from 'express'; | ||||
| import * as proxyAddr from 'proxy-addr'; | ||||
| import Xev from 'xev'; | ||||
| 
 | ||||
| const ev = new Xev(); | ||||
| 
 | ||||
| export default function(req: express.Request) { | ||||
| 	const ip = proxyAddr(req, () => true); | ||||
| 
 | ||||
| 	const md5 = crypto.createHash('md5'); | ||||
| 	md5.update(ip); | ||||
| 	const hashedIp = md5.digest('hex').substr(0, 3); | ||||
| 
 | ||||
| 	ev.emit('request', { | ||||
| 		ip: hashedIp, | ||||
| 		method: req.method, | ||||
| 		hostname: req.hostname, | ||||
| 		path: req.originalUrl | ||||
| 	}); | ||||
| } | ||||
|  | @ -11,6 +11,7 @@ import * as morgan from 'morgan'; | |||
| import Accesses from 'accesses'; | ||||
| import vhost = require('vhost'); | ||||
| 
 | ||||
| import log from './log-request'; | ||||
| import config from './conf'; | ||||
| 
 | ||||
| /** | ||||
|  | @ -35,6 +36,11 @@ app.use(morgan(process.env.NODE_ENV == 'production' ? 'combined' : 'dev', { | |||
| 	stream: config.accesslog ? fs.createWriteStream(config.accesslog) : null | ||||
| })); | ||||
| 
 | ||||
| app.use((req, res, next) => { | ||||
| 	log(req); | ||||
| 	next(); | ||||
| }); | ||||
| 
 | ||||
| // Drop request when without 'Host' header
 | ||||
| app.use((req, res, next) => { | ||||
| 	if (!req.headers['host']) { | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import * as riot from 'riot'; | |||
| import activateMe from './i'; | ||||
| import activateApi from './api'; | ||||
| import ServerStreamManager from '../scripts/server-stream-manager'; | ||||
| import RequestsStreamManager from '../scripts/requests-stream-manager'; | ||||
| 
 | ||||
| export default (me, stream) => { | ||||
| 	activateMe(me); | ||||
|  | @ -11,4 +12,5 @@ export default (me, stream) => { | |||
| 	(riot as any).mixin('stream', { stream }); | ||||
| 
 | ||||
| 	(riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); | ||||
| 	(riot as any).mixin('requests-stream', { requestsStream: new RequestsStreamManager() }); | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										12
									
								
								src/web/app/common/scripts/requests-stream-manager.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/web/app/common/scripts/requests-stream-manager.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import StreamManager from './stream-manager'; | ||||
| import Connection from './requests-stream'; | ||||
| 
 | ||||
| export default class RequestsStreamManager extends StreamManager<Connection> { | ||||
| 	public getConnection() { | ||||
| 		if (this.connection == null) { | ||||
| 			this.connection = new Connection(); | ||||
| 		} | ||||
| 
 | ||||
| 		return this.connection; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										14
									
								
								src/web/app/common/scripts/requests-stream.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/web/app/common/scripts/requests-stream.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| import Stream from './stream'; | ||||
| 
 | ||||
| /** | ||||
|  * Requests stream connection | ||||
|  */ | ||||
| class Connection extends Stream { | ||||
| 	constructor() { | ||||
| 		super('requests'); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export default Connection; | ||||
							
								
								
									
										93
									
								
								src/web/app/desktop/tags/home-widgets/access-log.tag
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/web/app/desktop/tags/home-widgets/access-log.tag
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | |||
| <mk-access-log-home-widget> | ||||
| 	<virtual if={ data.design == 0 }> | ||||
| 		<p class="title"><i class="fa fa-server"></i>%i18n:desktop.tags.mk-access-log-home-widget.title%</p> | ||||
| 	</virtual> | ||||
| 	<div ref="log"> | ||||
| 		<p each={ requests }> | ||||
| 			<span class="ip" style="color:{ fg }; background:{ bg }">{ ip }</span> | ||||
| 			<span>{ method }</span> | ||||
| 			<span>{ path }</span> | ||||
| 		</p> | ||||
| 	</div> | ||||
| 	<style> | ||||
| 		:scope | ||||
| 			display block | ||||
| 			overflow hidden | ||||
| 			background #fff | ||||
| 
 | ||||
| 			> .title | ||||
| 				z-index 1 | ||||
| 				margin 0 | ||||
| 				padding 0 16px | ||||
| 				line-height 42px | ||||
| 				font-size 0.9em | ||||
| 				font-weight bold | ||||
| 				color #888 | ||||
| 				box-shadow 0 1px rgba(0, 0, 0, 0.07) | ||||
| 
 | ||||
| 				> i | ||||
| 					margin-right 4px | ||||
| 
 | ||||
| 			> div | ||||
| 				max-height 250px | ||||
| 				overflow auto | ||||
| 
 | ||||
| 				> p | ||||
| 					margin 0 | ||||
| 					padding 8px | ||||
| 					font-size 0.8em | ||||
| 					color #555 | ||||
| 
 | ||||
| 					&:nth-child(odd) | ||||
| 						background rgba(0, 0, 0, 0.025) | ||||
| 
 | ||||
| 					> .ip | ||||
| 						margin-right 4px | ||||
| 
 | ||||
| 	</style> | ||||
| 	<script> | ||||
| 		import seedrandom from 'seedrandom'; | ||||
| 
 | ||||
| 		this.data = { | ||||
| 			design: 0 | ||||
| 		}; | ||||
| 
 | ||||
| 		this.mixin('widget'); | ||||
| 
 | ||||
| 		this.mixin('requests-stream'); | ||||
| 		this.connection = this.requestsStream.getConnection(); | ||||
| 		this.connectionId = this.requestsStream.use(); | ||||
| 
 | ||||
| 		this.requests = []; | ||||
| 
 | ||||
| 		this.on('mount', () => { | ||||
| 			this.connection.on('request', this.onRequest); | ||||
| 		}); | ||||
| 
 | ||||
| 		this.on('unmount', () => { | ||||
| 			this.connection.off('request', this.onRequest); | ||||
| 			this.requestsStream.dispose(this.connectionId); | ||||
| 		}); | ||||
| 
 | ||||
| 		this.onRequest = request => { | ||||
| 			const random = seedrandom(request.ip); | ||||
| 			const r = Math.floor(random() * 255); | ||||
| 			const g = Math.floor(random() * 255); | ||||
| 			const b = Math.floor(random() * 255); | ||||
| 			const luma = (0.2126 * r) + (0.7152 * g) + (0.0722 * b); // SMPTE C, Rec. 709 weightings | ||||
| 			request.bg = `rgb(${r}, ${g}, ${b})`; | ||||
| 			request.fg = luma >= 165 ? '#000' : '#fff'; | ||||
| 
 | ||||
| 			this.requests.push(request); | ||||
| 			if (this.requests.length > 30) this.requests.shift(); | ||||
| 			this.update(); | ||||
| 
 | ||||
| 			this.refs.log.scrollTop = this.refs.log.scrollHeight; | ||||
| 		}; | ||||
| 
 | ||||
| 		this.func = () => { | ||||
| 			if (++this.data.design == 2) this.data.design = 0; | ||||
| 			this.save(); | ||||
| 		}; | ||||
| 	</script> | ||||
| </mk-access-log-home-widget> | ||||
|  | @ -20,6 +20,7 @@ | |||
| 					<option value="recommended-polls">投票</option> | ||||
| 					<option value="post-form">投稿フォーム</option> | ||||
| 					<option value="channel">チャンネル</option> | ||||
| 					<option value="access-log">アクセスログ</option> | ||||
| 					<option value="server">サーバー情報</option> | ||||
| 					<option value="donation">寄付のお願い</option> | ||||
| 					<option value="nav">ナビゲーション</option> | ||||
|  |  | |||
|  | @ -43,6 +43,7 @@ require('./home-widgets/slideshow.tag'); | |||
| require('./home-widgets/channel.tag'); | ||||
| require('./home-widgets/timemachine.tag'); | ||||
| require('./home-widgets/post-form.tag'); | ||||
| require('./home-widgets/access-log.tag'); | ||||
| require('./timeline.tag'); | ||||
| require('./messaging/window.tag'); | ||||
| require('./messaging/room-window.tag'); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue