1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-08 15:14:48 +02:00

Compare commits

..

36 Commits

Author SHA1 Message Date
syuilo
af2d36a3c9 2.34.3 2018-06-09 11:41:22 +09:00
syuilo
42a4f92cfa Fix #1125 2018-06-09 11:40:42 +09:00
syuilo
ccb9ed3489 🎨 2018-06-09 11:29:50 +09:00
syuilo
773b2aa3d1 2.34.2 2018-06-09 10:16:29 +09:00
syuilo
30d5b8d65b 🎨 2018-06-09 10:15:45 +09:00
syuilo
763676a18c ✌️ 2018-06-09 10:12:03 +09:00
syuilo
e166ad6780 Better init screen 2018-06-09 10:06:27 +09:00
syuilo
034c96d070 Merge branch 'master' of https://github.com/syuilo/misskey 2018-06-09 08:38:33 +09:00
syuilo
f34f8d304c Improve performance 2018-06-09 08:38:30 +09:00
syuilo
944000c05c Update README.md 2018-06-09 06:52:33 +09:00
syuilo
52db63bca2 2.34.1 2018-06-09 06:28:22 +09:00
syuilo
55dfd9e2a1 Merge pull request #1687 from syuilo/l10n_master
New Crowdin translations
2018-06-09 06:27:57 +09:00
syuilo
d193cbf2b7 ✌️ 2018-06-09 06:27:12 +09:00
syuilo
bdec56a543 ✌️ 2018-06-09 06:25:41 +09:00
syuilo
e0a6d9740c ✌️ 2018-06-09 06:21:13 +09:00
syuilo
0ce9c057e1 Fix chart rendering 2018-06-09 06:04:41 +09:00
syuilo
12a2fdbc20 New translations ja.yml (Portuguese) 2018-06-09 04:21:28 +09:00
syuilo
57c294bc89 New translations ja.yml (Korean) 2018-06-09 04:21:26 +09:00
syuilo
9758757805 New translations ja.yml (Polish) 2018-06-09 04:21:25 +09:00
syuilo
f9350fa35f New translations ja.yml (Chinese Simplified) 2018-06-09 04:21:22 +09:00
syuilo
e120da4ecd New translations ja.yml (Italian) 2018-06-09 04:21:20 +09:00
syuilo
328a10b70c New translations ja.yml (Russian) 2018-06-09 04:21:18 +09:00
syuilo
1ed97c8deb New translations ja.yml (English) 2018-06-09 04:21:17 +09:00
syuilo
91b970e2aa New translations ja.yml (Spanish) 2018-06-09 04:21:15 +09:00
syuilo
99af1bb479 New translations ja.yml (German) 2018-06-09 04:21:13 +09:00
syuilo
11ddcbdee3 New translations ja.yml (French) 2018-06-09 04:21:11 +09:00
syuilo
6e8a1086d8 2.34.0 2018-06-09 04:15:18 +09:00
syuilo
c78945436e #1686 2018-06-09 04:14:26 +09:00
syuilo
6eff8fde74 サーバーの統計情報をメモリに記憶するようにするなど 2018-06-09 01:45:25 +09:00
syuilo
726d5a177e Merge pull request #1685 from 2vg/patch-1
fix: when text is null, bug can pass validation.
2018-06-08 22:04:22 +09:00
momf
33495b5cb3 fix: "or" operator. 2018-06-08 22:04:07 +09:00
syuilo
fe159a13a9 2.33.2 2018-06-08 22:03:24 +09:00
syuilo
22a1dc0566 Better log 2018-06-08 22:03:14 +09:00
momf
02e6b732e9 fix: when text is null, bug can pass validation.
fixed. (maybe?)
2018-06-08 22:00:18 +09:00
syuilo
cc6fa135ac 2.33.1 2018-06-08 21:50:41 +09:00
syuilo
5747732156 Fix 2018-06-08 21:50:31 +09:00
45 changed files with 531 additions and 101 deletions

View File

@@ -23,6 +23,7 @@ ultimately sophisticated professional microblogging software.
* Reactions
* User lists
* Customizable column view (known as MisskeyDeck)
* and widgets!
* Private messages
* Mute
* Streaming

View File

@@ -57,6 +57,7 @@ common:
memo: "Notizen"
trends: "Trends"
photo-stream: "Bilder"
posts-monitor: "投稿チャート"
slideshow: "Diashow"
version: "Version"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "Fotostream"
no-photos: "Keine Fotos"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "Serverinformationen"
toggle: "Sicht umschalten"

View File

@@ -57,6 +57,7 @@ common:
memo: "Memo"
trends: "Trends"
photo-stream: "Photo stream"
posts-monitor: "投稿チャート"
slideshow: "Slideshow"
version: "Version"
broadcast: "Broadcast"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "Photostream"
no-photos: "No photos"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "Server info"
toggle: "Toggle views"

View File

@@ -57,6 +57,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -57,6 +57,7 @@ common:
memo: "Note"
trends: "Tendances"
photo-stream: "Flux de photos"
posts-monitor: "投稿チャート"
slideshow: "Diaporama"
version: "Version"
broadcast: "Diffusion"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "Flux de photo"
no-photos: "Pas de photos"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "Info sur le serveur"
toggle: "Afficher les vues"

View File

@@ -57,6 +57,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -63,6 +63,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -249,6 +250,10 @@ common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -57,6 +57,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -57,6 +57,7 @@ common:
memo: "Notatki"
trends: "Na czasie"
photo-stream: "Photostream"
posts-monitor: "投稿チャート"
slideshow: "Pokaz slajdów"
version: "Wersja"
broadcast: "Transmisja"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "Photostream"
no-photos: "Brak zdjęć"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "Informacje o serwerze"
toggle: "Przełącz widok"

View File

@@ -57,6 +57,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -57,6 +57,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -57,6 +57,7 @@ common:
memo: "メモ"
trends: "トレンド"
photo-stream: "フォトストリーム"
posts-monitor: "投稿チャート"
slideshow: "スライドショー"
version: "バージョン"
broadcast: "ブロードキャスト"
@@ -220,6 +221,9 @@ common/views/widgets/donation.vue:
common/views/widgets/photo-stream.vue:
title: "フォトストリーム"
no-photos: "写真はありません"
common/views/widgets/posts-monitor.vue:
title: "投稿チャート"
toggle: "表示を切り替え"
common/views/widgets/server.vue:
title: "サーバー情報"
toggle: "表示を切り替え"

View File

@@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "2.33.0",
"clientVersion": "1.0.6292",
"version": "2.34.3",
"clientVersion": "1.0.6328",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@@ -152,7 +152,7 @@
"mkdirp": "0.5.1",
"mocha": "5.2.0",
"moji": "0.5.1",
"mongodb": "3.0.8",
"mongodb": "3.0.10",
"monk": "6.0.6",
"ms": "2.1.1",
"nan": "2.10.0",
@@ -218,6 +218,6 @@
"webpack-cli": "2.1.4",
"websocket": "1.0.26",
"ws": "5.2.0",
"xev": "2.0.0"
"xev": "2.0.1"
}
}

View File

@@ -42,7 +42,7 @@ html
| JavaScriptを有効にしてください
br
| Please turn on your JavaScript
div#ini: p
span .
span .
span .
div#ini.
<svg viewBox="0 0 50 50">
<path fill=#{themeColor} d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" />
</svg>

View File

@@ -3,15 +3,15 @@ import StreamManager from './stream-manager';
import MiOS from '../../../mios';
/**
* Server stream connection
* Notes stats stream connection
*/
export class ServerStream extends Stream {
export class NotesStatsStream extends Stream {
constructor(os: MiOS) {
super(os, 'server');
super(os, 'notes-stats');
}
}
export class ServerStreamManager extends StreamManager<ServerStream> {
export class NotesStatsStreamManager extends StreamManager<NotesStatsStream> {
private os: MiOS;
constructor(os: MiOS) {
@@ -22,7 +22,7 @@ export class ServerStreamManager extends StreamManager<ServerStream> {
public getConnection() {
if (this.connection == null) {
this.connection = new ServerStream(this.os);
this.connection = new NotesStatsStream(this.os);
}
return this.connection;

View File

@@ -0,0 +1,30 @@
import Stream from './stream';
import StreamManager from './stream-manager';
import MiOS from '../../../mios';
/**
* Server stats stream connection
*/
export class ServerStatsStream extends Stream {
constructor(os: MiOS) {
super(os, 'server-stats');
}
}
export class ServerStatsStreamManager extends StreamManager<ServerStatsStream> {
private os: MiOS;
constructor(os: MiOS) {
super();
this.os = os;
}
public getConnection() {
if (this.connection == null) {
this.connection = new ServerStatsStream(this.os);
}
return this.connection;
}
}

View File

@@ -75,7 +75,7 @@ root(isDark)
align-self center
margin 0 .5em 0 0
padding 1px 6px
font-size 12px
font-size 80%
color isDark ? #758188 : #aaa
border solid 1px isDark ? #57616f : #ddd
border-radius 3px

View File

@@ -59,7 +59,7 @@ export default Vue.extend({
created() {
if (this.mode == 'relative' || this.mode == 'detail') {
this.tick();
this.tickId = setInterval(this.tick, 5000);
this.tickId = setInterval(this.tick, 10000);
}
},
destroyed() {

View File

@@ -4,6 +4,7 @@ import wAnalogClock from './analog-clock.vue';
import wVersion from './version.vue';
import wRss from './rss.vue';
import wServer from './server.vue';
import wPostsMonitor from './posts-monitor.vue';
import wMemo from './memo.vue';
import wBroadcast from './broadcast.vue';
import wCalendar from './calendar.vue';
@@ -22,6 +23,7 @@ Vue.component('mkw-tips', wTips);
Vue.component('mkw-donation', wDonation);
Vue.component('mkw-broadcast', wBroadcast);
Vue.component('mkw-server', wServer);
Vue.component('mkw-posts-monitor', wPostsMonitor);
Vue.component('mkw-memo', wMemo);
Vue.component('mkw-rss', wRss);
Vue.component('mkw-version', wVersion);

View File

@@ -0,0 +1,211 @@
<template>
<div class="mkw-posts-monitor">
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header">%fa:chart-line%%i18n:@title%</template>
<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }" :data-darkmode="$store.state.device.darkmode">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">
<defs>
<linearGradient :id="localGradientId" x1="0" x2="0" y1="1" y2="0">
<stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
<stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
</linearGradient>
<mask :id="localMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
<polygon
:points="localPolygonPoints"
fill="#fff"
fill-opacity="0.5"/>
<polyline
:points="localPolylinePoints"
fill="none"
stroke="#fff"
stroke-width="1"/>
<circle
:cx="localHeadX"
:cy="localHeadY"
r="1.5"
fill="#fff"/>
</mask>
</defs>
<rect
x="-2" y="-2"
:width="viewBoxX + 4" :height="viewBoxY + 4"
:style="`stroke: none; fill: url(#${ localGradientId }); mask: url(#${ localMaskId })`"/>
<text x="1" y="5">Local</text>
</svg>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 1">
<defs>
<linearGradient :id="fediGradientId" x1="0" x2="0" y1="1" y2="0">
<stop offset="0%" stop-color="hsl(200, 80%, 70%)"></stop>
<stop offset="100%" stop-color="hsl(90, 80%, 70%)"></stop>
</linearGradient>
<mask :id="fediMaskId" x="0" y="0" :width="viewBoxX" :height="viewBoxY">
<polygon
:points="fediPolygonPoints"
fill="#fff"
fill-opacity="0.5"/>
<polyline
:points="fediPolylinePoints"
fill="none"
stroke="#fff"
stroke-width="1"/>
<circle
:cx="fediHeadX"
:cy="fediHeadY"
r="1.5"
fill="#fff"/>
</mask>
</defs>
<rect
x="-2" y="-2"
:width="viewBoxX + 4" :height="viewBoxY + 4"
:style="`stroke: none; fill: url(#${ fediGradientId }); mask: url(#${ fediMaskId })`"/>
<text x="1" y="5">Fedi</text>
</svg>
</div>
</mk-widget-container>
</div>
</template>
<script lang="ts">
import define from '../../../common/define-widget';
import * as uuid from 'uuid';
export default define({
name: 'server',
props: () => ({
design: 0,
view: 0
})
}).extend({
data() {
return {
connection: null,
connectionId: null,
viewBoxY: 30,
stats: [],
fediGradientId: uuid(),
fediMaskId: uuid(),
localGradientId: uuid(),
localMaskId: uuid(),
fediPolylinePoints: '',
localPolylinePoints: '',
fediPolygonPoints: '',
localPolygonPoints: '',
fediHeadX: null,
fediHeadY: null,
localHeadX: null,
localHeadY: null
};
},
computed: {
viewBoxX(): number {
return this.props.view == 0 ? 50 : 100;
}
},
watch: {
viewBoxX() {
this.draw();
}
},
mounted() {
this.connection = (this as any).os.streams.notesStatsStream.getConnection();
this.connectionId = (this as any).os.streams.notesStatsStream.use();
this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog);
this.connection.send({
type: 'requestLog',
id: Math.random().toString()
});
},
beforeDestroy() {
this.connection.off('stats', this.onStats);
this.connection.off('statsLog', this.onStatsLog);
(this as any).os.streams.notesStatsStream.dispose(this.connectionId);
},
methods: {
toggle() {
if (this.props.view == 2) {
this.props.view = 0;
} else {
this.props.view++;
}
this.save();
},
func() {
if (this.props.design == 2) {
this.props.design = 0;
} else {
this.props.design++;
}
this.save();
},
draw() {
const stats = this.props.view == 0 ? this.stats.slice(-50) : this.stats;
const fediPeak = Math.max.apply(null, stats.map(x => x.all)) || 1;
const localPeak = Math.max.apply(null, stats.map(x => x.local)) || 1;
const fediPolylinePoints = stats.map((s, i) => [this.viewBoxX - ((stats.length - 1) - i), (1 - (s.all / fediPeak)) * this.viewBoxY]);
const localPolylinePoints = stats.map((s, i) => [this.viewBoxX - ((stats.length - 1) - i), (1 - (s.local / localPeak)) * this.viewBoxY]);
this.fediPolylinePoints = fediPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
this.localPolylinePoints = localPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
this.fediPolygonPoints = `${this.viewBoxX - (stats.length - 1)},${ this.viewBoxY } ${ this.fediPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
this.localPolygonPoints = `${this.viewBoxX - (stats.length - 1)},${ this.viewBoxY } ${ this.localPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
this.fediHeadX = fediPolylinePoints[fediPolylinePoints.length - 1][0];
this.fediHeadY = fediPolylinePoints[fediPolylinePoints.length - 1][1];
this.localHeadX = localPolylinePoints[localPolylinePoints.length - 1][0];
this.localHeadY = localPolylinePoints[localPolylinePoints.length - 1][1];
},
onStats(stats) {
this.stats.push(stats);
if (this.stats.length > 100) this.stats.shift();
this.draw();
},
onStatsLog(statsLog) {
statsLog.forEach(stats => this.onStats(stats));
}
}
});
</script>
<style lang="stylus" scoped>
root(isDark)
&.dual
> svg
width 50%
float left
&:first-child
padding-right 5px
&:last-child
padding-left 5px
> svg
display block
padding 10px
width 100%
> text
font-size 5px
fill isDark ? rgba(#fff, 0.55) : rgba(#000, 0.55)
> tspan
opacity 0.5
&:after
content ""
display block
clear both
.qpdmibaztplkylerhdbllwcokyrfxeyj[data-darkmode]
root(true)
.qpdmibaztplkylerhdbllwcokyrfxeyj:not([data-darkmode])
root(false)
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div class="cpu-memory">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
<defs>
<linearGradient :id="cpuGradientId" x1="0" x2="0" y1="1" y2="0">
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
@@ -16,15 +16,20 @@
fill="none"
stroke="#fff"
stroke-width="1"/>
<circle
:cx="cpuHeadX"
:cy="cpuHeadY"
r="1.5"
fill="#fff"/>
</mask>
</defs>
<rect
x="-1" y="-1"
:width="viewBoxX + 2" :height="viewBoxY + 2"
x="-2" y="-2"
:width="viewBoxX + 4" :height="viewBoxY + 4"
:style="`stroke: none; fill: url(#${ cpuGradientId }); mask: url(#${ cpuMaskId })`"/>
<text x="1" y="5">CPU <tspan>{{ cpuP }}%</tspan></text>
</svg>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
<defs>
<linearGradient :id="memGradientId" x1="0" x2="0" y1="1" y2="0">
<stop offset="0%" stop-color="hsl(180, 80%, 70%)"></stop>
@@ -40,11 +45,16 @@
fill="none"
stroke="#fff"
stroke-width="1"/>
<circle
:cx="memHeadX"
:cy="memHeadY"
r="1.5"
fill="#fff"/>
</mask>
</defs>
<rect
x="-1" y="-1"
:width="viewBoxX + 2" :height="viewBoxY + 2"
x="-2" y="-2"
:width="viewBoxX + 4" :height="viewBoxY + 4"
:style="`stroke: none; fill: url(#${ memGradientId }); mask: url(#${ memMaskId })`"/>
<text x="1" y="5">MEM <tspan>{{ memP }}%</tspan></text>
</svg>
@@ -70,15 +80,25 @@ export default Vue.extend({
memPolylinePoints: '',
cpuPolygonPoints: '',
memPolygonPoints: '',
cpuHeadX: null,
cpuHeadY: null,
memHeadX: null,
memHeadY: null,
cpuP: '',
memP: ''
};
},
mounted() {
this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog);
this.connection.send({
type: 'requestLog',
id: Math.random().toString()
});
},
beforeDestroy() {
this.connection.off('stats', this.onStats);
this.connection.off('statsLog', this.onStatsLog);
},
methods: {
onStats(stats) {
@@ -86,14 +106,24 @@ export default Vue.extend({
this.stats.push(stats);
if (this.stats.length > 50) this.stats.shift();
this.cpuPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - s.cpu_usage) * this.viewBoxY}`).join(' ');
this.memPolylinePoints = this.stats.map((s, i) => `${this.viewBoxX - ((this.stats.length - 1) - i)},${(1 - (s.mem.used / s.mem.total)) * this.viewBoxY}`).join(' ');
const cpuPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - s.cpu_usage) * this.viewBoxY]);
const memPolylinePoints = this.stats.map((s, i) => [this.viewBoxX - ((this.stats.length - 1) - i), (1 - (s.mem.used / s.mem.total)) * this.viewBoxY]);
this.cpuPolylinePoints = cpuPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
this.memPolylinePoints = memPolylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
this.cpuPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ this.cpuPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
this.memPolygonPoints = `${this.viewBoxX - (this.stats.length - 1)},${ this.viewBoxY } ${ this.memPolylinePoints } ${ this.viewBoxX },${ this.viewBoxY }`;
this.cpuHeadX = cpuPolylinePoints[cpuPolylinePoints.length - 1][0];
this.cpuHeadY = cpuPolylinePoints[cpuPolylinePoints.length - 1][1];
this.memHeadX = memPolylinePoints[memPolylinePoints.length - 1][0];
this.memHeadY = memPolylinePoints[memPolylinePoints.length - 1][1];
this.cpuP = (stats.cpu_usage * 100).toFixed(0);
this.memP = (stats.mem.used / stats.mem.total * 100).toFixed(0);
},
onStatsLog(statsLog) {
statsLog.forEach(stats => this.onStats(stats));
}
}
});

View File

@@ -55,11 +55,11 @@ export default define({
this.fetching = false;
});
this.connection = (this as any).os.streams.serverStream.getConnection();
this.connectionId = (this as any).os.streams.serverStream.use();
this.connection = (this as any).os.streams.serverStatsStream.getConnection();
this.connectionId = (this as any).os.streams.serverStatsStream.use();
},
beforeDestroy() {
(this as any).os.streams.serverStream.dispose(this.connectionId);
(this as any).os.streams.serverStatsStream.dispose(this.connectionId);
},
methods: {
toggle() {

View File

@@ -1,5 +1,5 @@
<template>
<svg viewBox="0 0 21 7" preserveAspectRatio="none">
<svg viewBox="0 0 21 7">
<rect v-for="record in data" class="day"
width="1" height="1"
:x="record.x" :y="record.date.weekday"
@@ -15,7 +15,7 @@
style="pointer-events: none;"/>
<rect class="today"
width="1" height="1"
:x="data[data.length - 1].x" :y="data[data.length - 1].date.weekday"
:x="data[0].x" :y="data[0].date.weekday"
rx="1" ry="1"
fill="none"
stroke-width="0.1"
@@ -33,7 +33,7 @@ export default Vue.extend({
const peak = Math.max.apply(null, this.data.map(d => d.total));
let x = 0;
this.data.reverse().forEach(d => {
this.data.slice().reverse().forEach(d => {
d.x = x;
d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();

View File

@@ -1,5 +1,5 @@
<template>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" @mousedown.prevent="onMousedown">
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" @mousedown.prevent="onMousedown">
<title>%i18n:@total%<br/>%i18n:@notes%<br/>%i18n:@replies%<br/>%i18n:@renotes%</title>
<polyline
:points="pointsNote"
@@ -55,7 +55,6 @@ export default Vue.extend({
};
},
created() {
this.data.reverse();
this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
this.render();
},
@@ -63,10 +62,11 @@ export default Vue.extend({
render() {
const peak = Math.max.apply(null, this.data.map(d => d.total));
if (peak != 0) {
this.pointsNote = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
this.pointsReply = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
this.pointsRenote = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
this.pointsTotal = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
const data = this.data.slice().reverse();
this.pointsNote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
this.pointsReply = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
this.pointsRenote = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
this.pointsTotal = data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
}
},
onMousedown(e) {

View File

@@ -23,6 +23,7 @@
<option value="post-form">%i18n:common.widgets.post-form%</option>
<option value="messaging">%i18n:common.widgets.messaging%</option>
<option value="memo">%i18n:common.widgets.memo%</option>
<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
<option value="server">%i18n:common.widgets.server%</option>
<option value="donation">%i18n:common.widgets.donation%</option>
<option value="nav">%i18n:common.widgets.nav%</option>

View File

@@ -206,7 +206,7 @@ root(isDark)
margin 0
padding 16px
overflow-wrap break-word
font-size 12px
font-size 13px
border-bottom solid 1px isDark ? #1c2023 : rgba(#000, 0.05)
&:last-child

View File

@@ -200,7 +200,7 @@ export default Vue.extend({
@import '~const.styl'
root(isDark)
font-size 12px
font-size 13px
border-bottom solid 1px isDark ? #1c2023 : #eaeaea
&:last-of-type
@@ -216,7 +216,7 @@ root(isDark)
> .renote
display flex
align-items center
padding 8px 16px
padding 8px 16px 0 16px
line-height 28px
white-space pre
color #9dbb00

View File

@@ -112,7 +112,7 @@ export default Vue.extend({
root(isDark)
> .notification
padding 16px
font-size 12px
font-size 13px
overflow-wrap break-word
&:after

View File

@@ -96,7 +96,7 @@ export default Vue.extend({
fetchMoreNotifications() {
this.fetchingMoreNotifications = true;
const max = 30;
const max = 20;
(this as any).api('i/notifications', {
limit: max + 1,

View File

@@ -23,6 +23,7 @@
<option value="post-form">%i18n:common.widgets.post-form%</option>
<option value="messaging">%i18n:common.widgets.messaging%</option>
<option value="memo">%i18n:common.widgets.memo%</option>
<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
<option value="server">%i18n:common.widgets.server%</option>
<option value="donation">%i18n:common.widgets.donation%</option>
<option value="nav">%i18n:common.widgets.nav%</option>

View File

@@ -32,42 +32,30 @@ body > noscript {
left: 0;
width: 100%;
height: 100%;
text-align: center;
background: #fff;
cursor: wait;
}
#ini > p {
display: block;
user-select: none;
margin: 32px;
font-size: 4em;
color: #555;
#ini > svg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
width: 64px;
height: 64px;
animation: ini 0.6s infinite linear;
}
#ini > p > span {
animation: ini 1.4s infinite ease-in-out both;
}
#ini > p > span:nth-child(1) {
animation-delay: 0s;
}
#ini > p > span:nth-child(2) {
animation-delay: 0.16s;
}
#ini > p > span:nth-child(3) {
animation-delay: 0.32s;
}
html[data-darkmode] #ini {
background: #191b22;
}
html[data-darkmode] #ini > p {
color: #fff;
}
@keyframes ini {
0%, 80%, 100% {
opacity: 1;
from {
transform: rotate(0deg);
}
40% {
opacity: 0;
to {
transform: rotate(360deg);
}
}

View File

@@ -8,7 +8,8 @@ import Progress from './common/scripts/loading';
import Connection from './common/scripts/streaming/stream';
import { HomeStreamManager } from './common/scripts/streaming/home';
import { DriveStreamManager } from './common/scripts/streaming/drive';
import { ServerStreamManager } from './common/scripts/streaming/server';
import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats';
import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats';
import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index';
import { OthelloStreamManager } from './common/scripts/streaming/othello';
@@ -104,14 +105,16 @@ export default class MiOS extends EventEmitter {
localTimelineStream: LocalTimelineStreamManager;
globalTimelineStream: GlobalTimelineStreamManager;
driveStream: DriveStreamManager;
serverStream: ServerStreamManager;
serverStatsStream: ServerStatsStreamManager;
notesStatsStream: NotesStatsStreamManager;
messagingIndexStream: MessagingIndexStreamManager;
othelloStream: OthelloStreamManager;
} = {
localTimelineStream: null,
globalTimelineStream: null,
driveStream: null,
serverStream: null,
serverStatsStream: null,
notesStatsStream: null,
messagingIndexStream: null,
othelloStream: null
};
@@ -218,7 +221,8 @@ export default class MiOS extends EventEmitter {
this.store = initStore(this);
//#region Init stream managers
this.streams.serverStream = new ServerStreamManager(this);
this.streams.serverStatsStream = new ServerStatsStreamManager(this);
this.streams.notesStatsStream = new NotesStatsStreamManager(this);
this.once('signedin', () => {
// Init home stream manager

View File

@@ -15,6 +15,7 @@
<option value="rss">%i18n:common.widgets.rss%</option>
<option value="photo-stream">%i18n:common.widgets.photo-stream%</option>
<option value="slideshow">%i18n:common.widgets.slideshow%</option>
<option value="posts-monitor">%i18n:common.widgets.posts-monitor%</option>
<option value="version">%i18n:common.widgets.version%</option>
<option value="server">%i18n:common.widgets.server%</option>
<option value="memo">%i18n:common.widgets.memo%</option>

View File

@@ -12,7 +12,10 @@ const uri = u && p
*/
import mongo from 'monk';
const db = mongo(uri);
const db = mongo(uri, {
poolSize: 16,
keepAlive: 1
});
export default db;

View File

@@ -17,7 +17,8 @@ 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 serverStats from './server-stats';
import notesStats from './notes-stats';
import loadConfig from './config/load';
import { Config } from './config/types';
@@ -49,7 +50,8 @@ function main() {
masterMain(opt);
ev.mount();
stats();
serverStats();
notesStats();
} else {
workerMain(opt);
}

View File

@@ -16,6 +16,9 @@ import Following from './following';
const Note = db.get<INote>('notes');
Note.createIndex('uri', { sparse: true, unique: true });
Note.createIndex('userId');
Note.createIndex({
createdAt: -1
});
export default Note;
export function isValidText(text: string): boolean {

22
src/notes-stats-child.ts Normal file
View File

@@ -0,0 +1,22 @@
import Note from './models/note';
const interval = 5000;
setInterval(async () => {
const [all, local] = await Promise.all([Note.count({
createdAt: {
$gte: new Date(Date.now() - interval)
}
}), Note.count({
createdAt: {
$gte: new Date(Date.now() - interval)
},
'_user.host': null
})]);
const stats = {
all, local
};
process.send(stats);
}, interval);

20
src/notes-stats.ts Normal file
View File

@@ -0,0 +1,20 @@
import * as childProcess from 'child_process';
import Xev from 'xev';
const ev = new Xev();
export default function() {
const log = [];
const p = childProcess.fork(__dirname + '/notes-stats-child.js');
p.on('message', stats => {
ev.emit('notesStats', stats);
log.push(stats);
if (log.length > 100) log.shift();
});
ev.on('requestNotesStatsLog', id => {
ev.emit('notesStatsLog:' + id, log);
});
}

View File

@@ -6,13 +6,19 @@ import Xev from 'xev';
const ev = new Xev();
/**
* Report stats regularly
* Report server stats regularly
*/
export default function() {
const log = [];
ev.on('requestServerStatsLog', id => {
ev.emit('serverStatsLog:' + id, log);
});
setInterval(() => {
osUtils.cpuUsage(cpuUsage => {
const disk = diskusage.checkSync(os.platform() == 'win32' ? 'c:' : '/');
ev.emit('stats', {
const stats = {
cpu_usage: cpuUsage,
mem: {
total: os.totalmem(),
@@ -21,7 +27,10 @@ export default function() {
disk,
os_uptime: os.uptime(),
process_uptime: process.uptime()
});
};
ev.emit('serverStats', stats);
log.push(stats);
if (log.length > 50) log.shift();
});
}, 1000);
}

View File

@@ -140,7 +140,7 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
}
// テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー
if (text === undefined && files === null && renote === null && poll === undefined) {
if ((text === undefined || text === null) && files === null && renote === null && poll === undefined) {
return rej('text, mediaIds, renoteId or poll is required');
}

View File

@@ -0,0 +1,35 @@
import * as websocket from 'websocket';
import Xev from 'xev';
const ev = new Xev();
export default function(request: websocket.request, connection: websocket.connection): void {
const onStats = stats => {
connection.send(JSON.stringify({
type: 'stats',
body: stats
}));
};
connection.on('message', async data => {
const msg = JSON.parse(data.utf8Data);
switch (msg.type) {
case 'requestLog':
ev.once('notesStatsLog:' + msg.id, statsLog => {
connection.send(JSON.stringify({
type: 'statsLog',
body: statsLog
}));
});
ev.emit('requestNotesStatsLog', msg.id);
break;
}
});
ev.addListener('notesStats', onStats);
connection.on('close', () => {
ev.removeListener('notesStats', onStats);
});
}

View File

@@ -0,0 +1,35 @@
import * as websocket from 'websocket';
import Xev from 'xev';
const ev = new Xev();
export default function(request: websocket.request, connection: websocket.connection): void {
const onStats = stats => {
connection.send(JSON.stringify({
type: 'stats',
body: stats
}));
};
connection.on('message', async data => {
const msg = JSON.parse(data.utf8Data);
switch (msg.type) {
case 'requestLog':
ev.once('serverStatsLog:' + msg.id, statsLog => {
connection.send(JSON.stringify({
type: 'statsLog',
body: statsLog
}));
});
ev.emit('requestServerStatsLog', msg.id);
break;
}
});
ev.addListener('serverStats', onStats);
connection.on('close', () => {
ev.removeListener('serverStats', onStats);
});
}

View File

@@ -1,19 +0,0 @@
import * as websocket from 'websocket';
import Xev from 'xev';
const ev = new Xev();
export default function(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', () => {
ev.removeListener('stats', onStats);
});
}

View File

@@ -12,7 +12,8 @@ import messagingStream from './stream/messaging';
import messagingIndexStream from './stream/messaging-index';
import othelloGameStream from './stream/othello-game';
import othelloStream from './stream/othello';
import serverStream from './stream/server';
import serverStatsStream from './stream/server-stats';
import notesStatsStream from './stream/notes-stats';
import requestsStream from './stream/requests';
import { ParsedUrlQuery } from 'querystring';
import authenticate from './authenticate';
@@ -28,8 +29,13 @@ module.exports = (server: http.Server) => {
ws.on('request', async (request) => {
const connection = request.accept();
if (request.resourceURL.pathname === '/server') {
serverStream(request, connection);
if (request.resourceURL.pathname === '/server-stats') {
serverStatsStream(request, connection);
return;
}
if (request.resourceURL.pathname === '/notes-stats') {
notesStatsStream(request, connection);
return;
}

View File

@@ -164,8 +164,8 @@ export default async function(
'metadata.deletedAt': { $exists: false }
});
if (much !== null) {
log('file with same hash is found');
if (much) {
log(`file with same hash is found: ${much._id}`);
return much;
}
}