Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
0f65b1bcc5 | |||
a628821834 | |||
6ceff60c1e | |||
d762a6ce58 | |||
75a8037a46 | |||
1179920790 | |||
b323a160e3 | |||
b157e9535e | |||
7668475bd6 | |||
8bda2a1fb7 | |||
b092086b5b | |||
69a0d9034f | |||
f5be8fd313 | |||
7f835d7f76 | |||
ddbb7c5993 | |||
00a3fe39e8 | |||
7537fb88d4 | |||
a81bc71a1e | |||
0a0aa0e2db | |||
c56b94ae96 | |||
e90712706d | |||
eb0623331f | |||
d15bd59109 |
13
CHANGELOG.md
13
CHANGELOG.md
@ -5,6 +5,19 @@ If you encounter any problems with updating, please try the following:
|
|||||||
1. `npm run clean` or `npm run cleanall`
|
1. `npm run clean` or `npm run cleanall`
|
||||||
2. Retry update (Don't forget `npm i`)
|
2. Retry update (Don't forget `npm i`)
|
||||||
|
|
||||||
|
10.94.0
|
||||||
|
----------
|
||||||
|
* Faviconを設定できるように
|
||||||
|
* アカウントを凍結したときすべてのフォローを解除するように
|
||||||
|
* シェアページが機能していない問題を修正
|
||||||
|
* インスタンスブロックをしていてもRenote等すると取得されてしまう問題を修正
|
||||||
|
* デザインの調整
|
||||||
|
|
||||||
|
10.93.1
|
||||||
|
----------
|
||||||
|
* データのエクスポートとインポートの動作を修正
|
||||||
|
* デザインの調整
|
||||||
|
|
||||||
10.93.0
|
10.93.0
|
||||||
----------
|
----------
|
||||||
* フォローリストをインポートできるように
|
* フォローリストをインポートできるように
|
||||||
|
@ -14,6 +14,7 @@ const merge = (...args) => args.reduce((a, c) => ({
|
|||||||
}), {});
|
}), {});
|
||||||
|
|
||||||
const languages = [
|
const languages = [
|
||||||
|
'cs-CZ',
|
||||||
'de-DE',
|
'de-DE',
|
||||||
'en-US',
|
'en-US',
|
||||||
'es-ES',
|
'es-ES',
|
||||||
@ -24,9 +25,11 @@ const languages = [
|
|||||||
'nl-NL',
|
'nl-NL',
|
||||||
'pl-PL',
|
'pl-PL',
|
||||||
'zh-CN',
|
'zh-CN',
|
||||||
|
'zh-TW',
|
||||||
];
|
];
|
||||||
|
|
||||||
const primaries = {
|
const primaries = {
|
||||||
|
'en': 'US',
|
||||||
'ja': 'JP',
|
'ja': 'JP',
|
||||||
'zh': 'CN',
|
'zh': 'CN',
|
||||||
};
|
};
|
||||||
|
43
package.json
43
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.93.0",
|
"version": "10.94.0",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -46,7 +46,6 @@
|
|||||||
"@types/gulp-uglify": "3.0.6",
|
"@types/gulp-uglify": "3.0.6",
|
||||||
"@types/gulp-util": "3.0.34",
|
"@types/gulp-util": "3.0.34",
|
||||||
"@types/is-root": "1.0.0",
|
"@types/is-root": "1.0.0",
|
||||||
"@types/is-svg": "3.0.0",
|
|
||||||
"@types/is-url": "1.2.28",
|
"@types/is-url": "1.2.28",
|
||||||
"@types/js-yaml": "3.12.0",
|
"@types/js-yaml": "3.12.0",
|
||||||
"@types/jsdom": "12.2.3",
|
"@types/jsdom": "12.2.3",
|
||||||
@ -96,7 +95,7 @@
|
|||||||
"@types/websocket": "0.0.40",
|
"@types/websocket": "0.0.40",
|
||||||
"@types/ws": "6.0.1",
|
"@types/ws": "6.0.1",
|
||||||
"animejs": "3.0.1",
|
"animejs": "3.0.1",
|
||||||
"apexcharts": "3.5.0",
|
"apexcharts": "3.6.2",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autosize": "4.0.2",
|
"autosize": "4.0.2",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
@ -109,20 +108,20 @@
|
|||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"commander": "2.19.0",
|
"commander": "2.19.0",
|
||||||
"crc-32": "1.2.0",
|
"crc-32": "1.2.0",
|
||||||
"css-loader": "2.1.0",
|
"css-loader": "2.1.1",
|
||||||
"cssnano": "4.1.10",
|
"cssnano": "4.1.10",
|
||||||
"dateformat": "3.0.3",
|
"dateformat": "3.0.3",
|
||||||
"deep-equal": "1.0.1",
|
"deep-equal": "1.0.1",
|
||||||
"deepcopy": "0.6.3",
|
"deepcopy": "0.6.3",
|
||||||
"diskusage": "1.0.0",
|
"diskusage": "1.0.0",
|
||||||
"double-ended-queue": "2.1.0-0",
|
"double-ended-queue": "2.1.0-0",
|
||||||
"elasticsearch": "15.3.1",
|
"elasticsearch": "15.4.1",
|
||||||
"emojilib": "2.4.0",
|
"emojilib": "2.4.0",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eslint": "5.15.0",
|
"eslint": "5.15.1",
|
||||||
"eslint-plugin-vue": "5.2.2",
|
"eslint-plugin-vue": "5.2.2",
|
||||||
"eventemitter3": "3.1.0",
|
"eventemitter3": "3.1.0",
|
||||||
"feed": "2.0.2",
|
"feed": "2.0.4",
|
||||||
"file-type": "10.9.0",
|
"file-type": "10.9.0",
|
||||||
"fuckadblock": "3.2.1",
|
"fuckadblock": "3.2.1",
|
||||||
"gulp": "4.0.0",
|
"gulp": "4.0.0",
|
||||||
@ -131,20 +130,20 @@
|
|||||||
"gulp-mocha": "6.0.0",
|
"gulp-mocha": "6.0.0",
|
||||||
"gulp-rename": "1.4.0",
|
"gulp-rename": "1.4.0",
|
||||||
"gulp-replace": "1.0.0",
|
"gulp-replace": "1.0.0",
|
||||||
"gulp-sourcemaps": "2.6.4",
|
"gulp-sourcemaps": "2.6.5",
|
||||||
"gulp-stylus": "2.7.0",
|
"gulp-stylus": "2.7.0",
|
||||||
"gulp-tslint": "8.1.3",
|
"gulp-tslint": "8.1.4",
|
||||||
"gulp-typescript": "5.0.0",
|
"gulp-typescript": "5.0.0",
|
||||||
"gulp-uglify": "3.0.1",
|
"gulp-uglify": "3.0.2",
|
||||||
"gulp-util": "3.0.8",
|
"gulp-util": "3.0.8",
|
||||||
"hard-source-webpack-plugin": "0.13.1",
|
"hard-source-webpack-plugin": "0.13.1",
|
||||||
"html-minifier": "3.5.21",
|
"html-minifier": "3.5.21",
|
||||||
"http-signature": "1.2.0",
|
"http-signature": "1.2.0",
|
||||||
"insert-text-at-cursor": "0.1.2",
|
"insert-text-at-cursor": "0.1.2",
|
||||||
"is-root": "2.0.0",
|
"is-root": "2.0.0",
|
||||||
"is-svg": "3.0.0",
|
"is-svg": "4.0.0",
|
||||||
"js-yaml": "3.12.1",
|
"js-yaml": "3.12.2",
|
||||||
"jsdom": "13.2.0",
|
"jsdom": "14.0.0",
|
||||||
"json5": "2.1.0",
|
"json5": "2.1.0",
|
||||||
"json5-loader": "1.0.1",
|
"json5-loader": "1.0.1",
|
||||||
"katex": "0.10.1",
|
"katex": "0.10.1",
|
||||||
@ -189,13 +188,13 @@
|
|||||||
"pug": "2.0.3",
|
"pug": "2.0.3",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
"qrcode": "1.3.3",
|
"qrcode": "1.3.3",
|
||||||
"randomcolor": "0.5.3",
|
"randomcolor": "0.5.4",
|
||||||
"ratelimiter": "3.2.0",
|
"ratelimiter": "3.3.0",
|
||||||
"recaptcha-promise": "0.1.3",
|
"recaptcha-promise": "0.1.3",
|
||||||
"reconnecting-websocket": "4.1.10",
|
"reconnecting-websocket": "4.1.10",
|
||||||
"redis": "2.8.0",
|
"redis": "2.8.0",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"request-promise-native": "1.0.5",
|
"request-promise-native": "1.0.7",
|
||||||
"request-stats": "3.0.0",
|
"request-stats": "3.0.0",
|
||||||
"rimraf": "2.6.3",
|
"rimraf": "2.6.3",
|
||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
@ -210,14 +209,14 @@
|
|||||||
"stylus": "0.54.5",
|
"stylus": "0.54.5",
|
||||||
"stylus-loader": "3.0.2",
|
"stylus-loader": "3.0.2",
|
||||||
"summaly": "2.2.0",
|
"summaly": "2.2.0",
|
||||||
"systeminformation": "4.0.14",
|
"systeminformation": "4.0.16",
|
||||||
"syuilo-password-strength": "0.0.1",
|
"syuilo-password-strength": "0.0.1",
|
||||||
"terser-webpack-plugin": "1.2.3",
|
"terser-webpack-plugin": "1.2.3",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"tinycolor2": "1.4.1",
|
"tinycolor2": "1.4.1",
|
||||||
"tmp": "0.0.33",
|
"tmp": "0.0.33",
|
||||||
"ts-loader": "5.3.3",
|
"ts-loader": "5.3.3",
|
||||||
"ts-node": "8.0.2",
|
"ts-node": "8.0.3",
|
||||||
"tslint": "5.13.1",
|
"tslint": "5.13.1",
|
||||||
"tslint-sonarts": "1.9.0",
|
"tslint-sonarts": "1.9.0",
|
||||||
"typescript": "3.3.3333",
|
"typescript": "3.3.3333",
|
||||||
@ -232,7 +231,7 @@
|
|||||||
"vue-color": "2.7.0",
|
"vue-color": "2.7.0",
|
||||||
"vue-content-loading": "1.5.3",
|
"vue-content-loading": "1.5.3",
|
||||||
"vue-cropperjs": "3.0.0",
|
"vue-cropperjs": "3.0.0",
|
||||||
"vue-i18n": "8.8.2",
|
"vue-i18n": "8.9.0",
|
||||||
"vue-js-modal": "1.3.28",
|
"vue-js-modal": "1.3.28",
|
||||||
"vue-json-pretty": "1.4.1",
|
"vue-json-pretty": "1.4.1",
|
||||||
"vue-loader": "15.7.0",
|
"vue-loader": "15.7.0",
|
||||||
@ -241,9 +240,9 @@
|
|||||||
"vue-router": "3.0.2",
|
"vue-router": "3.0.2",
|
||||||
"vue-sequential-entrance": "1.1.3",
|
"vue-sequential-entrance": "1.1.3",
|
||||||
"vue-style-loader": "4.1.2",
|
"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",
|
"vue-template-compiler": "2.6.8",
|
||||||
"vuedraggable": "2.18.1",
|
"vuedraggable": "2.19.2",
|
||||||
"vuewordcloud": "18.7.11",
|
"vuewordcloud": "18.7.11",
|
||||||
"vuex": "3.1.0",
|
"vuex": "3.1.0",
|
||||||
"vuex-persistedstate": "2.5.4",
|
"vuex-persistedstate": "2.5.4",
|
||||||
@ -252,7 +251,7 @@
|
|||||||
"webpack": "4.28.4",
|
"webpack": "4.28.4",
|
||||||
"webpack-cli": "3.2.3",
|
"webpack-cli": "3.2.3",
|
||||||
"websocket": "1.0.28",
|
"websocket": "1.0.28",
|
||||||
"ws": "6.1.4",
|
"ws": "6.2.0",
|
||||||
"xev": "2.0.1"
|
"xev": "2.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
196
src/client/app/admin/views/dashboard.queue-charts.vue
Normal file
196
src/client/app/admin/views/dashboard.queue-charts.vue
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<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';
|
||||||
|
|
||||||
|
const limit = 150;
|
||||||
|
|
||||||
|
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: limit
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$once('hook:beforeDestroy', () => {
|
||||||
|
connection.dispose();
|
||||||
|
this.inChart.destroy();
|
||||||
|
this.outChart.destroy();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onStats(stats) {
|
||||||
|
this.stats.push(stats);
|
||||||
|
if (this.stats.length > limit) 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>
|
@ -73,6 +73,10 @@
|
|||||||
<x-charts ref="charts"/>
|
<x-charts ref="charts"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="queue">
|
||||||
|
<x-queue/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="cpu-memory">
|
<div class="cpu-memory">
|
||||||
<x-cpu-memory :connection="connection"/>
|
<x-cpu-memory :connection="connection"/>
|
||||||
</div>
|
</div>
|
||||||
@ -86,9 +90,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import XCpuMemory from "./cpu-memory.vue";
|
import XCpuMemory from "./dashboard.cpu-memory.vue";
|
||||||
import XCharts from "./charts.vue";
|
import XQueue from "./dashboard.queue-charts.vue";
|
||||||
import XApLog from "./ap-log.vue";
|
import XCharts from "./dashboard.charts.vue";
|
||||||
|
import XApLog from "./dashboard.ap-log.vue";
|
||||||
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||||
import MarqueeText from 'vue-marquee-text-component';
|
import MarqueeText from 'vue-marquee-text-component';
|
||||||
import randomColor from 'randomcolor';
|
import randomColor from 'randomcolor';
|
||||||
@ -98,6 +103,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
components: {
|
components: {
|
||||||
XCpuMemory,
|
XCpuMemory,
|
||||||
|
XQueue,
|
||||||
XCharts,
|
XCharts,
|
||||||
XApLog,
|
XApLog,
|
||||||
MarqueeText
|
MarqueeText
|
||||||
@ -274,6 +280,9 @@ export default Vue.extend({
|
|||||||
> .charts
|
> .charts
|
||||||
margin-bottom 16px
|
margin-bottom 16px
|
||||||
|
|
||||||
|
> .queue
|
||||||
|
margin-bottom 16px
|
||||||
|
|
||||||
> .cpu-memory
|
> .cpu-memory
|
||||||
margin-bottom 16px
|
margin-bottom 16px
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<ui-input :value="host" readonly>{{ $t('host') }}</ui-input>
|
<ui-input :value="host" readonly>{{ $t('host') }}</ui-input>
|
||||||
<ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
|
<ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
|
||||||
<ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
|
<ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
|
||||||
|
<ui-input v-model="iconUrl"><template #icon><fa icon="link"/></template>{{ $t('icon-url') }}</ui-input>
|
||||||
<ui-input v-model="mascotImageUrl"><template #icon><fa icon="link"/></template>{{ $t('logo-url') }}</ui-input>
|
<ui-input v-model="mascotImageUrl"><template #icon><fa icon="link"/></template>{{ $t('logo-url') }}</ui-input>
|
||||||
<ui-input v-model="bannerUrl"><template #icon><fa icon="link"/></template>{{ $t('banner-url') }}</ui-input>
|
<ui-input v-model="bannerUrl"><template #icon><fa icon="link"/></template>{{ $t('banner-url') }}</ui-input>
|
||||||
<ui-input v-model="errorImageUrl"><template #icon><fa icon="link"/></template>{{ $t('error-image-url') }}</ui-input>
|
<ui-input v-model="errorImageUrl"><template #icon><fa icon="link"/></template>{{ $t('error-image-url') }}</ui-input>
|
||||||
@ -157,6 +158,7 @@ export default Vue.extend({
|
|||||||
mascotImageUrl: null,
|
mascotImageUrl: null,
|
||||||
bannerUrl: null,
|
bannerUrl: null,
|
||||||
errorImageUrl: null,
|
errorImageUrl: null,
|
||||||
|
iconUrl: null,
|
||||||
name: null,
|
name: null,
|
||||||
description: null,
|
description: null,
|
||||||
languages: null,
|
languages: null,
|
||||||
@ -207,6 +209,7 @@ export default Vue.extend({
|
|||||||
this.mascotImageUrl = meta.mascotImageUrl;
|
this.mascotImageUrl = meta.mascotImageUrl;
|
||||||
this.bannerUrl = meta.bannerUrl;
|
this.bannerUrl = meta.bannerUrl;
|
||||||
this.errorImageUrl = meta.errorImageUrl;
|
this.errorImageUrl = meta.errorImageUrl;
|
||||||
|
this.iconUrl = meta.iconUrl;
|
||||||
this.name = meta.name;
|
this.name = meta.name;
|
||||||
this.description = meta.description;
|
this.description = meta.description;
|
||||||
this.languages = meta.langs.join(' ');
|
this.languages = meta.langs.join(' ');
|
||||||
@ -267,6 +270,7 @@ export default Vue.extend({
|
|||||||
mascotImageUrl: this.mascotImageUrl,
|
mascotImageUrl: this.mascotImageUrl,
|
||||||
bannerUrl: this.bannerUrl,
|
bannerUrl: this.bannerUrl,
|
||||||
errorImageUrl: this.errorImageUrl,
|
errorImageUrl: this.errorImageUrl,
|
||||||
|
iconImageUrl: this.iconImageUrl,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
langs: this.languages.split(' '),
|
langs: this.languages.split(' '),
|
||||||
|
@ -5,14 +5,25 @@
|
|||||||
<section class="wptihjuy">
|
<section class="wptihjuy">
|
||||||
<header><fa :icon="faPaperPlane"/> Deliver</header>
|
<header><fa :icon="faPaperPlane"/> Deliver</header>
|
||||||
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
||||||
<ui-input :value="latestStats.deliver.waiting | number" type="text" readonly>
|
<ui-input :value="latestStats.deliver.activeSincePrevTick | number" type="text" readonly>
|
||||||
<span>Waiting</span>
|
<span>Process</span>
|
||||||
</ui-input>
|
<template #prefix><fa :icon="fasPlayCircle"/></template>
|
||||||
<ui-input :value="latestStats.deliver.delayed | number" type="text" readonly>
|
<template #suffix>jobs/s</template>
|
||||||
<span>Delayed</span>
|
|
||||||
</ui-input>
|
</ui-input>
|
||||||
<ui-input :value="latestStats.deliver.active | number" type="text" readonly>
|
<ui-input :value="latestStats.deliver.active | number" type="text" readonly>
|
||||||
<span>Active</span>
|
<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-input>
|
||||||
</ui-horizon-group>
|
</ui-horizon-group>
|
||||||
<div ref="deliverChart" class="chart"></div>
|
<div ref="deliverChart" class="chart"></div>
|
||||||
@ -20,14 +31,25 @@
|
|||||||
<section class="wptihjuy">
|
<section class="wptihjuy">
|
||||||
<header><fa :icon="faInbox"/> Inbox</header>
|
<header><fa :icon="faInbox"/> Inbox</header>
|
||||||
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
<ui-horizon-group inputs v-if="latestStats" class="fit-bottom">
|
||||||
<ui-input :value="latestStats.inbox.waiting | number" type="text" readonly>
|
<ui-input :value="latestStats.inbox.activeSincePrevTick | number" type="text" readonly>
|
||||||
<span>Waiting</span>
|
<span>Process</span>
|
||||||
</ui-input>
|
<template #prefix><fa :icon="fasPlayCircle"/></template>
|
||||||
<ui-input :value="latestStats.inbox.delayed | number" type="text" readonly>
|
<template #suffix>jobs/s</template>
|
||||||
<span>Delayed</span>
|
|
||||||
</ui-input>
|
</ui-input>
|
||||||
<ui-input :value="latestStats.inbox.active | number" type="text" readonly>
|
<ui-input :value="latestStats.inbox.active | number" type="text" readonly>
|
||||||
<span>Active</span>
|
<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-input>
|
||||||
</ui-horizon-group>
|
</ui-horizon-group>
|
||||||
<div ref="inboxChart" class="chart"></div>
|
<div ref="inboxChart" class="chart"></div>
|
||||||
@ -44,8 +66,10 @@ import Vue from 'vue';
|
|||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import ApexCharts from 'apexcharts';
|
import ApexCharts from 'apexcharts';
|
||||||
import * as tinycolor from 'tinycolor2';
|
import * as tinycolor from 'tinycolor2';
|
||||||
import { faTasks, faInbox } from '@fortawesome/free-solid-svg-icons';
|
import { faTasks, faInbox, faStopwatch, faPlayCircle as fasPlayCircle } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
|
import { faPaperPlane, faStopCircle, faPlayCircle as farPlayCircle } from '@fortawesome/free-regular-svg-icons';
|
||||||
|
|
||||||
|
const limit = 150;
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('admin/views/queue.vue'),
|
i18n: i18n('admin/views/queue.vue'),
|
||||||
@ -55,7 +79,7 @@ export default Vue.extend({
|
|||||||
stats: [],
|
stats: [],
|
||||||
deliverChart: null,
|
deliverChart: null,
|
||||||
inboxChart: null,
|
inboxChart: null,
|
||||||
faTasks, faPaperPlane, faInbox
|
faTasks, faPaperPlane, faInbox, faStopwatch, faStopCircle, farPlayCircle, fasPlayCircle
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -68,23 +92,37 @@ export default Vue.extend({
|
|||||||
watch: {
|
watch: {
|
||||||
stats(stats) {
|
stats(stats) {
|
||||||
this.inboxChart.updateSeries([{
|
this.inboxChart.updateSeries([{
|
||||||
name: 'Active',
|
name: 'Process',
|
||||||
|
type: 'area',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
|
||||||
|
}, {
|
||||||
|
name: 'Active',
|
||||||
|
type: 'area',
|
||||||
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
|
||||||
}, {
|
}, {
|
||||||
name: 'Waiting',
|
name: 'Waiting',
|
||||||
|
type: 'line',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
|
||||||
}, {
|
}, {
|
||||||
name: 'Delayed',
|
name: 'Delayed',
|
||||||
|
type: 'line',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
|
||||||
}]);
|
}]);
|
||||||
this.deliverChart.updateSeries([{
|
this.deliverChart.updateSeries([{
|
||||||
name: 'Active',
|
name: 'Process',
|
||||||
|
type: 'area',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
|
||||||
|
}, {
|
||||||
|
name: 'Active',
|
||||||
|
type: 'area',
|
||||||
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
|
||||||
}, {
|
}, {
|
||||||
name: 'Waiting',
|
name: 'Waiting',
|
||||||
|
type: 'line',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
|
||||||
}, {
|
}, {
|
||||||
name: 'Delayed',
|
name: 'Delayed',
|
||||||
|
type: 'line',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
@ -127,7 +165,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
series: [] as any,
|
series: [] as any,
|
||||||
colors: ['#00BCD4', '#FFEB3B', '#e53935'],
|
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
|
||||||
xaxis: {
|
xaxis: {
|
||||||
type: 'numeric',
|
type: 'numeric',
|
||||||
labels: {
|
labels: {
|
||||||
@ -154,7 +192,7 @@ export default Vue.extend({
|
|||||||
connection.on('statsLog', this.onStatsLog);
|
connection.on('statsLog', this.onStatsLog);
|
||||||
connection.send('requestLog', {
|
connection.send('requestLog', {
|
||||||
id: Math.random().toString().substr(2, 8),
|
id: Math.random().toString().substr(2, 8),
|
||||||
length: 100
|
length: limit
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$once('hook:beforeDestroy', () => {
|
this.$once('hook:beforeDestroy', () => {
|
||||||
@ -184,7 +222,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
onStats(stats) {
|
onStats(stats) {
|
||||||
this.stats.push(stats);
|
this.stats.push(stats);
|
||||||
if (this.stats.length > 100) this.stats.shift();
|
if (this.stats.length > limit) this.stats.shift();
|
||||||
},
|
},
|
||||||
|
|
||||||
onStatsLog(statsLog) {
|
onStatsLog(statsLog) {
|
||||||
|
@ -366,9 +366,6 @@ root(fill)
|
|||||||
&[type='file']
|
&[type='file']
|
||||||
display none
|
display none
|
||||||
|
|
||||||
&[type='number']
|
|
||||||
text-align right
|
|
||||||
|
|
||||||
> .prefix
|
> .prefix
|
||||||
> .suffix
|
> .suffix
|
||||||
display block
|
display block
|
||||||
|
@ -28,10 +28,10 @@ export default Vue.extend({
|
|||||||
computed: {
|
computed: {
|
||||||
template(): string {
|
template(): string {
|
||||||
let t = '';
|
let t = '';
|
||||||
if (this.title && this.url) t += `【[${title}](${url})】\n`;
|
if (this.title && this.url) t += `【[${this.title}](${this.url})】\n`;
|
||||||
if (this.title && !this.url) t += `【${title}】\n`;
|
if (this.title && !this.url) t += `【${this.title}】\n`;
|
||||||
if (this.text) t += `${text}\n`;
|
if (this.text) t += `${this.text}\n`;
|
||||||
if (!this.title && this.url) t += `${url}`;
|
if (!this.title && this.url) t += `${this.url}`;
|
||||||
return t.trim();
|
return t.trim();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -42,13 +42,29 @@ export default define({
|
|||||||
watch: {
|
watch: {
|
||||||
stats(stats) {
|
stats(stats) {
|
||||||
this.inChart.updateSeries([{
|
this.inChart.updateSeries([{
|
||||||
|
type: 'area',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.activeSincePrevTick }))
|
||||||
}, {
|
}, {
|
||||||
|
type: 'area',
|
||||||
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.active }))
|
||||||
|
}, {
|
||||||
|
type: 'line',
|
||||||
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.waiting }))
|
||||||
|
}, {
|
||||||
|
type: 'line',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
|
data: stats.map((x, i) => ({ x: i, y: x.inbox.delayed }))
|
||||||
}]);
|
}]);
|
||||||
this.outChart.updateSeries([{
|
this.outChart.updateSeries([{
|
||||||
|
type: 'area',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.activeSincePrevTick }))
|
||||||
}, {
|
}, {
|
||||||
|
type: 'area',
|
||||||
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.active }))
|
||||||
|
}, {
|
||||||
|
type: 'line',
|
||||||
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.waiting }))
|
||||||
|
}, {
|
||||||
|
type: 'line',
|
||||||
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
|
data: stats.map((x, i) => ({ x: i, y: x.deliver.delayed }))
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
@ -81,11 +97,8 @@ export default define({
|
|||||||
curve: 'straight',
|
curve: 'straight',
|
||||||
width: 1
|
width: 1
|
||||||
},
|
},
|
||||||
series: [{
|
colors: ['#00E396', '#00BCD4', '#FFB300', '#e53935'],
|
||||||
data: [] as any
|
series: [{ data: [] }, { data: [] }, { data: [] }, { data: [] }] as any,
|
||||||
}, {
|
|
||||||
data: [] as any
|
|
||||||
}],
|
|
||||||
yaxis: {
|
yaxis: {
|
||||||
min: 0,
|
min: 0,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as isSvg from 'is-svg';
|
import isSvg from 'is-svg';
|
||||||
|
|
||||||
export default function(path: string) {
|
export default function(path: string) {
|
||||||
try {
|
try {
|
||||||
|
27
src/misc/convert-host.ts
Normal file
27
src/misc/convert-host.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import config from '../config';
|
||||||
|
import { toUnicode, toASCII } from 'punycode';
|
||||||
|
import { URL } from 'url';
|
||||||
|
|
||||||
|
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 extractDbHost(uri: string) {
|
||||||
|
const url = new URL(uri);
|
||||||
|
return toDbHost(url.hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
@ -198,6 +198,7 @@ export type IMeta = {
|
|||||||
mascotImageUrl?: string;
|
mascotImageUrl?: string;
|
||||||
bannerUrl?: string;
|
bannerUrl?: string;
|
||||||
errorImageUrl?: string;
|
errorImageUrl?: string;
|
||||||
|
iconUrl?: string;
|
||||||
|
|
||||||
cacheRemoteFiles?: boolean;
|
cacheRemoteFiles?: boolean;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import addFile from '../../../services/drive/add-file';
|
|||||||
import User from '../../../models/user';
|
import User from '../../../models/user';
|
||||||
import dateFormat = require('dateformat');
|
import dateFormat = require('dateformat');
|
||||||
import Blocking from '../../../models/blocking';
|
import Blocking from '../../../models/blocking';
|
||||||
import config from '../../../config';
|
import { getFullApAccount } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-blocking');
|
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) {
|
for (const block of blockings) {
|
||||||
const u = await User.findOne({ _id: block.blockeeId }, { fields: { username: true, host: true } });
|
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) => {
|
await new Promise((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -8,7 +8,7 @@ import addFile from '../../../services/drive/add-file';
|
|||||||
import User from '../../../models/user';
|
import User from '../../../models/user';
|
||||||
import dateFormat = require('dateformat');
|
import dateFormat = require('dateformat');
|
||||||
import Following from '../../../models/following';
|
import Following from '../../../models/following';
|
||||||
import config from '../../../config';
|
import { getFullApAccount } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-following');
|
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) {
|
for (const following of followings) {
|
||||||
const u = await User.findOne({ _id: following.followeeId }, { fields: { username: true, host: true } });
|
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) => {
|
await new Promise((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -8,7 +8,7 @@ import addFile from '../../../services/drive/add-file';
|
|||||||
import User from '../../../models/user';
|
import User from '../../../models/user';
|
||||||
import dateFormat = require('dateformat');
|
import dateFormat = require('dateformat');
|
||||||
import Mute from '../../../models/mute';
|
import Mute from '../../../models/mute';
|
||||||
import config from '../../../config';
|
import { getFullApAccount } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-mute');
|
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) {
|
for (const mute of mutes) {
|
||||||
const u = await User.findOne({ _id: mute.muteeId }, { fields: { username: true, host: true } });
|
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) => {
|
await new Promise((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -7,8 +7,8 @@ import { queueLogger } from '../../logger';
|
|||||||
import addFile from '../../../services/drive/add-file';
|
import addFile from '../../../services/drive/add-file';
|
||||||
import User from '../../../models/user';
|
import User from '../../../models/user';
|
||||||
import dateFormat = require('dateformat');
|
import dateFormat = require('dateformat');
|
||||||
import config from '../../../config';
|
|
||||||
import UserList from '../../../models/user-list';
|
import UserList from '../../../models/user-list';
|
||||||
|
import { getFullApAccount } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-user-lists');
|
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) {
|
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}`;
|
const content = `${list.title},${acct}`;
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
|
@ -3,13 +3,13 @@ import * as mongo from 'mongodb';
|
|||||||
|
|
||||||
import { queueLogger } from '../../logger';
|
import { queueLogger } from '../../logger';
|
||||||
import User from '../../../models/user';
|
import User from '../../../models/user';
|
||||||
import config from '../../../config';
|
|
||||||
import follow from '../../../services/following/create';
|
import follow from '../../../services/following/create';
|
||||||
import DriveFile from '../../../models/drive-file';
|
import DriveFile from '../../../models/drive-file';
|
||||||
import { getOriginalUrl } from '../../../misc/get-drive-file-url';
|
import { getOriginalUrl } from '../../../misc/get-drive-file-url';
|
||||||
import parseAcct from '../../../misc/acct/parse';
|
import parseAcct from '../../../misc/acct/parse';
|
||||||
import resolveUser from '../../../remote/resolve-user';
|
import resolveUser from '../../../remote/resolve-user';
|
||||||
import { downloadTextFile } from '../../../misc/download-text-file';
|
import { downloadTextFile } from '../../../misc/download-text-file';
|
||||||
|
import { isSelfHost, toDbHost } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('import-following');
|
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')) {
|
for (const line of csv.trim().split('\n')) {
|
||||||
const { username, host } = parseAcct(line.trim());
|
const { username, host } = parseAcct(line.trim());
|
||||||
|
|
||||||
let target = host === config.host ? await User.findOne({
|
let target = isSelfHost(host) ? await User.findOne({
|
||||||
host: null,
|
host: null,
|
||||||
usernameLower: username.toLowerCase()
|
usernameLower: username.toLowerCase()
|
||||||
}) : await User.findOne({
|
}) : await User.findOne({
|
||||||
host: host,
|
host: toDbHost(host),
|
||||||
usernameLower: username.toLowerCase()
|
usernameLower: username.toLowerCase()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import * as mongo from 'mongodb';
|
|||||||
|
|
||||||
import { queueLogger } from '../../logger';
|
import { queueLogger } from '../../logger';
|
||||||
import User from '../../../models/user';
|
import User from '../../../models/user';
|
||||||
import config from '../../../config';
|
|
||||||
import UserList from '../../../models/user-list';
|
import UserList from '../../../models/user-list';
|
||||||
import DriveFile from '../../../models/drive-file';
|
import DriveFile from '../../../models/drive-file';
|
||||||
import { getOriginalUrl } from '../../../misc/get-drive-file-url';
|
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 resolveUser from '../../../remote/resolve-user';
|
||||||
import { pushUserToUserList } from '../../../services/user-list/push';
|
import { pushUserToUserList } from '../../../services/user-list/push';
|
||||||
import { downloadTextFile } from '../../../misc/download-text-file';
|
import { downloadTextFile } from '../../../misc/download-text-file';
|
||||||
|
import { isSelfHost, toDbHost } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('import-user-lists');
|
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,
|
host: null,
|
||||||
usernameLower: username.toLowerCase()
|
usernameLower: username.toLowerCase()
|
||||||
}) : await User.findOne({
|
}) : await User.findOne({
|
||||||
host: host,
|
host: toDbHost(host),
|
||||||
usernameLower: username.toLowerCase()
|
usernameLower: username.toLowerCase()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ import { IAnnounce, INote } from '../../type';
|
|||||||
import { fetchNote, resolveNote } from '../../models/note';
|
import { fetchNote, resolveNote } from '../../models/note';
|
||||||
import { resolvePerson } from '../../models/person';
|
import { resolvePerson } from '../../models/person';
|
||||||
import { apLogger } from '../../logger';
|
import { apLogger } from '../../logger';
|
||||||
|
import { extractDbHost } from '../../../../misc/convert-host';
|
||||||
|
import Instance from '../../../../models/instance';
|
||||||
|
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
@ -23,6 +25,11 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity:
|
|||||||
throw new Error('invalid announce');
|
throw new Error('invalid announce');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// アナウンス先をブロックしてたら中断
|
||||||
|
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||||
|
const instance = await Instance.findOne({ host: extractDbHost(uri) });
|
||||||
|
if (instance && instance.isBlocked) return;
|
||||||
|
|
||||||
// 既に同じURIを持つものが登録されていないかチェック
|
// 既に同じURIを持つものが登録されていないかチェック
|
||||||
const exist = await fetchNote(uri);
|
const exist = await fetchNote(uri);
|
||||||
if (exist) {
|
if (exist) {
|
||||||
|
@ -19,6 +19,8 @@ import vote from '../../../services/note/polls/vote';
|
|||||||
import { apLogger } from '../logger';
|
import { apLogger } from '../logger';
|
||||||
import { IDriveFile } from '../../../models/drive-file';
|
import { IDriveFile } from '../../../models/drive-file';
|
||||||
import { deliverQuestionUpdate } from '../../../services/note/polls/update';
|
import { deliverQuestionUpdate } from '../../../services/note/polls/update';
|
||||||
|
import Instance from '../../../models/instance';
|
||||||
|
import { extractDbHost } from '../../../misc/convert-host';
|
||||||
|
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
@ -116,7 +118,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 => {
|
? await resolveNote(note.inReplyTo, resolver).catch(e => {
|
||||||
// 4xxの場合はリプライしてないことにする
|
// 4xxの場合はリプライしてないことにする
|
||||||
if (e.statusCode >= 400 && e.statusCode < 500) {
|
if (e.statusCode >= 400 && e.statusCode < 500) {
|
||||||
@ -132,7 +134,15 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
|
|||||||
let quote: INote;
|
let quote: INote;
|
||||||
|
|
||||||
if (note._misskey_quote && typeof note._misskey_quote == 'string') {
|
if (note._misskey_quote && typeof note._misskey_quote == 'string') {
|
||||||
quote = await resolveNote(note._misskey_quote).catch(() => null);
|
quote = await resolveNote(note._misskey_quote).catch(e => {
|
||||||
|
// 4xxの場合は引用してないことにする
|
||||||
|
if (e.statusCode >= 400 && e.statusCode < 500) {
|
||||||
|
logger.warn(`Ignored quote target ${note.inReplyTo} - ${e.statusCode} `);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
logger.warn(`Error in quote target ${note.inReplyTo} - ${e.statusCode || e}`);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const cw = note.summary === '' ? null : note.summary;
|
const cw = note.summary === '' ? null : note.summary;
|
||||||
@ -214,6 +224,11 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
|
|||||||
export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<INote> {
|
export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<INote> {
|
||||||
const uri = typeof value == 'string' ? value : value.id;
|
const uri = typeof value == 'string' ? value : value.id;
|
||||||
|
|
||||||
|
// ブロックしてたら中断
|
||||||
|
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
|
||||||
|
const instance = await Instance.findOne({ host: extractDbHost(uri) });
|
||||||
|
if (instance && instance.isBlocked) throw { statusCode: 451 };
|
||||||
|
|
||||||
//#region このサーバーに既に登録されていたらそれを返す
|
//#region このサーバーに既に登録されていたらそれを返す
|
||||||
const exist = await fetchNote(uri);
|
const exist = await fetchNote(uri);
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import Logger from "../services/logger";
|
import Logger from '../services/logger';
|
||||||
|
|
||||||
export const remoteLogger = new Logger('remote', 'cyan');
|
export const remoteLogger = new Logger('remote', 'cyan');
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import $ from 'cafy';
|
import $ from 'cafy';
|
||||||
import ID, { transform } from '../../../../misc/cafy-id';
|
import ID, { transform } from '../../../../misc/cafy-id';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import User from '../../../../models/user';
|
import User, { IUser } from '../../../../models/user';
|
||||||
|
import Following from '../../../../models/following';
|
||||||
|
import deleteFollowing from '../../../../services/following/delete';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
@ -51,5 +53,25 @@ export default define(meta, async (ps) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
unFollowAll(user);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function unFollowAll(follower: IUser) {
|
||||||
|
const followings = await Following.find({
|
||||||
|
followerId: follower._id
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const following of followings) {
|
||||||
|
const followee = await User.findOne({
|
||||||
|
_id: following.followeeId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (followee == null) {
|
||||||
|
throw `Cant find followee ${following.followeeId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteFollowing(follower, followee, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -69,6 +69,13 @@ export const meta = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
iconUrl: {
|
||||||
|
validator: $.optional.nullable.str,
|
||||||
|
desc: {
|
||||||
|
'ja-JP': 'インスタンスのアイコンURL'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
name: {
|
name: {
|
||||||
validator: $.optional.nullable.str,
|
validator: $.optional.nullable.str,
|
||||||
desc: {
|
desc: {
|
||||||
@ -356,6 +363,10 @@ export default define(meta, async (ps) => {
|
|||||||
set.bannerUrl = ps.bannerUrl;
|
set.bannerUrl = ps.bannerUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.iconUrl !== undefined) {
|
||||||
|
set.iconUrl = ps.iconUrl;
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.name !== undefined) {
|
if (ps.name !== undefined) {
|
||||||
set.name = ps.name;
|
set.name = ps.name;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ import Note, { pack as packNote, INote } from '../../../../models/note';
|
|||||||
import { createNote } from '../../../../remote/activitypub/models/note';
|
import { createNote } from '../../../../remote/activitypub/models/note';
|
||||||
import Resolver from '../../../../remote/activitypub/resolver';
|
import Resolver from '../../../../remote/activitypub/resolver';
|
||||||
import { ApiError } from '../../error';
|
import { ApiError } from '../../error';
|
||||||
|
import Instance from '../../../../models/instance';
|
||||||
|
import { extractDbHost } from '../../../../misc/convert-host';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['federation'],
|
tags: ['federation'],
|
||||||
@ -61,6 +63,10 @@ async function fetchAny(uri: string) {
|
|||||||
if (packed !== null) return packed;
|
if (packed !== null) return packed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ブロックしてたら中断
|
||||||
|
const instance = await Instance.findOne({ host: extractDbHost(uri) });
|
||||||
|
if (instance && instance.isBlocked) return null;
|
||||||
|
|
||||||
// URI(AP Object id)としてDB検索
|
// URI(AP Object id)としてDB検索
|
||||||
{
|
{
|
||||||
const [user, note] = await Promise.all([
|
const [user, note] = await Promise.all([
|
||||||
|
@ -250,7 +250,10 @@ router.get('/reversi', async ctx => ctx.redirect(override(ctx.URL.pathname, 'gam
|
|||||||
router.get('*', async ctx => {
|
router.get('*', async ctx => {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta();
|
||||||
await ctx.render('base', {
|
await ctx.render('base', {
|
||||||
img: meta.bannerUrl
|
img: meta.bannerUrl,
|
||||||
|
title: meta.name,
|
||||||
|
desc: meta.description,
|
||||||
|
icon: meta.iconUrl
|
||||||
});
|
});
|
||||||
ctx.set('Cache-Control', 'public, max-age=300');
|
ctx.set('Cache-Control', 'public, max-age=300');
|
||||||
});
|
});
|
||||||
|
@ -8,17 +8,18 @@ html
|
|||||||
|
|
||||||
head
|
head
|
||||||
meta(charset='utf-8')
|
meta(charset='utf-8')
|
||||||
meta(name='application-name' content='Misskey')
|
meta(name='application-name' content= title || 'Misskey')
|
||||||
meta(name='referrer' content='origin')
|
meta(name='referrer' content='origin')
|
||||||
meta(property='og:site_name' content='Misskey')
|
meta(property='og:site_name' content= title || 'Misskey')
|
||||||
|
link(rel='icon' href= icon || '/favicon.ico')
|
||||||
link(rel='manifest' href='/manifest.json')
|
link(rel='manifest' href='/manifest.json')
|
||||||
|
|
||||||
title
|
title
|
||||||
block title
|
block title
|
||||||
| Misskey
|
= title || 'Misskey'
|
||||||
|
|
||||||
block desc
|
block desc
|
||||||
meta(name='description' content='✨🌎✨ A federated blogging platform ✨🚀✨')
|
meta(name='description' content= desc || '✨🌎✨ A federated blogging platform ✨🚀✨')
|
||||||
|
|
||||||
block meta
|
block meta
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import instanceChart from '../../services/chart/instance';
|
|||||||
|
|
||||||
const logger = new Logger('following/delete');
|
const logger = new Logger('following/delete');
|
||||||
|
|
||||||
export default async function(follower: IUser, followee: IUser) {
|
export default async function(follower: IUser, followee: IUser, silent = false) {
|
||||||
const following = await Following.findOne({
|
const following = await Following.findOne({
|
||||||
followerId: follower._id,
|
followerId: follower._id,
|
||||||
followeeId: followee._id
|
followeeId: followee._id
|
||||||
@ -71,7 +71,7 @@ export default async function(follower: IUser, followee: IUser) {
|
|||||||
perUserFollowingChart.update(follower, followee, false);
|
perUserFollowingChart.update(follower, followee, false);
|
||||||
|
|
||||||
// Publish unfollow event
|
// Publish unfollow event
|
||||||
if (isLocalUser(follower)) {
|
if (!silent && isLocalUser(follower)) {
|
||||||
packUser(followee, follower, {
|
packUser(followee, follower, {
|
||||||
detail: true
|
detail: true
|
||||||
}).then(packed => publishMainStream(follower._id, 'unfollow', packed));
|
}).then(packed => publishMainStream(follower._id, 'unfollow', packed));
|
||||||
|
Reference in New Issue
Block a user