Compare commits
	
		
			9 Commits
		
	
	
		
			13.11.0-be
			...
			13.11.0-be
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | e72d6c9f2c | ||
|   | e23cf77f86 | ||
|   | 0f328e8472 | ||
|   | 7636536973 | ||
|   | 975e79fa3b | ||
|   | eefa06622b | ||
|   | 9114c8cb8e | ||
|   | 132feea1fa | ||
|   | 33e9428510 | 
| @@ -51,6 +51,23 @@ db: | ||||
|   #extra: | ||||
|   #  ssl: true | ||||
|  | ||||
| dbReplications: false | ||||
|  | ||||
| # You can configure any number of replicas here | ||||
| #dbSlaves: | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
|  | ||||
| #   ┌─────────────────────┐ | ||||
| #───┘ Redis configuration └───────────────────────────────────── | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,23 @@ db: | ||||
|   #extra: | ||||
|   #  ssl: true | ||||
|  | ||||
| dbReplications: false | ||||
|  | ||||
| # You can configure any number of replicas here | ||||
| #dbSlaves: | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
|  | ||||
| #   ┌─────────────────────┐ | ||||
| #───┘ Redis configuration └───────────────────────────────────── | ||||
|  | ||||
|   | ||||
| @@ -51,6 +51,23 @@ db: | ||||
|   #extra: | ||||
|   #  ssl: true | ||||
|  | ||||
| dbReplications: false | ||||
|  | ||||
| # You can configure any number of replicas here | ||||
| #dbSlaves: | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
|  | ||||
| #   ┌─────────────────────┐ | ||||
| #───┘ Redis configuration └───────────────────────────────────── | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| - アカウントの引っ越し(フォロワー引き継ぎ)に対応 | ||||
|  | ||||
| ### Client | ||||
| - 投稿フォームのデザインを改善 | ||||
| - 検索ページでURLを入力した際に照会したときと同等の挙動をするように | ||||
| - ノートのリアクションを大きく表示するオプションを追加 | ||||
| - ギャラリー一覧にメディア表示と同じように NSFW 設定を反映するように(ホバーで表示) | ||||
| @@ -38,6 +39,8 @@ | ||||
| - Add Minimizing ("folding") of windows | ||||
|  | ||||
| ### Server | ||||
| - PostgreSQLのレプリケーション対応 | ||||
| 	- 設定ファイルの `dbReplications` および `dbSlaves` にて設定できます | ||||
| - イベント用Redisを別サーバーに分離できるように | ||||
| - ジョブキュー用Redisを別サーバーに分離できるように | ||||
| - サーバーの全体的なパフォーマンスを向上 | ||||
|   | ||||
| @@ -72,6 +72,23 @@ db: | ||||
|   #extra: | ||||
|   #  ssl: true | ||||
|  | ||||
| dbReplications: false | ||||
|  | ||||
| # You can configure any number of replicas here | ||||
| #dbSlaves: | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
| #  - | ||||
| #    host:  | ||||
| #    port:  | ||||
| #    db:  | ||||
| #    user:  | ||||
| #    pass:  | ||||
|  | ||||
| #   ┌─────────────────────┐ | ||||
| #───┘ Redis configuration └───────────────────────────────────── | ||||
|  | ||||
|   | ||||
| @@ -989,16 +989,18 @@ enableChartsForFederatedInstances: "リモートサーバーのチャートを | ||||
| showClipButtonInNoteFooter: "ノートのアクションにクリップを追加" | ||||
| largeNoteReactions: "ノートのリアクションを大きく表示" | ||||
| noteIdOrUrl: "ノートIDまたはURL" | ||||
| migration: "アカウントの引っ越し" | ||||
| moveTo: "このアカウントを新しいアカウントに引っ越す" | ||||
| moveToLabel: "引っ越し先のアカウント:" | ||||
| moveAccountDescription: "この操作は取り消せません。まずは引っ越し先のアカウントでこのアカウントに対しエイリアスを作成したことを確認してください。エイリアス作成後、引っ越し先のアカウントをこのように入力してください:@person@instance.com" | ||||
| moveFrom: "別のアカウントからこのアカウントに引っ越す" | ||||
| moveFromLabel: "引っ越し元のアカウント:" | ||||
| moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。必ず引っ越しを実行する前に作成してください!引っ越し元のアカウントをこのように入力してください:@person@instance.com" | ||||
| migrationConfirm: "本当にこのアカウントを {account} に引っ越しますか?一度引っ越しを行うと取り消せず、二度とこのアカウントを元の状態で使用できなくなります。\nまた、引っ越し先のアカウントでエイリアスを作成したことを確認してください。" | ||||
| accountMigration: "アカウントの引っ越し" | ||||
| accountMoved: "このユーザーは新しいアカウントに引っ越しました:" | ||||
|  | ||||
| _accountMigration: | ||||
|   moveTo: "このアカウントを新しいアカウントに引っ越す" | ||||
|   moveToLabel: "引っ越し先のアカウント:" | ||||
|   moveAccountDescription: "この操作は取り消せません。まずは引っ越し先のアカウントでこのアカウントに対しエイリアスを作成したことを確認してください。エイリアス作成後、引っ越し先のアカウントをこのように入力してください:@person@instance.com" | ||||
|   moveFrom: "別のアカウントからこのアカウントに引っ越す" | ||||
|   moveFromLabel: "引っ越し元のアカウント:" | ||||
|   moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。必ず引っ越しを実行する前に作成してください!引っ越し元のアカウントをこのように入力してください:@person@instance.com" | ||||
|   migrationConfirm: "本当にこのアカウントを {account} に引っ越しますか?一度引っ越しを行うと取り消せず、二度とこのアカウントを元の状態で使用できなくなります。\nまた、引っ越し先のアカウントでエイリアスを作成したことを確認してください。" | ||||
|  | ||||
| _achievements: | ||||
|   earnedAt: "獲得日時" | ||||
|   _types: | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
| 	"name": "misskey", | ||||
| 	"version": "13.11.0-beta.7", | ||||
| 	"version": "13.11.0-beta.8", | ||||
| 	"codename": "nasubi", | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
|   | ||||
| @@ -25,6 +25,14 @@ export type Source = { | ||||
| 		disableCache?: boolean; | ||||
| 		extra?: { [x: string]: string }; | ||||
| 	}; | ||||
| 	dbReplications?: boolean; | ||||
| 	dbSlaves?: { | ||||
| 		host: string; | ||||
| 		port: number; | ||||
| 		db: string; | ||||
| 		user: string; | ||||
| 		pass: string; | ||||
| 	}[]; | ||||
| 	redis: { | ||||
| 		host: string; | ||||
| 		port: number; | ||||
|   | ||||
| @@ -736,13 +736,17 @@ export class ApInboxService { | ||||
| 		// fetch the new and old accounts | ||||
| 		const targetUri = getApHrefNullable(activity.target); | ||||
| 		if (!targetUri) return 'skip: invalid activity target'; | ||||
| 		const new_acc = await this.apPersonService.resolvePerson(targetUri); | ||||
| 		const old_acc = await this.apPersonService.resolvePerson(actor.uri); | ||||
| 		let new_acc = await this.apPersonService.resolvePerson(targetUri); | ||||
| 		let old_acc = await this.apPersonService.resolvePerson(actor.uri); | ||||
|  | ||||
| 		// update them if they're remote | ||||
| 		if (new_acc.uri) await this.apPersonService.updatePerson(new_acc.uri); | ||||
| 		if (old_acc.uri) await this.apPersonService.updatePerson(old_acc.uri); | ||||
|  | ||||
| 		// retrieve updated users | ||||
| 		new_acc = await this.apPersonService.resolvePerson(targetUri); | ||||
| 		old_acc = await this.apPersonService.resolvePerson(actor.uri); | ||||
|  | ||||
| 		// check if alsoKnownAs of the new account is valid | ||||
| 		let isValidMove = true; | ||||
| 		if (old_acc.uri) { | ||||
|   | ||||
| @@ -200,6 +200,22 @@ export function createPostgresDataSource(config: Config) { | ||||
| 			statement_timeout: 1000 * 10, | ||||
| 			...config.db.extra, | ||||
| 		}, | ||||
| 		replication: config.dbReplications ? { | ||||
| 			master: { | ||||
| 				host: config.db.host, | ||||
| 				port: config.db.port, | ||||
| 				username: config.db.user, | ||||
| 				password: config.db.pass, | ||||
| 				database: config.db.db, | ||||
| 			}, | ||||
| 			slaves: config.dbSlaves!.map(rep => ({ | ||||
| 				host: rep.host, | ||||
| 				port: rep.port, | ||||
| 				username: rep.user, | ||||
| 				password: rep.pass, | ||||
| 				database: rep.db, | ||||
| 			})), | ||||
| 		} : undefined, | ||||
| 		synchronize: process.env.NODE_ENV === 'test', | ||||
| 		dropSchema: process.env.NODE_ENV === 'test', | ||||
| 		cache: !config.db.disableCache && process.env.NODE_ENV !== 'test' ? { // dbをcloseしても何故かredisのコネクションが内部的に残り続けるようで、テストの際に支障が出るため無効にする(キャッシュも含めてテストしたいため本当は有効にしたいが...) | ||||
|   | ||||
| @@ -42,7 +42,7 @@ export const meta = { | ||||
| 			id: '4362f8dc-731f-4ad8-a694-be2a88922a24', | ||||
| 		}, | ||||
| 		rootForbidden: { | ||||
| 			message: 'The root cant migrate.', | ||||
| 			message: 'The root can\'t migrate.', | ||||
| 			code: 'NOT_ROOT_FORBIDDEN', | ||||
| 			id: '4362e8dc-731f-4ad8-a694-be2a88922a24', | ||||
| 		}, | ||||
| @@ -92,42 +92,46 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		private apPersonService: ApPersonService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			// Check parameter | ||||
| 			// check parameter | ||||
| 			if (!ps.moveToAccount) throw new ApiError(meta.errors.noSuchMoveTarget); | ||||
| 			// Abort if user is the root | ||||
| 			// abort if user is the root | ||||
| 			if (me.isRoot) throw new ApiError(meta.errors.rootForbidden); | ||||
| 			// Abort if user has already moved | ||||
| 			// abort if user has already moved | ||||
| 			if (me.movedToUri) throw new ApiError(meta.errors.alreadyMoved); | ||||
|  | ||||
| 			let unfiltered = ps.moveToAccount; | ||||
| 			if (!unfiltered) throw new ApiError(meta.errors.noSuchMoveTarget); | ||||
|  | ||||
| 			// Parse user's input into the destination account | ||||
| 			// parse user's input into the destination account | ||||
| 			if (unfiltered.startsWith('acct:')) unfiltered = unfiltered.substring(5); | ||||
| 			if (unfiltered.startsWith('@')) unfiltered = unfiltered.substring(1); | ||||
| 			if (!unfiltered.includes('@')) throw new ApiError(meta.errors.notRemote); | ||||
|  | ||||
| 			const userAddress = unfiltered.split('@'); | ||||
| 			// Retrieve the destination account | ||||
| 			const remoteMoveTo = await this.remoteUserResolveService.resolveUser(userAddress[0], userAddress[1]).catch((e) => { | ||||
| 			// retrieve the destination account | ||||
| 			let moveTo = await this.remoteUserResolveService.resolveUser(userAddress[0], userAddress[1]).catch((e) => { | ||||
| 				this.apiLoggerService.logger.warn(`failed to resolve remote user: ${e}`); | ||||
| 				throw new ApiError(meta.errors.noSuchMoveTarget); | ||||
| 			}); | ||||
| 			const moveTo = await this.getterService.getRemoteUser(remoteMoveTo.id); | ||||
| 			if (!moveTo.uri) throw new ApiError(meta.errors.uriNull); | ||||
| 			await this.apPersonService.updatePerson(moveTo.uri); | ||||
| 			// Only allow moving to a remote account | ||||
| 			const remoteMoveTo = await this.getterService.getRemoteUser(moveTo.id); | ||||
| 			if (!remoteMoveTo.uri) throw new ApiError(meta.errors.uriNull); | ||||
|  | ||||
| 			// update local db | ||||
| 			await this.apPersonService.updatePerson(remoteMoveTo.uri); | ||||
| 			// retrieve updated user | ||||
| 			moveTo = await this.apPersonService.resolvePerson(remoteMoveTo.uri); | ||||
| 			// only allow moving to a remote account | ||||
| 			if (this.userEntityService.isLocalUser(moveTo)) throw new ApiError(meta.errors.notRemote); | ||||
|  | ||||
| 			let allowed = false; | ||||
|  | ||||
| 			const fromUrl = `${this.config.url}/users/${me.id}`; | ||||
| 			// Make sure that the user has indicated the old account as an alias | ||||
| 			// make sure that the user has indicated the old account as an alias | ||||
| 			moveTo.alsoKnownAs?.forEach((elem) => { | ||||
| 				if (fromUrl.includes(elem)) allowed = true; | ||||
| 			}); | ||||
|  | ||||
| 			// Abort if unintended | ||||
| 			// abort if unintended | ||||
| 			if (!(allowed && moveTo.uri && fromUrl)) throw new ApiError(meta.errors.remoteAccountForbids); | ||||
|  | ||||
| 			return await this.accountMoveService.moveToRemote(me, moveTo); | ||||
|   | ||||
| @@ -17,8 +17,8 @@ | ||||
| 		<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> | ||||
| 			<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template> | ||||
| 			<template #caption> | ||||
| 				<span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })" /> | ||||
| 				<span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })" /> | ||||
| 				<span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/> | ||||
| 				<span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/> | ||||
| 			</template> | ||||
| 		</MkInput> | ||||
| 		<MkSelect v-if="select" v-model="selectedValue" autofocus> | ||||
| @@ -32,11 +32,11 @@ | ||||
| 			</template> | ||||
| 		</MkSelect> | ||||
| 		<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons"> | ||||
| 			<MkButton v-if="showOkButton" inline primary :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> | ||||
| 			<MkButton v-if="showCancelButton || input || select" inline @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> | ||||
| 			<MkButton v-if="showOkButton" inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton> | ||||
| 			<MkButton v-if="showCancelButton || input || select" inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton> | ||||
| 		</div> | ||||
| 		<div v-if="actions" :class="$style.buttons"> | ||||
| 			<MkButton v-for="action in actions" :key="action.text" inline :primary="action.primary" :danger="action.danger" @click="() => { action.callback(); modal?.close(); }">{{ action.text }}</MkButton> | ||||
| 			<MkButton v-for="action in actions" :key="action.text" inline rounded :primary="action.primary" :danger="action.danger" @click="() => { action.callback(); modal?.close(); }">{{ action.text }}</MkButton> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </MkModal> | ||||
|   | ||||
| @@ -4,12 +4,16 @@ | ||||
| 	<MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> | ||||
| 	<div v-if="user.isCat" :class="[$style.ears, { [$style.mask]: useBlurEffect }]"> | ||||
| 		<div :class="$style.earLeft"> | ||||
| 			<div v-if="useBlurEffect" :class="$style.layer"> | ||||
| 			<div v-if="false" :class="$style.layer"> | ||||
| 				<div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/> | ||||
| 				<div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/> | ||||
| 				<div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div :class="$style.earRight"> | ||||
| 			<div v-if="useBlurEffect" :class="$style.layer"> | ||||
| 			<div v-if="false" :class="$style.layer"> | ||||
| 				<div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/> | ||||
| 				<div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/> | ||||
| 				<div :class="$style.plot" :style="{ backgroundImage: `url(${JSON.stringify(url)})` }"/> | ||||
| 			</div> | ||||
| 		</div> | ||||
| @@ -195,11 +199,21 @@ watch(() => props.user.avatarBlurhash, () => { | ||||
|  | ||||
| 				> .plot { | ||||
| 					contain: strict; | ||||
| 					position: absolute; | ||||
| 					width: 100%; | ||||
| 					height: 100%; | ||||
| 					clip-path: path('M0 0H1V1H0z'); | ||||
| 					transform: scale(32767); | ||||
| 					transform-origin: 0 0; | ||||
| 					opacity: 0.5; | ||||
|  | ||||
| 					&:first-child { | ||||
| 						opacity: 1; | ||||
| 					} | ||||
|  | ||||
| 					&:last-child { | ||||
| 						opacity: calc(1 / 3); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -221,6 +235,14 @@ watch(() => props.user.avatarBlurhash, () => { | ||||
|  | ||||
| 				> .plot { | ||||
| 					background-position: 20% 10%; /* ~= 37.5deg */ | ||||
|  | ||||
| 					&:first-child { | ||||
| 						background-position-x: 21%; | ||||
| 					} | ||||
|  | ||||
| 					&:last-child { | ||||
| 						background-position-y: 11%; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -241,7 +263,16 @@ watch(() => props.user.avatarBlurhash, () => { | ||||
| 										-38.5857864376%); /* 40 - 2 * sqrt(2) */ | ||||
|  | ||||
| 				> .plot { | ||||
| 					position: absolute; | ||||
| 					background-position: 80% 10%; /* ~= 37.5deg */ | ||||
|  | ||||
| 					&:first-child { | ||||
| 						background-position-x: 79%; | ||||
| 					} | ||||
|  | ||||
| 					&:last-child { | ||||
| 						background-position-y: 11%; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -166,7 +166,7 @@ const menuDef = computed(() => [{ | ||||
| 		active: currentPage?.route.name === 'import-export', | ||||
| 	}, { | ||||
| 		icon: 'ti ti-plane', | ||||
| 		text: i18n.ts.migration, | ||||
| 		text: i18n.ts.accountMigration, | ||||
| 		to: '/settings/migration', | ||||
| 		active: currentPage?.route.name === 'migration', | ||||
| 	}, { | ||||
|   | ||||
| @@ -1,22 +1,22 @@ | ||||
| <template> | ||||
| <div class="_gaps_m"> | ||||
| 	<FormSection first> | ||||
| 		<template #label>{{ i18n.ts.moveTo }}</template> | ||||
| 		<template #label>{{ i18n.ts._accountMigration.moveTo }}</template> | ||||
| 		<MkInput v-model="moveToAccount" manual-save> | ||||
| 			<template #prefix><i class="ti ti-plane-departure"></i></template> | ||||
| 			<template #label>{{ i18n.ts.moveToLabel }}</template> | ||||
| 			<template #label>{{ i18n.ts._accountMigration.moveToLabel }}</template> | ||||
| 		</MkInput> | ||||
| 	</FormSection> | ||||
| 	<FormInfo warn>{{ i18n.ts.moveAccountDescription }}</FormInfo> | ||||
| 	<FormInfo warn>{{ i18n.ts._accountMigration.moveAccountDescription }}</FormInfo> | ||||
|  | ||||
| 	<FormSection> | ||||
| 		<template #label>{{ i18n.ts.moveFrom }}</template> | ||||
| 		<template #label>{{ i18n.ts._accountMigration.moveFrom }}</template> | ||||
| 		<MkInput v-model="accountAlias" manual-save> | ||||
| 			<template #prefix><i class="ti ti-plane-arrival"></i></template> | ||||
| 			<template #label>{{ i18n.ts.moveFromLabel }}</template> | ||||
| 			<template #label>{{ i18n.ts._accountMigration.moveFromLabel }}</template> | ||||
| 		</MkInput> | ||||
| 	</FormSection> | ||||
| 	<FormInfo warn>{{ i18n.ts.moveFromDescription }}</FormInfo> | ||||
| 	<FormInfo warn>{{ i18n.ts._accountMigration.moveFromDescription }}</FormInfo> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -60,7 +60,7 @@ watch(moveToAccount, async () => { | ||||
| }); | ||||
|  | ||||
| definePageMetadata({ | ||||
| 	title: i18n.ts.migration, | ||||
| 	title: i18n.ts.accountMigration, | ||||
| 	icon: 'ti ti-plane', | ||||
| }); | ||||
| </script> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user