diff --git a/locales/en.yml b/locales/en.yml index 6104cbdc9d..e15a7c9ae4 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -243,6 +243,10 @@ desktop: title: "Notifications" settings: "Notification settings" + mk-server-home-widget: + title: "Server info" + toggle: "Toggle views" + mk-activity-home-widget: title: "Activity" toggle: "Toggle views" diff --git a/locales/ja.yml b/locales/ja.yml index e83c0eb724..5ed1d4f1ee 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -243,6 +243,10 @@ desktop: title: "通知" settings: "通知の設定" + mk-server-home-widget: + title: "サーバー情報" + toggle: "表示を切り替え" + mk-activity-home-widget: title: "アクティビティ" toggle: "表示を切り替え" diff --git a/package.json b/package.json index fd10ea50cb..93f87561fa 100644 --- a/package.json +++ b/package.json @@ -1,150 +1,153 @@ { - "name": "misskey", - "author": "syuilo ", - "version": "0.0.2038", - "license": "MIT", - "description": "A miniblog-based SNS", - "bugs": "https://github.com/syuilo/misskey/issues", - "repository": "https://github.com/syuilo/misskey.git", - "main": "./built/index.js", - "private": true, - "scripts": { - "config": "node ./tools/init.js", - "start": "node ./built", - "debug": "DEBUG=misskey:* node ./built", - "swagger": "node ./swagger.js", - "build": "gulp build", - "rebuild": "gulp rebuild", - "clean": "gulp clean", - "cleanall": "gulp cleanall", - "lint": "gulp lint", - "test": "gulp test" - }, - "devDependencies": { - "@types/bcryptjs": "2.4.0", - "@types/body-parser": "1.16.3", - "@types/chai": "4.0.0", - "@types/chai-http": "0.0.30", - "@types/chalk": "0.4.31", - "@types/compression": "0.0.33", - "@types/cors": "2.8.1", - "@types/debug": "0.0.29", - "@types/deep-equal": "1.0.0", - "@types/elasticsearch": "5.0.13", - "@types/event-stream": "3.3.31", - "@types/express": "4.0.35", - "@types/gm": "1.17.31", - "@types/gulp": "4.0.3", - "@types/gulp-mocha": "0.0.30", - "@types/gulp-rename": "0.0.32", - "@types/gulp-replace": "0.0.30", - "@types/gulp-tslint": "3.6.31", - "@types/gulp-typescript": "2.13.0", - "@types/gulp-uglify": "0.0.30", - "@types/gulp-util": "3.0.31", - "@types/inquirer": "0.0.34", - "@types/is-root": "1.0.0", - "@types/is-url": "1.2.28", - "@types/js-yaml": "3.5.30", - "@types/mocha": "2.2.41", - "@types/mongodb": "2.2.3", - "@types/monk": "1.0.5", - "@types/morgan": "1.7.32", - "@types/ms": "0.7.29", - "@types/multer": "0.0.34", - "@types/node": "7.0.29", - "@types/ratelimiter": "2.1.28", - "@types/redis": "2.6.0", - "@types/request": "0.0.43", - "@types/rimraf": "0.0.28", - "@types/riot": "2.6.2", - "@types/serve-favicon": "2.2.28", - "@types/uuid": "3.0.0", - "@types/webpack": "2.2.15", - "@types/webpack-stream": "3.2.7", - "@types/websocket": "0.0.33", - "chai": "4.0.2", - "chai-http": "3.0.0", - "css-loader": "0.28.4", - "event-stream": "3.3.4", - "gulp": "3.9.1", - "gulp-cssnano": "2.1.2", - "gulp-imagemin": "3.3.0", - "gulp-mocha": "4.3.1", - "gulp-pug": "3.3.0", - "gulp-rename": "1.2.2", - "gulp-replace": "0.5.4", - "gulp-tslint": "8.1.1", - "gulp-typescript": "3.1.7", - "gulp-uglify": "3.0.0", - "gulp-util": "3.0.8", - "mocha": "3.4.2", - "riot-tag-loader": "1.0.0", - "string-replace-webpack-plugin": "0.1.3", - "style-loader": "0.18.2", - "stylus": "0.54.5", - "stylus-loader": "3.0.1", - "swagger-jsdoc": "1.9.4", - "tslint": "5.4.3", - "uglify-es": "3.0.15", - "uglify-es-webpack-plugin": "0.0.2", - "uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony", - "webpack": "2.6.1" - }, - "dependencies": { - "accesses": "2.5.0", - "animejs": "2.0.2", - "autwh": "0.0.1", - "bcryptjs": "2.4.3", - "body-parser": "1.17.2", - "cafy": "2.4.0", - "chalk": "1.1.3", - "compression": "1.6.2", - "cors": "2.8.3", - "cropperjs": "1.0.0-rc.2", - "crypto": "0.0.3", - "debug": "2.6.8", - "deep-equal": "1.0.1", - "deepcopy": "0.6.3", - "download": "6.2.2", - "elasticsearch": "13.0.1", - "escape-regexp": "0.0.1", - "express": "4.15.3", - "file-type": "5.0.0", - "fuckadblock": "3.2.1", - "gm": "1.23.0", - "inquirer": "3.1.0", - "is-root": "1.0.0", - "is-url": "1.2.2", - "js-yaml": "3.8.4", - "mongodb": "2.2.28", - "monk": "6.0.0", - "morgan": "1.8.2", - "ms": "2.0.0", - "multer": "1.3.0", - "nprogress": "0.2.0", - "page": "1.7.1", - "pictograph": "2.0.4", - "prominence": "0.2.0", - "pug": "2.0.0-rc.2", - "ratelimiter": "3.0.3", - "recaptcha-promise": "0.1.2", - "reconnecting-websocket": "3.0.5", - "redis": "2.7.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "riot": "3.6.0", - "rndstr": "1.0.0", - "s-age": "1.1.0", - "serve-favicon": "2.4.3", - "summaly": "2.0.3", - "syuilo-password-strength": "0.0.1", - "tcp-port-used": "0.1.2", - "textarea-caret": "3.0.2", - "ts-node": "3.0.6", - "typescript": "2.3.4", - "uuid": "3.0.1", - "vhost": "3.0.2", - "websocket": "1.0.24" - } + "name": "misskey", + "author": "syuilo ", + "version": "0.0.2038", + "license": "MIT", + "description": "A miniblog-based SNS", + "bugs": "https://github.com/syuilo/misskey/issues", + "repository": "https://github.com/syuilo/misskey.git", + "main": "./built/index.js", + "private": true, + "scripts": { + "config": "node ./tools/init.js", + "start": "node ./built", + "debug": "DEBUG=misskey:* node ./built", + "swagger": "node ./swagger.js", + "build": "gulp build", + "rebuild": "gulp rebuild", + "clean": "gulp clean", + "cleanall": "gulp cleanall", + "lint": "gulp lint", + "test": "gulp test" + }, + "devDependencies": { + "@types/bcryptjs": "2.4.0", + "@types/body-parser": "1.16.3", + "@types/chai": "4.0.0", + "@types/chai-http": "0.0.30", + "@types/chalk": "0.4.31", + "@types/compression": "0.0.33", + "@types/cors": "2.8.1", + "@types/debug": "0.0.29", + "@types/deep-equal": "1.0.0", + "@types/elasticsearch": "5.0.13", + "@types/event-stream": "3.3.31", + "@types/express": "4.0.35", + "@types/gm": "1.17.31", + "@types/gulp": "4.0.3", + "@types/gulp-mocha": "0.0.30", + "@types/gulp-rename": "0.0.32", + "@types/gulp-replace": "0.0.30", + "@types/gulp-tslint": "3.6.31", + "@types/gulp-typescript": "2.13.0", + "@types/gulp-uglify": "0.0.30", + "@types/gulp-util": "3.0.31", + "@types/inquirer": "0.0.34", + "@types/is-root": "1.0.0", + "@types/is-url": "1.2.28", + "@types/js-yaml": "3.5.30", + "@types/mocha": "2.2.41", + "@types/mongodb": "2.2.3", + "@types/monk": "1.0.5", + "@types/morgan": "1.7.32", + "@types/ms": "0.7.29", + "@types/multer": "0.0.34", + "@types/node": "7.0.29", + "@types/ratelimiter": "2.1.28", + "@types/redis": "2.6.0", + "@types/request": "0.0.43", + "@types/rimraf": "0.0.28", + "@types/riot": "2.6.2", + "@types/serve-favicon": "2.2.28", + "@types/uuid": "3.0.0", + "@types/webpack": "2.2.15", + "@types/webpack-stream": "3.2.7", + "@types/websocket": "0.0.33", + "chai": "4.0.2", + "chai-http": "3.0.0", + "css-loader": "0.28.4", + "event-stream": "3.3.4", + "gulp": "3.9.1", + "gulp-cssnano": "2.1.2", + "gulp-imagemin": "3.3.0", + "gulp-mocha": "4.3.1", + "gulp-pug": "3.3.0", + "gulp-rename": "1.2.2", + "gulp-replace": "0.5.4", + "gulp-tslint": "8.1.1", + "gulp-typescript": "3.1.7", + "gulp-uglify": "3.0.0", + "gulp-util": "3.0.8", + "mocha": "3.4.2", + "riot-tag-loader": "1.0.0", + "string-replace-webpack-plugin": "0.1.3", + "style-loader": "0.18.2", + "stylus": "0.54.5", + "stylus-loader": "3.0.1", + "swagger-jsdoc": "1.9.4", + "tslint": "5.4.3", + "uglify-es": "3.0.15", + "uglify-es-webpack-plugin": "0.0.2", + "uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony", + "webpack": "2.6.1" + }, + "dependencies": { + "accesses": "2.5.0", + "animejs": "2.0.2", + "autwh": "0.0.1", + "bcryptjs": "2.4.3", + "body-parser": "1.17.2", + "cafy": "2.4.0", + "chalk": "1.1.3", + "compression": "1.6.2", + "cors": "2.8.3", + "cropperjs": "1.0.0-rc.2", + "crypto": "0.0.3", + "debug": "2.6.8", + "deep-equal": "1.0.1", + "deepcopy": "0.6.3", + "diskusage": "^0.2.2", + "download": "6.2.2", + "elasticsearch": "13.0.1", + "escape-regexp": "0.0.1", + "express": "4.15.3", + "file-type": "5.0.0", + "fuckadblock": "3.2.1", + "gm": "1.23.0", + "inquirer": "3.1.0", + "is-root": "1.0.0", + "is-url": "1.2.2", + "js-yaml": "3.8.4", + "mongodb": "2.2.28", + "monk": "6.0.0", + "morgan": "1.8.2", + "ms": "2.0.0", + "multer": "1.3.0", + "nprogress": "0.2.0", + "os-utils": "0.0.14", + "page": "1.7.1", + "pictograph": "2.0.4", + "prominence": "0.2.0", + "pug": "2.0.0-rc.2", + "ratelimiter": "3.0.3", + "recaptcha-promise": "0.1.2", + "reconnecting-websocket": "3.0.5", + "redis": "2.7.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "riot": "3.6.0", + "rndstr": "1.0.0", + "s-age": "1.1.0", + "serve-favicon": "2.4.3", + "summaly": "2.0.3", + "syuilo-password-strength": "0.0.1", + "tcp-port-used": "0.1.2", + "textarea-caret": "3.0.2", + "ts-node": "3.0.6", + "typescript": "2.3.4", + "uuid": "3.0.1", + "vhost": "3.0.2", + "websocket": "1.0.24", + "xev": "^2.0.0" + } } diff --git a/src/api/stream/server.ts b/src/api/stream/server.ts new file mode 100644 index 0000000000..6de5337499 --- /dev/null +++ b/src/api/stream/server.ts @@ -0,0 +1,20 @@ +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 onStats = stats => { + connection.send(JSON.stringify({ + type: 'stats', + body: stats + })); + }; + + ev.addListener('stats', onStats); + + connection.on('close', () => { + console.log('yooo'); + ev.removeListener('stats', onStats); + }); +} diff --git a/src/api/streaming.ts b/src/api/streaming.ts index e1d79481d3..c71132100c 100644 --- a/src/api/streaming.ts +++ b/src/api/streaming.ts @@ -8,6 +8,7 @@ import isNativeToken from './common/is-native-token'; import homeStream from './stream/home'; import messagingStream from './stream/messaging'; +import serverStream from './stream/server'; module.exports = (server: http.Server) => { /** @@ -20,6 +21,11 @@ module.exports = (server: http.Server) => { ws.on('request', async (request) => { const connection = request.accept(); + if (request.resourceURL.pathname === '/server') { + serverStream(request, connection); + return; + } + const user = await authenticate(connection, request.resourceURL.query.i); if (user == null) { diff --git a/src/index.ts b/src/index.ts index 0b568b5d44..da83dde108 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,17 +12,20 @@ import * as chalk from 'chalk'; // import portUsed = require('tcp-port-used'); import isRoot = require('is-root'); import { master } from 'accesses'; +import Xev from 'xev'; import Logger from './utils/logger'; import ProgressBar from './utils/cli/progressbar'; import EnvironmentInfo from './utils/environmentInfo'; import MachineInfo from './utils/machineInfo'; import DependencyInfo from './utils/dependencyInfo'; +import stats from './utils/stats'; import { Config, path as configPath } from './config'; import loadConfig from './config'; const clusterLog = debug('misskey:cluster'); +const ev = new Xev(); process.title = 'Misskey'; @@ -35,6 +38,9 @@ main(); function main() { if (cluster.isMaster) { masterMain(); + + ev.mount(); + stats(); } else { workerMain(); } diff --git a/src/utils/stats.ts b/src/utils/stats.ts new file mode 100644 index 0000000000..1615268310 --- /dev/null +++ b/src/utils/stats.ts @@ -0,0 +1,25 @@ +import * as os from 'os'; +const osUtils = require('os-utils'); +import * as diskusage from 'diskusage'; +import Xev from 'xev'; + +const ev = new Xev(); + +/** + * Report stats regularly + */ +export default function() { + setInterval(() => { + osUtils.cpuUsage(cpuUsage => { + const disk = diskusage.checkSync(os.platform() == 'win32' ? 'c:' : '/'); + ev.emit('stats', { + cpu_usage: cpuUsage, + mem: { + total: os.totalmem(), + free: os.freemem() + }, + disk + }); + }); + }, 1000); +} diff --git a/src/web/app/common/scripts/home-stream.js b/src/web/app/common/scripts/home-stream.js new file mode 100644 index 0000000000..24f13cd291 --- /dev/null +++ b/src/web/app/common/scripts/home-stream.js @@ -0,0 +1,18 @@ +'use strict'; + +import Stream from './stream'; + +/** + * Home stream connection + */ +class Connection extends Stream { + constructor(me) { + super('', { + i: me.token + }); + + this.on('i_updated', me.update); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/messaging-stream.js b/src/web/app/common/scripts/messaging-stream.js index 50d41c2be9..261525d5f6 100644 --- a/src/web/app/common/scripts/messaging-stream.js +++ b/src/web/app/common/scripts/messaging-stream.js @@ -1,42 +1,22 @@ -const ReconnectingWebSocket = require('reconnecting-websocket'); -import * as riot from 'riot'; -import CONFIG from './config'; +'use strict'; -class Connection { +import Stream from './stream'; + +/** + * Messaging stream connection + */ +class Connection extends Stream { constructor(me, otherparty) { - // BIND ----------------------------------- - this.onOpen = this.onOpen.bind(this); - this.onMessage = this.onMessage.bind(this); - this.close = this.close.bind(this); - // ---------------------------------------- + super('messaging', { + i: me.token, + otherparty + }); - this.event = riot.observable(); - this.me = me; - - const host = CONFIG.apiUrl.replace('http', 'ws'); - this.socket = new ReconnectingWebSocket(`${host}/messaging?i=${me.token}&otherparty=${otherparty}`); - this.socket.addEventListener('open', this.onOpen); - this.socket.addEventListener('message', this.onMessage); - } - - onOpen() { - this.socket.send(JSON.stringify({ - i: this.me.token - })); - } - - onMessage(message) { - try { - const msg = JSON.parse(message.data); - if (msg.type) this.event.trigger(msg.type, msg.body); - } catch(e) { - // noop - } - } - - close() { - this.socket.removeEventListener('open', this.onOpen); - this.socket.removeEventListener('message', this.onMessage); + this.on('_connected_', () => { + this.send({ + i: me.token + }); + }); } } diff --git a/src/web/app/common/scripts/server-stream.js b/src/web/app/common/scripts/server-stream.js new file mode 100644 index 0000000000..a1c466b35d --- /dev/null +++ b/src/web/app/common/scripts/server-stream.js @@ -0,0 +1,14 @@ +'use strict'; + +import Stream from './stream'; + +/** + * Server stream connection + */ +class Connection extends Stream { + constructor() { + super('server'); + } +} + +export default Connection; diff --git a/src/web/app/common/scripts/stream.js b/src/web/app/common/scripts/stream.js index ac3dd67153..981118b5de 100644 --- a/src/web/app/common/scripts/stream.js +++ b/src/web/app/common/scripts/stream.js @@ -5,10 +5,10 @@ import * as riot from 'riot'; import CONFIG from './config'; /** - * Home stream connection + * Misskey stream connection */ class Connection { - constructor(me) { + constructor(endpoint, params) { // BIND ----------------------------------- this.onOpen = this.onOpen.bind(this); this.onClose = this.onClose.bind(this); @@ -20,16 +20,19 @@ class Connection { riot.observable(this); this.state = 'initializing'; - this.me = me; this.buffer = []; const host = CONFIG.apiUrl.replace('http', 'ws'); - this.socket = new ReconnectingWebSocket(`${host}?i=${me.token}`); + const query = params + ? Object.keys(params) + .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) + .join('&') + : null; + + this.socket = new ReconnectingWebSocket(`${host}/${endpoint}${query ? '?' + query : ''}`); this.socket.addEventListener('open', this.onOpen); this.socket.addEventListener('close', this.onClose); this.socket.addEventListener('message', this.onMessage); - - this.on('i_updated', me.update); } /** diff --git a/src/web/app/common/tags/messaging/room.tag b/src/web/app/common/tags/messaging/room.tag index bd49a4d782..93f07f2cee 100644 --- a/src/web/app/common/tags/messaging/room.tag +++ b/src/web/app/common/tags/messaging/room.tag @@ -137,8 +137,8 @@ this.connection = new MessagingStreamConnection(this.I, this.user.id); this.on('mount', () => { - this.connection.event.on('message', this.onMessage); - this.connection.event.on('read', this.onRead); + this.connection.on('message', this.onMessage); + this.connection.on('read', this.onRead); document.addEventListener('visibilitychange', this.onVisibilitychange); @@ -153,8 +153,8 @@ }); this.on('unmount', () => { - this.connection.event.off('message', this.onMessage); - this.connection.event.off('read', this.onRead); + this.connection.off('message', this.onMessage); + this.connection.off('read', this.onRead); this.connection.close(); document.removeEventListener('visibilitychange', this.onVisibilitychange); @@ -174,10 +174,10 @@ this.messages.push(message); if (message.user_id != this.I.id && !document.hidden) { - this.connection.socket.send(JSON.stringify({ + this.connection.send({ type: 'read', id: message.id - })); + }); } this.update(); @@ -239,10 +239,10 @@ if (document.hidden) return; this.messages.forEach(message => { if (message.user_id !== this.I.id && !message.is_read) { - this.connection.socket.send(JSON.stringify({ + this.connection.send({ type: 'read', id: message.id - })); + }); } }); }; diff --git a/src/web/app/desktop/tags/home-widgets/activity.tag b/src/web/app/desktop/tags/home-widgets/activity.tag index 621702fa17..3a8afdfa33 100644 --- a/src/web/app/desktop/tags/home-widgets/activity.tag +++ b/src/web/app/desktop/tags/home-widgets/activity.tag @@ -100,6 +100,8 @@ + + + + + CPU + + + + + MEM + + + + + + + + +

Maintainer: { meta.maintainer }

+ + +
+ diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag index 0e4a2ced10..41053ccbbe 100644 --- a/src/web/app/desktop/tags/home.tag +++ b/src/web/app/desktop/tags/home.tag @@ -70,6 +70,7 @@ 'rss-reader', 'trends', 'photo-stream', + 'server', 'version' ], right: [ diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js index 1e0ebd44cf..177ba41293 100644 --- a/src/web/app/desktop/tags/index.js +++ b/src/web/app/desktop/tags/index.js @@ -45,6 +45,7 @@ require('./home-widgets/version.tag'); require('./home-widgets/recommended-polls.tag'); require('./home-widgets/trends.tag'); require('./home-widgets/activity.tag'); +require('./home-widgets/server.tag'); require('./timeline.tag'); require('./messaging/window.tag'); require('./messaging/room-window.tag'); diff --git a/src/web/app/init.js b/src/web/app/init.js index 17f9a2d09e..b442d36a15 100644 --- a/src/web/app/init.js +++ b/src/web/app/init.js @@ -8,7 +8,7 @@ import * as riot from 'riot'; import api from './common/scripts/api'; import signout from './common/scripts/signout'; import checkForUpdate from './common/scripts/check-for-update'; -import Connection from './common/scripts/stream'; +import Connection from './common/scripts/home-stream'; import mixin from './common/mixins'; import generateDefaultUserdata from './common/scripts/generate-default-userdata'; import CONFIG from './common/scripts/config'; @@ -95,7 +95,7 @@ export default callback => { }); } - // Init stream connection + // Init home stream connection const stream = me ? new Connection(me) : null; // ミックスイン初期化