merge: upstream

This commit is contained in:
Marie 2023-12-23 14:26:24 +01:00
commit b3b89567ee
15 changed files with 80 additions and 12 deletions

1
.npmrc
View File

@ -1 +1,2 @@
@sharkey:registry=https://git.joinsharkey.org/api/packages/Sharkey/npm/ @sharkey:registry=https://git.joinsharkey.org/api/packages/Sharkey/npm/
engine-strict = true

View File

@ -15,22 +15,23 @@
## 2023.12.0 ## 2023.12.0
### Note ### Note
- Node.js 20.10.0が最小要件になりました - 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました
- 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします - 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。 - 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
**影響:** **影響:**
それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された投稿用のピン留め絵文字が使われるため)。 それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。
投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。 投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。
1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。 1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。
2. 「ピン留 (全般)」のタブを選択します。 2. 「ピン留 (全般)」のタブを選択します。
3. 「リアクション設定からコピーする」ボタンを押すことで、アップデート前の状態に戻すことができます。 3. 「リアクション設定から上書きする」ボタンを押すことで、アップデート前の状態に戻すことができます。
### General ### General
- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed) - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83) - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
- Feat: TL上からートが見えなくなるワードミュートであるハードミュートを追加 - Feat: TL上からートが見えなくなるワードミュートであるハードミュートを追加
- Enhance: 指定したドメインのメールアドレスの登録を弾くことができるように
- Enhance: 公開ロールにアサインされたときに通知が作成されるように - Enhance: 公開ロールにアサインされたときに通知が作成されるように
- Enhance: アイコンデコレーションを複数設定できるように - Enhance: アイコンデコレーションを複数設定できるように
- Enhance: アイコンデコレーションの位置を微調整できるように - Enhance: アイコンデコレーションの位置を微調整できるように
@ -72,6 +73,7 @@
- Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる - Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる
- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加 - Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加
- Enhance: 設定したタグをトレンドに表示させないようにする項目を管理画面で設定できるように - Enhance: 設定したタグをトレンドに表示させないようにする項目を管理画面で設定できるように
- Enhance: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように
- Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正 - Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
- Fix: コードエディタが正しく表示されない問題を修正 - Fix: コードエディタが正しく表示されない問題を修正
@ -242,7 +244,6 @@
### Client ### Client
- Enhance: TLの返信表示オプションを記憶するように - Enhance: TLの返信表示オプションを記憶するように
- Enhance: 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく - Enhance: 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく
- Feat: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように
### Server ### Server
- Enhance: タイムライン取得時のパフォーマンスを向上 - Enhance: タイムライン取得時のパフォーマンスを向上

View File

@ -1917,6 +1917,7 @@ _notification:
pollEnded: "Sondages se cloturant" pollEnded: "Sondages se cloturant"
receiveFollowRequest: "Demande d'abonnement reçue" receiveFollowRequest: "Demande d'abonnement reçue"
followRequestAccepted: "Demande d'abonnement acceptée" followRequestAccepted: "Demande d'abonnement acceptée"
roleAssigned: "Rôle reçu"
achievementEarned: "Accomplissement" achievementEarned: "Accomplissement"
app: "Notifications provenant des apps" app: "Notifications provenant des apps"
_actions: _actions:

1
locales/index.d.ts vendored
View File

@ -1783,6 +1783,7 @@ export interface Locale {
"disposable": string; "disposable": string;
"mx": string; "mx": string;
"smtp": string; "smtp": string;
"banned": string;
}; };
"_ffVisibility": { "_ffVisibility": {
"public": string; "public": string;

View File

@ -1690,6 +1690,7 @@ _emailUnavailable:
disposable: "恒久的に使用可能なアドレスではありません" disposable: "恒久的に使用可能なアドレスではありません"
mx: "正しいメールサーバーではありません" mx: "正しいメールサーバーではありません"
smtp: "メールサーバーが応答しません" smtp: "メールサーバーが応答しません"
banned: "このメールアドレスでは登録できません"
_ffVisibility: _ffVisibility:
public: "公開" public: "公開"

View File

@ -2193,6 +2193,7 @@ _notification:
pollEnded: "투표가 종료됨" pollEnded: "투표가 종료됨"
receiveFollowRequest: "팔로우 요청을 받았을 때" receiveFollowRequest: "팔로우 요청을 받았을 때"
followRequestAccepted: "팔로우 요청이 승인되었을 때" followRequestAccepted: "팔로우 요청이 승인되었을 때"
roleAssigned: "역할이 부여 됨"
achievementEarned: "도전 과제 획득" achievementEarned: "도전 과제 획득"
app: "연동된 앱을 통한 알림" app: "연동된 앱을 통한 알림"
_actions: _actions:

View File

@ -2193,6 +2193,7 @@ _notification:
pollEnded: "問卷調查結束" pollEnded: "問卷調查結束"
receiveFollowRequest: "已收到追隨請求" receiveFollowRequest: "已收到追隨請求"
followRequestAccepted: "追隨請求已接受" followRequestAccepted: "追隨請求已接受"
roleAssigned: "已授予角色"
achievementEarned: "獲得成就" achievementEarned: "獲得成就"
app: "應用程式通知" app: "應用程式通知"
_actions: _actions:

View File

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class bannedEmailDomains1703209889304 {
constructor() {
this.name = 'bannedEmailDomains1703209889304';
}
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "bannedEmailDomains" character varying(1024) array NOT NULL DEFAULT '{}'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bannedEmailDomains"`);
}
}

View File

@ -9,6 +9,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { validate as validateEmail } from 'deep-email-validator'; import { validate as validateEmail } from 'deep-email-validator';
import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js'; import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
@ -30,6 +31,7 @@ export class EmailService {
private metaService: MetaService, private metaService: MetaService,
private loggerService: LoggerService, private loggerService: LoggerService,
private utilityService: UtilityService,
private httpRequestService: HttpRequestService, private httpRequestService: HttpRequestService,
) { ) {
this.logger = this.loggerService.getLogger('email'); this.logger = this.loggerService.getLogger('email');
@ -155,7 +157,7 @@ export class EmailService {
@bindThis @bindThis
public async validateEmailForAccount(emailAddress: string): Promise<{ public async validateEmailForAccount(emailAddress: string): Promise<{
available: boolean; available: boolean;
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned';
}> { }> {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
@ -164,7 +166,6 @@ export class EmailService {
email: emailAddress, email: emailAddress,
}); });
const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
let validated; let validated;
if (meta.enableActiveEmailValidation) { if (meta.enableActiveEmailValidation) {
@ -184,12 +185,16 @@ export class EmailService {
validated = { valid: true, reason: null }; validated = { valid: true, reason: null };
} }
const available = exist === 0 && validated.valid; const emailDomain: string = emailAddress.split('@')[1];
const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain);
const available = exist === 0 && validated.valid && !isBanned;
return { return {
available, available,
reason: available ? null : reason: available ? null :
exist !== 0 ? 'used' : exist !== 0 ? 'used' :
isBanned ? 'banned' :
validated.reason === 'regex' ? 'format' : validated.reason === 'regex' ? 'format' :
validated.reason === 'disposable' ? 'disposable' : validated.reason === 'disposable' ? 'disposable' :
validated.reason === 'mx' ? 'mx' : validated.reason === 'mx' ? 'mx' :

View File

@ -462,7 +462,7 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
// Check blocking // Check blocking
if (this.isQuote(data)) { if (data.renote && !this.isQuote(data)) {
if (data.renote.userHost === null) { if (data.renote.userHost === null) {
if (data.renote.userId !== user.id) { if (data.renote.userId !== user.id) {
const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id); const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);

View File

@ -510,6 +510,13 @@ export class MiMeta {
}) })
public manifestJsonOverride: string; public manifestJsonOverride: string;
@Column('varchar', {
length: 1024,
array: true,
default: '{}',
})
public bannedEmailDomains: string[];
@Column('varchar', { @Column('varchar', {
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" }',
}) })

View File

@ -149,6 +149,14 @@ export const meta = {
type: 'string', type: 'string',
}, },
}, },
bannedEmailDomains: {
type: 'array',
optional: true, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
preservedUsernames: { preservedUsernames: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,
@ -537,6 +545,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
enableServerMachineStats: instance.enableServerMachineStats, enableServerMachineStats: instance.enableServerMachineStats,
enableAchievements: instance.enableAchievements, enableAchievements: instance.enableAchievements,
enableIdenticonGeneration: instance.enableIdenticonGeneration, enableIdenticonGeneration: instance.enableIdenticonGeneration,
bannedEmailDomains: instance.bannedEmailDomains,
policies: { ...DEFAULT_POLICIES, ...instance.policies }, policies: { ...DEFAULT_POLICIES, ...instance.policies },
manifestJsonOverride: instance.manifestJsonOverride, manifestJsonOverride: instance.manifestJsonOverride,
enableFanoutTimeline: instance.enableFanoutTimeline, enableFanoutTimeline: instance.enableFanoutTimeline,

View File

@ -126,6 +126,7 @@ export const paramDef = {
enableAchievements: { type: 'boolean' }, enableAchievements: { type: 'boolean' },
enableIdenticonGeneration: { type: 'boolean' }, enableIdenticonGeneration: { type: 'boolean' },
serverRules: { type: 'array', items: { type: 'string' } }, serverRules: { type: 'array', items: { type: 'string' } },
bannedEmailDomains: { type: 'array', items: { type: 'string' } },
preservedUsernames: { type: 'array', items: { type: 'string' } }, preservedUsernames: { type: 'array', items: { type: 'string' } },
bubbleInstances: { type: 'array', items: { type: 'string' } }, bubbleInstances: { type: 'array', items: { type: 'string' } },
manifestJsonOverride: { type: 'string' }, manifestJsonOverride: { type: 'string' },
@ -535,6 +536,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.notesPerOneAd = ps.notesPerOneAd; set.notesPerOneAd = ps.notesPerOneAd;
} }
if (ps.bannedEmailDomains !== undefined) {
set.bannedEmailDomains = ps.bannedEmailDomains;
}
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

@ -38,6 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span> <span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span> <span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span> <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
<span v-else-if="emailState === 'unavailable:banned'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.banned }}</span>
<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span> <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span> <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts.unavailable }}</span> <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ph-warning ph-bold ph-lg ti-fw"></i> {{ i18n.ts.unavailable }}</span>
@ -116,7 +117,7 @@ const invitationCode = ref<string>('');
const reason = ref<string>(''); const reason = ref<string>('');
const email = ref(''); const email = ref('');
const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null); const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null); const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:banned' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>(''); const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
const passwordRetypeState = ref<null | 'match' | 'not-match'>(null); const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
const submitting = ref<boolean>(false); const submitting = ref<boolean>(false);
@ -215,6 +216,7 @@ function onChangeEmail(): void {
result.reason === 'used' ? 'unavailable:used' : result.reason === 'used' ? 'unavailable:used' :
result.reason === 'format' ? 'unavailable:format' : result.reason === 'format' ? 'unavailable:format' :
result.reason === 'disposable' ? 'unavailable:disposable' : result.reason === 'disposable' ? 'unavailable:disposable' :
result.reason === 'banned' ? 'unavailable:banned' :
result.reason === 'mx' ? 'unavailable:mx' : result.reason === 'mx' ? 'unavailable:mx' :
result.reason === 'smtp' ? 'unavailable:smtp' : result.reason === 'smtp' ? 'unavailable:smtp' :
'unavailable'; 'unavailable';

View File

@ -31,15 +31,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>Enable</template> <template #label>Enable</template>
</MkSwitch> </MkSwitch>
<MkSwitch v-model="enableVerifymailApi" @update:modelValue="save"> <MkSwitch v-model="enableVerifymailApi" @update:modelValue="save">
<template #label>Use Verifymail API</template> <template #label>Use Verifymail.io API</template>
</MkSwitch> </MkSwitch>
<MkInput v-model="verifymailAuthKey" @update:modelValue="save"> <MkInput v-model="verifymailAuthKey" @update:modelValue="save">
<template #prefix><i class="ti ti-key"></i></template> <template #prefix><i class="ti ti-key"></i></template>
<template #label>Verifymail API Auth Key</template> <template #label>Verifymail.io API Auth Key</template>
</MkInput> </MkInput>
</div> </div>
</MkFolder> </MkFolder>
<MkFolder>
<template #label>Banned Email Domains</template>
<div class="_gaps_m">
<MkTextarea v-model="bannedEmailDomains">
<template #label>Banned Email Domains List</template>
</MkTextarea>
<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</div>
</MkFolder>
<MkFolder> <MkFolder>
<template #label>Log IP address</template> <template #label>Log IP address</template>
<template v-if="enableIpLogging" #suffix>Enabled</template> <template v-if="enableIpLogging" #suffix>Enabled</template>
@ -81,6 +92,7 @@ import FormSuspense from '@/components/form/suspense.vue';
import MkRange from '@/components/MkRange.vue'; import MkRange from '@/components/MkRange.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import * as os from '@/os.js'; import * as os from '@/os.js';
import { fetchInstance } from '@/instance.js'; import { fetchInstance } from '@/instance.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
@ -94,6 +106,7 @@ const enableIpLogging = ref<boolean>(false);
const enableActiveEmailValidation = ref<boolean>(false); const enableActiveEmailValidation = ref<boolean>(false);
const enableVerifymailApi = ref<boolean>(false); const enableVerifymailApi = ref<boolean>(false);
const verifymailAuthKey = ref<string | null>(null); const verifymailAuthKey = ref<string | null>(null);
const bannedEmailDomains = ref<string>('');
async function init() { async function init() {
const meta = await os.api('admin/meta'); const meta = await os.api('admin/meta');
@ -105,6 +118,7 @@ async function init() {
enableActiveEmailValidation.value = meta.enableActiveEmailValidation; enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
enableVerifymailApi.value = meta.enableVerifymailApi; enableVerifymailApi.value = meta.enableVerifymailApi;
verifymailAuthKey.value = meta.verifymailAuthKey; verifymailAuthKey.value = meta.verifymailAuthKey;
bannedEmailDomains.value = meta.bannedEmailDomains.join('\n');
} }
function save() { function save() {
@ -114,6 +128,7 @@ function save() {
enableActiveEmailValidation: enableActiveEmailValidation.value, enableActiveEmailValidation: enableActiveEmailValidation.value,
enableVerifymailApi: enableVerifymailApi.value, enableVerifymailApi: enableVerifymailApi.value,
verifymailAuthKey: verifymailAuthKey.value, verifymailAuthKey: verifymailAuthKey.value,
bannedEmailDomains: bannedEmailDomains.value.split('\n'),
}).then(() => { }).then(() => {
fetchInstance(); fetchInstance();
}); });