Compare commits

...

11 Commits

Author SHA1 Message Date
f5be8fd313 10.93.1 2019-03-13 09:34:14 +09:00
7f835d7f76 🎨 2019-03-13 09:32:10 +09:00
ddbb7c5993 🎨 2019-03-13 09:26:38 +09:00
00a3fe39e8 Update dependencies 🚀 2019-03-13 09:19:48 +09:00
7537fb88d4 Refactor 2019-03-13 00:14:44 +09:00
a81bc71a1e Resolve #4454 2019-03-13 00:13:56 +09:00
0a0aa0e2db Fix #4484 (#4485)
* Fix #4484

* import order
2019-03-12 23:38:11 +09:00
c56b94ae96 Add type annotation to avoid type error 2019-03-12 23:31:18 +09:00
e90712706d Add icons 🎨 2019-03-12 23:30:44 +09:00
eb0623331f 🎨 2019-03-12 23:14:18 +09:00
d15bd59109 Fix queue charts (#4482) 2019-03-12 21:53:36 +09:00
16 changed files with 321 additions and 60 deletions

View File

@ -5,6 +5,11 @@ If you encounter any problems with updating, please try the following:
1. `npm run clean` or `npm run cleanall`
2. Retry update (Don't forget `npm i`)
10.93.1
----------
* データのエクスポートとインポートの動作を修正
* デザインの調整
10.93.0
----------
* フォローリストをインポートできるように

View File

@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.93.0",
"version": "10.93.1",
"codename": "nighthike",
"repository": {
"type": "git",
@ -46,7 +46,6 @@
"@types/gulp-uglify": "3.0.6",
"@types/gulp-util": "3.0.34",
"@types/is-root": "1.0.0",
"@types/is-svg": "3.0.0",
"@types/is-url": "1.2.28",
"@types/js-yaml": "3.12.0",
"@types/jsdom": "12.2.3",
@ -96,7 +95,7 @@
"@types/websocket": "0.0.40",
"@types/ws": "6.0.1",
"animejs": "3.0.1",
"apexcharts": "3.5.0",
"apexcharts": "3.6.2",
"autobind-decorator": "2.4.0",
"autosize": "4.0.2",
"autwh": "0.1.0",
@ -109,20 +108,20 @@
"chalk": "2.4.2",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "2.1.0",
"css-loader": "2.1.1",
"cssnano": "4.1.10",
"dateformat": "3.0.3",
"deep-equal": "1.0.1",
"deepcopy": "0.6.3",
"diskusage": "1.0.0",
"double-ended-queue": "2.1.0-0",
"elasticsearch": "15.3.1",
"elasticsearch": "15.4.1",
"emojilib": "2.4.0",
"escape-regexp": "0.0.1",
"eslint": "5.15.0",
"eslint": "5.15.1",
"eslint-plugin-vue": "5.2.2",
"eventemitter3": "3.1.0",
"feed": "2.0.2",
"feed": "2.0.4",
"file-type": "10.9.0",
"fuckadblock": "3.2.1",
"gulp": "4.0.0",
@ -131,20 +130,20 @@
"gulp-mocha": "6.0.0",
"gulp-rename": "1.4.0",
"gulp-replace": "1.0.0",
"gulp-sourcemaps": "2.6.4",
"gulp-sourcemaps": "2.6.5",
"gulp-stylus": "2.7.0",
"gulp-tslint": "8.1.3",
"gulp-tslint": "8.1.4",
"gulp-typescript": "5.0.0",
"gulp-uglify": "3.0.1",
"gulp-uglify": "3.0.2",
"gulp-util": "3.0.8",
"hard-source-webpack-plugin": "0.13.1",
"html-minifier": "3.5.21",
"http-signature": "1.2.0",
"insert-text-at-cursor": "0.1.2",
"is-root": "2.0.0",
"is-svg": "3.0.0",
"js-yaml": "3.12.1",
"jsdom": "13.2.0",
"is-svg": "4.0.0",
"js-yaml": "3.12.2",
"jsdom": "14.0.0",
"json5": "2.1.0",
"json5-loader": "1.0.1",
"katex": "0.10.1",
@ -189,13 +188,13 @@
"pug": "2.0.3",
"punycode": "2.1.1",
"qrcode": "1.3.3",
"randomcolor": "0.5.3",
"ratelimiter": "3.2.0",
"randomcolor": "0.5.4",
"ratelimiter": "3.3.0",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.10",
"redis": "2.8.0",
"request": "2.88.0",
"request-promise-native": "1.0.5",
"request-promise-native": "1.0.7",
"request-stats": "3.0.0",
"rimraf": "2.6.3",
"rndstr": "1.0.0",
@ -210,14 +209,14 @@
"stylus": "0.54.5",
"stylus-loader": "3.0.2",
"summaly": "2.2.0",
"systeminformation": "4.0.14",
"systeminformation": "4.0.16",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.2.3",
"textarea-caret": "3.1.0",
"tinycolor2": "1.4.1",
"tmp": "0.0.33",
"ts-loader": "5.3.3",
"ts-node": "8.0.2",
"ts-node": "8.0.3",
"tslint": "5.13.1",
"tslint-sonarts": "1.9.0",
"typescript": "3.3.3333",
@ -232,7 +231,7 @@
"vue-color": "2.7.0",
"vue-content-loading": "1.5.3",
"vue-cropperjs": "3.0.0",
"vue-i18n": "8.8.2",
"vue-i18n": "8.9.0",
"vue-js-modal": "1.3.28",
"vue-json-pretty": "1.4.1",
"vue-loader": "15.7.0",
@ -241,9 +240,9 @@
"vue-router": "3.0.2",
"vue-sequential-entrance": "1.1.3",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.12",
"vue-svg-inline-loader": "1.2.13",
"vue-template-compiler": "2.6.8",
"vuedraggable": "2.18.1",
"vuedraggable": "2.19.2",
"vuewordcloud": "18.7.11",
"vuex": "3.1.0",
"vuex-persistedstate": "2.5.4",
@ -252,7 +251,7 @@
"webpack": "4.28.4",
"webpack-cli": "3.2.3",
"websocket": "1.0.28",
"ws": "6.1.4",
"ws": "6.2.0",
"xev": "2.0.1"
}
}

View File

@ -0,0 +1,194 @@
<template>
<div class="mzxlfysy">
<div>
<header>
<span><fa :icon="faInbox"/> In</span>
<span v-if="latestStats">{{ latestStats.inbox.activeSincePrevTick | number }} / {{ latestStats.inbox.delayed | number }}</span>
</header>
<div ref="in"></div>
</div>
<div>
<header>
<span><fa :icon="faPaperPlane"/> Out</span>
<span v-if="latestStats">{{ latestStats.deliver.activeSincePrevTick | number }} / {{ latestStats.deliver.delayed | number }}</span>
</header>
<div ref="out"></div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faInbox } from '@fortawesome/free-solid-svg-icons';
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
import ApexCharts from 'apexcharts';
export default Vue.extend({
data() {
return {
stats: [],
inChart: null,
outChart: null,
faInbox, faPaperPlane
};
},
computed: {
latestStats(): any {
return this.stats[this.stats.length - 1];
}
},
watch: {
stats(stats) {
this.inChart.updateSeries([{
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
}]);
this.outChart.updateSeries([{
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
}]);
}
},
mounted() {
const chartOpts = {
chart: {
type: 'area',
height: 200,
animations: {
dynamicAnimation: {
enabled: false
}
},
toolbar: {
show: false
},
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
},
grid: {
clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
},
stroke: {
curve: 'straight',
width: 2
},
tooltip: {
enabled: false
},
legend: {
show: false
},
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
series: [{ data: [] }, { data: [] }, { data: [] }, { data: [] }] as any,
xaxis: {
type: 'numeric',
labels: {
show: false
},
tooltip: {
enabled: false
}
},
yaxis: {
show: false,
min: 0,
}
};
this.inChart = new ApexCharts(this.$refs.in, chartOpts);
this.outChart = new ApexCharts(this.$refs.out, chartOpts);
this.inChart.render();
this.outChart.render();
const connection = this.$root.stream.useSharedConnection('queueStats');
connection.on('stats', this.onStats);
connection.on('statsLog', this.onStatsLog);
connection.send('requestLog', {
id: Math.random().toString().substr(2, 8),
length: 100
});
this.$once('hook:beforeDestroy', () => {
connection.dispose();
this.inChart.destroy();
this.outChart.destroy();
});
},
methods: {
onStats(stats) {
this.stats.push(stats);
if (this.stats.length > 100) this.stats.shift();
},
onStatsLog(statsLog) {
for (const stats of statsLog.reverse()) {
this.onStats(stats);
}
}
}
});
</script>
<style lang="stylus" scoped>
.mzxlfysy
display flex
> div
display block
flex 1
padding 20px 12px 0 12px
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
background var(--face)
border-radius 8px
&:first-child
margin-right 16px
> header
display flex
padding 0 8px
margin-bottom -16px
color var(--adminDashboardCardFg)
font-size 14px
> span
&:last-child
margin-left auto
opacity 0.7
> span
opacity 0.7
> div
margin-bottom -10px
@media (max-width 1000px)
display block
margin-bottom 26px
> div
&:first-child
margin-right 0
margin-bottom 26px
</style>

View File

@ -73,6 +73,10 @@
<x-charts ref="charts"/>
</div>
<div class="queue">
<x-queue/>
</div>
<div class="cpu-memory">
<x-cpu-memory :connection="connection"/>
</div>
@ -86,7 +90,8 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import XCpuMemory from "./cpu-memory.vue";
import XCpuMemory from "./dashboard.cpu-memory.vue";
import XQueue from "./dashboard.queue-charts.vue";
import XCharts from "./charts.vue";
import XApLog from "./ap-log.vue";
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
@ -98,6 +103,7 @@ export default Vue.extend({
components: {
XCpuMemory,
XQueue,
XCharts,
XApLog,
MarqueeText
@ -274,6 +280,9 @@ export default Vue.extend({
> .charts
margin-bottom 16px
> .queue
margin-bottom 16px
> .cpu-memory
margin-bottom 16px

View File

@ -5,14 +5,25 @@
<section class="wptihjuy">
<header><fa :icon="faPaperPlane"/> Deliver</header>
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
<ui-input :value="latestStats.deliver.waiting | number" type="text" readonly>
<span>Waiting</span>
</ui-input>
<ui-input :value="latestStats.deliver.delayed | number" type="text" readonly>
<span>Delayed</span>
<ui-input :value="latestStats.deliver.activeSincePrevTick | number" type="text" readonly>
<span>Process</span>
<template #prefix><fa :icon="fasPlayCircle"/></template>
<template #suffix>jobs/s</template>
</ui-input>
<ui-input :value="latestStats.deliver.active | number" type="text" readonly>
<span>Active</span>
<template #prefix><fa :icon="farPlayCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.deliver.waiting | number" type="text" readonly>
<span>Waiting</span>
<template #prefix><fa :icon="faStopCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.deliver.delayed | number" type="text" readonly>
<span>Delayed</span>
<template #prefix><fa :icon="faStopwatch"/></template>
<template #suffix>jobs</template>
</ui-input>
</ui-horizon-group>
<div ref="deliverChart" class="chart"></div>
@ -20,14 +31,25 @@
<section class="wptihjuy">
<header><fa :icon="faInbox"/> Inbox</header>
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
<ui-input :value="latestStats.inbox.waiting | number" type="text" readonly>
<span>Waiting</span>
</ui-input>
<ui-input :value="latestStats.inbox.delayed | number" type="text" readonly>
<span>Delayed</span>
<ui-input :value="latestStats.inbox.activeSincePrevTick | number" type="text" readonly>
<span>Process</span>
<template #prefix><fa :icon="fasPlayCircle"/></template>
<template #suffix>jobs/s</template>
</ui-input>
<ui-input :value="latestStats.inbox.active | number" type="text" readonly>
<span>Active</span>
<template #prefix><fa :icon="farPlayCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.inbox.waiting | number" type="text" readonly>
<span>Waiting</span>
<template #prefix><fa :icon="faStopCircle"/></template>
<template #suffix>jobs</template>
</ui-input>
<ui-input :value="latestStats.inbox.delayed | number" type="text" readonly>
<span>Delayed</span>
<template #prefix><fa :icon="faStopwatch"/></template>
<template #suffix>jobs</template>
</ui-input>
</ui-horizon-group>
<div ref="inboxChart" class="chart"></div>
@ -44,8 +66,8 @@ import Vue from 'vue';
import i18n from '../../i18n';
import ApexCharts from 'apexcharts';
import * as tinycolor from 'tinycolor2';
import { faTasks, faInbox } from '@fortawesome/free-solid-svg-icons';
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
import { faTasks, faInbox, faStopwatch, faPlayCircle as fasPlayCircle } from '@fortawesome/free-solid-svg-icons';
import { faPaperPlane, faStopCircle, faPlayCircle as farPlayCircle } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/queue.vue'),
@ -55,7 +77,7 @@ export default Vue.extend({
stats: [],
deliverChart: null,
inboxChart: null,
faTasks, faPaperPlane, faInbox
faTasks, faPaperPlane, faInbox, faStopwatch, faStopCircle, farPlayCircle, fasPlayCircle
};
},
@ -68,8 +90,11 @@ export default Vue.extend({
watch: {
stats(stats) {
this.inboxChart.updateSeries([{
name: 'Active',
name: 'Process',
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
}, {
name: 'Active',
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
}, {
name: 'Waiting',
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
@ -78,8 +103,11 @@ export default Vue.extend({
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
}]);
this.deliverChart.updateSeries([{
name: 'Active',
name: 'Process',
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
}, {
name: 'Active',
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
}, {
name: 'Waiting',
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
@ -127,7 +155,7 @@ export default Vue.extend({
},
},
series: [] as any,
colors: ['#00BCD4', '#FFEB3B', '#e53935'],
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
xaxis: {
type: 'numeric',
labels: {

View File

@ -43,11 +43,19 @@ export default define({
stats(stats) {
this.inChart.updateSeries([{
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
}]);
this.outChart.updateSeries([{
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
}, {
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
}]);
@ -81,11 +89,8 @@ export default define({
curve: 'straight',
width: 1
},
series: [{
data: [] as any
}, {
data: [] as any
}],
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
series: [{ data: [] }, { data: [] }, { data: [] }, { data: [] }] as any,
yaxis: {
min: 0,
}

View File

@ -1,5 +1,5 @@
import * as fs from 'fs';
import * as isSvg from 'is-svg';
import isSvg from 'is-svg';
export default function(path: string) {
try {

21
src/misc/convert-host.ts Normal file
View File

@ -0,0 +1,21 @@
import config from '../config';
import { toUnicode, toASCII } from 'punycode';
export function getFullApAccount(username: string, host: string) {
return host ? `${username}@${toApHost(host)}` : `${username}@${toApHost(config.host)}`;
}
export function isSelfHost(host: string) {
if (host == null) return true;
return toApHost(config.host) === toApHost(host);
}
export function toDbHost(host: string) {
if (host == null) return null;
return toUnicode(host.toLowerCase());
}
export function toApHost(host: string) {
if (host == null) return null;
return toASCII(host.toLowerCase());
}

View File

@ -8,7 +8,7 @@ import addFile from '../../../services/drive/add-file';
import User from '../../../models/user';
import dateFormat = require('dateformat');
import Blocking from '../../../models/blocking';
import config from '../../../config';
import { getFullApAccount } from '../../../misc/convert-host';
const logger = queueLogger.createSubLogger('export-blocking');
@ -56,7 +56,7 @@ export async function exportBlocking(job: Bull.Job, done: any): Promise<void> {
for (const block of blockings) {
const u = await User.findOne({ _id: block.blockeeId }, { fields: { username: true, host: true } });
const content = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
const content = getFullApAccount(u.username, u.host);
await new Promise((res, rej) => {
stream.write(content + '\n', err => {
if (err) {

View File

@ -8,7 +8,7 @@ import addFile from '../../../services/drive/add-file';
import User from '../../../models/user';
import dateFormat = require('dateformat');
import Following from '../../../models/following';
import config from '../../../config';
import { getFullApAccount } from '../../../misc/convert-host';
const logger = queueLogger.createSubLogger('export-following');
@ -56,7 +56,7 @@ export async function exportFollowing(job: Bull.Job, done: any): Promise<void> {
for (const following of followings) {
const u = await User.findOne({ _id: following.followeeId }, { fields: { username: true, host: true } });
const content = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
const content = getFullApAccount(u.username, u.host);
await new Promise((res, rej) => {
stream.write(content + '\n', err => {
if (err) {

View File

@ -8,7 +8,7 @@ import addFile from '../../../services/drive/add-file';
import User from '../../../models/user';
import dateFormat = require('dateformat');
import Mute from '../../../models/mute';
import config from '../../../config';
import { getFullApAccount } from '../../../misc/convert-host';
const logger = queueLogger.createSubLogger('export-mute');
@ -56,7 +56,7 @@ export async function exportMute(job: Bull.Job, done: any): Promise<void> {
for (const mute of mutes) {
const u = await User.findOne({ _id: mute.muteeId }, { fields: { username: true, host: true } });
const content = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
const content = getFullApAccount(u.username, u.host);
await new Promise((res, rej) => {
stream.write(content + '\n', err => {
if (err) {

View File

@ -7,8 +7,8 @@ import { queueLogger } from '../../logger';
import addFile from '../../../services/drive/add-file';
import User from '../../../models/user';
import dateFormat = require('dateformat');
import config from '../../../config';
import UserList from '../../../models/user-list';
import { getFullApAccount } from '../../../misc/convert-host';
const logger = queueLogger.createSubLogger('export-user-lists');
@ -46,7 +46,7 @@ export async function exportUserLists(job: Bull.Job, done: any): Promise<void> {
});
for (const u of users) {
const acct = u.host ? `${u.username}@${u.host}` : `${u.username}@${config.host}`;
const acct = getFullApAccount(u.username, u.host);
const content = `${list.title},${acct}`;
await new Promise((res, rej) => {
stream.write(content + '\n', err => {

View File

@ -3,13 +3,13 @@ import * as mongo from 'mongodb';
import { queueLogger } from '../../logger';
import User from '../../../models/user';
import config from '../../../config';
import follow from '../../../services/following/create';
import DriveFile from '../../../models/drive-file';
import { getOriginalUrl } from '../../../misc/get-drive-file-url';
import parseAcct from '../../../misc/acct/parse';
import resolveUser from '../../../remote/resolve-user';
import { downloadTextFile } from '../../../misc/download-text-file';
import { isSelfHost, toDbHost } from '../../../misc/convert-host';
const logger = queueLogger.createSubLogger('import-following');
@ -31,11 +31,11 @@ export async function importFollowing(job: Bull.Job, done: any): Promise<void> {
for (const line of csv.trim().split('\n')) {
const { username, host } = parseAcct(line.trim());
let target = host === config.host ? await User.findOne({
let target = isSelfHost(host) ? await User.findOne({
host: null,
usernameLower: username.toLowerCase()
}) : await User.findOne({
host: host,
host: toDbHost(host),
usernameLower: username.toLowerCase()
});

View File

@ -3,7 +3,6 @@ import * as mongo from 'mongodb';
import { queueLogger } from '../../logger';
import User from '../../../models/user';
import config from '../../../config';
import UserList from '../../../models/user-list';
import DriveFile from '../../../models/drive-file';
import { getOriginalUrl } from '../../../misc/get-drive-file-url';
@ -11,6 +10,7 @@ import parseAcct from '../../../misc/acct/parse';
import resolveUser from '../../../remote/resolve-user';
import { pushUserToUserList } from '../../../services/user-list/push';
import { downloadTextFile } from '../../../misc/download-text-file';
import { isSelfHost, toDbHost } from '../../../misc/convert-host';
const logger = queueLogger.createSubLogger('import-user-lists');
@ -47,11 +47,11 @@ export async function importUserLists(job: Bull.Job, done: any): Promise<void> {
});
}
let target = host === config.host ? await User.findOne({
let target = isSelfHost(host) ? await User.findOne({
host: null,
usernameLower: username.toLowerCase()
}) : await User.findOne({
host: host,
host: toDbHost(host),
usernameLower: username.toLowerCase()
});

View File

@ -116,7 +116,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
: [];
// リプライ
const reply = note.inReplyTo
const reply: INote = note.inReplyTo
? await resolveNote(note.inReplyTo, resolver).catch(e => {
// 4xxの場合はリプライしてないことにする
if (e.statusCode >= 400 && e.statusCode < 500) {