enhance: TLキャッシュ容量を設定できるように

This commit is contained in:
syuilo 2023-10-04 08:46:27 +09:00
parent 5fd0cb31f6
commit 17b83ff4c1
12 changed files with 251 additions and 74 deletions

1
locales/index.d.ts vendored
View file

@ -1131,6 +1131,7 @@ export interface Locale {
"fileAttachedOnly": string; "fileAttachedOnly": string;
"showRepliesToOthersInTimeline": string; "showRepliesToOthersInTimeline": string;
"hideRepliesToOthersInTimeline": string; "hideRepliesToOthersInTimeline": string;
"externalServices": string;
"_announcement": { "_announcement": {
"forExistingUsers": string; "forExistingUsers": string;
"forExistingUsersDescription": string; "forExistingUsersDescription": string;

View file

@ -1128,6 +1128,7 @@ mutualFollow: "相互フォロー"
fileAttachedOnly: "ファイル付きのみ" fileAttachedOnly: "ファイル付きのみ"
showRepliesToOthersInTimeline: "TLに他の人への返信を含める" showRepliesToOthersInTimeline: "TLに他の人への返信を含める"
hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない" hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない"
externalServices: "外部サービス"
_announcement: _announcement:
forExistingUsers: "既存ユーザーのみ" forExistingUsers: "既存ユーザーのみ"

View file

@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class MetaCacheSettings1696373953614 {
name = 'MetaCacheSettings1696373953614'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "perLocalUserUserTimelineCacheMax" integer NOT NULL DEFAULT '300'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "perRemoteUserUserTimelineCacheMax" integer NOT NULL DEFAULT '100'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "perUserHomeTimelineCacheMax" integer NOT NULL DEFAULT '300'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "perUserListTimelineCacheMax" integer NOT NULL DEFAULT '300'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "perUserListTimelineCacheMax"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "perUserHomeTimelineCacheMax"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "perRemoteUserUserTimelineCacheMax"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "perLocalUserUserTimelineCacheMax"`);
}
}

View file

@ -803,6 +803,8 @@ export class NoteCreateService implements OnApplicationShutdown {
@bindThis @bindThis
private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) { private async pushToTl(note: MiNote, user: { id: MiUser['id']; host: MiUser['host']; }) {
const meta = await this.metaService.fetch();
const redisPipeline = this.redisForTimelines.pipeline(); const redisPipeline = this.redisForTimelines.pipeline();
if (note.channelId) { if (note.channelId) {
@ -816,14 +818,14 @@ export class NoteCreateService implements OnApplicationShutdown {
for (const channelFollowing of channelFollowings) { for (const channelFollowing of channelFollowings) {
redisPipeline.xadd( redisPipeline.xadd(
`homeTimeline:${channelFollowing.followerId}`, `homeTimeline:${channelFollowing.followerId}`,
'MAXLEN', '~', '200', 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(),
'*', '*',
'note', note.id); 'note', note.id);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
redisPipeline.xadd( redisPipeline.xadd(
`homeTimelineWithFiles:${channelFollowing.followerId}`, `homeTimelineWithFiles:${channelFollowing.followerId}`,
'MAXLEN', '~', '100', 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(),
'*', '*',
'note', note.id); 'note', note.id);
} }
@ -855,14 +857,14 @@ export class NoteCreateService implements OnApplicationShutdown {
redisPipeline.xadd( redisPipeline.xadd(
`homeTimeline:${following.followerId}`, `homeTimeline:${following.followerId}`,
'MAXLEN', '~', '200', 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(),
'*', '*',
'note', note.id); 'note', note.id);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
redisPipeline.xadd( redisPipeline.xadd(
`homeTimelineWithFiles:${following.followerId}`, `homeTimelineWithFiles:${following.followerId}`,
'MAXLEN', '~', '100', 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(),
'*', '*',
'note', note.id); 'note', note.id);
} }
@ -882,14 +884,14 @@ export class NoteCreateService implements OnApplicationShutdown {
redisPipeline.xadd( redisPipeline.xadd(
`userListTimeline:${userListMembership.userListId}`, `userListTimeline:${userListMembership.userListId}`,
'MAXLEN', '~', '200', 'MAXLEN', '~', meta.perUserListTimelineCacheMax.toString(),
'*', '*',
'note', note.id); 'note', note.id);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
redisPipeline.xadd( redisPipeline.xadd(
`userListTimelineWithFiles:${userListMembership.userListId}`, `userListTimelineWithFiles:${userListMembership.userListId}`,
'MAXLEN', '~', '100', 'MAXLEN', '~', (meta.perUserListTimelineCacheMax / 2).toString(),
'*', '*',
'note', note.id); 'note', note.id);
} }
@ -898,14 +900,14 @@ export class NoteCreateService implements OnApplicationShutdown {
{ // 自分自身のHTL { // 自分自身のHTL
redisPipeline.xadd( redisPipeline.xadd(
`homeTimeline:${user.id}`, `homeTimeline:${user.id}`,
'MAXLEN', '~', '200', 'MAXLEN', '~', meta.perUserHomeTimelineCacheMax.toString(),
'*', '*',
'note', note.id); 'note', note.id);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
redisPipeline.xadd( redisPipeline.xadd(
`homeTimelineWithFiles:${user.id}`, `homeTimelineWithFiles:${user.id}`,
'MAXLEN', '~', '100', 'MAXLEN', '~', (meta.perUserHomeTimelineCacheMax / 2).toString(),
'*', '*',
'note', note.id); 'note', note.id);
} }
@ -916,20 +918,20 @@ export class NoteCreateService implements OnApplicationShutdown {
if (note.replyId && note.replyUserId !== note.userId) { if (note.replyId && note.replyUserId !== note.userId) {
redisPipeline.xadd( redisPipeline.xadd(
`userTimelineWithReplies:${user.id}`, `userTimelineWithReplies:${user.id}`,
'MAXLEN', '~', '1000', 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(),
'*', '*',
'note', note.id); 'note', note.id);
} else { } else {
redisPipeline.xadd( redisPipeline.xadd(
`userTimeline:${user.id}`, `userTimeline:${user.id}`,
'MAXLEN', '~', '1000', 'MAXLEN', '~', note.userHost == null ? meta.perLocalUserUserTimelineCacheMax.toString() : meta.perRemoteUserUserTimelineCacheMax.toString(),
'*', '*',
'note', note.id); 'note', note.id);
if (note.fileIds.length > 0) { if (note.fileIds.length > 0) {
redisPipeline.xadd( redisPipeline.xadd(
`userTimelineWithFiles:${user.id}`, `userTimelineWithFiles:${user.id}`,
'MAXLEN', '~', '500', 'MAXLEN', '~', note.userHost == null ? (meta.perLocalUserUserTimelineCacheMax / 2).toString() : (meta.perRemoteUserUserTimelineCacheMax / 2).toString(),
'*', '*',
'note', note.id); 'note', note.id);
} }

View file

@ -471,4 +471,24 @@ export class MiMeta {
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }', length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
}) })
public preservedUsernames: string[]; public preservedUsernames: string[];
@Column('integer', {
default: 300,
})
public perLocalUserUserTimelineCacheMax: number;
@Column('integer', {
default: 100,
})
public perRemoteUserUserTimelineCacheMax: number;
@Column('integer', {
default: 300,
})
public perUserHomeTimelineCacheMax: number;
@Column('integer', {
default: 300,
})
public perUserListTimelineCacheMax: number;
} }

View file

@ -105,40 +105,32 @@ export const meta = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
userStarForReactionFallback: {
type: 'boolean',
optional: true, nullable: false,
},
pinnedUsers: { pinnedUsers: {
type: 'array', type: 'array',
optional: true, nullable: false, optional: false, nullable: false,
items: { items: {
type: 'string', type: 'string',
optional: false, nullable: false,
}, },
}, },
hiddenTags: { hiddenTags: {
type: 'array', type: 'array',
optional: true, nullable: false, optional: false, nullable: false,
items: { items: {
type: 'string', type: 'string',
optional: false, nullable: false,
}, },
}, },
blockedHosts: { blockedHosts: {
type: 'array', type: 'array',
optional: true, nullable: false, optional: false, nullable: false,
items: { items: {
type: 'string', type: 'string',
optional: false, nullable: false,
}, },
}, },
sensitiveWords: { sensitiveWords: {
type: 'array', type: 'array',
optional: true, nullable: false, optional: false, nullable: false,
items: { items: {
type: 'string', type: 'string',
optional: false, nullable: false,
}, },
}, },
preservedUsernames: { preservedUsernames: {
@ -146,129 +138,124 @@ export const meta = {
optional: false, nullable: false, optional: false, nullable: false,
items: { items: {
type: 'string', type: 'string',
optional: false, nullable: false,
}, },
}, },
hcaptchaSecretKey: { hcaptchaSecretKey: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
recaptchaSecretKey: { recaptchaSecretKey: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
turnstileSecretKey: { turnstileSecretKey: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
sensitiveMediaDetection: { sensitiveMediaDetection: {
type: 'string', type: 'string',
optional: true, nullable: false, optional: false, nullable: false,
}, },
sensitiveMediaDetectionSensitivity: { sensitiveMediaDetectionSensitivity: {
type: 'string', type: 'string',
optional: true, nullable: false, optional: false, nullable: false,
}, },
setSensitiveFlagAutomatically: { setSensitiveFlagAutomatically: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
enableSensitiveMediaDetectionForVideos: { enableSensitiveMediaDetectionForVideos: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
proxyAccountId: { proxyAccountId: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
format: 'id', format: 'id',
}, },
summaryProxy: {
type: 'string',
optional: true, nullable: true,
},
email: { email: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
smtpSecure: { smtpSecure: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
smtpHost: { smtpHost: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
smtpPort: { smtpPort: {
type: 'number', type: 'number',
optional: true, nullable: true, optional: false, nullable: true,
}, },
smtpUser: { smtpUser: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
smtpPass: { smtpPass: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
swPrivateKey: { swPrivateKey: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
useObjectStorage: { useObjectStorage: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
objectStorageBaseUrl: { objectStorageBaseUrl: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStorageBucket: { objectStorageBucket: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStoragePrefix: { objectStoragePrefix: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStorageEndpoint: { objectStorageEndpoint: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStorageRegion: { objectStorageRegion: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStoragePort: { objectStoragePort: {
type: 'number', type: 'number',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStorageAccessKey: { objectStorageAccessKey: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStorageSecretKey: { objectStorageSecretKey: {
type: 'string', type: 'string',
optional: true, nullable: true, optional: false, nullable: true,
}, },
objectStorageUseSSL: { objectStorageUseSSL: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
objectStorageUseProxy: { objectStorageUseProxy: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
objectStorageSetPublicRead: { objectStorageSetPublicRead: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
enableIpLogging: { enableIpLogging: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
enableActiveEmailValidation: { enableActiveEmailValidation: {
type: 'boolean', type: 'boolean',
optional: true, nullable: false, optional: false, nullable: false,
}, },
enableChartsForRemoteUser: { enableChartsForRemoteUser: {
type: 'boolean', type: 'boolean',
@ -288,12 +275,28 @@ export const meta = {
}, },
manifestJsonOverride: { manifestJsonOverride: {
type: 'string', type: 'string',
optional: true, nullable: false, optional: false, nullable: false,
}, },
policies: { policies: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,
}, },
perLocalUserUserTimelineCacheMax: {
type: 'number',
optional: false, nullable: false,
},
perRemoteUserUserTimelineCacheMax: {
type: 'number',
optional: false, nullable: false,
},
perUserHomeTimelineCacheMax: {
type: 'number',
optional: false, nullable: false,
},
perUserListTimelineCacheMax: {
type: 'number',
optional: false, nullable: false,
},
}, },
}, },
} as const; } as const;
@ -313,7 +316,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private metaService: MetaService, private metaService: MetaService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async () => {
const instance = await this.metaService.fetch(true); const instance = await this.metaService.fetch(true);
return { return {
@ -399,6 +402,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
enableIdenticonGeneration: instance.enableIdenticonGeneration, enableIdenticonGeneration: instance.enableIdenticonGeneration,
policies: { ...DEFAULT_POLICIES, ...instance.policies }, policies: { ...DEFAULT_POLICIES, ...instance.policies },
manifestJsonOverride: instance.manifestJsonOverride, manifestJsonOverride: instance.manifestJsonOverride,
perLocalUserUserTimelineCacheMax: instance.perLocalUserUserTimelineCacheMax,
perRemoteUserUserTimelineCacheMax: instance.perRemoteUserUserTimelineCacheMax,
perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax,
perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax,
}; };
}); });
} }

View file

@ -108,6 +108,10 @@ export const paramDef = {
serverRules: { type: 'array', items: { type: 'string' } }, serverRules: { type: 'array', items: { type: 'string' } },
preservedUsernames: { type: 'array', items: { type: 'string' } }, preservedUsernames: { type: 'array', items: { type: 'string' } },
manifestJsonOverride: { type: 'string' }, manifestJsonOverride: { type: 'string' },
perLocalUserUserTimelineCacheMax: { type: 'integer' },
perRemoteUserUserTimelineCacheMax: { type: 'integer' },
perUserHomeTimelineCacheMax: { type: 'integer' },
perUserListTimelineCacheMax: { type: 'integer' },
}, },
required: [], required: [],
} as const; } as const;
@ -441,6 +445,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.manifestJsonOverride = ps.manifestJsonOverride; set.manifestJsonOverride = ps.manifestJsonOverride;
} }
if (ps.perLocalUserUserTimelineCacheMax !== undefined) {
set.perLocalUserUserTimelineCacheMax = ps.perLocalUserUserTimelineCacheMax;
}
if (ps.perRemoteUserUserTimelineCacheMax !== undefined) {
set.perRemoteUserUserTimelineCacheMax = ps.perRemoteUserUserTimelineCacheMax;
}
if (ps.perUserHomeTimelineCacheMax !== undefined) {
set.perUserHomeTimelineCacheMax = ps.perUserHomeTimelineCacheMax;
}
if (ps.perUserListTimelineCacheMax !== undefined) {
set.perUserListTimelineCacheMax = ps.perUserListTimelineCacheMax;
}
const before = await this.metaService.fetch(true); const before = await this.metaService.fetch(true);
await this.metaService.update(set); await this.metaService.update(set);

View file

@ -214,11 +214,11 @@ export const meta = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
localTimeLine: { localTimeline: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
globalTimeLine: { globalTimeline: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },

View file

@ -0,0 +1,81 @@
<!--
SPDX-FileCopyrightText: syuilo and other misskey contributors
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<MkStickyContainer>
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
<FormSuspense :p="init">
<FormSection>
<template #label>DeepL Translation</template>
<div class="_gaps_m">
<MkInput v-model="deeplAuthKey">
<template #prefix><i class="ti ti-key"></i></template>
<template #label>DeepL Auth Key</template>
</MkInput>
<MkSwitch v-model="deeplIsPro">
<template #label>Pro account</template>
</MkSwitch>
</div>
</FormSection>
</FormSuspense>
</MkSpacer>
<template #footer>
<div :class="$style.footer">
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
</MkSpacer>
</div>
</template>
</MkStickyContainer>
</template>
<script lang="ts" setup>
import { } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
import FormSuspense from '@/components/form/suspense.vue';
import FormSection from '@/components/form/section.vue';
import * as os from '@/os.js';
import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
let deeplAuthKey: string = $ref('');
let deeplIsPro: boolean = $ref(false);
async function init() {
const meta = await os.api('admin/meta');
deeplAuthKey = meta.deeplAuthKey;
deeplIsPro = meta.deeplIsPro;
}
function save() {
os.apiWithDialog('admin/update-meta', {
deeplAuthKey,
deeplIsPro,
}).then(() => {
fetchInstance();
});
}
const headerActions = $computed(() => []);
const headerTabs = $computed(() => []);
definePageMetadata({
title: i18n.ts.instanceBlocking,
icon: 'ti ti-ban',
});
</script>
<style lang="scss" module>
.footer {
-webkit-backdrop-filter: var(--blur, blur(15px));
backdrop-filter: var(--blur, blur(15px));
}
</style>

View file

@ -198,6 +198,11 @@ const menuDef = $computed(() => [{
text: i18n.ts.proxyAccount, text: i18n.ts.proxyAccount,
to: '/admin/proxy-account', to: '/admin/proxy-account',
active: currentPage?.route.name === 'proxy-account', active: currentPage?.route.name === 'proxy-account',
}, {
icon: 'ti ti-link',
text: i18n.ts.externalServices,
to: '/admin/external-services',
active: currentPage?.route.name === 'external-services',
}, { }, {
icon: 'ti ti-adjustments', icon: 'ti ti-adjustments',
text: i18n.ts.other, text: i18n.ts.other,

View file

@ -81,16 +81,24 @@ SPDX-License-Identifier: AGPL-3.0-only
</FormSection> </FormSection>
<FormSection> <FormSection>
<template #label>DeepL Translation</template> <template #label>Timeline caching</template>
<div class="_gaps_m"> <div class="_gaps_m">
<MkInput v-model="deeplAuthKey"> <MkInput v-model="perLocalUserUserTimelineCacheMax" type="number">
<template #prefix><i class="ti ti-key"></i></template> <template #label>perLocalUserUserTimelineCacheMax</template>
<template #label>DeepL Auth Key</template> </MkInput>
<MkInput v-model="perRemoteUserUserTimelineCacheMax" type="number">
<template #label>perRemoteUserUserTimelineCacheMax</template>
</MkInput>
<MkInput v-model="perUserHomeTimelineCacheMax" type="number">
<template #label>perUserHomeTimelineCacheMax</template>
</MkInput>
<MkInput v-model="perUserListTimelineCacheMax" type="number">
<template #label>perUserListTimelineCacheMax</template>
</MkInput> </MkInput>
<MkSwitch v-model="deeplIsPro">
<template #label>Pro account</template>
</MkSwitch>
</div> </div>
</FormSection> </FormSection>
</div> </div>
@ -133,8 +141,10 @@ let cacheRemoteSensitiveFiles: boolean = $ref(false);
let enableServiceWorker: boolean = $ref(false); let enableServiceWorker: boolean = $ref(false);
let swPublicKey: any = $ref(null); let swPublicKey: any = $ref(null);
let swPrivateKey: any = $ref(null); let swPrivateKey: any = $ref(null);
let deeplAuthKey: string = $ref(''); let perLocalUserUserTimelineCacheMax: number = $ref(0);
let deeplIsPro: boolean = $ref(false); let perRemoteUserUserTimelineCacheMax: number = $ref(0);
let perUserHomeTimelineCacheMax: number = $ref(0);
let perUserListTimelineCacheMax: number = $ref(0);
async function init(): Promise<void> { async function init(): Promise<void> {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
@ -149,8 +159,10 @@ async function init(): Promise<void> {
enableServiceWorker = meta.enableServiceWorker; enableServiceWorker = meta.enableServiceWorker;
swPublicKey = meta.swPublickey; swPublicKey = meta.swPublickey;
swPrivateKey = meta.swPrivateKey; swPrivateKey = meta.swPrivateKey;
deeplAuthKey = meta.deeplAuthKey; perLocalUserUserTimelineCacheMax = meta.perLocalUserUserTimelineCacheMax;
deeplIsPro = meta.deeplIsPro; perRemoteUserUserTimelineCacheMax = meta.perRemoteUserUserTimelineCacheMax;
perUserHomeTimelineCacheMax = meta.perUserHomeTimelineCacheMax;
perUserListTimelineCacheMax = meta.perUserListTimelineCacheMax;
} }
function save(): void { function save(): void {
@ -166,8 +178,10 @@ function save(): void {
enableServiceWorker, enableServiceWorker,
swPublicKey, swPublicKey,
swPrivateKey, swPrivateKey,
deeplAuthKey, perLocalUserUserTimelineCacheMax,
deeplIsPro, perRemoteUserUserTimelineCacheMax,
perUserHomeTimelineCacheMax,
perUserListTimelineCacheMax,
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });

View file

@ -435,6 +435,10 @@ export const routes = [{
path: '/proxy-account', path: '/proxy-account',
name: 'proxy-account', name: 'proxy-account',
component: page(() => import('./pages/admin/proxy-account.vue')), component: page(() => import('./pages/admin/proxy-account.vue')),
}, {
path: '/external-services',
name: 'external-services',
component: page(() => import('./pages/admin/external-services.vue')),
}, { }, {
path: '/other-settings', path: '/other-settings',
name: 'other-settings', name: 'other-settings',