attempt fix

This commit is contained in:
ThatOneCalculator 2023-02-13 19:48:57 -08:00
parent fe7cb29d78
commit 0e9f9063b4
2 changed files with 33 additions and 538 deletions

View File

@ -304,7 +304,7 @@ export function apiAccountMastodon(router: Router): void {
const client = getClient(BASE_URL, accessTokens); const client = getClient(BASE_URL, accessTokens);
let users; let users;
try { try {
const idsRaw = ctx.request.body?["id[]"] : null; const idsRaw = ctx.request.body ? ["id[]"] : null;
const ids = typeof idsRaw === "string" ? [idsRaw] : idsRaw; const ids = typeof idsRaw === "string" ? [idsRaw] : idsRaw;
users = ids; users = ids;
relationshopModel.id = idsRaw?.toString() || "1"; relationshopModel.id = idsRaw?.toString() || "1";

View File

@ -1,260 +1,84 @@
<template> <template>
<MkSpacer :content-max="900"> <MkSpacer :content-max="1000">
<div ref="rootEl" v-size="{ max: [740] }" class="edbbcaef"> <div ref="rootEl" class="edbbcaef">
<div class="left"> <MkFoldableSection class="item">
<div v-if="stats" class="container stats">
<div class="title">Stats</div>
<div class="body">
<div class="number _panel">
<div class="label">Users</div>
<div class="value _monospace">
{{ number(stats.originalUsersCount) }}
<MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
<div class="number _panel">
<div class="label">Notes</div>
<div class="value _monospace">
{{ number(stats.originalNotesCount) }}
<MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
</div>
</div>
<div class="container queue">
<div class="title">Job queue</div>
<div class="body">
<div class="chart deliver">
<div class="title">Deliver</div>
<XQueueChart :connection="queueStatsConnection" domain="deliver"/>
</div>
<div class="chart inbox">
<div class="title">Inbox</div>
<XQueueChart :connection="queueStatsConnection" domain="inbox"/>
</div>
</div>
</div>
<div class="container users">
<div class="title">New users</div>
<div v-if="newUsers" class="body">
<XUser v-for="user in newUsers" :key="user.id" class="user" :user="user"/>
</div>
</div>
<div class="container files">
<div class="title">Recent files</div>
<div class="body">
<MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/>
</div>
</div>
<div class="container env">
<div class="title">Enviroment</div>
<div class="body">
<div class="number _panel">
<div class="label">Calckey</div>
<div class="value _monospace">{{ version }}</div>
</div>
<div v-if="serverInfo" class="number _panel">
<div class="label">Node.js</div>
<div class="value _monospace">{{ serverInfo.node }}</div>
</div>
<div v-if="serverInfo" class="number _panel">
<div class="label">PostgreSQL</div>
<div class="value _monospace">{{ serverInfo.psql }}</div>
</div>
<div v-if="serverInfo" class="number _panel">
<div class="label">Redis</div>
<div class="value _monospace">{{ serverInfo.redis }}</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="container charts">
<div class="title">Active users</div>
<div class="body">
<canvas ref="chartEl"></canvas>
</div>
</div>
<div class="container federation">
<div class="title">Active instances</div>
<div class="body">
<XFederation/>
</div>
</div>
<div v-if="stats" class="container federationStats">
<div class="title">Federation</div>
<div class="body">
<div class="number _panel">
<div class="label">Sub</div>
<div class="value _monospace">
{{ number(federationSubActive) }}
<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationSubActiveDiff"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
<div class="number _panel">
<div class="label">Pub</div>
<div class="value _monospace">
{{ number(federationPubActive) }}
<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationPubActiveDiff"><template #before>(</template><template #after>)</template></MkNumberDiff>
</div>
</div>
</div>
</div>
<div class="container tagCloud">
<div class="body">
<MkTagCloud v-if="activeInstances">
<li v-for="instance in activeInstances">
<a @click.prevent="onInstanceClick(instance)">
<img style="width: 32px;" :src="instance.iconUrl">
</a>
</li>
</MkTagCloud>
</div>
</div>
<div v-if="topSubInstancesForPie && topPubInstancesForPie" class="container federationPies">
<div class="body">
<div class="chart deliver">
<div class="title">Sub</div>
<XPie :data="topSubInstancesForPie"/>
<div class="subTitle">Top 10</div>
</div>
<div class="chart inbox">
<div class="title">Pub</div>
<XPie :data="topPubInstancesForPie"/>
<div class="subTitle">Top 10</div>
</div>
</div>
</div>
<div class="item">
<template #header>Stats</template> <template #header>Stats</template>
<XStats/> <XStats/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Active users</template> <template #header>Active users</template>
<XActiveUsers/> <XActiveUsers/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Heatmap</template> <template #header>Heatmap</template>
<XHeatmap/> <XHeatmap/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Moderators</template> <template #header>Moderators</template>
<XModerators/> <XModerators/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Federation</template> <template #header>Federation</template>
<XFederation/> <XFederation/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Instances</template> <template #header>Instances</template>
<XInstances/> <XInstances/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Ap requests</template> <template #header>Ap requests</template>
<XApRequests/> <XApRequests/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>New users</template> <template #header>New users</template>
<XUsers/> <XUsers/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Deliver queue</template> <template #header>Deliver queue</template>
<XQueue domain="deliver"/> <XQueue domain="deliver"/>
</div> </MkFoldableSection>
<div class="item"> <MkFoldableSection class="item">
<template #header>Inbox queue</template> <template #header>Inbox queue</template>
<XQueue domain="inbox"/> <XQueue domain="inbox"/>
</div> </MkFoldableSection>
</div> </div>
</div> </MkSpacer>
</MkSpacer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { markRaw, version as vueVersion, onMounted, onBeforeUnmount, nextTick } from 'vue';
import {
Chart,
ArcElement,
LineElement,
BarElement,
PointElement,
BarController,
LineController,
CategoryScale,
LinearScale,
TimeScale,
Legend,
Title,
Tooltip,
SubTitle,
Filler,
} from 'chart.js';
import { enUS } from 'date-fns/locale';
import tinycolor from 'tinycolor2';
import MagicGrid from 'magic-grid';
import XMetrics from './metrics.vue';
import XFederation from './overview.federation.vue'; import XFederation from './overview.federation.vue';
import XQueueChart from './overview.queue-chart.vue'; import XInstances from './overview.instances.vue';
import XQueue from './overview.queue.vue';
import XApRequests from './overview.ap-requests.vue'; import XApRequests from './overview.ap-requests.vue';
import XUsers from './overview.users.vue'; import XUsers from './overview.users.vue';
import XActiveUsers from './overview.active-users.vue'; import XActiveUsers from './overview.active-users.vue';
import XStats from './overview.stats.vue'; import XStats from './overview.stats.vue';
import XModerators from './overview.moderators.vue'; import XModerators from './overview.moderators.vue';
import XHeatmap from './overview.heatmap.vue'; import XHeatmap from './overview.heatmap.vue';
import XUser from './overview.user.vue';
import XPie from './overview.pie.vue';
import MkNumberDiff from '@/components/MkNumberDiff.vue';
import MkTagCloud from '@/components/MkTagCloud.vue'; import MkTagCloud from '@/components/MkTagCloud.vue';
import { version, url } from '@/config'; import { version, url } from '@/config';
import number from '@/filters/number';
import * as os from '@/os'; import * as os from '@/os';
import { stream } from '@/stream'; import { stream } from '@/stream';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
import 'chartjs-adapter-date-fns';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue'; import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
Chart.register( const rootEl = $shallowRef<HTMLElement>();
ArcElement,
LineElement,
BarElement,
PointElement,
BarController,
LineController,
CategoryScale,
LinearScale,
TimeScale,
Legend,
Title,
Tooltip,
SubTitle,
Filler,
//gradient,
);
const rootEl = $ref<HTMLElement>();
const chartEl = $ref<HTMLCanvasElement>(null);
let stats: any = $ref(null);
let serverInfo: any = $ref(null); let serverInfo: any = $ref(null);
let topSubInstancesForPie: any = $ref(null); let topSubInstancesForPie: any = $ref(null);
let topPubInstancesForPie: any = $ref(null); let topPubInstancesForPie: any = $ref(null);
let usersComparedToThePrevDay: any = $ref(null);
let notesComparedToThePrevDay: any = $ref(null);
let federationPubActive = $ref<number | null>(null); let federationPubActive = $ref<number | null>(null);
let federationPubActiveDiff = $ref<number | null>(null); let federationPubActiveDiff = $ref<number | null>(null);
let federationSubActive = $ref<number | null>(null); let federationSubActive = $ref<number | null>(null);
@ -263,170 +87,12 @@ let newUsers = $ref(null);
let activeInstances = $shallowRef(null); let activeInstances = $shallowRef(null);
const queueStatsConnection = markRaw(stream.useChannel('queueStats')); const queueStatsConnection = markRaw(stream.useChannel('queueStats'));
const now = new Date(); const now = new Date();
let chartInstance: Chart = null;
const chartLimit = 30;
const filesPagination = { const filesPagination = {
endpoint: 'admin/drive/files' as const, endpoint: 'admin/drive/files' as const,
limit: 9, limit: 9,
noPaging: true, noPaging: true,
}; };
const { handler: externalTooltipHandler } = useChartTooltip();
async function renderChart() {
if (chartInstance) {
chartInstance.destroy();
}
const getDate = (ago: number) => {
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
return new Date(y, m, d - ago);
};
const format = (arr) => {
return arr.map((v, i) => ({
x: getDate(i).getTime(),
y: v,
}));
};
const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' });
const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
//
Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
const color = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
chartInstance = new Chart(chartEl, {
type: 'bar',
data: {
//labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
datasets: [{
parsing: false,
label: 'a',
data: format(raw.readWrite).slice().reverse(),
tension: 0.3,
pointRadius: 0,
borderWidth: 0,
borderJoinStyle: 'round',
borderRadius: 3,
backgroundColor: color,
/*gradient: props.bar ? undefined : {
backgroundColor: {
axis: 'y',
colors: {
0: alpha(x.color ? x.color : getColor(i), 0),
[maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
},
},
},*/
barPercentage: 0.9,
categoryPercentage: 0.9,
clip: 8,
}],
},
options: {
aspectRatio: 2.5,
layout: {
padding: {
left: 0,
right: 0,
top: 0,
bottom: 0,
},
},
scales: {
x: {
type: 'time',
display: false,
stacked: true,
offset: false,
time: {
stepSize: 1,
unit: 'month',
},
grid: {
display: false,
},
ticks: {
display: false,
},
adapters: {
date: {
locale: enUS,
},
},
min: getDate(chartLimit).getTime(),
},
y: {
display: false,
position: 'left',
stacked: true,
grid: {
display: false,
},
ticks: {
display: false,
//mirror: true,
},
},
},
interaction: {
intersect: false,
mode: 'index',
},
elements: {
point: {
hoverRadius: 5,
hoverBorderWidth: 2,
},
},
animation: false,
plugins: {
legend: {
display: false,
},
tooltip: {
enabled: false,
mode: 'index',
animation: {
duration: 0,
},
external: externalTooltipHandler,
},
//gradient,
},
},
plugins: [{
id: 'vLine',
beforeDraw(chart, args, options) {
if (chart.tooltip?._active?.length) {
const activePoint = chart.tooltip._active[0];
const ctx = chart.ctx;
const x = activePoint.element.x;
const topY = chart.scales.y.top;
const bottomY = chart.scales.y.bottom;
ctx.save();
ctx.beginPath();
ctx.moveTo(x, bottomY);
ctx.lineTo(x, topY);
ctx.lineWidth = 1;
ctx.strokeStyle = vLineColor;
ctx.stroke();
ctx.restore();
}
},
}],
});
}
function onInstanceClick(i) { function onInstanceClick(i) {
os.pageWindow(`/instance-info/${i.host}`); os.pageWindow(`/instance-info/${i.host}`);
} }
@ -442,20 +108,6 @@ onMounted(async () => {
magicGrid.listen(); magicGrid.listen();
*/ */
renderChart();
os.api('stats', {}).then(statsResponse => {
stats = statsResponse;
os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1];
});
os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1];
});
});
os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => { os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => {
federationPubActive = chart.pubActive[0]; federationPubActive = chart.pubActive[0];
federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1]; federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
@ -494,7 +146,7 @@ onMounted(async () => {
}); });
os.api('federation/instances', { os.api('federation/instances', {
sort: '+lastCommunicatedAt', sort: '+latestRequestReceivedAt',
limit: 25, limit: 25,
}).then(res => { }).then(res => {
activeInstances = res; activeInstances = res;
@ -518,171 +170,14 @@ const headerTabs = $computed(() => []);
definePageMetadata({ definePageMetadata({
title: i18n.ts.dashboard, title: i18n.ts.dashboard,
icon: 'ph-gauge-bold ph-lg', icon: 'ti ti-dashboard',
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.edbbcaef { .edbbcaef {
display: flex; display: grid;
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
> .left, > .right { grid-gap: 16px;
box-sizing: border-box;
width: 50%;
> .container {
margin: 32px 0;
> .title {
font-weight: bold;
margin-bottom: 16px;
}
&.stats, &.federationStats {
> .body {
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
> .number {
padding: 14px 20px;
> .label {
opacity: 0.7;
font-size: 0.8em;
}
> .value {
font-weight: bold;
font-size: 1.5em;
> .diff {
font-size: 0.7em;
}
}
}
}
}
&.env {
> .body {
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
> .number {
padding: 14px 20px;
> .label {
opacity: 0.7;
font-size: 0.8em;
}
> .value {
font-size: 1.1em;
}
}
}
}
&.charts {
> .body {
padding: 32px;
background: var(--panel);
border-radius: var(--radius);
}
}
&.users {
> .body {
background: var(--panel);
border-radius: var(--radius);
> .user {
padding: 16px 20px;
&:not(:last-child) {
border-bottom: solid 0.5px var(--divider);
}
}
}
}
&.federation {
> .body {
background: var(--panel);
border-radius: var(--radius);
overflow: clip;
}
}
&.queue {
> .body {
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
> .chart {
position: relative;
padding: 20px;
background: var(--panel);
border-radius: var(--radius);
> .title {
position: absolute;
top: 20px;
left: 20px;
font-size: 90%;
}
}
}
}
&.federationPies {
> .body {
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
> .chart {
position: relative;
padding: 20px;
background: var(--panel);
border-radius: var(--radius);
> .title {
position: absolute;
top: 20px;
left: 20px;
font-size: 90%;
}
> .subTitle {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 85%;
}
}
}
}
&.tagCloud {
> .body {
background: var(--panel);
border-radius: var(--radius);
overflow: clip;
}
}
}
}
> .left {
padding-right: 16px;
}
> .right {
padding-left: 16px;
}
} }
</style> </style>