summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-02-23 10:47:17 +0900
committerGitHub <noreply@github.com>2024-02-23 10:47:17 +0900
commitd20542c495dd3342b23ef9f1c759c2f4f2dce63e (patch)
treefd8ab7178b16de76a07b84be3c72f666fdbb97d5
parentflash/update で部分的に変更できるようにする (#13396) (diff)
downloadsharkey-d20542c495dd3342b23ef9f1c759c2f4f2dce63e.tar.gz
sharkey-d20542c495dd3342b23ef9f1c759c2f4f2dce63e.tar.bz2
sharkey-d20542c495dd3342b23ef9f1c759c2f4f2dce63e.zip
enhance: `meta`をSSR HTMLに埋め込む (#13436)
* enhance: `meta`をSSR HTMLに埋め込む * HTML Metaの有効時間を指定 * 1時間 * MetaEntityService * JSONをPackするように * :v: --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
-rw-r--r--packages/backend/src/core/CoreModule.ts6
-rw-r--r--packages/backend/src/core/entities/MetaEntityService.ts154
-rw-r--r--packages/backend/src/misc/json-schema.ts8
-rw-r--r--packages/backend/src/models/json-schema/meta.ts328
-rw-r--r--packages/backend/src/server/api/endpoints/meta.ts417
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts24
-rw-r--r--packages/backend/src/server/web/views/base.pug3
-rw-r--r--packages/frontend/src/boot/main-boot.ts12
-rw-r--r--packages/frontend/src/instance.ts30
-rw-r--r--packages/frontend/src/local-storage.ts1
-rw-r--r--packages/frontend/src/pages/admin/bot-protection.vue2
-rw-r--r--packages/frontend/src/pages/admin/branding.vue2
-rw-r--r--packages/frontend/src/pages/admin/email-settings.vue2
-rw-r--r--packages/frontend/src/pages/admin/external-services.vue2
-rw-r--r--packages/frontend/src/pages/admin/instance-block.vue2
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue2
-rw-r--r--packages/frontend/src/pages/admin/object-storage.vue2
-rw-r--r--packages/frontend/src/pages/admin/other-settings.vue2
-rw-r--r--packages/frontend/src/pages/admin/proxy-account.vue2
-rw-r--r--packages/frontend/src/pages/admin/security.vue4
-rw-r--r--packages/frontend/src/pages/admin/server-rules.vue2
-rw-r--r--packages/frontend/src/pages/admin/settings.vue2
-rw-r--r--packages/frontend/src/scripts/clear-cache.ts4
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md14
-rw-r--r--packages/misskey-js/src/autogen/models.ts3
-rw-r--r--packages/misskey-js/src/autogen/types.ts176
26 files changed, 676 insertions, 530 deletions
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index c31cef36e8..2c27d33c06 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -116,6 +116,7 @@ import { FlashEntityService } from './entities/FlashEntityService.js';
import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
import { RoleEntityService } from './entities/RoleEntityService.js';
import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js';
+import { MetaEntityService } from './entities/MetaEntityService.js';
import { ApAudienceService } from './activitypub/ApAudienceService.js';
import { ApDbResolverService } from './activitypub/ApDbResolverService.js';
@@ -254,6 +255,7 @@ const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisti
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService };
const $ReversiGameEntityService: Provider = { provide: 'ReversiGameEntityService', useExisting: ReversiGameEntityService };
+const $MetaEntityService: Provider = { provide: 'MetaEntityService', useExisting: MetaEntityService };
const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService };
const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService };
@@ -393,6 +395,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
FlashLikeEntityService,
RoleEntityService,
ReversiGameEntityService,
+ MetaEntityService,
ApAudienceService,
ApDbResolverService,
@@ -528,6 +531,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$FlashLikeEntityService,
$RoleEntityService,
$ReversiGameEntityService,
+ $MetaEntityService,
$ApAudienceService,
$ApDbResolverService,
@@ -663,6 +667,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
FlashLikeEntityService,
RoleEntityService,
ReversiGameEntityService,
+ MetaEntityService,
ApAudienceService,
ApDbResolverService,
@@ -797,6 +802,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$FlashLikeEntityService,
$RoleEntityService,
$ReversiGameEntityService,
+ $MetaEntityService,
$ApAudienceService,
$ApDbResolverService,
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
new file mode 100644
index 0000000000..b50d76288f
--- /dev/null
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -0,0 +1,154 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Brackets } from 'typeorm';
+import { Inject, Injectable } from '@nestjs/common';
+import JSON5 from 'json5';
+import type { Packed } from '@/misc/json-schema.js';
+import type { MiMeta } from '@/models/Meta.js';
+import type { AdsRepository } from '@/models/_.js';
+import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
+import { MetaService } from '@/core/MetaService.js';
+import { bindThis } from '@/decorators.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { InstanceActorService } from '@/core/InstanceActorService.js';
+import type { Config } from '@/config.js';
+import { DI } from '@/di-symbols.js';
+import { DEFAULT_POLICIES } from '@/core/RoleService.js';
+
+@Injectable()
+export class MetaEntityService {
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.adsRepository)
+ private adsRepository: AdsRepository,
+
+ private userEntityService: UserEntityService,
+ private metaService: MetaService,
+ private instanceActorService: InstanceActorService,
+ ) { }
+
+ @bindThis
+ public async pack(meta?: MiMeta): Promise<Packed<'MetaLite'>> {
+ let instance = meta;
+
+ if (!instance) {
+ instance = await this.metaService.fetch();
+ }
+
+ const ads = await this.adsRepository.createQueryBuilder('ads')
+ .where('ads.expiresAt > :now', { now: new Date() })
+ .andWhere('ads.startsAt <= :now', { now: new Date() })
+ .andWhere(new Brackets(qb => {
+ // 曜日のビットフラグを確認する
+ qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
+ .orWhere('ads.dayOfWeek = 0');
+ }))
+ .getMany();
+
+ const packed: Packed<'MetaLite'> = {
+ maintainerName: instance.maintainerName,
+ maintainerEmail: instance.maintainerEmail,
+
+ version: this.config.version,
+ providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl,
+
+ name: instance.name,
+ shortName: instance.shortName,
+ uri: this.config.url,
+ description: instance.description,
+ langs: instance.langs,
+ tosUrl: instance.termsOfServiceUrl,
+ repositoryUrl: instance.repositoryUrl,
+ feedbackUrl: instance.feedbackUrl,
+ impressumUrl: instance.impressumUrl,
+ privacyPolicyUrl: instance.privacyPolicyUrl,
+ disableRegistration: instance.disableRegistration,
+ emailRequiredForSignup: instance.emailRequiredForSignup,
+ enableHcaptcha: instance.enableHcaptcha,
+ hcaptchaSiteKey: instance.hcaptchaSiteKey,
+ enableMcaptcha: instance.enableMcaptcha,
+ mcaptchaSiteKey: instance.mcaptchaSitekey,
+ mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl,
+ enableRecaptcha: instance.enableRecaptcha,
+ recaptchaSiteKey: instance.recaptchaSiteKey,
+ enableTurnstile: instance.enableTurnstile,
+ turnstileSiteKey: instance.turnstileSiteKey,
+ swPublickey: instance.swPublicKey,
+ themeColor: instance.themeColor,
+ mascotImageUrl: instance.mascotImageUrl ?? '/assets/ai.png',
+ bannerUrl: instance.bannerUrl,
+ infoImageUrl: instance.infoImageUrl,
+ serverErrorImageUrl: instance.serverErrorImageUrl,
+ notFoundImageUrl: instance.notFoundImageUrl,
+ iconUrl: instance.iconUrl,
+ backgroundImageUrl: instance.backgroundImageUrl,
+ logoImageUrl: instance.logoImageUrl,
+ maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
+ // クライアントの手間を減らすためあらかじめJSONに変換しておく
+ defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
+ defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
+ ads: ads.map(ad => ({
+ id: ad.id,
+ url: ad.url,
+ place: ad.place,
+ ratio: ad.ratio,
+ imageUrl: ad.imageUrl,
+ dayOfWeek: ad.dayOfWeek,
+ })),
+ notesPerOneAd: instance.notesPerOneAd,
+ enableEmail: instance.enableEmail,
+ enableServiceWorker: instance.enableServiceWorker,
+
+ translatorAvailable: instance.deeplAuthKey != null,
+
+ serverRules: instance.serverRules,
+
+ policies: { ...DEFAULT_POLICIES, ...instance.policies },
+
+ mediaProxy: this.config.mediaProxy,
+ };
+
+ return packed;
+ }
+
+ @bindThis
+ public async packDetailed(meta?: MiMeta): Promise<Packed<'MetaDetailed'>> {
+ let instance = meta;
+
+ if (!instance) {
+ instance = await this.metaService.fetch();
+ }
+
+ const packed = await this.pack(instance);
+
+ const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
+
+ const packDetailed: Packed<'MetaDetailed'> = {
+ ...packed,
+ cacheRemoteFiles: instance.cacheRemoteFiles,
+ cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
+ requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
+ proxyAccountName: proxyAccount ? proxyAccount.username : null,
+ features: {
+ localTimeline: instance.policies.ltlAvailable,
+ globalTimeline: instance.policies.gtlAvailable,
+ registration: !instance.disableRegistration,
+ emailRequiredForSignup: instance.emailRequiredForSignup,
+ hcaptcha: instance.enableHcaptcha,
+ recaptcha: instance.enableRecaptcha,
+ turnstile: instance.enableTurnstile,
+ objectStorage: instance.useObjectStorage,
+ serviceWorker: instance.enableServiceWorker,
+ miauth: true,
+ },
+ };
+
+ return packDetailed;
+ }
+}
+
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index de38f145b2..8449e5ff07 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -50,6 +50,11 @@ import {
} from '@/models/json-schema/role.js';
import { packedAdSchema } from '@/models/json-schema/ad.js';
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
+import {
+ packedMetaLiteSchema,
+ packedMetaDetailedOnlySchema,
+ packedMetaDetailedSchema,
+} from '@/models/json-schema/meta.js';
export const refs = {
UserLite: packedUserLiteSchema,
@@ -99,6 +104,9 @@ export const refs = {
RolePolicies: packedRolePoliciesSchema,
ReversiGameLite: packedReversiGameLiteSchema,
ReversiGameDetailed: packedReversiGameDetailedSchema,
+ MetaLite: packedMetaLiteSchema,
+ MetaDetailedOnly: packedMetaDetailedOnlySchema,
+ MetaDetailed: packedMetaDetailedSchema,
};
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
new file mode 100644
index 0000000000..17789f3b46
--- /dev/null
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -0,0 +1,328 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const packedMetaLiteSchema = {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ maintainerName: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ maintainerEmail: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ version: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ providesTarball: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ name: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ shortName: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ uri: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'url',
+ example: 'https://misskey.example.com',
+ },
+ description: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ langs: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+ tosUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ repositoryUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ default: 'https://github.com/misskey-dev/misskey',
+ },
+ feedbackUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ default: 'https://github.com/misskey-dev/misskey/issues/new',
+ },
+ defaultDarkTheme: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ defaultLightTheme: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ disableRegistration: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ emailRequiredForSignup: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ enableHcaptcha: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ hcaptchaSiteKey: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ enableMcaptcha: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ mcaptchaSiteKey: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ mcaptchaInstanceUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ enableRecaptcha: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ recaptchaSiteKey: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ enableTurnstile: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ turnstileSiteKey: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ swPublickey: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ mascotImageUrl: {
+ type: 'string',
+ optional: false, nullable: false,
+ default: '/assets/ai.png',
+ },
+ bannerUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ serverErrorImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ infoImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ notFoundImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ iconUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ maxNoteTextLength: {
+ type: 'number',
+ optional: false, nullable: false,
+ },
+ ads: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ example: 'xxxxxxxxxx',
+ },
+ url: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'url',
+ },
+ place: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ ratio: {
+ type: 'number',
+ optional: false, nullable: false,
+ },
+ imageUrl: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'url',
+ },
+ dayOfWeek: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ },
+ },
+ },
+ notesPerOneAd: {
+ type: 'number',
+ optional: false, nullable: false,
+ default: 0,
+ },
+ enableEmail: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ enableServiceWorker: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ translatorAvailable: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ mediaProxy: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ backgroundImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ impressumUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ logoImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ privacyPolicyUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ serverRules: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ },
+ },
+ themeColor: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ policies: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'RolePolicies',
+ },
+ },
+} as const;
+
+export const packedMetaDetailedOnlySchema = {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ features: {
+ type: 'object',
+ optional: true, nullable: false,
+ properties: {
+ registration: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ emailRequiredForSignup: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ localTimeline: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ globalTimeline: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ hcaptcha: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ turnstile: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ recaptcha: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ objectStorage: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ serviceWorker: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ miauth: {
+ type: 'boolean',
+ optional: true, nullable: false,
+ default: true,
+ },
+ },
+ },
+ proxyAccountName: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ requireSetup: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ example: false,
+ },
+ cacheRemoteFiles: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ cacheRemoteSensitiveFiles: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ },
+} as const;
+
+export const packedMetaDetailedSchema = {
+ type: 'object',
+ allOf: [
+ {
+ type: 'object',
+ ref: 'MetaLite',
+ },
+ {
+ type: 'object',
+ ref: 'MetaDetailedOnly',
+ },
+ ],
+} as const;
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 834158baf4..5460635e1d 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -3,18 +3,9 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm';
-import { Inject, Injectable } from '@nestjs/common';
-import JSON5 from 'json5';
-import type { AdsRepository } from '@/models/_.js';
-import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
+import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-import { MetaService } from '@/core/MetaService.js';
-import { InstanceActorService } from '@/core/InstanceActorService.js';
-import type { Config } from '@/config.js';
-import { DI } from '@/di-symbols.js';
-import { DEFAULT_POLICIES } from '@/core/RoleService.js';
+import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
export const meta = {
tags: ['meta'],
@@ -23,297 +14,10 @@ export const meta = {
res: {
type: 'object',
- optional: false, nullable: false,
- properties: {
- maintainerName: {
- type: 'string',
- optional: false, nullable: true,
- },
- maintainerEmail: {
- type: 'string',
- optional: false, nullable: true,
- },
- version: {
- type: 'string',
- optional: false, nullable: false,
- },
- providesTarball: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- name: {
- type: 'string',
- optional: false, nullable: false,
- },
- shortName: {
- type: 'string',
- optional: false, nullable: true,
- },
- uri: {
- type: 'string',
- optional: false, nullable: false,
- format: 'url',
- example: 'https://misskey.example.com',
- },
- description: {
- type: 'string',
- optional: false, nullable: true,
- },
- langs: {
- type: 'array',
- optional: false, nullable: false,
- items: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
- tosUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- repositoryUrl: {
- type: 'string',
- optional: false, nullable: true,
- default: 'https://github.com/misskey-dev/misskey',
- },
- feedbackUrl: {
- type: 'string',
- optional: false, nullable: true,
- default: 'https://github.com/misskey-dev/misskey/issues/new',
- },
- defaultDarkTheme: {
- type: 'string',
- optional: false, nullable: true,
- },
- defaultLightTheme: {
- type: 'string',
- optional: false, nullable: true,
- },
- disableRegistration: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- cacheRemoteFiles: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- cacheRemoteSensitiveFiles: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- emailRequiredForSignup: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- enableHcaptcha: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- hcaptchaSiteKey: {
- type: 'string',
- optional: false, nullable: true,
- },
- enableMcaptcha: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- mcaptchaSiteKey: {
- type: 'string',
- optional: false, nullable: true,
- },
- mcaptchaInstanceUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- enableRecaptcha: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- recaptchaSiteKey: {
- type: 'string',
- optional: false, nullable: true,
- },
- enableTurnstile: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- turnstileSiteKey: {
- type: 'string',
- optional: false, nullable: true,
- },
- swPublickey: {
- type: 'string',
- optional: false, nullable: true,
- },
- mascotImageUrl: {
- type: 'string',
- optional: false, nullable: false,
- default: '/assets/ai.png',
- },
- bannerUrl: {
- type: 'string',
- optional: false, nullable: false,
- },
- serverErrorImageUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- infoImageUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- notFoundImageUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- iconUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- maxNoteTextLength: {
- type: 'number',
- optional: false, nullable: false,
- },
- ads: {
- type: 'array',
- optional: false, nullable: false,
- items: {
- type: 'object',
- optional: false, nullable: false,
- properties: {
- id: {
- type: 'string',
- optional: false, nullable: false,
- format: 'id',
- example: 'xxxxxxxxxx',
- },
- url: {
- type: 'string',
- optional: false, nullable: false,
- format: 'url',
- },
- place: {
- type: 'string',
- optional: false, nullable: false,
- },
- ratio: {
- type: 'number',
- optional: false, nullable: false,
- },
- imageUrl: {
- type: 'string',
- optional: false, nullable: false,
- format: 'url',
- },
- dayOfWeek: {
- type: 'integer',
- optional: false, nullable: false,
- },
- },
- },
- },
- notesPerOneAd: {
- type: 'number',
- optional: false, nullable: false,
- default: 0,
- },
- requireSetup: {
- type: 'boolean',
- optional: false, nullable: false,
- example: false,
- },
- enableEmail: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- enableServiceWorker: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- translatorAvailable: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- proxyAccountName: {
- type: 'string',
- optional: false, nullable: true,
- },
- mediaProxy: {
- type: 'string',
- optional: false, nullable: false,
- },
- features: {
- type: 'object',
- optional: true, nullable: false,
- properties: {
- registration: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- localTimeline: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- globalTimeline: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- hcaptcha: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- recaptcha: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- objectStorage: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- serviceWorker: {
- type: 'boolean',
- optional: false, nullable: false,
- },
- miauth: {
- type: 'boolean',
- optional: true, nullable: false,
- default: true,
- },
- },
- },
- backgroundImageUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- impressumUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- logoImageUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- privacyPolicyUrl: {
- type: 'string',
- optional: false, nullable: true,
- },
- serverRules: {
- type: 'array',
- optional: false, nullable: false,
- items: {
- type: 'string',
- },
- },
- themeColor: {
- type: 'string',
- optional: false, nullable: true,
- },
- policies: {
- type: 'object',
- optional: false, nullable: false,
- ref: 'RolePolicies',
- },
- },
+ oneOf: [
+ { type: 'object', ref: 'MetaLite' },
+ { type: 'object', ref: 'MetaDetailed' },
+ ],
},
} as const;
@@ -328,115 +32,10 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
- @Inject(DI.config)
- private config: Config,
-
- @Inject(DI.adsRepository)
- private adsRepository: AdsRepository,
-
- private userEntityService: UserEntityService,
- private metaService: MetaService,
- private instanceActorService: InstanceActorService,
+ private metaEntityService: MetaEntityService,
) {
super(meta, paramDef, async (ps, me) => {
- const instance = await this.metaService.fetch(true);
-
- const ads = await this.adsRepository.createQueryBuilder('ads')
- .where('ads.expiresAt > :now', { now: new Date() })
- .andWhere('ads.startsAt <= :now', { now: new Date() })
- .andWhere(new Brackets(qb => {
- // 曜日のビットフラグを確認する
- qb.where('ads.dayOfWeek & :dayOfWeek > 0', { dayOfWeek: 1 << new Date().getDay() })
- .orWhere('ads.dayOfWeek = 0');
- }))
- .getMany();
-
- const response: any = {
- maintainerName: instance.maintainerName,
- maintainerEmail: instance.maintainerEmail,
-
- version: this.config.version,
- providesTarball: this.config.publishTarballInsteadOfProvideRepositoryUrl,
-
- name: instance.name,
- shortName: instance.shortName,
- uri: this.config.url,
- description: instance.description,
- langs: instance.langs,
- tosUrl: instance.termsOfServiceUrl,
- repositoryUrl: instance.repositoryUrl,
- feedbackUrl: instance.feedbackUrl,
- impressumUrl: instance.impressumUrl,
- privacyPolicyUrl: instance.privacyPolicyUrl,
- disableRegistration: instance.disableRegistration,
- emailRequiredForSignup: instance.emailRequiredForSignup,
- enableHcaptcha: instance.enableHcaptcha,
- hcaptchaSiteKey: instance.hcaptchaSiteKey,
- enableMcaptcha: instance.enableMcaptcha,
- mcaptchaSiteKey: instance.mcaptchaSitekey,
- mcaptchaInstanceUrl: instance.mcaptchaInstanceUrl,
- enableRecaptcha: instance.enableRecaptcha,
- recaptchaSiteKey: instance.recaptchaSiteKey,
- enableTurnstile: instance.enableTurnstile,
- turnstileSiteKey: instance.turnstileSiteKey,
- swPublickey: instance.swPublicKey,
- themeColor: instance.themeColor,
- mascotImageUrl: instance.mascotImageUrl,
- bannerUrl: instance.bannerUrl,
- infoImageUrl: instance.infoImageUrl,
- serverErrorImageUrl: instance.serverErrorImageUrl,
- notFoundImageUrl: instance.notFoundImageUrl,
- iconUrl: instance.iconUrl,
- backgroundImageUrl: instance.backgroundImageUrl,
- logoImageUrl: instance.logoImageUrl,
- maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
- // クライアントの手間を減らすためあらかじめJSONに変換しておく
- defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
- defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
- ads: ads.map(ad => ({
- id: ad.id,
- url: ad.url,
- place: ad.place,
- ratio: ad.ratio,
- imageUrl: ad.imageUrl,
- dayOfWeek: ad.dayOfWeek,
- })),
- notesPerOneAd: instance.notesPerOneAd,
- enableEmail: instance.enableEmail,
- enableServiceWorker: instance.enableServiceWorker,
-
- translatorAvailable: instance.deeplAuthKey != null,
-
- serverRules: instance.serverRules,
-
- policies: { ...DEFAULT_POLICIES, ...instance.policies },
-
- mediaProxy: this.config.mediaProxy,
-
- ...(ps.detail ? {
- cacheRemoteFiles: instance.cacheRemoteFiles,
- cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
- requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
- } : {}),
- };
-
- if (ps.detail) {
- const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null;
-
- response.proxyAccountName = proxyAccount ? proxyAccount.username : null;
- response.features = {
- registration: !instance.disableRegistration,
- emailRequiredForSignup: instance.emailRequiredForSignup,
- hcaptcha: instance.enableHcaptcha,
- recaptcha: instance.enableRecaptcha,
- turnstile: instance.enableTurnstile,
- objectStorage: instance.useObjectStorage,
- serviceWorker: instance.enableServiceWorker,
- miauth: true,
- };
- }
-
- return response;
+ return ps.detail ? await this.metaEntityService.packDetailed() : await this.metaEntityService.pack();
});
}
}
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index f255e28fc2..e8908f50ec 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -28,6 +28,7 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { PageEntityService } from '@/core/entities/PageEntityService.js';
+import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
@@ -93,6 +94,7 @@ export class ClientServerService {
private userEntityService: UserEntityService,
private noteEntityService: NoteEntityService,
private pageEntityService: PageEntityService,
+ private metaEntityService: MetaEntityService,
private galleryPostEntityService: GalleryPostEntityService,
private clipEntityService: ClipEntityService,
private channelEntityService: ChannelEntityService,
@@ -173,7 +175,7 @@ export class ClientServerService {
}
@bindThis
- private generateCommonPugData(meta: MiMeta) {
+ private async generateCommonPugData(meta: MiMeta) {
return {
instanceName: meta.name ?? 'Misskey',
icon: meta.iconUrl,
@@ -183,6 +185,8 @@ export class ClientServerService {
infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
instanceUrl: this.config.url,
+ metaJson: JSON.stringify(await this.metaEntityService.packDetailed(meta)),
+ now: Date.now(),
};
}
@@ -433,7 +437,7 @@ export class ClientServerService {
url: this.config.url,
title: meta.name ?? 'Misskey',
desc: meta.description,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
};
@@ -520,7 +524,7 @@ export class ClientServerService {
user, profile, me,
avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
sub: request.params.sub,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
// リモートユーザーなので
@@ -570,7 +574,7 @@ export class ClientServerService {
avatarUrl: _note.user.avatarUrl,
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -609,7 +613,7 @@ export class ClientServerService {
page: _page,
profile,
avatarUrl: _page.user.avatarUrl,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -635,7 +639,7 @@ export class ClientServerService {
flash: _flash,
profile,
avatarUrl: _flash.user.avatarUrl,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -661,7 +665,7 @@ export class ClientServerService {
clip: _clip,
profile,
avatarUrl: _clip.user.avatarUrl,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -685,7 +689,7 @@ export class ClientServerService {
post: _post,
profile,
avatarUrl: _post.user.avatarUrl,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -704,7 +708,7 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=15');
return await reply.view('channel', {
channel: _channel,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -723,7 +727,7 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=3600');
return await reply.view('reversi-game', {
game: _game,
- ...this.generateCommonPugData(meta),
+ ...await this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index d167afe1e8..123336809b 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -68,6 +68,9 @@ html
var VERSION = "#{version}";
var CLIENT_ENTRY = "#{clientEntry.file}";
+ script(type='application/json' id='misskey_meta' data-generated-at=now)
+ != metaJson
+
script
include ../boot.js
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index b19d45a35e..61f04678bf 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -11,7 +11,7 @@ import { alert, confirm, popup, post, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import * as sound from '@/scripts/sound.js';
import { $i, signout, updateAccount } from '@/account.js';
-import { fetchInstance, instance } from '@/instance.js';
+import { instance } from '@/instance.js';
import { ColdDeviceStorage, defaultStore } from '@/store.js';
import { makeHotkey } from '@/scripts/hotkey.js';
import { reactionPicker } from '@/scripts/reaction-picker.js';
@@ -235,12 +235,10 @@ export async function mainBoot() {
}
}
- fetchInstance().then(() => {
- const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
- if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
- popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
- }
- });
+ const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read');
+ if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') {
+ popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, {}, 'closed');
+ }
if ('Notification' in window) {
// 許可を得ていなかったらリクエスト
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 2056023692..4232cbcd78 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -11,13 +11,24 @@ import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERR
// TODO: 他のタブと永続化されたstateを同期
-const cached = miLocalStorage.getItem('instance');
+//#region loader
+const providedMetaEl = document.getElementById('misskey_meta');
+
+let cachedMeta = miLocalStorage.getItem('instance') ? JSON.parse(miLocalStorage.getItem('instance')!) : null;
+let cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
+const providedMeta = providedMetaEl && providedMetaEl.textContent ? JSON.parse(providedMetaEl.textContent) : null;
+const providedAt = providedMetaEl && providedMetaEl.dataset.generatedAt ? parseInt(providedMetaEl.dataset.generatedAt) : 0;
+if (providedAt > cachedAt) {
+ miLocalStorage.setItem('instance', JSON.stringify(providedMeta));
+ miLocalStorage.setItem('instanceCachedAt', providedAt.toString());
+ cachedMeta = providedMeta;
+ cachedAt = providedAt;
+}
+//#endregion
// TODO: instanceをリアクティブにするかは再考の余地あり
-export const instance: Misskey.entities.MetaResponse = reactive(cached ? JSON.parse(cached) : {
- // TODO: set default values
-});
+export const instance: Misskey.entities.MetaResponse = reactive(cachedMeta ?? {});
export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL);
@@ -25,7 +36,15 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO
export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
-export async function fetchInstance() {
+export async function fetchInstance(force = false): Promise<void> {
+ if (!force) {
+ const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0;
+
+ if (Date.now() - cachedAt < 1000 * 60 * 60) {
+ return;
+ }
+ }
+
const meta = await misskeyApi('meta', {
detail: false,
});
@@ -35,4 +54,5 @@ export async function fetchInstance() {
}
miLocalStorage.setItem('instance', JSON.stringify(instance));
+ miLocalStorage.setItem('instanceCachedAt', Date.now().toString());
}
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 3de81c9bb9..8029bca68d 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -7,6 +7,7 @@ type Keys =
'v' |
'lastVersion' |
'instance' |
+ 'instanceCachedAt' |
'account' |
'accounts' |
'latestDonationInfoShownAt' |
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index e5e04fdeb8..73c5e1919f 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -142,7 +142,7 @@ function save() {
turnstileSiteKey: turnstileSiteKey.value,
turnstileSecretKey: turnstileSecretKey.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
</script>
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 2b559f92c9..fe1b7c561d 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -169,7 +169,7 @@ function save() {
feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value,
manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 839b9bee16..4a858887f3 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -124,7 +124,7 @@ function save() {
smtpUser: smtpUser.value,
smtpPass: smtpPass.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index ba3eb05e72..e0b82eb02e 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -61,7 +61,7 @@ function save() {
deeplAuthKey: deeplAuthKey.value,
deeplIsPro: deeplIsPro.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 5167b2e6b2..6b14bd42c2 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -50,7 +50,7 @@ function save() {
silencedHosts: silencedHosts.value.split('\n') || [],
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index d6cb1e39a7..9efb34ac9a 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -110,7 +110,7 @@ function save() {
hiddenTags: hiddenTags.value.split('\n'),
preservedUsernames: preservedUsernames.value.split('\n'),
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 4ff5ab09ca..5fddb715cd 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -143,7 +143,7 @@ function save() {
objectStorageSetPublicRead: objectStorageSetPublicRead.value,
objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index 651f0ef936..345cf333b5 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -73,7 +73,7 @@ function save() {
enableChartsForRemoteUser: enableChartsForRemoteUser.value,
enableChartsForFederatedInstances: enableChartsForFederatedInstances.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 02b506d13d..81db9f1da9 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -56,7 +56,7 @@ function save() {
os.apiWithDialog('admin/update-meta', {
proxyAccountId: proxyAccountId.value,
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index cadcf5a8cc..c4745978df 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -196,7 +196,7 @@ async function init() {
enableTruemailApi.value = meta.enableTruemailApi;
truemailInstance.value = meta.truemailInstance;
truemailAuthKey.value = meta.truemailAuthKey;
- bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || "";
+ bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || '';
}
function save() {
@@ -221,7 +221,7 @@ function save() {
truemailAuthKey: truemailAuthKey.value,
bannedEmailDomains: bannedEmailDomains.value.split('\n'),
}).then(() => {
- fetchInstance();
+ fetchInstance(true);
});
}
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 87318bccce..ff9b8d6299 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -58,7 +58,7 @@ const save = async () => {
await os.apiWithDialog('admin/update-meta', {
serverRules: serverRules.value,
});
- fetchInstance();
+ fetchInstance(true);
};
const remove = (index: number): void => {
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index c505d70aa9..9a198ee8a3 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -243,7 +243,7 @@ async function save(): void {
notesPerOneAd: notesPerOneAd.value,
});
- fetchInstance();
+ fetchInstance(true);
}
const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts
index f2db87c4fb..b20109ec72 100644
--- a/packages/frontend/src/scripts/clear-cache.ts
+++ b/packages/frontend/src/scripts/clear-cache.ts
@@ -2,14 +2,18 @@ import { unisonReload } from '@/scripts/unison-reload.js';
import * as os from '@/os.js';
import { miLocalStorage } from '@/local-storage.js';
import { fetchCustomEmojis } from '@/custom-emojis.js';
+import { fetchInstance } from '@/instance.js';
export async function clearCache() {
os.waiting();
+ miLocalStorage.removeItem('instance');
+ miLocalStorage.removeItem('instanceCachedAt');
miLocalStorage.removeItem('locale');
miLocalStorage.removeItem('localeVersion');
miLocalStorage.removeItem('theme');
miLocalStorage.removeItem('emojis');
miLocalStorage.removeItem('lastEmojisFetchedAt');
+ await fetchInstance(true);
await fetchCustomEmojis(true);
unisonReload();
}
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index c2428910fa..a2d5a4f514 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1715,7 +1715,10 @@ declare namespace entities {
Role,
RolePolicies,
ReversiGameLite,
- ReversiGameDetailed
+ ReversiGameDetailed,
+ MetaLite,
+ MetaDetailedOnly,
+ MetaDetailed
}
}
export { entities }
@@ -2224,6 +2227,15 @@ type MeDetailed = components['schemas']['MeDetailed'];
type MeDetailedOnly = components['schemas']['MeDetailedOnly'];
// @public (undocumented)
+type MetaDetailed = components['schemas']['MetaDetailed'];
+
+// @public (undocumented)
+type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
+
+// @public (undocumented)
+type MetaLite = components['schemas']['MetaLite'];
+
+// @public (undocumented)
type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
// @public (undocumented)
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 6400567a2d..ab49f9478a 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -46,3 +46,6 @@ export type Role = components['schemas']['Role'];
export type RolePolicies = components['schemas']['RolePolicies'];
export type ReversiGameLite = components['schemas']['ReversiGameLite'];
export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
+export type MetaLite = components['schemas']['MetaLite'];
+export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
+export type MetaDetailed = components['schemas']['MetaDetailed'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 0b2a88b537..18bc45b983 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -4724,6 +4724,96 @@ export type components = {
logs: number[][];
map: string[];
};
+ MetaLite: {
+ maintainerName: string | null;
+ maintainerEmail: string | null;
+ version: string;
+ providesTarball: boolean;
+ name: string | null;
+ shortName: string | null;
+ /**
+ * Format: url
+ * @example https://misskey.example.com
+ */
+ uri: string;
+ description: string | null;
+ langs: string[];
+ tosUrl: string | null;
+ /** @default https://github.com/misskey-dev/misskey */
+ repositoryUrl: string | null;
+ /** @default https://github.com/misskey-dev/misskey/issues/new */
+ feedbackUrl: string | null;
+ defaultDarkTheme: string | null;
+ defaultLightTheme: string | null;
+ disableRegistration: boolean;
+ emailRequiredForSignup: boolean;
+ enableHcaptcha: boolean;
+ hcaptchaSiteKey: string | null;
+ enableMcaptcha: boolean;
+ mcaptchaSiteKey: string | null;
+ mcaptchaInstanceUrl: string | null;
+ enableRecaptcha: boolean;
+ recaptchaSiteKey: string | null;
+ enableTurnstile: boolean;
+ turnstileSiteKey: string | null;
+ swPublickey: string | null;
+ /** @default /assets/ai.png */
+ mascotImageUrl: string;
+ bannerUrl: string | null;
+ serverErrorImageUrl: string | null;
+ infoImageUrl: string | null;
+ notFoundImageUrl: string | null;
+ iconUrl: string | null;
+ maxNoteTextLength: number;
+ ads: {
+ /**
+ * Format: id
+ * @example xxxxxxxxxx
+ */
+ id: string;
+ /** Format: url */
+ url: string;
+ place: string;
+ ratio: number;
+ /** Format: url */
+ imageUrl: string;
+ dayOfWeek: number;
+ }[];
+ /** @default 0 */
+ notesPerOneAd: number;
+ enableEmail: boolean;
+ enableServiceWorker: boolean;
+ translatorAvailable: boolean;
+ mediaProxy: string;
+ backgroundImageUrl: string | null;
+ impressumUrl: string | null;
+ logoImageUrl: string | null;
+ privacyPolicyUrl: string | null;
+ serverRules: string[];
+ themeColor: string | null;
+ policies: components['schemas']['RolePolicies'];
+ };
+ MetaDetailedOnly: {
+ features?: {
+ registration: boolean;
+ emailRequiredForSignup: boolean;
+ localTimeline: boolean;
+ globalTimeline: boolean;
+ hcaptcha: boolean;
+ turnstile: boolean;
+ recaptcha: boolean;
+ objectStorage: boolean;
+ serviceWorker: boolean;
+ /** @default true */
+ miauth?: boolean;
+ };
+ proxyAccountName: string | null;
+ /** @example false */
+ requireSetup: boolean;
+ cacheRemoteFiles: boolean;
+ cacheRemoteSensitiveFiles: boolean;
+ };
+ MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly'];
};
responses: never;
parameters: never;
@@ -19448,91 +19538,7 @@ export type operations = {
/** @description OK (with results) */
200: {
content: {
- 'application/json': {
- maintainerName: string | null;
- maintainerEmail: string | null;
- version: string;
- providesTarball: boolean;
- name: string;
- shortName: string | null;
- /**
- * Format: url
- * @example https://misskey.example.com
- */
- uri: string;
- description: string | null;
- langs: string[];
- tosUrl: string | null;
- /** @default https://github.com/misskey-dev/misskey */
- repositoryUrl: string | null;
- /** @default https://github.com/misskey-dev/misskey/issues/new */
- feedbackUrl: string | null;
- defaultDarkTheme: string | null;
- defaultLightTheme: string | null;
- disableRegistration: boolean;
- cacheRemoteFiles: boolean;
- cacheRemoteSensitiveFiles: boolean;
- emailRequiredForSignup: boolean;
- enableHcaptcha: boolean;
- hcaptchaSiteKey: string | null;
- enableMcaptcha: boolean;
- mcaptchaSiteKey: string | null;
- mcaptchaInstanceUrl: string | null;
- enableRecaptcha: boolean;
- recaptchaSiteKey: string | null;
- enableTurnstile: boolean;
- turnstileSiteKey: string | null;
- swPublickey: string | null;
- /** @default /assets/ai.png */
- mascotImageUrl: string;
- bannerUrl: string;
- serverErrorImageUrl: string | null;
- infoImageUrl: string | null;
- notFoundImageUrl: string | null;
- iconUrl: string | null;
- maxNoteTextLength: number;
- ads: {
- /**
- * Format: id
- * @example xxxxxxxxxx
- */
- id: string;
- /** Format: url */
- url: string;
- place: string;
- ratio: number;
- /** Format: url */
- imageUrl: string;
- dayOfWeek: number;
- }[];
- /** @default 0 */
- notesPerOneAd: number;
- /** @example false */
- requireSetup: boolean;
- enableEmail: boolean;
- enableServiceWorker: boolean;
- translatorAvailable: boolean;
- proxyAccountName: string | null;
- mediaProxy: string;
- features?: {
- registration: boolean;
- localTimeline: boolean;
- globalTimeline: boolean;
- hcaptcha: boolean;
- recaptcha: boolean;
- objectStorage: boolean;
- serviceWorker: boolean;
- /** @default true */
- miauth?: boolean;
- };
- backgroundImageUrl: string | null;
- impressumUrl: string | null;
- logoImageUrl: string | null;
- privacyPolicyUrl: string | null;
- serverRules: string[];
- themeColor: string | null;
- policies: components['schemas']['RolePolicies'];
- };
+ 'application/json': components['schemas']['MetaLite'] | components['schemas']['MetaDetailed'];
};
};
/** @description Client error */