Add access log widget

This commit is contained in:
syuilo 2017-11-13 19:58:29 +09:00
parent bc9a8283c6
commit 0a994e5b98
12 changed files with 181 additions and 0 deletions

View File

@ -390,6 +390,9 @@ desktop:
post: "Post" post: "Post"
placeholder: "What's happening?" placeholder: "What's happening?"
mk-access-log-home-widget:
title: "Access log"
mk-repost-form: mk-repost-form:
quote: "Quote..." quote: "Quote..."
cancel: "Cancel" cancel: "Cancel"

View File

@ -390,6 +390,9 @@ desktop:
post: "投稿" post: "投稿"
placeholder: "いまどうしてる?" placeholder: "いまどうしてる?"
mk-access-log-home-widget:
title: "アクセスログ"
mk-repost-form: mk-repost-form:
quote: "引用する..." quote: "引用する..."
cancel: "キャンセル" cancel: "キャンセル"

View 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);
});
}

View File

@ -9,6 +9,7 @@ import isNativeToken from './common/is-native-token';
import homeStream from './stream/home'; import homeStream from './stream/home';
import messagingStream from './stream/messaging'; import messagingStream from './stream/messaging';
import serverStream from './stream/server'; import serverStream from './stream/server';
import requestsStream from './stream/requests';
import channelStream from './stream/channel'; import channelStream from './stream/channel';
module.exports = (server: http.Server) => { module.exports = (server: http.Server) => {
@ -27,6 +28,11 @@ module.exports = (server: http.Server) => {
return; return;
} }
if (request.resourceURL.pathname === '/requests') {
requestsStream(request, connection);
return;
}
// Connect to Redis // Connect to Redis
const subscriber = redis.createClient( const subscriber = redis.createClient(
config.redis.port, config.redis.host); config.redis.port, config.redis.host);

21
src/log-request.ts Normal file
View 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
});
}

View File

@ -11,6 +11,7 @@ import * as morgan from 'morgan';
import Accesses from 'accesses'; import Accesses from 'accesses';
import vhost = require('vhost'); import vhost = require('vhost');
import log from './log-request';
import config from './conf'; 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 stream: config.accesslog ? fs.createWriteStream(config.accesslog) : null
})); }));
app.use((req, res, next) => {
log(req);
next();
});
// Drop request when without 'Host' header // Drop request when without 'Host' header
app.use((req, res, next) => { app.use((req, res, next) => {
if (!req.headers['host']) { if (!req.headers['host']) {

View File

@ -3,6 +3,7 @@ import * as riot from 'riot';
import activateMe from './i'; import activateMe from './i';
import activateApi from './api'; import activateApi from './api';
import ServerStreamManager from '../scripts/server-stream-manager'; import ServerStreamManager from '../scripts/server-stream-manager';
import RequestsStreamManager from '../scripts/requests-stream-manager';
export default (me, stream) => { export default (me, stream) => {
activateMe(me); activateMe(me);
@ -11,4 +12,5 @@ export default (me, stream) => {
(riot as any).mixin('stream', { stream }); (riot as any).mixin('stream', { stream });
(riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() }); (riot as any).mixin('server-stream', { serverStream: new ServerStreamManager() });
(riot as any).mixin('requests-stream', { requestsStream: new RequestsStreamManager() });
}; };

View 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;
}
}

View File

@ -0,0 +1,14 @@
'use strict';
import Stream from './stream';
/**
* Requests stream connection
*/
class Connection extends Stream {
constructor() {
super('requests');
}
}
export default Connection;

View 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>

View File

@ -20,6 +20,7 @@
<option value="recommended-polls">投票</option> <option value="recommended-polls">投票</option>
<option value="post-form">投稿フォーム</option> <option value="post-form">投稿フォーム</option>
<option value="channel">チャンネル</option> <option value="channel">チャンネル</option>
<option value="access-log">アクセスログ</option>
<option value="server">サーバー情報</option> <option value="server">サーバー情報</option>
<option value="donation">寄付のお願い</option> <option value="donation">寄付のお願い</option>
<option value="nav">ナビゲーション</option> <option value="nav">ナビゲーション</option>

View File

@ -43,6 +43,7 @@ require('./home-widgets/slideshow.tag');
require('./home-widgets/channel.tag'); require('./home-widgets/channel.tag');
require('./home-widgets/timemachine.tag'); require('./home-widgets/timemachine.tag');
require('./home-widgets/post-form.tag'); require('./home-widgets/post-form.tag');
require('./home-widgets/access-log.tag');
require('./timeline.tag'); require('./timeline.tag');
require('./messaging/window.tag'); require('./messaging/window.tag');
require('./messaging/room-window.tag'); require('./messaging/room-window.tag');