Files
misskey/packages/backend/src/server/api/endpoints/meta.ts
yukineko a6a91fec3a refactor: frontendのcomponentsの型エラーを改善 (#12926)
* add: safeFloatParserを追加

* fix: 欠けていた型を追加

* refactor: pageBlockTypesをjson-schemaに移植

* refactor: components/global内の型エラーが出ている箇所を修正

* lint: fix null check style

* refactor: fix type error

* refactor: fix some type errors

* fix: 翻訳が抜けていた箇所を修正

* refactor: getJsonSchemaで正しいスキーマが返されるように修正

* fix: MkChartの型エラーとbytesオプションが機能していない問題を修正

* fix(misskey-js): `drive`->`folderUpdated`のpayloadの型が間違っていたのを修正

* refactor: fix some type errors

* change: Captcha読み込み中の文言をLoadingに変更

* refactor(backend/misskey-js): MainEventの型を改善

* refactor: chartjs-plugin-gradientが二重でpluginに登録されていたのを修正

* update: misskey-js.api.md

* refactor: fix some type errors

* fix: backendのtypecheckが落ちていたのを修正

* update: misskey-js.api.md

* add: json-schemaのnoteにpollの型定義を追加

* refactor: noteのjson-schemaの型を改善

* refactor: MkPoll

* refactor: fix some type errors

* change: UserLiteにisLockedを持たせるように

* fix: notificationスキーマにroleが含まれていないのを修正

* Revert "change: UserLiteにisLockedを持たせるように"

This reverts commit 1bb0c8e7a9b19a4e9f21bf7381712b98f27672a5.

* fix: フォロー通知から鍵垢へのフォローを行うと処理中のまま止まってしまう問題を修正

* refactor: noteスキーマのvisibilityにenumを追加

* change: deepCloneのCloneableTypeにundefinedを追加

* refactor: fix some type errors

* refactor: `allowEmpty: false`を使用していた箇所を`minLength: 1`に置き換え

* enhance: API 'retension' のresponseの型を追加

* fix: Chart関連のtooltipが正しい位置に表示されない問題を修正

* refactor: fix some type errors

* fix: 型情報が不足していたのを修正

* enhance: announcementスキーマにenumを追加

* enhance: ロールポリシーの型定義をRoleServiceからjson-schemaに移植

* refactor: policiesを`ref: RolePolicies`に統一

* fix: API `meta` のレスポンスの型にpoliciesが含まれていないのを修正

* refactor: fix some type errors

* fix: backendのlintが落ちているのを修正

* fix: MkFoldableSectionの開閉時のanimationが適用されていない問題を修正

* fix: backendのtypecheckが落ちているのを修正

* update: run build-misskey-js-with-types

* fix: MkDialogのmount時に文字数制限の判定が行われない問題を修正

* update: CHANGELOG.md

* refactor: MkUserSelectDialogの型を改善

* fix: deepCloneでundefinedはcloneしないように (#9207)

* change: frontendのcloneをbackend側にも反映

* update: CHANGELOG.md

* fix: RoleServiceからPackを通して型RolePoliciesに依存させないように

* Update packages/frontend/src/scripts/get-note-summary.ts

* revert RoleService.ts changes

* change:  optional chaining -> non-null assertion

* remove: unused import

* fix: propsで渡されたuserがUserLiteの場合に意図しない動作になってしまうのを修正

* change: fix null check style

* refactor: fix type error

* change: fix null check style

* Update packages/frontend/src/components/MkDrive.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* refactor: css moduleでglobalを使わないように

* refactor: roleのiconUrlは必ず存在するものとして扱うように

* enhance: MenuButtonのactiveにcomputedを受け付けられるように

* Update packages/frontend/src/components/MkNotePreview.vue

* Update MkWindow.vue

* refactor: notification.noteは必ず存在するものとして扱うように

* Update packages/frontend/src/components/MkNotification.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* fix: MkSignupDialogでdoneのemit時にresを含んでいなかったのを修正

* Update packages/frontend/src/scripts/clone.ts

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* refactor: 不要な返り値の型を削除

* refactor: 不要なnullチェックを削除

* update: misskey-js-autogen

* update: clone.ts

* refactor

* Update MkNotification.vue

* Update MkNotification.vue

* ✌️

* Update MkNotification.vue

* Update MkNotification.vue

* Update MkNotification.vue

* Update MkNotifications.vue

* Update MkUserSetupDialog.Profile.vue

* Update MkUserCardMini.vue

* ✌️

* Update MkMenu.vue

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2024-01-30 19:53:53 +09:00

441 lines
11 KiB
TypeScript

/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* 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, UsersRepository } from '@/models/_.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { MetaService } from '@/core/MetaService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
export const meta = {
tags: ['meta'],
requireCredential: false,
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,
},
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: false,
default: 'https://github.com/misskey-dev/misskey',
},
feedbackUrl: {
type: 'string',
optional: false, nullable: false,
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',
},
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
detail: { type: 'boolean', default: true },
},
required: [],
} as const;
@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.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.adsRepository)
private adsRepository: AdsRepository,
private userEntityService: UserEntityService,
private metaService: MetaService,
) {
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,
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.usersRepository.countBy({
host: IsNull(),
})) === 0,
} : {}),
};
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;
});
}
}